llm-exe 2.3.5 → 2.3.6

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.mjs CHANGED
@@ -494,6 +494,21 @@ function toNumber(value) {
494
494
  return NaN;
495
495
  }
496
496
 
497
+ // src/utils/modules/errors.ts
498
+ var LlmExeError = class extends Error {
499
+ constructor(message, code, context) {
500
+ super(message ?? "");
501
+ __publicField(this, "code");
502
+ __publicField(this, "context");
503
+ this.name = this.constructor.name;
504
+ this.code = code ?? "unknown";
505
+ this.context = context;
506
+ if (Error.captureStackTrace) {
507
+ Error.captureStackTrace(this, this.constructor);
508
+ }
509
+ }
510
+ };
511
+
497
512
  // src/parser/parsers/NumberParser.ts
498
513
  var NumberParser = class extends BaseParser {
499
514
  constructor(options) {
@@ -501,7 +516,18 @@ var NumberParser = class extends BaseParser {
501
516
  }
502
517
  parse(text) {
503
518
  const match = text.match(/-?\d+(\.\d+)?/);
504
- return match && isFinite(toNumber(match[0])) ? toNumber(match[0]) : -1;
519
+ if (match && isFinite(toNumber(match[0]))) {
520
+ return toNumber(match[0]);
521
+ }
522
+ throw new LlmExeError(
523
+ `No numeric value found in input.`,
524
+ "parser",
525
+ {
526
+ parser: "number",
527
+ output: text,
528
+ error: `No numeric value found in input.`
529
+ }
530
+ );
505
531
  }
506
532
  };
507
533
 
@@ -1398,7 +1424,7 @@ var asyncCallWithTimeout = async (asyncPromise, timeLimit = 1e4) => {
1398
1424
  const timeoutPromise = new Promise((_resolve, reject) => {
1399
1425
  timeoutHandle = setTimeout(() => {
1400
1426
  return reject(
1401
- new Error("Unable to perform action. Try again, or use another action.")
1427
+ new Error(`LLM call timed out after ${timeLimit}ms`)
1402
1428
  );
1403
1429
  }, timeLimit);
1404
1430
  });
@@ -1486,21 +1512,6 @@ function validateParserSchema(schema, parsed) {
1486
1512
  return null;
1487
1513
  }
1488
1514
 
1489
- // src/utils/modules/errors.ts
1490
- var LlmExeError = class extends Error {
1491
- constructor(message, code, context) {
1492
- super(message ?? "");
1493
- __publicField(this, "code");
1494
- __publicField(this, "context");
1495
- this.name = this.constructor.name;
1496
- this.code = code ?? "unknown";
1497
- this.context = context;
1498
- if (Error.captureStackTrace) {
1499
- Error.captureStackTrace(this, this.constructor);
1500
- }
1501
- }
1502
- };
1503
-
1504
1515
  // src/parser/parsers/JsonParser.ts
1505
1516
  var JsonParser = class extends BaseParserWithJson {
1506
1517
  constructor(options = {}) {
@@ -1544,6 +1555,8 @@ function camelCase(input) {
1544
1555
  var ListToJsonParser = class extends BaseParserWithJson {
1545
1556
  constructor(options = {}) {
1546
1557
  super("listToJson", options);
1558
+ __publicField(this, "keyTransform");
1559
+ this.keyTransform = options.keyTransform ?? "camelCase";
1547
1560
  }
1548
1561
  parse(text) {
1549
1562
  const lines = text.split("\n");
@@ -1554,7 +1567,8 @@ var ListToJsonParser = class extends BaseParserWithJson {
1554
1567
  const key = line.slice(0, colonIndex);
1555
1568
  const value = line.slice(colonIndex + 1).trim();
1556
1569
  if (value) {
1557
- output[camelCase(key)] = value;
1570
+ const transformedKey = this.keyTransform === "preserve" ? key.trim() : camelCase(key);
1571
+ output[transformedKey] = value;
1558
1572
  }
1559
1573
  }
1560
1574
  });
@@ -1727,7 +1741,15 @@ var StringExtractParser = class extends BaseParser {
1727
1741
  return option;
1728
1742
  }
1729
1743
  }
1730
- return "";
1744
+ throw new LlmExeError(
1745
+ `No matching enum value found in input.`,
1746
+ "parser",
1747
+ {
1748
+ parser: "stringExtract",
1749
+ output: text,
1750
+ error: `No matching enum value found in input. Expected one of: ${this.enum.join(", ")}`
1751
+ }
1752
+ );
1731
1753
  }
1732
1754
  };
1733
1755
 
@@ -2244,6 +2266,7 @@ function cleanJsonSchemaFor(schema = {}, provider) {
2244
2266
  // src/llm/config/openai/compatible.ts
2245
2267
  function createOpenAiCompatibleConfiguration(overrides) {
2246
2268
  const [apiKeyPropertyKey, apiKeyPropertyValue] = overrides.apiKeyMapping;
2269
+ const isReasoningModel = overrides.isReasoningModel ?? (() => false);
2247
2270
  const config = {
2248
2271
  key: overrides.key,
2249
2272
  provider: overrides.provider,
@@ -2297,10 +2320,7 @@ function createOpenAiCompatibleConfiguration(overrides) {
2297
2320
  effort: {
2298
2321
  key: "reasoning_effort",
2299
2322
  transform: (v, _s) => {
2300
- if (
2301
- // only supported reasoning models
2302
- ["gpt-5"].includes(_s.model) && typeof v === "string" && ["minimal", "low", "medium", "high"].includes(v)
2303
- ) {
2323
+ if (typeof _s.model === "string" && isReasoningModel(_s.model) && typeof v === "string" && ["minimal", "low", "medium", "high"].includes(v)) {
2304
2324
  return v;
2305
2325
  }
2306
2326
  return void 0;
@@ -2347,7 +2367,8 @@ var openAiChatV1 = createOpenAiCompatibleConfiguration({
2347
2367
  key: "openai.chat.v1",
2348
2368
  provider: "openai.chat",
2349
2369
  endpoint: `https://api.openai.com/v1/chat/completions`,
2350
- apiKeyMapping: ["openAiApiKey", "OPENAI_API_KEY"]
2370
+ apiKeyMapping: ["openAiApiKey", "OPENAI_API_KEY"],
2371
+ isReasoningModel: (model) => model.startsWith("gpt-5") || model.startsWith("o3") || model.startsWith("o4")
2351
2372
  });
2352
2373
  var openAiChatMockV1 = {
2353
2374
  key: "openai.chat-mock.v1",
@@ -2393,10 +2414,12 @@ var openai = {
2393
2414
  "openai.gpt-4.1-nano": withDefaultModel(openAiChatV1, "gpt-4.1-nano"),
2394
2415
  // Reasoning models
2395
2416
  "openai.o3": withDefaultModel(openAiChatV1, "o3"),
2396
- "openai.o4-mini": withDefaultModel(openAiChatV1, "o4-mini"),
2397
2417
  // GPT-4o family
2418
+ "openai.gpt-4": withDefaultModel(openAiChatV1, "gpt-4"),
2398
2419
  "openai.gpt-4o": withDefaultModel(openAiChatV1, "gpt-4o"),
2399
- "openai.gpt-4o-mini": withDefaultModel(openAiChatV1, "gpt-4o-mini")
2420
+ "openai.gpt-4o-mini": withDefaultModel(openAiChatV1, "gpt-4o-mini"),
2421
+ // Deprecated
2422
+ "openai.o4-mini": withDefaultModel(openAiChatV1, "o4-mini")
2400
2423
  };
2401
2424
 
2402
2425
  // src/llm/config/anthropic/promptSanitizeMessageCallback.ts
@@ -2656,6 +2679,14 @@ var bedrock = {
2656
2679
 
2657
2680
  // src/llm/config/anthropic/index.ts
2658
2681
  var ANTHROPIC_VERSION = "2023-06-01";
2682
+ var MODELS_REJECTING_SAMPLING_PARAMS = ["claude-opus-4-7"];
2683
+ var isClaude4x = (model) => /^claude-(opus|sonnet|haiku)-4-/.test(model);
2684
+ var dropIfModelRejectsSamplingParams = (v, body) => MODELS_REJECTING_SAMPLING_PARAMS.includes(body.model) ? void 0 : v;
2685
+ var topPTransform = (v, body) => {
2686
+ if (MODELS_REJECTING_SAMPLING_PARAMS.includes(body.model)) return void 0;
2687
+ if (isClaude4x(body.model) && body.temperature !== void 0) return void 0;
2688
+ return v;
2689
+ };
2659
2690
  var anthropicChatV1 = {
2660
2691
  key: "anthropic.chat.v1",
2661
2692
  provider: "anthropic.chat",
@@ -2688,13 +2719,16 @@ var anthropicChatV1 = {
2688
2719
  transform: anthropicPromptSanitize
2689
2720
  },
2690
2721
  temperature: {
2691
- key: "temperature"
2722
+ key: "temperature",
2723
+ transform: dropIfModelRejectsSamplingParams
2692
2724
  },
2693
2725
  topP: {
2694
- key: "top_p"
2726
+ key: "top_p",
2727
+ transform: topPTransform
2695
2728
  },
2696
2729
  topK: {
2697
- key: "top_k"
2730
+ key: "top_k",
2731
+ transform: dropIfModelRejectsSamplingParams
2698
2732
  },
2699
2733
  stopSequences: {
2700
2734
  key: "stop_sequences"
@@ -2730,6 +2764,11 @@ var anthropicChatV1 = {
2730
2764
  };
2731
2765
  var anthropic = {
2732
2766
  "anthropic.chat.v1": anthropicChatV1,
2767
+ // Claude 4.7 models
2768
+ "anthropic.claude-opus-4-7": withDefaultModel(
2769
+ anthropicChatV1,
2770
+ "claude-opus-4-7"
2771
+ ),
2733
2772
  // Claude 4.6 models
2734
2773
  "anthropic.claude-opus-4-6": withDefaultModel(
2735
2774
  anthropicChatV1,
@@ -2739,7 +2778,20 @@ var anthropic = {
2739
2778
  anthropicChatV1,
2740
2779
  "claude-sonnet-4-6"
2741
2780
  ),
2742
- // Claude 4 models
2781
+ // Claude 4.5 models
2782
+ "anthropic.claude-haiku-4-5": withDefaultModel(
2783
+ anthropicChatV1,
2784
+ "claude-haiku-4-5"
2785
+ ),
2786
+ "anthropic.claude-sonnet-4-5": withDefaultModel(
2787
+ anthropicChatV1,
2788
+ "claude-sonnet-4-5"
2789
+ ),
2790
+ // Deprecated
2791
+ "anthropic.claude-opus-4-1": withDefaultModel(
2792
+ anthropicChatV1,
2793
+ "claude-opus-4-1-20250805"
2794
+ ),
2743
2795
  "anthropic.claude-sonnet-4": withDefaultModel(
2744
2796
  anthropicChatV1,
2745
2797
  "claude-sonnet-4-0"
@@ -2748,7 +2800,6 @@ var anthropic = {
2748
2800
  anthropicChatV1,
2749
2801
  "claude-opus-4-0"
2750
2802
  ),
2751
- // Deprecated
2752
2803
  "anthropic.claude-3-7-sonnet": withDefaultModel(
2753
2804
  anthropicChatV1,
2754
2805
  "claude-3-7-sonnet-20250219"
@@ -2764,10 +2815,6 @@ var anthropic = {
2764
2815
  "anthropic.claude-3-opus": withDefaultModel(
2765
2816
  anthropicChatV1,
2766
2817
  "claude-3-opus-20240229"
2767
- ),
2768
- "anthropic.claude-3-haiku": withDefaultModel(
2769
- anthropicChatV1,
2770
- "claude-3-haiku-20240307"
2771
2818
  )
2772
2819
  };
2773
2820
 
@@ -2776,7 +2823,13 @@ var xaiChatV1 = createOpenAiCompatibleConfiguration({
2776
2823
  key: "xai.chat.v1",
2777
2824
  provider: "xai.chat",
2778
2825
  endpoint: `https://api.x.ai/v1/chat/completions`,
2779
- apiKeyMapping: ["xAiApiKey", "XAI_API_KEY"]
2826
+ apiKeyMapping: ["xAiApiKey", "XAI_API_KEY"],
2827
+ // Per xAI docs (checked 2026-05-13), grok-4 errors if `reasoning_effort` is
2828
+ // sent; grok-3-mini and the fast-non-reasoning variants don't accept it
2829
+ // either. Only grok-4.3 and grok-4.20-multi-agent support it, and neither
2830
+ // has a shorthand here yet. Leave the predicate empty so effort is dropped
2831
+ // for every currently-shipped xAI shorthand.
2832
+ isReasoningModel: () => false
2780
2833
  });
2781
2834
  var xai = {
2782
2835
  "xai.chat.v1": xaiChatV1,
@@ -2784,7 +2837,8 @@ var xai = {
2784
2837
  "xai.grok-3": withDefaultModel(xaiChatV1, "grok-3"),
2785
2838
  "xai.grok-3-mini": withDefaultModel(xaiChatV1, "grok-3-mini"),
2786
2839
  "xai.grok-4": withDefaultModel(xaiChatV1, "grok-4"),
2787
- "xai.grok-4-fast": withDefaultModel(xaiChatV1, "grok-4-fast-non-reasoning")
2840
+ "xai.grok-4-fast": withDefaultModel(xaiChatV1, "grok-4-fast-non-reasoning"),
2841
+ "xai.grok-4-1-fast": withDefaultModel(xaiChatV1, "grok-4-1-fast-non-reasoning")
2788
2842
  };
2789
2843
 
2790
2844
  // src/llm/output/_utils/combineJsonl.ts
@@ -2891,7 +2945,11 @@ var ollama = {
2891
2945
  "ollama.llama3.3": withDefaultModel(ollamaChatV1, "llama3.3"),
2892
2946
  "ollama.llama3.2": withDefaultModel(ollamaChatV1, "llama3.2"),
2893
2947
  "ollama.llama3.1": withDefaultModel(ollamaChatV1, "llama3.1"),
2894
- "ollama.qwq": withDefaultModel(ollamaChatV1, "qwq")
2948
+ "ollama.qwq": withDefaultModel(ollamaChatV1, "qwq"),
2949
+ "ollama.gemma3": withDefaultModel(ollamaChatV1, "gemma3"),
2950
+ "ollama.mistral": withDefaultModel(ollamaChatV1, "mistral"),
2951
+ "ollama.qwen2.5": withDefaultModel(ollamaChatV1, "qwen2.5"),
2952
+ "ollama.qwen3": withDefaultModel(ollamaChatV1, "qwen3")
2895
2953
  };
2896
2954
 
2897
2955
  // src/llm/config/google/promptSanitizeMessageCallback.ts
@@ -3108,14 +3166,6 @@ var googleGeminiChatV1 = {
3108
3166
  };
3109
3167
  var google = {
3110
3168
  "google.chat.v1": googleGeminiChatV1,
3111
- "google.gemini-2.0-flash": withDefaultModel(
3112
- googleGeminiChatV1,
3113
- "gemini-2.0-flash"
3114
- ),
3115
- "google.gemini-2.0-flash-lite": withDefaultModel(
3116
- googleGeminiChatV1,
3117
- "gemini-2.0-flash-lite"
3118
- ),
3119
3169
  "google.gemini-2.5-flash": withDefaultModel(
3120
3170
  googleGeminiChatV1,
3121
3171
  "gemini-2.5-flash"
@@ -3124,13 +3174,22 @@ var google = {
3124
3174
  googleGeminiChatV1,
3125
3175
  "gemini-2.5-flash-lite"
3126
3176
  ),
3127
- "google.gemini-1.5-pro": withDefaultModel(
3128
- googleGeminiChatV1,
3129
- "gemini-1.5-pro"
3130
- ),
3131
3177
  "google.gemini-2.5-pro": withDefaultModel(
3132
3178
  googleGeminiChatV1,
3133
3179
  "gemini-2.5-pro"
3180
+ ),
3181
+ // Deprecated
3182
+ "google.gemini-2.0-flash": withDefaultModel(
3183
+ googleGeminiChatV1,
3184
+ "gemini-2.0-flash"
3185
+ ),
3186
+ "google.gemini-2.0-flash-lite": withDefaultModel(
3187
+ googleGeminiChatV1,
3188
+ "gemini-2.0-flash-lite"
3189
+ ),
3190
+ "google.gemini-1.5-pro": withDefaultModel(
3191
+ googleGeminiChatV1,
3192
+ "gemini-1.5-pro"
3134
3193
  )
3135
3194
  };
3136
3195
 
@@ -3143,7 +3202,9 @@ var deepseekChatV1 = createOpenAiCompatibleConfiguration({
3143
3202
  });
3144
3203
  var deepseek = {
3145
3204
  "deepseek.chat.v1": deepseekChatV1,
3146
- "deepseek.chat": withDefaultModel(deepseekChatV1, "deepseek-chat")
3205
+ "deepseek.chat": withDefaultModel(deepseekChatV1, "deepseek-chat"),
3206
+ "deepseek.v4-flash": withDefaultModel(deepseekChatV1, "deepseek-v4-flash"),
3207
+ "deepseek.v4-pro": withDefaultModel(deepseekChatV1, "deepseek-v4-pro")
3147
3208
  };
3148
3209
 
3149
3210
  // src/llm/config.ts
@@ -3258,12 +3319,19 @@ async function apiRequest(url, options) {
3258
3319
  const response = await fetch(url, finalOptions);
3259
3320
  if (!response.ok) {
3260
3321
  let message = `HTTP error. Status: ${response.status}. Error Message: ${response?.statusText || "Unknown error."}`;
3261
- if (url.startsWith("https://api.openai.com/")) {
3262
- try {
3263
- const body = await response.json();
3264
- message = `HTTP error. Status Code: ${response.status}. Error Message: ${body?.error?.message || "No further details provided."}`;
3265
- } catch (error) {
3322
+ try {
3323
+ const text = await response.text();
3324
+ if (text) {
3325
+ let detail = text;
3326
+ try {
3327
+ const body = JSON.parse(text);
3328
+ detail = body?.error?.message || body?.error || body?.message || text;
3329
+ if (typeof detail !== "string") detail = JSON.stringify(detail);
3330
+ } catch {
3331
+ }
3332
+ message = `HTTP error. Status Code: ${response.status}. Error Message: ${detail}`;
3266
3333
  }
3334
+ } catch {
3267
3335
  }
3268
3336
  throw new Error(message);
3269
3337
  }
@@ -4095,6 +4163,11 @@ var BasePrompt = class {
4095
4163
  return promptValue;
4096
4164
  }
4097
4165
  getReplacements(values) {
4166
+ if (values === void 0 || values === null) {
4167
+ throw new Error(
4168
+ "format() requires an input object. Did you forget to pass arguments?"
4169
+ );
4170
+ }
4098
4171
  const { input = "", ...restOfValues } = values;
4099
4172
  const replacements = Object.assign(
4100
4173
  {},
@@ -4107,11 +4180,11 @@ var BasePrompt = class {
4107
4180
  return replacements;
4108
4181
  }
4109
4182
  /**
4110
- * validate description
4111
- * @return {boolean} Returns false if the template is not valid.
4183
+ * Validates the prompt structure.
4184
+ * @return {boolean} Returns false if the prompt has no messages defined.
4112
4185
  */
4113
4186
  validate() {
4114
- return true;
4187
+ return this.messages.length > 0;
4115
4188
  }
4116
4189
  };
4117
4190
 
@@ -4708,14 +4781,6 @@ var ChatPrompt = class extends BasePrompt {
4708
4781
  }
4709
4782
  return messagesOut;
4710
4783
  }
4711
- /**
4712
- * validate Ensures there are not unresolved tokens in prompt.
4713
- * @TODO Make this work!
4714
- * @return Returns false if the template is not valid.
4715
- */
4716
- validate() {
4717
- return true;
4718
- }
4719
4784
  };
4720
4785
 
4721
4786
  // src/prompt/_functions.ts
@@ -4742,10 +4807,12 @@ var BaseStateItem = class {
4742
4807
  this.initialValue = initialValue;
4743
4808
  }
4744
4809
  setValue(value) {
4745
- assert(
4746
- typeof value === typeof this.value,
4747
- `Invalid value type. Expected ${typeof this.value}, received ${typeof value}`
4748
- );
4810
+ if (this.value !== void 0 && this.value !== null) {
4811
+ assert(
4812
+ typeof value === typeof this.value,
4813
+ `Invalid value type. Expected ${typeof this.value}, received ${typeof value}`
4814
+ );
4815
+ }
4749
4816
  this.value = value;
4750
4817
  }
4751
4818
  getKey() {
@@ -4881,11 +4948,42 @@ var Dialogue = class extends BaseStateItem {
4881
4948
  return this;
4882
4949
  }
4883
4950
  setMessageTurn(userMessage, assistantMessage, systemMessage = "") {
4951
+ this.setSystemMessage(systemMessage);
4884
4952
  this.setUserMessage(userMessage);
4885
4953
  this.setAssistantMessage(assistantMessage);
4886
- this.setSystemMessage(systemMessage);
4887
4954
  return this;
4888
4955
  }
4956
+ /**
4957
+ * Aliases using `add*` naming to match ChatPrompt's API.
4958
+ * These delegate to the corresponding `set*` methods.
4959
+ */
4960
+ addUserMessage(content, name) {
4961
+ return this.setUserMessage(content, name);
4962
+ }
4963
+ addAssistantMessage(content) {
4964
+ return this.setAssistantMessage(content);
4965
+ }
4966
+ addSystemMessage(content) {
4967
+ return this.setSystemMessage(content);
4968
+ }
4969
+ addToolMessage(content, name, id) {
4970
+ return this.setToolMessage(content, name, id);
4971
+ }
4972
+ addToolCallMessage(input) {
4973
+ return this.setToolCallMessage(input);
4974
+ }
4975
+ addFunctionMessage(content, name, id) {
4976
+ return this.setFunctionMessage(content, name, id);
4977
+ }
4978
+ addFunctionCallMessage(input) {
4979
+ return this.setFunctionCallMessage(input);
4980
+ }
4981
+ addMessageTurn(userMessage, assistantMessage, systemMessage = "") {
4982
+ return this.setMessageTurn(userMessage, assistantMessage, systemMessage);
4983
+ }
4984
+ addHistory(messages) {
4985
+ return this.setHistory(messages);
4986
+ }
4889
4987
  setHistory(messages) {
4890
4988
  for (const message of messages) {
4891
4989
  switch (message?.role) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-exe",
3
- "version": "2.3.5",
3
+ "version": "2.3.6",
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",
package/readme.md CHANGED
@@ -104,11 +104,34 @@ const parser = createCustomParser("MyUppercaseParser", (output, input) => {
104
104
 
105
105
  #### State
106
106
 
107
+ Manage conversation history and structured data across LLM calls:
108
+
107
109
  ```ts
110
+ import { createState, createDialogue, createStateItem } from "llm-exe";
111
+
112
+ // Create a state container
113
+ const state = createState();
114
+
115
+ // Dialogues — store conversation history
116
+ const chat = state.createDialogue("chat");
117
+ chat.setUserMessage("Hi");
118
+ chat.setAssistantMessage("Hello!");
119
+ chat.getHistory(); // returns message array
120
+
121
+ // Standalone dialogue (without state)
108
122
  const dialogue = createDialogue("chat");
109
123
  dialogue.setUserMessage("Hi");
110
- dialogue.setAssistantMessage("Hello!");
111
- dialogue.getHistory(); // returns chat array
124
+
125
+ // Context items — typed values with get/set/reset
126
+ const intent = createStateItem("userIntent", "unknown");
127
+ state.createContextItem(intent);
128
+ intent.setValue("booking");
129
+ intent.getValue(); // "booking"
130
+ intent.resetValue(); // resets to "unknown"
131
+
132
+ // Attributes — simple key-value metadata
133
+ state.setAttribute("userId", "abc-123");
134
+ state.attributes["userId"]; // "abc-123"
112
135
  ```
113
136
 
114
137
  #### Hooks