feat(compat): per-protocol model compatibility config (V5)

Add per-protocol compatibility options (compatByProtocol) allowing users to configure normalizeToolCallId and preserveOpenAIDeveloperRole per client request protocol (OpenAI Chat, Responses API, Anthropic Messages) instead of globally. Includes frontend Map lookup optimization, type safety improvements, and client-safe constant extraction.

Made-with: Cursor
This commit is contained in:
zhang-qiang
2026-03-21 15:23:42 +08:00
10 changed files with 873 additions and 142 deletions

View File

@@ -0,0 +1,9 @@
/**
* Model compatibility protocol keys — shared between client UI and server.
* Must not import Node or DB code so client components can import safely.
*/
/** Client request shapes from detectFormat — compat options apply when the client uses this protocol */
export const MODEL_COMPAT_PROTOCOL_KEYS = ["openai", "openai-responses", "claude"] as const;
export type ModelCompatProtocolKey = (typeof MODEL_COMPAT_PROTOCOL_KEYS)[number];

View File

@@ -340,6 +340,13 @@ export const clearModelAvailabilitySchema = z.object({
model: modelIdSchema,
});
const modelCompatPerProtocolSchema = z
.object({
normalizeToolCallId: z.boolean().optional(),
preserveOpenAIDeveloperRole: z.boolean().optional(),
})
.strict();
export const providerModelMutationSchema = z.object({
provider: z.string().trim().min(1, "provider is required").max(120),
modelId: z.string().trim().min(1, "modelId is required").max(240),
@@ -349,6 +356,9 @@ export const providerModelMutationSchema = z.object({
supportedEndpoints: z.array(z.enum(["chat", "embeddings", "images", "audio"])).default(["chat"]),
normalizeToolCallId: z.boolean().optional(),
preserveOpenAIDeveloperRole: z.boolean().nullable().optional(),
compatByProtocol: z
.record(z.enum(["openai", "openai-responses", "claude"]), modelCompatPerProtocolSchema)
.optional(),
});
const pricingFieldsSchema = z