Documentation
¶
Overview ¶
Package openai provides an OpenAI API client implementing the ai.Provider interface. It supports both Chat Completions API and Responses API (for codex models).
Index ¶
- func ClassifyChatHTTPErrorFor(provider string, statusCode int, body []byte) *ai.AIError
- func MapChatFinishReason(r string) string
- func ParseChatStepResponse(body []byte, requestedModel string) (*ai.Response, *ai.AIError)
- func ParseChatStepSSEStream(body io.Reader, requestedModel string, onChunk func(ai.StreamChunk)) (*ai.Response, *ai.AIError)
- type APIType
- type ChatStepChoice
- type ChatStepErrorEnvelope
- type ChatStepMessage
- type ChatStepPromptTokDetails
- type ChatStepRequest
- type ChatStepRespMessage
- type ChatStepResponse
- type ChatStepStreamChoice
- type ChatStepStreamChunk
- type ChatStepStreamDelta
- type ChatStepStreamToolCallFrag
- type ChatStepStreamToolCallFragFunction
- type ChatStepToolCall
- type ChatStepToolCallFunction
- type ChatStepToolDef
- type ChatStepToolDefFunction
- type ChatStepUsage
- type ChatStreamOption
- type Client
- func (c *Client) Generate(ctx context.Context, req *ai.Request) (*ai.Response, error)
- func (c *Client) Name() string
- func (c *Client) NewHandler(model string, opts ...ai.HandlerOption) *ai.Handler
- func (c *Client) Step(ctx context.Context, req *ai.Request) (*ai.Response, error)
- func (c *Client) StreamStep(ctx context.Context, req *ai.Request, onChunk func(ai.StreamChunk)) (*ai.Response, error)
- type ClientOption
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ClassifyChatHTTPErrorFor ¶ added in v0.15.2
ClassifyChatHTTPErrorFor classifies an OpenAI-format error envelope. Both OpenAI and OpenRouter speak this shape — provider distinguishes which name is reported in the AIError.Message when the body is empty.
func MapChatFinishReason ¶ added in v0.15.2
MapChatFinishReason converts the OpenAI Chat Completions finish_reason vocabulary into the normalized ai.Response.FinishReason values.
Mapping:
"stop" → "stop" "tool_calls" → "tool_calls" "function_call" → "tool_calls" (legacy single-function alias) "length" → "length" "content_filter" → "error" anything else → "error"
func ParseChatStepResponse ¶ added in v0.15.2
ParseChatStepResponse decodes a successful Chat Completions response body into an ai.Response. requestedModel is used as a fallback when the response model field is empty (rare but defensive).
func ParseChatStepSSEStream ¶ added in v0.18.7
func ParseChatStepSSEStream(body io.Reader, requestedModel string, onChunk func(ai.StreamChunk)) (*ai.Response, *ai.AIError)
ParseChatStepSSEStream consumes the SSE event stream from an OpenAI Chat Completions streaming response and produces a typed *ai.Response. The onChunk callback fires per content delta and once at end-of-stream with the final usage block.
SSE wire format (no event-type lines, just data: <json>):
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"Hello"}}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{"content":"!"}}]}
data: {"id":"...","object":"chat.completion.chunk","choices":[{"delta":{},"finish_reason":"stop"}]}
data: {"id":"...","object":"chat.completion.chunk","usage":{...}}
data: [DONE]
Errors are wrapped as *ai.AIError so callers don't need to translate.
Types ¶
type ChatStepChoice ¶ added in v0.15.2
type ChatStepChoice struct {
Index int `json:"index"`
Message ChatStepRespMessage `json:"message"`
FinishReason string `json:"finish_reason"`
}
ChatStepChoice is one completion choice. Message.Content is RawMessage so we can accept JSON null or a string transparently.
type ChatStepErrorEnvelope ¶ added in v0.15.2
type ChatStepErrorEnvelope struct {
Error struct {
Message string `json:"message"`
Type string `json:"type"`
Code string `json:"code"`
} `json:"error"`
}
ChatStepErrorEnvelope is the OpenAI/OpenRouter error response shape.
type ChatStepMessage ¶ added in v0.15.2
type ChatStepMessage struct {
Role string `json:"role"`
Content json.RawMessage `json:"content"`
ToolCallID string `json:"tool_call_id,omitempty"`
ToolCalls []ChatStepToolCall `json:"tool_calls,omitempty"`
}
ChatStepMessage is one entry in the messages array. Content is a json.RawMessage so callers can emit a JSON string OR JSON null (for assistant messages with tool_calls and empty text).
type ChatStepPromptTokDetails ¶ added in v0.18.4
type ChatStepPromptTokDetails struct {
CachedTokens int `json:"cached_tokens"`
}
ChatStepPromptTokDetails carries OpenAI's prompt-cache breakdown (cached_tokens). Anthropic-style cache_creation_input_tokens has no equivalent on OpenAI — the cache write isn't surfaced separately.
type ChatStepRequest ¶ added in v0.15.2
type ChatStepRequest struct {
Model string `json:"model"`
Messages []ChatStepMessage `json:"messages"`
Tools []ChatStepToolDef `json:"tools,omitempty"`
MaxTokens int `json:"max_tokens,omitempty"`
MaxCompletionTokens int `json:"max_completion_tokens,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
// Stream + StreamOptions are set by StreamStep (M-AI-STEP-STREAMING
// v0.18.7). omitempty keeps the non-streaming Step request bit-for-bit
// identical to pre-v0.18.7 wire bytes.
Stream bool `json:"stream,omitempty"`
StreamOptions *ChatStreamOption `json:"stream_options,omitempty"`
}
ChatStepRequest is the on-the-wire request body for a Chat Completions tool-use call. Exported so OpenRouter can extend it with its provider-routing field by composition (see openrouter package).
MaxTokens vs MaxCompletionTokens: OpenAI's GPT-5+ and o-series reasoning models (o1, o3) reject max_tokens with HTTP 400 "Unsupported parameter: 'max_tokens' is not supported with this model. Use 'max_completion_tokens' instead." BuildChatStepRequest routes the value to whichever field the model accepts (see usesMaxCompletionTokens). Only one of the two will be set on a given request — the omitempty tags keep the wire payload clean.
func BuildChatStepRequest ¶ added in v0.15.2
func BuildChatStepRequest(req *ai.Request) (*ChatStepRequest, *ai.AIError)
BuildChatStepRequest converts an ai.Request into the on-the-wire ChatStepRequest body. Returns *ai.AIError on translation failure (e.g. malformed JSON in a tool's Parameters schema).
The translation rules are documented on Client.Step.
type ChatStepRespMessage ¶ added in v0.15.2
type ChatStepRespMessage struct {
Role string `json:"role"`
Content json.RawMessage `json:"content"`
ToolCalls []ChatStepToolCall `json:"tool_calls,omitempty"`
}
ChatStepRespMessage is the assistant message inside a choice.
type ChatStepResponse ¶ added in v0.15.2
type ChatStepResponse struct {
ID string `json:"id"`
Object string `json:"object"`
Model string `json:"model"`
Choices []ChatStepChoice `json:"choices"`
Usage ChatStepUsage `json:"usage"`
}
ChatStepResponse is the parsed response body for a Chat Completions call.
type ChatStepStreamChoice ¶ added in v0.18.7
type ChatStepStreamChoice struct {
Index int `json:"index"`
Delta ChatStepStreamDelta `json:"delta"`
FinishReason string `json:"finish_reason,omitempty"`
}
ChatStepStreamChoice is one delta-bearing choice in a streaming chunk. Note that finish_reason is null until the second-to-last chunk.
type ChatStepStreamChunk ¶ added in v0.18.7
type ChatStepStreamChunk struct {
ID string `json:"id"`
Object string `json:"object"`
Model string `json:"model"`
Choices []ChatStepStreamChoice `json:"choices"`
Usage *ChatStepUsage `json:"usage,omitempty"`
}
ChatStepStreamChunk is one SSE chunk from a Chat Completions streaming response. Same envelope as ChatStepResponse but with `delta` instead of `message` inside each choice, and an OPTIONAL usage block that only appears in the final chunk (when stream_options.include_usage:true).
type ChatStepStreamDelta ¶ added in v0.18.7
type ChatStepStreamDelta struct {
Role string `json:"role,omitempty"`
Content string `json:"content,omitempty"`
ReasoningContent string `json:"reasoning_content,omitempty"`
Reasoning string `json:"reasoning,omitempty"`
ToolCalls []ChatStepStreamToolCallFrag `json:"tool_calls,omitempty"`
}
ChatStepStreamDelta is the partial assistant message in a streaming chunk. Role is set on the FIRST chunk; Content accumulates per chunk; ToolCalls fragments accumulate per chunk per tool-call index.
Reasoning fields (v0.18.8 + v0.18.9): per-chunk model-reasoning text. Surfaces as ai.StreamThinkingDelta to the user callback. Does NOT accumulate into Response.Text — the final assistant content comes only from delta.content. Two fields because providers don't agree:
- ReasoningContent (`reasoning_content`): OpenAI o1/o3 direct API. Per the OpenAI Chat Completions reasoning-model spec (v0.18.8).
- Reasoning (`reasoning`): OpenRouter's normalized field. EVERY OpenRouter-routed reasoning model surfaces here, regardless of the underlying provider's native field name (DeepSeek-R1 via Novita, Anthropic Claude via Bedrock, Qwen-thinking, etc.). Verified by direct-curl probe of openrouter.ai/api/v1 against deepseek/deepseek-r1 and anthropic/claude-opus-4.5 — both emit through `delta.reasoning` (v0.18.9).
At parse time we fire one ThinkingDelta per non-empty value across either field. In practice only one fires per chunk; the dual field support is just provider-shape defensive.
type ChatStepStreamToolCallFrag ¶ added in v0.18.7
type ChatStepStreamToolCallFrag struct {
Index int `json:"index"`
ID string `json:"id,omitempty"`
Type string `json:"type,omitempty"`
Function ChatStepStreamToolCallFragFunction `json:"function,omitempty"`
}
ChatStepStreamToolCallFrag is one fragment of a tool_call in a streaming chunk. ID + Function.Name appear ONCE on the first fragment for that tool-call index; Function.Arguments accumulates per chunk as a JSON-string fragment.
type ChatStepStreamToolCallFragFunction ¶ added in v0.18.7
type ChatStepStreamToolCallFragFunction struct {
Name string `json:"name,omitempty"`
Arguments string `json:"arguments,omitempty"`
}
ChatStepStreamToolCallFragFunction carries the per-fragment name + args.
type ChatStepToolCall ¶ added in v0.15.2
type ChatStepToolCall struct {
ID string `json:"id"`
Type string `json:"type"`
Function ChatStepToolCallFunction `json:"function"`
}
ChatStepToolCall is one tool invocation in an assistant message. Note that Function.Arguments is a JSON STRING (not an object) — OpenAI requires this.
type ChatStepToolCallFunction ¶ added in v0.15.2
type ChatStepToolCallFunction struct {
Name string `json:"name"`
Arguments json.RawMessage `json:"arguments"`
}
ChatStepToolCallFunction carries the tool name and the arguments STRING. json.RawMessage is used because the wire value MUST be a JSON string literal containing JSON — re-encoding as Go string would double-escape.
type ChatStepToolDef ¶ added in v0.15.2
type ChatStepToolDef struct {
Type string `json:"type"`
Function ChatStepToolDefFunction `json:"function"`
}
ChatStepToolDef is one entry in the tools array advertised to the model. Parameters is a decoded JSON Schema OBJECT (unlike Arguments which is a string).
type ChatStepToolDefFunction ¶ added in v0.15.2
type ChatStepToolDefFunction struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Parameters json.RawMessage `json:"parameters"`
}
ChatStepToolDefFunction carries the tool's name, description, and the JSON-Schema parameters as a decoded object on the wire.
type ChatStepUsage ¶ added in v0.15.2
type ChatStepUsage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
PromptTokensDetails *ChatStepPromptTokDetails `json:"prompt_tokens_details,omitempty"`
}
ChatStepUsage is the token-usage block.
type ChatStreamOption ¶ added in v0.18.7
type ChatStreamOption struct {
IncludeUsage bool `json:"include_usage,omitempty"`
}
ChatStreamOption controls per-stream behaviour. The only field we set is IncludeUsage which tells OpenAI to emit a final chunk carrying the usage block (otherwise streamed responses contain NO token counts at all).
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client implements ai.Provider for OpenAI's APIs. It supports both Chat Completions and Responses APIs.
func NewClient ¶
func NewClient(apiKey string, opts ...ClientOption) *Client
NewClient creates a new OpenAI client.
func (*Client) Generate ¶
Generate implements ai.Provider. It routes to either Chat Completions or Responses API based on the model.
func (*Client) NewHandler ¶
NewHandler creates an ai.Handler wrapping this client.
func (*Client) Step ¶ added in v0.15.2
Step is the multi-turn / tool-aware completion entry point introduced by M-AI-TOOL-LOOP (v0.17.0). It translates req.Messages + req.Tools into OpenAI's Chat Completions tool-use shape and parses tool_calls out of the response into resp.ToolCalls.
Wire shape contract:
- SystemPrompt is prepended as a {"role":"system"} message ONLY when req.Messages does not already contain a system-role entry. If req.Messages has a system message, req.Messages wins and SystemPrompt is dropped.
- User messages with no ToolCallID emit {"role":"user","content":<text>}.
- User messages with a ToolCallID, and Role="tool" messages, emit {"role":"tool","tool_call_id":<id>,"content":<text>}.
- Assistant messages with no ToolCalls emit {"role":"assistant","content":<text>}.
- Assistant messages with non-empty ToolCalls emit {"role":"assistant","content":null,"tool_calls":[...]} when Content is empty, or {"role":"assistant","content":<text>,"tool_calls":[...]} when Content is non-empty. tool_calls[].function.arguments is passed through verbatim — OpenAI emits it as a JSON STRING and round-trips it the same.
Errors are returned as *ai.AIError exclusively. Non-2xx responses are classified via ai.ClassifyHTTPError; transport / context errors via ai.ClassifyError. The inner error.message field of an OpenAI error envelope, when present, is hoisted into the AIError.Message for clarity.
func (*Client) StreamStep ¶ added in v0.18.7
func (c *Client) StreamStep(ctx context.Context, req *ai.Request, onChunk func(ai.StreamChunk)) (*ai.Response, error)
StreamStep is the streaming variant of Step, introduced by M-AI-STEP-STREAMING (v0.18.7). It builds the same request body as Step but sets stream:true + stream_options.include_usage:true and parses the resulting OpenAI Chat Completions SSE event stream into a typed StepResult — identical Response shape to Step on success.
Per-chunk callback semantics:
- ai.StreamContentDelta is fired once per non-empty content delta (choices[0].delta.content). The callback is the user's incremental- render hook.
- ai.StreamUsage is fired once at the final chunk (which contains the usage block per stream_options.include_usage:true).
- tool_calls deltas are accumulated into a per-index buffer; the assembled tool_calls become Response.ToolCalls. Tool deltas are NOT streamed individually in Phase 1 — that's deferred to Phase 2 (ToolCallDelta variant).
Implements ai.StreamingProvider so ai.Handler.StepWithStream type-asserts successfully and dispatches natively rather than NO-OP-falling-back.
type ClientOption ¶
type ClientOption func(*Client)
ClientOption configures a Client.
func WithAPIType ¶
func WithAPIType(apiType APIType) ClientOption
WithAPIType forces a specific API type (chat or responses).
func WithBaseURL ¶
func WithBaseURL(url string) ClientOption
WithBaseURL sets a custom base URL (useful for testing).
func WithHTTPClient ¶
func WithHTTPClient(client *http.Client) ClientOption
WithHTTPClient sets a custom HTTP client.