@tangle-network/agent-app 0.47.11 → 0.47.13
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/README.md
CHANGED
|
@@ -36,7 +36,7 @@ pnpm add @tangle-network/agent-eval @tangle-network/agent-integrations
|
|
|
36
36
|
| Peer | Required by | Range |
|
|
37
37
|
|---|---|---|
|
|
38
38
|
| `@tangle-network/agent-eval` | `/eval`, `/eval-campaign`, `/profile`, `/knowledge` | `>=0.179.0 <0.180.0` |
|
|
39
|
-
| `@tangle-network/agent-runtime` | `/runtime`, `/chat-routes` | `>=0.
|
|
39
|
+
| `@tangle-network/agent-runtime` | `/runtime`, `/chat-routes` | `>=0.207.0 <0.208.0` |
|
|
40
40
|
| `@tangle-network/agent-integrations` | `/integrations` | `>=0.53.55 <0.54.0` |
|
|
41
41
|
| `@tangle-network/agent-interface` | `/interactions`, `/chat-store`, `/harness` | `^2.6.0` |
|
|
42
42
|
| `@tangle-network/sandbox` | `/sandbox` | `>=0.38.2 <0.39.0` |
|
|
@@ -160,6 +160,10 @@ function summarizeFailoverReason(reason) {
|
|
|
160
160
|
const lastSpace = clipped.lastIndexOf(" ");
|
|
161
161
|
return `${(lastSpace > LIMIT - 30 ? clipped.slice(0, lastSpace) : clipped).trimEnd()}\u2026`;
|
|
162
162
|
}
|
|
163
|
+
function isLiveLifecycleEvent(event) {
|
|
164
|
+
const type = asString(asRecord(event)?.type) ?? "";
|
|
165
|
+
return type === "start" || type === "execution.started" || type === "status" || type === "model-processing" || type === "model.processing" || type === "session.created" || type === "session.updated" || type === "session.idle";
|
|
166
|
+
}
|
|
163
167
|
function classifyTerminalFailure(event) {
|
|
164
168
|
const record = asRecord(event);
|
|
165
169
|
if (!record) return null;
|
|
@@ -247,6 +251,8 @@ function streamWithModelFailover(options) {
|
|
|
247
251
|
DEFAULT_MODEL_FIRST_RESPONSE_TIMEOUT_MS,
|
|
248
252
|
"firstResponseTimeoutMs"
|
|
249
253
|
);
|
|
254
|
+
const liveLifecycle = options.liveLifecycleEvents === true;
|
|
255
|
+
const progress = createProgressQueue();
|
|
250
256
|
let serving;
|
|
251
257
|
let trail = [];
|
|
252
258
|
let fellBack = false;
|
|
@@ -368,7 +374,11 @@ function streamWithModelFailover(options) {
|
|
|
368
374
|
empty: false
|
|
369
375
|
};
|
|
370
376
|
}
|
|
371
|
-
|
|
377
|
+
if (liveLifecycle && !failure && !commits && isLiveLifecycleEvent(event)) {
|
|
378
|
+
progress.push(event);
|
|
379
|
+
} else {
|
|
380
|
+
buffered.push(event);
|
|
381
|
+
}
|
|
372
382
|
if (failure) {
|
|
373
383
|
firstResponseDeadline.clear();
|
|
374
384
|
return { outcome: { committed: true, buffered, iterator, abort }, empty: false };
|
|
@@ -437,24 +447,31 @@ function streamWithModelFailover(options) {
|
|
|
437
447
|
};
|
|
438
448
|
const events = (async function* () {
|
|
439
449
|
let handle;
|
|
450
|
+
const settled = runWithModelFailover({
|
|
451
|
+
models: options.models,
|
|
452
|
+
run: probe,
|
|
453
|
+
// The probe has already classified the raw payload with
|
|
454
|
+
// `isUpstreamUnavailable`; this reads its verdict rather than
|
|
455
|
+
// re-classifying a wrapper object, so the two can never disagree.
|
|
456
|
+
isUnavailableResult: (result) => result.committed === false,
|
|
457
|
+
onFallback: (attempt, nextModel) => {
|
|
458
|
+
const info = {
|
|
459
|
+
from: attempt.model,
|
|
460
|
+
to: nextModel,
|
|
461
|
+
reason: attempt.reason ?? "upstream unavailable"
|
|
462
|
+
};
|
|
463
|
+
options.log?.("[chat-routes] model upstream unavailable; falling over", { ...info });
|
|
464
|
+
options.onFallback?.(info);
|
|
465
|
+
}
|
|
466
|
+
}).finally(() => progress.close());
|
|
467
|
+
settled.catch(() => void 0);
|
|
468
|
+
for (; ; ) {
|
|
469
|
+
const item = await progress.pull();
|
|
470
|
+
if (item === PROGRESS_CLOSED) break;
|
|
471
|
+
yield item;
|
|
472
|
+
}
|
|
440
473
|
try {
|
|
441
|
-
const outcome = await
|
|
442
|
-
models: options.models,
|
|
443
|
-
run: probe,
|
|
444
|
-
// The probe has already classified the raw payload with
|
|
445
|
-
// `isUpstreamUnavailable`; this reads its verdict rather than
|
|
446
|
-
// re-classifying a wrapper object, so the two can never disagree.
|
|
447
|
-
isUnavailableResult: (result) => result.committed === false,
|
|
448
|
-
onFallback: (attempt, nextModel) => {
|
|
449
|
-
const info = {
|
|
450
|
-
from: attempt.model,
|
|
451
|
-
to: nextModel,
|
|
452
|
-
reason: attempt.reason ?? "upstream unavailable"
|
|
453
|
-
};
|
|
454
|
-
options.log?.("[chat-routes] model upstream unavailable; falling over", { ...info });
|
|
455
|
-
options.onFallback?.(info);
|
|
456
|
-
}
|
|
457
|
-
});
|
|
474
|
+
const outcome = await settled;
|
|
458
475
|
serving = outcome.model;
|
|
459
476
|
trail = outcome.attempts;
|
|
460
477
|
fellBack = outcome.usedFallback;
|
|
@@ -494,6 +511,33 @@ function streamWithModelFailover(options) {
|
|
|
494
511
|
usedFallback: () => fellBack
|
|
495
512
|
};
|
|
496
513
|
}
|
|
514
|
+
var PROGRESS_CLOSED = /* @__PURE__ */ Symbol("progress-closed");
|
|
515
|
+
function createProgressQueue() {
|
|
516
|
+
const items = [];
|
|
517
|
+
const waiters = [];
|
|
518
|
+
let closed = false;
|
|
519
|
+
const deliver = (value) => {
|
|
520
|
+
const waiter = waiters.shift();
|
|
521
|
+
if (waiter) waiter(value);
|
|
522
|
+
else items.push(value);
|
|
523
|
+
};
|
|
524
|
+
return {
|
|
525
|
+
push(event) {
|
|
526
|
+
if (closed) return;
|
|
527
|
+
deliver(event);
|
|
528
|
+
},
|
|
529
|
+
close() {
|
|
530
|
+
if (closed) return;
|
|
531
|
+
closed = true;
|
|
532
|
+
deliver(PROGRESS_CLOSED);
|
|
533
|
+
},
|
|
534
|
+
pull() {
|
|
535
|
+
if (items.length > 0) return Promise.resolve(items.shift());
|
|
536
|
+
if (closed) return Promise.resolve(PROGRESS_CLOSED);
|
|
537
|
+
return new Promise((resolve) => waiters.push(resolve));
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
}
|
|
497
541
|
|
|
498
542
|
// src/chat-routes/sandbox-turn-usage.ts
|
|
499
543
|
function addStepFinishUsage(part, usage) {
|
|
@@ -573,6 +617,17 @@ function extractReportedTurnUsage(data) {
|
|
|
573
617
|
if (costUsd !== null) reported.costUsd = costUsd;
|
|
574
618
|
return reported;
|
|
575
619
|
}
|
|
620
|
+
function extractUsageSnapshot(value) {
|
|
621
|
+
const inputTokens = toFiniteNumber(value?.promptTokens);
|
|
622
|
+
const outputTokens = toFiniteNumber(value?.completionTokens);
|
|
623
|
+
if (inputTokens === null || outputTokens === null || inputTokens < 0 || outputTokens < 0) return null;
|
|
624
|
+
const reported = { inputTokens, outputTokens };
|
|
625
|
+
const reasoningTokens = toFiniteNumber(value?.reasoningTokens);
|
|
626
|
+
if (reasoningTokens !== null && reasoningTokens >= 0) reported.reasoningTokens = reasoningTokens;
|
|
627
|
+
const costUsd = toFiniteNumber(value?.providerCostUsd);
|
|
628
|
+
if (costUsd !== null && costUsd >= 0) reported.costUsd = costUsd;
|
|
629
|
+
return reported;
|
|
630
|
+
}
|
|
576
631
|
function applyTerminalUsage(reported, usage) {
|
|
577
632
|
usage.inputTokens = reported.inputTokens;
|
|
578
633
|
usage.outputTokens = reported.outputTokens;
|
|
@@ -645,6 +700,7 @@ function createSandboxChatProducer(options) {
|
|
|
645
700
|
...options.firstResponseTimeoutMs !== void 0 ? { firstResponseTimeoutMs: options.firstResponseTimeoutMs } : {},
|
|
646
701
|
...options.emptyTurnRetries !== void 0 ? { emptyTurnRetries: options.emptyTurnRetries } : {},
|
|
647
702
|
...options.onEmptyTurnRetry ? { onEmptyTurnRetry: options.onEmptyTurnRetry } : {},
|
|
703
|
+
...options.liveLifecycleEvents !== void 0 ? { liveLifecycleEvents: options.liveLifecycleEvents } : {},
|
|
648
704
|
onFallback: (info) => {
|
|
649
705
|
modelNoticeCount += 1;
|
|
650
706
|
pendingModelNotices.push({
|
|
@@ -931,6 +987,12 @@ function createSandboxChatProducer(options) {
|
|
|
931
987
|
yield toProducerWireEvent(record);
|
|
932
988
|
continue;
|
|
933
989
|
}
|
|
990
|
+
if (event.type === "usage") {
|
|
991
|
+
const reported = extractUsageSnapshot(asRecord(record.usage));
|
|
992
|
+
if (reported) applyTerminalUsage(reported, usage);
|
|
993
|
+
yield toProducerWireEvent(record);
|
|
994
|
+
continue;
|
|
995
|
+
}
|
|
934
996
|
if (event.type === "result") {
|
|
935
997
|
const finalText = asString(event.data?.finalText);
|
|
936
998
|
if (finalText) fullText = finalText;
|
|
@@ -2787,6 +2849,7 @@ export {
|
|
|
2787
2849
|
immutableAttachmentPath,
|
|
2788
2850
|
isCommittingSandboxEvent,
|
|
2789
2851
|
isDraftContentEvent,
|
|
2852
|
+
isLiveLifecycleEvent,
|
|
2790
2853
|
mediaTypeForMentionPath,
|
|
2791
2854
|
mentionKindForPath,
|
|
2792
2855
|
normalizeChatPromptForSandbox,
|