api

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Oct 29, 2026 License: Apache-2.0, MIT Imports: 27 Imported by: 0

Documentation

Overview

Package api holds the protocol-neutral request model shared by every wire protocol implementation (OpenAI Chat Completions, OpenAI Responses, Anthropic Messages, Google Generative AI, Ollama) plus the helpers those implementations share: SSE reading, the SSRF-safe transport, streaming tool-call assembly, prompt-cache bookkeeping and usage logging.

Protocol packages live under api/<protocol>; vendor facts live in internal/models/catalog and internal/models/providers. This package must not import either of them.

Index

Constants

View Source
const (
	// LanguageForm is a multipart field named language (OpenAI's shape,
	// ISO-639-1).
	LanguageForm = "form"
	// LanguageHeader is an HTTP request header named language (MiniMax,
	// BCP-47).
	LanguageHeader = "header"
	// LanguageASROptions is asr_options.language in a chat-served request
	// body (Alibaba, Xiaomi).
	LanguageASROptions = "asr_options"
)

Where a transcription protocol puts the operator's language hint. Each vendor documents one place, or none.

View Source
const MaxResponseBytes = 64 << 20

MaxResponseBytes bounds a single non-streaming provider response body.

Streaming replies are assembled incrementally and are not covered by this. It has to clear every non-streaming payload the catalog can legitimately return: an embeddings batch is bounded by BATCH_EMBED_SIZE (default 5) times the model's dimensions, and a chat reply by the model's output-token ceiling, so both sit far below this even at their largest. It is also well above the 20 MiB the older models/chat layer applies to the same replies, so nothing that worked before can be refused by it.

Variables

View Source
var (
	DefaultChatTimeout   = envDurationSeconds("WEKNORA_LLM_CHAT_TIMEOUT_SECONDS", 300*time.Second)
	DefaultStreamTimeout = envDurationSeconds("WEKNORA_LLM_STREAM_TIMEOUT_SECONDS", 600*time.Second)
)

LLM 调用超时配置。仅作为"上层未设置 deadline 时"的兜底,避免 hung 请求 永久阻塞 worker。如果上层 ctx 已经设置了 deadline(无论比默认更短还是更长), 都会原样尊重,不再叠加默认超时。可通过环境变量覆盖:

  • WEKNORA_LLM_CHAT_TIMEOUT_SECONDS 非流式调用兜底超时(默认 600s)
  • WEKNORA_LLM_STREAM_TIMEOUT_SECONDS 流式调用兜底超时(默认 1800s)

AllReasoningEfforts is every accepted value, in UI order.

View Source
var ErrResponseTooLarge = errors.New("response exceeds maximum size")

ErrResponseTooLarge marks a body over the cap. It is deterministic: the same request returns the same oversized body, so a retry cannot help.

View Source
var HTTPClient = secutils.NewSSRFSafeHTTPClientWithTransport(
	secutils.SSRFSafeHTTPClientConfig{Timeout: 0, MaxRedirects: 10},
	httpTransport,
)

HTTPClient is the shared SSRF-safe client every protocol uses.

View Source
var LocalImageResolver func(storageURL string) ([]byte, bool)

LocalImageResolver resolves a resource:// or provider storage URL to bytes using the owning tenant's storage config. The application layer sets it at startup. Stored local:// URLs are relative to the storage base dir and do NOT encode the tenant's configured PathPrefix, so a plain env-based join would miss the prefix. When nil (e.g. in tests), callers fall back to the env-based LOCAL_STORAGE_BASE_DIR resolution below.

ReasoningLadder lists the graded levels from weakest to strongest. Off and Auto are deliberately excluded: they are switches, not intensities.

Functions

func ApplyCacheControlBreakpoints

func ApplyCacheControlBreakpoints(payload map[string]any, marker *CacheControlMarker)

ApplyCacheControlBreakpoints injects Anthropic-style cache_control markers into an OpenAI-compatible JSON body: the first instruction message, the last tool definition and the last conversation message.

func AttachSessionAffinityHeaders

func AttachSessionAffinityHeaders(req *http.Request, sessionID string)

AttachSessionAffinityHeaders sets the sticky-routing headers OpenAI-style gateways use to keep one conversation on one cache shard.

func BuildPromptCacheKey

func BuildPromptCacheKey(tenantID uint64, modelID, purpose, prefixFingerprint string) string

BuildPromptCacheKey derives an opaque process-local coordination key. Tenant and model identifiers are hashed rather than retained in memory.

func ClampPromptCacheKey

func ClampPromptCacheKey(key string) string

ClampPromptCacheKey trims a routing key to the 64 characters OpenAI accepts.

func EncodeMultipart

func EncodeMultipart(fields []FormField, file FormFile) ([]byte, string, error)

EncodeMultipart writes fields and one file into a multipart/form-data body and returns it with its Content-Type. It is encoded once: those bytes are what a signing vendor hashes and what every retry sends again.

The file part carries a filename and no Content-Type of its own. Servers identify the format from the extension then — OpenAI asks for "an extension-bearing filename", and vox-box (GPUStack's audio backend) falls back to guessing from it — whereas a guessed type that a server does not list is rejected outright.

func FingerprintPromptPrefix

func FingerprintPromptPrefix(parts ...string) string

FingerprintPromptPrefix returns a short, non-reversible identifier suitable for logs and cache routing. Raw prompts must never be used as metric labels.

func IsMultimodalNotSupportedError

func IsMultimodalNotSupportedError(err error) bool

IsMultimodalNotSupportedError checks if an error indicates the model does not support multimodal/image input.

The status is read off the error, never matched as text: HTTPError.Error() prints "status 400" for every 400, and vendor bodies carry "400" inside unrelated numbers ("max 4000 px"), so a substring test turned any failure that merely mentions an image into a "model is not multimodal" verdict and silently retried it stripped.

func IsSandboxMutationTool

func IsSandboxMutationTool(name string) bool

IsSandboxMutationTool reports whether a tool name writes sandbox files.

func LogRequest

func LogRequest(ctx context.Context, url, model string, body []byte, stream bool)

LogRequest emits the standard request log line with image payloads compacted so data URIs do not flood the log.

func LogUsage

func LogUsage(ctx context.Context, model string, u *types.TokenUsage)

LogUsage emits the standard "[LLM Usage]" line shared by every Chat implementation. It is a no-op when usage is nil so callers can pass through optional usage blocks without guarding at each call site.

func PlaceEmbeddings

func PlaceEmbeddings(want, got int, at func(i int) (int, []float32)) ([][]float32, error)

PlaceEmbeddings puts returned vectors back in the order the texts were sent, using the index each vendor reports, and refuses a response that does not cover every input.

Both halves matter. A vendor is free to answer out of order, so the index has to be honoured rather than assumed — and reading the wrong field for it silently collapses a whole batch onto slot 0, which is what Tencent/WeKnora#3484 was. An unfilled slot then reaches the index as an empty vector, where it is stored and poisons retrieval without anything failing, so a gap is an error instead. So is an index reported twice: one of the two vectors belongs to some other input.

func PromptPrefixFingerprint

func PromptPrefixFingerprint(messages []Message, opts *Options) string

PromptPrefixFingerprint hashes the stable portion common to normal chat and agent requests: leading system messages plus the deterministic tool schema. Dynamic conversation/user messages intentionally do not participate.

func Ptr

func Ptr[T any](value T) *T

Ptr returns a pointer for optional protocol settings.

func ReadResponseCapped added in v0.9.0

func ReadResponseCapped(body io.Reader, limit int64) ([]byte, error)

ReadResponseCapped reads a non-streaming response body, refusing anything larger than limit instead of buffering it. Oversized payloads are reported as an error rather than truncated, because a truncated body would otherwise be decoded as if it were the whole reply - producing a short answer or a plausible-looking partial embedding instead of an error. A non-positive limit falls back to MaxResponseBytes.

Endpoint.Do already bounds the error body; this is the same guard for the success body, which until now was read whole.

func ResolveImageURLForLLM

func ResolveImageURLForLLM(imageURL string) string

ResolveImageURLForLLM converts stored image paths to a format that LLM APIs can consume. - data: URIs and http(s):// URLs are returned as-is. - resource:// references and provider:// paths are read through the application resolver and converted to base64 data URIs.

func ResolveImageURLForOllama

func ResolveImageURLForOllama(imageURL string) []byte

ResolveImageURLForOllama converts stored image paths to raw bytes for the Ollama API.

func SignatureFor

func SignatureFor(protocol API, tagged string) string

SignatureFor returns the raw signature when it was issued by protocol, and "" for signatures from another protocol or without a protocol tag.

func StringPtr

func StringPtr(s string) *string

StringPtr is a small helper for building ThinkingLevelMap literals.

func TagSignature

func TagSignature(protocol API, signature string) string

TagSignature records which protocol issued a reasoning signature. Stored signatures are opaque to every other provider, so they must only be replayed to the protocol that produced them.

func TruncateForDebug

func TruncateForDebug(s string, maxRunes int) string

TruncateForDebug shortens a string for log previews.

func WithLLMTimeout

func WithLLMTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc)

WithLLMTimeout 仅在上层 ctx 没有 deadline 时附加一个兜底超时; 如果上层已显式设置 deadline(无论更短或更长),则原样返回, 让调用方对自己的超时策略拥有最终决定权。

Types

type API

type API string

API identifies a wire protocol. It is the value of the "api" field in a vendor's models.json and decides which protocol package talks to the endpoint.

const (
	// APIOpenAICompletions is the OpenAI Chat Completions protocol, the
	// lingua franca of almost every third-party vendor.
	APIOpenAICompletions API = "openai-completions"
	// APIOpenAIResponses is the OpenAI Responses protocol.
	APIOpenAIResponses API = "openai-responses"
	// APIAnthropicMessages is the Anthropic Messages protocol, also exposed by
	// MiniMax, Zhipu and Moonshot as a compatibility surface.
	APIAnthropicMessages API = "anthropic-messages"
	// APIGoogleGenerativeAI is the native Gemini generateContent protocol.
	APIGoogleGenerativeAI API = "google-generative-ai"
	// APIOllama is the local Ollama chat protocol.
	APIOllama API = "ollama"
)

func (API) Known

func (a API) Known() bool

Known reports whether the API value names a protocol this build ships.

type AnthropicMessagesCompat

