204 lines · 6.4 KB · TypeScript
RawDownload
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}`;}