nuabase 2.3.4 → 2.3.7

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 (66) hide show
  1. package/README.md +53 -2
  2. package/dist/cjs/chunks/logger-Co5uZG0A.cjs +90 -0
  3. package/dist/cjs/index.cjs +1516 -1380
  4. package/dist/cjs/local-agent.cjs +1043 -0
  5. package/dist/esm/chunks/logger-DnkHzkX_.mjs +88 -0
  6. package/dist/esm/index.mjs +1516 -1380
  7. package/dist/esm/local-agent.mjs +1041 -0
  8. package/dist/types/__tests__/helpers/client-factory.d.ts +33 -11
  9. package/dist/types/__tests__/helpers/index.d.ts +2 -1
  10. package/dist/types/backend/direct.d.ts +19 -7
  11. package/dist/types/backend/types.d.ts +7 -3
  12. package/dist/types/index.d.cts +3 -2
  13. package/dist/types/index.d.mts +3 -2
  14. package/dist/types/local-agent.d.cts +2 -0
  15. package/dist/types/local-agent.d.mts +2 -0
  16. package/dist/types/nua-llm-core/index.d.ts +23 -0
  17. package/dist/types/nua-llm-core/lib/error-utils.d.ts +6 -0
  18. package/dist/types/nua-llm-core/lib/logger.d.ts +44 -0
  19. package/dist/types/nua-llm-core/lib/nua-errors.d.ts +12 -0
  20. package/dist/types/nua-llm-core/lib/prompt-template-utils.d.ts +2 -0
  21. package/dist/types/nua-llm-core/lib/schema-utils.d.ts +1 -0
  22. package/dist/types/nua-llm-core/lib/stable-stringify.d.ts +1 -0
  23. package/dist/types/nua-llm-core/lib/strict-json-schema.d.ts +7 -0
  24. package/dist/types/nua-llm-core/lib/types.d.ts +8 -0
  25. package/dist/types/nua-llm-core/lib/validate-mappable-input-data.d.ts +4 -0
  26. package/dist/types/nua-llm-core/local-agent.d.ts +7 -0
  27. package/dist/types/nua-llm-core/modules/agent/agent-loop.d.ts +10 -0
  28. package/dist/types/nua-llm-core/modules/agent/agent-turn.d.ts +17 -0
  29. package/dist/types/nua-llm-core/modules/agent/provider-formatters.d.ts +22 -0
  30. package/dist/types/nua-llm-core/modules/agent/run-agent.d.ts +7 -0
  31. package/dist/types/nua-llm-core/modules/agent/types.d.ts +90 -0
  32. package/dist/types/nua-llm-core/modules/cast/cast-request.d.ts +32 -0
  33. package/dist/types/nua-llm-core/modules/cast/envelope.d.ts +9 -0
  34. package/dist/types/nua-llm-core/modules/cast/list-schema.d.ts +32 -0
  35. package/dist/types/nua-llm-core/modules/cast/prompts/list-prompt.d.ts +3 -0
  36. package/dist/types/nua-llm-core/modules/cast/prompts/render-prompt.d.ts +7 -0
  37. package/dist/types/nua-llm-core/modules/cast/prompts/value-prompt.d.ts +3 -0
  38. package/dist/types/nua-llm-core/modules/cast/run-cast.d.ts +44 -0
  39. package/dist/types/nua-llm-core/modules/engine/engine-router.d.ts +23 -0
  40. package/dist/types/nua-llm-core/modules/engine/http/http-engine.d.ts +12 -0
  41. package/dist/types/nua-llm-core/modules/engine/http/provider-call.d.ts +33 -0
  42. package/dist/types/nua-llm-core/modules/engine/http/provider-config.d.ts +65 -0
  43. package/dist/types/nua-llm-core/modules/engine/http/provider-engines.d.ts +35 -0
  44. package/dist/types/nua-llm-core/modules/engine/llm-engine.d.ts +73 -0
  45. package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/claude-code.d.ts +2 -0
  46. package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/codex.d.ts +2 -0
  47. package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/failure-classification.d.ts +4 -0
  48. package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/index.d.ts +3 -0
  49. package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/pi.d.ts +2 -0
  50. package/dist/types/nua-llm-core/modules/engine/local-agent/adapters/types.d.ts +61 -0
  51. package/dist/types/nua-llm-core/modules/engine/local-agent/agent-id.d.ts +4 -0
  52. package/dist/types/nua-llm-core/modules/engine/local-agent/create-local-agent.d.ts +36 -0
  53. package/dist/types/nua-llm-core/modules/engine/local-agent/detect.d.ts +20 -0
  54. package/dist/types/nua-llm-core/modules/engine/local-agent/local-agent-engine.d.ts +32 -0
  55. package/dist/types/nua-llm-core/modules/engine/local-agent/local-agent.d.ts +25 -0
  56. package/dist/types/nua-llm-core/modules/engine/local-agent/process-runner.d.ts +16 -0
  57. package/dist/types/nua-llm-core/modules/engine/local-agent/semaphore.d.ts +8 -0
  58. package/dist/types/nua-llm-core/modules/execution/llm-response-extraction.d.ts +5 -0
  59. package/dist/types/nua-llm-core/modules/json-schema-validation/ajv-instances.d.ts +12 -0
  60. package/dist/types/nua-llm-core/modules/json-schema-validation/nua-json-schema-value-validation.d.ts +10 -0
  61. package/dist/types/nua-llm-core/modules/json-schema-validation/validate-json-schema.d.ts +2 -0
  62. package/dist/types/nua-llm-core/modules/model-info/index.d.ts +15 -0
  63. package/dist/types/nua-llm-core/modules/streaming/sse-parser.d.ts +7 -0
  64. package/dist/types/nua-llm-core/nua-llm-client.d.ts +37 -0
  65. package/dist/types/nua.d.ts +6 -1
  66. package/package.json +19 -2
@@ -1,3 +1,4 @@
1
+ import { r as requireLogger } from './chunks/logger-DnkHzkX_.mjs';
1
2
  import { z } from 'zod';
2
3
  export { z } from 'zod';
3
4
 
@@ -5,91 +6,27 @@ var dist = {};
5
6
 
6
7
  var nuaLlmClient = {};
7
8
 
