Merge main into model-registry

This commit is contained in:
Mario Zechner
2026-06-22 14:00:18 +02:00
220 changed files with 10488 additions and 4354 deletions
@@ -25,7 +25,6 @@ import { type Static, Type } from "typebox";
import { Compile } from "typebox/compile";
import type { TLocalizedValidationError } from "typebox/error";
import { getAgentDir } from "../config.ts";
import { warnDeprecation } from "../utils/deprecation.ts";
import { stripJsonComments } from "../utils/json.ts";
import { normalizePath } from "../utils/paths.ts";
import type { AuthStatus, AuthStorage } from "./auth-storage.ts";
@@ -35,7 +34,6 @@ import {
getConfigValueEnvVarNames,
isCommandConfigValue,
isConfigValueConfigured,
isLegacyEnvVarNameConfigValue,
resolveConfigValueOrThrow,
resolveConfigValueUncached,
resolveHeadersOrThrow,
@@ -98,6 +96,13 @@ const ThinkingLevelMapSchema = Type.Object({
xhigh: Type.Optional(ThinkingLevelMapValueSchema),
});
const ChatTemplateKwargScalarSchema = Type.Union([Type.String(), Type.Number(), Type.Boolean(), Type.Null()]);
const ChatTemplateKwargVariableSchema = Type.Object({
$var: Type.Union([Type.Literal("thinking.enabled"), Type.Literal("thinking.effort")]),
omitWhenOff: Type.Optional(Type.Boolean()),
});
const ChatTemplateKwargSchema = Type.Union([ChatTemplateKwargScalarSchema, ChatTemplateKwargVariableSchema]);
const OpenAICompletionsCompatSchema = Type.Object({
supportsStore: Type.Optional(Type.Boolean()),
supportsDeveloperRole: Type.Optional(Type.Boolean()),
@@ -116,9 +121,13 @@ const OpenAICompletionsCompatSchema = Type.Object({
Type.Literal("deepseek"),
Type.Literal("zai"),
Type.Literal("qwen"),
Type.Literal("chat-template"),
Type.Literal("qwen-chat-template"),
Type.Literal("string-thinking"),
Type.Literal("ant-ling"),
]),
),
chatTemplateKwargs: Type.Optional(Type.Record(Type.String(), ChatTemplateKwargSchema)),
cacheControlFormat: Type.Optional(Type.Literal("anthropic")),
openRouterRouting: Type.Optional(OpenRouterRoutingSchema),
vercelGatewayRouting: Type.Optional(VercelGatewayRoutingSchema),
@@ -237,82 +246,12 @@ interface ProviderRequestConfig {
authHeader?: boolean;
}
function migrateLegacyRegisterProviderConfigValue(providerName: string, field: string, value: string): string {
if (!isLegacyEnvVarNameConfigValue(value)) return value;
warnDeprecation(
`registerProvider("${providerName}") ${field} value "${value}" is treated as a legacy environment variable reference. This will no longer be detected as an environment variable reference in a future release. Pass "$${value}" instead.`,
);
return `$${value}`;
}
function migrateLegacyRegisterProviderHeaders(
providerName: string,
field: string,
headers: Record<string, string> | undefined,
): Record<string, string> | undefined {
if (!headers) return undefined;
let migratedHeaders: Record<string, string> | undefined;
for (const [key, value] of Object.entries(headers)) {
const migratedValue = migrateLegacyRegisterProviderConfigValue(providerName, `${field} header "${key}"`, value);
if (migratedValue === value) continue;
migratedHeaders ??= { ...headers };
migratedHeaders[key] = migratedValue;
}
return migratedHeaders ?? headers;
}
function migrateLegacyRegisterProviderConfigValues(
providerName: string,
config: ProviderConfigInput,
): ProviderConfigInput {
let migratedConfig: ProviderConfigInput | undefined;
const setMigratedConfigValue = <TKey extends keyof ProviderConfigInput>(
key: TKey,
value: ProviderConfigInput[TKey],
) => {
migratedConfig ??= { ...config };
migratedConfig[key] = value;
};
if (config.apiKey) {
const apiKey = migrateLegacyRegisterProviderConfigValue(providerName, "apiKey", config.apiKey);
if (apiKey !== config.apiKey) {
setMigratedConfigValue("apiKey", apiKey);
}
}
const headers = migrateLegacyRegisterProviderHeaders(providerName, "headers", config.headers);
if (headers !== config.headers) {
setMigratedConfigValue("headers", headers);
}
if (config.models) {
let models: ProviderConfigInput["models"] | undefined;
for (let index = 0; index < config.models.length; index++) {
const model = config.models[index];
const modelHeaders = migrateLegacyRegisterProviderHeaders(
providerName,
`model "${model.id}" headers`,
model.headers,
);
if (modelHeaders === model.headers) continue;
models ??= [...config.models];
models[index] = { ...model, headers: modelHeaders };
}
if (models) {
setMigratedConfigValue("models", models);
}
}
return migratedConfig ?? config;
}
export type ResolvedRequestAuth =
| {
ok: true;
apiKey?: string;
headers?: Record<string, string>;
env?: Record<string, string>;
}
| {
ok: false;
@@ -361,6 +300,13 @@ function mergeCompat(
};
}
if (baseCompletions?.chatTemplateKwargs || overrideCompletions.chatTemplateKwargs) {
mergedCompletions.chatTemplateKwargs = {
...baseCompletions?.chatTemplateKwargs,
...overrideCompletions.chatTemplateKwargs,
};
}
return merged as Model<Api>["compat"];
}
@@ -757,17 +703,27 @@ export class ModelRegistry {
async getApiKeyAndHeaders(model: Model<Api>): Promise<ResolvedRequestAuth> {
try {
const providerConfig = this.providerRequestConfigs.get(model.provider);
const apiKeyFromAuthStorage = await this.authStorage.getApiKey(model.provider);
const providerEnv = this.authStorage.getProviderEnv(model.provider);
const apiKeyFromAuthStorage = await this.authStorage.getApiKey(model.provider, { includeFallback: false });
const apiKey =
apiKeyFromAuthStorage ??
(providerConfig?.apiKey
? resolveConfigValueOrThrow(providerConfig.apiKey, `API key for provider "${model.provider}"`)
? resolveConfigValueOrThrow(
providerConfig.apiKey,
`API key for provider "${model.provider}"`,
providerEnv,
)
: undefined);
const providerHeaders = resolveHeadersOrThrow(providerConfig?.headers, `provider "${model.provider}"`);
const providerHeaders = resolveHeadersOrThrow(
providerConfig?.headers,
`provider "${model.provider}"`,
providerEnv,
);
const modelHeaders = resolveHeadersOrThrow(
this.modelRequestHeaders.get(this.getModelRequestKey(model.provider, model.id)),
`model "${model.provider}/${model.id}"`,
providerEnv,
);
let headers =
@@ -786,6 +742,7 @@ export class ModelRegistry {
ok: true,
apiKey,
headers: headers && Object.keys(headers).length > 0 ? headers : undefined,
env: providerEnv && Object.keys(providerEnv).length > 0 ? providerEnv : undefined,
};
} catch (error) {
return {
@@ -850,7 +807,9 @@ export class ModelRegistry {
}
const providerApiKey = this.providerRequestConfigs.get(provider)?.apiKey;
return providerApiKey ? resolveConfigValueUncached(providerApiKey) : undefined;
return providerApiKey
? resolveConfigValueUncached(providerApiKey, this.authStorage.getProviderEnv(provider))
: undefined;
}
/**
@@ -869,10 +828,9 @@ export class ModelRegistry {
* If provider has oauth: registers OAuth provider for /login support.
*/
registerProvider(providerName: string, config: ProviderConfigInput): void {
const migratedConfig = migrateLegacyRegisterProviderConfigValues(providerName, config);
this.validateProviderConfig(providerName, migratedConfig);
this.applyProviderConfig(providerName, migratedConfig);
this.upsertRegisteredProvider(providerName, migratedConfig);
this.validateProviderConfig(providerName, config);
this.applyProviderConfig(providerName, config);
this.upsertRegisteredProvider(providerName, config);
}
/**