@layer-ai/core 1.0.0 → 2.0.1

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
@@ -3,6 +3,7 @@ export { default as gatesRouter } from './routes/v1/gates.js';
3
3
  export { default as keysRouter } from './routes/v1/keys.js';
4
4
  export { default as logsRouter } from './routes/v1/logs.js';
5
5
  export { default as completeRouter } from './routes/v2/complete.js';
6
+ export { default as chatRouter } from './routes/v3/chat.js';
6
7
  export { authenticate } from './middleware/auth.js';
7
8
  export type {} from './middleware/auth.js';
8
9
  export { db } from './lib/db/postgres.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGpE,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAG3C,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC9E,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,eAAO,MAAM,gBAAgB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,MAAM,CAGrE,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,IAAI,CAG3E,CAAC;AAGF,cAAc,6BAA6B,CAAC;AAG5C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,WAAW,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC"}
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;AAG5D,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAGpE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAG5D,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAG3C,OAAO,EAAE,EAAE,EAAE,MAAM,sBAAsB,CAAC;AAC1C,OAAO,EAAE,OAAO,IAAI,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAGrD,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC9E,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,eAAO,MAAM,gBAAgB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,MAAM,CAGrE,CAAC;AAEF,eAAO,MAAM,wBAAwB,GAAU,QAAQ,MAAM,KAAG,OAAO,CAAC,IAAI,CAG3E,CAAC;AAGF,cAAc,6BAA6B,CAAC;AAG5C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,WAAW,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC"}
package/dist/index.js CHANGED
@@ -3,7 +3,10 @@ export { default as authRouter } from './routes/v1/auth.js';
3
3
  export { default as gatesRouter } from './routes/v1/gates.js';
4
4
  export { default as keysRouter } from './routes/v1/keys.js';
5
5
  export { default as logsRouter } from './routes/v1/logs.js';
6
+ // v2 routes
6
7
  export { default as completeRouter } from './routes/v2/complete.js';
8
+ // v3 routes
9
+ export { default as chatRouter } from './routes/v3/chat.js';
7
10
  // Middleware
8
11
  export { authenticate } from './middleware/auth.js';
9
12
  // Database
@@ -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;AA+OpC,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;AAkVpC,eAAe,MAAM,CAAC"}
@@ -13,7 +13,6 @@ function isOverrideAllowed(allowOverrides, field) {
13
13
  return allowOverrides[field] ?? false;
14
14
  }
