@layer-ai/core 2.0.46 → 2.0.48

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.
package/dist/index.d.ts CHANGED
@@ -20,6 +20,7 @@ export type { EncryptedData } from '@layer-ai/sdk';
20
20
  export declare const createSessionKey: (userId: string) => Promise<string>;
21
21
  export declare const deleteSessionKeysForUser: (userId: string) => Promise<void>;
22
22
  export * from './services/task-analysis.js';
23
+ export { initializeRegistry, getRegistry, getModel, hasModel, getModelEntries } from './lib/registry.js';
23
24
  export { PROVIDER, PROVIDERS, type Provider, callAdapter, normalizeModelId, getProviderForModel } from './lib/provider-factory.js';
24
25
  export { spendingTracker } from './lib/spending-tracker.js';
25
26
  export { spendingJobs } from './lib/spending-jobs.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACnF,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGpE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGpE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG1D,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC9E,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,eAAO,MAAM,gBAAgB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,MAAM,CAGrE,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,IAAI,CAG3E,CAAC;AAGF,cAAc,6BAA6B,CAAC;AAG5C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,WAAW,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAGnI,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,qBAAqB,EAAE,MAAM,iCAAiC,CAAC;AACnF,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGpE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGpE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG1D,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAG3C,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC9E,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,eAAO,MAAM,gBAAgB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,MAAM,CAGrE,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,IAAI,CAG3E,CAAC;AAGF,cAAc,6BAA6B,CAAC;AAG5C,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGzG,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,WAAW,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAGnI,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC"}
package/dist/index.js CHANGED
@@ -33,6 +33,8 @@ export const deleteSessionKeysForUser = async (userId) => {
33
33
  };
34
34
  // Services
35
35
  export * from './services/task-analysis.js';
36
+ // Model Registry
37
+ export { initializeRegistry, getRegistry, getModel, hasModel, getModelEntries } from './lib/registry.js';
36
38
  // Provider Factory
37
39
  export { PROVIDER, PROVIDERS, callAdapter, normalizeModelId, getProviderForModel } from './lib/provider-factory.js';
38
40
  // Spending Management
