AI Providers

fabric-server does not run an LLM itself. Every AI turn is delegated to fabric-harness (the sole AI engine) — the agent loop, tool execution, and provider calls all live in the harness. fabric-server has only two backends:

  • ai.backend: harness (default) — forwards each turn to the harness
  • ai.backend: stub — an in-process echo engine for CI / UI dev; hits no provider

The former in-process AnthropicEngine / GeminiEngine / OpenAIEngine were removed 2026-06-08. BYOK now forwards the user’s key to the harness as a per-run override, rather than running a parallel in-process engine.

Which provider the harness runs on

The harness holds the provider keys (configured on the harness side; it picks a provider for which a key is registered, falling back to the echo stub when none is). A session runs by default on the provider fabric-server names via ai.harness_provider, with the model overridden by ai.model.

The harness natively supports four provider kinds:

ai.harness_providerVendorProtocol
deepseek (default)DeepSeekOpenAI-compatible
openaiOpenAIOpenAI-compatible
anthropicAnthropic Claudenative Messages API
googleGoogle Gemininative API

The wider OpenAI-compatible vendor registry lives in providers.go (Providers) — it supplies each vendor’s base_url + default model, reused by BYOK and the OpenAI-compatible endpoints (below).

Model catalog (the single source of truth)

Which models exist, what they cost, how big their context is, and how to ask them to reason — all four come from one place: llmgw’s model catalog, GET /v1/catalog.

Before it, fabric-server kept four private copies (providers.go’s default models, byok_catalog.go’s BYOK picker, platform_models.go’s platform picker, usage/pricing.go’s price table) with nothing tying them together. That is how claude-opus-4-7 came to bill at claude-opus-4-1’s price — 3x too high, on every turn’s cost and quota — with no test able to notice, and how a retired model id could sit in a picker until a user hit a 404.

Those four are now views over the catalog:

SurfaceReadsWithout a catalog
Platform picker (GET /ai/models)ai.PlatformModelsNow()falls back to platform_models.go
BYOK vendor picker (GET /byok/vendors)ai.BYOKVendorsNow()falls back to byok_catalog.go
BYOK vendor → endpointai.BYOKHarnessTarget()falls back to providers.go
Billing rates (incl. tiers)usage.SetPriceSource (injected by ai)falls back to usage/pricing.go

FABRIC_AI_CATALOG_URL points at llmgw; fabric-server polls it hourly (llmgw refreshes the catalog daily at 00:00 CST). An unreachable llmgw never blocks fabric-server’s boot — every consumer falls back to its release-time table, so an outage costs freshness, not availability.

Public face: https://www.appunvs.com/models, served from catalog.appunvs.com/v1/catalog — path-whitelisted, because the rest of llmgw spends platform provider keys.

Tiers: who may use which models

  • Platform-paid (user spends credits) = platform_eligible, currently mainland vendors only — foreign vendors need our own egress and cost more.
  • BYOK (user’s key, user’s egress) = byok_selectable, includes Claude / Gemini / ChatGPT.
  • llmgw’s protocol layer supports all four kinds regardless.

Vendors are a closed set — the user picks from the catalog and the server supplies the base_url; there is no free-text endpoint field. Since 2026-07 the model is validated against the catalog too (ai.ValidBYOKModel): daily refresh is what made a closed list practical without a server release per vendor launch.

OpenAI-compatible registry (providers.go, fallback)

Backend idVendorEnv var (key convention)
openaiOpenAIOPENAI_API_KEY
deepseekDeepSeekDEEPSEEK_API_KEY
moonshotMoonshot (Kimi)MOONSHOT_API_KEY
zhipuZhipu GLMZHIPU_API_KEY
dashscopeAliyun Bailian (Qwen)DASHSCOPE_API_KEY
minimaxMiniMaxMINIMAX_API_KEY
doubaoByteDance DoubaoARK_API_KEY
xiaomiXiaomi MiMoMIMO_API_KEY

Each vendor’s default model id is authoritative in providers.go — this doc does not copy specific model ids (they drift). go test ./internal/ai/’s TestProviderRegistryShape enforces that every provider fills in Name / BaseURL / EnvAPIKey. The env vars above are just each SDK/CLI’s default convention.

Configuration

On the fabric-server side (config.yaml or FABRIC_AI_*):

ai:
  backend:          harness                       # "harness" (default) or "stub"
  harness_url:      http://localhost:8090         # fabric-harness internal address
  harness_secret:   ${FABRIC_AI_HARNESS_SECRET}   # = the harness's FABRIC_HARNESS_INTERNAL_SECRET
  harness_provider: deepseek                      # deepseek | openai | anthropic | google (empty → deepseek)
  model:            ""                             # optional; overrides the provider's default model
  max_iters:        10
  max_tokens:       8000

Provider API keys are configured on the harness side — fabric-server no longer holds provider keys. The harness picks a provider by which key is registered; see fabric/harness.

BYOK (bring your own key)

A user picks a BYOK vendor and supplies their own key; that turn’s {kind, base_url, key, model} is forwarded to the harness as a per-run override — never persisted, never reused on another turn. The vendor catalog is byok_catalog.go:

  • OpenAI-compatible (base_url from the providers.go registry): deepseek / kimi / qwen / doubao / glm / minimax / xiaomi / chatgpt
  • Anthropic: claude
  • Gemini: gemini

Adding an OpenAI-compatible provider

  1. Add a row to the Providers map in providers.go (ID / Name / BaseURL / ModelChat / DocsURL / EnvAPIKey).
  2. If it isn’t strictly OpenAI-compatible (tool-call protocol / field-name differences), first verify with curl that POST /chat/completions + tools + stream yields proper tool_calls deltas before adding it.
  3. Run go test ./internal/ai/TestProviderRegistryShape requires the mandatory fields.
  4. Add it to byok_catalog.go to make it available as a BYOK vendor.

Routing (multi-provider dispatch)

There is no fabric-server-side runtime routing across providers — a session is bound to one harness_provider, and per-user switching goes through the BYOK per-run override. Picking a provider by turn characteristics (needs reasoning? vision? a fast-apply small edit?) is the harness’s job, not fabric-server’s.