Skip to content

studio: deterministic backend tool-calling wiring test#6836

Open
danielhanchen wants to merge 4 commits into
mainfrom
dettest-toolcall-wiring
Open

studio: deterministic backend tool-calling wiring test#6836
danielhanchen wants to merge 4 commits into
mainfrom
dettest-toolcall-wiring

Conversation

@danielhanchen

Copy link
Copy Markdown
Member

Follow-up to the tool-call parser test PRs #5620 and #5704.

What

Adds one deterministic, download-free backend test:
studio/backend/tests/test_safetensors_toolcall_wiring.py.

No production code changes. The file is auto-discovered by the existing Backend CI
pytest matrix (studio-backend-ci.yml), needs no workflow edit, and runs sub-second.

Why

test_safetensors_tool_loop.py already covers parse_tool_calls_from_text and the
cumulative-text state machine in run_safetensors_tool_loop exhaustively, using fake
single-turn generators. What it does not cover is the backend own tool-injection seam:
both InferenceBackend (transformers) and MLXInferenceBackend render the prompt through
the shared apply_chat_template_for_generation(..., tools=...) helper and then stream
cumulative text into the shared run_safetensors_tool_loop. A regression that drops the
tool schema before it reaches the tokenizer, or that fails to feed a tool result back into
generation, would pass the existing suite but break real tool calling.

Because MLX and safetensors share the exact same injection helper and loop, one CPU test of
that seam guards both paths, including the macOS MLX path, without importing mlx.

What it asserts

The test mirrors the real backend _single_turn wiring with fakes (a tokenizer that records
the tools it is handed, a canned tool-call generation, a stub executor) and checks the full
chain end to end:

  • the tool schema reaches the tokenizer via the real helper (catches a silent drop on a
    chat-template fallback)
  • the loop offers the tool to the first generation turn
  • exactly one tool_start, one dispatch with the parsed arguments, one tool_end with the result
  • the final answer streams after the tool result (the loop appended the tool output and
    re-entered generation)
  • the raw tool-call markup never leaks to the client as content

It imports no torch, unsloth, or mlx, so it stays in the portable Backend CI next to the parser
tests. It reuses the existing fake-tokenizer and stub-executor patterns from
test_safetensors_tool_loop.py.

Testing

  • cd studio/backend && python -m pytest tests/test_safetensors_toolcall_wiring.py -q passes in
    ~0.2s with only studio.txt + pytest installed (no torch/unsloth/mlx).
  • Re-ran tests/test_safetensors_tool_loop.py alongside it: no regressions.
  • ruff check clean.

danielhanchen and others added 2 commits July 3, 2026 08:06
Add a deterministic, download-free test that exercises the shared tool-calling
seam both inference backends use. InferenceBackend (transformers) and
MLXInferenceBackend both render the prompt through
apply_chat_template_for_generation(..., tools=...) and stream cumulative text
into run_safetensors_tool_loop. The existing test_safetensors_tool_loop.py
covers the parser and the loop state machine with fake generators but does not
cover the backend's own tool-injection seam, so a regression that drops the
tool schema before the tokenizer, or fails to feed a tool result back into
generation, would slip through.

The test drives that seam with fakes: a tokenizer that records the tools it is
handed, a canned tool-call generation, and a stub executor. It asserts the full
chain: tools reach the chat template, the loop parses the call, the tool is
dispatched once with the parsed arguments, the result is fed back, generation
re-enters, and the final answer streams after the tool result. It also guards
that the raw tool-call markup never leaks to the client as content.

The test imports no torch, unsloth, or mlx, so it runs in the portable Backend
CI alongside the tool-call parser tests and stays sub-second. Follow-up to the
parser test PRs #5620 and #5704.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new deterministic backend-wiring test suite (test_safetensors_toolcall_wiring.py) to verify the safetensors and MLX tool-calling path. It utilizes a fake tokenizer and a stub executor to validate the agentic tool loop end-to-end without external dependencies. There are no review comments, so no feedback is provided.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40c46cd709

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +121 to +123
active_tools_seen.append(active_tools)
apply_chat_template_for_generation(tok, conversation, tools=active_tools)
text = next(turns)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exercise the real tool-forwarding seam

