Documentation
¶
Overview ¶
Package provider is the kit a model adapter is built from: HTTP error classification, SSE framing, a cap on a streamed reply, Retry-After parsing, and the tool-result codec. The first-party adapters (model/anthropic, model/openai, model/gemini) use it, and a third-party adapter can too.
The agent core never imports this package. It holds adapter plumbing, not the contract a Model meets: that contract (agent.Model, agent.Stream, agent.NewStreamFunc, agent.Emit and the events, agent.APIError, agent.RateLimited and the error sentinels) stays in package agent.
Index ¶
- Constants
- func ClassifyHTTPError(provider string, resp *http.Response) error
- func ClassifyStreamError(provider string, data []byte) error
- func EncodeToolResultOr(c ToolResultCodec, raw json.RawMessage) string
- func LimitResponse(body io.ReadCloser, max int64) io.ReadCloser
- func NewSSEScanner(body io.Reader) *bufio.Scanner
- func ParseRetryAfter(s string) time.Duration
- func SSEPayload(line string) (string, bool)
- func SSEReadError(provider string, err error) error
- type JSONToolResultCodec
- type ToolResultCodec
Constants ¶
const DefaultMaxResponseBytes = 32 << 20
DefaultMaxResponseBytes is the most bytes of one streamed model reply an adapter reads unless it is configured otherwise (each adapter's WithMaxResponseBytes). MaxSSELine caps one line; this caps the whole reply, so a reply of many small lines cannot grow without limit either. A line is part of the reply, so a cap below MaxSSELine also bounds every line.
const MaxSSELine = 32 << 20
MaxSSELine is the longest SSE line NewSSEScanner reads. It is sized for the largest single event a model streams: a big tool-call arguments blob, or an image Gemini returns inline as base64, which runs to several megabytes.
Variables ¶
This section is empty.
Functions ¶
func ClassifyHTTPError ¶
ClassifyHTTPError turns a non-2xx model response into the appropriate SDK error. It reads at most 64KB of the body and parses the provider's error object ({"error":{...}} from OpenAI, Anthropic, and Gemini) so the result carries the provider's message, type, and code, each (and the Body) cut to 8KB:
- The account's quota or credit is used up (HTTP 402; an error type or code of insufficient_quota, billing_hard_limit_reached, billing_not_active, or billing_error; a Gemini quota violation whose quotaId is per day): an *agent.APIError wrapping agent.ErrQuotaExhausted, whatever the status, since no wait lifts it.
- HTTP 429 otherwise: a *agent.RateLimited whose RetryAfter is the Retry-After header or, without one, Gemini's RetryInfo.retryDelay.
- Anything else: an *agent.APIError with the status code.
It reads and closes resp.Body, so call it only on a response the adapter is abandoning (not one it will go on to stream). provider names the adapter for the wrapped message.
func ClassifyStreamError ¶
ClassifyStreamError turns an error object a provider sends inside a 200 stream (an OpenAI or Gemini data line holding {"error":{...}}, an Anthropic "error" event) into an SDK error. With a numeric code (Gemini sends the HTTP status) it is classified as ClassifyHTTPError would a response with that status. Without one, an exhausted quota is an *agent.APIError wrapping agent.ErrQuotaExhausted, a rate limit (type or code rate_limit_error or rate_limit_exceeded) is a *agent.RateLimited, and anything else (overloaded, server_error) is an agent.ErrModel carrying the provider's message, which middleware.Retryable retries.
func EncodeToolResultOr ¶
func EncodeToolResultOr(c ToolResultCodec, raw json.RawMessage) string
EncodeToolResultOr applies the codec to raw and returns the result. When the codec is nil it returns the canonical JSON string (the default behavior). When the codec returns an error it degrades to the same JSON string rather than failing the whole model request: the JSON form is always a correct payload, so a codec hiccup never drops a tool result. Adapters call this at the point where a ToolResult is serialized into a provider request.
func LimitResponse ¶
func LimitResponse(body io.ReadCloser, max int64) io.ReadCloser
LimitResponse returns body limited to max bytes (DefaultMaxResponseBytes when max <= 0): once the reply has more, a read returns the bytes up to the limit and an error wrapping agent.ErrResponseTooLarge, which SSEReadError passes on. Closing the result closes body. Every adapter wraps its streamed reply with it, so the cap is enforced in one place.
func NewSSEScanner ¶
NewSSEScanner returns a bufio.Scanner over an SSE stream body with the default 64KB line cap raised to MaxSSELine. A single SSE line can carry a large tool-call arguments blob or inline image, and the 64KB default would fail the stream on it (the openai-go #368 lesson). The buffer grows only as a line needs it. Every adapter reads its event stream through this so the cap is fixed in exactly one place; pass the scanner's Err to SSEReadError. A read that fails partway through a line (a connection cut, a reply over its LimitResponse cap) ends the scan with that error before the cut-off last line is returned, so an adapter never decodes a line the failure truncated: only a clean end of the body returns a last line that has no newline.
func ParseRetryAfter ¶
ParseRetryAfter parses an HTTP Retry-After header value. It accepts either an integer number of seconds or an HTTP-date, and returns 0 if the value is absent, unparseable, negative, or already in the past. A number of seconds too large for a time.Duration saturates at the longest one rather than overflowing.
func SSEPayload ¶
SSEPayload extracts the data payload from one raw SSE line. It reports ok=false for a line that is not `data:` framing (e.g. an `event:` line or a blank separator) or whose payload is empty, both of which callers skip; otherwise it returns the payload with the "data:" prefix stripped and surrounding whitespace trimmed. It does not interpret provider sentinels such as "[DONE]".
func SSEReadError ¶
SSEReadError wraps a failed read of a model's SSE stream (the Err of a NewSSEScanner scanner) as an agent.ErrModel. A line over MaxSSELine, or a reply over its LimitResponse cap, is agent.ErrResponseTooLarge, which the same request would hit again, so middleware.Retryable does not retry it; any other read failure (a connection cut partway through) is retried.
Types ¶
type JSONToolResultCodec ¶
type JSONToolResultCodec struct{}
JSONToolResultCodec is the default codec: it passes the canonical JSON through unchanged. It is equivalent to what an adapter does when no ToolResultCodec is configured.
func (JSONToolResultCodec) EncodeToolResult ¶
func (JSONToolResultCodec) EncodeToolResult(raw json.RawMessage) (string, error)
EncodeToolResult returns the JSON bytes as a string, unchanged.
type ToolResultCodec ¶
type ToolResultCodec interface {
EncodeToolResult(raw json.RawMessage) (string, error)
}
ToolResultCodec renders a tool result's canonical JSON bytes into the string content that is sent to the model. The journal always stores the JSON form (see agent.Record.Result), so this only changes what the model reads: an adapter can hand the model a more token-efficient or better-comprehended encoding (for example GCF via github.com/bide-ai/bide/codec/gcf) without touching the durable record or the audit trail. Implementations must be pure and deterministic.