@x12i/ai-tools 1.0.2 → 1.0.3

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 (66) hide show
  1. package/README.md +114 -12
  2. package/dist/{AiModelsCatalogClient-CSVlKql5.d.cts → AiModelsCatalogClient-CNeqFiFs.d.cts} +2 -1
  3. package/dist/{AiModelsCatalogClient-B-dNLXX0.d.ts → AiModelsCatalogClient-nwFoEaqL.d.ts} +2 -1
  4. package/dist/aliases/index.d.cts +4 -3
  5. package/dist/aliases/index.d.ts +4 -3
  6. package/dist/catalog/index.cjs +30 -0
  7. package/dist/catalog/index.cjs.map +1 -0
  8. package/dist/catalog/index.d.cts +100 -0
  9. package/dist/catalog/index.d.ts +100 -0
  10. package/dist/catalog/index.js +30 -0
  11. package/dist/catalog/index.js.map +1 -0
  12. package/dist/catalox/index.cjs +2 -2
  13. package/dist/catalox/index.d.cts +7 -19
  14. package/dist/catalox/index.d.ts +7 -19
  15. package/dist/catalox/index.js +1 -1
  16. package/dist/chunk-C3H7RTFR.cjs +1 -0
  17. package/dist/chunk-C3H7RTFR.cjs.map +1 -0
  18. package/dist/{chunk-ONA73BU6.cjs → chunk-DKHGWHXP.cjs} +21 -12
  19. package/dist/chunk-DKHGWHXP.cjs.map +1 -0
  20. package/dist/{chunk-HHNHWYTP.cjs → chunk-FGP3QXWL.cjs} +94 -36
  21. package/dist/chunk-FGP3QXWL.cjs.map +1 -0
  22. package/dist/chunk-HS74X2OJ.cjs +172 -0
  23. package/dist/chunk-HS74X2OJ.cjs.map +1 -0
  24. package/dist/chunk-HYGXZY25.js +163 -0
  25. package/dist/chunk-HYGXZY25.js.map +1 -0
  26. package/dist/chunk-M5TMA73F.js +1 -0
  27. package/dist/chunk-M5TMA73F.js.map +1 -0
  28. package/dist/chunk-MX3AMQFC.js +172 -0
  29. package/dist/chunk-MX3AMQFC.js.map +1 -0
  30. package/dist/{chunk-MLRHYOCD.js → chunk-VRFVF5RH.js} +21 -12
  31. package/dist/chunk-VRFVF5RH.js.map +1 -0
  32. package/dist/cli/index.cjs +133 -30
  33. package/dist/cli/index.cjs.map +1 -1
  34. package/dist/cli/index.js +134 -31
  35. package/dist/cli/index.js.map +1 -1
  36. package/dist/cost/index.d.cts +4 -3
  37. package/dist/cost/index.d.ts +4 -3
  38. package/dist/index.cjs +17 -6
  39. package/dist/index.cjs.map +1 -1
  40. package/dist/index.d.cts +10 -16
  41. package/dist/index.d.ts +10 -16
  42. package/dist/index.js +22 -11
  43. package/dist/{modelNameResolver-Bn8QnkSj.d.ts → modelNameResolver-D9V_GfUK.d.cts} +3 -27
  44. package/dist/{modelNameResolver-bZD-eBSJ.d.cts → modelNameResolver-DqFt7g6W.d.ts} +3 -27
  45. package/dist/models/index.d.cts +3 -2
  46. package/dist/models/index.d.ts +3 -2
  47. package/dist/sync/index.cjs +3 -3
  48. package/dist/sync/index.d.cts +6 -3
  49. package/dist/sync/index.d.ts +6 -3
  50. package/dist/sync/index.js +2 -2
  51. package/dist/syncAiModelsCatalog-CnXRLm2c.d.cts +32 -0
  52. package/dist/syncAiModelsCatalog-DpkN_w7S.d.ts +32 -0
  53. package/dist/types-BYXnCvKx.d.cts +137 -0
  54. package/dist/types-BYXnCvKx.d.ts +137 -0
  55. package/dist/types-CX6QFNNy.d.cts +144 -0
  56. package/dist/types-CuiPDcVs.d.ts +144 -0
  57. package/dist/upsertAiModelRecord-C831wOIF.d.ts +35 -0
  58. package/dist/upsertAiModelRecord-CjY-sny0.d.cts +35 -0
  59. package/package.json +8 -1
  60. package/dist/chunk-HHNHWYTP.cjs.map +0 -1
  61. package/dist/chunk-ML2FRR4L.js +0 -105
  62. package/dist/chunk-ML2FRR4L.js.map +0 -1
  63. package/dist/chunk-MLRHYOCD.js.map +0 -1
  64. package/dist/chunk-ONA73BU6.cjs.map +0 -1
  65. package/dist/types-DdGB3YaA.d.cts +0 -278
  66. package/dist/types-DdGB3YaA.d.ts +0 -278
