adapter

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Jun 24, 2026 License: MIT Imports: 25 Imported by: 0

Documentation

Index

Constants

View Source
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."
)
View Source
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.

View Source
const ChatDefaultMaxTokens int64 = 8192

ChatDefaultMaxTokens is the per-turn output cap sent on every Chat Completions request. Some OpenAI-compatible providers (NVIDIA NIM in particular) treat a missing max_tokens as zero and include that in the input+output ≤ window budget check — so a request with a full transcript and no max_tokens is rejected with 400 even when the input alone fits. Setting a real ceiling avoids that and reserves output headroom on every other provider too. 8192 matches the Anthropic adapter default — generous for code edits, within every vendor's per-turn cap, and not so large that a runaway response drains the wallet.

Exported so callers that need to plan around the reserved output (e.g. the auto-summarize input budget) can subtract the same value the adapter actually sends.

Variables

This section is empty.

Functions

func EffortInapplicableReason added in v0.3.0

func EffortInapplicableReason(cfg Config, provider Provider) string

EffortInapplicableReason explains, in one user-facing phrase, why the configured reasoning effort will NOT take effect for this provider+model — or "" when it will (or when no effort is set, which is never a no-op worth warning about). Mirrors exactly the gating each adapter applies, so the TUI can warn that a setting is silently a no-op on the active model instead of leaving the user guessing.

func IsContextOverflow added in v0.3.0

func IsContextOverflow(err error) bool

IsContextOverflow reports whether err is a provider-side rejection for exceeding the model's context window — the signal passive window-drift correction listens for. Conservative by design: an error that matches no known marker is never classified as overflow, because a false positive shrinks a window pin that then has to be re-proven upward by live traffic.

func SetOpenAIAuthAccountForTest added in v0.3.0

func SetOpenAIAuthAccountForTest(a *OpenAIAuthAccount)

SetOpenAIAuthAccountForTest seeds the cache from tests in other packages. Test-only.

func SetOpenAIAuthRateLimitForTest added in v0.3.0

func SetOpenAIAuthRateLimitForTest(memo *OpenAIAuthRateLimit)

SetOpenAIAuthRateLimitForTest seeds the package's rate-limit memo from tests in other packages (the /usage TUI renderer in particular). Test-only — never call from production paths.

func SetRateLimitForTest added in v0.3.0

func SetRateLimitForTest(provider Provider, snap *RateLimitSnapshot)

SetRateLimitForTest seeds (or clears, with nil) the snapshot for a provider so the /usage renderer can be exercised without a live API round-trip.

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

func New(baseURL, apiKey, model string) Client

New returns a Client wired to the provider implied by baseURL + model. Kept for compatibility with the original constructor shape.

func NewWithConfig

func NewWithConfig(cfg Config) Client

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
	// ModelMaxOutput and ModelSupportsThinking carry catalog-derived
	// facts about the active model so budget-based reasoning providers
	// (Anthropic, Gemini) can size a thinking budget without the adapter
	// package importing catalog (which would cycle:
	// adapter → catalog → auth/openai → adapter). Callers that have the
	// catalog handy fill these from catalog.FindByID; both are
	// zero/nil-safe — an unknown model leaves reasoning at the provider
	// default. ModelSupportsThinking is a tristate: nil = unknown.
	ModelMaxOutput         int
	ModelSupportsThinking  *bool
	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

type HealthOptions struct {
	Window    time.Duration
	Threshold int
}

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 ImageBlock added in v0.3.0

type ImageBlock struct {
	Data      []byte `json:"data"`
	MediaType string `json:"media_type"` // e.g. "image/png", "image/jpeg"
}

ImageBlock carries a single image as raw bytes plus its MIME type. Used in tool-result messages so the model can see screenshots, photos, diagrams, etc. alongside the textual output.

type Message

