Files
oh-my-pi/packages/coding-agent/src/config/models-config.ts
T
can1357 fbba331f8a feat(cross-cutting): added multi-syntax in-band tool-call support for runtime tool conversion
- Added optional Agent and SDK tool-call syntax controls (`toolCallSyntax`, `PI_OWNED_TOOLS`) for owned calls.
- Added in-band grammar scanners and renderers for Anthropic, DeepSeek, GLM, Hermes, Kimi, PI, and Qwen3.
- Added supportsTools propagation and model schema updates to route unsupported models to fallback syntax.
- Replaced stream-markup parsing with syntax-specific in-band scanners and event conversion.
2026-06-15 07:33:24 +02:00

132 lines
4.1 KiB
TypeScript

/**
* models.json config file handle and provider configuration validation.
*/
import type { Api, ModelSpec } from "@oh-my-pi/pi-ai/types";
import { ConfigFile } from "./config-file";
import {
type ModelsConfig,
ModelsConfigSchema,
type ProviderAuthMode,
type ProviderDiscovery,
} from "./models-config-schema";
export type ProviderValidationMode = "models-config" | "runtime-register";
export interface ProviderValidationModel {
id: string;
api?: Api;
contextWindow?: number;
supportsTools?: boolean;
maxTokens?: number;
}
export interface ProviderValidationConfig {
baseUrl?: string;
headers?: Record<string, string>;
apiKey?: string;
api?: Api;
auth?: ProviderAuthMode;
oauthConfigured?: boolean;
discovery?: ProviderDiscovery;
compat?: ModelSpec<Api>["compat"];
disableStrictTools?: boolean;
modelOverrides?: Record<string, unknown>;
models: ProviderValidationModel[];
}
export function validateProviderConfiguration(
providerName: string,
config: ProviderValidationConfig,
mode: ProviderValidationMode,
): void {
const hasProviderApi = !!config.api;
const models = config.models;
if (models.length === 0) {
if (mode === "models-config") {
const hasModelOverrides = config.modelOverrides && Object.keys(config.modelOverrides).length > 0;
if (
!config.baseUrl &&
!config.headers &&
!config.compat &&
!config.apiKey &&
config.auth !== "none" &&
!config.disableStrictTools &&
!hasModelOverrides &&
!config.discovery
) {
throw new Error(
`Provider ${providerName}: must specify "baseUrl", "headers", "apiKey", "auth: none", "compat", "disableStrictTools", "modelOverrides", "discovery", or "models"`,
);
}
}
} else {
if (!config.baseUrl) {
throw new Error(`Provider ${providerName}: "baseUrl" is required when defining custom models.`);
}
const requiresAuth =
mode === "runtime-register"
? !config.apiKey && !config.oauthConfigured
: !config.apiKey && (config.auth ?? "apiKey") !== "none";
if (requiresAuth) {
throw new Error(
mode === "runtime-register"
? `Provider ${providerName}: "apiKey" or "oauth" is required when defining models.`
: `Provider ${providerName}: "apiKey" is required when defining custom models unless auth is "none".`,
);
}
}
if (mode === "models-config" && config.discovery && !config.api && config.discovery.type !== "proxy") {
throw new Error(`Provider ${providerName}: "api" is required when discovery is enabled at provider level.`);
}
for (const modelDef of models) {
if (!hasProviderApi && !modelDef.api) {
throw new Error(
mode === "runtime-register"
? `Provider ${providerName}, model ${modelDef.id}: no "api" specified.`
: `Provider ${providerName}, model ${modelDef.id}: no "api" specified. Set at provider or model level.`,
);
}
if (!modelDef.id) {
throw new Error(`Provider ${providerName}: model missing "id"`);
}
if (mode === "models-config") {
if (modelDef.contextWindow !== undefined && modelDef.contextWindow <= 0) {
throw new Error(`Provider ${providerName}, model ${modelDef.id}: invalid contextWindow`);
}
if (modelDef.maxTokens !== undefined && modelDef.maxTokens <= 0) {
throw new Error(`Provider ${providerName}, model ${modelDef.id}: invalid maxTokens`);
}
}
}
}
export const ModelsConfigFile = new ConfigFile<ModelsConfig>("models", ModelsConfigSchema).withValidation(
"models",
config => {
const providers = config.providers ?? {};
for (const providerName in providers) {
const providerConfig = providers[providerName];
validateProviderConfiguration(
providerName,
{
baseUrl: providerConfig.baseUrl,
headers: providerConfig.headers,
apiKey: providerConfig.apiKey,
api: providerConfig.api as Api | undefined,
auth: (providerConfig.auth ?? "apiKey") as ProviderAuthMode,
discovery: providerConfig.discovery as ProviderDiscovery | undefined,
compat: providerConfig.compat,
disableStrictTools: providerConfig.disableStrictTools,
modelOverrides: providerConfig.modelOverrides,
models: (providerConfig.models ?? []) as ProviderValidationModel[],
},
"models-config",
);
}
},
);