@@ -0,0 +1,137 @@
1
+ /** OpenRouter Models API — https://openrouter.ai/api/v1/models */
2
+ type OpenRouterOutputModality = "text" | "image" | "audio" | "embeddings" | "video" | "speech" | "transcription" | "all";
3
+ type OpenRouterModelsQuery = {
4
+ /** Comma-separated or "all" — default on our sync is "all". */
5
+ output_modalities?: string;
6
+ supported_parameters?: string;
7
+ };
8
+ type OpenRouterPricingApi = {
9
+ prompt: string;
10
+ completion: string;
11
+ request: string;
12
+ image: string;
13
+ web_search?: string;
14
+ internal_reasoning?: string;
15
+ input_cache_read?: string;
16
+ input_cache_write?: string;
17
+ };
18
+ type OpenRouterArchitectureApi = {
19
+ modality?: string;
20
+ input_modalities: string[];
21
+ output_modalities: string[];
22
+ tokenizer: string;
23
+ instruct_type: string | null;
24
+ };
25
+ type OpenRouterTopProviderApi = {
26
+ context_length: number;
27
+ max_completion_tokens: number | null;
28
+ is_moderated: boolean;
29
+ };
30
+ type OpenRouterDefaultParametersApi = {
31
+ temperature?: number | null;
32
+ top_p?: number | null;
33
+ top_k?: number | null;
34
+ frequency_penalty?: number | null;
35
+ presence_penalty?: number | null;
36
+ repetition_penalty?: number | null;
37
+ };
38
+ type OpenRouterModelApi = {
39
+ id: string;
40
+ canonical_slug: string;
41
+ hugging_face_id?: string | null;
42
+ name: string;
43
+ created: number;
44
+ description: string;
45
+ context_length: number;
46
+ architecture: OpenRouterArchitectureApi;
47
+ pricing: OpenRouterPricingApi;
48
+ top_provider: OpenRouterTopProviderApi;
49
+ per_request_limits: unknown | null;
50
+ supported_parameters: string[];
51
+ default_parameters: OpenRouterDefaultParametersApi | null;
52
+ supported_voices?: unknown | null;
53
+ knowledge_cutoff?: string | null;
54
+ expiration_date?: string | null;
55
+ links?: {
56
+ details?: string;
57
+ } | null;
58
+ };
59
+ type OpenRouterModelsResponse = {
60
+ data: OpenRouterModelApi[];
61
+ };
62
+
63
+ type AiModelPricing = {
64
+ promptUsdPerToken: number;
65
+ completionUsdPerToken: number;
66
+ imageUsdPerUnit: number;
67
+ requestUsdPerRequest: number;
68
+ cacheReadUsdPerToken?: number;
69
+ cacheWriteUsdPerToken?: number;
70
+ reasoningUsdPerToken?: number;
71
+ webSearchUsdPerRequest?: number;
72
+ openRouterMarkupUsdPerInputToken?: number;
73
+ openRouterMarkupUsdPerOutputToken?: number;
74
+ pricedAt: string;
75
+ source: "openrouter" | "direct" | "manual";
76
+ };
77
+ /**
78
+ * Canonical catalog record — mirrors OpenRouter Models API fields plus normalized pricing.
79
+ * Stored as-is in Catalox `data`; scalar fields duplicated in `indexed` for queries.
80
+ */
81
+ type AiModelRecord = {
82
+ modelId: string;
83
+ name: string;
84
+ providerId: string;
85
+ canonicalSlug: string;
86
+ status: "active" | "deprecated" | "unknown";
87
+ description: string;
88
+ created: number;
89
+ expirationDate: string | null;
90
+ contextLength: number;
91
+ maxCompletionTokens: number | null;
92
+ isModerated: boolean;
93
+ modality: string;
94
+ inputModalities: string[];
95
+ outputModalities: string[];
96
+ tokenizer: string;
97
+ instructType: string | null;
98
+ supportedParameters: string[];
99
+ defaultParameters: OpenRouterDefaultParametersApi | null;
100
+ perRequestLimits: unknown | null;
101
+ pricing: AiModelPricing;
102
+ /** Raw OpenRouter pricing strings (USD per token/request/unit). */
103
+ openRouterPricing: OpenRouterPricingApi;
104
+ architecture: OpenRouterArchitectureApi;
105
+ topProvider: OpenRouterTopProviderApi;
106
+ /** Full OpenRouter API object — complete mirror for forward compatibility. */
107
+ openRouter: OpenRouterModelApi;
108
+ aliases: string[];
109
+ availableOnOpenRouter: boolean;
110
+ supportsStreaming: boolean;
111
+ supportsTools: boolean;
112
+ /** Exposes reasoning/thinking tokens (OpenRouter `reasoning` param and/or `internal_reasoning` pricing). */
113
+ supportsReasoning: boolean;
114
+ primaryOutputModality: string;
115
+ syncedAt: string;
116
+ syncSource: "openrouter" | "manual";
117
+ };
118
+ type ModelListFilters = {
119
+ providerId?: string;
120
+ status?: AiModelRecord["status"];
121
+ outputModality?: string;
122
+ inputModality?: string;
123
+ supportedParameter?: string;
124
+ supportsTools?: boolean;
125
+ supportsReasoning?: boolean;
126
+ search?: string;
127
+ limit?: number;
128
+ offset?: number;
129
+ };
130
+ type ModelListResult = {
131
+ models: AiModelRecord[];
132
+ total: number;
133
+ limit: number;
134
+ offset: number;
135
+ };
136
+
137
+ export type { AiModelPricing as A, ModelListFilters as M, OpenRouterArchitectureApi as O, AiModelRecord as a, ModelListResult as b, OpenRouterModelApi as c, OpenRouterModelsQuery as d, OpenRouterModelsResponse as e, OpenRouterOutputModality as f, OpenRouterPricingApi as g, OpenRouterTopProviderApi as h };
@@ -0,0 +1,144 @@
1
+ import { a as AiModelRecord } from './types-BYXnCvKx.cjs';
2
+
3
+ type AliasEntry = {
4
+ modelId: string;
5
+ provider: string;
6
+ description?: string;
7
+ tags?: string[];
8
+ addedAt: string;
9
+ updatedAt: string;
10
+ };
11
+ type AliasFileSchema = {
12
+ $schema?: string;
13
+ version: 1;
14
+ updatedAt: string;
15
+ aliases: Record<string, AliasEntry>;
16
+ };
17
+ type ResolvedModelRef = {
18
+ alias: string;
19
+ entry: AliasEntry;
20
+ modelRecord: AiModelRecord | null;
21
+ modelId: string;
22
+ provider: string;
23
+ name: string;
24
+ };
25
+ type AliasValidationReport = {
26
+ total: number;
27
+ ok: number;
28
+ unknown: number;
29
+ broken: number;
30
+ entries: Array<{
31
+ name: string;
32
+ status: "ok" | "unknown" | "broken";
33
+ modelId: string;
34
+ resolvedName?: string;
35
+ issue?: string;
36
+ }>;
37
+ };
38
+
39
+ type AliasRegistryOptions = {
40
+ aliasesPath?: string;
41
+ };
42
+ declare class AliasRegistry {
43
+ private readonly aliasesPath;
44
+ constructor(options?: AliasRegistryOptions);
45
+ get path(): string;
46
+ exists(): boolean;
47
+ init(): void;
48
+ load(): AliasFileSchema;
49
+ get(aliasName: string): AliasEntry | null;
50
+ list(filter?: {
51
+ tag?: string;
52
+ }): Array<{
53
+ name: string;
54
+ } & AliasEntry>;
55
+ set(aliasName: string, entry: Omit<AliasEntry, "addedAt" | "updatedAt">): AliasEntry;
56
+ remove(aliasName: string): boolean;
57
+ rename(from: string, to: string, options?: {
58
+ force?: boolean;
59
+ }): void;
60
+ private writeFile;
61
+ }
62
+
63
+ /**
64
+ * Env naming for vendor direct API keys: `{VENDOR}_API_KEY`
65
+ * where VENDOR is the provider id in UPPER_SNAKE (hyphens → underscores).
66
+ *
67
+ * Examples: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `META_LLAMA_API_KEY`, `X_AI_API_KEY`
68
+ */
69
+ declare function providerIdToEnvKeyPrefix(providerId: string): string;
70
+ declare function vendorApiKeyEnvName(providerId: string): string;
71
+ type OpenRouterRoutingConfig = {
72
+ /** OPENROUTER_API_KEY is set and non-empty */
73
+ hasOpenRouterKey: boolean;
74
+ /** USE_OPENROUTER=true or USE_OPENROUTER=1 */
75
+ useOpenRouterExplicit: boolean;
76
+ /** Read `{PROVIDER}_API_KEY` for a catalog provider id */
77
+ getVendorApiKey(providerId: string): string | undefined;
78
+ };
79
+ /**
80
+ * Load routing hints from process env and optional `.env` (via @x12/env).
81
+ */
82
+ declare function loadOpenRouterRoutingEnv(env?: Record<string, string | undefined>): OpenRouterRoutingConfig;
83
+ /**
84
+ * Default route via OpenRouter when:
85
+ * 1. OPENROUTER_API_KEY is set AND USE_OPENROUTER=true|1, or
86
+ * 2. OPENROUTER_API_KEY is set AND the vendor's `{VENDOR}_API_KEY` is missing.
87
+ */
88
+ declare function shouldDefaultRouteViaOpenRouter(providerId: string | undefined, config: OpenRouterRoutingConfig): boolean;
89
+
90
+ type ModelResolutionInput = {
91
+ /** Provider hint (openrouter, openai, anthropic, …). May be omitted. */
92
+ provider?: string;
93
+ /** Model id, alias, shorthand, or partial name. Required. */
94
+ model: string;
95
+ };
96
+ type ResolutionStrategy = "alias-registry" | "alias-as-provider-correction" | "exact-match" | "catalog-alias-match" | "canonical-slug-match" | "provider-prefix-injection" | "cross-provider-correction" | "version-suffix-strip" | "date-suffix-strip" | "shorthand-expansion" | "partial-name-match" | "local-provider-passthrough";
97
+ type ModelResolutionSuccess = {
98
+ found: true;
99
+ modelId: string;
100
+ record: AiModelRecord | null;
101
+ routedViaOpenRouter: boolean;
102
+ confidence: number;
103
+ resolvedVia: ResolutionStrategy[];
104
+ resolvedReason: string;
105
+ normalisedInput: string;
106
+ };
107
+ type ModelResolutionNotFound = {
108
+ found: false;
109
+ modelId: null;
110
+ record: null;
111
+ attemptedStrategies: ResolutionStrategy[];
112
+ bestRejectedCandidate?: {
113
+ modelId: string;
114
+ confidence: number;
115
+ reason: string;
116
+ };
117
+ reason: string;
118
+ };
119
+ type ModelResolutionResult = ModelResolutionSuccess | ModelResolutionNotFound;
120
+ type ModelResolverOptions = {
121
+ confidenceThreshold?: number;
122
+ aliasRegistry?: AliasRegistry;
123
+ additionalShorthands?: Record<string, string>;
124
+ additionalProviderPatterns?: Array<{
125
+ pattern: RegExp;
126
+ provider: string;
127
+ }>;
128
+ additionalLocalProviders?: string[];
129
+ /** Env-based OpenRouter vs direct routing. Defaults to loadOpenRouterRoutingEnv(). */
130
+ routingEnv?: OpenRouterRoutingConfig;
131
+ };
132
+ type CatalogIndexes = {
133
+ aliasIndex: Map<string, string>;
134
+ slugIndex: Map<string, string>;
135
+ providerPrefixesBySize: string[];
136
+ };
137
+ /** @deprecated Use ModelResolutionSuccess via ModelNameResolver */
138
+ type ResolvedModel = {
139
+ catalogModel: AiModelRecord;
140
+ matchedAlias: string;
141
+ routedViaOpenRouter: boolean;
142
+ };
143
+
144
+ export { type AliasEntry as A, type CatalogIndexes as C, type ModelResolutionInput as M, type OpenRouterRoutingConfig as O, type ResolutionStrategy as R, type AliasFileSchema as a, AliasRegistry as b, type AliasRegistryOptions as c, type AliasValidationReport as d, type ModelResolutionNotFound as e, type ModelResolutionResult as f, type ModelResolutionSuccess as g, type ModelResolverOptions as h, type ResolvedModel as i, type ResolvedModelRef as j, loadOpenRouterRoutingEnv as l, providerIdToEnvKeyPrefix as p, shouldDefaultRouteViaOpenRouter as s, vendorApiKeyEnvName as v };
@@ -0,0 +1,144 @@
1
+ import { a as AiModelRecord } from './types-BYXnCvKx.js';
2
+
3
+ type AliasEntry = {
4
+ modelId: string;
5
+ provider: string;
6
+ description?: string;
7
+ tags?: string[];
8
+ addedAt: string;
9
+ updatedAt: string;
10
+ };
11
+ type AliasFileSchema = {
12
+ $schema?: string;
13
+ version: 1;
14
+ updatedAt: string;
15
+ aliases: Record<string, AliasEntry>;
16
+ };
17
+ type ResolvedModelRef = {
18
+ alias: string;
19
+ entry: AliasEntry;
20
+ modelRecord: AiModelRecord | null;
21
+ modelId: string;
22
+ provider: string;
23
+ name: string;
24
+ };
25
+ type AliasValidationReport = {
26
+ total: number;
27
+ ok: number;
28
+ unknown: number;
29
+ broken: number;
30
+ entries: Array<{
31
+ name: string;
32
+ status: "ok" | "unknown" | "broken";
33
+ modelId: string;
34
+ resolvedName?: string;
35
+ issue?: string;
36
+ }>;
37
+ };
38
+
39
+ type AliasRegistryOptions = {
40
+ aliasesPath?: string;
41
+ };
42
+ declare class AliasRegistry {
43
+ private readonly aliasesPath;
44
+ constructor(options?: AliasRegistryOptions);
45
+ get path(): string;
46
+ exists(): boolean;
47
+ init(): void;
48
+ load(): AliasFileSchema;
49
+ get(aliasName: string): AliasEntry | null;
50
+ list(filter?: {
51
+ tag?: string;
52
+ }): Array<{
53
+ name: string;
54
+ } & AliasEntry>;
55
+ set(aliasName: string, entry: Omit<AliasEntry, "addedAt" | "updatedAt">): AliasEntry;
56
+ remove(aliasName: string): boolean;
57
+ rename(from: string, to: string, options?: {
58
+ force?: boolean;
59
+ }): void;
60
+ private writeFile;
61
+ }
62
+
63
+ /**
64
+ * Env naming for vendor direct API keys: `{VENDOR}_API_KEY`
65
+ * where VENDOR is the provider id in UPPER_SNAKE (hyphens → underscores).
66
+ *
67
+ * Examples: `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `META_LLAMA_API_KEY`, `X_AI_API_KEY`
68
+ */
69
+ declare function providerIdToEnvKeyPrefix(providerId: string): string;
70
+ declare function vendorApiKeyEnvName(providerId: string): string;
71
+ type OpenRouterRoutingConfig = {
72
+ /** OPENROUTER_API_KEY is set and non-empty */
73
+ hasOpenRouterKey: boolean;
74
+ /** USE_OPENROUTER=true or USE_OPENROUTER=1 */
75
+ useOpenRouterExplicit: boolean;
76
+ /** Read `{PROVIDER}_API_KEY` for a catalog provider id */
77
+ getVendorApiKey(providerId: string): string | undefined;
78
+ };
79
+ /**
80
+ * Load routing hints from process env and optional `.env` (via @x12/env).
81
+ */
82
+ declare function loadOpenRouterRoutingEnv(env?: Record<string, string | undefined>): OpenRouterRoutingConfig;
83
+ /**
84
+ * Default route via OpenRouter when:
85
+ * 1. OPENROUTER_API_KEY is set AND USE_OPENROUTER=true|1, or
86
+ * 2. OPENROUTER_API_KEY is set AND the vendor's `{VENDOR}_API_KEY` is missing.
87
+ */
88
+ declare function shouldDefaultRouteViaOpenRouter(providerId: string | undefined, config: OpenRouterRoutingConfig): boolean;
89
+
90
+ type ModelResolutionInput = {
91
+ /** Provider hint (openrouter, openai, anthropic, …). May be omitted. */
92
+ provider?: string;
93
+ /** Model id, alias, shorthand, or partial name. Required. */
94
+ model: string;
95
+ };
96
+ type ResolutionStrategy = "alias-registry" | "alias-as-provider-correction" | "exact-match" | "catalog-alias-match" | "canonical-slug-match" | "provider-prefix-injection" | "cross-provider-correction" | "version-suffix-strip" | "date-suffix-strip" | "shorthand-expansion" | "partial-name-match" | "local-provider-passthrough";
97
+ type ModelResolutionSuccess = {
98
+ found: true;
99
+ modelId: string;
100
+ record: AiModelRecord | null;
101
+ routedViaOpenRouter: boolean;
102
+ confidence: number;
103
+ resolvedVia: ResolutionStrategy[];
104
+ resolvedReason: string;
105
+ normalisedInput: string;
106
+ };
107
+ type ModelResolutionNotFound = {
108
+ found: false;
109
+ modelId: null;
110
+ record: null;
111
+ attemptedStrategies: ResolutionStrategy[];
112
+ bestRejectedCandidate?: {
113
+ modelId: string;
114
+ confidence: number;
115
+ reason: string;
116
+ };
117
+ reason: string;
118
+ };
119
+ type ModelResolutionResult = ModelResolutionSuccess | ModelResolutionNotFound;
120
+ type ModelResolverOptions = {
121
+ confidenceThreshold?: number;
122
+ aliasRegistry?: AliasRegistry;
123
+ additionalShorthands?: Record<string, string>;
124
+ additionalProviderPatterns?: Array<{
125
+ pattern: RegExp;
126
+ provider: string;
127
+ }>;
128
+ additionalLocalProviders?: string[];
129
+ /** Env-based OpenRouter vs direct routing. Defaults to loadOpenRouterRoutingEnv(). */
130
+ routingEnv?: OpenRouterRoutingConfig;
131
+ };
132
+ type CatalogIndexes = {
133
+ aliasIndex: Map<string, string>;
134
+ slugIndex: Map<string, string>;
135
+ providerPrefixesBySize: string[];
136
+ };
137
+ /** @deprecated Use ModelResolutionSuccess via ModelNameResolver */
138
+ type ResolvedModel = {
139
+ catalogModel: AiModelRecord;
140
+ matchedAlias: string;
141
+ routedViaOpenRouter: boolean;
142
+ };
143
+
144
+ export { type AliasEntry as A, type CatalogIndexes as C, type ModelResolutionInput as M, type OpenRouterRoutingConfig as O, type ResolutionStrategy as R, type AliasFileSchema as a, AliasRegistry as b, type AliasRegistryOptions as c, type AliasValidationReport as d, type ModelResolutionNotFound as e, type ModelResolutionResult as f, type ModelResolutionSuccess as g, type ModelResolverOptions as h, type ResolvedModel as i, type ResolvedModelRef as j, loadOpenRouterRoutingEnv as l, providerIdToEnvKeyPrefix as p, shouldDefaultRouteViaOpenRouter as s, vendorApiKeyEnvName as v };
@@ -0,0 +1,35 @@
1
+ import { Firestore } from 'firebase-admin/firestore';
2
+ import { CataloxContext } from '@x12i/catalox';
3
+ import { a as AiModelRecord } from './types-BYXnCvKx.js';
4
+
5
+ /** Firestore rejects `undefined` — strip before write. */
6
+ declare function sanitizeForFirestore<T>(value: T): T;
7
+ type UpsertAiModelRecordOptions = {
8
+ firestore: Firestore;
9
+ context: CataloxContext;
10
+ catalogId: string;
11
+ record: AiModelRecord;
12
+ };
13
+ /**
14
+ * Upserts one ai-models row via Firestore (safe doc ids for `provider/model` ids).
15
+ */
16
+ declare function upsertAiModelRecord(options: UpsertAiModelRecordOptions): Promise<void>;
17
+ type BatchUpsertProgress = {
18
+ completed: number;
19
+ total: number;
20
+ phase: "loading-existing" | "writing";
21
+ };
22
+ type BatchUpsertAiModelRecordsOptions = {
23
+ onProgress?: (progress: BatchUpsertProgress) => void;
24
+ maxChunkRetries?: number;
25
+ };
26
+ type BatchUpsertResult = {
27
+ upserted: number;
28
+ failed: Array<{
29
+ modelId: string;
30
+ error: string;
31
+ }>;
32
+ };
33
+ declare function batchUpsertAiModelRecords(firestore: Firestore, catalogId: string, context: CataloxContext, records: AiModelRecord[], batchOptions?: BatchUpsertAiModelRecordsOptions): Promise<BatchUpsertResult>;
34
+
35
+ export { type BatchUpsertProgress as B, batchUpsertAiModelRecords as b, sanitizeForFirestore as s, upsertAiModelRecord as u };
@@ -0,0 +1,35 @@
1
+ import { Firestore } from 'firebase-admin/firestore';
2
+ import { CataloxContext } from '@x12i/catalox';
3
+ import { a as AiModelRecord } from './types-BYXnCvKx.cjs';
4
+
5
+ /** Firestore rejects `undefined` — strip before write. */
6
+ declare function sanitizeForFirestore<T>(value: T): T;
7
+ type UpsertAiModelRecordOptions = {
8
+ firestore: Firestore;
9
+ context: CataloxContext;
10
+ catalogId: string;
11
+ record: AiModelRecord;
12
+ };
13
+ /**
14
+ * Upserts one ai-models row via Firestore (safe doc ids for `provider/model` ids).
15
+ */
16
+ declare function upsertAiModelRecord(options: UpsertAiModelRecordOptions): Promise<void>;
17
+ type BatchUpsertProgress = {
18
+ completed: number;
19
+ total: number;
20
+ phase: "loading-existing" | "writing";
21
+ };
22
+ type BatchUpsertAiModelRecordsOptions = {
23
+ onProgress?: (progress: BatchUpsertProgress) => void;
24
+ maxChunkRetries?: number;
25
+ };
26
+ type BatchUpsertResult = {
27
+ upserted: number;
28
+ failed: Array<{
29
+ modelId: string;
30
+ error: string;
31
+ }>;
32
+ };
33
+ declare function batchUpsertAiModelRecords(firestore: Firestore, catalogId: string, context: CataloxContext, records: AiModelRecord[], batchOptions?: BatchUpsertAiModelRecordsOptions): Promise<BatchUpsertResult>;
34
+
35
+ export { type BatchUpsertProgress as B, batchUpsertAiModelRecords as b, sanitizeForFirestore as s, upsertAiModelRecord as u };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@x12i/ai-tools",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "AI model catalog, cost calculation, and LLM utility belt backed by Catalox",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,6 +45,11 @@
45
45
  "types": "./dist/models/index.d.ts",
