@layer-ai/core 0.7.1 → 0.7.3

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.
Files changed (28) hide show
  1. package/dist/lib/key-resolver.d.ts +16 -0
  2. package/dist/lib/key-resolver.d.ts.map +1 -0
  3. package/dist/lib/key-resolver.js +56 -0
  4. package/dist/lib/provider-factory.d.ts +3 -1
  5. package/dist/lib/provider-factory.d.ts.map +1 -1
  6. package/dist/lib/provider-factory.js +4 -2
  7. package/dist/routes/v2/complete.js +10 -10
  8. package/dist/routes/v2/tests/test-byok-completion.d.ts +10 -0
  9. package/dist/routes/v2/tests/test-byok-completion.d.ts.map +1 -0
  10. package/dist/routes/v2/tests/test-byok-completion.js +82 -0
  11. package/dist/routes/v2/tests/test-byok.d.ts +8 -0
  12. package/dist/routes/v2/tests/test-byok.d.ts.map +1 -0
  13. package/dist/routes/v2/tests/test-byok.js +73 -0
  14. package/dist/services/providers/anthropic-adapter.d.ts +1 -1
  15. package/dist/services/providers/anthropic-adapter.d.ts.map +1 -1
  16. package/dist/services/providers/anthropic-adapter.js +13 -5
  17. package/dist/services/providers/base-adapter.d.ts +2 -1
  18. package/dist/services/providers/base-adapter.d.ts.map +1 -1
  19. package/dist/services/providers/google-adapter.d.ts +1 -1
  20. package/dist/services/providers/google-adapter.d.ts.map +1 -1
  21. package/dist/services/providers/google-adapter.js +25 -17
  22. package/dist/services/providers/mistral-adapter.d.ts +1 -1
  23. package/dist/services/providers/mistral-adapter.d.ts.map +1 -1
  24. package/dist/services/providers/mistral-adapter.js +19 -11
  25. package/dist/services/providers/openai-adapter.d.ts +1 -1
  26. package/dist/services/providers/openai-adapter.d.ts.map +1 -1
  27. package/dist/services/providers/openai-adapter.js +22 -14
  28. package/package.json +1 -1
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Key Resolver
3
+ *
4
+ * Resolves API keys for providers with BYOK support.
5
+ * Priority: User's BYOK key → Platform key → Error
6
+ */
7
+ import type { Provider } from './provider-constants.js';
8
+ /**
9
+ * Resolves the API key to use for a provider
10
+ * @param provider - The provider name
11
+ * @param userId - Optional user ID for BYOK lookup
12
+ * @param platformKey - The platform's API key (fallback)
13
+ * @returns The API key to use
14
+ */
15
+ export declare function resolveApiKey(provider: Provider, userId: string | undefined, platformKey: string | undefined): Promise<string>;
16
+ //# sourceMappingURL=key-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-resolver.d.ts","sourceRoot":"","sources":["../../src/lib/key-resolver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGxD;;;;;;GAMG;AACH,wBAAsB,aAAa,CACjC,QAAQ,EAAE,QAAQ,EAClB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,OAAO,CAAC,MAAM,CAAC,CAoBjB"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Key Resolver
3
+ *
4
+ * Resolves API keys for providers with BYOK support.
5
+ * Priority: User's BYOK key → Platform key → Error
6
+ */
7
+ import { decrypt } from './encryption.js';
8
+ /**
9
+ * Resolves the API key to use for a provider
10
+ * @param provider - The provider name
11
+ * @param userId - Optional user ID for BYOK lookup
12
+ * @param platformKey - The platform's API key (fallback)
13
+ * @returns The API key to use
14
+ */
15
+ export async function resolveApiKey(provider, userId, platformKey) {
16
+ // If userId is provided, check for BYOK key
17
+ if (userId) {
18
+ try {
19
+ const byokKey = await getUserProviderKey(userId, provider);
20
+ if (byokKey) {
21
+ return byokKey;
22
+ }
23
+ }
24
+ catch (error) {
25
+ console.error(`Failed to fetch BYOK key for user ${userId}, provider ${provider}:`, error);
26
+ // Continue to fallback
27
+ }
28
+ }
29
+ // Fallback to platform key
30
+ if (platformKey) {
31
+ return platformKey;
32
+ }
33
+ throw new Error(`No API key available for provider: ${provider}`);
34
+ }
35
+ /**
36
+ * Fetches and decrypts user's BYOK key for a provider
37
+ * @param userId - The user ID
38
+ * @param provider - The provider name
39
+ * @returns The decrypted API key, or null if not found
40
+ */
41
+ async function getUserProviderKey(userId, provider) {
42
+ // Dynamically import to avoid circular dependencies
43
+ const { db } = await import('./db/postgres.js');
44
+ const encryptionKey = process.env.ENCRYPTION_KEY;
45
+ if (!encryptionKey) {
46
+ console.warn('ENCRYPTION_KEY not set, BYOK disabled');
47
+ return null;
48
+ }
49
+ const providerKey = await db.getProviderKey(userId, provider);
50
+ if (!providerKey) {
51
+ return null;
52
+ }
53
+ // Decrypt the key
54
+ const decryptedKey = decrypt(providerKey.encryptedKey, encryptionKey);
55
+ return decryptedKey;
56
+ }
@@ -20,6 +20,8 @@ export declare function getProviderForModel(model: SupportedModel): Provider;
20
20
  /**
21
21
  * Calls the appropriate provider adapter for the given request.
22
22
  * This is the main entry point for executing AI model requests.
23
+ * @param request - The Layer request to execute
24
+ * @param userId - Optional user ID for BYOK key resolution
23
25
  */
24
- export declare function callAdapter(request: LayerRequest): Promise<LayerResponse>;
26
+ export declare function callAdapter(request: LayerRequest, userId?: string): Promise<LayerResponse>;
25
27
  //# sourceMappingURL=provider-factory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"provider-factory.d.ts","sourceRoot":"","sources":["../../src/lib/provider-factory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEjF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAG7E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,CAAC;AAa9C;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAmBhE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,QAAQ,CAMnE;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC,CAW/E"}
1
+ {"version":3,"file":"provider-factory.d.ts","sourceRoot":"","sources":["../../src/lib/provider-factory.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEjF,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAG7E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,QAAQ,EAAE,CAAC;AAa9C;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,cAAc,CAmBhE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,cAAc,GAAG,QAAQ,CAMnE;AAED;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAWhG"}
@@ -58,8 +58,10 @@ export function getProviderForModel(model) {
58
58
  /**
59
59
  * Calls the appropriate provider adapter for the given request.
60
60
  * This is the main entry point for executing AI model requests.
61
+ * @param request - The Layer request to execute
62
+ * @param userId - Optional user ID for BYOK key resolution
61
63
  */
62
- export async function callAdapter(request) {
64
+ export async function callAdapter(request, userId) {
63
65
  const normalizedModel = normalizeModelId(request.model);
64
66
  const provider = getProviderForModel(normalizedModel);
65
67
  const AdapterClass = PROVIDER_ADAPTERS[provider];
@@ -67,5 +69,5 @@ export async function callAdapter(request) {
67
69
  throw new Error(`No adapter found for provider: ${provider}`);
68
70
  }
69
71
  const adapter = new AdapterClass();
70
- return await adapter.call(request);
72
+ return await adapter.call(request, userId);
71
73
  }
@@ -82,14 +82,14 @@ function getModelsToTry(gateConfig, primaryModel) {
82
82
  }
83
83
  return modelsToTry;
84
84
  }
85
- async function executeWithFallback(request, modelsToTry) {
85
+ async function executeWithFallback(request, modelsToTry, userId) {
86
86
  let result = null;
87
87
  let lastError = null;
88
88
  let modelUsed = request.model;
89
89
  for (const modelToTry of modelsToTry) {
90
90
  try {
91
91
  const modelRequest = { ...request, model: modelToTry };
92
- result = await callAdapter(modelRequest);
92
+ result = await callAdapter(modelRequest, userId);
93
93
  modelUsed = modelToTry;
94
94
  break;
95
95
  }
@@ -104,28 +104,28 @@ async function executeWithFallback(request, modelsToTry) {
104
104
  }
105
105
  return { result, modelUsed };
106
106
  }
107
- async function executeWithRoundRobin(gateConfig, request) {
107
+ async function executeWithRoundRobin(gateConfig, request, userId) {
108
108
  if (!gateConfig.fallbackModels?.length) {
109
- const result = await callAdapter(request);
109
+ const result = await callAdapter(request, userId);
110
110
  return { result, modelUsed: request.model };
111
111
  }
112
112
  const allModels = [gateConfig.model, ...gateConfig.fallbackModels];
113
113
  const modelIndex = Math.floor(Math.random() * allModels.length);
114
114
  const selectedModel = allModels[modelIndex];
115
115
  const modelRequest = { ...request, model: selectedModel };
116
- const result = await callAdapter(modelRequest);
116
+ const result = await callAdapter(modelRequest, userId);
117
117
  return { result, modelUsed: selectedModel };
118
118
  }
119
- async function executeWithRouting(gateConfig, request) {
119
+ async function executeWithRouting(gateConfig, request, userId) {
120
120
  const modelsToTry = getModelsToTry(gateConfig, request.model);
121
121
  switch (gateConfig.routingStrategy) {
122
122
  case 'fallback':
123
- return await executeWithFallback(request, modelsToTry);
123
+ return await executeWithFallback(request, modelsToTry, userId);
124
124
  case 'round-robin':
125
- return await executeWithRoundRobin(gateConfig, request);
125
+ return await executeWithRoundRobin(gateConfig, request, userId);
126
126
  case 'single':
127
127
  default:
128
- const result = await callAdapter(request);
128
+ const result = await callAdapter(request, userId);
129
129
  return { result, modelUsed: request.model };
130
130
  }
131
131
  }
@@ -163,7 +163,7 @@ router.post('/', authenticate, async (req, res) => {
163
163
  }
164
164
  }
165
165
  const finalRequest = resolveFinalRequest(gateConfig, request);
166
- const { result, modelUsed } = await executeWithRouting(gateConfig, finalRequest);
166
+ const { result, modelUsed } = await executeWithRouting(gateConfig, finalRequest, userId);
167
167
  const latencyMs = Date.now() - startTime;
168
168
  // Log request to database
169
169
  db.logRequest({
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Test BYOK completion flow
3
+ *
4
+ * This test verifies that when making a completion request:
5
+ * 1. The system checks for user's BYOK keys
6
+ * 2. Uses BYOK keys when available
7
+ * 3. Falls back to platform keys when BYOK not configured
8
+ */
9
+ export {};
10
+ //# sourceMappingURL=test-byok-completion.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-byok-completion.d.ts","sourceRoot":"","sources":["../../../../src/routes/v2/tests/test-byok-completion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Test BYOK completion flow
3
+ *
4
+ * This test verifies that when making a completion request:
5
+ * 1. The system checks for user's BYOK keys
6
+ * 2. Uses BYOK keys when available
7
+ * 3. Falls back to platform keys when BYOK not configured
8
+ */
9
+ import { OpenAIAdapter } from '../../../services/providers/openai-adapter.js';
10
+ import { AnthropicAdapter } from '../../../services/providers/anthropic-adapter.js';
11
+ import { GoogleAdapter } from '../../../services/providers/google-adapter.js';
12
+ // Test user ID from the database
13
+ const TEST_USER_ID = 'ebd64998-465d-4211-ad67-87b4e01ad0da';
14
+ const SIMPLE_REQUEST = {
15
+ gate: 'byok-test',
16
+ model: 'gpt-4o-mini',
17
+ type: 'chat',
18
+ data: {
19
+ messages: [
20
+ {
21
+ role: 'user',
22
+ content: 'Say "BYOK test successful" and nothing else.',
23
+ },
24
+ ],
25
+ maxTokens: 20,
26
+ },
27
+ };
28
+ async function testBYOKCompletion() {
29
+ console.log('\n🧪 Testing BYOK Completion Flow...\n');
30
+ try {
31
+ // Test 1: OpenAI with BYOK (should use user's key)
32
+ console.log('Test 1: OpenAI completion with BYOK key');
33
+ console.log('----------------------------------------');
34
+ const openaiAdapter = new OpenAIAdapter();
35
+ const openaiRequest = { ...SIMPLE_REQUEST, model: 'gpt-4o-mini' };
36
+ console.log(`Making request to OpenAI with userId: ${TEST_USER_ID}`);
37
+ const openaiResult = await openaiAdapter.call(openaiRequest, TEST_USER_ID);
38
+ console.log('✓ OpenAI request successful');
39
+ console.log(`Response: ${openaiResult.content}`);
40
+ console.log('Note: This request used your BYOK OpenAI key\n');
41
+ // Test 2: Anthropic with BYOK (should use user's key)
42
+ console.log('Test 2: Anthropic completion with BYOK key');
43
+ console.log('------------------------------------------');
44
+ const anthropicAdapter = new AnthropicAdapter();
45
+ const anthropicRequest = { ...SIMPLE_REQUEST, model: 'claude-3-haiku-20240307' };
46
+ console.log(`Making request to Anthropic with userId: ${TEST_USER_ID}`);
47
+ const anthropicResult = await anthropicAdapter.call(anthropicRequest, TEST_USER_ID);
48
+ console.log('✓ Anthropic request successful');
49
+ console.log(`Response: ${anthropicResult.content}`);
50
+ console.log('Note: This request used your BYOK Anthropic key\n');
51
+ // Test 3: Google without BYOK (should use platform key)
52
+ console.log('Test 3: Google completion without BYOK key');
53
+ console.log('------------------------------------------');
54
+ const googleAdapter = new GoogleAdapter();
55
+ const googleRequest = { ...SIMPLE_REQUEST, model: 'gemini-2.0-flash' };
56
+ console.log(`Making request to Google with userId: ${TEST_USER_ID}`);
57
+ const googleResult = await googleAdapter.call(googleRequest, TEST_USER_ID);
58
+ console.log('✓ Google request successful');
59
+ console.log(`Response: ${googleResult.content}`);
60
+ console.log('Note: This request used the platform Google key (no BYOK configured)\n');
61
+ console.log('✅ All BYOK completion tests passed!\n');
62
+ console.log('Summary:');
63
+ console.log('- OpenAI: Used BYOK key ✓');
64
+ console.log('- Anthropic: Used BYOK key ✓');
65
+ console.log('- Google: Used platform key (fallback) ✓');
66
+ }
67
+ catch (error) {
68
+ console.error('\n❌ BYOK completion test failed:', error);
69
+ if (error instanceof Error) {
70
+ console.error('Error message:', error.message);
71
+ console.error('Stack:', error.stack);
72
+ }
73
+ throw error;
74
+ }
75
+ }
76
+ // Run the test
77
+ testBYOKCompletion()
78
+ .then(() => process.exit(0))
79
+ .catch((error) => {
80
+ console.error('Test failed:', error);
81
+ process.exit(1);
82
+ });
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Test BYOK (Bring Your Own Keys) functionality
3
+ *
4
+ * This test verifies that when a user has configured their own provider keys,
5
+ * those keys are used instead of the platform keys.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=test-byok.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-byok.d.ts","sourceRoot":"","sources":["../../../../src/routes/v2/tests/test-byok.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Test BYOK (Bring Your Own Keys) functionality
3
+ *
4
+ * This test verifies that when a user has configured their own provider keys,
5
+ * those keys are used instead of the platform keys.
6
+ */
7
+ import { db } from '../../../lib/db/postgres.js';
8
+ import { decrypt } from '../../../lib/encryption.js';
9
+ async function testBYOK() {
10
+ console.log('\n🔑 Testing BYOK Key Resolution...\n');
11
+ // Test user ID from the database query
12
+ const userId = 'ebd64998-465d-4211-ad67-87b4e01ad0da';
13
+ try {
14
+ // Test 1: Retrieve OpenAI key
15
+ console.log('Test 1: Retrieving OpenAI BYOK key...');
16
+ const openaiKey = await db.getProviderKey(userId, 'openai');
17
+ if (openaiKey) {
18
+ console.log('✓ OpenAI key found in database');
19
+ console.log(` Provider: ${openaiKey.provider}`);
20
+ console.log(` Key Prefix: ${openaiKey.keyPrefix}`);
21
+ console.log(` Created: ${openaiKey.createdAt}`);
22
+ // Decrypt and verify the key
23
+ const encryptionKey = process.env.ENCRYPTION_KEY;
24
+ if (!encryptionKey) {
25
+ throw new Error('ENCRYPTION_KEY not found in environment');
26
+ }
27
+ const decryptedKey = decrypt(openaiKey.encryptedKey, encryptionKey);
28
+ console.log(` Decrypted key starts with: ${decryptedKey.substring(0, 10)}...`);
29
+ console.log(` Decrypted key length: ${decryptedKey.length} characters`);
30
+ }
31
+ else {
32
+ console.log('✗ No OpenAI key found');
33
+ }
34
+ console.log('\nTest 2: Retrieving Anthropic BYOK key...');
35
+ const anthropicKey = await db.getProviderKey(userId, 'anthropic');
36
+ if (anthropicKey) {
37
+ console.log('✓ Anthropic key found in database');
38
+ console.log(` Provider: ${anthropicKey.provider}`);
39
+ console.log(` Key Prefix: ${anthropicKey.keyPrefix}`);
40
+ console.log(` Created: ${anthropicKey.createdAt}`);
41
+ const encryptionKey = process.env.ENCRYPTION_KEY;
42
+ if (!encryptionKey) {
43
+ throw new Error('ENCRYPTION_KEY not found in environment');
44
+ }
45
+ const decryptedKey = decrypt(anthropicKey.encryptedKey, encryptionKey);
46
+ console.log(` Decrypted key starts with: ${decryptedKey.substring(0, 14)}...`);
47
+ console.log(` Decrypted key length: ${decryptedKey.length} characters`);
48
+ }
49
+ else {
50
+ console.log('✗ No Anthropic key found');
51
+ }
52
+ console.log('\nTest 3: Retrieving Google BYOK key (should not exist)...');
53
+ const googleKey = await db.getProviderKey(userId, 'google');
54
+ if (googleKey) {
55
+ console.log('✓ Google key found in database');
56
+ }
57
+ else {
58
+ console.log('✓ No Google key found (as expected)');
59
+ }
60
+ console.log('\n✅ BYOK test completed successfully!\n');
61
+ }
62
+ catch (error) {
63
+ console.error('\n❌ BYOK test failed:', error);
64
+ throw error;
65
+ }
66
+ }
67
+ // Run the test
68
+ testBYOK()
69
+ .then(() => process.exit(0))
70
+ .catch((error) => {
71
+ console.error('Test failed:', error);
72
+ process.exit(1);
73
+ });
@@ -7,7 +7,7 @@ export declare class AnthropicAdapter extends BaseProviderAdapter {
7
7
  protected toolChoiceMappings: Record<string, string | object>;
8
8
  protected finishReasonMappings: Record<string, FinishReason>;
9
9
  protected mapToolChoice(choice: ToolChoice): string | object | undefined;
10
- call(request: LayerRequest): Promise<LayerResponse>;
10
+ call(request: LayerRequest, userId?: string): Promise<LayerResponse>;
11
11
  private handleChat;
12
12
  }
13
13
  //# sourceMappingURL=anthropic-adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"anthropic-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/anthropic-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAmB,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,YAAY,EACZ,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAa1E,qBAAa,gBAAiB,SAAQ,mBAAmB;IACvD,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAsB;IAElD,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAQ1C;IAEF,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAI3D;IAEF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAM1D;IAEF,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS;IAalE,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;YAiB3C,UAAU;CA4JzB"}
1
+ {"version":3,"file":"anthropic-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/anthropic-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAmB,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,YAAY,EACZ,UAAU,EACX,MAAM,eAAe,CAAC;AACvB,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAoB1E,qBAAa,gBAAiB,SAAQ,mBAAmB;IACvD,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAsB;IAElD,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAQ1C;IAEF,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAI3D;IAEF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAM1D;IAEF,SAAS,CAAC,aAAa,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS;IAalE,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;YAoB5D,UAAU;CA4JzB"}
@@ -1,8 +1,14 @@
1
1
  import Anthropic from "@anthropic-ai/sdk";
2
2
  import { BaseProviderAdapter, ADAPTER_HANDLED } from './base-adapter.js';
3
3
  import { PROVIDER } from "../../lib/provider-constants.js";
4
+ import { resolveApiKey } from '../../lib/key-resolver.js';
4
5
  let anthropic = null;
5
- function getAnthropicClient() {
6
+ function getAnthropicClient(apiKey) {
7
+ // If custom API key provided, create new client
8
+ if (apiKey) {
9
+ return new Anthropic({ apiKey });
10
+ }
11
+ // Otherwise use singleton with platform key
6
12
  if (!anthropic) {
7
13
  anthropic = new Anthropic({
8
14
  apiKey: process.env.ANTHROPIC_API_KEY,
@@ -47,10 +53,12 @@ export class AnthropicAdapter extends BaseProviderAdapter {
47
53
  // Handle string format using base mappings
48
54
  return super.mapToolChoice(choice);
49
55
  }
50
- async call(request) {
56
+ async call(request, userId) {
57
+ // Resolve API key (BYOK → Platform key)
58
+ const apiKey = await resolveApiKey(this.provider, userId, process.env.ANTHROPIC_API_KEY);
51
59
  switch (request.type) {
52
60
  case 'chat':
53
- return this.handleChat(request);
61
+ return this.handleChat(request, apiKey);
54
62
  case 'image':
55
63
  throw new Error('Image generation not yet supported by Anthropic');
56
64
  case 'embeddings':
@@ -63,9 +71,9 @@ export class AnthropicAdapter extends BaseProviderAdapter {
63
71
  throw new Error(`Unknown modality: ${request.type}`);
64
72
  }
65
73
  }
66
- async handleChat(request) {
74
+ async handleChat(request, apiKey) {
67
75
  const startTime = Date.now();
68
- const client = getAnthropicClient();
76
+ const client = getAnthropicClient(apiKey);
69
77
  const { data: chat, model } = request;
70
78
  if (!model) {
71
79
  throw new Error('Model is required for chat completions');
@@ -3,6 +3,7 @@ import type { Provider } from "../../lib/provider-constants.js";
3
3
  export { ADAPTER_HANDLED };
4
4
  export declare abstract class BaseProviderAdapter {
5
5
  protected abstract provider: Provider;
6
+ protected userId?: string;
6
7
  protected roleMappings?: Record<Role, string>;
7
8
  protected imageDetailMappings?: Record<ImageDetail, string>;
8
9
  protected toolChoiceMappings?: Record<string, string | object>;
@@ -15,7 +16,7 @@ export declare abstract class BaseProviderAdapter {
15
16
  protected audioMimeTypeMappings?: Record<AudioMimeType, string>;
16
17
  protected imageMimeTypeMappings?: Record<ImageMimeType, string>;
17
18
  protected encodingFormatMappings?: Record<EncodingFormat, string>;
18
- abstract call(request: LayerRequest): Promise<LayerResponse>;
19
+ abstract call(request: LayerRequest, userId?: string): Promise<LayerResponse>;
19
20
  protected mapRole(role: Role): string;
20
21
  protected mapImageDetail(detail: ImageDetail): string | undefined;
21
22
  protected mapImageSize(size: ImageSize): string | undefined;
@@ -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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"}
@@ -11,7 +11,7 @@ export declare class GoogleAdapter extends BaseProviderAdapter {
11
11
  aspectRatio: string;
12
12
  resolution: string;
13
13
  }>;
14
- call(request: LayerRequest): Promise<LayerResponse>;
14
+ call(request: LayerRequest, userId?: string): Promise<LayerResponse>;
15
15
  private handleChat;
16
16
  private handleImageGeneration;
17
17
  private handleEmbeddings;
@@ -1 +1 @@
1
- {"version":3,"file":"google-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/google-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,yBAAyB,EAI1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,YAAY,EACZ,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAW1E,qBAAa,aAAc,SAAQ,mBAAmB;IACpD,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAmB;IAE/C,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAQ1C;IAGF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAO1D;IAEF,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAIrE;IAGF,SAAS,CAAC,eAAe,EAAE,MAAM,CAC/B,SAAS,EACT;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAC5C,CAKC;IAEI,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;YAiB3C,UAAU;YAqLV,qBAAqB;YAkCrB,gBAAgB;YAmChB,qBAAqB;YAmHrB,kBAAkB;IAwChC,OAAO,CAAC,KAAK;CAGd"}
1
+ {"version":3,"file":"google-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/google-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAKL,yBAAyB,EAI1B,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,YAAY,EACZ,SAAS,EACV,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAExD,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAkB1E,qBAAa,aAAc,SAAQ,mBAAmB;IACpD,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAmB;IAE/C,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAQ1C;IAGF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAO1D;IAEF,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,yBAAyB,CAAC,CAIrE;IAGF,SAAS,CAAC,eAAe,EAAE,MAAM,CAC/B,SAAS,EACT;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAC5C,CAKC;IAEI,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;YAoB5D,UAAU;YAsLV,qBAAqB;YAmCrB,gBAAgB;YAoChB,qBAAqB;YAoHrB,kBAAkB;IAyChC,OAAO,CAAC,KAAK;CAGd"}
@@ -2,8 +2,14 @@ import { GoogleGenAI, FunctionCallingConfigMode, VideoGenerationReferenceType, }
2
2
  import { BaseProviderAdapter } from './base-adapter.js';
3
3
  import { ADAPTER_HANDLED } from './base-adapter.js';
4
4
  import { PROVIDER } from "../../lib/provider-constants.js";
5
+ import { resolveApiKey } from '../../lib/key-resolver.js';
5
6
  let client = null;
6
- function getGoogleClient() {
7
+ function getGoogleClient(apiKey) {
8
+ // If custom API key provided, create new client
9
+ if (apiKey) {
10
+ return new GoogleGenAI({ apiKey });
11
+ }
12
+ // Otherwise use singleton with platform key
7
13
  if (!client) {
8
14
  client = new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY || '' });
9
15
  }
@@ -44,25 +50,27 @@ export class GoogleAdapter extends BaseProviderAdapter {
44
50
  '1792x1024': { aspectRatio: '16:9', resolution: '1080p' },
45
51
  };
46
52
  }
47
- async call(request) {
53
+ async call(request, userId) {
54
+ // Resolve API key (BYOK → Platform key)
55
+ const apiKey = await resolveApiKey(this.provider, userId, process.env.GOOGLE_API_KEY);
48
56
  switch (request.type) {
49
57
  case 'chat':
50
- return this.handleChat(request);
58
+ return this.handleChat(request, apiKey);
51
59
  case 'image':
52
- return this.handleImageGeneration(request);
60
+ return this.handleImageGeneration(request, apiKey);
53
61
  case 'embeddings':
54
- return this.handleEmbeddings(request);
62
+ return this.handleEmbeddings(request, apiKey);
55
63
  case 'tts':
56
- return this.handleTextToSpeech(request);
64
+ return this.handleTextToSpeech(request, apiKey);
57
65
  case 'video':
58
- return this.handleVideoGeneration(request);
66
+ return this.handleVideoGeneration(request, apiKey);
59
67
  default:
60
68
  throw new Error(`Unknown modality: ${request.type}`);
61
69
  }
62
70
  }
63
- async handleChat(request) {
71
+ async handleChat(request, apiKey) {
64
72
  const startTime = Date.now();
65
- const client = getGoogleClient();
73
+ const client = getGoogleClient(apiKey);
66
74
  const { data: chat, model } = request;
67
75
  if (!model) {
68
76
  throw new Error('Model is required for chat completion');
@@ -215,9 +223,9 @@ export class GoogleAdapter extends BaseProviderAdapter {
215
223
  raw: response,
216
224
  };
217
225
  }
218
- async handleImageGeneration(request) {
226
+ async handleImageGeneration(request, apiKey) {
219
227
  const startTime = Date.now();
220
- const client = getGoogleClient();
228
+ const client = getGoogleClient(apiKey);
221
229
  const { data: image, model } = request;
222
230
  if (!model) {
223
231
  throw new Error('Model is required for chat completion');
@@ -241,9 +249,9 @@ export class GoogleAdapter extends BaseProviderAdapter {
241
249
  raw: response,
242
250
  };
243
251
  }
244
- async handleEmbeddings(request) {
252
+ async handleEmbeddings(request, apiKey) {
245
253
  const startTime = Date.now();
246
- const client = getGoogleClient();
254
+ const client = getGoogleClient(apiKey);
247
255
  const { data: embedding, model } = request;
248
256
  if (!model) {
249
257
  throw new Error('Model is required for chat completion');
@@ -268,9 +276,9 @@ export class GoogleAdapter extends BaseProviderAdapter {
268
276
  raw: response,
269
277
  };
270
278
  }
271
- async handleVideoGeneration(request) {
279
+ async handleVideoGeneration(request, apiKey) {
272
280
  const startTime = Date.now();
273
- const client = getGoogleClient();
281
+ const client = getGoogleClient(apiKey);
274
282
  const { data: video, model } = request;
275
283
  if (!model) {
276
284
  throw new Error('Model is required for chat completion');
@@ -371,9 +379,9 @@ export class GoogleAdapter extends BaseProviderAdapter {
371
379
  raw: operation.response,
372
380
  };
373
381
  }
374
- async handleTextToSpeech(request) {
382
+ async handleTextToSpeech(request, apiKey) {
375
383
  const startTime = Date.now();
376
- const client = getGoogleClient();
384
+ const client = getGoogleClient(apiKey);
377
385
  const { data: tts, model } = request;
378
386
  if (!model) {
379
387
  throw new Error('Model is required for chat completion');
@@ -6,7 +6,7 @@ export declare class MistralAdapter extends BaseProviderAdapter {
6
6
  protected roleMappings: Record<Role, string>;
7
7
  protected finishReasonMappings: Record<string, FinishReason>;
8
8
  protected toolChoiceMappings: Record<string, string>;
9
- call(request: LayerRequest): Promise<LayerResponse>;
9
+ call(request: LayerRequest, userId?: string): Promise<LayerResponse>;
10
10
  private handleChat;
11
11
  private handleEmbeddings;
12
12
  private handleOCR;
@@ -1 +1 @@
1
- {"version":3,"file":"mistral-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/mistral-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,YAAY,EAEb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAa1E,qBAAa,cAAe,SAAQ,mBAAmB;IACrD,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAoB;IAEhD,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAQ1C;IAGF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAM1D;IAEF,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIlD;IAEI,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;YAmB3C,UAAU;YA8LV,gBAAgB;YAuChB,SAAS;CA2ExB"}
1
+ {"version":3,"file":"mistral-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/mistral-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,YAAY,EAEb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAoB1E,qBAAa,cAAe,SAAQ,mBAAmB;IACrD,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAoB;IAEhD,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAQ1C;IAGF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAM1D;IAEF,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIlD;IAEI,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;YAsB5D,UAAU;YA+LV,gBAAgB;YAwChB,SAAS;CA4ExB"}
@@ -2,8 +2,14 @@ import { Mistral } from '@mistralai/mistralai';
2
2
  import { BaseProviderAdapter } from './base-adapter.js';
3
3
  import { ADAPTER_HANDLED, } from '@layer-ai/sdk';
4
4
  import { PROVIDER } from "../../lib/provider-constants.js";
5
+ import { resolveApiKey } from '../../lib/key-resolver.js';
5
6
  let client = null;
6
- function getMistralClient() {
7
+ function getMistralClient(apiKey) {
8
+ // If custom API key provided, create new client
9
+ if (apiKey) {
10
+ return new Mistral({ apiKey });
11
+ }
12
+ // Otherwise use singleton with platform key
7
13
  if (!client) {
8
14
  client = new Mistral({
9
15
  apiKey: process.env.MISTRAL_API_KEY || '',
@@ -38,14 +44,16 @@ export class MistralAdapter extends BaseProviderAdapter {
38
44
  required: 'any',
39
45
  };
40
46
  }
41
- async call(request) {
47
+ async call(request, userId) {
48
+ // Resolve API key (BYOK → Platform key)
49
+ const apiKey = await resolveApiKey(this.provider, userId, process.env.MISTRAL_API_KEY);
42
50
  switch (request.type) {
43
51
  case 'chat':
44
- return this.handleChat(request);
52
+ return this.handleChat(request, apiKey);
45
53
  case 'embeddings':
46
- return this.handleEmbeddings(request);
54
+ return this.handleEmbeddings(request, apiKey);
47
55
  case 'ocr':
48
- return this.handleOCR(request);
56
+ return this.handleOCR(request, apiKey);
49
57
  case 'image':
50
58
  throw new Error('Image generation not supported by Mistral');
51
59
  case 'tts':
@@ -56,9 +64,9 @@ export class MistralAdapter extends BaseProviderAdapter {
56
64
  throw new Error(`Unknown modality: ${request.type}`);
57
65
  }
58
66
  }
59
- async handleChat(request) {
67
+ async handleChat(request, apiKey) {
60
68
  const startTime = Date.now();
61
- const mistral = getMistralClient();
69
+ const mistral = getMistralClient(apiKey);
62
70
  const { data: chat, model } = request;
63
71
  if (!model) {
64
72
  throw new Error('Model is required for chat completion');
@@ -209,9 +217,9 @@ export class MistralAdapter extends BaseProviderAdapter {
209
217
  raw: response,
210
218
  };
211
219
  }
212
- async handleEmbeddings(request) {
220
+ async handleEmbeddings(request, apiKey) {
213
221
  const startTime = Date.now();
214
- const mistral = getMistralClient();
222
+ const mistral = getMistralClient(apiKey);
215
223
  const { data: embedding, model } = request;
216
224
  if (!model) {
217
225
  throw new Error('Model is required for embeddings');
@@ -239,9 +247,9 @@ export class MistralAdapter extends BaseProviderAdapter {
239
247
  raw: response,
240
248
  };
241
249
  }
242
- async handleOCR(request) {
250
+ async handleOCR(request, apiKey) {
243
251
  const startTime = Date.now();
244
- const mistral = getMistralClient();
252
+ const mistral = getMistralClient(apiKey);
245
253
  const { data: ocr, model } = request;
246
254
  const ocrModel = model || 'mistral-ocr-latest';
247
255
  let document;
@@ -11,7 +11,7 @@ export declare class OpenAIAdapter extends BaseProviderAdapter {
11
11
  protected imageStyleMappings: Record<ImageStyle, string>;
12
12
  protected videoSizeMappings: Record<VideoSize, string>;
13
13
  protected audioFormatMappings: Record<AudioFormat, string>;
14
- call(request: LayerRequest): Promise<LayerResponse>;
14
+ call(request: LayerRequest, userId?: string): Promise<LayerResponse>;
15
15
  private handleChat;
16
16
  private handleImageGeneration;
17
17
  private handleEmbeddings;
@@ -1 +1 @@
1
- {"version":3,"file":"openai-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/openai-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,WAAW,EACX,SAAS,EACT,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAa1E,qBAAa,aAAc,SAAQ,mBAAmB;IACpD,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAmB;IAE/C,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAQ1C;IAEF,SAAS,CAAC,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAIxD;IAEF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAK1D;IAEF,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAQpD;IAEF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAG1D;IAEF,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAGtD;IAEF,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAKpD;IAEF,SAAS,CAAC,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAOxD;IAEI,IAAI,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;YAiB3C,UAAU;YA2GV,qBAAqB;YA6BrB,gBAAgB;YAiChB,kBAAkB;CA8BjC"}
1
+ {"version":3,"file":"openai-adapter.d.ts","sourceRoot":"","sources":["../../../src/services/providers/openai-adapter.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EACL,YAAY,EACZ,aAAa,EACb,IAAI,EACJ,WAAW,EACX,SAAS,EACT,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,YAAY,EACb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,iCAAiC,CAAC;AAoB1E,qBAAa,aAAc,SAAQ,mBAAmB;IACpD,SAAS,CAAC,QAAQ,EAAE,QAAQ,CAAmB;IAE/C,SAAS,CAAC,YAAY,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAQ1C;IAEF,SAAS,CAAC,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAIxD;IAEF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAK1D;IAEF,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAQpD;IAEF,SAAS,CAAC,oBAAoB,EAAE,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAG1D;IAEF,SAAS,CAAC,kBAAkB,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,CAGtD;IAEF,SAAS,CAAC,iBAAiB,EAAE,MAAM,CAAC,SAAS,EAAE,MAAM,CAAC,CAKpD;IAEF,SAAS,CAAC,mBAAmB,EAAE,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAOxD;IAEI,IAAI,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;YAoB5D,UAAU;YA2GV,qBAAqB;YA6BrB,gBAAgB;YAiChB,kBAAkB;CA8BjC"}
@@ -1,8 +1,14 @@
1
1
  import OpenAI from 'openai';
2
2
  import { BaseProviderAdapter } from './base-adapter.js';
3
3
  import { PROVIDER } from "../../lib/provider-constants.js";
4
+ import { resolveApiKey } from '../../lib/key-resolver.js';
4
5
  let openai = null;
5
- function getOpenAIClient() {
6
+ function getOpenAIClient(apiKey) {
7
+ // If custom API key provided, create new client
8
+ if (apiKey) {
9
+ return new OpenAI({ apiKey });
10
+ }
11
+ // Otherwise use singleton with platform key
6
12
  if (!openai) {
7
13
  openai = new OpenAI({
8
14
  apiKey: process.env.OPENAI_API_KEY,
@@ -66,25 +72,27 @@ export class OpenAIAdapter extends BaseProviderAdapter {
66
72
  pcm: 'pcm',
67
73
  };
68
74
  }
69
- async call(request) {
75
+ async call(request, userId) {
76
+ // Resolve API key (BYOK → Platform key)
77
+ const apiKey = await resolveApiKey(this.provider, userId, process.env.OPENAI_API_KEY);
70
78
  switch (request.type) {
71
79
  case 'chat':
72
- return this.handleChat(request);
80
+ return this.handleChat(request, apiKey);
73
81
  case 'image':
74
- return this.handleImageGeneration(request);
82
+ return this.handleImageGeneration(request, apiKey);
75
83
  case 'embeddings':
76
- return this.handleEmbeddings(request);
84
+ return this.handleEmbeddings(request, apiKey);
77
85
  case 'tts':
78
- return this.handleTextToSpeech(request);
86
+ return this.handleTextToSpeech(request, apiKey);
79
87
  case 'video':
80
88
  throw new Error('Video generation not yet supported by OpenAI');
81
89
  default:
82
90
  throw new Error(`Unknown modality: ${request.type}`);
83
91
  }
84
92
  }
85
- async handleChat(request) {
93
+ async handleChat(request, apiKey) {
86
94
  const startTime = Date.now();
87
- const client = getOpenAIClient();
95
+ const client = getOpenAIClient(apiKey);
88
96
  const { data: chat, model } = request;
89
97
  if (!model) {
90
98
  throw new Error('Model is required for chat completion');
@@ -176,9 +184,9 @@ export class OpenAIAdapter extends BaseProviderAdapter {
176
184
  raw: response,
177
185
  };
178
186
  }
179
- async handleImageGeneration(request) {
187
+ async handleImageGeneration(request, apiKey) {
180
188
  const startTime = Date.now();
181
- const client = getOpenAIClient();
189
+ const client = getOpenAIClient(apiKey);
182
190
  const { data: image, model } = request;
183
191
  if (!model) {
184
192
  throw new Error('Model is required for image generation');
@@ -201,9 +209,9 @@ export class OpenAIAdapter extends BaseProviderAdapter {
201
209
  raw: response,
202
210
  };
203
211
  }
204
- async handleEmbeddings(request) {
212
+ async handleEmbeddings(request, apiKey) {
205
213
  const startTime = Date.now();
206
- const client = getOpenAIClient();
214
+ const client = getOpenAIClient(apiKey);
207
215
  const { data: embedding, model } = request;
208
216
  if (!model) {
209
217
  throw new Error('Model is required for embeddings');
@@ -229,9 +237,9 @@ export class OpenAIAdapter extends BaseProviderAdapter {
229
237
  raw: response,
230
238
  };
231
239
  }
232
- async handleTextToSpeech(request) {
240
+ async handleTextToSpeech(request, apiKey) {
233
241
  const startTime = Date.now();
234
- const client = getOpenAIClient();
242
+ const client = getOpenAIClient(apiKey);
235
243
  const { data: tts, model } = request;
236
244
  if (!model) {
237
245
  throw new Error('Model is required for tts');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@layer-ai/core",
3
- "version": "0.7.1",
3
+ "version": "0.7.3",
4
4
  "description": "Core API routes and services for Layer AI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",