Skip to content

test(gpt-oss): post-tool response integrity for harmony fix - #1548

Closed
CatherineSue wants to merge 1 commit into
mainfrom
test/gpt-oss-harmony-pretty-args-repro
Closed

CatherineSue wants to merge 1 commit into
mainfrom
test/gpt-oss-harmony-pretty-args-repro

Conversation

@CatherineSue

@CatherineSue CatherineSue commented May 26, 2026 •

Copy link
Copy Markdown
Member

Description

Problem

After a tool round trip on gpt-oss models served through the gateway, the post-tool assistant message in choices[].message.content should be a clean user-facing paragraph, not garbled or self-analysis text. PR #1547 fixes the Harmony round-trip path that can otherwise let malformed residue surface on a successful HTTP 200.

There is no integration test today that asserts this end-to-end. The Rust-level unit tests cover the parser/builder pieces individually but do not exercise the property as it would appear to an API client.

Solution

Adds test_post_tool_response_has_no_malformed_residue to TestChatCompletionGptOss in e2e_test/chat_completions/test_openai_server.py. TestChatCompletionGptOss120B inherits it, so the case runs on both the 20B and 120B targets.

The test drives a multi-turn tool-call flow with a style-constrained system prompt, a multi-tool nested-args schema, a confirmation turn, a minimal {"ok": true} tool result, and reasoning_effort="high". It then asserts that the final-turn choices[].message.content:

  • Is non-empty
  • Has no runs of invisible characters
  • Has no long whitespace runs (\s{6,})
  • Has no ellipsis loops
  • Has no leaked English self-analysis markers (The assistant, We need to, We should, We must, According to (rules|guidelines|the spec), Sorry, we|the)

These are the integrity properties #1547 is meant to guarantee. On failure the assertion message reports the matched marker plus the offending final_content so triage is direct.

Changes

  • Add test_post_tool_response_has_no_malformed_residue to TestChatCompletionGptOss in e2e_test/chat_completions/test_openai_server.py.
  • Add module-level constants for the system prompt, the 6-tool nested-args schema, and the four malformed-text detection regexes.

Test Plan

pytest -q -k "test_post_tool_response_has_no_malformed_residue" e2e_test/chat_completions/test_openai_server.py

Expected:

Once CI on this PR shows the failure mode on main, the same test will be cherry-picked onto #1547 to confirm it passes there.

Checklist
  • cargo +nightly fmt passes (no Rust changes)
  • cargo clippy --all-targets --all-features -- -D warnings passes (no Rust changes)
  • (Optional) Documentation updated
  • (Optional) Please join us on Slack #sig-smg to discuss, review, and merge PRs

Summary by CodeRabbit

  • Tests
    • Added an end-to-end test ensuring assistant responses after tool/tool-confirmation flows produce a non-empty, user-facing message free of invisible/malformed characters, unintended internal self-analysis, excessive whitespace runs, or repeating ellipses.

Review Change Stack

@github-actions github-actions Bot added the tests Test changes label May 26, 2026
@coderabbitai

coderabbitai Bot commented May 26, 2026 •

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0948cfcb-97f6-4460-8bf5-df9b6b6bf9c8

📥 Commits

Reviewing files that changed from the base of the PR and between fc8f497 and d7fce2f.

📒 Files selected for processing (1)
  • e2e_test/chat_completions/test_openai_server.py

📝 Walkthrough

Walkthrough

Adds GPT-OSS fixtures (system prompt, nested tool schema, and regex detectors) and a new E2E test that performs a post-tool round trip and asserts the final assistant output is non-empty and contains no malformed residue.

Changes

GPT-OSS post-tool validation

Layer / File(s) Summary
GPT-OSS system prompt, tools, and regex detectors
e2e_test/chat_completions/test_openai_server.py
Adds a style-constrained GPT-OSS system prompt, nested action-tool definitions, and compiled regex detectors for invisible character runs, English self-analysis leakage, long whitespace runs, and ellipsis loops.
Post-tool round-trip E2E test
e2e_test/chat_completions/test_openai_server.py
Adds test_post_tool_response_has_no_malformed_residue which drives a multi-turn chat completion to force a tool call (with a confirmation hop if needed), injects minimal tool results ({"ok": true}), generates the final assistant message, and asserts it's non-empty and does not match any malformed-text detectors.

Sequence Diagram

