| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Copilot's model catalog now marks some models with supported_endpoints that don't include /chat/completions - only /responses. Calling /chat/completions for those models fails with "not accessible via the /chat/completions endpoint", regardless of payload shape. createChatCompletions() now checks the selected model's supported_endpoints and, when /chat/completions isn't listed but /responses is, transparently translates the request/response through the Responses API instead (lib/responses-translation.ts). Both existing callers - the /chat/completions route and the Anthropic-compatible /v1/messages route - already go through this one function, so they pick up the fix with no changes of their own, for both streaming and non-streaming, text and tool calls. Verified against the live Copilot API with gpt-5.6-luna: chat completions and messages, streaming and non-streaming, plain text and tool calls, plus a regression check that an unaffected model (claude-haiku-4.5) still works unchanged.
WalkthroughThe pull request adds a Copilot Responses API client and its TypeScript contracts. It translates Chat Completions requests, responses, and stream events into the corresponding Responses API formats. Model metadata now identifies supported upstream endpoints. createChatCompletions routes Responses-only models through the new client and translates the result back to Chat Completions formats. Other requests retain the existing route. Priority: ➖ Normal Merge Risk: 🟡 Moderate · up to e6e41 Responses-only models can return incorrect results, conceal upstream failures, or reject image requests. These compatibility defects should be fixed before merge. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
Warning Some tools did not complete. Review the errors below. 🔧 ESLintsrc/lib/responses-translation.ts ESLint skipped: missing config or dependency (missing-dependency). The ESLint configuration references a package that is not available in the sandbox. src/services/copilot/create-chat-completions.tsESLint skipped: the matched ESLint configuration already failed (missing-dependency). src/services/copilot/create-responses.tsESLint skipped: the matched ESLint configuration already failed (missing-dependency).
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. ❤️ ShareComment @coderabbitai help to get the list of available commands. |
Sorry, something went wrong.
There was a problem hiding this comment.
Actionable comments posted: 4
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Advanced
Run ID: 3803bb94-a07b-4bde-9b25-49b152f2d2f6
📥 CommitsReviewing files that changed from the base of the PR and between 0ea08fe and e6e41de.
📒 Files selected for processing (4)Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.
📜 Review details 🔇 Additional comments (2)src/services/copilot/get-models.ts (1)src/services/copilot/create-chat-completions.ts (1)55-61: LGTM!
26-31: LGTM!
Also applies to: 42-55
Sorry, something went wrong.
| input.push(toMessageItem(message)) | ||
| } | ||
|
|
||
| return { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Preserve or reject n.
ChatCompletionsPayload permits n, but this translated payload does not carry it. responsesResultToChatCompletion() always creates one choice. A rerouted request with n: 2 silently returns one choice.
Reject n values other than 1 before routing, or implement compatible fan-out behavior.
Sorry, something went wrong.
| if (item.type === "message") { | ||
| const text = item.content.map((part) => part.text).join("") | ||
| content = (content ?? "") + text | ||
| } else { |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🔎 Supported by static analysis🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- outline ---'
ast-grep outline src/lib/responses-translation.ts
printf '%s\n' '--- target file ---'
cat -n src/lib/responses-translation.ts
printf '%s\n' '--- related response item declarations and usages ---'
rg -n -C 4 'function_call|output_text|Response.*Output|output_item|item\.type|responses-translation' src package.json README.md 2>/dev/null | head -n 300Repository: ericc-ch/copilot-api
Length of output: 25206
🏁 Script executed:
pwd; sed -n '110,170p' src/lib/responses-translation.ts; rg -n -C 3 'function_call|output_text|item.type' src/lib src 2>/dev/null | head -n 200Repository: ericc-ch/copilot-api
Length of output: 12971
🏁 Script executed:
set -eu
cat -n src/lib/responses-translation.ts
printf '\n--- related symbols ---\n'
rg -n -C 4 'function_call|output_text|Response.*Output|output_item|item\.type|responses-translation' src package.json README.md 2>/dev/null | head -n 300Repository: ericc-ch/copilot-api
Length of output: 24412
🏁 Script executed:
set -eu
printf '%s\n' '--- create-responses.ts ---'
cat -n src/services/copilot/create-responses.ts
printf '%s\n' '--- create-chat-completions.ts relevant sections ---'
sed -n '1,180p' src/services/copilot/create-chat-completions.tsRepository: ericc-ch/copilot-api
Length of output: 11350
🏁 Script executed:
set -eu
cat -n src/services/copilot/create-responses.ts
sed -n '1,180p' src/services/copilot/create-chat-completions.tsRepository: ericc-ch/copilot-api
Length of output: 11269
Handle only function_call output items. The non-streaming /responses body is cast without runtime validation, so responsesResultToChatCompletion can receive output types outside ResponseOutputItem. Its else branch converts every non-message item into a tool call. A reasoning item can therefore produce a malformed tool call with missing call_id, name, and arguments, and incorrectly set finish_reason to tool_calls. Check item.type === "function_call" before adding a tool call, then ignore or translate other output types.
Sorry, something went wrong.
|
|
||
| const finishReason = finalFinishReason( | ||
| toolCalls.length > 0, | ||
| result.status === "incomplete", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Do not translate failed Responses as stop.
Both call sites only distinguish incomplete. For the modeled status: "failed" and response.failed cases, finalFinishReason() returns "stop". Non-streaming callers receive a successful completion, and streaming callers receive a normal terminal chunk followed by [DONE].
Propagate failed Responses through the existing error path instead of emitting a successful completion.
Also applies to: 278-278
Sorry, something went wrong.
|
|
||
| const isAgentCall = payload.input.some( | ||
| (item) => | ||
| item.type === "function_call" || item.type === "function_call_output", |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Pass translated image input to copilotHeaders.
When a request is rerouted through /responses, chatPayloadToResponsesPayload converts image_url parts to input_image. createResponses calls copilotHeaders(state) with vision disabled, so Copilot can reject the request with a missing vision-header error.
Suggested fix+ const enableVision = payload.input.some(
+ (item) =>
+ item.type === "message"
+ && item.content.some((part) => part.type === "input_image"),
+ )
+
const headers: Record<string, string> = {
- ...copilotHeaders(state),
+ ...copilotHeaders(state, enableVision),
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Problem
Copilot's model catalog (GET /models) now tags some models with a
supported_endpoints list that doesn't include /chat/completions -
only /responses. This shows up on the GPT reasoning family
(gpt-5.3-codex, gpt-5.5, gpt-5.6-luna, gpt-5.6-sol,
gpt-5.6-terra, gpt-6-astra) and mai-code-1.1-flash. Requesting
one of those through /chat/completions (both the OpenAI-compatible
route and, since it goes through the same createChatCompletions,
the Anthropic-compatible /v1/messages route) fails outright with
not accessible via the /chat/completions endpoint, no matter what
the payload looks like - I checked this directly against
api.githubcopilot.com with a few different models before writing
any code.
Some models (gpt-5.4, gpt-5-mini) list both endpoints and keep
working today; this only affects the endpoint-restricted ones.
Fix
createChatCompletions() now looks up the selected model's
supported_endpoints. When /chat/completions isn't listed but
/responses is, it transparently translates the request into a
Responses API payload, calls /responses, and translates the result
back into the Chat Completions shape everything downstream already
expects (lib/responses-translation.ts, new services/copilot/ create-responses.ts).
Because both existing routes - /chat/completions and the
Anthropic-compatible /v1/messages - already funnel through this one
function, neither route, nor stream-translation.ts/non-stream- translation.ts, needed any changes. The fallback is invisible to
callers: same request in, same response shape out, whether the model
answers via /chat/completions or /responses under the hood.
Covers text and tool calls, both streaming and non-streaming.
Testing
I don't have automated tests for this (the repo has none for the
copilot service calls, and this needs a live, authenticated Copilot
session to exercise meaningfully), so I verified by hand against the
real api.githubcopilot.com with gpt-5.6-luna:
normal Claude Code stream
tool_use output
incremental tool_calls deltas and [DONE] terminator
only model) still works unchanged through /v1/messages
tsc and eslint pass clean on the changed/new files. The 5
pre-existing lint errors on master in unrelated files (lib/ proxy.ts, routes/messages/anthropic-types.ts, routes/messages/ non-stream-translation.ts, start.ts) are untouched by this PR.