@layer-ai/core 2.0.49 → 2.0.51

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
@@ -23,6 +23,6 @@ export * from './services/task-analysis.js';
23
23
  export { initializeRegistry, getRegistry, getModel, hasModel, getModelEntries } from './lib/registry.js';
24
24
  export { PROVIDER, PROVIDERS, type Provider, callAdapter, normalizeModelId, getProviderForModel } from './lib/provider-factory.js';
25
25
  export { spendingTracker } from './lib/spending-tracker.js';
26
- export { spendingJobs } from './lib/spending-jobs.js';
26
+ export { spendingWorker } from './lib/spending-jobs.js';
27
27
  export { gateSpendingTracker } from './lib/gate-spending-tracker.js';
28
28
  //# sourceMappingURL=index.d.ts.map
@@ -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,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"}
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,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC"}
package/dist/index.js CHANGED
@@ -39,5 +39,5 @@ export { initializeRegistry, getRegistry, getModel, hasModel, getModelEntries }
39
39
  export { PROVIDER, PROVIDERS, callAdapter, normalizeModelId, getProviderForModel } from './lib/provider-factory.js';
40
40
  // Spending Management
41
41
  export { spendingTracker } from './lib/spending-tracker.js';
42
- export { spendingJobs } from './lib/spending-jobs.js';
42
+ export { spendingWorker } from './lib/spending-jobs.js';
43
43
  export { gateSpendingTracker } from './lib/gate-spending-tracker.js';
@@ -0,0 +1,8 @@
1
+ -- Allow nullable fields for failed requests (validation errors before model selection)
2
+ ALTER TABLE requests ALTER COLUMN model_requested DROP NOT NULL;
3
+ ALTER TABLE requests ALTER COLUMN model_used DROP NOT NULL;
4
+ ALTER TABLE requests ALTER COLUMN prompt_tokens SET DEFAULT 0;
5
+ ALTER TABLE requests ALTER COLUMN completion_tokens SET DEFAULT 0;
6
+ ALTER TABLE requests ALTER COLUMN total_tokens SET DEFAULT 0;
7
+ ALTER TABLE requests ALTER COLUMN cost_usd SET DEFAULT 0;
8
+ ALTER TABLE requests ALTER COLUMN latency_ms SET DEFAULT 0;
@@ -1,8 +1,14 @@
1
- export declare const spendingJobs: {
2
- syncSpendingJob(): Promise<void>;
3
- resetSpendingPeriodsJob(): Promise<void>;
4
- resetGateSpendingPeriodsJob(): Promise<void>;
5
- resetUsageCountersJob(): Promise<void>;
6
- startScheduledJobs(): void;
7
- };
1
+ declare class SpendingWorker {
2
+ private isRunning;
3
+ private syncTask;
4
+ private resetTask;
5
+ start(): void;
6
+ stop(): void;
7
+ private syncSpendingJob;
8
+ private resetSpendingPeriodsJob;
9
+ private resetGateSpendingPeriodsJob;
10
+ private resetUsageCountersJob;
11
+ }
12
+ export declare const spendingWorker: SpendingWorker;
13
+ export {};
8
14
  //# sourceMappingURL=spending-jobs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spending-jobs.d.ts","sourceRoot":"","sources":["../../src/lib/spending-jobs.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,YAAY;uBACE,OAAO,CAAC,IAAI,CAAC;+BASL,OAAO,CAAC,IAAI,CAAC;mCAwBT,OAAO,CAAC,IAAI,CAAC;6BAuBnB,OAAO,CAAC,IAAI,CAAC;0BAWtB,IAAI;CAqC3B,CAAC"}
1
+ {"version":3,"file":"spending-jobs.d.ts","sourceRoot":"","sources":["../../src/lib/spending-jobs.ts"],"names":[],"mappings":"AAKA,cAAM,cAAc;IAClB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,SAAS,CAAmC;IAEpD,KAAK;IAkBL,IAAI;YAUU,eAAe;YAiBf,uBAAuB;YAwBvB,2BAA2B;YAuB3B,qBAAqB;CAUpC;AAED,eAAO,MAAM,cAAc,gBAAuB,CAAC"}
@@ -1,98 +1,102 @@
1
+ import * as cron from 'node-cron';
1
2
  import { db } from './db/postgres.js';
2
3
  import { cache } from './db/redis.js';
3
4
  import { spendingTracker } from './spending-tracker.js';
