Documentation
¶
Overview ¶
Package provider defines the (model, embedding, edit-apply) boundaries the engine talks to, plus the default Claude model tiers. v1 wires NO implementations here — these are the seams the model-backed phases (understand, learn, agent) plug into. Defaults are recorded now so the tiered strategy is explicit and configurable.
Index ¶
- Constants
- Variables
- func APITokenSource() string
- func APIURL() string
- func AttachedSources() []string
- func BackendServingLabel(backend string) string
- func ByokDelete(ctx context.Context, providerID string) error
- func ByokList(ctx context.Context) (cloudclient.ByokKeys, error)
- func ByokPut(ctx context.Context, providerID, key string) (cloudclient.ByokPutResult, error)
- func ByokValidate(ctx context.Context, providerID string) (bool, string, error)
- func CatalogKnows(id string) bool
- func CatalogWindow(id string) int
- func ConventionalKey(base string) string
- func DefaultModel(t Tier) string
- func EffectiveModel(s string) string
- func EndpointModels(ctx context.Context, ep Endpoint) []string
- func EndpointName(base string) string
- func ExplicitCredentialSource() bool
- func GlobalEnvPath() string
- func LaneBackendVendor(backend string) (vendor, kind string, ok bool)
- func LoadDotEnv()
- func OwnKeyVendor(baseURL string) (string, bool)
- func ResolveAlias(s string) string
- func SelectedSourceUnresolved() (string, bool)
- func SelectedSourcesUnresolved() []string
- func ServingLabel(name string) string
- func SetRetryNotify(prov ModelProvider, fn func(attempt int, err error, delay time.Duration))
- func ShortModel(id string) string
- func SourceVendor(source string) string
- func SubscriptionEndpointName(name string) bool
- type Advisor
- type Applier
- type ApplyResult
- type Connector
- type Edit
- type Endpoint
- type Endpointer
- type ErrLaneExhausted
- type ErrNoLane
- type LaneInfo
- type Laner
- type Lazy
- func (l *Lazy) Advise(ctx context.Context, question, effort string) (string, error)
- func (l *Lazy) ClearCredentials()
- func (l *Lazy) Complete(ctx context.Context, r wire.Request) (wire.Response, error)
- func (l *Lazy) CompleteOnGateway(ctx context.Context, r wire.Request) (wire.Response, error)
- func (l *Lazy) Connected() bool
- func (l *Lazy) Endpoint() (Endpoint, bool)
- func (l *Lazy) GatewayPresent() bool
- func (l *Lazy) Lanes() []LaneInfo
- func (l *Lazy) SetCredentials(url, token string)
- func (l *Lazy) SetRetryNotify(fn func(attempt int, err error, delay time.Duration))
- func (l *Lazy) Stream(ctx context.Context, r wire.Request, h wire.StreamHandler) (wire.Response, error)
- func (l *Lazy) StreamOnGateway(ctx context.Context, r wire.Request, h wire.StreamHandler) (wire.Response, error)
- func (l *Lazy) WebFetch(ctx context.Context, url string) (string, error)
- func (l *Lazy) WebSearch(ctx context.Context, query string) (string, error)
- type ModelFact
- type ModelProvider
- type ModelsInfo
- type PinnableModel
- type Streamer
- type Tier
- type WebFetcher
- type WebSearcher
Constants ¶
const ( // EnvEndpointURL is the FULL compat base including any path prefix // (Groq-style), e.g. http://localhost:11434/v1 — {base}/chat/completions is // the turn endpoint. Setting it (with no memcode token) puts the CLI in // endpoint mode. EnvEndpointURL = "MEMCODE_ENDPOINT_URL" // EnvEndpointKey is the endpoint's optional bearer credential; unset sends // no Authorization header (a keyless local endpoint). EnvEndpointKey = "MEMCODE_ENDPOINT_KEY" // EnvEndpointModel is the endpoint's INITIAL model id — used until a /model // choice is remembered for the endpoint (config wins once one exists). EnvEndpointModel = "MEMCODE_ENDPOINT_MODEL" )
const ( EnvAPIURL = "MEMCODE_API_URL" EnvAPIToken = "MEMCODE_API_TOKEN" // TokenPrefix marks org-scoped gateway keys minted by /login — the ONLY // kind of credential that exists. Its presence in the stored token IS the // local logged-in signal (zero network at boot). TokenPrefix = "memcode_" // DefaultAPIURL is the production memcode gateway. DefaultAPIURL = "https://code.memcode.ai" )
--- gateway connection ---
The CLI has exactly ONE backend: the memcode gateway (cli → api → llms). Hosted provider keys, BYOK storage, and metering live SERVER-side in the api module; the wire adapters and routing policy ship in this binary (the CLI is the agent — shared providers/*, llm selection). The endpoint defaults to production; MEMCODE_API_URL is a DEV OVERRIDE for pointing at a locally running gateway (`go run ./api`), never a requirement.
const EnvCredentialSource = "MEMCODE_CREDENTIAL_SOURCE"
EnvCredentialSource names the explicitly-selected credential source. Empty = own exported keys only (the ambient path). Set to a subscription source ("copilot", …) — by the first-run wizard or by hand — to activate a login that lives in another tool's files/keychain, which never auto-activates.
const EnvCredentials = "MEMCODE_CREDENTIALS"
EnvCredentials is the ordered CONSENT LIST of attached credential sources ("claude,codex"). It replaces the single-value MEMCODE_CREDENTIAL_SOURCE: with subscriptions as family lanes there is no "the" source anymore — every listed source the user has consented to attaches as a lane. The old var still reads as a one-item list and is migrated (rewritten to this var, old one deleted) the next time `memcode auth` writes; consent semantics are unchanged — a subscription NEVER activates from ambient state alone.
Variables ¶
var ErrGatewayOnly = errors.New("not available on a custom endpoint — this needs the memcode gateway (run /login)")
ErrGatewayOnly is returned by the side-channel capabilities (websearch / webfetch / advisor) in endpoint mode: they are memcode gateway services with no compat equivalent. Callers already degrade on error (webfetch falls back to the local fetch; the web_search tool def isn't advertised off-gateway).
var ErrNoEndpointModel = errors.New("no model selected for this endpoint — pick one with /model, or set MEMCODE_ENDPOINT_MODEL")
ErrNoEndpointModel is returned when a turn reaches an endpoint transport with no model anywhere (no session pin, no endpoint default).
var ErrNotLoggedIn = fmt.Errorf("not signed in — run /login to connect to memcode.ai")
ErrNotLoggedIn is returned by the lazy provider for any model-backed call made before /login. The TUI shows its own gate before dispatch; this is the backstop for any path that slips through.
Functions ¶
func APITokenSource ¶
func APITokenSource() string
APITokenSource reports where the gateway token resolves from, for diagnostics (e.g. `memcode doctor`) — "environment" or the global config path — or "" if none is found. Call it BEFORE LoadDotEnv so an already-loaded file isn't misreported as the process environment.
func APIURL ¶
func APIURL() string
APIURL resolves the gateway endpoint: the MEMCODE_API_URL override if set, otherwise production.
func AttachedSources ¶ added in v0.26.0
func AttachedSources() []string
AttachedSources returns the ordered, canonicalized, deduplicated consent list. MEMCODE_CREDENTIALS wins; the legacy single-value var reads as a one-item list so pre-migration installs keep working. Unknown tokens are dropped (the boot warning surfaces them via SelectedSourcesUnresolved's resolution pass, not here).
func BackendServingLabel ¶ added in v0.26.0
BackendServingLabel renders the per-turn "via X" for a lane-stamped backend, "" for gateway/exclusive stamps (callers fall back to "memcode" or the exclusive endpoint's name).
func ByokDelete ¶
ByokDelete removes the user's key for a provider.
func ByokList ¶
func ByokList(ctx context.Context) (cloudclient.ByokKeys, error)
ByokList fetches the provider roster + the user's masked key rows.
func ByokPut ¶
func ByokPut(ctx context.Context, providerID, key string) (cloudclient.ByokPutResult, error)
ByokPut stores/replaces the user's key for a provider (gateway live-probes it first). The caller is responsible for redacting the key from any UI/log surfaces BEFORE calling.
func ByokValidate ¶
ByokValidate live-probes the stored key.
func CatalogKnows ¶
CatalogKnows reports whether the embedded catalog has a real entry for a model id — the cost-display gate: uncataloged (local) models show token counts, not a $ figure priced off the defaults card.
func CatalogWindow ¶
CatalogWindow returns the embedded catalog's context window for a model id it KNOWS (exact id or label) — 0 otherwise. Endpoint mode keys on it for the /model picker's window column and the pin's meter sizing: known ids get real numbers, unknown local models get blank, never a made-up default.
func ConventionalKey ¶
ConventionalKey returns the standard env var's value for a known provider host, "" for local/unknown endpoints (a keyless Ollama stays keyless).
func DefaultModel ¶
DefaultModel returns the default model id for a tier.
planner, reviewer -> Sol (hard reasoning — the frontier tier) coder, synthesizer -> Terra (the everyday default) classifier -> Luna (the reducer's cheap, frequent router)
func EffectiveModel ¶
EffectiveModel resolves a configured tier value (alias or model id) to the model REQUESTED of the gateway. Model policy is the gateway's call — it may re-target a request at the self-hosted pool — but the requested id still matters: it names the tier intent and prices the counterfactual.
func EndpointModels ¶
EndpointModels lists the endpoint's model ids via GET {base}/models (part of the compat standard — OpenAI, Groq, Ollama, LM Studio, vLLM all serve it). When the base was configured WITHOUT its /v1 path prefix, {base}/models 404s on most local runtimes — one retry at {base}/v1/models covers that without the CLI ever rewriting the configured base for turns. Errors (endpoint lacks the route entirely) return nil — the /model picker then falls back to the config list / free-text entry.
func EndpointName ¶
EndpointName derives a short display name from a base URL — the host (with port) for a valid URL, the raw string otherwise. Config-listed endpoints carry their own names; this covers the env-defined ones.
func ExplicitCredentialSource ¶ added in v0.25.0
func ExplicitCredentialSource() bool
ExplicitCredentialSource reports the user has actively selected a credential source (memcode auth …) — the consent signal that lets a subscription login outrank other backends.
func GlobalEnvPath ¶
func GlobalEnvPath() string
GlobalEnvPath returns the user-level secrets file: $XDG_CONFIG_HOME/memcode/.env if XDG_CONFIG_HOME is set, otherwise ~/.config/memcode/.env. Returns "" if no home directory can be determined.
func LaneBackendVendor ¶ added in v0.26.0
LaneBackendVendor maps a per-turn Backend stamp to its lane identity: ("anthropic","sub",true) for "claude-sub", ("openai","ownkey",true) for "ownkey:openai", ok=false for gateway/exclusive stamps.
func LoadDotEnv ¶
func LoadDotEnv()
LoadDotEnv loads KEY=VALUE pairs into the process environment WITHOUT overriding variables already set, from the user-global file only (GlobalEnvPath, e.g. ~/.config/memcode/.env).
The working repo's .env is DELIBERATELY not read. That file belongs to the project, not the agent, and honoring it meant a cloned repo could silently hijack the agent: its MEMCODE_API_URL/MEMCODE_ENDPOINT_URL would redirect the whole conversation to an arbitrary endpoint, and an app's OPENAI_API_KEY meant for the app would get picked up and billed by the agent. Credentials come from the exported environment or the memcode-owned global file, never from the repo being worked on.
Precedence: real exported env > global file. Best-effort: a missing file is not an error. Secrets live in the gitignored global env file, never in .memcode config or the database.
func OwnKeyVendor ¶ added in v0.10.0
OwnKeyVendor reports the vendor name when a base URL is a direct-provider host memcode dials with an exported key ("anthropic", "openai"), ok=false for a generic/local endpoint. Lets diagnostics name an own-key backend for what it is instead of calling it a nameless "custom endpoint".
func ResolveAlias ¶
ResolveAlias maps the short aliases (opus|sonnet|haiku|sol|terra|luna) to model ids. The Claude aliases (opus|sonnet|haiku) are kept for backward compat with existing configs; the GPT-5.6 aliases (sol|terra|luna) are the current tiers. Any other value is treated as a literal model id and returned unchanged.
func SelectedSourceUnresolved ¶ added in v0.25.0
SelectedSourceUnresolved reports a credential source the user explicitly selected that did NOT resolve to a live login (expired token, signed out of the host tool). The TUI surfaces this at boot: serving silently falling back to another credential is exactly the failure users cannot see.
func SelectedSourcesUnresolved ¶ added in v0.26.0
func SelectedSourcesUnresolved() []string
SelectedSourcesUnresolved lists attached sources whose login did NOT resolve (expired token, signed out of the host tool). Boot warns per source: serving silently missing a consented lane is the failure users cannot see.
func ServingLabel ¶ added in v0.25.0
ServingLabel maps an endpoint's internal name to the word a user chose in the auth wizard — the "via X" in the TUI's served-by line. Empty for endpoints that aren't a subscription source (their Name already reads fine).
func SetRetryNotify ¶
func SetRetryNotify(prov ModelProvider, fn func(attempt int, err error, delay time.Duration))
SetRetryNotify wires a retry-notify callback into the gateway transport (if the provider is the SDK client, which it is in production). No-op for any provider that doesn't support it (test fakes, a future local backend) — those just get silent retry. This is the seam the runtime uses to surface "⊙ retrying…" in the TUI without coupling itself to the SDK's concrete client type.
func ShortModel ¶
ShortModel maps a model id back to its short alias for display (the inverse of ResolveAlias). Unknown ids are returned unchanged.
func SourceVendor ¶ added in v0.26.0
SourceVendor returns the catalog vendor a canonical source serves, "" for unknown sources.
func SubscriptionEndpointName ¶ added in v0.25.0
SubscriptionEndpointName reports whether an endpoint name is one of the wizard-selected subscription sources (vs a custom endpoint or own key).
Types ¶
type Advisor ¶
Advisor is an optional capability: a provider that can ask a second-opinion model (a different vendor) to advise the best path forward. Type-assert for it.
type Applier ¶
Applier merges an edit into a file. v1 = anchored search/replace; a fast-apply model can be swapped in later without touching callers.
type ApplyResult ¶
ApplyResult reports the outcome of applying an Edit.
type Connector ¶
Connector is the credential-swap capability the TUI needs from a provider: present on *Lazy, absent on test fakes (which count as connected). This is the seam runtime.Session.Connected forwards through — and since Phase C, Connected means hosted-OR-endpoint (any usable backend); Endpointer (above) is the sibling seam that says WHICH.
type Endpoint ¶
type Endpoint struct {
Name string // short display name ("ollama", or the host:port for env endpoints)
BaseURL string // full compat base incl. any path prefix (http://localhost:11434/v1)
Key string // optional bearer; "" = no Authorization header
Model string // session model id ("" = resolve via GET {base}/models or /model)
Models []string // optional curated picker list / allowlist from config
// Headers are extra request headers a subscription backend requires to
// accept the turn (a Copilot endpoint's Editor-Version / integration id).
// Set only by the credential sources; empty for a normal endpoint. Carried
// through to the compat transport.
Headers map[string]string
}
Endpoint describes one arbitrary OpenAI-compatible endpoint. Resolved from the environment (EndpointFromEnv) or from the project config's named endpoint list (config.ResolveEndpoint merges the two).
func EndpointFromEnv ¶
EndpointFromEnv resolves the env-configured endpoint (the dotenv chain loads MEMCODE_ENDPOINT_* like every other knob). ok=false when no URL is set.
type Endpointer ¶
Endpointer is the endpoint-mode introspection capability, sibling to Connector: present on *Lazy (and the raw conn), absent on test fakes. The runtime forwards it (Session.Endpoint) so the TUI can drive the /model picker, cost display, and capability gating off the ACTIVE backend rather than env sniffing.
type ErrLaneExhausted ¶ added in v0.26.0
type ErrLaneExhausted struct {
Lane LaneInfo
Status int
ResetAt time.Time // zero when unknown
CanFallback bool // a gateway base exists for a consented reissue
Err error
}
ErrLaneExhausted: a lane's credential hit its quota/rate window (HTTP 429/402 after the adapter's own retries). The provider NEVER reroutes on its own — the runtime raises the fallback-choice card and, on consent, reissues via CompleteOnGateway/StreamOnGateway.
func (*ErrLaneExhausted) Error ¶ added in v0.26.0
func (e *ErrLaneExhausted) Error() string
func (*ErrLaneExhausted) Unwrap ¶ added in v0.26.0
func (e *ErrLaneExhausted) Unwrap() error
type ErrNoLane ¶ added in v0.26.0
ErrNoLane: the turn's model belongs to a vendor with no serving path — no gateway login and no lane for that family. Terminal for the fallback walk; the message carries the remedies.
type LaneInfo ¶ added in v0.26.0
type LaneInfo struct {
Vendor string // "anthropic" | "openai" | "grok"
Name string // endpoint name: "claude-sub", "codex", "ownkey:anthropic", …
Kind string // "sub" | "ownkey"
Model string // the lane's default model id
}
LaneInfo is the UI/ledger surface of an attached lane.
type Laner ¶ added in v0.26.0
Laner is the policy-side seam over the lane set: selection consults it to prefer $0 subscription vendors and to clamp signed-out resolution to attached families.
type Lazy ¶
type Lazy struct {
// contains filtered or unexported fields
}
Lazy is a ModelProvider whose backend connection may be ABSENT at construction: the TUI always opens (mandatory-login boot), and /login swaps real credentials in without a restart. All capability methods forward to the inner connection (the compat turn transport + the gateway side-channel client, or the compat transport alone in endpoint mode — see wire.go), or fail with ErrNotLoggedIn while signed out. Safe for concurrent use (atomic pointer swap).
func NewFromEnvLazy ¶
NewFromEnvLazy constructs the lazy provider. Backend selection (one-wire Phase C): a real login (a memcode_-prefixed org key — the local logged-in signal) → the hosted gateway; else a configured endpoint (the caller's resolved config endpoint, or MEMCODE_ENDPOINT_URL) → that endpoint on the compat transport; else family lanes from attached subscriptions / own keys; else signed out — the TUI opens on the sign-in card. Unlike NewFromEnv it never fails — signed-out is a valid state for the TUI.
func (*Lazy) ClearCredentials ¶
func (l *Lazy) ClearCredentials()
ClearCredentials drops the gateway client (the /logout path). With a custom endpoint configured the connection falls BACK to it — the same backend selection boot applies — so signing out of memcode returns to endpoint mode, not dead air; otherwise subsequent model calls fail with ErrNotLoggedIn until the next SetCredentials.
func (*Lazy) CompleteOnGateway ¶ added in v0.26.0
CompleteOnGateway forces a turn onto the gateway base, bypassing lane dispatch — the consented-fallback path after an ErrLaneExhausted card.
func (*Lazy) Connected ¶
Connected reports whether a usable backend is present — hosted gateway credentials, a configured custom endpoint, or at least one family lane.
func (*Lazy) Endpoint ¶
Endpoint reports the ACTIVE custom endpoint, ok=false when hosted or signed out. This is the one backend-mode signal the runtime/TUI key on (via Session.Endpoint) — capability gating, the /model picker, cost display.
func (*Lazy) GatewayPresent ¶ added in v0.26.0
GatewayPresent reports a live hosted-gateway base (side channels exist). False in exclusive-endpoint mode and when signed out.
func (*Lazy) Lanes ¶ added in v0.26.0
Lanes reports the attached family lanes for UI/policy consumers (the provider.Laner seam). Empty in exclusive-endpoint mode and plain hosted.
func (*Lazy) SetCredentials ¶
SetCredentials swaps in a fresh gateway connection (the /login success path). A retry-notify callback registered before login is re-applied.
func (*Lazy) SetRetryNotify ¶
SetRetryNotify satisfies the retryNotifier seam: applied to the current client if present, and remembered for the client /login constructs later.
type ModelProvider ¶
type ModelProvider interface {
Complete(ctx context.Context, r wire.Request) (wire.Response, error)
}
ModelProvider performs reasoning/generation calls (Claude in v1).
func NewFromEnv ¶
func NewFromEnv(endpoints ...Endpoint) (ModelProvider, error)
NewFromEnv constructs the backend connection from the environment — the ONE place the connection story lives. Call it at the cmd boundary after LoadDotEnv. Backend selection: a memcode token → the hosted gateway at {api}/v1; else a configured endpoint (MEMCODE_ENDPOINT_URL, or a resolved config endpoint passed by the caller) → the same compat transport pointed at it; else the signed-out error. ONE turn transport either way (wire.go). The variadic endpoint lets callers that load project config pass its resolved endpoint without this package importing config.
type ModelsInfo ¶
type ModelsInfo = memcode.ModelsInfo
func FetchModels ¶
func FetchModels(ctx context.Context) (ModelsInfo, error)
FetchModels asks the gateway for the routing control plane, resolving the endpoint + credential from the environment the way every CLI surface does.
type PinnableModel ¶
type PinnableModel = memcode.PinnableModel
func AvailablePins ¶
func AvailablePins(ctx context.Context) []PinnableModel
AvailablePins asks the gateway which concrete models the /model picker may offer (the pinnable subset of the servable list). On gateway outage or a lane-only session, fall back to the embedded catalog so /model remains a real picker instead of collapsing to Automatic/free-text.
type Streamer ¶
type Streamer interface {
Stream(ctx context.Context, r wire.Request, h wire.StreamHandler) (wire.Response, error)
}
Streamer is an optional capability: a provider that can stream a completion, emitting text/usage as they arrive while still returning the fully assembled Response. Callers type-assert for it and fall back to Complete otherwise.
(Capability interfaces live at their consuming boundary — here, the CLI — not in the shared protocol package; the wire types they reference are common's.)
type Tier ¶
type Tier string
Tier names the role of a model call. Each maps to a sensible default model but is overridable via config.
type WebFetcher ¶
WebFetcher is an optional capability: a provider that can fetch a specific URL server-side (text/PDF; not JS-rendered pages) and return its readable content.