@x12i/ai-tools 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/README.md +214 -0
  2. package/dist/AiModelsCatalogClient-4RF5BCDL.cjs +9 -0
  3. package/dist/AiModelsCatalogClient-4RF5BCDL.cjs.map +1 -0
  4. package/dist/AiModelsCatalogClient-B-dNLXX0.d.ts +29 -0
  5. package/dist/AiModelsCatalogClient-CSVlKql5.d.cts +29 -0
  6. package/dist/AiModelsCatalogClient-NUF3CBLW.js +9 -0
  7. package/dist/AiModelsCatalogClient-NUF3CBLW.js.map +1 -0
  8. package/dist/aliases/index.cjs +11 -0
  9. package/dist/aliases/index.cjs.map +1 -0
  10. package/dist/aliases/index.d.cts +21 -0
  11. package/dist/aliases/index.d.ts +21 -0
  12. package/dist/aliases/index.js +11 -0
  13. package/dist/aliases/index.js.map +1 -0
  14. package/dist/catalox/index.cjs +21 -0
  15. package/dist/catalox/index.cjs.map +1 -0
  16. package/dist/catalox/index.d.cts +23 -0
  17. package/dist/catalox/index.d.ts +23 -0
  18. package/dist/catalox/index.js +21 -0
  19. package/dist/catalox/index.js.map +1 -0
  20. package/dist/chunk-2PYACSZ5.cjs +1 -0
  21. package/dist/chunk-2PYACSZ5.cjs.map +1 -0
  22. package/dist/chunk-3E67S427.cjs +1 -0
  23. package/dist/chunk-3E67S427.cjs.map +1 -0
  24. package/dist/chunk-4NAY6HRP.js +137 -0
  25. package/dist/chunk-4NAY6HRP.js.map +1 -0
  26. package/dist/chunk-5HNFAYTO.cjs +254 -0
  27. package/dist/chunk-5HNFAYTO.cjs.map +1 -0
  28. package/dist/chunk-6QGDZTGH.js +127 -0
  29. package/dist/chunk-6QGDZTGH.js.map +1 -0
  30. package/dist/chunk-7Q742NI3.cjs +106 -0
  31. package/dist/chunk-7Q742NI3.cjs.map +1 -0
  32. package/dist/chunk-AJEKEWWB.js +106 -0
  33. package/dist/chunk-AJEKEWWB.js.map +1 -0
  34. package/dist/chunk-AV6OE2YQ.cjs +127 -0
  35. package/dist/chunk-AV6OE2YQ.cjs.map +1 -0
  36. package/dist/chunk-COK34C6P.js +1 -0
  37. package/dist/chunk-COK34C6P.js.map +1 -0
  38. package/dist/chunk-DJ5SWJDY.js +729 -0
  39. package/dist/chunk-DJ5SWJDY.js.map +1 -0
  40. package/dist/chunk-F2F4UEFD.cjs +75 -0
  41. package/dist/chunk-F2F4UEFD.cjs.map +1 -0
  42. package/dist/chunk-G2G4KSC5.js +30 -0
  43. package/dist/chunk-G2G4KSC5.js.map +1 -0
  44. package/dist/chunk-HHNHWYTP.cjs +105 -0
  45. package/dist/chunk-HHNHWYTP.cjs.map +1 -0
  46. package/dist/chunk-HN6UAQAE.cjs +83 -0
  47. package/dist/chunk-HN6UAQAE.cjs.map +1 -0
  48. package/dist/chunk-KHODXGPV.js +1 -0
  49. package/dist/chunk-KHODXGPV.js.map +1 -0
  50. package/dist/chunk-KQOALKKX.js +75 -0
  51. package/dist/chunk-KQOALKKX.js.map +1 -0
  52. package/dist/chunk-LYOU7CA2.cjs +30 -0
  53. package/dist/chunk-LYOU7CA2.cjs.map +1 -0
  54. package/dist/chunk-ML2FRR4L.js +105 -0
  55. package/dist/chunk-ML2FRR4L.js.map +1 -0
  56. package/dist/chunk-MLRHYOCD.js +160 -0
  57. package/dist/chunk-MLRHYOCD.js.map +1 -0
  58. package/dist/chunk-O2A6OVEH.js +240 -0
  59. package/dist/chunk-O2A6OVEH.js.map +1 -0
  60. package/dist/chunk-ONA73BU6.cjs +160 -0
  61. package/dist/chunk-ONA73BU6.cjs.map +1 -0
  62. package/dist/chunk-QCRLKVB3.cjs +137 -0
  63. package/dist/chunk-QCRLKVB3.cjs.map +1 -0
  64. package/dist/chunk-QWAX7VQO.cjs +240 -0
  65. package/dist/chunk-QWAX7VQO.cjs.map +1 -0
  66. package/dist/chunk-TF4L2NEC.cjs +729 -0
  67. package/dist/chunk-TF4L2NEC.cjs.map +1 -0
  68. package/dist/chunk-XRBZQQQU.js +254 -0
  69. package/dist/chunk-XRBZQQQU.js.map +1 -0
  70. package/dist/chunk-YHO57D2V.js +83 -0
  71. package/dist/chunk-YHO57D2V.js.map +1 -0
  72. package/dist/cli/index.cjs +403 -0
  73. package/dist/cli/index.cjs.map +1 -0
  74. package/dist/cli/index.d.cts +1 -0
  75. package/dist/cli/index.d.ts +1 -0
  76. package/dist/cli/index.js +403 -0
  77. package/dist/cli/index.js.map +1 -0
  78. package/dist/cost/index.cjs +7 -0
  79. package/dist/cost/index.cjs.map +1 -0
  80. package/dist/cost/index.d.cts +52 -0
  81. package/dist/cost/index.d.ts +52 -0
  82. package/dist/cost/index.js +7 -0
  83. package/dist/cost/index.js.map +1 -0
  84. package/dist/index.cjs +104 -0
  85. package/dist/index.cjs.map +1 -0
  86. package/dist/index.d.cts +58 -0
  87. package/dist/index.d.ts +58 -0
  88. package/dist/index.js +104 -0
  89. package/dist/index.js.map +1 -0
  90. package/dist/modelNameResolver-Bn8QnkSj.d.ts +80 -0
  91. package/dist/modelNameResolver-bZD-eBSJ.d.cts +80 -0
  92. package/dist/models/index.cjs +33 -0
  93. package/dist/models/index.cjs.map +1 -0
  94. package/dist/models/index.d.cts +75 -0
  95. package/dist/models/index.d.ts +75 -0
  96. package/dist/models/index.js +33 -0
  97. package/dist/models/index.js.map +1 -0
  98. package/dist/sync/index.cjs +38 -0
  99. package/dist/sync/index.cjs.map +1 -0
  100. package/dist/sync/index.d.cts +12 -0
  101. package/dist/sync/index.d.ts +12 -0
  102. package/dist/sync/index.js +38 -0
  103. package/dist/sync/index.js.map +1 -0
  104. package/dist/toolbox/index.cjs +7 -0
  105. package/dist/toolbox/index.cjs.map +1 -0
  106. package/dist/toolbox/index.d.cts +72 -0
  107. package/dist/toolbox/index.d.ts +72 -0
  108. package/dist/toolbox/index.js +7 -0
  109. package/dist/toolbox/index.js.map +1 -0
  110. package/dist/types-DdGB3YaA.d.cts +278 -0
  111. package/dist/types-DdGB3YaA.d.ts +278 -0
  112. package/package.json +115 -0
  113. package/schemas/aliases.json +28 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-TF4L2NEC.cjs","../src/cache/modelCache.ts","../src/catalog/aiModelsCatalogDescriptor.ts","../src/sync/openRouterRoutingEnv.ts","../src/sync/modelNameResolver/constants.ts","../src/sync/modelNameResolver/normalize.ts","../src/sync/modelNameResolver/catalogIndexes.ts","../src/sync/modelNameResolver/ModelNameResolver.ts"],"names":[],"mappings":"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","file":"/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-TF4L2NEC.cjs","sourcesContent":[null,"import { createNxCache } from \"nx-cache\";\nimport type { AiModelRecord } from \"../cost/types.js\";\n\nconst cache = createNxCache<Map<string, AiModelRecord>, { cachedAt: string; count: number }>();\n\nexport const DEFAULT_MODEL_CACHE_TTL_MS = 60 * 60 * 1000;\n\nexport type ModelCacheMetadata = { cachedAt: string; count: number };\n\nexport function readCachedModels(\n appId: string,\n ttlMs = DEFAULT_MODEL_CACHE_TTL_MS,\n): Map<string, AiModelRecord> | null {\n const res = cache.read(\"ai-models\", { appId });\n if (!res.found) return null;\n\n const cachedAt = res.metadata?.cachedAt;\n if (cachedAt) {\n const age = Date.now() - new Date(cachedAt).getTime();\n if (age > ttlMs) return null;\n } else if (res.meta.isExpired) {\n return null;\n }\n\n return res.value;\n}\n\nexport function writeCachedModels(\n appId: string,\n models: Map<string, AiModelRecord>,\n ttlMs = DEFAULT_MODEL_CACHE_TTL_MS,\n): void {\n cache.write(\"ai-models\", models, {\n scope: { appId },\n ttlMs,\n metadata: { cachedAt: new Date().toISOString(), count: models.size },\n });\n}\n\nexport function invalidateModelsCache(appId: string): void {\n cache.resetKey(\"ai-models\", { appId });\n}\n","import type { CatalogDescriptor } from \"@x12i/catalox\";\n\nexport const AI_MODELS_CATALOG_ID = \"ai-models\";\nexport const AI_TOOLS_APP_ID = \"ai-tools\";\n\nexport const AI_MODELS_DESCRIPTOR: CatalogDescriptor = {\n catalogId: AI_MODELS_CATALOG_ID,\n label: \"AI Models\",\n description:\n \"Full mirror of OpenRouter Models API metadata and pricing, synced on a schedule.\",\n itemLabel: \"Model\",\n sourceMode: \"native\",\n status: \"active\",\n visibility: \"visible\",\n capabilities: {\n canList: true,\n canGet: true,\n canCreate: false,\n canEdit: false,\n canDelete: false,\n canExport: true,\n canValidate: true,\n },\n identity: {\n itemIdStrategy: \"natural\",\n itemIdField: \"modelId\",\n titleField: \"name\",\n subtitleField: \"providerId\",\n statusField: \"status\",\n updatedAtField: \"syncedAt\",\n },\n queryableFields: [\n { key: \"modelId\", label: \"Model ID\", type: \"string\", indexed: true, filterable: true },\n { key: \"providerId\", label: \"Provider\", type: \"string\", indexed: true, filterable: true },\n { key: \"name\", label: \"Name\", type: \"string\", filterable: true },\n { key: \"canonicalSlug\", label: \"Canonical Slug\", type: \"string\", indexed: true, filterable: true },\n { key: \"contextLength\", label: \"Context Length\", type: \"number\", sortable: true },\n {\n key: \"status\",\n label: \"Status\",\n type: \"enum\",\n indexed: true,\n filterable: true,\n enumValues: [\"active\", \"deprecated\", \"unknown\"],\n },\n {\n key: \"primaryOutputModality\",\n label: \"Primary Output\",\n type: \"string\",\n indexed: true,\n filterable: true,\n },\n { key: \"supportsTools\", label: \"Tools\", type: \"boolean\", indexed: true, filterable: true },\n {\n key: \"supportsReasoning\",\n label: \"Reasoning\",\n type: \"boolean\",\n indexed: true,\n filterable: true,\n },\n { key: \"supportsStreaming\", label: \"Streams\", type: \"boolean\", filterable: true },\n { key: \"isModerated\", label: \"Moderated\", type: \"boolean\", filterable: true },\n { key: \"syncedAt\", label: \"Synced At\", type: \"datetime\", sortable: true },\n ],\n defaultSort: { field: \"syncedAt\", direction: \"desc\" },\n};\n","import { loadDotenv } from \"@x12i/env\";\n\n/**\n * Env naming for vendor direct API keys: `{VENDOR}_API_KEY`\n * where VENDOR is the provider id in UPPER_SNAKE (hyphens → underscores).\n *\n * Examples: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `META_LLAMA_API_KEY`, `X_AI_API_KEY`\n */\nexport function providerIdToEnvKeyPrefix(providerId: string): string {\n return providerId\n .trim()\n .toLowerCase()\n .replace(/-/g, \"_\")\n .toUpperCase();\n}\n\nexport function vendorApiKeyEnvName(providerId: string): string {\n return `${providerIdToEnvKeyPrefix(providerId)}_API_KEY`;\n}\n\nexport type OpenRouterRoutingConfig = {\n /** OPENROUTER_API_KEY is set and non-empty */\n hasOpenRouterKey: boolean;\n /** USE_OPENROUTER=true or USE_OPENROUTER=1 */\n useOpenRouterExplicit: boolean;\n /** Read `{PROVIDER}_API_KEY` for a catalog provider id */\n getVendorApiKey(providerId: string): string | undefined;\n};\n\n/**\n * Load routing hints from process env and optional `.env` (via @x12/env).\n */\nexport function loadOpenRouterRoutingEnv(\n env: Record<string, string | undefined> = process.env,\n): OpenRouterRoutingConfig {\n const dotenv = loadDotenv();\n const merged: Record<string, string> = {};\n for (const [k, v] of Object.entries(dotenv)) {\n if (v !== undefined) merged[k] = v;\n }\n for (const [k, v] of Object.entries(env)) {\n if (v !== undefined) merged[k] = v;\n }\n\n const openRouterKey = merged.OPENROUTER_API_KEY?.trim();\n const useRaw = merged.USE_OPENROUTER?.trim().toLowerCase();\n const useOpenRouterExplicit = useRaw === \"true\" || useRaw === \"1\";\n\n return {\n hasOpenRouterKey: Boolean(openRouterKey),\n useOpenRouterExplicit,\n getVendorApiKey(providerId: string) {\n const name = vendorApiKeyEnvName(providerId);\n const val = merged[name]?.trim();\n return val || undefined;\n },\n };\n}\n\n/**\n * Default route via OpenRouter when:\n * 1. OPENROUTER_API_KEY is set AND USE_OPENROUTER=true|1, or\n * 2. OPENROUTER_API_KEY is set AND the vendor's `{VENDOR}_API_KEY` is missing.\n */\nexport function shouldDefaultRouteViaOpenRouter(\n providerId: string | undefined,\n config: OpenRouterRoutingConfig,\n): boolean {\n if (!config.hasOpenRouterKey) return false;\n if (config.useOpenRouterExplicit) return true;\n if (providerId && providerId !== \"openrouter\") {\n return !config.getVendorApiKey(providerId);\n }\n return false;\n}\n","export const DEFAULT_CONFIDENCE_THRESHOLD = 0.6;\n\nexport const PROVIDER_NORMALISATION_MAP: Record<string, string> = {\n \"open-router\": \"openrouter\",\n or: \"openrouter\",\n oai: \"openai\",\n \"open-ai\": \"openai\",\n claude: \"anthropic\",\n ant: \"anthropic\",\n gemini: \"google\",\n gcp: \"google\",\n \"google-ai\": \"google\",\n vertex: \"google\",\n meta: \"meta-llama\",\n llama: \"meta-llama\",\n \"mistral-ai\": \"mistral\",\n \"cohere-ai\": \"cohere\",\n};\n\nexport const LOCAL_PROVIDERS = new Set([\n \"ollama\",\n \"lmstudio\",\n \"lm-studio\",\n \"localai\",\n \"local-ai\",\n \"llamacpp\",\n \"llama.cpp\",\n \"llamafile\",\n \"jan\",\n \"koboldcpp\",\n]);\n\nexport const PROVIDER_INFERENCE_MAP: Array<{\n pattern: RegExp;\n provider: string;\n notes?: string;\n}> = [\n { pattern: /^gpt-/, provider: \"openai\" },\n { pattern: /^o1($|-)/, provider: \"openai\" },\n { pattern: /^o3($|-)/, provider: \"openai\" },\n { pattern: /^o4($|-)/, provider: \"openai\" },\n { pattern: /^chatgpt-/, provider: \"openai\" },\n { pattern: /^text-davinci/, provider: \"openai\" },\n { pattern: /^dall-e/, provider: \"openai\" },\n { pattern: /^whisper/, provider: \"openai\" },\n { pattern: /^tts-/, provider: \"openai\" },\n { pattern: /^claude-/, provider: \"anthropic\" },\n { pattern: /^gemini-/, provider: \"google\" },\n { pattern: /^palm-/, provider: \"google\" },\n { pattern: /^bison/, provider: \"google\" },\n { pattern: /^llama-?[23]/, provider: \"meta-llama\" },\n { pattern: /^llama/, provider: \"meta-llama\" },\n { pattern: /^codellama/, provider: \"meta-llama\" },\n { pattern: /^mistral-/, provider: \"mistral\" },\n { pattern: /^mixtral-/, provider: \"mistral\" },\n { pattern: /^codestral/, provider: \"mistral\" },\n { pattern: /^ministral/, provider: \"mistral\" },\n { pattern: /^command-/, provider: \"cohere\" },\n { pattern: /^c4ai-/, provider: \"cohere\" },\n { pattern: /^deepseek-/, provider: \"deepseek\" },\n { pattern: /^grok-/, provider: \"x-ai\" },\n { pattern: /^qwen/, provider: \"qwen\" },\n { pattern: /^yi-/, provider: \"01-ai\" },\n { pattern: /^sonar/, provider: \"perplexity\" },\n { pattern: /^pplx-/, provider: \"perplexity\" },\n { pattern: /^titan-/, provider: \"amazon\" },\n { pattern: /^nova-/, provider: \"amazon\" },\n];\n\nexport const SHORTHAND_MAP: Record<string, string> = {\n gpt4: \"openai/gpt-4\",\n gpt4o: \"openai/gpt-4o\",\n \"gpt-4-omni\": \"openai/gpt-4o\",\n gpt4omni: \"openai/gpt-4o\",\n \"gpt4-turbo\": \"openai/gpt-4-turbo\",\n gpt4turbo: \"openai/gpt-4-turbo\",\n \"gpt4-mini\": \"openai/gpt-4o-mini\",\n \"gpt-4o-mini\": \"openai/gpt-4o-mini\",\n o1: \"openai/o1\",\n \"o1-mini\": \"openai/o1-mini\",\n o3: \"openai/o3\",\n \"o3-mini\": \"openai/o3-mini\",\n \"o4-mini\": \"openai/o4-mini\",\n \"3.5-turbo\": \"openai/gpt-3.5-turbo\",\n gpt35: \"openai/gpt-3.5-turbo\",\n gpt3: \"openai/gpt-3.5-turbo\",\n claude3: \"anthropic/claude-3-5-sonnet-20241022\",\n \"claude-3\": \"anthropic/claude-3-5-sonnet-20241022\",\n \"claude3-sonnet\": \"anthropic/claude-3-5-sonnet-20241022\",\n \"claude-sonnet\": \"anthropic/claude-3-5-sonnet-20241022\",\n \"claude-haiku\": \"anthropic/claude-3-5-haiku-20241022\",\n \"claude3-haiku\": \"anthropic/claude-3-5-haiku-20241022\",\n \"claude-opus\": \"anthropic/claude-opus-4\",\n \"claude3-opus\": \"anthropic/claude-3-opus-20240229\",\n claude4: \"anthropic/claude-opus-4\",\n \"claude4-sonnet\": \"anthropic/claude-sonnet-4\",\n \"claude4-opus\": \"anthropic/claude-opus-4\",\n sonnet: \"anthropic/claude-3-5-sonnet-20241022\",\n haiku: \"anthropic/claude-3-5-haiku-20241022\",\n opus: \"anthropic/claude-opus-4\",\n gemini: \"google/gemini-pro\",\n \"gemini-pro\": \"google/gemini-pro\",\n \"gemini-flash\": \"google/gemini-flash-1.5\",\n gemini2: \"google/gemini-2.0-flash-001\",\n \"gemini-2\": \"google/gemini-2.0-flash-001\",\n llama3: \"meta-llama/llama-3.1-8b-instruct\",\n \"llama-3\": \"meta-llama/llama-3.1-8b-instruct\",\n llama2: \"meta-llama/llama-2-13b-chat\",\n \"llama-2\": \"meta-llama/llama-2-13b-chat\",\n mistral: \"mistral/mistral-7b-instruct\",\n \"mistral-large\": \"mistral/mistral-large-latest\",\n mixtral: \"mistral/mixtral-8x7b-instruct\",\n deepseek: \"deepseek/deepseek-chat\",\n \"deepseek-r1\": \"deepseek/deepseek-r1\",\n};\n","import { PROVIDER_NORMALISATION_MAP } from \"./constants.js\";\n\nexport function normalizeString(input: string): string {\n let s = input.trim();\n s = s.replace(/\\s+/g, \" \").replace(/ /g, \"-\");\n s = s.toLowerCase();\n s = s.replace(/^\\/+|\\/+$/g, \"\");\n s = s.replace(/\\/+/g, \"/\");\n if (\n (s.startsWith('\"') && s.endsWith('\"')) ||\n (s.startsWith(\"'\") && s.endsWith(\"'\"))\n ) {\n s = s.slice(1, -1);\n }\n return s;\n}\n\nexport function normalizeProvider(provider: string | undefined): string | undefined {\n if (provider === undefined || provider === \"\") return undefined;\n const n = normalizeString(provider);\n return PROVIDER_NORMALISATION_MAP[n] ?? n;\n}\n\nexport function tokenise(s: string): string[] {\n return s.split(/[-_./]+/).filter(Boolean);\n}\n\nexport function modelSlug(modelId: string): string {\n const slash = modelId.indexOf(\"/\");\n return slash >= 0 ? modelId.slice(slash + 1) : modelId;\n}\n","import type { AiModelRecord } from \"../../models/types.js\";\nimport { normalizeString } from \"./normalize.js\";\nimport type { CatalogIndexes } from \"./types.js\";\n\nexport function buildCatalogIndexes(catalog: Map<string, AiModelRecord>): CatalogIndexes {\n const aliasIndex = new Map<string, string>();\n const slugIndex = new Map<string, string>();\n const prefixCounts = new Map<string, number>();\n\n for (const record of catalog.values()) {\n for (const alias of record.aliases) {\n aliasIndex.set(normalizeString(alias), record.modelId);\n }\n if (record.canonicalSlug) {\n slugIndex.set(normalizeString(record.canonicalSlug), record.modelId);\n }\n const pid = record.providerId;\n prefixCounts.set(pid, (prefixCounts.get(pid) ?? 0) + 1);\n }\n\n const providerPrefixesBySize = [...prefixCounts.entries()]\n .sort((a, b) => b[1] - a[1])\n .map(([p]) => p);\n\n return { aliasIndex, slugIndex, providerPrefixesBySize };\n}\n","import { ModelResolutionError } from \"../../errors.js\";\nimport type { AiModelRecord } from \"../../models/types.js\";\nimport {\n loadOpenRouterRoutingEnv,\n shouldDefaultRouteViaOpenRouter,\n} from \"../openRouterRoutingEnv.js\";\nimport { buildCatalogIndexes } from \"./catalogIndexes.js\";\nimport {\n DEFAULT_CONFIDENCE_THRESHOLD,\n LOCAL_PROVIDERS,\n PROVIDER_INFERENCE_MAP,\n SHORTHAND_MAP,\n} from \"./constants.js\";\nimport { modelSlug, normalizeProvider, normalizeString, tokenise } from \"./normalize.js\";\nimport type {\n CatalogIndexes,\n ModelResolutionInput,\n ModelResolutionNotFound,\n ModelResolutionResult,\n ModelResolutionSuccess,\n ModelResolverOptions,\n ResolutionStrategy,\n ResolverContext,\n} from \"./types.js\";\n\ntype MatchCandidate = {\n modelId: string;\n record: AiModelRecord | null;\n confidence: number;\n strategy: ResolutionStrategy;\n reason: string;\n};\n\nconst VERSION_STRIP_PATTERNS: Array<{ re: RegExp; label: string }> = [\n { re: /-\\d{4}-\\d{2}-\\d{2}$/, label: \"date YYYY-MM-DD\" },\n { re: /-\\d{8}$/, label: \"date YYYYMMDD\" },\n { re: /-v\\d+$/, label: \"version -vN\" },\n { re: /-\\d+\\.\\d+\\.\\d+$/, label: \"semver\" },\n { re: /:\\d+[a-z]?$/i, label: \"colon version\" },\n { re: /-\\d{4,}$/, label: \"build number\" },\n { re: /@\\d+$/, label: \"epoch @NNNN\" },\n];\n\nexport class ModelNameResolver {\n private readonly catalog: Map<string, AiModelRecord>;\n private readonly indexes: CatalogIndexes;\n private readonly ctx: ResolverContext;\n\n constructor(\n catalog: Map<string, AiModelRecord>,\n options: ModelResolverOptions = {},\n ) {\n this.catalog = catalog;\n this.indexes = buildCatalogIndexes(catalog);\n this.ctx = {\n catalog,\n indexes: this.indexes,\n options: {\n confidenceThreshold: options.confidenceThreshold ?? DEFAULT_CONFIDENCE_THRESHOLD,\n aliasRegistry: options.aliasRegistry,\n additionalShorthands: options.additionalShorthands ?? {},\n additionalProviderPatterns: options.additionalProviderPatterns ?? [],\n additionalLocalProviders: options.additionalLocalProviders ?? [],\n routingEnv: options.routingEnv ?? loadOpenRouterRoutingEnv(),\n },\n };\n }\n\n resolve(input: ModelResolutionInput): ModelResolutionResult {\n const attempted: ResolutionStrategy[] = [];\n const threshold = this.ctx.options.confidenceThreshold;\n\n let provider = normalizeProvider(input.provider);\n let model = normalizeString(input.model);\n const normalisedInput = model;\n\n // S0 — alias registry on model string\n if (this.ctx.options.aliasRegistry) {\n attempted.push(\"alias-registry\");\n const entry = this.ctx.options.aliasRegistry.get(model);\n if (entry) {\n model = normalizeString(entry.modelId);\n provider = normalizeProvider(entry.provider) ?? provider;\n }\n }\n\n // Alias used as provider name (e.g. provider=\"best\")\n if (this.ctx.options.aliasRegistry && provider) {\n const aliasAsProvider = this.ctx.options.aliasRegistry.get(provider);\n if (aliasAsProvider) {\n attempted.push(\"alias-as-provider-correction\");\n model = normalizeString(aliasAsProvider.modelId);\n provider = normalizeProvider(aliasAsProvider.provider) ?? provider;\n }\n }\n\n // S10 — local provider (before catalog strategies)\n const localProviders = new Set([\n ...LOCAL_PROVIDERS,\n ...this.ctx.options.additionalLocalProviders.map((p) => normalizeString(p)),\n ]);\n if (provider && localProviders.has(provider)) {\n attempted.push(\"local-provider-passthrough\");\n return this.success({\n modelId: model,\n record: null,\n confidence: 0.99,\n strategy: \"local-provider-passthrough\",\n reason: `Local provider \"${provider}\" — model not looked up in catalog`,\n resolvedVia: attempted,\n normalisedInput,\n provider,\n originalProvider: input.provider,\n });\n }\n\n const preCorrection = [...attempted];\n\n const { match, rejected } = this.runPipeline(model, provider, attempted, threshold, []);\n if (match) {\n const via = [\n ...preCorrection.filter((s) => !match.resolvedVia.includes(s)),\n ...match.resolvedVia,\n ];\n return this.success({\n modelId: match.modelId,\n record: match.record,\n confidence: match.confidence,\n strategy: match.strategy,\n reason: match.reason,\n resolvedVia: via,\n normalisedInput,\n provider,\n originalProvider: input.provider,\n });\n }\n\n const notFound: ModelResolutionNotFound = {\n found: false,\n modelId: null,\n record: null,\n attemptedStrategies: attempted,\n reason: \"No catalog entry matched after all strategies.\",\n bestRejectedCandidate: rejected,\n };\n return notFound;\n }\n\n resolveMany(inputs: ModelResolutionInput[]): ModelResolutionResult[] {\n return inputs.map((i) => this.resolve(i));\n }\n\n resolveOrThrow(input: ModelResolutionInput): ModelResolutionSuccess {\n const result = this.resolve(input);\n if (result.found) return result;\n throw new ModelResolutionError(input, result);\n }\n\n private runPipeline(\n model: string,\n provider: string | undefined,\n attempted: ResolutionStrategy[],\n threshold: number,\n resolvedVia: ResolutionStrategy[],\n depth = 0,\n ): {\n match: (MatchCandidate & { resolvedVia: ResolutionStrategy[] }) | null;\n rejected?: ModelResolutionNotFound[\"bestRejectedCandidate\"];\n } {\n if (depth > 12) return { match: null };\n\n const tryMatch = (\n strategy: ResolutionStrategy,\n fn: () => MatchCandidate | null,\n ): (MatchCandidate & { resolvedVia: ResolutionStrategy[] }) | null => {\n if (!attempted.includes(strategy)) attempted.push(strategy);\n const m = fn();\n if (m && m.confidence >= threshold) {\n return { ...m, resolvedVia: [...resolvedVia, strategy] };\n }\n return null;\n };\n\n let hit = tryMatch(\"exact-match\", () => this.exactMatch(model));\n if (hit) return { match: hit };\n\n hit = tryMatch(\"catalog-alias-match\", () => this.catalogAliasMatch(model));\n if (hit) return { match: hit };\n\n hit = tryMatch(\"canonical-slug-match\", () => this.slugMatch(model));\n if (hit) return { match: hit };\n\n hit = tryMatch(\"provider-prefix-injection\", () =>\n this.providerPrefixInjection(model, provider),\n );\n if (hit) return { match: hit };\n\n hit = tryMatch(\"cross-provider-correction\", () => this.crossProviderCorrection(model));\n if (hit) return { match: hit };\n\n const stripped = this.stripVersionSuffixes(model);\n if (stripped && stripped !== model) {\n if (!attempted.includes(\"version-suffix-strip\")) attempted.push(\"version-suffix-strip\");\n const inner = this.runPipeline(\n stripped,\n provider,\n attempted,\n threshold,\n [...resolvedVia, \"version-suffix-strip\"],\n depth + 1,\n );\n if (inner.match) {\n return {\n match: {\n ...inner.match,\n confidence: 0.85,\n strategy: \"version-suffix-strip\",\n reason: `Stripped version suffix from \"${model}\" → \"${stripped}\" then ${inner.match.reason}`,\n resolvedVia: [...resolvedVia, \"version-suffix-strip\", ...inner.match.resolvedVia],\n },\n };\n }\n }\n\n hit = tryMatch(\"date-suffix-strip\", () => this.dateSuffixMatch(model));\n if (hit) return { match: hit };\n\n const shorthand = this.shorthandMap()[model];\n if (shorthand) {\n if (!attempted.includes(\"shorthand-expansion\")) attempted.push(\"shorthand-expansion\");\n const inner = this.runPipeline(\n normalizeString(shorthand),\n provider,\n attempted,\n threshold,\n [...resolvedVia, \"shorthand-expansion\"],\n depth + 1,\n );\n if (inner.match) {\n return {\n match: {\n ...inner.match,\n confidence: 0.8,\n strategy: \"shorthand-expansion\",\n reason: `Expanded shorthand \"${model}\" → \"${shorthand}\" then ${inner.match.reason}`,\n resolvedVia: [...resolvedVia, \"shorthand-expansion\", ...inner.match.resolvedVia],\n },\n };\n }\n }\n\n const partial = this.partialMatch(model, provider);\n if (partial) {\n if (!attempted.includes(\"partial-name-match\")) attempted.push(\"partial-name-match\");\n if (partial.confidence >= threshold) {\n return {\n match: { ...partial, resolvedVia: [...resolvedVia, \"partial-name-match\"] },\n };\n }\n return {\n match: null,\n rejected: {\n modelId: partial.modelId,\n confidence: partial.confidence,\n reason: partial.reason,\n },\n };\n }\n\n return { match: null };\n }\n\n private exactMatch(model: string): MatchCandidate | null {\n const record = this.catalog.get(model);\n if (!record) return null;\n return {\n modelId: model,\n record,\n confidence: 1,\n strategy: \"exact-match\",\n reason: `Exact catalog match for \"${model}\"`,\n };\n }\n\n private catalogAliasMatch(model: string): MatchCandidate | null {\n const modelId = this.indexes.aliasIndex.get(model);\n if (!modelId) return null;\n const record = this.catalog.get(modelId);\n if (!record) return null;\n return {\n modelId,\n record,\n confidence: 1,\n strategy: \"catalog-alias-match\",\n reason: `Catalog alias \"${model}\" → \"${modelId}\"`,\n };\n }\n\n private slugMatch(model: string): MatchCandidate | null {\n const modelId = this.indexes.slugIndex.get(model);\n if (!modelId) return null;\n const record = this.catalog.get(modelId);\n if (!record) return null;\n return {\n modelId,\n record,\n confidence: 0.95,\n strategy: \"canonical-slug-match\",\n reason: `Canonical slug match \"${model}\" → \"${modelId}\"`,\n };\n }\n\n private inferProviderFromSlug(slug: string): string | undefined {\n const patterns = [\n ...this.ctx.options.additionalProviderPatterns,\n ...PROVIDER_INFERENCE_MAP,\n ];\n for (const { pattern, provider } of patterns) {\n if (pattern.test(slug)) return provider;\n }\n return undefined;\n }\n\n private providerPrefixInjection(\n model: string,\n provider: string | undefined,\n ): MatchCandidate | null {\n if (model.includes(\"/\")) return null;\n\n const prefixes: string[] = [];\n if (provider && provider !== \"openrouter\") {\n prefixes.push(provider);\n }\n const inferred = this.inferProviderFromSlug(model);\n if (inferred && !prefixes.includes(inferred)) prefixes.push(inferred);\n for (const p of this.indexes.providerPrefixesBySize) {\n if (!prefixes.includes(p)) prefixes.push(p);\n }\n\n for (const prefix of prefixes) {\n const candidate = `${prefix}/${model}`;\n let record = this.catalog.get(candidate);\n let modelId = candidate;\n if (!record) {\n const latest = this.findLatestVersioned(candidate);\n if (latest) {\n record = latest.record;\n modelId = latest.modelId;\n }\n }\n if (record) {\n const viaInference = inferred === prefix;\n return {\n modelId,\n record,\n confidence: 0.95,\n strategy: \"provider-prefix-injection\",\n reason: viaInference\n ? `Injected provider prefix \"${prefix}/\" inferred from model name pattern`\n : `Injected provider prefix \"${prefix}/\"`,\n };\n }\n }\n return null;\n }\n\n private crossProviderCorrection(model: string): MatchCandidate | null {\n if (!model.includes(\"/\")) return null;\n const [wrongPrefix, ...rest] = model.split(\"/\");\n const slug = rest.join(\"/\");\n const inferred = this.inferProviderFromSlug(slug);\n if (!inferred || inferred === wrongPrefix) return null;\n const candidate = `${inferred}/${slug}`;\n let record = this.catalog.get(candidate);\n let modelId = candidate;\n if (!record) {\n const latest = this.findLatestVersioned(candidate);\n if (!latest) return null;\n record = latest.record;\n modelId = latest.modelId;\n }\n return {\n modelId,\n record,\n confidence: 0.9,\n strategy: \"cross-provider-correction\",\n reason: `Corrected provider prefix from \"${wrongPrefix}\" to \"${inferred}\" based on model name pattern`,\n };\n }\n\n private stripVersionSuffixes(model: string): string | null {\n for (const { re } of VERSION_STRIP_PATTERNS) {\n if (re.test(model)) {\n return model.replace(re, \"\");\n }\n }\n return null;\n }\n\n private dateSuffixMatch(model: string): MatchCandidate | null {\n if (model.includes(\"/\")) {\n const [prefix, slug] = model.split(\"/\", 2);\n const stripped = this.stripVersionSuffixes(slug);\n if (stripped && stripped !== slug) {\n const candidate = `${prefix}/${stripped}`;\n const record = this.catalog.get(candidate);\n if (record) {\n return {\n modelId: candidate,\n record,\n confidence: 0.85,\n strategy: \"date-suffix-strip\",\n reason: `Stripped date from slug \"${slug}\" → \"${stripped}\"`,\n };\n }\n }\n }\n\n const latest = this.findLatestVersioned(model);\n if (latest) {\n return {\n modelId: latest.modelId,\n record: latest.record,\n confidence: 0.85,\n strategy: \"date-suffix-strip\",\n reason: `Matched \"${model}\" to versioned catalog entry \"${latest.modelId}\" (latest)`,\n };\n }\n return null;\n }\n\n private findLatestVersioned(\n baseModelId: string,\n ): { modelId: string; record: AiModelRecord } | null {\n const candidates: Array<{ modelId: string; record: AiModelRecord; suffix: string }> = [];\n for (const [modelId, record] of this.catalog) {\n if (modelId === baseModelId) continue;\n if (modelId.startsWith(`${baseModelId}-`)) {\n candidates.push({\n modelId,\n record,\n suffix: modelId.slice(baseModelId.length + 1),\n });\n }\n }\n if (candidates.length === 0) return null;\n candidates.sort((a, b) => b.suffix.localeCompare(a.suffix));\n const best = candidates[0]!;\n return { modelId: best.modelId, record: best.record };\n }\n\n private shorthandMap(): Record<string, string> {\n return { ...SHORTHAND_MAP, ...this.ctx.options.additionalShorthands };\n }\n\n private partialMatch(\n model: string,\n provider: string | undefined,\n ): (MatchCandidate & { resolvedVia?: ResolutionStrategy[] }) | null {\n let best: { modelId: string; record: AiModelRecord; score: number } | null = null;\n const inputTokens = tokenise(model);\n if (inputTokens.length === 0) return null;\n\n for (const record of this.catalog.values()) {\n const recordTokens = new Set([\n ...tokenise(record.modelId),\n ...tokenise(record.name ?? \"\"),\n ]);\n const intersection = inputTokens.filter((t) => recordTokens.has(t));\n const overlapRatio = intersection.length / inputTokens.length;\n let score = overlapRatio * 0.6;\n\n if (record.modelId.includes(model)) score += 0.3;\n const slug = modelSlug(record.modelId);\n if (model.includes(slug)) score += 0.2;\n\n if (provider && provider === record.providerId) score += 0.15;\n\n const lenPenalty =\n (Math.abs(model.length - slug.length) / Math.max(model.length, slug.length, 1)) * 0.1;\n score -= lenPenalty;\n\n if (!best || score > best.score) {\n best = { modelId: record.modelId, record, score };\n }\n }\n\n if (!best || best.score < 0.65) return null;\n return {\n modelId: best.modelId,\n record: best.record,\n confidence: 0.65,\n strategy: \"partial-name-match\",\n reason: `Partial token match (score ${best.score.toFixed(2)})`,\n };\n }\n\n private computeRoutedViaOpenRouter(\n modelId: string,\n record: AiModelRecord | null,\n provider: string | undefined,\n originalProvider: string | undefined,\n ): boolean {\n const norm = normalizeProvider(originalProvider) ?? provider;\n if (norm === \"openrouter\") return true;\n if (norm && norm !== \"openrouter\") {\n if (shouldDefaultRouteViaOpenRouter(norm, this.ctx.options.routingEnv)) return true;\n return false;\n }\n\n const recordProvider = record?.providerId;\n if (recordProvider && shouldDefaultRouteViaOpenRouter(recordProvider, this.ctx.options.routingEnv)) {\n return true;\n }\n\n if (record?.availableOnOpenRouter && modelId.includes(\"/\")) return true;\n return false;\n }\n\n private success(args: {\n modelId: string;\n record: AiModelRecord | null;\n confidence: number;\n strategy: ResolutionStrategy;\n reason: string;\n resolvedVia: ResolutionStrategy[];\n normalisedInput: string;\n provider?: string;\n originalProvider?: string;\n }): ModelResolutionSuccess {\n return {\n found: true,\n modelId: args.modelId,\n record: args.record,\n routedViaOpenRouter: this.computeRoutedViaOpenRouter(\n args.modelId,\n args.record,\n args.provider,\n args.originalProvider,\n ),\n confidence: args.confidence,\n resolvedVia: args.resolvedVia,\n resolvedReason: args.reason,\n normalisedInput: args.normalisedInput,\n };\n }\n}\n"]}
