llm-exe 3.0.0-beta.1 → 3.0.0-beta.3

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
@@ -1200,9 +1200,12 @@ var BOOLEAN_VALUES = ["true", "false", "yes", "no", "y", "n", "1", "0"];
1200
1200
  var TRUTHY_VALUES = /* @__PURE__ */ new Set(["true", "yes", "y", "1"]);
1201
1201
  var FALSY_VALUES = /* @__PURE__ */ new Set(["false", "no", "n", "0"]);
1202
1202
  var MAX_ERROR_INPUT_EXCERPT_LENGTH = 500;
1203
+ var BOOLEAN_TOKEN_PATTERN = /(?<![\p{L}\p{N}])(?:true|false|yes|no|y|n|1|0)(?![\p{L}\p{N}])/giu;
1203
1204
  var BooleanParser = class extends BaseParser {
1204
- constructor() {
1205
+ constructor(options) {
1205
1206
  super("boolean");
1207
+ __publicField(this, "match");
1208
+ this.match = options?.match ?? "exact";
1206
1209
  }
1207
1210
  getInputErrorContext(text) {
1208
1211
  const context = {
@@ -1250,6 +1253,36 @@ var BooleanParser = class extends BaseParser {
1250
1253
  if (FALSY_VALUES.has(clean)) {
1251
1254
  return false;
1252
1255
  }
1256
+ if (this.match === "extract") {
1257
+ const matches = Array.from(
1258
+ text.toLowerCase().matchAll(BOOLEAN_TOKEN_PATTERN)
1259
+ ).map((match) => match[0]);
1260
+ const values = Array.from(
1261
+ new Set(
1262
+ matches.map((match) => {
1263
+ if (TRUTHY_VALUES.has(match)) return true;
1264
+ return false;
1265
+ })
1266
+ )
1267
+ );
1268
+ if (values.length === 1) {
1269
+ return values[0];
1270
+ }
1271
+ if (values.length > 1) {
1272
+ throw new LlmExeError(`Multiple boolean values found in input.`, {
1273
+ code: "parser.parse_failed",
1274
+ context: {
1275
+ operation: "BooleanParser.parse",
1276
+ parser: "boolean",
1277
+ reason: "ambiguous_boolean",
1278
+ expected: "one boolean value",
1279
+ match: this.match,
1280
+ matchCount: values.length,
1281
+ ...this.getInputErrorContext(text)
1282
+ }
1283
+ });
1284
+ }
1285
+ }
1253
1286
  throw new LlmExeError(`No boolean value found in input.`, {
1254
1287
  code: "parser.parse_failed",
1255
1288
  context: {
@@ -1257,6 +1290,7 @@ var BooleanParser = class extends BaseParser {
1257
1290
  parser: "boolean",
1258
1291
  reason: "unrecognized_boolean",
1259
1292
  expected: BOOLEAN_VALUES,
1293
+ match: this.match,
1260
1294
  ...this.getInputErrorContext(text)
1261
1295
  }
1262
1296
  });
@@ -1281,7 +1315,7 @@ function toNumber(value) {
1281
1315
 
1282
1316
  // src/parser/parsers/NumberParser.ts
1283
1317
  var NUMERIC_TOKEN_PATTERN = /(^|[^\w.,])([+-]?(?:(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?|\.\d+)(?:[eE][+-]?\d+)?)(?![\w,])/g;
1284
- var WHOLE_NUMERIC_PATTERN = /^[+-]?(?:(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?|\.\d+)(?:[eE][+-]?\d+)?$/;
1318
+ var EXACT_NUMERIC_PATTERN = /^[+-]?(?:(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?|\.\d+)(?:[eE][+-]?\d+)?$/;
1285
1319
  var NumberParser = class extends BaseParser {
1286
1320
  constructor(options) {
1287
1321
  super("number");
@@ -1316,16 +1350,16 @@ var NumberParser = class extends BaseParser {
1316
1350
  }
1317
1351
  });
1318
1352
  }
1319
- if (this.match === "whole") {
1353
+ if (this.match === "exact") {
1320
1354
  const trimmed = text.trim();
1321
- if (!WHOLE_NUMERIC_PATTERN.test(trimmed)) {
1322
- throw new LlmExeError(`Input is not a whole numeric value.`, {
1355
+ if (!EXACT_NUMERIC_PATTERN.test(trimmed)) {
1356
+ throw new LlmExeError(`Input is not an exact numeric value.`, {
1323
1357
  code: "parser.parse_failed",
1324
1358
  context: {
1325
1359
  operation: "NumberParser.parse",
1326
1360
  parser: "number",
1327
1361
  reason: "no_numeric_value",
1328
- expected: "whole number",
1362
+ expected: "exact number",
1329
1363
  match: this.match,
1330
1364
  inputLength: text.length
1331
1365
  }
@@ -1592,9 +1626,11 @@ function isPlainObject2(value) {
1592
1626
  const proto = Object.getPrototypeOf(value);
1593
1627
  return proto === Object.prototype || proto === null;
1594
1628
  }
1595
- function normalizeWholeResponseJsonText(input) {
1629
+ function normalizeExactResponseJsonText(input) {
1596
1630
  const trimmed = input.trim();
1597
- const fenceMatch = trimmed.match(/^```([a-zA-Z]*)[^\S\r\n]*\r?\n([\s\S]*)```$/);
1631
+ const fenceMatch = trimmed.match(
1632
+ /^```([a-zA-Z]*)[^\S\r\n]*\r?\n([\s\S]*)```$/
1633
+ );
1598
1634
  if (!fenceMatch) {
1599
1635
  return trimmed;
1600
1636
  }
@@ -1604,19 +1640,82 @@ function normalizeWholeResponseJsonText(input) {
1604
1640
  }
1605
1641
  return body.trim();
1606
1642
  }
1643
+ function findBalancedJsonEnd(input, start) {
1644
+ const first = input[start];
1645
+ const stack = first === "{" ? ["}"] : first === "[" ? ["]"] : [];
1646
+ let inString = false;
1647
+ let escaping = false;
1648
+ for (let index = start + 1; index < input.length; index += 1) {
1649
+ const char = input[index];
1650
+ if (inString) {
1651
+ if (escaping) {
1652
+ escaping = false;
1653
+ } else if (char === "\\") {
1654
+ escaping = true;
1655
+ } else if (char === '"') {
1656
+ inString = false;
1657
+ }
1658
+ continue;
1659
+ }
1660
+ if (char === '"') {
1661
+ inString = true;
1662
+ continue;
1663
+ }
1664
+ if (char === "{" || char === "[") {
1665
+ stack.push(char === "{" ? "}" : "]");
1666
+ continue;
1667
+ }
1668
+ if (char !== "}" && char !== "]") {
1669
+ continue;
1670
+ }
1671
+ if (stack[stack.length - 1] !== char) {
1672
+ return void 0;
1673
+ }
1674
+ stack.pop();
1675
+ if (stack.length === 0) {
1676
+ return index;
1677
+ }
1678
+ }
1679
+ return void 0;
1680
+ }
1681
+ function extractJsonCandidates(input) {
1682
+ const candidates = [];
1683
+ for (let index = 0; index < input.length; index += 1) {
1684
+ const char = input[index];
1685
+ if (char !== "{" && char !== "[") {
1686
+ continue;
1687
+ }
1688
+ const end = findBalancedJsonEnd(input, index);
1689
+ if (end === void 0) {
1690
+ continue;
1691
+ }
1692
+ const candidate = input.slice(index, end + 1);
1693
+ try {
1694
+ candidates.push({
1695
+ parsed: JSON.parse(candidate)
1696
+ });
1697
+ index = end;
1698
+ } catch {
1699
+ }
1700
+ }
1701
+ return candidates;
1702
+ }
1607
1703
  var JsonParser = class extends BaseParserWithJson {
1608
1704
  constructor(options = {}) {
1609
1705
  super("json", options);
1610
1706
  __publicField(this, "shouldValidateSchema");
1707
+ __publicField(this, "match");
1708
+ this.match = options.match ?? "exact";
1611
1709
  this.shouldValidateSchema = !!options.schema && options.validateSchema !== false;
1612
1710
  }
1613
1711
  /**
1614
1712
  * v3 parser contract:
1615
1713
  * Category: strict
1616
- * Mode: whole-output
1714
+ * Mode: exact
1617
1715
  *
1618
- * Parses strict JSON object/array output only. Invalid JSON, empty input,
1619
- * JSON primitives, and non-plain runtime objects throw typed parser errors.
1716
+ * Parses strict JSON object/array output by default. Pass match: "extract"
1717
+ * to extract one JSON object or array from surrounding text. Invalid JSON,
1718
+ * empty input, JSON primitives, and non-plain runtime objects throw typed parser errors.
1620
1719
  * Schema validation is on by default when a schema is provided unless
1621
1720
  * validateSchema: false is explicitly set.
1622
1721
  *
@@ -1639,19 +1738,52 @@ var JsonParser = class extends BaseParserWithJson {
1639
1738
  });
1640
1739
  }
1641
1740
  try {
1642
- parsed = JSON.parse(normalizeWholeResponseJsonText(text));
1741
+ parsed = JSON.parse(normalizeExactResponseJsonText(text));
1643
1742
  } catch (cause) {
1644
- throw new LlmExeError(`Invalid JSON input.`, {
1645
- code: "parser.parse_failed",
1646
- context: {
1647
- operation: "JsonParser.parse",
1648
- parser: "json",
1649
- reason: "invalid_json",
1650
- expected: "JSON object or array",
1651
- inputLength
1652
- },
1653
- cause
1654
- });
1743
+ if (this.match === "extract") {
1744
+ const candidates = extractJsonCandidates(text);
1745
+ if (candidates.length === 1) {
1746
+ parsed = candidates[0].parsed;
1747
+ } else if (candidates.length > 1) {
1748
+ throw new LlmExeError(`Multiple JSON values found in input.`, {
1749
+ code: "parser.parse_failed",
1750
+ context: {
1751
+ operation: "JsonParser.parse",
1752
+ parser: "json",
1753
+ reason: "ambiguous_json_match",
1754
+ expected: "one JSON object or array",
1755
+ match: this.match,
1756
+ inputLength,
1757
+ matchCount: candidates.length
1758
+ }
1759
+ });
1760
+ } else {
1761
+ throw new LlmExeError(`No JSON value found in input.`, {
1762
+ code: "parser.parse_failed",
1763
+ context: {
1764
+ operation: "JsonParser.parse",
1765
+ parser: "json",
1766
+ reason: "no_json_value",
1767
+ expected: "JSON object or array",
1768
+ match: this.match,
1769
+ inputLength
1770
+ },
1771
+ cause
1772
+ });
1773
+ }
1774
+ } else {
1775
+ throw new LlmExeError(`Invalid JSON input.`, {
1776
+ code: "parser.parse_failed",
1777
+ context: {
1778
+ operation: "JsonParser.parse",
1779
+ parser: "json",
1780
+ reason: "invalid_json",
1781
+ expected: "JSON object or array",
1782
+ inputLength
1783
+ },
1784
+ cause
1785
+ });
1786
+ }
1655
1787
  }
1656
1788
  } else if (Array.isArray(text) || isPlainObject2(text)) {
1657
1789
  parsed = text;
@@ -2922,7 +3054,7 @@ var StringExtractParser = class extends BaseParser {
2922
3054
  * Mode: configured value extraction
2923
3055
  *
2924
3056
  * Returns the single configured enum value found in input. Matching is
2925
- * word-bounded by default; pass match: "whole" to require exact input or
3057
+ * word-bounded by default; pass match: "exact" to require exact input or
2926
3058
  * match: "substring" for legacy contains() behavior. Case-insensitive by
2927
3059
  * default.
2928
3060
  *
@@ -3004,8 +3136,8 @@ var StringExtractParser = class extends BaseParser {
3004
3136
  }
3005
3137
  findMatches(text) {
3006
3138
  switch (this.match) {
3007
- case "whole":
3008
- return this.matchWhole(text);
3139
+ case "exact":
3140
+ return this.matchExact(text);
3009
3141
  case "substring":
3010
3142
  return this.matchSubstring(text);
3011
3143
  case "word":
@@ -3013,7 +3145,7 @@ var StringExtractParser = class extends BaseParser {
3013
3145
  return this.matchWord(text);
3014
3146
  }
3015
3147
  }
3016
- matchWhole(text) {
3148
+ matchExact(text) {
3017
3149
  const candidate = this.ignoreCase ? text.trim().toLowerCase() : text.trim();
3018
3150
  return this.enum.filter((option) => {
3019
3151
  if (option === "") return false;
@@ -3063,36 +3195,33 @@ function createParser(...args) {
3063
3195
  case "replaceStringTemplate":
3064
3196
  return new ReplaceStringTemplateParser();
3065
3197
  case "boolean":
3066
- return new BooleanParser();
3198
+ return new BooleanParser(options);
3067
3199
  case "number":
3068
3200
  return new NumberParser(options);
3069
3201
  case "string":
3070
3202
  return new StringParser();
3071
3203
  default:
3072
- throw new LlmExeError(
3073
- `Invalid parser type: "${type}"`,
3074
- {
3075
- code: "parser.invalid_type",
3076
- context: {
3077
- operation: "createParser",
3078
- parser: type,
3079
- availableParsers: [
3080
- "json",
3081
- "string",
3082
- "boolean",
3083
- "number",
3084
- "stringExtract",
3085
- "listToArray",
3086
- "listToJson",
3087
- "listToKeyValue",
3088
- "replaceStringTemplate",
3089
- "markdownCodeBlock",
3090
- "markdownCodeBlocks"
3091
- ],
3092
- resolution: "Use a registered parser type, or define a custom parser."
3093
- }
3204
+ throw new LlmExeError(`Invalid parser type: "${type}"`, {
3205
+ code: "parser.invalid_type",
3206
+ context: {
3207
+ operation: "createParser",
3208
+ parser: type,
3209
+ availableParsers: [
3210
+ "json",
3211
+ "string",
3212
+ "boolean",
3213
+ "number",
3214
+ "stringExtract",
3215
+ "listToArray",
3216
+ "listToJson",
3217
+ "listToKeyValue",
3218
+ "replaceStringTemplate",
3219
+ "markdownCodeBlock",
3220
+ "markdownCodeBlocks"
3221
+ ],
3222
+ resolution: "Use a registered parser type, or define a custom parser."
3094
3223
  }
3095
- );
3224
+ });
3096
3225
  }
3097
3226
  }
3098
3227
  function createCustomParser(name, parserFn) {
@@ -4157,6 +4286,20 @@ function anthropicPromptSanitize(_messages, _inputBodyObj, _outputObj) {
4157
4286
  return mergeConsecutiveSameRole(result);
4158
4287
  }
4159
4288
 
4289
+ // src/llm/output/_utils/getBedrockTokenCounts.ts
4290
+ function getBedrockTokenCounts(headers) {
4291
+ if (!headers) return void 0;
4292
+ const input = parseInt(headers["x-amzn-bedrock-input-token-count"], 10);
4293
+ const output = parseInt(headers["x-amzn-bedrock-output-token-count"], 10);
4294
+ if (!Number.isFinite(input) && !Number.isFinite(output)) {
4295
+ return void 0;
4296
+ }
4297
+ return {
4298
+ input_tokens: Number.isFinite(input) ? input : 0,
4299
+ output_tokens: Number.isFinite(output) ? output : 0
4300
+ };
4301
+ }
4302
+
4160
4303
  // src/llm/output/claude.ts
4161
4304
  function formatResult2(response) {
4162
4305
  const content = response?.content || [];
@@ -4179,15 +4322,18 @@ function formatResult2(response) {
4179
4322
  }
4180
4323
  return out;
4181
4324
  }
4182
- function OutputAnthropicClaude3Chat(result, _config) {
4325
+ function OutputAnthropicClaude3Chat(result, _config, headers) {
4183
4326
  const id = result.id;
4184
4327
  const name = result.model || _config?.options.model?.default || "anthropic.unknown";
4185
4328
  const stopReason = result.stop_reason;
4186
4329
  const content = formatResult2(result);
4330
+ const headerUsage = getBedrockTokenCounts(headers);
4331
+ const input_tokens = result?.usage?.input_tokens ?? headerUsage?.input_tokens ?? 0;
4332
+ const output_tokens = result?.usage?.output_tokens ?? headerUsage?.output_tokens ?? 0;
4187
4333
  const usage = {
4188
- input_tokens: result?.usage?.input_tokens,
4189
- output_tokens: result?.usage?.output_tokens,
4190
- total_tokens: result?.usage?.input_tokens + result?.usage?.output_tokens
4334
+ input_tokens,
4335
+ output_tokens,
4336
+ total_tokens: input_tokens + output_tokens
4191
4337
  };
4192
4338
  return {
4193
4339
  id,
@@ -4200,7 +4346,7 @@ function OutputAnthropicClaude3Chat(result, _config) {
4200
4346
  }
4201
4347
 
4202
4348
  // src/llm/output/llama.ts
4203
- function OutputMetaLlama3Chat(result, _config) {
4349
+ function OutputMetaLlama3Chat(result, _config, headers) {
4204
4350
  const id = uuidv4();
4205
4351
  const name = _config?.options?.model?.default || "meta";
4206
4352
  const created = (/* @__PURE__ */ new Date()).getTime();
@@ -4208,10 +4354,13 @@ function OutputMetaLlama3Chat(result, _config) {
4208
4354
  const content = [
4209
4355
  { type: "text", text: result.generation }
4210
4356
  ];
4357
+ const headerUsage = getBedrockTokenCounts(headers);
4358
+ const output_tokens = result?.generation_token_count ?? headerUsage?.output_tokens ?? 0;
4359
+ const input_tokens = result?.prompt_token_count ?? headerUsage?.input_tokens ?? 0;
4211
4360
  const usage = {
4212
- output_tokens: result?.generation_token_count,
4213
- input_tokens: result?.prompt_token_count,
4214
- total_tokens: result?.generation_token_count + result?.prompt_token_count
4361
+ output_tokens,
4362
+ input_tokens,
4363
+ total_tokens: output_tokens + input_tokens
4215
4364
  };
4216
4365
  return {
4217
4366
  id,
@@ -4329,7 +4478,7 @@ var bedrock = {
4329
4478
 
4330
4479
  // src/llm/config/anthropic/index.ts
4331
4480
  var ANTHROPIC_VERSION = "2023-06-01";
4332
- var MODELS_REJECTING_SAMPLING_PARAMS = ["claude-opus-4-7"];
4481
+ var MODELS_REJECTING_SAMPLING_PARAMS = ["claude-opus-4-7", "claude-opus-4-8"];
4333
4482
  var isClaude4x = (model) => /^claude-(opus|sonnet|haiku)-4-/.test(model);
4334
4483
  var dropIfModelRejectsSamplingParams = (v, body) => MODELS_REJECTING_SAMPLING_PARAMS.includes(body.model) ? void 0 : v;
4335
4484
  var topPTransform = (v, body) => {
@@ -4414,6 +4563,11 @@ var anthropicChatV1 = {
4414
4563
  };
4415
4564
  var anthropic = {
4416
4565
  "anthropic.chat.v1": anthropicChatV1,
4566
+ // Claude 4.8 models
4567
+ "anthropic.claude-opus-4-8": withDefaultModel(
4568
+ anthropicChatV1,
4569
+ "claude-opus-4-8"
4570
+ ),
4417
4571
  // Claude 4.7 models
4418
4572
  "anthropic.claude-opus-4-7": withDefaultModel(
4419
4573
  anthropicChatV1,
@@ -4597,7 +4751,11 @@ var ollamaChatV1 = {
4597
4751
  provider: "ollama.chat",
4598
4752
  endpoint: `${getEnvironmentVariable("OLLAMA_ENDPOINT") || `http://localhost:11434`}/api/chat`,
4599
4753
  options: {
4600
- prompt: {}
4754
+ prompt: {},
4755
+ temperature: {},
4756
+ topP: {},
4757
+ maxTokens: {},
4758
+ stopSequences: {}
4601
4759
  },
4602
4760
  method: "POST",
4603
4761
  headers: `{"Content-Type": "application/json" }`,
@@ -4613,6 +4771,18 @@ var ollamaChatV1 = {
4613
4771
  },
4614
4772
  model: {
4615
4773
  key: "model"
4774
+ },
4775
+ temperature: {
4776
+ key: "options.temperature"
4777
+ },
4778
+ topP: {
4779
+ key: "options.top_p"
4780
+ },
4781
+ maxTokens: {
4782
+ key: "options.num_predict"
4783
+ },
4784
+ stopSequences: {
4785
+ key: "options.stop"
4616
4786
  }
4617
4787
  },
4618
4788
  transformResponse: OutputOllamaChat
@@ -4808,7 +4978,10 @@ var googleGeminiChatV1 = {
4808
4978
  options: {
4809
4979
  effort: {},
4810
4980
  prompt: {},
4811
- // topP: {},
4981
+ temperature: {},
4982
+ topP: {},
4983
+ maxTokens: {},
4984
+ stopSequences: {},
4812
4985
  geminiApiKey: {
4813
4986
  default: getEnvironmentVariable("GEMINI_API_KEY")
4814
4987
  }
@@ -4820,6 +4993,18 @@ var googleGeminiChatV1 = {
4820
4993
  key: "contents",
4821
4994
  transform: googleGeminiPromptSanitize
4822
4995
  },
4996
+ temperature: {
4997
+ key: "generationConfig.temperature"
4998
+ },
4999
+ topP: {
5000
+ key: "generationConfig.topP"
5001
+ },
5002
+ maxTokens: {
5003
+ key: "generationConfig.maxOutputTokens"
5004
+ },
5005
+ stopSequences: {
5006
+ key: "generationConfig.stopSequences"
5007
+ },
4823
5008
  effort: {
4824
5009
  key: "config.thinkingConfig.thinkingBudget",
4825
5010
  transform: (v, _s) => {
@@ -4961,6 +5146,13 @@ function isValidUrl(input) {
4961
5146
  }
4962
5147
 
4963
5148
  // src/utils/modules/request.ts
5149
+ function headersToRecord(headers) {
5150
+ const record = {};
5151
+ headers.forEach((value, key) => {
5152
+ record[key.toLowerCase()] = value;
5153
+ });
5154
+ return record;
5155
+ }
4964
5156
  async function apiRequest(url, options) {
4965
5157
  const finalOptions = {
4966
5158
  ...options
@@ -5046,13 +5238,16 @@ async function apiRequest(url, options) {
5046
5238
  });
5047
5239
  }
5048
5240
  const contentType = response.headers.get("content-type");
5241
+ const headers = headersToRecord(response.headers);
5049
5242
  if (contentType?.includes("application/json")) {
5050
- const responseData = await response.json();
5051
- return responseData;
5052
- } else {
5053
- const responseData = await response.text();
5054
- return responseData;
5243
+ const responseData2 = await response.json();
5244
+ return { data: responseData2, headers };
5055
5245
  }
5246
+ const responseData = await response.text();
5247
+ return {
5248
+ data: responseData,
5249
+ headers
5250
+ };
5056
5251
  }
5057
5252
 
5058
5253
  // src/utils/modules/convertDotNotation.ts
@@ -5424,7 +5619,7 @@ async function useLlm_call(state, messages, _options) {
5424
5619
  }
5425
5620
  ]
5426
5621
  };
5427
- return BaseLlmOutput(transformResponse(mockResponse, config));
5622
+ return BaseLlmOutput(transformResponse(mockResponse, config, {}));
5428
5623
  }
5429
5624
  try {
5430
5625
  const response = await apiRequest(url, {
@@ -5432,7 +5627,9 @@ async function useLlm_call(state, messages, _options) {
5432
5627
  body,
5433
5628
  headers
5434
5629
  });
5435
- return BaseLlmOutput(transformResponse(response, config));
5630
+ return BaseLlmOutput(
5631
+ transformResponse(response.data, config, response.headers)
5632
+ );
5436
5633
  } catch (e) {
5437
5634
  if (!isLlmExeError(e, "request.http_error")) throw e;
5438
5635
  const ctx = e.context ?? {};
@@ -5613,7 +5810,9 @@ var embeddingConfigs = {
5613
5810
  "openai.embedding.v1": {
5614
5811
  key: "openai.embedding.v1",
5615
5812
  provider: "openai.embedding",
5616
- endpoint: `https://api.openai.com/v1/embeddings`,
5813
+ // Templated host: `baseUrl` defaults to OpenAI. Override to point at any
5814
+ // OpenAI-compatible embeddings server (Baseten, vLLM, TEI, Together, etc.).
5815
+ endpoint: `{{baseUrl}}/embeddings`,
5617
5816
  method: "POST",
5618
5817
  headers: `{"Authorization":"Bearer {{openAiApiKey}}", "Content-Type": "application/json" }`,
5619
5818
  options: {
@@ -5624,6 +5823,9 @@ var embeddingConfigs = {
5624
5823
  encodingFormat: {},
5625
5824
  openAiApiKey: {
5626
5825
  default: getEnvironmentVariable("OPENAI_API_KEY")
5826
+ },
5827
+ baseUrl: {
5828
+ default: "https://api.openai.com/v1"
5627
5829
  }
5628
5830
  },
5629
5831
  mapBody: {
@@ -5810,15 +6012,34 @@ function AmazonTitanEmbedding(result, config) {
5810
6012
  }
5811
6013
 
5812
6014
  // src/embedding/output/CohereBedrockEmbedding.ts
5813
- function CohereBedrockEmbedding(result, config) {
6015
+ function resolveEmbeddings(embeddings, model) {
6016
+ if (Array.isArray(embeddings)) {
6017
+ return embeddings;
6018
+ }
6019
+ if (embeddings && typeof embeddings === "object") {
6020
+ if (Array.isArray(embeddings.float)) {
6021
+ return embeddings.float;
6022
+ }
6023
+ throw new Error(
6024
+ `Unexpected embeddings in Cohere Bedrock response (model: "${model}"). Expected float embeddings, received object with keys: [${Object.keys(embeddings).join(", ")}]`
6025
+ );
6026
+ }
6027
+ throw new Error(
6028
+ `Unexpected embeddings shape in Cohere Bedrock response (model: "${model}"). Expected an array (Embed v3) or an object with float embeddings (Embed v4), received: ${embeddings === null ? "null" : typeof embeddings}`
6029
+ );
6030
+ }
6031
+ function CohereBedrockEmbedding(result, config, headers) {
6032
+ const headerUsage = getBedrockTokenCounts(headers);
5814
6033
  const __result = deepClone(result);
5815
6034
  const model = config.model || "cohere.unknown";
5816
6035
  const created = (/* @__PURE__ */ new Date()).getTime();
5817
- const embedding = Array.isArray(__result.embeddings) ? __result.embeddings : [];
6036
+ const embedding = resolveEmbeddings(__result.embeddings, model);
6037
+ const input_tokens = headerUsage?.input_tokens ?? 0;
6038
+ const output_tokens = headerUsage?.output_tokens ?? 0;
5818
6039
  const usage = {
5819
- output_tokens: 0,
5820
- input_tokens: 0,
5821
- total_tokens: 0
6040
+ output_tokens,
6041
+ input_tokens,
6042
+ total_tokens: input_tokens + output_tokens
5822
6043
  };
5823
6044
  return BaseEmbeddingOutput({
5824
6045
  id: __result.id,
@@ -5850,14 +6071,14 @@ function OpenAiEmbedding(result, config) {
5850
6071
  }
5851
6072
 
5852
6073
  // src/embedding/output/getEmbeddingOutputParser.ts
5853
- function getEmbeddingOutputParser(config, response) {
6074
+ function getEmbeddingOutputParser(config, response, headers) {
5854
6075
  switch (config.key) {
5855
6076
  case "openai.embedding.v1":
5856
6077
  return OpenAiEmbedding(response, config);
5857
6078
  case "amazon.embedding.v1":
5858
6079
  return AmazonTitanEmbedding(response, config);
5859
6080
  case "amazon:cohere.embedding.v1":
5860
- return CohereBedrockEmbedding(response, config);
6081
+ return CohereBedrockEmbedding(response, config, headers);
5861
6082
  default:
5862
6083
  throw new LlmExeError("Unsupported provider", {
5863
6084
  code: "embedding.invalid_response_shape",
@@ -5894,7 +6115,7 @@ async function createEmbedding_call(state, _input, _options) {
5894
6115
  body,
5895
6116
  headers
5896
6117
  });
5897
- return getEmbeddingOutputParser(state, request);
6118
+ return getEmbeddingOutputParser(state, request.data, request.headers);
5898
6119
  } catch (e) {
5899
6120
  if (!isLlmExeError(e, "request.http_error")) throw e;
5900
6121
  const ctx = e.context ?? {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "llm-exe",
3
- "version": "3.0.0-beta.1",
3
+ "version": "3.0.0-beta.3",
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
@@ -9,7 +9,7 @@ A package that provides simplified base components to make building and maintain
9
9
  - Write functions powered by LLM's with easy to use building blocks.
10
10
  - Pure Javascript and Typescript. Allows you to pass and infer types.
11
11
  - Supercharge your prompts by using handlebars within prompt template.
12
- - Support for text-based (llama-3) and chat-based prompts. (gpt-4o, claude-3.5, grok-3, Gemini, Bedrock, Ollama, etc)
12
+ - Support for text-based and chat-based prompts. (ChatGPT, Claude, Grok, Gemini, Bedrock, Ollama, etc)
13
13
  - Call LLM's from different providers without changing your code. (OpenAi/Anthropic/xAI/Google/AWS Bedrock/Ollama/Deepseek)
14
14
  - Allow LLM's to call functions (or call other LLM executors).
15
15
  - Not very opinionated. You have control on how you use it.
@@ -85,7 +85,7 @@ Welcome back!
85
85
  ```ts
86
86
  createParser("string"); // pass-through, returns string
87
87
  createParser("json", { schema }); // JSON with optional schema validation
88
- createParser("boolean"); // extracts boolean from response
88
+ createParser("boolean"); // parses a boolean token
89
89
  createParser("number"); // extracts number from response
90
90
  createParser("stringExtract", { enum: ["yes", "no"] }); // match one of the enum values
91
91
  createParser("listToArray"); // newline-separated list → string[]