Skip to content

Conversation

@roomote
Copy link
Contributor

@roomote roomote bot commented Sep 3, 2025

Summary

This PR fixes the rendering of <think> tags in OpenAI-compatible mode. Previously, these tags were displayed as plain text instead of being collapsed into a "Thinking..." spoiler as expected.

Problem

When using Roo Code in OpenAI-compatible mode with models that output <think> tags (like gemini-2.5-flash or gemini-2.5-pro), the thinking content was shown as normal text without the <think> tag, breaking the intended UI behavior.

Solution

  • Added XmlMatcher to parse <think> tags in streaming responses from OpenAI-compatible providers
  • Converts <think> content to reasoning chunks for proper UI rendering
  • Fixed XmlMatcher position parameter to allow matching tags anywhere in content (not just at the beginning)

Testing

Added comprehensive test suite with 10 tests covering:

  • Basic <think> tag parsing
  • Nested <think> tags
  • Partial tags across streaming chunks
  • Content without tags
  • Multiple <think> blocks
  • Empty <think> tags
  • Preservation of existing functionality

Impact

This fix benefits all 8 providers that extend BaseOpenAiCompatibleProvider:

  • FireworksHandler
  • IOIntelligenceHandler
  • ChutesHandler
  • ZAiHandler
  • FeatherlessHandler
  • SambaNovaHandler
  • GroqHandler
  • RooHandler

Fixes #7615


Important

Fixes <think> tag rendering in OpenAI-compatible mode by using XmlMatcher to parse and convert them into reasoning chunks.

  • Behavior:
    • Fixes rendering of <think> tags in OpenAI-compatible mode by collapsing them into "Thinking..." spoilers.
    • Uses XmlMatcher in BaseOpenAiCompatibleProvider to parse <think> tags and convert them to reasoning chunks.
    • Adjusts XmlMatcher position parameter to match tags anywhere in content.
  • Testing:
    • Adds base-openai-compatible-provider.spec.ts with 10 tests for <think> tag parsing, nested tags, partial tags, content without tags, multiple blocks, and empty tags.
  • Impact:
    • Affects all 8 providers extending BaseOpenAiCompatibleProvider, including FireworksHandler, IOIntelligenceHandler, and ChutesHandler.

This description was created by Ellipsis for 0cd73fd. You can customize this summary. It will automatically update as commits are pushed.

- Add XmlMatcher to parse <think> tags in streaming responses
- Convert <think> content to reasoning chunks for proper UI rendering
- Add comprehensive tests for various <think> tag scenarios
- Fix position parameter to allow matching tags anywhere in content

Fixes #7615
@roomote roomote bot requested review from cte, jr and mrubens as code owners September 3, 2025 06:39
@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Sep 3, 2025
Copy link
Contributor Author

@roomote roomote bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewing my own code is like debugging in a mirror - everything looks backwards but the bugs are still mine.

type: chunk.matched ? "reasoning" : "text",
text: chunk.data,
}) as const,
Number.MAX_SAFE_INTEGER,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I notice that we're using Number.MAX_SAFE_INTEGER here to match tags anywhere in the content, but other providers like openai.ts and ollama.ts don't specify a position parameter at all (using the default of 0). Should we standardize this approach across all providers for consistency? Or is there a specific reason why BaseOpenAiCompatibleProvider needs different behavior?

afterEach(() => {
vi.restoreAllMocks()
})

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great comprehensive test coverage! However, I notice we don't have a specific test that verifies the position parameter allows matching tags anywhere in the content (not just at the beginning). Consider adding a test case that validates tags are matched when they appear after position 0 in the content.

const stream = await this.createStream(systemPrompt, messages, metadata)

// Initialize XmlMatcher to parse <think>...</think> tags
// Set position to MAX_SAFE_INTEGER to match tags anywhere in the content
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment explains the purpose well, but could we make it clearer why we use Number.MAX_SAFE_INTEGER specifically? Maybe consider using a named constant like MATCH_ANYWHERE_POSITION or explain why this value over alternatives like Infinity?

vi.restoreAllMocks()
})

describe("<think> tag handling", () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider organizing these tests into nested describe blocks for better structure. For example:

  • "edge cases" (empty tags, nested tags)
  • "streaming scenarios" (partial tags across chunks)
  • "basic functionality" (single tags, multiple tags)

This would make the test suite easier to navigate as it grows.

@hannesrudolph hannesrudolph added the Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. label Sep 3, 2025
@daniel-lxs daniel-lxs moved this from Triage to PR [Needs Prelim Review] in Roo Code Roadmap Sep 4, 2025
@hannesrudolph hannesrudolph added PR - Needs Preliminary Review and removed Issue/PR - Triage New issue. Needs quick review to confirm validity and assign labels. labels Sep 4, 2025
@daniel-lxs
Copy link
Member

Closing see #7615 (comment)

@daniel-lxs daniel-lxs closed this Sep 8, 2025
@github-project-automation github-project-automation bot moved this from New to Done in Roo Code Roadmap Sep 8, 2025
@github-project-automation github-project-automation bot moved this from PR [Needs Prelim Review] to Done in Roo Code Roadmap Sep 8, 2025
@daniel-lxs daniel-lxs deleted the fix/openai-compatible-think-tag-7615 branch September 8, 2025 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working PR - Needs Preliminary Review size:L This PR changes 100-499 lines, ignoring generated files.

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

<think> tag is displayed as plain text instead of hidden reasoning in OpenAI Compatible provider

4 participants