llm-exe 2.3.2 → 2.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -226,14 +226,14 @@ declare class StringExtractParser extends BaseParser<string> {
226
226
  * Creates a parser based on the given type.
227
227
  * @template S - JSON schema type.
228
228
  * @param type - The type of parser to create.
229
- * @returns An instance of ListToKeyValueParser.
229
+ * @returns An instance of MarkdownCodeBlocksParser.
230
230
  */
231
231
  declare function createParser<T extends Extract<CreateParserType, "markdownCodeBlocks">>(type: T, options?: BaseParserOptions): MarkdownCodeBlocksParser;
232
232
  /**
233
233
  * Creates a parser based on the given type.
234
234
  * @template S - JSON schema type.
235
235
  * @param type - The type of parser to create.
236
- * @returns An instance of ListToKeyValueParser.
236
+ * @returns An instance of MarkdownCodeBlockParser.
237
237
  */
238
238
  declare function createParser<T extends Extract<CreateParserType, "markdownCodeBlock">>(type: T, options?: BaseParserOptions): MarkdownCodeBlockParser;
239
239
  /**
@@ -432,8 +432,8 @@ declare class ChatPrompt<I extends Record<string, any>> extends BasePrompt<I> {
432
432
  readonly type: ChatPromptType;
433
433
  /**
434
434
  * @property parseUserTemplates - Whether or not to allow parsing
435
- * user messages with the template engine. This could be a risk,
436
- * so we only parse user messages if explicitly set.
435
+ * user messages with the template engine. Defaults to true.
436
+ * Set `allowUnsafeUserTemplate: false` in options to disable.
437
437
  */
438
438
  private parseUserTemplates;
439
439
  /**
@@ -468,13 +468,13 @@ declare class ChatPrompt<I extends Record<string, any>> extends BasePrompt<I> {
468
468
  */
469
469
  addAssistantMessage(content: string): ChatPrompt<I>;
470
470
  /**
471
- * addFunctionMessage Helper to add an assistant message to the prompt.
471
+ * addFunctionMessage Helper to add a function message to the prompt.
472
472
  * @param content The message content.
473
473
  * @return ChatPrompt so it can be chained.
474
474
  */
475
475
  addFunctionMessage(content: string, name: string, id?: string): ChatPrompt<I>;
476
476
  /**
477
- * addFunctionCallMessage Helper to add an assistant message to the prompt.
477
+ * addFunctionCallMessage Helper to add a function_call message to the prompt.
478
478
  * @param content The message content.
479
479
  * @return ChatPrompt so it can be chained.
480
480
  */
@@ -690,7 +690,7 @@ declare class Dialogue extends BaseStateItem<IChatMessages> {
690
690
  setUserMessage(content: string | IChatMessageContentDetailed[], name?: string): this;
691
691
  setAssistantMessage(content: string | OutputResultsText): this;
692
692
  setSystemMessage(content: string): this;
693
- setToolMessage(content: string, name: string, id?: string): void;
693
+ setToolMessage(content: string, name: string, id?: string): this;
694
694
  setFunctionMessage(content: string, name: string, id?: string): this;
695
695
  /**
696
696
  * Set
@@ -699,7 +699,7 @@ declare class Dialogue extends BaseStateItem<IChatMessages> {
699
699
  name: string;
700
700
  arguments: string;
701
701
  id?: string;
702
- }): void;
702
+ }): this;
703
703
  setFunctionCallMessage(input: {
704
704
  name: string;
705
705
  arguments: string;
@@ -1057,6 +1057,30 @@ type AllLlm = {
1057
1057
  };
1058
1058
  };
1059
1059
  type AllUseLlmOptions = AllLlm & {
1060
+ "openai.gpt-5.2": {
1061
+ input: Omit<OpenAiRequest, "model">;
1062
+ };
1063
+ "openai.gpt-5-mini": {
1064
+ input: Omit<OpenAiRequest, "model">;
1065
+ };
1066
+ "openai.gpt-5-nano": {
1067
+ input: Omit<OpenAiRequest, "model">;
1068
+ };
1069
+ "openai.gpt-4.1": {
1070
+ input: Omit<OpenAiRequest, "model">;
1071
+ };
1072
+ "openai.gpt-4.1-mini": {
1073
+ input: Omit<OpenAiRequest, "model">;
1074
+ };
1075
+ "openai.gpt-4.1-nano": {
1076
+ input: Omit<OpenAiRequest, "model">;
1077
+ };
1078
+ "openai.o3": {
1079
+ input: Omit<OpenAiRequest, "model">;
1080
+ };
1081
+ "openai.o4-mini": {
1082
+ input: Omit<OpenAiRequest, "model">;
1083
+ };
1060
1084
  "openai.gpt-4": {
1061
1085
  input: OpenAiRequest;
1062
1086
  };
@@ -1066,6 +1090,12 @@ type AllUseLlmOptions = AllLlm & {
1066
1090
  "openai.gpt-4o-mini": {
1067
1091
  input: Omit<OpenAiRequest, "model">;
1068
1092
  };
1093
+ "anthropic.claude-opus-4-6": {
1094
+ input: Omit<AnthropicRequest, "model">;
1095
+ };
1096
+ "anthropic.claude-sonnet-4-6": {
1097
+ input: Omit<AnthropicRequest, "model">;
1098
+ };
1069
1099
  "anthropic.claude-sonnet-4-0": {
1070
1100
  input: Omit<AnthropicRequest, "model">;
1071
1101
  };
@@ -1120,9 +1150,15 @@ type AllUseLlmOptions = AllLlm & {
1120
1150
  "xai.grok-3": {
1121
1151
  input: OpenAiRequest;
1122
1152
  };
1153
+ "xai.grok-3-mini": {
1154
+ input: Omit<OpenAiRequest, "model">;
1155
+ };
1123
1156
  "xai.grok-4": {
1124
1157
  input: OpenAiRequest;
1125
1158
  };
1159
+ "xai.grok-4-fast": {
1160
+ input: Omit<OpenAiRequest, "model">;
1161
+ };
1126
1162
  "ollama.deepseek-r1": {
1127
1163
  input: GenericLLm;
1128
1164
  };
@@ -1466,10 +1502,14 @@ declare const configs: {
1466
1502
  "xai.chat.v1": Config<keyof AllLlm>;
1467
1503
  "xai.grok-2": Config<keyof AllLlm>;
1468
1504
  "xai.grok-3": Config<keyof AllLlm>;
1505
+ "xai.grok-3-mini": Config<keyof AllLlm>;
1469
1506
  "xai.grok-4": Config<keyof AllLlm>;
1507
+ "xai.grok-4-fast": Config<keyof AllLlm>;
1470
1508
  "amazon:anthropic.chat.v1": Config<keyof AllLlm>;
1471
1509
  "amazon:meta.chat.v1": Config<keyof AllLlm>;
1472
1510
  "anthropic.chat.v1": Config<keyof AllLlm>;
1511
+ "anthropic.claude-opus-4-6": Config<keyof AllLlm>;
1512
+ "anthropic.claude-sonnet-4-6": Config<keyof AllLlm>;
1473
1513
  "anthropic.claude-sonnet-4": Config<keyof AllLlm>;
1474
1514
  "anthropic.claude-opus-4": Config<keyof AllLlm>;
1475
1515
  "anthropic.claude-3-7-sonnet": Config<keyof AllLlm>;
@@ -1479,6 +1519,14 @@ declare const configs: {
1479
1519
  "anthropic.claude-3-haiku": Config<keyof AllLlm>;
1480
1520
  "openai.chat.v1": Config<keyof AllLlm>;
1481
1521
  "openai.chat-mock.v1": Config<keyof AllLlm>;
1522
+ "openai.gpt-5.2": Config<keyof AllLlm>;
1523
+ "openai.gpt-5-mini": Config<keyof AllLlm>;
1524
+ "openai.gpt-5-nano": Config<keyof AllLlm>;
1525
+ "openai.gpt-4.1": Config<keyof AllLlm>;
1526
+ "openai.gpt-4.1-mini": Config<keyof AllLlm>;
1527
+ "openai.gpt-4.1-nano": Config<keyof AllLlm>;
1528
+ "openai.o3": Config<keyof AllLlm>;
1529
+ "openai.o4-mini": Config<keyof AllLlm>;
1482
1530
  "openai.gpt-4o": Config<keyof AllLlm>;
1483
1531
  "openai.gpt-4o-mini": Config<keyof AllLlm>;
1484
1532
  };
package/dist/index.d.ts CHANGED
@@ -226,14 +226,14 @@ declare class StringExtractParser extends BaseParser<string> {
226
226
  * Creates a parser based on the given type.
227
227
  * @template S - JSON schema type.
228
228
  * @param type - The type of parser to create.
229
- * @returns An instance of ListToKeyValueParser.
229
+ * @returns An instance of MarkdownCodeBlocksParser.
230
230
  */
231
231
  declare function createParser<T extends Extract<CreateParserType, "markdownCodeBlocks">>(type: T, options?: BaseParserOptions): MarkdownCodeBlocksParser;
232
232
  /**
233
233
  * Creates a parser based on the given type.
234
234
  * @template S - JSON schema type.
235
235
  * @param type - The type of parser to create.
236
- * @returns An instance of ListToKeyValueParser.
236
+ * @returns An instance of MarkdownCodeBlockParser.
237
237
  */
238
238
  declare function createParser<T extends Extract<CreateParserType, "markdownCodeBlock">>(type: T, options?: BaseParserOptions): MarkdownCodeBlockParser;
239
239
  /**
@@ -432,8 +432,8 @@ declare class ChatPrompt<I extends Record<string, any>> extends BasePrompt<I> {
432
432
  readonly type: ChatPromptType;
433
433
  /**
434
434
  * @property parseUserTemplates - Whether or not to allow parsing
435
- * user messages with the template engine. This could be a risk,
436
- * so we only parse user messages if explicitly set.
435
+ * user messages with the template engine. Defaults to true.
436
+ * Set `allowUnsafeUserTemplate: false` in options to disable.
437
437
  */
438
438
  private parseUserTemplates;
439
439
  /**
@@ -468,13 +468,13 @@ declare class ChatPrompt<I extends Record<string, any>> extends BasePrompt<I> {
468
468
  */
469
469
  addAssistantMessage(content: string): ChatPrompt<I>;
470
470
  /**
471
- * addFunctionMessage Helper to add an assistant message to the prompt.
471
+ * addFunctionMessage Helper to add a function message to the prompt.
472
472
  * @param content The message content.
473
473
  * @return ChatPrompt so it can be chained.
474
474
  */
475
475
  addFunctionMessage(content: string, name: string, id?: string): ChatPrompt<I>;
476
476
  /**
477
- * addFunctionCallMessage Helper to add an assistant message to the prompt.
477
+ * addFunctionCallMessage Helper to add a function_call message to the prompt.
478
478
  * @param content The message content.
479
479
  * @return ChatPrompt so it can be chained.
480
480
  */
@@ -690,7 +690,7 @@ declare class Dialogue extends BaseStateItem<IChatMessages> {
690
690
  setUserMessage(content: string | IChatMessageContentDetailed[], name?: string): this;
691
691
  setAssistantMessage(content: string | OutputResultsText): this;
692
692
  setSystemMessage(content: string): this;
693
- setToolMessage(content: string, name: string, id?: string): void;
693
+ setToolMessage(content: string, name: string, id?: string): this;
694
694
  setFunctionMessage(content: string, name: string, id?: string): this;
695
695
  /**
696
696
  * Set
@@ -699,7 +699,7 @@ declare class Dialogue extends BaseStateItem<IChatMessages> {
699
699
  name: string;
700
700
  arguments: string;
701
701
  id?: string;
702
- }): void;
702
+ }): this;
703
703
  setFunctionCallMessage(input: {
704
704
  name: string;
705
705
  arguments: string;
@@ -1057,6 +1057,30 @@ type AllLlm = {
1057
1057
  };
1058
1058
  };
1059
1059
  type AllUseLlmOptions = AllLlm & {
1060
+ "openai.gpt-5.2": {
1061
+ input: Omit<OpenAiRequest, "model">;
1062
+ };
1063
+ "openai.gpt-5-mini": {
1064
+ input: Omit<OpenAiRequest, "model">;
1065
+ };
1066
+ "openai.gpt-5-nano": {
1067
+ input: Omit<OpenAiRequest, "model">;
1068
+ };
1069
+ "openai.gpt-4.1": {
1070
+ input: Omit<OpenAiRequest, "model">;
1071
+ };
1072
+ "openai.gpt-4.1-mini": {
1073
+ input: Omit<OpenAiRequest, "model">;
1074
+ };
1075
+ "openai.gpt-4.1-nano": {
1076
+ input: Omit<OpenAiRequest, "model">;
1077
+ };
1078
+ "openai.o3": {
1079
+ input: Omit<OpenAiRequest, "model">;
1080
+ };
1081
+ "openai.o4-mini": {
1082
+ input: Omit<OpenAiRequest, "model">;
1083
+ };
1060
1084
  "openai.gpt-4": {
1061
1085
  input: OpenAiRequest;
1062
1086
  };
@@ -1066,6 +1090,12 @@ type AllUseLlmOptions = AllLlm & {
1066
1090
  "openai.gpt-4o-mini": {
1067
1091
  input: Omit<OpenAiRequest, "model">;
1068
1092
  };
1093
+ "anthropic.claude-opus-4-6": {
1094
+ input: Omit<AnthropicRequest, "model">;
1095
+ };
1096
+ "anthropic.claude-sonnet-4-6": {
1097
+ input: Omit<AnthropicRequest, "model">;
1098
+ };
1069
1099
  "anthropic.claude-sonnet-4-0": {
1070
1100
  input: Omit<AnthropicRequest, "model">;
1071
1101
  };
@@ -1120,9 +1150,15 @@ type AllUseLlmOptions = AllLlm & {
1120
1150
  "xai.grok-3": {
1121
1151
  input: OpenAiRequest;
1122
1152
  };
1153
+ "xai.grok-3-mini": {
1154
+ input: Omit<OpenAiRequest, "model">;
1155
+ };
1123
1156
  "xai.grok-4": {
1124
1157
  input: OpenAiRequest;
1125
1158
  };
1159
+ "xai.grok-4-fast": {
1160
+ input: Omit<OpenAiRequest, "model">;
1161
+ };
1126
1162
  "ollama.deepseek-r1": {
1127
1163
  input: GenericLLm;
1128
1164
  };
@@ -1466,10 +1502,14 @@ declare const configs: {
1466
1502
  "xai.chat.v1": Config<keyof AllLlm>;
1467
1503
  "xai.grok-2": Config<keyof AllLlm>;
1468
1504
  "xai.grok-3": Config<keyof AllLlm>;
1505
+ "xai.grok-3-mini": Config<keyof AllLlm>;
1469
1506
  "xai.grok-4": Config<keyof AllLlm>;
1507
+ "xai.grok-4-fast": Config<keyof AllLlm>;
1470
1508
  "amazon:anthropic.chat.v1": Config<keyof AllLlm>;
1471
1509
  "amazon:meta.chat.v1": Config<keyof AllLlm>;
1472
1510
  "anthropic.chat.v1": Config<keyof AllLlm>;
1511
+ "anthropic.claude-opus-4-6": Config<keyof AllLlm>;
1512
+ "anthropic.claude-sonnet-4-6": Config<keyof AllLlm>;
1473
1513
  "anthropic.claude-sonnet-4": Config<keyof AllLlm>;
1474
1514
  "anthropic.claude-opus-4": Config<keyof AllLlm>;
1475
1515
  "anthropic.claude-3-7-sonnet": Config<keyof AllLlm>;
@@ -1479,6 +1519,14 @@ declare const configs: {
1479
1519
  "anthropic.claude-3-haiku": Config<keyof AllLlm>;
1480
1520
  "openai.chat.v1": Config<keyof AllLlm>;
1481
1521
  "openai.chat-mock.v1": Config<keyof AllLlm>;
1522
+ "openai.gpt-5.2": Config<keyof AllLlm>;
1523
+ "openai.gpt-5-mini": Config<keyof AllLlm>;
1524
+ "openai.gpt-5-nano": Config<keyof AllLlm>;
1525
+ "openai.gpt-4.1": Config<keyof AllLlm>;
1526
+ "openai.gpt-4.1-mini": Config<keyof AllLlm>;
1527
+ "openai.gpt-4.1-nano": Config<keyof AllLlm>;
1528
+ "openai.o3": Config<keyof AllLlm>;
1529
+ "openai.o4-mini": Config<keyof AllLlm>;
1482
1530
  "openai.gpt-4o": Config<keyof AllLlm>;
1483
1531
  "openai.gpt-4o-mini": Config<keyof AllLlm>;
1484
1532
  };
package/dist/index.js CHANGED
@@ -283,6 +283,10 @@ var BaseExecutor = class {
283
283
  try {
284
284
  hookFn(_metadata, this.getMetadata());
285
285
  } catch (error) {
286
+ console.warn(
287
+ `[llm-exe] Error in "${String(hook)}" hook:`,
288
+ error
289
+ );
286
290
  }
287
291
  }
288
292
  }
@@ -530,7 +534,7 @@ var BooleanParser = class extends BaseParser {
530
534
  `Invalid input. Expected string. Received ${typeof text}.`
531
535
  );
532
536
  const clean = text.toLowerCase().trim();
533
- if (clean === "true") {
537
+ if (clean === "true" || clean === "yes" || clean === "y" || clean === "1") {
534
538
  return true;
535
539
  }
536
540
  return false;
@@ -559,7 +563,7 @@ var NumberParser = class extends BaseParser {
559
563
  super("number", options);
560
564
  }
561
565
  parse(text) {
562
- const match = text.match(/\d/g);
566
+ const match = text.match(/-?\d+(\.\d+)?/);
563
567
  return match && isFinite(toNumber(match[0])) ? toNumber(match[0]) : -1;
564
568
  }
565
569
  };
@@ -1461,9 +1465,8 @@ var asyncCallWithTimeout = async (asyncPromise, timeLimit = 1e4) => {
1461
1465
  );
1462
1466
  }, timeLimit);
1463
1467
  });
1464
- return Promise.race([asyncPromise, timeoutPromise]).then((result) => {
1468
+ return Promise.race([asyncPromise, timeoutPromise]).finally(() => {
1465
1469
  clearTimeout(timeoutHandle);
1466
- return result;
1467
1470
  });
1468
1471
  };
1469
1472
 
@@ -1609,9 +1612,13 @@ var ListToJsonParser = class extends BaseParserWithJson {
1609
1612
  const lines = text.split("\n");
1610
1613
  const output = {};
1611
1614
  lines.forEach((line) => {
1612
- const [key, value] = line.split(":");
1613
- if (value) {
1614
- output[camelCase(key)] = value.trim();
1615
+ const colonIndex = line.indexOf(":");
1616
+ if (colonIndex !== -1) {
1617
+ const key = line.slice(0, colonIndex);
1618
+ const value = line.slice(colonIndex + 1).trim();
1619
+ if (value) {
1620
+ output[camelCase(key)] = value;
1621
+ }
1615
1622
  }
1616
1623
  });
1617
1624
  if (this.schema) {
@@ -1683,7 +1690,7 @@ var ListToArrayParser = class extends BaseParser {
1683
1690
  super("listToArray");
1684
1691
  }
1685
1692
  parse(text) {
1686
- const lines = text.split("\n").map((s) => s.replace(/^- /, "").replace(/'/g, "'").trim());
1693
+ const lines = text.split("\n").map((s) => s.replace(/^(?:[-*] |\d+\. )/, "").replace(/'/g, "\u2019").trim());
1687
1694
  return lines;
1688
1695
  }
1689
1696
  };
@@ -1811,8 +1818,11 @@ function createParser(type, options = {}) {
1811
1818
  case "stringExtract":
1812
1819
  return new StringExtractParser(options);
1813
1820
  case "string":
1814
- default:
1815
1821
  return new StringParser();
1822
+ default:
1823
+ throw new Error(
1824
+ `Invalid parser type: "${type}". Valid types are: json, string, boolean, number, stringExtract, listToArray, listToJson, listToKeyValue, replaceStringTemplate, markdownCodeBlock, markdownCodeBlocks`
1825
+ );
1816
1826
  }
1817
1827
  }
1818
1828
  function createCustomParser(name, parserFn) {
@@ -2416,6 +2426,18 @@ var openAiChatMockV1 = {
2416
2426
  var openai = {
2417
2427
  "openai.chat.v1": openAiChatV1,
2418
2428
  "openai.chat-mock.v1": openAiChatMockV1,
2429
+ // GPT-5 family
2430
+ "openai.gpt-5.2": withDefaultModel(openAiChatV1, "gpt-5.2"),
2431
+ "openai.gpt-5-mini": withDefaultModel(openAiChatV1, "gpt-5-mini"),
2432
+ "openai.gpt-5-nano": withDefaultModel(openAiChatV1, "gpt-5-nano"),
2433
+ // GPT-4.1 family
2434
+ "openai.gpt-4.1": withDefaultModel(openAiChatV1, "gpt-4.1"),
2435
+ "openai.gpt-4.1-mini": withDefaultModel(openAiChatV1, "gpt-4.1-mini"),
2436
+ "openai.gpt-4.1-nano": withDefaultModel(openAiChatV1, "gpt-4.1-nano"),
2437
+ // Reasoning models
2438
+ "openai.o3": withDefaultModel(openAiChatV1, "o3"),
2439
+ "openai.o4-mini": withDefaultModel(openAiChatV1, "o4-mini"),
2440
+ // GPT-4o family
2419
2441
  "openai.gpt-4o": withDefaultModel(openAiChatV1, "gpt-4o"),
2420
2442
  "openai.gpt-4o-mini": withDefaultModel(openAiChatV1, "gpt-4o-mini")
2421
2443
  };
@@ -2454,6 +2476,20 @@ function anthropicPromptMessageCallback(_message) {
2454
2476
  }
2455
2477
 
2456
2478
  // src/llm/config/anthropic/promptSanitize.ts
2479
+ function mergeConsecutiveSameRole(messages) {
2480
+ if (messages.length <= 1) return messages;
2481
+ const merged = [messages[0]];
2482
+ for (let i = 1; i < messages.length; i++) {
2483
+ const prev = merged[merged.length - 1];
2484
+ const curr = messages[i];
2485
+ if (curr.role === prev.role && Array.isArray(prev.content) && Array.isArray(curr.content)) {
2486
+ prev.content = [...prev.content, ...curr.content];
2487
+ } else {
2488
+ merged.push(curr);
2489
+ }
2490
+ }
2491
+ return merged;
2492
+ }
2457
2493
  function anthropicPromptSanitize(_messages, _inputBodyObj, _outputObj) {
2458
2494
  if (typeof _messages === "string") {
2459
2495
  return [{ role: "user", content: _messages }].map(
@@ -2471,14 +2507,15 @@ function anthropicPromptSanitize(_messages, _inputBodyObj, _outputObj) {
2471
2507
  }
2472
2508
  if (first.role === "system" && messages.length > 0) {
2473
2509
  _outputObj.system = first.content;
2474
- return messages.map((m) => {
2510
+ const result2 = messages.map((m) => {
2475
2511
  if (m.role === "system") {
2476
2512
  return { ...m, role: "user" };
2477
2513
  }
2478
2514
  return m;
2479
2515
  }).map(anthropicPromptMessageCallback);
2516
+ return mergeConsecutiveSameRole(result2);
2480
2517
  }
2481
- return [
2518
+ const result = [
2482
2519
  first,
2483
2520
  ...messages.map((m) => {
2484
2521
  if (m.role === "system") {
@@ -2487,6 +2524,7 @@ function anthropicPromptSanitize(_messages, _inputBodyObj, _outputObj) {
2487
2524
  return m;
2488
2525
  })
2489
2526
  ].map(anthropicPromptMessageCallback);
2527
+ return mergeConsecutiveSameRole(result);
2490
2528
  }
2491
2529
 
2492
2530
  // src/llm/output/claude.ts
@@ -2735,7 +2773,16 @@ var anthropicChatV1 = {
2735
2773
  };
2736
2774
  var anthropic = {
2737
2775
  "anthropic.chat.v1": anthropicChatV1,
2738
- // Claude 4 models (latest generation)
2776
+ // Claude 4.6 models
2777
+ "anthropic.claude-opus-4-6": withDefaultModel(
2778
+ anthropicChatV1,
2779
+ "claude-opus-4-6"
2780
+ ),
2781
+ "anthropic.claude-sonnet-4-6": withDefaultModel(
2782
+ anthropicChatV1,
2783
+ "claude-sonnet-4-6"
2784
+ ),
2785
+ // Claude 4 models
2739
2786
  "anthropic.claude-sonnet-4": withDefaultModel(
2740
2787
  anthropicChatV1,
2741
2788
  "claude-sonnet-4-0"
@@ -2779,7 +2826,9 @@ var xai = {
2779
2826
  "xai.chat.v1": xaiChatV1,
2780
2827
  "xai.grok-2": withDefaultModel(xaiChatV1, "grok-2-latest"),
2781
2828
  "xai.grok-3": withDefaultModel(xaiChatV1, "grok-3"),
2782
- "xai.grok-4": withDefaultModel(xaiChatV1, "grok-4")
2829
+ "xai.grok-3-mini": withDefaultModel(xaiChatV1, "grok-3-mini"),
2830
+ "xai.grok-4": withDefaultModel(xaiChatV1, "grok-4"),
2831
+ "xai.grok-4-fast": withDefaultModel(xaiChatV1, "grok-4-fast-non-reasoning")
2783
2832
  };
2784
2833
 
2785
2834
  // src/llm/output/_utils/combineJsonl.ts
@@ -2900,7 +2949,7 @@ function googleGeminiPromptMessageCallback(_message) {
2900
2949
  message.role = "model";
2901
2950
  }
2902
2951
  let { role, ...payload } = message;
2903
- if (typeof payload.content === "string") {
2952
+ if (typeof payload.content === "string" && message.role !== "function") {
2904
2953
  parts.push({ text: message.content });
2905
2954
  }
2906
2955
  if (message.role === "function") {
@@ -2939,6 +2988,20 @@ function googleGeminiPromptMessageCallback(_message) {
2939
2988
  }
2940
2989
 
2941
2990
  // src/llm/config/google/promptSanitize.ts
2991
+ function mergeConsecutiveSameRole2(messages) {
2992
+ if (messages.length <= 1) return messages;
2993
+ const merged = [messages[0]];
2994
+ for (let i = 1; i < messages.length; i++) {
2995
+ const prev = merged[merged.length - 1];
2996
+ const curr = messages[i];
2997
+ if (curr.role === prev.role && Array.isArray(prev.parts) && Array.isArray(curr.parts)) {
2998
+ prev.parts = [...prev.parts, ...curr.parts];
2999
+ } else {
3000
+ merged.push(curr);
3001
+ }
3002
+ }
3003
+ return merged;
3004
+ }
2942
3005
  function googleGeminiPromptSanitize(_messages, _inputBodyObj, _outputObj) {
2943
3006
  if (typeof _messages === "string") {
2944
3007
  return [{ role: "user", parts: [{ text: _messages }] }];
@@ -2965,9 +3028,13 @@ function googleGeminiPromptSanitize(_messages, _inputBodyObj, _outputObj) {
2965
3028
  text: message.content
2966
3029
  }))
2967
3030
  };
2968
- return withoutSystemInstructions.map(googleGeminiPromptMessageCallback);
3031
+ const result2 = withoutSystemInstructions.map(
3032
+ googleGeminiPromptMessageCallback
3033
+ );
3034
+ return mergeConsecutiveSameRole2(result2);
2969
3035
  }
2970
- return _messages.map(googleGeminiPromptMessageCallback);
3036
+ const result = _messages.map(googleGeminiPromptMessageCallback);
3037
+ return mergeConsecutiveSameRole2(result);
2971
3038
  }
2972
3039
  throw new Error("Invalid messages format");
2973
3040
  }
@@ -4171,8 +4238,8 @@ var ChatPrompt = class extends BasePrompt {
4171
4238
  __publicField(this, "type", "chat");
4172
4239
  /**
4173
4240
  * @property parseUserTemplates - Whether or not to allow parsing
4174
- * user messages with the template engine. This could be a risk,
4175
- * so we only parse user messages if explicitly set.
4241
+ * user messages with the template engine. Defaults to true.
4242
+ * Set `allowUnsafeUserTemplate: false` in options to disable.
4176
4243
  */
4177
4244
  __publicField(this, "parseUserTemplates", true);
4178
4245
  if (typeof options?.allowUnsafeUserTemplate === "boolean") {
@@ -4233,7 +4300,7 @@ var ChatPrompt = class extends BasePrompt {
4233
4300
  return this;
4234
4301
  }
4235
4302
  /**
4236
- * addFunctionMessage Helper to add an assistant message to the prompt.
4303
+ * addFunctionMessage Helper to add a function message to the prompt.
4237
4304
  * @param content The message content.
4238
4305
  * @return ChatPrompt so it can be chained.
4239
4306
  */
@@ -4247,7 +4314,7 @@ var ChatPrompt = class extends BasePrompt {
4247
4314
  return this;
4248
4315
  }
4249
4316
  /**
4250
- * addFunctionCallMessage Helper to add an assistant message to the prompt.
4317
+ * addFunctionCallMessage Helper to add a function_call message to the prompt.
4251
4318
  * @param content The message content.
4252
4319
  * @return ChatPrompt so it can be chained.
4253
4320
  */
@@ -4802,7 +4869,7 @@ var Dialogue = class extends BaseStateItem {
4802
4869
  return this;
4803
4870
  }
4804
4871
  setToolMessage(content, name, id) {
4805
- this.setFunctionMessage(content, name, id);
4872
+ return this.setFunctionMessage(content, name, id);
4806
4873
  }
4807
4874
  setFunctionMessage(content, name, id) {
4808
4875
  if (content) {
@@ -4819,7 +4886,7 @@ var Dialogue = class extends BaseStateItem {
4819
4886
  * Set
4820
4887
  */
4821
4888
  setToolCallMessage(input) {
4822
- this.setFunctionCallMessage(input);
4889
+ return this.setFunctionCallMessage(input);
4823
4890
  }
4824
4891
  setFunctionCallMessage(input) {
4825
4892
  if (!input || typeof input !== "object") {
package/dist/index.mjs CHANGED
@@ -220,6 +220,10 @@ var BaseExecutor = class {
220
220
  try {
221
221
  hookFn(_metadata, this.getMetadata());
222
222
  } catch (error) {
223
+ console.warn(
224
+ `[llm-exe] Error in "${String(hook)}" hook:`,
225
+ error
226
+ );
223
227
  }
224
228
  }
225
229
  }
@@ -467,7 +471,7 @@ var BooleanParser = class extends BaseParser {
467
471
  `Invalid input. Expected string. Received ${typeof text}.`
468
472
  );
469
473
  const clean = text.toLowerCase().trim();
470
- if (clean === "true") {
474
+ if (clean === "true" || clean === "yes" || clean === "y" || clean === "1") {
471
475
  return true;
472
476
  }
473
477
  return false;
@@ -496,7 +500,7 @@ var NumberParser = class extends BaseParser {
496
500
  super("number", options);
497
501
  }
498
502
  parse(text) {
499
- const match = text.match(/\d/g);
503
+ const match = text.match(/-?\d+(\.\d+)?/);
500
504
  return match && isFinite(toNumber(match[0])) ? toNumber(match[0]) : -1;
501
505
  }
502
506
  };
@@ -1398,9 +1402,8 @@ var asyncCallWithTimeout = async (asyncPromise, timeLimit = 1e4) => {
1398
1402
  );
1399
1403
  }, timeLimit);
1400
1404
  });
1401
- return Promise.race([asyncPromise, timeoutPromise]).then((result) => {
1405
+ return Promise.race([asyncPromise, timeoutPromise]).finally(() => {
1402
1406
  clearTimeout(timeoutHandle);
1403
- return result;
1404
1407
  });
1405
1408
  };
1406
1409
 
@@ -1546,9 +1549,13 @@ var ListToJsonParser = class extends BaseParserWithJson {
1546
1549
  const lines = text.split("\n");
1547
1550
  const output = {};
1548
1551
  lines.forEach((line) => {
1549
- const [key, value] = line.split(":");
1550
- if (value) {
1551
- output[camelCase(key)] = value.trim();
1552
+ const colonIndex = line.indexOf(":");
1553
+ if (colonIndex !== -1) {
1554
+ const key = line.slice(0, colonIndex);
1555
+ const value = line.slice(colonIndex + 1).trim();
1556
+ if (value) {
1557
+ output[camelCase(key)] = value;
1558
+ }
1552
1559
  }
1553
1560
  });
1554
1561
  if (this.schema) {
@@ -1620,7 +1627,7 @@ var ListToArrayParser = class extends BaseParser {
1620
1627
  super("listToArray");
1621
1628
  }
1622
1629
  parse(text) {
1623
- const lines = text.split("\n").map((s) => s.replace(/^- /, "").replace(/'/g, "'").trim());
1630
+ const lines = text.split("\n").map((s) => s.replace(/^(?:[-*] |\d+\. )/, "").replace(/'/g, "\u2019").trim());
1624
1631
  return lines;
1625
1632
  }
1626
1633
  };
@@ -1748,8 +1755,11 @@ function createParser(type, options = {}) {
1748
1755
  case "stringExtract":
1749
1756
  return new StringExtractParser(options);
1750
1757
  case "string":
1751
- default:
1752
1758
  return new StringParser();
1759
+ default:
1760
+ throw new Error(
1761
+ `Invalid parser type: "${type}". Valid types are: json, string, boolean, number, stringExtract, listToArray, listToJson, listToKeyValue, replaceStringTemplate, markdownCodeBlock, markdownCodeBlocks`
1762
+ );
1753
1763
  }
1754
1764
  }
1755
1765
  function createCustomParser(name, parserFn) {
@@ -2353,6 +2363,18 @@ var openAiChatMockV1 = {
2353
2363
  var openai = {
2354
2364
  "openai.chat.v1": openAiChatV1,
2355
2365
  "openai.chat-mock.v1": openAiChatMockV1,
2366
+ // GPT-5 family
2367
+ "openai.gpt-5.2": withDefaultModel(openAiChatV1, "gpt-5.2"),
2368
+ "openai.gpt-5-mini": withDefaultModel(openAiChatV1, "gpt-5-mini"),
2369
+ "openai.gpt-5-nano": withDefaultModel(openAiChatV1, "gpt-5-nano"),
2370
+ // GPT-4.1 family
2371
+ "openai.gpt-4.1": withDefaultModel(openAiChatV1, "gpt-4.1"),
2372
+ "openai.gpt-4.1-mini": withDefaultModel(openAiChatV1, "gpt-4.1-mini"),
2373
+ "openai.gpt-4.1-nano": withDefaultModel(openAiChatV1, "gpt-4.1-nano"),
2374
+ // Reasoning models
2375
+ "openai.o3": withDefaultModel(openAiChatV1, "o3"),
2376
+ "openai.o4-mini": withDefaultModel(openAiChatV1, "o4-mini"),
2377
+ // GPT-4o family
2356
2378
  "openai.gpt-4o": withDefaultModel(openAiChatV1, "gpt-4o"),
2357
2379
  "openai.gpt-4o-mini": withDefaultModel(openAiChatV1, "gpt-4o-mini")
2358
2380
  };
@@ -2391,6 +2413,20 @@ function anthropicPromptMessageCallback(_message) {
2391
2413
  }
2392
2414
 
2393
2415
  // src/llm/config/anthropic/promptSanitize.ts
2416
+ function mergeConsecutiveSameRole(messages) {
2417
+ if (messages.length <= 1) return messages;
2418
+ const merged = [messages[0]];
2419
+ for (let i = 1; i < messages.length; i++) {
2420
+ const prev = merged[merged.length - 1];
2421
+ const curr = messages[i];
2422
+ if (curr.role === prev.role && Array.isArray(prev.content) && Array.isArray(curr.content)) {
2423
+ prev.content = [...prev.content, ...curr.content];
2424
+ } else {
2425
+ merged.push(curr);
2426
+ }
2427
+ }
2428
+ return merged;
2429
+ }
2394
2430
  function anthropicPromptSanitize(_messages, _inputBodyObj, _outputObj) {
2395
2431
  if (typeof _messages === "string") {
2396
2432
  return [{ role: "user", content: _messages }].map(
@@ -2408,14 +2444,15 @@ function anthropicPromptSanitize(_messages, _inputBodyObj, _outputObj) {
2408
2444
  }
2409
2445
  if (first.role === "system" && messages.length > 0) {
2410
2446
  _outputObj.system = first.content;
2411
- return messages.map((m) => {
2447
+ const result2 = messages.map((m) => {
2412
2448
  if (m.role === "system") {
2413
2449
  return { ...m, role: "user" };
2414
2450
  }
2415
2451
  return m;
2416
2452
  }).map(anthropicPromptMessageCallback);
2453
+ return mergeConsecutiveSameRole(result2);
2417
2454
  }
2418
- return [
2455
+ const result = [
2419
2456
  first,
2420
2457
  ...messages.map((m) => {
2421
2458
  if (m.role === "system") {
@@ -2424,6 +2461,7 @@ function anthropicPromptSanitize(_messages, _inputBodyObj, _outputObj) {
2424
2461
  return m;
2425
2462
  })
2426
2463
  ].map(anthropicPromptMessageCallback);
2464
+ return mergeConsecutiveSameRole(result);
2427
2465
  }
2428
2466
 
2429
2467
  // src/llm/output/claude.ts
@@ -2672,7 +2710,16 @@ var anthropicChatV1 = {
2672
2710
  };
2673
2711
  var anthropic = {
2674
2712
  "anthropic.chat.v1": anthropicChatV1,
2675
- // Claude 4 models (latest generation)
2713
+ // Claude 4.6 models
2714
+ "anthropic.claude-opus-4-6": withDefaultModel(
2715
+ anthropicChatV1,
2716
+ "claude-opus-4-6"
2717
+ ),
2718
+ "anthropic.claude-sonnet-4-6": withDefaultModel(
2719
+ anthropicChatV1,
2720
+ "claude-sonnet-4-6"
2721
+ ),
2722
+ // Claude 4 models
2676
2723
  "anthropic.claude-sonnet-4": withDefaultModel(
2677
2724
  anthropicChatV1,
2678
2725
  "claude-sonnet-4-0"
@@ -2716,7 +2763,9 @@ var xai = {
2716
2763
  "xai.chat.v1": xaiChatV1,
2717
2764
  "xai.grok-2": withDefaultModel(xaiChatV1, "grok-2-latest"),
2718
2765
  "xai.grok-3": withDefaultModel(xaiChatV1, "grok-3"),
2719
- "xai.grok-4": withDefaultModel(xaiChatV1, "grok-4")
2766
+ "xai.grok-3-mini": withDefaultModel(xaiChatV1, "grok-3-mini"),
2767
+ "xai.grok-4": withDefaultModel(xaiChatV1, "grok-4"),
2768
+ "xai.grok-4-fast": withDefaultModel(xaiChatV1, "grok-4-fast-non-reasoning")
2720
2769
  };
2721
2770
 
2722
2771
  // src/llm/output/_utils/combineJsonl.ts
@@ -2837,7 +2886,7 @@ function googleGeminiPromptMessageCallback(_message) {
2837
2886
  message.role = "model";
2838
2887
  }
2839
2888
  let { role, ...payload } = message;
2840
- if (typeof payload.content === "string") {
2889
+ if (typeof payload.content === "string" && message.role !== "function") {
2841
2890
  parts.push({ text: message.content });
2842
2891
  }
2843
2892
  if (message.role === "function") {
@@ -2876,6 +2925,20 @@ function googleGeminiPromptMessageCallback(_message) {
2876
2925
  }
2877
2926
 
2878
2927
  // src/llm/config/google/promptSanitize.ts
2928
+ function mergeConsecutiveSameRole2(messages) {
2929
+ if (messages.length <= 1) return messages;
2930
+ const merged = [messages[0]];
2931
+ for (let i = 1; i < messages.length; i++) {
2932
+ const prev = merged[merged.length - 1];
2933
+ const curr = messages[i];
2934
+ if (curr.role === prev.role && Array.isArray(prev.parts) && Array.isArray(curr.parts)) {
2935
+ prev.parts = [...prev.parts, ...curr.parts];
2936
+ } else {
2937
+ merged.push(curr);
2938
+ }
2939
+ }
2940
+ return merged;
2941
+ }
2879
2942
  function googleGeminiPromptSanitize(_messages, _inputBodyObj, _outputObj) {
2880
2943
  if (typeof _messages === "string") {
2881
2944
  return [{ role: "user", parts: [{ text: _messages }] }];
@@ -2902,9 +2965,13 @@ function googleGeminiPromptSanitize(_messages, _inputBodyObj, _outputObj) {
2902
2965
  text: message.content
2903
2966
  }))
2904
2967
  };
2905
- return withoutSystemInstructions.map(googleGeminiPromptMessageCallback);
2968
+ const result2 = withoutSystemInstructions.map(
2969
+ googleGeminiPromptMessageCallback
2970
+ );
2971
+ return mergeConsecutiveSameRole2(result2);
2906
2972
  }
2907
- return _messages.map(googleGeminiPromptMessageCallback);
2973
+ const result = _messages.map(googleGeminiPromptMessageCallback);
2974
+ return mergeConsecutiveSameRole2(result);
2908
2975
  }
2909
2976
  throw new Error("Invalid messages format");
2910
2977
  }
@@ -4108,8 +4175,8 @@ var ChatPrompt = class extends BasePrompt {
4108
4175
  __publicField(this, "type", "chat");
4109
4176
  /**
4110
4177
  * @property parseUserTemplates - Whether or not to allow parsing
4111
- * user messages with the template engine. This could be a risk,
4112
- * so we only parse user messages if explicitly set.
4178
+ * user messages with the template engine. Defaults to true.
4179
+ * Set `allowUnsafeUserTemplate: false` in options to disable.
4113
4180
  */
4114
4181
  __publicField(this, "parseUserTemplates", true);
4115
4182
  if (typeof options?.allowUnsafeUserTemplate === "boolean") {
@@ -4170,7 +4237,7 @@ var ChatPrompt = class extends BasePrompt {
4170
4237
  return this;
4171
4238
  }
4172
4239
  /**
4173
- * addFunctionMessage Helper to add an assistant message to the prompt.
4240
+ * addFunctionMessage Helper to add a function message to the prompt.
4174
4241
  * @param content The message content.
4175
4242
  * @return ChatPrompt so it can be chained.
4176
4243
  */
@@ -4184,7 +4251,7 @@ var ChatPrompt = class extends BasePrompt {
4184
4251
  return this;
4185
4252
  }
4186
4253
  /**
4187
- * addFunctionCallMessage Helper to add an assistant message to the prompt.
4254
+ * addFunctionCallMessage Helper to add a function_call message to the prompt.
4188
4255
  * @param content The message content.
4189
4256
  * @return ChatPrompt so it can be chained.
4190
4257
  */
@@ -4739,7 +4806,7 @@ var Dialogue = class extends BaseStateItem {
4739
4806
  return this;
4740
4807
  }
4741
4808
  setToolMessage(content, name, id) {
4742
- this.setFunctionMessage(content, name, id);
4809
+ return this.setFunctionMessage(content, name, id);
4743
4810
  }
4744
4811
  setFunctionMessage(content, name, id) {
4745
4812
  if (content) {
@@ -4756,7 +4823,7 @@ var Dialogue = class extends BaseStateItem {
4756
4823
  * Set
4757
4824
  */
4758
4825
  setToolCallMessage(input) {
4759
- this.setFunctionCallMessage(input);
4826
+ return this.setFunctionCallMessage(input);
4760
4827
  }
4761
4828
  setFunctionCallMessage(input) {
4762
4829
  if (!input || typeof input !== "object") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-exe",
3
- "version": "2.3.2",
3
+ "version": "2.3.4",
4
4
  "description": "Simplify building LLM-powered apps with easy-to-use base components, supporting text and chat-based prompts with handlebars template engine, output parsers, and flexible function calling capabilities.",
5
5
  "keywords": [
6
6
  "ai",
@@ -38,7 +38,7 @@
38
38
  "typecheck": "tsc --noEmit",
39
39
  "test": "NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.ts --detectOpenHandles --coverage --forceExit",
40
40
  "test-examples": "eval $(cat .env) NODE_OPTIONS=--experimental-vm-modules jest --config jest.config.examples.ts --detectOpenHandles --coverage --forceExit",
41
- "test-one": "eval $(cat .env) NODE_OPTIONS=--experimental-vm-modules jest --detectOpenHandles --coverage --forceExit --config jest.config.examples.ts examples/intentBot.test.ts",
41
+ "test-one": "eval $(cat .env) NODE_OPTIONS=--experimental-vm-modules jest --detectOpenHandles --coverage --forceExit --config jest.config.examples.ts examples/helloWorld.test.ts",
42
42
  "run-one": "eval $(cat .env) ts-node ./examples/chains/self-refinement/usage.ts",
43
43
  "build": "(concurrently \"tsc --p ./tsconfig.json -w\" \"tsc-alias -p tsconfig.json -w\")",
44
44
  "build:ci": "tsup src/index.ts --format cjs,esm --dts --clean",
@@ -46,7 +46,7 @@
46
46
  "build:package": "tsup src/index.ts --format cjs,esm --dts --clean --external jsonschema,json-schema-to-ts,exponential-backoff,@aws-sdk/credential-providers,@aws-crypto/sha256-js,@smithy/protocol-http,@smithy/signature-v4",
47
47
  "build:docs-examples": "./node_modules/.bin/esbuild examples/prompt/index.ts examples/state/index.ts --bundle --outdir=docs/.vitepress/components/examples --platform=node --target=es6 --format=esm",
48
48
  "build:watch:docs-examples": "./node_modules/.bin/esbuild examples/prompt/index.ts examples/state/index.ts --watch --bundle --outdir=docs/.vitepress/components/examples --platform=node --target=es6 --format=esm",
49
- "predocs:build": "npm run build:docs-examples",
49
+ "predocs:build": "npm run build:docs-examples && bash scripts/generate-llms-txt.sh",
50
50
  "docs:dev": "eval $(cat docs/.env) && concurrently \"vitepress dev docs\" \"npm run build:watch:docs-examples\"",
51
51
  "docs:build": "eval $(cat docs/.env) && vitepress build docs",
52
52
  "docs:update-providers": "node docs/.vitepress/scripts/updateProviders.js",
package/readme.md CHANGED
@@ -41,6 +41,8 @@ const llmExe = require("llm-exe");
41
41
  ## Overview
42
42
 
43
43
  ```ts
44
+ import { useLlm, createChatPrompt, createParser, createLlmExecutor, defineSchema } from "llm-exe";
45
+
44
46
  // Prompt
45
47
  const prompt = createChatPrompt("You are a support agent. Help the user.");
46
48
  prompt.addUserMessage("I need help with my order.");
@@ -48,8 +50,16 @@ prompt.addUserMessage("I need help with my order.");
48
50
  // LLM
49
51
  const llm = useLlm("openai.gpt-4o");
50
52
 
51
- // Parser
52
- const parser = createParser("json", { schema: mySchema });
53
+ // Parser — schema uses JSON Schema (via defineSchema)
54
+ const schema = defineSchema({
55
+ type: "object",
56
+ properties: {
57
+ answer: { type: "string" },
58
+ action: { type: "string" },
59
+ },
60
+ required: ["answer", "action"],
61
+ } as const);
62
+ const parser = createParser("json", { schema });
53
63
 
54
64
  // Executor
55
65
  const executor = createLlmExecutor({ llm, prompt, parser });
@@ -71,11 +81,17 @@ Welcome back!
71
81
  #### Built-In Parsers
72
82
 
73
83
  ```ts
74
- createParser("stringExtract", { enum: ["yes", "no"] });
75
- createParser("listToJson");
76
- createParser("listToArray");
77
- createParser("markdownCodeBlock");
78
- // ...etc
84
+ createParser("string"); // pass-through, returns string
85
+ createParser("json", { schema }); // JSON with optional schema validation
86
+ createParser("boolean"); // extracts boolean from response
87
+ createParser("number"); // extracts number from response
88
+ createParser("stringExtract", { enum: ["yes", "no"] }); // match one of the enum values
89
+ createParser("listToArray"); // newline-separated list → string[]
90
+ createParser("listToJson"); // key: value list → object (with optional schema)
91
+ createParser("listToKeyValue"); // key: value list → Array<{ key, value }>
92
+ createParser("markdownCodeBlock"); // single code block → { code, language }
93
+ createParser("markdownCodeBlocks"); // multiple code blocks → Array<{ code, language }>
94
+ createParser("replaceStringTemplate"); // handlebars-based output templating
79
95
  ```
80
96
 
81
97
  #### Custom Parsers