feat: Add text-tags fallback mode for LLM tool calling (fixes #1138) - #1149
feat: Add text-tags fallback mode for LLM tool calling (fixes #1138)#1149manideep-malyala wants to merge 2 commits into
Conversation
Greptile SummaryThis PR adds a configurable text-tag fallback for models that cannot reliably produce native structured tool calls.
Confidence Score: 4/5This PR should not merge until the interceptor parses the SDK message content parts, because its new fallback currently fails to dispatch normal tagged tool calls. The model wrapper checks for tags against a structured content list while existing SDK consumers extract text from each content part, so the main feature silently leaves tagged responses unconverted. Files Needing Attention: strix/config/models.py Important Files Changed
Prompt To Fix All With AI### Issue 1
strix/config/models.py:503-504
**Structured message content breaks dispatch**
When a model returns the prompted `[TOOL: ...]...[/TOOL]` call in a normal SDK assistant message, `item.content` is a list of structured text parts, so this list-membership check never finds the tag inside a part's text. The response remains unchanged and no `ResponseFunctionToolCall` is synthesized, preventing the fallback mode from dispatching the tool and allowing the tool-driven scan to progress.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (1): Last reviewed commit: "feat: Add text-tags fallback mode for LL..." | Re-trigger Greptile |
| if content and "[TOOL:" in content: | ||
| matches = list(TEXT_TAG_PATTERN.finditer(content)) |
There was a problem hiding this comment.
Structured message content breaks dispatch
When a model returns the prompted [TOOL: ...]...[/TOOL] call in a normal SDK assistant message, item.content is a list of structured text parts, so this list-membership check never finds the tag inside a part's text. The response remains unchanged and no ResponseFunctionToolCall is synthesized, preventing the fallback mode from dispatching the tool and allowing the tool-driven scan to progress.
Knowledge Base Used: Agents and Prompts
Prompt To Fix With AI
This is a comment left during a code review.
Path: strix/config/models.py
Line: 503-504
Comment:
**Structured message content breaks dispatch**
When a model returns the prompted `[TOOL: ...]...[/TOOL]` call in a normal SDK assistant message, `item.content` is a list of structured text parts, so this list-membership check never finds the tag inside a part's text. The response remains unchanged and no `ResponseFunctionToolCall` is synthesized, preventing the fallback mode from dispatching the tool and allowing the tool-driven scan to progress.
**Knowledge Base Used:** [Agents and Prompts](https://app.greptile.com/strix-org-3/-/custom-context/knowledge-base/usestrix/strix/-/docs/agents-and-prompts.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
Fixed in the latest commit. The interceptor now handles cases where item.content is delivered as a list of structured TextPart dictionaries by normalizing it into a flat string before executing the regex search and extraction.
The Problem
Currently, the Strix execution engine relies on native, structured API tool calling formats (e.g. strict JSON schemas). While commercial models handle this well, many local, open-source models (like Llama-3, Mistral, or Qwen via Ollama/vLLM) lack fine-tuning for structured tool calling. When passed a list of tools, they often ignore them, hallucinate arguments, or output malformed JSON directly into their conversational text block, causing Strix to crash.
The Solution
This PR addresses #1138 by introducing a robust fallback mechanism: Text-Tag Tool Dispatch.
When enabled, Strix will instruct the LLM to output tool calls as plain-text markdown tags (e.g.,
[TOOL: tool_name] {"args": ...} [/TOOL]). An interceptor then strips these tags from the conversation history and translates them into the nativeResponseFunctionToolCallinstances that the Strix engine expects.This creates an invisible compatibility layer, allowing any generic text-generating model to seamlessly use Strix tools.
Implementation Details
--llm-tool-mode=text-tagsCLI argument.tool_modeparameter insettings.llm(defaulting to"native").build_scope_contextnow threadstool_modedown to the root and leaf agents.system_prompt.jinjadynamically appends formatting instructions teaching the model how to use the[TOOL: ...]syntax.models.py):_TextTagDispatchModelwrapper class.[TOOL:]...[/TOOL]blocks, and synthesizes them into native Strix SDK tool calls.text-tagsautomatically wraps the LLM provider in_NonStreamingModel. This ensures the regex always acts on a fully aggregated text block.tests/test_runner_root_prompt.pyto account for the newtool_modeattribute in the mock assertions. The full suite of 955 tests passes.Closes #1138