@x12i/ai-tools 1.0.3 → 2.0.0

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 (153) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +146 -204
  3. package/dist/AiModelsCatalogClient-B5FMI9gj.d.cts +58 -0
  4. package/dist/AiModelsCatalogClient-CPPNI6Ry.d.ts +58 -0
  5. package/dist/aliases/index.cjs +4 -3
  6. package/dist/aliases/index.cjs.map +1 -1
  7. package/dist/aliases/index.d.cts +3 -5
  8. package/dist/aliases/index.d.ts +3 -5
  9. package/dist/aliases/index.js +2 -2
  10. package/dist/catalog/index.cjs +18 -9
  11. package/dist/catalog/index.cjs.map +1 -1
  12. package/dist/catalog/index.d.cts +13 -100
  13. package/dist/catalog/index.d.ts +13 -100
  14. package/dist/catalog/index.js +31 -23
  15. package/dist/{chunk-AJEKEWWB.js → chunk-2PTCWPHV.js} +17 -3
  16. package/dist/chunk-2PTCWPHV.js.map +1 -0
  17. package/dist/chunk-56R4XA2S.js +1 -0
  18. package/dist/chunk-5GUKLOEK.cjs +1 -0
  19. package/dist/chunk-5GUKLOEK.cjs.map +1 -0
  20. package/dist/chunk-5XAAMBDO.cjs +1988 -0
  21. package/dist/chunk-5XAAMBDO.cjs.map +1 -0
  22. package/dist/chunk-6BQBKROR.js +95 -0
  23. package/dist/chunk-6BQBKROR.js.map +1 -0
  24. package/dist/chunk-AB5GNXJ4.js +46 -0
  25. package/dist/chunk-AB5GNXJ4.js.map +1 -0
  26. package/dist/{chunk-O2A6OVEH.js → chunk-ANVONYJF.js} +2 -2
  27. package/dist/{chunk-O2A6OVEH.js.map → chunk-ANVONYJF.js.map} +1 -1
  28. package/dist/chunk-B3V2EHRY.js +225 -0
  29. package/dist/chunk-B3V2EHRY.js.map +1 -0
  30. package/dist/{chunk-QWAX7VQO.cjs → chunk-BAHBDADJ.cjs} +11 -11
  31. package/dist/{chunk-QWAX7VQO.cjs.map → chunk-BAHBDADJ.cjs.map} +1 -1
  32. package/dist/{chunk-TF4L2NEC.cjs → chunk-DXZOL3VN.cjs} +62 -313
  33. package/dist/chunk-DXZOL3VN.cjs.map +1 -0
  34. package/dist/chunk-EDMCKHO6.cjs +225 -0
  35. package/dist/chunk-EDMCKHO6.cjs.map +1 -0
  36. package/dist/{chunk-DJ5SWJDY.js → chunk-EYHMQVAL.js} +48 -299
  37. package/dist/chunk-EYHMQVAL.js.map +1 -0
  38. package/dist/chunk-GS7T56RP.cjs +8 -0
  39. package/dist/chunk-GS7T56RP.cjs.map +1 -0
  40. package/dist/chunk-NF2SKQR7.cjs +973 -0
  41. package/dist/chunk-NF2SKQR7.cjs.map +1 -0
  42. package/dist/chunk-OPN6BGNH.js +1985 -0
  43. package/dist/chunk-OPN6BGNH.js.map +1 -0
  44. package/dist/{chunk-7Q742NI3.cjs → chunk-PADNCGZB.cjs} +17 -3
  45. package/dist/chunk-PADNCGZB.cjs.map +1 -0
  46. package/dist/chunk-PRCICORG.cjs +95 -0
  47. package/dist/chunk-PRCICORG.cjs.map +1 -0
  48. package/dist/{chunk-6QGDZTGH.js → chunk-SIH4GPV4.js} +4 -29
  49. package/dist/chunk-SIH4GPV4.js.map +1 -0
  50. package/dist/chunk-U2YDDUVP.js +970 -0
  51. package/dist/chunk-U2YDDUVP.js.map +1 -0
  52. package/dist/{chunk-4NAY6HRP.js → chunk-VJHLO2R3.js} +7 -58
  53. package/dist/chunk-VJHLO2R3.js.map +1 -0
  54. package/dist/chunk-XAWBTX3N.cjs +46 -0
  55. package/dist/chunk-XAWBTX3N.cjs.map +1 -0
  56. package/dist/{chunk-AV6OE2YQ.cjs → chunk-XOKUDUUI.cjs} +14 -39
  57. package/dist/chunk-XOKUDUUI.cjs.map +1 -0
  58. package/dist/chunk-YQDSN6R6.cjs +86 -0
  59. package/dist/chunk-YQDSN6R6.cjs.map +1 -0
  60. package/dist/cli/index.cjs +59 -201
  61. package/dist/cli/index.cjs.map +1 -1
  62. package/dist/cli/index.js +53 -198
  63. package/dist/cli/index.js.map +1 -1
  64. package/dist/cost/index.cjs +19 -3
  65. package/dist/cost/index.cjs.map +1 -1
  66. package/dist/cost/index.d.cts +10 -50
  67. package/dist/cost/index.d.ts +10 -50
  68. package/dist/cost/index.js +18 -3
  69. package/dist/index.cjs +24 -16
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.d.cts +13 -13
  72. package/dist/index.d.ts +13 -13
  73. package/dist/index.js +44 -37
  74. package/dist/modelCache-BzRn6t_C.d.ts +113 -0
  75. package/dist/modelCache-CJftI-Ko.d.cts +113 -0
  76. package/dist/{modelNameResolver-DqFt7g6W.d.ts → modelNameResolver-5XkBMctP.d.ts} +2 -6
  77. package/dist/{modelNameResolver-D9V_GfUK.d.cts → modelNameResolver-C5CSTGFF.d.cts} +2 -6
  78. package/dist/models/index.cjs +9 -6
  79. package/dist/models/index.cjs.map +1 -1
  80. package/dist/models/index.d.cts +9 -31
  81. package/dist/models/index.d.ts +9 -31
  82. package/dist/models/index.js +9 -7
  83. package/dist/resolveUsageModel-BFwf80Hz.d.ts +140 -0
  84. package/dist/resolveUsageModel-C_YmGR1M.d.cts +140 -0
  85. package/dist/sync/index.cjs +7 -9
  86. package/dist/sync/index.cjs.map +1 -1
  87. package/dist/sync/index.d.cts +3 -8
  88. package/dist/sync/index.d.ts +3 -8
  89. package/dist/sync/index.js +8 -11
  90. package/dist/toolbox/index.cjs +1 -0
  91. package/dist/toolbox/index.cjs.map +1 -1
  92. package/dist/types-BrzJWsTU.d.cts +277 -0
  93. package/dist/types-BrzJWsTU.d.ts +277 -0
  94. package/package.json +9 -20
  95. package/src/data/models-catalog.json +670 -0
  96. package/src/data/openrouter-models-catalog.json +857 -0
  97. package/dist/AiModelsCatalogClient-4RF5BCDL.cjs +0 -9
  98. package/dist/AiModelsCatalogClient-4RF5BCDL.cjs.map +0 -1
  99. package/dist/AiModelsCatalogClient-CNeqFiFs.d.cts +0 -30
  100. package/dist/AiModelsCatalogClient-NUF3CBLW.js +0 -9
  101. package/dist/AiModelsCatalogClient-nwFoEaqL.d.ts +0 -30
  102. package/dist/catalox/index.cjs +0 -21
  103. package/dist/catalox/index.cjs.map +0 -1
  104. package/dist/catalox/index.d.cts +0 -11
  105. package/dist/catalox/index.d.ts +0 -11
  106. package/dist/catalox/index.js +0 -21
  107. package/dist/catalox/index.js.map +0 -1
  108. package/dist/chunk-4NAY6HRP.js.map +0 -1
  109. package/dist/chunk-6QGDZTGH.js.map +0 -1
  110. package/dist/chunk-7Q742NI3.cjs.map +0 -1
  111. package/dist/chunk-AJEKEWWB.js.map +0 -1
  112. package/dist/chunk-AV6OE2YQ.cjs.map +0 -1
  113. package/dist/chunk-C3H7RTFR.cjs +0 -1
  114. package/dist/chunk-C3H7RTFR.cjs.map +0 -1
  115. package/dist/chunk-DJ5SWJDY.js.map +0 -1
  116. package/dist/chunk-DKHGWHXP.cjs +0 -169
  117. package/dist/chunk-DKHGWHXP.cjs.map +0 -1
  118. package/dist/chunk-F2F4UEFD.cjs +0 -75
  119. package/dist/chunk-F2F4UEFD.cjs.map +0 -1
  120. package/dist/chunk-FGP3QXWL.cjs +0 -163
  121. package/dist/chunk-FGP3QXWL.cjs.map +0 -1
  122. package/dist/chunk-G2G4KSC5.js +0 -30
  123. package/dist/chunk-G2G4KSC5.js.map +0 -1
  124. package/dist/chunk-HN6UAQAE.cjs +0 -83
  125. package/dist/chunk-HN6UAQAE.cjs.map +0 -1
  126. package/dist/chunk-HS74X2OJ.cjs +0 -172
  127. package/dist/chunk-HS74X2OJ.cjs.map +0 -1
  128. package/dist/chunk-HYGXZY25.js +0 -163
  129. package/dist/chunk-HYGXZY25.js.map +0 -1
  130. package/dist/chunk-KQOALKKX.js +0 -75
  131. package/dist/chunk-KQOALKKX.js.map +0 -1
  132. package/dist/chunk-LYOU7CA2.cjs +0 -30
  133. package/dist/chunk-LYOU7CA2.cjs.map +0 -1
  134. package/dist/chunk-M5TMA73F.js +0 -1
  135. package/dist/chunk-M5TMA73F.js.map +0 -1
  136. package/dist/chunk-MX3AMQFC.js +0 -172
  137. package/dist/chunk-MX3AMQFC.js.map +0 -1
  138. package/dist/chunk-QCRLKVB3.cjs +0 -137
  139. package/dist/chunk-QCRLKVB3.cjs.map +0 -1
  140. package/dist/chunk-TF4L2NEC.cjs.map +0 -1
  141. package/dist/chunk-VRFVF5RH.js +0 -169
  142. package/dist/chunk-VRFVF5RH.js.map +0 -1
  143. package/dist/chunk-YHO57D2V.js +0 -83
  144. package/dist/chunk-YHO57D2V.js.map +0 -1
  145. package/dist/syncAiModelsCatalog-CnXRLm2c.d.cts +0 -32
  146. package/dist/syncAiModelsCatalog-DpkN_w7S.d.ts +0 -32
  147. package/dist/types-BYXnCvKx.d.cts +0 -137
  148. package/dist/types-BYXnCvKx.d.ts +0 -137
  149. package/dist/types-CX6QFNNy.d.cts +0 -144
  150. package/dist/types-CuiPDcVs.d.ts +0 -144
  151. package/dist/upsertAiModelRecord-C831wOIF.d.ts +0 -35
  152. package/dist/upsertAiModelRecord-CjY-sny0.d.cts +0 -35
  153. /package/dist/{AiModelsCatalogClient-NUF3CBLW.js.map → chunk-56R4XA2S.js.map} +0 -0
