fix(api): isolate request sampling; land tools-path honesty on Completions max_tool_calls - #588
fix(api): isolate request sampling; land tools-path honesty on Completions max_tool_calls#588cursor[bot] wants to merge 12 commits into
Conversation
…closed otherwise Chat history: message-level audio and legacy function_call are null/empty omit no-ops; non-empty fail closed with named errors (including tools passthrough). Tip substrate from #577 assistant refusal/annotations honesty. Local full unit: 940 passed.
…ed otherwise OpenAI fine-tune style message weight is not applied on this gateway. Accept null/0/1 as honest no-ops; reject other types and values with invalid_message_weight. Tip substrate from #578. Local full unit: 943 passed.
…ion role Reject unsupported message keys with named unknown_message_fields (not silent strip or tools-passthrough smuggle). Reject legacy function role with invalid_message_role migration to tool. Tip substrate from #579. Local full unit: 947 passed.
OpenAI partial-assistant prefix flag is not applied on this gateway. null/false are honest no-ops; true and non-booleans fail closed with invalid_message_prefix. Tip substrate from #580. Local full unit: 950 passed.
…therwise Named invalid_max_tool_calls on /v1/chat/completions instead of opaque unknown_fields. Aligns with Responses max_tool_calls honesty; gateway has no multi-step tool loop.
…losed otherwise Legacy /v1/completions treated max_tool_calls as unknown_fields. Accept the key for named invalid_max_tool_calls (null/empty/whitespace omit-equivalent), matching chat/Responses honesty so SDKs get a clear migration path.
Validate weight/prefix/refusal/annotations before the tools early-return so SDK tool-calling bodies cannot smuggle unsupported message fields. Strip omit-equivalent max_tool_calls before provider passthrough and name-reject the field on /v1/completions. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Reject non-OpenAI keys on assistant tool_calls objects with named unknown_tool_call_fields / unknown_tool_call_function_fields. Accept optional non-negative index (null omit) for stream-assembled histories. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Developer role, empty user/system content, multimodal part shape, and participant name were only checked in _validate_messages, which the tools/response_format early-return skips. SDK tool-calling bodies could therefore proxy unsupported values. Run the same fail-closed checks before passthrough and cover the realistic tools+field HTTP cases. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
…calls Bring #586 fail-closed message honesty (weight/prefix/refusal/annotations, role/content/name, tool_calls keys) and omit-pop for max_tool_calls onto this branch so the Completions named reject is not shipped with a tools-path hole. Keep Completions tests for max_tool_calls=1/false/0/true. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
ThreadingHTTPServer shares one ModelClient. Mutating default_temperature (and siblings) for the duration of a request raced concurrent Completions and chat calls. apply_request_sampling stores knobs on threading.local so each request keeps its own temperature, top_p, penalties, and max tokens. Concurrent mock chats assert isolation; process defaults stay 0.2. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
APA 7th vendor documentation for per-request temperature/top_p/penalties and max_tokens, grounding why ModelClient must not mutate process defaults. Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
There was a problem hiding this comment.
Do not merge #588 at a72b6b0 as the stack repair
GitHub rejects REQUEST_CHANGES on this automation's own PR, so this is a COMMENT with the same merge block. The unique work on this head is real. It is not a complete fail-closed tools path. Inline comments were omitted because GitHub rejected the review payload: this PR diff exceeds the 20k-line review-diff cap (143 files).
Verified on this tip
- Thread-local sampling —
ModelClient.apply_request_samplingbinds temperature / top_p / penalties / max tokens onthreading.local. Processdefault_temperatureis no longer mutated.tests/test_model_client_request_sampling.pypassed. - Completions
max_tool_calls— null / empty / whitespace omit-and-pop;1/false/0/true→invalid_max_tool_calls. Completions honesty tests passed. - #586 message honesty is on this stack — developer role, empty user content,
name, weight / prefix / refusal / annotations, andtool_callskeys fail closed before passthrough. Those HTTP tests passed.
Live HTTP still 200 (proxied) when tools is present
contextual_orchestrator/server.py around the PASSTHROUGH_TRIGGER_KEYS early-return (HEAD ~3610–3629) still skips _validate_messages, _validate_completions_model, stream, stream_options, and sampling-range checks.
| Case | Status on a72b6b0 |
|---|---|
empty / missing / null messages |
200 |
stream: true |
200 JSON (not SSE) |
omitted model |
200 |
temperature: 99 |
200 |
stream_options.include_usage: true |
200 |
Successors already open. Do not open another honesty-stack PR from this review:
- empty messages → #589 / #594
- stream / model / sampling range → #591 / #593
- unknown
stream_optionsnull keys → #592
Completions tools + max_tool_calls
Non-empty tools + max_tool_calls: 1 returns invalid_tools because _validate_completions_tools_surface runs first (server.py ~3407 then 3408 / 3415). Completions has no tools passthrough, so this is not a leak. The title still overclaims “tools-path honesty on Completions max_tool_calls”: tests/test_completions_max_tool_calls_http_honesty.py never sends tools, and _validate_max_tool_calls is invoked twice. Drop the duplicate. If the named Completions reject must win when both fields are present, call it before the tools surface check and add that HTTP case.
Next action
Supersede this 143-file stack with the linear successor that hoists empty-messages and stream / model / temperature before the passthrough early-return (#594 + #591, or one branch that contains both). Do not merge #582–#588 at their current heads. Independent non-author APPROVE is still required on the successor. Product gates (Full unit + Semgrep) were still queued at review time.
CodeRabbit CLI 0.7.3 is present in this environment; agent login timed out on the browser callback. This review is from source read + live loopback HTTP on a72b6b0.
Sent by Cursor Automation: Fix Issues
There was a problem hiding this comment.
Pull request overview
OpenCode cannot approve yet because required coverage evidence did not pass.
Review outcome
1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence
-
Problem: The required coverage-evidence job result was
failure, so OpenCode cannot establish approval sufficiency for this head. -
Root cause: Automated approval is only valid when the same-head coverage-evidence job proves supported repository test suites passed and configured docstring gates passed or were advisory, or reports not applicable because no supported source files or package manifests exist. Missing, failed, skipped, unavailable, or unsupported-tooling test evidence is a blocker.
-
Fix: Install or configure the repository test/docstring evidence tooling when source files or package manifests exist, rerun the current-head coverage-evidence job, and approve only after it reports
successwith required evidence or explicit no-source not-applicable evidence. -
Regression test: Keep the approval branch checking
needs.coverage-evidence.result == successbefore posting APPROVE, and publish REQUEST_CHANGES when coverage-evidence blocker states such as cancelled, skipped, failed, unsupported-tooling, or below-100 evidence are present. -
Result: REQUEST_CHANGES
-
Reason: coverage-evidence result was
failure, so required test/docstring evidence was not proven for current heada72b6b067eb472f9a8d872abbca00721c544fc92. -
Head SHA:
a72b6b067eb472f9a8d872abbca00721c544fc92 -
Workflow run: 32076714849
-
Workflow attempt: 1
Coverage evidence
Coverage evidence job did not run or did not publish coverage evidence.
Changed-File Evidence Map
flowchart LR
Evidence["OpenCode evidence"] --> Review["Current PR review path"]
Review --> Verify["Required checks"]
OpenCode Review Overview
Pull request overviewOpenCode cannot approve yet because required coverage evidence did not pass. Review outcome1. HIGH .github/workflows/opencode-review.yml:1 - Coverage evidence did not prove required test/docstring evidence
Coverage evidenceCoverage evidence job did not run or did not publish coverage evidence. Changed-File Evidence Mapflowchart LR
Evidence["OpenCode evidence"] --> Review["Current PR review path"]
Review --> Verify["Required checks"]
|


Summary
max_tool_callsnamed reject: weight / prefix / refusal / annotations / role / content / name / tool_calls keys fail closed before the passthrough early-return.max_tool_calls(null/ empty / whitespace) is popped before provider proxy.ModelClientthread-local state (apply_request_sampling). Concurrent requests onThreadingHTTPServerno longer race shareddefault_temperature.max_tool_calls=1/false/0/trueas named 400s. Concurrent mock chats assert temperature isolation.CHANGELOG.mdand cite OpenAI Chat Completions (APA 7th) for per-request sampling fields.Do not merge #583 at
3a0d35ee— tools +weight: 0.5/prefix: true/ nonempty refusal or annotations /role: developer/ empty user content returned 200. This branch is the mergeable repair of #583 / #585 / #586 plus the sampling race that those heads left open.Test plan
python3 tests/test_model_client_request_sampling.pypython3 tests/test_completions_max_tool_calls_http_honesty.pypython3 tests/test_message_weight_http_honesty.pypython3 tests/test_message_prefix_http_honesty.pypython3 tests/test_message_role_content_name_tools_passthrough_http_honesty.pypython3 tests/test_chat_max_tool_calls_http_honesty.pypython3 tests/test_assistant_refusal_annotations_http_honesty.pypython3 tests/test_tool_calls_entry_keys_http_honesty.pypython3 tests/test_completions_sampling_knobs_http_honesty.pypython3 tests/test_chat_temperature_top_p_http_honesty.pypython3 tests/test_true_streaming.pypython3 tests/test_paper_contracts.pytest_conventions.pytest_api_contract.pyProduct gates only: Full unit + Semgrep (Strix ignored). Independent non-author APPROVE still required.
References