Skip to content

Iris: Fix local session titles not updating#412

Open
bassner wants to merge 7 commits intomainfrom
fix/iris-local-title-prompt-braces
Open

Iris: Fix local session titles not updating#412
bassner wants to merge 7 commits intomainfrom
fix/iris-local-title-prompt-braces

Conversation

@bassner
Copy link
Member

@bassner bassner commented Feb 6, 2026

Summary

Fix local session title generation so titles update reliably with local Ollama models.

The main issue was that the title decision prompt shape could produce empty model output ("") in local mode, even when regular chat responses worked.

Changes

  • Keep rendered prompt text safe for LangChain templating (avoid brace parsing issues in message content).
  • Refactor title generation prompting to a clearer two-role structure:
    • system: decision policy/rules
    • user: current title + recent messages + strict output instruction
  • Pass recent messages as readable newline text instead of Python list representation.
  • Limit title context size for local robustness:
    • last 10 messages
    • truncate each message line to 400 chars
  • Make title generation deterministic and less fragile:
    • temperature=0.0
    • max_tokens=128
  • Keep raw title-output logging for debugging (Session title raw LLM output).

Test plan

  • cd iris && poetry run pytest -q
  • cd iris && poetry run black --check src/iris/pipeline/session_title_generation_pipeline.py tests/test_session_title_generation_pipeline.py
  • Validate local runtime with Ollama-backed chat (session title updates now work)

Summary by CodeRabbit

  • New Features

    • Improved session title generation: recent messages are formatted and filtered to ignore empty/whitespace messages, and prompts now enforce a single-line KEEP or UPDATE response.
    • Language model behavior tuned for more consistent title decisions.
  • Tests

    • Added unit tests validating session title generation, retry behavior, and pipeline outputs.

Use a dedicated prompt variable when constructing the title-generation ChatPromptTemplate so rendered message text is treated as data instead of format syntax. This prevents ValueError crashes when recent messages include brace patterns that appear in local/Ollama outputs. Add a regression test that covers the failing conversion-spec and nested-brace cases.
@bassner bassner requested a review from a team as a code owner February 6, 2026 16:38
@bassner bassner added this to the v1.7.0 milestone Feb 6, 2026
@bassner bassner self-assigned this Feb 6, 2026
@github-actions github-actions bot added the iris label Feb 6, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 6, 2026

📝 Walkthrough

Walkthrough

Formats and truncates recent messages, moves system prompt text into a {prompt_text} placeholder, adjusts LLM settings, logs raw LLM output, retries once on an empty LLM response, filters out empty/whitespace messages from history, and makes TextMessageContentDTO.text_content optional with a default "".

Changes

Cohort / File(s) Summary
Session Title Pipeline
iris/src/iris/pipeline/session_title_generation_pipeline.py
Adds MAX_RECENT_MESSAGES and MAX_MESSAGE_CHARS; sets LLM temperature=0.0, max_tokens=128; adds format_recent_messages classmethod to cap/truncate/prefix recent messages; uses {prompt_text} system placeholder and supplies formatted recent messages in the user prompt; logs raw LLM output and keeps token assignment; retains existing error handling and a single retry on empty response.
Prompt Template
iris/src/iris/pipeline/prompts/templates/session_title_generation_prompt.j2
Removes direct inclusion of current_session_title and recent_messages from the template; template now instructs model to output KEEP or UPDATE and relies on pipeline to pass contextual data.
Pipeline Unit Tests
iris/tests/test_session_title_generation_pipeline.py
Adds two tests: one verifies brace-containing recent messages are handled and pipeline token assignment; the other simulates an initial empty LLM response then a successful retry, asserting two invocations and final non-empty output.
Abstract Agent Pipeline
iris/src/iris/pipeline/abstract_agent_pipeline.py
Adds helper _filter_empty_messages(messages) and applies it when deriving state.message_history and when retrieving recent history, filtering out empty or whitespace-only text message contents.
Text Message DTO
iris/src/iris/domain/data/text_message_content_dto.py
Makes text_content optional by adding a default empty string (text_content: str = Field(alias="textContent", default="")).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Pipeline
  participant Formatter
  participant LLM
  participant Logger
  participant HistoryFilter

  Client->>Pipeline: invoke(current_title, recent_messages, user_language)
  Pipeline->>HistoryFilter: filter recent history (remove empty/whitespace messages)
  HistoryFilter-->>Pipeline: filtered_history
  Pipeline->>Formatter: format_recent_messages(filtered_history)
  Formatter-->>Pipeline: formatted_recent_messages
  Pipeline->>LLM: call(system prompt "{prompt_text}" + user prompt with current_title, formatted_recent_messages, user_language)
  LLM-->>Pipeline: raw_response
  Pipeline->>Logger: log(raw_response)
  alt raw_response empty
    Pipeline->>LLM: retry call (require single non-empty line)
    LLM-->>Pipeline: retry_response
    Pipeline->>Logger: log(retry_response)
    Pipeline-->>Client: return final_title (from retry_response)
  else raw_response non-empty
    Pipeline-->>Client: return final_title (from raw_response)
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I trimmed long lines, numbered the thread,
Filtered the quiet, nudged the LLM’s head,
One extra hop if silence appears,
Short titles bloom after tidy ears,
I hop off happy — carrot crown on my head. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and concisely summarizes the main change: fixing local session titles that were not updating. It's specific, clear, and accurately reflects the primary objective of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch fix/iris-local-title-prompt-braces

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Pylint (4.0.4)
iris/src/iris/domain/data/text_message_content_dto.py
iris/src/iris/pipeline/abstract_agent_pipeline.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Local models often return minor variations of the title decision format (case/spacing, multiline context, or localized keywords). Relax session title decision parsing to accept UPDATE/KEEP variants while keeping strict line-anchored matching to avoid false positives. Add parser-focused tests and keep the brace-regression test.
Add an info log in the session title generation pipeline that records the raw model decision output (truncated) to simplify debugging local/cloud behavior differences.
Revert the relaxed title decision parser and keep strict KEEP/UPDATE parsing. Focus on the root issue instead: local models sometimes return empty output for the title decision call. Add a user-turn prompt and a single fallback retry when attempt 1 is empty, with raw output logging per attempt. Add regression coverage for the retry path.
Refactor title generation to a two-role prompt (system policy + user data), pass recent messages as formatted newline text instead of Python list repr, cap title context to the most recent 10 messages with per-line truncation, and make decoding deterministic with temperature=0 and max_tokens=128. Remove retry-specific test and add coverage for message formatting limits.
@bassner bassner changed the title Iris: Fix session title generation crash with brace content Iris: Fix local session titles not updating Feb 6, 2026
Artemis can send chat history entries with {'type': 'text'} but no
textContent field, causing Pyris to reject the request with 422.
Default textContent to "" and filter out empty messages before they
reach any pipeline.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant