style: reformat
This commit is contained in:
@@ -20,7 +20,13 @@ import {
|
||||
} from "../types";
|
||||
import { AssistantMessageEventStream } from "../utils/event-stream";
|
||||
import { finalizeErrorMessage, type RawHttpRequestDump } from "../utils/http-inspector";
|
||||
import { createFirstEventWatchdog, getOpenAIStreamIdleTimeoutMs, getStreamFirstEventTimeoutMs, iterateWithIdleTimeout, markFirstStreamEvent } from "../utils/idle-iterator";
|
||||
import {
|
||||
createFirstEventWatchdog,
|
||||
getOpenAIStreamIdleTimeoutMs,
|
||||
getStreamFirstEventTimeoutMs,
|
||||
iterateWithIdleTimeout,
|
||||
markFirstStreamEvent,
|
||||
} from "../utils/idle-iterator";
|
||||
import { mapToOpenAIResponsesToolChoice } from "../utils/tool-choice";
|
||||
import { supportsDeveloperRole } from "./openai-responses";
|
||||
import {
|
||||
@@ -121,9 +127,8 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
|
||||
? AbortSignal.any([options.signal, requestAbortController.signal])
|
||||
: requestAbortController.signal;
|
||||
const idleTimeoutMs = getOpenAIStreamIdleTimeoutMs();
|
||||
const firstEventWatchdog = createFirstEventWatchdog(
|
||||
getStreamFirstEventTimeoutMs(idleTimeoutMs),
|
||||
() => requestAbortController.abort(),
|
||||
const firstEventWatchdog = createFirstEventWatchdog(getStreamFirstEventTimeoutMs(idleTimeoutMs), () =>
|
||||
requestAbortController.abort(),
|
||||
);
|
||||
options?.onPayload?.(params);
|
||||
rawRequestDump = {
|
||||
@@ -138,14 +143,11 @@ export const streamAzureOpenAIResponses: StreamFunction<"azure-openai-responses"
|
||||
stream.push({ type: "start", partial: output });
|
||||
|
||||
await processResponsesStream(
|
||||
iterateWithIdleTimeout(
|
||||
markFirstStreamEvent(openaiStream, firstEventWatchdog),
|
||||
{
|
||||
idleTimeoutMs,
|
||||
errorMessage: "Azure OpenAI responses stream stalled while waiting for the next event",
|
||||
onIdle: () => requestAbortController.abort(),
|
||||
},
|
||||
),
|
||||
iterateWithIdleTimeout(markFirstStreamEvent(openaiStream, firstEventWatchdog), {
|
||||
idleTimeoutMs,
|
||||
errorMessage: "Azure OpenAI responses stream stalled while waiting for the next event",
|
||||
onIdle: () => requestAbortController.abort(),
|
||||
}),
|
||||
output,
|
||||
stream,
|
||||
model,
|
||||
|
||||
@@ -30,7 +30,13 @@ import {
|
||||
} from "../utils";
|
||||
import { AssistantMessageEventStream } from "../utils/event-stream";
|
||||
import { finalizeErrorMessage, type RawHttpRequestDump } from "../utils/http-inspector";
|
||||
import { createFirstEventWatchdog, getOpenAIStreamIdleTimeoutMs, getStreamFirstEventTimeoutMs, iterateWithIdleTimeout, markFirstStreamEvent } from "../utils/idle-iterator";
|
||||
import {
|
||||
createFirstEventWatchdog,
|
||||
getOpenAIStreamIdleTimeoutMs,
|
||||
getStreamFirstEventTimeoutMs,
|
||||
iterateWithIdleTimeout,
|
||||
markFirstStreamEvent,
|
||||
} from "../utils/idle-iterator";
|
||||
import { adaptSchemaForStrict, NO_STRICT } from "../utils/schema";
|
||||
import { mapToOpenAIResponsesToolChoice } from "../utils/tool-choice";
|
||||
import {
|
||||
@@ -173,9 +179,8 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
||||
? AbortSignal.any([options.signal, requestAbortController.signal])
|
||||
: requestAbortController.signal;
|
||||
const idleTimeoutMs = getOpenAIStreamIdleTimeoutMs();
|
||||
const firstEventWatchdog = createFirstEventWatchdog(
|
||||
getStreamFirstEventTimeoutMs(idleTimeoutMs),
|
||||
() => requestAbortController.abort(),
|
||||
const firstEventWatchdog = createFirstEventWatchdog(getStreamFirstEventTimeoutMs(idleTimeoutMs), () =>
|
||||
requestAbortController.abort(),
|
||||
);
|
||||
options?.onPayload?.(params);
|
||||
rawRequestDump = {
|
||||
@@ -192,14 +197,11 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
|
||||
|
||||
const nativeOutputItems: Array<Record<string, unknown>> = [];
|
||||
await processResponsesStream(
|
||||
iterateWithIdleTimeout(
|
||||
markFirstStreamEvent(openaiStream, firstEventWatchdog),
|
||||
{
|
||||
idleTimeoutMs,
|
||||
errorMessage: "OpenAI responses stream stalled while waiting for the next event",
|
||||
onIdle: () => requestAbortController.abort(),
|
||||
},
|
||||
),
|
||||
iterateWithIdleTimeout(markFirstStreamEvent(openaiStream, firstEventWatchdog), {
|
||||
idleTimeoutMs,
|
||||
errorMessage: "OpenAI responses stream stalled while waiting for the next event",
|
||||
onIdle: () => requestAbortController.abort(),
|
||||
}),
|
||||
output,
|
||||
stream,
|
||||
model,
|
||||
|
||||
@@ -26,7 +26,10 @@ export function getOpenAIStreamIdleTimeoutMs(): number | undefined {
|
||||
* Set `PI_STREAM_FIRST_EVENT_TIMEOUT_MS=0` to disable the watchdog.
|
||||
*/
|
||||
export function getStreamFirstEventTimeoutMs(idleTimeoutMs?: number): number | undefined {
|
||||
const fallback = Math.min(DEFAULT_STREAM_FIRST_EVENT_TIMEOUT_MS, idleTimeoutMs ?? DEFAULT_STREAM_FIRST_EVENT_TIMEOUT_MS);
|
||||
const fallback = Math.min(
|
||||
DEFAULT_STREAM_FIRST_EVENT_TIMEOUT_MS,
|
||||
idleTimeoutMs ?? DEFAULT_STREAM_FIRST_EVENT_TIMEOUT_MS,
|
||||
);
|
||||
return normalizeIdleTimeoutMs($env.PI_STREAM_FIRST_EVENT_TIMEOUT_MS, fallback);
|
||||
}
|
||||
|
||||
@@ -39,10 +42,7 @@ export interface FirstEventWatchdog {
|
||||
* Starts a watchdog that aborts a request if no first stream event arrives in time.
|
||||
* Call `markFirstEventReceived()` as soon as the first event is observed.
|
||||
*/
|
||||
export function createFirstEventWatchdog(
|
||||
timeoutMs: number | undefined,
|
||||
onTimeout: () => void,
|
||||
): FirstEventWatchdog {
|
||||
export function createFirstEventWatchdog(timeoutMs: number | undefined, onTimeout: () => void): FirstEventWatchdog {
|
||||
let timer: NodeJS.Timeout | undefined;
|
||||
if (timeoutMs !== undefined && timeoutMs > 0) {
|
||||
timer = setTimeout(() => {
|
||||
@@ -105,7 +105,10 @@ export async function* iterateWithIdleTimeout<T>(
|
||||
options: IdleTimeoutIteratorOptions,
|
||||
): AsyncGenerator<T> {
|
||||
const firstItemTimeoutMs = options.firstItemTimeoutMs ?? options.idleTimeoutMs;
|
||||
if ((firstItemTimeoutMs === undefined || firstItemTimeoutMs <= 0) && (options.idleTimeoutMs === undefined || options.idleTimeoutMs <= 0)) {
|
||||
if (
|
||||
(firstItemTimeoutMs === undefined || firstItemTimeoutMs <= 0) &&
|
||||
(options.idleTimeoutMs === undefined || options.idleTimeoutMs <= 0)
|
||||
) {
|
||||
for await (const item of iterable) {
|
||||
yield item;
|
||||
}
|
||||
@@ -152,7 +155,9 @@ export async function* iterateWithIdleTimeout<T>(
|
||||
if (returnPromise) {
|
||||
void returnPromise.catch(() => {});
|
||||
}
|
||||
throw new Error(sawFirstItem ? options.errorMessage : (options.firstItemErrorMessage ?? options.errorMessage));
|
||||
throw new Error(
|
||||
sawFirstItem ? options.errorMessage : (options.firstItemErrorMessage ?? options.errorMessage),
|
||||
);
|
||||
}
|
||||
if (outcome.kind === "error") {
|
||||
throw outcome.error;
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
|
||||
import type { OAuthController } from "./types";
|
||||
|
||||
const AUTH_URL =
|
||||
"https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai-gateway%2Fapi-keys&title=AI+Gateway+API+Keys";
|
||||
const AUTH_URL = "https://vercel.com/d?to=%2F%5Bteam%5D%2F%7E%2Fai-gateway%2Fapi-keys&title=AI+Gateway+API+Keys";
|
||||
|
||||
/**
|
||||
* Login to Vercel AI Gateway.
|
||||
@@ -20,32 +19,29 @@ const AUTH_URL =
|
||||
* Opens browser to Vercel AI Gateway docs and prompts for an API key.
|
||||
* Returns the API key directly (not OAuthCredentials - this isn't OAuth).
|
||||
*/
|
||||
export async function loginVercelAiGateway(
|
||||
options: OAuthController,
|
||||
): Promise<string> {
|
||||
if (!options.onPrompt) {
|
||||
throw new Error("Vercel AI Gateway login requires onPrompt callback");
|
||||
}
|
||||
export async function loginVercelAiGateway(options: OAuthController): Promise<string> {
|
||||
if (!options.onPrompt) {
|
||||
throw new Error("Vercel AI Gateway login requires onPrompt callback");
|
||||
}
|
||||
|
||||
options.onAuth?.({
|
||||
url: AUTH_URL,
|
||||
instructions:
|
||||
"Copy your Vercel AI Gateway API key from the Vercel dashboard",
|
||||
});
|
||||
options.onAuth?.({
|
||||
url: AUTH_URL,
|
||||
instructions: "Copy your Vercel AI Gateway API key from the Vercel dashboard",
|
||||
});
|
||||
|
||||
const apiKey = await options.onPrompt({
|
||||
message: "Paste your Vercel AI Gateway API key",
|
||||
placeholder: "vck_...",
|
||||
});
|
||||
const apiKey = await options.onPrompt({
|
||||
message: "Paste your Vercel AI Gateway API key",
|
||||
placeholder: "vck_...",
|
||||
});
|
||||
|
||||
if (options.signal?.aborted) {
|
||||
throw new Error("Login cancelled");
|
||||
}
|
||||
if (options.signal?.aborted) {
|
||||
throw new Error("Login cancelled");
|
||||
}
|
||||
|
||||
const trimmed = apiKey.trim();
|
||||
if (!trimmed) {
|
||||
throw new Error("API key is required");
|
||||
}
|
||||
const trimmed = apiKey.trim();
|
||||
if (!trimmed) {
|
||||
throw new Error("API key is required");
|
||||
}
|
||||
|
||||
return trimmed;
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,9 @@ describe("isProviderRetryableError", () => {
|
||||
});
|
||||
|
||||
it("retries first-event timeout errors", () => {
|
||||
expect(isProviderRetryableError(new Error("Anthropic stream timed out while waiting for the first event"))).toBe(true);
|
||||
expect(isProviderRetryableError(new Error("Anthropic stream timed out while waiting for the first event"))).toBe(
|
||||
true,
|
||||
);
|
||||
});
|
||||
|
||||
it("does not retry non-transient validation errors", () => {
|
||||
|
||||
@@ -100,9 +100,9 @@ describe("anthropic first-event timeout retries", () => {
|
||||
vi.spyOn(Messages.prototype, "stream").mockImplementation((_body, requestOptions) => {
|
||||
attempt += 1;
|
||||
const signal = (requestOptions as { signal?: AbortSignal } | undefined)?.signal;
|
||||
return (attempt === 1
|
||||
? createHangingAnthropicStream(signal)
|
||||
: createSuccessfulAnthropicStream("retry recovered")) as never;
|
||||
return (
|
||||
attempt === 1 ? createHangingAnthropicStream(signal) : createSuccessfulAnthropicStream("retry recovered")
|
||||
) as never;
|
||||
});
|
||||
|
||||
const result = await streamAnthropic(model, context, { apiKey: "sk-ant-test" }).result();
|
||||
|
||||
@@ -31,7 +31,7 @@ import { type ConfigError, ConfigFile } from "../config";
|
||||
import { parseModelString } from "../config/model-resolver";
|
||||
import { isValidThemeColor, type ThemeColor } from "../modes/theme/theme";
|
||||
import type { AuthStorage, OAuthCredential } from "../session/auth-storage";
|
||||
import { settings, type Settings } from "./settings";
|
||||
import { type Settings, settings } from "./settings";
|
||||
|
||||
export const kNoAuth = "N/A";
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
shortenPath,
|
||||
truncateDiffByHunk,
|
||||
} from "../tools/render-utils";
|
||||
import { Ellipsis, Hasher, type RenderCache, renderStatusLine, truncateToWidth } from "../tui";
|
||||
import { Hasher, type RenderCache, renderStatusLine, truncateToWidth } from "../tui";
|
||||
import type { HashlineToolEdit } from "./index";
|
||||
import type { DiffError, DiffResult, Operation } from "./types";
|
||||
|
||||
@@ -242,7 +242,9 @@ function wrapEditRendererLine(line: string, width: number): string[] {
|
||||
const continuationPrefix = `${" ".repeat(Math.max(0, prefixWidth - 1))}|`;
|
||||
const wrappedContent = wrapTextWithAnsi(content, contentWidth);
|
||||
|
||||
return wrappedContent.map((segment, index) => `${startAnsi}${index === 0 ? prefix : continuationPrefix}${segment}\x1b[39m`);
|
||||
return wrappedContent.map(
|
||||
(segment, index) => `${startAnsi}${index === 0 ? prefix : continuationPrefix}${segment}\x1b[39m`,
|
||||
);
|
||||
}
|
||||
|
||||
export const editToolRenderer = {
|
||||
@@ -379,7 +381,8 @@ export const editToolRenderer = {
|
||||
);
|
||||
}
|
||||
|
||||
const lines = width > 0 ? text.split("\n").flatMap(line => wrapEditRendererLine(line, width)) : text.split("\n");
|
||||
const lines =
|
||||
width > 0 ? text.split("\n").flatMap(line => wrapEditRendererLine(line, width)) : text.split("\n");
|
||||
cached = { key, lines };
|
||||
return lines;
|
||||
},
|
||||
|
||||
@@ -28,7 +28,6 @@ function getTitleModel(
|
||||
const availableModels = registry.getAvailable();
|
||||
if (availableModels.length === 0) return undefined;
|
||||
|
||||
const matchPreferences = { usageOrder: settings.getStorage()?.getModelUsageOrder() };
|
||||
const titleModel = resolveRoleSelection(["commit", "smol"], settings, availableModels);
|
||||
if (titleModel) {
|
||||
return { model: titleModel.model, thinkingLevel: titleModel.thinkingLevel };
|
||||
|
||||
Reference in New Issue
Block a user