From 6e2dc338e2e2bb8f3f8a79249820e60d12faae37 Mon Sep 17 00:00:00 2001 From: Kit Langton Date: Tue, 5 May 2026 16:15:10 -0400 Subject: [PATCH] docs(llm): remove resolver references --- packages/llm/AGENTS.md | 11 +++---- packages/llm/ARCHITECTURE.md | 2 +- .../PROPOSAL.openai-compatible-wrappers.md | 32 ++++++++++--------- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/packages/llm/AGENTS.md b/packages/llm/AGENTS.md index 8ba8dcf9ff..33ba1e9e75 100644 --- a/packages/llm/AGENTS.md +++ b/packages/llm/AGENTS.md @@ -88,9 +88,8 @@ packages/llm/src/ bedrock-converse.ts openai-compatible-chat.ts // adapter that reuses OpenAIChat.protocol openai-compatible-family.ts // family lookups (deepseek, togetherai, ...) - azure.ts / amazon-bedrock.ts / github-copilot.ts / google.ts / xai.ts / ... // ProviderResolver entries + azure.ts / amazon-bedrock.ts / github-copilot.ts / google.ts / xai.ts / ... // provider model helpers - provider-resolver.ts // OpenCode-bridge resolver layer tool.ts // typed tool() helper tool-runtime.ts // ToolRuntime.run with full tool-loop type safety ``` @@ -267,7 +266,7 @@ Do not blanket re-record an entire test file when adding one cassette. `RECORD=t - [x] Add a generic OpenAI-compatible Chat adapter for non-OpenAI providers that expose `/chat/completions`. - [x] Keep OpenAI Responses as a separate first-class protocol for providers that actually implement `/responses`; do not treat generic OpenAI-compatible providers as Responses-capable by default. - [x] Cover OpenAI-compatible provider families that can share the generic adapter first: DeepSeek, TogetherAI, Cerebras, Baseten, Fireworks, DeepInfra, and similar providers. -- [ ] Decide which providers need thin dedicated wrappers over OpenAI-compatible Chat because they have custom parsing/options: Mistral, Groq, Perplexity, and Cohere. xAI already has a thin resolver that routes to OpenAI Responses. +- [ ] Decide which providers need thin dedicated wrappers over OpenAI-compatible Chat because they have custom parsing/options: Mistral, Groq, Perplexity, and Cohere. xAI already has a thin model helper that routes to OpenAI Responses. - [x] Add Bedrock Converse support: wire format (messages / system / inferenceConfig / toolConfig), AWS event stream binary framing via `@smithy/eventstream-codec`, SigV4 signing via `aws4fetch` (or Bearer API key path), text/reasoning/tool/usage/finish decoding, cache hints, image/document content, deterministic tests, and recorded basic text/tool cassettes. Additional model-specific fields are still TODO. - [ ] Decide Vertex shape after Bedrock/OpenAI-compatible are stable: Vertex Gemini as Gemini target/http patch vs adapter, and Vertex Anthropic as Anthropic target/http patch vs adapter. - [ ] Add Gateway/OpenRouter-style routing support only after the generic OpenAI-compatible adapter and provider option patch model are stable. @@ -279,7 +278,7 @@ Do not blanket re-record an entire test file when adding one cassette. `RECORD=t - [ ] Port DeepSeek reasoning handling and interleaved reasoning field mapping. - [ ] Add unsupported attachment fallback patches keyed by model capabilities. - [ ] Add cache hint patches for Anthropic, OpenRouter, Bedrock, OpenAI-compatible, Copilot, and Alibaba-style providers. -- [ ] Add provider option namespacing patches for Gateway, OpenRouter, OpenAI-compatible wrappers, and other provider-specific option bags. Azure already has resolver-level base URL, `api-version`, and Chat-vs-Responses routing; future Azure work should cover any remaining provider-specific option mapping. +- [ ] Add provider option namespacing patches for Gateway, OpenRouter, OpenAI-compatible wrappers, and other provider-specific option bags. Azure already has model-helper support for base URL, `api-version`, and Chat-vs-Responses routing; future Azure work should cover any remaining provider-specific option mapping. - [ ] Add model-specific reasoning option patches for providers that need effort, summary, or native reasoning fields. - [ ] Add provider-specific metadata extraction patches only where OpenCode needs returned reasoning, citations, usage details, or provider-native fields. @@ -289,7 +288,7 @@ Do not blanket re-record an entire test file when adding one cassette. `RECORD=t - [x] Build a pure `session.llm` -> `LLM.request(...)` bridge for system prompts, message history, tool definitions, tool choice, generation options, reasoning variants, cache hints, and attachments. - [x] Add a typed `ToolRuntime` that drives the tool loop with Schema-typed parameters/success per tool, single-`ToolFailure` error channel, and `maxSteps`/`stopWhen` controls. - [x] Provider-defined tool pass-through: `providerExecuted` flag on `tool-call`/`tool-result` events; Anthropic `server_tool_use` / `web_search_tool_result` / `code_execution_tool_result` / `web_fetch_tool_result` round-trip; OpenAI Responses hosted-tool items decoded as `tool-call` + `tool-result` pairs; runtime skips client dispatch when `providerExecuted: true`. -- [ ] Keep auth and deployment concerns in the OpenCode bridge where possible: Bedrock credentials/region/profile, Vertex project/location/token, remaining Azure deployment concerns, and Gateway/OpenRouter routing headers. Azure resolver support already derives the resource base URL and `api-version` from provider options. +- [ ] Keep auth and deployment concerns in the OpenCode bridge where possible: Bedrock credentials/region/profile, Vertex project/location/token, remaining Azure deployment concerns, and Gateway/OpenRouter routing headers. Azure model helper support already derives the resource base URL and `api-version` from provider options. - [ ] Keep initial OpenCode integration behind a local flag/path until request payload parity and stream event parity are proven against the existing `session/llm.test.ts` cases. ### Native OpenCode Rollout @@ -329,7 +328,7 @@ Do not blanket re-record an entire test file when adding one cassette. `RECORD=t - [ ] DeepInfra OpenAI-compatible Chat basic streaming text and tool-call flow. - [ ] Provider-error cassettes for stable, non-secret error bodies where the provider returns deterministic 4xx/5xx payloads. - [ ] Mistral, Groq, Perplexity, and Cohere basic/tool cassettes after deciding whether each stays generic OpenAI-compatible or gets a thin wrapper. -- [ ] xAI basic/tool cassettes for its OpenAI Responses resolver path. +- [ ] xAI basic/tool cassettes for its OpenAI Responses model helper path. - [x] Bedrock Converse basic text and tool-call cassettes (recorded against `us.amazon.nova-micro-v1:0` in us-east-1). Cache-hint cassettes still TODO. - [ ] Vertex Gemini and Vertex Anthropic basic/tool cassettes after the Vertex adapter/patch shape is decided. - [ ] Gateway/OpenRouter routing-header cassettes after routing support lands. diff --git a/packages/llm/ARCHITECTURE.md b/packages/llm/ARCHITECTURE.md index b35f51669b..7319451805 100644 --- a/packages/llm/ARCHITECTURE.md +++ b/packages/llm/ARCHITECTURE.md @@ -601,6 +601,6 @@ The `@opencode-ai/llm` native path currently works in two modes: | In-process model helper | `OpenAI.model(...)`, `OpenAICompatible.model(...)`, or a third-party helper returns a model handle bound to an adapter. | Library users and code that imports the provider package directly. | | Explicit adapter registry | `LLMClient.make({ adapters: [...] })` maps revived `ModelRef.protocol` values to shipped adapters. | OpenCode config/models.dev bridges, tests, request replay, serialized models. | -So OpenCode native integration is not “import any AI SDK provider package and it just works” yet. Today it supports the protocols/providers we can resolve to known native adapters, plus generic OpenAI-compatible deployments. A config-defined provider with `@ai-sdk/openai-compatible` can resolve to `openai-compatible-chat`; a brand-new protocol needs a native adapter and resolver mapping. +So OpenCode native integration is not “import any AI SDK provider package and it just works” yet. Today it supports protocols/providers that the OpenCode bridge can map to known native model helpers and adapters, plus generic OpenAI-compatible deployments. A config-defined provider with `@ai-sdk/openai-compatible` can map to `openai-compatible-chat`; a brand-new protocol needs a native adapter and bridge mapping. The core package is now open enough for external protocols: `ProtocolID` is just a string, so a third-party package can define `Protocol.define(...)`, `Adapter.make(...)`, and a model helper without changing this package. To make OpenCode load those from config the same way it loads AI SDK packages, we would add an explicit native-provider loader/registry analogous to the AI SDK `model.api.npm` loader. diff --git a/packages/llm/PROPOSAL.openai-compatible-wrappers.md b/packages/llm/PROPOSAL.openai-compatible-wrappers.md index dd5c610e82..124b3ddaf1 100644 --- a/packages/llm/PROPOSAL.openai-compatible-wrappers.md +++ b/packages/llm/PROPOSAL.openai-compatible-wrappers.md @@ -6,7 +6,7 @@ Keep `OpenAICompatibleChat` as the shared implementation for providers that expo | Level | Use When | Example | | --- | --- | --- | -| Profile | Provider only needs `provider`, `baseURL`, capabilities, and resolver defaults. | DeepSeek text/tool basics, TogetherAI, Cerebras, Fireworks. | +| Profile | Provider only needs `provider`, `baseURL`, and capabilities. | DeepSeek text/tool basics, TogetherAI, Cerebras, Fireworks. | | Thin wrapper | Provider speaks OpenAI Chat shape but needs named options, patches, capability defaults, metadata extraction, or provider-defined tools. | Mistral, Groq, Perplexity. | | Dedicated protocol | Request lowering or stream parsing stops being OpenAI Chat-compatible. | Not justified for these providers yet. | @@ -71,10 +71,12 @@ const llm = LLMClient.make({ adapters: OpenAICompatible.adapters }) Current OpenCode bridge shape: ```ts -const resolved = OpenAICompatibleProfiles.resolve("deepseek") -// provider: "deepseek" -// protocol: "openai-compatible-chat" -// baseURL: "https://api.deepseek.com/v1" +OpenAICompatible.model("deepseek-chat", { + provider: "deepseek", + baseURL: OpenAICompatibleProfiles.profiles.deepseek.baseURL, + apiKey, +}) +// provider: "deepseek", protocol: "openai-compatible-chat" ``` Current default patches already contain provider-specific OpenAI-compatible policy: @@ -105,7 +107,7 @@ The lesson is not “copy AI SDK and create full dedicated adapters.” The less ## Proposed Shape -A thin wrapper is a provider-local module that reuses the common OpenAI-compatible adapter and protocol, then exports provider-specific model helpers, resolver, and patches. +A thin wrapper is a provider-local module that reuses the common OpenAI-compatible adapter and protocol, then exports provider-specific model helpers, adapters, and patches. Example Mistral wrapper: @@ -133,8 +135,6 @@ export const adapters = [ OpenAICompatibleChat.adapter.withPatches([mistralIncludeUsage]), ] -export const resolver = OpenAICompatibleProfiles.resolverFor(profile) - export * as Mistral from "./mistral" ``` @@ -162,13 +162,15 @@ const model = OpenAICompatible.model("some-model", { }) ``` -OpenCode resolver call sites become clearer: +OpenCode bridge call sites become clearer: ```ts -Mistral.resolver.resolve(ProviderResolver.input("mistral-large-latest", "mistral", {})) -// provider: "mistral" -// protocol: "openai-compatible-chat" -// baseURL: "https://api.mistral.ai/v1" +Mistral.chat({ + id: "mistral-large-latest", + apiKey, +}) +// provider: "mistral", protocol: "openai-compatible-chat" +// baseURL defaults to "https://api.mistral.ai/v1" ``` ## Provider Recommendations @@ -180,7 +182,7 @@ Mistral.resolver.resolve(ProviderResolver.input("mistral-large-latest", "mistral | Mistral | No profile helper yet, but default Mistral patches exist. | Add thin wrapper. | Policy already exists and AI SDK has enough Mistral-specific behavior to justify a named home. | | Groq | No profile helper yet. | Start as profile or thin wrapper with only base URL; promote when reasoning/browser-search lands. | Basic OpenAI-compatible flow should work, but provider-defined tools and reasoning options need a wrapper. | | Perplexity | No profile helper yet. | Add thin wrapper if citations/sources matter; otherwise start as profile for text only. | The value of Perplexity is source/search metadata, not just text. | -| xAI/Grok | Resolver currently points to `openai-responses`. | Keep separate from generic profiles. | xAI search/reasoning behavior is provider policy, and AI SDK treats chat as dedicated. | +| xAI/Grok | Model helper currently points to `openai-responses`. | Keep separate from generic profiles. | xAI search/reasoning behavior is provider policy, and AI SDK treats chat as dedicated. | ## Why This Is Better Than Adding More Profiles Only @@ -215,7 +217,7 @@ If a recorded cassette later shows a provider emits incompatible stream chunks, ## Implementation Plan 1. Add `src/provider/mistral.ts` as the first thin wrapper because Mistral policy already exists in `ProviderPatch.defaults`. -2. Add Mistral to exports and provider resolver tests. +2. Add Mistral to exports and model-helper bridge tests. 3. Add a recorded Mistral text cassette and tool cassette. 4. Only then decide whether Mistral needs target patches for tool-choice or structured-output behavior. 5. Add Groq as a profile first, unless we immediately implement reasoning/browser-search options.