import { DEFAULT_MODEL_BY_PROVIDER, type ClaudeModelOptions, type CodexModelOptions, type ModelCapabilities, type ProviderKind, type ServerProvider, type ServerProviderModel,} from "@t3tools/contracts";import { getDefaultEffort, hasEffortLevel, normalizeModelSlug, trimOrNull,} from "@t3tools/shared/model"; const EMPTY_CAPABILITIES: ModelCapabilities = { reasoningEffortLevels: [], supportsFastMode: false, supportsThinkingToggle: false, promptInjectedEffortLevels: [],}; export function getProviderModels( providers: ReadonlyArray<ServerProvider>, provider: ProviderKind,): ReadonlyArray<ServerProviderModel> { return providers.find((candidate) => candidate.provider === provider)?.models ?? [];} export function getProviderSnapshot( providers: ReadonlyArray<ServerProvider>, provider: ProviderKind,): ServerProvider | undefined { return providers.find((candidate) => candidate.provider === provider);} export function isProviderEnabled( providers: ReadonlyArray<ServerProvider>, provider: ProviderKind,): boolean { return getProviderSnapshot(providers, provider)?.enabled ?? true;} export function resolveSelectableProvider( providers: ReadonlyArray<ServerProvider>, provider: ProviderKind | null | undefined,): ProviderKind { const requested = provider ?? "codex"; if (isProviderEnabled(providers, requested)) { return requested; } return providers.find((candidate) => candidate.enabled)?.provider ?? requested;} export function getProviderModelCapabilities( models: ReadonlyArray<ServerProviderModel>, model: string | null | undefined, provider: ProviderKind,): ModelCapabilities { const slug = normalizeModelSlug(model, provider); return models.find((candidate) => candidate.slug === slug)?.capabilities ?? EMPTY_CAPABILITIES;} export function getDefaultServerModel( providers: ReadonlyArray<ServerProvider>, provider: ProviderKind,): string { const models = getProviderModels(providers, provider); return ( models.find((model) => !model.isCustom)?.slug ?? models[0]?.slug ?? DEFAULT_MODEL_BY_PROVIDER[provider] );} export function normalizeCodexModelOptionsWithCapabilities( caps: ModelCapabilities, modelOptions: CodexModelOptions | null | undefined,): CodexModelOptions | undefined { const defaultReasoningEffort = getDefaultEffort(caps); const reasoningEffort = trimOrNull(modelOptions?.reasoningEffort) ?? defaultReasoningEffort; const fastModeEnabled = modelOptions?.fastMode === true; const nextOptions: CodexModelOptions = { ...(reasoningEffort && reasoningEffort !== defaultReasoningEffort ? { reasoningEffort: reasoningEffort as CodexModelOptions["reasoningEffort"] } : {}), ...(fastModeEnabled ? { fastMode: true } : {}), }; return Object.keys(nextOptions).length > 0 ? nextOptions : undefined;} export function normalizeClaudeModelOptionsWithCapabilities( caps: ModelCapabilities, modelOptions: ClaudeModelOptions | null | undefined,): ClaudeModelOptions | undefined { const defaultReasoningEffort = getDefaultEffort(caps); const resolvedEffort = trimOrNull(modelOptions?.effort); const isPromptInjected = caps.promptInjectedEffortLevels.includes(resolvedEffort ?? ""); const effort = resolvedEffort && !isPromptInjected && hasEffortLevel(caps, resolvedEffort) && resolvedEffort !== defaultReasoningEffort ? resolvedEffort : undefined; const thinking = caps.supportsThinkingToggle && modelOptions?.thinking === false ? false : undefined; const fastMode = caps.supportsFastMode && modelOptions?.fastMode === true ? true : undefined; const nextOptions: ClaudeModelOptions = { ...(thinking === false ? { thinking: false } : {}), ...(effort ? { effort } : {}), ...(fastMode ? { fastMode: true } : {}), }; return Object.keys(nextOptions).length > 0 ? nextOptions : undefined;}
import {
DEFAULT_MODEL_BY_PROVIDER,
type ClaudeModelOptions,
type CodexModelOptions,
type ModelCapabilities,
type ProviderKind,
type ServerProvider,
type ServerProviderModel,
} from "@t3tools/contracts";
import {
getDefaultEffort,
hasEffortLevel,
normalizeModelSlug,
trimOrNull,
} from "@t3tools/shared/model";
const EMPTY_CAPABILITIES: ModelCapabilities = {
reasoningEffortLevels: [],
supportsFastMode: false,
supportsThinkingToggle: false,
promptInjectedEffortLevels: [],
};
export function getProviderModels(
providers: ReadonlyArray<ServerProvider>,
provider: ProviderKind,
): ReadonlyArray<ServerProviderModel> {
return providers.find((candidate) => candidate.provider === provider)?.models ?? [];
}
export function getProviderSnapshot(
providers: ReadonlyArray<ServerProvider>,
provider: ProviderKind,
): ServerProvider | undefined {
return providers.find((candidate) => candidate.provider === provider);
}
export function isProviderEnabled(
providers: ReadonlyArray<ServerProvider>,
provider: ProviderKind,
): boolean {
return getProviderSnapshot(providers, provider)?.enabled ?? true;
}
export function resolveSelectableProvider(
providers: ReadonlyArray<ServerProvider>,
provider: ProviderKind | null | undefined,
): ProviderKind {
const requested = provider ?? "codex";
if (isProviderEnabled(providers, requested)) {
return requested;
}
return providers.find((candidate) => candidate.enabled)?.provider ?? requested;
}
export function getProviderModelCapabilities(
models: ReadonlyArray<ServerProviderModel>,
model: string | null | undefined,
provider: ProviderKind,
): ModelCapabilities {
const slug = normalizeModelSlug(model, provider);
return models.find((candidate) => candidate.slug === slug)?.capabilities ?? EMPTY_CAPABILITIES;
}
export function getDefaultServerModel(
providers: ReadonlyArray<ServerProvider>,
provider: ProviderKind,
): string {
const models = getProviderModels(providers, provider);
return (
models.find((model) => !model.isCustom)?.slug ??
models[0]?.slug ??
DEFAULT_MODEL_BY_PROVIDER[provider]
);
}
export function normalizeCodexModelOptionsWithCapabilities(
caps: ModelCapabilities,
modelOptions: CodexModelOptions | null | undefined,
): CodexModelOptions | undefined {
const defaultReasoningEffort = getDefaultEffort(caps);
const reasoningEffort = trimOrNull(modelOptions?.reasoningEffort) ?? defaultReasoningEffort;
const fastModeEnabled = modelOptions?.fastMode === true;
const nextOptions: CodexModelOptions = {
...(reasoningEffort && reasoningEffort !== defaultReasoningEffort
? { reasoningEffort: reasoningEffort as CodexModelOptions["reasoningEffort"] }
: {}),
...(fastModeEnabled ? { fastMode: true } : {}),
};
return Object.keys(nextOptions).length > 0 ? nextOptions : undefined;
}
export function normalizeClaudeModelOptionsWithCapabilities(
caps: ModelCapabilities,
modelOptions: ClaudeModelOptions | null | undefined,
): ClaudeModelOptions | undefined {
const defaultReasoningEffort = getDefaultEffort(caps);
const resolvedEffort = trimOrNull(modelOptions?.effort);
const isPromptInjected = caps.promptInjectedEffortLevels.includes(resolvedEffort ?? "");
const effort =
resolvedEffort &&
!isPromptInjected &&
hasEffortLevel(caps, resolvedEffort) &&
resolvedEffort !== defaultReasoningEffort
? resolvedEffort
: undefined;
const thinking =
caps.supportsThinkingToggle && modelOptions?.thinking === false ? false : undefined;
const fastMode = caps.supportsFastMode && modelOptions?.fastMode === true ? true : undefined;
const nextOptions: ClaudeModelOptions = {
...(thinking === false ? { thinking: false } : {}),
...(effort ? { effort } : {}),
...(fastMode ? { fastMode: true } : {}),
};
return Object.keys(nextOptions).length > 0 ? nextOptions : undefined;
}