type Message struct {
	Role       Role         `json:"role"`
	Content    string       `json:"content,omitempty"`
	Images     []ImageBlock `json:"images,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"`
	// CacheHeadBytes marks how many leading bytes of Content form a
	// stable, cacheable prefix — set by the composer on the system
	// message to the length of the static base prompt, ahead of the
	// per-turn memory tail. The Anthropic adapter splits Content there
	// and puts a cache breakpoint on the head, so the big static prefix
	// keeps hitting the prompt cache even when the memory tail changes
	// between user turns. Other adapters ignore it (their providers
	// cache the longest stable prefix automatically). 0 = no hint.
	CacheHeadBytes int `json:"cache_head_bytes,omitempty"`
	// Usage is the provider-reported token counts for the turn that
	// produced this message. Pointer so nil ≠ "zero tokens" — adapters
	// that didn't observe usage data leave it unset, and the /usage
	// command can tell the difference.
	Usage *Usage `json:"usage,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 OpenAIAuthAccount added in v0.3.0

type OpenAIAuthAccount struct {
	Email    string
	Plan     string
	Provider string // e.g. "google", "email" — login method
	ProbedAt time.Time
}

OpenAIAuthAccount captures what the best-effort backend-api probe learned about the signed-in ChatGPT account. Every field is optional — the probe degrades gracefully when the endpoint returns less than we hoped for, and the /usage renderer prints only what's actually populated.

The endpoints we hit are NOT documented by OpenAI. They're the same surface the official Codex CLI talks to; if the shape changes upstream, this probe will silently empty out (the renderer falls back to the 429 memo and the "subscription" label).

func LastOpenAIAuthAccount added in v0.3.0

func LastOpenAIAuthAccount() *OpenAIAuthAccount

LastOpenAIAuthAccount returns the cached probe result, or nil if the cache is empty or expired. Pure accessor — never makes a network call. Callers in interactive paths (e.g. /usage) should call ProbeOpenAIAuthAccount first to refresh.

func ProbeOpenAIAuthAccount added in v0.3.0

func ProbeOpenAIAuthAccount(ctx context.Context) *OpenAIAuthAccount

ProbeOpenAIAuthAccount fires a one-shot GET against the undocumented /backend-api/me endpoint, parses whatever account info comes back, caches it, and returns the result. nil return means the probe found nothing — either the user isn't logged in, the token can't reach the endpoint, the endpoint changed shape, or any HTTP failure. Callers must treat nil as "no data," not "error" — /usage renders provider notes from the 429 memo when the probe degrades.

Re-uses any cached result younger than openAIAuthAccountTTL.

type OpenAIAuthRateLimit added in v0.3.0

type OpenAIAuthRateLimit struct {
	Observed time.Time
	PlanType string
	ResetsAt time.Time
}

OpenAIAuthRateLimit captures the last 429 the openai-auth adapter saw. Populated as a side effect of formatRateLimitHint parsing and exposed via LastOpenAIAuthRateLimit() so the /usage command can surface plan + reset metadata for the subscription-based provider (no quota query endpoint exists; the 429 is the only public signal).

func LastOpenAIAuthRateLimit added in v0.3.0

func LastOpenAIAuthRateLimit() *OpenAIAuthRateLimit

LastOpenAIAuthRateLimit returns the most recent rate-limit memo captured by this process, or nil if no 429 was observed. The returned pointer is a copy — callers can read it without holding the package lock.

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 exposes /v1/models like the OpenAI-compatible providers but authenticates with x-api-key + anthropic-version headers rather than a Bearer token, so it gets a dedicated request shape (probeAnthropic) before folding into the shared response handling.

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.

const (
	ProviderOpenAI           Provider = "openai"
	ProviderOpenAIAuth       Provider = "openai-auth"
	ProviderCopilot          Provider = "copilot"
	ProviderXAI              Provider = "xai"
	ProviderOllama           Provider = "ollama"
	ProviderAnthropic        Provider = "anthropic"
	ProviderGemini           Provider = "gemini"
	ProviderOpenAICompatible Provider = "openai-compatible"
)

type ProviderProfile

type ProviderProfile struct {
	Provider                Provider `json:"provider"`
	UsesResponsesAPI        bool     `json:"uses_responses_api"`
	SupportsReasoning       bool     `json:"supports_reasoning"`
	SupportsImages          bool     `json:"supports_images"`
	SupportsWebSearch       bool     `json:"supports_web_search"`
	SupportsXSearch         bool     `json:"supports_x_search"`
	SupportsCodeInterpreter bool     `json:"supports_code_interpreter"`
	// SupportsUsageReporting indicates the adapter populates per-turn
	// Usage on its returned Message. False for local/free providers
	// (Ollama, NVIDIA NIM) where /usage has no meaningful surface.
	SupportsUsageReporting bool              `json:"supports_usage_reporting"`
	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 RateLimitSnapshot added in v0.3.0

type RateLimitSnapshot struct {
	Provider Provider
	Observed time.Time

	// Requests-per-window budget.
	HasRequests       bool
	RequestsLimit     int64
	RequestsRemaining int64
	RequestsReset     time.Time

	// Token budget. For OpenAI this is the single tokens bucket; for
	// Anthropic it's the combined tokens bucket (the most restrictive
	// limit in effect, per their docs).
	HasTokens       bool
	TokensLimit     int64
	TokensRemaining int64
	TokensReset     time.Time
}

RateLimitSnapshot is the parsed quota state from the most recent successful API response for a provider. Pay-per-use providers (OpenAI, Anthropic, xAI) return per-minute rate-limit headers on every 200, costing nothing extra beyond the inference call we already make. /usage surfaces this as a live "how much headroom is left this minute" gauge — distinct from cost (which we estimate from token counts) and from the openai-auth 429 memo (which is only populated reactively on a rate-limit error).

The Has* flags distinguish "header absent" from "header present and zero" so the renderer can omit a family entirely rather than show a misleading 0.

func LastRateLimit added in v0.3.0

func LastRateLimit(provider Provider) *RateLimitSnapshot

LastRateLimit returns the most recent rate-limit snapshot for a provider, or nil if none has been observed this run. The returned pointer is a copy — callers can read it without holding the lock.

type Role

type Role string

Role enumerates the speakers in a conversation.

const (
	RoleSystem    Role = "system"
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
	RoleTool      Role = "tool"
)

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.

const (
	TierUnspecified Tier = ""
	TierCheap       Tier = "cheap"
	TierBalanced    Tier = "balanced"
	TierExpensive   Tier = "expensive"
)

type Tool

type Tool struct {
	Name        string
	Description string
	Schema      map[string]any
}

Tool is the schema the adapter advertises to the model. Schema must be a JSON-schema object describing the function's parameters.

type ToolCall

type ToolCall struct {
	ID       string `json:"id"`
	Name     string `json:"name"`
	ArgsJSON string `json:"args_json"`
	// ThoughtSignature is Gemini's opaque reasoning-continuity token.
	// Thinking models (Gemini 3, and 2.5 with thinking enabled) attach
	// it to functionCall parts and REQUIRE it to be replayed on those
	// parts when the history goes back — otherwise the API rejects the
	// next turn with "Function call is missing a thought_signature in
	// functionCall parts." Empty for every other provider; omitempty
	// keeps it out of their persisted transcripts.
	ThoughtSignature string `json:"thought_signature,omitempty"`
}

ToolCall is a model's request to invoke a tool. ArgsJSON is the raw JSON string the model produced; validation happens at execution time.

type Usage added in v0.3.0

type Usage struct {
	InputTokens         int64 `json:"input_tokens,omitempty"`
	OutputTokens        int64 `json:"output_tokens,omitempty"`
	CacheCreationTokens int64 `json:"cache_creation_tokens,omitempty"`
	CacheReadTokens     int64 `json:"cache_read_tokens,omitempty"`
	ReasoningTokens     int64 `json:"reasoning_tokens,omitempty"`
}

Usage is the per-turn token breakdown reported by the provider. Fields are normalized across providers: cache fields are populated only by providers that expose prompt caching (Anthropic, OpenAI's cached_input); ReasoningTokens is populated by o-series and Gemini "thoughts". All fields are int64 to match the JSON wire types and to keep arithmetic on session totals overflow-safe.

func (*Usage) Add added in v0.3.0

func (u *Usage) Add(other *Usage)

Add accumulates other into u. Used by the session accumulator and the daily rollup. Treats nil receivers/args as zero so callers can chain without nil-guarding.

func (*Usage) IsZero added in v0.3.0

func (u *Usage) IsZero() bool

IsZero reports whether all token counts are zero. A non-nil zero Usage is the "we tried but the provider returned no data" signal — distinct from nil ("we didn't try / never reached the parsing path"). The /usage renderer uses this to format empty sessions.

Jump to

Keyboard shortcuts

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