@@ -0,0 +1,254 @@
1
+ // src/toolbox/fallback.ts
2
+ function isRetriableError(error) {
3
+ if (error && typeof error === "object") {
4
+ const e = error;
5
+ if (typeof e.status === "number" && e.status >= 400) return true;
6
+ if (e.code === "ECONNRESET" || e.code === "ETIMEDOUT") return true;
7
+ }
8
+ if (error instanceof Error) {
9
+ return true;
10
+ }
11
+ return false;
12
+ }
13
+ function createFallback(onFallback) {
14
+ const trips = /* @__PURE__ */ new Map();
15
+ const isTripped = (provider) => {
16
+ const t = trips.get(provider);
17
+ if (!t) return false;
18
+ if (Date.now() < t.trippedUntil) return true;
19
+ trips.delete(provider);
20
+ return false;
21
+ };
22
+ const recordFailure = (provider) => {
23
+ const prev = trips.get(provider) ?? { failures: 0, trippedUntil: 0 };
24
+ const failures = prev.failures + 1;
25
+ if (failures >= 3) {
26
+ trips.set(provider, { failures, trippedUntil: Date.now() + 5 * 60 * 1e3 });
27
+ } else {
28
+ trips.set(provider, { failures, trippedUntil: 0 });
29
+ }
30
+ };
31
+ const recordSuccess = (provider) => {
32
+ trips.delete(provider);
33
+ };
34
+ return {
35
+ async execute(chain, fn) {
36
+ const models = [chain.primary, ...chain.fallbackChain];
37
+ const maxRetries = chain.maxRetries ?? 1;
38
+ let lastError;
39
+ for (let i = 0; i < models.length; i++) {
40
+ const model = models[i];
41
+ const provider = model.split("/")[0] ?? model;
42
+ if (isTripped(provider)) {
43
+ continue;
44
+ }
45
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
46
+ try {
47
+ const result = await fn(model);
48
+ recordSuccess(provider);
49
+ if (i > 0) onFallback?.(model);
50
+ return result;
51
+ } catch (error) {
52
+ lastError = error;
53
+ if (!isRetriableError(error)) throw error;
54
+ recordFailure(provider);
55
+ }
56
+ }
57
+ }
58
+ throw lastError ?? new Error("All models in fallback chain failed");
59
+ }
60
+ };
61
+ }
62
+
63
+ // src/toolbox/guard.ts
64
+ function repairJson(text) {
65
+ let s = text.trim();
66
+ if (s.startsWith("```")) {
67
+ s = s.replace(/^```(?:json)?\s*/i, "").replace(/\s*```$/, "");
68
+ }
69
+ s = s.replace(/,\s*([}\]])/g, "$1");
70
+ return s;
71
+ }
72
+ function tokenize(text) {
73
+ return new Set(
74
+ text.toLowerCase().split(/\W+/).filter((t) => t.length > 2)
75
+ );
76
+ }
77
+ function groundingScore(response, source) {
78
+ const resp = tokenize(response);
79
+ const src = tokenize(source);
80
+ if (resp.size === 0) return 0;
81
+ let overlap = 0;
82
+ for (const t of resp) {
83
+ if (src.has(t)) overlap++;
84
+ }
85
+ return overlap / resp.size;
86
+ }
87
+ function validateJsonSchema(value, schema) {
88
+ const s = schema;
89
+ const errors = [];
90
+ if (s.type === "object" && (value === null || typeof value !== "object" || Array.isArray(value))) {
91
+ errors.push("Expected object");
92
+ return errors;
93
+ }
94
+ if (s.type === "object" && s.required) {
95
+ const obj = value;
96
+ for (const key of s.required) {
97
+ if (!(key in obj)) errors.push(`Missing required field: ${key}`);
98
+ }
99
+ }
100
+ return errors;
101
+ }
102
+ function createGuard() {
103
+ return {
104
+ verify(response, config) {
105
+ const errors = [];
106
+ if (config.banPhrases?.length) {
107
+ const lower = response.toLowerCase();
108
+ for (const phrase of config.banPhrases) {
109
+ if (lower.includes(phrase.toLowerCase())) {
110
+ errors.push(`Response contains banned phrase: ${phrase}`);
111
+ }
112
+ }
113
+ }
114
+ if (config.jsonSchema) {
115
+ try {
116
+ const repaired = repairJson(response);
117
+ const parsed = JSON.parse(repaired);
118
+ errors.push(...validateJsonSchema(parsed, config.jsonSchema));
119
+ } catch {
120
+ errors.push("Response is not valid JSON");
121
+ }
122
+ }
123
+ if (config.minGroundingScore !== void 0 && config.sourceText) {
124
+ const score = groundingScore(response, config.sourceText);
125
+ if (score < config.minGroundingScore) {
126
+ errors.push(
127
+ `Grounding score ${score.toFixed(2)} below threshold ${config.minGroundingScore}`
128
+ );
129
+ }
130
+ }
131
+ return { isValid: errors.length === 0, errors };
132
+ }
133
+ };
134
+ }
135
+
136
+ // src/toolbox/router.ts
137
+ var TECH_KEYWORDS = [
138
+ "code",
139
+ "sql",
140
+ "diff",
141
+ "function",
142
+ "class",
143
+ "typescript",
144
+ "python",
145
+ "api",
146
+ "debug",
147
+ "refactor",
148
+ "algorithm"
149
+ ];
150
+ function createRouter(defaultModel = "openai/gpt-4o-mini") {
151
+ const rules = [];
152
+ return {
153
+ addRule(rule) {
154
+ rules.push(rule);
155
+ },
156
+ async determineModel(prompt) {
157
+ let complexity = "medium";
158
+ if (prompt.length < 200) {
159
+ complexity = "low";
160
+ } else if (TECH_KEYWORDS.some((kw) => prompt.toLowerCase().includes(kw))) {
161
+ complexity = "high";
162
+ }
163
+ const match = rules.find((r) => r.complexity === complexity) ?? rules.find((r) => r.complexity === "medium");
164
+ if (match) {
165
+ return {
166
+ model: match.targetModel,
167
+ reason: `Matched ${complexity} complexity rule`
168
+ };
169
+ }
170
+ return { model: defaultModel, reason: `Default model for ${complexity} complexity` };
171
+ }
172
+ };
173
+ }
174
+
175
+ // src/toolbox/tracker.ts
176
+ import { randomUUID } from "crypto";
177
+ import { EventEmitter } from "events";
178
+ function estimateTokens(text) {
179
+ return Math.max(1, Math.ceil(text.length / 4));
180
+ }
181
+ function extractUsage(response) {
182
+ if (!response || typeof response !== "object") return null;
183
+ const r = response;
184
+ const usage = r.usage ?? r.data;
185
+ if (!usage) return null;
186
+ const prompt = usage.prompt_tokens ?? usage.promptTokens ?? usage.input_tokens;
187
+ const completion = usage.completion_tokens ?? usage.completionTokens ?? usage.output_tokens;
188
+ if (typeof prompt === "number" && typeof completion === "number") {
189
+ return { prompt, completion };
190
+ }
191
+ return null;
192
+ }
193
+ function createTracker(_options = {}) {
194
+ const emitter = new EventEmitter();
195
+ const memoryLogs = [];
196
+ const logExchange = async (request, response) => {
197
+ const start = Date.now();
198
+ const usage = extractUsage(response);
199
+ const reqText = JSON.stringify(request ?? "");
200
+ const promptTokens = usage?.prompt ?? estimateTokens(reqText);
201
+ const completionTokens = usage?.completion ?? estimateTokens(JSON.stringify(response ?? ""));
202
+ const result = {
203
+ requestId: randomUUID(),
204
+ durationMs: Date.now() - start,
205
+ promptTokens,
206
+ completionTokens,
207
+ estimatedCostUSD: 0,
208
+ requestPayload: request,
209
+ responsePayload: response
210
+ };
211
+ memoryLogs.push(result);
212
+ emitter.emit("exchange", result);
213
+ return result;
214
+ };
215
+ return {
216
+ logExchange,
217
+ wrap(fn) {
218
+ const wrapped = (async (...args) => {
219
+ const request = args[0];
220
+ const response = await fn(...args);
221
+ await logExchange(request, response);
222
+ return response;
223
+ });
224
+ return wrapped;
225
+ },
226
+ on(event, handler) {
227
+ emitter.on(event, handler);
228
+ }
229
+ };
230
+ }
231
+
232
+ // src/toolbox/AIToolbox.ts
233
+ var AIToolbox = class {
234
+ tracker;
235
+ router;
236
+ guard;
237
+ fallback;
238
+ constructor(options = {}) {
239
+ this.tracker = createTracker({
240
+ storage: options.storage,
241
+ sqlitePath: options.sqlitePath
242
+ });
243
+ this.router = createRouter(
244
+ options.defaultModel ?? (options.defaultProvider ? `${options.defaultProvider}/gpt-4o-mini` : "openai/gpt-4o-mini")
245
+ );
246
+ this.guard = createGuard();
247
+ this.fallback = createFallback();
248
+ }
249
+ };
250
+
251
+ export {
252
+ AIToolbox
253
+ };
254
+ //# sourceMappingURL=chunk-XRBZQQQU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/toolbox/fallback.ts","../src/toolbox/guard.ts","../src/toolbox/router.ts","../src/toolbox/tracker.ts","../src/toolbox/AIToolbox.ts"],"sourcesContent":["export type FallbackChain = {\n primary: string;\n fallbackChain: string[];\n maxRetries?: number;\n};\n\ntype ProviderTrip = { failures: number; trippedUntil: number };\n\nexport interface Fallback {\n execute<T>(chain: FallbackChain, fn: (model: string) => Promise<T>): Promise<T>;\n}\n\nfunction isRetriableError(error: unknown): boolean {\n if (error && typeof error === \"object\") {\n const e = error as { status?: number; code?: string };\n if (typeof e.status === \"number\" && e.status >= 400) return true;\n if (e.code === \"ECONNRESET\" || e.code === \"ETIMEDOUT\") return true;\n }\n if (error instanceof Error) {\n return true;\n }\n return false;\n}\n\nexport function createFallback(onFallback?: (model: string) => void): Fallback {\n const trips = new Map<string, ProviderTrip>();\n\n const isTripped = (provider: string): boolean => {\n const t = trips.get(provider);\n if (!t) return false;\n if (Date.now() < t.trippedUntil) return true;\n trips.delete(provider);\n return false;\n };\n\n const recordFailure = (provider: string) => {\n const prev = trips.get(provider) ?? { failures: 0, trippedUntil: 0 };\n const failures = prev.failures + 1;\n if (failures >= 3) {\n trips.set(provider, { failures, trippedUntil: Date.now() + 5 * 60 * 1000 });\n } else {\n trips.set(provider, { failures, trippedUntil: 0 });\n }\n };\n\n const recordSuccess = (provider: string) => {\n trips.delete(provider);\n };\n\n return {\n async execute<T>(chain: FallbackChain, fn: (model: string) => Promise<T>): Promise<T> {\n const models = [chain.primary, ...chain.fallbackChain];\n const maxRetries = chain.maxRetries ?? 1;\n let lastError: unknown;\n\n for (let i = 0; i < models.length; i++) {\n const model = models[i]!;\n const provider = model.split(\"/\")[0] ?? model;\n\n if (isTripped(provider)) {\n continue;\n }\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const result = await fn(model);\n recordSuccess(provider);\n if (i > 0) onFallback?.(model);\n return result;\n } catch (error) {\n lastError = error;\n if (!isRetriableError(error)) throw error;\n recordFailure(provider);\n }\n }\n }\n\n throw lastError ?? new Error(\"All models in fallback chain failed\");\n },\n };\n}\n","export type GuardConfig = {\n jsonSchema?: object;\n banPhrases?: string[];\n minGroundingScore?: number;\n sourceText?: string;\n};\n\nexport interface Guard {\n verify(response: string, config: GuardConfig): { isValid: boolean; errors: string[] };\n}\n\nfunction repairJson(text: string): string {\n let s = text.trim();\n if (s.startsWith(\"```\")) {\n s = s.replace(/^```(?:json)?\\s*/i, \"\").replace(/\\s*```$/, \"\");\n }\n s = s.replace(/,\\s*([}\\]])/g, \"$1\");\n return s;\n}\n\nfunction tokenize(text: string): Set<string> {\n return new Set(\n text\n .toLowerCase()\n .split(/\\W+/)\n .filter((t) => t.length > 2),\n );\n}\n\nfunction groundingScore(response: string, source: string): number {\n const resp = tokenize(response);\n const src = tokenize(source);\n if (resp.size === 0) return 0;\n let overlap = 0;\n for (const t of resp) {\n if (src.has(t)) overlap++;\n }\n return overlap / resp.size;\n}\n\nfunction validateJsonSchema(value: unknown, schema: object): string[] {\n const s = schema as { type?: string; required?: string[]; properties?: Record<string, unknown> };\n const errors: string[] = [];\n\n if (s.type === \"object\" && (value === null || typeof value !== \"object\" || Array.isArray(value))) {\n errors.push(\"Expected object\");\n return errors;\n }\n\n if (s.type === \"object\" && s.required) {\n const obj = value as Record<string, unknown>;\n for (const key of s.required) {\n if (!(key in obj)) errors.push(`Missing required field: ${key}`);\n }\n }\n\n return errors;\n}\n\nexport function createGuard(): Guard {\n return {\n verify(response: string, config: GuardConfig) {\n const errors: string[] = [];\n\n if (config.banPhrases?.length) {\n const lower = response.toLowerCase();\n for (const phrase of config.banPhrases) {\n if (lower.includes(phrase.toLowerCase())) {\n errors.push(`Response contains banned phrase: ${phrase}`);\n }\n }\n }\n\n if (config.jsonSchema) {\n try {\n const repaired = repairJson(response);\n const parsed = JSON.parse(repaired);\n errors.push(...validateJsonSchema(parsed, config.jsonSchema));\n } catch {\n errors.push(\"Response is not valid JSON\");\n }\n }\n\n if (config.minGroundingScore !== undefined && config.sourceText) {\n const score = groundingScore(response, config.sourceText);\n if (score < config.minGroundingScore) {\n errors.push(\n `Grounding score ${score.toFixed(2)} below threshold ${config.minGroundingScore}`,\n );\n }\n }\n\n return { isValid: errors.length === 0, errors };\n },\n };\n}\n","export type RouteRule = {\n maxTokens?: number;\n keywords?: string[];\n complexity: \"low\" | \"medium\" | \"high\";\n targetModel: string;\n};\n\nconst TECH_KEYWORDS = [\n \"code\",\n \"sql\",\n \"diff\",\n \"function\",\n \"class\",\n \"typescript\",\n \"python\",\n \"api\",\n \"debug\",\n \"refactor\",\n \"algorithm\",\n];\n\nexport interface Router {\n addRule(rule: RouteRule): void;\n determineModel(prompt: string): Promise<{ model: string; reason: string }>;\n}\n\nexport function createRouter(defaultModel = \"openai/gpt-4o-mini\"): Router {\n const rules: RouteRule[] = [];\n\n return {\n addRule(rule: RouteRule) {\n rules.push(rule);\n },\n\n async determineModel(prompt: string) {\n let complexity: RouteRule[\"complexity\"] = \"medium\";\n\n if (prompt.length < 200) {\n complexity = \"low\";\n } else if (TECH_KEYWORDS.some((kw) => prompt.toLowerCase().includes(kw))) {\n complexity = \"high\";\n }\n\n const match =\n rules.find((r) => r.complexity === complexity) ??\n rules.find((r) => r.complexity === \"medium\");\n\n if (match) {\n return {\n model: match.targetModel,\n reason: `Matched ${complexity} complexity rule`,\n };\n }\n\n return { model: defaultModel, reason: `Default model for ${complexity} complexity` };\n },\n };\n}\n","import { randomUUID } from \"node:crypto\";\nimport { EventEmitter } from \"node:events\";\n\nexport type TrackResult = {\n requestId: string;\n durationMs: number;\n promptTokens: number;\n completionTokens: number;\n estimatedCostUSD: number;\n requestPayload: unknown;\n responsePayload: unknown;\n};\n\nexport type TrackerStorage = \"memory\" | \"sqlite\";\n\nexport type TrackerOptions = {\n storage?: TrackerStorage;\n sqlitePath?: string;\n};\n\nfunction estimateTokens(text: string): number {\n return Math.max(1, Math.ceil(text.length / 4));\n}\n\nfunction extractUsage(response: unknown): { prompt: number; completion: number } | null {\n if (!response || typeof response !== \"object\") return null;\n const r = response as Record<string, unknown>;\n const usage = (r.usage ?? (r as { data?: { usage?: unknown } }).data) as\n | Record<string, number>\n | undefined;\n if (!usage) return null;\n const prompt = usage.prompt_tokens ?? usage.promptTokens ?? usage.input_tokens;\n const completion =\n usage.completion_tokens ?? usage.completionTokens ?? usage.output_tokens;\n if (typeof prompt === \"number\" && typeof completion === \"number\") {\n return { prompt, completion };\n }\n return null;\n}\n\nexport interface Tracker {\n logExchange(request: unknown, response: unknown): Promise<TrackResult>;\n wrap<T extends (...args: unknown[]) => Promise<unknown>>(fn: T): T;\n on(event: \"exchange\", handler: (data: TrackResult) => void): void;\n}\n\nexport function createTracker(_options: TrackerOptions = {}): Tracker {\n const emitter = new EventEmitter();\n const memoryLogs: TrackResult[] = [];\n\n const logExchange = async (request: unknown, response: unknown): Promise<TrackResult> => {\n const start = Date.now();\n const usage = extractUsage(response);\n const reqText = JSON.stringify(request ?? \"\");\n const promptTokens = usage?.prompt ?? estimateTokens(reqText);\n const completionTokens =\n usage?.completion ?? estimateTokens(JSON.stringify(response ?? \"\"));\n\n const result: TrackResult = {\n requestId: randomUUID(),\n durationMs: Date.now() - start,\n promptTokens,\n completionTokens,\n estimatedCostUSD: 0,\n requestPayload: request,\n responsePayload: response,\n };\n\n memoryLogs.push(result);\n emitter.emit(\"exchange\", result);\n return result;\n };\n\n return {\n logExchange,\n wrap<T extends (...args: unknown[]) => Promise<unknown>>(fn: T): T {\n const wrapped = (async (...args: unknown[]) => {\n const request = args[0];\n const response = await fn(...args);\n await logExchange(request, response);\n return response;\n }) as T;\n return wrapped;\n },\n on(event: \"exchange\", handler: (data: TrackResult) => void) {\n emitter.on(event, handler);\n },\n };\n}\n","import { createFallback, type Fallback } from \"./fallback.js\";\nimport { createGuard, type Guard } from \"./guard.js\";\nimport { createRouter, type Router } from \"./router.js\";\nimport { createTracker, type Tracker, type TrackerOptions } from \"./tracker.js\";\n\nexport type AiToolboxOptions = {\n defaultProvider?: string;\n defaultModel?: string;\n apiKey?: string;\n storage?: TrackerOptions[\"storage\"];\n sqlitePath?: string;\n};\n\nexport class AIToolbox {\n readonly tracker: Tracker;\n readonly router: Router;\n readonly guard: Guard;\n readonly fallback: Fallback;\n\n constructor(options: AiToolboxOptions = {}) {\n this.tracker = createTracker({\n storage: options.storage,\n sqlitePath: options.sqlitePath,\n });\n this.router = createRouter(\n options.defaultModel ??\n (options.defaultProvider\n ? `${options.defaultProvider}/gpt-4o-mini`\n : \"openai/gpt-4o-mini\"),\n );\n this.guard = createGuard();\n this.fallback = createFallback();\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,83 @@
1
+ // src/cost/CostCalculator.ts
2
+ var CostCalculator = class {
3
+ constructor(catalog, options = {}) {
4
+ this.catalog = catalog;
5
+ this.aliasRegistry = options.aliasRegistry;
6
+ this.includeBreakdown = options.includeBreakdown ?? true;
7
+ this.resolverOptions = options.resolverOptions;
8
+ }
9
+ catalog;
10
+ aliasRegistry;
11
+ includeBreakdown;
12
+ resolverOptions;
13
+ async calculate(input) {
14
+ const resolved = await this.catalog.resolveModel(
15
+ { model: input.modelUsed, provider: input.provider },
16
+ {
17
+ ...this.resolverOptions,
18
+ aliasRegistry: this.aliasRegistry ?? this.resolverOptions?.aliasRegistry
19
+ }
20
+ );
21
+ if (!resolved.found || !resolved.record) {
22
+ console.warn(
23
+ `[ai-tools] Unknown model "${input.modelUsed}" \u2014 returning zero-cost fallback.`
24
+ );
25
+ const emptyPricing = {
26
+ promptUsdPerToken: 0,
27
+ completionUsdPerToken: 0,
28
+ imageUsdPerUnit: 0,
29
+ requestUsdPerRequest: 0,
30
+ pricedAt: (/* @__PURE__ */ new Date()).toISOString(),
31
+ source: "manual"
32
+ };
33
+ return {
34
+ cost: 0,
35
+ resolvedModelId: input.modelUsed,
36
+ routedViaOpenRouter: resolved.found ? resolved.routedViaOpenRouter : input.provider === "openrouter",
37
+ isAuthoritative: false,
38
+ pricingSnapshot: emptyPricing,
39
+ source: "estimate-fallback"
40
+ };
41
+ }
42
+ const { record, routedViaOpenRouter, modelId } = resolved;
43
+ const pricing = record.pricing;
44
+ const { tokens } = input;
45
+ let promptCost = tokens.prompt * pricing.promptUsdPerToken;
46
+ let completionCost = tokens.completion * pricing.completionUsdPerToken;
47
+ const cachingCost = (tokens.cacheWrite ?? 0) * (pricing.cacheWriteUsdPerToken ?? 0) + (tokens.cached ?? 0) * (pricing.cacheReadUsdPerToken ?? 0);
48
+ const reasoningCost = (tokens.reasoning ?? 0) * (pricing.reasoningUsdPerToken ?? pricing.promptUsdPerToken);
49
+ const audioCost = (tokens.audio ?? 0) * pricing.promptUsdPerToken;
50
+ const imageCost = (tokens.image ?? 0) * (pricing.imageUsdPerUnit ?? 0);
51
+ const requestFlat = pricing.requestUsdPerRequest;
52
+ if (routedViaOpenRouter) {
53
+ promptCost += tokens.prompt * (pricing.openRouterMarkupUsdPerInputToken ?? 0);
54
+ completionCost += tokens.completion * (pricing.openRouterMarkupUsdPerOutputToken ?? 0);
55
+ }
56
+ const cost = promptCost + completionCost + cachingCost + reasoningCost + audioCost + imageCost + requestFlat;
57
+ const result = {
58
+ cost,
59
+ resolvedModelId: modelId,
60
+ routedViaOpenRouter,
61
+ isAuthoritative: true,
62
+ pricingSnapshot: pricing,
63
+ source: "catalog"
64
+ };
65
+ if (this.includeBreakdown) {
66
+ result.breakdown = {
67
+ promptCostUsd: promptCost,
68
+ completionCostUsd: completionCost,
69
+ cachingCostUsd: cachingCost || void 0,
70
+ reasoningCostUsd: reasoningCost || void 0,
71
+ audioCostUsd: audioCost || void 0,
72
+ imageCostUsd: imageCost || void 0,
73
+ requestFlatCostUsd: requestFlat || void 0
74
+ };
75
+ }
76
+ return result;
77
+ }
78
+ };
79
+
80
+ export {
81
+ CostCalculator
82
+ };
83
+ //# sourceMappingURL=chunk-YHO57D2V.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cost/CostCalculator.ts"],"sourcesContent":["import type { AliasRegistry } from \"../aliases/AliasRegistry.js\";\nimport type { AiModelsCatalogClient } from \"../catalox/AiModelsCatalogClient.js\";\nimport type { ModelResolverOptions } from \"../sync/modelNameResolver/types.js\";\nimport type { AiCostResult, AiModelPricing, AiUsageInput } from \"./types.js\";\n\nexport type CostCalculatorOptions = {\n aliasRegistry?: AliasRegistry;\n includeBreakdown?: boolean;\n resolverOptions?: ModelResolverOptions;\n};\n\nexport class CostCalculator {\n private readonly aliasRegistry?: AliasRegistry;\n private readonly includeBreakdown: boolean;\n private readonly resolverOptions?: ModelResolverOptions;\n\n constructor(\n private readonly catalog: AiModelsCatalogClient,\n options: CostCalculatorOptions = {},\n ) {\n this.aliasRegistry = options.aliasRegistry;\n this.includeBreakdown = options.includeBreakdown ?? true;\n this.resolverOptions = options.resolverOptions;\n }\n\n async calculate(input: AiUsageInput): Promise<AiCostResult> {\n const resolved = await this.catalog.resolveModel(\n { model: input.modelUsed, provider: input.provider },\n {\n ...this.resolverOptions,\n aliasRegistry: this.aliasRegistry ?? this.resolverOptions?.aliasRegistry,\n },\n );\n\n if (!resolved.found || !resolved.record) {\n console.warn(\n `[ai-tools] Unknown model \"${input.modelUsed}\" — returning zero-cost fallback.`,\n );\n const emptyPricing: AiModelPricing = {\n promptUsdPerToken: 0,\n completionUsdPerToken: 0,\n imageUsdPerUnit: 0,\n requestUsdPerRequest: 0,\n pricedAt: new Date().toISOString(),\n source: \"manual\",\n };\n return {\n cost: 0,\n resolvedModelId: input.modelUsed,\n routedViaOpenRouter:\n resolved.found ? resolved.routedViaOpenRouter : input.provider === \"openrouter\",\n isAuthoritative: false,\n pricingSnapshot: emptyPricing,\n source: \"estimate-fallback\",\n };\n }\n\n const { record, routedViaOpenRouter, modelId } = resolved;\n const pricing = record.pricing;\n const { tokens } = input;\n\n let promptCost = tokens.prompt * pricing.promptUsdPerToken;\n let completionCost = tokens.completion * pricing.completionUsdPerToken;\n\n const cachingCost =\n (tokens.cacheWrite ?? 0) * (pricing.cacheWriteUsdPerToken ?? 0) +\n (tokens.cached ?? 0) * (pricing.cacheReadUsdPerToken ?? 0);\n\n const reasoningCost =\n (tokens.reasoning ?? 0) *\n (pricing.reasoningUsdPerToken ?? pricing.promptUsdPerToken);\n\n const audioCost = (tokens.audio ?? 0) * pricing.promptUsdPerToken;\n const imageCost = (tokens.image ?? 0) * (pricing.imageUsdPerUnit ?? 0);\n const requestFlat = pricing.requestUsdPerRequest;\n\n if (routedViaOpenRouter) {\n promptCost +=\n tokens.prompt * (pricing.openRouterMarkupUsdPerInputToken ?? 0);\n completionCost +=\n tokens.completion * (pricing.openRouterMarkupUsdPerOutputToken ?? 0);\n }\n\n const cost =\n promptCost +\n completionCost +\n cachingCost +\n reasoningCost +\n audioCost +\n imageCost +\n requestFlat;\n\n const result: AiCostResult = {\n cost,\n resolvedModelId: modelId,\n routedViaOpenRouter,\n isAuthoritative: true,\n pricingSnapshot: pricing,\n source: \"catalog\",\n };\n\n if (this.includeBreakdown) {\n result.breakdown = {\n promptCostUsd: promptCost,\n completionCostUsd: completionCost,\n cachingCostUsd: cachingCost || undefined,\n reasoningCostUsd: reasoningCost || undefined,\n audioCostUsd: audioCost || undefined,\n imageCostUsd: imageCost || undefined,\n requestFlatCostUsd: requestFlat || undefined,\n };\n }\n\n return result;\n }\n}\n"],"mappings":";AAWO,IAAM,iBAAN,MAAqB;AAAA,EAK1B,YACmB,SACjB,UAAiC,CAAC,GAClC;AAFiB;AAGjB,SAAK,gBAAgB,QAAQ;AAC7B,SAAK,mBAAmB,QAAQ,oBAAoB;AACpD,SAAK,kBAAkB,QAAQ;AAAA,EACjC;AAAA,EANmB;AAAA,EALF;AAAA,EACA;AAAA,EACA;AAAA,EAWjB,MAAM,UAAU,OAA4C;AAC1D,UAAM,WAAW,MAAM,KAAK,QAAQ;AAAA,MAClC,EAAE,OAAO,MAAM,WAAW,UAAU,MAAM,SAAS;AAAA,MACnD;AAAA,QACE,GAAG,KAAK;AAAA,QACR,eAAe,KAAK,iBAAiB,KAAK,iBAAiB;AAAA,MAC7D;AAAA,IACF;AAEA,QAAI,CAAC,SAAS,SAAS,CAAC,SAAS,QAAQ;AACvC,cAAQ;AAAA,QACN,6BAA6B,MAAM,SAAS;AAAA,MAC9C;AACA,YAAM,eAA+B;AAAA,QACnC,mBAAmB;AAAA,QACnB,uBAAuB;AAAA,QACvB,iBAAiB;AAAA,QACjB,sBAAsB;AAAA,QACtB,WAAU,oBAAI,KAAK,GAAE,YAAY;AAAA,QACjC,QAAQ;AAAA,MACV;AACA,aAAO;AAAA,QACL,MAAM;AAAA,QACN,iBAAiB,MAAM;AAAA,QACvB,qBACE,SAAS,QAAQ,SAAS,sBAAsB,MAAM,aAAa;AAAA,QACrE,iBAAiB;AAAA,QACjB,iBAAiB;AAAA,QACjB,QAAQ;AAAA,MACV;AAAA,IACF;AAEA,UAAM,EAAE,QAAQ,qBAAqB,QAAQ,IAAI;AACjD,UAAM,UAAU,OAAO;AACvB,UAAM,EAAE,OAAO,IAAI;AAEnB,QAAI,aAAa,OAAO,SAAS,QAAQ;AACzC,QAAI,iBAAiB,OAAO,aAAa,QAAQ;AAEjD,UAAM,eACH,OAAO,cAAc,MAAM,QAAQ,yBAAyB,MAC5D,OAAO,UAAU,MAAM,QAAQ,wBAAwB;AAE1D,UAAM,iBACH,OAAO,aAAa,MACpB,QAAQ,wBAAwB,QAAQ;AAE3C,UAAM,aAAa,OAAO,SAAS,KAAK,QAAQ;AAChD,UAAM,aAAa,OAAO,SAAS,MAAM,QAAQ,mBAAmB;AACpE,UAAM,cAAc,QAAQ;AAE5B,QAAI,qBAAqB;AACvB,oBACE,OAAO,UAAU,QAAQ,oCAAoC;AAC/D,wBACE,OAAO,cAAc,QAAQ,qCAAqC;AAAA,IACtE;AAEA,UAAM,OACJ,aACA,iBACA,cACA,gBACA,YACA,YACA;AAEF,UAAM,SAAuB;AAAA,MAC3B;AAAA,MACA,iBAAiB;AAAA,MACjB;AAAA,MACA,iBAAiB;AAAA,MACjB,iBAAiB;AAAA,MACjB,QAAQ;AAAA,IACV;AAEA,QAAI,KAAK,kBAAkB;AACzB,aAAO,YAAY;AAAA,QACjB,eAAe;AAAA,QACf,mBAAmB;AAAA,QACnB,gBAAgB,eAAe;AAAA,QAC/B,kBAAkB,iBAAiB;AAAA,QACnC,cAAc,aAAa;AAAA,QAC3B,cAAc,aAAa;AAAA,QAC3B,oBAAoB,eAAe;AAAA,MACrC;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AACF;","names":[]}