@@ -1,75 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
2
-
3
-
4
-
5
-
6
-
7
-
8
-
9
- var _chunkTF4L2NECcjs = require('./chunk-TF4L2NEC.cjs');
10
-
11
- // src/catalox/AiModelsCatalogClient.ts
12
- function recordFromItem(data) {
13
- return data;
14
- }
15
- var AiModelsCatalogClient = class {
16
-
17
-
18
-
19
-
20
-
21
- constructor(options) {
22
- this.catalox = options.catalox;
23
- this.appId = _nullishCoalesce(options.appId, () => ( _chunkTF4L2NECcjs.AI_TOOLS_APP_ID));
24
- this.catalogId = _nullishCoalesce(options.catalogId, () => ( _chunkTF4L2NECcjs.AI_MODELS_CATALOG_ID));
25
- this.cacheTtlMs = _nullishCoalesce(options.cacheTtlMs, () => ( _chunkTF4L2NECcjs.DEFAULT_MODEL_CACHE_TTL_MS));
26
- this.resolverOptions = options.resolverOptions;
27
- }
28
- context() {
29
- return { appId: this.appId, superAdmin: true };
30
- }
31
- async getAllModels() {
32
- const cached = _chunkTF4L2NECcjs.readCachedModels.call(void 0, this.appId, this.cacheTtlMs);
33
- if (cached) return cached;
34
- const map = /* @__PURE__ */ new Map();
35
- const pageSize = 500;
36
- let offset = 0;
37
- for (; ; ) {
38
- const result = await this.catalox.listCatalogItems(this.context(), this.catalogId, {
39
- limit: pageSize,
40
- offset
41
- });
42
- for (const item of result.items) {
43
- const record = recordFromItem(item.data);
44
- if (record.modelId) map.set(record.modelId, record);
45
- }
46
- if (result.items.length < pageSize) break;
47
- offset += pageSize;
48
- }
49
- _chunkTF4L2NECcjs.writeCachedModels.call(void 0, this.appId, map, this.cacheTtlMs);
50
- return map;
51
- }
52
- resolver(models, options) {
53
- return new (0, _chunkTF4L2NECcjs.ModelNameResolver)(models, { ...this.resolverOptions, ...options });
54
- }
55
- /**
56
- * Resolve a (provider, model) pair to a canonical model id and catalog record.
57
- */
58
- async resolveModel(input, options) {
59
- const models = await this.getAllModels();
60
- return this.resolver(models, options).resolve(input);
61
- }
62
- async getModel(modelId, provider, options) {
63
- const result = await this.resolveModel({ model: modelId, provider }, options);
64
- return result.found ? result.record : null;
65
- }
66
- async refresh() {
67
- _chunkTF4L2NECcjs.invalidateModelsCache.call(void 0, this.appId);
68
- await this.getAllModels();
69
- }
70
- };
71
-
72
-
73
-
74
- exports.AiModelsCatalogClient = AiModelsCatalogClient;
75
- //# sourceMappingURL=chunk-F2F4UEFD.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-F2F4UEFD.cjs","../src/catalox/AiModelsCatalogClient.ts"],"names":[],"mappings":"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","file":"/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-F2F4UEFD.cjs","sourcesContent":[null,"import type { Catalox, CataloxContext } from \"@x12i/catalox\";\nimport {\n DEFAULT_MODEL_CACHE_TTL_MS,\n invalidateModelsCache,\n readCachedModels,\n writeCachedModels,\n} from \"../cache/modelCache.js\";\nimport type { AiModelRecord } from \"../models/types.js\";\nimport { AI_MODELS_CATALOG_ID, AI_TOOLS_APP_ID } from \"../catalog/aiModelsCatalogDescriptor.js\";\nimport { ModelNameResolver } from \"../sync/modelNameResolver/ModelNameResolver.js\";\nimport type {\n ModelResolutionInput,\n ModelResolutionResult,\n ModelResolverOptions,\n} from \"../sync/modelNameResolver/types.js\";\n\nexport type AiModelsCatalogClientOptions = {\n catalox: Catalox;\n appId?: string;\n catalogId?: string;\n cacheTtlMs?: number;\n resolverOptions?: Omit<ModelResolverOptions, \"aliasRegistry\">;\n};\n\nfunction recordFromItem(data: Record<string, unknown>): AiModelRecord {\n return data as unknown as AiModelRecord;\n}\n\nexport class AiModelsCatalogClient {\n private readonly catalox: Catalox;\n private readonly appId: string;\n private readonly catalogId: string;\n private readonly cacheTtlMs: number;\n private readonly resolverOptions: AiModelsCatalogClientOptions[\"resolverOptions\"];\n\n constructor(options: AiModelsCatalogClientOptions) {\n this.catalox = options.catalox;\n this.appId = options.appId ?? AI_TOOLS_APP_ID;\n this.catalogId = options.catalogId ?? AI_MODELS_CATALOG_ID;\n this.cacheTtlMs = options.cacheTtlMs ?? DEFAULT_MODEL_CACHE_TTL_MS;\n this.resolverOptions = options.resolverOptions;\n }\n\n private context(): CataloxContext {\n return { appId: this.appId, superAdmin: true };\n }\n\n async getAllModels(): Promise<Map<string, AiModelRecord>> {\n const cached = readCachedModels(this.appId, this.cacheTtlMs);\n if (cached) return cached;\n\n const map = new Map<string, AiModelRecord>();\n const pageSize = 500;\n let offset = 0;\n\n for (;;) {\n const result = await this.catalox.listCatalogItems(this.context(), this.catalogId, {\n limit: pageSize,\n offset,\n });\n\n for (const item of result.items) {\n const record = recordFromItem(item.data as Record<string, unknown>);\n if (record.modelId) map.set(record.modelId, record);\n }\n\n if (result.items.length < pageSize) break;\n offset += pageSize;\n }\n\n writeCachedModels(this.appId, map, this.cacheTtlMs);\n return map;\n }\n\n private resolver(models: Map<string, AiModelRecord>, options?: ModelResolverOptions): ModelNameResolver {\n return new ModelNameResolver(models, { ...this.resolverOptions, ...options });\n }\n\n /**\n * Resolve a (provider, model) pair to a canonical model id and catalog record.\n */\n async resolveModel(\n input: ModelResolutionInput,\n options?: ModelResolverOptions,\n ): Promise<ModelResolutionResult> {\n const models = await this.getAllModels();\n return this.resolver(models, options).resolve(input);\n }\n\n async getModel(\n modelId: string,\n provider?: string,\n options?: ModelResolverOptions,\n ): Promise<AiModelRecord | null> {\n const result = await this.resolveModel({ model: modelId, provider }, options);\n return result.found ? result.record : null;\n }\n\n async refresh(): Promise<void> {\n invalidateModelsCache(this.appId);\n await this.getAllModels();\n }\n}\n"]}
@@ -1,163 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/catalox/modelDocId.ts
2
- var CX = "cx~";
3
- var MODEL_MARKER = "m~";
4
- function encodeModelFirestoreDocId(modelId) {
5
- if (!modelId.includes("/")) return modelId;
6
- return `${CX}${MODEL_MARKER}${Buffer.from(modelId, "utf8").toString("base64url")}`;
7
- }
8
- function decodeModelFirestoreDocId(docId) {
9
- const prefix = `${CX}${MODEL_MARKER}`;
10
- if (docId.startsWith(prefix)) {
11
- return Buffer.from(docId.slice(prefix.length), "base64url").toString("utf8");
12
- }
13
- return docId;
14
- }
15
-
16
- // src/catalox/upsertAiModelRecord.ts
17
-
18
-
19
-
20
-
21
- var _firebase = require('@x12i/catalox/firebase');
22
- function sanitizeForFirestore(value) {
23
- return JSON.parse(JSON.stringify(value));
24
- }
25
- function buildIndexed(record) {
26
- return {
27
- modelId: record.modelId,
28
- providerId: record.providerId,
29
- name: record.name,
30
- canonicalSlug: record.canonicalSlug,
31
- contextLength: record.contextLength,
32
- status: record.status,
33
- primaryOutputModality: record.primaryOutputModality,
34
- supportsTools: record.supportsTools,
35
- supportsReasoning: record.supportsReasoning,
36
- supportsStreaming: record.supportsStreaming,
37
- isModerated: record.isModerated,
38
- syncedAt: record.syncedAt
39
- };
40
- }
41
- function buildCatalogItemUpsertPayload(record, existing, options) {
42
- const { catalogId, appId, now } = options;
43
- return sanitizeForFirestore({
44
- itemId: record.modelId,
45
- catalogId,
46
- appScopedOwnerId: appId,
47
- indexed: buildIndexed(record),
48
- data: sanitizeForFirestore(record),
49
- metadata: {
50
- createdAt: String(_nullishCoalesce(_optionalChain([existing, 'optionalAccess', _ => _.metadata, 'optionalAccess', _2 => _2.createdAt]), () => ( now))),
51
- lastUpdate: now,
52
- domainIds: [],
53
- agentIds: []
54
- },
55
- version: (_nullishCoalesce(_optionalChain([existing, 'optionalAccess', _3 => _3.version]), () => ( 0))) + 1
56
- });
57
- }
58
- var EXISTING_READ_BATCH = 50;
59
- async function loadExistingByStorageId(col, records) {
60
- const map = /* @__PURE__ */ new Map();
61
- for (let i = 0; i < records.length; i += EXISTING_READ_BATCH) {
62
- const slice = records.slice(i, i + EXISTING_READ_BATCH);
63
- const snaps = await Promise.all(
64
- slice.map((r) => col.doc(encodeModelFirestoreDocId(r.modelId)).get())
65
- );
66
- for (let j = 0; j < slice.length; j++) {
67
- const storageDocId = encodeModelFirestoreDocId(slice[j].modelId);
68
- const data = snaps[j].data();
69
- if (data) map.set(storageDocId, data);
70
- }
71
- }
72
- return map;
73
- }
74
- async function upsertAiModelRecord(options) {
75
- const { record, catalogId, context, firestore } = options;
76
- const appId = _nullishCoalesce(context.appId, () => ( "ai-tools"));
77
- const layout = await _firebase.resolveNativeItemsLayout.call(void 0, firestore, catalogId);
78
- const col = layout === "legacy" ? _firebase.legacyNativeItemsCollectionRef.call(void 0, firestore, catalogId) : _firebase.flatNativeItemsCollectionRef.call(void 0, firestore, catalogId);
79
- const storageDocId = encodeModelFirestoreDocId(record.modelId);
80
- const existing = (await col.doc(storageDocId).get()).data();
81
- const now = (/* @__PURE__ */ new Date()).toISOString();
82
- const payload = buildCatalogItemUpsertPayload(record, existing, { catalogId, appId, now });
83
- await col.doc(storageDocId).set(payload, { merge: true });
84
- }
85
- var BATCH_SIZE = 400;
86
- var DEFAULT_CHUNK_RETRIES = 3;
87
- var RETRY_BASE_MS = 500;
88
- function sleep(ms) {
89
- return new Promise((resolve) => setTimeout(resolve, ms));
90
- }
91
- async function commitChunkWithRetry(firestore, col, chunk, existingByStorageId, options) {
92
- const { catalogId, appId, now, maxChunkRetries } = options;
93
- for (let attempt = 0; attempt < maxChunkRetries; attempt++) {
94
- try {
95
- const batch = firestore.batch();
96
- for (const record of chunk) {
97
- const storageDocId = encodeModelFirestoreDocId(record.modelId);
98
- const existing = existingByStorageId.get(storageDocId);
99
- const payload = buildCatalogItemUpsertPayload(record, existing, { catalogId, appId, now });
100
- batch.set(col.doc(storageDocId), payload, { merge: true });
101
- }
102
- await batch.commit();
103
- return { upserted: chunk.length, failed: [] };
104
- } catch (e) {
105
- if (attempt < maxChunkRetries - 1) {
106
- await sleep(RETRY_BASE_MS * 2 ** attempt);
107
- }
108
- }
109
- }
110
- const failed = [];
111
- let upserted = 0;
112
- for (const record of chunk) {
113
- try {
114
- const storageDocId = encodeModelFirestoreDocId(record.modelId);
115
- const existing = existingByStorageId.get(storageDocId);
116
- const payload = buildCatalogItemUpsertPayload(record, existing, { catalogId, appId, now });
117
- await col.doc(storageDocId).set(payload, { merge: true });
118
- upserted++;
119
- } catch (error) {
120
- failed.push({
121
- modelId: record.modelId,
122
- error: error instanceof Error ? error.message : String(error)
123
- });
124
- }
125
- }
126
- return { upserted, failed };
127
- }
128
- async function batchUpsertAiModelRecords(firestore, catalogId, context, records, batchOptions = {}) {
129
- const layout = await _firebase.resolveNativeItemsLayout.call(void 0, firestore, catalogId);
130
- const col = layout === "legacy" ? _firebase.legacyNativeItemsCollectionRef.call(void 0, firestore, catalogId) : _firebase.flatNativeItemsCollectionRef.call(void 0, firestore, catalogId);
131
- const appId = _nullishCoalesce(context.appId, () => ( "ai-tools"));
132
- const now = (/* @__PURE__ */ new Date()).toISOString();
133
- const maxChunkRetries = _nullishCoalesce(batchOptions.maxChunkRetries, () => ( DEFAULT_CHUNK_RETRIES));
134
- const total = records.length;
135
- _optionalChain([batchOptions, 'access', _4 => _4.onProgress, 'optionalCall', _5 => _5({ completed: 0, total, phase: "loading-existing" })]);
136
- const existingByStorageId = await loadExistingByStorageId(col, records);
137
- let completed = 0;
138
- let upserted = 0;
139
- const failed = [];
140
- for (let i = 0; i < records.length; i += BATCH_SIZE) {
141
- const chunk = records.slice(i, i + BATCH_SIZE);
142
- const chunkResult = await commitChunkWithRetry(firestore, col, chunk, existingByStorageId, {
143
- catalogId,
144
- appId,
145
- now,
146
- maxChunkRetries
147
- });
148
- upserted += chunkResult.upserted;
149
- failed.push(...chunkResult.failed);
150
- completed += chunk.length;
151
- _optionalChain([batchOptions, 'access', _6 => _6.onProgress, 'optionalCall', _7 => _7({ completed, total, phase: "writing" })]);
152
- }
153
- return { upserted, failed };
154
- }
155
-
156
-
157
-
158
-
159
-
160
-
161
-
162
- exports.encodeModelFirestoreDocId = encodeModelFirestoreDocId; exports.decodeModelFirestoreDocId = decodeModelFirestoreDocId; exports.sanitizeForFirestore = sanitizeForFirestore; exports.upsertAiModelRecord = upsertAiModelRecord; exports.batchUpsertAiModelRecords = batchUpsertAiModelRecords;
163
- //# sourceMappingURL=chunk-FGP3QXWL.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-FGP3QXWL.cjs","../src/catalox/modelDocId.ts","../src/catalox/upsertAiModelRecord.ts"],"names":[],"mappings":"AAAA;ACIA,IAAM,GAAA,EAAK,KAAA;AACX,IAAM,aAAA,EAAe,IAAA;AAEd,SAAS,yBAAA,CAA0B,OAAA,EAAyB;AACjE,EAAA,GAAA,CAAI,CAAC,OAAA,CAAQ,QAAA,CAAS,GAAG,CAAA,EAAG,OAAO,OAAA;AACnC,EAAA,OAAO,CAAA,EAAA;AACT;AAEgB;AACR,EAAA;AACF,EAAA;AACF,IAAA;AACF,EAAA;AACO,EAAA;AACT;ADJU;AACA;AEbV;AACE;AACA;AACA;AACK;AAKS;AACP,EAAA;AACT;AAES;AACA,EAAA;AACL,IAAA;AACA,IAAA;AACM,IAAA;AACN,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AAQgB;AAKN,EAAA;AACD,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACM,IAAA;AACN,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACD,EAAA;AACH;AAEM;AAEN;AAIQ,EAAA;AACN,EAAA;AACQ,IAAA;AACA,IAAA;AACJ,MAAA;AACF,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACI,MAAA;AACN,IAAA;AACF,EAAA;AACO,EAAA;AACT;AAYA;AACU,EAAA;AACF,EAAA;AAEA,EAAA;AACA,EAAA;AAKA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEA,EAAA;AACR;AAEM;AACA;AACA;AAkBG;AACA,EAAA;AACT;AAEA;AAYU,EAAA;AACR,EAAA;AACM,IAAA;AACF,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACM,MAAA;AACF,QAAA;AACF,MAAA;AACF,IAAA;AACF,EAAA;AAEM,EAAA;AACF,EAAA;AAEJ,EAAA;AACM,IAAA;AACF,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACE,MAAA;AACE,QAAA;AACA,QAAA;AACD,MAAA;AACH,IAAA;AACF,EAAA;AAEO,EAAA;AACT;AAEA;AAOQ,EAAA;AACA,EAAA;AAKA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEN,kBAAA;AACM,EAAA;AAEF,EAAA;AACA,EAAA;AACE,EAAA;AAEN,EAAA;AACQ,IAAA;AACA,IAAA;AACJ,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACD,IAAA;AACD,IAAA;AACA,IAAA;AACA,IAAA;AACA,oBAAA;AACF,EAAA;AAEO,EAAA;AACT;AFvEU;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-FGP3QXWL.cjs","sourcesContent":[null,"/**\n * Firestore document ids cannot contain `/`. OpenRouter model ids use `provider/model`.\n * We encode global item doc ids as `cx~m~<base64url(modelId)>` while keeping logical `modelId` in data.\n */\nconst CX = \"cx~\";\nconst MODEL_MARKER = \"m~\";\n\nexport function encodeModelFirestoreDocId(modelId: string): string {\n if (!modelId.includes(\"/\")) return modelId;\n return `${CX}${MODEL_MARKER}${Buffer.from(modelId, \"utf8\").toString(\"base64url\")}`;\n}\n\nexport function decodeModelFirestoreDocId(docId: string): string {\n const prefix = `${CX}${MODEL_MARKER}`;\n if (docId.startsWith(prefix)) {\n return Buffer.from(docId.slice(prefix.length), \"base64url\").toString(\"utf8\");\n }\n return docId;\n}\n\nexport function modelIdNeedsEncodedDocId(modelId: string): boolean {\n return modelId.includes(\"/\");\n}\n","import type { Firestore } from \"firebase-admin/firestore\";\nimport type { CataloxContext } from \"@x12i/catalox\";\nimport {\n flatNativeItemsCollectionRef,\n legacyNativeItemsCollectionRef,\n resolveNativeItemsLayout,\n} from \"@x12i/catalox/firebase\";\nimport type { AiModelRecord } from \"../models/types.js\";\nimport { encodeModelFirestoreDocId } from \"./modelDocId.js\";\n\n/** Firestore rejects `undefined` — strip before write. */\nexport function sanitizeForFirestore<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T;\n}\n\nfunction buildIndexed(record: AiModelRecord): Record<string, string | number | boolean> {\n return {\n modelId: record.modelId,\n providerId: record.providerId,\n name: record.name,\n canonicalSlug: record.canonicalSlug,\n contextLength: record.contextLength,\n status: record.status,\n primaryOutputModality: record.primaryOutputModality,\n supportsTools: record.supportsTools,\n supportsReasoning: record.supportsReasoning,\n supportsStreaming: record.supportsStreaming,\n isModerated: record.isModerated,\n syncedAt: record.syncedAt,\n };\n}\n\ntype ExistingCatalogDoc = {\n metadata?: { createdAt?: string };\n version?: number;\n};\n\n/** @internal Exported for unit tests. */\nexport function buildCatalogItemUpsertPayload(\n record: AiModelRecord,\n existing: ExistingCatalogDoc | undefined,\n options: { catalogId: string; appId: string; now: string },\n) {\n const { catalogId, appId, now } = options;\n return sanitizeForFirestore({\n itemId: record.modelId,\n catalogId,\n appScopedOwnerId: appId,\n indexed: buildIndexed(record),\n data: sanitizeForFirestore(record),\n metadata: {\n createdAt: String(existing?.metadata?.createdAt ?? now),\n lastUpdate: now,\n domainIds: [],\n agentIds: [],\n },\n version: (existing?.version ?? 0) + 1,\n });\n}\n\nconst EXISTING_READ_BATCH = 50;\n\nasync function loadExistingByStorageId(\n col: ReturnType<typeof flatNativeItemsCollectionRef>,\n records: AiModelRecord[],\n): Promise<Map<string, ExistingCatalogDoc>> {\n const map = new Map<string, ExistingCatalogDoc>();\n for (let i = 0; i < records.length; i += EXISTING_READ_BATCH) {\n const slice = records.slice(i, i + EXISTING_READ_BATCH);\n const snaps = await Promise.all(\n slice.map((r) => col.doc(encodeModelFirestoreDocId(r.modelId)).get()),\n );\n for (let j = 0; j < slice.length; j++) {\n const storageDocId = encodeModelFirestoreDocId(slice[j]!.modelId);\n const data = snaps[j]!.data() as ExistingCatalogDoc | undefined;\n if (data) map.set(storageDocId, data);\n }\n }\n return map;\n}\n\nexport type UpsertAiModelRecordOptions = {\n firestore: Firestore;\n context: CataloxContext;\n catalogId: string;\n record: AiModelRecord;\n};\n\n/**\n * Upserts one ai-models row via Firestore (safe doc ids for `provider/model` ids).\n */\nexport async function upsertAiModelRecord(options: UpsertAiModelRecordOptions): Promise<void> {\n const { record, catalogId, context, firestore } = options;\n const appId = context.appId ?? \"ai-tools\";\n\n const layout = await resolveNativeItemsLayout(firestore, catalogId);\n const col =\n layout === \"legacy\"\n ? legacyNativeItemsCollectionRef(firestore, catalogId)\n : flatNativeItemsCollectionRef(firestore, catalogId);\n\n const storageDocId = encodeModelFirestoreDocId(record.modelId);\n const existing = (await col.doc(storageDocId).get()).data() as ExistingCatalogDoc | undefined;\n const now = new Date().toISOString();\n const payload = buildCatalogItemUpsertPayload(record, existing, { catalogId, appId, now });\n\n await col.doc(storageDocId).set(payload, { merge: true });\n}\n\nconst BATCH_SIZE = 400;\nconst DEFAULT_CHUNK_RETRIES = 3;\nconst RETRY_BASE_MS = 500;\n\nexport type BatchUpsertProgress = {\n completed: number;\n total: number;\n phase: \"loading-existing\" | \"writing\";\n};\n\nexport type BatchUpsertAiModelRecordsOptions = {\n onProgress?: (progress: BatchUpsertProgress) => void;\n maxChunkRetries?: number;\n};\n\nexport type BatchUpsertResult = {\n upserted: number;\n failed: Array<{ modelId: string; error: string }>;\n};\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function commitChunkWithRetry(\n firestore: Firestore,\n col: ReturnType<typeof flatNativeItemsCollectionRef>,\n chunk: AiModelRecord[],\n existingByStorageId: Map<string, ExistingCatalogDoc>,\n options: {\n catalogId: string;\n appId: string;\n now: string;\n maxChunkRetries: number;\n },\n): Promise<{ upserted: number; failed: Array<{ modelId: string; error: string }> }> {\n const { catalogId, appId, now, maxChunkRetries } = options;\n for (let attempt = 0; attempt < maxChunkRetries; attempt++) {\n try {\n const batch = firestore.batch();\n for (const record of chunk) {\n const storageDocId = encodeModelFirestoreDocId(record.modelId);\n const existing = existingByStorageId.get(storageDocId);\n const payload = buildCatalogItemUpsertPayload(record, existing, { catalogId, appId, now });\n batch.set(col.doc(storageDocId), payload, { merge: true });\n }\n await batch.commit();\n return { upserted: chunk.length, failed: [] };\n } catch {\n if (attempt < maxChunkRetries - 1) {\n await sleep(RETRY_BASE_MS * 2 ** attempt);\n }\n }\n }\n\n const failed: Array<{ modelId: string; error: string }> = [];\n let upserted = 0;\n\n for (const record of chunk) {\n try {\n const storageDocId = encodeModelFirestoreDocId(record.modelId);\n const existing = existingByStorageId.get(storageDocId);\n const payload = buildCatalogItemUpsertPayload(record, existing, { catalogId, appId, now });\n await col.doc(storageDocId).set(payload, { merge: true });\n upserted++;\n } catch (error) {\n failed.push({\n modelId: record.modelId,\n error: error instanceof Error ? error.message : String(error),\n });\n }\n }\n\n return { upserted, failed };\n}\n\nexport async function batchUpsertAiModelRecords(\n firestore: Firestore,\n catalogId: string,\n context: CataloxContext,\n records: AiModelRecord[],\n batchOptions: BatchUpsertAiModelRecordsOptions = {},\n): Promise<BatchUpsertResult> {\n const layout = await resolveNativeItemsLayout(firestore, catalogId);\n const col =\n layout === \"legacy\"\n ? legacyNativeItemsCollectionRef(firestore, catalogId)\n : flatNativeItemsCollectionRef(firestore, catalogId);\n\n const appId = context.appId ?? \"ai-tools\";\n const now = new Date().toISOString();\n const maxChunkRetries = batchOptions.maxChunkRetries ?? DEFAULT_CHUNK_RETRIES;\n const total = records.length;\n\n batchOptions.onProgress?.({ completed: 0, total, phase: \"loading-existing\" });\n const existingByStorageId = await loadExistingByStorageId(col, records);\n\n let completed = 0;\n let upserted = 0;\n const failed: Array<{ modelId: string; error: string }> = [];\n\n for (let i = 0; i < records.length; i += BATCH_SIZE) {\n const chunk = records.slice(i, i + BATCH_SIZE);\n const chunkResult = await commitChunkWithRetry(firestore, col, chunk, existingByStorageId, {\n catalogId,\n appId,\n now,\n maxChunkRetries,\n });\n upserted += chunkResult.upserted;\n failed.push(...chunkResult.failed);\n completed += chunk.length;\n batchOptions.onProgress?.({ completed, total, phase: \"writing\" });\n }\n\n return { upserted, failed };\n}\n"]}
@@ -1,30 +0,0 @@
1
- import {
2
- ModelNameResolver
3
- } from "./chunk-DJ5SWJDY.js";
4
-
5
- // src/sync/modelNameResolver.ts
6
- function createModelNameResolver(catalog, options) {
7
- return new ModelNameResolver(catalog, options);
8
- }
9
- function resolveModel(input, models, aliasRegistry, provider) {
10
- const resolver = new ModelNameResolver(models, { aliasRegistry });
11
- const result = resolver.resolve({ model: input, provider });
12
- if (!result.found || !result.record) return null;
13
- return {
14
- catalogModel: result.record,
15
- matchedAlias: result.modelId,
16
- routedViaOpenRouter: result.routedViaOpenRouter
17
- };
18
- }
19
- function isRoutedViaOpenRouter(provider, model, matchedKey) {
20
- if (provider === "openrouter") return true;
21
- if (provider && provider !== "openrouter") return false;
22
- return model.modelId.includes("/") && matchedKey.includes("/");
23
- }
24
-
25
- export {
26
- createModelNameResolver,
27
- resolveModel,
28
- isRoutedViaOpenRouter
29
- };
30
- //# sourceMappingURL=chunk-G2G4KSC5.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/sync/modelNameResolver.ts"],"sourcesContent":["import type { AliasRegistry } from \"../aliases/AliasRegistry.js\";\nimport type { AiModelRecord } from \"../models/types.js\";\nimport { ModelNameResolver } from \"./modelNameResolver/ModelNameResolver.js\";\nimport type {\n ModelResolutionInput,\n ModelResolutionResult,\n ModelResolutionSuccess,\n ModelResolverOptions,\n ResolvedModel,\n} from \"./modelNameResolver/types.js\";\n\nexport { ModelNameResolver } from \"./modelNameResolver/ModelNameResolver.js\";\nexport { buildCatalogIndexes } from \"./modelNameResolver/catalogIndexes.js\";\nexport * from \"./modelNameResolver/types.js\";\nexport {\n loadOpenRouterRoutingEnv,\n shouldDefaultRouteViaOpenRouter,\n providerIdToEnvKeyPrefix,\n vendorApiKeyEnvName,\n} from \"./openRouterRoutingEnv.js\";\nexport type { OpenRouterRoutingConfig } from \"./openRouterRoutingEnv.js\";\nexport { normalizeString, normalizeProvider } from \"./modelNameResolver/normalize.js\";\n\nexport function createModelNameResolver(\n catalog: Map<string, AiModelRecord>,\n options?: ModelResolverOptions,\n): ModelNameResolver {\n return new ModelNameResolver(catalog, options);\n}\n\n/**\n * @deprecated Prefer `ModelNameResolver.resolve()` for full resolution metadata.\n */\nexport function resolveModel(\n input: string,\n models: Map<string, AiModelRecord>,\n aliasRegistry?: AliasRegistry,\n provider?: string,\n): ResolvedModel | null {\n const resolver = new ModelNameResolver(models, { aliasRegistry });\n const result = resolver.resolve({ model: input, provider });\n if (!result.found || !result.record) return null;\n return {\n catalogModel: result.record,\n matchedAlias: result.modelId,\n routedViaOpenRouter: result.routedViaOpenRouter,\n };\n}\n\nexport function isRoutedViaOpenRouter(\n provider: string | undefined,\n model: AiModelRecord,\n matchedKey: string,\n): boolean {\n if (provider === \"openrouter\") return true;\n if (provider && provider !== \"openrouter\") return false;\n return model.modelId.includes(\"/\") && matchedKey.includes(\"/\");\n}\n\nexport type {\n ModelResolutionInput,\n ModelResolutionResult,\n ModelResolutionSuccess,\n ModelResolverOptions,\n};\n"],"mappings":";;;;;AAuBO,SAAS,wBACd,SACA,SACmB;AACnB,SAAO,IAAI,kBAAkB,SAAS,OAAO;AAC/C;AAKO,SAAS,aACd,OACA,QACA,eACA,UACsB;AACtB,QAAM,WAAW,IAAI,kBAAkB,QAAQ,EAAE,cAAc,CAAC;AAChE,QAAM,SAAS,SAAS,QAAQ,EAAE,OAAO,OAAO,SAAS,CAAC;AAC1D,MAAI,CAAC,OAAO,SAAS,CAAC,OAAO,OAAQ,QAAO;AAC5C,SAAO;AAAA,IACL,cAAc,OAAO;AAAA,IACrB,cAAc,OAAO;AAAA,IACrB,qBAAqB,OAAO;AAAA,EAC9B;AACF;AAEO,SAAS,sBACd,UACA,OACA,YACS;AACT,MAAI,aAAa,aAAc,QAAO;AACtC,MAAI,YAAY,aAAa,aAAc,QAAO;AAClD,SAAO,MAAM,QAAQ,SAAS,GAAG,KAAK,WAAW,SAAS,GAAG;AAC/D;","names":[]}
@@ -1,83 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/cost/CostCalculator.ts
2
- var CostCalculator = class {
3
- constructor(catalog, options = {}) {
4
- this.catalog = catalog;
5
- this.aliasRegistry = options.aliasRegistry;
6
- this.includeBreakdown = _nullishCoalesce(options.includeBreakdown, () => ( true));
7
- this.resolverOptions = options.resolverOptions;
8
- }
9
-
10
-
11
-
12
-
13
- async calculate(input) {
14
- const resolved = await this.catalog.resolveModel(
15
- { model: input.modelUsed, provider: input.provider },
16
- {
17
- ...this.resolverOptions,
18
- aliasRegistry: _nullishCoalesce(this.aliasRegistry, () => ( _optionalChain([this, 'access', _ => _.resolverOptions, 'optionalAccess', _2 => _2.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 = (_nullishCoalesce(tokens.cacheWrite, () => ( 0))) * (_nullishCoalesce(pricing.cacheWriteUsdPerToken, () => ( 0))) + (_nullishCoalesce(tokens.cached, () => ( 0))) * (_nullishCoalesce(pricing.cacheReadUsdPerToken, () => ( 0)));
48
- const reasoningCost = (_nullishCoalesce(tokens.reasoning, () => ( 0))) * (_nullishCoalesce(pricing.reasoningUsdPerToken, () => ( pricing.promptUsdPerToken)));
49
- const audioCost = (_nullishCoalesce(tokens.audio, () => ( 0))) * pricing.promptUsdPerToken;
50
- const imageCost = (_nullishCoalesce(tokens.image, () => ( 0))) * (_nullishCoalesce(pricing.imageUsdPerUnit, () => ( 0)));
51
- const requestFlat = pricing.requestUsdPerRequest;
52
- if (routedViaOpenRouter) {
53
- promptCost += tokens.prompt * (_nullishCoalesce(pricing.openRouterMarkupUsdPerInputToken, () => ( 0)));
54
- completionCost += tokens.completion * (_nullishCoalesce(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
-
81
-
82
- exports.CostCalculator = CostCalculator;
83
- //# sourceMappingURL=chunk-HN6UAQAE.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-HN6UAQAE.cjs","../src/cost/CostCalculator.ts"],"names":[],"mappings":"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","file":"/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-HN6UAQAE.cjs","sourcesContent":[null,"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"]}
@@ -1,172 +0,0 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } async function _asyncNullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return await rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
2
-
3
-
4
-
5
- var _chunkDKHGWHXPcjs = require('./chunk-DKHGWHXP.cjs');
6
-
7
-
8
-
9
- var _chunkFGP3QXWLcjs = require('./chunk-FGP3QXWL.cjs');
10
-
11
-
12
- var _chunkF2F4UEFDcjs = require('./chunk-F2F4UEFD.cjs');
13
-
14
-
15
-
16
-
17
-
18
- var _chunkTF4L2NECcjs = require('./chunk-TF4L2NEC.cjs');
19
-
20
- // src/catalog/verifyAiModelsCatalog.ts
21
- function sample(ids, limit) {
22
- return ids.length <= limit ? ids : ids.slice(0, limit);
23
- }
24
- async function verifyAiModelsCatalog(options) {
25
- const start = Date.now();
26
- const appId = _nullishCoalesce(options.appId, () => ( _chunkTF4L2NECcjs.AI_TOOLS_APP_ID));
27
- const catalogId = _nullishCoalesce(options.catalogId, () => ( _chunkTF4L2NECcjs.AI_MODELS_CATALOG_ID));
28
- const sampleLimit = _nullishCoalesce(options.sampleLimit, () => ( 20));
29
- if (options.ensureDescriptor !== false) {
30
- await _chunkDKHGWHXPcjs.ensureAiModelsCatalog.call(void 0, options.catalox, { appId, catalogId });
31
- }
32
- const orIds = await _asyncNullishCoalesce(options.expectedModelIds, async () => ( new Set(
33
- (await _asyncNullishCoalesce(options.openRouterModels, async () => ( await new (0, _chunkDKHGWHXPcjs.OpenRouterSyncProvider)({
34
- apiKey: options.openRouterApiKey,
35
- query: _nullishCoalesce(options.openRouterQuery, () => ( { output_modalities: "all" }))
36
- }).fetchModels()))).map((m) => m.modelId)
37
- )));
38
- _chunkTF4L2NECcjs.invalidateModelsCache.call(void 0, appId);
39
- const client = new (0, _chunkF2F4UEFDcjs.AiModelsCatalogClient)({ catalox: options.catalox, appId, catalogId });
40
- const catMap = await client.getAllModels();
41
- const catIds = new Set(catMap.keys());
42
- const missingInCatalox = [...orIds].filter((id) => !catIds.has(id));
43
- const extraInCatalox = [...catIds].filter((id) => !orIds.has(id));
44
- let supportsReasoningMissing = 0;
45
- let openRouterMirrorMissing = 0;
46
- for (const m of catMap.values()) {
47
- if (m.supportsReasoning === void 0) supportsReasoningMissing++;
48
- if (!_optionalChain([m, 'access', _ => _.openRouter, 'optionalAccess', _2 => _2.id])) openRouterMirrorMissing++;
49
- }
50
- const descriptor = await options.catalox.getCatalogDescriptor(
51
- { appId, superAdmin: true },
52
- catalogId
53
- );
54
- const remoteKeys = _nullishCoalesce(_optionalChain([descriptor, 'optionalAccess', _3 => _3.queryableFields, 'optionalAccess', _4 => _4.map, 'call', _5 => _5((f) => f.key)]), () => ( []));
55
- const localKeys = _chunkTF4L2NECcjs.AI_MODELS_DESCRIPTOR.queryableFields.map((f) => f.key);
56
- const descriptorKeysMatch = remoteKeys.length === localKeys.length && remoteKeys.every((k, i) => k === localKeys[i]);
57
- const ok = missingInCatalox.length === 0 && extraInCatalox.length === 0 && supportsReasoningMissing === 0 && openRouterMirrorMissing === 0 && descriptorKeysMatch;
58
- return {
59
- ok,
60
- openRouterCount: orIds.size,
61
- cataloxCount: catIds.size,
62
- missingInCatalox: sample(missingInCatalox, sampleLimit),
63
- extraInCatalox: sample(extraInCatalox, sampleLimit),
64
- supportsReasoningMissing,
65
- openRouterMirrorMissing,
66
- descriptorKeysMatch,
67
- durationMs: Date.now() - start
68
- };
69
- }
70
-
71
- // src/catalog/pruneStaleCatalogModels.ts
72
-
73
-
74
-
75
-
76
- var _firebase = require('@x12i/catalox/firebase');
77
- var DELETE_BATCH = 400;
78
- async function pruneStaleCatalogModels(options) {
79
- const { firestore, catalogId, activeModelIds, dryRun } = options;
80
- const layout = await _firebase.resolveNativeItemsLayout.call(void 0, firestore, catalogId);
81
- const col = layout === "legacy" ? _firebase.legacyNativeItemsCollectionRef.call(void 0, firestore, catalogId) : _firebase.flatNativeItemsCollectionRef.call(void 0, firestore, catalogId);
82
- const snapshot = await col.get();
83
- const toDelete = [];
84
- for (const doc of snapshot.docs) {
85
- const modelId = _chunkFGP3QXWLcjs.decodeModelFirestoreDocId.call(void 0, doc.id);
86
- if (!activeModelIds.has(modelId)) {
87
- toDelete.push(modelId);
88
- }
89
- }
90
- if (!dryRun && toDelete.length > 0) {
91
- for (let i = 0; i < toDelete.length; i += DELETE_BATCH) {
92
- const chunk = toDelete.slice(i, i + DELETE_BATCH);
93
- const batch = firestore.batch();
94
- for (const modelId of chunk) {
95
- batch.delete(col.doc(_chunkFGP3QXWLcjs.encodeModelFirestoreDocId.call(void 0, modelId)));
96
- }
97
- await batch.commit();
98
- }
99
- }
100
- return {
101
- scanned: snapshot.size,
102
- pruned: toDelete.length,
103
- prunedModelIds: toDelete
104
- };
105
- }
106
-
107
- // src/catalog/runAiModelsCatalogSync.ts
108
- var CatalogSyncJobError = class extends Error {
109
- constructor(message, sync, verify) {
110
- super(message);
111
- this.sync = sync;
112
- this.verify = verify;
113
- this.name = "CatalogSyncJobError";
114
- }
115
-
116
-
117
- };
118
- async function runAiModelsCatalogSync(options) {
119
- const verifyAfter = options.verifyAfter !== false;
120
- const failOnVerifyError = options.failOnVerifyError !== false;
121
- const appId = _nullishCoalesce(options.appId, () => ( _chunkTF4L2NECcjs.AI_TOOLS_APP_ID));
122
- const catalogId = _nullishCoalesce(options.catalogId, () => ( _chunkTF4L2NECcjs.AI_MODELS_CATALOG_ID));
123
- const ctx = { appId, superAdmin: true };
124
- const sync = await _chunkDKHGWHXPcjs.syncAiModelsCatalog.call(void 0, options);
125
- let prune;
126
- if (options.pruneStale && !options.dryRun) {
127
- prune = await pruneStaleCatalogModels({
128
- firestore: options.firestore,
129
- catalogId,
130
- context: ctx,
131
- activeModelIds: new Set(sync.syncedModelIds),
132
- dryRun: options.dryRun
133
- });
134
- _chunkTF4L2NECcjs.invalidateModelsCache.call(void 0, appId);
135
- }
136
- let verify;
137
- if (verifyAfter) {
138
- verify = await verifyAiModelsCatalog({
139
- catalox: options.catalox,
140
- appId,
141
- catalogId,
142
- expectedModelIds: new Set(sync.syncedModelIds),
143
- ensureDescriptor: false
144
- });
145
- }
146
- const syncFailed = sync.errors.length > 0 || sync.upserted < sync.fetched;
147
- const verifyFailed = verify !== void 0 && !verify.ok;
148
- const ok = !syncFailed && !verifyFailed;
149
- if (!ok && failOnVerifyError) {
150
- const parts = [];
151
- if (syncFailed) {
152
- parts.push(
153
- `sync incomplete: upserted ${sync.upserted}/${sync.fetched}, ${sync.errors.length} errors`
154
- );
155
- }
156
- if (verifyFailed && verify) {
157
- parts.push(
158
- `verify failed: catalox ${verify.cataloxCount} vs openrouter ${verify.openRouterCount}, missing ${verify.missingInCatalox.length}, extra ${verify.extraInCatalox.length}`
159
- );
160
- }
161
- throw new CatalogSyncJobError(parts.join("; "), sync, verify);
162
- }
163
- return { sync, verify, prune, ok };
164
- }
165
-
166
-
167
-
168
-
169
-
170
-
171
- exports.verifyAiModelsCatalog = verifyAiModelsCatalog; exports.pruneStaleCatalogModels = pruneStaleCatalogModels; exports.CatalogSyncJobError = CatalogSyncJobError; exports.runAiModelsCatalogSync = runAiModelsCatalogSync;
172
- //# sourceMappingURL=chunk-HS74X2OJ.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-HS74X2OJ.cjs","../src/catalog/verifyAiModelsCatalog.ts","../src/catalog/pruneStaleCatalogModels.ts","../src/catalog/runAiModelsCatalogSync.ts"],"names":[],"mappings":"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","file":"/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-HS74X2OJ.cjs","sourcesContent":[null,"import type { Catalox } from \"@x12i/catalox\";\nimport { invalidateModelsCache } from \"../cache/modelCache.js\";\nimport { AiModelsCatalogClient } from \"../catalox/AiModelsCatalogClient.js\";\nimport type { AiModelRecord } from \"../models/types.js\";\nimport { OpenRouterSyncProvider } from \"../sync/OpenRouterSyncProvider.js\";\nimport type { OpenRouterModelsQuery } from \"../models/openrouter.types.js\";\nimport {\n AI_MODELS_CATALOG_ID,\n AI_MODELS_DESCRIPTOR,\n AI_TOOLS_APP_ID,\n} from \"./aiModelsCatalogDescriptor.js\";\nimport { ensureAiModelsCatalog } from \"./ensureAiModelsCatalog.js\";\n\nexport type CatalogVerifyOptions = {\n catalox: Catalox;\n appId?: string;\n catalogId?: string;\n /** When set, compare against this id set (e.g. from a sync that just ran). */\n expectedModelIds?: Set<string>;\n /** When set, compare against this list instead of fetching OpenRouter. */\n openRouterModels?: AiModelRecord[];\n openRouterApiKey?: string;\n openRouterQuery?: OpenRouterModelsQuery;\n /** Ensure descriptor is registered before reading (default true). */\n ensureDescriptor?: boolean;\n /** Max model ids to include in report samples (default 20). */\n sampleLimit?: number;\n};\n\nexport type CatalogVerifyReport = {\n ok: boolean;\n openRouterCount: number;\n cataloxCount: number;\n missingInCatalox: string[];\n extraInCatalox: string[];\n supportsReasoningMissing: number;\n openRouterMirrorMissing: number;\n descriptorKeysMatch: boolean;\n durationMs: number;\n};\n\nfunction sample(ids: string[], limit: number): string[] {\n return ids.length <= limit ? ids : ids.slice(0, limit);\n}\n\n/**\n * Compare the live OpenRouter catalog with Catalox/Firestore ai-models items.\n * Use after sync in CI/cron, or standalone health checks.\n */\nexport async function verifyAiModelsCatalog(\n options: CatalogVerifyOptions,\n): Promise<CatalogVerifyReport> {\n const start = Date.now();\n const appId = options.appId ?? AI_TOOLS_APP_ID;\n const catalogId = options.catalogId ?? AI_MODELS_CATALOG_ID;\n const sampleLimit = options.sampleLimit ?? 20;\n\n if (options.ensureDescriptor !== false) {\n await ensureAiModelsCatalog(options.catalox, { appId, catalogId });\n }\n\n const orIds =\n options.expectedModelIds ??\n new Set(\n (\n options.openRouterModels ??\n (await new OpenRouterSyncProvider({\n apiKey: options.openRouterApiKey,\n query: options.openRouterQuery ?? { output_modalities: \"all\" },\n }).fetchModels())\n ).map((m) => m.modelId),\n );\n\n invalidateModelsCache(appId);\n const client = new AiModelsCatalogClient({ catalox: options.catalox, appId, catalogId });\n const catMap = await client.getAllModels();\n const catIds = new Set(catMap.keys());\n\n const missingInCatalox = [...orIds].filter((id) => !catIds.has(id));\n const extraInCatalox = [...catIds].filter((id) => !orIds.has(id));\n\n let supportsReasoningMissing = 0;\n let openRouterMirrorMissing = 0;\n for (const m of catMap.values()) {\n if (m.supportsReasoning === undefined) supportsReasoningMissing++;\n if (!m.openRouter?.id) openRouterMirrorMissing++;\n }\n\n const descriptor = await options.catalox.getCatalogDescriptor(\n { appId, superAdmin: true },\n catalogId,\n );\n const remoteKeys = descriptor?.queryableFields?.map((f) => f.key) ?? [];\n const localKeys = AI_MODELS_DESCRIPTOR.queryableFields.map((f) => f.key);\n const descriptorKeysMatch =\n remoteKeys.length === localKeys.length &&\n remoteKeys.every((k, i) => k === localKeys[i]);\n\n const ok =\n missingInCatalox.length === 0 &&\n extraInCatalox.length === 0 &&\n supportsReasoningMissing === 0 &&\n openRouterMirrorMissing === 0 &&\n descriptorKeysMatch;\n\n return {\n ok,\n openRouterCount: orIds.size,\n cataloxCount: catIds.size,\n missingInCatalox: sample(missingInCatalox, sampleLimit),\n extraInCatalox: sample(extraInCatalox, sampleLimit),\n supportsReasoningMissing,\n openRouterMirrorMissing,\n descriptorKeysMatch,\n durationMs: Date.now() - start,\n };\n}\n","import type { Firestore } from \"firebase-admin/firestore\";\nimport type { CataloxContext } from \"@x12i/catalox\";\nimport {\n flatNativeItemsCollectionRef,\n legacyNativeItemsCollectionRef,\n resolveNativeItemsLayout,\n} from \"@x12i/catalox/firebase\";\nimport { decodeModelFirestoreDocId, encodeModelFirestoreDocId } from \"../catalox/modelDocId.js\";\n\nexport type PruneStaleCatalogModelsOptions = {\n firestore: Firestore;\n catalogId: string;\n context: CataloxContext;\n /** Canonical model ids from the latest OpenRouter fetch. */\n activeModelIds: Set<string>;\n dryRun?: boolean;\n};\n\nexport type PruneStaleCatalogModelsResult = {\n scanned: number;\n pruned: number;\n prunedModelIds: string[];\n};\n\nconst DELETE_BATCH = 400;\n\n/**\n * Remove catalog documents that are no longer listed on OpenRouter.\n * Off by default — enable explicitly when running production sync jobs.\n */\nexport async function pruneStaleCatalogModels(\n options: PruneStaleCatalogModelsOptions,\n): Promise<PruneStaleCatalogModelsResult> {\n const { firestore, catalogId, activeModelIds, dryRun } = options;\n const layout = await resolveNativeItemsLayout(firestore, catalogId);\n const col =\n layout === \"legacy\"\n ? legacyNativeItemsCollectionRef(firestore, catalogId)\n : flatNativeItemsCollectionRef(firestore, catalogId);\n\n const snapshot = await col.get();\n const toDelete: string[] = [];\n\n for (const doc of snapshot.docs) {\n const modelId = decodeModelFirestoreDocId(doc.id);\n if (!activeModelIds.has(modelId)) {\n toDelete.push(modelId);\n }\n }\n\n if (!dryRun && toDelete.length > 0) {\n for (let i = 0; i < toDelete.length; i += DELETE_BATCH) {\n const chunk = toDelete.slice(i, i + DELETE_BATCH);\n const batch = firestore.batch();\n for (const modelId of chunk) {\n batch.delete(col.doc(encodeModelFirestoreDocId(modelId)));\n }\n await batch.commit();\n }\n }\n\n return {\n scanned: snapshot.size,\n pruned: toDelete.length,\n prunedModelIds: toDelete,\n };\n}\n","import { invalidateModelsCache } from \"../cache/modelCache.js\";\nimport { syncAiModelsCatalog } from \"../sync/syncAiModelsCatalog.js\";\nimport type { SyncOptions, SyncResult } from \"../sync/syncAiModelsCatalog.js\";\nimport { pruneStaleCatalogModels } from \"./pruneStaleCatalogModels.js\";\nimport type { CatalogVerifyReport } from \"./verifyAiModelsCatalog.js\";\nimport { verifyAiModelsCatalog } from \"./verifyAiModelsCatalog.js\";\nimport { AI_MODELS_CATALOG_ID, AI_TOOLS_APP_ID } from \"./aiModelsCatalogDescriptor.js\";\n\nexport type CatalogSyncJobOptions = SyncOptions & {\n /**\n * After upsert, compare Catalox to OpenRouter (default true).\n * Set false only when you will verify separately.\n */\n verifyAfter?: boolean;\n /** Fail the job when verification does not pass (default true). */\n failOnVerifyError?: boolean;\n /** Delete Firestore rows not present in the latest OpenRouter list (default false). */\n pruneStale?: boolean;\n};\n\nexport type CatalogSyncJobResult = {\n sync: SyncResult;\n verify?: CatalogVerifyReport;\n prune?: {\n scanned: number;\n pruned: number;\n prunedModelIds: string[];\n };\n ok: boolean;\n};\n\nexport class CatalogSyncJobError extends Error {\n constructor(\n message: string,\n public readonly sync: SyncResult,\n public readonly verify?: CatalogVerifyReport,\n ) {\n super(message);\n this.name = \"CatalogSyncJobError\";\n }\n}\n\n/**\n * Production entry point: sync OpenRouter → Catalox, optionally prune stale rows, then verify.\n * Suitable for cron, Cloud Run jobs, and `ai-tools sync`.\n */\nexport async function runAiModelsCatalogSync(\n options: CatalogSyncJobOptions,\n): Promise<CatalogSyncJobResult> {\n const verifyAfter = options.verifyAfter !== false;\n const failOnVerifyError = options.failOnVerifyError !== false;\n const appId = options.appId ?? AI_TOOLS_APP_ID;\n const catalogId = options.catalogId ?? AI_MODELS_CATALOG_ID;\n const ctx = { appId, superAdmin: true as const };\n\n const sync = await syncAiModelsCatalog(options);\n\n let prune: CatalogSyncJobResult[\"prune\"];\n\n if (options.pruneStale && !options.dryRun) {\n prune = await pruneStaleCatalogModels({\n firestore: options.firestore,\n catalogId,\n context: ctx,\n activeModelIds: new Set(sync.syncedModelIds),\n dryRun: options.dryRun,\n });\n invalidateModelsCache(appId);\n }\n\n let verify: CatalogVerifyReport | undefined;\n if (verifyAfter) {\n verify = await verifyAiModelsCatalog({\n catalox: options.catalox,\n appId,\n catalogId,\n expectedModelIds: new Set(sync.syncedModelIds),\n ensureDescriptor: false,\n });\n }\n\n const syncFailed = sync.errors.length > 0 || sync.upserted < sync.fetched;\n const verifyFailed = verify !== undefined && !verify.ok;\n const ok = !syncFailed && !verifyFailed;\n\n if (!ok && failOnVerifyError) {\n const parts: string[] = [];\n if (syncFailed) {\n parts.push(\n `sync incomplete: upserted ${sync.upserted}/${sync.fetched}, ${sync.errors.length} errors`,\n );\n }\n if (verifyFailed && verify) {\n parts.push(\n `verify failed: catalox ${verify.cataloxCount} vs openrouter ${verify.openRouterCount}, ` +\n `missing ${verify.missingInCatalox.length}, extra ${verify.extraInCatalox.length}`,\n );\n }\n throw new CatalogSyncJobError(parts.join(\"; \"), sync, verify);\n }\n\n return { sync, verify, prune, ok };\n}\n"]}