type AnthropicMessagesCompat struct {
	ThinkingMode               *AnthropicThinkingMode  `json:"thinking_mode,omitempty"`
	ThinkingOffType            *string                 `json:"thinking_off_type,omitempty"`
	SupportsEffort             *bool                   `json:"supports_effort,omitempty"`
	ThinkingBudgets            map[ReasoningEffort]int `json:"thinking_budgets,omitempty"`
	DefaultMaxTokens           *int                    `json:"default_max_tokens,omitempty"`
	SupportsTemperature        *bool                   `json:"supports_temperature,omitempty"`
	TemperatureWithThinking    *bool                   `json:"temperature_with_thinking,omitempty"`
	SupportsTopP               *bool                   `json:"supports_top_p,omitempty"`
	SupportsCacheControl       *bool                   `json:"supports_cache_control,omitempty"`
	SupportsCacheControlOnTool *bool                   `json:"supports_cache_control_on_tools,omitempty"`
	LongCacheTTL               *string                 `json:"long_cache_ttl,omitempty"`
	Version                    *string                 `json:"version,omitempty"`
	BetaHeaders                []string                `json:"beta_headers,omitempty"`
	InterleavedThinkingBeta    *string                 `json:"interleaved_thinking_beta,omitempty"`
	PromptCacheAccounting      *bool                   `json:"prompt_cache_accounting,omitempty"`
	ExtraBody                  map[string]any          `json:"extra_body,omitempty"`
}

AnthropicMessagesCompat is the overlay for the Messages protocol.

type AnthropicMessagesSettings

type AnthropicMessagesSettings struct {
	ThinkingMode AnthropicThinkingMode
	// ThinkingOffType is the thinking.type sent to turn thinking off in
	// adaptive mode; empty means "disabled". Claude Sonnet 5.5 rejects
	// "disabled" and turns off up-front thinking with "between_tools".
	ThinkingOffType string `json:",omitempty"`
	SupportsEffort  bool
	// ThinkingBudgets maps graded levels to budget_tokens for budget mode.
	ThinkingBudgets map[ReasoningEffort]int
	// DefaultMaxTokens is used when the caller set no budget (max_tokens is
	// mandatory on this protocol).
	DefaultMaxTokens           int
	SupportsTemperature        bool
	TemperatureWithThinking    bool
	SupportsTopP               bool
	SupportsCacheControl       bool
	SupportsCacheControlOnTool bool
	LongCacheTTL               string
	Version                    string
	BetaHeaders                []string
	// InterleavedThinkingBeta is added to anthropic-beta when thinking and
	// tools are both active (budget mode only). Empty disables it.
	InterleavedThinkingBeta string
	PromptCacheAccounting   bool
	ExtraBody               map[string]any
}

AnthropicMessagesSettings is the resolved Messages configuration.

func DefaultAnthropicMessages

func DefaultAnthropicMessages() AnthropicMessagesSettings

DefaultAnthropicMessages is the anthropic.com baseline for the budget-thinking generation of models.

type AnthropicThinkingMode

type AnthropicThinkingMode string

AnthropicThinkingMode selects the thinking request shape.

const (
	// AnthropicThinkingBudget sends {"type":"enabled","budget_tokens":N}.
	AnthropicThinkingBudget AnthropicThinkingMode = "budget"
	// AnthropicThinkingAdaptive sends {"type":"adaptive"} and steers the
	// intensity through output_config.effort.
	AnthropicThinkingAdaptive AnthropicThinkingMode = "adaptive"
)

type AuthFunc

type AuthFunc func(req *http.Request, body []byte)

AuthFunc stamps credentials onto an outbound request. body is the exact bytes being sent so signing vendors can hash them.

func BearerAuth

func BearerAuth(apiKey string) AuthFunc

BearerAuth is the default: Authorization: Bearer <key>.

func HeaderAuth

func HeaderAuth(header, apiKey string) AuthFunc

HeaderAuth writes the key under an arbitrary header (api-key for Azure, x-api-key for Anthropic, x-goog-api-key for Gemini).

func NoAuth

func NoAuth() AuthFunc

NoAuth sends no credentials at all (local deployments).

type Batch

type Batch struct {
	Start int
	Items []string
}

Batch is one slice of the input, and where it started.

func SplitBatches

func SplitBatches(items []string, fixedRunes int, limits BatchLimits) ([]Batch, error)

SplitBatches divides items into requests that satisfy limits. fixedRunes is charged to every batch (the query, which each request repeats).

A single item that cannot fit on its own is an error rather than a silent truncation: the caller asked for this document to be scored, and scoring a prefix of it returns a number for something the user never sent.

type BatchLimits

type BatchLimits struct {
	// MaxItems caps how many documents one request may carry.
	MaxItems int
	// MaxItemRunes caps a single document. Vendors state these in characters
	// or tokens; runes are the closest thing we can count without a
	// tokenizer, and undercounting is the safe direction.
	MaxItemRunes int
	// MaxTotalRunes caps the whole request, query included.
	MaxTotalRunes int
}

BatchLimits are the per-request ceilings a vendor documents. A zero field means the vendor states no limit, and nothing is enforced for it.

type CacheControlMarker

type CacheControlMarker struct {
	Type string `json:"type"`
	TTL  string `json:"ttl,omitempty"`
}

CacheControlMarker is the Anthropic-style cache breakpoint object that several OpenAI-compatible gateways (OpenRouter, DashScope) also accept.

func CacheControlFor

func CacheControlFor(retention CacheRetention, longTTL string) *CacheControlMarker

CacheControlFor builds the marker for a retention preference, or nil when the caller disabled caching for this request.

type CacheRetention

type CacheRetention string

CacheRetention is the prompt-cache TTL preference. Empty means short (the default 5-minute provider cache). Compaction/summarization uses none so a different prompt prefix does not occupy the session's cache slot.

const (
	CacheRetentionNone  CacheRetention = "none"
	CacheRetentionShort CacheRetention = "short"
	CacheRetentionLong  CacheRetention = "long"
)

Cache retention preferences.

func ResolveCacheRetention

func ResolveCacheRetention(opts *Options) CacheRetention

ResolveCacheRetention returns the caller's retention preference, defaulting to the provider's short cache.

type Credentials

type Credentials struct {
	APIKey    string
	AppID     string
	AppSecret string
}

Credentials is what the operator stored for one model row.

type Delta

type Delta struct {
	Reasoning    string
	Content      string
	ToolCalls    []ToolCallDelta
	FinishReason string
}

Delta is one protocol-neutral streaming event. A single wire chunk may carry several of these fields at once.

type EmbedImage

type EmbedImage struct {
	Data     []byte
	MIMEType string
}

EmbedImage is one image to embed. It travels inline rather than as a URL: the object store WeKnora reads images from is usually not reachable from the vendor, and every multimodal vendor in the catalog accepts base64.

func (EmbedImage) Base64

func (i EmbedImage) Base64() string

Base64 is the image in standard base64, Gemini's inlineData form.

func (EmbedImage) DataURI

func (i EmbedImage) DataURI() string

DataURI is the data:{mime};base64,{data} form Ark, DashScope and Jina document.

type EmbedInputType

type EmbedInputType string

EmbedInputType distinguishes the two sides of a retrieval pair. Several vendors score them differently and want to be told which one they are embedding; the field they want it in differs, so the catalog names it.

const (
	// EmbedDocument is text being indexed.
	EmbedDocument EmbedInputType = "document"
	// EmbedQuery is text being searched with.
	EmbedQuery EmbedInputType = "query"
)

type Embedder

type Embedder interface {
	Embed(ctx context.Context, texts []string, kind EmbedInputType) ([][]float32, error)
}

Embedder is what every embedding protocol package implements. Callers pass the whole batch; splitting it to the vendor's documented ceiling is the shared layer's job, not the protocol's.

type EmbeddingAPI

type EmbeddingAPI string

EmbeddingAPI names an embedding wire protocol. Like RerankAPI it is its own type, so a chat row's extra_config.api cannot name an embedding dialect and pass validation.

const (
	// EmbeddingOpenAI is POST {base}/embeddings with {model, input[]} answering
	// {data: [{embedding, index}]} — the shape OpenAI defined and every
	// compatible gateway copied. Jina, NVIDIA, Azure and WeKnora Cloud speak
	// it too, with their own optional fields, URL or credential.
	EmbeddingOpenAI EmbeddingAPI = "openai-embeddings"
	// EmbeddingDashScope is Alibaba Model Studio's native multimodal shape:
	// input.contents, parameters.dimension, output.embeddings.
	EmbeddingDashScope EmbeddingAPI = "dashscope-embeddings"
	// EmbeddingArk is Volcengine Ark's multimodal shape, which fuses every
	// input into one vector and therefore embeds one text per request.
	EmbeddingArk EmbeddingAPI = "ark-embeddings"
	// EmbeddingGoogle is Gemini's batchEmbedContents.
	EmbeddingGoogle EmbeddingAPI = "google-embeddings"
)

The embedding protocols WeKnora speaks.

func (EmbeddingAPI) Known

func (a EmbeddingAPI) Known() bool

Known reports whether the value names a protocol this build implements.

type EmbeddingImageFormat

type EmbeddingImageFormat string

EmbeddingImageFormat is how an image travels on the OpenAI embedding shape, which has no standard image input of its own.

const (
	// EmbeddingImageObject puts {ImageField: data URI} in `input`, as Jina
	// and SGLang document.
	EmbeddingImageObject EmbeddingImageFormat = "object"
	// EmbeddingImageMessages sends a chat conversation in `messages` instead
	// of `input`, which is how vLLM serves multimodal embedding models
	// (https://docs.vllm.ai/en/latest/models/pooling_models/embed/). Each
	// conversation yields one vector; BatchMessages can batch conversations.
	EmbeddingImageMessages EmbeddingImageFormat = "messages"
)

func (EmbeddingImageFormat) Known

func (f EmbeddingImageFormat) Known() bool

Known reports whether f is a format this build can send; empty is known.

type EmbeddingsCompat

type EmbeddingsCompat struct {
	// API overrides the vendor's embedding protocol for one model. Aliyun and
	// Volcengine each serve two: a text endpoint in the OpenAI shape and a
	// multimodal one of their own, chosen by which model the row names.
	API                *EmbeddingAPI     `json:"api,omitempty"`
	Path               *string           `json:"path,omitempty"`
	SendEncodingFormat *bool             `json:"send_encoding_format,omitempty"`
	DimensionsField    *string           `json:"dimensions_field,omitempty"`
	TruncateField      *string           `json:"truncate_field,omitempty"`
	TruncateValue      *string           `json:"truncate_value,omitempty"`
	InputTypeField     *string           `json:"input_type_field,omitempty"`
	InputTypeValues    map[string]string `json:"input_type_values,omitempty"`
	MaxBatchSize       *int              `json:"max_batch_size,omitempty"`
	MaxInputChars      *int              `json:"max_input_chars,omitempty"`
	// AcceptsTruncatePromptTokens marks a vLLM-class runtime, the only kind
	// that implements the `truncate_prompt_tokens` extension.
	AcceptsTruncatePromptTokens *bool                 `json:"accepts_truncate_prompt_tokens,omitempty"`
	RequestTimeout              *int                  `json:"request_timeout_seconds,omitempty"`
	ExtraBody                   map[string]any        `json:"extra_body,omitempty"`
	ImageField                  *string               `json:"image_field,omitempty"`
	MaxImageBatchSize           *int                  `json:"max_image_batch_size,omitempty"`
	MaxImageBytes               *int                  `json:"max_image_bytes,omitempty"`
	ImageMIMETypes              []string              `json:"image_mime_types,omitempty"`
	ImageFormat                 *EmbeddingImageFormat `json:"image_format,omitempty"`
	ImagePrompt                 *string               `json:"image_prompt,omitempty"`
	TextAsMessages              *bool                 `json:"text_as_messages,omitempty"`
	BatchMessages               *bool                 `json:"batch_messages,omitempty"`
}

