nuabase 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +23 -3
- package/dist/chunks/{local-agent-Bj-QKikS.d.cts → local-agent-CPIXKS41.d.cts} +61 -19
- package/dist/chunks/{local-agent-Bj-QKikS.d.mts → local-agent-CPIXKS41.d.mts} +61 -19
- package/dist/cjs/index.cjs +67 -21
- package/dist/cjs/local-agent.cjs +241 -71
- package/dist/esm/index.mjs +67 -21
- package/dist/esm/local-agent.mjs +241 -72
- package/dist/index.d.cts +13 -3
- package/dist/index.d.mts +13 -3
- package/dist/local-agent.d.cts +55 -14
- package/dist/local-agent.d.mts +55 -14
- package/package.json +1 -1
package/dist/esm/index.mjs
CHANGED
|
@@ -293,21 +293,27 @@ function renderPrompt(cast, enforcement) {
|
|
|
293
293
|
}
|
|
294
294
|
|
|
295
295
|
function refusedCast(reason) {
|
|
296
|
-
return { success: false, error: reason, reason, attempts: 0 };
|
|
296
|
+
return { success: false, error: reason, reason, attempts: 0, cancelled: false };
|
|
297
297
|
}
|
|
298
298
|
const RETRY_DELAYS_MS = [1e3, 2e3];
|
|
299
299
|
const MAX_ATTEMPTS = RETRY_DELAYS_MS.length + 1;
|
|
300
|
-
async function runCast(engine, request, logger) {
|
|
300
|
+
async function runCast(engine, request, logger, controls = {}) {
|
|
301
301
|
const validate = validatorFor(request.schema);
|
|
302
302
|
let plan = planAttempt(request, chooseEnforcement(engine, request.jsonSchema));
|
|
303
303
|
let lastFailure = { error: "", prompt: plan.prompt };
|
|
304
304
|
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
305
|
-
|
|
306
|
-
prompt:
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
305
|
+
if (controls.signal?.aborted) {
|
|
306
|
+
return cancelledCast(attempt - 1, attempt > 1 ? lastFailure.prompt : void 0);
|
|
307
|
+
}
|
|
308
|
+
const outcome = await engine.attempt(
|
|
309
|
+
{
|
|
310
|
+
prompt: plan.prompt.full,
|
|
311
|
+
reusablePrefix: plan.prompt.reusablePrefix,
|
|
312
|
+
maxTokens: request.maxTokens,
|
|
313
|
+
enforcement: plan.enforcement
|
|
314
|
+
},
|
|
315
|
+
controls
|
|
316
|
+
);
|
|
311
317
|
const verdict = judgeOutcome(outcome, validate);
|
|
312
318
|
if (verdict.kind === "accept") {
|
|
313
319
|
return {
|
|
@@ -320,7 +326,17 @@ async function runCast(engine, request, logger) {
|
|
|
320
326
|
};
|
|
321
327
|
}
|
|
322
328
|
if (verdict.kind === "give-up") {
|
|
323
|
-
return {
|
|
329
|
+
return {
|
|
330
|
+
success: false,
|
|
331
|
+
error: verdict.error,
|
|
332
|
+
reason: verdict.error,
|
|
333
|
+
attempts: attempt,
|
|
334
|
+
cancelled: false,
|
|
335
|
+
prompt: plan.prompt
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
if (verdict.kind === "cancelled") {
|
|
339
|
+
return cancelledCast(attempt, plan.prompt);
|
|
324
340
|
}
|
|
325
341
|
lastFailure = { error: verdict.error, prompt: plan.prompt };
|
|
326
342
|
if (verdict.kind === "retry-in-prompt") {
|
|
@@ -334,7 +350,7 @@ async function runCast(engine, request, logger) {
|
|
|
334
350
|
if (attempt < MAX_ATTEMPTS) {
|
|
335
351
|
const delayMs = RETRY_DELAYS_MS[attempt - 1];
|
|
336
352
|
logger.warn("LLM call attempt failed; retrying", { attempt, delayMs, error: verdict.error });
|
|
337
|
-
await delay(delayMs);
|
|
353
|
+
await delay(delayMs, controls.signal);
|
|
338
354
|
}
|
|
339
355
|
}
|
|
340
356
|
return {
|
|
@@ -342,6 +358,7 @@ async function runCast(engine, request, logger) {
|
|
|
342
358
|
error: `LLM call failed after ${MAX_ATTEMPTS} attempts. Last error: ${lastFailure.error}`,
|
|
343
359
|
reason: lastFailure.error,
|
|
344
360
|
attempts: MAX_ATTEMPTS,
|
|
361
|
+
cancelled: false,
|
|
345
362
|
prompt: lastFailure.prompt
|
|
346
363
|
};
|
|
347
364
|
}
|
|
@@ -386,6 +403,8 @@ function verdictForFailure(failure) {
|
|
|
386
403
|
return { kind: "retry-in-prompt", error: failure.message };
|
|
387
404
|
case "fatal":
|
|
388
405
|
return { kind: "give-up", error: failure.message };
|
|
406
|
+
case "cancelled":
|
|
407
|
+
return { kind: "cancelled" };
|
|
389
408
|
}
|
|
390
409
|
}
|
|
391
410
|
function readAnswer(answer) {
|
|
@@ -421,8 +440,20 @@ function readStructuredAnswer(envelope) {
|
|
|
421
440
|
function errorMessage(error) {
|
|
422
441
|
return error instanceof Error ? error.message : String(error);
|
|
423
442
|
}
|
|
424
|
-
function
|
|
425
|
-
|
|
443
|
+
function cancelledCast(attempts, prompt) {
|
|
444
|
+
const reason = "The call was cancelled.";
|
|
445
|
+
return { success: false, error: reason, reason, attempts, cancelled: true, prompt };
|
|
446
|
+
}
|
|
447
|
+
function delay(ms, signal) {
|
|
448
|
+
return new Promise((resolve) => {
|
|
449
|
+
const done = () => {
|
|
450
|
+
clearTimeout(timer);
|
|
451
|
+
signal?.removeEventListener("abort", done);
|
|
452
|
+
resolve();
|
|
453
|
+
};
|
|
454
|
+
const timer = setTimeout(done, ms);
|
|
455
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
456
|
+
});
|
|
426
457
|
}
|
|
427
458
|
|
|
428
459
|
const DEFAULT_MAX_TOKENS = 4096;
|
|
@@ -445,7 +476,10 @@ class NuaLlmClient {
|
|
|
445
476
|
jsonSchema,
|
|
446
477
|
maxTokens: params.maxTokens ?? DEFAULT_MAX_TOKENS
|
|
447
478
|
};
|
|
448
|
-
return runCast(route.engine, request, this.engines.logger
|
|
479
|
+
return runCast(route.engine, request, this.engines.logger, {
|
|
480
|
+
onOutput: params.onOutput,
|
|
481
|
+
signal: params.signal
|
|
482
|
+
});
|
|
449
483
|
}
|
|
450
484
|
async castArray(params) {
|
|
451
485
|
const { primaryKey, outputName, schema } = params.output;
|
|
@@ -468,7 +502,10 @@ class NuaLlmClient {
|
|
|
468
502
|
jsonSchema,
|
|
469
503
|
maxTokens: params.maxTokens ?? DEFAULT_MAX_TOKENS
|
|
470
504
|
};
|
|
471
|
-
return runCast(route.engine, request, this.engines.logger
|
|
505
|
+
return runCast(route.engine, request, this.engines.logger, {
|
|
506
|
+
onOutput: params.onOutput,
|
|
507
|
+
signal: params.signal
|
|
508
|
+
});
|
|
472
509
|
}
|
|
473
510
|
}
|
|
474
511
|
|
|
@@ -903,7 +940,8 @@ async function callProvider(call, logger) {
|
|
|
903
940
|
const response = await fetch(request.url, {
|
|
904
941
|
method,
|
|
905
942
|
headers: request.headers ?? {},
|
|
906
|
-
body: toFetchableBody(request.body)
|
|
943
|
+
body: toFetchableBody(request.body),
|
|
944
|
+
signal: call.signal
|
|
907
945
|
});
|
|
908
946
|
status = response.status;
|
|
909
947
|
const reply = await call.parseResponse(response);
|
|
@@ -949,7 +987,7 @@ class HttpEngine {
|
|
|
949
987
|
if (!providerConfigs[provider].strictSchemaModels?.has(model)) return null;
|
|
950
988
|
return toStrictSchema(envelopeSchema);
|
|
951
989
|
}
|
|
952
|
-
async attempt(request) {
|
|
990
|
+
async attempt(request, { signal } = {}) {
|
|
953
991
|
const { provider, model, apiKey } = this.target;
|
|
954
992
|
const config = providerConfigs[provider];
|
|
955
993
|
const responseSchema = request.enforcement.kind === "native" ? request.enforcement.schema : void 0;
|
|
@@ -970,11 +1008,15 @@ class HttpEngine {
|
|
|
970
1008
|
),
|
|
971
1009
|
parseResponse: config.parseResponse,
|
|
972
1010
|
responseLength: (reply) => reply.text.length,
|
|
973
|
-
usage: (reply) => reply.usage
|
|
1011
|
+
usage: (reply) => reply.usage,
|
|
1012
|
+
signal
|
|
974
1013
|
},
|
|
975
1014
|
this.logger
|
|
976
1015
|
);
|
|
977
1016
|
if (call.kind === "failed") {
|
|
1017
|
+
if (signal?.aborted) {
|
|
1018
|
+
return { kind: "failed", failure: { kind: "cancelled", message: "The call was cancelled." } };
|
|
1019
|
+
}
|
|
978
1020
|
return { kind: "failed", failure: failureForStatus(call.status, call.message, responseSchema !== void 0) };
|
|
979
1021
|
}
|
|
980
1022
|
const answer = responseSchema ? readEnvelope(call.reply.text) : { kind: "text", text: call.reply.text };
|
|
@@ -1077,7 +1119,9 @@ class Nua {
|
|
|
1077
1119
|
const result = await this.client.castValue({
|
|
1078
1120
|
model: options?.model,
|
|
1079
1121
|
input: { prompt, data: options?.input ?? null },
|
|
1080
|
-
output: { name: outputName, schema: outputSchema }
|
|
1122
|
+
output: { name: outputName, schema: outputSchema },
|
|
1123
|
+
onOutput: options?.onOutput,
|
|
1124
|
+
signal: options?.signal
|
|
1081
1125
|
});
|
|
1082
1126
|
return toDirectResult(result, Date.now() - startTime);
|
|
1083
1127
|
}
|
|
@@ -1100,7 +1144,9 @@ class Nua {
|
|
|
1100
1144
|
const result = await this.client.castArray({
|
|
1101
1145
|
model: options.model,
|
|
1102
1146
|
input: { prompt, data: options.input },
|
|
1103
|
-
output
|
|
1147
|
+
output,
|
|
1148
|
+
onOutput: options.onOutput,
|
|
1149
|
+
signal: options.signal
|
|
1104
1150
|
});
|
|
1105
1151
|
return toDirectResult(result, Date.now() - startTime);
|
|
1106
1152
|
}
|
|
@@ -1113,8 +1159,8 @@ function enginesFor(config) {
|
|
|
1113
1159
|
}
|
|
1114
1160
|
function toDirectResult(result, latencyMs) {
|
|
1115
1161
|
if (!result.success) {
|
|
1116
|
-
const { error, reason, attempts } = result;
|
|
1117
|
-
return { success: false, error, reason, attempts, source: "direct", latencyMs };
|
|
1162
|
+
const { error, reason, attempts, cancelled } = result;
|
|
1163
|
+
return { success: false, error, reason, attempts, cancelled, source: "direct", latencyMs };
|
|
1118
1164
|
}
|
|
1119
1165
|
return {
|
|
1120
1166
|
success: true,
|