/** * Shared type definitions for the OpenCode custom providers. * * These describe the data-driven catalog that powers the per-model protocol * routing fix. The actual LLM Model objects are built at registration * time from these catalog entries (see catalog.ts → buildProviderModels). */ import type { AnthropicMessagesCompat, Api, Model, OpenAICompletionsCompat, ThinkingLevelMap, } from "@earendil-works/pi-ai" /** * The wire protocol an OpenCode upstream speaks. Each maps to a pi-ai built-in * streamer subpath: * - "anthropic-messages" → /messages (streamSimpleAnthropic) * - "openai-responses" → /responses (streamSimpleOpenAIResponses) * - "openai-completions" → /chat/completions (streamSimpleOpenAICompletions) * - "google-generative-ai"→ /models/{id} (streamSimpleGoogle) */ export type ProtocolType = | "anthropic-messages" | "openai-responses" | "openai-completions" | "google-generative-ai" /** OpenCode deployment tier — Zen (public catalog) or Go (static). */ export type OpencodeTier = "zen" | "go" /** Per-token pricing (USD per million tokens). */ export interface ModelCost { input: number output: number cacheRead: number cacheWrite: number } /** * Compat overrides keyed by protocol so callers get a strongly-typed value that * matches Model["compat"] for the model's resolved protocol. */ export type CompatOverrides = Partial< OpenAICompletionsCompat & AnthropicMessagesCompat & Record > /** * A catalog entry describing one OpenCode model and its CORRECT protocol. * * This is the source of truth for the routing fix: the `api` field here is what * the built-in pi-ai providers get WRONG for several models (Go Qwen, Go * MiniMax M3, …). Every entry intentionally pins the protocol that the upstream * actually accepts. */ export interface OpenCodeModel { /** Model id sent to the upstream (e.g. "minimax-m3"). */ id: string /** Human-readable display name. */ name: string /** The wire protocol this model must use. */ api: ProtocolType /** Whether the model supports extended thinking / reasoning. */ reasoning: boolean /** Maps pi thinking levels to upstream values; null marks unsupported. */ thinkingLevelMap?: ThinkingLevelMap /** Supported input modalities. */ input: ("text" | "image")[] /** Per-token cost. Defaults to zero when omitted. */ cost?: ModelCost /** Max context window in tokens. */ contextWindow: number /** Max output tokens. */ maxTokens: number /** Provider-specific compatibility overrides (forceAdaptiveThinking, etc.). */ compat?: CompatOverrides } /** * Resolved routing decision for a model id on a given tier. */ export interface RoutingEntry { /** The wire protocol to use. */ protocol: ProtocolType /** Base URL the pi-ai streamer should target (SDK appends the path). */ baseUrl: string /** Whether the model id matched an explicit catalog entry (vs. pattern fallback). */ known: boolean } /** * Minimal model shape that pi.registerProvider expects per model. Mirrors * ProviderModelConfig from pi-coding-agent so we avoid importing that optional * peer here. */ export interface ProviderModelConfig { id: string name: string api?: Api baseUrl?: string reasoning: boolean thinkingLevelMap?: Model["thinkingLevelMap"] input: ("text" | "image")[] cost: ModelCost contextWindow: number maxTokens: number headers?: Record compat?: Model["compat"] }