sequenceDiagram
  participant TestClient
  participant GPT_OSS_Model
  participant ToolRunner
  TestClient->>GPT_OSS_Model: send system + user prompt (request action via tools)
  GPT_OSS_Model->>TestClient: assistant confirmation or tool_call
  alt assistant did not emit tool_call
    TestClient->>GPT_OSS_Model: confirmation user turn (ask to proceed)
    GPT_OSS_Model->>TestClient: emits tool_call
  end
  GPT_OSS_Model->>ToolRunner: tool_call(payload)
  ToolRunner->>GPT_OSS_Model: tool_result {"ok": true}
  GPT_OSS_Model->>TestClient: final assistant message (validated against regexes)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • lightseekorg/smg#569: Affects GPT-OSS e2e execution in the same test module; that PR fixes MRO-aware class marker resolution used for selecting model contexts in tests.
  • lightseekorg/smg#796: Also modifies e2e_test/chat_completions/test_openai_server.py and restructures the TestChatCompletion… test class.

Suggested labels

grpc, tests

Suggested reviewers

  • key4ng
  • slin1237
  • XinyueZhang369

Poem

I nudge the prompt with careful paws,
Through nested tools and regex laws.
No hidden glyph, no trailing space,
A tidy message takes its place.
🐰✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'test(gpt-oss): post-tool response integrity for harmony fix' accurately reflects the main change: adding a test to verify post-tool response integrity for the GPT-OSS harmony fix mentioned in the PR objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/gpt-oss-harmony-pretty-args-repro

Comment @coderabbitai help to get the list of available commands and usage tips.

@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 end-to-end test, test_tool_call_arguments_are_compact_json, to verify that tool-call arguments returned to the client are formatted as compact JSON. The review feedback suggests making the test assertion more robust by allowing both Python's default compact JSON format (with spaces after separators) and Rust's ultra-compact format (without spaces after separators) to prevent false positives from minor whitespace differences.

Comment thread e2e_test/chat_completions/test_openai_server.py Outdated

@claude claude 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.

Clean, well-documented test. The idempotence check (args == json.dumps(json.loads(args))) correctly catches pretty-printed JSON while matching vLLM's normalization behavior. Tool schema is well-chosen to trigger nested pretty-printing. No issues found.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e_test/chat_completions/test_openai_server.py`:
- Around line 529-531: The test builds a canonical JSON string using
json.dumps(parsed) which emits spaces and can mismatch compact args; change the
construction of canonical to use compact JSON by calling json.dumps(parsed,
separators=(',', ':')) (keep any existing sort_keys usage if required) so the
assertion comparing args to canonical uses the same compact formatting; update
the line that defines canonical (currently canonical = json.dumps(parsed)) to
use the separators parameter.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 38671555-3ae7-49e6-89f2-3f82a46aa401

📥 Commits

Reviewing files that changed from the base of the PR and between a87c7e3 and 1b29a51.

📒 Files selected for processing (1)
  • e2e_test/chat_completions/test_openai_server.py

Comment thread e2e_test/chat_completions/test_openai_server.py Outdated
@CatherineSue
CatherineSue force-pushed the test/gpt-oss-harmony-pretty-args-repro branch from 1b29a51 to fc8f497 Compare May 26, 2026 08:03
@CatherineSue CatherineSue changed the title test(gpt-oss): repro for harmony pretty JSON tool args test(gpt-oss): repro for harmony post-tool malformed text May 26, 2026
Comment thread e2e_test/chat_completions/test_openai_server.py

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

♻️ Duplicate comments (1)
e2e_test/chat_completions/test_openai_server.py (1)

638-779: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing assertion for canonical compact tool-call arguments (core repro contract).

Line 638 onward adds a residue-focused flow, but it does not assert the compact JSON invariant for tool_calls[].function.arguments, which is the PR’s stated repro target. This can let the pretty-JSON argument regression slip through undetected.

Proposed minimal assertion helper and checks
@@
     def test_post_tool_response_has_no_malformed_residue(self, model, api_client):
@@
+        def _assert_compact_tool_args(tool_calls):
+            for tc in tool_calls or []:
+                args = tc.function.arguments
+                parsed = json.loads(args)  # also validates JSON
+                canonical = json.dumps(parsed, separators=(",", ":"))
+                assert args == canonical, (
+                    "tool call arguments must be canonical compact JSON; "
+                    f"got {args!r}"
+                )
+
@@
         msg1 = response1.choices[0].message
+        _assert_compact_tool_args(msg1.tool_calls)
@@
             msg2 = response2.choices[0].message
