Archestra LLM Providers & Proxy
SkillAI & modelsUse when adding an LLM provider, changing proxy adapters or provider routes, fixing streaming/tool-call translation bugs, editing model fetchers or model handling, or touching provider credentials/enums and model constants.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Archestra LLM Providers & Proxy skill
What this skill tells your AI
The instructions your AI receives, as published by archestra-ai/archestra in .agents/skills/archestra-dev-llm-providers/SKILL.md and read by ahel’s review.
Use this skill before adding an LLM provider or changing provider translation, streaming, or model handling. Run commands from platform/ unless specifically instructed otherwise.
Provider surface map
For a chat-capable provider, check all of these (use github-copilot as the worked example — it is OpenAI-compatible, so it follows the default path; microsoft-365-copilot shows the harder shape, with its own graph translator):
backend/src/types/llm-providers/<provider>/—api.ts,messages.ts,tools.ts,index.ts(some also havemodels.ts).index.tsdefault-exports a namespace (e.g.GithubCopilot) withAPI/Messages/Toolsplus aTypessub-namespace; register it intypes/llm-providers/index.ts. OpenAI-compatible providers re-export OpenAI schemas with.passthrough().backend/src/routes/proxy/adapters/<provider>.ts— exports<provider>AdapterFactory; re-export it fromadapters/index.ts.backend/src/routes/proxy/routes/<provider>.ts— Fastify plugin:fastifyHttpProxycatch-all withcreateProxyPreHandler(fromproxy-prehandler.ts), explicitPOST .../chat/completionshandlers (default-agent and:agentIdvariants) callinghandleLLMProxy, and model-listing GETs viaproxy-model-listing.ts. Register the plugin in BOTH places: re-export it frombackend/src/routes/index.ts(the main API surface iteratesObject.values(routes)) AND add it toregisterWorkerRoutesinserver.ts.shared/model-constants.ts— add toSupportedProvidersSchema,SupportedProvidersDiscriminatorSchema(<provider>:chatCompletionsfor OpenAI-compatible; others name their API shape, e.g.anthropic:messages,bedrock:converse), andproviderDisplayNames. Membership inPROVIDERS_WITH_OPTIONAL_API_KEY,PROVIDERS_REQUIRING_BASE_URL, andPROVIDERS_REQUIRING_PER_USER_CREDENTIALsilently changes auth behavior: per-user-credential providers get personal-scope keys only, no team/org/env fallback (see thegithub-copilotrationale comment there).backend/src/routes/chat/model-fetchers/— add a fetcher and register it in themodelFetchersrecord inmodel-fetchers/index.ts; itsRecord<SupportedProvider, ModelFetcher>type makes a missing provider a compile error.registry.ts#testProviderApiKeyuses it to validate keys on creation. Simple bearer/modelsendpoints reusemakeBearerFetcher/makeStaticFetcherfrombearer-fetcher.ts.- Message normalization for the chat feature lives in
backend/src/routes/chat/normalization/(notablyprepare-for-provider.ts) andprepare-model-messages.ts— provider-specific message-shape rules go here, not in the proxy adapters. - Frontend: provider key management at
frontend/src/app/llm/model-providers/page.tsx+frontend/src/components/create-llm-provider-api-key-dialog.tsx; provider icon atfrontend/public/icons/<provider>.png; model pickers (components/llm-model-select.tsx,components/chat/model-selector.tsx) useproviderDisplayNames. - Also:
backend/src/config.ts+.env.examplefor base-URL/key env vars,../docs/pages/platform-supported-llm-providers.md.
For embeddings-only providers, follow voyage: register the provider and model fetcher, wire the embedding client, and add it to EMBEDDING_ONLY_PROVIDER_LIST in shared/model-constants.ts. Chat surfaces use providerSupportsChat and ChatProvider; do not invent chat routes, adapters, or chat-matrix entries for a provider with no chat API.
Default path: OpenAI-compatible
- Most new providers are OpenAI-compatible. Do not hand-roll a translator: call
createOpenAiCompatibleAdapterFactoryfromadapters/openai-compatible-adapter.tswithprovider,interactionType,getBaseUrl, andcreateClient— it reusesOpenAIRequestAdapter/OpenAIResponseAdapter/OpenAIStreamAdapterwholesale. Seeadapters/deepseek.ts(minimal) andadapters/github-copilot.ts(custom auth via a fetch wrapper, sincecreateClientis synchronous). - Providers with genuinely different wire formats get translator modules next to the adapter (
gemini-openai-translator.ts,bedrock-openai-translator.ts,cohere-openai-translator.ts,anthropic-openai-translator.ts) — fix translation bugs there, with a matching*.test.ts.
Guard rails
backend/src/routes/proxy/routes/provider-matrix.test.ts—providerConfigsByProviderissatisfies Record<ChatProvider, ProviderTestConfig>, so adding a chat-capable provider without a matrix entry (route plugin + adapter factory + endpoints) fails typecheck. The suite then exercises every chat provider's real route with a mocked client: declared-tool persistence, execution IDs, streaming tool calls, TOON compression, and limit blocking.- The
modelFetchersrecord (above) enforces the same exhaustiveness for model listing.
Translation gotchas (real handling, check before "fixing")
- Empty assistant turns:
convertToModelMessagescan produce assistant messages with empty content that providers reject.buildModelMessagesForProviderinroutes/chat/prepare-model-messages.tsfilters them (isEmptyAssistantModelMessage) and then repairs unanswered tool calls (ensureToolCallsHaveResults) sotool_use/tool_resultadjacency holds. The Cohere proxy adapter (adapters/cohere.ts) does its own empty-assistant filtering. - Tool-call name repair: harmony-format models leak reasoning-channel sentinels into tool names (
name<|channel|>commentary).routes/chat/tool-call-repair.ts#repairHarmonyToolNamestrips them, gated on an exact match against registered tools; wired viaexperimental_repairToolCallinroutes/chat/routes.ts. - Provider message-shape rules: Gemini and Bedrock require the first non-system turn to be from the user —
ensureLeadingUserTurninprepare-model-messages.tsprepends one for both (the provider set gating it lives next to the function). Bedrock content rules (every message non-empty, user messages need a text part) are enforced innormalization/prepare-for-provider.ts(ensureBedrockMessageHasContent,ensureBedrockUserMessageHasTextPart); the same file decides per provider whether text documents stay nativedocumentblocks (Anthropic/Bedrock) or are inlined as decoded text (everyone else). - Output-token ceilings:
agents/agent-output-budget.ts#resolveAgentMaxOutputTokensclampsmaxOutputTokensto the model's real output limit from model metadata (sanitizeOutputLimitfromclients/models-dev-client.ts, 8192 fallback) and the operator ceiling — don't hardcode max-token values.
Validation
cd backend && npx vitest run src/routes/proxy/routes/provider-matrix.test.ts
cd backend && npx vitest run src/routes/proxy/adapters/<provider>*.test.ts # adapter/translator unit tests
pnpm type-check
- Manual end-to-end check:
PROVIDER_SMOKE_TEST.md(repo root ofplatform/) is a browser-automation smoke runbook covering chat, policies, TOON, and proxy flows — run it after provider/proxy changes that unit tests can't cover.
Related skills
archestra-dev-backend— general route/codegen/permission conventions (route shape,RouteId, endpoint permissions).archestra-dev-backend-tests— vitest projects, mocking rules, DB fixtures for the tests above.
Signals
- GitHub stars
- 4k
- Forks
- 1k
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
archestra-dev-llm-providers- Source
- github.com/archestra-ai/archestra