@imenam/unified-llm-client 2.0.0 → 2.0.2

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/README.md CHANGED
@@ -207,6 +207,32 @@ const client = new UnifiedLLMClient(MODELS.GEMINI_FLASH_2_5, {
207
207
  const response = await client.chat({ message: "Hello!" });
208
208
  ```
209
209
 
210
+ The proxy server also supports an `apiKeyResolver` for per-request dynamic key resolution (e.g. multi-tenant scenarios):
211
+
212
+ ```typescript
213
+ import { ProxyServer } from "@imenam/unified-llm-client";
214
+ import type { ApiKeyResolver } from "@imenam/unified-llm-client";
215
+
216
+ const resolver: ApiKeyResolver<{ userId: string }> = async (ctx) =>
217
+ (await userKeyStore.get(ctx.userId)) ?? false;
218
+
219
+ const server = new ProxyServer({ port: 3000, apiKey: "my-secret", apiKeyResolver: resolver });
220
+ await server.start();
221
+ ```
222
+
223
+ ```typescript
224
+ // Client passes the context — server resolves the key
225
+ const client = new UnifiedLLMClient(MODELS.GPT_4O_MINI, {
226
+ proxyUrl: "http://localhost:3000",
227
+ proxyApiKey: "my-secret",
228
+ });
229
+
230
+ const response = await client.chat({
231
+ message: "Hello!",
232
+ apiKeyContext: { userId: "user-123" },
233
+ });
234
+ ```
235
+
210
236
  ## API Key Resolver
211
237
 
212
238
  Dynamically resolve which API key to use for each request based on a custom context (e.g. per-user key, request metadata):
@@ -215,8 +241,8 @@ Dynamically resolve which API key to use for each request based on a custom cont
215
241
  import { UnifiedLLMClient, MODELS } from "@imenam/unified-llm-client";
216
242
  import type { ApiKeyResolver } from "@imenam/unified-llm-client";
217
243
 
218
- const resolver: ApiKeyResolver<{ userId: string }> = (ctx) => {
219
- const key = userKeyStore.get(ctx.userId);
244
+ const resolver: ApiKeyResolver<{ userId: string }> = async (ctx) => {
245
+ const key = await userKeyStore.get(ctx.userId);
220
246
  return key ?? false; // return false to deny access
221
247
  };
222
248
 
@@ -239,7 +265,7 @@ UnifiedLLMClient.setDefaultApiKeyResolver((ctx) => {
239
265
  ```
240
266
 
241
267
  > The resolver is called on every `chat()` call. Returning `false` throws an access-denied error.
242
- > The `apiKeyResolver` option is incompatible with proxy mode.
268
+ > The client-side `apiKeyResolver` option is for direct mode only. In proxy mode, the resolver is defined on the `ProxyServer` — the client passes `apiKeyContext` and the server resolves the key.
243
269
 
244
270
  ## Cancellation
245
271
 
@@ -294,7 +320,7 @@ UnifiedLLMClient.setDefaultLogger((log) => {
294
320
  | `options.proxyUrl` | `string` | Proxy server URL — activates proxy mode |
295
321
  | `options.proxyApiKey` | `string` | Key sent in the `x-api-key` header (proxy mode) |
296
322
  | `options.apiKey` | `string` | Static API key passed directly to the provider (overrides env var) |
297
- | `options.apiKeyResolver` | `ApiKeyResolver` | Dynamic per-request key resolver (incompatible with proxy mode) |
323
+ | `options.apiKeyResolver` | `ApiKeyResolver` | Dynamic per-request key resolver (direct mode only — in proxy mode, set this on `ProxyServer`) |
298
324
 
299
325
  ### `client.chat(options)`
300
326
 
@@ -1 +1 @@
1
- {"version":3,"file":"AnthropicLogAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/anthropic/AnthropicLogAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAY,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAE5D,qBAAa,mBAAoB,YAAW,UAAU;IACpD,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU;CAoCxC"}
1
+ {"version":3,"file":"AnthropicLogAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/anthropic/AnthropicLogAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAY,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAE5D,qBAAa,mBAAoB,YAAW,UAAU;IACpD,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU;CAqCxC"}
@@ -17,11 +17,7 @@ class AnthropicLogAdapter {
17
17
  provider: provider,
18
18
  model,
19
19
  prompt: messages,
20
- output: {
21
- content: response.content,
22
- toolCalls: response.toolCalls,
23
- finishReason: response.finishReason,
24
- },
20
+ output: Object.assign({ content: response.content, toolCalls: response.toolCalls, finishReason: response.finishReason }, (response.error ? { error: response.error } : {})),
25
21
  usage: {
26
22
  promptTokens: inputTokens,
27
23
  completionTokens: outputTokens,
@@ -1 +1 @@
1
- {"version":3,"file":"GeminiLogAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/gemini/GeminiLogAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAY,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAI5D,qBAAa,gBAAiB,YAAW,UAAU;IACjD,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU;CAsCxC"}
1
+ {"version":3,"file":"GeminiLogAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/gemini/GeminiLogAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAY,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAI5D,qBAAa,gBAAiB,YAAW,UAAU;IACjD,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU;CAuCxC"}
@@ -14,11 +14,7 @@ class GeminiLogAdapter {
14
14
  provider: provider,
15
15
  model: model,
16
16
  prompt: messages,
17
- output: {
18
- content: response.content,
19
- toolCalls: response.toolCalls,
20
- finishReason: response.finishReason,
21
- },
17
+ output: Object.assign({ content: response.content, toolCalls: response.toolCalls, finishReason: response.finishReason }, (response.error ? { error: response.error } : {})),
22
18
  usage: {
23
19
  promptTokens: (usage === null || usage === void 0 ? void 0 : usage.promptTokens) || 0,
24
20
  completionTokens: (usage === null || usage === void 0 ? void 0 : usage.completionTokens) || 0,
@@ -1 +1 @@
1
- {"version":3,"file":"OpenAiLogAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/openai/OpenAiLogAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAY,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAE5D,qBAAa,gBAAiB,YAAW,UAAU;IACjD,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU;CA+BxC"}
1
+ {"version":3,"file":"OpenAiLogAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/openai/OpenAiLogAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAY,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAE5D,qBAAa,gBAAiB,YAAW,UAAU;IACjD,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU;CAgCxC"}
@@ -13,11 +13,7 @@ class OpenAiLogAdapter {
13
13
  provider: provider,
14
14
  model: model,
15
15
  prompt: messages,
16
- output: {
17
- content: response.content,
18
- toolCalls: response.toolCalls,
19
- finishReason: response.finishReason,
20
- },
16
+ output: Object.assign({ content: response.content, toolCalls: response.toolCalls, finishReason: response.finishReason }, (response.error ? { error: response.error } : {})),
21
17
  usage: {
22
18
  promptTokens: (usage === null || usage === void 0 ? void 0 : usage.promptTokens) || 0,
23
19
  completionTokens: (usage === null || usage === void 0 ? void 0 : usage.completionTokens) || 0,
@@ -1 +1 @@
1
- {"version":3,"file":"QwenLogAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/qwen/QwenLogAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAY,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAE5D,qBAAa,cAAe,YAAW,UAAU;IAC/C,YAAY,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE;IAapC,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU;CA+BxC"}
1
+ {"version":3,"file":"QwenLogAdapter.d.ts","sourceRoot":"","sources":["../../../src/adapters/qwen/QwenLogAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAY,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAE5D,qBAAa,cAAe,YAAW,UAAU;IAC/C,YAAY,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,GAAG,EAAE;IAapC,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU;CAgCxC"}
@@ -25,11 +25,7 @@ class QwenLogAdapter {
25
25
  provider: provider,
26
26
  model: model,
27
27
  prompt: this.removeImages(messages),
28
- output: {
29
- content: response.content,
30
- toolCalls: response.toolCalls,
31
- finishReason: response.finishReason,
32
- },
28
+ output: Object.assign({ content: response.content, toolCalls: response.toolCalls, finishReason: response.finishReason }, (response.error ? { error: response.error } : {})),
33
29
  usage: {
34
30
  promptTokens: (usage === null || usage === void 0 ? void 0 : usage.promptTokens) || ((_a = raw === null || raw === void 0 ? void 0 : raw.usage) === null || _a === void 0 ? void 0 : _a.prompt_tokens) || 0,
35
31
  completionTokens: (usage === null || usage === void 0 ? void 0 : usage.completionTokens) || ((_b = raw === null || raw === void 0 ? void 0 : raw.usage) === null || _b === void 0 ? void 0 : _b.completion_tokens) || 0,
@@ -1,5 +1,5 @@
1
1
  export type Provider = "openai" | "gemini" | "qwen" | "anthropic";
2
- export type ApiKeyResolver<T = unknown> = (context: T) => string | false;
2
+ export type ApiKeyResolver<T = unknown> = (context: T) => Promise<string | false> | string | false;
3
3
  export type ChatMessageRole = "user" | "assistant" | "system" | "tool" | "function";
4
4
  export interface ChatMessage {
5
5
  tool_call_id?: string;
@@ -37,6 +37,7 @@ export interface ChatResponse {
37
37
  finishReason?: string;
38
38
  requestId?: string;
39
39
  llmLogId?: number;
40
+ error?: string;
40
41
  lastToolArguments?: ToolCall["arguments"];
41
42
  }
42
43
  export type Model = {
@@ -78,6 +79,7 @@ export interface UnifiedLog {
78
79
  content: string | null;
79
80
  toolCalls?: ToolCall[];
80
81
  finishReason?: string;
82
+ error?: string;
81
83
  };
82
84
  usage: UnifiedUsage;
83
85
  latencyMs: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/common/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/common/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"UnifiedLLMClient.d.ts","sourceRoot":"","sources":["../../src/core/UnifiedLLMClient.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"UnifiedLLMClient.d.ts","sourceRoot":"","sources":["../../src/core/UnifiedLLMClient.ts"],"names":[],"mappings":"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"}
@@ -64,7 +64,7 @@ class UnifiedLLMClient {
64
64
  if (apiKeyContext === undefined) {
65
65
  throw new Error("[ApiKeyResolver] A resolver is defined but no apiKeyContext was provided.");
66
66
  }
67
- const resolvedKey = effectiveResolver(apiKeyContext);
67
+ const resolvedKey = yield effectiveResolver(apiKeyContext);
68
68
  if (resolvedKey === false)
69
69
  throw new Error("[ApiKeyResolver] The resolver returned false — access denied.");
70
70
  const resolved = LLMAdapter_1.PROVIDERS_CONFIG[this.model.provider](resolvedKey);
@@ -79,35 +79,44 @@ class UnifiedLLMClient {
79
79
  this.checkNotImplementedFeatures(m);
80
80
  const messages = yield Promise.all(messageArray.map(m => promptAdapter.formatMessage(m)));
81
81
  let finalResponse;
82
- if (stream) {
83
- if (!promptAdapter.streamFromProvider) {
84
- throw new Error("The current adapter does not support streaming.");
85
- }
86
- let aggregatedContent = "";
87
- finalResponse = yield promptAdapter.streamFromProvider({
88
- messages,
89
- tools,
90
- toolChoice,
91
- model: this.model.modelName,
92
- config: effectiveConfig,
93
- onChunk: (chunk) => {
94
- aggregatedContent += chunk.contentChunk || "";
95
- if (onChunk) {
96
- onChunk(chunk);
97
- }
98
- },
99
- onFinalResponse: (resp) => {
100
- resp.content = aggregatedContent;
101
- if (onFinalResponse) {
102
- onFinalResponse(resp);
103
- }
82
+ try {
83
+ if (stream) {
84
+ if (!promptAdapter.streamFromProvider) {
85
+ throw new Error("The current adapter does not support streaming.");
104
86
  }
105
- });
106
- finalResponse.content = aggregatedContent;
87
+ let aggregatedContent = "";
88
+ finalResponse = yield promptAdapter.streamFromProvider({
89
+ messages,
90
+ tools,
91
+ toolChoice,
92
+ model: this.model.modelName,
93
+ config: effectiveConfig,
94
+ onChunk: (chunk) => {
95
+ aggregatedContent += chunk.contentChunk || "";
96
+ if (onChunk) {
97
+ onChunk(chunk);
98
+ }
99
+ },
100
+ onFinalResponse: (resp) => {
101
+ resp.content = aggregatedContent;
102
+ if (onFinalResponse) {
103
+ onFinalResponse(resp);
104
+ }
105
+ }
106
+ });
107
+ finalResponse.content = aggregatedContent;
108
+ }
109
+ else {
110
+ const providerResponse = yield promptAdapter.sendToProvider(messages, tools, toolChoice, undefined, this.model.modelName, effectiveConfig);
111
+ finalResponse = outputAdapter.parse(providerResponse);
112
+ }
107
113
  }
108
- else {
109
- const providerResponse = yield promptAdapter.sendToProvider(messages, tools, toolChoice, undefined, this.model.modelName, effectiveConfig);
110
- finalResponse = outputAdapter.parse(providerResponse);
114
+ catch (error) {
115
+ const latencyMs = Date.now() - startTime;
116
+ const errorMessage = error instanceof Error ? error.message : String(error);
117
+ const errorResponse = { content: null, raw: null, error: errorMessage };
118
+ yield this.handleLogging(messageArray, errorResponse, latencyMs, effectiveConfig === null || effectiveConfig === void 0 ? void 0 : effectiveConfig.metadata, onLog);
119
+ throw error;
111
120
  }
112
121
  const latencyMs = Date.now() - startTime;
113
122
  yield this.handleLogging(messageArray, finalResponse, latencyMs, effectiveConfig === null || effectiveConfig === void 0 ? void 0 : effectiveConfig.metadata, onLog);
@@ -22,7 +22,7 @@ const __1 = require("..");
22
22
  * Exemple de resolver : renvoie la clé pour un tenant connu, sinon `false` (refus).
23
23
  * En production, vous chargeriez la clé depuis une base, un vault, etc.
24
24
  */
25
- const demoResolver = (ctx) => {
25
+ const demoResolver = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
26
26
  const { tenantId } = ctx;
27
27
  const byTenant = {
28
28
  // Un seul slot de démo : même clé env que la config Gemini par défaut.
@@ -32,7 +32,7 @@ const demoResolver = (ctx) => {
32
32
  if (!key)
33
33
  return false;
34
34
  return key;
35
- };
35
+ });
36
36
  function main() {
37
37
  return __awaiter(this, void 0, void 0, function* () {
38
38
  if (!process.env.GOOGLE_API_KEY) {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=example-error-logging.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"example-error-logging.d.ts","sourceRoot":"","sources":["../../src/examples/example-error-logging.ts"],"names":[],"mappings":""}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const __1 = require("..");
13
+ const logger = (log) => {
14
+ var _a;
15
+ const status = log.output.error ? "ERROR" : "OK";
16
+ console.log(`\n[${status}] ${log.provider} / ${log.model} — ${log.latencyMs}ms`);
17
+ if (log.output.error) {
18
+ console.log("***** error captured in logger *****");
19
+ console.log(` error : ${log.output.error}`);
20
+ console.log(` prompt : ${JSON.stringify(log.prompt.map(m => m.content))}`);
21
+ }
22
+ else {
23
+ console.log("***** content captured in logger *****");
24
+ console.log(` content : ${(_a = log.output.content) === null || _a === void 0 ? void 0 : _a.slice(0, 80)}...`);
25
+ console.log(` tokens : ${log.usage.totalTokens}`);
26
+ }
27
+ };
28
+ function run() {
29
+ return __awaiter(this, void 0, void 0, function* () {
30
+ // --- Cas 1 : clé API invalide → erreur provider loggée ---
31
+ console.log("=== Test 1 : clé API invalide ===");
32
+ const badKeyClient = new __1.UnifiedLLMClient(__1.MODELS.GEMINI_FLASH_2_5, {
33
+ apiKey: "INVALID_KEY",
34
+ logger,
35
+ });
36
+ try {
37
+ yield badKeyClient.chat({ message: "Bonjour !" });
38
+ }
39
+ catch (_a) {
40
+ console.log(" (exception capturée par l'appelant — comportement attendu)");
41
+ }
42
+ // --- Cas 2 : appel réussi pour montrer que le logger fonctionne normalement ---
43
+ console.log("\n=== Test 2 : appel réussi ===");
44
+ const client = new __1.UnifiedLLMClient(__1.MODELS.GEMINI_FLASH_2_5, { logger });
45
+ yield client.chat({ message: "Dis-moi bonjour en une phrase." });
46
+ // --- Cas 3 : erreur loggée via onLog (logger ad-hoc par appel) ---
47
+ console.log("\n=== Test 3 : clé invalide avec onLog ===");
48
+ const badKeyClient2 = new __1.UnifiedLLMClient(__1.MODELS.GEMINI_FLASH_2_5, {
49
+ apiKey: "ANOTHER_BAD_KEY",
50
+ });
51
+ try {
52
+ yield badKeyClient2.chat({
53
+ message: "Test onLog error",
54
+ onLog: logger,
55
+ });
56
+ }
57
+ catch (_b) {
58
+ console.log(" (exception capturée par l'appelant — comportement attendu)");
59
+ }
60
+ });
61
+ }
62
+ run().catch(console.error);
@@ -29,13 +29,13 @@ function main() {
29
29
  const server = new __1.ProxyServer({
30
30
  port: PROXY_PORT,
31
31
  apiKey: PROXY_API_KEY,
32
- apiKeyResolver: (ctx) => {
32
+ apiKeyResolver: (ctx) => __awaiter(this, void 0, void 0, function* () {
33
33
  const { tenantId } = ctx;
34
34
  if (tenantId === "demo") {
35
35
  return process.env.GOOGLE_API_KEY;
36
36
  }
37
37
  return false;
38
- },
38
+ }),
39
39
  });
40
40
  yield server.start();
41
41
  console.log(`[example] Proxy listening on http://localhost:${PROXY_PORT}\n`);
@@ -132,7 +132,7 @@ class ProxyServer {
132
132
  res.end(JSON.stringify({ error: "apiKeyResolver is defined but no apiKeyContext was provided" }));
133
133
  return;
134
134
  }
135
- const result = this.options.apiKeyResolver(request.apiKeyContext);
135
+ const result = yield this.options.apiKeyResolver(request.apiKeyContext);
136
136
  if (result === false) {
137
137
  res.writeHead(403, { "Content-Type": "application/json" });
138
138
  res.end(JSON.stringify({ error: "apiKeyResolver returned false" }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@imenam/unified-llm-client",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "A unified interface for multiple LLM providers (OpenAI, Gemini, Anthropic, Qwen) with tool-calling, streaming, and audio support",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -24,7 +24,8 @@
24
24
  "example:proxy": "node dist/examples/proxy-example.js",
25
25
  "example:proxy-api-key-resolver": "node dist/examples/proxy-api-key-resolver-example.js",
26
26
  "example:api-key-resolver": "node dist/examples/api-key-resolver-example.js",
27
- "proxy:start": "node dist/examples/proxy-server-start.js"
27
+ "proxy:start": "node dist/examples/proxy-server-start.js",
28
+ "example:error-logging": "node dist/examples/example-error-logging.js"
28
29
  },
29
30
  "keywords": [
30
31
  "llm",