@imenam/unified-llm-client 1.4.1 → 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/README.md +150 -24
- package/dist/common/types.d.ts +1 -0
- package/dist/common/types.d.ts.map +1 -1
- package/dist/core/LLMAdapter.d.ts +2 -1
- package/dist/core/LLMAdapter.d.ts.map +1 -1
- package/dist/core/LLMAdapter.js +9 -9
- package/dist/core/UnifiedLLMClient.d.ts +10 -2
- package/dist/core/UnifiedLLMClient.d.ts.map +1 -1
- package/dist/core/UnifiedLLMClient.js +49 -13
- package/dist/examples/api-key-resolver-example.d.ts +2 -0
- package/dist/examples/api-key-resolver-example.d.ts.map +1 -0
- package/dist/examples/api-key-resolver-example.js +65 -0
- package/dist/examples/example.js +6 -7
- package/dist/examples/proxy-api-key-resolver-example.d.ts +2 -0
- package/dist/examples/proxy-api-key-resolver-example.d.ts.map +1 -0
- package/dist/examples/proxy-api-key-resolver-example.js +75 -0
- package/dist/examples/proxy-example.d.ts +2 -0
- package/dist/examples/proxy-example.d.ts.map +1 -0
- package/dist/examples/proxy-example.js +120 -0
- package/dist/examples/proxy-server-start.d.ts +2 -0
- package/dist/examples/proxy-server-start.d.ts.map +1 -0
- package/dist/examples/proxy-server-start.js +30 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/proxy/ProxyPromptAdapter.d.ts +17 -0
- package/dist/proxy/ProxyPromptAdapter.d.ts.map +1 -0
- package/dist/proxy/ProxyPromptAdapter.js +238 -0
- package/dist/proxy/ProxyServer.d.ts +13 -0
- package/dist/proxy/ProxyServer.d.ts.map +1 -0
- package/dist/proxy/ProxyServer.js +243 -0
- package/dist/proxy/types.d.ts +32 -0
- package/dist/proxy/types.d.ts.map +1 -0
- package/dist/proxy/types.js +2 -0
- package/package.json +6 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @imenam/unified-llm-client
|
|
2
2
|
|
|
3
|
-
A unified interface for multiple LLM providers — OpenAI, Gemini, Anthropic, and Qwen — with built-in support for tool-calling, streaming, multimodal inputs, and
|
|
3
|
+
A unified interface for multiple LLM providers — OpenAI, Gemini, Anthropic, and Qwen — with built-in support for tool-calling, streaming, multimodal inputs, audio generation, and a proxy mode for API key isolation.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
@@ -9,6 +9,9 @@ A unified interface for multiple LLM providers — OpenAI, Gemini, Anthropic, an
|
|
|
9
9
|
- **Streaming** — chunk-by-chunk responses via callbacks
|
|
10
10
|
- **Multimodal** — image and audio file inputs
|
|
11
11
|
- **Audio generation** — Text-to-Speech (Gemini)
|
|
12
|
+
- **Proxy mode** — delegate LLM calls to a server, no API keys on the client
|
|
13
|
+
- **API key resolver** — dynamically resolve API keys per request from a user-provided context
|
|
14
|
+
- **Cancellation** — native `AbortSignal` support
|
|
12
15
|
- **Usage tracking** — token counts and estimated costs
|
|
13
16
|
- **Full TypeScript support** — exported types and `.d.ts` declarations
|
|
14
17
|
|
|
@@ -35,7 +38,7 @@ Set your API keys in a `.env` file:
|
|
|
35
38
|
OPENAI_API_KEY=sk-...
|
|
36
39
|
GOOGLE_API_KEY=...
|
|
37
40
|
ANTHROPIC_API_KEY=sk-ant-...
|
|
38
|
-
|
|
41
|
+
ALIBABA_CLOUD_API_KEY=... # for Qwen
|
|
39
42
|
```
|
|
40
43
|
|
|
41
44
|
```typescript
|
|
@@ -73,6 +76,7 @@ MODELS.CLAUDE_OPUS_4_6
|
|
|
73
76
|
// Qwen
|
|
74
77
|
MODELS.QWEN_VL_3
|
|
75
78
|
MODELS.QWEN_VL_FLASH
|
|
79
|
+
MODELS.QWEN_VL_3_FLASH
|
|
76
80
|
```
|
|
77
81
|
|
|
78
82
|
You can also pass a custom model:
|
|
@@ -81,6 +85,8 @@ You can also pass a custom model:
|
|
|
81
85
|
const client = new UnifiedLLMClient({
|
|
82
86
|
provider: "openai",
|
|
83
87
|
modelName: "gpt-4o",
|
|
88
|
+
costsPer1MTokens: { prompt: 2.5, output: 10 },
|
|
89
|
+
currency: "USD",
|
|
84
90
|
});
|
|
85
91
|
```
|
|
86
92
|
|
|
@@ -124,11 +130,11 @@ console.log(response.content);
|
|
|
124
130
|
```typescript
|
|
125
131
|
const client = new UnifiedLLMClient(MODELS.GPT_4O_MINI);
|
|
126
132
|
|
|
127
|
-
|
|
133
|
+
await client.chat({
|
|
128
134
|
message: "Tell me a short story.",
|
|
129
135
|
stream: true,
|
|
130
136
|
onChunk: (chunk) => {
|
|
131
|
-
process.stdout.write(chunk.
|
|
137
|
+
process.stdout.write(chunk.contentChunk ?? "");
|
|
132
138
|
},
|
|
133
139
|
onFinalResponse: (final) => {
|
|
134
140
|
console.log("\n\nTotal tokens:", final.usage?.totalTokens);
|
|
@@ -136,23 +142,6 @@ const response = await client.chat({
|
|
|
136
142
|
});
|
|
137
143
|
```
|
|
138
144
|
|
|
139
|
-
## Conversation History
|
|
140
|
-
|
|
141
|
-
The client maintains conversation history internally. Use `getHistory()` and `setHistory()` to persist or restore it:
|
|
142
|
-
|
|
143
|
-
```typescript
|
|
144
|
-
const client = new UnifiedLLMClient(MODELS.CLAUDE_SONNET_4_6);
|
|
145
|
-
|
|
146
|
-
await client.chat({ message: "My name is Alice." });
|
|
147
|
-
await client.chat({ message: "What's my name?" });
|
|
148
|
-
// → "Your name is Alice."
|
|
149
|
-
|
|
150
|
-
const history = client.getHistory();
|
|
151
|
-
// Save and restore later:
|
|
152
|
-
const newClient = new UnifiedLLMClient(MODELS.CLAUDE_SONNET_4_6);
|
|
153
|
-
newClient.setHistory(history);
|
|
154
|
-
```
|
|
155
|
-
|
|
156
145
|
## Multimodal Inputs
|
|
157
146
|
|
|
158
147
|
Attach images or audio files directly to a message:
|
|
@@ -167,6 +156,18 @@ const response = await client.chat({
|
|
|
167
156
|
});
|
|
168
157
|
```
|
|
169
158
|
|
|
159
|
+
Multiple files are also supported:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
await client.chat({
|
|
163
|
+
message: {
|
|
164
|
+
role: "user",
|
|
165
|
+
content: "Compare these two images.",
|
|
166
|
+
attachedFile: ["./image1.jpg", "./image2.png"],
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
```
|
|
170
|
+
|
|
170
171
|
## Audio Generation (Gemini)
|
|
171
172
|
|
|
172
173
|
```typescript
|
|
@@ -181,6 +182,113 @@ const audio = await client.generateAudio({
|
|
|
181
182
|
});
|
|
182
183
|
```
|
|
183
184
|
|
|
185
|
+
## Proxy Mode
|
|
186
|
+
|
|
187
|
+
Run an HTTP server that holds your API keys server-side, and connect clients without any keys:
|
|
188
|
+
|
|
189
|
+
```typescript
|
|
190
|
+
import { ProxyServer } from "@imenam/unified-llm-client";
|
|
191
|
+
|
|
192
|
+
// Server side — API keys are read from process.env
|
|
193
|
+
const server = new ProxyServer({ port: 3000, apiKey: "my-secret" });
|
|
194
|
+
await server.start();
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
```typescript
|
|
198
|
+
// Client side — no API keys needed
|
|
199
|
+
import { UnifiedLLMClient, MODELS } from "@imenam/unified-llm-client";
|
|
200
|
+
|
|
201
|
+
const client = new UnifiedLLMClient(MODELS.GEMINI_FLASH_2_5, {
|
|
202
|
+
proxyUrl: "http://localhost:3000",
|
|
203
|
+
proxyApiKey: "my-secret",
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
// The chat() API is identical — streaming and file attachments are fully supported
|
|
207
|
+
const response = await client.chat({ message: "Hello!" });
|
|
208
|
+
```
|
|
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
|
+
|
|
236
|
+
## API Key Resolver
|
|
237
|
+
|
|
238
|
+
Dynamically resolve which API key to use for each request based on a custom context (e.g. per-user key, request metadata):
|
|
239
|
+
|
|
240
|
+
```typescript
|
|
241
|
+
import { UnifiedLLMClient, MODELS } from "@imenam/unified-llm-client";
|
|
242
|
+
import type { ApiKeyResolver } from "@imenam/unified-llm-client";
|
|
243
|
+
|
|
244
|
+
const resolver: ApiKeyResolver<{ userId: string }> = async (ctx) => {
|
|
245
|
+
const key = await userKeyStore.get(ctx.userId);
|
|
246
|
+
return key ?? false; // return false to deny access
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
const client = new UnifiedLLMClient(MODELS.GPT_4O_MINI, {
|
|
250
|
+
apiKeyResolver: resolver,
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const response = await client.chat({
|
|
254
|
+
message: "Hello!",
|
|
255
|
+
apiKeyContext: { userId: "user-123" },
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
You can also set a global default resolver for all instances:
|
|
260
|
+
|
|
261
|
+
```typescript
|
|
262
|
+
UnifiedLLMClient.setDefaultApiKeyResolver((ctx) => {
|
|
263
|
+
return ctx.apiKey ?? false;
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
> The resolver is called on every `chat()` call. Returning `false` throws an access-denied error.
|
|
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.
|
|
269
|
+
|
|
270
|
+
## Cancellation
|
|
271
|
+
|
|
272
|
+
Pass an `AbortSignal` to cancel a request mid-flight:
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
const ac = new AbortController();
|
|
276
|
+
setTimeout(() => ac.abort(), 800);
|
|
277
|
+
|
|
278
|
+
try {
|
|
279
|
+
await client.chat({
|
|
280
|
+
message: "Write a very long story...",
|
|
281
|
+
stream: true,
|
|
282
|
+
signal: ac.signal,
|
|
283
|
+
onChunk: (chunk) => process.stdout.write(chunk.contentChunk ?? ""),
|
|
284
|
+
});
|
|
285
|
+
} catch (e) {
|
|
286
|
+
if (e instanceof Error && e.name === "AbortError") {
|
|
287
|
+
console.log("Request cancelled.");
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
184
292
|
## Logging
|
|
185
293
|
|
|
186
294
|
Pass a `logger` callback to capture structured logs for every request:
|
|
@@ -188,30 +296,46 @@ Pass a `logger` callback to capture structured logs for every request:
|
|
|
188
296
|
```typescript
|
|
189
297
|
const client = new UnifiedLLMClient(MODELS.GPT_4O_MINI, {
|
|
190
298
|
logger: (log) => {
|
|
191
|
-
console.log(`[${log.provider}] ${log.model} — ${log.totalTokens} tokens`);
|
|
299
|
+
console.log(`[${log.provider}] ${log.model} — ${log.usage.totalTokens} tokens`);
|
|
192
300
|
},
|
|
193
301
|
});
|
|
194
302
|
```
|
|
195
303
|
|
|
304
|
+
Set a global default logger applied to all instances:
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
UnifiedLLMClient.setDefaultLogger((log) => {
|
|
308
|
+
console.log(`[${log.provider}] ${log.model} — ${log.usage.totalTokens} tokens`);
|
|
309
|
+
});
|
|
310
|
+
```
|
|
311
|
+
|
|
196
312
|
## API Reference
|
|
197
313
|
|
|
198
314
|
### `new UnifiedLLMClient(model, options?)`
|
|
199
315
|
|
|
200
316
|
| Parameter | Type | Description |
|
|
201
317
|
|-----------|------|-------------|
|
|
202
|
-
| `model` | `Model` | Model object from `MODELS` or a custom `{ provider, modelName }` |
|
|
318
|
+
| `model` | `Model` | Model object from `MODELS` or a custom `{ provider, modelName, costsPer1MTokens, currency }` |
|
|
203
319
|
| `options.logger` | `(log: UnifiedLog) => void` | Optional logging callback |
|
|
320
|
+
| `options.proxyUrl` | `string` | Proxy server URL — activates proxy mode |
|
|
321
|
+
| `options.proxyApiKey` | `string` | Key sent in the `x-api-key` header (proxy mode) |
|
|
322
|
+
| `options.apiKey` | `string` | Static API key passed directly to the provider (overrides env var) |
|
|
323
|
+
| `options.apiKeyResolver` | `ApiKeyResolver` | Dynamic per-request key resolver (direct mode only — in proxy mode, set this on `ProxyServer`) |
|
|
204
324
|
|
|
205
325
|
### `client.chat(options)`
|
|
206
326
|
|
|
207
327
|
| Option | Type | Default | Description |
|
|
208
328
|
|--------|------|---------|-------------|
|
|
209
|
-
| `message` | `string \| ChatMessage \| ChatMessage[]` | — | User message or history |
|
|
329
|
+
| `message` | `string \| ChatMessage \| ChatMessage[]` | — | User message or full conversation history |
|
|
210
330
|
| `tools` | `ToolDefinition[]` | `[]` | Tools available to the LLM |
|
|
211
331
|
| `toolChoice` | `"auto" \| "none" \| string` | `"auto"` | Tool selection strategy |
|
|
212
332
|
| `stream` | `boolean` | `false` | Enable streaming |
|
|
213
333
|
| `onChunk` | `(chunk: StreamedChatChunk) => void` | — | Streaming chunk callback |
|
|
214
334
|
| `onFinalResponse` | `(res: ChatResponse) => void` | — | Final response callback |
|
|
335
|
+
| `onLog` | `(log: UnifiedLog) => void` | — | Per-call log callback |
|
|
336
|
+
| `config` | `ChatOptionalConfig` | — | Advanced options: `thinkingBudget`, `metadata` |
|
|
337
|
+
| `signal` | `AbortSignal` | — | Cancellation signal |
|
|
338
|
+
| `apiKeyContext` | `unknown` | — | Context passed to `apiKeyResolver` to resolve the API key |
|
|
215
339
|
|
|
216
340
|
### `ChatResponse`
|
|
217
341
|
|
|
@@ -224,6 +348,8 @@ interface ChatResponse {
|
|
|
224
348
|
completionTokens: number;
|
|
225
349
|
totalTokens: number;
|
|
226
350
|
};
|
|
351
|
+
finishReason?: string;
|
|
352
|
+
requestId?: string;
|
|
227
353
|
raw: unknown;
|
|
228
354
|
}
|
|
229
355
|
```
|
package/dist/common/types.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type Provider = "openai" | "gemini" | "qwen" | "anthropic";
|
|
2
|
+
export type ApiKeyResolver<T = unknown> = (context: T) => Promise<string | false> | string | false;
|
|
2
3
|
export type ChatMessageRole = "user" | "assistant" | "system" | "tool" | "function";
|
|
3
4
|
export interface ChatMessage {
|
|
4
5
|
tool_call_id?: string;
|
|
@@ -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,5 +1,5 @@
|
|
|
1
1
|
import { ChatMessage, ToolDefinition, ChatResponse, ToolCall, AudioGenerationOptions, AudioGenerationResponse, UnifiedLog, Provider } from "../common/types";
|
|
2
|
-
export declare const PROVIDERS_CONFIG: Record<Provider, () => ProviderOptions>;
|
|
2
|
+
export declare const PROVIDERS_CONFIG: Record<Provider, (apiKey?: string) => ProviderOptions>;
|
|
3
3
|
export type StreamedChatChunk = {
|
|
4
4
|
contentChunk?: string;
|
|
5
5
|
toolCallChunk?: ToolCall[];
|
|
@@ -28,6 +28,7 @@ export type ChatOptionalConfig = {
|
|
|
28
28
|
thinkingBudget?: number;
|
|
29
29
|
metadata?: Record<string, any>;
|
|
30
30
|
signal?: AbortSignal;
|
|
31
|
+
apiKeyContext?: unknown;
|
|
31
32
|
};
|
|
32
33
|
export interface LogData {
|
|
33
34
|
model: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LLMAdapter.d.ts","sourceRoot":"","sources":["../../src/core/LLMAdapter.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,QAAQ,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAM7J,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,QAAQ,EAAE,MAAM,eAAe,
|
|
1
|
+
{"version":3,"file":"LLMAdapter.d.ts","sourceRoot":"","sources":["../../src/core/LLMAdapter.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,YAAY,EAAE,QAAQ,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAM7J,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,eAAe,CA4CnF,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,QAAQ,EAAE,CAAC;IAC3B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,GAAG,CAAC;IACf,KAAK,CAAC,EAAE,GAAG,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,YAAY,CAAC;CACxB,CAAC;AAEF,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,kBAAkB,CAAC;IAC5B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,aAAa,EAAE,YAAY,KAAK,IAAI,CAAC;CACzD;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAA;AAED,MAAM,WAAW,OAAO;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,QAAQ,EAAE,YAAY,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,GAAG,CAAC;IACzC,cAAc,CACZ,QAAQ,EAAE,WAAW,EAAE,EACvB,KAAK,CAAC,EAAE,cAAc,EAAE,EACxB,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EACrC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,EAChC,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,CAAC,CAAC;IAEzB,kBAAkB,CAAC,CAAC,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CACxE;AAED,MAAM,WAAW,WAAW;IAC1B,oBAAoB,CAAC,QAAQ,EAAE,QAAQ,GAAG,GAAG,CAAC;CAC/C;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,gBAAgB,EAAE,GAAG,GAAG,YAAY,CAAC;CAC5C;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,aAAa,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACjF,mBAAmB,CAAC,OAAO,EAAE,sBAAsB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAC5E,kBAAkB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CACzC;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,aAAa,EAAE,aAAa,CAAC;IAC7B,aAAa,EAAE,aAAa,CAAC;IAC7B,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAA"}
|
package/dist/core/LLMAdapter.js
CHANGED
|
@@ -24,22 +24,22 @@ const AnthropicLogAdapter_1 = require("../adapters/anthropic/AnthropicLogAdapter
|
|
|
24
24
|
const dotenv_1 = __importDefault(require("dotenv"));
|
|
25
25
|
dotenv_1.default.config();
|
|
26
26
|
exports.PROVIDERS_CONFIG = {
|
|
27
|
-
gemini: () => ({
|
|
27
|
+
gemini: (apiKey) => ({
|
|
28
28
|
promptAdapter: new GeminiPromptAdapter_1.GeminiPromptAdapter({
|
|
29
|
-
apiKey: process.env.GOOGLE_API_KEY,
|
|
29
|
+
apiKey: apiKey !== null && apiKey !== void 0 ? apiKey : process.env.GOOGLE_API_KEY,
|
|
30
30
|
model: "gemini-2.5-flash",
|
|
31
31
|
}),
|
|
32
32
|
outputAdapter: new GeminiOutputAdapter_1.GeminiOutputAdapter(),
|
|
33
33
|
toolAdapter: new GeminiToolAdapter_1.GeminiToolAdapter(),
|
|
34
34
|
audioAdapter: new GeminiAudioAdapter_1.GeminiAudioAdapter({
|
|
35
|
-
apiKey: process.env.GOOGLE_API_KEY,
|
|
35
|
+
apiKey: apiKey !== null && apiKey !== void 0 ? apiKey : process.env.GOOGLE_API_KEY,
|
|
36
36
|
model: "gemini-2.5-pro-preview-tts",
|
|
37
37
|
}),
|
|
38
38
|
logAdapter: new GeminiLogAdapter_1.GeminiLogAdapter(),
|
|
39
39
|
}),
|
|
40
|
-
openai: () => ({
|
|
40
|
+
openai: (apiKey) => ({
|
|
41
41
|
promptAdapter: new OpenAIPromptAdapter_1.OpenAIPromptAdapter({
|
|
42
|
-
apiKey: process.env.OPENAI_API_KEY,
|
|
42
|
+
apiKey: apiKey !== null && apiKey !== void 0 ? apiKey : process.env.OPENAI_API_KEY,
|
|
43
43
|
model: "gpt-4o-mini",
|
|
44
44
|
}),
|
|
45
45
|
toolAdapter: new OpenAiToolAdapter_1.OpenAiToolAdapter(),
|
|
@@ -47,9 +47,9 @@ exports.PROVIDERS_CONFIG = {
|
|
|
47
47
|
logAdapter: new OpenAiLogAdapter_1.OpenAiLogAdapter(),
|
|
48
48
|
// Note: OpenAI n'a pas encore d'audioAdapter - peut être ajouté plus tard
|
|
49
49
|
}),
|
|
50
|
-
qwen: () => ({
|
|
50
|
+
qwen: (apiKey) => ({
|
|
51
51
|
promptAdapter: new QwenPromptAdapter_1.QwenPromptAdapter({
|
|
52
|
-
apiKey: process.env.ALIBABA_CLOUD_API_KEY,
|
|
52
|
+
apiKey: apiKey !== null && apiKey !== void 0 ? apiKey : process.env.ALIBABA_CLOUD_API_KEY,
|
|
53
53
|
model: "qwen3-vl-235b-a22b-instruct",
|
|
54
54
|
baseUrl: "https://dashscope-intl.aliyuncs.com/compatible-mode/v1"
|
|
55
55
|
}),
|
|
@@ -58,9 +58,9 @@ exports.PROVIDERS_CONFIG = {
|
|
|
58
58
|
logAdapter: new QwenLogAdapter_1.QwenLogAdapter(),
|
|
59
59
|
// Note: Qwen n'a pas d'audioAdapter pour le moment
|
|
60
60
|
}),
|
|
61
|
-
anthropic: () => ({
|
|
61
|
+
anthropic: (apiKey) => ({
|
|
62
62
|
promptAdapter: new AnthropicPromptAdapter_1.AnthropicPromptAdapter({
|
|
63
|
-
apiKey: process.env.ANTHROPIC_API_KEY,
|
|
63
|
+
apiKey: apiKey !== null && apiKey !== void 0 ? apiKey : process.env.ANTHROPIC_API_KEY,
|
|
64
64
|
model: "claude-sonnet-4-5-20250929",
|
|
65
65
|
}),
|
|
66
66
|
outputAdapter: new AnthropicOutputAdapter_1.AnthropicOutputAdapter(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ChatMessage, ChatResponse, ToolDefinition, AudioGenerationOptions, AudioGenerationResponse, UnifiedLog } from "../common/types";
|
|
1
|
+
import { ApiKeyResolver, ChatMessage, ChatResponse, ToolDefinition, AudioGenerationOptions, AudioGenerationResponse, UnifiedLog } from "../common/types";
|
|
2
2
|
import { PromptAdapterOptions, StreamedChatChunk, ChatOptionalConfig } from "./LLMAdapter";
|
|
3
3
|
import { Model } from "../common/types";
|
|
4
4
|
export type UnifiedLLMClientOptions = {
|
|
@@ -16,6 +16,7 @@ export type ChatOptions = {
|
|
|
16
16
|
stream?: boolean;
|
|
17
17
|
config?: ChatOptionalConfig;
|
|
18
18
|
signal?: AbortSignal;
|
|
19
|
+
apiKeyContext?: unknown;
|
|
19
20
|
};
|
|
20
21
|
export declare class UnifiedLLMClient {
|
|
21
22
|
private promptAdapter;
|
|
@@ -26,12 +27,19 @@ export declare class UnifiedLLMClient {
|
|
|
26
27
|
private rawMessages;
|
|
27
28
|
private model;
|
|
28
29
|
private logger?;
|
|
30
|
+
private apiKeyResolver?;
|
|
31
|
+
private readonly isProxyMode;
|
|
29
32
|
static setDefaultLogger(logger: (log: UnifiedLog) => any | Promise<any>): void;
|
|
33
|
+
static setDefaultApiKeyResolver(resolver: ApiKeyResolver): void;
|
|
30
34
|
constructor(model: Model, options?: {
|
|
31
35
|
logger?: (log: UnifiedLog) => any | Promise<any>;
|
|
36
|
+
proxyUrl?: string;
|
|
37
|
+
proxyApiKey?: string;
|
|
38
|
+
apiKeyResolver?: ApiKeyResolver;
|
|
39
|
+
apiKey?: string;
|
|
32
40
|
});
|
|
33
41
|
private checkNotImplementedFeatures;
|
|
34
|
-
chat({ message, tools, toolChoice, onChunk, onFinalResponse, onLog, stream, config, signal }: ChatOptions): Promise<ChatResponse>;
|
|
42
|
+
chat({ message, tools, toolChoice, onChunk, onFinalResponse, onLog, stream, config, signal, apiKeyContext }: ChatOptions): Promise<ChatResponse>;
|
|
35
43
|
private handleLogging;
|
|
36
44
|
/**
|
|
37
45
|
* Vérifie si la génération d'audio est supportée pour le provider actuel
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UnifiedLLMClient.d.ts","sourceRoot":"","sources":["../../src/core/UnifiedLLMClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAY,cAAc,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"UnifiedLLMClient.d.ts","sourceRoot":"","sources":["../../src/core/UnifiedLLMClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,19 +11,37 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.UnifiedLLMClient = void 0;
|
|
13
13
|
const LLMAdapter_1 = require("./LLMAdapter");
|
|
14
|
+
const ProxyPromptAdapter_1 = require("../proxy/ProxyPromptAdapter");
|
|
14
15
|
class UnifiedLLMClient {
|
|
15
16
|
static setDefaultLogger(logger) {
|
|
16
17
|
global._unifiedLlmDefaultLogger = logger;
|
|
17
18
|
}
|
|
19
|
+
static setDefaultApiKeyResolver(resolver) {
|
|
20
|
+
global._unifiedLlmDefaultApiKeyResolver = resolver;
|
|
21
|
+
}
|
|
18
22
|
constructor(model, options) {
|
|
19
23
|
this.rawMessages = [];
|
|
20
24
|
this.model = model;
|
|
21
|
-
|
|
22
|
-
this.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
this.apiKeyResolver = options === null || options === void 0 ? void 0 : options.apiKeyResolver;
|
|
26
|
+
this.isProxyMode = Boolean(options === null || options === void 0 ? void 0 : options.proxyUrl);
|
|
27
|
+
if (options === null || options === void 0 ? void 0 : options.proxyUrl) {
|
|
28
|
+
const proxyAdapter = new ProxyPromptAdapter_1.ProxyPromptAdapter({
|
|
29
|
+
serverUrl: options.proxyUrl,
|
|
30
|
+
apiKey: options.proxyApiKey,
|
|
31
|
+
model,
|
|
32
|
+
});
|
|
33
|
+
this.promptAdapter = proxyAdapter;
|
|
34
|
+
this.outputAdapter = { parse: (r) => r };
|
|
35
|
+
this.toolAdapter = { formatToolCallResult: (tc) => tc };
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
const config = LLMAdapter_1.PROVIDERS_CONFIG[model.provider](options === null || options === void 0 ? void 0 : options.apiKey);
|
|
39
|
+
this.promptAdapter = config.promptAdapter;
|
|
40
|
+
this.outputAdapter = config.outputAdapter;
|
|
41
|
+
this.toolAdapter = config.toolAdapter;
|
|
42
|
+
this.audioAdapter = config.audioAdapter;
|
|
43
|
+
this.logAdapter = config.logAdapter;
|
|
44
|
+
}
|
|
27
45
|
this.logger = (options === null || options === void 0 ? void 0 : options.logger) || global._unifiedLlmDefaultLogger;
|
|
28
46
|
}
|
|
29
47
|
checkNotImplementedFeatures(message) {
|
|
@@ -32,23 +50,41 @@ class UnifiedLLMClient {
|
|
|
32
50
|
}
|
|
33
51
|
}
|
|
34
52
|
chat(_a) {
|
|
35
|
-
return __awaiter(this, arguments, void 0, function* ({ message, tools, toolChoice = "auto", onChunk, onFinalResponse, onLog, stream = false, config, signal }) {
|
|
36
|
-
|
|
53
|
+
return __awaiter(this, arguments, void 0, function* ({ message, tools, toolChoice = "auto", onChunk, onFinalResponse, onLog, stream = false, config, signal, apiKeyContext }) {
|
|
54
|
+
var _b;
|
|
55
|
+
let effectiveConfig = config !== undefined || signal !== undefined
|
|
37
56
|
? Object.assign(Object.assign({}, config), (signal !== undefined ? { signal } : {})) : undefined;
|
|
57
|
+
if (apiKeyContext !== undefined) {
|
|
58
|
+
effectiveConfig = Object.assign(Object.assign({}, effectiveConfig), { apiKeyContext });
|
|
59
|
+
}
|
|
60
|
+
const effectiveResolver = (_b = this.apiKeyResolver) !== null && _b !== void 0 ? _b : global._unifiedLlmDefaultApiKeyResolver;
|
|
61
|
+
let promptAdapter = this.promptAdapter;
|
|
62
|
+
let outputAdapter = this.outputAdapter;
|
|
63
|
+
if (!this.isProxyMode && effectiveResolver) {
|
|
64
|
+
if (apiKeyContext === undefined) {
|
|
65
|
+
throw new Error("[ApiKeyResolver] A resolver is defined but no apiKeyContext was provided.");
|
|
66
|
+
}
|
|
67
|
+
const resolvedKey = yield effectiveResolver(apiKeyContext);
|
|
68
|
+
if (resolvedKey === false)
|
|
69
|
+
throw new Error("[ApiKeyResolver] The resolver returned false — access denied.");
|
|
70
|
+
const resolved = LLMAdapter_1.PROVIDERS_CONFIG[this.model.provider](resolvedKey);
|
|
71
|
+
promptAdapter = resolved.promptAdapter;
|
|
72
|
+
outputAdapter = resolved.outputAdapter;
|
|
73
|
+
}
|
|
38
74
|
const startTime = Date.now();
|
|
39
75
|
const messageArray = Array.isArray(message)
|
|
40
76
|
? message
|
|
41
77
|
: [typeof message === "string" ? { role: "user", content: message } : message];
|
|
42
78
|
for (const m of messageArray)
|
|
43
79
|
this.checkNotImplementedFeatures(m);
|
|
44
|
-
const messages = yield Promise.all(messageArray.map(m =>
|
|
80
|
+
const messages = yield Promise.all(messageArray.map(m => promptAdapter.formatMessage(m)));
|
|
45
81
|
let finalResponse;
|
|
46
82
|
if (stream) {
|
|
47
|
-
if (!
|
|
83
|
+
if (!promptAdapter.streamFromProvider) {
|
|
48
84
|
throw new Error("The current adapter does not support streaming.");
|
|
49
85
|
}
|
|
50
86
|
let aggregatedContent = "";
|
|
51
|
-
finalResponse = yield
|
|
87
|
+
finalResponse = yield promptAdapter.streamFromProvider({
|
|
52
88
|
messages,
|
|
53
89
|
tools,
|
|
54
90
|
toolChoice,
|
|
@@ -70,8 +106,8 @@ class UnifiedLLMClient {
|
|
|
70
106
|
finalResponse.content = aggregatedContent;
|
|
71
107
|
}
|
|
72
108
|
else {
|
|
73
|
-
const providerResponse = yield
|
|
74
|
-
finalResponse =
|
|
109
|
+
const providerResponse = yield promptAdapter.sendToProvider(messages, tools, toolChoice, undefined, this.model.modelName, effectiveConfig);
|
|
110
|
+
finalResponse = outputAdapter.parse(providerResponse);
|
|
75
111
|
}
|
|
76
112
|
const latencyMs = Date.now() - startTime;
|
|
77
113
|
yield this.handleLogging(messageArray, finalResponse, latencyMs, effectiveConfig === null || effectiveConfig === void 0 ? void 0 : effectiveConfig.metadata, onLog);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-key-resolver-example.d.ts","sourceRoot":"","sources":["../../src/examples/api-key-resolver-example.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,65 @@
|
|
|
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
|
+
/**
|
|
13
|
+
* Exemple : résolution dynamique de clé API (`apiKeyResolver`) en mode direct,
|
|
14
|
+
* sans serveur proxy. Les clés viennent ici d’un mapping applicatif (ex. par tenant).
|
|
15
|
+
*
|
|
16
|
+
* Prérequis : `GOOGLE_API_KEY` dans l’environnement (provider Gemini dans cet exemple).
|
|
17
|
+
*
|
|
18
|
+
* Lancer après build : `npm run example:api-key-resolver`
|
|
19
|
+
*/
|
|
20
|
+
const __1 = require("..");
|
|
21
|
+
/**
|
|
22
|
+
* Exemple de resolver : renvoie la clé pour un tenant connu, sinon `false` (refus).
|
|
23
|
+
* En production, vous chargeriez la clé depuis une base, un vault, etc.
|
|
24
|
+
*/
|
|
25
|
+
const demoResolver = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
|
|
26
|
+
const { tenantId } = ctx;
|
|
27
|
+
const byTenant = {
|
|
28
|
+
// Un seul slot de démo : même clé env que la config Gemini par défaut.
|
|
29
|
+
demo: process.env.GOOGLE_API_KEY,
|
|
30
|
+
};
|
|
31
|
+
const key = byTenant[tenantId];
|
|
32
|
+
if (!key)
|
|
33
|
+
return false;
|
|
34
|
+
return key;
|
|
35
|
+
});
|
|
36
|
+
function main() {
|
|
37
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
38
|
+
if (!process.env.GOOGLE_API_KEY) {
|
|
39
|
+
console.error("[api-key-resolver-example] Définissez GOOGLE_API_KEY pour exécuter cet exemple.");
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
const client = new __1.UnifiedLLMClient(__1.MODELS.GEMINI_FLASH_2_5, {
|
|
43
|
+
apiKeyResolver: demoResolver,
|
|
44
|
+
});
|
|
45
|
+
console.log("--- Chat avec apiKeyContext (resolver instance) ---\n");
|
|
46
|
+
const res = yield client.chat({
|
|
47
|
+
message: "Réponds en une courte phrase : que signifie « apiKeyResolver » dans ce SDK ?",
|
|
48
|
+
apiKeyContext: { tenantId: "demo" },
|
|
49
|
+
});
|
|
50
|
+
console.log(res.content);
|
|
51
|
+
console.log();
|
|
52
|
+
console.log("--- setDefaultApiKeyResolver (global) + même appel ---\n");
|
|
53
|
+
__1.UnifiedLLMClient.setDefaultApiKeyResolver(demoResolver);
|
|
54
|
+
const clientNoInstanceResolver = new __1.UnifiedLLMClient(__1.MODELS.GEMINI_FLASH_2_5);
|
|
55
|
+
const res2 = yield clientNoInstanceResolver.chat({
|
|
56
|
+
message: "Dis seulement : OK.",
|
|
57
|
+
apiKeyContext: { tenantId: "demo" },
|
|
58
|
+
});
|
|
59
|
+
console.log(res2.content);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
main().catch((err) => {
|
|
63
|
+
console.error(err);
|
|
64
|
+
process.exit(1);
|
|
65
|
+
});
|
package/dist/examples/example.js
CHANGED
|
@@ -243,7 +243,6 @@ function getExercisesBoundaries() {
|
|
|
243
243
|
}
|
|
244
244
|
function exerciseMatching() {
|
|
245
245
|
return __awaiter(this, void 0, void 0, function* () {
|
|
246
|
-
var _a, _b;
|
|
247
246
|
const answers = [
|
|
248
247
|
{
|
|
249
248
|
index: 0,
|
|
@@ -310,14 +309,14 @@ function exerciseMatching() {
|
|
|
310
309
|
const res = yield client.chat({
|
|
311
310
|
message: {
|
|
312
311
|
role: "user",
|
|
313
|
-
content: "Voila les reponses des etudiants, et le sujet du devoir. Peux-tu les associer ? answers:\n" + JSON.stringify(answers),
|
|
314
|
-
|
|
312
|
+
// content: "Voila les reponses des etudiants, et le sujet du devoir. Peux-tu les associer ? answers:\n" + JSON.stringify(answers),
|
|
313
|
+
content: "Quelles sont les questions du sujet ?",
|
|
315
314
|
attachedFile: "./media/subject.pdf"
|
|
316
315
|
},
|
|
317
316
|
tools: [tool]
|
|
318
317
|
});
|
|
319
|
-
|
|
320
|
-
console.log("response",
|
|
318
|
+
console.log("response", res.content);
|
|
319
|
+
// console.log("response", res.toolCalls?.[0]?.arguments);
|
|
321
320
|
});
|
|
322
321
|
}
|
|
323
322
|
function parseSubject() {
|
|
@@ -364,7 +363,7 @@ function multiFileSend() {
|
|
|
364
363
|
console.log("response", res.content);
|
|
365
364
|
});
|
|
366
365
|
}
|
|
367
|
-
basicUsage();
|
|
366
|
+
// basicUsage();
|
|
368
367
|
// Annulation d'une requête chat (côté app consommatrice) :
|
|
369
368
|
// const ac = new AbortController();
|
|
370
369
|
// setTimeout(() => ac.abort(), 500);
|
|
@@ -373,6 +372,6 @@ basicUsage();
|
|
|
373
372
|
// basicUsageWithAudio();
|
|
374
373
|
// basicAudioGeneration();
|
|
375
374
|
// getExercisesBoundaries();
|
|
376
|
-
|
|
375
|
+
exerciseMatching();
|
|
377
376
|
// parseSubject();
|
|
378
377
|
// multiFileSend();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proxy-api-key-resolver-example.d.ts","sourceRoot":"","sources":["../../src/examples/proxy-api-key-resolver-example.ts"],"names":[],"mappings":""}
|