@node-llm/core 1.5.2 → 1.5.4

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
@@ -1,16 +1,16 @@
1
1
  <p align="left">
2
2
  <a href="https://node-llm.eshaiju.com/">
3
- <img src="https://github.com/node-llm/node-llm/raw/main/docs/assets/images/logo.jpg" alt="NodeLLM logo" width="300" />
3
+ <img src="docs/assets/images/logo.jpg" alt="NodeLLM logo" width="300" />
4
4
  </a>
5
5
  </p>
6
6
 
7
7
  # NodeLLM
8
8
 
9
- **An opinionated architectural layer for integrating Large Language Models in Node.js.**
9
+ **An architectural layer for integrating Large Language Models in Node.js.**
10
10
 
11
11
  **Provider-agnostic by design.**
12
12
 
13
- Most LLM SDKs **tightly couple** your application to vendors, APIs, and churn. NodeLLM provides a unified, production-oriented API for interacting with over 540+ models across multiple providers (OpenAI, Gemini, Anthropic, DeepSeek, OpenRouter, Ollama, etc.) without the SDK fatigue.
13
+ Integrating multiple LLM providers often means juggling different SDKs, API styles, and update cycles. NodeLLM provides a single, unified, production-oriented API for interacting with over 540+ models across multiple providers (OpenAI, Gemini, Anthropic, DeepSeek, OpenRouter, Ollama, etc.) that stays consistent even when providers change.
14
14
 
15
15
  <p align="left">
16
16
  <img src="https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/openai.svg" height="28" />
@@ -52,14 +52,14 @@ NodeLLM is **NOT**:
52
52
 
53
53
  ## 🏗️ Why NodeLLM?
54
54
 
55
- Most AI integrations today are provider-specific, SDK-driven, and leaky at abstraction boundaries. This creates long-term architectural risk. **LLMs should be treated as infrastructure**, and NodeLLM exists to help you integrate them without vendor lock-in.
55
+ Direct integrations often become tightly coupled to specific providers, making it difficult to adapt as models evolve. **LLMs should be treated as infrastructure**, and NodeLLM helps you build a stable foundation that persists regardless of which model is currently "state of the art."
56
56
 
57
- NodeLLM exists to solve **architectural problems**, not just provide API access. It is the core architectural layer for LLMs in the Node.js ecosystem.
57
+ NodeLLM helps solve **architectural problems**, not just provide API access. It serves as the core integration layer for LLMs in the Node.js ecosystem.
58
58
 
59
59
  ### Strategic Goals
60
60
  - **Provider Isolation**: Decouple your services from vendor SDKs.
61
- - **Production-Ready**: Native support for streaming, retries, and unified error handling.
62
- - **Predictable API**: Consistent behavior for Tools, Vision, and Structured Outputs across all models.
61
+ - **Production-Ready**: Native support for streaming, automatic retries, and unified error handling.
62
+ - **Predictable API**: Consistent behavior for Tools, Vision, and Structured Outputs across all models, **now including full parity for streaming**.
63
63
 
64
64
  ---
65
65
 
@@ -68,7 +68,7 @@ NodeLLM exists to solve **architectural problems**, not just provide API access.
68
68
  ```ts
69
69
  import { NodeLLM } from "@node-llm/core";
70
70
 
71
- // 1. Configure once
71
+ // 1. Configure once (Safe for ESM + dotenv race conditions)
72
72
  NodeLLM.configure({ provider: "openai" });
73
73
 
74
74
  // 2. Chat (High-level request/response)
@@ -77,29 +77,37 @@ const response = await chat.ask("Explain event-driven architecture");
77
77
  console.log(response.content);
78
78
 
79
79
  // 3. Streaming (Standard AsyncIterator)
80
+ // NOW with full support for Tools, Vision, and Schemas!
80
81
  for await (const chunk of chat.stream("Explain event-driven architecture")) {
81
82
  process.stdout.write(chunk.content);
82
83
  }
83
84
  ```
84
85
 
86
+ ### 🎯 Real-World Example: Brand Perception Checker
87
+
88
+ Built with NodeLLM - Multi-provider AI analysis, tool calling, and structured outputs working together:
89
+
90
+ <p align="center">
91
+ <img src="assets/brand-perception-checker.png" alt="Brand Perception Checker" width="800" />
92
+ </p>
93
+
94
+ **[View Example →](examples/brand-perception-checker/)**
95
+
85
96
 
