Issues / #1526
#1526 A </think> the model quotes while thinking still ends the reasoning (output side of #537)
open · @BlueKingMuch · 0 comentários · No GitHub
Descrição
**Version:** v0.1.40.3 and v0.1.40.4 (`serve/` is the same in both), Qwen3.8-Flash-Next IQ3_S
#537 reported a literal `</think>` both in the prompt and in the model's output. c16f31f8 fixed the prompt side: the tag in message text is now encoded as text. The output side is still open: in `OutputParser.feed` the reasoning ends at the first `</think>` in the model's output (`i = self.buf.find(THINK_END)`), even when the model only quotes the tag while thinking.
**Seen in practice.** In agent runs with Oh My Pi, Claude Code and Codex CLI against v0.1.40.3 on Strata's own `serve/` code, this happened 7 times in 4 of 6 runs, in all three harnesses. The model quoted, for example, `RecordingPrompt(tok, "</think>\n\n2", ...)` or `prompt.endswith("</think>\n\n")` from `serve/test_server.py`, `` `</think>` `` in prose, or `text = ("</think>\n\n...` in a code block. Then:
- the rest of the thinking came back as `content` (up to about 25,000 characters in one reply), with the real `</think>` as visible text;
- once, an example call from `ToolCallTerminators` that the model quoted later in its thinking was run as a real `write` call (the agent noticed and deleted the file);
- once, the model stopped mid-thought; the reply already had content, so the client took the leaked thinking as the final answer and the run ended.
**Reproduction** (parser only, from the repo root):
```python
from serve.frontend import OutputParser
p = OutputParser(thinking=True)
evs = p.feed('The mock engine returns "</think>\\n\\n2" here.\n</think>\n\nThe answer.') + p.finish()
for kind in ("reasoning", "content"):
print(kind, repr("".join(e.text for e in evs if e.kind == kind)))
# reasoning 'The mock engine returns "'
# content '\\n\\n2" here.\n</think>\n\nThe answer.'
```
**Proposed fix.** A `</think>` written right after a quote or backtick and followed by anything but a line break is reasoning text. The end of the thinking is followed by a line break, so it still ends the reasoning. Until the character after such a tag arrives, the tag is held like a partial one, so streamed and whole outputs agree; if the turn ends right there, it was the end (a forced tool call still opens there, and the #1053 retry is not triggered, as before). This keeps all 7 cases above in the reasoning. A tag that does not directly follow a quote mark or backtick (for example `"\n</think>\n\n"` in code, or one alone on a line in a code block) still ends the thinking: the rule stays narrow so that a real end is not taken for a quote.
Branch (one commit on v0.1.40.4, 6 new tests, the full suite passes): https://github.com/BlueKingMuch/Strata/tree/quoted-think-end
No site
Links install, modelos, releases.