Documentation
¶
Index ¶
- func DecodeResponse(body []byte) (*inference.Response, error)
- func EncodeRequest(req inference.Request) ([]byte, error)
- 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, _ 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 DecodeError
- type DuplicateKeyError
- type EncodeError
- type GenerateContentRequest
- type GenerateContentResponse
- type InvalidToolNameError
- type PromptBlockedError
- type SafetyRating
- type SamplingRangeError
- type ServerDecodeError
- type StreamAPIError
- type StreamDecodeError
- type StreamEventDecodeError
- type StreamTerminatedError
- type UnsupportedBlockError
- type UnsupportedChunkError
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DecodeResponse ¶
DecodeResponse parses a Gemini generateContent JSON response body into a provider-neutral *inference.Response. It reads candidates[0]; a body with no candidates is a *failure.APIError (matching the sibling OpenAI codec) — or a *PromptBlockedError, which unwraps to one, when promptFeedback says why — and malformed JSON is a *DecodeError.
func EncodeRequest ¶
EncodeRequest converts a provider-neutral inference.Request to a Gemini generateContent JSON body. Note there is no stream parameter: Gemini's generateContent and streamGenerateContent bodies are byte-for-byte identical — the transport selects the endpoint and adds `?alt=sse`, so Codec.EncodeRequest ignores its RequestMode.
Types ¶
type Codec ¶
type Codec struct{}
Codec is the Google Gemini generateContent 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. Methods delegate to package-level free functions so the two surfaces cannot diverge.
func (Codec) DecodeEvent ¶
DecodeEvent decodes one already-de-framed streamGenerateContent chunk (a partial GenerateContentResponse) into the chunk(s) it yields. Tolerance is scoped to SHAPE, not to validity: a well-formed chunk this dialect has no mapping for (no candidates, an unmodeled part type, a future top-level field) returns (nil, nil) so a new Gemini feature cannot break the stream, but malformed or truncated JSON is a *StreamEventDecodeError. Dropping it would let a half-delivered answer finish as a clean, complete-looking one. A frame reporting a blocked prompt (promptFeedback.blockReason) is the same kind of failure as that envelope: it is well-formed and candidate-less, so skipping it left the stream to end with no terminal event and be reported as truncated rather than refused. DecodeEvent is stateless; cross-event assembly happens downstream in the stream accumulator.
func (Codec) DecodeRequest ¶
DecodeRequest decodes a matched request into a codec.DecodedRequest, delegating to the free decodeGenerateContentRequest. The {model} path segment becomes RequestedModel; which of the two routes matched sets Streaming.
func (Codec) DecodeResponse ¶
DecodeResponse parses a non-streaming Gemini generateContent body, delegating to the free DecodeResponse.
func (Codec) DecodeStream ¶
DecodeStream frames a successful Gemini streamGenerateContent response (served as SSE via ?alt=sse) with wire/sse and maps each frame through the codec's per-event decode logic. Gemini has no terminal payload sentinel — the body simply ends — so the terminal signal is a candidate carrying a finishReason, and a body that reaches EOF without one fails with a *StreamDecodeError rather than reporting a clean, truncated success. It owns resp.Body: the returned reader's Close closes it.
Unlike Codec.DecodeEvent, this path is stream-scoped, so it can number reasoning blocks across events (streamResultCollector.thoughtBase) rather than restarting at 0 in each one — see the INDEX SEMANTICS note on decodeEvent.
func (Codec) EncodeRequest ¶
func (Codec) EncodeRequest(req inference.Request, _ codec.RequestMode) (codec.EncodedRequest, error)
EncodeRequest builds the Gemini generateContent request: a JSON body reader plus the application/json content type as an EncodedRequest. The RequestMode is intentionally ignored: Gemini's generateContent and streamGenerateContent bodies are identical — streaming is chosen by the transport via the route (`:streamGenerateContent?alt=sse`), not a body field — so Invoke and Stream produce the same bytes.
func (Codec) MatchRequest ¶
MatchRequest reports whether req is a POST to either of this codec's two owned routes: :generateContent (non-streaming) or :streamGenerateContent (streaming).
func (Codec) OpenStream ¶
func (Codec) OpenStream(w http.ResponseWriter) (codec.StreamEncoder, error)
OpenStream begins the native streamGenerateContent streaming response and returns its request-scoped StreamEncoder, delegating to the free openGenerateContentStream.
func (Codec) WriteError ¶
func (Codec) WriteError(w http.ResponseWriter, err error)
WriteError encodes err as the native generateContent error envelope, delegating to the free writeGenerateContentError.
func (Codec) WriteResponse ¶
WriteResponse encodes a complete inference.Response as the native generateContent non-streaming response, delegating to the free writeGenerateContentResponse.
type DecodeError ¶
DecodeError is a failure while parsing a Gemini response body into a provider-neutral Response (a JSON unmarshal failure). The distinct "no candidates, no explanation" case returns *failure.APIError instead, matching the sibling OpenAI codec so the transport and callers treat every dialect uniformly; a candidate-less body that DOES explain itself returns *PromptBlockedError, which unwraps to that same APIError.
func (*DecodeError) Error ¶
func (e *DecodeError) Error() string
func (*DecodeError) Unwrap ¶
func (e *DecodeError) Unwrap() error
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 EncodeError ¶
EncodeError is a failure while translating an inference.Request into the Gemini wire body — an unknown conversation type or a JSON marshal failure. Typed per CLAUDE.md so callers can errors.As it to distinguish an encode fault from a transport or API error.
func (*EncodeError) Error ¶
func (e *EncodeError) Error() string
func (*EncodeError) Unwrap ¶
func (e *EncodeError) Unwrap() error
type GenerateContentRequest ¶
type GenerateContentRequest struct {
Contents []geminiContent `json:"contents"`
SystemInstruction *geminiContent `json:"systemInstruction,omitempty"`
Tools []geminiTool `json:"tools,omitempty"`
ToolConfig *toolConfig `json:"toolConfig,omitempty"`
GenerationConfig *generationConfig `json:"generationConfig,omitempty"`
}
GenerateContentRequest is the Gemini generateContent / streamGenerateContent wire request body. The two endpoints share an identical body — streaming is a URL + `?alt=sse` concern owned by the transport, not a body field — so there is no "stream" flag here (unlike the OpenAI dialect). Exported so provider packages can embed it in a typed extension struct without round-tripping through map[string]json.RawMessage.
func BuildGenerateContentRequest ¶
func BuildGenerateContentRequest(req inference.Request) (GenerateContentRequest, error)
BuildGenerateContentRequest converts a provider-neutral inference.Request into a GenerateContentRequest struct. Exported so provider packages can embed or extend the result before marshaling. The effective sampling is Request.Override when non-nil, else Model.Sampling — the same precedence every codec honors.
type GenerateContentResponse ¶
type GenerateContentResponse struct {
Candidates []candidate `json:"candidates"`
UsageMetadata *usageMetadata `json:"usageMetadata"`
ModelVersion string `json:"modelVersion"`
// PromptFeedback explains a response that carries no candidates. The
// discovery document states the API "returns no candidates at all only if
// there was something wrong with the prompt (check prompt_feedback)", so
// this is the only place such a response says WHY — there is no error
// envelope and the HTTP status is a success. Decoded so that case becomes
// a *PromptBlockedError instead of an anonymous failure (decode.go).
PromptFeedback *promptFeedback `json:"promptFeedback"`
// Error carries the `{"error":{...}}` envelope (a google.rpc.Status:
// code/message/status) Google can emit as a stream frame AFTER the request
// already returned a successful HTTP status. It is modeled here rather than
// left unknown because such a frame is otherwise a perfectly valid object
// with no candidates — indistinguishable, to a tolerant decoder, from an
// uninteresting chunk — so ignoring it let a failed generation finish as a
// clean, truncated success. It reuses the same geminiErrorBody this codec's
// server direction writes (server_encode.go).
Error *geminiErrorBody `json:"error"`
}
GenerateContentResponse is the Gemini generateContent response body and the per-chunk streamGenerateContent event (identical shape; a streamed chunk is a partial GenerateContentResponse).
type InvalidToolNameError ¶
InvalidToolNameError is returned by the encoder when a function name cannot satisfy the class FunctionDeclaration.name publishes — "Must be a-z, A-Z, 0-9, or contain underscores, colons, dots, and dashes, with a maximum length of 128".
The constraint lives in the discovery document's prose, not in its schema: `name` is typed as a bare string, so the conformance gate accepts a name Gemini's server will refuse (measured — see TestTheGenerateContentGateHoldsNoneOfThis). This error is the only thing holding the line. Shaped after the sibling anthropicapi error of the same name, whose class is narrower — no "." and no ":" — which is why a tool set that encodes here may not encode there.
func (*InvalidToolNameError) Error ¶
func (e *InvalidToolNameError) Error() string
type PromptBlockedError ¶
type PromptBlockedError struct {
// BlockReason is a member of PromptFeedback's published blockReason enum,
// or "" when the provider sent a value this codec does not recognize.
BlockReason string
SafetyRatings []SafetyRating
Usage *usage.Usage
}
PromptBlockedError reports a generateContent response that returned no candidates because the PROMPT was refused by Gemini's content filters, with the reason the response carried in promptFeedback.
It exists because that response is otherwise indistinguishable from an unknown failure: it arrives with a successful HTTP status, no candidates and no error envelope, and this codec used to report it as a statusless, codeless *failure.APIError — a caller could not tell a policy block from a broken response. The discovery document is explicit that the API "returns no candidates at all only if there was something wrong with the prompt (check prompt_feedback)".
Usage carries the token accounting the response reported. A blocked prompt is still a billed prompt, so those counts are retained on the failure rather than discarded with it; it is nil when the response carried no usage, or usage this codec could not normalize.
func (*PromptBlockedError) Error ¶
func (e *PromptBlockedError) Error() string
func (*PromptBlockedError) Unwrap ¶
func (e *PromptBlockedError) Unwrap() error
Unwrap keeps every existing caller that classifies on *failure.APIError working, and upgrades what it sees: the neutral error this codec used to return for a candidate-less body carried no code at all, where a blocked prompt is precisely a content-policy refusal. promptFeedback is defined as "the prompt's feedback related to the content filters", so every blockReason it can hold is one — the code does not vary by reason.
The APIError is built on demand so a PromptBlockedError composed by hand (in a test, or by a future caller) unwraps the same way one built here does.
type SafetyRating ¶
SafetyRating is one category of Gemini's content classification of a prompt, carried on PromptBlockedError. Category and Probability are members of the closed enums the discovery document publishes for SafetyRating; a value outside them is withheld rather than copied through, so no unbounded provider string reaches an error. Blocked reports whether this particular category is the one that refused the prompt.
type SamplingRangeError ¶
SamplingRangeError is returned by the encoder when a sampling knob falls outside the interval this dialect documents — temperature [0, 2], topP [0, 1].
Min and Max are carried on the error because the bounds differ per field and, more importantly, per provider: Anthropic and Bedrock cap temperature at 1 where Gemini and OpenAI reach 2. The shared model.Sampling vocabulary spans every dialect, so a session moved onto a Gemini model can carry a value that was legal at its source, and only the destination codec knows it is not legal here.
func (*SamplingRangeError) Error ¶
func (e *SamplingRangeError) Error() string
type ServerDecodeError ¶
ServerDecodeError reports a native generateContent/streamGenerateContent request this codec cannot decode into the provider-neutral vocabulary: malformed shape, an unrecognized route, 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 Gemini `{"error":{...}}` envelope (a google.rpc.Status carrying code/message/status) received after a streaming request already crossed the successful HTTP-status boundary. Only the structured fields are retained, never the raw frame.
func (*StreamAPIError) Error ¶
func (e *StreamAPIError) Error() string
type StreamDecodeError ¶
StreamDecodeError reports a streamGenerateContent response that is framed and parseable but structurally wrong — currently only a stream that ends without any candidate carrying a finishReason, which per the v1beta discovery document ("If empty, the model has not stopped generating tokens") 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, the module's strictest streaming dialect.
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 streamGenerateContent stream. Mirrors the sibling openaiapi/openairesponses codecs: invalid or truncated streaming JSON is an error, never a successful response with silently missing content. A well-formed chunk this dialect simply has no mapping for stays a tolerant skip, so forward compatibility is unaffected.
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 put on the wire: its concrete type has no home in the Gemini generateContent dialect (e.g. a media block on a model turn), or the block is modeled but carries something the dialect refuses — a media type absent from Blob's documented list, or no source at all. Block holds the Go type name and Reason the specific defect, both for diagnosis. Fail-secure per CLAUDE.md and consistent with the sibling bedrockconverse codec's identically shaped error: such a 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 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