openaiapi

package
v0.12.1 Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func DecodeResponse

func DecodeResponse(body []byte) (*inference.Response, error)

DecodeResponse parses an OpenAI chat completions JSON response body into a provider-neutral *inference.Response.

func EncodeRequest

func EncodeRequest(req inference.Request, stream bool) ([]byte, error)

EncodeRequest converts a provider-neutral inference.Request to an OpenAI chat completions JSON body. stream=true adds "stream":true to the body. Request.System is prepended as a system message if non-empty.

func NewStream

func NewStream(body io.ReadCloser) *stream.StreamReader[content.Chunk]

NewStream adapts a raw OpenAI SSE body into a chunk stream. Exposed for provider extensions and dialect tests that drive a body directly; the transport uses DecodeStream. The caller must Close the returned reader when done.

Types

type ChatRequest

type ChatRequest struct {
	Model          string          `json:"model"`
	Messages       []chatMessage   `json:"messages"`
	Tools          []chatTool      `json:"tools,omitempty"`
	ResponseFormat *responseFormat `json:"response_format,omitempty"`
	// ToolChoice is ChatCompletionToolChoiceOption, a oneOf over a mode
	// string and three named-tool objects, so it is carried as raw JSON
	// rather than a string: BuildChatRequest emits either the "required"
	// mode or a ChatCompletionNamedToolChoice object.
	ToolChoice  json.RawMessage `json:"tool_choice,omitempty"`
	Temperature *float64        `json:"temperature,omitempty"`
	TopP        *float64        `json:"top_p,omitempty"`
	// MaxTokens and MaxCompletionTokens are the two mutually exclusive
	// token-limit spellings. OpenAI's spec marks max_tokens deprecated and
	// "not compatible with o-series models"; max_completion_tokens replaces
	// it and is the only form gpt-5 / o-series accept. BuildChatRequest
	// populates exactly one — see its capability gate — because plenty of
	// OpenAI-compatible servers speaking this dialect still know only
	// max_tokens.
	MaxTokens           *int               `json:"max_tokens,omitempty"`
	MaxCompletionTokens *int               `json:"max_completion_tokens,omitempty"`
	Stop                []string           `json:"stop,omitempty"`
	Stream              bool               `json:"stream,omitempty"`
	StreamOptions       *chatStreamOptions `json:"stream_options,omitempty"`

	// o-series reasoning
	ReasoningEffort string `json:"reasoning_effort,omitempty"`
}

ChatRequest is the OpenAI chat completions wire request. Exported so provider packages can embed it in a typed extension struct (e.g. adding an encrypted-response public key) without round-tripping through map[string]json.RawMessage.

func BuildChatRequest

func BuildChatRequest(req inference.Request, stream bool) (ChatRequest, error)

BuildChatRequest converts a provider-neutral inference.Request into a ChatRequest struct. Exported so provider packages can embed or extend the result before marshaling (e.g. a provider extension adds an encrypted-response public-key field).

type Codec

type Codec struct{}

Codec is the OpenAI Chat Completions wire dialect expressed as an codec.Codec (and, via DecodeStream, an codec.StreamingCodec). It is stateless (an empty struct with value-receiver methods), so one value is safely shared across goroutines: the transport owns HTTP mechanics, the Codec owns the JSON body, per-event semantics, and SSE stream decoding. The methods delegate to package-level free functions kept for provider extensions and the existing tests, so the two surfaces cannot diverge.

func (Codec) DecodeEvent

func (Codec) DecodeEvent(event []byte) ([]content.Chunk, error)

DecodeEvent decodes one already-de-framed SSE data payload into the chunk(s) it yields. Unknown valid shapes with no choices and role-only/empty deltas are skipped; malformed JSON is an error. A single delta carrying multiple tool-call entries returns all of them, and a delta combining reasoning, text, and/or tool calls returns a chunk for each. DecodeEvent is stateless: cross-event tool-argument assembly happens downstream in the stream accumulator, not here.

func (Codec) DecodeRequest

func (Codec) DecodeRequest(req *http.Request) (codec.DecodedRequest, error)

