@layer-ai/core 2.0.50 → 2.0.52

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.
@@ -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;
@@ -10,12 +10,12 @@ class SpendingWorker {
10
10
  }
11
11
  start() {
12
12
  console.log('[Spending Worker] Starting scheduled jobs');
13
- // Sync Redis to DB every 5 minutes
14
- this.syncTask = cron.schedule('*/5 * * * *', async () => {
13
+ // Sync Redis to DB every 5 minutes (offset :02 to avoid other workers)
14
+ this.syncTask = cron.schedule('2/5 * * * *', async () => {
15
15
  await this.syncSpendingJob();
16
16
  });
17
- // Check for billing period resets every hour at :15
18
- this.resetTask = cron.schedule('15 * * * *', async () => {
17
+ // Check for billing period resets every hour at :20
18
+ this.resetTask = cron.schedule('20 * * * *', async () => {
19
19
  await this.resetSpendingPeriodsJob();
20
20
  await this.resetGateSpendingPeriodsJob();
21
21
  await this.resetUsageCountersJob();
@@ -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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layer-ai/core",
3
- "version": "2.0.50",
3
+ "version": "2.0.52",
4
4
  "description": "Core API routes and services for Layer AI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -37,7 +37,7 @@
37
37
  "node-cron": "^4.2.1",
38
38
  "openai": "^4.24.0",
39
39
  "pg": "^8.11.3",
40
- "@layer-ai/sdk": "^2.5.14"
40
+ "@layer-ai/sdk": "^2.5.15"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/bcryptjs": "^2.4.6",