86
97
  ---
87
98
 
88
99
  ## 🔧 Strategic Configuration
89
100
 
90
- NodeLLM provides a flexible configuration system designed for enterprise usage:
101
+ NodeLLM provides a flexible, **lazy-initialized** configuration system designed for enterprise usage. It is safe for ESM and resolved only when your first request is made, eliminating the common `dotenv` race condition.
91
102
 
92
103
  ```ts
93
104
  // Recommended for multi-provider pipelines
94
- NodeLLM.configure((config) => {
95
- config.openaiApiKey = process.env.OPENAI_API_KEY;
96
- config.anthropicApiKey = process.env.ANTHROPIC_API_KEY;
97
- config.ollamaApiBase = process.env.OLLAMA_API_BASE;
105
+ NodeLLM.configure({
106
+ openaiApiKey: process.env.OPENAI_API_KEY,
107
+ anthropicApiKey: process.env.ANTHROPIC_API_KEY,
108
+ ollamaApiBase: process.env.OLLAMA_API_BASE,
98
109
  });
99
110
 
100
- // Switch providers at the framework level
101
- NodeLLM.configure({ provider: "anthropic" });
102
-
103
111
  // Support for Custom Endpoints (e.g., Azure or LocalAI)
104
112
  NodeLLM.configure({
105
113
  openaiApiKey: process.env.AZURE_KEY,
@@ -107,7 +115,7 @@ NodeLLM.configure({
107
115
  });
108
116
  ```
109
117
 