DecodeRequest decodes a matched POST /v1/chat/completions request into a codec.DecodedRequest, delegating to the free decodeChatCompletionsRequest.

func (Codec) DecodeResponse

func (Codec) DecodeResponse(body []byte) (*inference.Response, error)

DecodeResponse parses a non-streaming OpenAI chat completions body, delegating to the free DecodeResponse.

func (Codec) DecodeStream

func (Codec) DecodeStream(resp *http.Response) (*stream.StreamReader[content.Chunk], error)

DecodeStream frames a successful OpenAI streaming response with wire/sse and maps each frame through the codec's per-event decode logic. A body that ends without either end-of-generation signal — a choice's finish_reason or the [DONE] sentinel — fails with a *StreamDecodeError rather than reporting a clean, truncated success. It owns resp.Body: the returned reader's Close closes it (and DecodeStreamFrames closes it if it errors before returning a reader).

func (Codec) EncodeRequest

func (Codec) EncodeRequest(req inference.Request, mode codec.RequestMode) (codec.EncodedRequest, error)

EncodeRequest builds the OpenAI chat completions request: a JSON body reader plus the application/json content type as an EncodedRequest. RequestModeStream sets "stream":true in the body, every other mode omits it.

func (Codec) MatchRequest

func (Codec) MatchRequest(req *http.Request) bool

MatchRequest reports whether req is a POST /v1/chat/completions request.

func (Codec) OpenStream

func (Codec) OpenStream(w http.ResponseWriter) (codec.StreamEncoder, error)

OpenStream begins the native Chat Completions streaming response and returns its request-scoped StreamEncoder, delegating to the free openChatStream.

func (Codec) WriteError

func (Codec) WriteError(w http.ResponseWriter, err error)

WriteError encodes err as the native Chat Completions error envelope, delegating to the free writeChatError.

func (Codec) WriteResponse

func (Codec) WriteResponse(w http.ResponseWriter, resp *inference.Response) error

WriteResponse encodes a complete inference.Response as the native Chat Completions non-streaming response, delegating to the free writeChatResponse.

type DuplicateKeyError

type DuplicateKeyError struct {
	Key string
}

DuplicateKeyError reports a request body with a duplicate JSON object member name. encoding/json silently takes the last occurrence; this codec rejects the request instead so a client cannot smuggle a semantically different value past a naive review of the first occurrence.

func (*DuplicateKeyError) Error

func (e *DuplicateKeyError) Error() string

type InvalidToolNameError

type InvalidToolNameError struct {
	Name   string
	Reason string
}

InvalidToolNameError is returned by the encoder when a tool name cannot satisfy the class FunctionObject.name publishes — "Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length of 64".

The constraint is carried in the specification's prose, not in its JSON Schema: `name` is typed as a bare string with no `pattern` and no `maxLength`, so the conformance gate accepts a name OpenAI's server will refuse (measured — see TestTheChatRequestGateHoldsSamplingButNotToolNames). This error is the only thing holding the line, which is why it exists at all.

Tool servers hand out names the class excludes: MCP names routinely carry "." or "/", and a namespaced name easily runs past 64 characters. Rejecting here names the tool and the violated rule; the provider's 400 names neither. Shaped after the sibling anthropicapi error of the same name.

func (*InvalidToolNameError) Error

func (e *InvalidToolNameError) Error() string

type SamplingRangeError

type SamplingRangeError struct {
	Field string
	Value float64
	Min   float64
	Max   float64
}

SamplingRangeError is returned by the encoder when a sampling knob falls outside the interval CreateChatCompletionRequest declares for it — temperature [0, 2], top_p [0, 1].

Min and Max are carried on the error rather than baked into the message because the two fields have DIFFERENT bounds here, and because the bound that matters is the destination's: Anthropic and Bedrock cap temperature at 1 and OpenAI at 2, so a session moved between providers carries a value that was legal at its source into a request where it is not. The shared model.Sampling vocabulary is wide enough to hold every dialect's range, so the narrowing has to happen in the codec that owns the destination contract.

func (*SamplingRangeError) Error

func (e *SamplingRangeError) Error() string

type ServerDecodeError

type ServerDecodeError struct {
	Reason string
	Detail string
}

