provider

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Aug 9, 2026 License: MIT Imports: 21 Imported by: 0

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

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

Variables

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

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

View Source
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(root string) string

APITokenSource reports where the gateway token resolves from, for diagnostics (e.g. `memcode doctor`) — "environment", a repo .env path, 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 ByokDelete

func ByokDelete(ctx context.Context, providerID string) error

ByokDelete removes the user's key for a provider.

func ByokList

func ByokList(ctx context.Context) (client.ByokKeys, error)

ByokList fetches the provider roster + the user's masked key rows.

func ByokPut

func ByokPut(ctx context.Context, providerID, key string) (client.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

func ByokValidate(ctx context.Context, providerID string) (bool, string, error)

ByokValidate live-probes the stored key.

func CatalogKnows

func CatalogKnows(id string) bool

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

func CatalogWindow(id string) int

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

func ConventionalKey(base string) string

ConventionalKey returns the standard env var's value for a known provider host, "" for local/unknown endpoints (a keyless Ollama stays keyless).

func DefaultModel

func DefaultModel(t Tier) string

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

func EffectiveModel(s string) string

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

func EndpointModels(ctx context.Context, ep Endpoint) []string

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

func EndpointName(base string) string

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 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 LoadDotEnv

func LoadDotEnv(root string)

LoadDotEnv loads KEY=VALUE pairs into the process environment WITHOUT overriding variables already set, from (in order): the repo's <root>/.env, then a user-global file (GlobalEnvPath, e.g. ~/.config/memcode/.env).

Precedence is therefore: real exported env > repo .env > global. So a key set once in the global file "just works" in every repository, while a repo-local .env (or an explicit export) can still override it. Best-effort: missing files are not an error. Secrets live in these gitignored env files, never in .memcode config or the database.

func ResolveAlias

func ResolveAlias(s string) string

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 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

func ShortModel(id string) string

ShortModel maps a model id back to its short alias for display (the inverse of ResolveAlias). Unknown ids are returned unchanged.

Types

type Advisor

type Advisor interface {
	Advise(ctx context.Context, question, effort string) (string, error)
}

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

type Applier interface {
	Apply(ctx context.Context, path string, edit Edit) (ApplyResult, error)
}

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

type ApplyResult struct {
	Diff    string
	Applied bool
}

ApplyResult reports the outcome of applying an Edit.

type Connector

type Connector interface {
	Connected() bool
	SetCredentials(url, token string)
}

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 Edit

type Edit struct {
	OldString  string
	NewString  string
	ReplaceAll bool
}

Edit is an anchored search/replace edit (see the Agent Runtime Contract).

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
}

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

func EndpointFromEnv() (Endpoint, bool)

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

type Endpointer interface {
	Endpoint() (Endpoint, bool)
}

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 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

func NewFromEnvLazy(endpoints ...Endpoint) *Lazy

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 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) Advise

func (l *Lazy) Advise(ctx context.Context, question, effort string) (string, error)

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) Complete

func (l *Lazy) Complete(ctx context.Context, r wire.Request) (wire.Response, error)

func (*Lazy) Connected

func (l *Lazy) Connected() bool

Connected reports whether a usable backend is present — hosted gateway credentials OR a configured custom endpoint (Phase C widening: endpoint mode is a connected state; only token-less-and-endpoint-less is signed out).

func (*Lazy) Endpoint

func (l *Lazy) Endpoint() (Endpoint, bool)

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) SetCredentials

func (l *Lazy) SetCredentials(url, token string)

SetCredentials swaps in a fresh gateway connection (the /login success path). A retry-notify callback registered before login is re-applied.

func (*Lazy) SetRetryNotify

func (l *Lazy) SetRetryNotify(fn func(attempt int, err error, delay time.Duration))

SetRetryNotify satisfies the retryNotifier seam: applied to the current client if present, and remembered for the client /login constructs later.

func (*Lazy) Stream

func (l *Lazy) Stream(ctx context.Context, r wire.Request, h wire.StreamHandler) (wire.Response, error)

func (*Lazy) WebFetch

func (l *Lazy) WebFetch(ctx context.Context, url string) (string, error)

func (*Lazy) WebSearch

func (l *Lazy) WebSearch(ctx context.Context, query string) (string, error)

type ModelFact

type ModelFact = memcode.ModelFact

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). Empty on error — the picker then shows Automatic only.

type RoleModel

type RoleModel = memcode.RoleModel

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.

const (
	TierPlanner     Tier = "planner"
	TierCoder       Tier = "coder"
	TierReviewer    Tier = "reviewer"
	TierSynthesizer Tier = "synthesizer"
	TierClassifier  Tier = "classifier"
)

type WebFetcher

type WebFetcher interface {
	WebFetch(ctx context.Context, url string) (string, error)
}

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.

type WebSearcher

type WebSearcher interface {
	WebSearch(ctx context.Context, query string) (string, error)
}

WebSearcher is an optional capability: a provider that can answer a query using server-side web search, returning a synthesized text answer. Type-assert for it.

Jump to

Keyboard shortcuts

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