110
- **[Full Configuration Guide →](https://node-llm.eshaiju.com/getting-started/configuration)**
118
+ **[Full Configuration Guide →](docs/getting_started/configuration.md)**
111
119
 
112
120
  ---
113
121
 
@@ -123,7 +131,7 @@ await chat.ask("Hello world");
123
131
  ```
124
132
 
125
133
  ### 👁️ Smart Vision & Files
126
- Pass images, PDFs, or audio files directly. We handle the heavy lifting: fetching remote URLs, base64 encoding, and MIME type mapping.
134
+ Pass images, PDFs, or audio files directly to **both `ask()` and `stream()`**. We handle the heavy lifting: fetching remote URLs, base64 encoding, and MIME type mapping.
127
135
  ```ts
128
136
  await chat.ask("Analyze this interface", {
129
137
  files: ["./screenshot.png", "https://example.com/spec.pdf"]
@@ -142,42 +150,18 @@ class WeatherTool extends Tool {
142
150
  description = "Get current weather";
143
151
  schema = z.object({ location: z.string() });
144
152
 
145
- async handler({ location }) {
153
+ async execute({ location }) {
146
154
  return `Sunny in ${location}`;
147
155
  }
148
156
  }
149
157
 
150
158
  // Now the model can use it automatically
151
159
  await chat.withTool(WeatherTool).ask("What's the weather in Tokyo?");
152
- ```
153
160
 
154
- ### 🛡️ Loop Protection & Resource Limits
155
- Prevent runaway costs, infinite loops, and hanging requests with comprehensive protection against resource exhaustion.
156
-
157
- NodeLLM provides **defense-in-depth** security that you can configure globally or per-request:
158
-
159
- ```ts
160
- // 1. Global config
161
- NodeLLM.configure({
162
- requestTimeout: 30000, // Timeout requests after 30 seconds (default)
163
- maxToolCalls: 5, // Stop after 5 sequential tool execution turns
164
- maxRetries: 2, // Retry provider-level errors up to 2 times
165
- maxTokens: 4096 // Limit output to 4K tokens (default)
166
- });
167
-
168
- // 2. Per request override
169
- await chat.ask("Deep search task", {
170
- requestTimeout: 120000, // 2 minutes for this request
171
- maxToolCalls: 10,
172
- maxTokens: 8192 // 8K tokens for this request
173
- });
161
+ // Lifecycle Hooks for Error & Flow Control
162
+ chat.onToolCallError((call, err) => "STOP");
174
163
  ```
175
-
176
- **Security Benefits:**
177
- - **`requestTimeout`**: Prevents DoS attacks and hanging requests
178
- - **`maxToolCalls`**: Prevents infinite tool execution loops
179
- - **`maxRetries`**: Prevents retry storms during outages
180
- - **`maxTokens`**: Prevents excessive output and cost overruns
164
+ **[Full Tool Calling Guide →](https://node-llm.eshaiju.com/core-features/tool-calling)**
181
165
 
182
166
  ### 🔍 Comprehensive Debug Logging
183
167
  Enable detailed logging for all API requests and responses across every feature and provider:
@@ -193,42 +177,6 @@ process.env.NODELLM_DEBUG = "true";
193
177
  ```
194
178
  **Covers:** Chat, Streaming, Images, Embeddings, Transcription, Moderation - across all providers!
195
179
 
196
- ### 🛡️ Content Policy Hooks
197
- NodeLLM provides pluggable hooks to implement custom security, compliance, and moderation logic. Instead of hard-coded rules, you can inject your own policies at the edge.
198
-
199
- - **`beforeRequest()`**: Intercept and modify messages before they hit the LLM (e.g., PII detection/redaction).
200
- - **`afterResponse()`**: Process the final response before it returns to your code (e.g., output masking or compliance checks).
201
-
202
- ```ts
203
- chat
204
- .beforeRequest(async (messages) => {
205
- // Detect PII and redact
206
- return redactSSN(messages);
207
- })
208
- .afterResponse(async (response) => {
209
- // Ensure output compliance
210
- return response.withContent(maskSensitiveData(response.content));
211
- });
212
- ```
213
-
214
- ### 🧱 Smart Context Isolation
215
- Stop worrying about prompt injection or instruction drift. NodeLLM automatically separates system instructions from the conversation history, providing a higher level of protection and strictness.
216
-
217
- - **Zero-Config Security**: Enabled by default for all chats. No special flags required.
218
- - **Smart Model Mapping**: Automatically uses OpenAI's modern `developer` role for compatible models (GPT-4o, o1, o3) while safely falling back to the standard `system` role for older or local models (Ollama, DeepSeek, etc.).
219
- - **Universal Context**: Instructions stay separated internally, ensuring they are always prioritized by the model and never accidentally overridden by user messages.
220
- - **Provider Agnostic**: Write instructions once; NodeLLM handles the specific role requirements for every major provider (OpenAI, Anthropic, Gemini).
221
-
222
- ### 🔍 Observability & Tool Auditing
223
- For enterprise compliance, NodeLLM provides deep visibility into the tool execution lifecycle. You can monitor, log, and audit every step of a tool's execution.
224
-
225
- ```ts
226
- chat
227
- .onToolCallStart((call) => log(`Starting tool: ${call.function.name}`))
228
- .onToolCallEnd((call, res) => log(`Tool ${call.id} finished with: ${res}`))
229
- .onToolCallError((call, err) => alert(`Tool ${call.function.name} failed: ${err.message}`));
230
- ```
231
-
232
180
  ### ✨ Structured Output
233
181
  Get type-safe, validated JSON back using **Zod** schemas.
234
182
  ```ts
@@ -285,7 +233,7 @@ console.log(res.reasoning); // Chain-of-thought
285
233
 
286
234
  | Provider | Supported Features |
287
235
  | :--- | :--- |
288
- | <img src="https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/openai.svg" height="18"> **OpenAI** | Chat, **Streaming + Tools**, Vision, Audio, Images, Transcription, **Reasoning**, **Smart Developer Role** |
236
+ | <img src="https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/openai.svg" height="18"> **OpenAI** | Chat, **Streaming + Tools**, Vision, Audio, Images, Transcription, **Reasoning** |
289
237
  | <img src="https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/gemini-color.svg" height="18"> **Gemini** | Chat, **Streaming + Tools**, Vision, Audio, Video, Embeddings |
290
238
  | <img src="https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/anthropic-text.svg" height="12"> **Anthropic** | Chat, **Streaming + Tools**, Vision, PDF, Structured Output |
291
239
  | <img src="https://registry.npmmirror.com/@lobehub/icons-static-svg/latest/files/icons/deepseek-color.svg" height="18"> **DeepSeek** | Chat (V3), **Reasoning (R1)**, **Streaming + Tools** |
@@ -302,11 +250,21 @@ npm install @node-llm/core
302
250
 
303
251
  **[View Full Documentation ↗](https://node-llm.eshaiju.com/)**
304
252
 
253
+ ### 🍿 Try the Live Demo
254
+ Want to see it in action? Run this in your terminal:
255
+ ```bash
256
+ git clone https://github.com/node-llm/node-llm.git
257
+ cd node-llm
258
+ npm install
259
+ npm run demo
260
+ ```
261
+
262
+
305
263
  ---
306
264
 
307
265
  ## 🤝 Contributing
308
266
 
309
- We welcome contributions! Please see our **[Contributing Guide](https://github.com/node-llm/node-llm/blob/main/CONTRIBUTING.md)** for more details on how to get started.
267
+ We welcome contributions! Please see our **[Contributing Guide](CONTRIBUTING.md)** for more details on how to get started.
310
268
 
311
269
  ---
312
270
 
@@ -15,6 +15,7 @@ export interface AskOptions {
15
15
  headers?: Record<string, string>;
16
16
  maxToolCalls?: number;
17
17
  requestTimeout?: number;
18
+ signal?: AbortSignal;
18
19
  }
19
20
  import { ChatResponseString } from "./ChatResponse.js";
20
21
  export declare class Chat {
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../src/chat/Chat.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../src/chat/Chat.ts"],"names":[],"mappings":"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"}
package/dist/chat/Chat.js CHANGED
@@ -10,6 +10,7 @@ import { ToolExecutionMode } from "../constants.js";
10
10
  import { ConfigurationError } from "../errors/index.js";
11
11
  import { ChatValidator } from "./Validation.js";
12
12
  import { ToolHandler } from "./ToolHandler.js";
13
+ import { logger } from "../utils/logger.js";
13
14
  import { ChatResponseString } from "./ChatResponse.js";
14
15
  export class Chat {
15
16
  provider;
@@ -285,6 +286,7 @@ export class Chat {
285
286
  headers: { ...this.options.headers, ...options?.headers },
286
287
  response_format: responseFormat, // Pass to provider
287
288
  requestTimeout: options?.requestTimeout ?? this.options.requestTimeout ?? config.requestTimeout,
289
+ signal: options?.signal,
288
290
  ...this.options.params,
289
291
  };
290
292
  // --- Content Policy Hooks (Input) ---
@@ -378,15 +380,20 @@ export class Chat {
378
380
  if (isFatal) {
379
381
  throw error;
380
382
  }
381
- console.error(`[NodeLLM] Tool execution failed for '${toolCall.function.name}':`, error);
383
+ logger.error(`Tool execution failed for '${toolCall.function.name}':`, error);
382
384
  }
383
385
  }
384
386
  response = await this.executor.executeChat({
385
387
  model: this.model,
386
388
  messages: [...this.systemMessages, ...this.messages],
387
389
  tools: this.options.tools,
390
+ temperature: options?.temperature ?? this.options.temperature,
391
+ max_tokens: options?.maxTokens ?? this.options.maxTokens ?? config.maxTokens,
388
392
  headers: this.options.headers,
393
+ response_format: responseFormat,
389
394
  requestTimeout: options?.requestTimeout ?? this.options.requestTimeout ?? config.requestTimeout,
395
+ signal: options?.signal,
396
+ ...this.options.params,
390
397
  });
391
398
  trackUsage(response.usage);
392
399
  assistantMessage = new ChatResponseString(response.content ?? "", response.usage ?? { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model, this.provider.id, response.reasoning);
@@ -429,7 +436,7 @@ export class Chat {
429
436
  toolInstance = new tool();
430
437
  }
431
438
  catch (e) {
432
- console.error(`[NodeLLM] Failed to instantiate tool class: ${tool.name}`, e);
439
+ logger.error(`Failed to instantiate tool class: ${tool.name}`, e);
433
440
  return null;
434
441
  }
435
442
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ChatStream.d.ts","sourceRoot":"","sources":["../../src/chat/ChatStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAA4C,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAS,MAAM,0BAA0B,CAAC;AAEtE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGhD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAOvC;;;GAGG;AACH,qBAAa,UAAU;IAKnB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN1B,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,cAAc,CAAY;gBAGf,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,EAC1C,QAAQ,CAAC,EAAE,OAAO,EAAE,EACpB,cAAc,CAAC,EAAE,OAAO,EAAE;IA6B5B,IAAI,OAAO,IAAI,SAAS,OAAO,EAAE,CAEhC;IAED,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,MAAM,CAAC,SAAS,CAAC;CAsOrF"}
1
+ {"version":3,"file":"ChatStream.d.ts","sourceRoot":"","sources":["../../src/chat/ChatStream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAA4C,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAS,MAAM,0BAA0B,CAAC;AAEtE,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGhD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAQvC;;;GAGG;AACH,qBAAa,UAAU;IAKnB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN1B,OAAO,CAAC,QAAQ,CAAY;IAC5B,OAAO,CAAC,cAAc,CAAY;gBAGf,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,EAC1C,QAAQ,CAAC,EAAE,OAAO,EAAE,EACpB,cAAc,CAAC,EAAE,OAAO,EAAE;IA6B5B,IAAI,OAAO,IAAI,SAAS,OAAO,EAAE,CAEhC;IAED,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,MAAM,CAAC,SAAS,CAAC;CAsOrF"}
@@ -7,6 +7,7 @@ import { FileLoader } from "../utils/FileLoader.js";
7
7
  import { toJsonSchema } from "../schema/to-json-schema.js";
8
8
  import { ChatValidator } from "./Validation.js";
9
9
  import { ToolHandler } from "./ToolHandler.js";
10
+ import { logger } from "../utils/logger.js";
10
11
  /**
11
12
  * Internal handler for chat streaming logic.
12
13
  * Wraps the provider's stream with side effects like history updates and events.
@@ -210,7 +211,7 @@ export class ChatStream {
210
211
  if (isFatal) {
211
212
  throw error;
212
213
  }
213
- console.error(`[NodeLLM] Tool execution failed for '${toolCall.function.name}':`, error);
214
+ logger.error(`Tool execution failed for '${toolCall.function.name}':`, error);
214
215
  }
215
216
  }
216
217
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ToolHandler.d.ts","sourceRoot":"","sources":["../../src/chat/ToolHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGpD,qBAAa,WAAW;IACtB,MAAM,CAAC,kBAAkB,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE,iBAAiB,GAAG,OAAO;WAM7E,uBAAuB,CAClC,QAAQ,EAAE,GAAG,EACb,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,GACpD,OAAO,CAAC,OAAO,CAAC;WAMN,OAAO,CAClB,QAAQ,EAAE,GAAG,EACb,KAAK,EAAE,GAAG,EAAE,GAAG,SAAS,EACxB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,EAC7B,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,GACvC,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAwBpE"}
1
+ {"version":3,"file":"ToolHandler.d.ts","sourceRoot":"","sources":["../../src/chat/ToolHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAGpD,qBAAa,WAAW;IACtB,MAAM,CAAC,kBAAkB,CAAC,SAAS,EAAE,GAAG,EAAE,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE,iBAAiB,GAAG,OAAO;WAM7E,uBAAuB,CAClC,QAAQ,EAAE,GAAG,EACb,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,GACpD,OAAO,CAAC,OAAO,CAAC;WAMN,OAAO,CAClB,QAAQ,EAAE,GAAG,EACb,KAAK,EAAE,GAAG,EAAE,GAAG,SAAS,EACxB,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,EAC7B,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,KAAK,IAAI,GACvC,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;CAyBpE"}
@@ -22,12 +22,13 @@ export class ToolHandler {
22
22
  try {
23
23
  const args = JSON.parse(toolCall.function.arguments);
24
24
  const result = await tool.handler(args);
25
+ const safeResult = typeof result === 'string' ? result : JSON.stringify(result);
25
26
  if (onEnd)
26
27
  onEnd(toolCall, result);
27
28
  return {
28
29
  role: "tool",
29
30
  tool_call_id: toolCall.id,
30
- content: result,
31
+ content: safeResult,
31
32
  };
32
33
  }
33
34
  catch (error) {
@@ -1 +1 @@
1
- {"version":3,"file":"Validation.d.ts","sourceRoot":"","sources":["../../src/chat/Validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,qBAAa,aAAa;IACxB,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB;IAYvG,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB;IAYrG,MAAM,CAAC,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB;CAWlH"}
1
+ {"version":3,"file":"Validation.d.ts","sourceRoot":"","sources":["../../src/chat/Validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGpD,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED,qBAAa,aAAa;IACxB,MAAM,CAAC,cAAc,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB;IAYvG,MAAM,CAAC,aAAa,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB;IAYrG,MAAM,CAAC,wBAAwB,CAAC,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,iBAAiB;CAWlH"}
@@ -1,3 +1,4 @@
1
+ import { logger } from "../utils/logger.js";
1
2
  export class ChatValidator {
2
3
  static validateVision(provider, model, hasBinary, options) {
3
4
  if (!hasBinary)
@@ -6,7 +7,7 @@ export class ChatValidator {
6
7
  throw new Error(`Model ${model} does not support vision/binary files.`);
7
8
  }
8
9
  if (options.assumeModelExists) {
9
- console.warn(`[NodeLLM] Skipping vision capability validation for model ${model}`);
10
+ logger.warn(`Skipping vision capability validation for model ${model}`);
10
11
  }
11
12
  }
12
13
  static validateTools(provider, model, hasTools, options) {
@@ -16,7 +17,7 @@ export class ChatValidator {
16
17
  throw new Error(`Model ${model} does not support tool calling.`);
17
18
  }
18
19
  if (options.assumeModelExists) {
19
- console.warn(`[NodeLLM] Skipping tool capability validation for model ${model}`);
20
+ logger.warn(`Skipping tool capability validation for model ${model}`);
20
21
  }
21
22
  }
22
23
  static validateStructuredOutput(provider, model, hasSchema, options) {
@@ -26,7 +27,7 @@ export class ChatValidator {
26
27
  throw new Error(`Model ${model} does not support structured output.`);
27
28
  }
28
29
  if (options.assumeModelExists) {
29
- console.warn(`[NodeLLM] Skipping structured output capability validation for model ${model}`);
30
+ logger.warn(`Skipping structured output capability validation for model ${model}`);
30
31
  }
31
32
  }
32
33
  }
package/dist/llm.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"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"}
package/dist/llm.js CHANGED
@@ -7,6 +7,7 @@ import { Moderation } from "./moderation/Moderation.js";
7
7
  import { Embedding } from "./embedding/Embedding.js";
8
8
  import { ProviderNotConfiguredError, UnsupportedFeatureError, ModelCapabilityError } from "./errors/index.js";
9
9
  import { resolveModelAlias } from "./model_aliases.js";
10
+ import { logger } from "./utils/logger.js";
10
11
  import { config, Configuration } from "./config.js";
11
12
  // Provider registration map
12
13
  const PROVIDER_REGISTRARS = {
@@ -165,7 +166,7 @@ export class NodeLLMCore {
165
166
  const rawModel = options?.model;
166
167
  const model = resolveModelAlias(rawModel || "", provider.id);
167
168
  if (options?.assumeModelExists) {
168
- console.warn(`[NodeLLM] Skipping validation for model ${model}`);
169
+ logger.warn(`Skipping validation for model ${model}`);
169
170
  }
170
171
  else if (model && provider.capabilities && !provider.capabilities.supportsImageGeneration(model)) {
171
172
  throw new ModelCapabilityError(model, "image generation");
@@ -183,7 +184,7 @@ export class NodeLLMCore {
183
184
  const rawModel = options?.model || this.defaultTranscriptionModelId || "";
184
185
  const model = resolveModelAlias(rawModel, provider.id);
185
186
  if (options?.assumeModelExists) {
186
- console.warn(`[NodeLLM] Skipping validation for model ${model}`);
187
+ logger.warn(`Skipping validation for model ${model}`);
187
188
  }
188
189
  else if (model && provider.capabilities && !provider.capabilities.supportsTranscription(model)) {
189
190
  throw new ModelCapabilityError(model, "transcription");
@@ -213,7 +214,7 @@ export class NodeLLMCore {
213
214
  const rawModel = options?.model || this.defaultModerationModelId || "";
214
215
  const model = resolveModelAlias(rawModel, provider.id);
215
216
  if (options?.assumeModelExists) {
216
- console.warn(`[NodeLLM] Skipping validation for model ${model}`);
217
+ logger.warn(`Skipping validation for model ${model}`);
217
218
  }
218
219
  else if (model && provider.capabilities && !provider.capabilities.supportsModeration(model)) {
219
220
  throw new ModelCapabilityError(model, "moderation");
@@ -237,7 +238,7 @@ export class NodeLLMCore {
237
238
  requestTimeout: options?.requestTimeout ?? this.config.requestTimeout,
238
239
  };
239
240
  if (options?.assumeModelExists) {
240
- console.warn(`[NodeLLM] Skipping validation for model ${request.model}`);
241
+ logger.warn(`Skipping validation for model ${request.model}`);
241
242
  }
242
243
  else if (request.model && provider.capabilities && !provider.capabilities.supportsEmbeddings(request.model)) {
243
244
  throw new ModelCapabilityError(request.model, "embeddings");
@@ -11,7 +11,7 @@ export class GeminiImage {
11
11
  const modelId = request.model || "imagen-4.0-generate-001";
12
12
  const url = `${this.baseUrl}/models/${modelId}:predict?key=${this.apiKey}`;
13
13
  if (request.size) {
14
- console.log(`[Gemini] Ignoring size ${request.size}. Gemini does not support image size customization.`);
14
+ logger.warn(`[Gemini] Ignoring size ${request.size}. Gemini does not support image size customization.`);
15
15
  }
16
16
  const body = {
17
17
  instances: [
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAS,MAAM,gBAAgB,CAAC;AASlE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,qBAAa,UAAU;IAGT,OAAO,CAAC,QAAQ,CAAC,aAAa;IAA2B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAF5F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAEJ,aAAa,EAAE,cAAc,GAAG,MAAM,EAAmB,MAAM,EAAE,MAAM;IAI9F,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CA0E3D"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../../src/providers/openai/Chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAS,MAAM,gBAAgB,CAAC;AASlE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD,qBAAa,UAAU;IAGT,OAAO,CAAC,QAAQ,CAAC,aAAa;IAA2B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAF5F,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAEJ,aAAa,EAAE,cAAc,GAAG,MAAM,EAAmB,MAAM,EAAE,MAAM;IAI9F,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;CA2E3D"}
@@ -20,7 +20,7 @@ export class OpenAIChat {
20
20
  const supportsDeveloperRole = isMainOpenAI && (typeof this.providerOrUrl === "string"
21
21
  ? Capabilities.supportsDeveloperRole(request.model)
22
22
  : this.providerOrUrl.capabilities?.supportsDeveloperRole(request.model));
23
- const { model, messages, tools, temperature: _, max_tokens, response_format, headers, requestTimeout, ...rest } = request;
23
+ const { model, messages, tools, temperature: _, max_tokens, response_format, headers, requestTimeout, signal, ...rest } = request;
24
24
  const mappedMessages = mapSystemMessages(messages, !!supportsDeveloperRole);
25
25
  const body = {
26
26
  model,
@@ -51,6 +51,7 @@ export class OpenAIChat {
51
51
  ...request.headers,
52
52
  },
53
53
  body: JSON.stringify(body),
54
+ signal,
54
55
  }, request.requestTimeout);
55
56
  if (!response.ok) {
56
57
  await handleOpenAIError(response, request.model);
@@ -15,7 +15,7 @@ export class OpenAIStreaming {
15
15
  this.baseUrl = typeof providerOrUrl === "string" ? providerOrUrl : providerOrUrl.apiBase();
16
16
  }
17
17
  async *execute(request, controller) {
18
- const abortController = controller || new AbortController();
18
+ const abortController = controller || (request.signal ? { signal: request.signal } : new AbortController());
19
19
  const temperature = Capabilities.normalizeTemperature(request.temperature, request.model);
20
20
  const isMainOpenAI = this.baseUrl.includes("api.openai.com");
21
21
  const supportsDeveloperRole = isMainOpenAI && (typeof this.providerOrUrl === "string"
@@ -1 +1 @@
1
- {"version":3,"file":"Binary.d.ts","sourceRoot":"","sources":["../../src/utils/Binary.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,WAAW;IACtB;;OAEG;WACU,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA4C9D,OAAO,CAAC,MAAM,CAAC,aAAa;CAgB7B"}
1
+ {"version":3,"file":"Binary.d.ts","sourceRoot":"","sources":["../../src/utils/Binary.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,WAAW;IACtB;;OAEG;WACU,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IA0C9D,OAAO,CAAC,MAAM,CAAC,aAAa;CAgB7B"}
@@ -1,5 +1,6 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
+ import { logger } from "./logger.js";
3
4
  export class BinaryUtils {
4
5
  /**
5
6
  * Converts a URL (data:, http:, or local path) to a base64 string and mime type.
@@ -28,25 +29,23 @@ export class BinaryUtils {
28
29
  };
29
30
  }
30
31
  catch (e) {
31
- console.error("Error converting URL to base64:", e);
32
+ logger.error("Error converting URL to base64:", e);
32
33
  return null;
33
34
  }
34
35
  }
35
36
  else {
36
37
  // Assume local file path
37
38
  try {
38
- if (fs.existsSync(url)) {
39
- const buffer = fs.readFileSync(url);
40
- const base64 = buffer.toString("base64");
41
- const mimeType = this.guessMimeType(url);
42
- return {
43
- mimeType,
44
- data: base64,
45
- };
46
- }
39
+ const buffer = await fs.promises.readFile(url);
40
+ const base64 = buffer.toString("base64");
41
+ const mimeType = this.guessMimeType(url);
42
+ return {
43
+ mimeType,
44
+ data: base64,
45
+ };
47
46
  }
48
47
  catch (e) {
49
- console.error("Error reading local file for base64:", e);
48
+ logger.error("Error reading local file for base64:", e);
50
49
  return null;
51
50
  }
52
51
  }
@@ -15,7 +15,7 @@ export class AudioUtils {
15
15
  fileName = path.basename(urlPath) || "audio.mp3";
16
16
  }
17
17
  else {
18
- const buffer = fs.readFileSync(filePath);
18
+ const buffer = await fs.promises.readFile(filePath);
19
19
  data = new Uint8Array(buffer);
20
20
  fileName = path.basename(filePath);
21
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,WAAgB,EACzB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,CAAC,CA2BnB"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/utils/fetch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE,WAAgB,EACzB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,QAAQ,CAAC,CAuCnB"}
@@ -9,7 +9,12 @@
9
9
  * @throws Error if the request times out
10
10
  */
11
11
  export async function fetchWithTimeout(url, options = {}, timeoutMs) {
12
- // If no timeout is specified, use standard fetch
12
+ const userSignal = options.signal;
13
+ // If no timeout is specified and no user signal, use standard fetch
14
+ if ((!timeoutMs || timeoutMs <= 0) && !userSignal) {
15
+ return fetch(url, options);
16
+ }
17
+ // If only user signal (no timeout), use it directly
13
18
  if (!timeoutMs || timeoutMs <= 0) {
14
19
  return fetch(url, options);
15
20
  }
@@ -18,17 +23,21 @@ export async function fetchWithTimeout(url, options = {}, timeoutMs) {
18
23
  if (timeoutId.unref)
19
24
  timeoutId.unref();
20
25
  try {
26
+ // Merge user signal with timeout signal
27
+ const mergedSignal = userSignal
28
+ ? AbortSignal.any([userSignal, controller.signal])
29
+ : controller.signal;
21
30
  const response = await fetch(url, {
22
31
  ...options,
23
- signal: controller.signal,
32
+ signal: mergedSignal,
24
33
  });
25
34
  clearTimeout(timeoutId);
26
35
  return response;
27
36
  }
28
37
  catch (error) {
29
38
  clearTimeout(timeoutId);
30
- // Check if the error was due to abort (timeout)
31
- if (error.name === 'AbortError') {
39
+ // Check if the error was due to timeout abort
40
+ if (error.name === 'AbortError' && controller.signal.aborted) {
32
41
  throw new Error(`Request timeout after ${timeoutMs}ms`);
33
42
  }
34
43
  throw error;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-llm/core",
3
- "version": "1.5.2",
3
+ "version": "1.5.4",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",