8
- var logger = {};
9
-
10
- var hasRequiredLogger;
11
- function requireLogger() {
12
- if (hasRequiredLogger) return logger;
13
- hasRequiredLogger = 1;
14
- Object.defineProperty(logger, "__esModule", { value: true });
15
- logger.logLlmCallError = logger.logLlmCallComplete = logger.logLlmCallStart = logger.generateSpanId = logger.ConsoleLogger = void 0;
16
- logger.pickHeaders = pickHeaders;
17
- logger.normalizeError = normalizeError;
18
- class ConsoleLogger {
19
- debug(message, context) {
20
- console.debug(`[DEBUG] ${message}`, context || "");
21
- }
22
- info(message, context) {
23
- console.info(`[INFO] ${message}`, context || "");
24
- }
25
- warn(message, context) {
26
- console.warn(`[WARN] ${message}`, context || "");
27
- }
28
- error(message, context) {
29
- console.error(`[ERROR] ${message}`, context || "");
30
- }
31
- }
32
- logger.ConsoleLogger = ConsoleLogger;
33
- const generateSpanId = () => crypto.randomUUID();
34
- logger.generateSpanId = generateSpanId;
35
- function pickHeaders(headers, allowlist, redactSet) {
36
- const result = {};
37
- for (const [key, value] of Object.entries(headers)) {
38
- const lowerKey = key.toLowerCase();
39
- if (redactSet?.has(lowerKey)) {
40
- result[key] = "[REDACTED]";
41
- } else if (allowlist.has(lowerKey)) {
42
- result[key] = Array.isArray(value) ? value.join(", ") : value ?? "";
43
- }
44
- }
45
- return result;
46
- }
47
- function normalizeError(error) {
48
- return error instanceof Error ? { message: error.message, stack: error.stack } : String(error);
49
- }
50
- const LLM_RESPONSE_HEADER_ALLOWLIST = /* @__PURE__ */ new Set([
51
- "x-request-id",
52
- "x-ratelimit-limit-requests",
53
- "x-ratelimit-limit-tokens",
54
- "x-ratelimit-remaining-requests",
55
- "x-ratelimit-remaining-tokens",
56
- "x-groq-region"
57
- ]);
58
- const logLlmCallStart = (logger2, spanId, service, method, request) => {
59
- logger2.info("LLM call started", {
60
- type: "llm_call_start",
61
- span_id: spanId,
62
- service,
63
- method,
64
- ...request
65
- });
9
+ var nuaErrors = {};
10
+
11
+ var hasRequiredNuaErrors;
12
+ function requireNuaErrors() {
13
+ if (hasRequiredNuaErrors) return nuaErrors;
14
+ hasRequiredNuaErrors = 1;
15
+ Object.defineProperty(nuaErrors, "__esModule", { value: true });
16
+ nuaErrors.isNuaError = nuaErrors.isNuaInternalError = nuaErrors.isNuaValidationError = void 0;
17
+ const isNuaValidationError = (a) => {
18
+ return typeof a == "object" && a != null && "kind" in a && a.kind == "validation-error";
66
19
  };
67
- logger.logLlmCallStart = logLlmCallStart;
68
- const logLlmCallComplete = (logger2, spanId, service, method, response, duration) => {
69
- const { headers, ...rest } = response;
70
- logger2.info("LLM call completed", {
71
- type: "llm_call_complete",
72
- span_id: spanId,
73
- service,
74
- method,
75
- ...rest,
76
- ...pickHeaders(headers, LLM_RESPONSE_HEADER_ALLOWLIST),
77
- duration_ms: duration
78
- });
20
+ nuaErrors.isNuaValidationError = isNuaValidationError;
21
+ const isNuaInternalError = (a) => {
22
+ return typeof a == "object" && a != null && "kind" in a && a.kind == "internal-error";
79
23
  };
80
- logger.logLlmCallComplete = logLlmCallComplete;
81
- const logLlmCallError = (logger2, spanId, service, method, error, duration) => {
82
- logger2.error("LLM call failed", {
83
- type: "llm_call_error",
84
- span_id: spanId,
85
- service,
86
- method,
87
- error: normalizeError(error),
88
- duration_ms: duration
89
- });
24
+ nuaErrors.isNuaInternalError = isNuaInternalError;
25
+ const isNuaError = (a) => {
26
+ return typeof a == "object" && a != null && "kind" in a && (a.kind == "internal-error" || a.kind == "validation-error");
90
27
  };
91
- logger.logLlmCallError = logLlmCallError;
92
- return logger;
28
+ nuaErrors.isNuaError = isNuaError;
29
+ return nuaErrors;
93
30
  }
94
31
 
95
32
  var validateMappableInputData = {};
@@ -132,7 +69,7 @@ function requireValidateMappableInputData() {
132
69
  return validateMappableInputData;
133
70
  }
134
71
 
135
- var callLlmService = {};
72
+ var runCast = {};
136
73
 
137
74
  var llmResponseExtraction = {};
138
75
 
@@ -241,60 +178,6 @@ function requireLlmResponseExtraction() {
241
178
  return llmResponseExtraction;
242
179
  }
243
180
 
244
- var hasRequiredCallLlmService;
245
- function requireCallLlmService() {
246
- if (hasRequiredCallLlmService) return callLlmService;
247
- hasRequiredCallLlmService = 1;
248
- Object.defineProperty(callLlmService, "__esModule", { value: true });
249
- callLlmService.callLLM = callLLM;
250
- const llm_response_extraction_1 = requireLlmResponseExtraction();
251
- const delay = (ms) => {
252
- return new Promise((resolve) => setTimeout(resolve, ms));
253
- };
254
- async function callLLM(llmClient, prompt, model, maxTokens, temperature, maxRetries, validationFunction) {
255
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
256
- try {
257
- const providerResponse = await llmClient.sendRequest(prompt, model, maxTokens);
258
- const response = providerResponse.text;
259
- let parsedResponse;
260
- try {
261
- const { cleanedResponse } = (0, llm_response_extraction_1.extractThinkingFromResponse)(response);
262
- parsedResponse = (0, llm_response_extraction_1.parseJsonFromLlmResponse)(cleanedResponse);
263
- } catch (parseError) {
264
- const errorMessage2 = `Failed to parse LLM response as JSON: ${parseError instanceof Error ? parseError.message : "Unknown parsing error"}`;
265
- throw new Error(`${errorMessage2}
266
-
267
- Raw LLM response:
268
- ${response}`);
269
- }
270
- const validationResult = validationFunction(parsedResponse);
271
- if (validationResult.success && validationResult.data) {
272
- return {
273
- data: validationResult.data,
274
- usage: providerResponse.usage
275
- };
276
- }
277
- const errorMessage = `Validation failed: ${validationResult.error || "Unknown validation error"}`;
278
- throw new Error(`${errorMessage}
279
-
280
- Raw LLM response:
281
- ${response}`);
282
- } catch (error) {
283
- const isLastAttempt = attempt === maxRetries;
284
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
285
- if (isLastAttempt) {
286
- throw new Error(`LLM call failed after ${maxRetries} attempts. Last error: ${errorMessage}`);
287
- }
288
- const backoffDelay = Math.pow(2, attempt - 1) * 1e3;
289
- console.warn(`LLM call attempt ${attempt} failed: ${errorMessage}. Retrying in ${backoffDelay}ms...`);
290
- await delay(backoffDelay);
291
- }
292
- }
293
- throw new Error("Unexpected error: retry loop completed without success or failure");
294
- }
295
- return callLlmService;
296
- }
297
-
298
181
  var nuaJsonSchemaValueValidation = {};
299
182
 
300
183
  var ajvInstances = {};
@@ -9496,7 +9379,418 @@ function requireNuaJsonSchemaValueValidation() {
9496
9379
  return nuaJsonSchemaValueValidation;
9497
9380
  }
9498
9381
 
9499
- var httpLlmClient = {};
9382
+ var envelope = {};
9383
+
9384
+ var hasRequiredEnvelope;
9385
+ function requireEnvelope() {
9386
+ if (hasRequiredEnvelope) return envelope;
9387
+ hasRequiredEnvelope = 1;
9388
+ (function(exports$1) {
9389
+ Object.defineProperty(exports$1, "__esModule", { value: true });
9390
+ exports$1.ENVELOPE_KEY = void 0;
9391
+ exports$1.wrapInEnvelope = wrapInEnvelope;
9392
+ exports$1.unwrapEnvelope = unwrapEnvelope;
9393
+ exports$1.ENVELOPE_KEY = "value";
9394
+ function wrapInEnvelope(schema) {
9395
+ return {
9396
+ type: "object",
9397
+ properties: { [exports$1.ENVELOPE_KEY]: withoutSchemaKeyword(schema) },
9398
+ required: [exports$1.ENVELOPE_KEY],
9399
+ additionalProperties: false
9400
+ };
9401
+ }
9402
+ function unwrapEnvelope(envelope2) {
9403
+ if (typeof envelope2 !== "object" || envelope2 === null || !(exports$1.ENVELOPE_KEY in envelope2)) {
9404
+ return { found: false };
9405
+ }
9406
+ return { found: true, value: envelope2[exports$1.ENVELOPE_KEY] };
9407
+ }
9408
+ const SUBSCHEMA_KEYWORDS = /* @__PURE__ */ new Set([
9409
+ "items",
9410
+ "additionalItems",
9411
+ "prefixItems",
9412
+ "contains",
9413
+ "additionalProperties",
9414
+ "propertyNames",
9415
+ "unevaluatedItems",
9416
+ "unevaluatedProperties",
9417
+ "not",
9418
+ "if",
9419
+ "then",
9420
+ "else",
9421
+ "allOf",
9422
+ "anyOf",
9423
+ "oneOf"
9424
+ ]);
9425
+ const SUBSCHEMA_MAP_KEYWORDS = /* @__PURE__ */ new Set([
9426
+ "properties",
9427
+ "patternProperties",
9428
+ "dependentSchemas",
9429
+ "dependencies",
9430
+ "$defs",
9431
+ "definitions"
9432
+ ]);
9433
+ function withoutSchemaKeyword(schema) {
9434
+ const result = {};
9435
+ for (const [keyword, value] of Object.entries(schema)) {
9436
+ if (keyword === "$schema")
9437
+ continue;
9438
+ if (SUBSCHEMA_KEYWORDS.has(keyword)) {
9439
+ result[keyword] = Array.isArray(value) ? value.map(subschema) : subschema(value);
9440
+ } else if (SUBSCHEMA_MAP_KEYWORDS.has(keyword) && isObject(value)) {
9441
+ result[keyword] = Object.fromEntries(Object.entries(value).map(([name, child]) => [name, subschema(child)]));
9442
+ } else {
9443
+ result[keyword] = value;
9444
+ }
9445
+ }
9446
+ return result;
9447
+ }
9448
+ function subschema(value) {
9449
+ return isObject(value) ? withoutSchemaKeyword(value) : value;
9450
+ }
9451
+ function isObject(value) {
9452
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9453
+ }
9454
+ })(envelope);
9455
+ return envelope;
9456
+ }
9457
+
9458
+ var renderPrompt = {};
9459
+
9460
+ var listPrompt = {};
9461
+
9462
+ var promptTemplateUtils = {};
9463
+
9464
+ var hasRequiredPromptTemplateUtils;
9465
+ function requirePromptTemplateUtils() {
9466
+ if (hasRequiredPromptTemplateUtils) return promptTemplateUtils;
9467
+ hasRequiredPromptTemplateUtils = 1;
9468
+ (function(exports$1) {
9469
+ Object.defineProperty(exports$1, "__esModule", { value: true });
9470
+ exports$1.replaceTemplateVarName = exports$1.templateVarName = void 0;
9471
+ const templateVarName = (varName) => `<<{${varName}}>>`;
9472
+ exports$1.templateVarName = templateVarName;
9473
+ const replaceTemplateVarName = (str, varName, value) => str.replaceAll((0, exports$1.templateVarName)(varName), value);
9474
+ exports$1.replaceTemplateVarName = replaceTemplateVarName;
9475
+ })(promptTemplateUtils);
9476
+ return promptTemplateUtils;
9477
+ }
9478
+
9479
+ var hasRequiredListPrompt;
9480
+ function requireListPrompt() {
9481
+ if (hasRequiredListPrompt) return listPrompt;
9482
+ hasRequiredListPrompt = 1;
9483
+ Object.defineProperty(listPrompt, "__esModule", { value: true });
9484
+ listPrompt.renderListPrompt = renderListPrompt;
9485
+ const prompt_template_utils_1 = requirePromptTemplateUtils();
9486
+ const envelope_1 = requireEnvelope();
9487
+ const DESCRIBE_OUTPUT = [
9488
+ `Your task is to transform the provided <input-data> into a JSON array, that represents a list of`,
9489
+ `${(0, prompt_template_utils_1.templateVarName)("OUTPUT_TYPE_NAME")}. The schema for this JSON array is defined below in <json-schema-spec> for you reference.`,
9490
+ `It uses the JSON Schema spec.`
9491
+ ];
9492
+ const MATCH_PRIMARY_KEYS = [
9493
+ `Note that the primary key of each element in the input-data is '${(0, prompt_template_utils_1.templateVarName)("PRIMARY_KEY")}'. In the output JSON array,`,
9494
+ `each object should have the same key with the same value of the original data, so we can map the input element against the output element.`
9495
+ ];
9496
+ const SYSTEM_PROMPTS = {
9497
+ "in-prompt": [
9498
+ ...DESCRIBE_OUTPUT,
9499
+ `Respond with only the instance, not the schema. It should not be wrapped in any wrapper object -- return exactly what the JSON schema is expecting.`,
9500
+ `Your entire response should be just the value of the JSON array containing objects with '${(0, prompt_template_utils_1.templateVarName)("OUTPUT_TYPE_NAME")}'`,
9501
+ `and its primary key '${(0, prompt_template_utils_1.templateVarName)("PRIMARY_KEY")}', as valid JSON.`,
9502
+ `We will validate the result with the AJV Node library, using ajv.parse, against the JSON schema, and we expect it to validate correctly.`,
9503
+ ...MATCH_PRIMARY_KEYS
9504
+ ].join(" "),
9505
+ native: [
9506
+ ...DESCRIBE_OUTPUT,
9507
+ `Respond with only the instance, not the schema. Your reply is enforced by a JSON schema that wraps the result in an object:`,
9508
+ `put the JSON array, containing objects with '${(0, prompt_template_utils_1.templateVarName)("OUTPUT_TYPE_NAME")}' and its primary key '${(0, prompt_template_utils_1.templateVarName)("PRIMARY_KEY")}', in its "${envelope_1.ENVELOPE_KEY}" field.`,
9509
+ `We will validate the "${envelope_1.ENVELOPE_KEY}" field with the AJV Node library against the JSON schema, and we expect it to validate correctly.`,
9510
+ ...MATCH_PRIMARY_KEYS
9511
+ ].join(" ")
9512
+ };
9513
+ function renderListPrompt(cast, enforcement) {
9514
+ const withPrimaryKey = (0, prompt_template_utils_1.replaceTemplateVarName)(SYSTEM_PROMPTS[enforcement], "PRIMARY_KEY", cast.primaryKey);
9515
+ const system = (0, prompt_template_utils_1.replaceTemplateVarName)(withPrimaryKey, "OUTPUT_TYPE_NAME", cast.outputName);
9516
+ const full = [
9517
+ cast.prompt || "Transform the provided data according to the schema.",
9518
+ system,
9519
+ `<json-schema-spec> ${JSON.stringify(cast.schema)} </json-schema-spec>`,
9520
+ `<input-data>${JSON.stringify(cast.rows)}</input-data>`
9521
+ ].join("\n");
9522
+ return { system, full };
9523
+ }
9524
+ return listPrompt;
9525
+ }
9526
+
9527
+ var valuePrompt = {};
9528
+
9529
+ var hasRequiredValuePrompt;
9530
+ function requireValuePrompt() {
9531
+ if (hasRequiredValuePrompt) return valuePrompt;
9532
+ hasRequiredValuePrompt = 1;
9533
+ Object.defineProperty(valuePrompt, "__esModule", { value: true });
9534
+ valuePrompt.renderValuePrompt = renderValuePrompt;
9535
+ const prompt_template_utils_1 = requirePromptTemplateUtils();
9536
+ const envelope_1 = requireEnvelope();
9537
+ const DESCRIBE_OUTPUT = [
9538
+ `Please respond with a single JSON object that represents '${(0, prompt_template_utils_1.templateVarName)("OUTPUT_TYPE_NAME")}'.`,
9539
+ `The schema for this object (which uses the JSON Schema spec), is defined below for your reference.`,
9540
+ `Your task is to create an instance of this object. Respond with only the instance, not the schema.`
9541
+ ];
9542
+ const SYSTEM_PROMPTS = {
9543
+ "in-prompt": [
9544
+ ...DESCRIBE_OUTPUT,
9545
+ `Your entire response should be just the value of '${(0, prompt_template_utils_1.templateVarName)("OUTPUT_TYPE_NAME")}', as valid JSON.`,
9546
+ `And it should not be wrapped in any wrapper object -- return exactly what the JSON schema is expecting.`,
9547
+ `We will validate the result with the AJV Node library, using ajv.parse, against the given schema, and we expect`,
9548
+ `it to validate correctly.`,
9549
+ `IMPORTANT: Output raw JSON only. No markdown code fences, no explanation, no commentary, no extra fields beyond the schema. Do not correct yourself -- produce the right answer on the first try.`
9550
+ ].join(" "),
9551
+ native: [
9552
+ ...DESCRIBE_OUTPUT,
9553
+ `Your reply is enforced by a JSON schema that wraps the result in an object:`,
9554
+ `put the value of '${(0, prompt_template_utils_1.templateVarName)("OUTPUT_TYPE_NAME")}' in its "${envelope_1.ENVELOPE_KEY}" field.`,
9555
+ `We will validate the "${envelope_1.ENVELOPE_KEY}" field with the AJV Node library against the schema below, and we expect`,
9556
+ `it to validate correctly.`
9557
+ ].join(" ")
9558
+ };
9559
+ function renderValuePrompt(cast, enforcement) {
9560
+ const system = (0, prompt_template_utils_1.replaceTemplateVarName)(SYSTEM_PROMPTS[enforcement], "OUTPUT_TYPE_NAME", cast.outputName);
9561
+ const full = [
9562
+ cast.prompt || "Transform the provided data according to the schema.",
9563
+ system,
9564
+ `<json-schema-spec> ${JSON.stringify(cast.schema)} </json-schema-spec>`,
9565
+ cast.data ? `<input-data>${JSON.stringify(cast.data)}</input-data>` : ""
9566
+ ].join("\n");
9567
+ return { system, full };
9568
+ }
9569
+ return valuePrompt;
9570
+ }
9571
+
9572
+ var hasRequiredRenderPrompt;
9573
+ function requireRenderPrompt() {
9574
+ if (hasRequiredRenderPrompt) return renderPrompt;
9575
+ hasRequiredRenderPrompt = 1;
9576
+ Object.defineProperty(renderPrompt, "__esModule", { value: true });
9577
+ renderPrompt.renderPrompt = renderPrompt$1;
9578
+ const list_prompt_1 = requireListPrompt();
9579
+ const value_prompt_1 = requireValuePrompt();
9580
+ function renderPrompt$1(cast, enforcement) {
9581
+ switch (cast.kind) {
9582
+ case "value":
9583
+ return (0, value_prompt_1.renderValuePrompt)(cast, enforcement);
9584
+ case "list":
9585
+ return (0, list_prompt_1.renderListPrompt)(cast, enforcement);
9586
+ }
9587
+ }
9588
+ return renderPrompt;
9589
+ }
9590
+
9591
+ var hasRequiredRunCast;
9592
+ function requireRunCast() {
9593
+ if (hasRequiredRunCast) return runCast;
9594
+ hasRequiredRunCast = 1;
9595
+ Object.defineProperty(runCast, "__esModule", { value: true });
9596
+ runCast.runCast = runCast$1;
9597
+ runCast.judgeOutcome = judgeOutcome;
9598
+ const llm_response_extraction_1 = requireLlmResponseExtraction();
9599
+ const nua_json_schema_value_validation_1 = requireNuaJsonSchemaValueValidation();
9600
+ const envelope_1 = requireEnvelope();
9601
+ const render_prompt_1 = requireRenderPrompt();
9602
+ const RETRY_DELAYS_MS = [1e3, 2e3];
9603
+ const MAX_ATTEMPTS = RETRY_DELAYS_MS.length + 1;
9604
+ async function runCast$1(engine, request, logger) {
9605
+ let validate;
9606
+ try {
9607
+ validate = (0, nua_json_schema_value_validation_1.buildNuaJsonSchemaValueValidation)("library-req", request.schema);
9608
+ } catch (error) {
9609
+ return { success: false, error: `Invalid output schema: ${errorMessage(error)}` };
9610
+ }
9611
+ let plan = planAttempt(request, chooseEnforcement(engine, request.schema));
9612
+ let lastFailure = { error: "", prompt: plan.prompt };
9613
+ for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
9614
+ const outcome = await engine.attempt({
9615
+ prompt: plan.prompt.full,
9616
+ maxTokens: request.maxTokens,
9617
+ enforcement: plan.enforcement
9618
+ });
9619
+ const verdict = judgeOutcome(outcome, validate);
9620
+ if (verdict.kind === "accept") {
9621
+ return {
9622
+ success: true,
9623
+ data: verdict.data,
9624
+ usage: verdict.usage,
9625
+ prompt: plan.prompt,
9626
+ origin: verdict.origin,
9627
+ schemaEnforcement: plan.enforcement.kind
9628
+ };
9629
+ }
9630
+ if (verdict.kind === "give-up") {
9631
+ return { success: false, error: verdict.error, prompt: plan.prompt };
9632
+ }
9633
+ lastFailure = { error: verdict.error, prompt: plan.prompt };
9634
+ if (verdict.kind === "retry-in-prompt") {
9635
+ logger.warn("Engine rejected the native output schema; retrying with the schema in the prompt", {
9636
+ attempt,
9637
+ error: verdict.error
9638
+ });
9639
+ plan = planAttempt(request, { kind: "in-prompt" });
9640
+ continue;
9641
+ }
9642
+ if (attempt < MAX_ATTEMPTS) {
9643
+ const delayMs = RETRY_DELAYS_MS[attempt - 1];
9644
+ logger.warn("LLM call attempt failed; retrying", { attempt, delayMs, error: verdict.error });
9645
+ await delay(delayMs);
9646
+ }
9647
+ }
9648
+ return {
9649
+ success: false,
9650
+ error: `LLM call failed after ${MAX_ATTEMPTS} attempts. Last error: ${lastFailure.error}`,
9651
+ prompt: lastFailure.prompt
9652
+ };
9653
+ }
9654
+ function chooseEnforcement(engine, schema) {
9655
+ const nativeSchema = engine.nativeSchema((0, envelope_1.wrapInEnvelope)(schema));
9656
+ return nativeSchema ? { kind: "native", schema: nativeSchema } : { kind: "in-prompt" };
9657
+ }
9658
+ function planAttempt(request, enforcement) {
9659
+ return { enforcement, prompt: (0, render_prompt_1.renderPrompt)(request, enforcement.kind) };
9660
+ }
9661
+ function judgeOutcome(outcome, validate) {
9662
+ if (outcome.kind === "failed") {
9663
+ return verdictForFailure(outcome.failure);
9664
+ }
9665
+ const candidate = readAnswer(outcome.answer);
9666
+ if (!candidate.ok) {
9667
+ return { kind: "retry", error: candidate.error };
9668
+ }
9669
+ const validation = validate(candidate.value);
9670
+ if (!validation.success) {
9671
+ return {
9672
+ kind: "retry",
9673
+ error: `Validation failed: ${validation.error || "Unknown validation error"}
9674
+
9675
+ Raw LLM response:
9676
+ ${candidate.raw}`
9677
+ };
9678
+ }
9679
+ return { kind: "accept", data: candidate.value, usage: outcome.usage, origin: outcome.origin };
9680
+ }
9681
+ function verdictForFailure(failure) {
9682
+ switch (failure.kind) {
9683
+ case "transient":
9684
+ return { kind: "retry", error: failure.message };
9685
+ case "schema-rejected":
9686
+ return { kind: "retry-in-prompt", error: failure.message };
9687
+ case "fatal":
9688
+ return { kind: "give-up", error: failure.message };
9689
+ }
9690
+ }
9691
+ function readAnswer(answer) {
9692
+ switch (answer.kind) {
9693
+ case "text":
9694
+ return readTextAnswer(answer.text);
9695
+ case "structured":
9696
+ return readStructuredAnswer(answer.envelope);
9697
+ }
9698
+ }
9699
+ function readTextAnswer(text) {
9700
+ try {
9701
+ const { cleanedResponse } = (0, llm_response_extraction_1.extractThinkingFromResponse)(text);
9702
+ return { ok: true, value: (0, llm_response_extraction_1.parseJsonFromLlmResponse)(cleanedResponse), raw: text };
9703
+ } catch (error) {
9704
+ return {
9705
+ ok: false,
9706
+ error: `Failed to parse LLM response as JSON: ${errorMessage(error)}
9707
+
9708
+ Raw LLM response:
9709
+ ${text}`
9710
+ };
9711
+ }
9712
+ }
9713
+ function readStructuredAnswer(envelope) {
9714
+ const raw = JSON.stringify(envelope);
9715
+ const unwrapped = (0, envelope_1.unwrapEnvelope)(envelope);
9716
+ if (!unwrapped.found) {
9717
+ return { ok: false, error: `Structured output has no "${envelope_1.ENVELOPE_KEY}" field: ${raw}` };
9718
+ }
9719
+ return { ok: true, value: unwrapped.value, raw };
9720
+ }
9721
+ function errorMessage(error) {
9722
+ return error instanceof Error ? error.message : String(error);
9723
+ }
9724
+ function delay(ms) {
9725
+ return new Promise((resolve) => setTimeout(resolve, ms));
9726
+ }
9727
+ return runCast;
9728
+ }
9729
+
9730
+ var hasRequiredNuaLlmClient;
9731
+ function requireNuaLlmClient() {
9732
+ if (hasRequiredNuaLlmClient) return nuaLlmClient;
9733
+ hasRequiredNuaLlmClient = 1;
9734
+ Object.defineProperty(nuaLlmClient, "__esModule", { value: true });
9735
+ nuaLlmClient.NuaLlmClient = void 0;
9736
+ const nua_errors_1 = requireNuaErrors();
9737
+ const validate_mappable_input_data_1 = requireValidateMappableInputData();
9738
+ const run_cast_1 = requireRunCast();
9739
+ const DEFAULT_MAX_TOKENS = 4096;
9740
+ class NuaLlmClient {
9741
+ constructor(engines) {
9742
+ this.engines = engines;
9743
+ }
9744
+ async castValue(params) {
9745
+ const route = this.engines.route(params.model);
9746
+ if (route.kind === "unroutable") {
9747
+ return { success: false, error: route.message };
9748
+ }
9749
+ const request = {
9750
+ kind: "value",
9751
+ prompt: params.input.prompt,
9752
+ data: params.input.data,
9753
+ outputName: params.output.name,
9754
+ schema: params.output.schema,
9755
+ maxTokens: params.maxTokens ?? DEFAULT_MAX_TOKENS
9756
+ };
9757
+ return (0, run_cast_1.runCast)(route.engine, request, this.engines.logger);
9758
+ }
9759
+ async castArray(params) {
9760
+ const { primaryKey, outputName, jsonSchema } = params.output;
9761
+ const rows = (0, validate_mappable_input_data_1.validateMappableInputData)(params.input.data, primaryKey);
9762
+ if ((0, nua_errors_1.isNuaValidationError)(rows)) {
9763
+ return { success: false, error: rows.message };
9764
+ }
9765
+ const route = this.engines.route(params.model);
9766
+ if (route.kind === "unroutable") {
9767
+ return { success: false, error: route.message };
9768
+ }
9769
+ const request = {
9770
+ kind: "list",
9771
+ prompt: params.input.prompt,
9772
+ rows,
9773
+ primaryKey,
9774
+ outputName,
9775
+ schema: jsonSchema,
9776
+ maxTokens: params.maxTokens ?? DEFAULT_MAX_TOKENS
9777
+ };
9778
+ return (0, run_cast_1.runCast)(route.engine, request, this.engines.logger);
9779
+ }
9780
+ }
9781
+ nuaLlmClient.NuaLlmClient = NuaLlmClient;
9782
+ return nuaLlmClient;
9783
+ }
9784
+
9785
+ var types = {};
9786
+
9787
+ var hasRequiredTypes;
9788
+ function requireTypes() {
9789
+ if (hasRequiredTypes) return types;
9790
+ hasRequiredTypes = 1;
9791
+ Object.defineProperty(types, "__esModule", { value: true });
9792
+ return types;
9793
+ }
9500
9794
 
9501
9795
  var providerConfig = {};
9502
9796
 
@@ -9715,346 +10009,433 @@ function requireProviderConfig() {
9715
10009
  return providerConfig;
9716
10010
  }
9717
10011
 
9718
- var providerFormatters = {};
9719
-
9720
- var sseParser = {};
10012
+ var providerEngines = {};
9721
10013
 
9722
- var hasRequiredSseParser;
9723
- function requireSseParser() {
9724
- if (hasRequiredSseParser) return sseParser;
9725
- hasRequiredSseParser = 1;
9726
- Object.defineProperty(sseParser, "__esModule", { value: true });
9727
- sseParser.parseSSEStream = parseSSEStream;
9728
- async function* parseSSEStream(stream) {
9729
- const reader = stream.getReader();
9730
- const decoder = new TextDecoder();
9731
- let buffer = "";
9732
- try {
9733
- while (true) {
9734
- const { done, value } = await reader.read();
9735
- if (done)
9736
- break;
9737
- buffer += decoder.decode(value, { stream: true });
9738
- const parts = buffer.split(/\r?\n\r?\n/);
9739
- buffer = parts.pop();
9740
- for (const part of parts) {
9741
- for (const line of part.split(/\r?\n/)) {
9742
- if (line.startsWith("data: ")) {
9743
- const data = line.slice(6);
9744
- if (data === "[DONE]") {
9745
- return;
9746
- }
9747
- yield data;
9748
- }
9749
- }
9750
- }
9751
- }
9752
- if (buffer.trim()) {
9753
- for (const line of buffer.split(/\r?\n/)) {
9754
- if (line.startsWith("data: ")) {
9755
- const data = line.slice(6);
9756
- if (data === "[DONE]") {
9757
- return;
9758
- }
9759
- yield data;
9760
- }
9761
- }
9762
- }
9763
- } finally {
9764
- reader.releaseLock();
9765
- }
9766
- }
9767
- return sseParser;
9768
- }
10014
+ var modelInfo = {};
9769
10015
 
9770
- var hasRequiredProviderFormatters;
9771
- function requireProviderFormatters() {
9772
- if (hasRequiredProviderFormatters) return providerFormatters;
9773
- hasRequiredProviderFormatters = 1;
9774
- Object.defineProperty(providerFormatters, "__esModule", { value: true });
9775
- providerFormatters.buildOpenAiAgenticRequest = buildOpenAiAgenticRequest;
9776
- providerFormatters.parseOpenAiAgenticResponse = parseOpenAiAgenticResponse;
9777
- providerFormatters.buildGeminiAgenticRequest = buildGeminiAgenticRequest;
9778
- providerFormatters.parseGeminiAgenticResponse = parseGeminiAgenticResponse;
9779
- providerFormatters.streamOpenAiAgenticResponse = streamOpenAiAgenticResponse;
9780
- providerFormatters.streamGeminiAgenticResponse = streamGeminiAgenticResponse;
9781
- const provider_config_1 = requireProviderConfig();
9782
- const sse_parser_1 = requireSseParser();
9783
- function convertMessagesToOpenAi(messages, systemPrompt) {
9784
- const result = [];
9785
- if (systemPrompt) {
9786
- result.push({ role: "system", content: systemPrompt });
10016
+ var hasRequiredModelInfo;
10017
+ function requireModelInfo() {
10018
+ if (hasRequiredModelInfo) return modelInfo;
10019
+ hasRequiredModelInfo = 1;
10020
+ (function(exports$1) {
10021
+ Object.defineProperty(exports$1, "__esModule", { value: true });
10022
+ exports$1.MODEL_ALIAS_NAMES = exports$1.MODEL_ALIASES = void 0;
10023
+ exports$1.isLlmProviderId = isLlmProviderId;
10024
+ exports$1.isModelAliasName = isModelAliasName;
10025
+ exports$1.resolveModelInput = resolveModelInput;
10026
+ const provider_config_1 = requireProviderConfig();
10027
+ exports$1.MODEL_ALIASES = {
10028
+ fast: [
10029
+ { provider: "cerebras", model: "qwen-3.8-27b" },
10030
+ { provider: "groq", model: "qwen/qwen3.8-27b" },
10031
+ { provider: "openrouter", model: "z-ai/glm-5.2" }
10032
+ ],
10033
+ gemini: [
10034
+ { provider: "gemini", model: "gemini-2.5-flash" },
10035
+ { provider: "openrouter", model: "google/gemini-2.5-flash" }
10036
+ ],
10037
+ qwen: [
10038
+ { provider: "openrouter", model: "qwen/qwen3-vl-235b-a22b-instruct" }
10039
+ ],
10040
+ sonnet: [
10041
+ { provider: "openrouter", model: "anthropic/claude-sonnet-4.6" }
10042
+ ],
10043
+ opus: [
10044
+ { provider: "openrouter", model: "anthropic/claude-opus-4.7" }
10045
+ ],
10046
+ haiku: [
10047
+ { provider: "openrouter", model: "anthropic/claude-haiku-4.5" }
10048
+ ],
10049
+ kimi: [
10050
+ { provider: "openrouter", model: "moonshotai/kimi-k2.7-code" }
10051
+ ],
10052
+ gpt: [
10053
+ { provider: "openrouter", model: "openai/gpt-5" }
10054
+ ]
10055
+ };
10056
+ exports$1.MODEL_ALIAS_NAMES = Object.keys(exports$1.MODEL_ALIASES);
10057
+ const MODEL_ALIAS_SET = new Set(exports$1.MODEL_ALIAS_NAMES);
10058
+ const LLM_PROVIDER_ID_SET = new Set(provider_config_1.LLM_PROVIDER_IDS);
10059
+ function isLlmProviderId(value) {
10060
+ return typeof value === "string" && LLM_PROVIDER_ID_SET.has(value);
9787
10061
  }
9788
- for (const msg of messages) {
9789
- switch (msg.role) {
9790
- case "user":
9791
- result.push({ role: "user", content: msg.content });
9792
- break;
9793
- case "assistant": {
9794
- const textParts = msg.content.filter((c) => c.type === "text").map((c) => c.text).join("");
9795
- const toolCalls = msg.content.filter((c) => c.type === "toolCall").map((c) => ({
9796
- id: c.id,
9797
- type: "function",
9798
- function: {
9799
- name: c.name,
9800
- arguments: JSON.stringify(c.arguments)
9801
- }
9802
- }));
9803
- const openAiMsg = {
9804
- role: "assistant",
9805
- content: textParts || null,
9806
- ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
9807
- };
9808
- result.push(openAiMsg);
9809
- break;
9810
- }
9811
- case "toolResult":
9812
- result.push({
9813
- role: "tool",
9814
- tool_call_id: msg.toolCallId,
9815
- content: msg.content
9816
- });
9817
- break;
10062
+ function isModelAliasName(value) {
10063
+ return typeof value === "string" && MODEL_ALIAS_SET.has(value);
10064
+ }
10065
+ function validationError(message) {
10066
+ return {
10067
+ kind: "validation-error",
10068
+ message
10069
+ };
10070
+ }
10071
+ function validateProviderModel(input, configuredProviders) {
10072
+ if (!isLlmProviderId(input.provider)) {
10073
+ return validationError(`Invalid LLM provider: ${String(input.provider)}`);
10074
+ }
10075
+ if (!configuredProviders.has(input.provider)) {
10076
+ return validationError(`LLM provider ${input.provider} is not configured. Please check your API keys.`);
10077
+ }
10078
+ if (typeof input.model !== "string") {
10079
+ return validationError("model.model must be a string");
10080
+ }
10081
+ const model = input.model.trim();
10082
+ if (!model) {
10083
+ return validationError("model.model must be a non-empty string");
10084
+ }
10085
+ if (input.provider === "gemini" && model.startsWith("models/")) {
10086
+ return validationError('Gemini model names must use bare model IDs, for example "gemini-2.5-flash", not "models/gemini-2.5-flash".');
9818
10087
  }
10088
+ return {
10089
+ provider: input.provider,
10090
+ model
10091
+ };
9819
10092
  }
9820
- return result;
9821
- }
9822
- function convertToolsToOpenAi(tools) {
9823
- return tools.map((t) => ({
9824
- type: "function",
9825
- function: {
9826
- name: t.name,
9827
- description: t.description,
9828
- parameters: t.parameters
10093
+ function resolveModelInput(input, configuredProviders) {
10094
+ if (input == null) {
10095
+ return resolveModelInput({ alias: "fast" }, configuredProviders);
9829
10096
  }
9830
- }));
10097
+ if (typeof input !== "object") {
10098
+ return validationError("model must be an object with either alias or provider/model fields");
10099
+ }
10100
+ if ("provider" in input || "model" in input) {
10101
+ return validateProviderModel(input, configuredProviders);
10102
+ }
10103
+ if ("alias" in input) {
10104
+ const alias = input.alias;
10105
+ if (!isModelAliasName(alias)) {
10106
+ return validationError(`Invalid model alias: ${String(alias)}`);
10107
+ }
10108
+ const configuredTarget = exports$1.MODEL_ALIASES[alias].find((target) => configuredProviders.has(target.provider));
10109
+ if (!configuredTarget) {
10110
+ return validationError(`No configured provider found for model alias ${alias}. Please check your API keys.`);
10111
+ }
10112
+ return configuredTarget;
10113
+ }
10114
+ return validationError("model must include either alias or provider/model fields");
10115
+ }
10116
+ })(modelInfo);
10117
+ return modelInfo;
10118
+ }
10119
+
10120
+ var httpEngine = {};
10121
+
10122
+ var providerCall = {};
10123
+
10124
+ var hasRequiredProviderCall;
10125
+ function requireProviderCall() {
10126
+ if (hasRequiredProviderCall) return providerCall;
10127
+ hasRequiredProviderCall = 1;
10128
+ Object.defineProperty(providerCall, "__esModule", { value: true });
10129
+ providerCall.callProvider = callProvider;
10130
+ const logger_1 = requireLogger();
10131
+ const provider_config_1 = requireProviderConfig();
10132
+ async function callProvider(call, logger) {
10133
+ const { target, apiOperation, request } = call;
10134
+ const method = request.method ?? "POST";
10135
+ const spanId = (0, logger_1.generateSpanId)();
10136
+ const startTime = Date.now();
10137
+ let status;
10138
+ (0, logger_1.logLlmCallStart)(logger, spanId, target.provider, apiOperation, {
10139
+ url: request.loggableUrl ?? request.url,
10140
+ httpMethod: method,
10141
+ model: target.model,
10142
+ maxTokens: call.maxTokens
10143
+ });
10144
+ try {
10145
+ const response = await fetch(request.url, {
10146
+ method,
10147
+ headers: request.headers ?? {},
10148
+ body: toFetchableBody(request.body)
10149
+ });
10150
+ status = response.status;
10151
+ const reply = await call.parseResponse(response);
10152
+ (0, logger_1.logLlmCallComplete)(logger, spanId, target.provider, apiOperation, {
10153
+ status: response.status,
10154
+ responseLength: call.responseLength(reply),
10155
+ headers: Object.fromEntries(response.headers.entries()),
10156
+ usage: call.usage(reply)
10157
+ }, Date.now() - startTime);
10158
+ return { kind: "ok", reply };
10159
+ } catch (error) {
10160
+ (0, logger_1.logLlmCallError)(logger, spanId, target.provider, apiOperation, error, Date.now() - startTime);
10161
+ return {
10162
+ kind: "failed",
10163
+ status,
10164
+ message: `${provider_config_1.providerConfigs[target.provider].errorLabel} API request failed: ${error instanceof Error ? error.message : "Unknown error"}`
10165
+ };
10166
+ }
9831
10167
  }
9832
- function buildOpenAiAgenticRequest(options, baseUrl, apiKey) {
9833
- const messages = convertMessagesToOpenAi(options.messages, options.systemPrompt);
9834
- const tools = convertToolsToOpenAi(options.tools);
9835
- const body = {
9836
- messages,
9837
- tools: tools.length > 0 ? tools : void 0,
9838
- model: options.model,
9839
- max_completion_tokens: options.maxTokens,
9840
- stream: options.stream ?? false
9841
- };
9842
- if (options.stream) {
9843
- body.stream_options = { include_usage: true };
10168
+ function toFetchableBody(body) {
10169
+ if (typeof body === "string" || body === void 0) {
10170
+ return body;
9844
10171
  }
9845
- const headers = {
9846
- "Content-Type": "application/json",
9847
- Authorization: `Bearer ${apiKey}`
9848
- };
9849
- return {
9850
- url: baseUrl,
9851
- method: "POST",
9852
- headers,
9853
- body,
9854
- loggableHeaders: {
9855
- ...headers,
9856
- Authorization: "Bearer [REDACTED]"
9857
- },
9858
- loggableBody: body
9859
- };
10172
+ return JSON.stringify(body);
9860
10173
  }
9861
- function parseOpenAiToolArguments(rawArguments, toolName, errorLabel) {
9862
- if (!rawArguments.trim()) {
9863
- return {};
10174
+ return providerCall;
10175
+ }
10176
+
10177
+ var hasRequiredHttpEngine;
10178
+ function requireHttpEngine() {
10179
+ if (hasRequiredHttpEngine) return httpEngine;
10180
+ hasRequiredHttpEngine = 1;
10181
+ Object.defineProperty(httpEngine, "__esModule", { value: true });
10182
+ httpEngine.HttpEngine = void 0;
10183
+ const provider_call_1 = requireProviderCall();
10184
+ const provider_config_1 = requireProviderConfig();
10185
+ const FATAL_HTTP_STATUSES = /* @__PURE__ */ new Set([401, 403, 404]);
10186
+ class HttpEngine {
10187
+ constructor(target, logger) {
10188
+ this.target = target;
10189
+ this.logger = logger;
9864
10190
  }
9865
- let parsed;
9866
- try {
9867
- parsed = JSON.parse(rawArguments);
9868
- } catch {
9869
- const preview = rawArguments.length > 500 ? rawArguments.slice(0, 500) + "..." : rawArguments;
9870
- throw new Error(`${errorLabel} API error: Invalid JSON arguments for tool "${toolName}": ${preview}`);
10191
+ /** Provider APIs are given the schema in the prompt; none is enforced natively yet. */
10192
+ nativeSchema() {
10193
+ return null;
9871
10194
  }
9872
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
9873
- throw new Error(`${errorLabel} API error: Tool "${toolName}" arguments must be a JSON object`);
10195
+ async attempt(request) {
10196
+ const { provider, model, apiKey } = this.target;
10197
+ const config = provider_config_1.providerConfigs[provider];
10198
+ const call = await (0, provider_call_1.callProvider)({
10199
+ target: this.target,
10200
+ apiOperation: config.apiOperation,
10201
+ maxTokens: request.maxTokens,
10202
+ request: config.buildRequest({ prompt: request.prompt, model, maxTokens: request.maxTokens }, apiKey),
10203
+ parseResponse: config.parseResponse,
10204
+ responseLength: (reply) => reply.text.length,
10205
+ usage: (reply) => reply.usage
10206
+ }, this.logger);
10207
+ if (call.kind === "failed") {
10208
+ return { kind: "failed", failure: failureForStatus(call.status, call.message) };
10209
+ }
10210
+ return {
10211
+ kind: "answered",
10212
+ answer: { kind: "text", text: call.reply.text },
10213
+ usage: call.reply.usage,
10214
+ origin: { engine: "http", provider, model }
10215
+ };
9874
10216
  }
9875
- return parsed;
9876
10217
  }
9877
- async function parseOpenAiAgenticResponse(response, errorLabel) {
9878
- if (response.status !== 200) {
9879
- const errorBody = await response.text();
9880
- try {
9881
- const parsed = JSON.parse(errorBody);
9882
- const message = parsed?.error?.message || errorBody;
9883
- throw new Error(`${errorLabel} API error: ${response.status} - ${message}`);
9884
- } catch (e) {
9885
- if (e instanceof Error && e.message.startsWith(`${errorLabel} API error:`))
9886
- throw e;
9887
- throw new Error(`${errorLabel} API error: ${response.status} - ${errorBody}`);
10218
+ httpEngine.HttpEngine = HttpEngine;
10219
+ function failureForStatus(status, message) {
10220
+ const fatal = status !== void 0 && FATAL_HTTP_STATUSES.has(status);
10221
+ return { kind: fatal ? "fatal" : "transient", message };
10222
+ }
10223
+ return httpEngine;
10224
+ }
10225
+
10226
+ var hasRequiredProviderEngines;
10227
+ function requireProviderEngines() {
10228
+ if (hasRequiredProviderEngines) return providerEngines;
10229
+ hasRequiredProviderEngines = 1;
10230
+ Object.defineProperty(providerEngines, "__esModule", { value: true });
10231
+ providerEngines.ProviderEngines = void 0;
10232
+ providerEngines.providerEngines = providerEngines$1;
10233
+ const logger_1 = requireLogger();
10234
+ const nua_errors_1 = requireNuaErrors();
10235
+ const model_info_1 = requireModelInfo();
10236
+ const http_engine_1 = requireHttpEngine();
10237
+ function providerEngines$1(config) {
10238
+ return new ProviderEngines(config);
10239
+ }
10240
+ class ProviderEngines {
10241
+ constructor(config) {
10242
+ this.logger = config.logger ?? new logger_1.ConsoleLogger();
10243
+ this.defaultModel = config.model;
10244
+ const apiKeys = /* @__PURE__ */ new Map();
10245
+ for (const [id, provider] of Object.entries(config.providers)) {
10246
+ if (provider?.apiKey) {
10247
+ apiKeys.set(id, provider.apiKey);
10248
+ }
9888
10249
  }
10250
+ this.apiKeys = apiKeys;
9889
10251
  }
9890
- const responseBody = await response.json();
9891
- const choice = responseBody.choices?.[0];
9892
- if (!choice?.message) {
9893
- throw new Error(`${errorLabel} API error: No message in response. Body: ${JSON.stringify(responseBody)}`);
9894
- }
9895
- const contentBlocks = [];
9896
- const assistantText = (0, provider_config_1.extractOpenAiMessageText)(choice.message);
9897
- if (assistantText) {
9898
- contentBlocks.push({
9899
- type: "text",
9900
- text: assistantText
9901
- });
10252
+ route(model) {
10253
+ const resolved = this.resolve(model);
10254
+ if (resolved.kind === "unroutable") {
10255
+ return resolved;
10256
+ }
10257
+ return { kind: "routed", engine: new http_engine_1.HttpEngine(resolved.target, this.logger) };
9902
10258
  }
9903
- if (choice.message.tool_calls) {
9904
- for (const tc of choice.message.tool_calls) {
9905
- contentBlocks.push({
9906
- type: "toolCall",
9907
- id: tc.id,
9908
- name: tc.function.name,
9909
- arguments: parseOpenAiToolArguments(tc.function.arguments, tc.function.name, errorLabel)
9910
- });
10259
+ /** The provider model, with its API key, that a model choice runs on. */
10260
+ resolve(model) {
10261
+ const providerModel = (0, model_info_1.resolveModelInput)(model ?? this.defaultModel, new Set(this.apiKeys.keys()));
10262
+ if ((0, nua_errors_1.isNuaValidationError)(providerModel)) {
10263
+ return { kind: "unroutable", message: providerModel.message };
9911
10264
  }
10265
+ const apiKey = this.apiKeys.get(providerModel.provider);
10266
+ if (apiKey === void 0) {
10267
+ return { kind: "unroutable", message: `LLM provider ${providerModel.provider} is not configured.` };
10268
+ }
10269
+ return { kind: "resolved", target: { ...providerModel, apiKey } };
9912
10270
  }
9913
- const usage = (0, provider_config_1.normalizeOpenAiUsage)(responseBody.usage);
9914
- const stopReason = choice.finish_reason === "tool_calls" || choice.finish_reason === "stop" ? choice.finish_reason === "tool_calls" ? "tool_use" : "stop" : choice.message.tool_calls && choice.message.tool_calls.length > 0 ? "tool_use" : "stop";
9915
- return {
9916
- message: {
9917
- role: "assistant",
9918
- content: contentBlocks
9919
- },
9920
- usage,
9921
- stopReason
9922
- };
9923
10271
  }
9924
- function convertMessagesToGemini(messages) {
9925
- const result = [];
9926
- for (const msg of messages) {
9927
- switch (msg.role) {
9928
- case "user":
9929
- result.push({
9930
- role: "user",
9931
- parts: [{ text: msg.content }]
9932
- });
9933
- break;
9934
- case "assistant": {
9935
- const parts = [];
9936
- for (const block of msg.content) {
9937
- if (block.type === "text") {
9938
- parts.push({ text: block.text });
9939
- } else if (block.type === "toolCall") {
9940
- parts.push({
9941
- functionCall: {
9942
- name: block.name,
9943
- args: block.arguments
9944
- }
9945
- });
9946
- }
9947
- }
9948
- result.push({ role: "model", parts });
9949
- break;
9950
- }
9951
- case "toolResult": {
9952
- result.push({
9953
- role: "user",
9954
- parts: [
9955
- {
9956
- functionResponse: {
9957
- name: msg.toolName,
9958
- response: { content: msg.content }
9959
- }
9960
- }
9961
- ]
9962
- });
9963
- break;
9964
- }
9965
- }
9966
- }
9967
- return result;
9968
- }
9969
- function convertToolsToGemini(tools) {
9970
- if (tools.length === 0)
9971
- return [];
9972
- return [
9973
- {
9974
- functionDeclarations: tools.map((t) => ({
9975
- name: t.name,
9976
- description: t.description,
9977
- parameters: t.parameters
9978
- }))
9979
- }
9980
- ];
10272
+ providerEngines.ProviderEngines = ProviderEngines;
10273
+ return providerEngines;
10274
+ }
10275
+
10276
+ var validateJsonSchema = {};
10277
+
10278
+ var errorUtils = {};
10279
+
10280
+ var hasRequiredErrorUtils;
10281
+ function requireErrorUtils() {
10282
+ if (hasRequiredErrorUtils) return errorUtils;
10283
+ hasRequiredErrorUtils = 1;
10284
+ Object.defineProperty(errorUtils, "__esModule", { value: true });
10285
+ errorUtils.getErrorMessageFromException = getErrorMessageFromException;
10286
+ function isErrorWithMessage(error) {
10287
+ return typeof error === "object" && error !== null && "message" in error && typeof error.message === "string";
9981
10288
  }
9982
- function buildGeminiAgenticRequest(options, apiKey) {
9983
- const contents = convertMessagesToGemini(options.messages);
9984
- const tools = convertToolsToGemini(options.tools);
9985
- const body = {
9986
- contents,
9987
- generationConfig: {
9988
- maxOutputTokens: options.maxTokens
9989
- }
9990
- };
9991
- if (tools.length > 0) {
9992
- body.tools = tools;
10289
+ function getErrorMessageFromException(error) {
10290
+ if (isErrorWithMessage(error)) {
10291
+ return error.message;
9993
10292
  }
9994
- if (options.systemPrompt) {
9995
- body.systemInstruction = {
9996
- parts: [{ text: options.systemPrompt }]
10293
+ return String(error);
10294
+ }
10295
+ return errorUtils;
10296
+ }
10297
+
10298
+ var hasRequiredValidateJsonSchema;
10299
+ function requireValidateJsonSchema() {
10300
+ if (hasRequiredValidateJsonSchema) return validateJsonSchema;
10301
+ hasRequiredValidateJsonSchema = 1;
10302
+ Object.defineProperty(validateJsonSchema, "__esModule", { value: true });
10303
+ validateJsonSchema.validateJsonSchema = void 0;
10304
+ const error_utils_1 = requireErrorUtils();
10305
+ const ajv_instances_1 = requireAjvInstances();
10306
+ const validateJsonSchema$1 = (schema) => {
10307
+ if (!schema || typeof schema !== "object") {
10308
+ return {
10309
+ kind: "validation-error",
10310
+ message: "Schema must be a valid object"
9997
10311
  };
9998
10312
  }
9999
- const action = options.stream ? "streamGenerateContent" : "generateContent";
10000
- const altParam = options.stream ? "&alt=sse" : "";
10001
- const url = `https://generativelanguage.googleapis.com/v1beta/models/${options.model}:${action}?key=${apiKey}${altParam}`;
10002
- return {
10003
- url,
10004
- method: "POST",
10005
- headers: {
10006
- "Content-Type": "application/json"
10007
- },
10008
- body,
10009
- loggableUrl: url.replace(/key=[^&]+/, "key=[REDACTED]"),
10010
- loggableBody: body
10011
- };
10012
- }
10013
- async function parseGeminiAgenticResponse(response) {
10014
- if (response.status !== 200) {
10015
- const errorBody = await response.text();
10313
+ const ajvInstance = ajv_instances_1.ajvInstances.getInstanceForSchema(schema);
10314
+ const isValidSchema = ajvInstance.validateSchema(schema);
10315
+ let errors = "";
10316
+ if (ajvInstance.errors) {
10317
+ errors = ajvInstance.errors.map((e) => JSON.stringify(e, null, 2)).join("; ");
10318
+ } else if (isValidSchema) {
10016
10319
  try {
10017
- const parsed = JSON.parse(errorBody);
10018
- const message = parsed?.error?.message || errorBody;
10019
- throw new Error(`Gemini API error: ${response.status} - ${message}`);
10320
+ ajvInstance.compile(schema);
10321
+ return true;
10020
10322
  } catch (e) {
10021
- if (e instanceof Error && e.message.startsWith("Gemini API error:"))
10022
- throw e;
10023
- throw new Error(`Gemini API error: ${response.status} - ${errorBody}`);
10323
+ errors = (0, error_utils_1.getErrorMessageFromException)(e);
10024
10324
  }
10325
+ } else {
10326
+ errors = "unexpected-situation-no-errors-found-for-invalid-schema";
10025
10327
  }
10026
- const responseBody = await response.json();
10027
- const candidate = responseBody.candidates?.[0];
10028
- if (!candidate?.content?.parts) {
10029
- throw new Error(`Gemini API error: No content in response. Body: ${JSON.stringify(responseBody)}`);
10030
- }
10031
- const contentBlocks = [];
10032
- let hasToolCalls = false;
10033
- for (const part of candidate.content.parts) {
10034
- if (part.text) {
10035
- contentBlocks.push({ type: "text", text: part.text });
10328
+ return {
10329
+ kind: "validation-error",
10330
+ message: `Unable to validate JSON schema: ${errors}`
10331
+ };
10332
+ };
10333
+ validateJsonSchema.validateJsonSchema = validateJsonSchema$1;
10334
+ return validateJsonSchema;
10335
+ }
10336
+
10337
+ var stableStringify = {};
10338
+
10339
+ var hasRequiredStableStringify;
10340
+ function requireStableStringify() {
10341
+ if (hasRequiredStableStringify) return stableStringify;
10342
+ hasRequiredStableStringify = 1;
10343
+ Object.defineProperty(stableStringify, "__esModule", { value: true });
10344
+ stableStringify.stableStringify = stableStringify$1;
10345
+ function stableStringify$1(value) {
10346
+ const seen = /* @__PURE__ */ new WeakSet();
10347
+ const sorter = (_key, currentValue) => {
10348
+ if (currentValue && typeof currentValue === "object" && !Array.isArray(currentValue)) {
10349
+ if (seen.has(currentValue)) {
10350
+ return currentValue;
10351
+ }
10352
+ seen.add(currentValue);
10353
+ return Object.keys(currentValue).sort().reduce((acc, key) => {
10354
+ acc[key] = currentValue[key];
10355
+ return acc;
10356
+ }, {});
10036
10357
  }
10037
- if (part.functionCall) {
10038
- hasToolCalls = true;
10039
- contentBlocks.push({
10040
- type: "toolCall",
10041
- id: crypto.randomUUID(),
10042
- name: part.functionCall.name,
10043
- arguments: part.functionCall.args
10044
- });
10358
+ return currentValue;
10359
+ };
10360
+ return JSON.stringify(value, sorter) ?? "";
10361
+ }
10362
+ return stableStringify;
10363
+ }
10364
+
10365
+ var schemaUtils = {};
10366
+
10367
+ var hasRequiredSchemaUtils;
10368
+ function requireSchemaUtils() {
10369
+ if (hasRequiredSchemaUtils) return schemaUtils;
10370
+ hasRequiredSchemaUtils = 1;
10371
+ Object.defineProperty(schemaUtils, "__esModule", { value: true });
10372
+ return schemaUtils;
10373
+ }
10374
+
10375
+ var listSchema = {};
10376
+
10377
+ var hasRequiredListSchema;
10378
+ function requireListSchema() {
10379
+ if (hasRequiredListSchema) return listSchema;
10380
+ hasRequiredListSchema = 1;
10381
+ Object.defineProperty(listSchema, "__esModule", { value: true });
10382
+ listSchema.listSchema = listSchema$1;
10383
+ listSchema.parseListSchema = parseListSchema;
10384
+ function listSchema$1(itemSchema, keys) {
10385
+ const jsonSchema = {
10386
+ type: "array",
10387
+ items: {
10388
+ type: "object",
10389
+ required: [keys.primaryKey, keys.outputName],
10390
+ properties: {
10391
+ [keys.primaryKey]: {
10392
+ anyOf: [{ type: "string" }, { type: "number" }, { type: "integer" }]
10393
+ },
10394
+ [keys.outputName]: itemSchema
10395
+ }
10045
10396
  }
10397
+ };
10398
+ if (typeof itemSchema.$schema === "string") {
10399
+ jsonSchema.$schema = itemSchema.$schema;
10400
+ }
10401
+ return brand(keys, jsonSchema);
10402
+ }
10403
+ function parseListSchema(jsonSchema, keys) {
10404
+ if (isObject(jsonSchema) && jsonSchema.type === "array" && rowsHoldKeys(jsonSchema.items, keys)) {
10405
+ return brand(keys, jsonSchema);
10046
10406
  }
10047
- const usage = (0, provider_config_1.normalizeGeminiUsage)(responseBody.usageMetadata);
10048
- const stopReason = hasToolCalls ? "tool_use" : "stop";
10049
10407
  return {
10050
- message: {
10051
- role: "assistant",
10052
- content: contentBlocks
10053
- },
10054
- usage,
10055
- stopReason
10408
+ kind: "validation-error",
10409
+ message: `Not a list schema with rows keyed by "${keys.primaryKey}" holding "${keys.outputName}"`
10056
10410
  };
10057
10411
  }
10412
+ function rowsHoldKeys(items, keys) {
10413
+ if (!isObject(items) || !isObject(items.properties) || !Array.isArray(items.required)) {
10414
+ return false;
10415
+ }
10416
+ const { properties, required } = items;
10417
+ return [keys.primaryKey, keys.outputName].every((key) => key in properties && required.includes(key));
10418
+ }
10419
+ function brand(keys, jsonSchema) {
10420
+ return { primaryKey: keys.primaryKey, outputName: keys.outputName, jsonSchema };
10421
+ }
10422
+ function isObject(value) {
10423
+ return typeof value === "object" && value !== null && !Array.isArray(value);
10424
+ }
10425
+ return listSchema;
10426
+ }
10427
+
10428
+ var runAgent = {};
10429
+
10430
+ var agentLoop = {};
10431
+
10432
+ var hasRequiredAgentLoop;
10433
+ function requireAgentLoop() {
10434
+ if (hasRequiredAgentLoop) return agentLoop;
10435
+ hasRequiredAgentLoop = 1;
10436
+ Object.defineProperty(agentLoop, "__esModule", { value: true });
10437
+ agentLoop.runAgentLoop = runAgentLoop;
10438
+ const provider_config_1 = requireProviderConfig();
10058
10439
  function safeEmit(onEvent, event) {
10059
10440
  if (!onEvent)
10060
10441
  return;
@@ -10063,946 +10444,713 @@ function requireProviderFormatters() {
10063
10444
  } catch {
10064
10445
  }
10065
10446
  }
10066
- async function streamOpenAiAgenticResponse(response, errorLabel, onEvent) {
10067
- if (response.status !== 200) {
10068
- const errorBody = await response.text();
10447
+ async function runAgentLoop(params) {
10448
+ const messages = [...params.messages];
10449
+ let totalUsage = { ...provider_config_1.normalizedUsageZero };
10450
+ const toolsByName = new Map(params.tools.map((tool) => [tool.name, tool]));
10451
+ let completionReason = "max_turns";
10452
+ const onEvent = params.onEvent;
10453
+ for (let turn = 0; turn < params.maxTurns; turn++) {
10454
+ safeEmit(onEvent, { type: "turn_start", turn });
10455
+ let response;
10069
10456
  try {
10070
- const parsed = JSON.parse(errorBody);
10071
- const message = parsed?.error?.message || errorBody;
10072
- throw new Error(`${errorLabel} API error: ${response.status} - ${message}`);
10073
- } catch (e) {
10074
- if (e instanceof Error && e.message.startsWith(`${errorLabel} API error:`))
10075
- throw e;
10076
- throw new Error(`${errorLabel} API error: ${response.status} - ${errorBody}`);
10077
- }
10078
- }
10079
- if (!response.body) {
10080
- throw new Error(`${errorLabel} API error: Response body is null`);
10081
- }
10082
- let textContent = "";
10083
- const toolCallAccumulators = /* @__PURE__ */ new Map();
10084
- let finishReason = null;
10085
- let usage = { ...provider_config_1.normalizedUsageZero };
10086
- for await (const data of (0, sse_parser_1.parseSSEStream)(response.body)) {
10087
- let chunk;
10088
- try {
10089
- chunk = JSON.parse(data);
10090
- } catch {
10091
- continue;
10092
- }
10093
- if (chunk.usage) {
10094
- usage = (0, provider_config_1.normalizeOpenAiUsage)(chunk.usage);
10095
- }
10096
- const choice = chunk.choices?.[0];
10097
- if (!choice)
10098
- continue;
10099
- if (choice.finish_reason) {
10100
- finishReason = choice.finish_reason;
10101
- }
10102
- const delta = choice.delta;
10103
- if (!delta)
10104
- continue;
10105
- if (delta.content) {
10106
- textContent += delta.content;
10107
- safeEmit(onEvent, { type: "text_delta", text: delta.content });
10108
- }
10109
- if (delta.tool_calls) {
10110
- for (const tc of delta.tool_calls) {
10111
- const existing = toolCallAccumulators.get(tc.index);
10112
- if (existing) {
10113
- if (tc.function?.arguments) {
10114
- existing.arguments += tc.function.arguments;
10115
- }
10116
- } else {
10117
- toolCallAccumulators.set(tc.index, {
10118
- id: tc.id ?? "",
10119
- name: tc.function?.name ?? "",
10120
- arguments: tc.function?.arguments ?? ""
10121
- });
10122
- }
10123
- }
10457
+ response = await params.sendRequest(messages, params.tools, params.systemPrompt);
10458
+ } catch (error) {
10459
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
10460
+ safeEmit(onEvent, { type: "error", error: errorMessage });
10461
+ return buildAgentResult(messages, totalUsage, "error", errorMessage);
10124
10462
  }
10125
- }
10126
- const contentBlocks = [];
10127
- if (textContent) {
10128
- contentBlocks.push({ type: "text", text: textContent });
10129
- }
10130
- const sortedToolCalls = [...toolCallAccumulators.entries()].sort(([a], [b]) => a - b);
10131
- for (const [, tc] of sortedToolCalls) {
10132
- contentBlocks.push({
10133
- type: "toolCall",
10134
- id: tc.id,
10135
- name: tc.name,
10136
- arguments: parseOpenAiToolArguments(tc.arguments, tc.name, errorLabel)
10463
+ messages.push(response.message);
10464
+ totalUsage = addUsage(totalUsage, response.usage);
10465
+ safeEmit(onEvent, {
10466
+ type: "response_complete",
10467
+ message: response.message,
10468
+ usage: response.usage,
10469
+ stopReason: response.stopReason
10137
10470
  });
10138
- }
10139
- const stopReason2 = finishReason === "tool_calls" || sortedToolCalls.length > 0 ? "tool_use" : "stop";
10140
- return {
10141
- message: { role: "assistant", content: contentBlocks },
10142
- usage,
10143
- stopReason: stopReason2
10144
- };
10145
- }
10146
- async function streamGeminiAgenticResponse(response, onEvent) {
10147
- if (response.status !== 200) {
10148
- const errorBody = await response.text();
10149
- try {
10150
- const parsed = JSON.parse(errorBody);
10151
- const message = parsed?.error?.message || errorBody;
10152
- throw new Error(`Gemini API error: ${response.status} - ${message}`);
10153
- } catch (e) {
10154
- if (e instanceof Error && e.message.startsWith("Gemini API error:"))
10155
- throw e;
10156
- throw new Error(`Gemini API error: ${response.status} - ${errorBody}`);
10157
- }
10158
- }
10159
- if (!response.body) {
10160
- throw new Error("Gemini API error: Response body is null");
10161
- }
10162
- const contentBlocks = [];
10163
- let hasToolCalls2 = false;
10164
- let textContent2 = "";
10165
- let usage2 = { ...provider_config_1.normalizedUsageZero };
10166
- for await (const data of (0, sse_parser_1.parseSSEStream)(response.body)) {
10167
- let chunk;
10168
- try {
10169
- chunk = JSON.parse(data);
10170
- } catch {
10171
- continue;
10172
- }
10173
- if (chunk.usageMetadata) {
10174
- usage2 = (0, provider_config_1.normalizeGeminiUsage)(chunk.usageMetadata);
10471
+ const toolCalls = response.message.content.filter((c) => c.type === "toolCall");
10472
+ if (toolCalls.length === 0 || response.stopReason === "stop") {
10473
+ completionReason = "stop";
10474
+ return buildAgentResult(messages, totalUsage, completionReason);
10175
10475
  }
10176
- const candidate = chunk.candidates?.[0];
10177
- if (!candidate?.content?.parts)
10178
- continue;
10179
- for (const part of candidate.content.parts) {
10180
- if (part.text) {
10181
- textContent2 += part.text;
10182
- safeEmit(onEvent, { type: "text_delta", text: part.text });
10476
+ for (const toolCall of toolCalls) {
10477
+ safeEmit(onEvent, {
10478
+ type: "tool_start",
10479
+ toolCallId: toolCall.id,
10480
+ toolName: toolCall.name,
10481
+ arguments: toolCall.arguments
10482
+ });
10483
+ const tool = toolsByName.get(toolCall.name);
10484
+ if (!tool) {
10485
+ const result = {
10486
+ content: `Tool "${toolCall.name}" not found`,
10487
+ isError: true
10488
+ };
10489
+ messages.push({
10490
+ role: "toolResult",
10491
+ toolCallId: toolCall.id,
10492
+ toolName: toolCall.name,
10493
+ content: result.content,
10494
+ isError: true
10495
+ });
10496
+ safeEmit(onEvent, {
10497
+ type: "tool_complete",
10498
+ toolCallId: toolCall.id,
10499
+ toolName: toolCall.name,
10500
+ result
10501
+ });
10502
+ continue;
10183
10503
  }
10184
- if (part.functionCall) {
10185
- hasToolCalls2 = true;
10186
- contentBlocks.push({
10187
- type: "toolCall",
10188
- id: crypto.randomUUID(),
10189
- name: part.functionCall.name,
10190
- arguments: part.functionCall.args
10504
+ try {
10505
+ const result = await tool.execute(toolCall.arguments);
10506
+ messages.push({
10507
+ role: "toolResult",
10508
+ toolCallId: toolCall.id,
10509
+ toolName: toolCall.name,
10510
+ content: result.content,
10511
+ isError: result.isError ?? false
10512
+ });
10513
+ safeEmit(onEvent, {
10514
+ type: "tool_complete",
10515
+ toolCallId: toolCall.id,
10516
+ toolName: toolCall.name,
10517
+ result
10518
+ });
10519
+ } catch (error) {
10520
+ const errorContent = `Tool execution error: ${error instanceof Error ? error.message : "Unknown error"}`;
10521
+ const result = { content: errorContent, isError: true };
10522
+ messages.push({
10523
+ role: "toolResult",
10524
+ toolCallId: toolCall.id,
10525
+ toolName: toolCall.name,
10526
+ content: errorContent,
10527
+ isError: true
10528
+ });
10529
+ safeEmit(onEvent, {
10530
+ type: "tool_complete",
10531
+ toolCallId: toolCall.id,
10532
+ toolName: toolCall.name,
10533
+ result
10191
10534
  });
10192
10535
  }
10193
10536
  }
10194
10537
  }
10195
- if (textContent2) {
10196
- contentBlocks.unshift({ type: "text", text: textContent2 });
10197
- }
10198
- const stopReason3 = hasToolCalls2 ? "tool_use" : "stop";
10538
+ return buildAgentResult(messages, totalUsage, completionReason, `Reached maxTurns limit (${params.maxTurns}) before receiving a final response`);
10539
+ }
10540
+ function addUsage(a, b) {
10199
10541
  return {
10200
- message: { role: "assistant", content: contentBlocks },
10201
- usage: usage2,
10202
- stopReason: stopReason3
10542
+ promptTokens: a.promptTokens + b.promptTokens,
10543
+ completionTokens: a.completionTokens + b.completionTokens,
10544
+ totalTokens: a.totalTokens + b.totalTokens
10203
10545
  };
10204
10546
  }
10205
- return providerFormatters;
10206
- }
10207
-
10208
- var hasRequiredHttpLlmClient;
10209
- function requireHttpLlmClient() {
10210
- if (hasRequiredHttpLlmClient) return httpLlmClient;
10211
- hasRequiredHttpLlmClient = 1;
10212
- Object.defineProperty(httpLlmClient, "__esModule", { value: true });
10213
- httpLlmClient.HttpLlmClient = void 0;
10214
- const logger_1 = requireLogger();
10215
- const provider_config_1 = requireProviderConfig();
10216
- const provider_formatters_1 = requireProviderFormatters();
10217
- class HttpLlmClient {
10218
- constructor(providerId, apiKey, logger) {
10219
- this.providerId = providerId;
10220
- this.apiKey = apiKey;
10221
- this.logger = logger;
10222
- }
10223
- async sendRequest(prompt, model, maxTokens, assistantPrefillPrompt) {
10224
- const provider = this.getProviderConfig();
10225
- const requestOptions = provider.buildRequest({
10226
- prompt,
10227
- model,
10228
- maxTokens,
10229
- assistantPrefillPrompt
10230
- }, this.apiKey);
10231
- return this.executeProviderCall({
10232
- provider,
10233
- model,
10234
- maxTokens,
10235
- apiOperation: provider.apiOperation,
10236
- requestOptions,
10237
- parseResponse: provider.parseResponse,
10238
- getResponseLength: (parsedResponse) => parsedResponse.text.length,
10239
- getUsage: (parsedResponse) => parsedResponse.usage
10240
- });
10241
- }
10242
- async sendAgenticRequest(messages, tools, model, maxTokens, systemPrompt, onEvent) {
10243
- const provider = this.getProviderConfig();
10244
- const codec = this.getAgenticProviderCodec(provider.errorLabel);
10245
- const requestOptions = codec.buildRequest({
10246
- messages,
10247
- tools,
10248
- model,
10249
- maxTokens,
10250
- systemPrompt,
10251
- stream: !!onEvent
10252
- });
10253
- const parseResponse = onEvent ? (response) => codec.parseStreamingResponse(response, onEvent) : codec.parseResponse;
10254
- return this.executeProviderCall({
10255
- provider,
10256
- model,
10257
- maxTokens,
10258
- apiOperation: "agentic",
10259
- requestOptions,
10260
- parseResponse,
10261
- getResponseLength: (parsedResponse) => parsedResponse.message.content.reduce((total, content) => total + (content.type === "text" ? content.text.length : 0), 0),
10262
- getUsage: (parsedResponse) => parsedResponse.usage
10263
- });
10264
- }
10265
- getProviderConfig() {
10266
- const provider = provider_config_1.providerConfigs[this.providerId];
10267
- if (!provider) {
10268
- throw new Error(`Unknown LLM provider: ${this.providerId}`);
10269
- }
10270
- return provider;
10271
- }
10272
- async executeProviderCall(params) {
10273
- const method = params.requestOptions.method ?? "POST";
10274
- const headers = params.requestOptions.headers ?? {};
10275
- const body = this.toFetchableBody(params.requestOptions.body);
10276
- const spanId = (0, logger_1.generateSpanId)();
10277
- const startTime = Date.now();
10278
- (0, logger_1.logLlmCallStart)(this.logger, spanId, params.provider.providerId, params.apiOperation, {
10279
- url: params.requestOptions.loggableUrl ?? params.requestOptions.url,
10280
- httpMethod: method,
10281
- model: params.model,
10282
- maxTokens: params.maxTokens
10283
- });
10284
- try {
10285
- const response = await fetch(params.requestOptions.url, {
10286
- method,
10287
- headers,
10288
- body
10289
- });
10290
- const parsedResponse = await params.parseResponse(response);
10291
- const duration = Date.now() - startTime;
10292
- (0, logger_1.logLlmCallComplete)(this.logger, spanId, params.provider.providerId, params.apiOperation, {
10293
- status: response.status,
10294
- responseLength: params.getResponseLength(parsedResponse),
10295
- headers: Object.fromEntries(response.headers.entries()),
10296
- usage: params.getUsage(parsedResponse)
10297
- }, duration);
10298
- return parsedResponse;
10299
- } catch (error) {
10300
- const duration = Date.now() - startTime;
10301
- (0, logger_1.logLlmCallError)(this.logger, spanId, params.provider.providerId, params.apiOperation, error, duration);
10302
- throw new Error(`${params.provider.errorLabel} API request failed: ${error instanceof Error ? error.message : "Unknown error"}`);
10303
- }
10304
- }
10305
- toFetchableBody(bodyPayload) {
10306
- if (typeof bodyPayload === "string") {
10307
- return bodyPayload;
10308
- }
10309
- if (bodyPayload === void 0) {
10310
- return void 0;
10311
- }
10312
- return JSON.stringify(bodyPayload);
10313
- }
10314
- getAgenticProviderCodec(errorLabel) {
10315
- switch (this.providerId) {
10316
- case "groq":
10317
- return {
10318
- buildRequest: (options) => (0, provider_formatters_1.buildOpenAiAgenticRequest)(options, "https://api.groq.com/openai/v1/chat/completions", this.apiKey),
10319
- parseResponse: (response) => (0, provider_formatters_1.parseOpenAiAgenticResponse)(response, errorLabel),
10320
- parseStreamingResponse: (response, onEvent) => (0, provider_formatters_1.streamOpenAiAgenticResponse)(response, errorLabel, onEvent)
10321
- };
10322
- case "cerebras":
10323
- return {
10324
- buildRequest: (options) => (0, provider_formatters_1.buildOpenAiAgenticRequest)(options, "https://api.cerebras.ai/v1/chat/completions", this.apiKey),
10325
- parseResponse: (response) => (0, provider_formatters_1.parseOpenAiAgenticResponse)(response, errorLabel),
10326
- parseStreamingResponse: (response, onEvent) => (0, provider_formatters_1.streamOpenAiAgenticResponse)(response, errorLabel, onEvent)
10327
- };
10328
- case "openrouter":
10329
- return {
10330
- buildRequest: (options) => (0, provider_formatters_1.buildOpenAiAgenticRequest)(options, "https://openrouter.ai/api/v1/chat/completions", this.apiKey),
10331
- parseResponse: (response) => (0, provider_formatters_1.parseOpenAiAgenticResponse)(response, errorLabel),
10332
- parseStreamingResponse: (response, onEvent) => (0, provider_formatters_1.streamOpenAiAgenticResponse)(response, errorLabel, onEvent)
10333
- };
10334
- case "gemini":
10335
- return {
10336
- buildRequest: (options) => (0, provider_formatters_1.buildGeminiAgenticRequest)(options, this.apiKey),
10337
- parseResponse: provider_formatters_1.parseGeminiAgenticResponse,
10338
- parseStreamingResponse: (response, onEvent) => (0, provider_formatters_1.streamGeminiAgenticResponse)(response, onEvent)
10339
- };
10340
- default:
10341
- throw new Error(`Unsupported provider for agentic requests: ${this.providerId}`);
10342
- }
10547
+ function extractLastAssistantText(messages) {
10548
+ const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
10549
+ if (lastAssistant?.role !== "assistant") {
10550
+ return void 0;
10343
10551
  }
10552
+ const text = lastAssistant.content.filter((c) => c.type === "text").map((c) => c.text).join("");
10553
+ return text || void 0;
10554
+ }
10555
+ function buildAgentResult(messages, usage, completionReason, error) {
10556
+ const success = completionReason === "stop" && !error;
10557
+ return {
10558
+ success,
10559
+ completionReason,
10560
+ messages,
10561
+ textResponse: extractLastAssistantText(messages),
10562
+ usage,
10563
+ error
10564
+ };
10344
10565
  }
10345
- httpLlmClient.HttpLlmClient = HttpLlmClient;
10346
- return httpLlmClient;
10566
+ return agentLoop;
10347
10567
  }
10348
10568
 
10349
- var modelInfo = {};
10569
+ var agentTurn = {};
10350
10570
 
10351
- var hasRequiredModelInfo;
10352
- function requireModelInfo() {
10353
- if (hasRequiredModelInfo) return modelInfo;
10354
- hasRequiredModelInfo = 1;
10355
- (function(exports$1) {
10356
- Object.defineProperty(exports$1, "__esModule", { value: true });
10357
- exports$1.MODEL_ALIAS_NAMES = exports$1.MODEL_ALIASES = void 0;
10358
- exports$1.isLlmProviderId = isLlmProviderId;
10359
- exports$1.isModelAliasName = isModelAliasName;
10360
- exports$1.resolveModelInput = resolveModelInput;
10361
- const provider_config_1 = requireProviderConfig();
10362
- exports$1.MODEL_ALIASES = {
10363
- fast: [
10364
- { provider: "cerebras", model: "gpt-oss-120b" },
10365
- { provider: "groq", model: "openai/gpt-oss-120b" },
10366
- { provider: "openrouter", model: "openai/gpt-oss-120b" }
10367
- ],
10368
- gemini: [
10369
- { provider: "gemini", model: "gemini-2.5-flash" },
10370
- { provider: "openrouter", model: "google/gemini-2.5-flash" }
10371
- ],
10372
- qwen: [
10373
- { provider: "openrouter", model: "qwen/qwen3-vl-235b-a22b-instruct" }
10374
- ],
10375
- sonnet: [
10376
- { provider: "openrouter", model: "anthropic/claude-sonnet-4.6" }
10377
- ],
10378
- opus: [
10379
- { provider: "openrouter", model: "anthropic/claude-opus-4.7" }
10380
- ],
10381
- haiku: [
10382
- { provider: "openrouter", model: "anthropic/claude-haiku-4.5" }
10383
- ],
10384
- kimi: [
10385
- { provider: "openrouter", model: "moonshotai/kimi-k2.6" }
10386
- ],
10387
- gpt: [
10388
- { provider: "openrouter", model: "openai/gpt-5" }
10389
- ]
10390
- };
10391
- exports$1.MODEL_ALIAS_NAMES = Object.keys(exports$1.MODEL_ALIASES);
10392
- const MODEL_ALIAS_SET = new Set(exports$1.MODEL_ALIAS_NAMES);
10393
- const LLM_PROVIDER_ID_SET = new Set(provider_config_1.LLM_PROVIDER_IDS);
10394
- function isLlmProviderId(value) {
10395
- return typeof value === "string" && LLM_PROVIDER_ID_SET.has(value);
10396
- }
10397
- function isModelAliasName(value) {
10398
- return typeof value === "string" && MODEL_ALIAS_SET.has(value);
10399
- }
10400
- function validationError(message) {
10401
- return {
10402
- kind: "validation-error",
10403
- message
10404
- };
10405
- }
10406
- function validateProviderModel(input, configuredProviders) {
10407
- if (!isLlmProviderId(input.provider)) {
10408
- return validationError(`Invalid LLM provider: ${String(input.provider)}`);
10409
- }
10410
- if (!configuredProviders.has(input.provider)) {
10411
- return validationError(`LLM provider ${input.provider} is not configured. Please check your API keys.`);
10412
- }
10413
- if (typeof input.model !== "string") {
10414
- return validationError("model.model must be a string");
10415
- }
10416
- const model = input.model.trim();
10417
- if (!model) {
10418
- return validationError("model.model must be a non-empty string");
10419
- }
10420
- if (input.provider === "gemini" && model.startsWith("models/")) {
10421
- return validationError('Gemini model names must use bare model IDs, for example "gemini-2.5-flash", not "models/gemini-2.5-flash".');
10422
- }
10423
- return {
10424
- provider: input.provider,
10425
- model
10426
- };
10427
- }
10428
- function resolveModelInput(input, configuredProviders) {
10429
- if (input == null) {
10430
- return resolveModelInput({ alias: "fast" }, configuredProviders);
10431
- }
10432
- if (typeof input !== "object") {
10433
- return validationError("model must be an object with either alias or provider/model fields");
10434
- }
10435
- if ("provider" in input || "model" in input) {
10436
- return validateProviderModel(input, configuredProviders);
10437
- }
10438
- if ("alias" in input) {
10439
- const alias = input.alias;
10440
- if (!isModelAliasName(alias)) {
10441
- return validationError(`Invalid model alias: ${String(alias)}`);
10571
+ var providerFormatters = {};
10572
+
10573
+ var sseParser = {};
10574
+
10575
+ var hasRequiredSseParser;
10576
+ function requireSseParser() {
10577
+ if (hasRequiredSseParser) return sseParser;
10578
+ hasRequiredSseParser = 1;
10579
+ Object.defineProperty(sseParser, "__esModule", { value: true });
10580
+ sseParser.parseSSEStream = parseSSEStream;
10581
+ async function* parseSSEStream(stream) {
10582
+ const reader = stream.getReader();
10583
+ const decoder = new TextDecoder();
10584
+ let buffer = "";
10585
+ try {
10586
+ while (true) {
10587
+ const { done, value } = await reader.read();
10588
+ if (done)
10589
+ break;
10590
+ buffer += decoder.decode(value, { stream: true });
10591
+ const parts = buffer.split(/\r?\n\r?\n/);
10592
+ buffer = parts.pop();
10593
+ for (const part of parts) {
10594
+ for (const line of part.split(/\r?\n/)) {
10595
+ if (line.startsWith("data: ")) {
10596
+ const data = line.slice(6);
10597
+ if (data === "[DONE]") {
10598
+ return;
10599
+ }
10600
+ yield data;
10601
+ }
10602
+ }
10442
10603
  }
10443
- const configuredTarget = exports$1.MODEL_ALIASES[alias].find((target) => configuredProviders.has(target.provider));
10444
- if (!configuredTarget) {
10445
- return validationError(`No configured provider found for model alias ${alias}. Please check your API keys.`);
10604
+ }
10605
+ if (buffer.trim()) {
10606
+ for (const line of buffer.split(/\r?\n/)) {
10607
+ if (line.startsWith("data: ")) {
10608
+ const data = line.slice(6);
10609
+ if (data === "[DONE]") {
10610
+ return;
10611
+ }
10612
+ yield data;
10613
+ }
10446
10614
  }
10447
- return configuredTarget;
10448
10615
  }
10449
- return validationError("model must include either alias or provider/model fields");
10616
+ } finally {
10617
+ reader.releaseLock();
10450
10618
  }
10451
- })(modelInfo);
10452
- return modelInfo;
10453
- }
10454
-
10455
- var castArrayPromptBuilder = {};
10456
-
10457
- var promptTemplateUtils = {};
10458
-
10459
- var hasRequiredPromptTemplateUtils;
10460
- function requirePromptTemplateUtils() {
10461
- if (hasRequiredPromptTemplateUtils) return promptTemplateUtils;
10462
- hasRequiredPromptTemplateUtils = 1;
10463
- (function(exports$1) {
10464
- Object.defineProperty(exports$1, "__esModule", { value: true });
10465
- exports$1.replaceTemplateVarName = exports$1.templateVarName = void 0;
10466
- const templateVarName = (varName) => `<<{${varName}}>>`;
10467
- exports$1.templateVarName = templateVarName;
10468
- const replaceTemplateVarName = (str, varName, value) => str.replaceAll((0, exports$1.templateVarName)(varName), value);
10469
- exports$1.replaceTemplateVarName = replaceTemplateVarName;
10470
- })(promptTemplateUtils);
10471
- return promptTemplateUtils;
10472
- }
10473
-
10474
- var hasRequiredCastArrayPromptBuilder;
10475
- function requireCastArrayPromptBuilder() {
10476
- if (hasRequiredCastArrayPromptBuilder) return castArrayPromptBuilder;
10477
- hasRequiredCastArrayPromptBuilder = 1;
10478
- Object.defineProperty(castArrayPromptBuilder, "__esModule", { value: true });
10479
- const prompt_template_utils_1 = requirePromptTemplateUtils();
10480
- const builder = () => {
10481
- const BASE_SYSTEM_PROMPT_WHEN_OUTPUT_TYPE_NAME_PROVIDED = [
10482
- `Your task is to transform the provided <input-data> into a JSON array, that represents a list of`,
10483
- `${(0, prompt_template_utils_1.templateVarName)("OUTPUT_TYPE_NAME")}. The schema for this JSON array is defined below in <json-schema-spec> for you reference.`,
10484
- `It uses the JSON Schema spec.`,
10485
- `Respond with only the instance, not the schema. It should not be wrapped in any wrapper object -- return exactly what the JSON schema is expecting.`,
10486
- `Your entire response should be just the value of the JSON array containing objects with '${(0, prompt_template_utils_1.templateVarName)("OUTPUT_TYPE_NAME")}'`,
10487
- `and its primary key '${(0, prompt_template_utils_1.templateVarName)("PRIMARY_KEY")}', as valid JSON.`,
10488
- `We will validate the result with the AJV Node library, using ajv.parse, against the JSON schema, and we expect it to validate correctly.`,
10489
- `Note that the primary key of each element in the input-data is '${(0, prompt_template_utils_1.templateVarName)("PRIMARY_KEY")}'. In the output JSON array,`,
10490
- `each object should have the same key with the same value of the original data, so we can map the input element against the output element.`
10491
- ].join(" ");
10492
- const buildSystemPrompt = (primaryKey, outputName) => {
10493
- let a = BASE_SYSTEM_PROMPT_WHEN_OUTPUT_TYPE_NAME_PROVIDED;
10494
- a = (0, prompt_template_utils_1.replaceTemplateVarName)(a, "PRIMARY_KEY", primaryKey);
10495
- a = (0, prompt_template_utils_1.replaceTemplateVarName)(a, "OUTPUT_TYPE_NAME", outputName);
10496
- return a;
10497
- };
10498
- const buildFullPrompt = (req, uncachedInputDataRows) => {
10499
- const prompt = req.input.prompt;
10500
- const fullPrompt = [
10501
- prompt || "Transform the provided data according to the schema.",
10502
- // user prompt
10503
- buildSystemPrompt(req.input.primaryKey, req.output.name),
10504
- `<json-schema-spec> ${JSON.stringify(req.output.effectiveSchema)} </json-schema-spec>`,
10505
- `<input-data>${JSON.stringify(uncachedInputDataRows)}</input-data>`
10506
- ].join("\n");
10507
- return fullPrompt;
10508
- };
10509
- return {
10510
- buildSystemPrompt,
10511
- buildFullPrompt
10512
- };
10513
- };
10514
- const castArrayPromptBuilder$1 = builder();
10515
- castArrayPromptBuilder.default = castArrayPromptBuilder$1;
10516
- return castArrayPromptBuilder;
10619
+ }
10620
+ return sseParser;
10517
10621
  }
10518
10622
 
10519
- var castPromptBuilder = {};
10520
-
10521
- var hasRequiredCastPromptBuilder;
10522
- function requireCastPromptBuilder() {
10523
- if (hasRequiredCastPromptBuilder) return castPromptBuilder;
10524
- hasRequiredCastPromptBuilder = 1;
10525
- Object.defineProperty(castPromptBuilder, "__esModule", { value: true });
10526
- const prompt_template_utils_1 = requirePromptTemplateUtils();
10527
- const builder = () => {
10528
- const BASE_SYSTEM_PROMPT_WHEN_OUTPUT_TYPE_NAME_PROVIDED = [
10529
- `Please respond with a single JSON object that represents '${(0, prompt_template_utils_1.templateVarName)("OUTPUT_TYPE_NAME")}'.`,
10530
- `The schema for this object (which uses the JSON Schema spec), is defined below for your reference.`,
10531
- `Your task is to create an instance of this object. Respond with only the instance, not the schema.`,
10532
- `Your entire response should be just the value of '${(0, prompt_template_utils_1.templateVarName)("OUTPUT_TYPE_NAME")}', as valid JSON.`,
10533
- `And it should not be wrapped in any wrapper object -- return exactly what the JSON schema is expecting.`,
10534
- `We will validate the result with the AJV Node library, using ajv.parse, against the given schema, and we expect`,
10535
- `it to validate correctly.`,
10536
- `IMPORTANT: Output raw JSON only. No markdown code fences, no explanation, no commentary, no extra fields beyond the schema. Do not correct yourself -- produce the right answer on the first try.`
10537
- ].join(" ");
10538
- const buildSystemPrompt = (reqParams) => {
10539
- let a = BASE_SYSTEM_PROMPT_WHEN_OUTPUT_TYPE_NAME_PROVIDED;
10540
- a = (0, prompt_template_utils_1.replaceTemplateVarName)(a, "OUTPUT_TYPE_NAME", reqParams.output.name);
10541
- return a;
10623
+ var hasRequiredProviderFormatters;
10624
+ function requireProviderFormatters() {
10625
+ if (hasRequiredProviderFormatters) return providerFormatters;
10626
+ hasRequiredProviderFormatters = 1;
10627
+ Object.defineProperty(providerFormatters, "__esModule", { value: true });
10628
+ providerFormatters.buildOpenAiAgenticRequest = buildOpenAiAgenticRequest;
10629
+ providerFormatters.parseOpenAiAgenticResponse = parseOpenAiAgenticResponse;
10630
+ providerFormatters.buildGeminiAgenticRequest = buildGeminiAgenticRequest;
10631
+ providerFormatters.parseGeminiAgenticResponse = parseGeminiAgenticResponse;
10632
+ providerFormatters.streamOpenAiAgenticResponse = streamOpenAiAgenticResponse;
10633
+ providerFormatters.streamGeminiAgenticResponse = streamGeminiAgenticResponse;
10634
+ const provider_config_1 = requireProviderConfig();
10635
+ const sse_parser_1 = requireSseParser();
10636
+ function convertMessagesToOpenAi(messages, systemPrompt) {
10637
+ const result = [];
10638
+ if (systemPrompt) {
10639
+ result.push({ role: "system", content: systemPrompt });
10640
+ }
10641
+ for (const msg of messages) {
10642
+ switch (msg.role) {
10643
+ case "user":
10644
+ result.push({ role: "user", content: msg.content });
10645
+ break;
10646
+ case "assistant": {
10647
+ const textParts = msg.content.filter((c) => c.type === "text").map((c) => c.text).join("");
10648
+ const toolCalls = msg.content.filter((c) => c.type === "toolCall").map((c) => ({
10649
+ id: c.id,
10650
+ type: "function",
10651
+ function: {
10652
+ name: c.name,
10653
+ arguments: JSON.stringify(c.arguments)
10654
+ }
10655
+ }));
10656
+ const openAiMsg = {
10657
+ role: "assistant",
10658
+ content: textParts || null,
10659
+ ...toolCalls.length > 0 ? { tool_calls: toolCalls } : {}
10660
+ };
10661
+ result.push(openAiMsg);
10662
+ break;
10663
+ }
10664
+ case "toolResult":
10665
+ result.push({
10666
+ role: "tool",
10667
+ tool_call_id: msg.toolCallId,
10668
+ content: msg.content
10669
+ });
10670
+ break;
10671
+ }
10672
+ }
10673
+ return result;
10674
+ }
10675
+ function convertToolsToOpenAi(tools) {
10676
+ return tools.map((t) => ({
10677
+ type: "function",
10678
+ function: {
10679
+ name: t.name,
10680
+ description: t.description,
10681
+ parameters: t.parameters
10682
+ }
10683
+ }));
10684
+ }
10685
+ function buildOpenAiAgenticRequest(options, baseUrl, apiKey) {
10686
+ const messages = convertMessagesToOpenAi(options.messages, options.systemPrompt);
10687
+ const tools = convertToolsToOpenAi(options.tools);
10688
+ const body = {
10689
+ messages,
10690
+ tools: tools.length > 0 ? tools : void 0,
10691
+ model: options.model,
10692
+ max_completion_tokens: options.maxTokens,
10693
+ stream: options.stream ?? false
10542
10694
  };
10543
- const buildFullPrompt = (reqParams) => {
10544
- const { prompt, data } = reqParams.input;
10545
- const { effectiveSchema } = reqParams.output;
10546
- const fullPrompt = [
10547
- prompt || "Transform the provided data according to the schema.",
10548
- // user prompt
10549
- buildSystemPrompt(reqParams),
10550
- `<json-schema-spec> ${JSON.stringify(effectiveSchema)} </json-schema-spec>`,
10551
- data ? `<input-data>${JSON.stringify(data)}</input-data>` : ""
10552
- ].join("\n");
10553
- return fullPrompt;
10695
+ if (options.stream) {
10696
+ body.stream_options = { include_usage: true };
10697
+ }
10698
+ const headers = {
10699
+ "Content-Type": "application/json",
10700
+ Authorization: `Bearer ${apiKey}`
10554
10701
  };
10555
10702
  return {
10556
- buildSystemPrompt,
10557
- buildFullPrompt
10703
+ url: baseUrl,
10704
+ method: "POST",
10705
+ headers,
10706
+ body,
10707
+ loggableHeaders: {
10708
+ ...headers,
10709
+ Authorization: "Bearer [REDACTED]"
10710
+ },
10711
+ loggableBody: body
10558
10712
  };
10559
- };
10560
- const castPromptBuilder$1 = builder();
10561
- castPromptBuilder.default = castPromptBuilder$1;
10562
- return castPromptBuilder;
10563
- }
10564
-
10565
- var nuaErrors = {};
10566
-
10567
- var hasRequiredNuaErrors;
10568
- function requireNuaErrors() {
10569
- if (hasRequiredNuaErrors) return nuaErrors;
10570
- hasRequiredNuaErrors = 1;
10571
- Object.defineProperty(nuaErrors, "__esModule", { value: true });
10572
- nuaErrors.isNuaError = nuaErrors.isNuaInternalError = nuaErrors.isNuaValidationError = void 0;
10573
- const isNuaValidationError = (a) => {
10574
- return typeof a == "object" && a != null && "kind" in a && a.kind == "validation-error";
10575
- };
10576
- nuaErrors.isNuaValidationError = isNuaValidationError;
10577
- const isNuaInternalError = (a) => {
10578
- return typeof a == "object" && a != null && "kind" in a && a.kind == "internal-error";
10579
- };
10580
- nuaErrors.isNuaInternalError = isNuaInternalError;
10581
- const isNuaError = (a) => {
10582
- return typeof a == "object" && a != null && "kind" in a && (a.kind == "internal-error" || a.kind == "validation-error");
10583
- };
10584
- nuaErrors.isNuaError = isNuaError;
10585
- return nuaErrors;
10586
- }
10587
-
10588
- var agentLoop = {};
10589
-
10590
- var hasRequiredAgentLoop;
10591
- function requireAgentLoop() {
10592
- if (hasRequiredAgentLoop) return agentLoop;
10593
- hasRequiredAgentLoop = 1;
10594
- Object.defineProperty(agentLoop, "__esModule", { value: true });
10595
- agentLoop.runAgentLoop = runAgentLoop;
10596
- const provider_config_1 = requireProviderConfig();
10597
- function safeEmit(onEvent, event) {
10598
- if (!onEvent)
10599
- return;
10713
+ }
10714
+ function parseOpenAiToolArguments(rawArguments, toolName, errorLabel) {
10715
+ if (!rawArguments.trim()) {
10716
+ return {};
10717
+ }
10718
+ let parsed;
10600
10719
  try {
10601
- onEvent(event);
10720
+ parsed = JSON.parse(rawArguments);
10602
10721
  } catch {
10722
+ const preview = rawArguments.length > 500 ? rawArguments.slice(0, 500) + "..." : rawArguments;
10723
+ throw new Error(`${errorLabel} API error: Invalid JSON arguments for tool "${toolName}": ${preview}`);
10603
10724
  }
10725
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
10726
+ throw new Error(`${errorLabel} API error: Tool "${toolName}" arguments must be a JSON object`);
10727
+ }
10728
+ return parsed;
10604
10729
  }
10605
- async function runAgentLoop(params) {
10606
- const messages = [...params.messages];
10607
- let totalUsage = { ...provider_config_1.normalizedUsageZero };
10608
- const toolsByName = new Map(params.tools.map((tool) => [tool.name, tool]));
10609
- let completionReason = "max_turns";
10610
- const onEvent = params.onEvent;
10611
- for (let turn = 0; turn < params.maxTurns; turn++) {
10612
- safeEmit(onEvent, { type: "turn_start", turn });
10613
- let response;
10730
+ async function parseOpenAiAgenticResponse(response, errorLabel) {
10731
+ if (response.status !== 200) {
10732
+ const errorBody = await response.text();
10614
10733
  try {
10615
- response = await params.sendRequest(messages, params.tools, params.systemPrompt);
10616
- } catch (error) {
10617
- const errorMessage = error instanceof Error ? error.message : "Unknown error";
10618
- safeEmit(onEvent, { type: "error", error: errorMessage });
10619
- return buildAgentResult(messages, totalUsage, "error", errorMessage);
10734
+ const parsed = JSON.parse(errorBody);
10735
+ const message = parsed?.error?.message || errorBody;
10736
+ throw new Error(`${errorLabel} API error: ${response.status} - ${message}`);
10737
+ } catch (e) {
10738
+ if (e instanceof Error && e.message.startsWith(`${errorLabel} API error:`))
10739
+ throw e;
10740
+ throw new Error(`${errorLabel} API error: ${response.status} - ${errorBody}`);
10620
10741
  }
10621
- messages.push(response.message);
10622
- totalUsage = addUsage(totalUsage, response.usage);
10623
- safeEmit(onEvent, {
10624
- type: "response_complete",
10625
- message: response.message,
10626
- usage: response.usage,
10627
- stopReason: response.stopReason
10742
+ }
10743
+ const responseBody = await response.json();
10744
+ const choice = responseBody.choices?.[0];
10745
+ if (!choice?.message) {
10746
+ throw new Error(`${errorLabel} API error: No message in response. Body: ${JSON.stringify(responseBody)}`);
10747
+ }
10748
+ const contentBlocks = [];
10749
+ const assistantText = (0, provider_config_1.extractOpenAiMessageText)(choice.message);
10750
+ if (assistantText) {
10751
+ contentBlocks.push({
10752
+ type: "text",
10753
+ text: assistantText
10628
10754
  });
10629
- const toolCalls = response.message.content.filter((c) => c.type === "toolCall");
10630
- if (toolCalls.length === 0 || response.stopReason === "stop") {
10631
- completionReason = "stop";
10632
- return buildAgentResult(messages, totalUsage, completionReason);
10633
- }
10634
- for (const toolCall of toolCalls) {
10635
- safeEmit(onEvent, {
10636
- type: "tool_start",
10637
- toolCallId: toolCall.id,
10638
- toolName: toolCall.name,
10639
- arguments: toolCall.arguments
10755
+ }
10756
+ if (choice.message.tool_calls) {
10757
+ for (const tc of choice.message.tool_calls) {
10758
+ contentBlocks.push({
10759
+ type: "toolCall",
10760
+ id: tc.id,
10761
+ name: tc.function.name,
10762
+ arguments: parseOpenAiToolArguments(tc.function.arguments, tc.function.name, errorLabel)
10640
10763
  });
10641
- const tool = toolsByName.get(toolCall.name);
10642
- if (!tool) {
10643
- const result = {
10644
- content: `Tool "${toolCall.name}" not found`,
10645
- isError: true
10646
- };
10647
- messages.push({
10648
- role: "toolResult",
10649
- toolCallId: toolCall.id,
10650
- toolName: toolCall.name,
10651
- content: result.content,
10652
- isError: true
10653
- });
10654
- safeEmit(onEvent, {
10655
- type: "tool_complete",
10656
- toolCallId: toolCall.id,
10657
- toolName: toolCall.name,
10658
- result
10764
+ }
10765
+ }
10766
+ const usage = (0, provider_config_1.normalizeOpenAiUsage)(responseBody.usage);
10767
+ const stopReason = choice.finish_reason === "tool_calls" || choice.finish_reason === "stop" ? choice.finish_reason === "tool_calls" ? "tool_use" : "stop" : choice.message.tool_calls && choice.message.tool_calls.length > 0 ? "tool_use" : "stop";
10768
+ return {
10769
+ message: {
10770
+ role: "assistant",
10771
+ content: contentBlocks
10772
+ },
10773
+ usage,
10774
+ stopReason
10775
+ };
10776
+ }
10777
+ function convertMessagesToGemini(messages) {
10778
+ const result = [];
10779
+ for (const msg of messages) {
10780
+ switch (msg.role) {
10781
+ case "user":
10782
+ result.push({
10783
+ role: "user",
10784
+ parts: [{ text: msg.content }]
10659
10785
  });
10660
- continue;
10786
+ break;
10787
+ case "assistant": {
10788
+ const parts = [];
10789
+ for (const block of msg.content) {
10790
+ if (block.type === "text") {
10791
+ parts.push({ text: block.text });
10792
+ } else if (block.type === "toolCall") {
10793
+ parts.push({
10794
+ functionCall: {
10795
+ name: block.name,
10796
+ args: block.arguments
10797
+ }
10798
+ });
10799
+ }
10800
+ }
10801
+ result.push({ role: "model", parts });
10802
+ break;
10661
10803
  }
10662
- try {
10663
- const result = await tool.execute(toolCall.arguments);
10664
- messages.push({
10665
- role: "toolResult",
10666
- toolCallId: toolCall.id,
10667
- toolName: toolCall.name,
10668
- content: result.content,
10669
- isError: result.isError ?? false
10670
- });
10671
- safeEmit(onEvent, {
10672
- type: "tool_complete",
10673
- toolCallId: toolCall.id,
10674
- toolName: toolCall.name,
10675
- result
10676
- });
10677
- } catch (error) {
10678
- const errorContent = `Tool execution error: ${error instanceof Error ? error.message : "Unknown error"}`;
10679
- const result = { content: errorContent, isError: true };
10680
- messages.push({
10681
- role: "toolResult",
10682
- toolCallId: toolCall.id,
10683
- toolName: toolCall.name,
10684
- content: errorContent,
10685
- isError: true
10686
- });
10687
- safeEmit(onEvent, {
10688
- type: "tool_complete",
10689
- toolCallId: toolCall.id,
10690
- toolName: toolCall.name,
10691
- result
10804
+ case "toolResult": {
10805
+ result.push({
10806
+ role: "user",
10807
+ parts: [
10808
+ {
10809
+ functionResponse: {
10810
+ name: msg.toolName,
10811
+ response: { content: msg.content }
10812
+ }
10813
+ }
10814
+ ]
10692
10815
  });
10816
+ break;
10693
10817
  }
10694
10818
  }
10695
10819
  }
10696
- return buildAgentResult(messages, totalUsage, completionReason, `Reached maxTurns limit (${params.maxTurns}) before receiving a final response`);
10820
+ return result;
10697
10821
  }
10698
- function addUsage(a, b) {
10822
+ function convertToolsToGemini(tools) {
10823
+ if (tools.length === 0)
10824
+ return [];
10825
+ return [
10826
+ {
10827
+ functionDeclarations: tools.map((t) => ({
10828
+ name: t.name,
10829
+ description: t.description,
10830
+ parameters: t.parameters
10831
+ }))
10832
+ }
10833
+ ];
10834
+ }
10835
+ function buildGeminiAgenticRequest(options, apiKey) {
10836
+ const contents = convertMessagesToGemini(options.messages);
10837
+ const tools = convertToolsToGemini(options.tools);
10838
+ const body = {
10839
+ contents,
10840
+ generationConfig: {
10841
+ maxOutputTokens: options.maxTokens
10842
+ }
10843
+ };
10844
+ if (tools.length > 0) {
10845
+ body.tools = tools;
10846
+ }
10847
+ if (options.systemPrompt) {
10848
+ body.systemInstruction = {
10849
+ parts: [{ text: options.systemPrompt }]
10850
+ };
10851
+ }
10852
+ const action = options.stream ? "streamGenerateContent" : "generateContent";
10853
+ const altParam = options.stream ? "&alt=sse" : "";
10854
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/${options.model}:${action}?key=${apiKey}${altParam}`;
10699
10855
  return {
10700
- promptTokens: a.promptTokens + b.promptTokens,
10701
- completionTokens: a.completionTokens + b.completionTokens,
10702
- totalTokens: a.totalTokens + b.totalTokens
10856
+ url,
10857
+ method: "POST",
10858
+ headers: {
10859
+ "Content-Type": "application/json"
10860
+ },
10861
+ body,
10862
+ loggableUrl: url.replace(/key=[^&]+/, "key=[REDACTED]"),
10863
+ loggableBody: body
10703
10864
  };
10704
10865
  }
10705
- function extractLastAssistantText(messages) {
10706
- const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant");
10707
- if (lastAssistant?.role !== "assistant") {
10708
- return void 0;
10866
+ async function parseGeminiAgenticResponse(response) {
10867
+ if (response.status !== 200) {
10868
+ const errorBody = await response.text();
10869
+ try {
10870
+ const parsed = JSON.parse(errorBody);
10871
+ const message = parsed?.error?.message || errorBody;
10872
+ throw new Error(`Gemini API error: ${response.status} - ${message}`);
10873
+ } catch (e) {
10874
+ if (e instanceof Error && e.message.startsWith("Gemini API error:"))
10875
+ throw e;
10876
+ throw new Error(`Gemini API error: ${response.status} - ${errorBody}`);
10877
+ }
10709
10878
  }
10710
- const text = lastAssistant.content.filter((c) => c.type === "text").map((c) => c.text).join("");
10711
- return text || void 0;
10712
- }
10713
- function buildAgentResult(messages, usage, completionReason, error) {
10714
- const success = completionReason === "stop" && !error;
10879
+ const responseBody = await response.json();
10880
+ const candidate = responseBody.candidates?.[0];
10881
+ if (!candidate?.content?.parts) {
10882
+ throw new Error(`Gemini API error: No content in response. Body: ${JSON.stringify(responseBody)}`);
10883
+ }
10884
+ const contentBlocks = [];
10885
+ let hasToolCalls = false;
10886
+ for (const part of candidate.content.parts) {
10887
+ if (part.text) {
10888
+ contentBlocks.push({ type: "text", text: part.text });
10889
+ }
10890
+ if (part.functionCall) {
10891
+ hasToolCalls = true;
10892
+ contentBlocks.push({
10893
+ type: "toolCall",
10894
+ id: crypto.randomUUID(),
10895
+ name: part.functionCall.name,
10896
+ arguments: part.functionCall.args
10897
+ });
10898
+ }
10899
+ }
10900
+ const usage = (0, provider_config_1.normalizeGeminiUsage)(responseBody.usageMetadata);
10901
+ const stopReason = hasToolCalls ? "tool_use" : "stop";
10715
10902
  return {
10716
- success,
10717
- completionReason,
10718
- messages,
10719
- textResponse: extractLastAssistantText(messages),
10903
+ message: {
10904
+ role: "assistant",
10905
+ content: contentBlocks
10906
+ },
10720
10907
  usage,
10721
- error
10908
+ stopReason
10722
10909
  };
10723
10910
  }
10724
- return agentLoop;
10725
- }
10726
-
10727
- var hasRequiredNuaLlmClient;
10728
- function requireNuaLlmClient() {
10729
- if (hasRequiredNuaLlmClient) return nuaLlmClient;
10730
- hasRequiredNuaLlmClient = 1;
10731
- var __importDefault = nuaLlmClient && nuaLlmClient.__importDefault || function(mod) {
10732
- return mod && mod.__esModule ? mod : { "default": mod };
10733
- };
10734
- Object.defineProperty(nuaLlmClient, "__esModule", { value: true });
10735
- nuaLlmClient.NuaLlmClient = void 0;
10736
- const logger_1 = requireLogger();
10737
- const validate_mappable_input_data_1 = requireValidateMappableInputData();
10738
- const call_llm_service_1 = requireCallLlmService();
10739
- const nua_json_schema_value_validation_1 = requireNuaJsonSchemaValueValidation();
10740
- const http_llm_client_1 = requireHttpLlmClient();
10741
- const provider_config_1 = requireProviderConfig();
10742
- const model_info_1 = requireModelInfo();
10743
- const cast_array_prompt_builder_1 = __importDefault(requireCastArrayPromptBuilder());
10744
- const cast_prompt_builder_1 = __importDefault(requireCastPromptBuilder());
10745
- const nua_errors_1 = requireNuaErrors();
10746
- const agent_loop_1 = requireAgentLoop();
10747
- class NuaLlmClient {
10748
- constructor(config) {
10749
- this.logger = config.logger || new logger_1.ConsoleLogger();
10750
- this.clients = /* @__PURE__ */ new Map();
10751
- Object.entries(config.providers).forEach(([providerId, providerConfig]) => {
10752
- if (providerConfig?.apiKey) {
10753
- this.clients.set(providerId, new http_llm_client_1.HttpLlmClient(providerId, providerConfig.apiKey, this.logger));
10754
- }
10755
- });
10911
+ function safeEmit(onEvent, event) {
10912
+ if (!onEvent)
10913
+ return;
10914
+ try {
10915
+ onEvent(event);
10916
+ } catch {
10917
+ }
10918
+ }
10919
+ async function streamOpenAiAgenticResponse(response, errorLabel, onEvent) {
10920
+ if (response.status !== 200) {
10921
+ const errorBody = await response.text();
10922
+ try {
10923
+ const parsed = JSON.parse(errorBody);
10924
+ const message = parsed?.error?.message || errorBody;
10925
+ throw new Error(`${errorLabel} API error: ${response.status} - ${message}`);
10926
+ } catch (e) {
10927
+ if (e instanceof Error && e.message.startsWith(`${errorLabel} API error:`))
10928
+ throw e;
10929
+ throw new Error(`${errorLabel} API error: ${response.status} - ${errorBody}`);
10930
+ }
10756
10931
  }
10757
- getConfiguredProviderIds() {
10758
- return new Set(this.clients.keys());
10932
+ if (!response.body) {
10933
+ throw new Error(`${errorLabel} API error: Response body is null`);
10759
10934
  }
10760
- resolveModel(model) {
10761
- const result = (0, model_info_1.resolveModelInput)(model, this.getConfiguredProviderIds());
10762
- if ((0, nua_errors_1.isNuaValidationError)(result)) {
10763
- throw new Error(result.message);
10935
+ let textContent = "";
10936
+ const toolCallAccumulators = /* @__PURE__ */ new Map();
10937
+ let finishReason = null;
10938
+ let usage = { ...provider_config_1.normalizedUsageZero };
10939
+ for await (const data of (0, sse_parser_1.parseSSEStream)(response.body)) {
10940
+ let chunk;
10941
+ try {
10942
+ chunk = JSON.parse(data);
10943
+ } catch {
10944
+ continue;
10945
+ }
10946
+ if (chunk.usage) {
10947
+ usage = (0, provider_config_1.normalizeOpenAiUsage)(chunk.usage);
10948
+ }
10949
+ const choice = chunk.choices?.[0];
10950
+ if (!choice)
10951
+ continue;
10952
+ if (choice.finish_reason) {
10953
+ finishReason = choice.finish_reason;
10954
+ }
10955
+ const delta = choice.delta;
10956
+ if (!delta)
10957
+ continue;
10958
+ if (delta.content) {
10959
+ textContent += delta.content;
10960
+ safeEmit(onEvent, { type: "text_delta", text: delta.content });
10961
+ }
10962
+ if (delta.tool_calls) {
10963
+ for (const tc of delta.tool_calls) {
10964
+ const existing = toolCallAccumulators.get(tc.index);
10965
+ if (existing) {
10966
+ if (tc.function?.arguments) {
10967
+ existing.arguments += tc.function.arguments;
10968
+ }
10969
+ } else {
10970
+ toolCallAccumulators.set(tc.index, {
10971
+ id: tc.id ?? "",
10972
+ name: tc.function?.name ?? "",
10973
+ arguments: tc.function?.arguments ?? ""
10974
+ });
10975
+ }
10976
+ }
10764
10977
  }
10765
- return result;
10766
10978
  }
10767
- getClientForProvider(provider) {
10768
- const client = this.clients.get(provider);
10769
- if (!client) {
10770
- throw new Error(`LLM provider ${provider} is not configured. Please check your API keys.`);
10771
- }
10772
- return client;
10979
+ const contentBlocks = [];
10980
+ if (textContent) {
10981
+ contentBlocks.push({ type: "text", text: textContent });
10773
10982
  }
10774
- async castValue(params) {
10775
- const providerModel = this.resolveModel(params.model);
10776
- const client = this.getClientForProvider(providerModel.provider);
10777
- const maxTokens = params.maxTokens || 4096;
10778
- const temperature = params.temperature ?? 0;
10779
- const prompt = cast_prompt_builder_1.default.buildFullPrompt(params);
10780
- const validationFn = (0, nua_json_schema_value_validation_1.buildNuaJsonSchemaValueValidation)("library-req", params.output.effectiveSchema);
10781
- try {
10782
- const result = await (0, call_llm_service_1.callLLM)(
10783
- client,
10784
- prompt,
10785
- providerModel.model,
10786
- maxTokens,
10787
- temperature,
10788
- 3,
10789
- // default retries
10790
- validationFn
10791
- );
10792
- return {
10793
- success: true,
10794
- data: result.data,
10795
- usage: result.usage,
10796
- prompt
10797
- };
10798
- } catch (e) {
10799
- return {
10800
- success: false,
10801
- error: e instanceof Error ? e.message : String(e),
10802
- prompt
10803
- };
10804
- }
10983
+ const sortedToolCalls = [...toolCallAccumulators.entries()].sort(([a], [b]) => a - b);
10984
+ for (const [, tc] of sortedToolCalls) {
10985
+ contentBlocks.push({
10986
+ type: "toolCall",
10987
+ id: tc.id,
10988
+ name: tc.name,
10989
+ arguments: parseOpenAiToolArguments(tc.arguments, tc.name, errorLabel)
10990
+ });
10805
10991
  }
10806
- async castArray(params) {
10807
- const providerModel = this.resolveModel(params.model);
10808
- const client = this.getClientForProvider(providerModel.provider);
10809
- const maxTokens = params.maxTokens || 4096;
10810
- const temperature = params.temperature ?? 0;
10811
- const validData = (0, validate_mappable_input_data_1.validateMappableInputData)(params.data, params.input.primaryKey);
10812
- if ((0, nua_errors_1.isNuaValidationError)(validData)) {
10813
- return {
10814
- success: false,
10815
- error: validData.message
10816
- };
10817
- }
10818
- const prompt = cast_array_prompt_builder_1.default.buildFullPrompt(params, validData);
10819
- const validationFn = (0, nua_json_schema_value_validation_1.buildNuaJsonSchemaValueValidation)("library-req", params.output.effectiveSchema);
10992
+ const stopReason2 = finishReason === "tool_calls" || sortedToolCalls.length > 0 ? "tool_use" : "stop";
10993
+ return {
10994
+ message: { role: "assistant", content: contentBlocks },
10995
+ usage,
10996
+ stopReason: stopReason2
10997
+ };
10998
+ }
10999
+ async function streamGeminiAgenticResponse(response, onEvent) {
11000
+ if (response.status !== 200) {
11001
+ const errorBody = await response.text();
10820
11002
  try {
10821
- const result = await (0, call_llm_service_1.callLLM)(client, prompt, providerModel.model, maxTokens, temperature, 3, validationFn);
10822
- return {
10823
- success: true,
10824
- data: result.data,
10825
- usage: result.usage,
10826
- prompt
10827
- };
11003
+ const parsed = JSON.parse(errorBody);
11004
+ const message = parsed?.error?.message || errorBody;
11005
+ throw new Error(`Gemini API error: ${response.status} - ${message}`);
10828
11006
  } catch (e) {
10829
- return {
10830
- success: false,
10831
- error: e instanceof Error ? e.message : String(e),
10832
- prompt
10833
- };
11007
+ if (e instanceof Error && e.message.startsWith("Gemini API error:"))
11008
+ throw e;
11009
+ throw new Error(`Gemini API error: ${response.status} - ${errorBody}`);
10834
11010
  }
10835
11011
  }
10836
- async runAgent(params) {
11012
+ if (!response.body) {
11013
+ throw new Error("Gemini API error: Response body is null");
11014
+ }
11015
+ const contentBlocks = [];
11016
+ let hasToolCalls2 = false;
11017
+ let textContent2 = "";
11018
+ let usage2 = { ...provider_config_1.normalizedUsageZero };
11019
+ for await (const data of (0, sse_parser_1.parseSSEStream)(response.body)) {
11020
+ let chunk;
10837
11021
  try {
10838
- const providerModel = this.resolveModel(params.model);
10839
- const client = this.getClientForProvider(providerModel.provider);
10840
- const maxTokens = params.maxTokens ?? 4096;
10841
- const maxTurns = params.maxTurns ?? 10;
10842
- return (0, agent_loop_1.runAgentLoop)({
10843
- messages: params.messages,
10844
- tools: params.tools,
10845
- systemPrompt: params.systemPrompt,
10846
- maxTurns,
10847
- onEvent: params.onEvent,
10848
- sendRequest: (messages, tools, systemPrompt) => client.sendAgenticRequest(messages, tools, providerModel.model, maxTokens, systemPrompt, params.onEvent)
10849
- });
10850
- } catch (e) {
10851
- return {
10852
- success: false,
10853
- completionReason: "error",
10854
- messages: params.messages,
10855
- usage: provider_config_1.normalizedUsageZero,
10856
- error: e instanceof Error ? e.message : String(e)
10857
- };
11022
+ chunk = JSON.parse(data);
11023
+ } catch {
11024
+ continue;
11025
+ }
11026
+ if (chunk.usageMetadata) {
11027
+ usage2 = (0, provider_config_1.normalizeGeminiUsage)(chunk.usageMetadata);
11028
+ }
11029
+ const candidate = chunk.candidates?.[0];
11030
+ if (!candidate?.content?.parts)
11031
+ continue;
11032
+ for (const part of candidate.content.parts) {
11033
+ if (part.text) {
11034
+ textContent2 += part.text;
11035
+ safeEmit(onEvent, { type: "text_delta", text: part.text });
11036
+ }
11037
+ if (part.functionCall) {
11038
+ hasToolCalls2 = true;
11039
+ contentBlocks.push({
11040
+ type: "toolCall",
11041
+ id: crypto.randomUUID(),
11042
+ name: part.functionCall.name,
11043
+ arguments: part.functionCall.args
11044
+ });
11045
+ }
10858
11046
  }
10859
11047
  }
10860
- }
10861
- nuaLlmClient.NuaLlmClient = NuaLlmClient;
10862
- return nuaLlmClient;
10863
- }
10864
-
10865
- var types = {};
10866
-
10867
- var hasRequiredTypes;
10868
- function requireTypes() {
10869
- if (hasRequiredTypes) return types;
10870
- hasRequiredTypes = 1;
10871
- Object.defineProperty(types, "__esModule", { value: true });
10872
- return types;
10873
- }
10874
-
10875
- var validateJsonSchema = {};
10876
-
10877
- var errorUtils = {};
10878
-
10879
- var hasRequiredErrorUtils;
10880
- function requireErrorUtils() {
10881
- if (hasRequiredErrorUtils) return errorUtils;
10882
- hasRequiredErrorUtils = 1;
10883
- Object.defineProperty(errorUtils, "__esModule", { value: true });
10884
- errorUtils.getErrorMessageFromException = getErrorMessageFromException;
10885
- function isErrorWithMessage(error) {
10886
- return typeof error === "object" && error !== null && "message" in error && typeof error.message === "string";
10887
- }
10888
- function getErrorMessageFromException(error) {
10889
- if (isErrorWithMessage(error)) {
10890
- return error.message;
11048
+ if (textContent2) {
11049
+ contentBlocks.unshift({ type: "text", text: textContent2 });
10891
11050
  }
10892
- return String(error);
11051
+ const stopReason3 = hasToolCalls2 ? "tool_use" : "stop";
11052
+ return {
11053
+ message: { role: "assistant", content: contentBlocks },
11054
+ usage: usage2,
11055
+ stopReason: stopReason3
11056
+ };
10893
11057
  }
10894
- return errorUtils;
11058
+ return providerFormatters;
10895
11059
  }
10896
11060
 
10897
- var hasRequiredValidateJsonSchema;
10898
- function requireValidateJsonSchema() {
10899
- if (hasRequiredValidateJsonSchema) return validateJsonSchema;
10900
- hasRequiredValidateJsonSchema = 1;
10901
- Object.defineProperty(validateJsonSchema, "__esModule", { value: true });
10902
- validateJsonSchema.validateJsonSchema = void 0;
10903
- const error_utils_1 = requireErrorUtils();
10904
- const ajv_instances_1 = requireAjvInstances();
10905
- const validateJsonSchema$1 = (schema) => {
10906
- if (!schema || typeof schema !== "object") {
10907
- return {
10908
- kind: "validation-error",
10909
- message: "Schema must be a valid object"
10910
- };
11061
+ var hasRequiredAgentTurn;
11062
+ function requireAgentTurn() {
11063
+ if (hasRequiredAgentTurn) return agentTurn;
11064
+ hasRequiredAgentTurn = 1;
11065
+ Object.defineProperty(agentTurn, "__esModule", { value: true });
11066
+ agentTurn.sendAgentTurn = sendAgentTurn;
11067
+ const provider_call_1 = requireProviderCall();
11068
+ const provider_config_1 = requireProviderConfig();
11069
+ const provider_formatters_1 = requireProviderFormatters();
11070
+ async function sendAgentTurn(turn, logger) {
11071
+ const codec = agentCodec(turn.target);
11072
+ const onEvent = turn.onEvent;
11073
+ const call = await (0, provider_call_1.callProvider)({
11074
+ target: turn.target,
11075
+ apiOperation: "agentic",
11076
+ maxTokens: turn.maxTokens,
11077
+ request: codec.buildRequest({
11078
+ messages: turn.messages,
11079
+ tools: turn.tools,
11080
+ model: turn.target.model,
11081
+ maxTokens: turn.maxTokens,
11082
+ systemPrompt: turn.systemPrompt,
11083
+ stream: onEvent !== void 0
11084
+ }),
11085
+ parseResponse: onEvent ? (response) => codec.parseStreamingResponse(response, onEvent) : codec.parseResponse,
11086
+ responseLength: (reply) => reply.message.content.reduce((total, content) => total + (content.type === "text" ? content.text.length : 0), 0),
11087
+ usage: (reply) => reply.usage
11088
+ }, logger);
11089
+ if (call.kind === "failed") {
11090
+ throw new Error(call.message);
10911
11091
  }
10912
- const ajvInstance = ajv_instances_1.ajvInstances.getInstanceForSchema(schema);
10913
- const isValidSchema = ajvInstance.validateSchema(schema);
10914
- let errors = "";
10915
- if (ajvInstance.errors) {
10916
- errors = ajvInstance.errors.map((e) => JSON.stringify(e, null, 2)).join("; ");
10917
- } else if (isValidSchema) {
10918
- try {
10919
- ajvInstance.compile(schema);
10920
- return true;
10921
- } catch (e) {
10922
- errors = (0, error_utils_1.getErrorMessageFromException)(e);
10923
- }
10924
- } else {
10925
- errors = "unexpected-situation-no-errors-found-for-invalid-schema";
11092
+ return call.reply;
11093
+ }
11094
+ function agentCodec({ provider, apiKey }) {
11095
+ const errorLabel = provider_config_1.providerConfigs[provider].errorLabel;
11096
+ switch (provider) {
11097
+ case "groq":
11098
+ return openAiStyleCodec("https://api.groq.com/openai/v1/chat/completions", apiKey, errorLabel);
11099
+ case "cerebras":
11100
+ return openAiStyleCodec("https://api.cerebras.ai/v1/chat/completions", apiKey, errorLabel);
11101
+ case "openrouter":
11102
+ return openAiStyleCodec("https://openrouter.ai/api/v1/chat/completions", apiKey, errorLabel);
11103
+ case "gemini":
11104
+ return {
11105
+ buildRequest: (options) => (0, provider_formatters_1.buildGeminiAgenticRequest)(options, apiKey),
11106
+ parseResponse: provider_formatters_1.parseGeminiAgenticResponse,
11107
+ parseStreamingResponse: provider_formatters_1.streamGeminiAgenticResponse
11108
+ };
10926
11109
  }
11110
+ }
11111
+ function openAiStyleCodec(url, apiKey, errorLabel) {
10927
11112
  return {
10928
- kind: "validation-error",
10929
- message: `Unable to validate JSON schema: ${errors}`
10930
- };
10931
- };
10932
- validateJsonSchema.validateJsonSchema = validateJsonSchema$1;
10933
- return validateJsonSchema;
10934
- }
10935
-
10936
- var stableStringify = {};
10937
-
10938
- var hasRequiredStableStringify;
10939
- function requireStableStringify() {
10940
- if (hasRequiredStableStringify) return stableStringify;
10941
- hasRequiredStableStringify = 1;
10942
- Object.defineProperty(stableStringify, "__esModule", { value: true });
10943
- stableStringify.stableStringify = stableStringify$1;
10944
- function stableStringify$1(value) {
10945
- const seen = /* @__PURE__ */ new WeakSet();
10946
- const sorter = (_key, currentValue) => {
10947
- if (currentValue && typeof currentValue === "object" && !Array.isArray(currentValue)) {
10948
- if (seen.has(currentValue)) {
10949
- return currentValue;
10950
- }
10951
- seen.add(currentValue);
10952
- return Object.keys(currentValue).sort().reduce((acc, key) => {
10953
- acc[key] = currentValue[key];
10954
- return acc;
10955
- }, {});
10956
- }
10957
- return currentValue;
11113
+ buildRequest: (options) => (0, provider_formatters_1.buildOpenAiAgenticRequest)(options, url, apiKey),
11114
+ parseResponse: (response) => (0, provider_formatters_1.parseOpenAiAgenticResponse)(response, errorLabel),
11115
+ parseStreamingResponse: (response, onEvent) => (0, provider_formatters_1.streamOpenAiAgenticResponse)(response, errorLabel, onEvent)
10958
11116
  };
10959
- return JSON.stringify(value, sorter) ?? "";
10960
11117
  }
10961
- return stableStringify;
11118
+ return agentTurn;
10962
11119
  }
10963
11120
 
10964
- var schemaUtils = {};
10965
-
10966
- var hasRequiredSchemaUtils;
10967
- function requireSchemaUtils() {
10968
- if (hasRequiredSchemaUtils) return schemaUtils;
10969
- hasRequiredSchemaUtils = 1;
10970
- Object.defineProperty(schemaUtils, "__esModule", { value: true });
10971
- schemaUtils.wrapArraySchema = wrapArraySchema;
10972
- schemaUtils.unwrapArraySchema = unwrapArraySchema;
10973
- function wrapArraySchema(schema, options) {
10974
- const { primaryKey, outputName } = options;
10975
- const schemaVersion = typeof schema.$schema === "string" ? schema.$schema : void 0;
10976
- const wrappedSchema = {
10977
- type: "array",
10978
- items: {
10979
- type: "object",
10980
- required: [primaryKey, outputName],
10981
- properties: {
10982
- [primaryKey]: {
10983
- anyOf: [{ type: "string" }, { type: "number" }, { type: "integer" }]
10984
- },
10985
- [outputName]: schema
10986
- }
10987
- }
10988
- };
10989
- if (schemaVersion) {
10990
- wrappedSchema.$schema = schemaVersion;
10991
- }
10992
- return wrappedSchema;
10993
- }
10994
- function unwrapArraySchema(wrappedSchema, outputName) {
10995
- const items = wrappedSchema.items;
10996
- if (!items || typeof items !== "object") {
10997
- return null;
10998
- }
10999
- const properties = items.properties;
11000
- if (!properties || typeof properties !== "object") {
11001
- return null;
11121
+ var hasRequiredRunAgent;
11122
+ function requireRunAgent() {
11123
+ if (hasRequiredRunAgent) return runAgent;
11124
+ hasRequiredRunAgent = 1;
11125
+ Object.defineProperty(runAgent, "__esModule", { value: true });
11126
+ runAgent.runAgent = runAgent$1;
11127
+ const provider_config_1 = requireProviderConfig();
11128
+ const agent_loop_1 = requireAgentLoop();
11129
+ const agent_turn_1 = requireAgentTurn();
11130
+ const DEFAULT_MAX_TOKENS = 4096;
11131
+ const DEFAULT_MAX_TURNS = 10;
11132
+ async function runAgent$1(providers, params) {
11133
+ const resolved = providers.resolve(params.model);
11134
+ if (resolved.kind === "unroutable") {
11135
+ return {
11136
+ success: false,
11137
+ completionReason: "error",
11138
+ messages: params.messages,
11139
+ usage: provider_config_1.normalizedUsageZero,
11140
+ error: resolved.message
11141
+ };
11002
11142
  }
11003
- return properties[outputName] ?? null;
11143
+ const maxTokens = params.maxTokens ?? DEFAULT_MAX_TOKENS;
11144
+ return (0, agent_loop_1.runAgentLoop)({
11145
+ messages: params.messages,
11146
+ tools: params.tools,
11147
+ systemPrompt: params.systemPrompt,
11148
+ maxTurns: params.maxTurns ?? DEFAULT_MAX_TURNS,
11149
+ onEvent: params.onEvent,
11150
+ sendRequest: (messages, tools, systemPrompt) => (0, agent_turn_1.sendAgentTurn)({ target: resolved.target, maxTokens, messages, tools, systemPrompt, onEvent: params.onEvent }, providers.logger)
11151
+ });
11004
11152
  }
11005
- return schemaUtils;
11153
+ return runAgent;
11006
11154
  }
11007
11155
 
11008
11156
  var hasRequiredDist;
@@ -11026,23 +11174,12 @@ function requireDist() {
11026
11174
  var __exportStar = dist && dist.__exportStar || function(m, exports2) {
11027
11175
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p);
11028
11176
  };
11029
- var __importDefault = dist && dist.__importDefault || function(mod) {
11030
- return mod && mod.__esModule ? mod : { "default": mod };
11031
- };
11032
11177
  Object.defineProperty(exports$1, "__esModule", { value: true });
11033
- exports$1.runAgentLoop = exports$1.stableStringify = exports$1.validateJsonSchema = exports$1.providerConfigs = exports$1.normalizedUsageZero = exports$1.LLM_PROVIDER_IDS = exports$1.castArrayPromptBuilder = exports$1.castPromptBuilder = void 0;
11178
+ exports$1.runAgentLoop = exports$1.runAgent = exports$1.parseListSchema = exports$1.listSchema = exports$1.stableStringify = exports$1.validateJsonSchema = exports$1.providerEngines = exports$1.providerConfigs = exports$1.normalizedUsageZero = exports$1.LLM_PROVIDER_IDS = void 0;
11034
11179
  __exportStar(requireNuaLlmClient(), exports$1);
11035
11180
  __exportStar(requireLogger(), exports$1);
11036
11181
  __exportStar(requireTypes(), exports$1);
11037
11182
  __exportStar(requireNuaErrors(), exports$1);
11038
- var cast_prompt_builder_1 = requireCastPromptBuilder();
11039
- Object.defineProperty(exports$1, "castPromptBuilder", { enumerable: true, get: function() {
11040
- return __importDefault(cast_prompt_builder_1).default;
11041
- } });
11042
- var cast_array_prompt_builder_1 = requireCastArrayPromptBuilder();
11043
- Object.defineProperty(exports$1, "castArrayPromptBuilder", { enumerable: true, get: function() {
11044
- return __importDefault(cast_array_prompt_builder_1).default;
11045
- } });
11046
11183
  var provider_config_1 = requireProviderConfig();
11047
11184
  Object.defineProperty(exports$1, "LLM_PROVIDER_IDS", { enumerable: true, get: function() {
11048
11185
  return provider_config_1.LLM_PROVIDER_IDS;
@@ -11053,6 +11190,10 @@ function requireDist() {
11053
11190
  Object.defineProperty(exports$1, "providerConfigs", { enumerable: true, get: function() {
11054
11191
  return provider_config_1.providerConfigs;
11055
11192
  } });
11193
+ var provider_engines_1 = requireProviderEngines();
11194
+ Object.defineProperty(exports$1, "providerEngines", { enumerable: true, get: function() {
11195
+ return provider_engines_1.providerEngines;
11196
+ } });
11056
11197
  var validate_json_schema_1 = requireValidateJsonSchema();
11057
11198
  Object.defineProperty(exports$1, "validateJsonSchema", { enumerable: true, get: function() {
11058
11199
  return validate_json_schema_1.validateJsonSchema;
@@ -11063,6 +11204,17 @@ function requireDist() {
11063
11204
  } });
11064
11205
  __exportStar(requireModelInfo(), exports$1);
11065
11206
  __exportStar(requireSchemaUtils(), exports$1);
11207
+ var list_schema_1 = requireListSchema();
11208
+ Object.defineProperty(exports$1, "listSchema", { enumerable: true, get: function() {
11209
+ return list_schema_1.listSchema;
11210
+ } });
11211
+ Object.defineProperty(exports$1, "parseListSchema", { enumerable: true, get: function() {
11212
+ return list_schema_1.parseListSchema;
11213
+ } });
11214
+ var run_agent_1 = requireRunAgent();
11215
+ Object.defineProperty(exports$1, "runAgent", { enumerable: true, get: function() {
11216
+ return run_agent_1.runAgent;
11217
+ } });
11066
11218
  var agent_loop_1 = requireAgentLoop();
11067
11219
  Object.defineProperty(exports$1, "runAgentLoop", { enumerable: true, get: function() {
11068
11220
  return agent_loop_1.runAgentLoop;
@@ -11073,80 +11225,59 @@ function requireDist() {
11073
11225
 
11074
11226
  var distExports = requireDist();
11075
11227
 
11076
- const normalizedUsageZero = {
11077
- promptTokens: 0,
11078
- completionTokens: 0,
11079
- totalTokens: 0
11080
- };
11081
11228
  class DirectBackend {
11082
11229
  constructor(config) {
11083
- this.model = config.model;
11084
- this.client = new distExports.NuaLlmClient({ providers: config.providers });
11230
+ this.client = new distExports.NuaLlmClient(enginesFor(config));
11085
11231
  }
11086
11232
  async castValue(params) {
11087
- const model = params.model ?? this.model;
11088
11233
  const startTime = Date.now();
11089
11234
  const result = await this.client.castValue({
11090
- model,
11235
+ model: params.model,
11091
11236
  input: { prompt: params.prompt, data: params.data },
11092
- output: { name: params.outputName, effectiveSchema: params.outputSchema }
11237
+ output: { name: params.outputName, schema: params.outputSchema }
11093
11238
  });
11094
- const latencyMs = Date.now() - startTime;
11095
- if (!result.success) {
11096
- return {
11097
- success: false,
11098
- error: result.error || "Unknown error",
11099
- source: "direct",
11100
- latencyMs
11101
- };
11102
- }
11103
- return {
11104
- success: true,
11105
- data: result.data,
11106
- usage: result.usage || normalizedUsageZero,
11107
- model: formatModelInput(model),
11108
- latencyMs,
11109
- source: "direct",
11110
- meta: {}
11111
- };
11239
+ return toDirectResult(result, Date.now() - startTime);
11112
11240
  }
11113
11241
  async castArray(params) {
11114
- const model = params.model ?? this.model;
11115
11242
  const startTime = Date.now();
11116
- const wrappedSchema = distExports.wrapArraySchema(params.outputSchema, {
11117
- primaryKey: params.primaryKey,
11118
- outputName: params.outputName
11119
- });
11120
11243
  const result = await this.client.castArray({
11121
- model,
11122
- data: params.data,
11123
- input: { prompt: params.prompt, primaryKey: params.primaryKey },
11124
- output: { name: params.outputName, effectiveSchema: wrappedSchema }
11244
+ model: params.model,
11245
+ input: { prompt: params.prompt, data: params.data },
11246
+ output: distExports.listSchema(params.outputSchema, {
11247
+ primaryKey: params.primaryKey,
11248
+ outputName: params.outputName
11249
+ })
11125
11250
  });
11126
- const latencyMs = Date.now() - startTime;
11127
- if (!result.success) {
11128
- return {
11129
- success: false,
11130
- error: result.error || "Unknown error",
11131
- source: "direct",
11132
- latencyMs
11133
- };
11134
- }
11135
- return {
11136
- success: true,
11137
- data: result.data,
11138
- usage: result.usage || normalizedUsageZero,
11139
- model: formatModelInput(model),
11140
- latencyMs,
11141
- source: "direct",
11142
- meta: {}
11143
- };
11251
+ return toDirectResult(result, Date.now() - startTime);
11252
+ }
11253
+ }
11254
+ function enginesFor(config) {
11255
+ if (config.localAgent) {
11256
+ return config.localAgent;
11144
11257
  }
11258
+ return distExports.providerEngines({ providers: config.providers, model: config.model });
11145
11259
  }
11146
- function formatModelInput(model) {
11147
- if (!model) return "fast";
11148
- if ("alias" in model) return model.alias;
11149
- return `${model.provider}:${model.model}`;
11260
+ function toDirectResult(result, latencyMs) {
11261
+ if (!result.success) {
11262
+ return { success: false, error: result.error, source: "direct", latencyMs };
11263
+ }
11264
+ return {
11265
+ success: true,
11266
+ data: result.data,
11267
+ usage: result.usage,
11268
+ model: describeModel(result.origin),
11269
+ latencyMs,
11270
+ source: "direct",
11271
+ meta: { ...result.origin, schemaEnforcement: result.schemaEnforcement }
11272
+ };
11273
+ }
11274
+ function describeModel(origin) {
11275
+ switch (origin.engine) {
11276
+ case "http":
11277
+ return `${origin.provider}:${origin.model}`;
11278
+ case "local-agent":
11279
+ return `${origin.agent}:${origin.model ?? "default"}`;
11280
+ }
11150
11281
  }
11151
11282
 
11152
11283
  function resolveConfigValue(config, key, envVar) {
@@ -11396,6 +11527,11 @@ class Nua {
11396
11527
  static gateway(config) {
11397
11528
  return new Nua(new GatewayBackend(config));
11398
11529
  }
11530
+ /**
11531
+ * Runs casts in this process: on LLM providers' APIs with your API keys (`providers`), or
11532
+ * on a coding-agent CLI installed on this machine (`localAgent`, set up with `localAgent()` or
11533
+ * `findLocalAgent()` from "nuabase/local-agent").
11534
+ */
11399
11535
  static direct(config) {
11400
11536
  return new Nua(new DirectBackend(config));
11401
11537
  }