opencode-go hard-coded targetFormat: claude per model, so every client format was force-routed to /messages (Codex/OpenAI clients paid a lossy Responses->OpenAI->Claude double translation). Declare the existing upstream multi-endpoint transports [openai, claude, openai-responses] and guard per model via registry supportedFormats: kimi/glm/mimo only support /chat/completions, minimax/qwen add /messages, deepseek adds /responses. Undeclared models keep the upstream default. Drop the bespoke OpenCodeGoExecutor (its shared _lastModel cache could cross auth headers between concurrent requests); DefaultExecutor already consumes runtimeTransport and injects reasoning content.
49 lines
1.9 KiB
JavaScript
49 lines
1.9 KiB
JavaScript
import { deriveModelName } from "./namePatterns.js";
|
|
|
|
// Normalize version separators in a model id: hyphen between two digits becomes a dot.
|
|
// Registry ids use dots for versions ("claude-sonnet-4.5") but clients (CLIs, aliases)
|
|
// often send them with dashes ("claude-sonnet-4-5"). Only digit-digit hyphens are
|
|
// touched, so word/suffix hyphens stay intact ("-thinking", "-agentic", "qwen3-coder-next").
|
|
export function normalizeModelId(modelId) {
|
|
if (typeof modelId !== "string") return modelId;
|
|
return modelId.replace(/(\d)-(\d)/g, "$1.$2");
|
|
}
|
|
|
|
// Model defaults centralized (was scattered as `m.kind || "llm"`, `quotaFamily || "normal"`, etc.)
|
|
export const MODEL_DEFAULTS = {
|
|
kind: "llm",
|
|
quotaFamily: "normal",
|
|
strip: [],
|
|
targetFormat: null
|
|
};
|
|
|
|
// Normalize a registry model entry: accept terse "id" string, fill name via regex when omitted.
|
|
// Override always wins (raw spread last); name falls back to regex → id.
|
|
export function normalizeModel(raw) {
|
|
const model = typeof raw === "string" ? { id: raw } : raw;
|
|
if (model.name !== undefined) return model;
|
|
return { ...model, name: deriveModelName(model.id) };
|
|
}
|
|
|
|
// Resolve model kind with default (accepts legacy `type` field)
|
|
export function modelKind(model) {
|
|
return model?.kind || model?.type || MODEL_DEFAULTS.kind;
|
|
}
|
|
export function modelQuotaFamily(model) {
|
|
return model?.quotaFamily || MODEL_DEFAULTS.quotaFamily;
|
|
}
|
|
export function modelStrip(model) {
|
|
return model?.strip || [];
|
|
}
|
|
export function modelTargetFormat(model) {
|
|
return model?.targetFormat || MODEL_DEFAULTS.targetFormat;
|
|
}
|
|
|
|
// Per-model declared upstream formats (e.g. ["openai", "claude"]). Guards the
|
|
// sourceFormat-matched transport for multi-endpoint providers whose models differ
|
|
// in endpoint support (opencode-go: kimi/glm only do /chat/completions, minimax/qwen
|
|
// also do /messages, deepseek also does /responses).
|
|
export function modelSupportedFormats(model) {
|
|
return model?.supportedFormats || null;
|
|
}
|