4
- export const spendingJobs = {
5
+ class SpendingWorker {
6
+ constructor() {
7
+ this.isRunning = false;
8
+ this.syncTask = null;
9
+ this.resetTask = null;
10
+ }
11
+ start() {
12
+ console.log('[Spending Worker] Starting scheduled jobs');
13
+ // Sync Redis to DB every 5 minutes (offset :02 to avoid other workers)
14
+ this.syncTask = cron.schedule('2/5 * * * *', async () => {
15
+ await this.syncSpendingJob();
16
+ });
17
+ // Check for billing period resets every hour at :20
18
+ this.resetTask = cron.schedule('20 * * * *', async () => {
19
+ await this.resetSpendingPeriodsJob();
20
+ await this.resetGateSpendingPeriodsJob();
21
+ await this.resetUsageCountersJob();
22
+ });
23
+ console.log('[Spending Worker] Cron schedules activated');
24
+ }
25
+ stop() {
26
+ if (this.syncTask) {
27
+ this.syncTask.stop();
28
+ }
29
+ if (this.resetTask) {
30
+ this.resetTask.stop();
31
+ }
32
+ console.log('[Spending Worker] Stopped');
33
+ }
5
34
  async syncSpendingJob() {
6
- console.log('[Spending Job] Starting periodic sync...');
35
+ if (this.isRunning) {
36
+ console.log('[Spending Worker] Sync already in progress, skipping');
37
+ return;
38
+ }
39
+ this.isRunning = true;
7
40
  try {
41
+ console.log('[Spending Worker] Starting periodic sync...');
8
42
  await spendingTracker.syncAllSpending();
9
43
  }
10
44
  catch (error) {
11
- console.error('[Spending Job] Sync failed:', error);
45
+ console.error('[Spending Worker] Sync failed:', error);
12
46
  }
13
- },
47
+ finally {
48
+ this.isRunning = false;
49
+ }
50
+ }
14
51
  async resetSpendingPeriodsJob() {
15
- console.log('[Spending Job] Checking for billing periods to reset...');
52
+ console.log('[Spending Worker] Checking for billing periods to reset...');
16
53
  try {
17
54
  const usersToReset = await db.getUsersToResetSpending();
18
55
  if (usersToReset.length === 0) {
19
- console.log('[Spending Job] No users need reset');
56
+ console.log('[Spending Worker] No users need reset');
20
57
  return;
21
58
  }
22
- console.log(`[Spending Job] Resetting ${usersToReset.length} users`);
59
+ console.log(`[Spending Worker] Resetting ${usersToReset.length} users`);
23
60
  for (const userId of usersToReset) {
24
61
  await db.resetUserSpending(userId);
25
62
  await cache.invalidateUserSpending(userId);
26
- console.log(`[Spending Job] Reset user ${userId}`);
63
+ console.log(`[Spending Worker] Reset user ${userId}`);
27
64
  }
28
- console.log('[Spending Job] Reset complete');
65
+ console.log('[Spending Worker] Reset complete');
29
66
  }
30
67
  catch (error) {
31
- console.error('[Spending Job] Reset failed:', error);
68
+ console.error('[Spending Worker] Reset failed:', error);
32
69
  }
33
- },
70
+ }
34
71
  async resetGateSpendingPeriodsJob() {
35
- console.log('[Spending Job] Checking for gate spending periods to reset...');
72
+ console.log('[Spending Worker] Checking for gate spending periods to reset...');
36
73
  try {
37
74
  const gatesToReset = await db.getGatesToResetSpending();
38
75
  if (gatesToReset.length === 0) {
39
- console.log('[Spending Job] No gates need reset');
76
+ console.log('[Spending Worker] No gates need reset');
40
77
  return;
41
78
  }
42
- console.log(`[Spending Job] Resetting ${gatesToReset.length} gates`);
79
+ console.log(`[Spending Worker] Resetting ${gatesToReset.length} gates`);
43
80
  for (const gateId of gatesToReset) {
44
81
  await db.resetGateSpending(gateId);
45
- console.log(`[Spending Job] Reset gate ${gateId}`);
82
+ console.log(`[Spending Worker] Reset gate ${gateId}`);
46
83
  }
47
- console.log('[Spending Job] Gate reset complete');
84
+ console.log('[Spending Worker] Gate reset complete');
48
85
  }
49
86
  catch (error) {
50
- console.error('[Spending Job] Gate reset failed:', error);
87
+ console.error('[Spending Worker] Gate reset failed:', error);
51
88
  }
52
- },
89
+ }
53
90
  async resetUsageCountersJob() {
54
- console.log('[Spending Job] Checking for usage counters to reset...');
91
+ console.log('[Spending Worker] Checking for usage counters to reset...');
55
92
  try {
56
93
  await db.resetDailyUsage();
57
94
  await db.resetMonthlyUsage();
58
- console.log('[Spending Job] Usage counters reset complete');
95
+ console.log('[Spending Worker] Usage counters reset complete');
59
96
  }
60
97
  catch (error) {
61
- console.error('[Spending Job] Usage counter reset failed:', error);
98
+ console.error('[Spending Worker] Usage counter reset failed:', error);
62
99
  }
63
- },
64
- startScheduledJobs() {
65
- // Sync Redis to DB every 5 minutes
66
- setInterval(() => {
67
- this.syncSpendingJob().catch(err => {
68
- console.error('[Spending Job] Sync interval error:', err);
69
- });
70
- }, 5 * 60 * 1000);
71
- // Check for billing period resets every hour
72
- setInterval(() => {
73
- this.resetSpendingPeriodsJob().catch(err => {
74
- console.error('[Spending Job] Reset interval error:', err);
75
- });
76
- this.resetGateSpendingPeriodsJob().catch(err => {
77
- console.error('[Spending Job] Gate reset interval error:', err);
78
- });
79
- this.resetUsageCountersJob().catch(err => {
80
- console.error('[Spending Job] Usage counter reset interval error:', err);
81
- });
82
- }, 60 * 60 * 1000);
83
- // Run once on startup
84
- this.syncSpendingJob().catch(err => {
85
- console.error('[Spending Job] Initial sync error:', err);
86
- });
87
- this.resetSpendingPeriodsJob().catch(err => {
88
- console.error('[Spending Job] Initial reset error:', err);
89
- });
90
- this.resetGateSpendingPeriodsJob().catch(err => {
91
- console.error('[Spending Job] Initial gate reset error:', err);
92
- });
93
- this.resetUsageCountersJob().catch(err => {
94
- console.error('[Spending Job] Initial usage counter reset error:', err);
95
- });
96
- console.log('[Spending Job] Scheduled jobs started');
97
- },
98
- };
100
+ }
101
+ }
102
+ export const spendingWorker = new SpendingWorker();
@@ -1 +1 @@
1
- {"version":3,"file":"chat-completions.d.ts","sourceRoot":"","sources":["../../../src/routes/v1/chat-completions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAapD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAkTpC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"chat-completions.d.ts","sourceRoot":"","sources":["../../../src/routes/v1/chat-completions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAapD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AA0TpC,eAAe,MAAM,CAAC"}
@@ -44,9 +44,11 @@ router.post('/', async (req, res) => {
44
44
  }
45
45
  }
46
46
  if (!gateId) {
47
+ const msg = 'Missing required field: gateId (provide in request body, X-Layer-Gate-Id header, or as part of model field)';
48
+ db.logRequest({ userId, gateId: null, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: openaiReq, responsePayload: null }).catch(() => { });
47
49
  const error = {
48
50
  error: {
49
- message: 'Missing required field: gateId (provide in request body, X-Layer-Gate-Id header, or as part of model field)',
51
+ message: msg,
50
52
  type: 'invalid_request_error',
51
53
  param: 'gateId',
52
54
  code: 'missing_field',
@@ -57,9 +59,11 @@ router.post('/', async (req, res) => {
57
59
  }
58
60
  const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(gateId);
59
61
  if (!isUUID) {
62
+ const msg = 'gateId must be a valid UUID';
63
+ db.logRequest({ userId, gateId, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: openaiReq, responsePayload: null }).catch(() => { });
60
64
  const error = {
61
65
  error: {
62
- message: 'gateId must be a valid UUID',
66
+ message: msg,
63
67
  type: 'invalid_request_error',
64
68
  param: 'gateId',
65
69
  code: 'invalid_format',
@@ -70,9 +74,11 @@ router.post('/', async (req, res) => {
70
74
  }
71
75
  gateConfig = await db.getGateByUserAndId(userId, gateId);
72
76
  if (!gateConfig) {
77
+ const msg = `Gate with ID "${gateId}" not found`;
78
+ db.logRequest({ userId, gateId, gateName: null, modelRequested: openaiReq.model || null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: openaiReq, responsePayload: null }).catch(() => { });
73
79
  const error = {
74
80
  error: {
75
- message: `Gate with ID "${gateId}" not found`,
81
+ message: msg,
76
82
  type: 'invalid_request_error',
77
83
  param: 'gateId',
78
84
  code: 'not_found',
@@ -82,9 +88,11 @@ router.post('/', async (req, res) => {
82
88
  return;
83
89
  }
84
90
  if (!openaiReq.messages || !Array.isArray(openaiReq.messages) || openaiReq.messages.length === 0) {
91
+ const msg = 'Missing required field: messages (must be a non-empty array)';
92
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: openaiReq.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: openaiReq, responsePayload: null }).catch(() => { });
85
93
  const error = {
86
94
  error: {
87
- message: 'Missing required field: messages (must be a non-empty array)',
95
+ message: msg,
88
96
  type: 'invalid_request_error',
89
97
  param: 'messages',
90
98
  code: 'missing_field',
@@ -1 +1 @@
1
- {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../src/routes/v1/messages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAapD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAyUpC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../../src/routes/v1/messages.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAapD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAmVpC,eAAe,MAAM,CAAC"}
@@ -43,11 +43,13 @@ router.post('/', async (req, res) => {
43
43
  }
44
44
  }
45
45
  if (!gateId) {
46
+ const msg = 'Missing required field: gateId (provide in request body, X-Layer-Gate-Id header, or as part of model field)';
47
+ db.logRequest({ userId, gateId: null, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: anthropicReq, responsePayload: null }).catch(() => { });
46
48
  const error = {
47
49
  type: 'error',
48
50
  error: {
49
51
  type: 'invalid_request_error',
50
- message: 'Missing required field: gateId (provide in request body, X-Layer-Gate-Id header, or as part of model field)',
52
+ message: msg,
51
53
  },
52
54
  };
53
55
  res.status(400).json(error);
@@ -55,11 +57,13 @@ router.post('/', async (req, res) => {
55
57
  }
56
58
  const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(gateId);
57
59
  if (!isUUID) {
60
+ const msg = 'gateId must be a valid UUID';
61
+ db.logRequest({ userId, gateId, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: anthropicReq, responsePayload: null }).catch(() => { });
58
62
  const error = {
59
63
  type: 'error',
60
64
  error: {
61
65
  type: 'invalid_request_error',
62
- message: 'gateId must be a valid UUID',
66
+ message: msg,
63
67
  },
64
68
  };
65
69
  res.status(400).json(error);
@@ -67,33 +71,39 @@ router.post('/', async (req, res) => {
67
71
  }
68
72
  gateConfig = await db.getGateByUserAndId(userId, gateId);
69
73
  if (!gateConfig) {
74
+ const msg = `Gate with ID "${gateId}" not found`;
75
+ db.logRequest({ userId, gateId, gateName: null, modelRequested: anthropicReq.model || null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: anthropicReq, responsePayload: null }).catch(() => { });
70
76
  const error = {
71
77
  type: 'error',
72
78
  error: {
73
79
  type: 'not_found_error',
74
- message: `Gate with ID "${gateId}" not found`,
80
+ message: msg,
75
81
  },
76
82
  };
77
83
  res.status(404).json(error);
78
84
  return;
79
85
  }
80
86
  if (!anthropicReq.messages || !Array.isArray(anthropicReq.messages) || anthropicReq.messages.length === 0) {
87
+ const msg = 'Missing required field: messages (must be a non-empty array)';
88
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: anthropicReq.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: anthropicReq, responsePayload: null }).catch(() => { });
81
89
  const error = {
82
90
  type: 'error',
83
91
  error: {
84
92
  type: 'invalid_request_error',
85
- message: 'Missing required field: messages (must be a non-empty array)',
93
+ message: msg,
86
94
  },
87
95
  };
88
96
  res.status(400).json(error);
89
97
  return;
90
98
  }
91
99
  if (!anthropicReq.max_tokens) {
100
+ const msg = 'Missing required field: max_tokens';
101
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: anthropicReq.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: anthropicReq, responsePayload: null }).catch(() => { });
92
102
  const error = {
93
103
  type: 'error',
94
104
  error: {
95
105
  type: 'invalid_request_error',
96
- message: 'Missing required field: max_tokens',
106
+ message: msg,
97
107
  },
98
108
  };
99
109
  res.status(400).json(error);
@@ -1 +1 @@
1
- {"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/complete.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AASpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAsVpC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"complete.d.ts","sourceRoot":"","sources":["../../../src/routes/v2/complete.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AASpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAwWpC,eAAe,MAAM,CAAC"}
@@ -169,17 +169,23 @@ router.post('/', async (req, res) => {
169
169
  try {
170
170
  const rawRequest = req.body;
171
171
  if (!rawRequest.gateId) {
172
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: gateId' });
172
+ const msg = 'Missing required field: gateId';
173
+ db.logRequest({ userId, gateId: null, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
174
+ res.status(400).json({ error: 'bad_request', message: msg });
173
175
  return;
174
176
  }
175
177
  const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(rawRequest.gateId);
176
178
  if (!isUUID) {
177
- res.status(400).json({ error: 'bad_request', message: 'gateId must be a valid UUID. Gate names are no longer supported.' });
179
+ const msg = 'gateId must be a valid UUID. Gate names are no longer supported.';
180
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
181
+ res.status(400).json({ error: 'bad_request', message: msg });
178
182
  return;
179
183
  }
180
184
  gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
181
185
  if (!gateConfig) {
182
- res.status(404).json({ error: 'not_found', message: `Gate with ID "${rawRequest.gateId}" not found` });
186
+ const msg = `Gate with ID "${rawRequest.gateId}" not found`;
187
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: rawRequest.model || null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
188
+ res.status(404).json({ error: 'not_found', message: msg });
183
189
  return;
184
190
  }
185
191
  // Default to gate's taskType, allow override via request.type
@@ -200,37 +206,49 @@ router.post('/', async (req, res) => {
200
206
  switch (request.type) {
201
207
  case 'chat':
202
208
  if (!request.data.messages || !Array.isArray(request.data.messages) || request.data.messages.length === 0) {
203
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.messages' });
209
+ const msg = 'Missing required field: data.messages';
210
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
211
+ res.status(400).json({ error: 'bad_request', message: msg });
204
212
  return;
205
213
  }
206
214
  break;
207
215
  case 'image':
208
216
  if (!request.data.prompt) {
209
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.prompt' });
217
+ const msg = 'Missing required field: data.prompt';
218
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
219
+ res.status(400).json({ error: 'bad_request', message: msg });
210
220
  return;
211
221
  }
212
222
  break;
213
223
  case 'video':
214
224
  if (!request.data.prompt) {
215
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.prompt' });
225
+ const msg = 'Missing required field: data.prompt';
226
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
227
+ res.status(400).json({ error: 'bad_request', message: msg });
216
228
  return;
217
229
  }
218
230
  break;
219
231
  case 'embeddings':
220
232
  if (!request.data.input) {
221
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.input' });
233
+ const msg = 'Missing required field: data.input';
234
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
235
+ res.status(400).json({ error: 'bad_request', message: msg });
222
236
  return;
223
237
  }
224
238
  break;
225
239
  case 'tts':
226
240
  if (!request.data.input) {
227
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.input' });
241
+ const msg = 'Missing required field: data.input';
242
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
243
+ res.status(400).json({ error: 'bad_request', message: msg });
228
244
  return;
229
245
  }
230
246
  break;
231
247
  case 'ocr':
232
248
  if (!request.data.documentUrl && !request.data.imageUrl && !request.data.base64) {
233
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data must contain one of documentUrl, imageUrl, or base64' });
249
+ const msg = 'Missing required field: data must contain one of documentUrl, imageUrl, or base64';
250
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
251
+ res.status(400).json({ error: 'bad_request', message: msg });
234
252
  return;
235
253
  }
236
254
  break;
@@ -1 +1 @@
1
- {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/chat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAGpD,OAAO,KAAK,EAAE,YAAY,EAAiB,IAAI,EAA+C,MAAM,eAAe,CAAC;AAIpH,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAiBpC,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,IAAI,EAChB,OAAO,EAAE,YAAY,GACpB,YAAY,CAiFd;AAwWD,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/chat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAGpD,OAAO,KAAK,EAAE,YAAY,EAAiB,IAAI,EAA+C,MAAM,eAAe,CAAC;AAIpH,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAiBpC,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,IAAI,EAChB,OAAO,EAAE,YAAY,GACpB,YAAY,CAiFd;AAgXD,eAAe,MAAM,CAAC"}
@@ -195,22 +195,30 @@ router.post('/', async (req, res) => {
195
195
  try {
196
196
  const rawRequest = req.body;
197
197
  if (!rawRequest.gateId) {
198
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: gateId' });
198
+ const msg = 'Missing required field: gateId';
199
+ db.logRequest({ userId, gateId: null, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
200
+ res.status(400).json({ error: 'bad_request', message: msg });
199
201
  return;
200
202
  }
201
203
  const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(rawRequest.gateId);
202
204
  if (!isUUID) {
203
- res.status(400).json({ error: 'bad_request', message: 'gateId must be a valid UUID' });
205
+ const msg = 'gateId must be a valid UUID';
206
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
207
+ res.status(400).json({ error: 'bad_request', message: msg });
204
208
  return;
205
209
  }
206
210
  gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
207
211
  if (!gateConfig) {
208
- res.status(404).json({ error: 'not_found', message: `Gate with ID "${rawRequest.gateId}" not found` });
212
+ const msg = `Gate with ID "${rawRequest.gateId}" not found`;
213
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: rawRequest.model || null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
214
+ res.status(404).json({ error: 'not_found', message: msg });
209
215
  return;
210
216
  }
211
217
  // Validate chat-specific fields
212
218
  if (!rawRequest.data?.messages || !Array.isArray(rawRequest.data.messages) || rawRequest.data.messages.length === 0) {
213
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.messages (must be a non-empty array)' });
219
+ const msg = 'Missing required field: data.messages (must be a non-empty array)';
220
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
221
+ res.status(400).json({ error: 'bad_request', message: msg });
214
222
  return;
215
223
  }
216
224
  // Warn if gate is configured for a different task type
@@ -1 +1 @@
1
- {"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/embeddings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAMpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAmQpC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"embeddings.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/embeddings.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAMpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAiRpC,eAAe,MAAM,CAAC"}
@@ -95,37 +95,51 @@ router.post('/', async (req, res) => {
95
95
  try {
96
96
  const rawRequest = req.body;
97
97
  if (!rawRequest.gateId) {
98
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: gateId' });
98
+ const msg = 'Missing required field: gateId';
99
+ db.logRequest({ userId, gateId: null, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
100
+ res.status(400).json({ error: 'bad_request', message: msg });
99
101
  return;
100
102
  }
101
103
  const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(rawRequest.gateId);
102
104
  if (!isUUID) {
103
- res.status(400).json({ error: 'bad_request', message: 'gateId must be a valid UUID' });
105
+ const msg = 'gateId must be a valid UUID';
106
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
107
+ res.status(400).json({ error: 'bad_request', message: msg });
104
108
  return;
105
109
  }
106
110
  gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
107
111
  if (!gateConfig) {
108
- res.status(404).json({ error: 'not_found', message: `Gate with ID "${rawRequest.gateId}" not found` });
112
+ const msg = `Gate with ID "${rawRequest.gateId}" not found`;
113
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: rawRequest.model || null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
114
+ res.status(404).json({ error: 'not_found', message: msg });
109
115
  return;
110
116
  }
111
117
  // Validate embeddings-specific fields
112
118
  if (!rawRequest.data?.input) {
113
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.input' });
119
+ const msg = 'Missing required field: data.input';
120
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
121
+ res.status(400).json({ error: 'bad_request', message: msg });
114
122
  return;
115
123
  }
116
124
  // Validate input is string or array of strings
117
125
  const input = rawRequest.data.input;
118
126
  if (typeof input !== 'string' && !Array.isArray(input)) {
119
- res.status(400).json({ error: 'bad_request', message: 'data.input must be a string or array of strings' });
127
+ const msg = 'data.input must be a string or array of strings';
128
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
129
+ res.status(400).json({ error: 'bad_request', message: msg });
120
130
  return;
121
131
  }
122
132
  if (Array.isArray(input)) {
123
133
  if (input.length === 0) {
124
- res.status(400).json({ error: 'bad_request', message: 'data.input array must not be empty' });
134
+ const msg = 'data.input array must not be empty';
135
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
136
+ res.status(400).json({ error: 'bad_request', message: msg });
125
137
  return;
126
138
  }
127
139
  if (!input.every(item => typeof item === 'string')) {
128
- res.status(400).json({ error: 'bad_request', message: 'data.input array must contain only strings' });
140
+ const msg = 'data.input array must contain only strings';
141
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
142
+ res.status(400).json({ error: 'bad_request', message: msg });
129
143
  return;
130
144
  }
131
145
  }
@@ -1 +1 @@
1
- {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/image.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAMpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AA4PpC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/image.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAMpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAoQpC,eAAe,MAAM,CAAC"}
@@ -105,22 +105,30 @@ router.post('/', async (req, res) => {
105
105
  try {
106
106
  const rawRequest = req.body;
107
107
  if (!rawRequest.gateId) {
108
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: gateId' });
108
+ const msg = 'Missing required field: gateId';
109
+ db.logRequest({ userId, gateId: null, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
110
+ res.status(400).json({ error: 'bad_request', message: msg });
109
111
  return;
110
112
  }
111
113
  const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(rawRequest.gateId);
112
114
  if (!isUUID) {
113
- res.status(400).json({ error: 'bad_request', message: 'gateId must be a valid UUID' });
115
+ const msg = 'gateId must be a valid UUID';
116
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
117
+ res.status(400).json({ error: 'bad_request', message: msg });
114
118
  return;
115
119
  }
116
120
  gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
117
121
  if (!gateConfig) {
118
- res.status(404).json({ error: 'not_found', message: `Gate with ID "${rawRequest.gateId}" not found` });
122
+ const msg = `Gate with ID "${rawRequest.gateId}" not found`;
123
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: rawRequest.model || null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
124
+ res.status(404).json({ error: 'not_found', message: msg });
119
125
  return;
120
126
  }
121
127
  // Validate image-specific fields
122
128
  if (!rawRequest.data?.prompt || typeof rawRequest.data.prompt !== 'string' || rawRequest.data.prompt.trim().length === 0) {
123
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.prompt (must be a non-empty string)' });
129
+ const msg = 'Missing required field: data.prompt (must be a non-empty string)';
130
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
131
+ res.status(400).json({ error: 'bad_request', message: msg });
124
132
  return;
125
133
  }
126
134
  // Warn if gate is configured for a different task type
@@ -1 +1 @@
1
- {"version":3,"file":"ocr.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/ocr.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAMpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAqPpC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"ocr.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/ocr.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAMpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AA6PpC,eAAe,MAAM,CAAC"}
@@ -95,25 +95,33 @@ router.post('/', async (req, res) => {
95
95
  try {
96
96
  const rawRequest = req.body;
97
97
  if (!rawRequest.gateId) {
98
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: gateId' });
98
+ const msg = 'Missing required field: gateId';
99
+ db.logRequest({ userId, gateId: null, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
100
+ res.status(400).json({ error: 'bad_request', message: msg });
99
101
  return;
100
102
  }
101
103
  const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(rawRequest.gateId);
102
104
  if (!isUUID) {
103
- res.status(400).json({ error: 'bad_request', message: 'gateId must be a valid UUID' });
105
+ const msg = 'gateId must be a valid UUID';
106
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
107
+ res.status(400).json({ error: 'bad_request', message: msg });
104
108
  return;
105
109
  }
106
110
  gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
107
111
  if (!gateConfig) {
108
- res.status(404).json({ error: 'not_found', message: `Gate with ID "${rawRequest.gateId}" not found` });
112
+ const msg = `Gate with ID "${rawRequest.gateId}" not found`;
113
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: rawRequest.model || null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
114
+ res.status(404).json({ error: 'not_found', message: msg });
109
115
  return;
110
116
  }
111
117
  // Validate OCR-specific fields - must have one of documentUrl, imageUrl, or base64
112
118
  const data = rawRequest.data;
113
119
  if (!data || (!data.documentUrl && !data.imageUrl && !data.base64)) {
120
+ const msg = 'Missing required field: data must contain one of documentUrl, imageUrl, or base64';
121
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
114
122
  res.status(400).json({
115
123
  error: 'bad_request',
116
- message: 'Missing required field: data must contain one of documentUrl, imageUrl, or base64'
124
+ message: msg
117
125
  });
118
126
  return;
119
127
  }
@@ -1 +1 @@
1
- {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/tts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAMpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAiPpC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/tts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAMpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAyPpC,eAAe,MAAM,CAAC"}
@@ -95,22 +95,30 @@ router.post('/', async (req, res) => {
95
95
  try {
96
96
  const rawRequest = req.body;
97
97
  if (!rawRequest.gateId) {
98
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: gateId' });
98
+ const msg = 'Missing required field: gateId';
99
+ db.logRequest({ userId, gateId: null, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
100
+ res.status(400).json({ error: 'bad_request', message: msg });
99
101
  return;
100
102
  }
101
103
  const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(rawRequest.gateId);
102
104
  if (!isUUID) {
103
- res.status(400).json({ error: 'bad_request', message: 'gateId must be a valid UUID' });
105
+ const msg = 'gateId must be a valid UUID';
106
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
107
+ res.status(400).json({ error: 'bad_request', message: msg });
104
108
  return;
105
109
  }
106
110
  gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
107
111
  if (!gateConfig) {
108
- res.status(404).json({ error: 'not_found', message: `Gate with ID "${rawRequest.gateId}" not found` });
112
+ const msg = `Gate with ID "${rawRequest.gateId}" not found`;
113
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: rawRequest.model || null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
114
+ res.status(404).json({ error: 'not_found', message: msg });
109
115
  return;
110
116
  }
111
117
  // Validate TTS-specific fields
112
118
  if (!rawRequest.data?.input || typeof rawRequest.data.input !== 'string' || rawRequest.data.input.trim().length === 0) {
113
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.input (must be a non-empty string)' });
119
+ const msg = 'Missing required field: data.input (must be a non-empty string)';
120
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
121
+ res.status(400).json({ error: 'bad_request', message: msg });
114
122
  return;
115
123
  }
116
124
  // Warn if gate is configured for a different task type
@@ -1 +1 @@
1
- {"version":3,"file":"video.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/video.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAMpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AA4PpC,eAAe,MAAM,CAAC"}
1
+ {"version":3,"file":"video.d.ts","sourceRoot":"","sources":["../../../src/routes/v3/video.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAMpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAoQpC,eAAe,MAAM,CAAC"}
@@ -105,22 +105,30 @@ router.post('/', async (req, res) => {
105
105
  try {
106
106
  const rawRequest = req.body;
107
107
  if (!rawRequest.gateId) {
108
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: gateId' });
108
+ const msg = 'Missing required field: gateId';
109
+ db.logRequest({ userId, gateId: null, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
110
+ res.status(400).json({ error: 'bad_request', message: msg });
109
111
  return;
110
112
  }
111
113
  const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(rawRequest.gateId);
112
114
  if (!isUUID) {
113
- res.status(400).json({ error: 'bad_request', message: 'gateId must be a valid UUID' });
115
+ const msg = 'gateId must be a valid UUID';
116
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
117
+ res.status(400).json({ error: 'bad_request', message: msg });
114
118
  return;
115
119
  }
116
120
  gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
117
121
  if (!gateConfig) {
118
- res.status(404).json({ error: 'not_found', message: `Gate with ID "${rawRequest.gateId}" not found` });
122
+ const msg = `Gate with ID "${rawRequest.gateId}" not found`;
123
+ db.logRequest({ userId, gateId: rawRequest.gateId, gateName: null, modelRequested: rawRequest.model || null, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
124
+ res.status(404).json({ error: 'not_found', message: msg });
119
125
  return;
120
126
  }
121
127
  // Validate video-specific fields
122
128
  if (!rawRequest.data?.prompt || typeof rawRequest.data.prompt !== 'string' || rawRequest.data.prompt.trim().length === 0) {
123
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.prompt (must be a non-empty string)' });
129
+ const msg = 'Missing required field: data.prompt (must be a non-empty string)';
130
+ db.logRequest({ userId, gateId: gateConfig.id, gateName: gateConfig.name, modelRequested: rawRequest.model || gateConfig.model, modelUsed: null, promptTokens: 0, completionTokens: 0, totalTokens: 0, costUsd: 0, latencyMs: Date.now() - startTime, success: false, errorMessage: msg, userAgent: req.headers['user-agent'] || null, ipAddress: req.ip || null, requestPayload: rawRequest, responsePayload: null }).catch(() => { });
131
+ res.status(400).json({ error: 'bad_request', message: msg });
124
132
  return;
125
133
  }
126
134
  // Warn if gate is configured for a different task type
@@ -9,7 +9,7 @@ function getAnthropicClient(apiKey) {
9
9
  }
10
10
  if (!anthropic) {
11
11
  anthropic = new Anthropic({
12
- apiKey: process.env.ANTHROPIC_API_KEY,
12
+ apiKey: process.env.LAYER_PLATFORM_ANTHROPIC_API_KEY,
13
13
  });
14
14
  }
15
15
  return anthropic;
@@ -52,7 +52,7 @@ export class AnthropicAdapter extends BaseProviderAdapter {
52
52
  return super.mapToolChoice(choice);
53
53
  }
54
54
  async call(request, userId) {
55
- const resolved = await resolveApiKey(this.provider, userId, process.env.ANTHROPIC_API_KEY);
55
+ const resolved = await resolveApiKey(this.provider, userId, process.env.LAYER_PLATFORM_ANTHROPIC_API_KEY);
56
56
  switch (request.type) {
57
57
  case 'chat':
58
58
  return this.handleChat(request, resolved.key, resolved.usedPlatformKey);
@@ -69,7 +69,7 @@ export class AnthropicAdapter extends BaseProviderAdapter {
69
69
  }
70
70
  }
71
71
  async *callStream(request, userId) {
72
- const resolved = await resolveApiKey(this.provider, userId, process.env.ANTHROPIC_API_KEY);
72
+ const resolved = await resolveApiKey(this.provider, userId, process.env.LAYER_PLATFORM_ANTHROPIC_API_KEY);
73
73
  switch (request.type) {
74
74
  case 'chat':
75
75
  yield* this.handleChatStream(request, resolved.key, resolved.usedPlatformKey);
@@ -9,7 +9,7 @@ function getGoogleClient(apiKey) {
9
9
  return new GoogleGenAI({ apiKey });
10
10
  }
11
11
  if (!client) {
12
- client = new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY || '' });
12
+ client = new GoogleGenAI({ apiKey: process.env.LAYER_PLATFORM_GOOGLE_API_KEY || '' });
13
13
  }
14
14
  return client;
15
15
  }
@@ -56,7 +56,7 @@ export class GoogleAdapter extends BaseProviderAdapter {
56
56
  };
57
57
  }
58
58
  async call(request, userId) {
59
- const resolved = await resolveApiKey(this.provider, userId, process.env.GOOGLE_API_KEY);
59
+ const resolved = await resolveApiKey(this.provider, userId, process.env.LAYER_PLATFORM_GOOGLE_API_KEY);
60
60
  switch (request.type) {
61
61
  case 'chat':
62
62
  return this.handleChat(request, resolved.key, resolved.usedPlatformKey);
@@ -73,7 +73,7 @@ export class GoogleAdapter extends BaseProviderAdapter {
73
73
  }
74
74
  }
75
75
  async *callStream(request, userId) {
76
- const resolved = await resolveApiKey(this.provider, userId, process.env.GOOGLE_API_KEY);
76
+ const resolved = await resolveApiKey(this.provider, userId, process.env.LAYER_PLATFORM_GOOGLE_API_KEY);
77
77
  switch (request.type) {
78
78
  case 'chat':
79
79
  yield* this.handleChatStream(request, resolved.key, resolved.usedPlatformKey);
@@ -12,7 +12,7 @@ function getMistralClient(apiKey) {
12
12
  // Otherwise use singleton with platform key
13
13
  if (!client) {
14
14
  client = new Mistral({
15
- apiKey: process.env.MISTRAL_API_KEY || '',
15
+ apiKey: process.env.LAYER_PLATFORM_MISTRAL_API_KEY || '',
16
16
  });
17
17
  }
18
18
  return client;
@@ -46,7 +46,7 @@ export class MistralAdapter extends BaseProviderAdapter {
46
46
  }
47
47
  async call(request, userId) {
48
48
  // Resolve API key (BYOK → Platform key)
49
- const resolved = await resolveApiKey(this.provider, userId, process.env.MISTRAL_API_KEY);
49
+ const resolved = await resolveApiKey(this.provider, userId, process.env.LAYER_PLATFORM_MISTRAL_API_KEY);
50
50
  switch (request.type) {
51
51
  case 'chat':
52
52
  return this.handleChat(request, resolved.key, resolved.usedPlatformKey);
@@ -65,7 +65,7 @@ export class MistralAdapter extends BaseProviderAdapter {
65
65
  }
66
66
  }
67
67
  async *callStream(request, userId) {
68
- const resolved = await resolveApiKey(this.provider, userId, process.env.MISTRAL_API_KEY);
68
+ const resolved = await resolveApiKey(this.provider, userId, process.env.LAYER_PLATFORM_MISTRAL_API_KEY);
69
69
  switch (request.type) {
70
70
  case 'chat':
71
71
  yield* this.handleChatStream(request, resolved.key, resolved.usedPlatformKey);
@@ -9,7 +9,7 @@ function getOpenAIClient(apiKey) {
9
9
  }
10
10
  if (!openai) {
11
11
  openai = new OpenAI({
12
- apiKey: process.env.OPENAI_API_KEY,
12
+ apiKey: process.env.LAYER_PLATFORM_OPENAI_API_KEY,
13
13
  });
14
14
  }
15
15
  return openai;
@@ -71,7 +71,7 @@ export class OpenAIAdapter extends BaseProviderAdapter {
71
71
  };
72
72
  }
73
73
  async call(request, userId) {
74
- const resolved = await resolveApiKey(this.provider, userId, process.env.OPENAI_API_KEY);
74
+ const resolved = await resolveApiKey(this.provider, userId, process.env.LAYER_PLATFORM_OPENAI_API_KEY);
75
75
  switch (request.type) {
76
76
  case 'chat':
77
77
  return this.handleChat(request, resolved.key, resolved.usedPlatformKey);
@@ -88,7 +88,7 @@ export class OpenAIAdapter extends BaseProviderAdapter {
88
88
  }
89
89
  }
90
90
  async *callStream(request, userId) {
91
- const resolved = await resolveApiKey(this.provider, userId, process.env.OPENAI_API_KEY);
91
+ const resolved = await resolveApiKey(this.provider, userId, process.env.LAYER_PLATFORM_OPENAI_API_KEY);
92
92
  switch (request.type) {
93
93
  case 'chat':
94
94
  yield* this.handleChatStream(request, resolved.key, resolved.usedPlatformKey);
@@ -53,7 +53,7 @@ Return ONLY the task type as a single word, nothing else.`;
53
53
  }
54
54
  export async function analyzeTask(description, userPreferences) {
55
55
  const anthropic = new Anthropic({
56
- apiKey: process.env.ANTHROPIC_API_KEY
56
+ apiKey: process.env.LAYER_PLATFORM_ANTHROPIC_API_KEY
57
57
  });
58
58
  const costWeight = userPreferences?.costWeight ?? 0.33;
59
59
  const latencyWeight = userPreferences?.latencyWeight ?? 0.33;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layer-ai/core",
3
- "version": "2.0.49",
3
+ "version": "2.0.51",
4
4
  "description": "Core API routes and services for Layer AI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -34,6 +34,7 @@
34
34
  "ioredis": "^5.3.2",
35
35
  "jsonwebtoken": "^9.0.2",
36
36
  "nanoid": "^5.0.4",
37
+ "node-cron": "^4.2.1",
37
38
  "openai": "^4.24.0",
38
39
  "pg": "^8.11.3",
39
40
  "@layer-ai/sdk": "^2.5.14"
@@ -43,6 +44,7 @@
43
44
  "@types/express": "^4.17.21",
44
45
  "@types/jsonwebtoken": "^9.0.5",
45
46
  "@types/node": "^20.10.4",
47
+ "@types/node-cron": "^3.0.11",
46
48
  "@types/pg": "^8.10.9",
47
49
  "typescript": "^5.3.3"
48
50
  },