ServerDecodeError reports a native Chat Completions request body this codec cannot decode into the provider-neutral vocabulary: malformed shape, a missing required field, or a recognized-but-unsupported feature. Reason is a short machine-checkable diagnostic code; Detail elaborates for logs/messages.

func (*ServerDecodeError) Error

func (e *ServerDecodeError) Error() string

type StreamAPIError

type StreamAPIError struct {
	Code    string
	Message string
}

StreamAPIError reports a well-formed provider error object carried inside an otherwise successful Chat Completions stream — the spec's ErrorResponse envelope ({"error": {...}}) delivered over HTTP 200, as OpenAI-compatible gateways such as OpenRouter document. It is the streaming twin of the non-streaming path's failure.APIError, and is deliberately distinct from StreamEventDecodeError: the frame parsed fine, the provider is reporting a failure. Only the structured code and message are retained, never the raw frame. Code prefers the object's `code`, falling back to its `type`.

func (*StreamAPIError) Error

func (e *StreamAPIError) Error() string

type StreamDecodeError

type StreamDecodeError struct {
	Reason string
	Err    error
}

StreamDecodeError reports a Chat Completions stream that is framed and parseable but structurally wrong — currently only a body that reaches EOF without either end-of-generation signal the format defines (a choice's non-null finish_reason, or the [DONE] sentinel), which means the answer was truncated in flight. It never includes the raw provider payload in its diagnostic. Named and shaped after the equivalent type in codec/bedrockconverse and codec/geminiapi. It lives here rather than in errors.go because the gate it serves is the only thing that raises it.

func (*StreamDecodeError) Error

func (e *StreamDecodeError) Error() string

func (*StreamDecodeError) Unwrap

func (e *StreamDecodeError) Unwrap() error

type StreamEventDecodeError

type StreamEventDecodeError struct{ Err error }

StreamEventDecodeError reports malformed JSON inside an otherwise successfully framed Chat Completions stream.

func (*StreamEventDecodeError) Error

func (e *StreamEventDecodeError) Error() string

func (*StreamEventDecodeError) Unwrap

func (e *StreamEventDecodeError) Unwrap() error

type StreamTerminatedError

type StreamTerminatedError struct{}

StreamTerminatedError is returned by StreamEncoder.WriteChunk, Finish, or Fail once the stream has already been terminated by a prior Finish or Fail call, per the single-termination-ownership rule in codec.StreamEncoder.

func (*StreamTerminatedError) Error

func (e *StreamTerminatedError) Error() string

type UnsupportedBlockError

type UnsupportedBlockError struct {
	Block  string
	Reason string
}

UnsupportedBlockError is returned by the encoder when a content block cannot be placed on the wire: a concrete type the OpenAI chat completions dialect does not model in that position (any non-text block in a text-only tool message), or a block whose value falls outside what the position's schema accepts (an audio media type outside `input_audio.format`'s two-member enum, a file part with no filename to go with its inline data). Block holds the Go type name for diagnosis; Reason, when set, names the specific limitation — mirroring the sibling bedrockconverse codec's error of the same name. Fail-secure per CLAUDE.md and consistent with the sibling anthropicapi and geminiapi codecs: an unencodable block is refused, never silently dropped, so the model never receives less than the caller sent. Callers may errors.As to detect it.

func (*UnsupportedBlockError) Error

func (e *UnsupportedBlockError) Error() string

type UnsupportedChoiceCountError

type UnsupportedChoiceCountError struct {
	N int
}

UnsupportedChoiceCountError reports a request that asked for more than one completion choice ("n" > 1). The neutral one-response contract has no concept of multiple parallel choices, so this fails closed rather than silently returning only the first choice a harness may have expected N of.

func (*UnsupportedChoiceCountError) Error

type UnsupportedChunkError

type UnsupportedChunkError struct {
	Chunk string
}

UnsupportedChunkError is returned when a content.Chunk has a concrete type this dialect's stream encoder does not model. content.Chunk is a sealed interface, so this only guards against future variants added to the vocabulary.

func (*UnsupportedChunkError) Error

func (e *UnsupportedChunkError) Error() string

Jump to

Keyboard shortcuts

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