@node-llm/core 1.9.0 → 1.11.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.
Files changed (52) hide show
  1. package/README.md +89 -6
  2. package/dist/agent/Agent.d.ts +191 -0
  3. package/dist/agent/Agent.d.ts.map +1 -0
  4. package/dist/agent/Agent.js +272 -0
  5. package/dist/aliases.d.ts +102 -9
  6. package/dist/aliases.d.ts.map +1 -1
  7. package/dist/aliases.js +102 -9
  8. package/dist/chat/Chat.d.ts +1 -0
  9. package/dist/chat/Chat.d.ts.map +1 -1
  10. package/dist/chat/Chat.js +184 -131
  11. package/dist/chat/ChatOptions.d.ts +2 -0
  12. package/dist/chat/ChatOptions.d.ts.map +1 -1
  13. package/dist/chat/ChatStream.d.ts.map +1 -1
  14. package/dist/chat/ChatStream.js +109 -66
  15. package/dist/chat/Tool.d.ts +43 -2
  16. package/dist/chat/Tool.d.ts.map +1 -1
  17. package/dist/chat/Tool.js +50 -0
  18. package/dist/chat/ToolHandler.d.ts +10 -5
  19. package/dist/chat/ToolHandler.d.ts.map +1 -1
  20. package/dist/chat/ToolHandler.js +10 -2
  21. package/dist/index.d.ts +5 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +3 -0
  24. package/dist/llm.d.ts +8 -1
  25. package/dist/llm.d.ts.map +1 -1
  26. package/dist/llm.js +156 -59
  27. package/dist/middlewares/CostGuardMiddleware.d.ts +24 -0
  28. package/dist/middlewares/CostGuardMiddleware.d.ts.map +1 -0
  29. package/dist/middlewares/CostGuardMiddleware.js +23 -0
  30. package/dist/middlewares/PIIMaskMiddleware.d.ts +23 -0
  31. package/dist/middlewares/PIIMaskMiddleware.d.ts.map +1 -0
  32. package/dist/middlewares/PIIMaskMiddleware.js +41 -0
  33. package/dist/middlewares/UsageLoggerMiddleware.d.ts +22 -0
  34. package/dist/middlewares/UsageLoggerMiddleware.d.ts.map +1 -0
  35. package/dist/middlewares/UsageLoggerMiddleware.js +30 -0
  36. package/dist/middlewares/index.d.ts +4 -0
  37. package/dist/middlewares/index.d.ts.map +1 -0
  38. package/dist/middlewares/index.js +3 -0
  39. package/dist/models/models.json +1458 -448
  40. package/dist/providers/BaseProvider.d.ts +6 -1
  41. package/dist/providers/BaseProvider.d.ts.map +1 -1
  42. package/dist/providers/BaseProvider.js +19 -0
  43. package/dist/providers/openai/OpenAIProvider.d.ts +1 -1
  44. package/dist/providers/openai/OpenAIProvider.d.ts.map +1 -1
  45. package/dist/providers/openai/OpenAIProvider.js +13 -2
  46. package/dist/types/Middleware.d.ts +106 -0
  47. package/dist/types/Middleware.d.ts.map +1 -0
  48. package/dist/types/Middleware.js +1 -0
  49. package/dist/utils/middleware-runner.d.ts +7 -0
  50. package/dist/utils/middleware-runner.d.ts.map +1 -0
  51. package/dist/utils/middleware-runner.js +23 -0
  52. package/package.json +6 -2
@@ -14,7 +14,32 @@ export interface ToolDefinition {
14
14
  description?: string;
15
15
  parameters: Record<string, unknown>;
16
16
  };
17
- handler?: (args: unknown) => Promise<string>;
17
+ handler?: (args: unknown) => Promise<string | ToolHalt>;
18
+ }
19
+ /**
20
+ * Stops the agentic tool execution loop and returns the content as the final response.
21
+ * Return this from a tool's execute() method to immediately end the conversation turn.
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * class PaymentTool extends Tool {
26
+ * async execute({ amount }) {
27
+ * if (amount > 10000) {
28
+ * return this.halt("Payment requires manager approval. Please contact support.");
29
+ * }
30
+ * return processPayment(amount);
31
+ * }
32
+ * }
33
+ * ```
34
+ */
35
+ export declare class ToolHalt {
36
+ readonly content: string;
37
+ constructor(content: string);
38
+ toString(): string;
39
+ toJSON(): {
40
+ halt: true;
41
+ content: string;
42
+ };
18
43
  }
