import { DEFAULT_MODEL_BY_PROVIDER, MODEL_CAPABILITIES_INDEX, MODEL_OPTIONS_BY_PROVIDER, MODEL_SLUG_ALIASES_BY_PROVIDER, type ClaudeModelOptions, type ClaudeCodeEffort, type CodexModelOptions, type ModelCapabilities, type ModelSlug, type ProviderKind, CodexReasoningEffort,} from "@t3tools/contracts"; const MODEL_SLUG_SET_BY_PROVIDER: Record<ProviderKind, ReadonlySet<ModelSlug>> = { claudeAgent: new Set(MODEL_OPTIONS_BY_PROVIDER.claudeAgent.map((option) => option.slug)), codex: new Set(MODEL_OPTIONS_BY_PROVIDER.codex.map((option) => option.slug)),}; export interface SelectableModelOption { slug: string; name: string;} export function getModelOptions(provider: ProviderKind = "codex") { return MODEL_OPTIONS_BY_PROVIDER[provider];} export function getDefaultModel(provider: ProviderKind = "codex"): ModelSlug { return DEFAULT_MODEL_BY_PROVIDER[provider];} // ── Effort helpers ──────────────────────────────────────────────────── /** Check whether a capabilities object includes a given effort value. */export function hasEffortLevel(caps: ModelCapabilities, value: string): boolean { return caps.reasoningEffortLevels.some((l) => l.value === value);} /** Return the default effort value for a capabilities object, or null if none. */export function getDefaultEffort(caps: ModelCapabilities): string | null { return caps.reasoningEffortLevels.find((l) => l.isDefault)?.value ?? null;} // ── Data-driven capability resolver ─────────────────────────────────── export function getModelCapabilities( provider: ProviderKind, model: string | null | undefined,): ModelCapabilities { const slug = normalizeModelSlug(model, provider); if (slug && MODEL_CAPABILITIES_INDEX[provider]?.[slug]) { return MODEL_CAPABILITIES_INDEX[provider][slug]; } return { reasoningEffortLevels: [], supportsFastMode: false, supportsThinkingToggle: false, promptInjectedEffortLevels: [], };} export function isClaudeUltrathinkPrompt(text: string | null | undefined): boolean { return typeof text === "string" && /\bultrathink\b/i.test(text);} export function normalizeModelSlug( model: string | null | undefined, provider: ProviderKind = "codex",): ModelSlug | null { if (typeof model !== "string") { return null; } const trimmed = model.trim(); if (!trimmed) { return null; } const aliases = MODEL_SLUG_ALIASES_BY_PROVIDER[provider] as Record<string, ModelSlug>; const aliased = Object.prototype.hasOwnProperty.call(aliases, trimmed) ? aliases[trimmed] : undefined; return typeof aliased === "string" ? aliased : (trimmed as ModelSlug);} export function resolveSelectableModel( provider: ProviderKind, value: string | null | undefined, options: ReadonlyArray<SelectableModelOption>,): ModelSlug | null { if (typeof value !== "string") { return null; } const trimmed = value.trim(); if (!trimmed) { return null; } const direct = options.find((option) => option.slug === trimmed); if (direct) { return direct.slug; } const byName = options.find((option) => option.name.toLowerCase() === trimmed.toLowerCase()); if (byName) { return byName.slug; } const normalized = normalizeModelSlug(trimmed, provider); if (!normalized) { return null; } const resolved = options.find((option) => option.slug === normalized); return resolved ? resolved.slug : null;} export function resolveModelSlug( model: string | null | undefined, provider: ProviderKind = "codex",): ModelSlug { const normalized = normalizeModelSlug(model, provider); if (!normalized) { return DEFAULT_MODEL_BY_PROVIDER[provider]; } return MODEL_SLUG_SET_BY_PROVIDER[provider].has(normalized) ? normalized : DEFAULT_MODEL_BY_PROVIDER[provider];} export function resolveModelSlugForProvider( provider: ProviderKind, model: string | null | undefined,): ModelSlug { return resolveModelSlug(model, provider);} /** Trim a string, returning null for empty/missing values. */export function trimOrNull<T extends string>(value: T | null | undefined): T | null { if (typeof value !== "string") return null; const trimmed = value.trim() as T; return trimmed || null;} export function normalizeCodexModelOptions( model: string | null | undefined, modelOptions: CodexModelOptions | null | undefined,): CodexModelOptions | undefined { const caps = getModelCapabilities("codex", model); const defaultReasoningEffort = getDefaultEffort(caps) as CodexReasoningEffort; const reasoningEffort = trimOrNull(modelOptions?.reasoningEffort) ?? defaultReasoningEffort; const fastModeEnabled = modelOptions?.fastMode === true; const nextOptions: CodexModelOptions = { ...(reasoningEffort !== defaultReasoningEffort ? { reasoningEffort } : {}), ...(fastModeEnabled ? { fastMode: true } : {}), }; return Object.keys(nextOptions).length > 0 ? nextOptions : undefined;} export function normalizeClaudeModelOptions( model: string | null | undefined, modelOptions: ClaudeModelOptions | null | undefined,): ClaudeModelOptions | undefined { const caps = getModelCapabilities("claudeAgent", model); 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;} export function applyClaudePromptEffortPrefix( text: string, effort: ClaudeCodeEffort | null | undefined,): string { const trimmed = text.trim(); if (!trimmed) { return trimmed; } if (effort !== "ultrathink") { return trimmed; } if (trimmed.startsWith("Ultrathink:")) { return trimmed; } return `Ultrathink:\n${trimmed}`;}
import {
DEFAULT_MODEL_BY_PROVIDER,
MODEL_CAPABILITIES_INDEX,
MODEL_OPTIONS_BY_PROVIDER,
MODEL_SLUG_ALIASES_BY_PROVIDER,
type ClaudeModelOptions,
type ClaudeCodeEffort,
type CodexModelOptions,
type ModelCapabilities,
type ModelSlug,
type ProviderKind,
CodexReasoningEffort,
} from "@t3tools/contracts";
const MODEL_SLUG_SET_BY_PROVIDER: Record<ProviderKind, ReadonlySet<ModelSlug>> = {
claudeAgent: new Set(MODEL_OPTIONS_BY_PROVIDER.claudeAgent.map((option) => option.slug)),
codex: new Set(MODEL_OPTIONS_BY_PROVIDER.codex.map((option) => option.slug)),
};
export interface SelectableModelOption {
slug: string;
name: string;
}
export function getModelOptions(provider: ProviderKind = "codex") {
return MODEL_OPTIONS_BY_PROVIDER[provider];
}
export function getDefaultModel(provider: ProviderKind = "codex"): ModelSlug {
return DEFAULT_MODEL_BY_PROVIDER[provider];
}
// ── Effort helpers ────────────────────────────────────────────────────
/** Check whether a capabilities object includes a given effort value. */
export function hasEffortLevel(caps: ModelCapabilities, value: string): boolean {
return caps.reasoningEffortLevels.some((l) => l.value === value);
}
/** Return the default effort value for a capabilities object, or null if none. */
export function getDefaultEffort(caps: ModelCapabilities): string | null {
return caps.reasoningEffortLevels.find((l) => l.isDefault)?.value ?? null;
}
// ── Data-driven capability resolver ───────────────────────────────────
export function getModelCapabilities(
provider: ProviderKind,
model: string | null | undefined,
): ModelCapabilities {
const slug = normalizeModelSlug(model, provider);
if (slug && MODEL_CAPABILITIES_INDEX[provider]?.[slug]) {
return MODEL_CAPABILITIES_INDEX[provider][slug];
}
return {
reasoningEffortLevels: [],
supportsFastMode: false,
supportsThinkingToggle: false,
promptInjectedEffortLevels: [],
};
}
export function isClaudeUltrathinkPrompt(text: string | null | undefined): boolean {
return typeof text === "string" && /\bultrathink\b/i.test(text);
}
export function normalizeModelSlug(
model: string | null | undefined,
provider: ProviderKind = "codex",
): ModelSlug | null {
if (typeof model !== "string") {
return null;
}
const trimmed = model.trim();
if (!trimmed) {
return null;
}
const aliases = MODEL_SLUG_ALIASES_BY_PROVIDER[provider] as Record<string, ModelSlug>;
const aliased = Object.prototype.hasOwnProperty.call(aliases, trimmed)
? aliases[trimmed]
: undefined;
return typeof aliased === "string" ? aliased : (trimmed as ModelSlug);
}
export function resolveSelectableModel(
provider: ProviderKind,
value: string | null | undefined,
options: ReadonlyArray<SelectableModelOption>,
): ModelSlug | null {
if (typeof value !== "string") {
return null;
}
const trimmed = value.trim();
if (!trimmed) {
return null;
}
const direct = options.find((option) => option.slug === trimmed);
if (direct) {
return direct.slug;
}
const byName = options.find((option) => option.name.toLowerCase() === trimmed.toLowerCase());
if (byName) {
return byName.slug;
}
const normalized = normalizeModelSlug(trimmed, provider);
if (!normalized) {
return null;
}
const resolved = options.find((option) => option.slug === normalized);
return resolved ? resolved.slug : null;
}
export function resolveModelSlug(
model: string | null | undefined,
provider: ProviderKind = "codex",
): ModelSlug {
const normalized = normalizeModelSlug(model, provider);
if (!normalized) {
return DEFAULT_MODEL_BY_PROVIDER[provider];
}
return MODEL_SLUG_SET_BY_PROVIDER[provider].has(normalized)
? normalized
: DEFAULT_MODEL_BY_PROVIDER[provider];
}
export function resolveModelSlugForProvider(
provider: ProviderKind,
model: string | null | undefined,
): ModelSlug {
return resolveModelSlug(model, provider);
}
/** Trim a string, returning null for empty/missing values. */
export function trimOrNull<T extends string>(value: T | null | undefined): T | null {
if (typeof value !== "string") return null;
const trimmed = value.trim() as T;
return trimmed || null;
}
export function normalizeCodexModelOptions(
model: string | null | undefined,
modelOptions: CodexModelOptions | null | undefined,
): CodexModelOptions | undefined {
const caps = getModelCapabilities("codex", model);
const defaultReasoningEffort = getDefaultEffort(caps) as CodexReasoningEffort;
const reasoningEffort = trimOrNull(modelOptions?.reasoningEffort) ?? defaultReasoningEffort;
const fastModeEnabled = modelOptions?.fastMode === true;
const nextOptions: CodexModelOptions = {
...(reasoningEffort !== defaultReasoningEffort ? { reasoningEffort } : {}),
...(fastModeEnabled ? { fastMode: true } : {}),
};
return Object.keys(nextOptions).length > 0 ? nextOptions : undefined;
}
export function normalizeClaudeModelOptions(
model: string | null | undefined,
modelOptions: ClaudeModelOptions | null | undefined,
): ClaudeModelOptions | undefined {
const caps = getModelCapabilities("claudeAgent", model);
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;
}
export function applyClaudePromptEffortPrefix(
text: string,
effort: ClaudeCodeEffort | null | undefined,
): string {
const trimmed = text.trim();
if (!trimmed) {
return trimmed;
}
if (effort !== "ultrathink") {
return trimmed;
}
if (trimmed.startsWith("Ultrathink:")) {
return trimmed;
}
return `Ultrathink:\n${trimmed}`;
}