universal-llm-client 4.3.0 → 4.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -24
- package/README.md +60 -11
- package/dist/ai-model.d.ts +12 -1
- package/dist/ai-model.d.ts.map +1 -1
- package/dist/ai-model.js +36 -1
- package/dist/ai-model.js.map +1 -1
- package/dist/auditor.js.map +1 -1
- package/dist/client.js.map +1 -1
- package/dist/gemma-channel.d.ts +14 -0
- package/dist/gemma-channel.d.ts.map +1 -0
- package/dist/gemma-channel.js +38 -0
- package/dist/gemma-channel.js.map +1 -0
- package/dist/gemma-diffusion.d.ts +49 -0
- package/dist/gemma-diffusion.d.ts.map +1 -0
- package/dist/gemma-diffusion.js +147 -0
- package/dist/gemma-diffusion.js.map +1 -0
- package/dist/http.d.ts +4 -0
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +14 -1
- package/dist/http.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/interfaces.d.ts +163 -7
- package/dist/interfaces.d.ts.map +1 -1
- package/dist/interfaces.js.map +1 -1
- package/dist/mcp.js.map +1 -1
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +28 -3
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/google.d.ts +22 -1
- package/dist/providers/google.d.ts.map +1 -1
- package/dist/providers/google.js +223 -13
- package/dist/providers/google.js.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/ollama.d.ts +2 -0
- package/dist/providers/ollama.d.ts.map +1 -1
- package/dist/providers/ollama.js +59 -30
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.d.ts +14 -0
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +200 -22
- package/dist/providers/openai.js.map +1 -1
- package/dist/router.d.ts +2 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +4 -0
- package/dist/router.js.map +1 -1
- package/dist/stream-decoder.d.ts +12 -0
- package/dist/stream-decoder.d.ts.map +1 -1
- package/dist/stream-decoder.js +182 -5
- package/dist/stream-decoder.js.map +1 -1
- package/dist/structured-output.js.map +1 -1
- package/dist/thinking.d.ts +36 -0
- package/dist/thinking.d.ts.map +1 -0
- package/dist/thinking.js +52 -0
- package/dist/thinking.js.map +1 -0
- package/dist/tools.js.map +1 -1
- package/dist/zod-adapter.js.map +1 -1
- package/package.json +4 -1
- package/src/ai-model.ts +400 -0
- package/src/auditor.ts +213 -0
- package/src/client.ts +402 -0
- package/src/debug/debug-google-streaming.ts +97 -0
- package/src/debug/debug-tool-execution.ts +86 -0
- package/src/debug/test-lmstudio-tools.ts +155 -0
- package/src/demos/README.md +47 -0
- package/src/demos/basic/universal-llm-examples.ts +161 -0
- package/src/demos/diffusion-gemma/.env +29 -0
- package/src/demos/diffusion-gemma/.env.example +27 -0
- package/src/demos/diffusion-gemma/CLAUDE.md +95 -0
- package/src/demos/diffusion-gemma/README.md +59 -0
- package/src/demos/diffusion-gemma/canvas.ts +1606 -0
- package/src/demos/diffusion-gemma/docker-compose.yml +29 -0
- package/src/demos/diffusion-gemma/probe-stream.ts +51 -0
- package/src/demos/diffusion-gemma/probe-tools.ts +55 -0
- package/src/demos/diffusion-gemma/server.ts +1205 -0
- package/src/demos/diffusion-gemma/start-vllm.sh +98 -0
- package/src/demos/mcp/astrid-memory-demo.ts +295 -0
- package/src/demos/mcp/astrid-persona-memory.ts +357 -0
- package/src/demos/mcp/mcp-mongodb-demo.ts +275 -0
- package/src/demos/mcp/simple-astrid-memory.ts +148 -0
- package/src/demos/mcp/simple-mcp-demo.ts +68 -0
- package/src/demos/mcp/working-mcp-demo.ts +62 -0
- package/src/demos/model-alias-demo.ts +0 -0
- package/src/demos/tools/RAG_MEMORY_INTEGRATION.md +267 -0
- package/src/demos/tools/astrid-memory-demo.ts +270 -0
- package/src/demos/tools/astrid-production-memory-clean.ts +785 -0
- package/src/demos/tools/astrid-production-memory.ts +558 -0
- package/src/demos/tools/basic-translation-test.ts +66 -0
- package/src/demos/tools/chromadb-similarity-tuning.ts +390 -0
- package/src/demos/tools/clean-multilingual-conversation.ts +209 -0
- package/src/demos/tools/clean-translation-test.ts +119 -0
- package/src/demos/tools/clean-universal-multilingual-test.ts +131 -0
- package/src/demos/tools/complete-rag-demo.ts +369 -0
- package/src/demos/tools/complete-tool-demo.ts +132 -0
- package/src/demos/tools/demo-tool-calling.ts +124 -0
- package/src/demos/tools/dynamic-language-switching-test.ts +251 -0
- package/src/demos/tools/hybrid-thinking-test.ts +154 -0
- package/src/demos/tools/memory-integration-test.ts +420 -0
- package/src/demos/tools/multilingual-memory-system.ts +802 -0
- package/src/demos/tools/ondemand-translation-demo.ts +655 -0
- package/src/demos/tools/production-tool-demo.ts +245 -0
- package/src/demos/tools/revolutionary-multilingual-test.ts +151 -0
- package/src/demos/tools/rigorous-language-analysis.ts +218 -0
- package/src/demos/tools/test-universal-memory-system.ts +126 -0
- package/src/demos/tools/translation-integration-guide.ts +346 -0
- package/src/demos/tools/universal-memory-system.ts +560 -0
- package/src/gemma-channel.ts +47 -0
- package/src/gemma-diffusion.ts +167 -0
- package/src/http.ts +261 -0
- package/src/index.ts +180 -0
- package/src/interfaces.ts +843 -0
- package/src/mcp.ts +345 -0
- package/src/providers/anthropic.ts +796 -0
- package/src/providers/google.ts +840 -0
- package/src/providers/index.ts +8 -0
- package/src/providers/ollama.ts +503 -0
- package/src/providers/openai.ts +587 -0
- package/src/router.ts +785 -0
- package/src/stream-decoder.ts +535 -0
- package/src/structured-output.ts +759 -0
- package/src/test-scripts/test-advanced-tools.ts +310 -0
- package/src/test-scripts/test-google-deep-research.ts +33 -0
- package/src/test-scripts/test-google-streaming-enhanced.ts +147 -0
- package/src/test-scripts/test-google-streaming.ts +63 -0
- package/src/test-scripts/test-google-system-prompt-comprehensive.ts +189 -0
- package/src/test-scripts/test-google-thinking.ts +46 -0
- package/src/test-scripts/test-mcp-config.ts +28 -0
- package/src/test-scripts/test-mcp-connection.ts +29 -0
- package/src/test-scripts/test-system-message-positions.ts +163 -0
- package/src/test-scripts/test-system-prompt-improvement-demo.ts +83 -0
- package/src/test-scripts/test-tool-calling.ts +231 -0
- package/src/test-scripts/test-vllm-qwen36.ts +256 -0
- package/src/tests/ai-model.test.ts +1614 -0
- package/src/tests/auditor.test.ts +224 -0
- package/src/tests/gemma-diffusion.test.ts +115 -0
- package/src/tests/http.test.ts +200 -0
- package/src/tests/interfaces.test.ts +117 -0
- package/src/tests/providers/anthropic.test.ts +118 -0
- package/src/tests/providers/google.test.ts +841 -0
- package/src/tests/providers/ollama.test.ts +1034 -0
- package/src/tests/providers/openai.test.ts +1511 -0
- package/src/tests/router.test.ts +254 -0
- package/src/tests/stream-decoder.test.ts +263 -0
- package/src/tests/structured-output.test.ts +1450 -0
- package/src/tests/thinking.test.ts +65 -0
- package/src/tests/tools.test.ts +175 -0
- package/src/thinking.ts +73 -0
- package/src/tools.ts +246 -0
- package/src/zod-adapter.ts +72 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified thinking/reasoning resolution shared by all providers.
|
|
3
|
+
*
|
|
4
|
+
* Applications set a single `thinking` value — `true`/`false` or a level
|
|
5
|
+
* ('minimal' | 'low' | 'medium' | 'high') — at the model level and/or per call.
|
|
6
|
+
* Each provider maps the resolved intent to its native control (Gemini
|
|
7
|
+
* `thinkingLevel`/`thinkingBudget`, OpenAI `reasoning_effort`, vLLM
|
|
8
|
+
* `enable_thinking`, Anthropic `budget_tokens`, Ollama `think`).
|
|
9
|
+
*/
|
|
10
|
+
import type { ThinkingLevel } from './interfaces.js';
|
|
11
|
+
export interface ResolvedThinking {
|
|
12
|
+
/** Whether reasoning should be enabled at all. */
|
|
13
|
+
enabled: boolean;
|
|
14
|
+
/** Explicit level when the user provided one (absent for a bare `true`). */
|
|
15
|
+
level?: ThinkingLevel;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Resolve the effective thinking intent from a per-call value (highest
|
|
19
|
+
* precedence) and the model-level config value. Returns `undefined` when
|
|
20
|
+
* neither is set, so providers omit the control entirely (and don't perturb
|
|
21
|
+
* servers that reject unknown fields).
|
|
22
|
+
*/
|
|
23
|
+
export declare function resolveThinking(perCall: boolean | ThinkingLevel | undefined, config: boolean | ThinkingLevel | undefined): ResolvedThinking | undefined;
|
|
24
|
+
/** Heuristic: OpenAI reasoning models use `reasoning_effort` (o-series, GPT-5). */
|
|
25
|
+
export declare function isOpenAIReasoningModel(model: string): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Gemini 2.5 `thinkingBudget` for a level. 0 disables, -1 is dynamic, and the
|
|
28
|
+
* Flash range is 0–24576. A bare `true` (no level) maps to dynamic (-1).
|
|
29
|
+
*/
|
|
30
|
+
export declare function geminiThinkingBudget(level: ThinkingLevel | undefined): number;
|
|
31
|
+
/**
|
|
32
|
+
* Anthropic extended-thinking `budget_tokens` for a level, kept >= 1024 (the
|
|
33
|
+
* API minimum) and < `maxTokens` (the API requires headroom for the answer).
|
|
34
|
+
*/
|
|
35
|
+
export declare function anthropicThinkingBudget(level: ThinkingLevel | undefined, maxTokens: number): number;
|
|
36
|
+
//# sourceMappingURL=thinking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thinking.d.ts","sourceRoot":"","sources":["../src/thinking.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,WAAW,gBAAgB;IAC7B,kDAAkD;IAClD,OAAO,EAAE,OAAO,CAAC;IACjB,4EAA4E;IAC5E,KAAK,CAAC,EAAE,aAAa,CAAC;CACzB;AAQD;;;;;GAKG;AACH,wBAAgB,eAAe,CAC3B,OAAO,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,EAC5C,MAAM,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS,GAC5C,gBAAgB,GAAG,SAAS,CAO9B;AAED,mFAAmF;AACnF,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE7D;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,MAAM,CAQ7E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAOnG"}
|
package/dist/thinking.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const LEVELS = ['minimal', 'low', 'medium', 'high'];
|
|
2
|
+
function isLevel(v) {
|
|
3
|
+
return typeof v === 'string' && LEVELS.includes(v);
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Resolve the effective thinking intent from a per-call value (highest
|
|
7
|
+
* precedence) and the model-level config value. Returns `undefined` when
|
|
8
|
+
* neither is set, so providers omit the control entirely (and don't perturb
|
|
9
|
+
* servers that reject unknown fields).
|
|
10
|
+
*/
|
|
11
|
+
export function resolveThinking(perCall, config) {
|
|
12
|
+
const value = perCall ?? config;
|
|
13
|
+
if (value === undefined)
|
|
14
|
+
return undefined;
|
|
15
|
+
if (value === false)
|
|
16
|
+
return { enabled: false };
|
|
17
|
+
if (value === true)
|
|
18
|
+
return { enabled: true };
|
|
19
|
+
if (isLevel(value))
|
|
20
|
+
return { enabled: true, level: value };
|
|
21
|
+
return undefined; // unknown string — ignore defensively
|
|
22
|
+
}
|
|
23
|
+
/** Heuristic: OpenAI reasoning models use `reasoning_effort` (o-series, GPT-5). */
|
|
24
|
+
export function isOpenAIReasoningModel(model) {
|
|
25
|
+
return /^(o\d|gpt-5)/i.test(model);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Gemini 2.5 `thinkingBudget` for a level. 0 disables, -1 is dynamic, and the
|
|
29
|
+
* Flash range is 0–24576. A bare `true` (no level) maps to dynamic (-1).
|
|
30
|
+
*/
|
|
31
|
+
export function geminiThinkingBudget(level) {
|
|
32
|
+
switch (level) {
|
|
33
|
+
case 'minimal': return 512;
|
|
34
|
+
case 'low': return 2048;
|
|
35
|
+
case 'medium': return 8192;
|
|
36
|
+
case 'high': return 24576;
|
|
37
|
+
default: return -1; // enabled without an explicit level → dynamic
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Anthropic extended-thinking `budget_tokens` for a level, kept >= 1024 (the
|
|
42
|
+
* API minimum) and < `maxTokens` (the API requires headroom for the answer).
|
|
43
|
+
*/
|
|
44
|
+
export function anthropicThinkingBudget(level, maxTokens) {
|
|
45
|
+
const base = level === 'high' ? 16384
|
|
46
|
+
: level === 'medium' ? 4096
|
|
47
|
+
: level === 'low' ? 1024
|
|
48
|
+
: level === 'minimal' ? 1024
|
|
49
|
+
: 2048; // bare `true`
|
|
50
|
+
return Math.max(1024, Math.min(base, maxTokens - 1024));
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=thinking.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"thinking.js","sourceRoot":"","sources":["../src/thinking.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,GAAsB,CAAC,SAAS,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;AAEvE,SAAS,OAAO,CAAC,CAAU;IACvB,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACvD,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC3B,OAA4C,EAC5C,MAA2C;IAE3C,MAAM,KAAK,GAAG,OAAO,IAAI,MAAM,CAAC;IAChC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC/C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7C,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC3D,OAAO,SAAS,CAAC,CAAC,sCAAsC;AAC5D,CAAC;AAED,mFAAmF;AACnF,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAChD,OAAO,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACvC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAgC;IACjE,QAAQ,KAAK,EAAE,CAAC;QACZ,KAAK,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC;QAC3B,KAAK,KAAK,CAAC,CAAC,OAAO,IAAI,CAAC;QACxB,KAAK,QAAQ,CAAC,CAAC,OAAO,IAAI,CAAC;QAC3B,KAAK,MAAM,CAAC,CAAC,OAAO,KAAK,CAAC;QAC1B,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,8CAA8C;IACtE,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CAAC,KAAgC,EAAE,SAAiB;IACvF,MAAM,IAAI,GAAG,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK;QACjC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI;YAC3B,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,IAAI;gBACxB,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI;oBAC5B,CAAC,CAAC,IAAI,CAAC,CAAC,cAAc;IAC1B,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC"}
|
package/dist/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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","sourcesContent":["/**\r\n * Universal LLM Client v3 — Tool Utilities\r\n *\r\n * ToolBuilder: Type-safe tool definition builder with fluent API.\r\n * ToolExecutor: Execution wrappers with timeout and validation.\r\n */\r\n\r\nimport type { LLMFunction, LLMToolDefinition, ToolHandler } from './interfaces.js';\r\n\r\n// ============================================================================\r\n// ToolBuilder\r\n// ============================================================================\r\n\r\n/**\r\n * Fluent builder for LLM tool definitions.\r\n *\r\n * Usage:\r\n * const tool = new ToolBuilder('get_weather')\r\n * .description('Get current weather for a location')\r\n * .addParameter('location', 'string', 'City name', true)\r\n * .addParameter('units', 'string', 'Temperature units', false, { enum: ['celsius', 'fahrenheit'] })\r\n * .build();\r\n */\r\nexport class ToolBuilder {\r\n private name: string;\r\n private desc: string = '';\r\n private properties: Record<string, unknown> = {};\r\n private required: string[] = [];\r\n\r\n constructor(name: string) {\r\n this.name = name;\r\n }\r\n\r\n description(desc: string): this {\r\n this.desc = desc;\r\n return this;\r\n }\r\n\r\n addParameter(\r\n name: string,\r\n type: string,\r\n description: string,\r\n isRequired: boolean = false,\r\n extra?: Record<string, unknown>,\r\n ): this {\r\n this.properties[name] = {\r\n type,\r\n description,\r\n ...extra,\r\n };\r\n if (isRequired) {\r\n this.required.push(name);\r\n }\r\n return this;\r\n }\r\n\r\n build(): LLMToolDefinition {\r\n return {\r\n type: 'function',\r\n function: {\r\n name: this.name,\r\n description: this.desc,\r\n parameters: {\r\n type: 'object',\r\n properties: this.properties,\r\n required: this.required.length > 0 ? this.required : undefined,\r\n },\r\n },\r\n };\r\n }\r\n\r\n /** Build and return the function definition only */\r\n buildFunction(): LLMFunction {\r\n return this.build().function;\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// ToolExecutor\r\n// ============================================================================\r\n\r\n/**\r\n * Utility wrappers for creating safe tool handlers.\r\n */\r\nexport class ToolExecutor {\r\n /**\r\n * Wrap a handler with a timeout.\r\n * Rejects if the handler doesn't complete within the specified ms.\r\n */\r\n static withTimeout(handler: ToolHandler, timeoutMs: number): ToolHandler {\r\n return async (args: unknown) => {\r\n const result = await Promise.race([\r\n Promise.resolve(handler(args)),\r\n new Promise<never>((_, reject) =>\r\n setTimeout(() => reject(new Error(`Tool execution timeout after ${timeoutMs}ms`)), timeoutMs),\r\n ),\r\n ]);\r\n return result;\r\n };\r\n }\r\n\r\n /**\r\n * Wrap a handler to catch errors and return them as strings\r\n * instead of throwing.\r\n */\r\n static safe(handler: ToolHandler): ToolHandler {\r\n return async (args: unknown) => {\r\n try {\r\n return await handler(args);\r\n } catch (error) {\r\n return {\r\n error: error instanceof Error ? error.message : String(error),\r\n };\r\n }\r\n };\r\n }\r\n\r\n /**\r\n * Wrap a handler with argument validation.\r\n * Checks that required fields are present before execution.\r\n */\r\n static withValidation(\r\n handler: ToolHandler,\r\n requiredFields: string[],\r\n ): ToolHandler {\r\n return async (args: unknown) => {\r\n if (!args || typeof args !== 'object') {\r\n throw new Error('Tool arguments must be an object');\r\n }\r\n const obj = args as Record<string, unknown>;\r\n for (const field of requiredFields) {\r\n if (obj[field] === undefined || obj[field] === null) {\r\n throw new Error(`Missing required argument: ${field}`);\r\n }\r\n }\r\n return handler(args);\r\n };\r\n }\r\n\r\n /**\r\n * Create a handler that measures execution time and\r\n * returns both the result and duration.\r\n */\r\n static timed(handler: ToolHandler): ToolHandler {\r\n return async (args: unknown) => {\r\n const start = Date.now();\r\n const result = await handler(args);\r\n return {\r\n result,\r\n duration: Date.now() - start,\r\n };\r\n };\r\n }\r\n\r\n /**\r\n * Compose multiple wrappers around a handler.\r\n * Applied from right to left (innermost to outermost).\r\n */\r\n static compose(\r\n handler: ToolHandler,\r\n ...wrappers: Array<(h: ToolHandler) => ToolHandler>\r\n ): ToolHandler {\r\n return wrappers.reduceRight((h, wrapper) => wrapper(h), handler);\r\n }\r\n}\r\n\r\n// ============================================================================\r\n// Common Tool Definitions\r\n// ============================================================================\r\n\r\n/**\r\n * Create a get_current_time tool definition and handler.\r\n */\r\nexport function createTimeTool(): {\r\n name: string;\r\n description: string;\r\n parameters: LLMFunction['parameters'];\r\n handler: ToolHandler;\r\n} {\r\n return {\r\n name: 'get_current_time',\r\n description: 'Get the current date and time',\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n timezone: {\r\n type: 'string',\r\n description: 'IANA timezone (e.g. \"America/New_York\"). Defaults to UTC.',\r\n },\r\n },\r\n },\r\n handler: (args: unknown) => {\r\n const { timezone } = (args ?? {}) as { timezone?: string };\r\n const now = new Date();\r\n try {\r\n return {\r\n iso: now.toISOString(),\r\n formatted: now.toLocaleString('en-US', {\r\n timeZone: timezone || 'UTC',\r\n dateStyle: 'full',\r\n timeStyle: 'long',\r\n }),\r\n timezone: timezone || 'UTC',\r\n timestamp: now.getTime(),\r\n };\r\n } catch {\r\n return {\r\n iso: now.toISOString(),\r\n formatted: now.toUTCString(),\r\n timezone: 'UTC',\r\n timestamp: now.getTime(),\r\n };\r\n }\r\n },\r\n };\r\n}\r\n\r\n/**\r\n * Create a random_number tool definition and handler.\r\n */\r\nexport function createRandomNumberTool(): {\r\n name: string;\r\n description: string;\r\n parameters: LLMFunction['parameters'];\r\n handler: ToolHandler;\r\n} {\r\n return {\r\n name: 'random_number',\r\n description: 'Generate a random number within a range',\r\n parameters: {\r\n type: 'object',\r\n properties: {\r\n min: { type: 'number', description: 'Minimum value (default 0)' },\r\n max: { type: 'number', description: 'Maximum value (default 100)' },\r\n },\r\n },\r\n handler: (args: unknown) => {\r\n const { min = 0, max = 100 } = (args ?? {}) as { min?: number; max?: number };\r\n return {\r\n value: Math.floor(Math.random() * (max - min + 1)) + min,\r\n min,\r\n max,\r\n };\r\n },\r\n };\r\n}\r\n"]}
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/dist/zod-adapter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"zod-adapter.js","sourceRoot":"","sources":["../src/zod-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,OAAO,CACnB,MAAoB,EACpB,OAAiD;IAEjD,gEAAgE;IAChE,MAAM,aAAa,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE;QACzC,MAAM,EAAE,UAAU;QAClB,eAAe,EAAE,KAAK;KACzB,CAAC,CAAC;IAEH,0DAA0D;IAC1D,MAAM,UAAU,GAAG,EAAE,GAAG,aAAa,EAA6B,CAAC;IACnE,OAAO,UAAU,CAAC,OAAO,CAAC;IAE1B,OAAO;QACH,UAAU,EAAE,UAAyD;QACrE,QAAQ,EAAE,CAAC,IAAa,EAAK,EAAE;YAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAClB,MAAM,MAAM,CAAC,KAAK,CAAC;YACvB,CAAC;YACD,OAAO,MAAM,CAAC,IAAI,CAAC;QACvB,CAAC;QACD,IAAI,EAAE,OAAO,EAAE,IAAI;QACnB,WAAW,EAAE,OAAO,EAAE,WAAW;KACpC,CAAC;AACN,CAAC;AAED,yEAAyE;AACzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC"
|
|
1
|
+
{"version":3,"file":"zod-adapter.js","sourceRoot":"","sources":["../src/zod-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,OAAO,CACnB,MAAoB,EACpB,OAAiD;IAEjD,gEAAgE;IAChE,MAAM,aAAa,GAAG,CAAC,CAAC,YAAY,CAAC,MAAM,EAAE;QACzC,MAAM,EAAE,UAAU;QAClB,eAAe,EAAE,KAAK;KACzB,CAAC,CAAC;IAEH,0DAA0D;IAC1D,MAAM,UAAU,GAAG,EAAE,GAAG,aAAa,EAA6B,CAAC;IACnE,OAAO,UAAU,CAAC,OAAO,CAAC;IAE1B,OAAO;QACH,UAAU,EAAE,UAAyD;QACrE,QAAQ,EAAE,CAAC,IAAa,EAAK,EAAE;YAC3B,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAClB,MAAM,MAAM,CAAC,KAAK,CAAC;YACvB,CAAC;YACD,OAAO,MAAM,CAAC,IAAI,CAAC;QACvB,CAAC;QACD,IAAI,EAAE,OAAO,EAAE,IAAI;QACnB,WAAW,EAAE,OAAO,EAAE,WAAW;KACpC,CAAC;AACN,CAAC;AAED,yEAAyE;AACzE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "universal-llm-client",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A universal LLM client with transparent provider failover, streaming tool execution, pluggable reasoning, and native observability.",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"files": [
|
|
43
43
|
"dist",
|
|
44
|
+
"src",
|
|
44
45
|
"README.md",
|
|
45
46
|
"CHANGELOG.md",
|
|
46
47
|
"LICENSE"
|
|
@@ -54,6 +55,8 @@
|
|
|
54
55
|
"test": "bun test",
|
|
55
56
|
"typecheck": "tsc --noEmit",
|
|
56
57
|
"lint": "tsc --noEmit --strict",
|
|
58
|
+
"demo:diffusion-gemma": "bun run src/demos/diffusion-gemma/server.ts",
|
|
59
|
+
"demo:diffusion-gemma:engine": "docker compose -f src/demos/diffusion-gemma/docker-compose.yml up -d",
|
|
57
60
|
"prepack": "bun run build",
|
|
58
61
|
"docs:dev": "vitepress dev docs",
|
|
59
62
|
"docs:build": "vitepress build docs",
|
package/src/ai-model.ts
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Universal LLM Client v3 — AIModel (The Universal Client)
|
|
3
|
+
*
|
|
4
|
+
* The only public-facing class. Developers configure one model with
|
|
5
|
+
* multiple provider backends for transparent failover.
|
|
6
|
+
*
|
|
7
|
+
* Provider classes are internal — the user never imports them.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
AIModelApiType,
|
|
12
|
+
type AIModelConfig,
|
|
13
|
+
type ProviderConfig,
|
|
14
|
+
type LLMClientOptions,
|
|
15
|
+
type LLMChatMessage,
|
|
16
|
+
type LLMChatResponse,
|
|
17
|
+
type ChatOptions,
|
|
18
|
+
type ModelMetadata,
|
|
19
|
+
type LLMFunction,
|
|
20
|
+
type ToolHandler,
|
|
21
|
+
type DeepResearchOptions,
|
|
22
|
+
type DeepResearchResult,
|
|
23
|
+
type DeepResearchEvent,
|
|
24
|
+
} from './interfaces.js';
|
|
25
|
+
import type { DecodedEvent } from './stream-decoder.js';
|
|
26
|
+
import { Router, type RouterConfig, type ProviderStatus } from './router.js';
|
|
27
|
+
import type { Auditor } from './auditor.js';
|
|
28
|
+
import { NoopAuditor } from './auditor.js';
|
|
29
|
+
import { OllamaClient } from './providers/ollama.js';
|
|
30
|
+
import { OpenAICompatibleClient } from './providers/openai.js';
|
|
31
|
+
import { GoogleClient } from './providers/google.js';
|
|
32
|
+
import { AnthropicClient } from './providers/anthropic.js';
|
|
33
|
+
import { BaseLLMClient } from './client.js';
|
|
34
|
+
import {
|
|
35
|
+
type StructuredOutputResult,
|
|
36
|
+
type SchemaConfig,
|
|
37
|
+
} from './structured-output.js';
|
|
38
|
+
|
|
39
|
+
// ============================================================================
|
|
40
|
+
// Default Provider URLs
|
|
41
|
+
// ============================================================================
|
|
42
|
+
|
|
43
|
+
const DEFAULT_URLS: Record<string, string> = {
|
|
44
|
+
ollama: 'http://localhost:11434',
|
|
45
|
+
openai: 'https://api.openai.com',
|
|
46
|
+
llamacpp: 'http://localhost:8080',
|
|
47
|
+
anthropic: 'https://api.anthropic.com',
|
|
48
|
+
// google and vertex build their own URLs internally
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// ============================================================================
|
|
52
|
+
// AIModel — The Universal Client
|
|
53
|
+
// ============================================================================
|
|
54
|
+
|
|
55
|
+
export class AIModel {
|
|
56
|
+
private router: Router;
|
|
57
|
+
private auditor: Auditor;
|
|
58
|
+
private config: AIModelConfig;
|
|
59
|
+
|
|
60
|
+
constructor(config: AIModelConfig) {
|
|
61
|
+
this.config = config;
|
|
62
|
+
this.auditor = config.auditor ?? new NoopAuditor();
|
|
63
|
+
|
|
64
|
+
const routerConfig: RouterConfig = {
|
|
65
|
+
retriesPerProvider: config.retries ?? 2,
|
|
66
|
+
auditor: this.auditor,
|
|
67
|
+
};
|
|
68
|
+
this.router = new Router(routerConfig);
|
|
69
|
+
|
|
70
|
+
// Initialize providers in order
|
|
71
|
+
for (let i = 0; i < config.providers.length; i++) {
|
|
72
|
+
const providerConfig = config.providers[i]!;
|
|
73
|
+
const client = this.createClient(providerConfig);
|
|
74
|
+
const id = `${this.normalizeType(providerConfig.type)}-${i}`;
|
|
75
|
+
|
|
76
|
+
this.router.addProvider({
|
|
77
|
+
id,
|
|
78
|
+
client,
|
|
79
|
+
priority: providerConfig.priority ?? i,
|
|
80
|
+
modelOverride: providerConfig.model,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ========================================================================
|
|
86
|
+
// Chat
|
|
87
|
+
// ========================================================================
|
|
88
|
+
|
|
89
|
+
/** Send a chat request with automatic failover across providers */
|
|
90
|
+
async chat(
|
|
91
|
+
messages: LLMChatMessage[],
|
|
92
|
+
options?: ChatOptions,
|
|
93
|
+
): Promise<LLMChatResponse> {
|
|
94
|
+
return this.router.chat(messages, options);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Chat with automatic tool execution (multi-turn loop) */
|
|
98
|
+
async chatWithTools(
|
|
99
|
+
messages: LLMChatMessage[],
|
|
100
|
+
options?: ChatOptions & { maxIterations?: number },
|
|
101
|
+
): Promise<LLMChatResponse> {
|
|
102
|
+
return this.router.chatWithTools(messages, options);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Stream chat response with pluggable decoder strategy */
|
|
106
|
+
async *chatStream(
|
|
107
|
+
messages: LLMChatMessage[],
|
|
108
|
+
options?: ChatOptions,
|
|
109
|
+
): AsyncGenerator<DecodedEvent, LLMChatResponse | void, unknown> {
|
|
110
|
+
return yield* this.router.chatStream(messages, options);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ========================================================================
|
|
114
|
+
// Structured Output
|
|
115
|
+
// ========================================================================
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Generate structured output from the LLM with automatic failover.
|
|
119
|
+
* Validates the response against the provided Zod schema.
|
|
120
|
+
* Throws StructuredOutputError on validation failure.
|
|
121
|
+
*
|
|
122
|
+
* @template T The output type
|
|
123
|
+
* @param config Schema configuration (JSON Schema + optional validator)
|
|
124
|
+
* @param messages Chat messages to send
|
|
125
|
+
* @param options Additional options (temperature, maxTokens, etc.)
|
|
126
|
+
* @returns Promise resolving to validated structured output
|
|
127
|
+
* @throws StructuredOutputError if JSON parsing fails or validation fails
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* import { fromZod } from 'universal-llm-client/zod';
|
|
132
|
+
* const UserConfig = fromZod(z.object({
|
|
133
|
+
* name: z.string(),
|
|
134
|
+
* age: z.number(),
|
|
135
|
+
* }));
|
|
136
|
+
*
|
|
137
|
+
* const user = await model.generateStructured(UserConfig, [
|
|
138
|
+
* { role: 'user', content: 'Generate a user profile' },
|
|
139
|
+
* ]);
|
|
140
|
+
* // user.name: string, user.age: number
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
async generateStructured<T>(
|
|
144
|
+
config: SchemaConfig<T>,
|
|
145
|
+
messages: LLMChatMessage[],
|
|
146
|
+
options?: ChatOptions,
|
|
147
|
+
): Promise<T> {
|
|
148
|
+
return this.router.generateStructured(config, messages, options);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Try to generate structured output, returning a result object instead of throwing.
|
|
153
|
+
* Same as generateStructured but returns { ok: true, value } on success
|
|
154
|
+
* and { ok: false, error, rawOutput } on failure.
|
|
155
|
+
*
|
|
156
|
+
* @template T The output type
|
|
157
|
+
* @param config Schema configuration (JSON Schema + optional validator)
|
|
158
|
+
* @param messages Chat messages to send
|
|
159
|
+
* @param options Additional options (temperature, maxTokens, etc.)
|
|
160
|
+
* @returns StructuredOutputResult<T> - either success with value or failure with error
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```typescript
|
|
164
|
+
* const result = await model.tryParseStructured(config, messages);
|
|
165
|
+
*
|
|
166
|
+
* if (result.ok) {
|
|
167
|
+
* console.log('User:', result.value.name);
|
|
168
|
+
* } else {
|
|
169
|
+
* console.log('Error:', result.error.message);
|
|
170
|
+
* console.log('Raw output:', result.rawOutput);
|
|
171
|
+
* }
|
|
172
|
+
* ```
|
|
173
|
+
*/
|
|
174
|
+
async tryParseStructured<T>(
|
|
175
|
+
config: SchemaConfig<T>,
|
|
176
|
+
messages: LLMChatMessage[],
|
|
177
|
+
options?: ChatOptions,
|
|
178
|
+
): Promise<StructuredOutputResult<T>> {
|
|
179
|
+
return this.router.tryParseStructured(config, messages, options);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Stream structured output with partial validated objects.
|
|
184
|
+
*
|
|
185
|
+
* Yields partial validated objects as JSON generates, then returns the
|
|
186
|
+
* complete validated object on stream completion.
|
|
187
|
+
*
|
|
188
|
+
* For invalid partial JSON, no yield occurs (partial validation is best-effort).
|
|
189
|
+
* On stream completion, if the final JSON fails validation, throws StructuredOutputError.
|
|
190
|
+
*
|
|
191
|
+
* @template T The output type
|
|
192
|
+
* @param config Schema configuration (JSON Schema + optional validator)
|
|
193
|
+
* @param messages Chat messages to send
|
|
194
|
+
* @param options Additional options (temperature, maxTokens, etc.)
|
|
195
|
+
* @yields Partial validated objects as the JSON stream progresses
|
|
196
|
+
* @returns Complete validated object on stream completion
|
|
197
|
+
* @throws StructuredOutputError if final validation fails
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* import { fromZod } from 'universal-llm-client/zod';
|
|
202
|
+
* const UserConfig = fromZod(z.object({
|
|
203
|
+
* name: z.string(),
|
|
204
|
+
* age: z.number(),
|
|
205
|
+
* }));
|
|
206
|
+
*
|
|
207
|
+
* const stream = model.generateStructuredStream(UserConfig, [
|
|
208
|
+
* { role: 'user', content: 'Generate a user' },
|
|
209
|
+
* ]);
|
|
210
|
+
*
|
|
211
|
+
* for await (const partial of stream) {
|
|
212
|
+
* console.log('Partial user:', partial);
|
|
213
|
+
* }
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
async *generateStructuredStream<T>(
|
|
217
|
+
config: SchemaConfig<T>,
|
|
218
|
+
messages: LLMChatMessage[],
|
|
219
|
+
options?: ChatOptions,
|
|
220
|
+
): AsyncGenerator<T, T, unknown> {
|
|
221
|
+
return yield* this.router.generateStructuredStream(config, messages, options);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ========================================================================
|
|
225
|
+
// Embeddings
|
|
226
|
+
// ========================================================================
|
|
227
|
+
|
|
228
|
+
/** Generate embedding for a single text */
|
|
229
|
+
async embed(text: string): Promise<number[]> {
|
|
230
|
+
return this.router.embed(text);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/** Generate embeddings for multiple texts */
|
|
234
|
+
async embedArray(texts: string[]): Promise<number[][]> {
|
|
235
|
+
return this.router.embedArray(texts);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// ========================================================================
|
|
239
|
+
// Deep Research (Gemini-only)
|
|
240
|
+
// ========================================================================
|
|
241
|
+
|
|
242
|
+
private getGoogleClient(method: string): GoogleClient {
|
|
243
|
+
const googleClients = this.router.getClients().filter(
|
|
244
|
+
(c): c is GoogleClient => c instanceof GoogleClient,
|
|
245
|
+
);
|
|
246
|
+
// Prefer an AI Studio client — Vertex AI doesn't support Deep Research.
|
|
247
|
+
const aiStudio = googleClients.find(c => c.supportsDeepResearch());
|
|
248
|
+
if (aiStudio) return aiStudio;
|
|
249
|
+
if (googleClients.length > 0) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`${method} requires an AI Studio Google provider (type: "google"); Vertex AI is not supported for Deep Research.`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
throw new Error(
|
|
255
|
+
`${method} requires a Google provider (type: "google"). None is configured.`,
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Run an agentic Deep Research interaction (Gemini only): creates it and
|
|
261
|
+
* polls until completion. Throws if no Google provider is configured.
|
|
262
|
+
*/
|
|
263
|
+
async deepResearch(input: string, options?: DeepResearchOptions): Promise<DeepResearchResult> {
|
|
264
|
+
return this.getGoogleClient('deepResearch').deepResearch(input, options);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Stream a Deep Research interaction's intermediate thought/text/step events
|
|
269
|
+
* (Gemini only), returning the final result. Throws if no Google provider.
|
|
270
|
+
*/
|
|
271
|
+
async *deepResearchStream(
|
|
272
|
+
input: string,
|
|
273
|
+
options?: DeepResearchOptions,
|
|
274
|
+
): AsyncGenerator<DeepResearchEvent, DeepResearchResult, unknown> {
|
|
275
|
+
return yield* this.getGoogleClient('deepResearchStream').deepResearchStream(input, options);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ========================================================================
|
|
279
|
+
// Tool Registration
|
|
280
|
+
// ========================================================================
|
|
281
|
+
|
|
282
|
+
/** Register a tool callable by the LLM (broadcast to all providers) */
|
|
283
|
+
registerTool(
|
|
284
|
+
name: string,
|
|
285
|
+
description: string,
|
|
286
|
+
parameters: LLMFunction['parameters'],
|
|
287
|
+
handler: ToolHandler,
|
|
288
|
+
): void {
|
|
289
|
+
this.router.registerTool(name, description, parameters, handler);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/** Register multiple tools at once */
|
|
293
|
+
registerTools(
|
|
294
|
+
tools: Array<{
|
|
295
|
+
name: string;
|
|
296
|
+
description: string;
|
|
297
|
+
parameters: LLMFunction['parameters'];
|
|
298
|
+
handler: ToolHandler;
|
|
299
|
+
}>,
|
|
300
|
+
): void {
|
|
301
|
+
this.router.registerTools(tools);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ========================================================================
|
|
305
|
+
// Model Management
|
|
306
|
+
// ========================================================================
|
|
307
|
+
|
|
308
|
+
/** Get available models from all configured providers */
|
|
309
|
+
async getModels(): Promise<string[]> {
|
|
310
|
+
return this.router.getModels();
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/** Get metadata about the current model (context length, capabilities) */
|
|
314
|
+
async getModelInfo(): Promise<ModelMetadata> {
|
|
315
|
+
return this.router.getModelInfo();
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/** Switch model at runtime (updates all providers) */
|
|
319
|
+
setModel(name: string): void {
|
|
320
|
+
this.config.model = name;
|
|
321
|
+
// The model name change will be picked up by the providers
|
|
322
|
+
// through the router on next request
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** Get the current model name */
|
|
326
|
+
get model(): string {
|
|
327
|
+
return this.config.model;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// ========================================================================
|
|
331
|
+
// Provider Status
|
|
332
|
+
// ========================================================================
|
|
333
|
+
|
|
334
|
+
/** Get health/status of all configured providers */
|
|
335
|
+
getProviderStatus(): ProviderStatus[] {
|
|
336
|
+
return this.router.getStatus();
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ========================================================================
|
|
340
|
+
// Lifecycle
|
|
341
|
+
// ========================================================================
|
|
342
|
+
|
|
343
|
+
/** Clean shutdown — flush auditor, disconnect MCP, etc. */
|
|
344
|
+
async dispose(): Promise<void> {
|
|
345
|
+
await this.auditor.flush?.();
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// ========================================================================
|
|
349
|
+
// Internal: Provider Factory
|
|
350
|
+
// ========================================================================
|
|
351
|
+
|
|
352
|
+
private createClient(providerConfig: ProviderConfig): BaseLLMClient {
|
|
353
|
+
const type = this.normalizeType(providerConfig.type);
|
|
354
|
+
const modelName = providerConfig.model ?? this.config.model;
|
|
355
|
+
|
|
356
|
+
const clientOptions: LLMClientOptions = {
|
|
357
|
+
model: modelName,
|
|
358
|
+
url: providerConfig.url ?? DEFAULT_URLS[type] ?? '',
|
|
359
|
+
apiType: type as AIModelApiType,
|
|
360
|
+
apiKey: providerConfig.apiKey,
|
|
361
|
+
timeout: this.config.timeout ?? 30000,
|
|
362
|
+
retries: this.config.retries ?? 2,
|
|
363
|
+
debug: this.config.debug ?? false,
|
|
364
|
+
defaultParameters: this.config.defaultParameters,
|
|
365
|
+
// Preserve `undefined` (not set) vs explicit false so providers can
|
|
366
|
+
// decide whether to send a thinking toggle at all.
|
|
367
|
+
thinking: this.config.thinking,
|
|
368
|
+
region: providerConfig.region,
|
|
369
|
+
apiVersion: providerConfig.apiVersion,
|
|
370
|
+
extraHeaders: providerConfig.headers,
|
|
371
|
+
queryParams: providerConfig.queryParams,
|
|
372
|
+
authHeader: providerConfig.authHeader,
|
|
373
|
+
authPrefix: providerConfig.authPrefix,
|
|
374
|
+
apiBasePath: providerConfig.apiBasePath,
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
switch (type) {
|
|
378
|
+
case 'ollama':
|
|
379
|
+
return new OllamaClient(clientOptions, this.auditor);
|
|
380
|
+
|
|
381
|
+
case 'openai':
|
|
382
|
+
case 'llamacpp':
|
|
383
|
+
return new OpenAICompatibleClient(clientOptions, this.auditor);
|
|
384
|
+
|
|
385
|
+
case 'google':
|
|
386
|
+
case 'vertex':
|
|
387
|
+
return new GoogleClient(clientOptions, this.auditor);
|
|
388
|
+
|
|
389
|
+
case 'anthropic':
|
|
390
|
+
return new AnthropicClient(clientOptions, this.auditor);
|
|
391
|
+
|
|
392
|
+
default:
|
|
393
|
+
throw new Error(`Unknown provider type: ${type}`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
private normalizeType(type: string): string {
|
|
398
|
+
return type.toLowerCase();
|
|
399
|
+
}
|
|
400
|
+
}
|