15
15
  function resolveFinalRequest(gateConfig, request) {
16
- const finalRequest = { ...request };
17
16
  let finalModel = gateConfig.model;
18
17
  if (request.model && isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Model)) {
19
18
  try {
@@ -23,33 +22,85 @@ function resolveFinalRequest(gateConfig, request) {
23
22
  finalModel = gateConfig.model;
24
23
  }
25
24
  }
26
- finalRequest.model = normalizeModelId(finalModel);
27
- if (request.type === 'chat') {
28
- const chatData = { ...request.data };
29
- if (!chatData.systemPrompt && gateConfig.systemPrompt) {
30
- chatData.systemPrompt = gateConfig.systemPrompt;
25
+ // Use discriminated union to handle each request type
26
+ switch (request.type) {
27
+ case 'chat': {
28
+ const chatData = { ...request.data };
29
+ if (!chatData.systemPrompt && gateConfig.systemPrompt) {
30
+ chatData.systemPrompt = gateConfig.systemPrompt;
31
+ }
32
+ if (chatData.temperature === undefined && gateConfig.temperature !== undefined) {
33
+ chatData.temperature = gateConfig.temperature;
34
+ }
35
+ else if (chatData.temperature !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Temperature)) {
36
+ chatData.temperature = gateConfig.temperature;
37
+ }
38
+ if (chatData.maxTokens === undefined && gateConfig.maxTokens !== undefined) {
39
+ chatData.maxTokens = gateConfig.maxTokens;
40
+ }
41
+ else if (chatData.maxTokens !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.MaxTokens)) {
42
+ chatData.maxTokens = gateConfig.maxTokens;
43
+ }
44
+ if (chatData.topP === undefined && gateConfig.topP !== undefined) {
45
+ chatData.topP = gateConfig.topP;
46
+ }
47
+ else if (chatData.topP !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.TopP)) {
48
+ chatData.topP = gateConfig.topP;
49
+ }
50
+ return {
51
+ ...request,
52
+ model: normalizeModelId(finalModel),
53
+ data: chatData,
54
+ };
31
55
  }
32
- if (chatData.temperature === undefined && gateConfig.temperature !== undefined) {
33
- chatData.temperature = gateConfig.temperature;
56
+ case 'image': {
57
+ // TODO: Future enhancement - intelligently apply gate-level defaults
58
+ // Potential features:
59
+ // - Apply systemPrompt by intelligently merging with user prompt
60
+ // - Support defaults for size, quality, style, count
61
+ // - Make this opt-in with a gate setting (e.g., applySystemPromptToAllTasks)
62
+ return {
63
+ ...request,
64
+ model: normalizeModelId(finalModel),
65
+ };
34
66
  }
35
- else if (chatData.temperature !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Temperature)) {
36
- chatData.temperature = gateConfig.temperature;
67
+ case 'video': {
68
+ // TODO: Future enhancement - intelligently apply gate-level defaults
69
+ // Similar to image generation, could support systemPrompt integration
70
+ // and video-specific defaults (duration, size, fps)
71
+ return {
72
+ ...request,
73
+ model: normalizeModelId(finalModel),
74
+ };
37
75
  }
38
- if (chatData.maxTokens === undefined && gateConfig.maxTokens !== undefined) {
39
- chatData.maxTokens = gateConfig.maxTokens;
76
+ case 'embeddings': {
77
+ // Embeddings are deterministic and don't typically need gate-level defaults
78
+ // beyond model selection (already handled)
79
+ return {
80
+ ...request,
81
+ model: normalizeModelId(finalModel),
82
+ };
40
83
  }
41
- else if (chatData.maxTokens !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.MaxTokens)) {
42
- chatData.maxTokens = gateConfig.maxTokens;
84
+ case 'tts': {
85
+ // TODO: Future enhancement - support defaults for voice, speed, format
86
+ return {
87
+ ...request,
88
+ model: normalizeModelId(finalModel),
89
+ };
43
90
  }
44
- if (chatData.topP === undefined && gateConfig.topP !== undefined) {
45
- chatData.topP = gateConfig.topP;
91
+ case 'ocr': {
92
+ // TODO: Future enhancement - support defaults for tableFormat, includeImageBase64, etc.
93
+ return {
94
+ ...request,
95
+ model: normalizeModelId(finalModel),
96
+ };
46
97
  }
47
- else if (chatData.topP !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.TopP)) {
48
- chatData.topP = gateConfig.topP;
98
+ default: {
99
+ // This will cause a TypeScript error if we miss a case
100
+ const exhaustiveCheck = request;
101
+ throw new Error(`Unhandled request type: ${exhaustiveCheck.type}`);
49
102
  }
50
- finalRequest.data = chatData;
51
103
  }
52
- return finalRequest;
53
104
  }
54
105
  function getModelsToTry(gateConfig, primaryModel) {
55
106
  const modelsToTry = [primaryModel];
@@ -131,7 +182,13 @@ router.post('/', authenticate, async (req, res) => {
131
182
  res.status(404).json({ error: 'not_found', message: `Gate with ID "${rawRequest.gateId}" not found` });
132
183
  return;
133
184
  }
134
- const requestType = rawRequest.type || gateConfig.taskType || 'chat';
185
+ // Default to gate's taskType, allow override via request.type
186
+ const requestType = rawRequest.type || gateConfig.taskType;
187
+ // Warn if request type doesn't match gate's taskType (possible misconfiguration)
188
+ if (rawRequest.type && gateConfig.taskType && rawRequest.type !== gateConfig.taskType) {
189
+ console.warn(`[Type Mismatch] Gate "${gateConfig.name}" (${gateConfig.id}) configured for taskType="${gateConfig.taskType}" ` +
190
+ `but received request with type="${rawRequest.type}". Using request type as override.`);
191
+ }
135
192
  request = {
136
193
  gateId: rawRequest.gateId,
137
194
  type: requestType,
@@ -139,11 +196,44 @@ router.post('/', authenticate, async (req, res) => {
139
196
  model: rawRequest.model,
140
197
  metadata: rawRequest.metadata
141
198
  };
142
- if (request.type === 'chat') {
143
- if (!request.data.messages || !Array.isArray(request.data.messages) || request.data.messages.length === 0) {
144
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.messages' });
145
- return;
146
- }
199
+ // Validate required fields based on request type
200
+ switch (request.type) {
201
+ case 'chat':
202
+ 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' });
204
+ return;
205
+ }
206
+ break;
207
+ case 'image':
208
+ if (!request.data.prompt) {
209
+ res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.prompt' });
210
+ return;
211
+ }
212
+ break;
213
+ case 'video':
214
+ if (!request.data.prompt) {
215
+ res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.prompt' });
216
+ return;
217
+ }
218
+ break;
219
+ case 'embeddings':
220
+ if (!request.data.input) {
221
+ res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.input' });
222
+ return;
223
+ }
224
+ break;
225
+ case 'tts':
226
+ if (!request.data.input) {
227
+ res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.input' });
228
+ return;
229
+ }
230
+ break;
231
+ case 'ocr':
232
+ 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' });
234
+ return;
235
+ }
236
+ break;
147
237
  }
148
238
  const finalRequest = resolveFinalRequest(gateConfig, request);
149
239
  const { result, modelUsed } = await executeWithRouting(gateConfig, finalRequest, userId);
@@ -0,0 +1,4 @@
1
+ import type { Router as RouterType } from 'express';
2
+ declare const router: RouterType;
3
+ export default router;
4
+ //# sourceMappingURL=chat.d.ts.map
@@ -0,0 +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;AAOpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAqPpC,eAAe,MAAM,CAAC"}
@@ -0,0 +1,203 @@
1
+ import { Router } from 'express';
2
+ import { db } from '../../lib/db/postgres.js';
3
+ import { authenticate } from '../../middleware/auth.js';
4
+ import { callAdapter, normalizeModelId } from '../../lib/provider-factory.js';
5
+ import { OverrideField } from '@layer-ai/sdk';
6
+ const router = Router();
7
+ // MARK:- Helper Functions
8
+ function isOverrideAllowed(allowOverrides, field) {
9
+ if (allowOverrides === undefined || allowOverrides === null || allowOverrides === true)
10
+ return true;
11
+ if (allowOverrides === false)
12
+ return false;
13
+ return allowOverrides[field] ?? false;
14
+ }
15
+ function resolveFinalRequest(gateConfig, request) {
16
+ let finalModel = gateConfig.model;
17
+ if (request.model && isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Model)) {
18
+ try {
19
+ finalModel = normalizeModelId(request.model);
20
+ }
21
+ catch {
22
+ finalModel = gateConfig.model;
23
+ }
24
+ }
25
+ // Since this is v3/chat endpoint, we know the data is ChatRequest
26
+ const chatData = { ...request.data };
27
+ if (!chatData.systemPrompt && gateConfig.systemPrompt) {
28
+ chatData.systemPrompt = gateConfig.systemPrompt;
29
+ }
30
+ if (chatData.temperature === undefined && gateConfig.temperature !== undefined) {
31
+ chatData.temperature = gateConfig.temperature;
32
+ }
33
+ else if (chatData.temperature !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Temperature)) {
34
+ chatData.temperature = gateConfig.temperature;
35
+ }
36
+ if (chatData.maxTokens === undefined && gateConfig.maxTokens !== undefined) {
37
+ chatData.maxTokens = gateConfig.maxTokens;
38
+ }
39
+ else if (chatData.maxTokens !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.MaxTokens)) {
40
+ chatData.maxTokens = gateConfig.maxTokens;
41
+ }
42
+ if (chatData.topP === undefined && gateConfig.topP !== undefined) {
43
+ chatData.topP = gateConfig.topP;
44
+ }
45
+ else if (chatData.topP !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.TopP)) {
46
+ chatData.topP = gateConfig.topP;
47
+ }
48
+ return {
49
+ ...request,
50
+ type: 'chat',
51
+ model: normalizeModelId(finalModel),
52
+ data: chatData,
53
+ };
54
+ }
55
+ function getModelsToTry(gateConfig, primaryModel) {
56
+ const modelsToTry = [primaryModel];
57
+ if (gateConfig.routingStrategy === 'fallback' && gateConfig.fallbackModels?.length) {
58
+ modelsToTry.push(...gateConfig.fallbackModels);
59
+ }
60
+ return modelsToTry;
61
+ }
62
+ async function executeWithFallback(request, modelsToTry, userId) {
63
+ let result = null;
64
+ let lastError = null;
65
+ let modelUsed = request.model;
66
+ for (const modelToTry of modelsToTry) {
67
+ try {
68
+ const modelRequest = { ...request, model: modelToTry };
69
+ result = await callAdapter(modelRequest, userId);
70
+ modelUsed = modelToTry;
71
+ break;
72
+ }
73
+ catch (error) {
74
+ lastError = error;
75
+ console.log(`Model ${modelToTry} failed, trying next fallback...`, error instanceof Error ? error.message : error);
76
+ continue;
77
+ }
78
+ }
79
+ if (!result) {
80
+ throw lastError || new Error('All models failed');
81
+ }
82
+ return { result, modelUsed };
83
+ }
84
+ async function executeWithRoundRobin(gateConfig, request, userId) {
85
+ if (!gateConfig.fallbackModels?.length) {
86
+ const result = await callAdapter(request, userId);
87
+ return { result, modelUsed: request.model };
88
+ }
89
+ const allModels = [gateConfig.model, ...gateConfig.fallbackModels];
90
+ const modelIndex = Math.floor(Math.random() * allModels.length);
91
+ const selectedModel = allModels[modelIndex];
92
+ const modelRequest = { ...request, model: selectedModel };
93
+ const result = await callAdapter(modelRequest, userId);
94
+ return { result, modelUsed: selectedModel };
95
+ }
96
+ async function executeWithRouting(gateConfig, request, userId) {
97
+ const modelsToTry = getModelsToTry(gateConfig, request.model);
98
+ switch (gateConfig.routingStrategy) {
99
+ case 'fallback':
100
+ return await executeWithFallback(request, modelsToTry, userId);
101
+ case 'round-robin':
102
+ return await executeWithRoundRobin(gateConfig, request, userId);
103
+ case 'single':
104
+ default:
105
+ const result = await callAdapter(request, userId);
106
+ return { result, modelUsed: request.model };
107
+ }
108
+ }
109
+ // MARK:- Route Handler
110
+ router.post('/', authenticate, async (req, res) => {
111
+ const startTime = Date.now();
112
+ if (!req.userId) {
113
+ res.status(401).json({ error: 'unauthorized', message: 'Missing user ID' });
114
+ return;
115
+ }
116
+ const userId = req.userId;
117
+ let gateConfig = null;
118
+ let request = null;
119
+ try {
120
+ const rawRequest = req.body;
121
+ if (!rawRequest.gateId) {
122
+ res.status(400).json({ error: 'bad_request', message: 'Missing required field: gateId' });
123
+ return;
124
+ }
125
+ 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);
126
+ if (!isUUID) {
127
+ res.status(400).json({ error: 'bad_request', message: 'gateId must be a valid UUID' });
128
+ return;
129
+ }
130
+ gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
131
+ if (!gateConfig) {
132
+ res.status(404).json({ error: 'not_found', message: `Gate with ID "${rawRequest.gateId}" not found` });
133
+ return;
134
+ }
135
+ // Validate chat-specific fields
136
+ if (!rawRequest.data?.messages || !Array.isArray(rawRequest.data.messages) || rawRequest.data.messages.length === 0) {
137
+ res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.messages (must be a non-empty array)' });
138
+ return;
139
+ }
140
+ // Warn if gate is configured for a different task type
141
+ if (gateConfig.taskType && gateConfig.taskType !== 'chat') {
142
+ console.warn(`[Type Mismatch] Gate "${gateConfig.name}" (${gateConfig.id}) configured for taskType="${gateConfig.taskType}" ` +
143
+ `but received request to /v3/chat endpoint. Processing as chat request.`);
144
+ }
145
+ request = {
146
+ gateId: rawRequest.gateId,
147
+ type: 'chat',
148
+ data: rawRequest.data,
149
+ model: rawRequest.model,
150
+ metadata: rawRequest.metadata
151
+ };
152
+ const finalRequest = resolveFinalRequest(gateConfig, request);
153
+ const { result, modelUsed } = await executeWithRouting(gateConfig, finalRequest, userId);
154
+ const latencyMs = Date.now() - startTime;
155
+ // Log request to database
156
+ db.logRequest({
157
+ userId,
158
+ gateId: gateConfig.id,
159
+ gateName: gateConfig.name,
160
+ modelRequested: request.model || gateConfig.model,
161
+ modelUsed: modelUsed,
162
+ promptTokens: result.usage?.promptTokens || 0,
163
+ completionTokens: result.usage?.completionTokens || 0,
164
+ totalTokens: result.usage?.totalTokens || 0,
165
+ costUsd: result.cost || 0,
166
+ latencyMs,
167
+ success: true,
168
+ errorMessage: null,
169
+ userAgent: req.headers['user-agent'] || null,
170
+ ipAddress: req.ip || null,
171
+ }).catch(err => console.error('Failed to log request:', err));
172
+ // Return LayerResponse with additional metadata
173
+ const response = {
174
+ ...result,
175
+ model: modelUsed,
176
+ latencyMs,
177
+ };
178
+ res.json(response);
179
+ }
180
+ catch (error) {
181
+ const latencyMs = Date.now() - startTime;
182
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
183
+ db.logRequest({
184
+ userId,
185
+ gateId: gateConfig?.id || null,
186
+ gateName: req.body?.gate || null,
187
+ modelRequested: (request?.model || gateConfig?.model) || 'unknown',
188
+ modelUsed: null,
189
+ promptTokens: 0,
190
+ completionTokens: 0,
191
+ totalTokens: 0,
192
+ costUsd: 0,
193
+ latencyMs,
194
+ success: false,
195
+ errorMessage,
196
+ userAgent: req.headers['user-agent'] || null,
197
+ ipAddress: req.ip || null,
198
+ }).catch(err => console.error('Failed to log request:', err));
199
+ console.error('Chat completion error:', error);
200
+ res.status(500).json({ error: 'internal_error', message: errorMessage });
201
+ }
202
+ });
203
+ export default router;
@@ -0,0 +1,4 @@
1
+ import type { Router as RouterType } from 'express';
2
+ declare const router: RouterType;
3
+ export default router;
4
+ //# sourceMappingURL=chat.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat.d.ts","sourceRoot":"","sources":["../../../../src/routes/v3/completions/chat.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAOpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAoNpC,eAAe,MAAM,CAAC"}
@@ -0,0 +1,178 @@
1
+ import { Router } from 'express';
2
+ import { db } from '../../../lib/db/postgres.js';
3
+ import { authenticate } from '../../../middleware/auth.js';
4
+ import { callAdapter, normalizeModelId } from '../../../lib/provider-factory.js';
5
+ import { OverrideField } from '@layer-ai/sdk';
6
+ const router = Router();
7
+ function isOverrideAllowed(allowOverrides, field) {
8
+ if (allowOverrides === undefined || allowOverrides === null || allowOverrides === true)
9
+ return true;
10
+ if (allowOverrides === false)
11
+ return false;
12
+ return allowOverrides[field] ?? false;
13
+ }
14
+ function resolveFinalRequest(gateConfig, request) {
15
+ let finalModel = gateConfig.model;
16
+ if (request.model && isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Model)) {
17
+ try {
18
+ finalModel = normalizeModelId(request.model);
19
+ }
20
+ catch {
21
+ finalModel = gateConfig.model;
22
+ }
23
+ }
24
+ const chatData = { ...request.data };
25
+ if (!chatData.systemPrompt && gateConfig.systemPrompt) {
26
+ chatData.systemPrompt = gateConfig.systemPrompt;
27
+ }
28
+ if (chatData.temperature === undefined && gateConfig.temperature !== undefined) {
29
+ chatData.temperature = gateConfig.temperature;
30
+ }
31
+ else if (chatData.temperature !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Temperature)) {
32
+ chatData.temperature = gateConfig.temperature;
33
+ }
34
+ if (chatData.maxTokens === undefined && gateConfig.maxTokens !== undefined) {
35
+ chatData.maxTokens = gateConfig.maxTokens;
36
+ }
37
+ else if (chatData.maxTokens !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.MaxTokens)) {
38
+ chatData.maxTokens = gateConfig.maxTokens;
39
+ }
40
+ if (chatData.topP === undefined && gateConfig.topP !== undefined) {
41
+ chatData.topP = gateConfig.topP;
42
+ }
43
+ else if (chatData.topP !== undefined && !isOverrideAllowed(gateConfig.allowOverrides, OverrideField.TopP)) {
44
+ chatData.topP = gateConfig.topP;
45
+ }
46
+ return {
47
+ ...request,
48
+ model: normalizeModelId(finalModel),
49
+ data: chatData,
50
+ };
51
+ }
52
+ function getModelsToTry(gateConfig, primaryModel) {
53
+ const modelsToTry = [primaryModel];
54
+ if (gateConfig.fallback1) {
55
+ try {
56
+ modelsToTry.push(normalizeModelId(gateConfig.fallback1));
57
+ }
58
+ catch (error) {
59
+ console.error(`Invalid fallback1 model: ${gateConfig.fallback1}`);
60
+ }
61
+ }
62
+ if (gateConfig.fallback2) {
63
+ try {
64
+ modelsToTry.push(normalizeModelId(gateConfig.fallback2));
65
+ }
66
+ catch (error) {
67
+ console.error(`Invalid fallback2 model: ${gateConfig.fallback2}`);
68
+ }
69
+ }
70
+ return modelsToTry;
71
+ }
72
+ async function executeWithRouting(gateConfig, request, userId) {
73
+ const modelsToTry = getModelsToTry(gateConfig, request.model);
74
+ let lastError = null;
75
+ for (let i = 0; i < modelsToTry.length; i++) {
76
+ const model = modelsToTry[i];
77
+ const requestWithModel = {
78
+ ...request,
79
+ model,
80
+ };
81
+ try {
82
+ const result = await callAdapter(requestWithModel, userId);
83
+ return { result, modelUsed: model };
84
+ }
85
+ catch (error) {
86
+ console.error(`Model ${model} failed (attempt ${i + 1}/${modelsToTry.length}):`, error);
87
+ lastError = error;
88
+ if (i < modelsToTry.length - 1) {
89
+ console.log(`Trying fallback model: ${modelsToTry[i + 1]}`);
90
+ }
91
+ }
92
+ }
93
+ throw lastError || new Error('All models failed');
94
+ }
95
+ router.post('/', authenticate, async (req, res) => {
96
+ const startTime = Date.now();
97
+ if (!req.userId) {
98
+ res.status(401).json({ error: 'unauthorized', message: 'Missing user ID' });
99
+ return;
100
+ }
101
+ const userId = req.userId;
102
+ let gateConfig = null;
103
+ try {
104
+ const rawRequest = req.body;
105
+ if (!rawRequest.gateId) {
106
+ res.status(400).json({ error: 'bad_request', message: 'Missing required field: gateId' });
107
+ return;
108
+ }
109
+ if (!rawRequest.data || !rawRequest.data.messages || !Array.isArray(rawRequest.data.messages) || rawRequest.data.messages.length === 0) {
110
+ res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.messages (must be a non-empty array)' });
111
+ return;
112
+ }
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);
114
+ if (!isUUID) {
115
+ res.status(400).json({ error: 'bad_request', message: 'gateId must be a valid UUID' });
116
+ return;
117
+ }
118
+ gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
119
+ if (!gateConfig) {
120
+ res.status(404).json({ error: 'not_found', message: `Gate with ID "${rawRequest.gateId}" not found` });
121
+ return;
122
+ }
123
+ if (gateConfig.taskType && gateConfig.taskType !== 'chat') {
124
+ console.warn(`[Type Mismatch] Gate "${gateConfig.name}" (${gateConfig.id}) configured for taskType="${gateConfig.taskType}" ` +
125
+ `but received request to /v3/chat endpoint. Processing as chat request.`);
126
+ }
127
+ const request = {
128
+ gateId: rawRequest.gateId,
129
+ type: 'chat',
130
+ data: rawRequest.data,
131
+ model: rawRequest.model,
132
+ metadata: rawRequest.metadata
133
+ };
134
+ const finalRequest = resolveFinalRequest(gateConfig, request);
135
+ const { result, modelUsed } = await executeWithRouting(gateConfig, finalRequest, userId);
136
+ const latencyMs = Date.now() - startTime;
137
+ db.logRequest({
138
+ userId,
139
+ gateId: gateConfig.id,
140
+ model: modelUsed,
141
+ promptTokens: result.usage?.promptTokens || 0,
142
+ completionTokens: result.usage?.completionTokens || 0,
143
+ totalTokens: result.usage?.totalTokens || 0,
144
+ cost: result.cost || 0,
145
+ latencyMs,
146
+ success: true,
147
+ }).catch(error => {
148
+ console.error('Failed to log request:', error);
149
+ });
150
+ res.status(200).json(result);
151
+ }
152
+ catch (error) {
153
+ const latencyMs = Date.now() - startTime;
154
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
155
+ console.error('[v3/chat] Error processing request:', error);
156
+ if (gateConfig) {
157
+ db.logRequest({
158
+ userId,
159
+ gateId: gateConfig.id,
160
+ model: gateConfig.model,
161
+ promptTokens: 0,
162
+ completionTokens: 0,
163
+ totalTokens: 0,
164
+ cost: 0,
165
+ latencyMs,
166
+ success: false,
167
+ errorMessage,
168
+ }).catch(logError => {
169
+ console.error('Failed to log error:', logError);
170
+ });
171
+ }
172
+ res.status(500).json({
173
+ error: 'internal_server_error',
174
+ message: errorMessage
175
+ });
176
+ }
177
+ });
178
+ export default router;
@@ -0,0 +1,4 @@
1
+ import type { Router as RouterType } from 'express';
2
+ declare const router: RouterType;
3
+ export default router;
4
+ //# sourceMappingURL=embed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embed.d.ts","sourceRoot":"","sources":["../../../../src/routes/v3/completions/embed.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAOpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAgGpC,eAAe,MAAM,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { Router } from 'express';
2
+ import { db } from '../../../lib/db/postgres.js';
3
+ import { authenticate } from '../../../middleware/auth.js';
4
+ import { callAdapter, normalizeModelId } from '../../../lib/provider-factory.js';
5
+ import { OverrideField } from '@layer-ai/sdk';
6
+ const router = Router();
7
+ function isOverrideAllowed(allowOverrides, field) {
8
+ if (allowOverrides === undefined || allowOverrides === null || allowOverrides === true)
9
+ return true;
10
+ if (allowOverrides === false)
11
+ return false;
12
+ return allowOverrides[field] ?? false;
13
+ }
14
+ function resolveFinalRequest(gateConfig, request) {
15
+ let finalModel = gateConfig.model;
16
+ if (request.model && isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Model)) {
17
+ try {
18
+ finalModel = normalizeModelId(request.model);
19
+ }
20
+ catch {
21
+ finalModel = gateConfig.model;
22
+ }
23
+ }
24
+ return { ...request, model: normalizeModelId(finalModel) };
25
+ }
26
+ function getModelsToTry(gateConfig, primaryModel) {
27
+ const modelsToTry = [primaryModel];
28
+ if (gateConfig.fallback1) {
29
+ try {
30
+ modelsToTry.push(normalizeModelId(gateConfig.fallback1));
31
+ }
32
+ catch (error) { }
33
+ }
34
+ if (gateConfig.fallback2) {
35
+ try {
36
+ modelsToTry.push(normalizeModelId(gateConfig.fallback2));
37
+ }
38
+ catch (error) { }
39
+ }
40
+ return modelsToTry;
41
+ }
42
+ async function executeWithRouting(gateConfig, request, userId) {
43
+ const modelsToTry = getModelsToTry(gateConfig, request.model);
44
+ let lastError = null;
45
+ for (let i = 0; i < modelsToTry.length; i++) {
46
+ const model = modelsToTry[i];
47
+ try {
48
+ const result = await callAdapter({ ...request, model }, userId);
49
+ return { result, modelUsed: model };
50
+ }
51
+ catch (error) {
52
+ lastError = error;
53
+ }
54
+ }
55
+ throw lastError || new Error('All models failed');
56
+ }
57
+ router.post('/', authenticate, async (req, res) => {
58
+ const startTime = Date.now();
59
+ if (!req.userId) {
60
+ res.status(401).json({ error: 'unauthorized', message: 'Missing user ID' });
61
+ return;
62
+ }
63
+ const userId = req.userId;
64
+ let gateConfig = null;
65
+ try {
66
+ const rawRequest = req.body;
67
+ if (!rawRequest.gateId || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(rawRequest.gateId)) {
68
+ res.status(400).json({ error: 'bad_request', message: 'Valid gateId (UUID) is required' });
69
+ return;
70
+ }
71
+ if (!rawRequest.data?.prompt || typeof rawRequest.data.input !== 'string' || !rawRequest.data.input.trim()) {
72
+ res.status(400).json({ error: 'bad_request', message: 'data.input is required' });
73
+ return;
74
+ }
75
+ gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
76
+ if (!gateConfig) {
77
+ res.status(404).json({ error: 'not_found', message: `Gate not found` });
78
+ return;
79
+ }
80
+ const request = { gateId: rawRequest.gateId, type: 'embeddings', data: rawRequest.data, model: rawRequest.model, metadata: rawRequest.metadata };
81
+ const finalRequest = resolveFinalRequest(gateConfig, request);
82
+ const { result, modelUsed } = await executeWithRouting(gateConfig, finalRequest, userId);
83
+ db.logRequest({ userId, gateId: gateConfig.id, model: modelUsed, promptTokens: result.usage?.promptTokens || 0, completionTokens: result.usage?.completionTokens || 0, totalTokens: result.usage?.totalTokens || 0, cost: result.cost || 0, latencyMs: Date.now() - startTime, success: true }).catch(() => { });
84
+ res.status(200).json(result);
85
+ }
86
+ catch (error) {
87
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
88
+ if (gateConfig) {
89
+ db.logRequest({ userId, gateId: gateConfig.id, model: gateConfig.model, promptTokens: 0, completionTokens: 0, totalTokens: 0, cost: 0, latencyMs: Date.now() - startTime, success: false, errorMessage }).catch(() => { });
90
+ }
91
+ res.status(500).json({ error: 'internal_server_error', message: errorMessage });
92
+ }
93
+ });
94
+ export default router;
@@ -0,0 +1,4 @@
1
+ import type { Router as RouterType } from 'express';
2
+ declare const router: RouterType;
3
+ export default router;
4
+ //# sourceMappingURL=image.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../../src/routes/v3/completions/image.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAOpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AA2LpC,eAAe,MAAM,CAAC"}
@@ -0,0 +1,155 @@
1
+ import { Router } from 'express';
2
+ import { db } from '../../../lib/db/postgres.js';
3
+ import { authenticate } from '../../../middleware/auth.js';
4
+ import { callAdapter, normalizeModelId } from '../../../lib/provider-factory.js';
5
+ import { OverrideField } from '@layer-ai/sdk';
6
+ const router = Router();
7
+ function isOverrideAllowed(allowOverrides, field) {
8
+ if (allowOverrides === undefined || allowOverrides === null || allowOverrides === true)
9
+ return true;
10
+ if (allowOverrides === false)
11
+ return false;
12
+ return allowOverrides[field] ?? false;
13
+ }
14
+ function resolveFinalRequest(gateConfig, request) {
15
+ let finalModel = gateConfig.model;
16
+ if (request.model && isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Model)) {
17
+ try {
18
+ finalModel = normalizeModelId(request.model);
19
+ }
20
+ catch {
21
+ finalModel = gateConfig.model;
22
+ }
23
+ }
24
+ return {
25
+ ...request,
26
+ model: normalizeModelId(finalModel),
27
+ };
28
+ }
29
+ function getModelsToTry(gateConfig, primaryModel) {
30
+ const modelsToTry = [primaryModel];
31
+ if (gateConfig.fallback1) {
32
+ try {
33
+ modelsToTry.push(normalizeModelId(gateConfig.fallback1));
34
+ }
35
+ catch (error) {
36
+ console.error(`Invalid fallback1 model: ${gateConfig.fallback1}`);
37
+ }
38
+ }
39
+ if (gateConfig.fallback2) {
40
+ try {
41
+ modelsToTry.push(normalizeModelId(gateConfig.fallback2));
42
+ }
43
+ catch (error) {
44
+ console.error(`Invalid fallback2 model: ${gateConfig.fallback2}`);
45
+ }
46
+ }
47
+ return modelsToTry;
48
+ }
49
+ async function executeWithRouting(gateConfig, request, userId) {
50
+ const modelsToTry = getModelsToTry(gateConfig, request.model);
51
+ let lastError = null;
52
+ for (let i = 0; i < modelsToTry.length; i++) {
53
+ const model = modelsToTry[i];
54
+ const requestWithModel = {
55
+ ...request,
56
+ model,
57
+ };
58
+ try {
59
+ const result = await callAdapter(requestWithModel, userId);
60
+ return { result, modelUsed: model };
61
+ }
62
+ catch (error) {
63
+ console.error(`Model ${model} failed (attempt ${i + 1}/${modelsToTry.length}):`, error);
64
+ lastError = error;
65
+ if (i < modelsToTry.length - 1) {
66
+ console.log(`Trying fallback model: ${modelsToTry[i + 1]}`);
67
+ }
68
+ }
69
+ }
70
+ throw lastError || new Error('All models failed');
71
+ }
72
+ router.post('/', authenticate, async (req, res) => {
73
+ const startTime = Date.now();
74
+ if (!req.userId) {
75
+ res.status(401).json({ error: 'unauthorized', message: 'Missing user ID' });
76
+ return;
77
+ }
78
+ const userId = req.userId;
79
+ let gateConfig = null;
80
+ try {
81
+ const rawRequest = req.body;
82
+ if (!rawRequest.gateId) {
83
+ res.status(400).json({ error: 'bad_request', message: 'Missing required field: gateId' });
84
+ return;
85
+ }
86
+ if (!rawRequest.data || !rawRequest.data.prompt || typeof rawRequest.data.prompt !== 'string' || rawRequest.data.prompt.trim().length === 0) {
87
+ res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.prompt (must be a non-empty string)' });
88
+ return;
89
+ }
90
+ 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);
91
+ if (!isUUID) {
92
+ res.status(400).json({ error: 'bad_request', message: 'gateId must be a valid UUID' });
93
+ return;
94
+ }
95
+ gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
96
+ if (!gateConfig) {
97
+ res.status(404).json({ error: 'not_found', message: `Gate with ID "${rawRequest.gateId}" not found` });
98
+ return;
99
+ }
100
+ if (gateConfig.taskType && gateConfig.taskType !== 'image') {
101
+ console.warn(`[Type Mismatch] Gate "${gateConfig.name}" (${gateConfig.id}) configured for taskType="${gateConfig.taskType}" ` +
102
+ `but received request to /v3/image endpoint. Processing as image request.`);
103
+ }
104
+ const request = {
105
+ gateId: rawRequest.gateId,
106
+ type: 'image',
107
+ data: rawRequest.data,
108
+ model: rawRequest.model,
109
+ metadata: rawRequest.metadata
110
+ };
111
+ const finalRequest = resolveFinalRequest(gateConfig, request);
112
+ const { result, modelUsed } = await executeWithRouting(gateConfig, finalRequest, userId);
113
+ const latencyMs = Date.now() - startTime;
114
+ db.logRequest({
115
+ userId,
116
+ gateId: gateConfig.id,
117
+ model: modelUsed,
118
+ promptTokens: result.usage?.promptTokens || 0,
119
+ completionTokens: result.usage?.completionTokens || 0,
120
+ totalTokens: result.usage?.totalTokens || 0,
121
+ cost: result.cost || 0,
122
+ latencyMs,
123
+ success: true,
124
+ }).catch(error => {
125
+ console.error('Failed to log request:', error);
126
+ });
127
+ res.status(200).json(result);
128
+ }
129
+ catch (error) {
130
+ const latencyMs = Date.now() - startTime;
131
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
132
+ console.error('[v3/image] Error processing request:', error);
133
+ if (gateConfig) {
134
+ db.logRequest({
135
+ userId,
136
+ gateId: gateConfig.id,
137
+ model: gateConfig.model,
138
+ promptTokens: 0,
139
+ completionTokens: 0,
140
+ totalTokens: 0,
141
+ cost: 0,
142
+ latencyMs,
143
+ success: false,
144
+ errorMessage,
145
+ }).catch(logError => {
146
+ console.error('Failed to log error:', logError);
147
+ });
148
+ }
149
+ res.status(500).json({
150
+ error: 'internal_server_error',
151
+ message: errorMessage
152
+ });
153
+ }
154
+ });
155
+ export default router;
@@ -0,0 +1,4 @@
1
+ import type { Router as RouterType } from 'express';
2
+ declare const router: RouterType;
3
+ export default router;
4
+ //# sourceMappingURL=ocr.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ocr.d.ts","sourceRoot":"","sources":["../../../../src/routes/v3/completions/ocr.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAOpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAgGpC,eAAe,MAAM,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { Router } from 'express';
2
+ import { db } from '../../../lib/db/postgres.js';
3
+ import { authenticate } from '../../../middleware/auth.js';
4
+ import { callAdapter, normalizeModelId } from '../../../lib/provider-factory.js';
5
+ import { OverrideField } from '@layer-ai/sdk';
6
+ const router = Router();
7
+ function isOverrideAllowed(allowOverrides, field) {
8
+ if (allowOverrides === undefined || allowOverrides === null || allowOverrides === true)
9
+ return true;
10
+ if (allowOverrides === false)
11
+ return false;
12
+ return allowOverrides[field] ?? false;
13
+ }
14
+ function resolveFinalRequest(gateConfig, request) {
15
+ let finalModel = gateConfig.model;
16
+ if (request.model && isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Model)) {
17
+ try {
18
+ finalModel = normalizeModelId(request.model);
19
+ }
20
+ catch {
21
+ finalModel = gateConfig.model;
22
+ }
23
+ }
24
+ return { ...request, model: normalizeModelId(finalModel) };
25
+ }
26
+ function getModelsToTry(gateConfig, primaryModel) {
27
+ const modelsToTry = [primaryModel];
28
+ if (gateConfig.fallback1) {
29
+ try {
30
+ modelsToTry.push(normalizeModelId(gateConfig.fallback1));
31
+ }
32
+ catch (error) { }
33
+ }
34
+ if (gateConfig.fallback2) {
35
+ try {
36
+ modelsToTry.push(normalizeModelId(gateConfig.fallback2));
37
+ }
38
+ catch (error) { }
39
+ }
40
+ return modelsToTry;
41
+ }
42
+ async function executeWithRouting(gateConfig, request, userId) {
43
+ const modelsToTry = getModelsToTry(gateConfig, request.model);
44
+ let lastError = null;
45
+ for (let i = 0; i < modelsToTry.length; i++) {
46
+ const model = modelsToTry[i];
47
+ try {
48
+ const result = await callAdapter({ ...request, model }, userId);
49
+ return { result, modelUsed: model };
50
+ }
51
+ catch (error) {
52
+ lastError = error;
53
+ }
54
+ }
55
+ throw lastError || new Error('All models failed');
56
+ }
57
+ router.post('/', authenticate, async (req, res) => {
58
+ const startTime = Date.now();
59
+ if (!req.userId) {
60
+ res.status(401).json({ error: 'unauthorized', message: 'Missing user ID' });
61
+ return;
62
+ }
63
+ const userId = req.userId;
64
+ let gateConfig = null;
65
+ try {
66
+ const rawRequest = req.body;
67
+ if (!rawRequest.gateId || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(rawRequest.gateId)) {
68
+ res.status(400).json({ error: 'bad_request', message: 'Valid gateId (UUID) is required' });
69
+ return;
70
+ }
71
+ if (!rawRequest.data?.prompt || typeof rawRequest.data.documentUrl !== 'string' || !rawRequest.data.documentUrl.trim()) {
72
+ res.status(400).json({ error: 'bad_request', message: 'data.documentUrl is required' });
73
+ return;
74
+ }
75
+ gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
76
+ if (!gateConfig) {
77
+ res.status(404).json({ error: 'not_found', message: `Gate not found` });
78
+ return;
79
+ }
80
+ const request = { gateId: rawRequest.gateId, type: 'ocr', data: rawRequest.data, model: rawRequest.model, metadata: rawRequest.metadata };
81
+ const finalRequest = resolveFinalRequest(gateConfig, request);
82
+ const { result, modelUsed } = await executeWithRouting(gateConfig, finalRequest, userId);
83
+ db.logRequest({ userId, gateId: gateConfig.id, model: modelUsed, promptTokens: result.usage?.promptTokens || 0, completionTokens: result.usage?.completionTokens || 0, totalTokens: result.usage?.totalTokens || 0, cost: result.cost || 0, latencyMs: Date.now() - startTime, success: true }).catch(() => { });
84
+ res.status(200).json(result);
85
+ }
86
+ catch (error) {
87
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
88
+ if (gateConfig) {
89
+ db.logRequest({ userId, gateId: gateConfig.id, model: gateConfig.model, promptTokens: 0, completionTokens: 0, totalTokens: 0, cost: 0, latencyMs: Date.now() - startTime, success: false, errorMessage }).catch(() => { });
90
+ }
91
+ res.status(500).json({ error: 'internal_server_error', message: errorMessage });
92
+ }
93
+ });
94
+ export default router;
@@ -0,0 +1,4 @@
1
+ import type { Router as RouterType } from 'express';
2
+ declare const router: RouterType;
3
+ export default router;
4
+ //# sourceMappingURL=tts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tts.d.ts","sourceRoot":"","sources":["../../../../src/routes/v3/completions/tts.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAOpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAgGpC,eAAe,MAAM,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { Router } from 'express';
2
+ import { db } from '../../../lib/db/postgres.js';
3
+ import { authenticate } from '../../../middleware/auth.js';
4
+ import { callAdapter, normalizeModelId } from '../../../lib/provider-factory.js';
5
+ import { OverrideField } from '@layer-ai/sdk';
6
+ const router = Router();
7
+ function isOverrideAllowed(allowOverrides, field) {
8
+ if (allowOverrides === undefined || allowOverrides === null || allowOverrides === true)
9
+ return true;
10
+ if (allowOverrides === false)
11
+ return false;
12
+ return allowOverrides[field] ?? false;
13
+ }
14
+ function resolveFinalRequest(gateConfig, request) {
15
+ let finalModel = gateConfig.model;
16
+ if (request.model && isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Model)) {
17
+ try {
18
+ finalModel = normalizeModelId(request.model);
19
+ }
20
+ catch {
21
+ finalModel = gateConfig.model;
22
+ }
23
+ }
24
+ return { ...request, model: normalizeModelId(finalModel) };
25
+ }
26
+ function getModelsToTry(gateConfig, primaryModel) {
27
+ const modelsToTry = [primaryModel];
28
+ if (gateConfig.fallback1) {
29
+ try {
30
+ modelsToTry.push(normalizeModelId(gateConfig.fallback1));
31
+ }
32
+ catch (error) { }
33
+ }
34
+ if (gateConfig.fallback2) {
35
+ try {
36
+ modelsToTry.push(normalizeModelId(gateConfig.fallback2));
37
+ }
38
+ catch (error) { }
39
+ }
40
+ return modelsToTry;
41
+ }
42
+ async function executeWithRouting(gateConfig, request, userId) {
43
+ const modelsToTry = getModelsToTry(gateConfig, request.model);
44
+ let lastError = null;
45
+ for (let i = 0; i < modelsToTry.length; i++) {
46
+ const model = modelsToTry[i];
47
+ try {
48
+ const result = await callAdapter({ ...request, model }, userId);
49
+ return { result, modelUsed: model };
50
+ }
51
+ catch (error) {
52
+ lastError = error;
53
+ }
54
+ }
55
+ throw lastError || new Error('All models failed');
56
+ }
57
+ router.post('/', authenticate, async (req, res) => {
58
+ const startTime = Date.now();
59
+ if (!req.userId) {
60
+ res.status(401).json({ error: 'unauthorized', message: 'Missing user ID' });
61
+ return;
62
+ }
63
+ const userId = req.userId;
64
+ let gateConfig = null;
65
+ try {
66
+ const rawRequest = req.body;
67
+ if (!rawRequest.gateId || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(rawRequest.gateId)) {
68
+ res.status(400).json({ error: 'bad_request', message: 'Valid gateId (UUID) is required' });
69
+ return;
70
+ }
71
+ if (!rawRequest.data?.prompt || typeof rawRequest.data.input !== 'string' || !rawRequest.data.input.trim()) {
72
+ res.status(400).json({ error: 'bad_request', message: 'data.input is required' });
73
+ return;
74
+ }
75
+ gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
76
+ if (!gateConfig) {
77
+ res.status(404).json({ error: 'not_found', message: `Gate not found` });
78
+ return;
79
+ }
80
+ const request = { gateId: rawRequest.gateId, type: 'tts', data: rawRequest.data, model: rawRequest.model, metadata: rawRequest.metadata };
81
+ const finalRequest = resolveFinalRequest(gateConfig, request);
82
+ const { result, modelUsed } = await executeWithRouting(gateConfig, finalRequest, userId);
83
+ db.logRequest({ userId, gateId: gateConfig.id, model: modelUsed, promptTokens: result.usage?.promptTokens || 0, completionTokens: result.usage?.completionTokens || 0, totalTokens: result.usage?.totalTokens || 0, cost: result.cost || 0, latencyMs: Date.now() - startTime, success: true }).catch(() => { });
84
+ res.status(200).json(result);
85
+ }
86
+ catch (error) {
87
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
88
+ if (gateConfig) {
89
+ db.logRequest({ userId, gateId: gateConfig.id, model: gateConfig.model, promptTokens: 0, completionTokens: 0, totalTokens: 0, cost: 0, latencyMs: Date.now() - startTime, success: false, errorMessage }).catch(() => { });
90
+ }
91
+ res.status(500).json({ error: 'internal_server_error', message: errorMessage });
92
+ }
93
+ });
94
+ export default router;
@@ -0,0 +1,4 @@
1
+ import type { Router as RouterType } from 'express';
2
+ declare const router: RouterType;
3
+ export default router;
4
+ //# sourceMappingURL=video.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video.d.ts","sourceRoot":"","sources":["../../../../src/routes/v3/completions/video.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,SAAS,CAAC;AAOpD,QAAA,MAAM,MAAM,EAAE,UAAqB,CAAC;AAgGpC,eAAe,MAAM,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { Router } from 'express';
2
+ import { db } from '../../../lib/db/postgres.js';
3
+ import { authenticate } from '../../../middleware/auth.js';
4
+ import { callAdapter, normalizeModelId } from '../../../lib/provider-factory.js';
5
+ import { OverrideField } from '@layer-ai/sdk';
6
+ const router = Router();
7
+ function isOverrideAllowed(allowOverrides, field) {
8
+ if (allowOverrides === undefined || allowOverrides === null || allowOverrides === true)
9
+ return true;
10
+ if (allowOverrides === false)
11
+ return false;
12
+ return allowOverrides[field] ?? false;
13
+ }
14
+ function resolveFinalRequest(gateConfig, request) {
15
+ let finalModel = gateConfig.model;
16
+ if (request.model && isOverrideAllowed(gateConfig.allowOverrides, OverrideField.Model)) {
17
+ try {
18
+ finalModel = normalizeModelId(request.model);
19
+ }
20
+ catch {
21
+ finalModel = gateConfig.model;
22
+ }
23
+ }
24
+ return { ...request, model: normalizeModelId(finalModel) };
25
+ }
26
+ function getModelsToTry(gateConfig, primaryModel) {
27
+ const modelsToTry = [primaryModel];
28
+ if (gateConfig.fallback1) {
29
+ try {
30
+ modelsToTry.push(normalizeModelId(gateConfig.fallback1));
31
+ }
32
+ catch (error) { }
33
+ }
34
+ if (gateConfig.fallback2) {
35
+ try {
36
+ modelsToTry.push(normalizeModelId(gateConfig.fallback2));
37
+ }
38
+ catch (error) { }
39
+ }
40
+ return modelsToTry;
41
+ }
42
+ async function executeWithRouting(gateConfig, request, userId) {
43
+ const modelsToTry = getModelsToTry(gateConfig, request.model);
44
+ let lastError = null;
45
+ for (let i = 0; i < modelsToTry.length; i++) {
46
+ const model = modelsToTry[i];
47
+ try {
48
+ const result = await callAdapter({ ...request, model }, userId);
49
+ return { result, modelUsed: model };
50
+ }
51
+ catch (error) {
52
+ lastError = error;
53
+ }
54
+ }
55
+ throw lastError || new Error('All models failed');
56
+ }
57
+ router.post('/', authenticate, async (req, res) => {
58
+ const startTime = Date.now();
59
+ if (!req.userId) {
60
+ res.status(401).json({ error: 'unauthorized', message: 'Missing user ID' });
61
+ return;
62
+ }
63
+ const userId = req.userId;
64
+ let gateConfig = null;
65
+ try {
66
+ const rawRequest = req.body;
67
+ if (!rawRequest.gateId || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(rawRequest.gateId)) {
68
+ res.status(400).json({ error: 'bad_request', message: 'Valid gateId (UUID) is required' });
69
+ return;
70
+ }
71
+ if (!rawRequest.data?.prompt || typeof rawRequest.data.prompt !== 'string' || !rawRequest.data.prompt.trim()) {
72
+ res.status(400).json({ error: 'bad_request', message: 'data.prompt is required' });
73
+ return;
74
+ }
75
+ gateConfig = await db.getGateByUserAndId(userId, rawRequest.gateId);
76
+ if (!gateConfig) {
77
+ res.status(404).json({ error: 'not_found', message: `Gate not found` });
78
+ return;
79
+ }
80
+ const request = { gateId: rawRequest.gateId, type: 'video', data: rawRequest.data, model: rawRequest.model, metadata: rawRequest.metadata };
81
+ const finalRequest = resolveFinalRequest(gateConfig, request);
82
+ const { result, modelUsed } = await executeWithRouting(gateConfig, finalRequest, userId);
83
+ db.logRequest({ userId, gateId: gateConfig.id, model: modelUsed, promptTokens: result.usage?.promptTokens || 0, completionTokens: result.usage?.completionTokens || 0, totalTokens: result.usage?.totalTokens || 0, cost: result.cost || 0, latencyMs: Date.now() - startTime, success: true }).catch(() => { });
84
+ res.status(200).json(result);
85
+ }
86
+ catch (error) {
87
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
88
+ if (gateConfig) {
89
+ db.logRequest({ userId, gateId: gateConfig.id, model: gateConfig.model, promptTokens: 0, completionTokens: 0, totalTokens: 0, cost: 0, latencyMs: Date.now() - startTime, success: false, errorMessage }).catch(() => { });
90
+ }
91
+ res.status(500).json({ error: 'internal_server_error', message: errorMessage });
92
+ }
93
+ });
94
+ export default router;
@@ -1 +1 @@
1
- {"version":3,"file":"base-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/base-adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/base-adapter.ts"],"names":[],"mappings":"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"}
@@ -82,9 +82,12 @@ export class BaseProviderAdapter {
82
82
  }
