@node-llm/core 1.8.0 → 1.10.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 (101) hide show
  1. package/README.md +35 -0
  2. package/dist/aliases.d.ts +105 -9
  3. package/dist/aliases.d.ts.map +1 -1
  4. package/dist/aliases.js +105 -9
  5. package/dist/chat/Chat.d.ts +8 -3
  6. package/dist/chat/Chat.d.ts.map +1 -1
  7. package/dist/chat/Chat.js +181 -131
  8. package/dist/chat/ChatOptions.d.ts +2 -0
  9. package/dist/chat/ChatOptions.d.ts.map +1 -1
  10. package/dist/chat/ChatResponse.d.ts +24 -3
  11. package/dist/chat/ChatResponse.d.ts.map +1 -1
  12. package/dist/chat/ChatResponse.js +72 -5
  13. package/dist/chat/ChatStream.d.ts.map +1 -1
  14. package/dist/chat/ChatStream.js +111 -56
  15. package/dist/config.d.ts.map +1 -1
  16. package/dist/config.js +9 -7
  17. package/dist/constants.d.ts +6 -0
  18. package/dist/constants.d.ts.map +1 -1
  19. package/dist/constants.js +6 -0
  20. package/dist/errors/index.d.ts +20 -2
  21. package/dist/errors/index.d.ts.map +1 -1
  22. package/dist/errors/index.js +31 -3
  23. package/dist/index.d.ts +5 -2
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/index.js +2 -0
  26. package/dist/llm.d.ts +8 -1
  27. package/dist/llm.d.ts.map +1 -1
  28. package/dist/llm.js +156 -59
  29. package/dist/middlewares/CostGuardMiddleware.d.ts +24 -0
  30. package/dist/middlewares/CostGuardMiddleware.d.ts.map +1 -0
  31. package/dist/middlewares/CostGuardMiddleware.js +23 -0
  32. package/dist/middlewares/PIIMaskMiddleware.d.ts +23 -0
  33. package/dist/middlewares/PIIMaskMiddleware.d.ts.map +1 -0
  34. package/dist/middlewares/PIIMaskMiddleware.js +41 -0
  35. package/dist/middlewares/UsageLoggerMiddleware.d.ts +22 -0
  36. package/dist/middlewares/UsageLoggerMiddleware.d.ts.map +1 -0
  37. package/dist/middlewares/UsageLoggerMiddleware.js +30 -0
  38. package/dist/middlewares/index.d.ts +4 -0
  39. package/dist/middlewares/index.d.ts.map +1 -0
  40. package/dist/middlewares/index.js +3 -0
  41. package/dist/models/ModelRegistry.d.ts.map +1 -1
  42. package/dist/models/ModelRegistry.js +4 -2
  43. package/dist/models/PricingRegistry.js +3 -3
  44. package/dist/models/{models.js → models.json} +9934 -8196
  45. package/dist/providers/BaseProvider.d.ts +6 -1
  46. package/dist/providers/BaseProvider.d.ts.map +1 -1
  47. package/dist/providers/BaseProvider.js +19 -0
  48. package/dist/providers/anthropic/AnthropicProvider.d.ts.map +1 -1
  49. package/dist/providers/anthropic/AnthropicProvider.js +2 -1
  50. package/dist/providers/anthropic/Chat.d.ts.map +1 -1
  51. package/dist/providers/anthropic/Chat.js +2 -1
  52. package/dist/providers/anthropic/Errors.d.ts.map +1 -1
  53. package/dist/providers/anthropic/Errors.js +15 -1
  54. package/dist/providers/anthropic/Streaming.d.ts.map +1 -1
  55. package/dist/providers/anthropic/Streaming.js +19 -3
  56. package/dist/providers/bedrock/Chat.d.ts.map +1 -1
  57. package/dist/providers/bedrock/Chat.js +2 -20
  58. package/dist/providers/bedrock/Errors.d.ts +2 -0
  59. package/dist/providers/bedrock/Errors.d.ts.map +1 -0
  60. package/dist/providers/bedrock/Errors.js +51 -0
  61. package/dist/providers/bedrock/Streaming.d.ts.map +1 -1
  62. package/dist/providers/bedrock/Streaming.js +2 -3
  63. package/dist/providers/deepseek/Chat.d.ts.map +1 -1
  64. package/dist/providers/deepseek/Chat.js +2 -2
  65. package/dist/providers/deepseek/DeepSeekProvider.d.ts.map +1 -1
  66. package/dist/providers/deepseek/DeepSeekProvider.js +2 -1
  67. package/dist/providers/deepseek/Errors.d.ts +2 -0
  68. package/dist/providers/deepseek/Errors.d.ts.map +1 -0
  69. package/dist/providers/deepseek/Errors.js +45 -0
  70. package/dist/providers/deepseek/Streaming.d.ts.map +1 -1
  71. package/dist/providers/deepseek/Streaming.js +13 -2
  72. package/dist/providers/gemini/Errors.d.ts.map +1 -1
  73. package/dist/providers/gemini/Errors.js +13 -1
  74. package/dist/providers/gemini/GeminiProvider.d.ts.map +1 -1
  75. package/dist/providers/gemini/GeminiProvider.js +2 -1
  76. package/dist/providers/ollama/OllamaProvider.d.ts.map +1 -1
  77. package/dist/providers/ollama/OllamaProvider.js +2 -2
  78. package/dist/providers/openai/Errors.d.ts.map +1 -1
  79. package/dist/providers/openai/Errors.js +31 -5
  80. package/dist/providers/openai/OpenAIProvider.d.ts +1 -1
  81. package/dist/providers/openai/OpenAIProvider.d.ts.map +1 -1
  82. package/dist/providers/openai/OpenAIProvider.js +15 -3
  83. package/dist/providers/openai/Streaming.d.ts.map +1 -1
  84. package/dist/providers/openai/Streaming.js +10 -0
  85. package/dist/providers/openrouter/OpenRouterProvider.d.ts.map +1 -1
  86. package/dist/providers/openrouter/OpenRouterProvider.js +2 -1
  87. package/dist/providers/registry.d.ts +3 -0
  88. package/dist/providers/registry.d.ts.map +1 -1
  89. package/dist/providers/registry.js +10 -2
  90. package/dist/types/Middleware.d.ts +98 -0
  91. package/dist/types/Middleware.d.ts.map +1 -0
  92. package/dist/types/Middleware.js +1 -0
  93. package/dist/utils/json.d.ts +6 -0
  94. package/dist/utils/json.d.ts.map +1 -0
  95. package/dist/utils/json.js +43 -0
  96. package/dist/utils/middleware-runner.d.ts +7 -0
  97. package/dist/utils/middleware-runner.d.ts.map +1 -0
  98. package/dist/utils/middleware-runner.js +20 -0
  99. package/package.json +1 -1
  100. package/dist/models/models.d.ts +0 -572
  101. package/dist/models/models.d.ts.map +0 -1
