geminiapi

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Aug 20, 2026 License: Apache-2.0 Imports: 23 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 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

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

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

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

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

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

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

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

DecodeResponse parses a non-streaming Gemini generateContent body, delegating to the free DecodeResponse.

func (Codec) DecodeStream

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

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

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

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

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

WriteResponse encodes a complete inference.Response as the native generateContent non-streaming response, delegating to the free writeGenerateContentResponse.

type DecodeError

type DecodeError struct {
	Reason string
	Err    error
}

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

type EncodeError struct {
	Reason string
	Err    error
}

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

type InvalidToolNameError struct {
	Name   string
	Reason string
}

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

type SafetyRating struct {
	Category    string
	Probability string
	Blocked     bool
}

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

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 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

type ServerDecodeError struct {
	Reason string
	Detail string
}

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

type StreamAPIError struct {
	Code    int
	Status  string
	Message string
}

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

type StreamDecodeError struct {
	Reason string
	Err    error
}

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

type UnsupportedBlockError struct {
	Block  string
	Reason string
}

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

type UnsupportedEffortError added in v0.12.0

type UnsupportedEffortError struct {
	Effort string
}

UnsupportedEffortError reports a neutral reasoning effort with no Gemini thinking-budget mapping.

func (*UnsupportedEffortError) Error added in v0.12.0

func (e *UnsupportedEffortError) Error() string

Jump to

Keyboard shortcuts

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