codex

package
v1.21.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 22, 2026 License: GPL-3.0 Imports: 22 Imported by: 0

Documentation

Overview

Package codex implements a native client for the Codex Responses API — the same backend the Codex CLI / Codex Desktop talk to (POST https://chatgpt.com/backend-api/codex/responses).

It is a Go port of the proxy core of https://github.com/icebear0828/codex-proxy: it authenticates with a ChatGPT account (OAuth access token, refreshed as needed), translates an OpenAI-style chat request into the Codex Responses wire format, streams the Server-Sent-Events response back, and translates the events into plain text + tool calls + usage.

Unlike codex-proxy this does not stand up an HTTP server; it is an in-process client so karma can use a ChatGPT subscription as just another model provider.

Index

Constants

View Source
const (
	// DefaultOAuthClientID is the public OAuth client id used by Codex (and
	// codex-proxy) for the refresh-token grant.
	DefaultOAuthClientID = "app_EMoamEEZ73f0CkXaXp7hrann"
	// DefaultOAuthTokenEndpoint is the OpenAI OAuth token endpoint.
	DefaultOAuthTokenEndpoint = "https://auth.openai.com/oauth/token"
)
View Source
const DefaultBaseURL = "https://chatgpt.com/backend-api"

DefaultBaseURL is the Codex backend the Codex CLI / Desktop use.

Variables

This section is empty.

Functions

func IsRetryable

func IsRetryable(err error) bool

IsRetryable reports whether err (or anything it wraps) is a transient *APIError. HTTP- and stream-layer Codex failures both flow through APIError, so this is the single retry signal for the Codex provider.

func ParseModelName

func ParseModelName(input string) (modelID, serviceTier, effort string)

ParseModelName splits a model string into its base id and any trailing service-tier / reasoning-effort suffixes.

Types

type APIError

type APIError struct {
	Status  int
	Body    string
	Headers http.Header // response headers, when from an HTTP response
}

APIError is returned for non-2xx HTTP responses and for mid-stream `error` / `response.failed` events from the Codex backend. Stream events are mapped to an HTTP-equivalent Status (see statusForCode) so callers can apply one consistent retry/recovery policy — mirroring codex-proxy's codexApiErrorFromEvent.

func (*APIError) Error

func (e *APIError) Error() string

func (*APIError) IsCloudflareChallenge

func (e *APIError) IsCloudflareChallenge() bool

IsCloudflareChallenge reports whether the response is a Cloudflare bot challenge (vs. an application error). Port of codex-proxy's isCfChallengeError: a 403/503 whose body or headers carry CF challenge markers.

func (*APIError) RetryAfter

func (e *APIError) RetryAfter() time.Duration

RetryAfter returns how long to wait before retrying, from the Retry-After header or the error body's resets_in_seconds / resets_at (Codex 429s), or 0.

func (*APIError) Retryable

func (e *APIError) Retryable() bool

Retryable reports whether the failure is transient and worth retrying (timeouts, conflicts, rate limits, 5xx — including the generic 502 a codeless mid-stream failure maps to — and Cloudflare challenges).

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client talks to the Codex Responses API on behalf of a ChatGPT account.

func NewClient

func NewClient(cfg Config) (*Client, error)

NewClient builds a Codex client, resolving credentials via NewTokenSource.

func Shared

func Shared(cfg Config) (*Client, error)

Shared returns a process-wide cached Client for the given config so the session cookie jar (cf_clearance / __cf_bm), warmup state, and token-refresh state stay warm across calls — the single-process analogue of codex-proxy's long-lived account sessions. Falls back to a fresh client on cache miss.

func (*Client) AccountID

func (c *Client) AccountID() string

AccountID returns the resolved ChatGPT account id (may be empty).

func (*Client) CreateResponse

func (c *Client) CreateResponse(ctx context.Context, req *ResponsesRequest) (*http.Response, error)

CreateResponse POSTs a streaming Responses request over HTTP and returns the raw response. The caller owns response.Body and must Close it. Non-2xx responses are returned as *APIError. Prefer Generate, which adds the WebSocket transport + consumption.

func (*Client) Generate

func (c *Client) Generate(ctx context.Context, req *ResponsesRequest, onText, onReasoning func(string) error) (*Result, error)

Generate runs a Responses request over the most reliable available transport and consumes the stream, returning the fully collected Result. onText / onReasoning, when non-nil, receive streamed deltas.

WebSocket is tried first — it is the transport codex-proxy uses for its primary endpoints and the one the backend advertises via prefer_websockets; the HTTP-SSE POST path is markedly more prone to transient 502s. On a WebSocket *transport* failure (before any output) it transparently falls back to HTTP SSE. A genuine upstream API error (rate limit, etc.) is surfaced as *APIError and not retried over the other transport.

func (*Client) ListModels

func (c *Client) ListModels(ctx context.Context) ([]ModelInfo, error)

ListModels discovers the models available to the account by probing the same Codex backend endpoints the Codex CLI uses, returning the flattened list. It returns an empty slice (no error) if the backend exposes no discovery endpoint — any model the account supports can still be requested by id.

type Config

type Config struct {
	BaseURL          string
	Originator       string
	AppVersion       string
	UserAgent        string // full User-Agent override; default derived from host
	Residency        string // x-openai-internal-codex-residency override
	ClientID         string // OAuth client id (refresh grant)
	TokenEndpoint    string // OAuth token endpoint
	HTTPClient       *http.Client
	DisableWebSocket bool // force HTTP-SSE transport (skip the WebSocket primary)
}

Config configures a Client. Zero values fall back to sensible defaults and the CODEX_* environment variables.

type ContentPart

type ContentPart struct {
	Type     string `json:"type"`                // "input_text" | "input_image"
	Text     string `json:"text,omitempty"`      // for input_text
	ImageURL string `json:"image_url,omitempty"` // for input_image (url or data: URI)
}

ContentPart is a single part of a multimodal user message.

type InputItem

type InputItem struct {
	Type      string `json:"type,omitempty"`
	Role      string `json:"role,omitempty"`
	Content   any    `json:"content,omitempty"`
	CallID    string `json:"call_id,omitempty"`
	Name      string `json:"name,omitempty"`
	Arguments string `json:"arguments,omitempty"`
	Output    string `json:"output,omitempty"`
}

InputItem is one entry in the Responses `input` array. It is intentionally a flat struct covering every item shape the Codex API accepts:

  • message: {role, content} (Type == "")
  • function_call: {type, call_id, name, arguments}
  • function_call_output: {type, call_id, output}

Content is either a string or a []ContentPart (for images).

type Message

type Message struct {
	Role       string // "user" | "assistant" | "system" | "developer" | "tool"
	Content    string
	Images     []string   // image URLs or data: URIs (user messages)
	ToolCalls  []ToolCall // assistant tool calls to replay
	ToolCallID string     // tool result correlation id
}

Message is a provider-agnostic chat message used to build a Responses request.

type ModelInfo

type ModelInfo struct {
	ID                        string
	DisplayName               string
	Description               string
	SupportedReasoningEfforts []string
}

ModelInfo describes a model available to the authenticated account.

type Reasoning

type Reasoning struct {
	Effort  string `json:"effort,omitempty"`
	Summary string `json:"summary,omitempty"`
}

Reasoning controls reasoning effort + summary mode on the Responses API.

type RequestOptions

type RequestOptions struct {
	Model           string
	Instructions    string // collected system/developer text
	Messages        []Message
	Tools           []Tool
	ToolChoice      any
	ReasoningEffort string // explicit override; suffix on Model is used otherwise
	ServiceTier     string // explicit override; suffix on Model is used otherwise
}

RequestOptions describes a Codex Responses request in protocol-neutral terms.

type ResponsesRequest

type ResponsesRequest struct {
	Model             string            `json:"model"`
	Instructions      string            `json:"instructions,omitempty"`
	Input             []InputItem       `json:"input"`
	Stream            bool              `json:"stream"`
	Store             bool              `json:"store"`
	Reasoning         *Reasoning        `json:"reasoning,omitempty"`
	ServiceTier       string            `json:"service_tier,omitempty"`
	Tools             []Tool            `json:"tools,omitempty"`
	ToolChoice        any               `json:"tool_choice,omitempty"`
	ParallelToolCalls *bool             `json:"parallel_tool_calls,omitempty"`
	Text              *TextFormat       `json:"text,omitempty"`
	PromptCacheKey    string            `json:"prompt_cache_key,omitempty"`
	ClientMetadata    map[string]string `json:"client_metadata,omitempty"`
	Include           []string          `json:"include,omitempty"`
}

ResponsesRequest is the body POSTed to /codex/responses. The Codex backend requires stream=true and store=false, so those fields are always serialized.

func BuildRequest

func BuildRequest(opts RequestOptions) *ResponsesRequest

BuildRequest translates protocol-neutral options into a CodexResponsesRequest.

type Result

type Result struct {
	Text       string
	Reasoning  string
	ToolCalls  []ToolCall
	Usage      Usage
	ResponseID string
}

Result is the fully-collected outcome of a (non-streaming) Codex response.

func Consume

func Consume(resp *http.Response, onText, onReasoning func(string) error) (*Result, error)

Consume reads a Codex Responses SSE stream (HTTP transport) to completion. When onText is non-nil it is invoked for each text delta (streaming); onReasoning, likewise, for reasoning-summary deltas. It always returns the fully collected Result.

type SSEEvent

type SSEEvent struct {
	Event string
	Data  json.RawMessage
}

SSEEvent is a single parsed Server-Sent Event from the Codex stream.

type TextFormat

type TextFormat struct {
	Format struct {
		Type   string         `json:"type"` // "text" | "json_object" | "json_schema"
		Name   string         `json:"name,omitempty"`
		Schema map[string]any `json:"schema,omitempty"`
		Strict *bool          `json:"strict,omitempty"`
	} `json:"format"`
}

TextFormat carries structured-output / JSON-mode configuration.

type TokenSource

type TokenSource struct {
	// contains filtered or unexported fields
}

TokenSource provides a valid ChatGPT access token + account id for Codex requests, transparently refreshing the token when it is close to expiry.

It is safe for concurrent use. Tokens are loaded from (in order):

  1. The CODEX_ACCESS_TOKEN env var (with optional CODEX_REFRESH_TOKEN / CODEX_ACCOUNT_ID), or
  2. The Codex CLI auth file at $CODEX_HOME/auth.json (default ~/.codex/auth.json), supporting both the nested `tokens` object the CLI writes and a flat token layout.

On refresh the new tokens are written back to the auth file when one was the source, so the refreshed access token survives process restarts.

func NewTokenSource

func NewTokenSource(clientID, tokenEndpoint string, httpClient *http.Client) (*TokenSource, error)

NewTokenSource builds a TokenSource, resolving tokens from the environment or the Codex CLI auth file. clientID/tokenEndpoint may be empty to use defaults.

func (*TokenSource) Residency

func (ts *TokenSource) Residency() string

Residency returns the account's compute residency (e.g. "us"), or "" if the token does not carry the claim.

func (*TokenSource) Token

func (ts *TokenSource) Token(ctx context.Context) (accessToken, accountID string, err error)

Token returns a currently-valid access token and account id, refreshing if the token is expired (or within the refresh margin) and a refresh token is available.

type Tool

type Tool struct {
	Type        string         `json:"type"` // "function"
	Name        string         `json:"name,omitempty"`
	Description string         `json:"description,omitempty"`
	Parameters  map[string]any `json:"parameters,omitempty"`
	Strict      bool           `json:"strict,omitempty"`
}

Tool is a function tool definition in the Codex Responses format. Note the flattened shape (name/parameters at the top level) — this differs from the OpenAI Chat Completions nesting under `function`.

type ToolCall

type ToolCall struct {
	ID        string
	Name      string
	Arguments string
}

ToolCall is a function call emitted by the model.

type Usage

type Usage struct {
	InputTokens     int
	OutputTokens    int
	CachedTokens    int
	ReasoningTokens int
}

Usage holds token accounting extracted from the terminal response event.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL