@opengeni/xai-subscription 0.1.0 → 0.1.2-canary.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/dist/{chunk-JEFGIUGN.js → chunk-QSLO7YEK.js} +3 -1
- package/dist/{chunk-JEFGIUGN.js.map → chunk-QSLO7YEK.js.map} +1 -1
- package/dist/constants.d.ts +1 -0
- package/dist/constants.js +3 -1
- package/dist/errors.d.ts +52 -0
- package/dist/index.js +553 -30
- package/dist/index.js.map +1 -1
- package/dist/oauth.d.ts +8 -0
- package/dist/request-context.d.ts +25 -0
- package/package.json +2 -2
- package/src/constants.ts +1 -0
- package/src/errors.ts +170 -0
- package/src/fetch.ts +529 -29
- package/src/oauth.ts +35 -0
- package/src/request-context.ts +26 -0
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
XAI_REFRESH_FALLBACK_MS,
|
|
14
14
|
XAI_REFRESH_WINDOW_MS,
|
|
15
15
|
XAI_RESPONSE_SDK_OUTER_TIMEOUT_MS,
|
|
16
|
+
XAI_RESPONSE_STREAM_IDLE_TIMEOUT_MS,
|
|
16
17
|
XAI_SUBSCRIPTION_AUTO_COMPACTION_PERCENT,
|
|
17
18
|
XAI_SUBSCRIPTION_EFFECTIVE_CONTEXT_PERCENT,
|
|
18
19
|
XAI_SUBSCRIPTION_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
|
|
@@ -31,7 +32,7 @@ import {
|
|
|
31
32
|
XAI_VIDEO_POLL_INTERVAL_MS,
|
|
32
33
|
XAI_VIDEO_POLL_REQUEST_TIMEOUT_MS,
|
|
33
34
|
XAI_VIDEO_START_TIMEOUT_MS
|
|
34
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-QSLO7YEK.js";
|
|
35
36
|
|
|
36
37
|
// src/errors.ts
|
|
37
38
|
var XaiSubscriptionError = class extends Error {
|
|
@@ -54,6 +55,113 @@ var XaiSubscriptionTransientError = class extends XaiSubscriptionError {
|
|
|
54
55
|
this.name = "XaiSubscriptionTransientError";
|
|
55
56
|
}
|
|
56
57
|
};
|
|
58
|
+
var XaiSubscriptionHostedToolContinuationError = class extends XaiSubscriptionError {
|
|
59
|
+
code = "xai_hosted_tool_continuation_stalled";
|
|
60
|
+
constructor() {
|
|
61
|
+
super(
|
|
62
|
+
"timeout",
|
|
63
|
+
"SuperGrok stopped responding after completing a hosted search. The partial response was preserved and was not replayed automatically."
|
|
64
|
+
);
|
|
65
|
+
this.name = "XaiSubscriptionHostedToolContinuationError";
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var XaiSubscriptionStreamingTerminalError = class extends XaiSubscriptionError {
|
|
69
|
+
code;
|
|
70
|
+
eventType;
|
|
71
|
+
requestId;
|
|
72
|
+
diagnosticTruncated;
|
|
73
|
+
headers;
|
|
74
|
+
constructor(input) {
|
|
75
|
+
super("provider_rejected", input.message, input.status);
|
|
76
|
+
this.name = "XaiSubscriptionStreamingTerminalError";
|
|
77
|
+
this.code = input.code;
|
|
78
|
+
this.eventType = input.eventType;
|
|
79
|
+
this.requestId = input.requestId ?? null;
|
|
80
|
+
this.diagnosticTruncated = input.diagnosticTruncated === true;
|
|
81
|
+
this.headers = input.headers ?? new Headers();
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var XAI_SUBSCRIPTION_RATE_LIMIT_CODES = /* @__PURE__ */ new Set([
|
|
85
|
+
"rate_limit_exceeded",
|
|
86
|
+
"too_many_requests",
|
|
87
|
+
"overloaded_error",
|
|
88
|
+
"server_overloaded",
|
|
89
|
+
"capacity_exceeded",
|
|
90
|
+
"resource_exhausted"
|
|
91
|
+
]);
|
|
92
|
+
var XAI_SUBSCRIPTION_RATE_LIMIT_MESSAGE = /the model is currently at capacity due to high demand/i;
|
|
93
|
+
function isXaiSubscriptionRateLimitDiagnostic(input) {
|
|
94
|
+
if (input.status === 429) return true;
|
|
95
|
+
const code = (input.code ?? "").trim().toLowerCase();
|
|
96
|
+
if (XAI_SUBSCRIPTION_RATE_LIMIT_CODES.has(code)) return true;
|
|
97
|
+
return XAI_SUBSCRIPTION_RATE_LIMIT_MESSAGE.test(input.message ?? "");
|
|
98
|
+
}
|
|
99
|
+
function classifyXaiSubscriptionStreamingTerminalError(error) {
|
|
100
|
+
let current = error;
|
|
101
|
+
for (let depth = 0; depth < 8 && current && typeof current === "object"; depth += 1) {
|
|
102
|
+
const value = current;
|
|
103
|
+
if (current instanceof XaiSubscriptionStreamingTerminalError || value.name === "XaiSubscriptionStreamingTerminalError") {
|
|
104
|
+
const code = typeof value.code === "string" && value.code.length > 0 ? value.code : null;
|
|
105
|
+
const eventType = typeof value.eventType === "string" && value.eventType.length > 0 ? value.eventType : null;
|
|
106
|
+
const message = typeof value.message === "string" ? value.message : "";
|
|
107
|
+
if (!code || !eventType || message.length === 0) return null;
|
|
108
|
+
const status = Number(value.status);
|
|
109
|
+
return {
|
|
110
|
+
message,
|
|
111
|
+
code,
|
|
112
|
+
eventType,
|
|
113
|
+
requestId: typeof value.requestId === "string" ? value.requestId : null,
|
|
114
|
+
...Number.isInteger(status) && status >= 100 && status <= 599 ? { status } : {},
|
|
115
|
+
diagnosticTruncated: value.diagnosticTruncated === true
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
current = value.cause;
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
var XaiSubscriptionStreamIdleTimeoutError = class extends XaiSubscriptionError {
|
|
123
|
+
constructor(requestId, responseObserved, eventCount, lastEventType, silenceDurationMs) {
|
|
124
|
+
super(
|
|
125
|
+
"timeout",
|
|
126
|
+
"SuperGrok stopped sending valid response events. The partial response was preserved and was not replayed automatically."
|
|
127
|
+
);
|
|
128
|
+
this.requestId = requestId;
|
|
129
|
+
this.responseObserved = responseObserved;
|
|
130
|
+
this.eventCount = eventCount;
|
|
131
|
+
this.lastEventType = lastEventType;
|
|
132
|
+
this.silenceDurationMs = silenceDurationMs;
|
|
133
|
+
this.name = "XaiSubscriptionStreamIdleTimeoutError";
|
|
134
|
+
}
|
|
135
|
+
code = "xai_response_stream_idle_timeout";
|
|
136
|
+
};
|
|
137
|
+
function classifyXaiSubscriptionStreamIdleTimeoutError(error) {
|
|
138
|
+
let current = error;
|
|
139
|
+
for (let depth = 0; depth < 8 && current && typeof current === "object"; depth += 1) {
|
|
140
|
+
const value = current;
|
|
141
|
+
if (current instanceof XaiSubscriptionStreamIdleTimeoutError || value.code === "xai_response_stream_idle_timeout") {
|
|
142
|
+
return {
|
|
143
|
+
requestId: typeof value.requestId === "string" ? value.requestId : null,
|
|
144
|
+
responseObserved: value.responseObserved === true,
|
|
145
|
+
eventCount: typeof value.eventCount === "number" && Number.isSafeInteger(value.eventCount) ? value.eventCount : 0,
|
|
146
|
+
lastEventType: typeof value.lastEventType === "string" ? value.lastEventType : null,
|
|
147
|
+
silenceDurationMs: typeof value.silenceDurationMs === "number" && Number.isFinite(value.silenceDurationMs) ? Math.max(0, value.silenceDurationMs) : 0
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
current = value.cause;
|
|
151
|
+
}
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
function isXaiSubscriptionHostedToolContinuationError(error) {
|
|
155
|
+
let current = error;
|
|
156
|
+
for (let depth = 0; depth < 6 && current && typeof current === "object"; depth += 1) {
|
|
157
|
+
if (current instanceof XaiSubscriptionHostedToolContinuationError) return true;
|
|
158
|
+
if (current.code === "xai_hosted_tool_continuation_stalled") {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
current = current.cause;
|
|
162
|
+
}
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
57
165
|
|
|
58
166
|
// src/bounded-operation.ts
|
|
59
167
|
async function runBoundedXaiOperation(operation, timeoutMs) {
|
|
@@ -303,6 +411,27 @@ async function fetchXaiVerifiedIdentity(accessToken, options = {}) {
|
|
|
303
411
|
name: nullableString(body.name)
|
|
304
412
|
};
|
|
305
413
|
}
|
|
414
|
+
function xaiIdentityFromDeviceTokens(tokens) {
|
|
415
|
+
const accessClaims = decodeXaiJwtPayload(tokens.accessToken);
|
|
416
|
+
const idClaims = tokens.idToken ? decodeXaiJwtPayload(tokens.idToken) : null;
|
|
417
|
+
const subject = nullableString(accessClaims?.principal_id) ?? nullableString(accessClaims?.principalId) ?? nullableString(idClaims?.sub) ?? nullableString(accessClaims?.sub);
|
|
418
|
+
if (!subject) {
|
|
419
|
+
throw new XaiSubscriptionError(
|
|
420
|
+
"invalid_response",
|
|
421
|
+
"xAI OAuth token response is missing account identity"
|
|
422
|
+
);
|
|
423
|
+
}
|
|
424
|
+
const givenName = nullableString(idClaims?.given_name) ?? nullableString(accessClaims?.given_name);
|
|
425
|
+
const familyName = nullableString(idClaims?.family_name) ?? nullableString(accessClaims?.family_name);
|
|
426
|
+
const combinedName = [givenName, familyName].filter(Boolean).join(" ") || null;
|
|
427
|
+
const emailVerifiedClaim = idClaims?.email_verified ?? accessClaims?.email_verified;
|
|
428
|
+
return {
|
|
429
|
+
subject,
|
|
430
|
+
email: nullableString(idClaims?.email) ?? nullableString(accessClaims?.email),
|
|
431
|
+
emailVerified: typeof emailVerifiedClaim === "boolean" ? emailVerifiedClaim : null,
|
|
432
|
+
name: nullableString(idClaims?.name) ?? nullableString(accessClaims?.name) ?? combinedName
|
|
433
|
+
};
|
|
434
|
+
}
|
|
306
435
|
function decodeXaiJwtPayload(jwt) {
|
|
307
436
|
const payload = jwt.split(".")[1];
|
|
308
437
|
if (!payload) return null;
|
|
@@ -425,6 +554,17 @@ var XAI_SUBSCRIPTION_REQUEST_BODY_NORMALIZED_HEADER = "x-opengeni-xai-subscripti
|
|
|
425
554
|
var XAI_SUBSCRIPTION_REQUEST_MODEL_HEADER = "x-opengeni-xai-subscription-model";
|
|
426
555
|
var XAI_SUBSCRIPTION_REQUEST_ID_HEADER = "x-opengeni-xai-subscription-request-id";
|
|
427
556
|
var MAX_ERROR_BODY_BYTES = 64 * 1024;
|
|
557
|
+
var MAX_STREAM_IDLE_TIMEOUT_MS = 24 * 60 * 6e4;
|
|
558
|
+
var MAX_TERMINAL_ERROR_FIELD_BYTES = 256;
|
|
559
|
+
var MAX_TERMINAL_ERROR_MESSAGE_BYTES = 4 * 1024;
|
|
560
|
+
var TERMINAL_ERROR_TRUNCATION_MARKER = "\u2026 [truncated]";
|
|
561
|
+
var NON_RETRYABLE_SSE_ERROR_CODES = /* @__PURE__ */ new Set([
|
|
562
|
+
"context_length_exceeded",
|
|
563
|
+
"invalid_prompt",
|
|
564
|
+
"invalid_request",
|
|
565
|
+
"insufficient_quota",
|
|
566
|
+
"usage_limit_reached"
|
|
567
|
+
]);
|
|
428
568
|
function xaiSubscriptionFetch(base) {
|
|
429
569
|
return async (input, init) => {
|
|
430
570
|
const context = xaiSubscriptionRequestStorage.getStore();
|
|
@@ -439,7 +579,7 @@ function xaiSubscriptionFetch(base) {
|
|
|
439
579
|
new Headers(init.headers).forEach((value, key) => originalHeaders.set(key, value));
|
|
440
580
|
}
|
|
441
581
|
const normalized = originalHeaders.get(XAI_SUBSCRIPTION_REQUEST_BODY_NORMALIZED_HEADER) === "1";
|
|
442
|
-
const requestId = originalHeaders.get(XAI_SUBSCRIPTION_REQUEST_ID_HEADER) ?? randomUUID();
|
|
582
|
+
const requestId = originalHeaders.get(XAI_SUBSCRIPTION_REQUEST_ID_HEADER) ?? context.nextRequestId?.() ?? randomUUID();
|
|
443
583
|
const handedOffModel = originalHeaders.get(XAI_SUBSCRIPTION_REQUEST_MODEL_HEADER);
|
|
444
584
|
originalHeaders.delete(XAI_SUBSCRIPTION_REQUEST_BODY_NORMALIZED_HEADER);
|
|
445
585
|
originalHeaders.delete(XAI_SUBSCRIPTION_REQUEST_ID_HEADER);
|
|
@@ -449,7 +589,10 @@ function xaiSubscriptionFetch(base) {
|
|
|
449
589
|
const parsed = body ? JSON.parse(body) : {};
|
|
450
590
|
const normalizedBody = normalized ? parsed : normalizeXaiSubscriptionRequestBody(parsed, context.resolveModel, context.hostedSearch);
|
|
451
591
|
const model = handedOffModel ?? (typeof normalizedBody.model === "string" ? normalizedBody.model : "grok-4.6");
|
|
452
|
-
const
|
|
592
|
+
const streamIdleTimeoutMs = boundedStreamIdleTimeout(
|
|
593
|
+
context.streamIdleTimeoutMs ?? context.hostedToolContinuationTimeoutMs
|
|
594
|
+
);
|
|
595
|
+
const send = async (refresh, transportAttempt) => {
|
|
453
596
|
const token = refresh ? await context.refresh() : await context.getToken();
|
|
454
597
|
const headers = new Headers(originalHeaders);
|
|
455
598
|
headers.set("authorization", `Bearer ${token.accessToken}`);
|
|
@@ -468,19 +611,64 @@ function xaiSubscriptionFetch(base) {
|
|
|
468
611
|
headers.set("x-grok-req-id", requestId);
|
|
469
612
|
headers.set("x-grok-agent-id", context.turnId);
|
|
470
613
|
headers.set("x-grok-model-override", model);
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
614
|
+
const audit = {
|
|
615
|
+
ctx: context,
|
|
616
|
+
requestId,
|
|
617
|
+
transportAttempt,
|
|
618
|
+
model,
|
|
619
|
+
startedAt: performance.now(),
|
|
620
|
+
streamIdleTimeoutMs,
|
|
621
|
+
eventCount: 0,
|
|
622
|
+
lastEventType: null,
|
|
623
|
+
lastProgressAt: null,
|
|
624
|
+
terminal: false,
|
|
625
|
+
terminalEventEmitted: false
|
|
626
|
+
};
|
|
627
|
+
let response;
|
|
628
|
+
try {
|
|
629
|
+
await emitRequestEvent(audit, {
|
|
630
|
+
phase: "started",
|
|
631
|
+
responseObserved: false
|
|
632
|
+
});
|
|
633
|
+
response = await base(url, {
|
|
634
|
+
...init,
|
|
635
|
+
method: init?.method ?? (input instanceof Request ? input.method : "POST"),
|
|
636
|
+
headers,
|
|
637
|
+
body: JSON.stringify(normalizedBody)
|
|
638
|
+
});
|
|
639
|
+
await emitRequestEvent(audit, {
|
|
640
|
+
phase: "headers",
|
|
641
|
+
responseObserved: true,
|
|
642
|
+
status: response.status,
|
|
643
|
+
...providerRequestIdFields(response.headers)
|
|
644
|
+
});
|
|
645
|
+
return { response, audit };
|
|
646
|
+
} catch (error) {
|
|
647
|
+
await response?.body?.cancel(error).catch(() => void 0);
|
|
648
|
+
await emitRequestEvent(audit, {
|
|
649
|
+
phase: "failed",
|
|
650
|
+
responseObserved: response !== void 0,
|
|
651
|
+
...response ? {
|
|
652
|
+
status: response.status,
|
|
653
|
+
...providerRequestIdFields(response.headers)
|
|
654
|
+
} : {}
|
|
655
|
+
}).catch(() => void 0);
|
|
656
|
+
throw error;
|
|
657
|
+
}
|
|
477
658
|
};
|
|
478
|
-
let
|
|
479
|
-
if (response.status === 401) {
|
|
480
|
-
await response.body?.cancel().catch(() => void 0);
|
|
481
|
-
|
|
659
|
+
let sent = await send(false, 1);
|
|
660
|
+
if (sent.response.status === 401) {
|
|
661
|
+
await sent.response.body?.cancel().catch(() => void 0);
|
|
662
|
+
await emitRequestEvent(sent.audit, {
|
|
663
|
+
phase: "failed",
|
|
664
|
+
responseObserved: true,
|
|
665
|
+
status: sent.response.status,
|
|
666
|
+
willRetry: true,
|
|
667
|
+
...providerRequestIdFields(sent.response.headers)
|
|
668
|
+
});
|
|
669
|
+
sent = await send(true, 2);
|
|
482
670
|
}
|
|
483
|
-
return await normalizeResponse(response, context.onFinalContextUsage);
|
|
671
|
+
return await normalizeResponse(sent.response, sent.audit, context.onFinalContextUsage);
|
|
484
672
|
};
|
|
485
673
|
}
|
|
486
674
|
async function requestBodyText(input, init, replayableBodyFactory) {
|
|
@@ -490,19 +678,33 @@ async function requestBodyText(input, init, replayableBodyFactory) {
|
|
|
490
678
|
if (init?.body === void 0 || init.body === null) return "";
|
|
491
679
|
throw new Error("SuperGrok subscription request body must be replayable JSON text");
|
|
492
680
|
}
|
|
493
|
-
async function normalizeResponse(response, onFinalContextUsage) {
|
|
681
|
+
async function normalizeResponse(response, audit, onFinalContextUsage) {
|
|
494
682
|
if (!response.ok) {
|
|
495
683
|
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
496
684
|
const bounded = bytes.byteLength > MAX_ERROR_BODY_BYTES ? bytes.slice(0, MAX_ERROR_BODY_BYTES) : bytes;
|
|
497
685
|
const headers = new Headers(response.headers);
|
|
498
686
|
headers.set(XAI_SUBSCRIPTION_TRANSPORT_ERROR_HEADER, "1");
|
|
687
|
+
await emitRequestEvent(audit, {
|
|
688
|
+
phase: "failed",
|
|
689
|
+
responseObserved: true,
|
|
690
|
+
status: response.status,
|
|
691
|
+
...providerRequestIdFields(response.headers)
|
|
692
|
+
});
|
|
499
693
|
return new Response(bounded, {
|
|
500
694
|
status: response.status,
|
|
501
695
|
statusText: response.statusText,
|
|
502
696
|
headers
|
|
503
697
|
});
|
|
504
698
|
}
|
|
505
|
-
if (!response.body)
|
|
699
|
+
if (!response.body) {
|
|
700
|
+
await emitRequestEvent(audit, {
|
|
701
|
+
phase: "completed",
|
|
702
|
+
responseObserved: true,
|
|
703
|
+
status: response.status,
|
|
704
|
+
...providerRequestIdFields(response.headers)
|
|
705
|
+
});
|
|
706
|
+
return response;
|
|
707
|
+
}
|
|
506
708
|
const contentType = response.headers.get("content-type") ?? "";
|
|
507
709
|
if (!contentType.includes("text/event-stream")) {
|
|
508
710
|
const value = await response.json();
|
|
@@ -512,6 +714,12 @@ async function normalizeResponse(response, onFinalContextUsage) {
|
|
|
512
714
|
});
|
|
513
715
|
emitContextUsage(normalized.value, normalized.finalContextTokens, onFinalContextUsage);
|
|
514
716
|
const responseValue = normalized.value && typeof normalized.value === "object" && !Array.isArray(normalized.value) ? normalized.value.response : value;
|
|
717
|
+
await emitRequestEvent(audit, {
|
|
718
|
+
phase: "completed",
|
|
719
|
+
responseObserved: true,
|
|
720
|
+
status: response.status,
|
|
721
|
+
...providerRequestIdFields(response.headers)
|
|
722
|
+
});
|
|
515
723
|
return Response.json(responseValue, {
|
|
516
724
|
status: response.status,
|
|
517
725
|
statusText: response.statusText,
|
|
@@ -522,24 +730,185 @@ async function normalizeResponse(response, onFinalContextUsage) {
|
|
|
522
730
|
const decoder = new TextDecoder();
|
|
523
731
|
const encoder = new TextEncoder();
|
|
524
732
|
let pending = "";
|
|
733
|
+
let idleTimer = null;
|
|
734
|
+
let controllerRef = null;
|
|
735
|
+
let silenceStartedAt = performance.now();
|
|
736
|
+
let streamSettled = false;
|
|
737
|
+
const errorStream = (error) => {
|
|
738
|
+
if (streamSettled || !controllerRef) return;
|
|
739
|
+
streamSettled = true;
|
|
740
|
+
controllerRef.error(error);
|
|
741
|
+
};
|
|
742
|
+
const closeStream = () => {
|
|
743
|
+
if (streamSettled || !controllerRef) return;
|
|
744
|
+
streamSettled = true;
|
|
745
|
+
controllerRef.close();
|
|
746
|
+
};
|
|
747
|
+
const clearIdleTimer = () => {
|
|
748
|
+
if (idleTimer !== null) clearTimeout(idleTimer);
|
|
749
|
+
idleTimer = null;
|
|
750
|
+
};
|
|
751
|
+
const armIdleTimer = () => {
|
|
752
|
+
clearIdleTimer();
|
|
753
|
+
idleTimer = setTimeout(() => {
|
|
754
|
+
if (audit.terminal || !controllerRef) return;
|
|
755
|
+
audit.terminal = true;
|
|
756
|
+
clearIdleTimer();
|
|
757
|
+
const now = performance.now();
|
|
758
|
+
const silenceDurationMs = Math.max(0, now - silenceStartedAt);
|
|
759
|
+
const error = new XaiSubscriptionStreamIdleTimeoutError(
|
|
760
|
+
audit.requestId,
|
|
761
|
+
audit.eventCount > 0,
|
|
762
|
+
audit.eventCount,
|
|
763
|
+
audit.lastEventType,
|
|
764
|
+
silenceDurationMs
|
|
765
|
+
);
|
|
766
|
+
void reader.cancel(error).catch(() => void 0);
|
|
767
|
+
void emitRequestEvent(audit, {
|
|
768
|
+
phase: "timed_out",
|
|
769
|
+
responseObserved: audit.eventCount > 0,
|
|
770
|
+
status: response.status,
|
|
771
|
+
silenceDurationMs,
|
|
772
|
+
...providerRequestIdFields(response.headers)
|
|
773
|
+
}).then(
|
|
774
|
+
() => errorStream(error),
|
|
775
|
+
() => errorStream(error)
|
|
776
|
+
);
|
|
777
|
+
}, audit.streamIdleTimeoutMs);
|
|
778
|
+
idleTimer.unref?.();
|
|
779
|
+
};
|
|
525
780
|
const body = new ReadableStream({
|
|
781
|
+
start(controller) {
|
|
782
|
+
controllerRef = controller;
|
|
783
|
+
silenceStartedAt = performance.now();
|
|
784
|
+
armIdleTimer();
|
|
785
|
+
},
|
|
526
786
|
async pull(controller) {
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
787
|
+
if (audit.terminal) return;
|
|
788
|
+
try {
|
|
789
|
+
while (!audit.terminal) {
|
|
790
|
+
const chunk = await reader.read();
|
|
791
|
+
if (audit.terminal) return;
|
|
792
|
+
pending += decoder.decode(chunk.value, { stream: !chunk.done });
|
|
793
|
+
const parts = pending.split(/\r?\n\r?\n/);
|
|
794
|
+
pending = parts.pop() ?? "";
|
|
795
|
+
if (chunk.done && pending) {
|
|
796
|
+
parts.push(pending);
|
|
797
|
+
pending = "";
|
|
798
|
+
}
|
|
799
|
+
let enqueued = false;
|
|
800
|
+
for (const part of parts) {
|
|
801
|
+
const progress = sseProgress(part);
|
|
802
|
+
if (!progress.valid) {
|
|
803
|
+
controller.enqueue(
|
|
804
|
+
encoder.encode(`${normalizeSseEvent(part, onFinalContextUsage)}
|
|
805
|
+
|
|
806
|
+
`)
|
|
807
|
+
);
|
|
808
|
+
enqueued = true;
|
|
809
|
+
continue;
|
|
810
|
+
}
|
|
811
|
+
clearIdleTimer();
|
|
812
|
+
const now = performance.now();
|
|
813
|
+
const interEventGapMs = Math.max(0, now - (audit.lastProgressAt ?? audit.startedAt));
|
|
814
|
+
audit.eventCount = Math.min(Number.MAX_SAFE_INTEGER, audit.eventCount + 1);
|
|
815
|
+
audit.lastEventType = progress.eventType;
|
|
816
|
+
audit.lastProgressAt = now;
|
|
817
|
+
silenceStartedAt = now;
|
|
818
|
+
if (audit.eventCount === 1) {
|
|
819
|
+
await emitRequestEvent(audit, {
|
|
820
|
+
phase: "first_event",
|
|
821
|
+
responseObserved: true,
|
|
822
|
+
status: response.status,
|
|
823
|
+
...providerRequestIdFields(response.headers)
|
|
824
|
+
});
|
|
825
|
+
} else {
|
|
826
|
+
emitDiagnosticEvent(audit, {
|
|
827
|
+
phase: "progress",
|
|
828
|
+
responseObserved: true,
|
|
829
|
+
status: response.status,
|
|
830
|
+
interEventGapMs,
|
|
831
|
+
...providerRequestIdFields(response.headers)
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
if (progress.terminal === "failed") {
|
|
835
|
+
audit.terminal = true;
|
|
836
|
+
clearIdleTimer();
|
|
837
|
+
await reader.cancel().catch(() => void 0);
|
|
838
|
+
await emitRequestEvent(audit, {
|
|
839
|
+
phase: "failed",
|
|
840
|
+
responseObserved: true,
|
|
841
|
+
status: response.status,
|
|
842
|
+
...providerRequestIdFields(response.headers)
|
|
843
|
+
});
|
|
844
|
+
errorStream(xaiSseTerminalError(response, audit, progress));
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
847
|
+
controller.enqueue(
|
|
848
|
+
encoder.encode(`${normalizeSseEvent(part, onFinalContextUsage)}
|
|
533
849
|
|
|
534
|
-
`)
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
850
|
+
`)
|
|
851
|
+
);
|
|
852
|
+
enqueued = true;
|
|
853
|
+
if (progress.terminal === "completed") {
|
|
854
|
+
audit.terminal = true;
|
|
855
|
+
clearIdleTimer();
|
|
856
|
+
await reader.cancel().catch(() => void 0);
|
|
857
|
+
await emitRequestEvent(audit, {
|
|
858
|
+
phase: "completed",
|
|
859
|
+
responseObserved: true,
|
|
860
|
+
status: response.status,
|
|
861
|
+
...providerRequestIdFields(response.headers)
|
|
862
|
+
});
|
|
863
|
+
closeStream();
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
866
|
+
armIdleTimer();
|
|
867
|
+
}
|
|
868
|
+
if (chunk.done) {
|
|
869
|
+
audit.terminal = true;
|
|
870
|
+
clearIdleTimer();
|
|
871
|
+
await emitRequestEvent(audit, {
|
|
872
|
+
phase: "failed",
|
|
873
|
+
responseObserved: true,
|
|
874
|
+
status: response.status,
|
|
875
|
+
...providerRequestIdFields(response.headers)
|
|
876
|
+
});
|
|
877
|
+
closeStream();
|
|
878
|
+
return;
|
|
879
|
+
}
|
|
880
|
+
if (enqueued) return;
|
|
881
|
+
}
|
|
882
|
+
} catch (error) {
|
|
883
|
+
if (audit.terminal) {
|
|
884
|
+
errorStream(error);
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
audit.terminal = true;
|
|
888
|
+
clearIdleTimer();
|
|
889
|
+
await reader.cancel(error).catch(() => void 0);
|
|
890
|
+
await emitRequestEvent(audit, {
|
|
891
|
+
phase: "failed",
|
|
892
|
+
responseObserved: true,
|
|
893
|
+
status: response.status,
|
|
894
|
+
...providerRequestIdFields(response.headers)
|
|
895
|
+
}).catch(() => void 0);
|
|
896
|
+
errorStream(error);
|
|
539
897
|
}
|
|
540
898
|
},
|
|
541
|
-
cancel(reason) {
|
|
542
|
-
|
|
899
|
+
async cancel(reason) {
|
|
900
|
+
streamSettled = true;
|
|
901
|
+
if (!audit.terminal) {
|
|
902
|
+
audit.terminal = true;
|
|
903
|
+
clearIdleTimer();
|
|
904
|
+
await emitRequestEvent(audit, {
|
|
905
|
+
phase: "failed",
|
|
906
|
+
responseObserved: true,
|
|
907
|
+
status: response.status,
|
|
908
|
+
...providerRequestIdFields(response.headers)
|
|
909
|
+
}).catch(() => void 0);
|
|
910
|
+
}
|
|
911
|
+
await reader.cancel(reason).catch(() => void 0);
|
|
543
912
|
}
|
|
544
913
|
});
|
|
545
914
|
return new Response(body, {
|
|
@@ -548,11 +917,155 @@ async function normalizeResponse(response, onFinalContextUsage) {
|
|
|
548
917
|
headers: response.headers
|
|
549
918
|
});
|
|
550
919
|
}
|
|
920
|
+
function sseProgress(block) {
|
|
921
|
+
for (const line of block.split("\n")) {
|
|
922
|
+
if (!line.startsWith("data:")) continue;
|
|
923
|
+
const data = line.slice(5).trim();
|
|
924
|
+
if (!data) continue;
|
|
925
|
+
if (data === "[DONE]") {
|
|
926
|
+
return { valid: true, eventType: "done", terminal: "completed", payload: null };
|
|
927
|
+
}
|
|
928
|
+
try {
|
|
929
|
+
const parsed = JSON.parse(data);
|
|
930
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
931
|
+
return { valid: false, eventType: null, terminal: null, payload: null };
|
|
932
|
+
}
|
|
933
|
+
const payload = parsed;
|
|
934
|
+
const rawType = payload.type;
|
|
935
|
+
if (typeof rawType !== "string" || !/^[a-z0-9_.-]{1,96}$/i.test(rawType)) {
|
|
936
|
+
return { valid: false, eventType: null, terminal: null, payload: null };
|
|
937
|
+
}
|
|
938
|
+
const type = rawType;
|
|
939
|
+
if (type === "response.completed") {
|
|
940
|
+
return { valid: true, eventType: type, terminal: "completed", payload };
|
|
941
|
+
}
|
|
942
|
+
if (type === "error" || type === "response.incomplete" || type === "response.failed" || type === "response.error") {
|
|
943
|
+
return { valid: true, eventType: type, terminal: "failed", payload };
|
|
944
|
+
}
|
|
945
|
+
return { valid: true, eventType: type, terminal: null, payload };
|
|
946
|
+
} catch {
|
|
947
|
+
return { valid: false, eventType: null, terminal: null, payload: null };
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
return { valid: false, eventType: null, terminal: null, payload: null };
|
|
951
|
+
}
|
|
952
|
+
function boundedTerminalErrorField(value, maxBytes) {
|
|
953
|
+
if (typeof value !== "string") return { truncated: false };
|
|
954
|
+
const encoder = new TextEncoder();
|
|
955
|
+
const encoded = encoder.encode(value);
|
|
956
|
+
if (encoded.byteLength <= maxBytes) return { value, truncated: false };
|
|
957
|
+
const markerBytes = encoder.encode(TERMINAL_ERROR_TRUNCATION_MARKER).byteLength;
|
|
958
|
+
let prefixEnd = Math.max(0, maxBytes - markerBytes);
|
|
959
|
+
while (prefixEnd > 0 && (encoded[prefixEnd] & 192) === 128) {
|
|
960
|
+
prefixEnd -= 1;
|
|
961
|
+
}
|
|
962
|
+
return {
|
|
963
|
+
value: `${new TextDecoder().decode(encoded.subarray(0, prefixEnd))}${TERMINAL_ERROR_TRUNCATION_MARKER}`,
|
|
964
|
+
truncated: true
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
function terminalErrorSource(payload, eventType) {
|
|
968
|
+
if (!payload) return null;
|
|
969
|
+
if (eventType === "error" || eventType === "response.error") {
|
|
970
|
+
return payload.error ?? payload;
|
|
971
|
+
}
|
|
972
|
+
const response = payload.response;
|
|
973
|
+
if (response && typeof response === "object" && !Array.isArray(response)) {
|
|
974
|
+
const nested = response.error;
|
|
975
|
+
if (nested !== void 0 && nested !== null) return nested;
|
|
976
|
+
}
|
|
977
|
+
return payload.error ?? payload;
|
|
978
|
+
}
|
|
979
|
+
function xaiSseTerminalError(source, audit, progress) {
|
|
980
|
+
const rawError = terminalErrorSource(progress.payload, progress.eventType);
|
|
981
|
+
const record2 = rawError && typeof rawError === "object" && !Array.isArray(rawError) ? rawError : {};
|
|
982
|
+
const eventTypeField = boundedTerminalErrorField(
|
|
983
|
+
progress.eventType,
|
|
984
|
+
MAX_TERMINAL_ERROR_FIELD_BYTES
|
|
985
|
+
);
|
|
986
|
+
const rawCode = record2.code ?? (typeof record2.type === "string" && record2.type !== eventTypeField.value && record2.type !== "error" ? record2.type : void 0);
|
|
987
|
+
const codeField = boundedTerminalErrorField(rawCode, MAX_TERMINAL_ERROR_FIELD_BYTES);
|
|
988
|
+
const messageField = boundedTerminalErrorField(
|
|
989
|
+
record2.message ?? (typeof rawError === "string" ? rawError : void 0),
|
|
990
|
+
MAX_TERMINAL_ERROR_MESSAGE_BYTES
|
|
991
|
+
);
|
|
992
|
+
const eventType = eventTypeField.value ?? "error";
|
|
993
|
+
const fallbackCode = eventType === "response.failed" ? "response_failed" : eventType === "response.incomplete" ? "response_incomplete" : "response_error";
|
|
994
|
+
const fallbackMessage = eventType === "response.incomplete" ? "The SuperGrok response was incomplete" : eventType === "response.failed" ? "The SuperGrok response failed" : "The SuperGrok response stream reported an error";
|
|
995
|
+
const message = messageField.value?.length ? messageField.value : fallbackMessage;
|
|
996
|
+
let code = codeField.value?.length ? codeField.value : fallbackCode;
|
|
997
|
+
const rateLimited = !NON_RETRYABLE_SSE_ERROR_CODES.has(code) && isXaiSubscriptionRateLimitDiagnostic({ code, message });
|
|
998
|
+
if (rateLimited && (code === fallbackCode || code === "response_error")) {
|
|
999
|
+
code = "rate_limit_exceeded";
|
|
1000
|
+
}
|
|
1001
|
+
const status = rateLimited ? 429 : NON_RETRYABLE_SSE_ERROR_CODES.has(code) ? 400 : 502;
|
|
1002
|
+
const headers = new Headers(source.headers);
|
|
1003
|
+
headers.set("content-type", "application/json");
|
|
1004
|
+
headers.set(XAI_SUBSCRIPTION_TRANSPORT_ERROR_HEADER, "1");
|
|
1005
|
+
headers.set("x-should-retry", "false");
|
|
1006
|
+
headers.delete("content-length");
|
|
1007
|
+
headers.delete("content-encoding");
|
|
1008
|
+
return new XaiSubscriptionStreamingTerminalError({
|
|
1009
|
+
message,
|
|
1010
|
+
code,
|
|
1011
|
+
eventType,
|
|
1012
|
+
requestId: audit.requestId,
|
|
1013
|
+
status,
|
|
1014
|
+
diagnosticTruncated: eventTypeField.truncated || codeField.truncated || messageField.truncated,
|
|
1015
|
+
headers
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
function boundedStreamIdleTimeout(value) {
|
|
1019
|
+
const resolved = value ?? XAI_RESPONSE_STREAM_IDLE_TIMEOUT_MS;
|
|
1020
|
+
if (!Number.isSafeInteger(resolved) || resolved < 1 || resolved > MAX_STREAM_IDLE_TIMEOUT_MS) {
|
|
1021
|
+
throw new RangeError("SuperGrok response stream idle timeout is invalid");
|
|
1022
|
+
}
|
|
1023
|
+
return resolved;
|
|
1024
|
+
}
|
|
1025
|
+
function providerRequestId(headers) {
|
|
1026
|
+
return headers.get("x-request-id") ?? headers.get("request-id") ?? void 0;
|
|
1027
|
+
}
|
|
1028
|
+
function providerRequestIdFields(headers) {
|
|
1029
|
+
const value = providerRequestId(headers);
|
|
1030
|
+
return value ? { providerRequestId: value } : {};
|
|
1031
|
+
}
|
|
1032
|
+
function requestEventFor(audit, event) {
|
|
1033
|
+
const now = performance.now();
|
|
1034
|
+
return {
|
|
1035
|
+
requestId: audit.requestId,
|
|
1036
|
+
transportAttempt: audit.transportAttempt,
|
|
1037
|
+
model: audit.model,
|
|
1038
|
+
durationMs: Math.max(0, now - audit.startedAt),
|
|
1039
|
+
streamIdleTimeoutMs: audit.streamIdleTimeoutMs,
|
|
1040
|
+
eventCount: audit.eventCount,
|
|
1041
|
+
...audit.lastEventType ? { lastEventType: audit.lastEventType } : {},
|
|
1042
|
+
...audit.lastProgressAt !== null ? {
|
|
1043
|
+
lastProgressDurationMs: Math.max(0, audit.lastProgressAt - audit.startedAt)
|
|
1044
|
+
} : {},
|
|
1045
|
+
...event
|
|
1046
|
+
};
|
|
1047
|
+
}
|
|
1048
|
+
function emitDiagnosticEvent(audit, event) {
|
|
1049
|
+
const observed = requestEventFor(audit, event);
|
|
1050
|
+
try {
|
|
1051
|
+
audit.ctx.onModelRequestDiagnostic?.(observed);
|
|
1052
|
+
} catch {
|
|
1053
|
+
}
|
|
1054
|
+
return observed;
|
|
1055
|
+
}
|
|
1056
|
+
async function emitRequestEvent(audit, event) {
|
|
1057
|
+
if (event.phase === "completed" || event.phase === "failed" || event.phase === "timed_out") {
|
|
1058
|
+
if (audit.terminalEventEmitted) return;
|
|
1059
|
+
audit.terminalEventEmitted = true;
|
|
1060
|
+
}
|
|
1061
|
+
const observed = emitDiagnosticEvent(audit, event);
|
|
1062
|
+
await audit.ctx.onModelRequestEvent?.(observed);
|
|
1063
|
+
}
|
|
551
1064
|
function normalizeSseEvent(block, onFinalContextUsage) {
|
|
552
1065
|
const lines = block.split("\n");
|
|
553
1066
|
return lines.map((line) => {
|
|
554
1067
|
if (!line.startsWith("data:")) return line;
|
|
555
|
-
const data = line.slice(5).
|
|
1068
|
+
const data = line.slice(5).trim();
|
|
556
1069
|
if (!data || data === "[DONE]") return line;
|
|
557
1070
|
try {
|
|
558
1071
|
const normalized = normalizeXaiResponseEventJson(JSON.parse(data));
|
|
@@ -580,6 +1093,7 @@ function emitContextUsage(value, finalContextTokens, sink) {
|
|
|
580
1093
|
function isXaiSubscriptionTransportError(error) {
|
|
581
1094
|
let current = error;
|
|
582
1095
|
for (let depth = 0; depth < 6 && current && typeof current === "object"; depth += 1) {
|
|
1096
|
+
if (current instanceof XaiSubscriptionStreamingTerminalError) return true;
|
|
583
1097
|
const value = current;
|
|
584
1098
|
const headers = value.headers;
|
|
585
1099
|
if (headers && typeof headers === "object" && typeof headers.get === "function" && headers.get(XAI_SUBSCRIPTION_TRANSPORT_ERROR_HEADER) === "1") {
|
|
@@ -1195,6 +1709,7 @@ export {
|
|
|
1195
1709
|
XAI_REFRESH_FALLBACK_MS,
|
|
1196
1710
|
XAI_REFRESH_WINDOW_MS,
|
|
1197
1711
|
XAI_RESPONSE_SDK_OUTER_TIMEOUT_MS,
|
|
1712
|
+
XAI_RESPONSE_STREAM_IDLE_TIMEOUT_MS,
|
|
1198
1713
|
XAI_SUBSCRIPTION_AUTO_COMPACTION_PERCENT,
|
|
1199
1714
|
XAI_SUBSCRIPTION_EFFECTIVE_CONTEXT_PERCENT,
|
|
1200
1715
|
XAI_SUBSCRIPTION_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
|
|
@@ -1218,8 +1733,13 @@ export {
|
|
|
1218
1733
|
XAI_VIDEO_POLL_REQUEST_TIMEOUT_MS,
|
|
1219
1734
|
XAI_VIDEO_START_TIMEOUT_MS,
|
|
1220
1735
|
XaiSubscriptionError,
|
|
1736
|
+
XaiSubscriptionHostedToolContinuationError,
|
|
1221
1737
|
XaiSubscriptionReloginRequired,
|
|
1738
|
+
XaiSubscriptionStreamIdleTimeoutError,
|
|
1739
|
+
XaiSubscriptionStreamingTerminalError,
|
|
1222
1740
|
XaiSubscriptionTransientError,
|
|
1741
|
+
classifyXaiSubscriptionStreamIdleTimeoutError,
|
|
1742
|
+
classifyXaiSubscriptionStreamingTerminalError,
|
|
1223
1743
|
decodeXaiJwtPayload,
|
|
1224
1744
|
fetchXaiProxyJson,
|
|
1225
1745
|
fetchXaiSubscriptionModels,
|
|
@@ -1228,6 +1748,8 @@ export {
|
|
|
1228
1748
|
generateXaiSubscriptionImage,
|
|
1229
1749
|
generateXaiSubscriptionVideo,
|
|
1230
1750
|
getXaiSubscriptionVideoStatus,
|
|
1751
|
+
isXaiSubscriptionHostedToolContinuationError,
|
|
1752
|
+
isXaiSubscriptionRateLimitDiagnostic,
|
|
1231
1753
|
isXaiSubscriptionTransportError,
|
|
1232
1754
|
normalizeXaiResponseEventJson,
|
|
1233
1755
|
normalizeXaiSubscriptionRequestBody,
|
|
@@ -1238,6 +1760,7 @@ export {
|
|
|
1238
1760
|
runBoundedXaiOperation,
|
|
1239
1761
|
startXaiSubscriptionVideoWithBody,
|
|
1240
1762
|
xaiAccessTokenExpiry,
|
|
1763
|
+
xaiIdentityFromDeviceTokens,
|
|
1241
1764
|
xaiSubscriptionFetch,
|
|
1242
1765
|
xaiSubscriptionProxyHeaders,
|
|
1243
1766
|
xaiSubscriptionRequestStorage
|