19
44
  /**
20
45
  * Anything that can be resolved into a ToolDefinition.
@@ -44,11 +69,27 @@ export declare abstract class Tool<T = Record<string, unknown>> {
44
69
  * 'args' will be parsed and validated based on 'schema'.
45
70
  */
46
71
  abstract execute(args: T): Promise<unknown>;
72
+ /**
73
+ * Stop the agentic loop and return this message as the final tool result.
74
+ * Use this when you want to end the conversation turn immediately.
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * async execute({ amount }) {
79
+ * if (amount > 10000) {
80
+ * return this.halt("Payment requires manager approval.");
81
+ * }
82
+ * return processPayment(amount);
83
+ * }
84
+ * ```
85
+ */
86
+ protected halt(message: string): ToolHalt;
47
87
  /**
48
88
  * Internal handler to bridge with LLM providers.
49
89
  * Converts any result to a string (usually JSON).
90
+ * Preserves ToolHalt instances for the execution loop to detect.
50
91
  */
51
- handler(args: T): Promise<string>;
92
+ handler(args: T): Promise<string | ToolHalt>;
52
93
  /**
53
94
  * Converts the tool definition and logic into a standard ToolDefinition interface.
54
95
  * This is called automatically by NodeLLM when registering tools.
@@ -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,OAAO,CAAC,CAAC;KACrC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG;IAAE,QAAQ,IAAI,CAAA;CAAE,GAAG,cAAc,CAAC;AAEtE;;GAEG;AACH,8BAAsB,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpD;;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,CAAC,CAAC,WAAW,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE7E;;;OAGG;aACa,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAElD;;;OAGG;IACU,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAM9C;;;OAGG;IACI,SAAS,IAAI,cAAc;CAuBnC"}
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,OAAO,CAAC,CAAC;KACrC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC;CACzD;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,QAAQ;aACS,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;IAE3C,QAAQ,IAAI,MAAM;IAIlB,MAAM,IAAI;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;CAG1C;AAED;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG,IAAI,GAAG;IAAE,QAAQ,IAAI,CAAA;CAAE,GAAG,cAAc,CAAC;AAEtE;;GAEG;AACH,8BAAsB,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACpD;;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,CAAC,CAAC,WAAW,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE7E;;;OAGG;aACa,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IAElD;;;;;;;;;;;;;OAaG;IACH,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,QAAQ;IAIzC;;;;OAIG;IACU,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,QAAQ,CAAC;IAYzD;;;OAGG;IACI,SAAS,IAAI,cAAc;CAuBnC"}
package/dist/chat/Tool.js CHANGED
@@ -1,14 +1,64 @@
1
1
  import { toJsonSchema } from "../schema/to-json-schema.js";
2
+ /**
3
+ * Stops the agentic tool execution loop and returns the content as the final response.
4
+ * Return this from a tool's execute() method to immediately end the conversation turn.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * class PaymentTool extends Tool {
9
+ * async execute({ amount }) {
10
+ * if (amount > 10000) {
11
+ * return this.halt("Payment requires manager approval. Please contact support.");
12
+ * }
13
+ * return processPayment(amount);
14
+ * }
15
+ * }
16
+ * ```
17
+ */
18
+ export class ToolHalt {
19
+ content;
20
+ constructor(content) {
21
+ this.content = content;
22
+ }
23
+ toString() {
24
+ return this.content;
25
+ }
26
+ toJSON() {
27
+ return { halt: true, content: this.content };
28
+ }
29
+ }
2
30
  /**
3
31
  * Subclass this to create tools with auto-generated schemas and type safety.
4
32
  */
