@cydm/pie 2.7.0 → 2.7.2
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 +17 -1
- package/dist/builtin/extensions/ask-user/index.js +3 -3
- package/dist/builtin/extensions/plan-mode/index.js +3 -3
- package/dist/builtin/extensions/subagent/index.js +15 -5
- package/dist/builtin/extensions/todo/index.js +3 -3
- package/dist/builtin/skills/computer-use-axi/SKILL.md +89 -4
- package/dist/builtin/skills/computer-use-axi/computer-use-axi-broker.js +11004 -0
- package/dist/builtin/skills/computer-use-axi/computer-use-axi-runtime.js +449 -9256
- package/dist/chunks/{chunk-6RBSTII7.js → chunk-65B6P5J5.js} +862 -265
- package/dist/chunks/{chunk-R5I2QLF2.js → chunk-BRFP5WZ6.js} +606 -97
- package/dist/chunks/{chunk-IPNAFMOJ.js → chunk-GECAIKZL.js} +70 -3
- package/dist/chunks/{chunk-TWILEFLD.js → chunk-GZDAIW6F.js} +1 -1
- package/dist/chunks/{src-E7NCXMU5.js → src-PRKTGADX.js} +2 -2
- package/dist/chunks/{test-stream-BWBJTFNI.js → test-stream-BGAEVPRX.js} +1 -1
- package/dist/cli.js +530 -145
- package/models.schema.json +28 -2
- package/package.json +2 -2
|
@@ -475,6 +475,7 @@ function calculateCost(model, usage) {
|
|
|
475
475
|
const cacheRead = model.cost.cacheRead / 1e6 * usage.cacheRead;
|
|
476
476
|
const cacheWrite = model.cost.cacheWrite / 1e6 * usage.cacheWrite;
|
|
477
477
|
const cacheWriteLongTokens = usage.cacheWriteLong ?? 0;
|
|
478
|
+
const hasExplicitLongRate = model.cost.cacheWriteLong !== void 0;
|
|
478
479
|
const cacheWriteLongRate = model.cost.cacheWriteLong ?? model.cost.input * 2;
|
|
479
480
|
const cacheWriteLong = cacheWriteLongTokens > 0 ? cacheWriteLongRate / 1e6 * cacheWriteLongTokens : 0;
|
|
480
481
|
return {
|
|
@@ -482,7 +483,10 @@ function calculateCost(model, usage) {
|
|
|
482
483
|
output,
|
|
483
484
|
cacheRead,
|
|
484
485
|
cacheWrite,
|
|
485
|
-
...cacheWriteLongTokens > 0 ? {
|
|
486
|
+
...cacheWriteLongTokens > 0 ? {
|
|
487
|
+
cacheWriteLong,
|
|
488
|
+
...hasExplicitLongRate ? {} : { cacheWriteLongEstimated: true }
|
|
489
|
+
} : {},
|
|
486
490
|
total: input + output + cacheRead + cacheWrite + cacheWriteLong
|
|
487
491
|
};
|
|
488
492
|
}
|
|
@@ -1872,6 +1876,8 @@ function getHttpAgent() {
|
|
|
1872
1876
|
return globalHttpAgent;
|
|
1873
1877
|
}
|
|
1874
1878
|
var NodeHttpClient = class {
|
|
1879
|
+
/** The Node client never re-sends a request: one `request()` call is one submission. */
|
|
1880
|
+
transportCapabilities = { singleAttempt: true };
|
|
1875
1881
|
async request(url, options = {}) {
|
|
1876
1882
|
const requestStartTime = Date.now();
|
|
1877
1883
|
const requestId = `${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
@@ -1884,6 +1890,10 @@ var NodeHttpClient = class {
|
|
|
1884
1890
|
let tieredBodyByteSeen = false;
|
|
1885
1891
|
let tierMarkBodyByte = null;
|
|
1886
1892
|
let requestTimeoutError;
|
|
1893
|
+
const setTimeoutKind = (error, kind) => {
|
|
1894
|
+
if (!error) return;
|
|
1895
|
+
error.timeoutKind = kind;
|
|
1896
|
+
};
|
|
1887
1897
|
const clearTieredFirstByteTimer = () => {
|
|
1888
1898
|
if (tieredFirstByteTimer !== void 0) {
|
|
1889
1899
|
clearTimeout(tieredFirstByteTimer);
|
|
@@ -1927,6 +1937,7 @@ var NodeHttpClient = class {
|
|
|
1927
1937
|
res.on("timeout", () => {
|
|
1928
1938
|
if (res.readableEnded || res.complete) return;
|
|
1929
1939
|
requestTimeoutError = new Error(`Request timeout (stream idle) after ${tieredStreamIdleTimeoutMs}ms`);
|
|
1940
|
+
setTimeoutKind(requestTimeoutError, "stream_idle");
|
|
1930
1941
|
logHttp("WARN", `Request [${reqTag}] stream idle timeout`, { idleTimeoutMs: tieredStreamIdleTimeoutMs });
|
|
1931
1942
|
req.destroy(requestTimeoutError);
|
|
1932
1943
|
});
|
|
@@ -2172,6 +2183,7 @@ var NodeHttpClient = class {
|
|
|
2172
2183
|
if (!tieredStreamTimeout) {
|
|
2173
2184
|
req.on("timeout", () => {
|
|
2174
2185
|
requestTimeoutError = new Error("Request timeout");
|
|
2186
|
+
setTimeoutKind(requestTimeoutError, "total");
|
|
2175
2187
|
logHttp("ERROR", `Request [${reqTag}] timeout`);
|
|
2176
2188
|
req.destroy(requestTimeoutError);
|
|
2177
2189
|
});
|
|
@@ -2180,6 +2192,7 @@ var NodeHttpClient = class {
|
|
|
2180
2192
|
tieredFirstByteTimer = setTimeout(() => {
|
|
2181
2193
|
tieredFirstByteTimer = void 0;
|
|
2182
2194
|
requestTimeoutError = new Error(`Request timeout (first byte) after ${tieredFirstByteTimeoutMs}ms`);
|
|
2195
|
+
setTimeoutKind(requestTimeoutError, "first_byte");
|
|
2183
2196
|
logHttp("WARN", `Request [${reqTag}] first byte timeout`, { firstByteTimeoutMs: tieredFirstByteTimeoutMs });
|
|
2184
2197
|
req.destroy(requestTimeoutError);
|
|
2185
2198
|
}, tieredFirstByteTimeoutMs);
|
|
@@ -2194,6 +2207,8 @@ var NodeHttpClient = class {
|
|
|
2194
2207
|
}
|
|
2195
2208
|
};
|
|
2196
2209
|
var PuerTSHttpClient = class {
|
|
2210
|
+
/** No internal re-send: each call starts exactly one bridge request. */
|
|
2211
|
+
transportCapabilities = { singleAttempt: true };
|
|
2197
2212
|
async request(url, options = {}) {
|
|
2198
2213
|
logHttp("DEBUG", `PuerTS Request started`, {
|
|
2199
2214
|
url: url.slice(0, 100),
|
|
@@ -3316,6 +3331,9 @@ function getHttpClient() {
|
|
|
3316
3331
|
}
|
|
3317
3332
|
|
|
3318
3333
|
// ../../packages/ai/src/utils/http.ts
|
|
3334
|
+
function getHttpTransport() {
|
|
3335
|
+
return getHttpClient();
|
|
3336
|
+
}
|
|
3319
3337
|
function httpRequest(url, options = {}) {
|
|
3320
3338
|
const client = getHttpClient();
|
|
3321
3339
|
return client.request(url, options);
|
|
@@ -3357,8 +3375,13 @@ function buildBaseOptions(model, options, apiKey) {
|
|
|
3357
3375
|
sessionId: options?.sessionId,
|
|
3358
3376
|
headers: options?.headers,
|
|
3359
3377
|
timeoutMs: options?.timeoutMs,
|
|
3378
|
+
firstResponseTimeoutMs: options?.firstResponseTimeoutMs,
|
|
3379
|
+
streamIdleTimeoutMs: options?.streamIdleTimeoutMs,
|
|
3380
|
+
requestTimeoutMs: options?.requestTimeoutMs,
|
|
3381
|
+
connectionIdleTimeoutMs: options?.connectionIdleTimeoutMs,
|
|
3360
3382
|
onPayload: options?.onPayload,
|
|
3361
3383
|
onTransportDiagnostic: options?.onTransportDiagnostic,
|
|
3384
|
+
onTransportLifecycle: options?.onTransportLifecycle,
|
|
3362
3385
|
maxRetryDelayMs: options?.maxRetryDelayMs,
|
|
3363
3386
|
metadata: options?.metadata
|
|
3364
3387
|
};
|
|
@@ -4411,6 +4434,35 @@ import Anthropic from "@anthropic-ai/sdk";
|
|
|
4411
4434
|
function sanitizeSurrogates(text) {
|
|
4412
4435
|
return text.replace(/[\uD800-\uDFFF]/g, "");
|
|
4413
4436
|
}
|
|
4437
|
+
function applyAnthropicUsageUpdate(model, usage, payload) {
|
|
4438
|
+
if (payload.input_tokens != null) usage.input = Math.max(0, payload.input_tokens);
|
|
4439
|
+
if (payload.output_tokens != null) usage.output = Math.max(0, payload.output_tokens);
|
|
4440
|
+
if (payload.cache_read_input_tokens != null) usage.cacheRead = Math.max(0, payload.cache_read_input_tokens);
|
|
4441
|
+
const reportedWriteTotal = payload.cache_creation_input_tokens;
|
|
4442
|
+
const breakdown = payload.cache_creation ?? void 0;
|
|
4443
|
+
const breakdownTotal = (breakdown?.ephemeral_5m_input_tokens ?? 0) + (breakdown?.ephemeral_1h_input_tokens ?? 0);
|
|
4444
|
+
if (reportedWriteTotal == null && breakdown !== void 0 && breakdownTotal === 0) {
|
|
4445
|
+
usage.cacheWrite = Math.max(0, usage.cacheWrite);
|
|
4446
|
+
} else if (reportedWriteTotal != null || breakdown !== void 0) {
|
|
4447
|
+
const previousLong = usage.cacheWriteLong ?? 0;
|
|
4448
|
+
const previousTotal = usage.cacheWrite + previousLong;
|
|
4449
|
+
const writeTotal = Math.max(
|
|
4450
|
+
0,
|
|
4451
|
+
reportedWriteTotal != null ? reportedWriteTotal : breakdownTotal > 0 ? breakdownTotal : previousTotal
|
|
4452
|
+
);
|
|
4453
|
+
const longTokens = breakdown?.ephemeral_1h_input_tokens ?? 0;
|
|
4454
|
+
if (longTokens > 0) {
|
|
4455
|
+
const long = Math.min(longTokens, writeTotal);
|
|
4456
|
+
usage.cacheWriteLong = long;
|
|
4457
|
+
usage.cacheWrite = writeTotal - long;
|
|
4458
|
+
} else {
|
|
4459
|
+
delete usage.cacheWriteLong;
|
|
4460
|
+
usage.cacheWrite = writeTotal;
|
|
4461
|
+
}
|
|
4462
|
+
}
|
|
4463
|
+
usage.totalTokens = usage.input + usage.output + usage.cacheRead + usage.cacheWrite + (usage.cacheWriteLong ?? 0);
|
|
4464
|
+
usage.cost = calculateCost(model, usage);
|
|
4465
|
+
}
|
|
4414
4466
|
var ANTHROPIC_PROVIDER_HEADERS = {
|
|
4415
4467
|
accept: "application/json",
|
|
4416
4468
|
"anthropic-dangerous-direct-browser-access": "true",
|
|
@@ -4696,17 +4748,7 @@ var streamAnthropic = (model, context, options) => {
|
|
|
4696
4748
|
const blocks = output.content;
|
|
4697
4749
|
for await (const event of anthropicStream) {
|
|
4698
4750
|
if (event.type === "message_start") {
|
|
4699
|
-
output.usage
|
|
4700
|
-
output.usage.output = event.message.usage.output_tokens || 0;
|
|
4701
|
-
output.usage.cacheRead = event.message.usage.cache_read_input_tokens || 0;
|
|
4702
|
-
output.usage.cacheWrite = event.message.usage.cache_creation_input_tokens || 0;
|
|
4703
|
-
const creation = event.message.usage.cache_creation;
|
|
4704
|
-
if (creation?.ephemeral_1h_input_tokens) {
|
|
4705
|
-
output.usage.cacheWriteLong = creation.ephemeral_1h_input_tokens;
|
|
4706
|
-
output.usage.cacheWrite = Math.max(0, output.usage.cacheWrite - creation.ephemeral_1h_input_tokens);
|
|
4707
|
-
}
|
|
4708
|
-
output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite + (output.usage.cacheWriteLong ?? 0);
|
|
4709
|
-
calculateCost(model, output.usage);
|
|
4751
|
+
applyAnthropicUsageUpdate(model, output.usage, event.message.usage);
|
|
4710
4752
|
} else if (event.type === "content_block_start") {
|
|
4711
4753
|
if (event.content_block.type === "text") {
|
|
4712
4754
|
const block = { type: "text", text: "", index: event.index };
|
|
@@ -4810,19 +4852,7 @@ var streamAnthropic = (model, context, options) => {
|
|
|
4810
4852
|
if (event.delta.stop_reason) {
|
|
4811
4853
|
output.stopReason = mapStopReason(event.delta.stop_reason);
|
|
4812
4854
|
}
|
|
4813
|
-
|
|
4814
|
-
if (event.usage.output_tokens != null) output.usage.output = event.usage.output_tokens;
|
|
4815
|
-
if (event.usage.cache_read_input_tokens != null) output.usage.cacheRead = event.usage.cache_read_input_tokens;
|
|
4816
|
-
if (event.usage.cache_creation_input_tokens != null) {
|
|
4817
|
-
output.usage.cacheWrite = event.usage.cache_creation_input_tokens;
|
|
4818
|
-
const creationDelta = event.usage.cache_creation;
|
|
4819
|
-
if (creationDelta?.ephemeral_1h_input_tokens) {
|
|
4820
|
-
output.usage.cacheWriteLong = creationDelta.ephemeral_1h_input_tokens;
|
|
4821
|
-
output.usage.cacheWrite = Math.max(0, output.usage.cacheWrite - creationDelta.ephemeral_1h_input_tokens);
|
|
4822
|
-
}
|
|
4823
|
-
}
|
|
4824
|
-
output.usage.totalTokens = output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite + (output.usage.cacheWriteLong ?? 0);
|
|
4825
|
-
calculateCost(model, output.usage);
|
|
4855
|
+
applyAnthropicUsageUpdate(model, output.usage, event.usage);
|
|
4826
4856
|
}
|
|
4827
4857
|
}
|
|
4828
4858
|
if (options?.signal?.aborted) {
|
|
@@ -4868,7 +4898,10 @@ function getResponsesCompat(model) {
|
|
|
4868
4898
|
supportsStore: compat.supportsStore ?? true,
|
|
4869
4899
|
supportsReasoningEffort: compat.supportsReasoningEffort ?? true,
|
|
4870
4900
|
supportsEncryptedReasoning: compat.supportsEncryptedReasoning ?? true,
|
|
4871
|
-
responsesContinuation: compat.responsesContinuation ?? false
|
|
4901
|
+
responsesContinuation: compat.responsesContinuation ?? false,
|
|
4902
|
+
supportsReasoningSummary: compat.supportsReasoningSummary ?? false,
|
|
4903
|
+
reasoningSummary: compat.reasoningSummary ?? "auto",
|
|
4904
|
+
timeoutReplay: compat.timeoutReplay ?? false
|
|
4872
4905
|
};
|
|
4873
4906
|
}
|
|
4874
4907
|
|
|
@@ -5055,6 +5088,10 @@ function buildResponsesRequest(model, context, options, compat = getResponsesCom
|
|
|
5055
5088
|
if (options?.reasoningEffort && model.reasoning && compat.supportsReasoningEffort) {
|
|
5056
5089
|
params.reasoning = { effort: options.reasoningEffort };
|
|
5057
5090
|
}
|
|
5091
|
+
if (model.reasoning && compat.supportsReasoningSummary && compat.reasoningSummary !== "none") {
|
|
5092
|
+
params.reasoning = params.reasoning ?? {};
|
|
5093
|
+
params.reasoning.summary = compat.reasoningSummary;
|
|
5094
|
+
}
|
|
5058
5095
|
if (compat.supportsEncryptedReasoning && model.reasoning) {
|
|
5059
5096
|
params.include = ["reasoning.encrypted_content"];
|
|
5060
5097
|
}
|
|
@@ -5567,10 +5604,28 @@ function reportSubmission(options, body, details) {
|
|
|
5567
5604
|
} catch {
|
|
5568
5605
|
}
|
|
5569
5606
|
}
|
|
5607
|
+
function reportLifecycle(options, event) {
|
|
5608
|
+
if (!options?.onTransportLifecycle) return;
|
|
5609
|
+
try {
|
|
5610
|
+
options.onTransportLifecycle({ ...event });
|
|
5611
|
+
} catch {
|
|
5612
|
+
}
|
|
5613
|
+
}
|
|
5614
|
+
var localRequestCounter = 0;
|
|
5615
|
+
function nextLocalRequestId(prefix = "req") {
|
|
5616
|
+
localRequestCounter += 1;
|
|
5617
|
+
return `${prefix}-${localRequestCounter}-${Math.random().toString(36).slice(2, 8)}`;
|
|
5618
|
+
}
|
|
5619
|
+
function monotonicNow() {
|
|
5620
|
+
const perf = globalThis.performance;
|
|
5621
|
+
return typeof perf?.now === "function" ? perf.now() : Date.now();
|
|
5622
|
+
}
|
|
5570
5623
|
|
|
5571
5624
|
// ../../packages/ai/src/providers/responses/responses-sse.ts
|
|
5572
5625
|
var DEFAULT_RESPONSES_STREAM_IDLE_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
5573
5626
|
var DEFAULT_RESPONSES_FIRST_BYTE_TIMEOUT_MS = 3 * 60 * 1e3;
|
|
5627
|
+
var REASONING_STREAM_IDLE_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
5628
|
+
var REASONING_FIRST_BYTE_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
5574
5629
|
function parseSSEDataLine2(line) {
|
|
5575
5630
|
const trimmed = line.trim();
|
|
5576
5631
|
if (!trimmed || trimmed.startsWith(":")) return null;
|
|
@@ -5578,15 +5633,137 @@ function parseSSEDataLine2(line) {
|
|
|
5578
5633
|
const data = trimmed.slice(5);
|
|
5579
5634
|
return data.startsWith(" ") ? data.slice(1) : data;
|
|
5580
5635
|
}
|
|
5636
|
+
function singleAttemptSupport() {
|
|
5637
|
+
let capabilities;
|
|
5638
|
+
try {
|
|
5639
|
+
capabilities = getHttpTransport().transportCapabilities;
|
|
5640
|
+
} catch {
|
|
5641
|
+
capabilities = void 0;
|
|
5642
|
+
}
|
|
5643
|
+
return capabilities?.singleAttempt === true ? "verified" : "unverified";
|
|
5644
|
+
}
|
|
5645
|
+
function assertSingleAttempt() {
|
|
5646
|
+
if (singleAttemptSupport() === "verified") return;
|
|
5647
|
+
throw new ResponsesProtocolError(
|
|
5648
|
+
"Responses SSE requires the platform HTTP adapter to declare transportCapabilities.singleAttempt; refusing to submit because a submitted generation must not be re-sent by the transport."
|
|
5649
|
+
);
|
|
5650
|
+
}
|
|
5651
|
+
function explicitBudget(value, field) {
|
|
5652
|
+
if (value === void 0) return void 0;
|
|
5653
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
5654
|
+
throw new ResponsesProtocolError(`Invalid ${field}: expected a finite positive number of milliseconds.`);
|
|
5655
|
+
}
|
|
5656
|
+
return value;
|
|
5657
|
+
}
|
|
5658
|
+
function resolveSSEBudgets(options, model) {
|
|
5659
|
+
const firstResponse = explicitBudget(options?.firstResponseTimeoutMs, "firstResponseTimeoutMs");
|
|
5660
|
+
const streamIdle = explicitBudget(options?.streamIdleTimeoutMs, "streamIdleTimeoutMs");
|
|
5661
|
+
const requestTimeoutMs = explicitBudget(options?.requestTimeoutMs, "requestTimeoutMs");
|
|
5662
|
+
explicitBudget(options?.connectionIdleTimeoutMs, "connectionIdleTimeoutMs");
|
|
5663
|
+
const reasoning = model.reasoning === true;
|
|
5664
|
+
const defaultFirstByte = reasoning ? REASONING_FIRST_BYTE_TIMEOUT_MS : DEFAULT_RESPONSES_FIRST_BYTE_TIMEOUT_MS;
|
|
5665
|
+
const defaultIdle = reasoning ? REASONING_STREAM_IDLE_TIMEOUT_MS : DEFAULT_RESPONSES_STREAM_IDLE_TIMEOUT_MS;
|
|
5666
|
+
if (firstResponse !== void 0 || streamIdle !== void 0) {
|
|
5667
|
+
return {
|
|
5668
|
+
http: {
|
|
5669
|
+
firstByteTimeoutMs: firstResponse ?? defaultFirstByte,
|
|
5670
|
+
streamIdleTimeoutMs: streamIdle ?? defaultIdle
|
|
5671
|
+
},
|
|
5672
|
+
requestTimeoutMs
|
|
5673
|
+
};
|
|
5674
|
+
}
|
|
5675
|
+
if (options?.timeoutMs !== void 0) return { http: { timeoutMs: options.timeoutMs }, requestTimeoutMs };
|
|
5676
|
+
return {
|
|
5677
|
+
http: {
|
|
5678
|
+
firstByteTimeoutMs: defaultFirstByte,
|
|
5679
|
+
streamIdleTimeoutMs: defaultIdle
|
|
5680
|
+
},
|
|
5681
|
+
requestTimeoutMs
|
|
5682
|
+
};
|
|
5683
|
+
}
|
|
5581
5684
|
function streamResponsesSSE(model, context, options) {
|
|
5582
5685
|
const stream = new AssistantMessageEventStream();
|
|
5583
5686
|
void (async () => {
|
|
5584
5687
|
const reducer = new ResponsesEventReducer(model);
|
|
5688
|
+
const localRequestId = nextLocalRequestId("sse");
|
|
5689
|
+
const startedAt = monotonicNow();
|
|
5690
|
+
let lastReceiveAt = startedAt;
|
|
5691
|
+
let receivedMessageCount = 0;
|
|
5692
|
+
let sawModelContent = false;
|
|
5693
|
+
let submitted = false;
|
|
5694
|
+
let terminalAccepted = false;
|
|
5695
|
+
let phase = "request_start";
|
|
5696
|
+
let budgets = {};
|
|
5697
|
+
let totalTimer;
|
|
5698
|
+
let internalTimeout;
|
|
5699
|
+
let localFailure = false;
|
|
5700
|
+
const requestAbort = new AbortController();
|
|
5701
|
+
const onUserAbort = () => requestAbort.abort();
|
|
5702
|
+
const clearTotalTimer = () => {
|
|
5703
|
+
if (totalTimer !== void 0) {
|
|
5704
|
+
clearTimeout(totalTimer);
|
|
5705
|
+
totalTimer = void 0;
|
|
5706
|
+
}
|
|
5707
|
+
};
|
|
5708
|
+
const lifecycle = (extra = {}) => reportLifecycle(options, {
|
|
5709
|
+
...extra,
|
|
5710
|
+
transport: "sse",
|
|
5711
|
+
localRequestId,
|
|
5712
|
+
requestGeneration: 1,
|
|
5713
|
+
reusedConnection: false,
|
|
5714
|
+
phase,
|
|
5715
|
+
requestAgeMs: Math.round(monotonicNow() - startedAt),
|
|
5716
|
+
lastReceiveAgeMs: Math.round(monotonicNow() - lastReceiveAt),
|
|
5717
|
+
budgets,
|
|
5718
|
+
receivedMessageCount,
|
|
5719
|
+
sawModelContent,
|
|
5720
|
+
submissionAttempted: submitted,
|
|
5721
|
+
terminalAccepted,
|
|
5722
|
+
singleAttempt: singleAttemptSupport()
|
|
5723
|
+
});
|
|
5585
5724
|
try {
|
|
5725
|
+
let resolved;
|
|
5726
|
+
try {
|
|
5727
|
+
resolved = resolveSSEBudgets(options, model);
|
|
5728
|
+
} catch (error) {
|
|
5729
|
+
localFailure = true;
|
|
5730
|
+
throw error;
|
|
5731
|
+
}
|
|
5732
|
+
budgets = {
|
|
5733
|
+
firstResponseTimeoutMs: resolved.http.firstByteTimeoutMs ?? resolved.http.timeoutMs,
|
|
5734
|
+
streamIdleTimeoutMs: resolved.http.streamIdleTimeoutMs ?? resolved.http.timeoutMs,
|
|
5735
|
+
requestTimeoutMs: resolved.requestTimeoutMs
|
|
5736
|
+
};
|
|
5737
|
+
options?.signal?.addEventListener("abort", onUserAbort, { once: true });
|
|
5738
|
+
if (options?.signal?.aborted) requestAbort.abort();
|
|
5739
|
+
if (resolved.requestTimeoutMs !== void 0) {
|
|
5740
|
+
totalTimer = setTimeout(() => {
|
|
5741
|
+
totalTimer = void 0;
|
|
5742
|
+
if (terminalAccepted || requestAbort.signal.aborted) return;
|
|
5743
|
+
internalTimeout = "request_total";
|
|
5744
|
+
phase = "timeout";
|
|
5745
|
+
lifecycle({
|
|
5746
|
+
timeoutKind: "request_total",
|
|
5747
|
+
terminalAccepted: false,
|
|
5748
|
+
cancelSource: "internal_timeout",
|
|
5749
|
+
retryAllowed: !submitted,
|
|
5750
|
+
retryDecision: submitted ? "not_retryable_after_submission" : "not_submitted"
|
|
5751
|
+
});
|
|
5752
|
+
requestAbort.abort();
|
|
5753
|
+
}, resolved.requestTimeoutMs);
|
|
5754
|
+
totalTimer.unref?.();
|
|
5755
|
+
}
|
|
5756
|
+
lifecycle();
|
|
5586
5757
|
const apiKey = options?.apiKey || getEnvApiKey(model.provider, model.apiKeyEnv) || "";
|
|
5587
5758
|
if (!apiKey) {
|
|
5588
5759
|
throw new Error(`No API key for provider: ${model.provider}`);
|
|
5589
5760
|
}
|
|
5761
|
+
try {
|
|
5762
|
+
assertSingleAttempt();
|
|
5763
|
+
} catch (error) {
|
|
5764
|
+
localFailure = true;
|
|
5765
|
+
throw error;
|
|
5766
|
+
}
|
|
5590
5767
|
const payload = buildResponsesSSEPayload(model, context, options);
|
|
5591
5768
|
options?.onPayload?.(payload);
|
|
5592
5769
|
const headers = {
|
|
@@ -5598,15 +5775,17 @@ function streamResponsesSSE(model, context, options) {
|
|
|
5598
5775
|
};
|
|
5599
5776
|
const body = JSON.stringify(payload);
|
|
5600
5777
|
reportSubmission(options, body, { transport: "sse", reusedConnection: false, continuation: false, attempt: 1 });
|
|
5778
|
+
submitted = true;
|
|
5779
|
+
phase = "submission_attempted";
|
|
5780
|
+
lifecycle();
|
|
5601
5781
|
const response = await httpRequest(`${model.baseUrl}/responses`, {
|
|
5602
5782
|
method: "POST",
|
|
5603
5783
|
headers,
|
|
5604
5784
|
body,
|
|
5605
|
-
signal:
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
},
|
|
5785
|
+
signal: requestAbort.signal,
|
|
5786
|
+
// The transport must not re-send a submitted generation.
|
|
5787
|
+
retryPolicy: "never",
|
|
5788
|
+
...resolved.http,
|
|
5610
5789
|
label: `${model.provider}/${model.id}`
|
|
5611
5790
|
});
|
|
5612
5791
|
if (!response.ok) {
|
|
@@ -5616,9 +5795,15 @@ function streamResponsesSSE(model, context, options) {
|
|
|
5616
5795
|
if (!response.readSSELines) {
|
|
5617
5796
|
throw new Error("SSE streaming not supported on this platform");
|
|
5618
5797
|
}
|
|
5619
|
-
for await (const line of response.readSSELines(
|
|
5798
|
+
for await (const line of response.readSSELines(requestAbort.signal)) {
|
|
5799
|
+
receivedMessageCount += 1;
|
|
5800
|
+
lastReceiveAt = monotonicNow();
|
|
5620
5801
|
const data = parseSSEDataLine2(line);
|
|
5621
5802
|
if (data === null) continue;
|
|
5803
|
+
if (phase === "submission_attempted") {
|
|
5804
|
+
phase = "first_application_message";
|
|
5805
|
+
lifecycle();
|
|
5806
|
+
}
|
|
5622
5807
|
if (data === "[DONE]") {
|
|
5623
5808
|
continue;
|
|
5624
5809
|
}
|
|
@@ -5632,35 +5817,59 @@ function streamResponsesSSE(model, context, options) {
|
|
|
5632
5817
|
if (!isResponsesEventLike(event)) {
|
|
5633
5818
|
throw new ResponsesProtocolError("Responses SSE payload is not an event object with a string type.");
|
|
5634
5819
|
}
|
|
5820
|
+
if (typeof event.type === "string" && (event.type.startsWith("response.output") || event.type.startsWith("response.content") || event.type.startsWith("response.reasoning"))) {
|
|
5821
|
+
sawModelContent = true;
|
|
5822
|
+
}
|
|
5635
5823
|
for (const assistantEvent of reducer.feed(event)) {
|
|
5636
5824
|
stream.push(assistantEvent);
|
|
5637
5825
|
}
|
|
5638
|
-
if (reducer.isTerminal)
|
|
5826
|
+
if (reducer.isTerminal) {
|
|
5827
|
+
terminalAccepted = true;
|
|
5828
|
+
phase = "terminal_accepted";
|
|
5829
|
+
clearTotalTimer();
|
|
5830
|
+
lifecycle({ retryAllowed: reducer.message.retryable !== false, retryDecision: "terminal" });
|
|
5831
|
+
break;
|
|
5832
|
+
}
|
|
5639
5833
|
}
|
|
5640
5834
|
if (reducer.isTerminal) {
|
|
5641
5835
|
stream.end();
|
|
5642
5836
|
return;
|
|
5643
5837
|
}
|
|
5644
|
-
if (
|
|
5645
|
-
throw new Error("Request was aborted");
|
|
5646
|
-
}
|
|
5647
|
-
if (!reducer.isTerminal) {
|
|
5648
|
-
throw reducer.finishWithoutTerminal();
|
|
5838
|
+
if (requestAbort.signal.aborted) {
|
|
5839
|
+
throw new Error(internalTimeout ? `Responses request timeout (${internalTimeout})` : "Request was aborted");
|
|
5649
5840
|
}
|
|
5650
|
-
|
|
5651
|
-
throw new Error(reducer.message.errorMessage ?? "An unknown error occurred");
|
|
5652
|
-
}
|
|
5653
|
-
stream.end();
|
|
5841
|
+
throw reducer.finishWithoutTerminal();
|
|
5654
5842
|
} catch (error) {
|
|
5655
5843
|
if (reducer.isTerminal) {
|
|
5656
5844
|
stream.end();
|
|
5657
5845
|
return;
|
|
5658
5846
|
}
|
|
5659
5847
|
const message = reducer.message;
|
|
5660
|
-
|
|
5661
|
-
message.
|
|
5848
|
+
const userAborted = options?.signal?.aborted === true;
|
|
5849
|
+
message.stopReason = userAborted ? "aborted" : "error";
|
|
5850
|
+
const adapterTimeoutKind = error instanceof Error ? error.timeoutKind : void 0;
|
|
5851
|
+
if (!internalTimeout && (adapterTimeoutKind === "first_byte" || adapterTimeoutKind === "stream_idle")) {
|
|
5852
|
+
internalTimeout = adapterTimeoutKind === "first_byte" ? "first_response" : "stream_idle";
|
|
5853
|
+
}
|
|
5854
|
+
message.errorMessage = internalTimeout ? `Responses request timeout (${internalTimeout}) after ${Math.round(monotonicNow() - startedAt)}ms` : error instanceof Error ? error.message : JSON.stringify(error);
|
|
5855
|
+
if (internalTimeout && getResponsesCompat(model).timeoutReplay) {
|
|
5856
|
+
message.replayEligible = true;
|
|
5857
|
+
message.replayClass = internalTimeout;
|
|
5858
|
+
}
|
|
5859
|
+
if (submitted || localFailure) message.retryable = false;
|
|
5860
|
+
phase = "cleanup";
|
|
5861
|
+
lifecycle({
|
|
5862
|
+
timeoutKind: internalTimeout,
|
|
5863
|
+
retryAllowed: !submitted && !userAborted && !localFailure,
|
|
5864
|
+
retryDecision: submitted ? "not_retryable_after_submission" : localFailure ? "local_configuration_error" : "not_submitted",
|
|
5865
|
+
cancelSource: userAborted ? "user" : internalTimeout ? "internal_timeout" : "none",
|
|
5866
|
+
cleanupOutcome: "request_abandoned"
|
|
5867
|
+
});
|
|
5662
5868
|
stream.push({ type: "error", reason: message.stopReason, error: message });
|
|
5663
5869
|
stream.end();
|
|
5870
|
+
} finally {
|
|
5871
|
+
clearTotalTimer();
|
|
5872
|
+
options?.signal?.removeEventListener("abort", onUserAbort);
|
|
5664
5873
|
}
|
|
5665
5874
|
})();
|
|
5666
5875
|
return stream;
|
|
@@ -5669,26 +5878,80 @@ function streamResponsesSSE(model, context, options) {
|
|
|
5669
5878
|
// ../../packages/ai/src/providers/responses/responses-ws.ts
|
|
5670
5879
|
var owners = /* @__PURE__ */ new WeakMap();
|
|
5671
5880
|
var MAX_ANCHOR_BYTES = 1024 * 1024;
|
|
5881
|
+
var DEFAULT_CONNECT_TIMEOUT_MS = 3e4;
|
|
5882
|
+
var DEFAULT_FIRST_RESPONSE_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
5883
|
+
var DEFAULT_STREAM_IDLE_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
5884
|
+
var DEFAULT_CONNECTION_IDLE_TIMEOUT_MS = 2 * 60 * 1e3;
|
|
5885
|
+
var REASONING_FIRST_RESPONSE_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
5886
|
+
var REASONING_STREAM_IDLE_TIMEOUT_MS2 = 5 * 60 * 1e3;
|
|
5887
|
+
var ITERATOR_CLEANUP_TIMEOUT_MS = 1e3;
|
|
5888
|
+
function explicitBudget2(value, field) {
|
|
5889
|
+
if (value === void 0) return void 0;
|
|
5890
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
5891
|
+
throw new ResponsesProtocolError(`Invalid ${field}: expected a finite positive number of milliseconds.`);
|
|
5892
|
+
}
|
|
5893
|
+
return value;
|
|
5894
|
+
}
|
|
5895
|
+
function resolveBudgets(options, model) {
|
|
5896
|
+
const firstResponse = explicitBudget2(options?.firstResponseTimeoutMs, "firstResponseTimeoutMs");
|
|
5897
|
+
const streamIdle = explicitBudget2(options?.streamIdleTimeoutMs, "streamIdleTimeoutMs");
|
|
5898
|
+
const requestTimeout = explicitBudget2(options?.requestTimeoutMs, "requestTimeoutMs");
|
|
5899
|
+
const connectionIdle = explicitBudget2(options?.connectionIdleTimeoutMs, "connectionIdleTimeoutMs");
|
|
5900
|
+
const legacy = options?.timeoutMs !== void 0 && Number.isFinite(options.timeoutMs) && options.timeoutMs > 0 ? options.timeoutMs : void 0;
|
|
5901
|
+
const reasoning = model.reasoning === true;
|
|
5902
|
+
return {
|
|
5903
|
+
connectTimeoutMs: legacy ?? DEFAULT_CONNECT_TIMEOUT_MS,
|
|
5904
|
+
firstResponseTimeoutMs: firstResponse ?? legacy ?? (reasoning ? REASONING_FIRST_RESPONSE_TIMEOUT_MS : DEFAULT_FIRST_RESPONSE_TIMEOUT_MS),
|
|
5905
|
+
streamIdleTimeoutMs: streamIdle ?? legacy ?? (reasoning ? REASONING_STREAM_IDLE_TIMEOUT_MS2 : DEFAULT_STREAM_IDLE_TIMEOUT_MS),
|
|
5906
|
+
requestTimeoutMs: requestTimeout,
|
|
5907
|
+
connectionIdleTimeoutMs: connectionIdle ?? DEFAULT_CONNECTION_IDLE_TIMEOUT_MS
|
|
5908
|
+
};
|
|
5909
|
+
}
|
|
5672
5910
|
function disposeModelTransports(owner) {
|
|
5673
5911
|
const state = owners.get(owner);
|
|
5674
5912
|
if (!state) return;
|
|
5675
5913
|
disposeState(owner, state);
|
|
5676
5914
|
}
|
|
5915
|
+
function clearConnectionIdleTimer(state) {
|
|
5916
|
+
if (state.idleTimer !== void 0) {
|
|
5917
|
+
clearTimeout(state.idleTimer);
|
|
5918
|
+
state.idleTimer = void 0;
|
|
5919
|
+
}
|
|
5920
|
+
}
|
|
5677
5921
|
function disposeState(owner, state) {
|
|
5678
5922
|
if (owners.get(owner) === state) owners.delete(owner);
|
|
5923
|
+
clearConnectionIdleTimer(state);
|
|
5679
5924
|
state.anchor = void 0;
|
|
5680
5925
|
state.abort.abort();
|
|
5681
|
-
|
|
5926
|
+
const connection = state.connection;
|
|
5927
|
+
state.connection = void 0;
|
|
5928
|
+
void connection?.close().catch(() => {
|
|
5682
5929
|
});
|
|
5683
5930
|
}
|
|
5684
5931
|
function stateFor(owner) {
|
|
5685
5932
|
let state = owners.get(owner);
|
|
5686
5933
|
if (!state) {
|
|
5687
|
-
state = { busy: false, abort: new AbortController() };
|
|
5934
|
+
state = { busy: false, abort: new AbortController(), requestGeneration: 0, connectionGeneration: 0 };
|
|
5688
5935
|
owners.set(owner, state);
|
|
5689
5936
|
}
|
|
5690
5937
|
return state;
|
|
5691
5938
|
}
|
|
5939
|
+
function armConnectionIdleRecycle(state, connection, generation, timeoutMs) {
|
|
5940
|
+
clearConnectionIdleTimer(state);
|
|
5941
|
+
if (!(timeoutMs > 0)) return;
|
|
5942
|
+
const timer = setTimeout(() => {
|
|
5943
|
+
if (state.idleTimer !== timer) return;
|
|
5944
|
+
state.idleTimer = void 0;
|
|
5945
|
+
if (state.busy || state.connectionGeneration !== generation || state.connection !== connection) return;
|
|
5946
|
+
state.connection = void 0;
|
|
5947
|
+
state.anchor = void 0;
|
|
5948
|
+
state.identity = void 0;
|
|
5949
|
+
void connection.close().catch(() => {
|
|
5950
|
+
});
|
|
5951
|
+
}, timeoutMs);
|
|
5952
|
+
timer.unref?.();
|
|
5953
|
+
state.idleTimer = timer;
|
|
5954
|
+
}
|
|
5692
5955
|
function websocketUrl(base) {
|
|
5693
5956
|
const url = new URL(`${base.replace(/\/$/, "")}/responses`);
|
|
5694
5957
|
if (url.protocol !== "https:" && url.protocol !== "http:") throw new ResponsesProtocolError("Responses requires an HTTP(S) API root.");
|
|
@@ -5706,19 +5969,193 @@ function streamResponsesWS(model, context, options) {
|
|
|
5706
5969
|
stream.end();
|
|
5707
5970
|
return stream;
|
|
5708
5971
|
}
|
|
5972
|
+
let budgets;
|
|
5973
|
+
try {
|
|
5974
|
+
budgets = resolveBudgets(options, model);
|
|
5975
|
+
} catch (error) {
|
|
5976
|
+
reducer.message.stopReason = "error";
|
|
5977
|
+
reducer.message.errorMessage = error instanceof Error ? error.message : String(error);
|
|
5978
|
+
reducer.message.retryable = false;
|
|
5979
|
+
stream.push({ type: "error", reason: "error", error: reducer.message });
|
|
5980
|
+
stream.end();
|
|
5981
|
+
return stream;
|
|
5982
|
+
}
|
|
5709
5983
|
state.busy = true;
|
|
5984
|
+
const localRequestId = nextLocalRequestId("ws");
|
|
5985
|
+
const requestGeneration = ++state.requestGeneration;
|
|
5986
|
+
const startedAt = monotonicNow();
|
|
5987
|
+
let lastReceiveAt = startedAt;
|
|
5988
|
+
let receivedMessageCount = 0;
|
|
5989
|
+
let sawResponseCreated = false;
|
|
5990
|
+
let sawModelContent = false;
|
|
5991
|
+
let reusedConnection = false;
|
|
5992
|
+
let connectionGeneration = state.connectionGeneration;
|
|
5710
5993
|
void (async () => {
|
|
5711
5994
|
let sent = false;
|
|
5712
5995
|
let finished = false;
|
|
5713
5996
|
let terminalEvents = [];
|
|
5714
5997
|
let fallback = false;
|
|
5998
|
+
let timedOut;
|
|
5999
|
+
let retryDecision = "none";
|
|
6000
|
+
let requestTimer;
|
|
6001
|
+
let totalTimer;
|
|
6002
|
+
let receiveCleanupFailed = false;
|
|
6003
|
+
const compat = getResponsesCompat(model);
|
|
6004
|
+
let cleanupCancelSource;
|
|
6005
|
+
const requestAbort = new AbortController();
|
|
5715
6006
|
const publish = (event) => {
|
|
5716
6007
|
if (event.type === "done" || event.type === "error") terminalEvents.push(event);
|
|
5717
6008
|
else stream.push(event);
|
|
5718
6009
|
};
|
|
6010
|
+
const ageMs = () => Math.round(monotonicNow() - startedAt);
|
|
6011
|
+
const lastReceiveAgeMs = () => Math.round(monotonicNow() - lastReceiveAt);
|
|
6012
|
+
const lifecycle = (phase, extra = {}) => reportLifecycle(options, {
|
|
6013
|
+
...extra,
|
|
6014
|
+
transport: "websocket",
|
|
6015
|
+
localRequestId,
|
|
6016
|
+
requestGeneration,
|
|
6017
|
+
connectionGeneration,
|
|
6018
|
+
reusedConnection,
|
|
6019
|
+
phase,
|
|
6020
|
+
requestAgeMs: ageMs(),
|
|
6021
|
+
lastReceiveAgeMs: lastReceiveAgeMs(),
|
|
6022
|
+
budgets,
|
|
6023
|
+
receivedMessageCount,
|
|
6024
|
+
sawResponseCreated,
|
|
6025
|
+
sawModelContent,
|
|
6026
|
+
// The WS transport never re-sends a submitted frame (no hidden retry).
|
|
6027
|
+
singleAttempt: "verified"
|
|
6028
|
+
});
|
|
6029
|
+
const clearRequestTimers = () => {
|
|
6030
|
+
if (requestTimer !== void 0) {
|
|
6031
|
+
clearTimeout(requestTimer);
|
|
6032
|
+
requestTimer = void 0;
|
|
6033
|
+
}
|
|
6034
|
+
if (totalTimer !== void 0) {
|
|
6035
|
+
clearTimeout(totalTimer);
|
|
6036
|
+
totalTimer = void 0;
|
|
6037
|
+
}
|
|
6038
|
+
};
|
|
6039
|
+
const armTotalTimer = () => {
|
|
6040
|
+
if (budgets.requestTimeoutMs === void 0 || totalTimer !== void 0) return;
|
|
6041
|
+
const generation = requestGeneration;
|
|
6042
|
+
totalTimer = setTimeout(() => {
|
|
6043
|
+
totalTimer = void 0;
|
|
6044
|
+
if (generation !== requestGeneration || finished || requestAbort.signal.aborted) return;
|
|
6045
|
+
timedOut = "request_total";
|
|
6046
|
+
retryDecision = sent ? "not_retryable_after_submission" : "not_submitted";
|
|
6047
|
+
lifecycle("timeout", {
|
|
6048
|
+
timeoutKind: "request_total",
|
|
6049
|
+
terminalAccepted: false,
|
|
6050
|
+
cancelSource: "internal_timeout",
|
|
6051
|
+
retryAllowed: !sent,
|
|
6052
|
+
retryDecision,
|
|
6053
|
+
submissionAttempted: sent
|
|
6054
|
+
});
|
|
6055
|
+
requestAbort.abort();
|
|
6056
|
+
}, budgets.requestTimeoutMs);
|
|
6057
|
+
totalTimer.unref?.();
|
|
6058
|
+
};
|
|
6059
|
+
const remainingRequestBudget = () => {
|
|
6060
|
+
if (budgets.requestTimeoutMs === void 0) return void 0;
|
|
6061
|
+
return Math.max(0, Math.floor(budgets.requestTimeoutMs - ageMs()));
|
|
6062
|
+
};
|
|
6063
|
+
let fallbackAbort;
|
|
6064
|
+
let detachFallbackAbort;
|
|
6065
|
+
const fallbackSignal = () => {
|
|
6066
|
+
if (!fallbackAbort) {
|
|
6067
|
+
const controller = new AbortController();
|
|
6068
|
+
const onAbort = () => controller.abort();
|
|
6069
|
+
options?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
6070
|
+
state.abort.signal.addEventListener("abort", onAbort, { once: true });
|
|
6071
|
+
if (options?.signal?.aborted || state.abort.signal.aborted) controller.abort();
|
|
6072
|
+
fallbackAbort = controller;
|
|
6073
|
+
detachFallbackAbort = () => {
|
|
6074
|
+
options?.signal?.removeEventListener("abort", onAbort);
|
|
6075
|
+
state.abort.signal.removeEventListener("abort", onAbort);
|
|
6076
|
+
};
|
|
6077
|
+
}
|
|
6078
|
+
return fallbackAbort.signal;
|
|
6079
|
+
};
|
|
6080
|
+
const closeReceiveIterator = async (iterator) => {
|
|
6081
|
+
const returnFn = iterator.return;
|
|
6082
|
+
if (typeof returnFn !== "function") return;
|
|
6083
|
+
let timer;
|
|
6084
|
+
try {
|
|
6085
|
+
await Promise.race([
|
|
6086
|
+
Promise.resolve().then(() => returnFn.call(iterator)),
|
|
6087
|
+
new Promise((_, reject) => {
|
|
6088
|
+
timer = setTimeout(() => reject(new Error("bounded iterator cleanup")), ITERATOR_CLEANUP_TIMEOUT_MS);
|
|
6089
|
+
timer.unref?.();
|
|
6090
|
+
})
|
|
6091
|
+
]);
|
|
6092
|
+
} catch {
|
|
6093
|
+
receiveCleanupFailed = true;
|
|
6094
|
+
state.connection = void 0;
|
|
6095
|
+
state.anchor = void 0;
|
|
6096
|
+
requestAbort.abort();
|
|
6097
|
+
} finally {
|
|
6098
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
6099
|
+
}
|
|
6100
|
+
};
|
|
6101
|
+
const armPhaseTimer = (kind, timeoutMs) => {
|
|
6102
|
+
if (requestTimer !== void 0) {
|
|
6103
|
+
clearTimeout(requestTimer);
|
|
6104
|
+
requestTimer = void 0;
|
|
6105
|
+
}
|
|
6106
|
+
if (timeoutMs === void 0 || timeoutMs <= 0 || finished) return;
|
|
6107
|
+
const generation = requestGeneration;
|
|
6108
|
+
requestTimer = setTimeout(() => {
|
|
6109
|
+
requestTimer = void 0;
|
|
6110
|
+
if (generation !== requestGeneration || finished || requestAbort.signal.aborted) return;
|
|
6111
|
+
timedOut = kind;
|
|
6112
|
+
retryDecision = sent ? "not_retryable_after_submission" : "not_submitted";
|
|
6113
|
+
lifecycle("timeout", {
|
|
6114
|
+
timeoutKind: kind,
|
|
6115
|
+
terminalAccepted: false,
|
|
6116
|
+
cancelSource: "internal_timeout",
|
|
6117
|
+
retryAllowed: !sent,
|
|
6118
|
+
retryDecision,
|
|
6119
|
+
submissionAttempted: sent,
|
|
6120
|
+
// L2 diagnostic surface: this timeout qualifies for bounded replay.
|
|
6121
|
+
...compat.timeoutReplay && sent ? { replayExempt: true } : {}
|
|
6122
|
+
});
|
|
6123
|
+
requestAbort.abort();
|
|
6124
|
+
}, timeoutMs);
|
|
6125
|
+
requestTimer.unref?.();
|
|
6126
|
+
};
|
|
6127
|
+
const onOwnerAbort = () => requestAbort.abort();
|
|
6128
|
+
if (state.abort.signal.aborted) requestAbort.abort();
|
|
6129
|
+
else state.abort.signal.addEventListener("abort", onOwnerAbort, { once: true });
|
|
5719
6130
|
const abort = () => disposeState(owner, state);
|
|
5720
6131
|
options?.signal?.addEventListener("abort", abort, { once: true });
|
|
6132
|
+
const runSSEFallback = async () => {
|
|
6133
|
+
const remaining = remainingRequestBudget();
|
|
6134
|
+
if (remaining !== void 0 && remaining <= 0) {
|
|
6135
|
+
timedOut = "request_total";
|
|
6136
|
+
retryDecision = sent ? "not_retryable_after_submission" : "not_submitted";
|
|
6137
|
+
lifecycle("timeout", {
|
|
6138
|
+
timeoutKind: "request_total",
|
|
6139
|
+
terminalAccepted: false,
|
|
6140
|
+
cancelSource: "internal_timeout",
|
|
6141
|
+
retryAllowed: !sent,
|
|
6142
|
+
retryDecision,
|
|
6143
|
+
submissionAttempted: sent
|
|
6144
|
+
});
|
|
6145
|
+
throw new Error("Responses request timeout (request_total)");
|
|
6146
|
+
}
|
|
6147
|
+
clearRequestTimers();
|
|
6148
|
+
fallback = true;
|
|
6149
|
+
const fallbackOptions = {
|
|
6150
|
+
...options,
|
|
6151
|
+
signal: fallbackSignal(),
|
|
6152
|
+
...remaining !== void 0 ? { requestTimeoutMs: remaining } : {}
|
|
6153
|
+
};
|
|
6154
|
+
for await (const event of streamResponsesSSE(model, context, fallbackOptions)) publish(event);
|
|
6155
|
+
finished = true;
|
|
6156
|
+
};
|
|
5721
6157
|
try {
|
|
6158
|
+
armTotalTimer();
|
|
5722
6159
|
if (options?.signal?.aborted) {
|
|
5723
6160
|
abort();
|
|
5724
6161
|
throw new Error("Request aborted");
|
|
@@ -5729,42 +6166,52 @@ function streamResponsesWS(model, context, options) {
|
|
|
5729
6166
|
const headers = { Authorization: `Bearer ${key}`, ...model.headers, ...options?.headers };
|
|
5730
6167
|
const { input, ...settings } = payload;
|
|
5731
6168
|
const identity = JSON.stringify([model.provider, model.api, model.id, model.baseUrl, headers, settings]);
|
|
6169
|
+
clearConnectionIdleTimer(state);
|
|
5732
6170
|
if (state.identity !== identity || state.connection?.state() !== "open") {
|
|
5733
|
-
|
|
6171
|
+
const stale = state.connection;
|
|
5734
6172
|
state.connection = void 0;
|
|
5735
6173
|
state.anchor = void 0;
|
|
5736
6174
|
state.identity = identity;
|
|
6175
|
+
await stale?.close().catch(() => {
|
|
6176
|
+
});
|
|
5737
6177
|
}
|
|
6178
|
+
reusedConnection = !!state.connection;
|
|
5738
6179
|
if (state.abort.signal.aborted) throw new Error("Request aborted");
|
|
6180
|
+
lifecycle("request_start");
|
|
5739
6181
|
const client = getWebSocketClient();
|
|
5740
6182
|
if (!client) {
|
|
5741
6183
|
if (options?.transport === "auto") {
|
|
5742
|
-
|
|
5743
|
-
for await (const event of streamResponsesSSE(model, context, { ...options, signal: state.abort.signal })) publish(event);
|
|
5744
|
-
finished = true;
|
|
6184
|
+
await runSSEFallback();
|
|
5745
6185
|
return;
|
|
5746
6186
|
}
|
|
5747
6187
|
throw new Error("WebSocket transport is not available on this host.");
|
|
5748
6188
|
}
|
|
5749
|
-
|
|
5750
|
-
if (!
|
|
6189
|
+
let connection = state.connection;
|
|
6190
|
+
if (!connection) {
|
|
5751
6191
|
try {
|
|
5752
|
-
|
|
6192
|
+
connection = await client.connect(websocketUrl(model.baseUrl), {
|
|
5753
6193
|
headers,
|
|
5754
|
-
signal:
|
|
5755
|
-
connectTimeoutMs:
|
|
5756
|
-
idleTimeoutMs: options?.timeoutMs ?? 12e4
|
|
6194
|
+
signal: requestAbort.signal,
|
|
6195
|
+
connectTimeoutMs: budgets.connectTimeoutMs
|
|
5757
6196
|
});
|
|
5758
6197
|
} catch (error) {
|
|
6198
|
+
if (error instanceof WebSocketTransportError && error.kind === "timeout") timedOut = "connect";
|
|
5759
6199
|
if (options?.transport === "auto" && error instanceof WebSocketTransportError && error.kind === "handshake" && [404, 405, 426, 501].includes(error.status ?? 0) && !options.signal?.aborted && !state.abort.signal.aborted) {
|
|
5760
|
-
|
|
5761
|
-
for await (const event of streamResponsesSSE(model, context, { ...options, signal: state.abort.signal })) publish(event);
|
|
5762
|
-
finished = true;
|
|
6200
|
+
await runSSEFallback();
|
|
5763
6201
|
return;
|
|
5764
6202
|
}
|
|
5765
6203
|
throw error;
|
|
5766
6204
|
}
|
|
6205
|
+
state.connection = connection;
|
|
6206
|
+
connectionGeneration = ++state.connectionGeneration;
|
|
6207
|
+
if (state.abort.signal.aborted || requestAbort.signal.aborted) {
|
|
6208
|
+
state.connection = void 0;
|
|
6209
|
+
void connection.close().catch(() => {
|
|
6210
|
+
});
|
|
6211
|
+
throw new Error("Request aborted");
|
|
6212
|
+
}
|
|
5767
6213
|
}
|
|
6214
|
+
const activeConnection = connection;
|
|
5768
6215
|
const fullInput = input;
|
|
5769
6216
|
const anchor = state.anchor;
|
|
5770
6217
|
if (!options?.onPayload && model.compat?.responsesContinuation && anchor && fullInput.length > anchor.count && JSON.stringify(fullInput.slice(0, anchor.count)) === anchor.prefix) {
|
|
@@ -5775,65 +6222,127 @@ function streamResponsesWS(model, context, options) {
|
|
|
5775
6222
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
5776
6223
|
let rebuild = false;
|
|
5777
6224
|
options?.onPayload?.(payload);
|
|
5778
|
-
if (state.abort.signal.aborted) throw new Error("Request aborted");
|
|
6225
|
+
if (state.abort.signal.aborted || requestAbort.signal.aborted) throw new Error("Request aborted");
|
|
5779
6226
|
sent = true;
|
|
5780
6227
|
const body = JSON.stringify(payload);
|
|
5781
6228
|
reportSubmission(options, body, { transport: "websocket", reusedConnection: reusedConnection || attempt > 0, continuation: !!payload.previous_response_id, attempt: attempt + 1 });
|
|
5782
|
-
|
|
6229
|
+
lifecycle("submission_attempted", { submissionAttempted: true });
|
|
6230
|
+
armPhaseTimer("first_response", budgets.firstResponseTimeoutMs);
|
|
6231
|
+
await activeConnection.send(body, { signal: requestAbort.signal });
|
|
5783
6232
|
let started = false;
|
|
5784
6233
|
let responseId;
|
|
5785
|
-
|
|
5786
|
-
|
|
5787
|
-
|
|
5788
|
-
|
|
5789
|
-
|
|
5790
|
-
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
rebuild = true;
|
|
5794
|
-
break;
|
|
5795
|
-
}
|
|
5796
|
-
if (!started && typeof event.type === "string" && event.type !== "error" && !event.type.startsWith("response.")) continue;
|
|
5797
|
-
if (!started && event.type !== "error") {
|
|
5798
|
-
if (event.type !== "response.created" || !isRecord2(event.response) || typeof event.response.id !== "string" || event.response.id === state.lastResponseId) {
|
|
5799
|
-
throw new ResponsesProtocolError("Expected a new response.created before output.");
|
|
6234
|
+
const receiveIterator = activeConnection.receive(requestAbort.signal)[Symbol.asyncIterator]();
|
|
6235
|
+
let receiveDone = false;
|
|
6236
|
+
try {
|
|
6237
|
+
while (true) {
|
|
6238
|
+
const step = await receiveIterator.next();
|
|
6239
|
+
if (step.done) {
|
|
6240
|
+
receiveDone = true;
|
|
6241
|
+
break;
|
|
5800
6242
|
}
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
if (
|
|
6243
|
+
const message = step.value;
|
|
6244
|
+
receivedMessageCount += 1;
|
|
6245
|
+
lastReceiveAt = monotonicNow();
|
|
6246
|
+
armPhaseTimer("stream_idle", budgets.streamIdleTimeoutMs);
|
|
6247
|
+
if (receivedMessageCount === 1) lifecycle("first_application_message");
|
|
6248
|
+
if (state.abort.signal.aborted || requestAbort.signal.aborted) {
|
|
6249
|
+
throw new Error(timedOut ? `Responses request timeout (${timedOut})` : "Request aborted");
|
|
6250
|
+
}
|
|
6251
|
+
if (message.isBinary) throw new ResponsesProtocolError("Responses requires JSON text messages.");
|
|
6252
|
+
const event = JSON.parse(message.data);
|
|
6253
|
+
if (!isRecord2(event)) throw new ResponsesProtocolError("Invalid Responses WebSocket event.");
|
|
6254
|
+
if (typeof event.type === "string") {
|
|
6255
|
+
if (event.type === "response.created") sawResponseCreated = true;
|
|
6256
|
+
else if (event.type.startsWith("response.output") || event.type.startsWith("response.content") || event.type.startsWith("response.reasoning")) sawModelContent = true;
|
|
6257
|
+
}
|
|
6258
|
+
if (!started && attempt === 0 && payload.previous_response_id && event.type === "error" && event.status === 400 && isRecord2(event.error) && event.error.code === "previous_response_not_found" && event.error.param === "previous_response_id") {
|
|
6259
|
+
delete payload.previous_response_id;
|
|
6260
|
+
payload.input = fullInput;
|
|
6261
|
+
rebuild = true;
|
|
6262
|
+
break;
|
|
6263
|
+
}
|
|
6264
|
+
if (!started && typeof event.type === "string" && event.type !== "error" && !event.type.startsWith("response.")) continue;
|
|
6265
|
+
if (!started && event.type !== "error") {
|
|
6266
|
+
if (event.type !== "response.created" || !isRecord2(event.response) || typeof event.response.id !== "string" || event.response.id === state.lastResponseId) {
|
|
6267
|
+
throw new ResponsesProtocolError("Expected a new response.created before output.");
|
|
6268
|
+
}
|
|
6269
|
+
started = true;
|
|
6270
|
+
responseId = event.response.id;
|
|
6271
|
+
}
|
|
6272
|
+
if (responseId && isRecord2(event.response) && event.response.id !== responseId) throw new ResponsesProtocolError("Response identity changed within a request.");
|
|
6273
|
+
const events = reducer.feed(event);
|
|
6274
|
+
if (reducer.isTerminal) {
|
|
6275
|
+
if (reducer.message.stopReason === "error") reducer.message.retryable = false;
|
|
6276
|
+
const response = reducer.terminalResponse;
|
|
6277
|
+
state.lastResponseId = response?.id;
|
|
6278
|
+
if (!options?.onPayload && response?.status === "completed" && reducer.message.stopReason !== "error") {
|
|
6279
|
+
const prefixItems = [...fullInput, ...buildResponsesOutput(reducer.message)];
|
|
6280
|
+
const prefix = JSON.stringify(prefixItems);
|
|
6281
|
+
if (prefix.length * 2 <= MAX_ANCHOR_BYTES) state.anchor = { id: response.id, prefix, count: prefixItems.length };
|
|
6282
|
+
}
|
|
6283
|
+
finished = true;
|
|
6284
|
+
clearRequestTimers();
|
|
6285
|
+
lifecycle("terminal_accepted", {
|
|
6286
|
+
terminalAccepted: true,
|
|
6287
|
+
retryAllowed: reducer.message.retryable !== false,
|
|
6288
|
+
retryDecision: "terminal"
|
|
6289
|
+
});
|
|
6290
|
+
for (const e of events) publish(e);
|
|
6291
|
+
break;
|
|
5814
6292
|
}
|
|
5815
|
-
finished = true;
|
|
5816
6293
|
for (const e of events) publish(e);
|
|
5817
|
-
break;
|
|
5818
6294
|
}
|
|
5819
|
-
|
|
6295
|
+
} finally {
|
|
6296
|
+
if (!receiveDone) await closeReceiveIterator(receiveIterator);
|
|
6297
|
+
}
|
|
6298
|
+
if (rebuild) {
|
|
6299
|
+
retryDecision = "rebuild_previous_response";
|
|
6300
|
+
continue;
|
|
5820
6301
|
}
|
|
5821
|
-
if (rebuild) continue;
|
|
5822
6302
|
if (!reducer.isTerminal) throw reducer.finishWithoutTerminal();
|
|
5823
6303
|
break;
|
|
5824
6304
|
}
|
|
5825
6305
|
} catch (error) {
|
|
5826
6306
|
if (!finished) {
|
|
5827
6307
|
const message = reducer.message;
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
6308
|
+
const userAborted = options?.signal?.aborted === true;
|
|
6309
|
+
const ownerDisposed = !userAborted && !timedOut && state.abort.signal.aborted;
|
|
6310
|
+
message.stopReason = userAborted || ownerDisposed ? "aborted" : "error";
|
|
6311
|
+
const cause = timedOut ? `Responses request timeout (${timedOut}) after ${ageMs()}ms` : error instanceof Error ? error.message : String(error);
|
|
6312
|
+
message.errorMessage = cause;
|
|
6313
|
+
if (timedOut && compat.timeoutReplay) {
|
|
6314
|
+
message.replayEligible = true;
|
|
6315
|
+
message.replayClass = timedOut;
|
|
6316
|
+
}
|
|
6317
|
+
if (sent) {
|
|
6318
|
+
message.retryable = false;
|
|
6319
|
+
retryDecision = "not_retryable_after_submission";
|
|
6320
|
+
}
|
|
6321
|
+
cleanupCancelSource = userAborted ? "user" : timedOut ? "internal_timeout" : ownerDisposed ? "owner_disposal" : "none";
|
|
5831
6322
|
stream.push({ type: "error", reason: message.stopReason, error: message });
|
|
5832
6323
|
}
|
|
5833
6324
|
} finally {
|
|
6325
|
+
clearRequestTimers();
|
|
6326
|
+
detachFallbackAbort?.();
|
|
6327
|
+
state.abort.signal.removeEventListener("abort", onOwnerAbort);
|
|
5834
6328
|
options?.signal?.removeEventListener("abort", abort);
|
|
5835
6329
|
state.busy = false;
|
|
5836
|
-
|
|
6330
|
+
const cancelSource = cleanupCancelSource ?? (options?.signal?.aborted ? "user" : timedOut ? "internal_timeout" : state.abort.signal.aborted ? "owner_disposal" : "none");
|
|
6331
|
+
const retain = finished && !fallback && !receiveCleanupFailed && !!options?.transportOwner && reducer.message.stopReason !== "error" && !timedOut && state.connection !== void 0 && !state.abort.signal.aborted;
|
|
6332
|
+
if (!retain) disposeState(owner, state);
|
|
6333
|
+
else if (state.connection) {
|
|
6334
|
+
armConnectionIdleRecycle(state, state.connection, connectionGeneration, budgets.connectionIdleTimeoutMs);
|
|
6335
|
+
}
|
|
6336
|
+
const cleanupOutcome = receiveCleanupFailed ? "cleanup_failed" : retain ? "connection_reused" : finished ? "connection_closed" : "request_abandoned";
|
|
6337
|
+
lifecycle("cleanup", {
|
|
6338
|
+
terminalAccepted: finished,
|
|
6339
|
+
timeoutKind: timedOut,
|
|
6340
|
+
submissionAttempted: sent,
|
|
6341
|
+
retryAllowed: !finished && !sent && reducer.message.stopReason !== "aborted" && reducer.message.retryable !== false,
|
|
6342
|
+
cancelSource,
|
|
6343
|
+
retryDecision,
|
|
6344
|
+
cleanupOutcome
|
|
6345
|
+
});
|
|
5837
6346
|
for (const event of terminalEvents) stream.push(event);
|
|
5838
6347
|
stream.end();
|
|
5839
6348
|
}
|