package/dist/chat/Chat.js CHANGED
@@ -5,6 +5,7 @@ import { ChatStream } from "./ChatStream.js";
5
5
  import { ModelRegistry } from "../models/ModelRegistry.js";
6
6
  import { Schema } from "../schema/Schema.js";
7
7
  import { toJsonSchema } from "../schema/to-json-schema.js";
8
+ import { randomUUID } from "node:crypto";
8
9
  import { z } from "zod";
9
10
  import { config } from "../config.js";
10
11
  import { ToolExecutionMode } from "../constants.js";
@@ -12,6 +13,7 @@ import { ConfigurationError } from "../errors/index.js";
12
13
  import { ChatValidator } from "./Validation.js";
13
14
  import { ToolHandler } from "./ToolHandler.js";
14
15
  import { logger } from "../utils/logger.js";
16
+ import { runMiddleware } from "../utils/middleware-runner.js";
15
17
  import { ChatResponseString } from "./ChatResponse.js";
16
18
  export class Chat {
17
19
  provider;
@@ -20,10 +22,12 @@ export class Chat {
20
22
  messages = [];
21
23
  systemMessages = [];
22
24
  executor;
25
+ middlewares = [];
23
26
  constructor(provider, model, options = {}, retryConfig = { attempts: 1, delayMs: 0 }) {
24
27
  this.provider = provider;
25
28
  this.model = model;
26
29
  this.options = options;
30
+ this.middlewares = options.middlewares || [];
27
31
  this.executor = new Executor(provider, retryConfig);
28
32
  if (options.systemPrompt) {
29
33
  this.withInstructions(options.systemPrompt);
@@ -150,6 +154,18 @@ export class Chat {
150
154
  }
151
155
  return this;
152
156
  }
157
+ /**
158
+ * Add a raw Message object to the chat history.
159
+ */
160
+ addMessage(message) {
161
+ if (message.role === "system" || message.role === "developer") {
162
+ this.systemMessages.push(message);
163
+ }
164
+ else {
165
+ this.messages.push(message);
166
+ }
167
+ return this;
168
+ }
153
169
  /**
154
170
  * Set the temperature for the chat session.
155
171
  * Controls randomness: 0.0 (deterministic) to 1.0 (creative).
@@ -290,6 +306,8 @@ export class Chat {
290
306
  * Ask the model a question
291
307
  */
292
308
  async ask(content, options) {
309
+ const requestId = randomUUID();
310
+ const state = {};
293
311
  let messageContent = content;
294
312
  const files = [...(options?.images ?? []), ...(options?.files ?? [])];
295
313
  if (files.length > 0) {
@@ -320,146 +338,74 @@ export class Chat {
320
338
  }
321
339
  };
322
340
  }
323
- const executeOptions = {
341
+ // Prepare Middleware Context
342
+ const context = {
343
+ requestId,
344
+ provider: this.provider.id,
324
345
  model: this.model,
325
346
  messages: [...this.systemMessages, ...this.messages],
326
- tools: this.options.tools,
327
- temperature: options?.temperature ?? this.options.temperature,
328
- max_tokens: options?.maxTokens ?? this.options.maxTokens ?? config.maxTokens,
329
- headers: { ...this.options.headers, ...options?.headers },
330
- response_format: responseFormat, // Pass to provider
331
- requestTimeout: options?.requestTimeout ?? this.options.requestTimeout ?? config.requestTimeout,
332
- thinking: options?.thinking ?? this.options.thinking,
333
- signal: options?.signal,
334
- ...this.options.params
347
+ options: this.options,
348
+ state
335
349
  };
336
- // --- Content Policy Hooks (Input) ---
337
- if (this.options.onBeforeRequest) {
338
- const messagesToProcess = [...this.systemMessages, ...this.messages];
339
- const result = await this.options.onBeforeRequest(messagesToProcess);
340
- if (result) {
341
- // If the hook returned modified messages, use them for this request
342
- executeOptions.messages = result;
343
- }
344
- }
345
- const totalUsage = { input_tokens: 0, output_tokens: 0, total_tokens: 0 };
346
- const trackUsage = (u) => {
347
- if (u) {
348
- // Fallback cost calculation if provider didn't return it
349
- if (u.cost === undefined) {
350
- const withCost = ModelRegistry.calculateCost(u, this.model, this.provider.id);
351
- u.cost = withCost.cost;
352
- u.input_cost = withCost.input_cost;
353
- u.output_cost = withCost.output_cost;
354
- }
355
- totalUsage.input_tokens += u.input_tokens;
356
- totalUsage.output_tokens += u.output_tokens;
357
- totalUsage.total_tokens += u.total_tokens;
358
- if (u.cached_tokens) {
359
- totalUsage.cached_tokens = (totalUsage.cached_tokens ?? 0) + u.cached_tokens;
360
- }
361
- if (u.cost !== undefined) {
362
- totalUsage.cost = (totalUsage.cost ?? 0) + u.cost;
363
- }
364
- if (u.input_cost !== undefined) {
365
- totalUsage.input_cost = (totalUsage.input_cost ?? 0) + u.input_cost;
366
- }
367
- if (u.output_cost !== undefined) {
368
- totalUsage.output_cost = (totalUsage.output_cost ?? 0) + u.output_cost;
350
+ try {
351
+ // 1. onRequest Hook
352
+ await runMiddleware(this.middlewares, "onRequest", context);
353
+ // Re-read mutable context
354
+ const messagesToUse = context.messages || [];
355
+ const executeOptions = {
356
+ model: this.model,
357
+ messages: messagesToUse,
358
+ tools: this.options.tools,
359
+ temperature: options?.temperature ?? this.options.temperature,
360
+ max_tokens: options?.maxTokens ?? this.options.maxTokens ?? config.maxTokens,
361
+ headers: { ...this.options.headers, ...options?.headers },
362
+ response_format: responseFormat, // Pass to provider
363
+ requestTimeout: options?.requestTimeout ?? this.options.requestTimeout ?? config.requestTimeout,
364
+ thinking: options?.thinking ?? this.options.thinking,
365
+ signal: options?.signal,
366
+ ...this.options.params
367
+ };
368
+ // --- Content Policy Hooks (Input) ---
369
+ if (this.options.onBeforeRequest) {
370
+ const result = await this.options.onBeforeRequest(executeOptions.messages);
371
+ if (result) {
372
+ executeOptions.messages = result;
369
373
  }
370
374
  }
371
- };
372
- // First round
373
- if (this.options.onNewMessage)
374
- this.options.onNewMessage();
375
- let response = await this.executor.executeChat(executeOptions);
376
- trackUsage(response.usage);
377
- let assistantMessage = new ChatResponseString(response.content ?? "", response.usage ?? { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model, this.provider.id, response.thinking, response.reasoning, response.tool_calls, response.finish_reason);
378
- // --- Content Policy Hooks (Output - Turn 1) ---
379
- if (this.options.onAfterResponse) {
380
- const result = await this.options.onAfterResponse(assistantMessage);
381
- if (result) {
382
- assistantMessage = result;
383
- }
384
- }
385
- this.messages.push({
386
- role: "assistant",
387
- content: assistantMessage?.toString() || null,
388
- tool_calls: response.tool_calls,
389
- usage: response.usage
390
- });
391
- if (this.options.onEndMessage && (!response.tool_calls || response.tool_calls.length === 0)) {
392
- this.options.onEndMessage(assistantMessage);
393
- }
394
- const maxToolCalls = options?.maxToolCalls ?? this.options.maxToolCalls ?? 5;
395
- let stepCount = 0;
396
- while (response.tool_calls && response.tool_calls.length > 0) {
397
- // Dry-run mode: stop after proposing tools
398
- if (!ToolHandler.shouldExecuteTools(response.tool_calls, this.options.toolExecution)) {
399
- break;
400
- }
401
- stepCount++;
402
- if (stepCount > maxToolCalls) {
403
- throw new Error(`[NodeLLM] Maximum tool execution calls (${maxToolCalls}) exceeded.`);
404
- }
405
- for (const toolCall of response.tool_calls) {
406
- // Human-in-the-loop: check for approval
407
- if (this.options.toolExecution === ToolExecutionMode.CONFIRM) {
408
- const approved = await ToolHandler.requestToolConfirmation(toolCall, this.options.onConfirmToolCall);
409
- if (!approved) {
410
- this.messages.push(this.provider.formatToolResultMessage(toolCall.id, "Action cancelled by user."));
411
- continue;
375
+ const totalUsage = { input_tokens: 0, output_tokens: 0, total_tokens: 0 };
376
+ const trackUsage = (u) => {
377
+ if (u) {
378
+ // Fallback cost calculation if provider didn't return it
379
+ if (u.cost === undefined) {
380
+ const withCost = ModelRegistry.calculateCost(u, this.model, this.provider.id);
381
+ u.cost = withCost.cost;
382
+ u.input_cost = withCost.input_cost;
383
+ u.output_cost = withCost.output_cost;
412
384
  }
413
- }
414
- try {
415
- const toolResult = await ToolHandler.execute(toolCall, this.options.tools, this.options.onToolCallStart, this.options.onToolCallEnd);
416
- this.messages.push(this.provider.formatToolResultMessage(toolResult.tool_call_id, toolResult.content));
417
- }
418
- catch (error) {
419
- let currentError = error;
420
- const directive = await this.options.onToolCallError?.(toolCall, currentError);
421
- if (directive === "STOP") {
422
- throw currentError;
385
+ totalUsage.input_tokens += u.input_tokens;
386
+ totalUsage.output_tokens += u.output_tokens;
387
+ totalUsage.total_tokens += u.total_tokens;
388
+ if (u.cached_tokens) {
389
+ totalUsage.cached_tokens = (totalUsage.cached_tokens ?? 0) + u.cached_tokens;
423
390
  }
424
- if (directive === "RETRY") {
425
- try {
426
- const toolResult = await ToolHandler.execute(toolCall, this.options.tools, this.options.onToolCallStart, this.options.onToolCallEnd);
427
- this.messages.push(this.provider.formatToolResultMessage(toolResult.tool_call_id, toolResult.content));
428
- continue;
429
- }
430
- catch (retryError) {
431
- // If retry also fails, fall through to default logic
432
- currentError = retryError;
433
- }
391
+ if (u.cost !== undefined) {
392
+ totalUsage.cost = (totalUsage.cost ?? 0) + u.cost;
434
393
  }
435
- this.messages.push(this.provider.formatToolResultMessage(toolCall.id, `Fatal error executing tool '${toolCall.function.name}': ${currentError.message}`, { isError: true }));
436
- if (directive === "CONTINUE") {
437
- continue;
394
+ if (u.input_cost !== undefined) {
395
+ totalUsage.input_cost = (totalUsage.input_cost ?? 0) + u.input_cost;
438
396
  }
439
- // Default short-circuit logic
440
- const errorObj = currentError;
441
- const isFatal = errorObj.fatal === true || errorObj.status === 401 || errorObj.status === 403;
442
- if (isFatal) {
443
- throw currentError;
397
+ if (u.output_cost !== undefined) {
398
+ totalUsage.output_cost = (totalUsage.output_cost ?? 0) + u.output_cost;
444
399
  }
445
- logger.error(`Tool execution failed for '${toolCall.function.name}':`, currentError);
446
400
  }
447
- }
448
- response = await this.executor.executeChat({
449
- model: this.model,
450
- messages: [...this.systemMessages, ...this.messages],
451
- tools: this.options.tools,
452
- temperature: options?.temperature ?? this.options.temperature,
453
- max_tokens: options?.maxTokens ?? this.options.maxTokens ?? config.maxTokens,
454
- headers: this.options.headers,
455
- response_format: responseFormat,
456
- requestTimeout: options?.requestTimeout ?? this.options.requestTimeout ?? config.requestTimeout,
457
- signal: options?.signal,
458
- ...this.options.params
459
- });
401
+ };
402
+ // First round
403
+ if (this.options.onNewMessage)
404
+ this.options.onNewMessage();
405
+ let response = await this.executor.executeChat(executeOptions);
460
406
  trackUsage(response.usage);
461
- assistantMessage = new ChatResponseString(response.content ?? "", response.usage ?? { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model, this.provider.id, response.thinking, response.reasoning, response.tool_calls, response.finish_reason);
462
- // --- Content Policy Hooks (Output - Tool Turns) ---
407
+ let assistantMessage = new ChatResponseString(response.content ?? "", response.usage ?? { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model, this.provider.id, response.thinking, response.reasoning, response.tool_calls, response.finish_reason, this.options.schema);
408
+ // --- Content Policy Hooks (Output - Turn 1) ---
463
409
  if (this.options.onAfterResponse) {
464
410
  const result = await this.options.onAfterResponse(assistantMessage);
465
411
  if (result) {
@@ -475,10 +421,114 @@ export class Chat {
475
421
  if (this.options.onEndMessage && (!response.tool_calls || response.tool_calls.length === 0)) {
476
422
  this.options.onEndMessage(assistantMessage);
477
423
  }
424
+ const maxToolCalls = options?.maxToolCalls ?? this.options.maxToolCalls ?? 5;
425
+ let stepCount = 0;
426
+ while (response.tool_calls && response.tool_calls.length > 0) {
427
+ // Dry-run mode: stop after proposing tools
428
+ if (!ToolHandler.shouldExecuteTools(response.tool_calls, this.options.toolExecution)) {
429
+ break;
430
+ }
431
+ stepCount++;
432
+ if (stepCount > maxToolCalls) {
433
+ throw new Error(`[NodeLLM] Maximum tool execution calls (${maxToolCalls}) exceeded.`);
434
+ }
435
+ for (const toolCall of response.tool_calls) {
436
+ // Human-in-the-loop: check for approval
437
+ if (this.options.toolExecution === ToolExecutionMode.CONFIRM) {
438
+ const approved = await ToolHandler.requestToolConfirmation(toolCall, this.options.onConfirmToolCall);
439
+ if (!approved) {
440
+ this.messages.push(this.provider.formatToolResultMessage(toolCall.id, "Action cancelled by user."));
441
+ continue;
442
+ }
443
+ }
444
+ // 2. onToolCallStart Hook
445
+ await runMiddleware(this.middlewares, "onToolCallStart", context, toolCall);
446
+ try {
447
+ const toolResult = await ToolHandler.execute(toolCall, this.options.tools, this.options.onToolCallStart, this.options.onToolCallEnd);
448
+ // 3. onToolCallEnd Hook
449
+ await runMiddleware(this.middlewares, "onToolCallEnd", context, toolCall, toolResult.content);
450
+ this.messages.push(this.provider.formatToolResultMessage(toolResult.tool_call_id, toolResult.content));
451
+ }
452
+ catch (error) {
453
+ let currentError = error;
454
+ // 4. onToolCallError Hook
455
+ const middlewareDirective = await runMiddleware(this.middlewares, "onToolCallError", context, toolCall, currentError);
456
+ const directive = middlewareDirective ||
457
+ (await this.options.onToolCallError?.(toolCall, currentError));
458
+ if (directive === "STOP") {
459
+ throw currentError;
460
+ }
461
+ if (directive === "RETRY") {
462
+ // ... retry logic (simplified: recurse or duplicate logic? adhering to original logic)
463
+ // Original logic duplicated the execution block. For brevity in this replacement, I'll simplified retry to "try once more"
464
+ try {
465
+ // Retry Hook? Maybe skip start hook on retry or re-run?
466
+ // Let's assume onToolCallStart fires again for cleanliness?
467
+ // Or just execute directly to match existing behavior.
468
+ // Existing logs show we just call ToolHandler.execute again.
469
+ const toolResult = await ToolHandler.execute(toolCall, this.options.tools, this.options.onToolCallStart, this.options.onToolCallEnd);
470
+ await runMiddleware(this.middlewares, "onToolCallEnd", context, toolCall, toolResult.content);
471
+ this.messages.push(this.provider.formatToolResultMessage(toolResult.tool_call_id, toolResult.content));
472
+ continue;
473
+ }
474
+ catch (retryError) {
475
+ currentError = retryError;
476
+ await runMiddleware(this.middlewares, "onToolCallError", context, toolCall, currentError);
477
+ }
478
+ }
479
+ this.messages.push(this.provider.formatToolResultMessage(toolCall.id, `Fatal error executing tool '${toolCall.function.name}': ${currentError.message}`, { isError: true }));
480
+ if (directive === "CONTINUE") {
481
+ continue;
482
+ }
483
+ // Default short-circuit logic
484
+ const errorObj = currentError;
485
+ const isFatal = errorObj.fatal === true || errorObj.status === 401 || errorObj.status === 403;
486
+ if (isFatal) {
487
+ throw currentError;
488
+ }
489
+ logger.error(`Tool execution failed for '${toolCall.function.name}':`, currentError);
490
+ }
491
+ }
492
+ response = await this.executor.executeChat({
493
+ model: this.model,
494
+ messages: [...this.systemMessages, ...this.messages], // Use updated history
495
+ tools: this.options.tools,
496
+ temperature: options?.temperature ?? this.options.temperature,
497
+ max_tokens: options?.maxTokens ?? this.options.maxTokens ?? config.maxTokens,
498
+ headers: this.options.headers,
499
+ response_format: responseFormat, // Pass to provider
500
+ requestTimeout: options?.requestTimeout ?? this.options.requestTimeout ?? config.requestTimeout,
501
+ signal: options?.signal,
502
+ ...this.options.params
503
+ });
504
+ trackUsage(response.usage);
505
+ assistantMessage = new ChatResponseString(response.content ?? "", response.usage ?? { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model, this.provider.id, response.thinking, response.reasoning, response.tool_calls, response.finish_reason, this.options.schema);
506
+ if (this.options.onAfterResponse) {
507
+ const result = await this.options.onAfterResponse(assistantMessage);
508
+ if (result)
509
+ assistantMessage = result;
510
+ }
511
+ this.messages.push({
512
+ role: "assistant",
513
+ content: assistantMessage?.toString() || null,
514
+ tool_calls: response.tool_calls,
515
+ usage: response.usage
516
+ });
517
+ if (this.options.onEndMessage &&
518
+ (!response.tool_calls || response.tool_calls.length === 0)) {
519
+ this.options.onEndMessage(assistantMessage);
520
+ }
521
+ }
522
+ const finalResponse = new ChatResponseString(assistantMessage.toString() || "", totalUsage, this.model, this.provider.id, assistantMessage.thinking, assistantMessage.reasoning, response.tool_calls, assistantMessage.finish_reason, this.options.schema);
523
+ // 5. onResponse Hook
524
+ await runMiddleware(this.middlewares, "onResponse", context, finalResponse);
525
+ return finalResponse;
526
+ }
527
+ catch (err) {
528
+ // 6. onError Hook
529
+ await runMiddleware(this.middlewares, "onError", context, err);
530
+ throw err;
478
531
  }
479
- // For the final return, we might want to aggregate reasoning too if it happened in multiple turns?
480
- // Usually reasoning only happens once or we just want the last one.
481
- return new ChatResponseString(assistantMessage.toString() || "", totalUsage, this.model, this.provider.id, assistantMessage.thinking, assistantMessage.reasoning, response.tool_calls, assistantMessage.finish_reason);
482
532
  }
483
533
  /**
484
534
  * Streams the model's response to a user question.
@@ -1,3 +1,4 @@
1
+ import { Middleware } from "../types/Middleware.js";
1
2
  import { Message } from "./Message.js";
2
3
  import { ToolResolvable } from "./Tool.js";
3
4
  import { Schema } from "../schema/Schema.js";
@@ -6,6 +7,7 @@ import { ToolExecutionMode } from "../constants.js";
6
7
  import { ResponseFormat, ThinkingConfig } from "../providers/Provider.js";
7
8
  export interface ChatOptions {
8
9
  systemPrompt?: string;
10
+ middlewares?: Middleware[];
9
11
  messages?: Message[];
10
12
  tools?: ToolResolvable[];
11
13
  temperature?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"ChatOptions.d.ts","sourceRoot":"","sources":["../../src/chat/ChatOptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACrD,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7D,eAAe,CAAC,EAAE,CAChB,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,KAAK,KACT,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACtE,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACrE,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;CACxF"}
1
+ {"version":3,"file":"ChatOptions.d.ts","sourceRoot":"","sources":["../../src/chat/ChatOptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE1E,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACrD,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC9C,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;IAC7D,eAAe,CAAC,EAAE,CAChB,QAAQ,EAAE,OAAO,EACjB,KAAK,EAAE,KAAK,KACT,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,UAAU,GAAG,OAAO,GAAG,IAAI,CAAC,CAAC;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,iBAAiB,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;IACtE,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,CAAC;IACrE,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,kBAAkB,KAAK,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAAC;CACxF"}
@@ -1,5 +1,6 @@
1
1
  import { Usage, ThinkingResult } from "../providers/Provider.js";
2
2
  import { ToolCall } from "./Tool.js";
3
+ import { Schema } from "../schema/Schema.js";
3
4
  /**
4
5
  * Enhanced string that includes token usage metadata.
5
6
  * Behaves like a regular string but has .usage and .input_tokens etc.
@@ -12,7 +13,10 @@ export declare class ChatResponseString extends String {
12
13
  readonly reasoning?: string | null | undefined;
13
14
  readonly tool_calls?: ToolCall[] | undefined;
14
15
  readonly finish_reason?: string | null | undefined;
15
- constructor(content: string, usage: Usage, model: string, provider: string, thinking?: ThinkingResult | undefined, reasoning?: string | null | undefined, tool_calls?: ToolCall[] | undefined, finish_reason?: string | null | undefined);
16
+ readonly schema?: Schema | undefined;
17
+ private _cachedData;
18
+ private _validationError;
19
+ constructor(content: string, usage: Usage, model: string, provider: string, thinking?: ThinkingResult | undefined, reasoning?: string | null | undefined, tool_calls?: ToolCall[] | undefined, finish_reason?: string | null | undefined, schema?: Schema | undefined);
16
20
  get finishReason(): string | null | undefined;
17
21
  get input_tokens(): number;
18
22
  get output_tokens(): number;
@@ -58,9 +62,26 @@ export declare class ChatResponseString extends String {
58
62
  */
59
63
  withContent(newContent: string): ChatResponseString;
60
64
  /**
61
- * Attempt to parse the content as JSON.
62
- * Returns the parsed object or null if parsing fails.
65
+ * Attempt to extract and parse the content as JSON.
63
66
  */
64
67
  get parsed(): unknown;
68
+ /**
69
+ * Access the parsed data.
70
+ * If a Zod schema was provided via .withSchema(), this will validate the data.
71
+ * Throws ZodError if validation fails.
72
+ */
73
+ get data(): unknown;
74
+ /**
75
+ * Safe version of .data that returns null instead of throwing on validation error.
76
+ */
77
+ get safeData(): unknown;
78
+ /**
79
+ * Returns true if the content contains valid JSON that matches the schema (if provided).
80
+ */
81
+ get isValid(): boolean;
82
+ /**
83
+ * Returns the validation error if the content doesn't match the schema.
84
+ */
85
+ get validationError(): Error | null;
65
86
  }
66
87
  //# sourceMappingURL=ChatResponse.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatResponse.d.ts","sourceRoot":"","sources":["../../src/chat/ChatResponse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAErC;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,MAAM;aAG1B,KAAK,EAAE,KAAK;aACZ,KAAK,EAAE,MAAM;aACb,QAAQ,EAAE,MAAM;aAChB,QAAQ,CAAC,EAAE,cAAc;aACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;aACzB,UAAU,CAAC,EAAE,QAAQ,EAAE;aACvB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;gBAP7C,OAAO,EAAE,MAAM,EACC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,cAAc,YAAA,EACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,YAAA,EACzB,UAAU,CAAC,EAAE,QAAQ,EAAE,YAAA,EACvB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,YAAA;IAK/C,IAAI,YAAY,8BAEf;IAED,IAAI,YAAY,WAEf;IACD,IAAI,aAAa,WAEhB;IACD,IAAI,YAAY,WAEf;IACD,IAAI,aAAa,uBAEhB;IACD,IAAI,IAAI,uBAEP;IACD,IAAI,UAAU,uBAEb;IACD,IAAI,WAAW,uBAEd;IAGD,IAAI,WAAW,WAEd;IACD,IAAI,YAAY,WAEf;IACD,IAAI,WAAW,WAEd;IACD,IAAI,YAAY,uBAEf;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED;;;OAGG;IACH,IAAI,IAAI;;;;;;;;MAUP;IAED;;OAEG;IACH,IAAI,GAAG;;;;;;;;MAEN;IAED,QAAQ;IAIR;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB;IAanD;;;OAGG;IACH,IAAI,MAAM,IAAI,OAAO,CAMpB;CACF"}
1
+ {"version":3,"file":"ChatResponse.d.ts","sourceRoot":"","sources":["../../src/chat/ChatResponse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAI7C;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,MAAM;aAM1B,KAAK,EAAE,KAAK;aACZ,KAAK,EAAE,MAAM;aACb,QAAQ,EAAE,MAAM;aAChB,QAAQ,CAAC,EAAE,cAAc;aACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI;aACzB,UAAU,CAAC,EAAE,QAAQ,EAAE;aACvB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;aAC7B,MAAM,CAAC,EAAE,MAAM;IAZjC,OAAO,CAAC,WAAW,CAAsB;IACzC,OAAO,CAAC,gBAAgB,CAAsB;gBAG5C,OAAO,EAAE,MAAM,EACC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,cAAc,YAAA,EACzB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,YAAA,EACzB,UAAU,CAAC,EAAE,QAAQ,EAAE,YAAA,EACvB,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,YAAA,EAC7B,MAAM,CAAC,EAAE,MAAM,YAAA;IAKjC,IAAI,YAAY,8BAEf;IAED,IAAI,YAAY,WAEf;IACD,IAAI,aAAa,WAEhB;IACD,IAAI,YAAY,WAEf;IACD,IAAI,aAAa,uBAEhB;IACD,IAAI,IAAI,uBAEP;IACD,IAAI,UAAU,uBAEb;IACD,IAAI,WAAW,uBAEd;IAGD,IAAI,WAAW,WAEd;IACD,IAAI,YAAY,WAEf;IACD,IAAI,WAAW,WAEd;IACD,IAAI,YAAY,uBAEf;IAED,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED;;;OAGG;IACH,IAAI,IAAI;;;;;;;;MAUP;IAED;;OAEG;IACH,IAAI,GAAG;;;;;;;;MAEN;IAED,QAAQ;IAIR;;OAEG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB;IAcnD;;OAEG;IACH,IAAI,MAAM,IAAI,OAAO,CAOpB;IAED;;;;OAIG;IACH,IAAI,IAAI,IAAI,OAAO,CAkBlB;IAED;;OAEG;IACH,IAAI,QAAQ,IAAI,OAAO,CAMtB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAQrB;IAED;;OAEG;IACH,IAAI,eAAe,IAAI,KAAK,GAAG,IAAI,CAOlC;CACF"}
@@ -1,3 +1,5 @@
1
+ import { extractJson } from "../utils/json.js";
2
+ import { z } from "zod";
1
3
  /**
2
4
  * Enhanced string that includes token usage metadata.
3
5
  * Behaves like a regular string but has .usage and .input_tokens etc.
@@ -10,7 +12,10 @@ export class ChatResponseString extends String {
10
12
  reasoning;
11
13
  tool_calls;
12
14
  finish_reason;
13
- constructor(content, usage, model, provider, thinking, reasoning, tool_calls, finish_reason) {
15
+ schema;
16
+ _cachedData = undefined;
17
+ _validationError = null;
18
+ constructor(content, usage, model, provider, thinking, reasoning, tool_calls, finish_reason, schema) {
14
19
  super(content);
15
20
  this.usage = usage;
16
21
  this.model = model;
@@ -19,6 +24,7 @@ export class ChatResponseString extends String {
19
24
  this.reasoning = reasoning;
20
25
  this.tool_calls = tool_calls;
21
26
  this.finish_reason = finish_reason;
27
+ this.schema = schema;
22
28
  }
23
29
  get finishReason() {
24
30
  return this.finish_reason;
@@ -91,18 +97,79 @@ export class ChatResponseString extends String {
91
97
  * Return a new ChatResponseString with modified content but preserved metadata.
92
98
  */
93
99
  withContent(newContent) {
94
- return new ChatResponseString(newContent, this.usage, this.model, this.provider, this.thinking, this.reasoning, this.tool_calls, this.finish_reason);
100
+ return new ChatResponseString(newContent, this.usage, this.model, this.provider, this.thinking, this.reasoning, this.tool_calls, this.finish_reason, this.schema);
95
101
  }
96
102
  /**
97
- * Attempt to parse the content as JSON.
98
- * Returns the parsed object or null if parsing fails.
103
+ * Attempt to extract and parse the content as JSON.
99
104
  */
100
105
  get parsed() {
101
106
  try {
102
- return JSON.parse(this.valueOf());
107
+ const cleanJson = extractJson(this.valueOf());
108
+ return JSON.parse(cleanJson);
103
109
  }
104
110
  catch {
105
111
  return null;
106
112
  }
107
113
  }
114
+ /**
115
+ * Access the parsed data.
116
+ * If a Zod schema was provided via .withSchema(), this will validate the data.
117
+ * Throws ZodError if validation fails.
118
+ */
119
+ get data() {
120
+ if (this._cachedData !== undefined)
121
+ return this._cachedData;
122
+ if (this._validationError)
123
+ throw this._validationError;
124
+ const json = this.parsed;
125
+ if (this.schema && this.schema.definition.schema instanceof z.ZodType) {
126
+ try {
127
+ this._cachedData = this.schema.definition.schema.parse(json);
128
+ return this._cachedData;
129
+ }
130
+ catch (e) {
131
+ this._validationError = e;
132
+ throw e;
133
+ }
134
+ }
135
+ this._cachedData = json;
136
+ return json;
137
+ }
138
+ /**
139
+ * Safe version of .data that returns null instead of throwing on validation error.
140
+ */
141
+ get safeData() {
142
+ try {
143
+ return this.data;
144
+ }
145
+ catch {
146
+ return null;
147
+ }
148
+ }
149
+ /**
150
+ * Returns true if the content contains valid JSON that matches the schema (if provided).
151
+ */
152
+ get isValid() {
153
+ if (!this.schema)
154
+ return this.parsed !== null;
155
+ try {
156
+ void this.data;
157
+ return true;
158
+ }
159
+ catch {
160
+ return false;
161
+ }
162
+ }
163
+ /**
164
+ * Returns the validation error if the content doesn't match the schema.
165
+ */
166
+ get validationError() {
167
+ try {
168
+ void this.data;
169
+ return null;
170
+ }
171
+ catch (e) {
172
+ return e;
173
+ }
174
+ }
108
175
  }
@@ -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,EACL,WAAW,EAIZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAyB,MAAM,0BAA0B,CAAC;AAEtF,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGhD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AASvC;;;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;CA4QrF"}
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,EACL,WAAW,EAIZ,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAyB,MAAM,0BAA0B,CAAC;AAEtF,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAGhD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAavC;;;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;CA8WrF"}