+            _assert_compact_tool_args(msg2.tool_calls)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e_test/chat_completions/test_openai_server.py` around lines 638 - 779, Add
an assertion that the tool call arguments are canonical compact JSON (no
pretty-print newlines/extra spaces) for the core repro: for each tool call in
tool_call_msg.tool_calls (referencing tool_call_msg and tc.function.arguments),
parse tc.function.arguments as JSON to ensure it's valid and assert the original
string contains no newline characters or repeated indentation (e.g., no '\n' or
two consecutive spaces) so pretty-printed argument regressions fail the test;
place this check after the loop that appends the minimal tool result messages
and before creating response3.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e_test/chat_completions/test_openai_server.py`:
- Around line 180-183: The _ENGLISH_SELF_ANALYSIS regular expression is too
broad and will false-positive on benign apologies like "Sorry, we apologize" or
"Sorry the delay" — tighten it by narrowing or removing the generic apology
branch; for example update the pattern in _ENGLISH_SELF_ANALYSIS to remove or
constrain the "Sorry,? (?:we|the)" fragment (e.g., only match "Sorry, we
(must|should|need to|have to|are going to)" or drop the "the" alternative) so it
only flags explicit self-analysis phrases while keeping the rest of the rule/
guideline and assistant anchors intact.

---

Duplicate comments:
In `@e2e_test/chat_completions/test_openai_server.py`:
- Around line 638-779: Add an assertion that the tool call arguments are
canonical compact JSON (no pretty-print newlines/extra spaces) for the core
repro: for each tool call in tool_call_msg.tool_calls (referencing tool_call_msg
and tc.function.arguments), parse tc.function.arguments as JSON to ensure it's
valid and assert the original string contains no newline characters or repeated
indentation (e.g., no '\n' or two consecutive spaces) so pretty-printed argument
regressions fail the test; place this check after the loop that appends the
minimal tool result messages and before creating response3.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 9548c180-9824-41f7-8cc6-319b8951a9a4

📥 Commits

Reviewing files that changed from the base of the PR and between 1b29a51 and fc8f497.

📒 Files selected for processing (1)
  • e2e_test/chat_completions/test_openai_server.py

Comment thread e2e_test/chat_completions/test_openai_server.py
Adds an integration test in TestChatCompletionGptOss (inherited by
TestChatCompletionGptOss120B) that drives a multi-turn tool-call flow
through the gateway and asserts the post-tool choices[].message.content
is non-empty and contains no:

- Runs of invisible characters
- Long whitespace runs
- Ellipsis loops
- Leaked English self-analysis markers

These are the integrity properties the gpt-oss Harmony round-trip fix
in PR #1547 is meant to guarantee. On failure the assertion reports
the matched marker for triage.

Signed-off-by: Chang Su <8605658+CatherineSue@users.noreply.github.com>
@CatherineSue
CatherineSue force-pushed the test/gpt-oss-harmony-pretty-args-repro branch from fc8f497 to d7fce2f Compare May 26, 2026 08:30
@CatherineSue CatherineSue changed the title test(gpt-oss): repro for harmony post-tool malformed text test(gpt-oss): post-tool response integrity for harmony fix May 26, 2026
# runs of invisible characters, ellipsis loops, long whitespace runs, and
# leaked English self-analysis. Tuned to flag runs (3+) of invisibles so
# isolated occurrences in normal text do not produce false positives.
_INVISIBLE_RUN = re.compile("[​‌‍⁠   ]{3,}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Nit: This character class contains 8 literal invisible Unicode codepoints (U+200B, U+200C, U+200D, U+2060, U+FEFF, U+00A0, U+2007, U+202F). They render identically in editors, diffs, and code review UIs — you can't tell what's in the class or whether someone accidentally added/removed one.

Using \u escapes makes the pattern auditable and diff-friendly:

Suggested change
_INVISIBLE_RUN = re.compile("[​‌‍⁠   ]{3,}")
_INVISIBLE_RUN = re.compile("[⁠   ]{3,}")

Comment on lines +722 to +724
final_content = response3.choices[0].message.content or ""

assert final_content.strip(), (

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Nit: If the model responds to the tool results with another tool call instead of text, content will be None/"" and this assertion fires with "must not be empty" — which doesn't explain the actual failure mode. Adding a guard for unexpected tool calls would make CI failures much easier to triage:

Suggested change
final_content = response3.choices[0].message.content or ""
assert final_content.strip(), (
final_content = response3.choices[0].message.content or ""
assert not response3.choices[0].message.tool_calls, (
"expected a text response after tool results, but the model "
"issued another tool call"
)
assert final_content.strip(), (

@zhyncs
zhyncs deleted the test/gpt-oss-harmony-pretty-args-repro branch May 26, 2026 20:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tests Test changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant