94 lines
3.0 KiB
TypeScript
94 lines
3.0 KiB
TypeScript
import type { GatewayStatus } from '@/types/gateway';
|
|
|
|
export const CHAT_HISTORY_RPC_TIMEOUT_MS = 35_000;
|
|
export const CHAT_HISTORY_STARTUP_RETRY_DELAYS_MS = [800, 2_000, 4_000, 8_000] as const;
|
|
export const CHAT_HISTORY_STARTUP_CONNECTION_GRACE_MS = 30_000;
|
|
export const CHAT_HISTORY_STARTUP_RUNNING_WINDOW_MS =
|
|
CHAT_HISTORY_RPC_TIMEOUT_MS + CHAT_HISTORY_STARTUP_CONNECTION_GRACE_MS;
|
|
export const CHAT_HISTORY_DEFAULT_LOADING_SAFETY_TIMEOUT_MS = 15_000;
|
|
export const CHAT_HISTORY_LOADING_SAFETY_TIMEOUT_MS =
|
|
CHAT_HISTORY_RPC_TIMEOUT_MS * (CHAT_HISTORY_STARTUP_RETRY_DELAYS_MS.length + 1)
|
|
+ CHAT_HISTORY_STARTUP_RETRY_DELAYS_MS.reduce((sum, delay) => sum + delay, 0)
|
|
+ 2_000;
|
|
|
|
export type HistoryRetryErrorKind = 'timeout' | 'gateway_unavailable' | 'gateway_startup';
|
|
|
|
export function classifyHistoryStartupRetryError(error: unknown): HistoryRetryErrorKind | null {
|
|
const message = String(error).toLowerCase();
|
|
|
|
if (
|
|
message.includes('unavailable during gateway startup')
|
|
|| message.includes('unavailable during startup')
|
|
|| message.includes('not yet ready')
|
|
|| message.includes('service not initialized')
|
|
) {
|
|
return 'gateway_startup';
|
|
}
|
|
|
|
if (
|
|
message.includes('rpc timeout: chat.history')
|
|
|| message.includes('gateway rpc timeout: chat.history')
|
|
|| message.includes('gateway ws timeout: chat.history')
|
|
|| message.includes('request timed out')
|
|
) {
|
|
return 'timeout';
|
|
}
|
|
|
|
if (
|
|
message.includes('gateway not connected')
|
|
|| message.includes('gateway socket is not connected')
|
|
|| message.includes('gateway is unavailable')
|
|
|| message.includes('service channel unavailable')
|
|
|| message.includes('websocket closed before handshake')
|
|
|| message.includes('connect handshake timeout')
|
|
|| message.includes('gateway ws connect timeout')
|
|
|| message.includes('gateway connection closed')
|
|
) {
|
|
return 'gateway_unavailable';
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export function shouldRetryStartupHistoryLoad(
|
|
gatewayStatus: GatewayStatus | undefined,
|
|
errorKind: HistoryRetryErrorKind | null,
|
|
): boolean {
|
|
if (!gatewayStatus || !errorKind) return false;
|
|
|
|
// The gateway explicitly told us it's still initializing -- always retry
|
|
if (errorKind === 'gateway_startup') {
|
|
return true;
|
|
}
|
|
|
|
if (gatewayStatus.state === 'starting') {
|
|
return true;
|
|
}
|
|
|
|
if (gatewayStatus.state !== 'running') {
|
|
return false;
|
|
}
|
|
|
|
if (gatewayStatus.connectedAt == null) {
|
|
return true;
|
|
}
|
|
|
|
return Date.now() - gatewayStatus.connectedAt <= CHAT_HISTORY_STARTUP_RUNNING_WINDOW_MS;
|
|
}
|
|
|
|
export async function sleep(ms: number): Promise<void> {
|
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
}
|
|
|
|
export function getStartupHistoryTimeoutOverride(
|
|
isInitialForegroundLoad: boolean,
|
|
): number | undefined {
|
|
return isInitialForegroundLoad ? CHAT_HISTORY_RPC_TIMEOUT_MS : undefined;
|
|
}
|
|
|
|
export function getHistoryLoadingSafetyTimeout(isInitialForegroundLoad: boolean): number {
|
|
return isInitialForegroundLoad
|
|
? CHAT_HISTORY_LOADING_SAFETY_TIMEOUT_MS
|
|
: CHAT_HISTORY_DEFAULT_LOADING_SAFETY_TIMEOUT_MS;
|
|
}
|