46
46
  "import": "./dist/models/index.js",
47
47
  "require": "./dist/models/index.cjs"
48
+ },
49
+ "./catalog": {
50
+ "types": "./dist/catalog/index.d.ts",
51
+ "import": "./dist/catalog/index.js",
52
+ "require": "./dist/catalog/index.cjs"
48
53
  }
49
54
  },
50
55
  "files": [
@@ -63,6 +68,8 @@
63
68
  "test:coverage": "vitest run --coverage",
64
69
  "seed": "tsx scripts/seed.ts",
65
70
  "seed:verbose": "tsx scripts/seed.ts --verbose",
71
+ "verify:sync": "tsx scripts/verify-catalog-sync.ts",
72
+ "verify:seed": "tsx scripts/verify-catalog-sync.ts --sync",
66
73
  "prepublishOnly": "npm run build && npm test"
67
74
  },
68
75
  "peerDependencies": {
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-HHNHWYTP.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;AAYA;AACU,EAAA;AACF,EAAA;AAEA,EAAA;AACA,EAAA;AAKA,EAAA;AACA,EAAA;AACA,EAAA;AACA,EAAA;AAEA,EAAA;AACJ,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACA,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACA,MAAA;AACF,IAAA;AACA,IAAA;AACD,EAAA;AAEK,EAAA;AACR;AAEM;AAEN;AAMQ,EAAA;AACA,EAAA;AAKA,EAAA;AACA,EAAA;AAEN,EAAA;AACQ,IAAA;AACA,IAAA;AAEN,IAAA;AACE,MAAA;AACA,MAAA;AACA,MAAA;AACE,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACA,QAAA;AACE,UAAA;AACA,UAAA;AACA,UAAA;AACA,UAAA;AACF,QAAA;AACA,QAAA;AACD,MAAA;AACD,MAAA;AACF,IAAA;AAEM,IAAA;AACR,EAAA;AACF;AFtBU;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/ami/Documents/prometheus/x12i/ai-tools/dist/chunk-HHNHWYTP.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\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();\n const now = new Date().toISOString();\n const data = sanitizeForFirestore(record);\n\n const payload = sanitizeForFirestore({\n itemId: record.modelId,\n catalogId,\n appScopedOwnerId: appId,\n indexed: buildIndexed(record),\n data,\n metadata: {\n createdAt: String(existing.data()?.metadata?.createdAt ?? now),\n lastUpdate: now,\n domainIds: [],\n agentIds: [],\n },\n version: (existing.data()?.version ?? 0) + 1,\n });\n\n await col.doc(storageDocId).set(payload, { merge: true });\n}\n\nconst BATCH_SIZE = 400;\n\nexport async function batchUpsertAiModelRecords(\n firestore: Firestore,\n catalogId: string,\n context: CataloxContext,\n records: AiModelRecord[],\n): Promise<void> {\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\n for (let i = 0; i < records.length; i += BATCH_SIZE) {\n const chunk = records.slice(i, i + BATCH_SIZE);\n const batch = firestore.batch();\n\n for (const record of chunk) {\n const storageDocId = encodeModelFirestoreDocId(record.modelId);\n const data = sanitizeForFirestore(record);\n const payload = sanitizeForFirestore({\n itemId: record.modelId,\n catalogId,\n appScopedOwnerId: appId,\n indexed: buildIndexed(record),\n data,\n metadata: {\n createdAt: now,\n lastUpdate: now,\n domainIds: [],\n agentIds: [],\n },\n version: 1,\n });\n batch.set(col.doc(storageDocId), payload, { merge: true });\n }\n\n await batch.commit();\n }\n}\n"]}
@@ -1,105 +0,0 @@
1
- // 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
- import {
18
- flatNativeItemsCollectionRef,
19
- legacyNativeItemsCollectionRef,
20
- resolveNativeItemsLayout
21
- } from "@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
- async function upsertAiModelRecord(options) {
42
- const { record, catalogId, context, firestore } = options;
43
- const appId = context.appId ?? "ai-tools";
44
- const layout = await resolveNativeItemsLayout(firestore, catalogId);
45
- const col = layout === "legacy" ? legacyNativeItemsCollectionRef(firestore, catalogId) : flatNativeItemsCollectionRef(firestore, catalogId);
46
- const storageDocId = encodeModelFirestoreDocId(record.modelId);
47
- const existing = await col.doc(storageDocId).get();
48
- const now = (/* @__PURE__ */ new Date()).toISOString();
49
- const data = sanitizeForFirestore(record);
50
- const payload = sanitizeForFirestore({
51
- itemId: record.modelId,
52
- catalogId,
53
- appScopedOwnerId: appId,
54
- indexed: buildIndexed(record),
55
- data,
56
- metadata: {
57
- createdAt: String(existing.data()?.metadata?.createdAt ?? now),
58
- lastUpdate: now,
59
- domainIds: [],
60
- agentIds: []
61
- },
62
- version: (existing.data()?.version ?? 0) + 1
63
- });
64
- await col.doc(storageDocId).set(payload, { merge: true });
65
- }
66
- var BATCH_SIZE = 400;
67
- async function batchUpsertAiModelRecords(firestore, catalogId, context, records) {
68
- const layout = await resolveNativeItemsLayout(firestore, catalogId);
69
- const col = layout === "legacy" ? legacyNativeItemsCollectionRef(firestore, catalogId) : flatNativeItemsCollectionRef(firestore, catalogId);
70
- const appId = context.appId ?? "ai-tools";
71
- const now = (/* @__PURE__ */ new Date()).toISOString();
72
- for (let i = 0; i < records.length; i += BATCH_SIZE) {
73
- const chunk = records.slice(i, i + BATCH_SIZE);
74
- const batch = firestore.batch();
75
- for (const record of chunk) {
76
- const storageDocId = encodeModelFirestoreDocId(record.modelId);
77
- const data = sanitizeForFirestore(record);
78
- const payload = sanitizeForFirestore({
79
- itemId: record.modelId,
80
- catalogId,
81
- appScopedOwnerId: appId,
82
- indexed: buildIndexed(record),
83
- data,
84
- metadata: {
85
- createdAt: now,
86
- lastUpdate: now,
87
- domainIds: [],
88
- agentIds: []
89
- },
90
- version: 1
91
- });
92
- batch.set(col.doc(storageDocId), payload, { merge: true });
93
- }
94
- await batch.commit();
95
- }
96
- }
97
-
98
- export {
99
- encodeModelFirestoreDocId,
100
- decodeModelFirestoreDocId,
101
- sanitizeForFirestore,
102
- upsertAiModelRecord,
103
- batchUpsertAiModelRecords
104
- };
105
- //# sourceMappingURL=chunk-ML2FRR4L.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/catalox/modelDocId.ts","../src/catalox/upsertAiModelRecord.ts"],"sourcesContent":["/**\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\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();\n const now = new Date().toISOString();\n const data = sanitizeForFirestore(record);\n\n const payload = sanitizeForFirestore({\n itemId: record.modelId,\n catalogId,\n appScopedOwnerId: appId,\n indexed: buildIndexed(record),\n data,\n metadata: {\n createdAt: String(existing.data()?.metadata?.createdAt ?? now),\n lastUpdate: now,\n domainIds: [],\n agentIds: [],\n },\n version: (existing.data()?.version ?? 0) + 1,\n });\n\n await col.doc(storageDocId).set(payload, { merge: true });\n}\n\nconst BATCH_SIZE = 400;\n\nexport async function batchUpsertAiModelRecords(\n firestore: Firestore,\n catalogId: string,\n context: CataloxContext,\n records: AiModelRecord[],\n): Promise<void> {\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\n for (let i = 0; i < records.length; i += BATCH_SIZE) {\n const chunk = records.slice(i, i + BATCH_SIZE);\n const batch = firestore.batch();\n\n for (const record of chunk) {\n const storageDocId = encodeModelFirestoreDocId(record.modelId);\n const data = sanitizeForFirestore(record);\n const payload = sanitizeForFirestore({\n itemId: record.modelId,\n catalogId,\n appScopedOwnerId: appId,\n indexed: buildIndexed(record),\n data,\n metadata: {\n createdAt: now,\n lastUpdate: now,\n domainIds: [],\n agentIds: [],\n },\n version: 1,\n });\n batch.set(col.doc(storageDocId), payload, { merge: true });\n }\n\n await batch.commit();\n }\n}\n"],"mappings":"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","names":[]}