This fake hard-codes the behavior the test is meant to protect: it passes active_tools directly into apply_chat_template_for_generation and then returns the next canned turn regardless of the conversation. A regression in the production wiring (for example InferenceOrchestrator.generate_chat_completion_with_tools or the backend generation method dropping tools before rendering, or failing to append the tool result before the second turn) would still pass because this single_turn does not use those production paths or require the tool result to be present in conversation. Make the fake depend on the received conversation/tools, or drive the actual backend wrapper seam, so the test fails for the wiring regressions it claims to cover.

Useful? React with 👍 / 👎.

Strengthen the wiring test so single_turn records each turn's conversation and
the test asserts the tool result message is present in the conversation handed
to the final generation turn. Event ordering alone did not catch a loop that
stops appending the tool output before re-entering generation, because the fake
generation ignores the conversation; this closes that gap.
@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cd01929ded

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# then yield cumulative snapshots (each yield is all text so far, per the loop contract).
active_tools_seen.append(active_tools)
conversations_seen.append([dict(m) for m in conversation])
apply_chat_template_for_generation(tok, conversation, tools = active_tools)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Drive the real tool-forwarding seam

When the regression is in the production _single_turn wiring (for example, InferenceOrchestrator.generate_chat_completion_with_tools or InferenceBackend.generate_chat_completion_with_tools stops passing tools = turn_tools), this test would still pass because the local fake performs the apply_chat_template_for_generation(..., tools = active_tools) call itself. That means the new test does not actually guard the backend seam it was added to cover; consider driving the real backend/orchestrator method with a stubbed generation backend/tokenizer instead of reimplementing the forwarding in the test.

Useful? React with 👍 / 👎.

@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: cd01929ded

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# then yield cumulative snapshots (each yield is all text so far, per the loop contract).
active_tools_seen.append(active_tools)
conversations_seen.append([dict(m) for m in conversation])
apply_chat_template_for_generation(tok, conversation, tools = active_tools)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Exercise the real backend wiring

In the production transformers path, the forwarding being guarded happens in InferenceBackend.generate_chat_completion_with_tools_generate_chat_response_inner (studio/backend/core/inference/inference.py:786-805, then :970-974), and MLX has its own helper calls (mlx_inference.py:360-364/:470-474). This fake bypasses all of those call sites and hands active_tools to apply_chat_template_for_generation itself, so dropping tools=turn_tools or tools=tools in the real backend would still leave this test green. To make this a backend-wiring test, patch/call the backend method and observe the tokenizer from there.

Useful? React with 👍 / 👎.

@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: cd01929ded

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: 804bfd4377

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Bravo.

Reviewed commit: 804bfd4377

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 804bfd4377

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

# then yield cumulative snapshots (each yield is all text so far, per the loop contract).
active_tools_seen.append(active_tools)
conversations_seen.append([dict(m) for m in conversation])
apply_chat_template_for_generation(tok, conversation, tools = active_tools)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Route the check through the backend seam

Because this fake single_turn itself invokes apply_chat_template_for_generation(..., tools=active_tools), the test still passes if the real safetensors backend stops forwarding tools=turn_tools before calling generate_chat_response / _generate_chat_response_inner; it only proves the loop passes active_tools to this local fake and that the helper forwards what it was given. In that regression scenario the production backend never injects schemas, which is the seam this test is meant to guard, so this should exercise the real backend method with generation/helper monkeypatched or assert at that boundary.

Useful? React with 👍 / 👎.

@danielhanchen

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Delightful!

Reviewed commit: 804bfd4377

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant