@ottocode/sdk 0.1.282 → 0.1.283
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/package.json
CHANGED
|
@@ -468,6 +468,108 @@ function trackResponsesStream(
|
|
|
468
468
|
});
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
+
async function waitForCodexStreamStart(
|
|
472
|
+
response: Response,
|
|
473
|
+
args: {
|
|
474
|
+
sessionId?: string;
|
|
475
|
+
model?: string;
|
|
476
|
+
parentSignal?: AbortSignal | null;
|
|
477
|
+
},
|
|
478
|
+
): Promise<Response> {
|
|
479
|
+
if (!response.ok || !response.body) {
|
|
480
|
+
return response;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const reader = response.body.getReader();
|
|
484
|
+
const idleTimeoutMs = getCodexStreamIdleTimeoutMs();
|
|
485
|
+
let timeout: Timer | undefined;
|
|
486
|
+
let removeAbortListener: (() => void) | undefined;
|
|
487
|
+
const cleanup = () => {
|
|
488
|
+
if (timeout) clearTimeout(timeout);
|
|
489
|
+
timeout = undefined;
|
|
490
|
+
removeAbortListener?.();
|
|
491
|
+
removeAbortListener = undefined;
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
try {
|
|
495
|
+
const first = await Promise.race([
|
|
496
|
+
reader.read(),
|
|
497
|
+
new Promise<never>((_resolve, reject) => {
|
|
498
|
+
timeout = setTimeout(() => {
|
|
499
|
+
reject(
|
|
500
|
+
new Error(
|
|
501
|
+
`OpenAI OAuth Codex stream idle timeout before first chunk after ${idleTimeoutMs}ms`,
|
|
502
|
+
),
|
|
503
|
+
);
|
|
504
|
+
}, idleTimeoutMs);
|
|
505
|
+
|
|
506
|
+
if (args.parentSignal) {
|
|
507
|
+
const onAbort = () => {
|
|
508
|
+
reject(args.parentSignal?.reason ?? new Error('Request aborted'));
|
|
509
|
+
};
|
|
510
|
+
if (args.parentSignal.aborted) {
|
|
511
|
+
onAbort();
|
|
512
|
+
} else {
|
|
513
|
+
args.parentSignal.addEventListener('abort', onAbort, {
|
|
514
|
+
once: true,
|
|
515
|
+
});
|
|
516
|
+
removeAbortListener = () =>
|
|
517
|
+
args.parentSignal?.removeEventListener('abort', onAbort);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}),
|
|
521
|
+
]);
|
|
522
|
+
cleanup();
|
|
523
|
+
|
|
524
|
+
if (first.done) {
|
|
525
|
+
return new Response(null, {
|
|
526
|
+
status: response.status,
|
|
527
|
+
statusText: response.statusText,
|
|
528
|
+
headers: response.headers,
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const body = new ReadableStream<Uint8Array>({
|
|
533
|
+
start(controller) {
|
|
534
|
+
controller.enqueue(first.value);
|
|
535
|
+
},
|
|
536
|
+
async pull(controller) {
|
|
537
|
+
try {
|
|
538
|
+
const next = await reader.read();
|
|
539
|
+
if (next.done) {
|
|
540
|
+
controller.close();
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
controller.enqueue(next.value);
|
|
544
|
+
} catch (error) {
|
|
545
|
+
controller.error(error);
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
async cancel(reason) {
|
|
549
|
+
await reader.cancel(reason);
|
|
550
|
+
},
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
return new Response(body, {
|
|
554
|
+
status: response.status,
|
|
555
|
+
statusText: response.statusText,
|
|
556
|
+
headers: response.headers,
|
|
557
|
+
});
|
|
558
|
+
} catch (error) {
|
|
559
|
+
cleanup();
|
|
560
|
+
loggerWarn('[openai-oauth] response stream did not start before timeout', {
|
|
561
|
+
sessionId: args.sessionId,
|
|
562
|
+
model: args.model,
|
|
563
|
+
timeoutMs: idleTimeoutMs,
|
|
564
|
+
error: summarizeError(error),
|
|
565
|
+
});
|
|
566
|
+
try {
|
|
567
|
+
await reader.cancel(error);
|
|
568
|
+
} catch {}
|
|
569
|
+
throw error;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
471
573
|
async function fetchWithCodexRequestTimeout(
|
|
472
574
|
url: string,
|
|
473
575
|
init: RequestInit,
|
|
@@ -491,10 +593,15 @@ async function fetchWithCodexRequestTimeout(
|
|
|
491
593
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
492
594
|
const attemptStartedAt = Date.now();
|
|
493
595
|
try {
|
|
494
|
-
|
|
596
|
+
const response = await fetchCodexRequestAttemptWithTimeout(url, init, {
|
|
495
597
|
...args,
|
|
496
598
|
requestStartedAt: attemptStartedAt,
|
|
497
599
|
});
|
|
600
|
+
return await waitForCodexStreamStart(response, {
|
|
601
|
+
sessionId: args.sessionId,
|
|
602
|
+
model: args.model,
|
|
603
|
+
parentSignal: init.signal,
|
|
604
|
+
});
|
|
498
605
|
} catch (error) {
|
|
499
606
|
lastError = error;
|
|
500
607
|
if (init.signal?.aborted || attempt >= maxRetries) {
|
|
@@ -502,13 +609,14 @@ async function fetchWithCodexRequestTimeout(
|
|
|
502
609
|
}
|
|
503
610
|
|
|
504
611
|
const retryDelayMs = getCodexRequestRetryDelayMs() * (attempt + 1);
|
|
505
|
-
loggerWarn('[openai-oauth] request attempt failed before
|
|
612
|
+
loggerWarn('[openai-oauth] request attempt failed before stream start', {
|
|
506
613
|
sessionId: args.sessionId,
|
|
507
614
|
model: args.model,
|
|
508
615
|
attempt: attempt + 1,
|
|
509
616
|
maxRetries,
|
|
510
617
|
nextAttempt: attempt + 2,
|
|
511
|
-
|
|
618
|
+
requestTimeoutMs: getCodexRequestTimeoutMs(),
|
|
619
|
+
streamIdleTimeoutMs: getCodexStreamIdleTimeoutMs(),
|
|
512
620
|
durationMs: Date.now() - attemptStartedAt,
|
|
513
621
|
retryDelayMs,
|
|
514
622
|
error: summarizeError(error),
|