Files
OmniRoute/src/domain/policyEngine.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

196 lines
5.2 KiB
TypeScript

/**
* Policy Engine — FASE-06 Architecture Refactoring
*
* Centralized policy evaluation that combines domain decisions from
* fallback, cost, lockout, and circuit-breaker modules into a single
* verdict before forwarding a request to a provider.
*
* @module domain/policyEngine
*/
import { checkLockout } from "./lockoutPolicy";
import { checkBudget } from "./costRules";
import { resolveFallbackChain } from "./fallbackPolicy";
interface PolicyRequest {
model: string;
apiKeyId?: string;
clientIp?: string;
provider?: string;
}
interface PolicyVerdict {
allowed: boolean;
reason: string | null;
adjustments: Record<string, unknown>;
policyPhase: string;
}
interface Policy {
id: string;
name: string;
type: string;
enabled: boolean;
priority: number;
conditions?: {
model_pattern?: string;
[key: string]: unknown;
};
actions?: {
prefer_provider?: string[];
block_model?: string[];
max_tokens?: number;
[key: string]: unknown;
};
}
export function evaluateRequest(request: PolicyRequest): PolicyVerdict {
const { model, apiKeyId, clientIp } = request;
// ── 1. Lockout Policy ──────────────────────────────
if (clientIp) {
const lockout = checkLockout(clientIp);
if (lockout.locked) {
return {
allowed: false,
reason: `Client locked out (${lockout.remainingMs}ms remaining)`,
adjustments: {},
policyPhase: "lockout",
};
}
}
// ── 2. Budget Policy ───────────────────────────────
if (apiKeyId) {
const budget = checkBudget(apiKeyId);
if (budget && !budget.allowed) {
return {
allowed: false,
reason: `Budget exceeded: ${budget.reason || "daily limit reached"}`,
adjustments: {},
policyPhase: "budget",
};
}
}
// ── 3. Fallback Chain Resolution ───────────────────
const fallbackChain = resolveFallbackChain(model);
return {
allowed: true,
reason: null,
adjustments: {
model,
fallbackChain: fallbackChain || [],
},
policyPhase: "passed",
};
}
export function evaluateFirstAllowed(models: string[], baseRequest: Omit<PolicyRequest, "model">) {
for (const model of models) {
const verdict = evaluateRequest({ ...baseRequest, model });
if (verdict.allowed) {
return { model, verdict };
}
}
// All models denied — return last denial
const lastVerdict = evaluateRequest({ ...baseRequest, model: models[models.length - 1] });
return { model: null, verdict: lastVerdict };
}
// ─── Class-Based Policy Engine ───────────────────────────────────────────────
function globMatch(pattern: string, value: string): boolean {
const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
return new RegExp(`^${escaped}$`).test(value);
}
export class PolicyEngine {
_policies: Policy[];
constructor() {
this._policies = [];
}
loadPolicies(policies: Policy[]) {
this._policies = [...policies];
}
addPolicy(policy: Policy) {
this._policies.push(policy);
}
removePolicy(id: string) {
this._policies = this._policies.filter((p) => p.id !== id);
}
getPolicies(): Policy[] {
return [...this._policies];
}
evaluate(context: { model: string }) {
const result: {
allowed: boolean;
reason: string | undefined;
preferredProviders: string[];
appliedPolicies: string[];
maxTokens: number | undefined;
} = {
allowed: true,
reason: undefined,
preferredProviders: [],
appliedPolicies: [],
maxTokens: undefined,
};
const sorted = [...this._policies]
.filter((p) => p.enabled)
.sort((a, b) => a.priority - b.priority);
for (const policy of sorted) {
// Check model condition
if (policy.conditions?.model_pattern) {
if (!globMatch(policy.conditions.model_pattern, context.model)) {
continue; // Model doesn't match — skip this policy
}
}
// Apply actions based on policy type
switch (policy.type) {
case "routing":
if (policy.actions?.prefer_provider) {
result.preferredProviders.push(...policy.actions.prefer_provider);
}
result.appliedPolicies.push(policy.name);
break;
case "access":
if (policy.actions?.block_model) {
const blocked = policy.actions.block_model.some((pattern) =>
globMatch(pattern, context.model)
);
if (blocked) {
result.allowed = false;
result.reason = `Model "${context.model}" blocked by policy "${policy.name}"`;
result.appliedPolicies.push(policy.name);
return result;
}
}
result.appliedPolicies.push(policy.name);
break;
case "budget":
if (policy.actions?.max_tokens != null) {
result.maxTokens = policy.actions.max_tokens;
}
result.appliedPolicies.push(policy.name);
break;
}
}
return result;
}
}