@skydiveai/pi-server 0.1.0-beta.1310 → 0.1.0-beta.1738
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +75 -9
- package/package.json +2 -3
package/dist/index.mjs
CHANGED
|
@@ -373,6 +373,57 @@ async function runConversation({ session, prompt, images, log, postPrompt }) {
|
|
|
373
373
|
});
|
|
374
374
|
}
|
|
375
375
|
/**
|
|
376
|
+
* Track the agent session's built-in model-call auto-retry so it leaves a
|
|
377
|
+
* trace.
|
|
378
|
+
*
|
|
379
|
+
* `AgentSession` already restarts a failed assistant turn in place (via
|
|
380
|
+
* `agent.continue()`, so no prompt is replayed and no tool re-executes) for the
|
|
381
|
+
* transient provider/transport failures pi classifies as retryable -- dropped
|
|
382
|
+
* streams, `terminated`, 5xx, overloaded, rate limits. It is on by default,
|
|
383
|
+
* with its own budget and backoff, and it emits `auto_retry_start` /
|
|
384
|
+
* `auto_retry_end` around each attempt.
|
|
385
|
+
*
|
|
386
|
+
* Nothing consumed those events, so a retry left no trace anywhere: a call that
|
|
387
|
+
* succeeded first try and one that burned the whole budget before failing
|
|
388
|
+
* produced the same terminal error, and the fleet-wide retry rate was
|
|
389
|
+
* unmeasurable. That gap is why a 2026-08-16 investigation into three runs lost
|
|
390
|
+
* to `provider_error: terminated` could not tell whether the budget had run out
|
|
391
|
+
* (ANY-7101).
|
|
392
|
+
*
|
|
393
|
+
* Exposed as a handler rather than its own `session.subscribe` call so each
|
|
394
|
+
* protocol feeds it from the single subscription it already owns -- one
|
|
395
|
+
* subscriber, explicit ordering.
|
|
396
|
+
*
|
|
397
|
+
* `attempts()` reports what has been spent so far, so a terminal error can
|
|
398
|
+
* carry the count to the worker, where it lands in a log group we can query
|
|
399
|
+
* fleet-wide (the sandbox's own logs are not).
|
|
400
|
+
*/
|
|
401
|
+
function createAutoRetryObserver(log) {
|
|
402
|
+
let attempts = 0;
|
|
403
|
+
return {
|
|
404
|
+
observe(event) {
|
|
405
|
+
if (event.type === "auto_retry_start") {
|
|
406
|
+
attempts = typeof event.attempt === "number" ? event.attempt : attempts + 1;
|
|
407
|
+
log.warn({
|
|
408
|
+
event: "model_call_auto_retry",
|
|
409
|
+
attempt: event.attempt,
|
|
410
|
+
max_attempts: event.maxAttempts,
|
|
411
|
+
delay_ms: event.delayMs,
|
|
412
|
+
error_message: event.errorMessage
|
|
413
|
+
}, "retrying failed model call in place");
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (event.type === "auto_retry_end") log.warn({
|
|
417
|
+
event: "model_call_auto_retry_end",
|
|
418
|
+
attempt: event.attempt,
|
|
419
|
+
success: event.success,
|
|
420
|
+
final_error: event.finalError
|
|
421
|
+
}, event.success ? "model call recovered after retry" : "model call retries exhausted");
|
|
422
|
+
},
|
|
423
|
+
attempts: () => attempts
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
376
427
|
* Hard-stop the in-flight turn for a session. Shared by every protocol's
|
|
377
428
|
* `/:id/abort` route: a cancel signals the stop explicitly instead of relying
|
|
378
429
|
* on a dropped connection. `session.abort()` interrupts the turn and resolves
|
|
@@ -1159,12 +1210,14 @@ function runStream$2({ session, sm, prompt, images, id, sessionId, modelName, lo
|
|
|
1159
1210
|
textBlockOpen = false;
|
|
1160
1211
|
};
|
|
1161
1212
|
let capturedModelError = null;
|
|
1213
|
+
const autoRetry = createAutoRetryObserver(log);
|
|
1162
1214
|
const emitProviderError = (providerError) => {
|
|
1163
1215
|
log.warn({
|
|
1164
1216
|
event: "model_provider_error",
|
|
1165
1217
|
code: providerError.code,
|
|
1166
1218
|
upstream_status: providerError.upstreamStatus,
|
|
1167
|
-
provider: providerError.provider
|
|
1219
|
+
provider: providerError.provider,
|
|
1220
|
+
retry_attempts: autoRetry.attempts()
|
|
1168
1221
|
}, "forwarding model-provider error to client");
|
|
1169
1222
|
sseEvent(writer, "error", {
|
|
1170
1223
|
type: "error",
|
|
@@ -1174,13 +1227,15 @@ function runStream$2({ session, sm, prompt, images, id, sessionId, modelName, lo
|
|
|
1174
1227
|
x_model_provider_error: {
|
|
1175
1228
|
code: providerError.code,
|
|
1176
1229
|
provider: providerError.provider,
|
|
1177
|
-
upstream_status: providerError.upstreamStatus
|
|
1230
|
+
upstream_status: providerError.upstreamStatus,
|
|
1231
|
+
retry_attempts: autoRetry.attempts()
|
|
1178
1232
|
}
|
|
1179
1233
|
}
|
|
1180
1234
|
});
|
|
1181
1235
|
};
|
|
1182
1236
|
session.subscribe((event) => {
|
|
1183
1237
|
const ev = event;
|
|
1238
|
+
autoRetry.observe(ev);
|
|
1184
1239
|
const endedMessage = ev.message ?? (Array.isArray(ev.messages) ? ev.messages[ev.messages.length - 1] : null);
|
|
1185
1240
|
if (endedMessage?.stopReason === "error" && typeof endedMessage.errorMessage === "string" && endedMessage.errorMessage.length > 0) capturedModelError = endedMessage.errorMessage;
|
|
1186
1241
|
if (ev.type !== "message_update") return;
|
|
@@ -1856,6 +1911,7 @@ function runStream$1({ session, sm, prompt, images, sessionId, created, reqStart
|
|
|
1856
1911
|
let usage = null;
|
|
1857
1912
|
let lastFollowUpCount = 0;
|
|
1858
1913
|
let capturedModelError = null;
|
|
1914
|
+
const autoRetry = createAutoRetryObserver(log);
|
|
1859
1915
|
const sendChunk = (delta, finishReason) => {
|
|
1860
1916
|
if (firstChunkAt === null) {
|
|
1861
1917
|
firstChunkAt = performance.now();
|
|
@@ -1886,6 +1942,7 @@ function runStream$1({ session, sm, prompt, images, sessionId, created, reqStart
|
|
|
1886
1942
|
let _evCount = 0;
|
|
1887
1943
|
session.subscribe((event) => {
|
|
1888
1944
|
const ev = event;
|
|
1945
|
+
autoRetry.observe(ev);
|
|
1889
1946
|
const endedMessage = ev.message ?? (Array.isArray(ev.messages) ? ev.messages[ev.messages.length - 1] : null);
|
|
1890
1947
|
if (endedMessage?.stopReason === "error" && typeof endedMessage.errorMessage === "string" && endedMessage.errorMessage.length > 0) capturedModelError = endedMessage.errorMessage;
|
|
1891
1948
|
if (ev.type === "queue_update") {
|
|
@@ -2013,13 +2070,15 @@ function runStream$1({ session, sm, prompt, images, sessionId, created, reqStart
|
|
|
2013
2070
|
code: providerError.code,
|
|
2014
2071
|
upstream_status: providerError.upstreamStatus,
|
|
2015
2072
|
provider: providerError.provider,
|
|
2073
|
+
retry_attempts: autoRetry.attempts(),
|
|
2016
2074
|
source: "stop_reason_error"
|
|
2017
2075
|
}, "forwarding model-provider error to client");
|
|
2018
2076
|
emitModelProviderError({
|
|
2019
2077
|
writer,
|
|
2020
2078
|
sessionId,
|
|
2021
2079
|
created,
|
|
2022
|
-
providerError
|
|
2080
|
+
providerError,
|
|
2081
|
+
retryAttempts: autoRetry.attempts()
|
|
2023
2082
|
});
|
|
2024
2083
|
writer.write("data: [DONE]\n\n");
|
|
2025
2084
|
} else {
|
|
@@ -2058,13 +2117,15 @@ function runStream$1({ session, sm, prompt, images, sessionId, created, reqStart
|
|
|
2058
2117
|
chatcmpl_id: sessionId,
|
|
2059
2118
|
code: providerError.code,
|
|
2060
2119
|
upstream_status: providerError.upstreamStatus,
|
|
2061
|
-
provider: providerError.provider
|
|
2120
|
+
provider: providerError.provider,
|
|
2121
|
+
retry_attempts: autoRetry.attempts()
|
|
2062
2122
|
}, "forwarding model-provider error to client");
|
|
2063
2123
|
emitModelProviderError({
|
|
2064
2124
|
writer,
|
|
2065
2125
|
sessionId,
|
|
2066
2126
|
created,
|
|
2067
|
-
providerError
|
|
2127
|
+
providerError,
|
|
2128
|
+
retryAttempts: autoRetry.attempts()
|
|
2068
2129
|
});
|
|
2069
2130
|
} else sendChunk({ content: `\n[error: ${err?.message ?? err}]` }, "stop");
|
|
2070
2131
|
writer.write("data: [DONE]\n\n");
|
|
@@ -2105,7 +2166,7 @@ function emitSessionMessagesTrailer({ writer, sm, baselineMessageCount, sessionI
|
|
|
2105
2166
|
* cleanly closes the stream for any OpenAI-shaped reader that ignores the
|
|
2106
2167
|
* extension field.
|
|
2107
2168
|
*/
|
|
2108
|
-
function emitModelProviderError({ writer, sessionId, created, providerError }) {
|
|
2169
|
+
function emitModelProviderError({ writer, sessionId, created, providerError, retryAttempts }) {
|
|
2109
2170
|
const chunk = {
|
|
2110
2171
|
id: sessionId,
|
|
2111
2172
|
object: "chat.completion.chunk",
|
|
@@ -2120,7 +2181,8 @@ function emitModelProviderError({ writer, sessionId, created, providerError }) {
|
|
|
2120
2181
|
message: providerError.message,
|
|
2121
2182
|
provider: providerError.provider,
|
|
2122
2183
|
type: providerError.type,
|
|
2123
|
-
upstream_status: providerError.upstreamStatus
|
|
2184
|
+
upstream_status: providerError.upstreamStatus,
|
|
2185
|
+
retry_attempts: retryAttempts
|
|
2124
2186
|
}
|
|
2125
2187
|
};
|
|
2126
2188
|
writer.write(`data: ${JSON.stringify(chunk)}\n\n`);
|
|
@@ -2500,6 +2562,7 @@ function runStream({ session, prompt, images, responseId, sessionId, modelName,
|
|
|
2500
2562
|
const tcByContentIdx = /* @__PURE__ */ new Map();
|
|
2501
2563
|
let nextOutputIndex = 0;
|
|
2502
2564
|
let capturedModelError = null;
|
|
2565
|
+
const autoRetry = createAutoRetryObserver(log);
|
|
2503
2566
|
const send = (event) => {
|
|
2504
2567
|
writer.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
2505
2568
|
};
|
|
@@ -2508,7 +2571,8 @@ function runStream({ session, prompt, images, responseId, sessionId, modelName,
|
|
|
2508
2571
|
event: "model_provider_error",
|
|
2509
2572
|
code: providerError.code,
|
|
2510
2573
|
upstream_status: providerError.upstreamStatus,
|
|
2511
|
-
provider: providerError.provider
|
|
2574
|
+
provider: providerError.provider,
|
|
2575
|
+
retry_attempts: autoRetry.attempts()
|
|
2512
2576
|
}, "forwarding model-provider error to client");
|
|
2513
2577
|
send({
|
|
2514
2578
|
type: "response.failed",
|
|
@@ -2522,7 +2586,8 @@ function runStream({ session, prompt, images, responseId, sessionId, modelName,
|
|
|
2522
2586
|
x_model_provider_error: {
|
|
2523
2587
|
provider: providerError.provider,
|
|
2524
2588
|
type: providerError.type,
|
|
2525
|
-
upstream_status: providerError.upstreamStatus
|
|
2589
|
+
upstream_status: providerError.upstreamStatus,
|
|
2590
|
+
retry_attempts: autoRetry.attempts()
|
|
2526
2591
|
}
|
|
2527
2592
|
}
|
|
2528
2593
|
}
|
|
@@ -2558,6 +2623,7 @@ function runStream({ session, prompt, images, responseId, sessionId, modelName,
|
|
|
2558
2623
|
};
|
|
2559
2624
|
session.subscribe((event) => {
|
|
2560
2625
|
const ev = event;
|
|
2626
|
+
autoRetry.observe(ev);
|
|
2561
2627
|
const endedMessage = ev.message ?? (Array.isArray(ev.messages) ? ev.messages[ev.messages.length - 1] : null);
|
|
2562
2628
|
if (endedMessage?.stopReason === "error" && typeof endedMessage.errorMessage === "string" && endedMessage.errorMessage.length > 0) capturedModelError = endedMessage.errorMessage;
|
|
2563
2629
|
if (ev.type !== "message_update") return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skydiveai/pi-server",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.1738",
|
|
4
4
|
"homepage": "https://skydive.com",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Create, Inc.",
|
|
@@ -29,8 +29,7 @@
|
|
|
29
29
|
"build": "tsdown",
|
|
30
30
|
"typecheck": "tsgo --noEmit",
|
|
31
31
|
"test:unit": "vitest run --passWithNoTests",
|
|
32
|
-
"test:ci": "vitest run --coverage --coverage.reporter=lcovonly --reporter=default --reporter=github-actions --minWorkers=1 --maxWorkers=2 --passWithNoTests"
|
|
33
|
-
"publish:system-artifacts": "doppler run --preserve-env -- node ../../scripts/anyone/publish-system-artifact.mjs"
|
|
32
|
+
"test:ci": "vitest run --coverage --coverage.reporter=lcovonly --reporter=default --reporter=github-actions --minWorkers=1 --maxWorkers=2 --passWithNoTests"
|
|
34
33
|
},
|
|
35
34
|
"dependencies": {
|
|
36
35
|
"@a2a-js/sdk": "^0.3.13",
|