@node-llm/core 1.5.1 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/chat/Chat.d.ts +8 -21
  2. package/dist/chat/Chat.d.ts.map +1 -1
  3. package/dist/chat/Chat.js +76 -122
  4. package/dist/chat/ChatOptions.d.ts +3 -3
  5. package/dist/chat/ChatOptions.d.ts.map +1 -1
  6. package/dist/chat/ChatStream.d.ts +3 -21
  7. package/dist/chat/ChatStream.d.ts.map +1 -1
  8. package/dist/chat/ChatStream.js +95 -97
  9. package/dist/chat/Content.d.ts +10 -0
  10. package/dist/chat/Content.d.ts.map +1 -1
  11. package/dist/chat/Content.js +34 -1
  12. package/dist/chat/Tool.d.ts +6 -0
  13. package/dist/chat/Tool.d.ts.map +1 -1
  14. package/dist/chat/ToolHandler.d.ts +11 -0
  15. package/dist/chat/ToolHandler.d.ts.map +1 -0
  16. package/dist/chat/ToolHandler.js +41 -0
  17. package/dist/chat/Validation.d.ts +10 -0
  18. package/dist/chat/Validation.d.ts.map +1 -0
  19. package/dist/chat/Validation.js +32 -0
  20. package/dist/config.d.ts +43 -14
  21. package/dist/config.d.ts.map +1 -1
  22. package/dist/config.js +58 -13
  23. package/dist/errors/index.d.ts +8 -0
  24. package/dist/errors/index.d.ts.map +1 -1
  25. package/dist/errors/index.js +13 -0
  26. package/dist/llm.d.ts.map +1 -1
  27. package/dist/llm.js +13 -3
  28. package/dist/providers/openai/Capabilities.d.ts +1 -0
  29. package/dist/providers/openai/Capabilities.d.ts.map +1 -1
  30. package/dist/providers/openai/Capabilities.js +3 -0
  31. package/dist/providers/openai/Chat.d.ts.map +1 -1
  32. package/dist/providers/openai/Chat.js +11 -3
  33. package/dist/providers/openai/Streaming.d.ts.map +1 -1
  34. package/dist/providers/openai/Streaming.js +6 -1
  35. package/dist/providers/openai/types.d.ts +4 -0
  36. package/dist/providers/openai/types.d.ts.map +1 -1
  37. package/dist/utils/fetch.d.ts.map +1 -1
  38. package/dist/utils/fetch.js +2 -0
  39. package/dist/utils/logger.js +1 -1
  40. package/package.json +1 -1
@@ -1,7 +1,12 @@
1
+ import { isBinaryContent, formatMultimodalContent } from "./Content.js";
1
2
  import { ChatResponseString } from "./ChatResponse.js";
2
3
  import { Stream } from "../streaming/Stream.js";
3
4
  import { config } from "../config.js";
4
5
  import { ToolExecutionMode } from "../constants.js";
6
+ import { FileLoader } from "../utils/FileLoader.js";
7
+ import { toJsonSchema } from "../schema/to-json-schema.js";
8
+ import { ChatValidator } from "./Validation.js";
9
+ import { ToolHandler } from "./ToolHandler.js";
5
10
  /**
6
11
  * Internal handler for chat streaming logic.
7
12
  * Wraps the provider's stream with side effects like history updates and events.
@@ -18,7 +23,6 @@ export class ChatStream {
18
23
  this.options = options;
19
24
  this.messages = messages ?? [];
20
25
  this.systemMessages = systemMessages ?? [];
21
- // Only initialize if we're starting a new history
22
26
  if (this.messages.length === 0 && this.systemMessages.length === 0) {
23
27
  if (options.systemPrompt) {
24
28
  this.systemMessages.push({
@@ -41,39 +45,74 @@ export class ChatStream {
41
45
  this.options.toolExecution = config.toolExecution || ToolExecutionMode.AUTO;
42
46
  }
43
47
  }
44
- /**
45
- * Read-only access to message history
46
- */
47
48
  get history() {
48
49
  return [...this.systemMessages, ...this.messages];
49
50
  }
