Skip to content
Open
31 changes: 29 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -245,9 +245,36 @@ jobs:
release_json="$(gh release view "$GITHUB_REF_NAME" --repo "$GITHUB_REPOSITORY" --json tagName,name,body,url)"
release_url="$(jq -r '.url' <<< "$release_json")"

# GoReleaser changelog lines look like:
# * 1e4ecfb8f194af0e843f64d7dc694e3a01bef1dc feat(scope): msg (#1191)
# In the embed the verbatim SHA is noise and bare (#NNNN) is not
# clickable. Strip the leading commit SHA entirely and link #NNNN
# references to the official repository.
body="$(jq -r '.body // ""' <<< "$release_json" \
| sed -E -e 's/^(\s*[*-]\s+)[0-9a-f]{7,40}\s+/\1/' \
-e 's/\(#([0-9]+)\)/([#\1](https:\/\/github.com\/ENTERPILOT\/GoModel\/pull\/\1))/g')"

# Discord embed descriptions cap at 4096 characters — jq counts
# characters, not bytes, so multibyte notes stay within the limit.
body="$(jq -r '(.body // "") | if length > 4000 then .[0:3997] + "..." else . end' <<< "$release_json")"
# characters, not bytes, so multibyte release notes stay within the
# limit. Oversized bodies are cut at the last complete changelog
# line and get a link to the full release notes instead of a
# mid-line ellipsis that could slice a Markdown link in half.
body="$(jq -nr --arg body "$body" --arg url "$release_url" '
$body
| if length <= 4000 then .
else "([Truncated](\($url)))" as $suffix
| (4000 - 1 - ($suffix | length)) as $budget
| (split("\n")) as $lines
| (reduce $lines[] as $line ({out: "", done: false};
if .done then .
else (.out | if . == "" then 0 else length + 1 end) as $cur
| if $cur + ($line | length) <= $budget
then {out: (if $cur == 0 then $line else .out + "\n" + $line end), done: false}
else {out: .out, done: true}
end
end)) as $r
| if $r.out == "" then $suffix else $r.out + "\n" + $suffix end
end')"

# Discord embed titles cap at 256 characters — a release with a
# long name must not lose the whole announce to an HTTP 400.
Expand Down
31 changes: 26 additions & 5 deletions docs/providers/chatgpt.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
title: "ChatGPT subscription"
description: "Route Responses API traffic through a ChatGPT subscription instead of an OpenAI Platform API key."
description: "Route Responses API traffic — and chat completions translated onto it — through a ChatGPT subscription instead of an OpenAI Platform API key."
icon: "message-circle"
keywords: ["ChatGPT", "Codex", "subscription", "OAuth", "Responses API", "provider setup"]
---
Expand Down Expand Up @@ -62,11 +62,27 @@ is not supported when using Codex with a ChatGPT account`.
Codex sessions authenticated with an OpenAI API key, through the `openai`
provider.

## Responses API only
## Supported surfaces

The Codex backend serves `/responses` and nothing else, so
`/v1/chat/completions` and `/v1/embeddings` answer `501` for `chatgpt` models.
Use `/v1/responses`, which is what Codex sends anyway.
The Codex backend serves `/responses` and nothing else. GoModel translates
`/v1/chat/completions` onto it — request, response, and streaming — so chat
clients work too. `/v1/embeddings` still answers `501`: the backend has no
embeddings endpoint.

Chat requests are converted to Responses requests, executed upstream, and
converted back to chat completions. The returned `chatcmpl-` IDs are labels,
not resource handles: GoModel pins `store: false` and drops
`previous_response_id` (the backend allows neither), so no chaining is
possible and chat clients resend full history as usual.

Chat parameters with no Responses equivalent are rejected with a `400` that
names the field, before any upstream call: `n` other than 1, `logit_bias`,
`stop`, `seed`, `frequency_penalty`, `presence_penalty`, `logprobs`,
`top_logprobs`, `modalities`, `audio`, `web_search_options`, and
the deprecated `functions` / `function_call` pair. Zero spellings of
`logprobs`, `top_logprobs`, and the penalties (`false` / `0`) are tolerated:
they change nothing. `prediction` is dropped instead: it is a speed hint and
never changes the answer.

The backend also validates against a strict parameter allowlist. GoModel
adapts requests rather than failing them, so callers keep using the standard
Expand All @@ -80,6 +96,11 @@ Responses API:
| `temperature`, `top_p`, `max_output_tokens`, `previous_response_id`, `truncation`, `metadata`, `user`, `service_tier`, `top_logprobs` | Dropped — unsupported upstream |
| `prompt_cache_retention`, `prompt_cache_options` | Dropped — the Codex CLI never sends them, so backend support is unverified |

Translated chat requests go through the same allowlist: `temperature`,
`top_p`, and `max_tokens` / `max_completion_tokens` map to valid Responses
fields and are then silently dropped upstream, exactly as for native Responses
requests.

Because the backend streams only, a non-streaming `POST /v1/responses` is
served by streaming upstream and returning the final response object. Clients
see a normal non-streaming response. See
Expand Down
7 changes: 4 additions & 3 deletions docs/providers/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ support, not every individual model capability exposed by an upstream provider.
| Provider | Credential | Example Model | Chat | `/responses` | Embed | Files | Batches | Passthru | Guide |
| -------- | ---------- | ------------- | :--: | :----------: | :---: | :---: | :-----: | :------: | ----- |
| OpenAI | `OPENAI_API_KEY` | `gpt-5.5` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | — |
| ChatGPT subscription | `CHATGPT_API_KEY` (Codex sign-in token) | `gpt-5.6-sol` | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | [ChatGPT subscription](/providers/chatgpt) |
| ChatGPT subscription | `CHATGPT_API_KEY` (Codex sign-in token) | `gpt-5.6-sol` | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | [ChatGPT subscription](/providers/chatgpt) |
| Anthropic | `ANTHROPIC_API_KEY` | `claude-sonnet-4-20250514` | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | [Anthropic](/providers/anthropic) |
| Cohere | `COHERE_API_KEY` | `command-a-plus-05-2026` | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | [Cohere](/providers/cohere) |
| Google Gemini | `GEMINI_API_KEY` | `gemini-3.7-flash` | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | [Google Gemini](/providers/gemini) |
Expand Down Expand Up @@ -177,8 +177,9 @@ support, not every individual model capability exposed by an upstream provider.
reject requests that omit it (override with
`OPENCODE_GO_DEFAULT_REASONING_EFFORT`). Set `OPENCODE_GO_API_KEY`; the base
URL defaults to `https://opencode.ai/zen/go/v1`.
- **ChatGPT subscription** — serves `/v1/responses` only, billed against the
ChatGPT plan's quota rather than API credit. The upstream accepts a strict
- **ChatGPT subscription** — serves `/v1/responses` and `/v1/chat/completions`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION (style) — already updated by CodeRabbit's outside-diff comment. Re-confirm the merge keeps this version.

(translated onto Responses), billed against the ChatGPT plan's quota rather
than API credit. The upstream accepts a strict
parameter allowlist and streams only; GoModel adapts requests and collapses
the stream for non-streaming callers. Set `CHATGPT_API_KEY` to the access
token from `codex login`. Reported cost is not real spend: these model IDs
Expand Down
Loading
Loading