5
33
  export class Tool {
34
+ /**
35
+ * Stop the agentic loop and return this message as the final tool result.
36
+ * Use this when you want to end the conversation turn immediately.
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * async execute({ amount }) {
41
+ * if (amount > 10000) {
42
+ * return this.halt("Payment requires manager approval.");
43
+ * }
44
+ * return processPayment(amount);
45
+ * }
46
+ * ```
47
+ */
48
+ halt(message) {
49
+ return new ToolHalt(message);
50
+ }
6
51
  /**
7
52
  * Internal handler to bridge with LLM providers.
8
53
  * Converts any result to a string (usually JSON).
54
+ * Preserves ToolHalt instances for the execution loop to detect.
9
55
  */
10
56
  async handler(args) {
11
57
  const result = await this.execute(args);
58
+ // Preserve ToolHalt for the execution loop to handle
59
+ if (result instanceof ToolHalt) {
60
+ return result;
61
+ }
12
62
  if (result === undefined || result === null)
13
63
  return "";
14
64
  return typeof result === "string" ? result : JSON.stringify(result);
@@ -1,12 +1,17 @@
1
1
  import { ToolExecutionMode } from "../constants.js";
2
2
  import { ToolCall, ToolDefinition } from "./Tool.js";
3
+ /**
4
+ * Result of a tool execution, including halt status
5
+ */
6
+ export interface ToolExecutionResult {
7
+ role: "tool";
8
+ tool_call_id: string;
9
+ content: string;
10
+ halted: boolean;
11
+ }
3
12
  export declare class ToolHandler {
4
13
  static shouldExecuteTools(toolCalls: ToolCall[] | undefined, mode?: ToolExecutionMode): boolean;
5
14
  static requestToolConfirmation(toolCall: ToolCall, onConfirm?: (call: ToolCall) => Promise<boolean> | boolean): Promise<boolean>;
6
- static execute(toolCall: ToolCall, tools: ToolDefinition[] | undefined, onStart?: (call: ToolCall) => void, onEnd?: (call: ToolCall, result: unknown) => void): Promise<{
7
- role: "tool";
8
- tool_call_id: string;
9
- content: string;
10
- }>;
15
+ static execute(toolCall: ToolCall, tools: ToolDefinition[] | undefined, onStart?: (call: ToolCall) => void, onEnd?: (call: ToolCall, result: unknown) => void): Promise<ToolExecutionResult>;
11
16
  }
12
17
  //# sourceMappingURL=ToolHandler.d.ts.map
@@ -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;AAEpD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAErD,qBAAa,WAAW;IACtB,MAAM,CAAC,kBAAkB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE,iBAAiB,GAAG,OAAO;WAMlF,uBAAuB,CAClC,QAAQ,EAAE,QAAQ,EAClB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,GACzD,OAAO,CAAC,OAAO,CAAC;WAMN,OAAO,CAClB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,cAAc,EAAE,GAAG,SAAS,EACnC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,EAClC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,GAChD,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;AAEpD,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAY,MAAM,WAAW,CAAC;AAE/D;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,qBAAa,WAAW;IACtB,MAAM,CAAC,kBAAkB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,SAAS,EAAE,IAAI,CAAC,EAAE,iBAAiB,GAAG,OAAO;WAMlF,uBAAuB,CAClC,QAAQ,EAAE,QAAQ,EAClB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,GACzD,OAAO,CAAC,OAAO,CAAC;WAMN,OAAO,CAClB,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,cAAc,EAAE,GAAG,SAAS,EACnC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,IAAI,EAClC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,GAChD,OAAO,CAAC,mBAAmB,CAAC;CAgChC"}
@@ -1,5 +1,6 @@
1
1
  import { ToolExecutionMode } from "../constants.js";
2
2
  import { ToolError } from "../errors/index.js";
3
+ import { ToolHalt } from "./Tool.js";
3
4
  export class ToolHandler {
4
5
  static shouldExecuteTools(toolCalls, mode) {
5
6
  if (!toolCalls || toolCalls.length === 0)
@@ -21,13 +22,20 @@ export class ToolHandler {
21
22
  if (tool?.handler) {
22
23
  const args = JSON.parse(toolCall.function.arguments);
23
24
  const result = await tool.handler(args);
24
- const safeResult = typeof result === "string" ? result : JSON.stringify(result);
25
+ // Check if this is a halt signal
26
+ const isHalt = typeof result === "object" && result !== null && result instanceof ToolHalt;
27
+ const content = isHalt
28
+ ? result.content
29
+ : typeof result === "string"
30
+ ? result
31
+ : JSON.stringify(result);
25
32
  if (onEnd)
26
33
  onEnd(toolCall, result);
27
34
  return {
28
35
  role: "tool",
29
36
  tool_call_id: toolCall.id,
30
- content: safeResult
37
+ content,
38
+ halted: isHalt
31
39
  };
32
40
  }
33
41
  else {
package/dist/index.d.ts CHANGED
@@ -6,6 +6,11 @@ export * from "./chat/ChatResponse.js";
6
6
  export * from "./chat/Chat.js";
7
7
  export * from "./chat/ChatStream.js";
8
8
  export * from "./streaming/Stream.js";
9
+ export * from "./errors/index.js";
10
+ export { Agent, defineAgent } from "./agent/Agent.js";
11
+ export type { AgentConfig } from "./agent/Agent.js";
12
+ export type { Middleware, MiddlewareContext } from "./types/Middleware.js";
13
+ export * from "./middlewares/index.js";
9
14
  export { z } from "zod";
10
15
  export { NodeLLM, LegacyNodeLLM, createLLM, NodeLLMCore, Transcription, Moderation, Embedding, ModelRegistry, PricingRegistry } from "./llm.js";
11
16
  export { config } from "./config.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AAEtC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,OAAO,EACP,aAAa,EACb,SAAS,EACT,WAAW,EACX,aAAa,EACb,UAAU,EACV,SAAS,EACT,aAAa,EACb,eAAe,EAChB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,WAAW,EACX,YAAY,EACZ,SAAS,EACT,cAAc,EACd,cAAc,EACd,KAAK,EACL,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,cAAc,EACf,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,wBAAwB,CAAC;AACvC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,sBAAsB,CAAC;AACrC,cAAc,uBAAuB,CAAC;AACtC,cAAc,mBAAmB,CAAC;AAClC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACtD,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC3E,cAAc,wBAAwB,CAAC;AAEvC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EACL,OAAO,EACP,aAAa,EACb,SAAS,EACT,WAAW,EACX,aAAa,EACb,UAAU,EACV,SAAS,EACT,aAAa,EACb,eAAe,EAChB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAChF,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EACL,QAAQ,EACR,oBAAoB,EACpB,WAAW,EACX,YAAY,EACZ,SAAS,EACT,cAAc,EACd,cAAc,EACd,KAAK,EACL,YAAY,EACZ,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EACL,iBAAiB,EACjB,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,uBAAuB,EACvB,cAAc,EACf,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -6,6 +6,9 @@ export * from "./chat/ChatResponse.js";
6
6
  export * from "./chat/Chat.js";
7
7
  export * from "./chat/ChatStream.js";
8
8
  export * from "./streaming/Stream.js";
9
+ export * from "./errors/index.js";
10
+ export { Agent, defineAgent } from "./agent/Agent.js";
11
+ export * from "./middlewares/index.js";
9
12
  export { z } from "zod";
10
13
  export { NodeLLM, LegacyNodeLLM, createLLM, NodeLLMCore, Transcription, Moderation, Embedding, ModelRegistry, PricingRegistry } from "./llm.js";
11
14
  export { config } from "./config.js";
package/dist/llm.d.ts CHANGED
@@ -8,6 +8,7 @@ import { Transcription } from "./transcription/Transcription.js";
8
8
  import { Moderation } from "./moderation/Moderation.js";
9
9
  import { Embedding } from "./embedding/Embedding.js";
10
10
  import { NodeLLMConfig } from "./config.js";
11
+ import { Middleware } from "./types/Middleware.js";
11
12
  export interface RetryOptions {
12
13
  attempts?: number;
13
14
  delayMs?: number;
@@ -15,6 +16,7 @@ export interface RetryOptions {
15
16
  type LLMConfig = {
16
17
  provider?: Provider | string;
17
18
  retry?: RetryOptions;
19
+ middlewares?: Middleware[];
18
20
  defaultChatModel?: string;
19
21
  defaultTranscriptionModel?: string;
20
22
  defaultModerationModel?: string;
@@ -24,10 +26,11 @@ export declare class NodeLLMCore {
24
26
  readonly config: NodeLLMConfig;
25
27
  readonly provider?: Provider | undefined;
26
28
  readonly retry: Required<RetryOptions>;
29
+ readonly middlewares: Middleware[];
27
30
  private readonly defaults;
28
31
  readonly models: typeof ModelRegistry;
29
32
  readonly pricing: typeof PricingRegistry;
30
- constructor(config: NodeLLMConfig, provider?: Provider | undefined, retry?: Required<RetryOptions>, defaults?: {
33
+ constructor(config: NodeLLMConfig, provider?: Provider | undefined, retry?: Required<RetryOptions>, middlewares?: Middleware[], defaults?: {
31
34
  chat?: string;
32
35
  transcription?: string;
33
36
  moderation?: string;
@@ -57,6 +60,7 @@ export declare class NodeLLMCore {
57
60
  quality?: string;
58
61
  assumeModelExists?: boolean;
59
62
  requestTimeout?: number;
63
+ middlewares?: Middleware[];
60
64
  }): Promise<GeneratedImage>;
61
65
  transcribe(file: string, options?: {
62
66
  model?: string;
@@ -66,17 +70,20 @@ export declare class NodeLLMCore {
66
70
  speakerReferences?: string[];
67
71
  assumeModelExists?: boolean;
68
72
  requestTimeout?: number;
73
+ middlewares?: Middleware[];
69
74
  }): Promise<Transcription>;
70
75
  moderate(input: string | string[], options?: {
71
76
  model?: string;
72
77
  assumeModelExists?: boolean;
73
78
  requestTimeout?: number;
79
+ middlewares?: Middleware[];
74
80
  }): Promise<Moderation>;
75
81
  embed(input: string | string[], options?: {
76
82
  model?: string;
77
83
  dimensions?: number;
78
84
  assumeModelExists?: boolean;
79
85
  requestTimeout?: number;
86
+ middlewares?: Middleware[];
80
87
  }): Promise<Embedding>;
81
88
  }
82
89
  export { Transcription, Moderation, Embedding, ModelRegistry, PricingRegistry };
package/dist/llm.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAW9D,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAUrD,OAAO,EAAU,aAAa,EAAiB,MAAM,aAAa,CAAC;AAEnE,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,SAAS,GAAG;IACf,QAAQ,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;IAC7B,KAAK,CAAC,EAAE,YAAY,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,UAAU,CAAC,CAAC;AAa7C,qBAAa,WAAW;aAKJ,MAAM,EAAE,aAAa;aACrB,QAAQ,CAAC,EAAE,QAAQ;aACnB,KAAK,EAAE,QAAQ,CAAC,YAAY,CAAC;IAC7C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAP3B,SAAgB,MAAM,uBAAiB;IACvC,SAAgB,OAAO,yBAAmB;gBAGxB,MAAM,EAAE,aAAa,EACrB,QAAQ,CAAC,EAAE,QAAQ,YAAA,EACnB,KAAK,GAAE,QAAQ,CAAC,YAAY,CAA+B,EAC1D,QAAQ,GAAE;QACzB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;KACf;IAOR,IAAI,gBAAgB,IAAI,MAAM,GAAG,SAAS,CAEzC;IACD,IAAI,yBAAyB,IAAI,MAAM,GAAG,SAAS,CAElD;IACD,IAAI,sBAAsB,IAAI,MAAM,GAAG,SAAS,CAE/C;IACD,IAAI,qBAAqB,IAAI,MAAM,GAAG,SAAS,CAE9C;IAED;;;OAGG;IACH,YAAY,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,WAAW;IAgBtF;;;OAGG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,QAAQ,GAAG,IAAI;IAI7D,cAAc;IAId,OAAO,CAAC,qBAAqB;IAY7B,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI;IAU3C,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAUlC,KAAK,CACT,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GACA,OAAO,CAAC,cAAc,CAAC;IA0BpB,UAAU,CACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;QAC7B,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GACA,OAAO,CAAC,aAAa,CAAC;IAyBnB,QAAQ,CACZ,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EACxB,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAAC,cAAc,CAAC,EAAE,MAAM,CAAA;KAAE,GACjF,OAAO,CAAC,UAAU,CAAC;IAqBhB,KAAK,CACT,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EACxB,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,iBAAiB,CAAC,EAAE,OAAO,CAAC;QAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,GACA,OAAO,CAAC,SAAS,CAAC;CA0BtB;AAED,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,eAAe,EAAE,CAAC;AAEhF;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,SAAc,GAAG,WAAW,CAkD9D;AAuCD;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO,EAAE,WA6BpB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,aAAa;wBACJ,SAAS,GAAG,CAAC,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,CAAC;CAOlE,CAAC"}
1
+ {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"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"}
package/dist/llm.js CHANGED
@@ -10,6 +10,8 @@ import { ProviderNotConfiguredError, UnsupportedFeatureError, ModelCapabilityErr
10
10
  import { resolveModelAlias } from "./model_aliases.js";
11
11
  import { logger } from "./utils/logger.js";
12
12
  import { config, Configuration } from "./config.js";
13
+ import { runMiddleware } from "./utils/middleware-runner.js";
14
+ import { randomUUID } from "node:crypto";
13
15
  // Provider registration map
14
16
  const PROVIDER_REGISTRARS = {
15
17
  openai: ensureOpenAIRegistered,
@@ -24,13 +26,15 @@ export class NodeLLMCore {
24
26
  config;
25
27
  provider;
26
28
  retry;
29
+ middlewares;
27
30
  defaults;
28
31
  models = ModelRegistry;
29
32
  pricing = PricingRegistry;
30
- constructor(config, provider, retry = { attempts: 1, delayMs: 0 }, defaults = {}) {
33
+ constructor(config, provider, retry = { attempts: 1, delayMs: 0 }, middlewares = [], defaults = {}) {
31
34
  this.config = config;
32
35
  this.provider = provider;
33
36
  this.retry = retry;
37
+ this.middlewares = middlewares;
34
38
  this.defaults = defaults;
35
39
  Object.freeze(this.config);
36
40
  Object.freeze(this.retry);
@@ -59,7 +63,8 @@ export class NodeLLMCore {
59
63
  ...baseConfig,
60
64
  ...scopedConfig,
61
65
  provider: providerName,
62
- // Preserve defaults unless overridden (conceptually, though createLLM takes specific keys)
66
+ // Preserve middlewares and defaults unless overridden
67
+ middlewares: this.middlewares,
63
68
  defaultChatModel: this.defaults.chat,
64
69
  defaultTranscriptionModel: this.defaults.transcription,
65
70
  defaultModerationModel: this.defaults.moderation,
@@ -85,13 +90,18 @@ export class NodeLLMCore {
85
90
  }
86
91
  return this.provider;
87
92
  }
88
- chat(model, options) {
93
+ chat(model, options = {}) {
89
94
  if (!this.provider) {
90
95
  throw new ProviderNotConfiguredError();
91
96
  }
92
97
  const rawModel = model || this.defaults.chat || this.provider.defaultModel("chat");
93
98
  const resolvedModel = resolveModelAlias(rawModel, this.provider.id);
94
- return new Chat(this.provider, resolvedModel, options, this.retry);
99
+ // Merge global middlewares with local ones
100
+ const combinedOptions = {
101
+ ...options,
102
+ middlewares: [...this.middlewares, ...(options.middlewares || [])]
103
+ };
104
+ return new Chat(this.provider, resolvedModel, combinedOptions, this.retry);
95
105
  }
96
106
  async listModels() {
97
107
  const provider = this.ensureProviderSupport("listModels");
@@ -102,82 +112,169 @@ export class NodeLLMCore {
102
112
  }
103
113
  async paint(prompt, options) {
104
114
  const provider = this.ensureProviderSupport("paint");
105
- const rawModel = options?.model;
106
- const model = resolveModelAlias(rawModel || "", provider.id);
107
- if (options?.assumeModelExists) {
108
- logger.warn(`Skipping validation for model ${model}`);
115
+ const rawModel = options?.model || provider.defaultModel("image");
116
+ const model = resolveModelAlias(rawModel, provider.id);
117
+ const requestId = randomUUID();
118
+ const state = {};
119
+ const context = {
120
+ requestId,
121
+ provider: provider.id,
122
+ model,
123
+ input: prompt,
124
+ imageOptions: options,
125
+ state
126
+ };
127
+ const middlewares = [...this.middlewares, ...(options?.middlewares || [])];
128
+ try {
129
+ await runMiddleware(middlewares, "onRequest", context);
130
+ const currentPrompt = context.input || prompt;
131
+ if (options?.assumeModelExists) {
132
+ logger.warn(`Skipping validation for model ${model}`);
133
+ }
134
+ else if (model &&
135
+ provider.capabilities &&
136
+ !provider.capabilities.supportsImageGeneration(model)) {
137
+ throw new ModelCapabilityError(model, "image generation");
138
+ }
139
+ const response = await provider.paint({
140
+ prompt: currentPrompt,
141
+ ...options,
142
+ model,
143
+ requestTimeout: options?.requestTimeout ?? this.config.requestTimeout
144
+ });
145
+ const imageResult = new GeneratedImage(response);
146
+ await runMiddleware(middlewares, "onResponse", context, imageResult);
147
+ return imageResult;
109
148
  }
110
- else if (model &&
111
- provider.capabilities &&
112
- !provider.capabilities.supportsImageGeneration(model)) {
113
- throw new ModelCapabilityError(model, "image generation");
149
+ catch (error) {
150
+ await runMiddleware(middlewares, "onError", context, error);
151
+ throw error;
114
152
  }
115
- const response = await provider.paint({
116
- prompt,
117
- ...options,
118
- model,
119
- requestTimeout: options?.requestTimeout ?? this.config.requestTimeout
120
- });
121
- return new GeneratedImage(response);
122
153
  }
123
154
  async transcribe(file, options) {
124
155
  const provider = this.ensureProviderSupport("transcribe");
125
- const rawModel = options?.model || this.defaults.transcription || "";
156
+ const rawModel = options?.model || this.defaults.transcription || provider.defaultModel("transcription");
126
157
  const model = resolveModelAlias(rawModel, provider.id);
127
- if (options?.assumeModelExists) {
128
- logger.warn(`Skipping validation for model ${model}`);
158
+ const requestId = randomUUID();
159
+ const state = {};
160
+ const context = {
161
+ requestId,
162
+ provider: provider.id,
163
+ model,
164
+ input: file,
165
+ transcriptionOptions: options,
166
+ state
167
+ };
168
+ const middlewares = [...this.middlewares, ...(options?.middlewares || [])];
169
+ try {
170
+ await runMiddleware(middlewares, "onRequest", context);
171
+ const currentFile = context.input || file;
172
+ if (options?.assumeModelExists) {
173
+ logger.warn(`Skipping validation for model ${model}`);
174
+ }
175
+ else if (model &&
176
+ provider.capabilities &&
177
+ !provider.capabilities.supportsTranscription(model)) {
178
+ throw new ModelCapabilityError(model, "transcription");
179
+ }
180
+ const response = await provider.transcribe({
181
+ file: currentFile,
182
+ ...options,
183
+ model,
184
+ requestTimeout: options?.requestTimeout ?? this.config.requestTimeout
185
+ });
186
+ const transcriptionResult = new Transcription(response);
187
+ await runMiddleware(middlewares, "onResponse", context, transcriptionResult);
188
+ return transcriptionResult;
129
189
  }
130
- else if (model &&
131
- provider.capabilities &&
132
- !provider.capabilities.supportsTranscription(model)) {
133
- throw new ModelCapabilityError(model, "transcription");
190
+ catch (error) {
191
+ await runMiddleware(middlewares, "onError", context, error);
192
+ throw error;
134
193
  }
135
- const response = await provider.transcribe({
136
- file,
137
- ...options,
138
- model,
139
- requestTimeout: options?.requestTimeout ?? this.config.requestTimeout
140
- });
141
- return new Transcription(response);
142
194
  }
143
195
  async moderate(input, options) {
144
196
  const provider = this.ensureProviderSupport("moderate");
145
- const rawModel = options?.model || this.defaults.moderation || "";
197
+ const rawModel = options?.model || this.defaults.moderation || provider.defaultModel("moderation");
146
198
  const model = resolveModelAlias(rawModel, provider.id);
147
- if (options?.assumeModelExists) {
148
- logger.warn(`Skipping validation for model ${model}`);
199
+ const requestId = randomUUID();
200
+ const state = {};
201
+ const context = {
202
+ requestId,
203
+ provider: provider.id,
204
+ model,
205
+ input,
206
+ moderationOptions: options,
207
+ state
208
+ };
209
+ const middlewares = [...this.middlewares, ...(options?.middlewares || [])];
210
+ try {
211
+ await runMiddleware(middlewares, "onRequest", context);
212
+ const currentInput = context.input || input;
213
+ if (options?.assumeModelExists) {
214
+ logger.warn(`Skipping validation for model ${model}`);
215
+ }
216
+ else if (model &&
217
+ provider.capabilities &&
218
+ !provider.capabilities.supportsModeration(model)) {
219
+ throw new ModelCapabilityError(model, "moderation");
220
+ }
221
+ const response = await provider.moderate({
222
+ input: currentInput,
223
+ ...options,
224
+ model,
225
+ requestTimeout: options?.requestTimeout ?? this.config.requestTimeout
226
+ });
227
+ const moderationResult = new Moderation(response);
228
+ await runMiddleware(middlewares, "onResponse", context, moderationResult);
229
+ return moderationResult;
149
230
  }
150
- else if (model && provider.capabilities && !provider.capabilities.supportsModeration(model)) {
151
- throw new ModelCapabilityError(model, "moderation");
231
+ catch (error) {
232
+ await runMiddleware(middlewares, "onError", context, error);
233
+ throw error;
152
234
  }
153
- const response = await provider.moderate({
154
- input,
155
- ...options,
156
- model,
157
- requestTimeout: options?.requestTimeout ?? this.config.requestTimeout
158
- });
159
- return new Moderation(response);
160
235
  }
161
236
  async embed(input, options) {
162
237
  const provider = this.ensureProviderSupport("embed");
163
- const rawModel = options?.model || this.defaults.embedding || "";
238
+ const rawModel = options?.model || this.defaults.embedding || provider.defaultModel("embedding");
164
239
  const model = resolveModelAlias(rawModel, provider.id);
165
- const request = {
166
- input,
240
+ const requestId = randomUUID();
241
+ const state = {};
242
+ const context = {
243
+ requestId,
244
+ provider: provider.id,
167
245
  model,
168
- dimensions: options?.dimensions,
169
- requestTimeout: options?.requestTimeout ?? this.config.requestTimeout
246
+ input,
247
+ embeddingOptions: options,
248
+ state
170
249
  };
171
- if (options?.assumeModelExists) {
172
- logger.warn(`Skipping validation for model ${request.model}`);
250
+ const middlewares = [...this.middlewares, ...(options?.middlewares || [])];
251
+ try {
252
+ await runMiddleware(middlewares, "onRequest", context);
253
+ // Re-read input from context as it might have been modified
254
+ const currentInput = context.input || input;
255
+ const request = {
256
+ input: currentInput,
257
+ model,
258
+ dimensions: options?.dimensions,
259
+ requestTimeout: options?.requestTimeout ?? this.config.requestTimeout
260
+ };
261
+ if (options?.assumeModelExists) {
262
+ logger.warn(`Skipping validation for model ${request.model}`);
263
+ }
264
+ else if (request.model &&
265
+ provider.capabilities &&
266
+ !provider.capabilities.supportsEmbeddings(request.model)) {
267
+ throw new ModelCapabilityError(request.model, "embeddings");
268
+ }
269
+ const response = await provider.embed(request);
270
+ const embeddingResult = new Embedding(response);
271
+ await runMiddleware(middlewares, "onResponse", context, embeddingResult);
272
+ return embeddingResult;
173
273
  }
174
- else if (request.model &&
175
- provider.capabilities &&
176
- !provider.capabilities.supportsEmbeddings(request.model)) {
177
- throw new ModelCapabilityError(request.model, "embeddings");
274
+ catch (error) {
275
+ await runMiddleware(middlewares, "onError", context, error);
276
+ throw error;
178
277
  }
179
- const response = await provider.embed(request);
180
- return new Embedding(response);
181
278
  }
182
279
  }
183
280
  export { Transcription, Moderation, Embedding, ModelRegistry, PricingRegistry };
@@ -230,7 +327,7 @@ export function createLLM(options = {}) {
230
327
  moderation: options.defaultModerationModel,
231
328
  embedding: options.defaultEmbeddingModel
232
329
  };
233
- return new NodeLLMCore(baseConfig, providerInstance, retry, defaults);
330
+ return new NodeLLMCore(baseConfig, providerInstance, retry, options.middlewares || [], defaults);
234
331
  }
235
332
  /**
236
333
  * DEFAULT IMMUTABLE INSTANCE
@@ -0,0 +1,24 @@
1
+ import { Middleware, MiddlewareContext } from "../types/Middleware.js";
2
+ import { ChatResponseString } from "../chat/ChatResponse.js";
3
+ export interface CostGuardOptions {
4
+ /**
5
+ * Maximum allowed cost (in USD) for a single request sequence.
6
+ */
7
+ maxCost: number;
8
+ /**
9
+ * Callback when the limit is exceeded.
10
+ */
11
+ onLimitExceeded?: (ctx: MiddlewareContext, currentCost: number) => void;
12
+ }
13
+ /**
14
+ * Middleware that monitors accumulated cost during a session (especially multi-turn tool calls)
15
+ * and throws an error if a defined limit is exceeded.
16
+ */
17
+ export declare class CostGuardMiddleware implements Middleware {
18
+ private options;
19
+ readonly name = "CostGuard";
20
+ private accumulatedCost;
21
+ constructor(options: CostGuardOptions);
22
+ onResponse(ctx: MiddlewareContext, result: ChatResponseString): Promise<void>;
23
+ }
24
+ //# sourceMappingURL=CostGuardMiddleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CostGuardMiddleware.d.ts","sourceRoot":"","sources":["../../src/middlewares/CostGuardMiddleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAE7D,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE;AAED;;;GAGG;AACH,qBAAa,mBAAoB,YAAW,UAAU;IAIxC,OAAO,CAAC,OAAO;IAH3B,SAAgB,IAAI,eAAe;IACnC,OAAO,CAAC,eAAe,CAAa;gBAEhB,OAAO,EAAE,gBAAgB;IAMvC,UAAU,CAAC,GAAG,EAAE,iBAAiB,EAAE,MAAM,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;CAWpF"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Middleware that monitors accumulated cost during a session (especially multi-turn tool calls)
3
+ * and throws an error if a defined limit is exceeded.
4
+ */
5
+ export class CostGuardMiddleware {
6
+ options;
7
+ name = "CostGuard";
8
+ accumulatedCost = 0;
9
+ constructor(options) {
10
+ this.options = options;
11
+ if (options.maxCost <= 0) {
12
+ throw new Error("CostGuard maxCost must be greater than 0");
13
+ }
14
+ }
15
+ async onResponse(ctx, result) {
16
+ const cost = result.usage?.cost || 0;
17
+ this.accumulatedCost += cost;
18
+ if (this.accumulatedCost > this.options.maxCost) {
19
+ this.options.onLimitExceeded?.(ctx, this.accumulatedCost);
20
+ throw new Error(`[CostGuard] Budget exceeded. Accumulated cost $${this.accumulatedCost.toFixed(6)} exceeds limit $${this.options.maxCost.toFixed(6)}`);
21
+ }
22
+ }
23
+ }