@@ -0,0 +1,2 @@
1
+ -- Rename 'document' task type to 'ocr' in gates table
2
+ UPDATE gates SET task_type = 'ocr' WHERE task_type = 'document';
@@ -1 +1 @@
1
- {"version":3,"file":"provider-factory.d.ts","sourceRoot":"","sources":["../../src/lib/provider-factory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEjF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAG7E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,CAAC;AAa9C;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAmBhE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,QAAQ,CAMnE;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAWhG;AAED;;;;;GAKG;AACH,wBAAuB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,aAAa,CAAC,CAgB7G"}
1
+ {"version":3,"file":"provider-factory.d.ts","sourceRoot":"","sources":["../../src/lib/provider-factory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAI7E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,CAAC;AAa9C;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAmBhE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,QAAQ,CAMnE;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAWhG;AAED;;;;;GAKG;AACH,wBAAuB,iBAAiB,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC,aAAa,CAAC,CAgB7G"}
@@ -9,8 +9,8 @@ import { OpenAIAdapter } from '../services/providers/openai-adapter.js';
9
9
  import { AnthropicAdapter } from '../services/providers/anthropic-adapter.js';
10
10
  import { GoogleAdapter } from '../services/providers/google-adapter.js';
11
11
  import { MistralAdapter } from '../services/providers/mistral-adapter.js';
12
- import { MODEL_REGISTRY } from '@layer-ai/sdk';
13
12
  import { PROVIDER, PROVIDERS } from './provider-constants.js';
13
+ import { getModel, hasModel } from './registry.js';
14
14
  // Re-export for convenience
15
15
  export { PROVIDER, PROVIDERS };
16
16
  /**
@@ -28,7 +28,7 @@ const PROVIDER_ADAPTERS = {
28
28
  * Supports both full IDs (e.g., "openai/gpt-4") and short IDs (e.g., "gpt-4").
29
29
  */
30
30
  export function normalizeModelId(modelId) {
31
- if (MODEL_REGISTRY[modelId]) {
31
+ if (hasModel(modelId)) {
32
32
  return modelId;
33
33
  }
34
34
  const providers = [
@@ -39,7 +39,7 @@ export function normalizeModelId(modelId) {
39
39
  ];
40
40
  for (const provider of providers) {
41
41
  const fullId = `${provider}/${modelId}`;
42
- if (MODEL_REGISTRY[fullId]) {
42
+ if (hasModel(fullId)) {
43
43
  return fullId;
44
44
  }
45
45
  }
@@ -49,7 +49,7 @@ export function normalizeModelId(modelId) {
49
49
  * Gets the provider type for a given model
50
50
  */
51
51
  export function getProviderForModel(model) {
52
- const modelInfo = MODEL_REGISTRY[model];
52
+ const modelInfo = getModel(model);
53
53
  if (!modelInfo) {
54
54
  throw new Error(`Model "${model}" not found in registry`);
55
55
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Runtime Model Registry
3
+ *
4
+ * Starts with static SDK data, fetches fresh data from the API
5
+ * at startup. Falls back to SDK data if the API is unreachable or
6
+ * API_URL is not set (e.g., local dev).
7
+ */
8
+ interface InitOptions {
9
+ refreshIntervalMs?: number;
10
+ }
11
+ export declare function initializeRegistry(options?: InitOptions): Promise<void>;
12
+ export declare function getRegistry(): Record<string, any>;
13
+ export declare function getModel(modelId: string): any | undefined;
14
+ export declare function hasModel(modelId: string): boolean;
15
+ export declare function getModelEntries(): [string, any][];
16
+ export {};
17
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/lib/registry.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAOH,UAAU,WAAW;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,wBAAsB,kBAAkB,CAAC,OAAO,GAAE,WAAgB,iBAcjE;AAgBD,wBAAgB,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAEjD;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,GAAG,SAAS,CAEzD;AAED,wBAAgB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,wBAAgB,eAAe,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,CAEjD"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Runtime Model Registry
3
+ *
4
+ * Starts with static SDK data, fetches fresh data from the API
5
+ * at startup. Falls back to SDK data if the API is unreachable or
6
+ * API_URL is not set (e.g., local dev).
7
+ */
8
+ import { MODEL_REGISTRY as STATIC_REGISTRY } from '@layer-ai/sdk';
9
+ let registry = { ...STATIC_REGISTRY };
10
+ let refreshTimer = null;
11
+ export async function initializeRegistry(options = {}) {
12
+ const apiUrl = process.env.API_URL;
13
+ if (!apiUrl) {
14
+ console.log('[Registry] No API_URL set, using static SDK data');
15
+ return;
16
+ }
17
+ await fetchRegistry(apiUrl);
18
+ if (options.refreshIntervalMs) {
19
+ refreshTimer = setInterval(() => fetchRegistry(apiUrl), options.refreshIntervalMs);
20
+ // Allow the process to exit even if the timer is active
21
+ refreshTimer.unref();
22
+ }
23
+ }
24
+ async function fetchRegistry(apiUrl) {
25
+ try {
26
+ const res = await fetch(`${apiUrl}/v1/registry`, {
27
+ signal: AbortSignal.timeout(10000),
28
+ });
29
+ if (!res.ok)
30
+ throw new Error(`HTTP ${res.status}`);
31
+ const data = await res.json();
32
+ registry = data.models;
33
+ console.log(`[Registry] Loaded ${Object.keys(registry).length} models from API`);
34
+ }
35
+ catch (err) {
36
+ console.warn('[Registry] Fetch failed, using existing data:', err);
37
+ }
38
+ }
39
+ export function getRegistry() {
40
+ return registry;
41
+ }
42
+ export function getModel(modelId) {
43
+ return registry[modelId];
44
+ }
45
+ export function hasModel(modelId) {
46
+ return modelId in registry;
47
+ }
48
+ export function getModelEntries() {
49
+ return Object.entries(registry);
50
+ }
@@ -291,7 +291,7 @@ async function testMultiProviderFallback() {
291
291
  };
292
292
  const modelsToTry = [
293
293
  'invalid-openai-model',
294
- 'claude-3-7-sonnet-20250219',
294
+ 'claude-sonnet-4-20250514',
295
295
  ];
296
296
  let chunkCount = 0;
297
297
  let fullContent = '';
@@ -138,7 +138,7 @@ async function testAnthropicBetaMode() {
138
138
  console.log('-'.repeat(80));
139
139
  const gateConfig = {
140
140
  ...baseGateConfig,
141
- model: 'claude-3-7-sonnet-20250219',
141
+ model: 'claude-sonnet-4-20250514',
142
142
  responseFormatEnabled: true,
143
143
  responseFormatType: 'json_object',
144
144
  };
@@ -2,7 +2,7 @@ import { Router } from 'express';
2
2
  import { db } from '../../lib/db/postgres.js';
3
3
  import { cache } from '../../lib/db/redis.js';
4
4
  import { callAdapter } from '../../lib/provider-factory.js';
5
- import { MODEL_REGISTRY } from '@layer-ai/sdk';
5
+ import { hasModel } from '../../lib/registry.js';
6
6
  import { detectSignificantChanges } from '../../lib/gate-utils.js';
7
7
  const router = Router();
8
8
  // POST / - Create a new gate
@@ -17,7 +17,7 @@ router.post('/', async (req, res) => {
17
17
  res.status(400).json({ error: 'bad_request', message: 'Missing required fields: name and model' });
18
18
  return;
19
19
  }
20
- if (!MODEL_REGISTRY[model]) {
20
+ if (!hasModel(model)) {
21
21
  res.status(400).json({ error: 'bad_request', message: `Unsupported model: ${model}` });
22
22
  return;
23
23
  }
@@ -259,7 +259,7 @@ router.patch('/:id', async (req, res) => {
259
259
  res.status(404).json({ error: 'not_found', message: 'Gate not found' });
260
260
  return;
261
261
  }
262
- if (model && !MODEL_REGISTRY[model]) {
262
+ if (model && !hasModel(model)) {
263
263
  res.status(400).json({ error: 'bad_request', message: `Unsupported model: ${model}` });
264
264
  return;
265
265
  }
@@ -118,7 +118,7 @@ router.post('/', async (req, res) => {
118
118
  return;
119
119
  }
120
120
  // Warn if gate is configured for a different task type
121
- if (gateConfig.taskType && gateConfig.taskType !== 'document') {
121
+ if (gateConfig.taskType && gateConfig.taskType !== 'ocr') {
122
122
  console.warn(`[Type Mismatch] Gate "${gateConfig.name}" (${gateConfig.id}) configured for taskType="${gateConfig.taskType}" ` +
123
123
  `but received request to /v3/ocr endpoint. Processing as OCR request.`);
124
124
  }
@@ -1 +1 @@
1
- {"version":3,"file":"base-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/base-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/base-adapter.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- import { ADAPTER_HANDLED, MODEL_REGISTRY, } from '@layer-ai/sdk';
1
+ import { ADAPTER_HANDLED, } from '@layer-ai/sdk';
2
+ import { getModel } from '../../lib/registry.js';
2
3
  export { ADAPTER_HANDLED };
3
4
  export class BaseProviderAdapter {
4
5
  mapRole(role) {
@@ -81,7 +82,7 @@ export class BaseProviderAdapter {
81
82
  return this.toolChoiceMappings[choice];
82
83
  }
83
84
  calculateCost(model, promptTokens, completionTokens) {
84
- const modelInfo = MODEL_REGISTRY[model];
85
+ const modelInfo = getModel(model);
85
86
  if (!modelInfo || !('pricing' in modelInfo) || !modelInfo.pricing?.input) {
86
87
  return 0;
87
88
  }
@@ -91,17 +92,17 @@ export class BaseProviderAdapter {
91
92
  return inputCost + outputCost;
92
93
  }
93
94
  calculateImageCost(model, quality, size, count = 1) {
94
- const modelInfo = MODEL_REGISTRY[model];
95
- if (!modelInfo || !('imagePricing' in modelInfo) || !modelInfo.imagePricing) {
95
+ const modelInfo = getModel(model);
96
+ if (!modelInfo || !('unitPricing' in modelInfo) || !modelInfo.unitPricing) {
96
97
  return 0;
97
98
  }
98
- const imagePricing = modelInfo.imagePricing;
99
- // Flat-rate pricing (e.g. Google Imagen models)
100
- if (typeof imagePricing === 'number') {
101
- return imagePricing * count;
99
+ const unitPricing = modelInfo.unitPricing;
100
+ // Flat-rate pricing (e.g. Google Imagen: unitPricing = 0.02)
101
+ if (typeof unitPricing === 'number') {
102
+ return unitPricing * count;
102
103
  }
103
- // Build pricing key from quality and size (e.g., 'hd-1024x1024' or 'standard-1024x1024')
104
- const pricingTable = imagePricing;
104
+ // Object pricing (e.g. DALL-E: unitPricing = { "hd-1024x1024": 0.08, ... })
105
+ const pricingTable = unitPricing;
105
106
  const pricingKey = quality && size ? `${quality}-${size}` : size || 'standard-1024x1024';
106
107
  const pricePerImage = pricingTable[pricingKey];
107
108
  if (!pricePerImage) {
@@ -112,16 +113,26 @@ export class BaseProviderAdapter {
112
113
  return pricePerImage * count;
113
114
  }
114
115
  calculateVideoCost(model, duration, count = 1) {
115
- const modelInfo = MODEL_REGISTRY[model];
116
- if (!modelInfo || !('videoPricing' in modelInfo) || !modelInfo.videoPricing) {
116
+ const modelInfo = getModel(model);
117
+ if (!modelInfo || !('unitPricing' in modelInfo) || !modelInfo.unitPricing) {
117
118
  return 0;
118
119
  }
119
- const videoPricing = modelInfo.videoPricing;
120
- // Video pricing might be per-second or per-video
121
- const pricePerUnit = videoPricing.perVideo || videoPricing.perSecond || 0;
122
- if (videoPricing.perSecond && duration) {
123
- return pricePerUnit * duration * count;
120
+ const unitPricing = modelInfo.unitPricing;
121
+ // Flat-rate pricing (e.g. unitPricing = 0.50 per video)
122
+ if (typeof unitPricing === 'number') {
123
+ return unitPricing * count;
124
124
  }
125
- return pricePerUnit * count;
125
+ // Object pricing with per_second or per_video keys
126
+ const pricing = unitPricing;
127
+ if (pricing.per_second && duration) {
128
+ return pricing.per_second * duration * count;
129
+ }
130
+ if (pricing.per_video) {
131
+ return pricing.per_video * count;
132
+ }
133
+ if (pricing.per_minute && duration) {
134
+ return pricing.per_minute * (duration / 60) * count;
135
+ }
136
+ return 0;
126
137
  }
127
138
  }
@@ -53,7 +53,7 @@ async function testImageGeneration() {
53
53
  console.log('Testing image generation...');
54
54
  const request = {
55
55
  gateId: 'test-gate',
56
- model: 'imagen-4.0-generate-001',
56
+ model: 'imagen-4.0-fast-generate-001',
57
57
  type: 'image',
58
58
  data: {
59
59
  prompt: 'A cute cat playing with a ball of yarn',
@@ -64,6 +64,10 @@ async function testImageGeneration() {
64
64
  console.log('Generated images:', response.images?.length);
65
65
  console.log('Image base64 length:', response.images?.[0]?.base64?.length);
66
66
  console.log('Latency:', response.latencyMs + 'ms');
67
+ console.log('Cost:', response.cost);
68
+ if (!response.cost || response.cost <= 0) {
69
+ throw new Error(`Expected image generation cost > 0, got ${response.cost}`);
70
+ }
67
71
  console.log('✅ Image generation test passed\n');
68
72
  }
69
73
  async function testEmbeddings() {
@@ -196,6 +200,14 @@ async function testVideoGeneration() {
196
200
  console.log('Video URL:', response.videos?.[0]?.url);
197
201
  console.log('Video duration:', response.videos?.[0]?.duration);
198
202
  console.log('Latency:', response.latencyMs + 'ms');
203
+ console.log('Cost:', response.cost);
204
+ // Note: video cost may be 0 if unitPricing is not set for this model in the registry
205
+ if (response.cost && response.cost > 0) {
206
+ console.log('✅ Video cost calculation working');
207
+ }
208
+ else {
209
+ console.warn('⚠️ Video cost is 0 (unitPricing may not be set for this model)');
210
+ }
199
211
  console.log('✅ Video generation test passed\n');
200
212
  }
201
213
  async function runTests() {
@@ -203,9 +215,6 @@ async function runTests() {
203
215
  console.log('Chat completion tests...');
204
216
  await testChatCompletion();
205
217
  await testChatWithVision();
206
- console.log('Embeddings...');
207
- await testEmbeddings();
208
- await testEmbeddingsMultiple();
209
218
  console.log('Image generation...');
210
219
  await testImageGeneration();
211
220
  console.log('Text-to-speech...');
@@ -214,6 +223,9 @@ async function runTests() {
214
223
  await testVideoGeneration();
215
224
  console.log('Tool calling...');
216
225
  await testToolCalling();
226
+ console.log('Embeddings...');
227
+ await testEmbeddings();
228
+ await testEmbeddingsMultiple();
217
229
  console.log('✅ All tests passed!');
218
230
  }
219
231
  catch (error) {
@@ -14,7 +14,7 @@ async function testJsonObjectMode() {
14
14
  console.log('-'.repeat(80));
15
15
  const request = {
16
16
  gateId: 'test-gate',
17
- model: 'gpt-4o-mini',
17
+ model: 'gpt-4o',
18
18
  type: 'chat',
19
19
  data: {
20
20
  messages: [
@@ -118,7 +118,7 @@ async function testTextMode() {
118
118
  console.log('-'.repeat(80));
119
119
  const request = {
120
120
  gateId: 'test-gate',
121
- model: 'gpt-4o-mini',
121
+ model: 'gpt-4o',
122
122
  type: 'chat',
123
123
  data: {
124
124
  messages: [
@@ -8,7 +8,7 @@ async function testChatCompletion() {
8
8
  const request = {
9
9
  gateId: 'test-gate',
10
10
  type: 'chat',
11
- model: 'mistral-small-latest',
11
+ model: 'mistral-small-2501',
12
12
  data: {
13
13
  messages: [
14
14
  {
@@ -33,7 +33,7 @@ async function testChatWithSystemPrompt() {
33
33
  const request = {
34
34
  gateId: 'test-gate',
35
35
  type: 'chat',
36
- model: 'mistral-small-latest',
36
+ model: 'mistral-small-2501',
37
37
  data: {
38
38
  systemPrompt: 'You are a helpful assistant that responds in JSON format only.',
39
39
  messages: [
@@ -56,7 +56,7 @@ async function testChatWithTools() {
56
56
  const request = {
57
57
  gateId: 'test-gate',
58
58
  type: 'chat',
59
- model: 'mistral-small-latest',
59
+ model: 'mistral-small-2501',
60
60
  data: {
61
61
  messages: [
62
62
  {
@@ -104,7 +104,7 @@ async function testToolResponse() {
104
104
  const initialRequest = {
105
105
  gateId: 'test-gate',
106
106
  type: 'chat',
107
- model: 'mistral-small-latest',
107
+ model: 'mistral-small-2501',
108
108
  data: {
109
109
  messages: [
110
110
  {
@@ -145,7 +145,7 @@ async function testToolResponse() {
145
145
  const followUpRequest = {
146
146
  gateId: 'test-gate',
147
147
  type: 'chat',
148
- model: 'mistral-small-latest',
148
+ model: 'mistral-small-2501',
149
149
  data: {
150
150
  messages: [
151
151
  {
@@ -226,7 +226,7 @@ async function testResponseFormat() {
226
226
  const request = {
227
227
  gateId: 'test-gate',
228
228
  type: 'chat',
229
- model: 'mistral-small-latest',
229
+ model: 'mistral-small-2501',
230
230
  data: {
231
231
  messages: [
232
232
  {
@@ -247,7 +247,7 @@ async function testMultiTurn() {
247
247
  const request = {
248
248
  gateId: 'test-gate',
249
249
  type: 'chat',
250
- model: 'mistral-small-latest',
250
+ model: 'mistral-small-2501',
251
251
  data: {
252
252
  messages: [
253
253
  {
@@ -297,7 +297,7 @@ async function testUnsupportedModality() {
297
297
  const request = {
298
298
  gateId: 'test-gate',
299
299
  type: 'image',
300
- model: 'mistral-large-latest',
300
+ model: 'mistral-large-2512',
301
301
  data: {
302
302
  prompt: 'A sunset over the ocean',
303
303
  },
@@ -4,7 +4,7 @@ async function testChatCompletion() {
4
4
  console.log('Testing chat completion...');
5
5
  const request = {
6
6
  gateId: 'test-gate',
7
- model: 'gpt-4o-mini',
7
+ model: 'gpt-4o',
8
8
  type: 'chat',
9
9
  data: {
10
10
  messages: [
@@ -26,7 +26,7 @@ async function testChatWithVision() {
26
26
  console.log('Testing chat with vision...');
27
27
  const request = {
28
28
  gateId: 'test-gate',
29
- model: 'gpt-4o-mini',
29
+ model: 'gpt-4o',
30
30
  type: 'chat',
31
31
  data: {
32
32
  messages: [
@@ -64,6 +64,10 @@ async function testImageGeneration() {
64
64
  console.log('Generated images:', response.images?.length);
65
65
  console.log('Image URL:', response.images?.[0]?.url);
66
66
  console.log('Revised prompt:', response.images?.[0]?.revisedPrompt);
67
+ console.log('Cost:', response.cost);
68
+ if (!response.cost || response.cost <= 0) {
69
+ throw new Error(`Expected image generation cost > 0, got ${response.cost}`);
70
+ }
67
71
  console.log('✅ Image generation test passed\n');
68
72
  }
69
73
  async function testEmbeddings() {
@@ -105,7 +109,7 @@ async function testToolCalling() {
105
109
  // Step 1: Initial request with tool available
106
110
  const request = {
107
111
  gateId: 'test-gate',
108
- model: 'gpt-4o-mini',
112
+ model: 'gpt-4o',
109
113
  type: 'chat',
110
114
  data: {
111
115
  messages: [
@@ -146,7 +150,7 @@ async function testToolCalling() {
146
150
  // Step 2: Send tool response back
147
151
  const toolResponseRequest = {
148
152
  gateId: 'test-gate',
149
- model: 'gpt-4o-mini',
153
+ model: 'gpt-4o',
150
154
  type: 'chat',
151
155
  data: {
152
156
  messages: [
@@ -174,7 +178,7 @@ async function testContentAndToolCalls() {
174
178
  // This tests the fix we made - assistant messages can have BOTH content and toolCalls
175
179
  const request = {
176
180
  gateId: 'test-gate',
177
- model: 'gpt-4o-mini',
181
+ model: 'gpt-4o',
178
182
  type: 'chat',
179
183
  data: {
180
184
  messages: [
@@ -4,7 +4,7 @@ async function testChatStreamingBasic() {
4
4
  console.log('Testing basic chat streaming...');
5
5
  const request = {
6
6
  gateId: 'test-gate',
7
- model: 'gpt-4o-mini',
7
+ model: 'gpt-4o',
8
8
  type: 'chat',
9
9
  data: {
10
10
  messages: [
@@ -48,7 +48,7 @@ async function testChatStreamingWithToolCalls() {
48
48
  console.log('Testing chat streaming with tool calls...');
49
49
  const request = {
50
50
  gateId: 'test-gate',
51
- model: 'gpt-4o-mini',
51
+ model: 'gpt-4o',
52
52
  type: 'chat',
53
53
  data: {
54
54
  messages: [
@@ -1 +1 @@
1
- {"version":3,"file":"task-analysis.d.ts","sourceRoot":"","sources":["../../src/services/task-analysis.ts"],"names":[],"mappings":"AACA,OAAO,EAAkB,YAAY,EAAmC,MAAM,eAAe,CAAC;AA2D9F,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,EACnB,eAAe,CAAC,EAAE;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B,GACA,OAAO,CAAC,YAAY,CAAC,CA6HvB"}
1
+ {"version":3,"file":"task-analysis.d.ts","sourceRoot":"","sources":["../../src/services/task-analysis.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAmC,MAAM,eAAe,CAAC;AA6D9E,wBAAsB,WAAW,CAC/B,WAAW,EAAE,MAAM,EACnB,eAAe,CAAC,EAAE;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B,GACA,OAAO,CAAC,YAAY,CAAC,CA6HvB"}
@@ -1,5 +1,5 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
- import { MODEL_REGISTRY } from '@layer-ai/sdk';
2
+ import { getModelEntries } from '../lib/registry.js';
3
3
  async function detectTaskType(description, anthropic) {
4
4
  const prompt = `Analyze this task description and determine what TYPE of AI task it is.
5
5
 
@@ -14,7 +14,8 @@ AVAILABLE TASK TYPES:
14
14
  - tts: Text-to-speech, voice synthesis
15
15
  - stt: Speech-to-text, audio transcription
16
16
  - embeddings: Text embeddings, semantic search
17
- - document: Document processing, OCR
17
+ - ocr: Document processing, OCR
18
+ - moderation: Content moderation
18
19
  - responses: Complex reasoning tasks (o3-pro style models)
19
20
  - language-completion: Legacy text completion
20
21
 
@@ -39,7 +40,7 @@ Return ONLY the task type as a single word, nothing else.`;
39
40
  throw new Error('Unexpected response type from Claude');
40
41
  }
41
42
  const detectedType = responseContent.text.trim().toLowerCase();
42
- const validTypes = ['chat', 'image', 'video', 'audio', 'tts', 'stt', 'embeddings', 'document', 'responses', 'language-completion'];
43
+ const validTypes = ['chat', 'image', 'video', 'audio', 'tts', 'stt', 'embeddings', 'ocr', 'moderation', 'responses', 'language-completion'];
43
44
  if (validTypes.includes(detectedType)) {
44
45
  return detectedType;
45
46
  }
@@ -66,7 +67,7 @@ export async function analyzeTask(description, userPreferences) {
66
67
  console.error('Failed to detect task type, defaulting to chat:', error);
67
68
  }
68
69
  const filteredRegistry = {};
69
- for (const [key, model] of Object.entries(MODEL_REGISTRY)) {
70
+ for (const [key, model] of getModelEntries()) {
70
71
  // Filter by task type
71
72
  if (model.type !== taskType) {
72
73
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layer-ai/core",
3
- "version": "2.0.46",
3
+ "version": "2.0.48",
4
4
  "description": "Core API routes and services for Layer AI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "nanoid": "^5.0.4",
37
37
  "openai": "^4.24.0",
38
38
  "pg": "^8.11.3",
39
- "@layer-ai/sdk": "^2.5.11"
39
+ "@layer-ai/sdk": "^2.5.12"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/bcryptjs": "^2.4.6",