Repository navigation
Conversation
Some OpenAI-compatible gateways stream the title model's reasoning inline in the text channel rather than as a separate reasoning part. The title code keeps the first non-empty line of that text, so a title model that writes `<think>…</think>` followed by the title produced `<think>…` as the session title for every new session. Strip `<think>…</think>` blocks before selecting the line, which is what v1 did before the session rewrite (ad8a4bc, anomalyco#1325). Stripping happens after the deltas join, so a block split across stream deltas still matches. Adds cases for a single block and for one split across deltas; both fail without the change.
|
The following comment was made by an LLM, it may be inaccurate: |
There was a problem hiding this comment.
Looks good. I ran the #57043 reproduction (a simulated title model that streams <think>…</think> and then the title) against the base and this branch. On the base the tab title is the raw reasoning; with this change it's MCP servers list. session-title.test.ts passes (17/17) and packages/core typechecks.
Public API: unchanged.
One thing worth handling: a reasoning block that is never closed (or uses uppercase tags) still ends up in the title. See the inline comment.
| // separate part, and its first line would otherwise become the title. | ||
| return chunks | ||
| .join("") | ||
| .replace(/<think>[\s\S]*?<\/think>\s*/g, "") |
There was a problem hiding this comment.
This only matches a closed, lowercase block. If the title output is cut off mid-reasoning (more likely once #54411 adds an output limit), the text is just <think>… and that becomes the title, which is the same symptom as #57043. A stray </think> or <THINK> also gets through. Could you make the match case-insensitive and also drop a trailing unclosed <think>… up to the end of the output, plus any leftover lone tags? For example:
.replace(/<think>[\s\S]*?(<\/think>|$)\s*/gi, "")
.replace(/<\/?think>/gi, "")Then reasoning-only or truncated output falls through to undefined and keeps the fallback title.
Follow-up to the previous commit: a title stream truncated mid-reasoning leaves `<think>…` with no closing tag, which the closed-block pattern did not match, so the tag still became the title. Uppercase tags and a stray closing tag without an opening block leaked the same way. Match case-insensitively, treat end of output as closing an unterminated block, and drop any leftover tag. Reasoning-only output then yields no title at all, so the session keeps its fallback instead of gaining a reasoning-only one. Adds cases for a truncated uppercase block and for a stray closing tag.
|
Applied both parts of the suggestion in 0d0704d: .replace(/<think>[\s\S]*?(<\/think>|$)\s*/gi, "")
.replace(/<\/?think>/gi, "")
Reasoning-only or truncated output now yields no title, so the session keeps its fallback rather than storing a tag. Two cases added, both red against the previous single-pattern version:
|


Issue for this PR
Closes #57043
Type of change
What does this PR do?
Some OpenAI-compatible gateways (LiteLLM in the report) stream the title model's reasoning inline in the text channel instead of as a separate reasoning part. The title code keeps the first non-empty line of that text, so a title model that writes
<think>…</think>followed by the title produced this as the session title:and the real title on the next line was never used.
This strips
<think>…</think>blocks before selecting the line, which is what v1 did before the session rewrite (ad8a4bc744, #1325). The strip runs after the deltas join, so a block split across stream deltas still matches:If the model emits nothing but reasoning, the result is empty and no rename happens, so a session keeps its fallback title instead of gaining a reasoning-only one.
Relationship to #52006
#52006 also touches this function, but it addresses a different input: it filters chunks whose
providerMetadata.phaseiscommentary, which is structured OpenAI Responses commentary. Inline<think>text inside an ordinarytextchunk carries no phase, so it is not covered by that change. The two are complementary; whichever lands second will need a trivial rebase.How did you verify your code works?
Two cases in
packages/core/test/session-title.test.ts, both confirmed to fail without the change (reverting onlytitle.tsturns both red, and the first reproduces the reported title verbatim):drops inline reasoning blocks from the generated title— the reported shape,<think>…</think>then the title on the next line.drops inline reasoning blocks split across stream deltas— pins that the strip happens after the join, which a per-delta strip would miss.Screenshots / recordings
Not applicable; no UI change.
Checklist
Added by the opencode bot (AI-generated).
Related: #54401, #54185