Files
OmniRoute/src/shared/utils/requestTimeout.ts
diegosouzapw 71d14209a4 feat: OmniRoute v1.0.0 — Intelligent AI Gateway & Universal LLM Proxy
OmniRoute is an intelligent API gateway that unifies 20+ AI providers behind a single
OpenAI-compatible endpoint. Features include intelligent routing with 6 strategies,
multi-format translation (OpenAI/Claude/Gemini/Responses API), circuit breakers,
semantic caching, combo fallback chains, real-time health monitoring, and a full
dashboard with provider management, analytics, and CLI tool integration.

Key highlights:
- 20+ providers (Claude Code, Codex, Gemini CLI, GitHub Copilot, iFlow, Qwen, Kiro, etc.)
- 6 routing strategies (Fill First, Round Robin, P2C, Random, Least Used, Cost Optimized)
- Export/Import database backup with full archive support
- Translator Playground with 4 modes (Playground, Chat Tester, Test Bench, Live Monitor)
- 100% TypeScript across src/ and open-sse/
- Docker support with multi-stage builds
- Comprehensive documentation and 9 dashboard screenshots
2026-02-18 00:02:15 -03:00

101 lines
2.8 KiB
TypeScript

/**
* Request Timeout Utility — FASE-04 Observability
*
* Wraps fetch/async calls with configurable timeouts and
* abort controller support.
*
* @module shared/utils/requestTimeout
*/
interface TimeoutOptions {
timeoutMs?: number;
label?: string;
signal?: AbortSignal;
}
export async function fetchWithTimeout(url: string, options: RequestInit & TimeoutOptions = {}) {
const { timeoutMs = 30000, label = "Request", signal: externalSignal, ...fetchOptions } = options;
const controller = new AbortController();
// Merge with external signal if provided
if (externalSignal) {
externalSignal.addEventListener("abort", () => controller.abort(externalSignal.reason));
}
const timeoutId = setTimeout(() => {
controller.abort(new Error(`${label} timed out after ${timeoutMs}ms`));
}, timeoutMs);
try {
const response = await fetch(url, {
...fetchOptions,
signal: controller.signal,
});
return response;
} catch (error: any) {
if (error.name === "AbortError" || controller.signal.aborted) {
const timeoutError: any = new Error(`${label} timed out after ${timeoutMs}ms`);
timeoutError.name = "TimeoutError";
timeoutError.originalUrl = url;
timeoutError.timeoutMs = timeoutMs;
throw timeoutError;
}
throw error;
} finally {
clearTimeout(timeoutId);
}
}
/**
* Execute any async function with a timeout.
*
* @template T
* @param {() => Promise<T>} fn - Async function to execute
* @param {number} timeoutMs - Timeout in milliseconds
* @param {string} [label='Operation'] - Label for error messages
* @returns {Promise<T>}
* @throws {Error} With name 'TimeoutError' if operation times out
*/
export async function withTimeout<T>(fn: () => Promise<T>, timeoutMs: number, label = "Operation"): Promise<T> {
return new Promise<T>((resolve, reject) => {
const timeoutId = setTimeout(() => {
const error: any = new Error(`${label} timed out after ${timeoutMs}ms`);
error.name = "TimeoutError";
error.timeoutMs = timeoutMs;
reject(error);
}, timeoutMs);
fn()
.then((result) => {
clearTimeout(timeoutId);
resolve(result);
})
.catch((error) => {
clearTimeout(timeoutId);
reject(error);
});
});
}
/**
* Default provider timeouts (ms).
*/
export const PROVIDER_TIMEOUTS: Record<string, number> = {
openai: 60000,
claude: 90000, // Claude can be slower for long outputs
gemini: 60000,
codex: 120000, // Coding tasks often take longer
qwen: 45000,
deepseek: 60000,
cohere: 45000,
groq: 30000, // Groq is fast
mistral: 45000,
openrouter: 60000,
default: 60000,
};
export function getProviderTimeout(provider: string): number {
return PROVIDER_TIMEOUTS[provider] || PROVIDER_TIMEOUTS.default;
}