Documentation
¶
Index ¶
- Constants
- Variables
- func EstimateDeepSeekCostUSD(promptTokens, cachedPromptTokens, completionTokens int) float64
- func IsAnthropicFamilyProvider(providerType string) bool
- func IsRemoteInferenceError(err error) bool
- func IsRetryableProviderError(err error) bool
- func IsRetryableTransport(err error) bool
- func ParseBaseURL(rawURL string) (*url.URL, error)
- func ProviderHTTPStatus(err error) (int, bool)
- func ProviderRetryAfter(err error) (time.Duration, bool)
- func ProviderStatusHint(status int) string
- func ResolveProviderModel(providerType, model string) (string, error)
- func SanitizeImageName(value string) string
- type AnthropicClient
- func (c *AnthropicClient) BaseURL() string
- func (c *AnthropicClient) ChatStream(ctx context.Context, opts ChatOptions, fn func(StreamChunk) error) error
- func (c *AnthropicClient) Embed(context.Context, string, []string) ([][]float32, error)
- func (c *AnthropicClient) Model() string
- func (c *AnthropicClient) Ping() error
- func (c *AnthropicClient) PingContext(ctx context.Context) error
- func (c *AnthropicClient) SetModel(model string) error
- type AnthropicOptions
- type ChatOptions
- type Client
- type DeepSeekOptions
- type ExpertModelResource
- type ExpertModelSnapshot
- type ImageData
- type LocalModel
- type Message
- type ModelContextPolicy
- type ModelManager
- func (m *ModelManager) ActiveProviderName() string
- func (m *ModelManager) BaseURL() string
- func (m *ModelManager) ChatStream(ctx context.Context, opts ChatOptions, fn func(StreamChunk) error) error
- func (m *ModelManager) ChatStreamForModel(ctx context.Context, model string, opts ChatOptions, ...) error
- func (m *ModelManager) ClearCurrentModel() error
- func (m *ModelManager) ClearRemoteProvider()
- func (m *ModelManager) Close()
- func (m *ModelManager) ConfigureLocalInventory(required bool, models []LocalModel, verified bool)
- func (m *ModelManager) ConfigureLocalOnly(required bool, models []string, verified bool)
- func (m *ModelManager) ConfigureOllamaCloudInventory(models []string, verified bool)
- func (m *ModelManager) ConfigureOllamaInventory(models []OllamaModel, verified bool)
- func (m *ModelManager) ConfigureOllamaRuntimeInventory(required bool, models []OllamaModel, verified bool)
- func (m *ModelManager) ConfigureProviderCatalog(catalog config.ProviderConfig, localOnly bool, ollamaModel string)
- func (m *ModelManager) ConfigureRemoteProvider(client RemoteChatClient, contextSize int, label string) error
- func (m *ModelManager) ConfiguredNumCtx() int
- func (m *ModelManager) ContextPolicy(model string) ModelContextPolicy
- func (m *ModelManager) CurrentModel() string
- func (m *ModelManager) CurrentModelReceipt(ctx context.Context) ModelReceipt
- func (m *ModelManager) EffectiveContext(model string) (int, bool)
- func (m *ModelManager) Embed(ctx context.Context, model string, texts []string) ([][]float32, error)
- func (m *ModelManager) EmbedWithCurrentModel(ctx context.Context, texts []string) ([][]float32, error)
- func (m *ModelManager) GetClient(modelName string) (*OllamaClient, error)
- func (m *ModelManager) GrantOllamaCloudModel(model string) error
- func (m *ModelManager) ListLocalModelInventory(ctx context.Context) ([]LocalModel, error)
- func (m *ModelManager) ListLocalModels(ctx context.Context) ([]string, error)
- func (m *ModelManager) ListOllamaModels(ctx context.Context) ([]OllamaModel, error)
- func (m *ModelManager) ListRunningOllamaModels(ctx context.Context) ([]OllamaRunningModel, error)
- func (m *ModelManager) LocalModelWeightBytes(model string) int64
- func (m *ModelManager) Model() string
- func (m *ModelManager) NumCtx() int
- func (m *ModelManager) OllamaVersion(ctx context.Context) (string, error)
- func (m *ModelManager) Ping() error
- func (m *ModelManager) PingContext(ctx context.Context) error
- func (m *ModelManager) PingModel(ctx context.Context, model string) error
- func (m *ModelManager) PrepareExpertModels(ctx context.Context, selected []string) (ExpertModelSnapshot, error)
- func (m *ModelManager) ProviderCatalog() []ProviderDescriptor
- func (m *ModelManager) ProviderNames() []string
- func (m *ModelManager) PullOllamaModel(ctx context.Context, model string, fn func(OllamaPullProgress) error) error
- func (m *ModelManager) ReleaseExpertModels(ctx context.Context, snapshot ExpertModelSnapshot) error
- func (m *ModelManager) RemoteProvider() bool
- func (m *ModelManager) RemoteProviderLabel() string
- func (m *ModelManager) RevokeOllamaCloudGrants()
- func (m *ModelManager) RevokeOllamaCloudModel(model string)
- func (m *ModelManager) SetCurrentModel(model string) error
- func (m *ModelManager) SetNumCtx(numCtx int) error
- func (m *ModelManager) ShowOllamaModel(ctx context.Context, model string) (OllamaModelInfo, error)
- func (m *ModelManager) SwitchProvider(name string) error
- func (m *ModelManager) SwitchProviderContext(ctx context.Context, name string) error
- type ModelReceipt
- type OllamaClient
- func (o *OllamaClient) BaseURL() string
- func (o *OllamaClient) ChatStream(ctx context.Context, opts ChatOptions, fn func(StreamChunk) error) error
- func (o *OllamaClient) Embed(ctx context.Context, model string, texts []string) ([][]float32, error)
- func (o *OllamaClient) ListModels(ctx context.Context) ([]OllamaModel, error)
- func (o *OllamaClient) ListRunningModels(ctx context.Context) ([]OllamaRunningModel, error)
- func (o *OllamaClient) Model() string
- func (o *OllamaClient) Ping() error
- func (o *OllamaClient) PingContext(ctx context.Context) error
- func (o *OllamaClient) PullModel(ctx context.Context, model string, fn func(OllamaPullProgress) error) error
- func (o *OllamaClient) ShowModel(ctx context.Context, model string) (OllamaModelInfo, error)
- func (o *OllamaClient) Unload(ctx context.Context) error
- func (o *OllamaClient) Version(ctx context.Context) (string, error)
- type OllamaModel
- type OllamaModelInfo
- type OllamaModelLocation
- type OllamaPullProgress
- type OllamaRunningModel
- type OpenAICompatibleClient
- func (c *OpenAICompatibleClient) BaseURL() string
- func (c *OpenAICompatibleClient) ChatStream(ctx context.Context, opts ChatOptions, fn func(StreamChunk) error) error
- func (c *OpenAICompatibleClient) Embed(context.Context, string, []string) ([][]float32, error)
- func (c *OpenAICompatibleClient) Model() string
- func (c *OpenAICompatibleClient) Ping() error
- func (c *OpenAICompatibleClient) PingContext(ctx context.Context) error
- func (c *OpenAICompatibleClient) SetModel(model string) error
- type OpenAICompatibleOptions
- type ProviderDescriptor
- type ProviderTiming
- type RemoteChatClient
- type StreamChunk
- type ToolBehavior
- type ToolCall
- type ToolDef
Constants ¶
const ( // AnthropicAPIVersion is the Messages API protocol version. It is // independent of model generation — every request, regardless of which // Claude (or Claude-compatible) model is targeted, sends this same value. AnthropicAPIVersion = "2023-06-01" // AnthropicDefaultMaxOutputTokens is the max_tokens fallback used when // neither the request nor the catalog's per-model default supplies one. // It is a conservative floor, not a target — NewAnthropicProviderClient // prefers the catalog's default_max_tokens for the resolved model. AnthropicDefaultMaxOutputTokens = 8192 // AnthropicBaseURL is the real Anthropic API endpoint. The catalog's // "anthropic" entry names its api_endpoint as the template // "$ANTHROPIC_API_ENDPOINT" (an optional self-hosted-proxy override), not // a literal URL, so a fallback is needed whenever no override is exported. // // It aliases config.AnthropicDefaultAPIEndpoint rather than repeating the // URL: the config layer now substitutes that template for every provider, // and two independently maintained copies of the same endpoint are exactly // how a dialect fallback and a config default drift apart. AnthropicBaseURL = config.AnthropicDefaultAPIEndpoint // AnthropicAPIKeyEnv names the environment variable holding the official // Anthropic API key. Only the name is ever configured; the value is read // from the process environment so a secret can never land in YAML. AnthropicAPIKeyEnv = "ANTHROPIC_API_KEY" // KimiCodingBaseURL, MiniMaxBaseURL, and MiniMaxChinaBaseURL are the three // Anthropic-Messages-API-compatible proxies the catalog lists with wire // type "anthropic". Unlike Anthropic's own template, the catalog gives // each of these a literal endpoint; they are reproduced here as // doc-verified constants (internal/catalog/providers.json) rather than // re-reading the catalog on every client build. KimiCodingBaseURL = "https://api.kimi.com/coding" MiniMaxBaseURL = "https://api.minimax.io/anthropic" MiniMaxChinaBaseURL = "https://api.minimaxi.com/anthropic" )
const ( AnthropicProviderID = "anthropic" KimiCodingProviderID = "kimi-coding" MiniMaxProviderID = "minimax" MiniMaxChinaProviderID = "minimax-china" )
Provider identities for the four catalog entries whose wire type is "anthropic". NewProviderClient selects the Anthropic dialect by checking a provider's identity against this set, not by reading the catalog's wire type string — see NewProviderClient's dialect-selection comment in deepseek.go for why that distinction is load-bearing.
const ( // DeepSeekBaseURL is the documented OpenAI-format endpoint. DeepSeek also // serves an Anthropic-format surface at /anthropic, which sonar does not use. DeepSeekBaseURL = "https://api.deepseek.com" // DeepSeekAPIKeyEnv names the environment variable holding the key. Only // the NAME is ever configured; the value is read from the process // environment so a secret can never land in YAML. DeepSeekAPIKeyEnv = "DEEPSEEK_API_KEY" // DeepSeekFlashModel is the model sonar pins to. Its current published // build is DeepSeek-V4-Flash-0731 (284B total / 13B active). DeepSeekFlashModel = "deepseek-v4-flash" // DeepSeekContextWindow is the 1M-token context DeepSeek serves by default. DeepSeekContextWindow = 1_000_000 // DeepSeekMaxOutputTokens is the published generation ceiling. DeepSeekMaxOutputTokens = 384_000 // DeepSeekDefaultEffort matches the API default for ordinary requests. // "low" and "medium" are mapped to "high" server-side; "xhigh" maps to "max". DeepSeekDefaultEffort = "high" // DeepSeekMaxEffort is the deepest setting, which DeepSeek selects // automatically for some agent clients. DeepSeekMaxEffort = "max" )
DeepSeek is sonar's only inference backend. Every fact below is taken from the official API contract (api-docs.deepseek.com) rather than inferred from the generic OpenAI shape, because three of them differ in ways that break an agent loop:
- Thinking is toggled by `{"thinking": {"type": ...}}`, not by reasoning_effort. reasoning_effort only grades depth once thinking is on.
- Thinking defaults to ENABLED. A harness that never sends the toggle pays for chain-of-thought on every turn.
- An assistant message that carries tool calls must echo its own reasoning_content back on all later requests, or the API answers 400.
See OpenAICompatibleClient's DialectDeepSeek handling for the wire details.
const ( DeepSeekInputCacheHitUSDPerMTok = 0.0028 DeepSeekInputCacheMissUSDPerMTok = 0.14 DeepSeekOutputUSDPerMTok = 0.28 )
DeepSeekPricing is the published per-million-token rate card, in USD. Cache hits are ~50x cheaper than misses, so prompt-prefix stability is the single biggest cost lever in a long agent session.
DeepSeek has announced peak/off-peak pricing that doubles every billing item during 09:00–12:00 and 14:00–18:00 Beijing time (UTC+8). These constants are the off-peak/regular rates; any cost surface must present them as an estimate, never as a settled charge.
const DialectDeepSeek = "deepseek"
DialectDeepSeek marks the DeepSeek flavor of the OpenAI chat contract.
Variables ¶
var ( // ErrNoModelSelected is a local preflight rejection. No provider request or // generation can have started when a Client returns this sentinel. ErrNoModelSelected = errors.New("no model selected") // ErrInferenceNotStarted identifies a host-side rejection that happened // before the provider's inference dispatch. Provider ChatStream errors are // intentionally not wrapped with this sentinel because dispatch may already // have happened by then. ErrInferenceNotStarted = errors.New("inference not started") )
var ErrStreamIdle = errors.New("provider stream produced no data within the idle timeout")
ErrStreamIdle reports that an accepted provider stream stopped producing data for longer than the idle watchdog allows. It is retryable.
Functions ¶
func EstimateDeepSeekCostUSD ¶
EstimateDeepSeekCostUSD returns the estimated turn cost from a token receipt. cachedPromptTokens is the cache-hit portion of promptTokens; pass 0 when the provider did not report one. The result is an estimate: it does not know whether the request landed in a peak-pricing window.
func IsAnthropicFamilyProvider ¶
IsAnthropicFamilyProvider reports whether providerType (already normalized by config.NormalizedProviderType) is one of the four catalog providers that speak the Anthropic Messages API.
func IsRemoteInferenceError ¶
IsRemoteInferenceError reports whether err carries exact provenance from a dispatched inference request against a credentialed remote provider. It deliberately does not classify local Ollama or host-side preflight failures, and never decides provenance by matching error strings.
func IsRetryableProviderError ¶
IsRetryableProviderError reports whether a dispatched provider request failed in a way that retrying the same request could plausibly resolve. It is a superset of IsRetryableTransport that additionally admits 429 (rate limit / quota exhaustion).
The 429 case is deliberately not folded into IsRetryableTransport: that predicate signals "safe to resend now with the same request," which is true for a dropped connection or a provider 5xx but never true for a rate limit — an immediate resend on a 429 wastes another billable request against a quota that is, by definition, already exhausted or throttled. A caller that receives true here for a 429 must consult ProviderRetryAfter (or otherwise back off) before resending; it must not treat this the same as an IsRetryableTransport-style immediate retry.
Every other 4xx status (401, 403, 404, 400, ...) returns false: the exact same request cannot succeed without changing credentials, the endpoint, or the payload, so retrying it is never productive.
func IsRetryableTransport ¶
IsRetryableTransport reports whether err looks like a transient provider transport failure (connection loss, truncated stream, server-side 5xx, a request timeout, or an idle-stream watchdog) that a caller may retry immediately with the same request. Deliberate cancellation and admission deadlines are never retryable.
A 429 (rate limit / quota exhaustion) is deliberately NOT included here, even though it is sometimes eventually retryable: unlike a 5xx or a dropped connection, resending immediately will not resolve it and only burns another billable request against an already-exhausted quota. Callers that want to retry a 429 must do so through IsRetryableProviderError, which forces them to consider ProviderRetryAfter (or their own backoff) instead of treating it the same as an ordinary transport hiccup.
func ParseBaseURL ¶
ParseBaseURL validates and normalizes an Ollama URL.
func ProviderHTTPStatus ¶
ProviderHTTPStatus reports the HTTP status a provider returned, when the error carries one. It recognizes both HTTP-error shapes this package produces: the OpenAI/Anthropic-dialect openAIHTTPError and Ollama's own ollamaHTTPError, so callers get one status lookup regardless of which client dispatched the request.
The status code alone is a bounded, host-safe fact: unlike the response body it cannot contain provider prose, an endpoint, or a credential. That makes it the one detail a startup diagnostic can surface without weakening the transcript sanitization that ProviderFailureCopy exists to enforce.
func ProviderRetryAfter ¶
ProviderRetryAfter reports the delay a provider asked for via a Retry-After response header, when the error carries one. It is populated for both HTTP-error shapes this package produces (openAIHTTPError and ollamaHTTPError), so it works uniformly across every dialect.
A caller that wants to retry a 429 (see IsRetryableProviderError) must honor this delay — or apply its own conservative backoff when ok is false — rather than resending immediately. Resending a rate-limited or quota-exhausted request with no delay cannot succeed and only spends another billable request.
func ProviderStatusHint ¶
ProviderStatusHint maps a provider HTTP status to an actionable cause. The text is host-authored, never the provider's.
func ResolveProviderModel ¶
ResolveProviderModel fills in a provider's default model when the caller left it empty, and otherwise passes the requested model through.
sonar runs many models; DeepSeek Flash is only the default. An id the catalog does not list is accepted rather than rejected: the catalog is a pinned snapshot, so refusing unlisted models would make the harness unusable the day a provider ships a new one. The cost is that such a model has no catalog-derived context window or pricing, and surfaces that depend on those must degrade rather than assume — see catalog.FindModel's miss path.
func SanitizeImageName ¶
SanitizeImageName removes directory components, control characters, and bidirectional-format characters from display metadata. It retains ordinary Unicode and punctuation because the bounded name is never used as a path.
Types ¶
type AnthropicClient ¶
type AnthropicClient struct {
// contains filtered or unexported fields
}
AnthropicClient is a streaming chat adapter for the Anthropic Messages API (https://api.anthropic.com/v1/messages) and its wire-compatible proxies. Four catalog providers share this exact contract — see NewAnthropicProviderClient.
The Messages API differs from the OpenAI chat-completions shape in ways that break a naive OpenAI-compatible client:
- The system prompt is a top-level "system" field, not a message with role "system".
- Message content is an array of typed content blocks ("text", "tool_use", "tool_result", "image"), not a plain string.
- Tool use arrives as tool_use content blocks; results are sent back as tool_result content blocks inside a user-role message.
- Streaming uses a distinct SSE event vocabulary (message_start, content_block_start/delta/stop, message_delta, message_stop) instead of OpenAI's choices[].delta shape.
- Auth is the x-api-key header plus a required anthropic-version header, not "Authorization: Bearer".
- max_tokens is required on every request; OpenAI treats it as optional.
Credentials are supplied by the process environment only — never from config files.
func NewAnthropicClient ¶
func NewAnthropicClient(opts AnthropicOptions) (*AnthropicClient, error)
NewAnthropicClient builds a client against baseURL (for example https://api.anthropic.com). It performs no provider-identity resolution — callers that know a catalog provider identity should prefer NewAnthropicProviderClient, which also supplies doc-verified endpoint fallbacks and a catalog-derived default max_tokens.
func NewAnthropicProviderClient ¶
func NewAnthropicProviderClient(providerID, baseURL, model, apiKey string) (*AnthropicClient, error)
NewAnthropicProviderClient builds the Anthropic Messages API client for one of the four anthropic-family catalog providers. model must already be resolved (see ResolveProviderModel); apiKey must already be resolved from the process environment by the caller.
baseURL is whatever the caller's configuration resolved. When it is empty, or is an unresolved catalog template (Catwalk's "anthropic" entry names its endpoint as the literal string "$ANTHROPIC_API_ENDPOINT"), this falls back to the provider's real, doc-verified endpoint instead of guessing.
func (*AnthropicClient) BaseURL ¶
func (c *AnthropicClient) BaseURL() string
BaseURL returns the configured Anthropic base URL for display.
func (*AnthropicClient) ChatStream ¶
func (c *AnthropicClient) ChatStream(ctx context.Context, opts ChatOptions, fn func(StreamChunk) error) error
ChatStream streams a chat completion via the Anthropic Messages SSE protocol.
func (*AnthropicClient) Embed ¶
Embed is not implemented for the Anthropic Messages API adapter: Anthropic serves no embeddings endpoint. ICE and local embeddings continue to use Ollama when enabled.
func (*AnthropicClient) Model ¶
func (c *AnthropicClient) Model() string
func (*AnthropicClient) Ping ¶
func (c *AnthropicClient) Ping() error
Ping checks that the API key and model are usable.
func (*AnthropicClient) PingContext ¶
func (c *AnthropicClient) PingContext(ctx context.Context) error
PingContext prefers GET /v1/models when available, then falls back to a minimal non-streaming Messages request so proxies without a models list still work.
func (*AnthropicClient) SetModel ¶
func (c *AnthropicClient) SetModel(model string) error
SetModel updates the model id for subsequent requests.
type AnthropicOptions ¶
type AnthropicOptions struct {
BaseURL string
Model string
APIKey string
// MaxTokens is the max_tokens value sent when ChatOptions.MaxEvalTokens is
// zero. Defaults to AnthropicDefaultMaxOutputTokens when zero or negative.
MaxTokens int
}
AnthropicOptions constructs a remote Anthropic Messages API client.
type ChatOptions ¶
type ChatOptions struct {
Messages []Message
Tools []ToolDef
System string
MaxEvalTokens int // zero leaves provider generation uncapped
// DisableReasoning asks providers with a native control to emit only the
// visible answer. It is used for bounded child-expert reports where private
// reasoning is neither a valid result nor safe durable content. Providers
// without such a control may ignore it; callers must still reject a terminal
// response that contains no visible text.
DisableReasoning bool
// NumThread is a host-only local inference cap. Zero leaves the provider
// default unchanged; positive values are sent as Ollama num_thread.
NumThread int
// ExpectedContext pins a host-side context budget to the request. Provider
// managers use it to reject a turn whose model policy changed after the
// agent took its budget snapshot. Direct clients may ignore it.
ExpectedContext int
// ExpectedModel pins the tokenizer/model identity paired with the prompt
// estimate. ModelManager validates it atomically before dispatch; direct
// clients may ignore it.
ExpectedModel string
}
ChatOptions holds parameters for a chat request.
type Client ¶
type Client interface {
// ChatStream sends messages to the LLM and streams the response.
// The callback is called for each chunk. Return a non-nil error to abort.
ChatStream(ctx context.Context, opts ChatOptions, fn func(StreamChunk) error) error
// Ping checks if the LLM is reachable and the model is available.
Ping() error
// Model returns the current model name.
Model() string
// Embed generates embeddings for the given texts using the specified model.
Embed(ctx context.Context, model string, texts []string) ([][]float32, error)
}
Client is the interface for LLM providers.
type DeepSeekOptions ¶
type DeepSeekOptions struct {
// APIKey is the resolved secret value, read from the environment by the
// caller. Required: sonar has no unauthenticated mode.
APIKey string
// Model defaults to DeepSeekFlashModel. Any other value is rejected.
Model string
// BaseURL overrides the endpoint for a gateway or proxy that speaks the
// same dialect. Defaults to DeepSeekBaseURL.
BaseURL string
// Thinking enables chain-of-thought. Defaults to true via NewDeepSeekClient,
// matching the API default.
Thinking bool
// ReasoningEffort grades thinking depth. Defaults to DeepSeekDefaultEffort.
ReasoningEffort string
}
DeepSeekOptions configures the pinned DeepSeek client.
type ExpertModelResource ¶
type ExpertModelResource struct {
Name string
WeightBytes int64
ResidentBytes int64
ContextLength int
Location OllamaModelLocation
Active bool
Resident bool
Current bool
Selected bool
ExpertOnly bool
}
ExpertModelResource is one live Ollama model fact used by the expert admission planner. Inventory weights come from /api/tags and residency comes from /api/ps; Active is the manager's process-local usage record.
type ExpertModelSnapshot ¶
type ExpertModelSnapshot struct {
Models []ExpertModelResource
InventoryVerified bool
// contains filtered or unexported fields
}
ExpertModelSnapshot is a point-in-time union of every manager-active, Ollama-resident, current, and selected model. InventoryVerified means the selected weights were resolved from the live daemon rather than startup configuration. The lease field is intentionally opaque and process-local.
type ImageData ¶
type ImageData struct {
SHA256 string `json:"sha256,omitempty"`
Name string `json:"name,omitempty"`
MediaType string `json:"mime_type,omitempty"`
Size int64 `json:"size_bytes,omitempty"`
Width int `json:"width,omitempty"`
Height int `json:"height,omitempty"`
Data []byte `json:"-"`
}
ImageData is one image input for a multimodal model. Its durable fields form a path-free content-addressed reference; Data is transient provider input. Ollama's native API has no codec allowlist in its wire contract, so this layer validates a syntactic image/* media type without inventing a narrower set.
func NewImageData ¶
NewImageData validates and copies an image payload for provider transport.
func NewReferencedImageData ¶
func NewReferencedImageData(name, mediaType string, width, height int, data []byte) (ImageData, error)
NewReferencedImageData creates a complete content-addressed reference and retains a defensive copy of the bytes for the current provider request. Callers are responsible for placing the same bytes in the resolver's backing store before persisting the returned reference.
func (ImageData) Validate ¶
Validate reports whether the image is safe to send through a provider adapter. Provider adapters call this even for struct literals so callers cannot bypass NewImageData's admission checks.
func (ImageData) ValidateReference ¶
ValidateReference verifies the complete durable, path-free metadata. It does not require Data, allowing a restored session to validate before resolving the payload.
type LocalModel ¶
LocalModel records an Ollama model identity and the byte size of its local weights. Remote/cloud entries never appear in this inventory.
type Message ¶
type Message struct {
Role string `json:"role"` // system, user, assistant, tool
Content string `json:"content"`
// Images carry path-free, content-addressed metadata across a durable
// session boundary. Raw Data remains provider-only and is never serialized.
// Persistence writers must use agent.SanitizeMessagesForPersistence, which
// drops transient images without a complete durable reference.
Images []ImageData `json:"images,omitempty"`
// DurableContent is the bounded replacement for transient tool content when
// history crosses a persistence or compaction boundary. It is host-only:
// providers receive Content, while JSON/session/checkpoint writers must call
// agent.SanitizeMessagesForPersistence before serialization.
DurableContent string `json:"-"`
// ReasoningContent is the provider's chain-of-thought for this assistant
// message. DeepSeek requires it echoed back verbatim on every later request
// whenever the same message also carries ToolCalls; dropping it makes the
// API reject the next agent iteration with HTTP 400. Host-only (`json:"-"`)
// so private reasoning never crosses a session, transcript, or checkpoint
// boundary.
ReasoningContent string `json:"-"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
ToolName string `json:"tool_name,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
// HostOwned marks a message whose exact contents were validated and
// authored by the local host. It is deliberately not persisted or sent on
// the wire: restore code must re-derive the marker from durable state, so a
// user-authored message cannot forge host authority through JSON history.
HostOwned bool `json:"-"`
}
Message represents a conversation message.
type ModelContextPolicy ¶
ModelContextPolicy is the context contract for one exact Ollama model. Native is the verified model maximum reported by Ollama. Request is the num_ctx value sent on the wire; zero deliberately omits num_ctx. Effective is the window host-side budgeting may rely on. Ollama Cloud models use their verified native maximum and omit num_ctx so the service keeps its documented maximum-by-default behavior.
type ModelManager ¶
type ModelManager struct {
// contains filtered or unexported fields
}
func NewModelManager ¶
func NewModelManager(baseURL string, numCtx int) *ModelManager
func (*ModelManager) ActiveProviderName ¶
func (m *ModelManager) ActiveProviderName() string
ActiveProviderName is the catalog profile currently selected.
func (*ModelManager) BaseURL ¶
func (m *ModelManager) BaseURL() string
func (*ModelManager) ChatStream ¶
func (m *ModelManager) ChatStream(ctx context.Context, opts ChatOptions, fn func(StreamChunk) error) error
func (*ModelManager) ChatStreamForModel ¶
func (m *ModelManager) ChatStreamForModel(ctx context.Context, model string, opts ChatOptions, fn func(StreamChunk) error) error
func (*ModelManager) ClearCurrentModel ¶
func (m *ModelManager) ClearCurrentModel() error
ClearCurrentModel removes inference authority from the current selection. It is deliberately fail-closed: the selection is cleared before a best- effort unload, so an Ollama inventory reclassification cannot leave a remote model usable merely because releasing its resident runner failed.
func (*ModelManager) ClearRemoteProvider ¶
func (m *ModelManager) ClearRemoteProvider()
ClearRemoteProvider returns chat inference to the local Ollama path.
func (*ModelManager) Close ¶
func (m *ModelManager) Close()
func (*ModelManager) ConfigureLocalInventory ¶
func (m *ModelManager) ConfigureLocalInventory(required bool, models []LocalModel, verified bool)
ConfigureLocalInventory installs a verified local-weight inventory. The legacy name-only method intentionally records unknown sizes and therefore cannot admit inference in local-only mode.
func (*ModelManager) ConfigureLocalOnly ¶
func (m *ModelManager) ConfigureLocalOnly(required bool, models []string, verified bool)
ConfigureLocalOnly requires all inference model names to be proven by an Ollama inventory entry with local weights. An unverified inventory keeps the UI available for diagnostics, but no client can be selected until a later operation successfully refreshes the inventory.
func (*ModelManager) ConfigureOllamaCloudInventory ¶
func (m *ModelManager) ConfigureOllamaCloudInventory(models []string, verified bool)
ConfigureOllamaCloudInventory records exact cloud identities reported by the configured Ollama daemon. It never grants access by itself.
func (*ModelManager) ConfigureOllamaInventory ¶
func (m *ModelManager) ConfigureOllamaInventory(models []OllamaModel, verified bool)
ConfigureOllamaInventory installs the context and execution-location facts from one verified Ollama inventory snapshot. Model names are canonicalized so implicit :latest aliases share a single policy. An unverified snapshot clears prior facts instead of allowing stale cloud or native-context metadata to authorize later requests.
func (*ModelManager) ConfigureOllamaRuntimeInventory ¶
func (m *ModelManager) ConfigureOllamaRuntimeInventory(required bool, models []OllamaModel, verified bool)
ConfigureOllamaRuntimeInventory atomically installs all privacy-admission and context facts from one Ollama snapshot. Keeping local weights, cloud identity, grants, and native limits in one inference-serialized commit prevents a refresh from reclassifying a model between admission and request creation.
func (*ModelManager) ConfigureProviderCatalog ¶
func (m *ModelManager) ConfigureProviderCatalog(catalog config.ProviderConfig, localOnly bool, ollamaModel string)
ConfigureProviderCatalog installs the multi-profile provider definitions used by /provider. localOnly is the host privacy gate for remote base URLs.
func (*ModelManager) ConfigureRemoteProvider ¶
func (m *ModelManager) ConfigureRemoteProvider(client RemoteChatClient, contextSize int, label string) error
ConfigureRemoteProvider attaches a remote chat backend (OpenAI-compatible or Anthropic Messages API). When set, ordinary chat, ping, and model selection use the remote client. Local Ollama inventory, admission guards, and embeddings remain available for ICE.
func (*ModelManager) ConfiguredNumCtx ¶
func (m *ModelManager) ConfiguredNumCtx() int
ConfiguredNumCtx returns the host-configured local KV allocation (the value sent as options.num_ctx for local models), independent of cloud effective windows.
func (*ModelManager) ContextPolicy ¶
func (m *ModelManager) ContextPolicy(model string) ModelContextPolicy
ContextPolicy returns the current model-specific request and host-budget policy. Cloud status is derived only from a verified Ollama inventory; model name suffixes never grant cloud semantics.
func (*ModelManager) CurrentModel ¶
func (m *ModelManager) CurrentModel() string
func (*ModelManager) CurrentModelReceipt ¶
func (m *ModelManager) CurrentModelReceipt(ctx context.Context) ModelReceipt
CurrentModelReceipt inspects the active model's verified identity. It is a bounded read-only probe: inventory errors degrade to a receipt without digest/residency rather than failing the caller's settlement path.
func (*ModelManager) EffectiveContext ¶
func (m *ModelManager) EffectiveContext(model string) (int, bool)
EffectiveContext returns the current host-side context budget and whether it is known. Local requests always have an explicit effective allocation. Cloud requests are known only when their native maximum came from verified Ollama metadata.
func (*ModelManager) EmbedWithCurrentModel ¶
func (*ModelManager) GetClient ¶
func (m *ModelManager) GetClient(modelName string) (*OllamaClient, error)
func (*ModelManager) GrantOllamaCloudModel ¶
func (m *ModelManager) GrantOllamaCloudModel(model string) error
GrantOllamaCloudModel grants one verified cloud model for the current conversation. The grant is exact, ephemeral, and never covers remote-host models.
func (*ModelManager) ListLocalModelInventory ¶
func (m *ModelManager) ListLocalModelInventory(ctx context.Context) ([]LocalModel, error)
ListLocalModelInventory returns local identities with their actual weight sizes so memory admission never has to infer safety from a tag string.
func (*ModelManager) ListLocalModels ¶
func (m *ModelManager) ListLocalModels(ctx context.Context) ([]string, error)
ListLocalModels returns only models with local weights. Ollama cloud entries are deliberately excluded so a "local-only" routing decision cannot silently cross the network.
func (*ModelManager) ListOllamaModels ¶
func (m *ModelManager) ListOllamaModels(ctx context.Context) ([]OllamaModel, error)
ListOllamaModels returns the configured Ollama host's unfiltered inventory. Privacy and routing policy must be applied by the caller using Location and Capabilities; unlike ListLocalModels, cloud and remote-host entries remain.
func (*ModelManager) ListRunningOllamaModels ¶
func (m *ModelManager) ListRunningOllamaModels(ctx context.Context) ([]OllamaRunningModel, error)
func (*ModelManager) LocalModelWeightBytes ¶
func (m *ModelManager) LocalModelWeightBytes(model string) int64
LocalModelWeightBytes returns the cached local weight size for model when known from inventory.
func (*ModelManager) Model ¶
func (m *ModelManager) Model() string
func (*ModelManager) NumCtx ¶
func (m *ModelManager) NumCtx() int
func (*ModelManager) OllamaVersion ¶
func (m *ModelManager) OllamaVersion(ctx context.Context) (string, error)
func (*ModelManager) Ping ¶
func (m *ModelManager) Ping() error
func (*ModelManager) PingContext ¶
func (m *ModelManager) PingContext(ctx context.Context) error
PingContext checks the currently selected provider/model while preserving the caller's cancellation and deadline across the actual network request.
func (*ModelManager) PingModel ¶
func (m *ModelManager) PingModel(ctx context.Context, model string) error
func (*ModelManager) PrepareExpertModels ¶
func (m *ModelManager) PrepareExpertModels(ctx context.Context, selected []string) (ExpertModelSnapshot, error)
PrepareExpertModels serializes expert consultations sharing one manager, refreshes live weights and residency, and returns an opaque cleanup lease. Ordinary current-model and embedding calls share a context-aware admission boundary and cannot change residency until the lease is released.
func (*ModelManager) ProviderCatalog ¶
func (m *ModelManager) ProviderCatalog() []ProviderDescriptor
ProviderCatalog returns descriptors for every installed profile.
func (*ModelManager) ProviderNames ¶
func (m *ModelManager) ProviderNames() []string
ProviderNames lists configured profile names for /provider list.
func (*ModelManager) PullOllamaModel ¶
func (m *ModelManager) PullOllamaModel(ctx context.Context, model string, fn func(OllamaPullProgress) error) error
func (*ModelManager) ReleaseExpertModels ¶
func (m *ModelManager) ReleaseExpertModels(ctx context.Context, snapshot ExpertModelSnapshot) error
ReleaseExpertModels unloads only selected models that were not protected by pre-existing non-expert residency, are still non-current, and have not gained a non-expert process-local user since the snapshot. It always releases the consultation gate, even when one unload fails.
func (*ModelManager) RemoteProvider ¶
func (m *ModelManager) RemoteProvider() bool
RemoteProvider reports whether chat inference uses a non-Ollama adapter.
func (*ModelManager) RemoteProviderLabel ¶
func (m *ModelManager) RemoteProviderLabel() string
RemoteProviderLabel is a short UI/startup label (for example "xai").
func (*ModelManager) RevokeOllamaCloudGrants ¶
func (m *ModelManager) RevokeOllamaCloudGrants()
func (*ModelManager) RevokeOllamaCloudModel ¶
func (m *ModelManager) RevokeOllamaCloudModel(model string)
func (*ModelManager) SetCurrentModel ¶
func (m *ModelManager) SetCurrentModel(model string) error
func (*ModelManager) SetNumCtx ¶
func (m *ModelManager) SetNumCtx(numCtx int) error
SetNumCtx updates the local KV-cache allocation used for subsequent local Ollama requests. Cloud profiles keep their native maximum. The current local client is rebuilt so the next turn picks up the new window; in-flight turns keep their frozen snapshot.
func (*ModelManager) ShowOllamaModel ¶
func (m *ModelManager) ShowOllamaModel(ctx context.Context, model string) (OllamaModelInfo, error)
func (*ModelManager) SwitchProvider ¶
func (m *ModelManager) SwitchProvider(name string) error
SwitchProvider activates a named profile from the installed catalog. Remote profiles resolve API keys from the process environment at switch time.
func (*ModelManager) SwitchProviderContext ¶
func (m *ModelManager) SwitchProviderContext(ctx context.Context, name string) error
SwitchProviderContext activates a named profile while allowing callers to cancel admission and bounded local-inventory refresh work. Once the provider mutation begins it is committed atomically under the manager locks; a cancellation that arrives during the best-effort inventory refresh only shortens that refresh and does not report a half-applied switch.
type ModelReceipt ¶
type ModelReceipt struct {
Name string
Digest string
Provider string
Remote bool
// VRAMBytes/TotalBytes report weights residency from the runtime's process
// inventory. VRAMBytes < TotalBytes means part of the model runs on CPU.
VRAMBytes int64
TotalBytes int64
OffloadKnown bool
}
ModelReceipt binds a run to the exact inference artifact it used. Digest and residency come from the live local inventory; both stay empty/unknown for remote providers or when the runtime cannot be reached, so a consumer can always distinguish "not offloaded" from "not inspected".
type OllamaClient ¶
type OllamaClient struct {
// contains filtered or unexported fields
}
OllamaClient is a deliberately small HTTP adapter for the local Ollama API. Keeping the wire client here avoids linking Ollama's server/runtime graph (GPU runners, archives, SSH helpers, and registry code) into this CLI.
func NewOllamaClient ¶
func NewOllamaClient(baseURL, model string, numCtx int) (*OllamaClient, error)
NewOllamaClient creates a new Ollama client without mutating process-wide environment state. baseURL wins over OLLAMA_HOST; otherwise the loopback default used by Ollama is selected.
func (*OllamaClient) BaseURL ¶
func (o *OllamaClient) BaseURL() string
BaseURL returns the configured Ollama base URL for display.
func (*OllamaClient) ChatStream ¶
func (o *OllamaClient) ChatStream(ctx context.Context, opts ChatOptions, fn func(StreamChunk) error) error
ChatStream sends a chat request and streams the response via callback.
func (*OllamaClient) Embed ¶
func (o *OllamaClient) Embed(ctx context.Context, model string, texts []string) ([][]float32, error)
Embed generates embeddings for the given texts using the specified model.
func (*OllamaClient) ListModels ¶
func (o *OllamaClient) ListModels(ctx context.Context) ([]OllamaModel, error)
ListModels returns every model reported by Ollama. Unlike ListLocalModelInventory, it intentionally does not apply privacy, memory, or static-catalog policy.
func (*OllamaClient) ListRunningModels ¶
func (o *OllamaClient) ListRunningModels(ctx context.Context) ([]OllamaRunningModel, error)
func (*OllamaClient) Model ¶
func (o *OllamaClient) Model() string
func (*OllamaClient) Ping ¶
func (o *OllamaClient) Ping() error
Ping checks Ollama is running and the model exists.
func (*OllamaClient) PingContext ¶
func (o *OllamaClient) PingContext(ctx context.Context) error
PingContext checks model availability within the caller's cancellation and deadline rather than creating an unrelated background operation.
func (*OllamaClient) PullModel ¶
func (o *OllamaClient) PullModel(ctx context.Context, model string, fn func(OllamaPullProgress) error) error
PullModel streams bounded Ollama pull progress. Cancellation propagates through the request context and callback errors stop the stream immediately.
func (*OllamaClient) ShowModel ¶
func (o *OllamaClient) ShowModel(ctx context.Context, model string) (OllamaModelInfo, error)
ShowModel obtains capability and model-native metadata without verbose token tables, keeping the response within the shared metadata bound.
type OllamaModel ¶
type OllamaModel struct {
Name string
Digest string
ModifiedAt time.Time
SizeBytes int64
RemoteModel string
RemoteHost string
Location OllamaModelLocation
Format string
Family string
Families []string
ParameterSize string
Quantization string
ContextLength int64
Capabilities []string
}
OllamaModel is one inventory entry returned by the configured Ollama host. It preserves cloud stubs and custom models; policy filtering belongs above the wire layer.
type OllamaModelInfo ¶
type OllamaModelInfo struct {
Model OllamaModel
Capabilities []string
ModelInfo map[string]any
Parameters string
Template string
System string
License string
NativeContext int64
}
OllamaModelInfo contains the authoritative metadata returned by /api/show.
type OllamaModelLocation ¶
type OllamaModelLocation string
OllamaModelLocation describes where Ollama executes a model. LocationLocal means /api/tags proved local weights; LocationCloud is an Ollama cloud stub. Unknown is deliberately retained instead of guessing from a model name.
const ( OllamaModelLocationUnknown OllamaModelLocation = "unknown" OllamaModelLocationLocal OllamaModelLocation = "local" OllamaModelLocationCloud OllamaModelLocation = "cloud" OllamaModelLocationRemote OllamaModelLocation = "remote-host" )
type OllamaPullProgress ¶
type OllamaRunningModel ¶
type OllamaRunningModel struct {
Model OllamaModel
ExpiresAt time.Time
SizeVRAM int64
ContextLength int64
}
OllamaRunningModel is transient residency data from /api/ps.
type OpenAICompatibleClient ¶
type OpenAICompatibleClient struct {
// contains filtered or unexported fields
}
OpenAICompatibleClient is a streaming chat adapter for OpenAI-compatible HTTP APIs (xAI, OpenAI, OpenRouter, local vLLM, etc.). Credentials are supplied by the process environment only — never from config files.
func NewDeepSeekClient ¶
func NewDeepSeekClient(opts DeepSeekOptions) (*OpenAICompatibleClient, error)
NewDeepSeekClient builds the pinned DeepSeek chat client.
func NewOpenAICompatibleClient ¶
func NewOpenAICompatibleClient(opts OpenAICompatibleOptions) (*OpenAICompatibleClient, error)
NewOpenAICompatibleClient builds a client against baseURL (for example https://api.x.ai/v1). The API key may be empty only for local open servers.
func (*OpenAICompatibleClient) BaseURL ¶
func (c *OpenAICompatibleClient) BaseURL() string
func (*OpenAICompatibleClient) ChatStream ¶
func (c *OpenAICompatibleClient) ChatStream(ctx context.Context, opts ChatOptions, fn func(StreamChunk) error) error
ChatStream streams a chat completion via the OpenAI SSE protocol.
func (*OpenAICompatibleClient) Embed ¶
Embed is not implemented for the generic OpenAI-compatible chat adapter. ICE and local embeddings continue to use Ollama when enabled.
func (*OpenAICompatibleClient) Model ¶
func (c *OpenAICompatibleClient) Model() string
func (*OpenAICompatibleClient) Ping ¶
func (c *OpenAICompatibleClient) Ping() error
Ping checks that the API key and model are usable.
func (*OpenAICompatibleClient) PingContext ¶
func (c *OpenAICompatibleClient) PingContext(ctx context.Context) error
PingContext prefers GET /models when available, then falls back to a minimal non-streaming chat completion so APIs without a models list still work.
func (*OpenAICompatibleClient) SetModel ¶
func (c *OpenAICompatibleClient) SetModel(model string) error
SetModel updates the model id for subsequent requests.
type OpenAICompatibleOptions ¶
type OpenAICompatibleOptions struct {
BaseURL string
Model string
APIKey string
// Dialect selects provider-specific request extensions. Empty is plain
// OpenAI. DialectDeepSeek adds the `thinking` toggle and the
// `reasoning_content` round-trip that DeepSeek's tool-call turns require.
Dialect string
// Thinking requests chain-of-thought for dialects that expose a toggle.
// Ignored by dialects without one.
Thinking bool
// ReasoningEffort grades thinking depth ("high", "max") when Thinking is on.
ReasoningEffort string
}
OpenAICompatibleOptions constructs a remote OpenAI-style client.
type ProviderDescriptor ¶
type ProviderDescriptor struct {
Name string
Type string
Model string
APIKeyEnv string
Remote bool
Active bool
KeyPresent bool // process env has a non-empty value for APIKeyEnv
}
ProviderDescriptor is a UI-safe view of one catalog profile. It never includes secret values — only whether the configured env var is currently set.
type ProviderTiming ¶
type ProviderTiming struct {
TimeToFirstToken time.Duration
TotalDuration time.Duration
LoadDuration time.Duration
PromptEvalDuration time.Duration
EvalDuration time.Duration
}
ProviderTiming carries the provider-reported request timings plus the client-measured time to first streamed token. Durations the provider did not report stay zero; consumers must treat zero as "not reported", never as "instant".
type RemoteChatClient ¶
type RemoteChatClient interface {
Client
// SetModel updates the model id for subsequent requests.
SetModel(model string) error
// PingContext checks reachability like Ping but honors ctx's
// cancellation and deadline.
PingContext(ctx context.Context) error
}
RemoteChatClient is the superset of Client that ModelManager needs to route ordinary chat, ping, and model switching through a single active remote provider (see ModelManager.ConfigureRemoteProvider). It exists so NewProviderClient can hand back distinct dialect implementations (the OpenAI-compatible client, the Anthropic Messages API client) through one interface without widening Client itself — every existing Client implementation, including the fakes littered across the agent/ice test suites, would otherwise be forced to grow SetModel/PingContext methods they never use.
func NewProviderClient ¶
func NewProviderClient(providerType, baseURL, model, apiKey string) (RemoteChatClient, error)
NewProviderClient builds the chat client for a resolved provider profile.
The dialect is chosen from the provider's identity, not from its endpoint shape. The catalog calls DeepSeek "openai-compat", which is true about the URL and wrong about the request contract: it needs a thinking toggle and a reasoning_content round-trip no generic OpenAI client sends. Selecting on wire type alone would produce a client that connects, answers once, and then fails every tool-call turn with a 400.
The anthropic-family branch below is the one place selecting by wire type would actually have been safe — Catwalk's "anthropic" type genuinely means "speaks the real Anthropic Messages API" for all four providers that carry it — but identity is still used, for the same reason as DeepSeek: it keeps dialect selection independent of how Catwalk chooses to classify a provider upstream, and it is one line longer than a wire-type switch would have been.
Both the startup path and the runtime /provider switch go through here so the two can never disagree about which dialect a provider gets.
type StreamChunk ¶
type StreamChunk struct {
Text string // incremental text content
Reasoning string // provider-native thinking/reasoning delta
ToolCalls []ToolCall // tool calls (usually in final chunk)
Done bool // true on the last chunk
EvalCount int // tokens generated (only on Done)
PromptEvalCount int // prompt tokens evaluated (only on Done)
// FinishReason is the provider's terminal reason (only on Done): "stop",
// "tool_calls", or "length" for a truncated generation. Empty when the
// provider did not report one — a truncated response must therefore be
// detected by "length", never by the absence of "stop".
FinishReason string
// Timing is attached to the terminal chunk when any timing fact is known.
Timing *ProviderTiming
}
StreamChunk is a piece of a streaming response.
type ToolBehavior ¶
type ToolBehavior struct {
Declared bool
ReadOnly bool
Destructive bool
Idempotent bool
OpenWorld bool
}
ToolBehavior is the bounded presentation projection of standard MCP tool annotations. It is untrusted server metadata and must never by itself alter authorization, durable effect classification, or recovery semantics.
type ToolCall ¶
type ToolCall struct {
ID string `json:"id"`
Name string `json:"name"`
Arguments map[string]any `json:"arguments"`
}
ToolCall represents a tool invocation requested by the LLM.
type ToolDef ¶
type ToolDef struct {
Name string `json:"name"`
Description string `json:"description"`
Parameters map[string]any `json:"parameters"` // JSON Schema
// DisplayName and Behavior are host-only MCP presentation metadata.
// They must never be sent to model providers as part of a tool schema.
DisplayName string `json:"-"`
Behavior ToolBehavior `json:"-"`
}
ToolDef defines a tool the LLM can call.