Skip to content

[WRONG BRANCH] feat: add GitHub Copilot App support via OpenAI-compatible chat completions - #278

Closed
HaydernCenterpoint wants to merge 1 commit into
lidge-jun:mainfrom
HaydernCenterpoint:feat/github-copilot-app-support
Closed

[WRONG BRANCH] feat: add GitHub Copilot App support via OpenAI-compatible chat completions#278
HaydernCenterpoint wants to merge 1 commit into
lidge-jun:mainfrom
HaydernCenterpoint:feat/github-copilot-app-support

Conversation

@HaydernCenterpoint

Copy link
Copy Markdown
Contributor

Summary

  • Add OpenAI-compatible POST /v1/chat/completions so GitHub Copilot App can use OpenCodex as a custom Model provider.
  • Translate Chat Completions requests into the existing Responses path and bridge stream/non-stream replies back to Chat Completions shape.
  • Document setup (docs/github-copilot-app.md) and cover discovery/chat endpoint behavior with tests.

This is a client integration for GitHub Copilot App (BYOK / Model providers). It is separate from the existing experimental upstream github-copilot provider.

How to use

  1. Run OpenCodex (ocx start)
  2. In GitHub Copilot App → Settings → Model providers → Add provider
  3. Base URL: http://127.0.0.1:10100/v1
  4. API key: empty on loopback
  5. Sync models / add model by id (provider/model)

Test plan

  • bun test tests/chat-completions-endpoint.test.ts
  • bun test tests/server-auth.test.ts -t "root fallback"
  • Manual: GET /v1/models returns OpenAI list shape
  • Manual: POST /v1/chat/completions stream + non-stream succeed for configured providers
  • Manual: GitHub Copilot App can chat after pointing at /v1 and selecting an authenticated model

Expose POST /v1/chat/completions so GitHub Copilot App and other OpenAI-compatible clients can use OpenCodex as a Model provider via GET /v1/models + chat completions, reusing the existing Responses path.
Copilot AI review requested due to automatic review settings July 22, 2026 09:59

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown

⚠️ Wrong target branch

This pull request currently targets main, but pull requests must target dev.

Its title has been prefixed with [WRONG BRANCH].

@HaydernCenterpoint Please retarget this PR to the dev branch. All contributions go to dev first, and main receives only release promotions. See our Contributing guide for details. Thanks! 🙏

This pull request is being kept as a draft automatically. Once the target branch is corrected, it will be marked ready for review again.

@github-actions github-actions Bot changed the title feat: add GitHub Copilot App support via OpenAI-compatible chat completions [WRONG BRANCH] feat: add GitHub Copilot App support via OpenAI-compatible chat completions Jul 22, 2026
@HaydernCenterpoint
HaydernCenterpoint deleted the feat/github-copilot-app-support branch July 22, 2026 10:01

@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: da10542afd

ℹ️ 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".

logCtx.providerAdapter = route.provider.adapter;
logCtx.requestedModel = requestedModel;
logCtx.provider = route.providerName;
if (route.provider.adapter === "openai-responses") {

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 Preserve chat limits for API-key Responses routes

When a client targets an openai-responses provider with API-key auth (for example the built-in openai-apikey provider), this condition still runs and deletes max_output_tokens, temperature, top_p, stop, and user even though the comment only applies to the ChatGPT forward backend. As a result, a Chat Completions request with max_tokens or stop against openai-apikey/... is silently replayed without those controls, which can change behavior and cost. Gate this cleanup on the forward ChatGPT backend instead of every openai-responses adapter.

Useful? React with 👍 / 👎.

Comment thread src/chat/outbound.ts
Comment on lines +194 to +198
let toolIndex = nextToolIndex > 0 ? nextToolIndex - 1 : 0;
const itemId = typeof data.item_id === "string" ? data.item_id : undefined;
if (itemId) {
// Some bridges put call_id on the item, not item_id; keep last index.
}

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 Map tool argument deltas by item id

When the Responses stream has more than one function call open, response.function_call_arguments.delta carries item_id identifying which call the bytes belong to, but this code ignores it and always emits the delta for the most recently added tool. If two tool calls are added before or interleaved while arguments stream, the Chat Completions client receives arguments on the wrong tool_calls[index], so subsequent tool execution uses corrupted inputs. Store the item.id→index mapping from response.output_item.added and use it here.

Useful? React with 👍 / 👎.

Comment thread src/chat/outbound.ts
Comment on lines +142 to +145
const frame = chunkBase(id, model, created);
frame.choices = [{ index: 0, delta: { content: `\n\n[error] ${message}` }, finish_reason: "stop" }];
emit(frame);
emit("[DONE]");

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 Surface failed streams as errors

When an adapter or native relay emits a response.failed terminal event after an HTTP 200 SSE has started, this path turns the failure into ordinary assistant content and then sends finish_reason: "stop" plus [DONE]. Non-streaming Chat Completions requests folded through collectChatCompletion therefore return a 200 chat.completion containing [error] ..., and streaming clients also see a successful assistant turn instead of an error. Emit an OpenAI-style error frame/state here so upstream failures are not treated as model output.

Useful? React with 👍 / 👎.

Comment thread src/chat/inbound.ts
if (typeof raw.user === "string") body.user = raw.user;
if (typeof raw.parallel_tool_calls === "boolean") body.parallel_tool_calls = raw.parallel_tool_calls;
if (typeof raw.prompt_cache_key === "string") body.prompt_cache_key = raw.prompt_cache_key;
if (raw.metadata !== undefined) body.metadata = raw.metadata;

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 Strip metadata before ChatGPT forward replay

When a Chat Completions client includes metadata and routes to the default ChatGPT-backed openai provider, this copies the field into the internal Responses body; the ChatGPT forward backend uses a strict Responses allowlist and rejects metadata, so otherwise valid chats fail with an upstream 400. The native-route cleanup strips other unsupported forwarded fields, but metadata needs the same treatment for the forward provider while remaining available for API-key Responses routes.

Useful? React with 👍 / 👎.

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