mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 12:22:14 +03:00
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
101 lines
2.8 KiB
TypeScript
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;
|
|
}
|