EmbeddingsCompat is the overlay form (every field optional) of the embedding settings. JSON keys are the documented names used in models.json and config/models.json.

type EmbeddingsSettings

type EmbeddingsSettings struct {
	// API is the protocol this model speaks, which may differ from the
	// vendor's default.
	API EmbeddingAPI
	// Path is appended to the base URL; vendors whose default base URL names
	// the full endpoint leave it empty.
	Path string
	// SendEncodingFormat sends encoding_format: "float". OpenAI documents it;
	// several compatible gateways do not list it at all.
	SendEncodingFormat bool
	// DimensionsField carries the requested vector width: "dimensions" on the
	// OpenAI shape, "dimension" on DashScope (nested under parameters),
	// "outputDimensionality" on Gemini, and empty where the vendor does not
	// let a request choose. It is sent only when the row opted in with
	// supports_dimension_override; the field name is the vendor's fact, the
	// decision to narrow the vector is the operator's.
	DimensionsField string
	// TruncateField and TruncateValue are the vendor's server-side truncation
	// switch for over-long input: a boolean on Jina, an enum on NIM. Empty
	// sends nothing.
	TruncateField string
	TruncateValue string
	// InputTypeField names the parameter that distinguishes an indexed
	// document from a search query, and InputTypeValues maps the neutral
	// EmbedInputType values onto the vendor's vocabulary. Asymmetric
	// models score the two sides differently and are measurably worse when
	// both are embedded the same way.
	InputTypeField  string
	InputTypeValues map[string]string
	// MaxBatchSize and MaxInputChars are the documented per-request ceilings.
	MaxBatchSize  int
	MaxInputChars int
	// AcceptsTruncatePromptTokens reports a vLLM-class runtime. The extension
	// appears in no managed vendor's schema, so it must never be sent to one.
	AcceptsTruncatePromptTokens bool
	// TruncatePromptTokens is the budget the operator opted into on this row,
	// honoured only where the vendor accepts the extension.
	TruncatePromptTokens int
	// RequestTimeout caps one request, in seconds. 0 leaves the client
	// without its own deadline and lets the caller's context govern.
	RequestTimeout int
	ExtraBody      map[string]any
	// ImageInput is how this endpoint takes images: on the OpenAI shape, the
	// key of the input object that carries one ("image" on Jina; that shape
	// has no standard image input, so an endpoint that names none takes no
	// images), plus the documented per-request and per-image limits.
	ImageInput
	// ImageFormat chooses between ImageField's object in `input` and a chat
	// conversation in `messages`. Empty means object when ImageField is set.
	ImageFormat EmbeddingImageFormat `json:",omitempty"`
	// ImagePrompt is the text sent beside each image in the messages format;
	// some chat templates (VLM2Vec's) expect an instruction there. Empty
	// sends the image alone.
	ImagePrompt string `json:",omitempty"`
	// TextAsMessages sends texts in the messages format too. A model whose
	// chat template frames its inputs only places text and images in the same
	// space when both pass through it; a plain `input` skips the template.
	TextAsMessages bool `json:",omitempty"`
	// BatchMessages enables independent conversations in a two-dimensional
	// messages array (vLLM 0.29 supports this). False preserves the one-input
	// request shape for older servers. Single inputs always keep that shape.
	BatchMessages bool `json:",omitempty"`
}

EmbeddingsSettings is the resolved (fully defaulted) form.

func DefaultEmbeddings

func DefaultEmbeddings() EmbeddingsSettings

DefaultEmbeddings is the protocol baseline, and it is deliberately bare: `model` and `input` are the only two fields every vendor in this catalog documents. The rest disagree — NVIDIA NIM and Volcengine's text endpoint have no `dimensions` at all, Zhipu has no `encoding_format`, and the parameter that separates a query from a document is spelled three different ways. A vendor that documents one declares it; nothing is sent on the assumption that an OpenAI-shaped endpoint accepts every OpenAI field.

The one non-wire default is the request deadline: every pre-catalog embedding client carried 60 seconds.

func (EmbeddingsSettings) BatchLimits

func (s EmbeddingsSettings) BatchLimits() BatchLimits

BatchLimits renders the documented ceilings for SplitBatches.

func (EmbeddingsSettings) InputTypeValue

func (s EmbeddingsSettings) InputTypeValue(kind EmbedInputType) string

InputTypeValue maps a neutral input kind onto the vendor's vocabulary, returning "" when this vendor does not distinguish the two sides.

func (EmbeddingsSettings) OpenAIImageFormat

func (s EmbeddingsSettings) OpenAIImageFormat() EmbeddingImageFormat

OpenAIImageFormat is the effective image format on the OpenAI shape, empty when the endpoint takes no images there.

type Endpoint

type Endpoint struct {
	// BaseURL is the vendor root without a trailing slash.
	BaseURL string
	// URL, when set, is the complete request URL and overrides the protocol
	// default of BaseURL + protocol path. Vendors with non-standard paths
	// (Azure deployments, WeKnora Cloud) set it.
	URL string
	// Query is appended to the final URL (Azure api-version).
	Query map[string]string
	// Model is the name sent on the wire; ModelID is WeKnora's own identifier.
	Model   string
	ModelID string
	// Auth stamps credentials; Headers are static extra headers (vendor
	// betas, user-configured custom headers). User headers never override
	// the protocol's own reserved headers.
	Auth    AuthFunc
	Headers map[string]string
	// Client is the HTTP client; nil uses the shared SSRF-safe client.
	Client *http.Client
}

Endpoint is everything a protocol client needs to reach one model.

func (Endpoint) Do

func (e Endpoint) Do(req *http.Request) (*http.Response, error)

Do sends the request and returns the response. Non-2xx responses are turned into an error carrying the body so callers surface vendor messages.

func (Endpoint) NewRequest

func (e Endpoint) NewRequest(ctx context.Context, url string, body any, stream bool) (*http.Request, []byte, error)

NewRequest prepares an authenticated JSON POST. The body is marshalled once so signing vendors see exactly what is sent.

func (Endpoint) PostJSON

func (e Endpoint) PostJSON(ctx context.Context, url string, body, out any) error

PostJSON sends one authenticated JSON request and decodes the reply into out. It is the whole round-trip for the non-streaming protocols (rerank, embeddings), which have no SSE to assemble.

It never returns a nil error with nothing decoded: every failure path — marshalling, SSRF, transport, non-2xx, decoding — returns an error, so a caller cannot mistake an empty result for a successful empty answer.

func (Endpoint) PostJSONWithRetry

func (e Endpoint) PostJSONWithRetry(
	ctx context.Context, url string, body, out any, policy RetryPolicy, label string,
) error

PostJSONWithRetry sends one authenticated JSON request, retrying transport failures, and decodes the reply into out.

