openai

package
v0.18.9 Latest Latest
Warning

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

Go to latest
Published: May 10, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func ClassifyChatHTTPErrorFor added in v0.15.2

func ClassifyChatHTTPErrorFor(provider string, statusCode int, body []byte) *ai.AIError

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

func MapChatFinishReason(r string) string

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

func ParseChatStepResponse(body []byte, requestedModel string) (*ai.Response, *ai.AIError)

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 APIType

type APIType string

APIType indicates which OpenAI API to use.

const (
	// APIChatCompletions uses the /v1/chat/completions endpoint.
	APIChatCompletions APIType = "chat"

	// APIResponses uses the /v1/responses endpoint (for codex models).
	APIResponses APIType = "responses"
)

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

func (c *Client) Generate(ctx context.Context, req *ai.Request) (*ai.Response, error)

Generate implements ai.Provider. It routes to either Chat Completions or Responses API based on the model.

func (*Client) Name

func (c *Client) Name() string

Name implements ai.Provider.

func (*Client) NewHandler

func (c *Client) NewHandler(model string, opts ...ai.HandlerOption) *ai.Handler

NewHandler creates an ai.Handler wrapping this client.

func (*Client) Step added in v0.15.2

func (c *Client) Step(ctx context.Context, req *ai.Request) (*ai.Response, error)

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.

Jump to

Keyboard shortcuts

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