@layer-ai/core 0.5.12 → 0.6.0

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.
@@ -12,6 +12,7 @@ export declare const db: {
12
12
  getApiKeysForUser(userId: string): Promise<ApiKey[]>;
13
13
  deleteApiKey(id: string, userId: string): Promise<boolean>;
14
14
  getGateByUserAndName(userId: string, gateName: string): Promise<Gate | null>;
15
+ getGateByUserAndId(userId: string, gateId: string): Promise<Gate | null>;
15
16
  getGatesForUser(userId: string): Promise<Gate[]>;
16
17
  createGate(userId: string, data: any): Promise<Gate>;
17
18
  getGateById(id: string): Promise<Gate | null>;
@@ -1 +1 @@
1
- {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../../src/lib/db/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAyB,MAAM,eAAe,CAAC;AAO/E,iBAAS,OAAO,IAAI,EAAE,CAAC,IAAI,CAqB1B;AA0BD,eAAO,MAAM,EAAE;gBAEK,MAAM,WAAW,GAAG,EAAE;0BASZ,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;oBAQnC,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;sBAQ3B,MAAM,gBAAgB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;6BASrC,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;yBAQnC,MAAM,WAAW,MAAM,aAAa,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;kCAQjE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;8BAO1B,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;qBAQnC,MAAM,UAAU,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;iCAS7B,MAAM,YAAY,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;4BAQpD,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;uBAQ7B,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;oBA4BpC,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;mBAQ9B,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;mBA4CxC,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;qBASvB,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;iCAgBP,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;6BAQhE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;qCAehB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAM9D,CAAC;AAEF,eAAe,OAAO,CAAC"}
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../../src/lib/db/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAyB,MAAM,eAAe,CAAC;AAO/E,iBAAS,OAAO,IAAI,EAAE,CAAC,IAAI,CAqB1B;AA0BD,eAAO,MAAM,EAAE;gBAEK,MAAM,WAAW,GAAG,EAAE;0BASZ,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;oBAQnC,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;sBAQ3B,MAAM,gBAAgB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;6BASrC,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;yBAQnC,MAAM,WAAW,MAAM,aAAa,MAAM,QAAQ,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;kCAQjE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;8BAO1B,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;qBAQnC,MAAM,UAAU,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;iCAS7B,MAAM,YAAY,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;+BAQjD,MAAM,UAAU,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;4BAQhD,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;uBAQ7B,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;oBA4BpC,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;mBAQ9B,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;mBA4CxC,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;qBASvB,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;iCAgBP,MAAM,GAAG,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;6BAQhE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;qCAehB,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAM9D,CAAC;AAEF,eAAe,OAAO,CAAC"}
@@ -84,11 +84,15 @@ export const db = {
84
84
  const result = await getPool().query('UPDATE api_keys SET is_active = false WHERE id = $1 AND user_id = $2', [id, userId]);
85
85
  return (result.rowCount ?? 0) > 0;
86
86
  },
87
- // Gates
87
+ // Gates
88
88
  async getGateByUserAndName(userId, gateName) {
89
89
  const result = await getPool().query('SELECT * FROM gates WHERE user_id = $1 AND name = $2', [userId, gateName]);
90
90
  return result.rows[0] ? toCamelCase(result.rows[0]) : null;
91
91
  },
92
+ async getGateByUserAndId(userId, gateId) {
93
+ const result = await getPool().query('SELECT * FROM gates WHERE user_id = $1 AND id = $2', [userId, gateId]);
94
+ return result.rows[0] ? toCamelCase(result.rows[0]) : null;
95
+ },
92
96
  async getGatesForUser(userId) {
93
97
  const result = await getPool().query('SELECT * FROM gates WHERE user_id = $1 ORDER BY created_at DESC', [userId]);
94
98
  return result.rows.map(toCamelCase);
@@ -3,6 +3,7 @@ import type { Gate } from '@layer-ai/sdk';
3
3
  declare const redis: Redis;
4
4
  export declare const cache: {
5
5
  getGate(userId: string, gateName: string): Promise<Gate | null>;
6
+ getGateById(userId: string, gateId: string): Promise<Gate | null>;
6
7
  setGate(userId: string, gateName: string, gate: Gate): Promise<void>;
7
8
  invalidateGate(userId: string, gateName: string): Promise<void>;
8
9
  invalidateUserGates(userId: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../../src/lib/db/redis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,QAAA,MAAM,KAAK,OAcT,CAAC;AAmBH,eAAO,MAAM,KAAK;oBAEM,MAAM,YAAY,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;oBAqB/C,MAAM,YAAY,MAAM,QAAQ,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;2BAW7C,MAAM,YAAY,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;gCAUnC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAc1C,OAAO,CAAC,OAAO,CAAC;CAQ/B,CAAC;AAEF,eAAe,KAAK,CAAC"}
1
+ {"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../../src/lib/db/redis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,SAAS,CAAC;AAC5B,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAG1C,QAAA,MAAM,KAAK,OAcT,CAAC;AAuBH,eAAO,MAAM,KAAK;oBAEM,MAAM,YAAY,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;wBAqB3C,MAAM,UAAU,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;oBAqBjD,MAAM,YAAY,MAAM,QAAQ,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;2BAe7C,MAAM,YAAY,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;gCAUnC,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAc1C,OAAO,CAAC,OAAO,CAAC;CAQ/B,CAAC;AAEF,eAAe,KAAK,CAAC"}
@@ -25,7 +25,10 @@ redis.on('error', (err) => {
25
25
  // Cache key builders
26
26
  const CACHE_TTL = 300;
27
27
  function getGateCacheKey(userId, gateName) {
28
- return `gate:${userId}:${gateName}`;
28
+ return `gate:${userId}:name:${gateName}`;
29
+ }
30
+ function getGateCacheKeyById(userId, gateId) {
31
+ return `gate:${userId}:id:${gateId}`;
29
32
  }
30
33
  // Cache operations
31
34
  export const cache = {
@@ -47,11 +50,32 @@ export const cache = {
47
50
  return null; // if we fail, then we fetch from the db
48
51
  }
49
52
  },
53
+ // get the gate by ID
54
+ async getGateById(userId, gateId) {
55
+ try {
56
+ const key = getGateCacheKeyById(userId, gateId);
57
+ const cached = await redis.get(key);
58
+ if (!cached) {
59
+ return null;
60
+ }
61
+ const gate = JSON.parse(cached);
62
+ gate.createdAt = new Date(gate.createdAt);
63
+ gate.updatedAt = new Date(gate.updatedAt);
64
+ return gate;
65
+ }
66
+ catch (error) {
67
+ console.error('Redis get error:', error);
68
+ return null; // if we fail, then we fetch from the db
69
+ }
70
+ },
50
71
  // Set gate in cache
51
72
  async setGate(userId, gateName, gate) {
52
73
  try {
53
74
  const key = getGateCacheKey(userId, gateName);
54
75
  await redis.setex(key, CACHE_TTL, JSON.stringify(gate));
76
+ // Also cache by ID for lookups by gate ID
77
+ const keyById = getGateCacheKeyById(userId, gate.id);
78
+ await redis.setex(keyById, CACHE_TTL, JSON.stringify(gate));
55
79
  }
56
80
  catch (error) {
57
81
  console.error('Redis set error:', 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;AAgPpC,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;AAkQpC,eAAe,MAAM,CAAC"}
@@ -13,12 +13,25 @@ function isOverrideAllowed(allowOverrides, field) {
13
13
  return false;
14
14
  return allowOverrides[field] ?? false;
15
15
  }
16
- async function getGateConfig(userId, gateName) {
17
- let gateConfig = await cache.getGate(userId, gateName);
18
- if (!gateConfig) {
19
- gateConfig = await db.getGateByUserAndName(userId, gateName);
20
- if (gateConfig) {
21
- await cache.setGate(userId, gateName, gateConfig);
16
+ async function getGateConfig(userId, gateIdentifier) {
17
+ const isUUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(gateIdentifier);
18
+ let gateConfig = null;
19
+ if (isUUID) {
20
+ gateConfig = await cache.getGateById(userId, gateIdentifier);
21
+ if (!gateConfig) {
22
+ gateConfig = await db.getGateByUserAndId(userId, gateIdentifier);
23
+ if (gateConfig) {
24
+ await cache.setGate(userId, gateConfig.name, gateConfig);
25
+ }
26
+ }
27
+ }
28
+ else {
29
+ gateConfig = await cache.getGate(userId, gateIdentifier);
30
+ if (!gateConfig) {
31
+ gateConfig = await db.getGateByUserAndName(userId, gateIdentifier);
32
+ if (gateConfig) {
33
+ await cache.setGate(userId, gateIdentifier, gateConfig);
34
+ }
22
35
  }
23
36
  }
24
37
  return gateConfig;
@@ -125,27 +138,30 @@ router.post('/', authenticate, async (req, res) => {
125
138
  }
126
139
  const userId = req.userId;
127
140
  try {
128
- const request = req.body;
129
- if (!request.gate) {
141
+ const rawRequest = req.body;
142
+ if (!rawRequest.gate) {
130
143
  res.status(400).json({ error: 'bad_request', message: 'Missing required field: gate' });
131
144
  return;
132
145
  }
133
- if (!request.type) {
134
- res.status(400).json({ error: 'bad_request', message: 'Missing required field: type' });
146
+ const gateConfig = await getGateConfig(userId, rawRequest.gate);
147
+ if (!gateConfig) {
148
+ res.status(404).json({ error: 'not_found', message: `Gate "${rawRequest.gate}" not found` });
135
149
  return;
136
150
  }
137
- // Validate chat-specific requirements
151
+ const requestType = rawRequest.type || gateConfig.taskType || 'chat';
152
+ const request = {
153
+ gate: rawRequest.gate,
154
+ type: requestType,
155
+ data: rawRequest.data,
156
+ model: rawRequest.model,
157
+ metadata: rawRequest.metadata
158
+ };
138
159
  if (request.type === 'chat') {
139
160
  if (!request.data.messages || !Array.isArray(request.data.messages) || request.data.messages.length === 0) {
140
161
  res.status(400).json({ error: 'bad_request', message: 'Missing required field: data.messages' });
141
162
  return;
142
163
  }
143
164
  }
144
- const gateConfig = await getGateConfig(userId, request.gate);
145
- if (!gateConfig) {
146
- res.status(404).json({ error: 'not_found', message: `Gate "${request.gate}" not found` });
147
- return;
148
- }
149
165
  const finalRequest = resolveFinalRequest(gateConfig, request);
150
166
  const { result, modelUsed } = await executeWithRouting(gateConfig, finalRequest);
151
167
  const latencyMs = Date.now() - startTime;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layer-ai/core",
3
- "version": "0.5.12",
3
+ "version": "0.6.0",
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": "^0.5.2"
39
+ "@layer-ai/sdk": "^0.6.0"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@types/bcryptjs": "^2.4.6",