It never returns a nil error without decoding: the eight hand-written retry loops this replaces kept `resp` and `err` in the enclosing scope and returned `(nil, err)` at the end, so writing `req, err := ...` inside the loop shadowed the error, left the outer one nil, and handed the caller a nil response with no error to check — a nil-pointer panic that took the process down instead of reporting "connection refused" (Tencent/WeKnora#3484). Here the last error is captured in the enclosing scope on purpose and the function cannot fall through without it.

Only transport failures are retried, including a reply cut off while it was being read. A non-2xx reply is the vendor answering, and repeating a rejected request neither fixes it nor tells the operator anything new; a reply that arrived whole but does not decode is the same answer every time, and sending it again would bill the request twice.

func (Endpoint) PostMultipartWithRetry

func (e Endpoint) PostMultipartWithRetry(
	ctx context.Context, url string, body []byte, contentType string,
	out any, policy RetryPolicy, label string,
) error

PostMultipartWithRetry sends an encoded multipart body, retrying transport failures only, and decodes the JSON reply into out. It has the guarantees of PostJSONWithRetry.

func (Endpoint) Resolve

func (e Endpoint) Resolve(defaultPath string) string

Resolve builds the request URL from the endpoint description.

type FormField

type FormField struct {
	Name  string
	Value string
}

FormField is one text part of a multipart form. Order is kept.

type FormFile

type FormFile struct {
	Field    string
	FileName string
	Data     []byte
}

FormFile is the file part of a multipart form.

type FunctionCall

type FunctionCall struct {
	Name      string `json:"name"`
	Arguments string `json:"arguments"` // JSON string
}

FunctionCall represents a function call

type FunctionDef

type FunctionDef struct {
	Name        string          `json:"name"`
	Description string          `json:"description"`
	Parameters  json.RawMessage `json:"parameters"`
}

FunctionDef represents a function definition

type GoogleGenerativeAICompat

type GoogleGenerativeAICompat struct {
	ThinkingMode     *GoogleThinkingMode     `json:"thinking_mode,omitempty"`
	ThinkingBudgets  map[ReasoningEffort]int `json:"thinking_budgets,omitempty"`
	IncludeThoughts  *bool                   `json:"include_thoughts,omitempty"`
	SupportsSeed     *bool                   `json:"supports_seed,omitempty"`
	SupportsPenalty  *bool                   `json:"supports_penalty,omitempty"`
	ExtraGeneration  map[string]any          `json:"extra_generation_config,omitempty"`
	PromptCacheAcct  *bool                   `json:"prompt_cache_accounting,omitempty"`
	APIVersionPrefix *string                 `json:"api_version_prefix,omitempty"`
}

GoogleGenerativeAICompat is the overlay for the Gemini protocol.

type GoogleGenerativeAISettings

type GoogleGenerativeAISettings struct {
	ThinkingMode    GoogleThinkingMode
	ThinkingBudgets map[ReasoningEffort]int
	IncludeThoughts bool
	SupportsSeed    bool
	SupportsPenalty bool
	ExtraGeneration map[string]any
	PromptCacheAcct bool
	// APIVersionPrefix is appended to a bare host base URL ("/v1beta").
	APIVersionPrefix string
}

GoogleGenerativeAISettings is the resolved Gemini configuration.

func DefaultGoogleGenerativeAI

func DefaultGoogleGenerativeAI() GoogleGenerativeAISettings

DefaultGoogleGenerativeAI is the generativelanguage.googleapis.com baseline.

type GoogleThinkingMode

type GoogleThinkingMode string

GoogleThinkingMode selects the thinkingConfig shape.

const (
	// GoogleThinkingBudget sends thinkingConfig.thinkingBudget (Gemini 2.5).
	GoogleThinkingBudget GoogleThinkingMode = "budget"
	// GoogleThinkingLevel sends thinkingConfig.thinkingLevel (Gemini 3+).
	GoogleThinkingLevel GoogleThinkingMode = "level"
	// GoogleThinkingNone sends no thinkingConfig.
	GoogleThinkingNone GoogleThinkingMode = "none"
)

type HTTPError

type HTTPError struct {
	StatusCode int
	Body       string
	// Header is the reply's headers, kept so a retry can honour the vendor's
	// Retry-After on a 429 or 503 instead of guessing its own backoff.
	Header http.Header
}

HTTPError is a non-2xx vendor reply.

func (*HTTPError) Error

func (e *HTTPError) Error() string

func (*HTTPError) RetryAfter

func (e *HTTPError) RetryAfter() time.Duration

RetryAfter is how long the vendor asked the caller to wait before trying again, from the Retry-After header in either of its two forms (delay in seconds or an HTTP date). It is zero when the header is absent, malformed or already in the past.

type ImageEmbedder

type ImageEmbedder interface {
	// AcceptsImages reports whether this endpoint can carry an image at all.
	AcceptsImages() bool
	EmbedImages(ctx context.Context, images []EmbedImage, kind EmbedInputType) ([][]float32, error)
}

ImageEmbedder is implemented by the protocols that can carry an image. Whether a given model maps images into the space of its text vectors is the catalog entry's input, not the protocol's, so the caller checks that first. Callers pass a batch within EmbeddingsSettings.ImageBatchLimit.

type ImageInput

type ImageInput struct {
	// ImageField is the key of the object that carries an image on a shape
	// with no standard image input ("image" on Jina). Protocols whose schema
	// fixes the image part ignore it.
	ImageField string `json:",omitempty"`
	// MaxImageBatchSize is the documented number of images per request.
	// 0 means one: a vendor that fuses its inputs, or does not say, must not
	// be sent several.
	MaxImageBatchSize int `json:",omitempty"`
	// MaxImageBytes is the documented size limit of one image, 0 when the
	// vendor states none. The caller shrinks an image to fit; the client
	// refuses one that does not rather than spend a request on it.
	MaxImageBytes int `json:",omitempty"`
	// ImageMIMETypes lists the formats the vendor documents, empty when it
	// takes any common one.
	ImageMIMETypes []string `json:",omitempty"`
}

ImageInput is how an endpoint takes images, shared by the embedding and rerank settings. Its fields are flattened into both, so the compat keys read the same on either: image_field, max_image_batch_size, max_image_bytes and image_mime_types.

func (ImageInput) CheckImage

func (s ImageInput) CheckImage(img EmbedImage) error

CheckImage reports an image the vendor documents it will not take.

func (ImageInput) ImageBatchLimit

func (s ImageInput) ImageBatchLimit() int

ImageBatchLimit is the number of images one request may carry.

type ImageReranker

type ImageReranker interface {
	// AcceptsImages reports whether this endpoint can carry an image at all.
	AcceptsImages() bool
	RerankImages(ctx context.Context, query string, images []EmbedImage) ([]RerankResult, error)
}

ImageReranker is implemented by the rerank protocols that can carry an image document. Whether a given model scores images is its catalog entry's input, not the protocol's, so the caller checks that first. The images go in a request of their own, at most RerankSettings.ImageBatchLimit of them; result indices refer to the images slice.

type ImageURL

type ImageURL struct {
	URL    string `json:"url"`              // URL or base64 data URI
	Detail string `json:"detail,omitempty"` // "auto", "low", "high"
}

ImageURL represents the image URL structure

type JSONFieldExtractor

type JSONFieldExtractor struct {
	// contains filtered or unexported fields
}

JSONFieldExtractor extracts a specific string field value from streaming JSON fragments. It processes incremental JSON argument chunks from LLM tool calls.

Example: for fieldName="answer", expected JSON format: {"answer":"...content..."} The extractor uses a simple state machine to skip the JSON prefix and extract the string value.

func NewJSONFieldExtractor

func NewJSONFieldExtractor(fieldName string) *JSONFieldExtractor

NewJSONFieldExtractor creates a new extractor instance for the given field name

func (*JSONFieldExtractor) Feed

func (e *JSONFieldExtractor) Feed(argsDelta string) string

Feed processes a new argument delta and returns any new content to emit. Returns empty string if no new content is available yet.

func (*JSONFieldExtractor) IsDone

func (e *JSONFieldExtractor) IsDone() bool

IsDone returns whether the extractor has finished (closing quote found)

type Message

type Message struct {
	Role         string               `json:"role"`                    // 角色:system, user, assistant, tool
	Content      string               `json:"content"`                 // 消息内容
	MultiContent []MessageContentPart `json:"multi_content,omitempty"` // 多内容消息(文本+图片)
	Name         string               `json:"name,omitempty"`          // Function/tool name (for tool role)
	ToolCallID   string               `json:"tool_call_id,omitempty"`  // Tool call ID (for tool role)
	ToolCalls    []ToolCall           `json:"tool_calls,omitempty"`    // Tool calls (for assistant role)
	// Images are image URLs for multimodal input (current user message only).
	Images []string `json:"images,omitempty"`
	// ReasoningContent 是 assistant 推理类模型上一轮输出的思考内容。部分供应商
	// (MiMo、DeepSeek V3.2+、Kimi K2.5+)要求多轮对话中把 assistant 的
	// reasoning_content 原样回传,否则会以 400 拒绝请求;其他不要求的供应商会
	// 忽略未知字段。Anthropic 则要求把 thinking block 连同签名一起回放。
	ReasoningContent string `json:"reasoning_content,omitempty"`
	// ReasoningSignature is the provider-issued signature that must accompany
	// ReasoningContent when it is replayed (Anthropic thinking blocks, Gemini
	// thought signatures on text parts). Empty when the provider issues none.
	ReasoningSignature string `json:"reasoning_signature,omitempty"`
	// ReasoningMetadata carries opaque provider state that must round-trip
	// with the assistant turn but has no protocol-neutral representation:
	// OpenAI Responses reasoning items (encrypted_content), OpenRouter
	// reasoning_details, and so on. Keyed by protocol / vendor namespace.
	ReasoningMetadata types.ProviderMetadata `json:"reasoning_metadata,omitempty"`
	// Kind is engine-internal bookkeeping. `json:"-"` keeps it off the wire:
	// providers reject unknown message fields on some endpoints, and this one
	// means nothing to them anyway.
	Kind MessageKind `json:"-"`
	// TurnID is the stored assistant message whose turn this history message
	// was replayed from. It is empty for the live turn and for messages the
	// engine synthesized. Compaction uses it to tell whether a summary ends
	// exactly on a stored turn, the only kind it can persist for later turns.
	// Engine-internal like Kind, and kept off the wire for the same reason.
	TurnID string `json:"-"`
}

Message 表示聊天消息

func NeutralizeMessageSpecialTokens

func NeutralizeMessageSpecialTokens(message Message) Message

NeutralizeMessageSpecialTokens returns a copy of the message with chat template delimiters broken so tokenizers treat them as data.

func StripImagesFromMessages

func StripImagesFromMessages(messages []Message) []Message

StripImagesFromMessages returns a copy of messages with all image data removed.

Images travel in two shapes: the Images list (chat history, Ollama) and image_url parts inside MultiContent, which is what the remote VLM path and every assembled multimodal turn actually send. Clearing only the first left the retry sending the identical request, so the "retry without images" fallback never recovered anything on those paths.

type MessageContentPart

type MessageContentPart struct {
	Type     string    `json:"type"`                // "text" or "image_url"
	Text     string    `json:"text,omitempty"`      // For type="text"
	ImageURL *ImageURL `json:"image_url,omitempty"` // For type="image_url"
}

MessageContentPart represents a part of multi-content message

type MessageKind

type MessageKind string

MessageKind marks messages the engine synthesized rather than received from the user or the model. Compaction needs to tell its own summary apart from a real user turn: a summary that looks like ordinary history gets fed back into the next summarization pass and degrades into a summary of a summary.

const MessageKindCompactionSummary MessageKind = "compaction_summary"

MessageKindCompactionSummary marks the message that replaces compacted history. It carries the `user` role because that is where providers expect conversation history, so the role alone cannot identify it.

type OpenAICompletionsCompat

type OpenAICompletionsCompat struct {
	MaxTokensField               *string         `json:"max_tokens_field,omitempty"`
	ThinkingFormat               *ThinkingFormat `json:"thinking_format,omitempty"`
	ThinkingEnabledValue         *string         `json:"thinking_enabled_value,omitempty"`
	ThinkingAlwaysSend           *bool           `json:"thinking_always_send,omitempty"`
	ThinkingDisableOnNonStream   *bool           `json:"thinking_disable_on_non_stream,omitempty"`
	ThinkingBudgetField          *string         `json:"thinking_budget_field,omitempty"`
	ThinkingBudgetExcludesEffort *bool           `json:"thinking_budget_excludes_effort,omitempty"`
	ReasoningEffortField         *string         `json:"reasoning_effort_field,omitempty"`
	SupportsReasoningEffort      *bool           `json:"supports_reasoning_effort,omitempty"`
	SupportsDeveloperRole        *bool           `json:"supports_developer_role,omitempty"`
	SupportsStore                *bool           `json:"supports_store,omitempty"`
	SupportsUsageInStreaming     *bool           `json:"supports_usage_in_streaming,omitempty"`
	SupportsTemperature          *bool           `json:"supports_temperature,omitempty"`
	FixedTemperature             *float64        `json:"fixed_temperature,omitempty"`
	SupportsSeed                 *bool           `json:"supports_seed,omitempty"`
	ToolChoiceModes              []string        `json:"tool_choice_modes,omitempty"`
	SupportsParallelToolCalls    *bool           `json:"supports_parallel_tool_calls,omitempty"`
	SupportsResponseFormat       *bool           `json:"supports_response_format,omitempty"`
	SupportsMultiContent         *bool           `json:"supports_multi_content,omitempty"`
	ReplayReasoningContent       *bool           `json:"replay_reasoning_content,omitempty"`
	PromptCacheKey               *bool           `json:"prompt_cache_key,omitempty"`
	CacheControlFormat           *string         `json:"cache_control_format,omitempty"`
	PromptCacheAccounting        *bool           `json:"prompt_cache_accounting,omitempty"`
	ToolCallExtraFields          []string        `json:"tool_call_extra_fields,omitempty"`
	ExtraBody                    map[string]any  `json:"extra_body,omitempty"`
	ReasoningFields              []string        `json:"reasoning_fields,omitempty"`
}

OpenAICompletionsCompat is the overlay form (every field optional) of the Chat Completions settings. JSON keys are the documented names used in models.json and config/models.json.

type OpenAICompletionsSettings

type OpenAICompletionsSettings struct {
	// MaxTokensField is "max_tokens" or "max_completion_tokens".
	MaxTokensField string
	ThinkingFormat ThinkingFormat
	// ThinkingEnabledValue is the "type" sent for thinking-type when enabled
	// ("enabled" by default; MiniMax M3 uses "adaptive").
	ThinkingEnabledValue string
	// ThinkingAlwaysSend pins the switch on every request even when the
	// caller expressed no preference (DashScope hybrid models require it).
	ThinkingAlwaysSend bool
	// ThinkingDisableOnNonStream forces the switch off for non-streaming
	// calls (Qwen3 rejects thinking without stream).
	ThinkingDisableOnNonStream bool
	// ThinkingBudgetField carries Options.ThinkingBudgetTokens ("thinking_budget").
	ThinkingBudgetField string
	// ThinkingBudgetExcludesEffort marks a vendor that rejects a request
	// carrying both the budget and the effort (DashScope's qwen3.8-max
	// series: "不支持 reasoning_effort 与 thinking_budget 同时设置,同时设置
	// 会报错"). The graded level is what the caller actually picked, so the
	// budget is the field that yields.
	ThinkingBudgetExcludesEffort bool
	// ReasoningEffortField is "reasoning_effort" unless a vendor renames it.
	ReasoningEffortField    string
	SupportsReasoningEffort bool
	// SupportsDeveloperRole switches the system prompt to the developer role
	// on reasoning models.
	SupportsDeveloperRole bool
	// SupportsStore sends store:false (OpenAI keeps completions otherwise).
	SupportsStore            bool
	SupportsUsageInStreaming bool
	// SupportsTemperature false drops every sampling parameter (o-series,
	// GPT-5 family, Kimi K2.5+).
	SupportsTemperature bool
	// FixedTemperature pins the value when the vendor only accepts one.
	FixedTemperature          *float64
	SupportsSeed              bool
	ToolChoiceModes           []string
	SupportsParallelToolCalls bool
	SupportsResponseFormat    bool
	// SupportsMultiContent false flattens image/text parts to plain text.
	SupportsMultiContent bool
	// ReplayReasoningContent sends reasoning_content on prior assistant turns.
	ReplayReasoningContent bool
	// PromptCacheKey sends prompt_cache_key / prompt_cache_retention and the
	// session-affinity headers.
	PromptCacheKey bool
	// CacheControlFormat "anthropic" injects cache_control breakpoints.
	CacheControlFormat string
	// PromptCacheAccounting states the vendor reports cache usage at all.
	PromptCacheAccounting bool
	// ToolCallExtraFields lists tool_call keys to round-trip opaquely
	// (Gemini-through-OpenAI "extra_content").
	ToolCallExtraFields []string
	// ExtraBody is merged into every request body (vendor knobs such as
	// enable_search, sampling defaults).
	ExtraBody map[string]any
	// ReasoningFields are the delta keys that carry thinking text, in
	// priority order.
	ReasoningFields []string
}

OpenAICompletionsSettings is the resolved (fully defaulted) form.

func DefaultOpenAICompletions

func DefaultOpenAICompletions() OpenAICompletionsSettings

DefaultOpenAICompletions is the protocol baseline: plain OpenAI semantics.

func (OpenAICompletionsSettings) AllowsToolChoice

func (s OpenAICompletionsSettings) AllowsToolChoice(mode string) bool

AllowsToolChoice reports whether a tool_choice mode is accepted.

type OpenAIResponsesCompat

type OpenAIResponsesCompat struct {
	SupportsDeveloperRole      *bool          `json:"supports_developer_role,omitempty"`
	SupportsMaxOutputTokens    *bool          `json:"supports_max_output_tokens,omitempty"`
	SupportsReasoningSummary   *bool          `json:"supports_reasoning_summary,omitempty"`
	SupportsEncryptedReasoning *bool          `json:"supports_encrypted_reasoning,omitempty"`
	SupportsStore              *bool          `json:"supports_store,omitempty"`
	SupportsTemperature        *bool          `json:"supports_temperature,omitempty"`
	PromptCacheKey             *bool          `json:"prompt_cache_key,omitempty"`
	SupportsLongCacheRetention *bool          `json:"supports_long_cache_retention,omitempty"`
	SupportsParallelToolCalls  *bool          `json:"supports_parallel_tool_calls,omitempty"`
	ExtraBody                  map[string]any `json:"extra_body,omitempty"`
}

OpenAIResponsesCompat is the overlay for the Responses protocol.

type OpenAIResponsesSettings

type OpenAIResponsesSettings struct {
	SupportsDeveloperRole      bool
	SupportsMaxOutputTokens    bool
	SupportsReasoningSummary   bool
	SupportsEncryptedReasoning bool
	SupportsStore              bool
	SupportsTemperature        bool
	PromptCacheKey             bool
	SupportsLongCacheRetention bool
	SupportsParallelToolCalls  bool
	ExtraBody                  map[string]any
}

OpenAIResponsesSettings is the resolved Responses configuration.

func DefaultOpenAIResponses

func DefaultOpenAIResponses() OpenAIResponsesSettings

DefaultOpenAIResponses is the openai.com baseline.

type OpenAIUsage

type OpenAIUsage struct {
	PromptTokens             int  `json:"prompt_tokens"`
	CompletionTokens         int  `json:"completion_tokens"`
	TotalTokens              int  `json:"total_tokens"`
	PromptCacheHitTokens     *int `json:"prompt_cache_hit_tokens"`
	PromptCacheMissTokens    *int `json:"prompt_cache_miss_tokens"`
	CachedTokens             *int `json:"cached_tokens"`
	CacheReadInputTokens     *int `json:"cache_read_input_tokens"`
	CacheCreationInputTokens *int `json:"cache_creation_input_tokens"`
	PromptTokensDetails      *struct {
		CachedTokens     *int `json:"cached_tokens"`
		CacheWriteTokens *int `json:"cache_write_tokens"`
	} `json:"prompt_tokens_details"`
	CompletionTokensDetails *struct {
		ReasoningTokens int `json:"reasoning_tokens"`
	} `json:"completion_tokens_details"`
}

OpenAIUsage is the superset of the usage shapes OpenAI-compatible endpoints return. Vendors disagree on where cache counters live:

  • OpenAI / Azure / most gateways: prompt_tokens_details.cached_tokens
  • DeepSeek: prompt_cache_hit_tokens / prompt_cache_miss_tokens
  • Kimi: top-level cached_tokens
  • Anthropic-through-OpenRouter: cache_read_input_tokens / cache_creation_input_tokens

All of them are decoded here so protocol code never guesses per vendor.

func (OpenAIUsage) ToTokenUsage

func (u OpenAIUsage) ToTokenUsage(cacheAccounting bool) types.TokenUsage

ToTokenUsage normalizes the counters. cacheAccounting states whether the vendor documents prompt-cache accounting at all: when it does not, a missing counter means "unsupported" rather than "unreported".

type Options

type Options struct {
	// RuntimeNotice is trusted, request-scoped recovery context. Adapters append
	// it after the conversation so stable instructions/history retain their cache
	// prefix. Never populate it with user input or external tool output.
	RuntimeNotice string `json:"-"`

	Temperature float64 `json:"temperature"` // 温度参数
	TopP        float64 `json:"top_p"`       // Top P 参数
	Seed        int     `json:"seed"`        // 随机种子
	// MaxTokens and MaxCompletionTokens are aliases for one completion budget.
	// Callers may set either; CompletionBudget() prefers MaxCompletionTokens.
	// The outbound JSON carries exactly one field, named per vendor.
	MaxTokens           int     `json:"max_tokens"`
	MaxCompletionTokens int     `json:"max_completion_tokens"`
	FrequencyPenalty    float64 `json:"frequency_penalty"` // 频率惩罚
	PresencePenalty     float64 `json:"presence_penalty"`  // 存在惩罚
	// Thinking is the legacy on/off switch. Prefer ReasoningEffort; when both
	// are set ReasoningEffort wins. true maps to ReasoningAuto, false to
	// ReasoningOff.
	Thinking *bool `json:"thinking"`
	// ReasoningEffort selects the thinking intensity. Empty leaves the model
	// default untouched (no thinking fields are sent unless the vendor
	// requires them on every request).
	ReasoningEffort ReasoningEffort `json:"reasoning_effort,omitempty"`
	// ThinkingBudgetTokens caps the thinking tokens where the vendor exposes a
	// budget (Anthropic budget_tokens, DashScope thinking_budget, Gemini
	// thinkingBudget). 0 leaves the vendor default.
	ThinkingBudgetTokens int    `json:"thinking_budget_tokens,omitempty"`
	Tools                []Tool `json:"tools,omitempty"` // 可用工具列表
	// ToolChoice is "auto", "required", "none" or a specific tool name.
	ToolChoice        string          `json:"tool_choice,omitempty"`
	ParallelToolCalls *bool           `json:"parallel_tool_calls,omitempty"` // 是否允许并行工具调用(默认 nil 表示由模型决定)
	Format            json.RawMessage `json:"format,omitempty"`              // 响应格式定义
	// PromptCacheKey is the provider routing key (OpenAI prompt_cache_key).
	// Empty falls back to the session ID on the call context.
	PromptCacheKey string `json:"-"`
	// CacheRetention controls provider prompt-cache TTL. none disables cache
	// markers; empty/short is the default 5-minute cache; long requests 1h/24h
	// where the provider accepts it.
	CacheRetention CacheRetention `json:"-"`
}

Options 聊天选项。It is protocol-neutral: every field is a capability the caller asks for, and the protocol package decides how (or whether) the vendor can honour it.

func WithSessionCacheKey

func WithSessionCacheKey(ctx context.Context, opts *Options) *Options

WithSessionCacheKey returns opts with PromptCacheKey filled from the session on ctx when the caller left it empty, so calls made inside a session keep their cache routing key. opts itself is never mutated.

func (*Options) CompletionBudget

func (o *Options) CompletionBudget() int

CompletionBudget is the single per-call generation cap. MaxTokens and MaxCompletionTokens on Options are input aliases for the same budget (YAML, older callers, and the agent UI all feed one of them). When both are set, the newer MaxCompletionTokens wins.

func (*Options) Reasoning

func (o *Options) Reasoning() (ReasoningEffort, bool)

Reasoning resolves the requested thinking level from ReasoningEffort and the legacy Thinking boolean. The bool result reports whether the caller expressed any preference at all; when false the model default applies.

The stored level is re-parsed here rather than trusted: the write paths validate, but rows persisted before that validation existed, builtin YAML agents and SummaryConfig all reach this struct through a plain string cast. An unparseable value ("hgih") is non-empty, so Enabled() would read it as "thinking on" and the protocol packages would send the vendor a level it never defined. Ignoring it falls back to the legacy boolean, which is what "no usable preference" has always meant.

func (*Options) ThinkingRequested

func (o *Options) ThinkingRequested() bool

ThinkingRequested reports whether the caller asked for thinking to be on.

type ReasoningEffort

type ReasoningEffort string

ReasoningEffort is the protocol-neutral thinking level. Vendors map it onto their own vocabulary through a ThinkingLevelMap; the protocol package then emits whichever wire field the vendor documents (reasoning_effort, thinking.type, enable_thinking, thinkingConfig, ...).

const (
	// ReasoningOff disables extended thinking where the model allows it.
	ReasoningOff ReasoningEffort = "off"
	// ReasoningAuto enables thinking but leaves the intensity to the provider
	// default. This is what the legacy boolean `thinking: true` maps to.
	ReasoningAuto ReasoningEffort = "auto"
	// ReasoningMinimal is the weakest graded level.
	ReasoningMinimal ReasoningEffort = "minimal"
	// ReasoningLow is the low graded level.
	ReasoningLow ReasoningEffort = "low"
	// ReasoningMedium is the medium graded level.
	ReasoningMedium ReasoningEffort = "medium"
	// ReasoningHigh is the high graded level.
	ReasoningHigh ReasoningEffort = "high"
	// ReasoningXHigh is the extra-high graded level (OpenAI, Zhipu).
	ReasoningXHigh ReasoningEffort = "xhigh"
	// ReasoningMax is the strongest graded level.
	ReasoningMax ReasoningEffort = "max"
)

func ParseReasoningEffort

func ParseReasoningEffort(s string) (ReasoningEffort, bool)

ParseReasoningEffort validates a user-provided level. Empty input yields ("", true) meaning "unset: let the model default apply".

func SanitizeReasoningEffort

func SanitizeReasoningEffort(ctx context.Context, raw, source string) ReasoningEffort

SanitizeReasoningEffort validates a stored or configured level on its way into Options. Callers that read the level out of a database row, a YAML agent or a session config cast a plain string, so this is where a typo is caught and dropped: an unknown level must never reach the vendor, and must never be mistaken for "thinking on". Returning "" means "no preference", leaving the legacy Thinking boolean to decide.

func (ReasoningEffort) Enabled

func (r ReasoningEffort) Enabled() bool

Enabled reports whether the level asks for thinking to be on.

func (ReasoningEffort) Graded

func (r ReasoningEffort) Graded() bool

Graded reports whether the level is one of the intensity rungs.

type RerankAPI

type RerankAPI string

RerankAPI names a rerank wire protocol. It is deliberately a separate type from API, the chat protocol selector: the two vocabularies must not mix, or a chat row whose extra_config.api names a rerank dialect would pass validation and fail at call time.

const (
	// RerankCohere is the de-facto standard shape — POST {base}/rerank with
	// {model, query, documents} and a results array of {index,
	// relevance_score, document}. Cohere defined it; Jina, Zhipu,
	// SiliconFlow, Qianfan, GPUStack and every OpenAI-compatible gateway
	// that serves rerank at all copy it.
	RerankCohere RerankAPI = "cohere-rerank"
	// RerankDashScope is Alibaba Model Studio's native shape, which wraps
	// the same fields in input/parameters and answers under output.
	RerankDashScope RerankAPI = "dashscope-rerank"
	// RerankNIM is NVIDIA NIM's retrieval shape: a query object, a passages
	// array, and rankings carrying an unbounded logit instead of a
	// probability.
	RerankNIM RerankAPI = "nim-rerank"
	// RerankPinecone is Pinecone Inference's standalone /rerank endpoint:
	// documents are objects and results live under data with a score field.
	RerankPinecone RerankAPI = "pinecone-rerank"
	// RerankTEI is the native Text Embeddings Inference shape: POST /rerank
	// with {query, texts}, answering a bare array of {index, score}.
	RerankTEI RerankAPI = "tei-rerank"
	// RerankTencentLKEAP is Tencent Cloud's RunRerank action, reached through
	// the official SDK because it is TC3-signed rather than key-authenticated.
	RerankTencentLKEAP RerankAPI = "tencent-lkeap"
	// RerankVolcengineKnowledge is Volcengine's managed Knowledge Service
	// rerank, reached through the vikingdb SDK for the same reason.
	RerankVolcengineKnowledge RerankAPI = "volcengine-knowledge"
)

The rerank protocols WeKnora speaks.

func (RerankAPI) HTTPServed

func (a RerankAPI) HTTPServed() bool

HTTPServed reports whether a protocol package implements the protocol. The SDK-backed ones are built by the rerank package itself, which is where the vendor SDKs are imported.

func (RerankAPI) Known

func (a RerankAPI) Known() bool

Known reports whether the value names a protocol this build implements.

type RerankCompat

type RerankCompat struct {
	Path             *string     `json:"path,omitempty"`
	SendTopN         *bool       `json:"send_top_n,omitempty"`
	SendReturnDocs   *bool       `json:"send_return_documents,omitempty"`
	ScoreScale       *ScoreScale `json:"score_scale,omitempty"`
	Truncate         *string     `json:"truncate,omitempty"`
	MaxDocuments     *int        `json:"max_documents,omitempty"`
	MaxQueryChars    *int        `json:"max_query_chars,omitempty"`
	MaxDocumentChars *int        `json:"max_document_chars,omitempty"`
	MaxRequestChars  *int        `json:"max_request_chars,omitempty"`
	MaxConcurrency   *int        `json:"max_concurrency,omitempty"`
	RequestTimeout   *int        `json:"request_timeout_seconds,omitempty"`
	// AcceptsTruncatePromptTokens marks a vLLM-class runtime.
	AcceptsTruncatePromptTokens *bool `json:"accepts_truncate_prompt_tokens,omitempty"`
	// UnsupportedReason marks a model this build cannot call.
	UnsupportedReason *string        `json:"unsupported_reason,omitempty"`
	ExtraBody         map[string]any `json:"extra_body,omitempty"`
	ImageField        *string        `json:"image_field,omitempty"`
	MaxImageBatchSize *int           `json:"max_image_batch_size,omitempty"`
	MaxImageBytes     *int           `json:"max_image_bytes,omitempty"`
	ImageMIMETypes    []string       `json:"image_mime_types,omitempty"`
}

RerankCompat is the overlay form (every field optional) of the rerank settings. JSON keys are the documented names used in models.json and config/models.json.

type RerankResult

type RerankResult struct {
	Index int
	Score float64
	// Text is the document echoed back, empty when the vendor was not asked
	// to return documents. Callers index into their own slice instead of
	// relying on it.
	Text string
}

RerankResult is one scored document, in the vendor's own score scale. Index refers to the position in the documents slice that was sent.

type RerankSettings

type RerankSettings struct {
	// Path is appended to the base URL by protocols that use one. Vendors
	// whose default base URL already names the full endpoint leave it empty.
	Path string
	// SendTopN asks for every document back by naming their count. Omitting
	// it means "all", which is what every protocol here does by default.
	SendTopN bool
	// SendReturnDocs asks the vendor to echo the document text. Callers index
	// into their own slice, so this is off unless a vendor needs it.
	SendReturnDocs bool
	// ScoreScale says what the returned numbers mean. See ScoreScale:
	// getting this wrong makes a relevance threshold meaningless.
	ScoreScale ScoreScale
	// Truncate is the vendor's server-side truncation setting for
	// over-long input ("END" on NIM). Empty sends nothing.
	Truncate string
	// Limits carry the documented per-request ceilings. MaxQueryChars is
	// checked on its own rather than through BatchLimits: the query is not an
	// item to be split, so an over-long one cannot be made to fit by batching.
	MaxDocuments     int
	MaxQueryChars    int
	MaxDocumentChars int
	MaxRequestChars  int
	// MaxConcurrency bounds in-flight batches when the input is split. 0
	// means the caller's default.
	MaxConcurrency int
	// RequestTimeout caps one request, in seconds. 0 uses the rerank
	// client's default (see rerank.newRerankHTTPClient).
	RequestTimeout int
	// AcceptsTruncatePromptTokens reports that this vendor is a vLLM-class
	// runtime, which is the only kind that implements the
	// `truncate_prompt_tokens` extension. It is not part of the Cohere shape
	// and appears in no managed vendor's schema, so it must never be sent to
	// one: that is how an undocumented field ends up on every request.
	AcceptsTruncatePromptTokens bool
	// UnsupportedReason is set on a catalog entry naming a rerank dialect no
	// protocol package implements. Resolve refuses it rather than letting a
	// request go out shaped for a different protocol, which surfaces as a
	// decode error far from its cause.
	UnsupportedReason string
	// TruncatePromptTokens is the budget the operator opted into on this row,
	// honoured only where AcceptsTruncatePromptTokens is set. It is a row
	// setting rather than a vendor fact because one runtime serves models
	// with different context windows. Zero sends nothing.
	TruncatePromptTokens int
	ExtraBody            map[string]any
	// ImageInput is how this endpoint takes image documents, for the models
	// whose catalog input lists image. Image documents are sent in requests
	// of their own, at most ImageBatchLimit per request; each is scored
	// against the query on its own, so their scores compare with the text
	// documents' as any two batches do.
	ImageInput
}

RerankSettings is the resolved (fully defaulted) form.

func DefaultRerank

func DefaultRerank() RerankSettings

DefaultRerank is the protocol baseline: ask for every document, expect a 0..1 relevance score, enforce no ceiling the vendor did not state.

func (RerankSettings) BatchLimits

func (s RerankSettings) BatchLimits() BatchLimits

BatchLimits renders the documented ceilings for SplitBatches.

type Reranker

type Reranker interface {
	Rerank(ctx context.Context, query string, documents []string) ([]RerankResult, error)
}

Reranker is what every rerank protocol package implements.

type RetryPolicy

type RetryPolicy struct {
	// MaxRetries is the number of retries after the first attempt.
	MaxRetries int
	// BaseDelay is the wait before the first retry; it doubles each time.
	BaseDelay time.Duration
	// MaxDelay caps the backoff.
	MaxDelay time.Duration
}

RetryPolicy describes how often to retry a failed request and how long to wait between attempts. The zero value performs one attempt and no waiting.

func DefaultRetryPolicy

func DefaultRetryPolicy() RetryPolicy

DefaultRetryPolicy is three retries with 1s, 2s, 4s backoff, which is what every embedding client used before this helper existed.

type SSEEvent

type SSEEvent struct {
	Data []byte
	Done bool
}

SSEEvent 表示一个 Server-Sent Events 事件

type SSEReader

type SSEReader struct {
	// contains filtered or unexported fields
}

SSEReader reads SSE from JSON model providers. It also retains compatibility with providers that omit blank separators between complete JSON packets.

func NewSSEReader

func NewSSEReader(reader io.Reader) *SSEReader

NewSSEReader 创建 SSE 读取器

func (*SSEReader) ReadEvent

func (r *SSEReader) ReadEvent() (*SSEEvent, error)

ReadEvent 读取下一个 SSE 事件

type SandboxFileProgress

type SandboxFileProgress struct {
	// contains filtered or unexported fields
}

SandboxFileProgress tracks a streamed write/edit tool call and emits throttled progress payloads.

func NewSandboxFileProgress

func NewSandboxFileProgress(toolName string) *SandboxFileProgress

NewSandboxFileProgress creates a tracker for one tool call.

func (*SandboxFileProgress) Feed

func (p *SandboxFileProgress) Feed(delta string) (map[string]any, bool)

Feed consumes one arguments delta. ok is true when the UI should be sent an updated stats payload.

type ScoreScale

type ScoreScale string

ScoreScale describes what a rerank score means, which differs per protocol and cannot be inferred from the number itself.

const (
	// ScoreProbability is a 0..1 relevance score. Every protocol but NIM
	// returns one.
	ScoreProbability ScoreScale = "probability"
	// ScoreLogit is an unbounded log-odds value that may be negative. NIM
	// returns one, so a threshold tuned for probabilities rejects almost
	// everything on that vendor.
	ScoreLogit ScoreScale = "logit"
)

type StreamAssembler

type StreamAssembler struct {
	ThinkingEmitter

	// Usage is captured from the final stream chunk when the vendor reports it.
	Usage *types.TokenUsage
	// LastFinishReason is the last observed finish_reason for the EOF fallback.
	LastFinishReason string
	// ReasoningSignature / ReasoningMetadata accumulate provider artifacts that
	// the final Done chunk hands back to the caller through Data.
	ReasoningSignature string
	ReasoningMetadata  types.ProviderMetadata
	// contains filtered or unexported fields
}

StreamAssembler turns protocol deltas into the types.StreamResponse sequence the agent engine and the chat pipeline consume. It owns the "reasoning then answer" hand-off, tool-call fragment assembly, the early tool-call notification (ResponseTypeToolCall) once a function name has stabilised, streaming extraction of the thinking tool's `thought` field and live sandbox file-edit progress.

func NewStreamAssembler

func NewStreamAssembler(ctx context.Context, model string) *StreamAssembler

NewStreamAssembler creates an assembler for one stream.

func (*StreamAssembler) Aborted

func (a *StreamAssembler) Aborted() bool

Aborted reports whether the consumer went away mid-stream. Protocol loops check it to stop reading instead of decoding a stream nobody listens to.

func (*StreamAssembler) Emit

func (a *StreamAssembler) Emit(ch chan<- types.StreamResponse, content string)

Emit forwards a reasoning chunk through the cancellation-aware send, shadowing the embedded ThinkingEmitter's blocking version.

func (*StreamAssembler) End

func (a *StreamAssembler) End(ch chan<- types.StreamResponse)

End emits the closing chunk once the vendor stream is exhausted. It logs usage and carries the assembled tool calls, usage and reasoning artifacts.

func (*StreamAssembler) EndAtEOF

func (a *StreamAssembler) EndAtEOF(ch chan<- types.StreamResponse)

EndAtEOF closes a stream whose body simply ran out. A vendor that finished the message has reported a finish reason by then; without one the connection was most likely cut by a proxy or load balancer, and the answer is marked incomplete, as the Anthropic loop already does, instead of reaching the caller as a clean stop.

func (*StreamAssembler) Fail

func (a *StreamAssembler) Fail(ch chan<- types.StreamResponse, err error)

Fail emits the error chunk for a broken stream.

func (*StreamAssembler) Finish

func (a *StreamAssembler) Finish(ch chan<- types.StreamResponse)

Finish emits the single thinking-done marker if one is owed, through the cancellation-aware send. Shadows the embedded ThinkingEmitter's version.

func (*StreamAssembler) OrderedToolCalls

func (a *StreamAssembler) OrderedToolCalls() []types.LLMToolCall

OrderedToolCalls returns the assembled calls in index order, or nil.

The indices are the vendor's, not ours: gateways number calls from 1, and parallel calls can arrive with gaps. Walking 0..len(map) would silently drop every call outside that range — and a round that loses its calls reaches the agent as a plain answer — so the keys are sorted instead.

func (*StreamAssembler) Process

func (a *StreamAssembler) Process(ch chan<- types.StreamResponse, d Delta)

Process consumes one delta and emits the resulting stream responses. It mirrors the event sequence of the original OpenAI stream loop so every downstream consumer sees exactly what it saw before the protocol split.

func (*StreamAssembler) ReplaceToolCalls

func (a *StreamAssembler) ReplaceToolCalls(calls []types.LLMToolCall)

ReplaceToolCalls swaps the assembled tool calls for a protocol's own final view (for example, one that drops calls the stream never closed).

func (*StreamAssembler) SetToolCallMetadata

func (a *StreamAssembler) SetToolCallMetadata(index int, metadata types.ToolCallMetadata)

SetToolCallMetadata attaches provider state to the call at index, creating a placeholder entry when the metadata arrives before the call itself.

func (*StreamAssembler) SetUsage

func (a *StreamAssembler) SetUsage(u types.TokenUsage)

SetUsage records usage from the vendor's final chunk.

func (*StreamAssembler) ToolCallCount

func (a *StreamAssembler) ToolCallCount() int

ToolCallCount reports how many distinct tool calls have been seen.

type StreamPacketDumper

type StreamPacketDumper struct {
	// contains filtered or unexported fields
}

StreamPacketDumper writes one stream session to a dedicated JSONL file: line 1 = request wrapper; following lines = raw provider chunk JSON.

func NewStreamPacketDumper

func NewStreamPacketDumper(modelName string, request any) *StreamPacketDumper

NewStreamPacketDumper opens a dump file for one stream, or returns nil when raw dumping is disabled.

func (*StreamPacketDumper) Close

func (d *StreamPacketDumper) Close()

Close flushes and closes the dump file.

func (*StreamPacketDumper) Path

func (d *StreamPacketDumper) Path() string

Path returns the dump file location.

func (*StreamPacketDumper) WriteError

func (d *StreamPacketDumper) WriteError(message string)

WriteError appends a terminal error record (stream read failure, API error, etc.).

func (*StreamPacketDumper) WriteHTTPError

func (d *StreamPacketDumper) WriteHTTPError(statusCode int, body []byte)

WriteHTTPError appends a non-2xx HTTP response before any SSE packets.

func (*StreamPacketDumper) WritePacket

func (d *StreamPacketDumper) WritePacket(v any)

WritePacket marshals v as one JSON object per line (SDK stream Recv path).

func (*StreamPacketDumper) WritePacketRaw

func (d *StreamPacketDumper) WritePacketRaw(raw []byte)

WritePacketRaw appends one provider chunk as a single JSONL line (valid JSON written as-is).

type ThinkingEmitter

type ThinkingEmitter struct {
	// contains filtered or unexported fields
}

ThinkingEmitter owns the "reasoning then answer" hand-off that every streaming Chat implementation shares: thinking chunks are forwarded as they arrive, and exactly one thinking-done marker is emitted before the first answer token (or when the stream ends without one). Centralizing the bookkeeping keeps the OpenAI-compatible and Ollama stream loops in sync.

func (*ThinkingEmitter) Emit

func (e *ThinkingEmitter) Emit(ch chan<- types.StreamResponse, content string)

Emit forwards a reasoning chunk and records that a thinking-done marker is still owed.

func (*ThinkingEmitter) Finish

func (e *ThinkingEmitter) Finish(ch chan<- types.StreamResponse)

Finish emits the single thinking-done marker if one is owed. Safe to call multiple times; only the first call after an emit sends anything.

type ThinkingFormat

type ThinkingFormat string

ThinkingFormat names how an OpenAI-compatible vendor encodes the thinking switch. It is the single most divergent field across vendors, which is why it is an enum rather than free text.

const (
	// ThinkingFormatNone sends no thinking fields at all.
	ThinkingFormatNone ThinkingFormat = "none"
	// ThinkingFormatOpenAI sends only reasoning_effort (OpenAI, Azure, xAI).
	ThinkingFormatOpenAI ThinkingFormat = "openai"
	// ThinkingFormatThinkingType sends {"thinking": {"type": "enabled"|"disabled"}}
	// plus reasoning_effort when supported (DeepSeek, Zhipu, Volcengine Ark,
	// Moonshot K2.6+, MiniMax M3, LKEAP).
	ThinkingFormatThinkingType ThinkingFormat = "thinking-type"
	// ThinkingFormatEnableThinking sends enable_thinking: bool plus
	// thinking_budget (DashScope, SiliconFlow, Hunyuan).
	ThinkingFormatEnableThinking ThinkingFormat = "enable-thinking"
	// ThinkingFormatChatTemplateKwargs sends chat_template_kwargs.enable_thinking
	// (vLLM, SGLang, NIM, LiteLLM in front of them).
	ThinkingFormatChatTemplateKwargs ThinkingFormat = "chat-template-kwargs"
	// ThinkingFormatOpenRouter sends {"reasoning": {"effort"|"enabled"}}.
	ThinkingFormatOpenRouter ThinkingFormat = "openrouter"
)

type ThinkingLevelMap

type ThinkingLevelMap map[ReasoningEffort]*string

ThinkingLevelMap maps protocol-neutral levels to a vendor's own vocabulary.

Semantics mirror Pi's thinkingLevelMap:

  • a missing key means "supported, pass the level name through" for minimal/low/medium/high and "unsupported" for xhigh/max;
  • a null value marks the level as unsupported (the caller clamps to the nearest supported rung);
  • a string value is what the vendor expects on the wire.

The special key "off" may be mapped to null to state that thinking cannot be switched off (always-on reasoning models).

func (ThinkingLevelMap) Clamp

Clamp returns the nearest supported graded level: first upward, then downward, mirroring Pi's clampThinkingLevel. Off/Auto are returned as-is (Off may still be unsupported; callers check Supports(ReasoningOff)).

func (ThinkingLevelMap) SupportedLevels

func (m ThinkingLevelMap) SupportedLevels() []ReasoningEffort

SupportedLevels lists the levels this map accepts, in UI order.

func (ThinkingLevelMap) Supports

func (m ThinkingLevelMap) Supports(level ReasoningEffort) bool

Supports reports whether the level is usable for this model.

func (ThinkingLevelMap) Value

func (m ThinkingLevelMap) Value(level ReasoningEffort) string

Value returns the vendor value for a supported level. Unmapped levels pass through under their own name.

type Tool

type Tool struct {
	Type     string      `json:"type"` // "function"
	Function FunctionDef `json:"function"`
}

Tool represents a function/tool definition

type ToolCall

type ToolCall struct {
	ID               string                 `json:"id"`
	Type             string                 `json:"type"` // "function"
	Function         FunctionCall           `json:"function"`
	ProviderMetadata types.ToolCallMetadata `json:"provider_metadata,omitempty"`
}

ToolCall represents a tool call in a message

type ToolCallDelta

type ToolCallDelta struct {
	Index     int
	ID        string
	Type      string
	Name      string
	Arguments string
}

ToolCallDelta is one streamed fragment of a tool call, already lifted out of whatever envelope the protocol uses (OpenAI delta.tool_calls, Anthropic input_json_delta, Gemini functionCall parts, Responses function_call_arguments.delta).

type Transcriber

type Transcriber interface {
	Transcribe(ctx context.Context, req TranscriptionRequest) (*Transcription, error)
}

Transcriber turns one audio file into text.

type Transcription

type Transcription struct {
	Text     string
	Segments []TranscriptionSegment
	// Duration is the audio length in seconds when the reply states it —
	// OpenAI-shaped duration or usage.seconds — and 0 otherwise.
	Duration float64
}

Transcription is what a protocol client returns.

type TranscriptionAPI

type TranscriptionAPI string

TranscriptionAPI names a speech-to-text wire protocol. Like RerankAPI and EmbeddingAPI it is its own type, so no other modality's value validates.

const (
	// TranscriptionOpenAI is POST {base}/audio/transcriptions as a multipart
	// form carrying file and model, answering {text} — or {text, segments}
	// when verbose_json is asked for.
	TranscriptionOpenAI TranscriptionAPI = "openai-transcriptions"
	// TranscriptionChatAudio is a dedicated speech-recognition model behind
	// POST {base}/chat/completions: the audio goes in as a base64 data URI in
	// an input_audio content part and the transcript comes back as the
	// assistant message. Alibaba's qwen3-asr-flash and Xiaomi's mimo-v2.5-asr
	// take no instruction; the model only transcribes.
	TranscriptionChatAudio TranscriptionAPI = "openai-chat-audio"
)

The transcription protocols WeKnora speaks.

func (TranscriptionAPI) Known

func (a TranscriptionAPI) Known() bool

Known reports whether the value names a protocol this build implements.

type TranscriptionRequest

type TranscriptionRequest struct {
	Audio    []byte
	FileName string
	// Language is the operator's hint, empty for auto-detection. Where it is
	// sent, and whether at all, is the vendor's declaration.
	Language string
}

TranscriptionRequest is one file to transcribe.

type TranscriptionSegment

type TranscriptionSegment struct {
	Start float64
	End   float64
	Text  string
}

TranscriptionSegment is one timed stretch of a transcript.

type TranscriptionsCompat

type TranscriptionsCompat struct {
	API  *TranscriptionAPI `json:"api,omitempty"`
	Path *string           `json:"path,omitempty"`
	// ResponseFormat is sent as response_format when non-empty. OpenAI's
	// gpt-4o-transcribe and gpt-4o-mini-transcribe accept only json, which is
	// also the default, so most models leave it unset.
	ResponseFormat *string `json:"response_format,omitempty"`
	// LanguageParam names where the language hint goes: form, header or
	// asr_options; empty sends none.
	LanguageParam *string `json:"language_param,omitempty"`
	// MaxFileBytes is the documented upload ceiling for the audio itself.
	MaxFileBytes *int `json:"max_file_bytes,omitempty"`
	// MaxEncodedBytes is the documented ceiling on the base64 data URI, for
	// the protocols that send the audio that way.
	MaxEncodedBytes *int `json:"max_encoded_bytes,omitempty"`
	// Formats lists the file extensions the vendor documents, without the
	// dot. Empty means the vendor documents no closed list.
	Formats        []string `json:"formats,omitempty"`
	RequestTimeout *int     `json:"request_timeout_seconds,omitempty"`
	// UnsupportedReason marks a model this build cannot call.
	UnsupportedReason *string `json:"unsupported_reason,omitempty"`
}

TranscriptionsCompat is the overlay form (every field optional) of the speech-to-text settings. JSON keys are the names used in models.json.

type TranscriptionsSettings

type TranscriptionsSettings struct {
	API            TranscriptionAPI
	Path           string
	ResponseFormat string
	LanguageParam  string
	// MaxFileBytes and MaxEncodedBytes refuse an upload the vendor would
	// reject anyway, before sending it; 0 leaves the check to the vendor.
	MaxFileBytes    int
	MaxEncodedBytes int
	Formats         []string
	// RequestTimeout caps one request, in seconds.
	RequestTimeout int
	// UnsupportedReason is set where a vendor's recognition models, or some
	// of them, cannot be called with the audio in the request — they take a
	// public URL or run as asynchronous tasks. Resolve refuses such a model
	// rather than sending it a shape it does not accept.
	UnsupportedReason string
}

TranscriptionsSettings is the resolved (fully defaulted) form.

func DefaultTranscriptions

func DefaultTranscriptions() TranscriptionsSettings

DefaultTranscriptions is the protocol baseline: a form with file and model and nothing else, which every speech-to-text endpoint in this catalog documents. The deadline is the one the pre-catalog client carried; audio transcription is slow.

type TransportError

type TransportError struct {
	// Op is the phase that failed: "send request" or "read response".
	Op  string
	Err error
}

TransportError is a request that never got a whole answer: DNS, connect, TLS, a reset or a timeout, while sending or while reading the reply. It is the only failure worth sending again.

func (*TransportError) Error

func (e *TransportError) Error() string

func (*TransportError) Unwrap

func (e *TransportError) Unwrap() error

Directories

Path Synopsis
Package anthropicmessages implements the Anthropic Messages wire protocol (https://docs.anthropic.com/en/api/messages).
Package anthropicmessages implements the Anthropic Messages wire protocol (https://docs.anthropic.com/en/api/messages).
Package arkembeddings implements Volcengine Ark's multimodal embedding shape: an input array of typed parts answering a single `data` object.
Package arkembeddings implements Volcengine Ark's multimodal embedding shape: an input array of typed parts answering a single `data` object.
Package cohererank implements the rerank shape Cohere defined and almost every other vendor copied: POST {base}/rerank with {model, query, documents} answering {results: [{index, relevance_score, document}]}.
Package cohererank implements the rerank shape Cohere defined and almost every other vendor copied: POST {base}/rerank with {model, query, documents} answering {results: [{index, relevance_score, document}]}.
Package dashscopeembeddings implements Alibaba Model Studio's native multimodal embedding shape: input.contents, parameters.dimension, and results under output.embeddings.
Package dashscopeembeddings implements Alibaba Model Studio's native multimodal embedding shape: input.contents, parameters.dimension, and results under output.embeddings.
Package dashscoperank implements Alibaba Model Studio's native text-rerank shape: the same fields as the Cohere dialect, wrapped in input/parameters and answered under output.
Package dashscoperank implements Alibaba Model Studio's native text-rerank shape: the same fields as the Cohere dialect, wrapped in input/parameters and answered under output.
Package googleembeddings implements Gemini's batchEmbedContents: one POST carrying a request per text, answering embeddings[].values in order.
Package googleembeddings implements Gemini's batchEmbedContents: one POST carrying a request per text, answering embeddings[].values in order.
Package googlegenai implements the native Google Gemini generateContent wire protocol (https://ai.google.dev/api/generate-content).
Package googlegenai implements the native Google Gemini generateContent wire protocol (https://ai.google.dev/api/generate-content).
Package nimrerank implements NVIDIA NIM's retrieval reranking shape: a query object, a passages array, and rankings carrying a logit.
Package nimrerank implements NVIDIA NIM's retrieval reranking shape: a query object, a passages array, and rankings carrying a logit.
Package openaichataudio implements speech recognition served through the chat completions endpoint: one user message whose only content part is {"type": "input_audio", "input_audio": {"data": "data:<mime>;base64,..."}}, answered by an assistant message whose content is the transcript.
Package openaichataudio implements speech recognition served through the chat completions endpoint: one user message whose only content part is {"type": "input_audio", "input_audio": {"data": "data:<mime>;base64,..."}}, answered by an assistant message whose content is the transcript.
Package openaicompletions implements the OpenAI Chat Completions wire protocol.
Package openaicompletions implements the OpenAI Chat Completions wire protocol.
Package openaiembeddings implements the embedding shape OpenAI defined and every compatible gateway copied: POST {base}/embeddings with {model, input} answering {data: [{embedding, index}]}.
Package openaiembeddings implements the embedding shape OpenAI defined and every compatible gateway copied: POST {base}/embeddings with {model, input} answering {data: [{embedding, index}]}.
Package openairesponses implements the OpenAI Responses wire protocol (POST /responses).
Package openairesponses implements the OpenAI Responses wire protocol (POST /responses).
Package openaitranscriptions implements the speech-to-text shape OpenAI defined and the compatible servers copied: a multipart POST to {base}/audio/transcriptions carrying file and model, answering {text}, or {text, segments} when response_format is verbose_json.
Package openaitranscriptions implements the speech-to-text shape OpenAI defined and the compatible servers copied: a multipart POST to {base}/audio/transcriptions carrying file and model, answering {text}, or {text, segments} when response_format is verbose_json.
Package pineconerank implements Pinecone Inference's standalone rerank API.
Package pineconerank implements Pinecone Inference's standalone rerank API.
Package teirank implements the native Text Embeddings Inference rerank protocol.
Package teirank implements the native Text Embeddings Inference rerank protocol.
Package tencentlkeap implements Tencent RunRerank through its official SDK.
Package tencentlkeap implements Tencent RunRerank through its official SDK.
Package volcengineknowledge implements Volcengine Knowledge Service rerank through its official SDK.
Package volcengineknowledge implements Volcengine Knowledge Service rerank through its official SDK.

Jump to

Keyboard shortcuts

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