Skip to content

fix(channel-test): use global Responses API policy instead of model name heuristic - #2963

Closed
0-don wants to merge 1 commit into
QuantumNous:mainfrom
0-don:fix/channel-test-responses-policy
Closed

fix(channel-test): use global Responses API policy instead of model name heuristic#2963
0-don wants to merge 1 commit into
QuantumNous:mainfrom
0-don:fix/channel-test-responses-policy

Conversation

@0-don

@0-don 0-don commented Feb 18, 2026

Copy link
Copy Markdown
Contributor
  • Channel test: use ShouldChatCompletionsUseResponsesGlobal() instead of hardcoded codex name check
  • Playground: inject user message into custom body before sending

Summary by CodeRabbit

  • Bug Fixes

    • Custom request payloads now properly include the latest user message when using custom request bodies.
  • Improvements

    • Request routing now uses policy-driven selection for API endpoint determination instead of heuristic checks.

…ame heuristic

The channel test hardcoded that any model containing "codex" in the name
should be tested via /v1/responses (Responses API). This caused test
failures for channels where the upstream doesn't support the Responses
API for codex models, even though the models work fine via
/v1/chat/completions.

Replace the model-name-based heuristic with
ShouldChatCompletionsUseResponsesGlobal() in both the request path
selection and the test request body construction, matching what the
production relay pipeline uses.

Also fix playground custom body mode not injecting the user's new
message into the custom payload's messages array before sending.
@coderabbitai

coderabbitai Bot commented Feb 18, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

This PR modifies two files: it replaces a heuristic-based decision (checking for "codex" in model names) with a policy-driven approach in the channel test logic, and adds message injection into custom request payloads in the Playground component.

Changes

Cohort / File(s) Summary
Policy-driven request routing
controller/channel-test.go
Replaced heuristic check for codex-like models with a call to service.ShouldChatCompletionsUseResponsesGlobal() to determine API path selection; added nil-channel guard to prevent potential nil dereference.
Playground payload injection
web/src/pages/Playground/index.jsx
When using custom request body, the code now injects the new user message into customPayload.messages if it exists as an array, ensuring the payload is updated with the latest user content before sending.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Suggested reviewers

  • creamlike1024

Poem

🐰 A heuristic hops away, policy bounds in,
Custom payloads dance with messages within,
Routes shift and flows align with care,
Two little changes, but oh so rare! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately reflects the main change: replacing a model name heuristic with a global Responses API policy check in channel-test.go, which is the primary focus of the changeset.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
web/src/pages/Playground/index.jsx (1)

249-270: ⚠️ Potential issue | 🟠 Major

Side-effect mutation inside a React state updater will cause double send and duplicate messages in Strict Mode

customPayload is mutated (customPayload.messages = [...]) and sendRequest is invoked all inside the setMessage updater callback. React 18 Strict Mode intentionally invokes state updaters twice to surface impure updaters. On the second invocation:

  1. customPayload.messages already contains the injected user message → the spread produces a doubled user message.
  2. sendRequest fires a second API request with the duplicated payload.

Move the injection and the sendRequest call outside the updater, keeping only the pure state derivation inside:

