Documentation
¶
Index ¶
- Constants
- type BuiltinToolKind
- type Candidate
- type CheapFirst
- type Citation
- type Client
- type Config
- type FallbackChain
- type HealthOptions
- type Message
- type MultiStreamer
- type MultiStreamerOption
- type Policy
- type ProbeResult
- type Provider
- type ProviderProfile
- type Role
- type StreamEvent
- type StreamEventKind
- type Streamer
- type Tier
- type Tool
- type ToolCall
Constants ¶
const ( // OpenAIAuthEndpoint is the ChatGPT-subscription Responses-API // endpoint reachable via OAuth bearer auth. Distinct from // api.openai.com/v1/responses both in URL and request contract. OpenAIAuthEndpoint = "https://chatgpt.com/backend-api/codex/responses" // OpenAIAuthDefaultInstructions is sent when the caller's message // slate has no system role. The Codex backend rejects requests // with empty/missing instructions ("Instructions are required"), // so we always send something. OpenAIAuthDefaultInstructions = "You are a helpful assistant." )
const AnthropicDefaultMaxTokens int64 = 8192
AnthropicDefaultMaxTokens is the upper bound on a single completion. Anthropic requires this — there is no "no limit" sentinel. 8192 is generous for code edits and within every claude-* model's per-turn cap; bigger documents land via tool_use round-trips, not single turns.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BuiltinToolKind ¶
type BuiltinToolKind string
BuiltinToolKind is a provider-native capability exposed directly by the model provider rather than by yottacode's local tool registry.
const ( BuiltinToolWebSearch BuiltinToolKind = "web_search" BuiltinToolXSearch BuiltinToolKind = "x_search" BuiltinToolCodeInterpreter BuiltinToolKind = "code_interpreter" )
type Candidate ¶
type Candidate struct {
Streamer Streamer
Label string
Tier Tier
Profile ProviderProfile
}
Candidate is one dispatch target the router may select. Label is the human-readable identifier surfaced in EventFallback so the user can see which provider/model just failed and which one took over — e.g. "anthropic/claude-haiku-4-5" or "openai/gpt-4o". Tier is optional metadata consumed by tier-aware policies (CheapFirst); policies that do not consult it (FallbackChain) can ignore it. Profile lets the router satisfy the Client interface — the TUI's system-prompt composition and connection probe both want a ProviderProfile, and the router's representative profile is the first candidate's.
type CheapFirst ¶
type CheapFirst struct{}
CheapFirst sorts candidates by tier ascending: cheap → balanced → expensive → unspecified. Uses a stable sort, so candidates with the same tier (or no tier) preserve their declared order — this lets the user disambiguate two cheap-tier candidates by listing the preferred one first.
func (CheapFirst) Name ¶
func (CheapFirst) Name() string
func (CheapFirst) Order ¶
func (CheapFirst) Order(candidates []Candidate) []Candidate
type Citation ¶
type Citation struct {
Type string `json:"type,omitempty"`
Title string `json:"title,omitempty"`
URL string `json:"url,omitempty"`
FileID string `json:"file_id,omitempty"`
Filename string `json:"filename,omitempty"`
}
Citation is a source reference attached to an assistant message by a provider-native search or retrieval capability.
type Client ¶
type Client interface {
Streamer
Profile() ProviderProfile
}
Client is the richer adapter surface used by entry points. The agent loop still only depends on ChatStream; Profile is for UI, diagnostics, and future routing decisions.
func New ¶
New returns a Client wired to the provider implied by baseURL + model. Kept for compatibility with the original constructor shape.
func NewWithConfig ¶
NewWithConfig returns a Client wired to the provider implied by the config.
Routing rules:
- api.openai.com + (o1*, o3*, o4*, gpt-5*) → Responses API (the only way to surface reasoning summaries for OpenAI's reasoning models — Chat Completions hides them.)
- api.openai.com + Responses-only built-in tools enabled (`web_search`, `code_interpreter`) → Responses API
- everything else → OpenAI-compatible Chat Completions (Ollama, Llama Stack, vLLM, xAI, Together, OpenRouter, NVIDIA NIM, Groq, real OpenAI for non-reasoning models like gpt-4o / gpt-4.1.)
Auto-detection covers ~all real-world cases. There is no flag override — if you point at api.openai.com with an o-series or gpt-5 model, you get reasoning streaming for free.
type Config ¶
type Config struct {
BaseURL string
APIKey string
Model string
ProviderOverride Provider
ReasoningEffort string
EnableWebSearch bool
DisableWebSearch bool
EnableXSearch bool
EnableCodeInterpreter bool
SearchAllowedDomains []string
SearchExcludedDomains []string
XSearchAllowedHandles []string
XSearchExcludedHandles []string
XSearchFromDate string
XSearchToDate string
}
Config configures adapter construction and provider-native capabilities.
type FallbackChain ¶
type FallbackChain struct{}
FallbackChain tries candidates in declared order. The simplest policy and the right default when the user has hand-ordered their providers by preference (typical: "use Anthropic by default, fall through to OpenAI if Anthropic is degraded").
func (FallbackChain) Name ¶
func (FallbackChain) Name() string
func (FallbackChain) Order ¶
func (FallbackChain) Order(candidates []Candidate) []Candidate
type HealthOptions ¶
HealthOptions configures router-level health observation. Zero value means disabled (the router never marks a candidate as degraded). The router consults this tracker between policy ordering and dispatch: degraded candidates get demoted to the back of the order so the next request prefers candidates that have been working recently.
Defaults documented in DefaultsTOML and applied by cli.BuildRouter:
- Window = 60 * time.Second — sliding-window length
- Threshold = 3 — failures-in-window that mark degraded
Set Threshold = 0 to disable observation entirely (every candidate stays "healthy" regardless of error history).
type Message ¶
type Message struct {
Role Role `json:"role"`
Content string `json:"content,omitempty"`
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
Citations []Citation `json:"citations,omitempty"`
StopReason string `json:"stop_reason,omitempty"`
ToolCallID string `json:"tool_call_id,omitempty"`
}
Message is the neutral conversation unit the agent persists and replays. Tool-role messages carry ToolCallID to bind them to the call they answer.
type MultiStreamer ¶
type MultiStreamer struct {
// contains filtered or unexported fields
}
MultiStreamer dispatches a ChatStream call across an ordered set of candidate Streamers, falling through on early failures according to the configured Policy. The whole point of yottacode being multi- provider — a single provider degradation should not interrupt the user's turn if another configured provider can serve it.
Fallback is intentionally conservative:
- A failure (EventErr) emitted before any user-visible token has streamed is "early" and triggers fallback to the next candidate.
- A failure emitted after one or more EventTokenDelta or EventReasoning events is "mid-stream" and bubbles up as a terminal error. Silently restarting on a different provider mid-reply would corrupt the conversation log and confuse the user — we'd rather surface the error and let the agent loop retry the whole turn.
- When the last candidate fails, its error bubbles up regardless of timing — there is nothing left to fall through to.
MultiStreamer emits EventFallback between the failed attempt and the next attempt, carrying enough metadata for the TUI to render a loud "fell through from X to Y because Z" line. Silent fallback is the failure mode this whole design is built to avoid.
func NewMultiStreamer ¶
func NewMultiStreamer(candidates []Candidate, policy Policy, opts ...MultiStreamerOption) (*MultiStreamer, error)
NewMultiStreamer constructs a router over the supplied candidates. Returns an error if no candidates were supplied or no policy was set — both are programmer errors, not runtime conditions, so we fail loud at construction rather than at request time. Variadic options configure health observation and (in tests) the clock.
func (*MultiStreamer) Candidates ¶
func (m *MultiStreamer) Candidates() []Candidate
Candidates returns a copy of the configured candidate list. Useful for diagnostics and `/router list`-style introspection commands.
func (*MultiStreamer) ChatStream ¶
func (m *MultiStreamer) ChatStream(ctx context.Context, messages []Message, tools []Tool) <-chan StreamEvent
ChatStream applies the policy to order the candidates, then drives them in sequence until one succeeds, all fail, or ctx is canceled. When health observation is enabled, recently-degraded candidates are stable-demoted to the back of the order so this request prefers candidates that have been working. The returned channel is closed when the request finally terminates.
func (*MultiStreamer) PolicyName ¶
func (m *MultiStreamer) PolicyName() string
PolicyName returns the name of the active policy. Mirrors what the MultiStreamer would emit on EventFallback.FallbackPolicy.
func (*MultiStreamer) Profile ¶
func (m *MultiStreamer) Profile() ProviderProfile
Profile satisfies adapter.Client by returning the first candidate's profile. The router does not synthesize a merged profile — capability gating across heterogeneous providers is a Phase 2 concern. For step 1.5 the convention is: list candidates in capability-aligned order; the first one is the representative for system-prompt composition.
type MultiStreamerOption ¶
type MultiStreamerOption func(*MultiStreamer)
MultiStreamerOption tunes router behavior at construction time. Variadic to keep the original two-arg NewMultiStreamer signature backward compatible with callers (and tests) that don't care about the new knobs.
func WithHealth ¶
func WithHealth(opts HealthOptions) MultiStreamerOption
WithHealth attaches a sliding-window health tracker. Zero values for Window or Threshold disable tracking — useful when a caller wants to pass an "unset" config struct without conditionally choosing between two constructors. Defaults are documented on HealthOptions.
type Policy ¶
type Policy interface {
// Order returns the candidates in the order they should be tried.
// Implementations must not mutate the input slice.
Order(candidates []Candidate) []Candidate
// Name is a short identifier surfaced in EventFallback for telemetry
// and TUI logging. e.g. "fallback-chain", "cheap-first".
Name() string
}
Policy decides the order in which a MultiStreamer tries its candidates for one request. It is invoked once per ChatStream call, with the full candidate slice; the returned slice is iterated in order until one succeeds (returns EventDone) or the list is exhausted.
Policy is intentionally not request-aware in this iteration. Smarter per-request routing (capability gating, request classification) is a Phase 2 concern; declarative policies first.
type ProbeResult ¶
type ProbeResult struct {
Profile ProviderProfile `json:"profile"`
BaseURL string `json:"base_url"`
Model string `json:"model"`
HTTPStatus int `json:"http_status,omitempty"`
EndpointReachable bool `json:"endpoint_reachable"`
AuthOK bool `json:"auth_ok"`
ModelVisible bool `json:"model_visible"`
AvailableModels []string `json:"available_models,omitempty"`
Issues []string `json:"issues,omitempty"`
Warnings []string `json:"warnings,omitempty"`
}
ProbeResult is the active diagnostics result for one provider config.
func Probe ¶
func Probe(ctx context.Context, cfg Config) ProbeResult
Probe runs a lightweight active diagnostics pass against the configured provider. It currently validates the /models surface because that is cheap, widely available on supported endpoints, and enough to distinguish network, auth, and model-visibility failures.
Anthropic is skipped: it does expose /v1/models, but with a different auth header shape (`x-api-key` rather than `Authorization: Bearer`) that the OpenAI-compatible probe gets wrong. Until we wire a dedicated Anthropic probe, the static profile is the diagnostic.
func StaticDiagnostics ¶
func StaticDiagnostics(cfg Config) ProbeResult
StaticDiagnostics resolves provider routing and static config diagnostics without making any network requests.
type Provider ¶
type Provider string
Provider identifies the upstream model vendor or compatibility family that yottacode believes it is talking to.
type ProviderProfile ¶
type ProviderProfile struct {
Provider Provider `json:"provider"`
UsesResponsesAPI bool `json:"uses_responses_api"`
SupportsReasoning bool `json:"supports_reasoning"`
SupportsWebSearch bool `json:"supports_web_search"`
SupportsXSearch bool `json:"supports_x_search"`
SupportsCodeInterpreter bool `json:"supports_code_interpreter"`
EnabledBuiltinTools []BuiltinToolKind `json:"enabled_builtin_tools,omitempty"`
Issues []string `json:"issues,omitempty"`
Warnings []string `json:"warnings,omitempty"`
}
ProviderProfile is the resolved view of provider capabilities for a given adapter instance.
type StreamEvent ¶
type StreamEvent struct {
Kind StreamEventKind
Token string
ProviderToolName string
ProviderToolPhase string
ProviderToolDetail string
Final *Message
Err error
// Fallback metadata — populated only when Kind == EventFallback.
FallbackFrom string
FallbackTo string
FallbackReason string
FallbackPolicy string
}
StreamEvent is one item emitted while a completion streams.
TokenDelta carries a chunk of assistant text to render live. Done carries the fully-accumulated assistant Message (with tool_calls, if any). Err carries a terminal error; no further events will follow. Fallback carries router metadata when MultiStreamer falls through from one candidate to another; the From/To/Reason/Policy fields are populated.
type StreamEventKind ¶
type StreamEventKind int
StreamEventKind discriminates StreamEvent variants.
const ( EventTokenDelta StreamEventKind = iota // EventReasoning is emitted for "thinking" tokens produced by reasoning // models (Qwen 3, DeepSeek R1, etc.). Render these subtly so the user can // see the model is working without mistaking them for the final answer. EventReasoning EventProviderTool EventDone EventErr // EventFallback is emitted by MultiStreamer when one candidate fails // before producing any visible output and the active policy elects to // retry on a different candidate. Surfaced loudly in the TUI so a // silent provider-degradation never hides behind the abstraction. EventFallback // EventStreamProgress is a heartbeat for stream activity that has no // visible text — used by adapters to surface in-flight tool-call // argument generation so the live "tok/s" indicator keeps moving on // turns where the model produces a function call without any // reasoning summary or text output (notably gpt-5* on the Responses // API). No payload: the consumer just increments its activity counter. EventStreamProgress )
type Streamer ¶
type Streamer interface {
ChatStream(ctx context.Context, messages []Message, tools []Tool) <-chan StreamEvent
}
Streamer is the slice of behavior every concrete adapter implements. Kept mirrored with agent.Streamer so the agent loop can take any adapter the router hands it.
type Tier ¶
type Tier string
Tier is the coarse cost/capability bucket a candidate model belongs to. Mirrors the string enum already used in config.toml's [[providers.models]] entries (cheap | balanced | expensive). Empty tier is "unspecified" and sorts last under cost-aware policies.