83
83
  calculateCost(model, promptTokens, completionTokens) {
84
84
  const modelInfo = MODEL_REGISTRY[model];
85
- if (!modelInfo || !('pricing' in modelInfo) || !modelInfo.pricing?.input || !modelInfo.pricing?.output) {
85
+ if (!modelInfo || !('pricing' in modelInfo) || !modelInfo.pricing?.input) {
86
86
  return 0;
87
87
  }
88
- return (promptTokens / 1000000 * modelInfo.pricing.input) + (completionTokens / 1000000 * modelInfo.pricing.output);
88
+ const pricing = modelInfo.pricing;
89
+ const inputCost = promptTokens / 1000000 * pricing.input;
90
+ const outputCost = ('output' in pricing && pricing.output) ? (completionTokens / 1000000 * pricing.output) : 0;
91
+ return inputCost + outputCost;
89
92
  }
90
93
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layer-ai/core",
3
- "version": "1.0.0",
3
+ "version": "2.0.1",
4
4
  "description": "Core API routes and services for Layer AI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "nanoid": "^5.0.4",
37
37
  "openai": "^4.24.0",
38
38
  "pg": "^8.11.3",
39
- "@layer-ai/sdk": "^2.0.0"
39
+ "@layer-ai/sdk": "^2.2.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/bcryptjs": "^2.4.6",