mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 14:52:09 +03:00
feat(compression): expose rule intensities and track usd savings
Add estimated USD savings to compression analytics so saved tokens can be reported in cost terms alongside existing token metrics. Expose caveman rule intensity metadata for settings consumers and add a settings API route alias for rule lookup. Also preserve system prompts when aggressive compression falls back to lite mode.
This commit is contained in:
@@ -1689,6 +1689,18 @@ export async function handleChatCore({
|
||||
try {
|
||||
const { insertCompressionAnalyticsRow } =
|
||||
await import("../../src/lib/db/compressionAnalytics.ts");
|
||||
const { calculateCost } = await import("../../src/lib/usage/costCalculator.ts");
|
||||
const tokensSaved = Math.max(
|
||||
0,
|
||||
result.stats.originalTokens - result.stats.compressedTokens
|
||||
);
|
||||
const estimatedUsdSaved = await calculateCost(
|
||||
provider ?? "",
|
||||
effectiveModel ?? "",
|
||||
{
|
||||
input: tokensSaved,
|
||||
}
|
||||
);
|
||||
insertCompressionAnalyticsRow({
|
||||
timestamp: new Date().toISOString(),
|
||||
combo_id: comboName ?? null,
|
||||
@@ -1696,12 +1708,10 @@ export async function handleChatCore({
|
||||
mode,
|
||||
original_tokens: result.stats.originalTokens,
|
||||
compressed_tokens: result.stats.compressedTokens,
|
||||
tokens_saved: Math.max(
|
||||
0,
|
||||
result.stats.originalTokens - result.stats.compressedTokens
|
||||
),
|
||||
tokens_saved: tokensSaved,
|
||||
duration_ms: result.stats.durationMs ?? null,
|
||||
request_id: skillRequestId,
|
||||
estimated_usd_saved: estimatedUsdSaved || null,
|
||||
validation_fallback: result.stats.fallbackApplied ? 1 : 0,
|
||||
output_mode: cavemanOutputModeApplied ? cavemanOutputModeIntensity : null,
|
||||
});
|
||||
|
||||
@@ -144,7 +144,10 @@ export function compressAggressive(
|
||||
}
|
||||
|
||||
try {
|
||||
const liteResult = applyLiteCompression({ messages: currentMessages });
|
||||
const liteResult = applyLiteCompression(
|
||||
{ messages: currentMessages },
|
||||
{ preserveSystemPrompt: cfg.preserveSystemPrompt !== false }
|
||||
);
|
||||
if (liteResult?.compressed && liteResult.stats) {
|
||||
const liteSavings = liteResult.stats.savingsPercent ?? 0;
|
||||
if (liteSavings > resultStats.savingsPercent) {
|
||||
|
||||
@@ -406,11 +406,15 @@ export function getRuleByName(name: string): CavemanRule | undefined {
|
||||
}
|
||||
|
||||
export function getCavemanRuleMetadata() {
|
||||
const intensities = ["lite", "full", "ultra"] as const;
|
||||
return CAVEMAN_RULES.map((rule) => ({
|
||||
name: rule.name,
|
||||
context: rule.context,
|
||||
category: rule.category ?? "terse",
|
||||
minIntensity: rule.minIntensity ?? "lite",
|
||||
intensities: intensities.filter(
|
||||
(intensity) => INTENSITY_RANK[intensity] >= INTENSITY_RANK[rule.minIntensity ?? "lite"]
|
||||
),
|
||||
description: rule.description ?? rule.name.replace(/_/g, " "),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ interface RuleMetadata {
|
||||
category: string;
|
||||
context: string;
|
||||
minIntensity: CavemanIntensity;
|
||||
intensities?: CavemanIntensity[];
|
||||
description: string;
|
||||
}
|
||||
|
||||
@@ -498,7 +499,7 @@ export default function CompressionSettingsTab() {
|
||||
<button
|
||||
key={rule.name}
|
||||
onClick={() => toggleCavemanRule(rule.name)}
|
||||
title={`${rule.category} · ${rule.context} · ${rule.minIntensity}`}
|
||||
title={`${rule.category} · ${rule.context} · ${(rule.intensities ?? [rule.minIntensity]).join("/")}`}
|
||||
className={`px-2 py-1 rounded text-xs border transition-all ${
|
||||
config.cavemanConfig!.skipRules.includes(rule.name)
|
||||
? "border-red-500/50 bg-red-500/10 text-red-400 line-through"
|
||||
|
||||
1
src/app/api/settings/compression/rules/route.ts
Normal file
1
src/app/api/settings/compression/rules/route.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { GET } from "@/app/api/compression/rules/route";
|
||||
@@ -1,7 +1,10 @@
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { cavemanCompress } from "../../../open-sse/services/compression/caveman.ts";
|
||||
import { getRuleByName } from "../../../open-sse/services/compression/cavemanRules.ts";
|
||||
import {
|
||||
getCavemanRuleMetadata,
|
||||
getRuleByName,
|
||||
} from "../../../open-sse/services/compression/cavemanRules.ts";
|
||||
import { applyCompression } from "../../../open-sse/services/compression/strategySelector.ts";
|
||||
|
||||
function compress(content: string, options = {}) {
|
||||
@@ -132,6 +135,7 @@ describe("Caveman v3.7.9 rule parity", () => {
|
||||
});
|
||||
|
||||
it("exposes metadata for new rule names", () => {
|
||||
const metadata = getCavemanRuleMetadata();
|
||||
for (const ruleName of [
|
||||
"articles",
|
||||
"pleasantries",
|
||||
@@ -140,6 +144,9 @@ describe("Caveman v3.7.9 rule parity", () => {
|
||||
"ultra_abbreviations",
|
||||
]) {
|
||||
assert.ok(getRuleByName(ruleName), `missing rule ${ruleName}`);
|
||||
const rule = metadata.find((entry) => entry.name === ruleName);
|
||||
assert.ok(rule?.category, `missing metadata category for ${ruleName}`);
|
||||
assert.ok(rule?.intensities?.length, `missing metadata intensities for ${ruleName}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -148,6 +148,26 @@ describe("Integration: strategySelector → aggressive pipeline", () => {
|
||||
assert.equal(systemMsg!.content, "You are a helpful coding assistant.");
|
||||
});
|
||||
|
||||
it("compressAggressive preserves duplicate system prompts during lite fallback", () => {
|
||||
const messages = [
|
||||
{ role: "system", content: "Policy exact. Keep exact." },
|
||||
{ role: "system", content: "Policy exact. Keep exact." },
|
||||
{ role: "user", content: "Short" },
|
||||
];
|
||||
|
||||
const result = compressAggressive(messages, {
|
||||
preserveSystemPrompt: true,
|
||||
minSavingsThreshold: 0.9,
|
||||
summarizerEnabled: false,
|
||||
maxTokensPerMessage: 999999,
|
||||
});
|
||||
|
||||
assert.deepEqual(
|
||||
result.messages.filter((message) => message.role === "system"),
|
||||
messages.filter((message) => message.role === "system")
|
||||
);
|
||||
});
|
||||
|
||||
it("compressAggressive downgrade chain works when aggressive fails", () => {
|
||||
// Empty messages should trigger downgrade gracefully
|
||||
const result = compressAggressive([]);
|
||||
|
||||
@@ -293,6 +293,23 @@ describe("compressionAnalytics", () => {
|
||||
assert.equal(summary.realUsage.bySource.provider, 1);
|
||||
});
|
||||
|
||||
it("aggregates estimated USD savings separately from token estimates", () => {
|
||||
insertCompressionAnalyticsRow({
|
||||
timestamp: new Date().toISOString(),
|
||||
mode: "standard",
|
||||
original_tokens: 1000,
|
||||
compressed_tokens: 700,
|
||||
tokens_saved: 300,
|
||||
request_id: "req-usd",
|
||||
estimated_usd_saved: 0.0015,
|
||||
});
|
||||
attachCompressionUsageReceipt("req-usd", { prompt_tokens: 700, total_tokens: 700 }, "provider");
|
||||
|
||||
const summary = getCompressionAnalyticsSummary();
|
||||
assert.equal(summary.realUsage.requestsWithReceipts, 1);
|
||||
assert.equal(summary.realUsage.estimatedUsdSaved, 0.0015);
|
||||
});
|
||||
|
||||
it("summarizes validation fallback and output mode rows", () => {
|
||||
insertCompressionAnalyticsRow({
|
||||
timestamp: new Date().toISOString(),
|
||||
|
||||
Reference in New Issue
Block a user