🐛 Proposed fix – hoist side-effects out of the updater
     if (customRequestMode && customRequestBody) {
       try {
         const customPayload = JSON.parse(customRequestBody);
 
-        setMessage((prevMessage) => {
-          const newMessages = [...prevMessage, userMessage, loadingMessage];
-
-          // Inject the user's new message into the custom payload's messages array
-          if (Array.isArray(customPayload.messages)) {
-            customPayload.messages = [
-              ...customPayload.messages,
-              { role: MESSAGE_ROLES.USER, content },
-            ];
-          }
-
-          // 发送自定义请求体
-          sendRequest(customPayload, customPayload.stream !== false);
-
-          // 发送消息后保存,传入新消息列表
-          setTimeout(() => saveMessagesImmediately(newMessages), 0);
-
-          return newMessages;
-        });
+        // Inject the user's new message into the custom payload's messages array
+        if (Array.isArray(customPayload.messages)) {
+          customPayload.messages = [
+            ...customPayload.messages,
+            { role: MESSAGE_ROLES.USER, content },
+          ];
+        }
+
+        // 发送自定义请求体
+        sendRequest(customPayload, customPayload.stream !== false);
+
+        setMessage((prevMessage) => {
+          const newMessages = [...prevMessage, userMessage, loadingMessage];
+          // 发送消息后保存,传入新消息列表
+          setTimeout(() => saveMessagesImmediately(newMessages), 0);
+          return newMessages;
+        });
         return;
       } catch (error) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/pages/Playground/index.jsx` around lines 249 - 270, The updater
passed to setMessage is performing side effects and mutating customPayload which
causes duplicate messages/requests in React Strict Mode; to fix, parse
customRequestBody into customPayload and build a newPayload (do not mutate
customPayload) by appending { role: MESSAGE_ROLES.USER, content } to its
messages array, call sendRequest(newPayload, newPayload.stream !== false) and
schedule saveMessagesImmediately(newMessages) outside the setMessage updater,
and keep the setMessage callback pure by only computing and returning the
newMessages array (using userMessage and loadingMessage) without touching
customPayload or calling sendRequest; ensure you reference customRequestBody,
customPayload/newPayload, setMessage, sendRequest, saveMessagesImmediately,
userMessage, loadingMessage and MESSAGE_ROLES.USER when applying the change.
controller/channel-test.go (1)

688-695: ⚠️ Potential issue | 🟡 Minor

Consolidate policy evaluation to eliminate TOCTOU window

ShouldChatCompletionsUseResponsesGlobal reads from mutable global state (model_setting.GetGlobalSettings().ChatCompletionsToResponsesPolicy). The two independent calls at line 125 (testChannel) and line 689 (buildTestRequest) create a TOCTOU window: if the config toggles between them, requestPath and the request body type will disagree. The RelayMode switch at lines 319–328 would then fail the type assertion.

Pass the policy decision as a parameter to buildTestRequest:

🔒 Proposed fix – single policy evaluation
-func buildTestRequest(model string, endpointType string, channel *model.Channel, isStream bool) dto.Request {
+func buildTestRequest(model string, endpointType string, channel *model.Channel, isStream bool, useResponsesAPI bool) dto.Request {
-	// Use Responses API if the global policy says this channel+model should use it
-	if channel != nil && service.ShouldChatCompletionsUseResponsesGlobal(channel.Id, channel.Type, model) {
+	// Use Responses API if the caller determined the policy already
+	if useResponsesAPI {

In testChannel, evaluate once and pass through:

+	useResponsesAPI := service.ShouldChatCompletionsUseResponsesGlobal(channel.Id, channel.Type, testModel)
 	// Use Responses API if the global policy says this channel+model should use it
-	if service.ShouldChatCompletionsUseResponsesGlobal(channel.Id, channel.Type, testModel) {
+	if useResponsesAPI {
 		requestPath = "/v1/responses"
 	}
-	request := buildTestRequest(testModel, endpointType, channel, isStream)
+	request := buildTestRequest(testModel, endpointType, channel, isStream, useResponsesAPI)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@controller/channel-test.go` around lines 688 - 695, The TOCTOU happens
because ShouldChatCompletionsUseResponsesGlobal is called twice (in testChannel
and inside buildTestRequest), causing requestPath and request body types to
possibly disagree; fix this by evaluating the policy once in testChannel and
passing the boolean decision into buildTestRequest (add a parameter like
useResponses bool to buildTestRequest and update all callers), remove the
internal call to ShouldChatCompletionsUseResponsesGlobal from buildTestRequest,
and make buildTestRequest use the passed useResponses to choose whether to
return an OpenAIResponsesRequest or ChatCompletions request and to set
requestPath so the RelayMode switch (which asserts the request type) always sees
a consistent body/type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@controller/channel-test.go`:
- Around line 688-695: The TOCTOU happens because
ShouldChatCompletionsUseResponsesGlobal is called twice (in testChannel and
inside buildTestRequest), causing requestPath and request body types to possibly
disagree; fix this by evaluating the policy once in testChannel and passing the
boolean decision into buildTestRequest (add a parameter like useResponses bool
to buildTestRequest and update all callers), remove the internal call to
ShouldChatCompletionsUseResponsesGlobal from buildTestRequest, and make
buildTestRequest use the passed useResponses to choose whether to return an
OpenAIResponsesRequest or ChatCompletions request and to set requestPath so the
RelayMode switch (which asserts the request type) always sees a consistent
body/type.

In `@web/src/pages/Playground/index.jsx`:
- Around line 249-270: The updater passed to setMessage is performing side
effects and mutating customPayload which causes duplicate messages/requests in
React Strict Mode; to fix, parse customRequestBody into customPayload and build
a newPayload (do not mutate customPayload) by appending { role:
MESSAGE_ROLES.USER, content } to its messages array, call
sendRequest(newPayload, newPayload.stream !== false) and schedule
saveMessagesImmediately(newMessages) outside the setMessage updater, and keep
the setMessage callback pure by only computing and returning the newMessages
array (using userMessage and loadingMessage) without touching customPayload or
calling sendRequest; ensure you reference customRequestBody,
customPayload/newPayload, setMessage, sendRequest, saveMessagesImmediately,
userMessage, loadingMessage and MESSAGE_ROLES.USER when applying the change.

@seefs001

Copy link
Copy Markdown
Collaborator

这个你用来替换的方法不是这种用途的,目前的端点选择预测就是单纯的只按照模型名的

@seefs001 seefs001 closed this Feb 18, 2026
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.

2 participants