mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 12:22:14 +03:00
PRICING UPDATES (01-09): - xAI Grok-4 family: grok-4-fast-non-reasoning (/usr/bin/bash.20/$0.50/M, 1143ms), grok-4-fast-reasoning, grok-4-1-fast-*, grok-4-0709, grok-3, grok-3-mini - Z.AI GLM-5 family: glm-5 + glm-5-turbo (128k maxOutput, $1.00/$3.20/M) - Gemini Flash Lite: price corrected $0.15→$0.10 / $1.25→$0.40 (per ClawRouter) - Gemini 3.1 Pro: new flagship (1.05M context, aliased as gemini-3.1-pro) - Anthropic Claude 4.5/4.6: haiku-4.5 ($1/$5), sonnet-4.6 ($3/$15), opus-4.6 ($5/$25) - DeepSeek native section: deepseek-chat/v3/v3.2 ($0.28/$0.42), deepseek-reasoner ($0.55/$2.19) - Kimi K2.5 Moonshot: kimi-k2.5 ($0.60/$3.00, 262k ctx), moonshot-kimi-k2.5 alias - MiniMax M2.5: minimax-m2.5 ($0.30/$1.20, 204k ctx, reasoning+tools) - NVIDIA free tier: gpt-oss-120b at $0.00/M via emergencyFallback.ts INFRASTRUCTURE FEATURES (10-14): - feat(router): add intentClassifier.ts for multilingual intent detection (9 langs) Detects code/reasoning/simple in EN, PT-BR, ES, ZH, JA, RU, DE, KO, AR - feat(dedup): add requestDedup.ts for concurrent request deduplication SHA-256 hash, skip streaming, skip high-temperature, 60s failsafe TTL - feat(autoCombo): add routerStrategy.ts pluggable strategy system RouterStrategy interface, RulesStrategy (6-factor) + CostStrategy, registry - feat(fallback): add emergencyFallback.ts budget-exhaustion detector Triggers on HTTP 402 or budget keywords, redirects to nvidia/gpt-oss-120b - feat(taskFitness): add fitness scores for Grok-4, Kimi K2.5, GLM-5, MiniMax M2.5, DeepSeek V3.2, Gemini 3.1 Pro across all task categories PROVIDERS: - providers.ts: add Z.AI (zai) provider entry for GLM-5 API key connections All features on branch: feat/clawrouter-improvements Source: github.com/BlockRunAI/ClawRouter analysis (2026-03-17)
121 lines
3.4 KiB
TypeScript
121 lines
3.4 KiB
TypeScript
/**
|
|
* Request Deduplication Service
|
|
*
|
|
* Deduplicates **concurrent** identical requests to the same upstream.
|
|
* Inspired by ClawRouter's dedup.ts (BlockRunAI / github.com/BlockRunAI/ClawRouter).
|
|
*
|
|
* IMPORTANT: In-memory only — does NOT persist across restarts and does NOT
|
|
* work across multiple process instances (no cross-instance dedup).
|
|
*/
|
|
|
|
import { createHash } from "node:crypto";
|
|
|
|
export interface DedupConfig {
|
|
enabled: boolean;
|
|
maxTemperatureForDedup: number;
|
|
timeoutMs: number;
|
|
}
|
|
|
|
export const DEFAULT_DEDUP_CONFIG: DedupConfig = {
|
|
enabled: true,
|
|
maxTemperatureForDedup: 0.1,
|
|
timeoutMs: 60_000,
|
|
};
|
|
|
|
export interface DedupResult<T> {
|
|
result: T;
|
|
wasDeduplicated: boolean;
|
|
hash: string;
|
|
}
|
|
|
|
const inflight = new Map<string, Promise<unknown>>();
|
|
|
|
/**
|
|
* Compute a deterministic hash for a request body.
|
|
* Includes: model, messages, temperature, tools, tool_choice, max_tokens, response_format
|
|
* Excludes: stream, user, metadata (don't affect LLM output)
|
|
*/
|
|
export function computeRequestHash(requestBody: unknown): string {
|
|
const body = requestBody as Record<string, unknown>;
|
|
const canonical = {
|
|
model: body.model ?? null,
|
|
messages: body.messages ?? null,
|
|
temperature: typeof body.temperature === "number" ? body.temperature : 1.0,
|
|
tools: body.tools ?? null,
|
|
tool_choice: body.tool_choice ?? null,
|
|
max_tokens: body.max_tokens ?? null,
|
|
response_format: body.response_format ?? null,
|
|
top_p: body.top_p ?? null,
|
|
frequency_penalty: body.frequency_penalty ?? null,
|
|
presence_penalty: body.presence_penalty ?? null,
|
|
};
|
|
return createHash("sha256").update(JSON.stringify(canonical)).digest("hex").slice(0, 16);
|
|
}
|
|
|
|
/** Determine whether a request should be deduplicated */
|
|
export function shouldDeduplicate(
|
|
requestBody: unknown,
|
|
config: DedupConfig = DEFAULT_DEDUP_CONFIG
|
|
): boolean {
|
|
if (!config.enabled) return false;
|
|
const body = requestBody as Record<string, unknown>;
|
|
if (body.stream === true) return false;
|
|
const temperature = typeof body.temperature === "number" ? body.temperature : 1.0;
|
|
if (temperature > config.maxTemperatureForDedup) return false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Execute a request with deduplication.
|
|
* Concurrent identical requests share one upstream call.
|
|
*/
|
|
export async function deduplicate<T>(
|
|
hash: string,
|
|
fn: () => Promise<T>,
|
|
config: DedupConfig = DEFAULT_DEDUP_CONFIG
|
|
): Promise<DedupResult<T>> {
|
|
if (!config.enabled) {
|
|
return { result: await fn(), wasDeduplicated: false, hash };
|
|
}
|
|
|
|
const existing = inflight.get(hash);
|
|
if (existing) {
|
|
const result = (await existing) as T;
|
|
return { result, wasDeduplicated: true, hash };
|
|
}
|
|
|
|
let resolve!: (value: T) => void;
|
|
let reject!: (reason: unknown) => void;
|
|
const sharedPromise = new Promise<T>((res, rej) => {
|
|
resolve = res;
|
|
reject = rej;
|
|
});
|
|
inflight.set(hash, sharedPromise as Promise<unknown>);
|
|
|
|
const timer = setTimeout(() => {
|
|
if (inflight.get(hash) === sharedPromise) inflight.delete(hash);
|
|
}, config.timeoutMs);
|
|
|
|
try {
|
|
const result = await fn();
|
|
resolve(result);
|
|
return { result, wasDeduplicated: false, hash };
|
|
} catch (err) {
|
|
reject(err);
|
|
throw err;
|
|
} finally {
|
|
clearTimeout(timer);
|
|
if (inflight.get(hash) === sharedPromise) inflight.delete(hash);
|
|
}
|
|
}
|
|
|
|
export function getInflightCount(): number {
|
|
return inflight.size;
|
|
}
|
|
export function getInflightHashes(): string[] {
|
|
return [...inflight.keys()];
|
|
}
|
|
export function clearInflight(): void {
|
|
inflight.clear();
|
|
}
|