Documentation
¶
Index ¶
- func DecodeResponse(body []byte) (*inference.Response, error)
- func EncodeRequest(req inference.Request, stream bool) ([]byte, error)
- func NewStream(body io.ReadCloser) *stream.StreamReader[content.Chunk]
- type ChatRequest
- type Codec
- func (Codec) DecodeEvent(event []byte) ([]content.Chunk, error)
- func (Codec) DecodeRequest(req *http.Request) (codec.DecodedRequest, error)
- func (Codec) DecodeResponse(body []byte) (*inference.Response, error)
- func (Codec) DecodeStream(resp *http.Response) (*stream.StreamReader[content.Chunk], error)
- func (Codec) EncodeRequest(req inference.Request, mode codec.RequestMode) (codec.EncodedRequest, error)
- func (Codec) MatchRequest(req *http.Request) bool
- func (Codec) OpenStream(w http.ResponseWriter) (codec.StreamEncoder, error)
- func (Codec) WriteError(w http.ResponseWriter, err error)
- func (Codec) WriteResponse(w http.ResponseWriter, resp *inference.Response) error
- type DuplicateKeyError
- type InvalidToolNameError
- type SamplingRangeError
- type ServerDecodeError
- type StreamAPIError
- type StreamDecodeError
- type StreamEventDecodeError
- type StreamTerminatedError
- type UnsupportedBlockError
- type UnsupportedChoiceCountError
- type UnsupportedChunkError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DecodeResponse ¶
DecodeResponse parses an OpenAI chat completions JSON response body into a provider-neutral *inference.Response.
func EncodeRequest ¶
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 ¶
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 ¶
DecodeRequest decodes a matched POST /v1/chat/completions request into a codec.DecodedRequest, delegating to the free decodeChatCompletionsRequest.
func (Codec) DecodeResponse ¶
DecodeResponse parses a non-streaming OpenAI chat completions body, delegating to the free DecodeResponse.
func (Codec) DecodeStream ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
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 ¶
func (e *UnsupportedChoiceCountError) Error() string
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