You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Support parsing <think> and <thinking> tags as reasoning blocks #11439
In v1.1.45, opencode supported parsing <think> tags in model responses and displaying them as special reasoning blocks in the UI. This was useful for adding thinking capability to models that don't have native extended thinking support (e.g., via system prompts or custom providers).
However, since v1.1.46, this functionality no longer works. Both <think> and <thinking> tags are now rendered as plain text mixed into the response content.
Root Cause
PR #11270 removed the extractReasoningMiddleware from packages/opencode/src/session/llm.ts:
Many users add thinking capability to models via proxy services or custom providers. Typical implementation flow:
Model emits reasoning event
↓
Detect thinking content, mark isThinking=true
↓
Output in different formats based on config:
- reasoning_content field (DeepSeek native)
- <think>...</think> tag (OpenAI style)
- <thinking>...</thinking> tag (Claude style)
↓
Client parses and displays as reasoning block
Core logic:
// 1. Detect thinking contentif(reasoning.text){onChunk(reasoning.text,undefined,true)// isThinking=true}// 2. Output based on format configif(isThinking){if(format==='think'){send({content: `<think>${text}</think>`})}elseif(format==='thinking'){send({content: `<thinking>${text}</thinking>`})}else{send({reasoning_content: text})// DeepSeek format}}
In v1.1.45, extractReasoningMiddleware correctly parsed <think> tags. After removal, these tags can only be displayed as plain text.
Suggested Solution
Add a configuration option to re-enable tag-based reasoning extraction:
This issue might be a duplicate of existing issues. Please check:
[FEATURE]: support extracting reasoning XML #6337: [FEATURE]: support extracting reasoning XML - requests support for extractReasoningMiddleware() to parse <think> tags, the exact same missing functionality
The PR title says the middleware was "preventing <think> blocks from being sent back as assistant message content".
This means opencode intentionally wants <think> tags to remain as plain text in the response, rather than being intercepted and converted to reasoning events.
3. Current behavior with different formats
Format
Behavior in v1.1.46+
<think> tag
Displayed as plain text (not as reasoning block)
<thinking> tag
Displayed as plain text (not as reasoning block)
reasoning_content field
Completely ignored - no content shown at all
4. Potential conflict
If users add thinking capability to models via proxy (like outputting <think> tags), and opencode's own prompts (e.g., beast.txt) also trigger thinking output, there could be:
Duplicate thinking content
Inconsistent formatting
Confusion about which thinking content is from the model vs the proxy
Suggestion
Consider providing a configuration option that allows users to choose how <think>/<thinking> tags should be handled:
Display as plain text (current behavior) - for models with native reasoning
Parse as reasoning blocks (v1.1.45 behavior) - for proxy-based thinking extensions
Strip completely - for users who don't want to see thinking content
This would give users flexibility based on their setup.
Also running into this. Our use case is that we have our own LLM inference which does not put reasoning in a separate reasoning_content field, it just outputs <think>...</think> tags around the reasoning output. It would be very nice to get this functionality back.
Reacted by Achim Herwig, kevin, OFYA61, Santiago M. Mola, Grace, Tim Schröder, psi00 and hussainbiedouh
The reason I did this is because you need to send those think tags back to the llm in the assistant message, so yeah we should prolly render them as reasoning but they actually are part of assistant message and need to be sent as such
Thanks for the explanation! Makes sense that tags need to stay for LLM context.
Would it be possible to handle this at the rendering layer — keep raw tags in storage, render as reasoning blocks in UI.
Thanks for your work!
Reacted by Johannes Loher, Nick, Lanyuanxiaoyao and Grace
Besides cosmetics, will the lack of parsing degrade the operation/function of OpenCode? Does it introduce any risks?
What's the current workaround, if any?
P.S. I connected qwen3-next-80b-a3b-thinking-mlx through LMStudio, and it of course spills out <think> tags.
In LM Studio, have you tried enabling: Settings -> Developer -> Experimental Settings -> "When applicable, separate reasoning_content and content in API responses"?
Reacted by Emerson Gomes, Kevin Cali, Elijah, luikore, theo and Svahnen-Perific
In LM Studio, have you tried enabling: Settings -> Developer -> Experimental Settings -> "When applicable, separate reasoning_content and content in API responses"?
Thanks, that worked for me!
Reacted by Vasili Puchko, Andris Ļaksa, theo and Svahnen-Perific
Besides cosmetics, will the lack of parsing degrade the operation/function of OpenCode? Does it introduce any risks?
What's the current workaround, if any?
P.S. I connected qwen3-next-80b-a3b-thinking-mlx through LMStudio, and it of course spills out <think> tags.
In LM Studio, have you tried enabling: Settings -> Developer -> Experimental Settings -> "When applicable, separate reasoning_content and content in API responses"?
Telling LM Studio to enable its separation of reasoning_content and content can cause random running agent terminations. There's extensive work about this problem:
If I'm not mistaken, disabling that option prevents the harness from sometimes suddenly stopping when using LM Studio. It's a parsing problem on LM Studio's part that causes our OpenCode to receive empty content fields in the response, with everything having been placed into the thinking's reasoning_content instead by LM Studio's messaging. The result is the OpenCode harness believing there's nothing left to do and so any running agent work just suddenly stops, ending on OpenCode's aesthetic thinking block (instead of a normal, non-thinking assistant message section).
It's not pretty, but currently not trusting the LM Studio parser to correctly separate thinking and non-thinking message parts is the safe and function option. Until either LM Studio addresses its parser or OpenCode uses a heuristic on its end to separate thinking and non-thinking message parts itself.
I'm thinking a simple atomic logic operation is all that's needed to determine when to start and stop OpenCode's aesthetic thinking block at the beginning of each message? Like, begin the aesthetic thinking block in the UI whenever <think> is at the message beginning and end the block when </think> is reached? Because currently OpenCode relies on logic that delegates through inversion of control, instead of, basically, having some sort of node within its message parsing process that gives it some responsibility for message formatting. Right now, I mean, it's just a simple if statement whose predicate is one function call result, through an interface contract that we should now be suspicious of.
EDIT:
Wow, I just noticed that there's a commit that possibly does fix this already:
Activity
github-actions commentedon Jan 31, 2026
This issue might be a duplicate of existing issues. Please check:
<think>tags, the exact same missing functionality<think>tags not properly parsed into block - reports that<thinking>tags aren't being properly parsed into reasoning blocks<think>tags - earlier request for rendering<think>tags in the UIreasoning_contenthandling due to extraction issuesFeel free to ignore if none of these address your specific case.
DevSissi commentedon Jan 31, 2026
Additional Finding
After further investigation, I found that opencode has its own thinking mechanism:
1. opencode expects
<think>tags in responsesIn
prompt.ts, there's code that strips<think>tags in certain scenarios (e.g., title generation):2. The PR #11270 intent
The PR title says the middleware was "preventing
<think>blocks from being sent back as assistant message content".This means opencode intentionally wants
<think>tags to remain as plain text in the response, rather than being intercepted and converted to reasoning events.3. Current behavior with different formats
<think>tag<thinking>tagreasoning_contentfield4. Potential conflict
If users add thinking capability to models via proxy (like outputting
<think>tags), and opencode's own prompts (e.g.,beast.txt) also trigger thinking output, there could be:Suggestion
Consider providing a configuration option that allows users to choose how
<think>/<thinking>tags should be handled:This would give users flexibility based on their setup.
ghost91- commentedon Jan 31, 2026
Also running into this. Our use case is that we have our own LLM inference which does not put reasoning in a separate
reasoning_contentfield, it just outputs<think>...</think>tags around the reasoning output. It would be very nice to get this functionality back.rekram1-node commentedon Jan 31, 2026
The reason I did this is because you need to send those think tags back to the llm in the assistant message, so yeah we should prolly render them as reasoning but they actually are part of assistant message and need to be sent as such
DevSissi commentedon Jan 31, 2026
Thanks for the explanation! Makes sense that tags need to stay for LLM context.
Would it be possible to handle this at the rendering layer — keep raw tags in storage, render as reasoning blocks in UI.
Thanks for your work!
erwinh22 commentedon Feb 27, 2026
I've submitted PR #15382 implementing the rendering-layer approach suggested here:
easoning-part div
Shared utility functions (splitThinkBlocks, stripThinkTags) handle both and tag variants with non-greedy regex matching.
meglio commentedon Mar 7, 2026
P.S. I connected
qwen3-next-80b-a3b-thinking-mlxthrough LMStudio, and it of course spills out<think>tags.JDNdeveloper commentedon Mar 7, 2026
In LM Studio, have you tried enabling: Settings -> Developer -> Experimental Settings -> "When applicable, separate
reasoning_contentandcontentin API responses"?fix: render <think> tags as reasoning blocks at display layer
meglio commentedon Mar 7, 2026
Tried just now, did not help:
emerzon commentedon Mar 8, 2026
Thanks, that worked for me!
TzYuiro commentedon Mar 8, 2026
Telling LM Studio to enable its separation of
reasoning_contentandcontentcan cause random running agent terminations. There's extensive work about this problem:https://www.reddit.com/r/LocalLLaMA/comments/1riwhcf/psa_lm_studios_parser_silently_breaks_qwen35_tool/
If I'm not mistaken, disabling that option prevents the harness from sometimes suddenly stopping when using LM Studio. It's a parsing problem on LM Studio's part that causes our OpenCode to receive empty
contentfields in the response, with everything having been placed into the thinking'sreasoning_contentinstead by LM Studio's messaging. The result is the OpenCode harness believing there's nothing left to do and so any running agent work just suddenly stops, ending on OpenCode's aesthetic thinking block (instead of a normal, non-thinking assistant message section).It's not pretty, but currently not trusting the LM Studio parser to correctly separate thinking and non-thinking message parts is the safe and function option. Until either LM Studio addresses its parser or OpenCode uses a heuristic on its end to separate thinking and non-thinking message parts itself.
I'm thinking a simple atomic logic operation is all that's needed to determine when to start and stop OpenCode's aesthetic thinking block at the beginning of each message? Like, begin the aesthetic thinking block in the UI whenever
<think>is at the message beginning and end the block when</think>is reached? Because currently OpenCode relies on logic that delegates through inversion of control, instead of, basically, having some sort of node within its message parsing process that gives it some responsibility for message formatting. Right now, I mean, it's just a simple if statement whose predicate is one function call result, through an interface contract that we should now be suspicious of.EDIT:
Wow, I just noticed that there's a commit that possibly does fix this already:
erwinh22@8cd9f45
I'm not on the bleeding edge, I have OpenCode installed through Arch Linux's Pacman, so I probably don't have that update yet.
fix: render think tags as reasoning blocks + fix MCP MaxListeners ove…
mishl-dev commentedon Apr 14, 2026
minimax-m2.7 uses tag
zuigzm commentedon May 18, 2026
Please optimize this issue, which is not fatal but is still annoying. Thank you.
github-actions commentedon Jul 18, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.
psi00 commentedon Aug 9, 2026
I would like to bump this as I've started using a different custom provider that leaks content in tags as plaintext in Opencode.