feat: Sign in with ChatGPT (use a ChatGPT subscription for OpenAI models)#65
Merged
Conversation
…enAI models Lets users drive OpenAI models with a paid ChatGPT plan (Plus/Pro/Business) instead of an API key, via `/login`. This replicates the OpenAI Codex CLI flow: OAuth 2.0 + PKCE against auth.openai.com, a loopback callback, and inference through the ChatGPT backend's Responses API. - auth/: new package — Codex OAuth constants (single file), OAuthTokens model + ChatGPTTokenManager (lazy + 401 refresh, injected persistence), JWT claim decode, and the browser-loopback login flow (PKCE, port 1455/1457, plan gating). - providers/chatgpt_oauth.py: ChatGPTOAuthProvider speaking the Responses API with a refreshing httpx.Auth, full message/tool/image conversion, and a streaming wrapper matching the existing text/thinking/tool_use_start contract. - Wiring: ModelProvider.OPENAI_CHATGPT, MODEL_SPECS (openai_responses_reasoning effort mode), LLMClient routing + token_manager threading, AgentConfig token field/validation/manager, build_agent_config, settings persistence (additive oauth_tokens, no schema bump), registry labels, and /login + /logout commands. - Settings tab guides users to /login for the OAuth provider; README + .env note. - Tests: token/refresh, PKCE + end-to-end loopback login, Responses conversion + streaming + provider + routing + config validation, plus a gated live test. Note: reuses the first-party Codex OAuth client and ChatGPT backend. This is unofficial and against the spirit of OpenAI's ToS (account-ban risk); the sanctioned "Sign in with ChatGPT" is identity-only and does not grant inference. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Parameterize /login and /logout so they take a provider target, leaving room for additional OAuth integrations later. `/login chatgpt` runs the ChatGPT flow; bare `/login` (or `/login help`) prints usage; an unknown target is rejected. Available targets live in KolegaCodeApp.LOGIN_TARGETS. Updated all user-facing hints, the settings placeholder, config errors, and the README. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fixes "openai couldn't process the request" from the ChatGPT backend and
refreshes the model list to the current Codex picker.
Request shape (verified against openai/codex codex-rs source):
- Stop sending `max_output_tokens` — Codex never sends it and the backend
rejects it. This was the primary cause of the 400.
- Always stream (the /responses backend is SSE-only); generate() now drains a
stream instead of issuing stream:false.
- Send the fields Codex sends: tool_choice="auto", parallel_tool_calls=false,
prompt_cache_key, and reasoning={effort, summary:"auto"}.
- Headers: drop OpenAI-Beta (HTTP path doesn't use it), add a codex_cli_rs
User-Agent, use ChatGPT-Account-ID / session-id casing.
Models: replace gpt-5-codex/gpt-5 with the current Codex slugs gpt-5.5 (default),
gpt-5.4, gpt-5.4-mini, and gpt-5.3-codex-spark, with Responses reasoning-effort
options. Context/output limits mirror the API gpt-5.x specs and are
server-enforced.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A settings.json whose active_model no longer exists in MODEL_SPECS (e.g. an old ChatGPT slug like gpt-5-codex after the model rename) made build_agent_config raise, which set config=None and disabled the chat composer — locking the user out with no way to type /model. _active_provider_model now coerces an unknown saved model to the provider's default (and treats an unknown saved provider as unconfigured), so a stale selection self-heals on the next launch instead of bricking the session. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…mpty output) The agent "said one thing and stopped" on the ChatGPT-subscription provider because the backend behaves differently from the standard Responses API: - response.completed arrives with an EMPTY output[] (the answer streams only as deltas). get_final_message preferred response.output, so it parsed [] and returned an empty assistant message — ending the turn with no content/tool calls. Now the final message is built from the accumulated stream deltas, and response.output is only a fallback when no deltas were seen. Verified live against the real backend (gpt-5.5 now returns text). - The backend hard-requires a non-empty `instructions` field (400 "Instructions are required"); always send one. - Also capture tool calls from response.output_item.done so they survive when the completed output is empty. Adds regression tests for the empty-output, output_item.done, and instructions cases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The two "Field name 'json' ... shadows an attribute in parent 'BaseModel'" warnings printed on every startup come from the firecrawl package itself — firecrawl/v2/types.py defines a model field named `json` that shadows BaseModel.json, which pydantic warns about at class-definition time. It is a bug in firecrawl's own code (not ours, and not fixable here), so we suppress exactly that warning where we import firecrawl. Keeps the eager import (and the module-level Firecrawl symbol the tests patch) intact. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
New Configuration page presenting the ChatGPT-subscription sign-in: eligibility, the /login chatgpt flow, the available Codex models, where credentials live, checking status with doctor, and /logout chatgpt. Adds it to the sidebar, lists /login and /logout in the slash-commands reference, adds the openai_chatgpt provider row to Providers & Models, and trims the README section to present the feature. Docs site builds clean (23 pages). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rovider too These models were only on the openai_chatgpt provider, but they work with a standard OPENAI_API_KEY as well. Add them to the `openai` provider so both providers offer the same line-up (gpt-5.5, gpt-5.4, gpt-5.4-mini, gpt-5.3-codex-spark). Uses the openai provider's Chat-Completions reasoning-effort convention; labels are already shared in the registry so the model picker and Settings UI pick them up automatically. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
gpt-5.4 and gpt-5.4-mini share the API-key `openai` provider's line-up with
gpt-5.5; all three verified working live against Chat Completions with an
OPENAI_API_KEY. gpt-5.3-codex-spark is intentionally excluded — it 404s on the
standard API ("model does not exist or you do not have access") and is a Codex
model only reachable through the ChatGPT-subscription backend (openai_chatgpt),
where it stays.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Lets users drive OpenAI models with a paid ChatGPT plan (Plus/Pro/Business) instead of an API key. Run
/loginin the TUI, complete the browser sign-in, and Kolega Code switches to the OpenAI (ChatGPT subscription) provider (e.g.gpt-5-codex). This replicates the OpenAI Codex CLI flow: OAuth 2.0 + PKCE againstauth.openai.com, a loopback callback, and inference through the ChatGPT backend's Responses API. Tokens are stored locally at0600, refreshed automatically (lazily + on 401);/logoutremoves them.This reuses the first-party Codex OAuth
client_idand thechatgpt.com/backend-api/codexbackend — the same approach other third-party agents (Cline, opencode) use. It is not an officially sanctioned integration: OpenAI's supported "Sign in with ChatGPT" (Apps SDK) is identity-only and does not grant model access to third-party tools. Using it is unofficial, against the spirit of OpenAI's ToS, and carries account-ban risk. A Free plan cannot run models this way. All Codex-specific constants live inauth/constants.pyso a forced change is a one-file edit.How it's built
kolega_code/auth/(new): Codex OAuth constants (single source),OAuthTokens+ChatGPTTokenManager(lazy + 401 refresh, injected persistence), JWT claim decode, and the browser-loopback login flow (PKCE, ports 1455/1457, plan gating).llm/providers/chatgpt_oauth.py:ChatGPTOAuthProvideron the Responses API with a refreshinghttpx.Auth, full message/tool/image→Responses conversion, and aResponsesStreamWrappermatching the existingtext/thinking/tool_use_start+get_final_message()contract.ModelProvider.OPENAI_CHATGPT,MODEL_SPECS(newopenai_responses_reasoningeffort mode),LLMClientrouting +token_managerthreading through all construction sites,AgentConfigtoken field/validation/manager accessor,build_agent_config, registry labels, and/login+/logoutcommands./loginfor the OAuth provider (disabled key field + clear error). README section +.env.examplenote.oauth_tokensonCliSettings(no schema bump, matching theweb_search/active_themeprecedent), chmod0600.Testing
1064unit tests pass; ruff clean on all changed/new files.LLMClientrouting +AgentConfigvalidation, plus a gated live test (test_chatgpt_live.py, skipped without credentials).Manual end-to-end
/loginwith a Plus/Pro account → confirmoauth_tokens.openai_chatgptinsettings.jsonat-rw-------→/model openai_chatgpt/gpt-5-codex→ send a prompt that triggers a tool call and confirm streaming + tool execution.🤖 Generated with Claude Code