50
- /**
51
- * Creates a high-level Stream object for the chat response.
52
- * @param content The user's question.
53
- */
54
- create(content) {
51
+ create(content, options = {}) {
55
52
  const controller = new AbortController();
56
- // We create a wrapper async generator that handles our side effects
57
- const sideEffectGenerator = async function* (self, provider, model, messages, systemMessages, options, abortController) {
58
- messages.push({ role: "user", content });
53
+ const sideEffectGenerator = async function* (self, provider, model, messages, systemMessages, baseOptions, abortController, content, requestOptions) {
54
+ const options = {
55
+ ...baseOptions,
56
+ ...requestOptions,
57
+ headers: { ...baseOptions.headers, ...requestOptions.headers }
58
+ };
59
+ // Process Multimodal Content
60
+ let messageContent = content;
61
+ const files = [...(requestOptions.images ?? []), ...(requestOptions.files ?? [])];
62
+ if (files.length > 0) {
63
+ const processedFiles = await Promise.all(files.map((f) => FileLoader.load(f)));
64
+ const hasBinary = processedFiles.some(isBinaryContent);
65
+ ChatValidator.validateVision(provider, model, hasBinary, options);
66
+ messageContent = formatMultimodalContent(content, processedFiles);
67
+ }
68
+ if (options.tools && options.tools.length > 0) {
69
+ ChatValidator.validateTools(provider, model, true, options);
70
+ }
71
+ messages.push({ role: "user", content: messageContent });
72
+ if (!provider.stream) {
73
+ throw new Error("Streaming not supported by provider");
74
+ }
75
+ // Process Schema/Structured Output
76
+ let responseFormat = options.responseFormat;
77
+ if (!responseFormat && options.schema) {
78
+ ChatValidator.validateStructuredOutput(provider, model, true, options);
79
+ const jsonSchema = toJsonSchema(options.schema.definition.schema);
80
+ responseFormat = {
81
+ type: "json_schema",
82
+ json_schema: {
83
+ name: options.schema.definition.name,
84
+ description: options.schema.definition.description,
85
+ strict: options.schema.definition.strict ?? true,
86
+ schema: jsonSchema,
87
+ }
88
+ };
89
+ }
59
90
  if (!provider.stream) {
60
91
  throw new Error("Streaming not supported by provider");
61
92
  }
62
- let fullContent = "";
63
- let fullReasoning = "";
64
- let toolCalls;
65
93
  let isFirst = true;
66
94
  const maxToolCalls = options.maxToolCalls ?? 5;
67
95
  let stepCount = 0;
68
- // Main streaming loop - may iterate multiple times for tool calls
96
+ let totalUsage = { input_tokens: 0, output_tokens: 0, total_tokens: 0 };
97
+ const trackUsage = (u) => {
98
+ if (u) {
99
+ totalUsage.input_tokens += u.input_tokens;
100
+ totalUsage.output_tokens += u.output_tokens;
101
+ totalUsage.total_tokens += u.total_tokens;
102
+ if (u.cached_tokens) {
103
+ totalUsage.cached_tokens = (totalUsage.cached_tokens ?? 0) + u.cached_tokens;
104
+ }
105
+ }
106
+ };
69
107
  while (true) {
70
108
  stepCount++;
71
109
  if (stepCount > maxToolCalls) {
72
110
  throw new Error(`[NodeLLM] Maximum tool execution calls (${maxToolCalls}) exceeded during streaming.`);
73
111
  }
74
- fullContent = "";
75
- fullReasoning = "";
76
- toolCalls = undefined;
112
+ let fullContent = "";
113
+ let fullReasoning = "";
114
+ let toolCalls;
115
+ let currentTurnUsage;
77
116
  try {
78
117
  let requestMessages = [...systemMessages, ...messages];
79
118
  if (options.onBeforeRequest) {
@@ -88,8 +127,11 @@ export class ChatStream {
88
127
  tools: options.tools,
89
128
  temperature: options.temperature,
90
129
  max_tokens: options.maxTokens ?? config.maxTokens,
130
+ response_format: responseFormat,
131
+ headers: options.headers,
91
132
  requestTimeout: options.requestTimeout ?? config.requestTimeout,
92
133
  signal: abortController.signal,
134
+ ...options.params,
93
135
  })) {
94
136
  if (isFirst) {
95
137
  if (options.onNewMessage)
@@ -104,43 +146,40 @@ export class ChatStream {
104
146
  fullReasoning += chunk.reasoning;
105
147
  yield { content: "", reasoning: chunk.reasoning };
106
148
  }
107
- // Accumulate tool calls from the final chunk
108
149
  if (chunk.tool_calls) {
109
150
  toolCalls = chunk.tool_calls;
110
151
  }
152
+ if (chunk.usage) {
153
+ currentTurnUsage = chunk.usage;
154
+ trackUsage(currentTurnUsage);
155
+ }
111
156
  }
112
- // Build the response object for hooks and history
113
- let assistantResponse = new ChatResponseString(fullContent || "", { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, model, provider.id, fullReasoning || undefined);
114
- // --- Content Policy Hooks (Output) ---
157
+ let assistantResponse = new ChatResponseString(fullContent || "", currentTurnUsage || { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, model, provider.id, fullReasoning || undefined);
115
158
  if (options.onAfterResponse) {
116
159
  const result = await options.onAfterResponse(assistantResponse);
117
160
  if (result) {
118
161
  assistantResponse = result;
119
162
  }
120
163
  }
121
- // Add assistant message to history (now potentially redacted)
122
164
  messages.push({
123
165
  role: "assistant",
124
166
  content: assistantResponse || null,
125
167
  tool_calls: toolCalls,
126
- reasoning: fullReasoning || undefined
168
+ reasoning: fullReasoning || undefined,
169
+ usage: currentTurnUsage
127
170
  });
128
- // If no tool calls, we're done
129
171
  if (!toolCalls || toolCalls.length === 0) {
130
172
  if (options.onEndMessage) {
131
173
  options.onEndMessage(assistantResponse);
132
174
  }
133
175
  break;
134
176
  }
135
- // Dry-run mode: stop after proposing tools
136
- if (!self.shouldExecuteTools(toolCalls, options.toolExecution)) {
177
+ if (!ToolHandler.shouldExecuteTools(toolCalls, options.toolExecution)) {
137
178
  break;
138
179
  }
139
- // Execute tool calls
140
180
  for (const toolCall of toolCalls) {
141
- // Confirm mode: request approval
142
181
  if (options.toolExecution === ToolExecutionMode.CONFIRM) {
143
- const approved = await self.requestToolConfirmation(toolCall, options.onConfirmToolCall);
182
+ const approved = await ToolHandler.requestToolConfirmation(toolCall, options.onConfirmToolCall);
144
183
  if (!approved) {
145
184
  messages.push({
146
185
  role: "tool",
@@ -150,80 +189,39 @@ export class ChatStream {
150
189
  continue;
151
190
  }
152
191
  }
153
- // Execute the tool
154
- const toolResult = await self.executeToolCall(toolCall, options.tools, options.onToolCallStart, options.onToolCallEnd, options.onToolCallError);
155
- messages.push(toolResult);
192
+ try {
193
+ const toolResult = await ToolHandler.execute(toolCall, options.tools, options.onToolCallStart, options.onToolCallEnd);
194
+ messages.push(toolResult);
195
+ }
196
+ catch (error) {
197
+ const directive = await options.onToolCallError?.(toolCall, error);
198
+ if (directive === 'STOP') {
199
+ throw error;
200
+ }
201
+ messages.push({
202
+ role: "tool",
203
+ tool_call_id: toolCall.id,
204
+ content: `Fatal error executing tool '${toolCall.function.name}': ${error.message}`,
205
+ });
206
+ if (directive === 'CONTINUE') {
207
+ continue;
208
+ }
209
+ const isFatal = error.fatal === true || error.status === 401 || error.status === 403;
210
+ if (isFatal) {
211
+ throw error;
212
+ }
213
+ console.error(`[NodeLLM] Tool execution failed for '${toolCall.function.name}':`, error);
214
+ }
156
215
  }
157
- // Continue loop to stream the next response after tool execution
158
216
  }
159
217
  catch (error) {
160
218
  if (error instanceof Error && error.name === 'AbortError') {
161
- // Stream was aborted
219
+ // Aborted
162
220
  }
163
221
  throw error;
164
222
  }
165
223
  }
166
224
  };
167
- return new Stream(() => sideEffectGenerator(this, this.provider, this.model, this.messages, this.systemMessages, this.options, controller), controller);
168
- }
169
- /**
170
- * Check if tool execution should proceed based on the current mode.
171
- */
172
- shouldExecuteTools(toolCalls, mode) {
173
- if (!toolCalls || toolCalls.length === 0)
174
- return false;
175
- if (mode === ToolExecutionMode.DRY_RUN)
176
- return false;
177
- return true;
178
- }
179
- /**
180
- * Request user confirmation for a tool call in "confirm" mode.
181
- * Returns true if approved, false if rejected.
182
- */
183
- async requestToolConfirmation(toolCall, onConfirm) {
184
- if (!onConfirm)
185
- return true;
186
- const confirmed = await onConfirm(toolCall);
187
- return confirmed !== false;
188
- }
189
- /**
190
- * Execute a single tool call and return the result message.
191
- */
192
- async executeToolCall(toolCall, tools, onStart, onEnd, onError) {
193
- if (onStart)
194
- onStart(toolCall);
195
- const tool = tools?.find((t) => t.function.name === toolCall.function.name);
196
- if (tool?.handler) {
197
- try {
198
- const args = JSON.parse(toolCall.function.arguments);
199
- const result = await tool.handler(args);
200
- if (onEnd)
201
- onEnd(toolCall, result);
202
- return {
203
- role: "tool",
204
- tool_call_id: toolCall.id,
205
- content: result,
206
- };
207
- }
208
- catch (error) {
209
- if (onError)
210
- onError(toolCall, error);
211
- return {
212
- role: "tool",
213
- tool_call_id: toolCall.id,
214
- content: `Error executing tool: ${error.message}`,
215
- };
216
- }
217
- }
218
- else {
219
- const error = new Error("Tool not found or no handler provided");
220
- if (onError)
221
- onError(toolCall, error);
222
- return {
223
- role: "tool",
224
- tool_call_id: toolCall.id,
225
- content: "Error: Tool not found or no handler provided",
226
- };
227
- }
225
+ return new Stream(() => sideEffectGenerator(this, this.provider, this.model, this.messages, this.systemMessages, this.options, controller, content, options), controller);
228
226
  }
229
227
  }
@@ -18,5 +18,15 @@ export type ContentPart = {
18
18
  url: string;
19
19
  };
20
20
  };
21
+ export declare const isBinaryContent: (part: ContentPart) => boolean;
22
+ export declare const isTextContent: (part: ContentPart) => boolean;
23
+ export declare const partitionContentParts: (parts: ContentPart[]) => {
24
+ textParts: ({
25
+ type: "text";
26
+ text: string;
27
+ })[];
28
+ binaryParts: ContentPart[];
29
+ };
30
+ export declare const formatMultimodalContent: (content: string | ContentPart[], parts: ContentPart[]) => MessageContent;
21
31
  export type MessageContent = string | String | ContentPart[];
22
32
  //# sourceMappingURL=Content.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Content.d.ts","sourceRoot":"","sources":["../../src/chat/Content.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAEtD,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,EAAE,CAAC"}
1
+ {"version":3,"file":"Content.d.ts","sourceRoot":"","sources":["../../src/chat/Content.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACjD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,GACtE;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAEtD,eAAO,MAAM,eAAe,GAAI,MAAM,WAAW,KAAG,OACmC,CAAC;AAExF,eAAO,MAAM,aAAa,GAAI,MAAM,WAAW,KAAG,OAC5B,CAAC;AAEvB,eAAO,MAAM,qBAAqB,GAAI,OAAO,WAAW,EAAE;eAEZ,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE;;CAG/E,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAAI,SAAS,MAAM,GAAG,WAAW,EAAE,EAAE,OAAO,WAAW,EAAE,KAAG,cA2B/F,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,WAAW,EAAE,CAAC"}
@@ -1 +1,34 @@
1
- export {};
1
+ export const isBinaryContent = (part) => part.type === "image_url" || part.type === "input_audio" || part.type === "video_url";
2
+ export const isTextContent = (part) => part.type === "text";
3
+ export const partitionContentParts = (parts) => {
4
+ return {
5
+ textParts: parts.filter(isTextContent),
6
+ binaryParts: parts.filter(isBinaryContent)
7
+ };
8
+ };
9
+ export const formatMultimodalContent = (content, parts) => {
10
+ const { textParts, binaryParts } = partitionContentParts(parts);
11
+ let fullText = typeof content === "string" ? content : "";
12
+ let currentParts = typeof content === "string" ? [] : content;
13
+ if (textParts.length > 0) {
14
+ const additionalText = textParts.map(f => f.text).join("\n");
15
+ if (typeof content === "string") {
16
+ fullText += "\n" + additionalText;
17
+ }
18
+ else {
19
+ currentParts.push({ type: "text", text: additionalText });
20
+ }
21
+ }
22
+ if (binaryParts.length > 0) {
23
+ if (typeof content === "string") {
24
+ return [
25
+ { type: "text", text: fullText },
26
+ ...binaryParts
27
+ ];
28
+ }
29
+ else {
30
+ return [...currentParts, ...binaryParts];
31
+ }
32
+ }
33
+ return typeof content === "string" ? fullText : currentParts;
34
+ };
@@ -16,6 +16,12 @@ export interface ToolDefinition {
16
16
  };
17
17
  handler?: (args: any) => Promise<string>;
18
18
  }
19
+ /**
20
+ * Anything that can be resolved into a ToolDefinition.
21
+ */
22
+ export type ToolResolvable = Tool | {
23
+ new (): Tool;
24
+ } | ToolDefinition | any;
19
25
  /**
20
26
  * Subclass this to create tools with auto-generated schemas and type safety.
21
27
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Tool.d.ts","sourceRoot":"","sources":["../../src/chat/Tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KACjC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C;AAED;;GAEG;AACH,8BAAsB,IAAI,CAAC,CAAC,GAAG,GAAG;IAChC;;OAEG;IACH,SAAgB,IAAI,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,SAAgB,WAAW,EAAE,MAAM,CAAC;IAEpC;;;OAGG;IACH,SAAgB,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE/D;;;OAGG;aACa,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAE9C;;;OAGG;IACU,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAM9C;;;OAGG;IACI,SAAS,IAAI,cAAc;CAoBnC"}
1
+ {"version":3,"file":"Tool.d.ts","sourceRoot":"","sources":["../../src/chat/Tool.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,CAAC;IACjB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KACjC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG;IAAE,QAAO,IAAI,CAAA;CAAE,GAAG,cAAc,GAAG,GAAG,CAAC;AAE3E;;GAEG;AACH,8BAAsB,IAAI,CAAC,CAAC,GAAG,GAAG;IAChC;;OAEG;IACH,SAAgB,IAAI,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,SAAgB,WAAW,EAAE,MAAM,CAAC;IAEpC;;;OAGG;IACH,SAAgB,MAAM,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAE/D;;;OAGG;aACa,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC;IAE9C;;;OAGG;IACU,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAM9C;;;OAGG;IACI,SAAS,IAAI,cAAc;CAoBnC"}
@@ -0,0 +1,11 @@
1
+ import { ToolExecutionMode } from "../constants.js";
2
+ export declare class ToolHandler {
3
+ static shouldExecuteTools(toolCalls: any[] | undefined, mode?: ToolExecutionMode): boolean;
4
+ static requestToolConfirmation(toolCall: any, onConfirm?: (call: any) => Promise<boolean> | boolean): Promise<boolean>;
5
+ static execute(toolCall: any, tools: any[] | undefined, onStart?: (call: any) => void, onEnd?: (call: any, result: any) => void): Promise<{
6
+ role: "tool";
7
+ tool_call_id: string;
8
+ content: string;
9
+ }>;
10
+ }
11
+ //# sourceMappingURL=ToolHandler.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,41 @@
1
+ import { ToolExecutionMode } from "../constants.js";
2
+ import { ToolError } from "../errors/index.js";
3
+ export class ToolHandler {
4
+ static shouldExecuteTools(toolCalls, mode) {
5
+ if (!toolCalls || toolCalls.length === 0)
6
+ return false;
7
+ if (mode === ToolExecutionMode.DRY_RUN)
8
+ return false;
9
+ return true;
10
+ }
11
+ static async requestToolConfirmation(toolCall, onConfirm) {
12
+ if (!onConfirm)
13
+ return true;
14
+ const confirmed = await onConfirm(toolCall);
15
+ return confirmed !== false;
16
+ }
17
+ static async execute(toolCall, tools, onStart, onEnd) {
18
+ if (onStart)
19
+ onStart(toolCall);
20
+ const tool = tools?.find((t) => t.function.name === toolCall.function.name);
21
+ if (tool?.handler) {
22
+ try {
23
+ const args = JSON.parse(toolCall.function.arguments);
24
+ const result = await tool.handler(args);
25
+ if (onEnd)
26
+ onEnd(toolCall, result);
27
+ return {
28
+ role: "tool",
29
+ tool_call_id: toolCall.id,
30
+ content: result,
31
+ };
32
+ }
33
+ catch (error) {
34
+ throw error;
35
+ }
36
+ }
37
+ else {
38
+ throw new ToolError("Tool not found or no handler provided", toolCall.function?.name ?? "unknown");
39
+ }
40
+ }
41
+ }
@@ -0,0 +1,10 @@
1
+ import { Provider } from "../providers/Provider.js";
2
+ export interface ValidationOptions {
3
+ assumeModelExists?: boolean;
4
+ }
5
+ export declare class ChatValidator {
6
+ static validateVision(provider: Provider, model: string, hasBinary: boolean, options: ValidationOptions): void;
7
+ static validateTools(provider: Provider, model: string, hasTools: boolean, options: ValidationOptions): void;
8
+ static validateStructuredOutput(provider: Provider, model: string, hasSchema: boolean, options: ValidationOptions): void;
9
+ }
10
+ //# sourceMappingURL=Validation.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,32 @@
1
+ export class ChatValidator {
2
+ static validateVision(provider, model, hasBinary, options) {
3
+ if (!hasBinary)
4
+ return;
5
+ if (!options.assumeModelExists && provider.capabilities && !provider.capabilities.supportsVision(model)) {
6
+ throw new Error(`Model ${model} does not support vision/binary files.`);
7
+ }
8
+ if (options.assumeModelExists) {
9
+ console.warn(`[NodeLLM] Skipping vision capability validation for model ${model}`);
10
+ }
11
+ }
12
+ static validateTools(provider, model, hasTools, options) {
13
+ if (!hasTools)
14
+ return;
15
+ if (!options.assumeModelExists && provider.capabilities && !provider.capabilities.supportsTools(model)) {
16
+ throw new Error(`Model ${model} does not support tool calling.`);
17
+ }
18
+ if (options.assumeModelExists) {
19
+ console.warn(`[NodeLLM] Skipping tool capability validation for model ${model}`);
20
+ }
21
+ }
22
+ static validateStructuredOutput(provider, model, hasSchema, options) {
23
+ if (!hasSchema)
24
+ return;
25
+ if (!options.assumeModelExists && provider.capabilities && !provider.capabilities.supportsStructuredOutput(model)) {
26
+ throw new Error(`Model ${model} does not support structured output.`);
27
+ }
28
+ if (options.assumeModelExists) {
29
+ console.warn(`[NodeLLM] Skipping structured output capability validation for model ${model}`);
30
+ }
31
+ }
32
+ }
package/dist/config.d.ts CHANGED
@@ -22,25 +22,54 @@ export interface NodeLLMConfig {
22
22
  toolExecution?: ToolExecutionMode;
23
23
  }
24
24
  import { ToolExecutionMode } from "./constants.js";
25
- declare class Configuration implements NodeLLMConfig {
26
- openaiApiKey?: string;
27
- openaiApiBase?: string;
28
- anthropicApiKey?: string;
29
- anthropicApiBase?: string;
30
- geminiApiKey?: string;
31
- geminiApiBase?: string;
32
- deepseekApiKey?: string;
33
- deepseekApiBase?: string;
34
- ollamaApiBase?: string;
35
- openrouterApiKey?: string;
36
- openrouterApiBase?: string;
37
- debug?: boolean;
25
+ export declare class Configuration implements NodeLLMConfig {
26
+ private _openaiApiKey?;
27
+ private _openaiApiBase?;
28
+ private _anthropicApiKey?;
29
+ private _anthropicApiBase?;
30
+ private _geminiApiKey?;
31
+ private _geminiApiBase?;
32
+ private _deepseekApiKey?;
33
+ private _deepseekApiBase?;
34
+ private _ollamaApiBase?;
35
+ private _openrouterApiKey?;
36
+ private _openrouterApiBase?;
37
+ private _debug?;
38
+ get openaiApiKey(): string | undefined;
39
+ set openaiApiKey(v: string | undefined);
40
+ get openaiApiBase(): string | undefined;
41
+ set openaiApiBase(v: string | undefined);
42
+ get anthropicApiKey(): string | undefined;
43
+ set anthropicApiKey(v: string | undefined);
44
+ get anthropicApiBase(): string | undefined;
45
+ set anthropicApiBase(v: string | undefined);
46
+ get geminiApiKey(): string | undefined;
47
+ set geminiApiKey(v: string | undefined);
48
+ get geminiApiBase(): string | undefined;
49
+ set geminiApiBase(v: string | undefined);
50
+ get deepseekApiKey(): string | undefined;
51
+ set deepseekApiKey(v: string | undefined);
52
+ get deepseekApiBase(): string | undefined;
53
+ set deepseekApiBase(v: string | undefined);
54
+ get ollamaApiBase(): string | undefined;
55
+ set ollamaApiBase(v: string | undefined);
56
+ get openrouterApiKey(): string | undefined;
57
+ set openrouterApiKey(v: string | undefined);
58
+ get openrouterApiBase(): string | undefined;
59
+ set openrouterApiBase(v: string | undefined);
60
+ get debug(): boolean | undefined;
61
+ set debug(v: boolean | undefined);
38
62
  maxToolCalls: number;
39
63
  maxRetries: number;
40
64
  requestTimeout: number;
41
65
  maxTokens: number;
42
66
  toolExecution: ToolExecutionMode;
67
+ /**
68
+ * Returns a plain object with all configuration values.
69
+ * This is useful for cloning or serialization.
70
+ * It handles getters (lazy-loaded values) correctly.
71
+ */
72
+ toPlainObject(): NodeLLMConfig;
43
73
  }
44
74
  export declare const config: Configuration;
45
- export {};
46
75
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,iBAAiB,CAAC;CACnC;AAED,OAAO,EAOL,iBAAiB,EAClB,MAAM,gBAAgB,CAAC;AAExB,cAAM,aAAc,YAAW,aAAa;IACnC,YAAY,CAAC,EAAE,MAAM,CAAsC;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAuC;IAC7D,eAAe,CAAC,EAAE,MAAM,CAAyC;IACjE,gBAAgB,CAAC,EAAE,MAAM,CAA0C;IACnE,YAAY,CAAC,EAAE,MAAM,CAAsC;IAC3D,aAAa,CAAC,EAAE,MAAM,CAAuC;IAC7D,cAAc,CAAC,EAAE,MAAM,CAAwC;IAC/D,eAAe,CAAC,EAAE,MAAM,CAAyC;IACjE,aAAa,CAAC,EAAE,MAAM,CAAkE;IACxF,gBAAgB,CAAC,EAAE,MAAM,CAA0C;IACnE,iBAAiB,CAAC,EAAE,MAAM,CAA2C;IACrE,KAAK,CAAC,EAAE,OAAO,CAAwC;IACvD,YAAY,EAAE,MAAM,CAA0B;IAC9C,UAAU,EAAE,MAAM,CAAuB;IACzC,cAAc,EAAE,MAAM,CAA2B;IACjD,SAAS,EAAE,MAAM,CAAsB;IACvC,aAAa,EAAE,iBAAiB,CAA0B;CAClE;AAED,eAAO,MAAM,MAAM,eAAsB,CAAC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
package/dist/config.js CHANGED
@@ -1,21 +1,66 @@
1
1
  import { DEFAULT_MAX_TOOL_CALLS, DEFAULT_MAX_RETRIES, DEFAULT_REQUEST_TIMEOUT, DEFAULT_MAX_TOKENS, DEFAULT_TOOL_EXECUTION, DEFAULT_OLLAMA_BASE_URL } from "./constants.js";
2
- class Configuration {
3
- openaiApiKey = process.env.OPENAI_API_KEY?.trim();
4
- openaiApiBase = process.env.OPENAI_API_BASE?.trim();
5
- anthropicApiKey = process.env.ANTHROPIC_API_KEY?.trim();
6
- anthropicApiBase = process.env.ANTHROPIC_API_BASE?.trim();
7
- geminiApiKey = process.env.GEMINI_API_KEY?.trim();
8
- geminiApiBase = process.env.GEMINI_API_BASE?.trim();
9
- deepseekApiKey = process.env.DEEPSEEK_API_KEY?.trim();
10
- deepseekApiBase = process.env.DEEPSEEK_API_BASE?.trim();
11
- ollamaApiBase = process.env.OLLAMA_API_BASE?.trim() || DEFAULT_OLLAMA_BASE_URL;
12
- openrouterApiKey = process.env.OPENROUTER_API_KEY?.trim();
13
- openrouterApiBase = process.env.OPENROUTER_API_BASE?.trim();
14
- debug = process.env.NODELLM_DEBUG === "true";
2
+ export class Configuration {
3
+ _openaiApiKey;
4
+ _openaiApiBase;
5
+ _anthropicApiKey;
6
+ _anthropicApiBase;
7
+ _geminiApiKey;
8
+ _geminiApiBase;
9
+ _deepseekApiKey;
10
+ _deepseekApiBase;
11
+ _ollamaApiBase;
12
+ _openrouterApiKey;
13
+ _openrouterApiBase;
14
+ _debug;
15
+ get openaiApiKey() { return this._openaiApiKey ?? process.env.OPENAI_API_KEY?.trim(); }
16
+ set openaiApiKey(v) { this._openaiApiKey = v; }
17
+ get openaiApiBase() { return this._openaiApiBase ?? process.env.OPENAI_API_BASE?.trim(); }
18
+ set openaiApiBase(v) { this._openaiApiBase = v; }
19
+ get anthropicApiKey() { return this._anthropicApiKey ?? process.env.ANTHROPIC_API_KEY?.trim(); }
20
+ set anthropicApiKey(v) { this._anthropicApiKey = v; }
21
+ get anthropicApiBase() { return this._anthropicApiBase ?? process.env.ANTHROPIC_API_BASE?.trim(); }
22
+ set anthropicApiBase(v) { this._anthropicApiBase = v; }
23
+ get geminiApiKey() { return this._geminiApiKey ?? process.env.GEMINI_API_KEY?.trim(); }
24
+ set geminiApiKey(v) { this._geminiApiKey = v; }
25
+ get geminiApiBase() { return this._geminiApiBase ?? process.env.GEMINI_API_BASE?.trim(); }
26
+ set geminiApiBase(v) { this._geminiApiBase = v; }
27
+ get deepseekApiKey() { return this._deepseekApiKey ?? process.env.DEEPSEEK_API_KEY?.trim(); }
28
+ set deepseekApiKey(v) { this._deepseekApiKey = v; }
29
+ get deepseekApiBase() { return this._deepseekApiBase ?? process.env.DEEPSEEK_API_BASE?.trim(); }
30
+ set deepseekApiBase(v) { this._deepseekApiBase = v; }
31
+ get ollamaApiBase() { return this._ollamaApiBase ?? process.env.OLLAMA_API_BASE?.trim() ?? DEFAULT_OLLAMA_BASE_URL; }
32
+ set ollamaApiBase(v) { this._ollamaApiBase = v; }
33
+ get openrouterApiKey() { return this._openrouterApiKey ?? process.env.OPENROUTER_API_KEY?.trim(); }
34
+ set openrouterApiKey(v) { this._openrouterApiKey = v; }
35
+ get openrouterApiBase() { return this._openrouterApiBase ?? process.env.OPENROUTER_API_BASE?.trim(); }
36
+ set openrouterApiBase(v) { this._openrouterApiBase = v; }
37
+ get debug() { return this._debug ?? process.env.NODELLM_DEBUG === "true"; }
38
+ set debug(v) { this._debug = v; }
15
39
  maxToolCalls = DEFAULT_MAX_TOOL_CALLS;
16
40
  maxRetries = DEFAULT_MAX_RETRIES;
17
41
  requestTimeout = DEFAULT_REQUEST_TIMEOUT;
18
42
  maxTokens = DEFAULT_MAX_TOKENS;
19
43
  toolExecution = DEFAULT_TOOL_EXECUTION;
44
+ /**
45
+ * Returns a plain object with all configuration values.
46
+ * This is useful for cloning or serialization.
47
+ * It handles getters (lazy-loaded values) correctly.
48
+ */
49
+ toPlainObject() {
50
+ const plain = { ...this }; // Capture all enumerable "own" properties (custom keys, overrides)
51
+ // Capture all getters from the class prototype (lazy-loaded values)
52
+ const prototype = Object.getPrototypeOf(this);
53
+ const propertyNames = Object.getOwnPropertyNames(prototype);
54
+ for (const name of propertyNames) {
55
+ if (name === "constructor")
56
+ continue;
57
+ const descriptor = Object.getOwnPropertyDescriptor(prototype, name);
58
+ if (descriptor && descriptor.get) {
59
+ // Trigger the getter to snapshot the live value (including env fallbacks)
60
+ plain[name] = this[name];
61
+ }
62
+ }
63
+ return plain;
64
+ }
20
65
  }
21
66
  export const config = new Configuration();
@@ -85,4 +85,12 @@ export declare class ModelCapabilityError extends LLMError {
85
85
  readonly capability: string;
86
86
  constructor(model: string, capability: string);
87
87
  }
88
+ /**
89
+ * Thrown when a tool execution fails
90
+ */
91
+ export declare class ToolError extends LLMError {
92
+ readonly toolName?: string | undefined;
93
+ readonly fatal: boolean;
94
+ constructor(message: string, toolName?: string | undefined, fatal?: boolean);
95
+ }
88
96
  //# sourceMappingURL=index.d.ts.map