mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
feat(compression): unify config validation and persist MCP savings
Centralize compression config schemas across settings, preview, RTK, and combo APIs to enforce consistent validation for stacked pipelines and engine-specific options. Expand caveman and stacked compression behavior by applying default combos at runtime, surfacing validation and fallback metadata, and exposing aggressive and ultra adapter schemas for configuration UI and tests. Persist MCP description compression snapshots into analytics without counting them as provider usage, and extend the dashboard with the new RTK controls and localized labels.
This commit is contained in:
@@ -1596,6 +1596,86 @@ export async function handleChatCore({
|
||||
log?.debug?.("COMPRESSION", "Prompt compression disabled or unavailable");
|
||||
}
|
||||
let compressionComboKey = comboName ?? null;
|
||||
let compressionComboApplied = false;
|
||||
type RuntimeCompressionCombo = {
|
||||
id: string;
|
||||
pipeline: NonNullable<CompressionConfig["stackedPipeline"]>;
|
||||
languagePacks: string[];
|
||||
outputMode: boolean;
|
||||
outputModeIntensity: string;
|
||||
};
|
||||
const isBuiltinStackedPipeline = (
|
||||
pipeline: CompressionConfig["stackedPipeline"] | undefined
|
||||
): boolean => {
|
||||
if (!Array.isArray(pipeline) || pipeline.length !== 2) return false;
|
||||
const [first, second] = pipeline;
|
||||
return (
|
||||
first?.engine === "rtk" &&
|
||||
(first.intensity === undefined || first.intensity === "standard") &&
|
||||
!first.config &&
|
||||
second?.engine === "caveman" &&
|
||||
(second.intensity === undefined || second.intensity === "full") &&
|
||||
!second.config
|
||||
);
|
||||
};
|
||||
const applyCompressionComboConfig = (
|
||||
compressionCombo: RuntimeCompressionCombo | null,
|
||||
routingOverrideIds: string[] = []
|
||||
): boolean => {
|
||||
if (!compressionCombo || compressionCombo.pipeline.length === 0) return false;
|
||||
const comboLanguagePacks = [
|
||||
...new Set(
|
||||
compressionCombo.languagePacks
|
||||
.map((pack) => pack.trim())
|
||||
.filter((pack) => pack.length > 0)
|
||||
),
|
||||
];
|
||||
const comboOutputIntensity = (
|
||||
["lite", "full", "ultra"].includes(compressionCombo.outputModeIntensity)
|
||||
? compressionCombo.outputModeIntensity
|
||||
: (config.cavemanOutputMode?.intensity ?? "full")
|
||||
) as "lite" | "full" | "ultra";
|
||||
const comboDefaultLanguage =
|
||||
comboLanguagePacks.find((pack) => pack === config.languageConfig?.defaultLanguage) ??
|
||||
comboLanguagePacks[0] ??
|
||||
config.languageConfig?.defaultLanguage ??
|
||||
"en";
|
||||
const comboOverrides = { ...(config.comboOverrides ?? {}) };
|
||||
for (const id of routingOverrideIds) {
|
||||
if (id) comboOverrides[id] = "stacked";
|
||||
}
|
||||
config = {
|
||||
...config,
|
||||
compressionComboId: compressionCombo.id,
|
||||
stackedPipeline: compressionCombo.pipeline,
|
||||
languageConfig: {
|
||||
...(config.languageConfig ?? {
|
||||
enabled: false,
|
||||
defaultLanguage: "en",
|
||||
autoDetect: true,
|
||||
enabledPacks: ["en"],
|
||||
}),
|
||||
enabled: true,
|
||||
defaultLanguage: comboDefaultLanguage,
|
||||
enabledPacks:
|
||||
comboLanguagePacks.length > 0
|
||||
? comboLanguagePacks
|
||||
: (config.languageConfig?.enabledPacks ?? ["en"]),
|
||||
},
|
||||
cavemanOutputMode: {
|
||||
...(config.cavemanOutputMode ?? {
|
||||
enabled: false,
|
||||
intensity: "full",
|
||||
autoClarity: true,
|
||||
}),
|
||||
enabled: compressionCombo.outputMode,
|
||||
intensity: comboOutputIntensity,
|
||||
},
|
||||
comboOverrides,
|
||||
};
|
||||
compressionComboApplied = true;
|
||||
return true;
|
||||
};
|
||||
if (isCombo && comboName) {
|
||||
try {
|
||||
const { getComboByName } = await import("../../src/lib/localDb");
|
||||
@@ -1644,59 +1724,12 @@ export async function handleChatCore({
|
||||
routingComboIds
|
||||
.map((id) => getCompressionComboForRoutingCombo(id))
|
||||
.find((combo) => combo !== null) ?? null;
|
||||
if (assignedCompressionCombo && assignedCompressionCombo.pipeline.length > 0) {
|
||||
const comboLanguagePacks = [
|
||||
...new Set(
|
||||
assignedCompressionCombo.languagePacks
|
||||
.map((pack) => pack.trim())
|
||||
.filter((pack) => pack.length > 0)
|
||||
),
|
||||
];
|
||||
const comboOutputIntensity = (
|
||||
["lite", "full", "ultra"].includes(assignedCompressionCombo.outputModeIntensity)
|
||||
? assignedCompressionCombo.outputModeIntensity
|
||||
: (config.cavemanOutputMode?.intensity ?? "full")
|
||||
) as "lite" | "full" | "ultra";
|
||||
const comboDefaultLanguage =
|
||||
comboLanguagePacks.find(
|
||||
(pack) => pack === config.languageConfig?.defaultLanguage
|
||||
) ??
|
||||
comboLanguagePacks[0] ??
|
||||
config.languageConfig?.defaultLanguage ??
|
||||
"en";
|
||||
config = {
|
||||
...config,
|
||||
compressionComboId: assignedCompressionCombo.id,
|
||||
stackedPipeline: assignedCompressionCombo.pipeline,
|
||||
languageConfig: {
|
||||
...(config.languageConfig ?? {
|
||||
enabled: false,
|
||||
defaultLanguage: "en",
|
||||
autoDetect: true,
|
||||
enabledPacks: ["en"],
|
||||
}),
|
||||
enabled: true,
|
||||
defaultLanguage: comboDefaultLanguage,
|
||||
enabledPacks:
|
||||
comboLanguagePacks.length > 0
|
||||
? comboLanguagePacks
|
||||
: (config.languageConfig?.enabledPacks ?? ["en"]),
|
||||
},
|
||||
cavemanOutputMode: {
|
||||
...(config.cavemanOutputMode ?? {
|
||||
enabled: false,
|
||||
intensity: "full",
|
||||
autoClarity: true,
|
||||
}),
|
||||
enabled: assignedCompressionCombo.outputMode,
|
||||
intensity: comboOutputIntensity,
|
||||
},
|
||||
comboOverrides: {
|
||||
...(config.comboOverrides ?? {}),
|
||||
...(comboName ? { [comboName]: "stacked" as const } : {}),
|
||||
...(comboConfig?.id ? { [comboConfig.id]: "stacked" as const } : {}),
|
||||
},
|
||||
};
|
||||
if (
|
||||
applyCompressionComboConfig(
|
||||
assignedCompressionCombo as RuntimeCompressionCombo | null,
|
||||
routingComboIds
|
||||
)
|
||||
) {
|
||||
compressionComboKey = comboName;
|
||||
}
|
||||
}
|
||||
@@ -1708,6 +1741,39 @@ export async function handleChatCore({
|
||||
);
|
||||
}
|
||||
}
|
||||
const modeBeforeOutputTransform = selectCompressionStrategy(
|
||||
config,
|
||||
compressionComboKey,
|
||||
estimatedTokens,
|
||||
body as Record<string, unknown>,
|
||||
{ provider, targetFormat, model: effectiveModel }
|
||||
);
|
||||
if (
|
||||
modeBeforeOutputTransform === "stacked" &&
|
||||
!compressionComboApplied &&
|
||||
!config.compressionComboId &&
|
||||
isBuiltinStackedPipeline(config.stackedPipeline)
|
||||
) {
|
||||
try {
|
||||
const { getDefaultCompressionCombo } =
|
||||
await import("../../src/lib/db/compressionCombos.ts");
|
||||
const defaultCompressionCombo = getDefaultCompressionCombo();
|
||||
if (
|
||||
applyCompressionComboConfig(defaultCompressionCombo as RuntimeCompressionCombo | null)
|
||||
) {
|
||||
log?.debug?.(
|
||||
"COMPRESSION",
|
||||
`Default compression combo applied: ${defaultCompressionCombo?.id}`
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
log?.debug?.(
|
||||
"COMPRESSION",
|
||||
"Default compression combo lookup skipped: " +
|
||||
(err instanceof Error ? err.message : String(err))
|
||||
);
|
||||
}
|
||||
}
|
||||
if (config.cavemanOutputMode?.enabled) {
|
||||
try {
|
||||
const { applyCavemanOutputMode } = await import("../services/compression/outputMode.ts");
|
||||
|
||||
@@ -32,6 +32,14 @@ const descriptionCompressionStats: McpDescriptionCompressionStats = {
|
||||
estimatedTokensSaved: 0,
|
||||
};
|
||||
|
||||
const persistedDescriptionCompressionStats: McpDescriptionCompressionStats = {
|
||||
descriptionsCompressed: 0,
|
||||
charsBefore: 0,
|
||||
charsAfter: 0,
|
||||
charsSaved: 0,
|
||||
estimatedTokensSaved: 0,
|
||||
};
|
||||
|
||||
const MCP_LIST_CONTAINER_KEYS = new Set(["tools", "prompts", "resources", "resourceTemplates"]);
|
||||
const MCP_METADATA_DESCRIPTION_FIELDS = ["description"];
|
||||
|
||||
@@ -163,10 +171,73 @@ export function getMcpDescriptionCompressionStats(): McpDescriptionCompressionSt
|
||||
return { ...descriptionCompressionStats };
|
||||
}
|
||||
|
||||
function getUnpersistedMcpDescriptionCompressionStats(): McpDescriptionCompressionStats {
|
||||
return {
|
||||
descriptionsCompressed:
|
||||
descriptionCompressionStats.descriptionsCompressed -
|
||||
persistedDescriptionCompressionStats.descriptionsCompressed,
|
||||
charsBefore:
|
||||
descriptionCompressionStats.charsBefore - persistedDescriptionCompressionStats.charsBefore,
|
||||
charsAfter:
|
||||
descriptionCompressionStats.charsAfter - persistedDescriptionCompressionStats.charsAfter,
|
||||
charsSaved:
|
||||
descriptionCompressionStats.charsSaved - persistedDescriptionCompressionStats.charsSaved,
|
||||
estimatedTokensSaved:
|
||||
descriptionCompressionStats.estimatedTokensSaved -
|
||||
persistedDescriptionCompressionStats.estimatedTokensSaved,
|
||||
};
|
||||
}
|
||||
|
||||
export async function snapshotMcpDescriptionCompressionStats(): Promise<McpDescriptionCompressionStats> {
|
||||
const delta = getUnpersistedMcpDescriptionCompressionStats();
|
||||
if (
|
||||
delta.descriptionsCompressed <= 0 ||
|
||||
delta.charsSaved <= 0 ||
|
||||
delta.estimatedTokensSaved <= 0
|
||||
) {
|
||||
return {
|
||||
descriptionsCompressed: 0,
|
||||
charsBefore: 0,
|
||||
charsAfter: 0,
|
||||
charsSaved: 0,
|
||||
estimatedTokensSaved: 0,
|
||||
};
|
||||
}
|
||||
|
||||
const originalTokens = Math.max(delta.estimatedTokensSaved, Math.ceil(delta.charsBefore / 4));
|
||||
const compressedTokens = Math.max(0, originalTokens - delta.estimatedTokensSaved);
|
||||
const { insertCompressionAnalyticsRow } =
|
||||
await import("../../src/lib/db/compressionAnalytics.ts");
|
||||
insertCompressionAnalyticsRow({
|
||||
timestamp: new Date().toISOString(),
|
||||
mode: "mcp-description",
|
||||
engine: "mcp-description",
|
||||
original_tokens: originalTokens,
|
||||
compressed_tokens: compressedTokens,
|
||||
tokens_saved: delta.estimatedTokensSaved,
|
||||
mcp_description_tokens_saved: delta.estimatedTokensSaved,
|
||||
});
|
||||
|
||||
persistedDescriptionCompressionStats.descriptionsCompressed =
|
||||
descriptionCompressionStats.descriptionsCompressed;
|
||||
persistedDescriptionCompressionStats.charsBefore = descriptionCompressionStats.charsBefore;
|
||||
persistedDescriptionCompressionStats.charsAfter = descriptionCompressionStats.charsAfter;
|
||||
persistedDescriptionCompressionStats.charsSaved = descriptionCompressionStats.charsSaved;
|
||||
persistedDescriptionCompressionStats.estimatedTokensSaved =
|
||||
descriptionCompressionStats.estimatedTokensSaved;
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
export function resetMcpDescriptionCompressionStats(): void {
|
||||
descriptionCompressionStats.descriptionsCompressed = 0;
|
||||
descriptionCompressionStats.charsBefore = 0;
|
||||
descriptionCompressionStats.charsAfter = 0;
|
||||
descriptionCompressionStats.charsSaved = 0;
|
||||
descriptionCompressionStats.estimatedTokensSaved = 0;
|
||||
persistedDescriptionCompressionStats.descriptionsCompressed = 0;
|
||||
persistedDescriptionCompressionStats.charsBefore = 0;
|
||||
persistedDescriptionCompressionStats.charsAfter = 0;
|
||||
persistedDescriptionCompressionStats.charsSaved = 0;
|
||||
persistedDescriptionCompressionStats.estimatedTokensSaved = 0;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,10 @@ import { getCompressionAnalyticsSummary } from "../../../src/lib/db/compressionA
|
||||
import { getCacheStatsSummary } from "../../../src/lib/db/compressionCacheStats.ts";
|
||||
import { listCompressionCombos } from "../../../src/lib/db/compressionCombos.ts";
|
||||
import type { McpToolExtraLike } from "../scopeEnforcement.ts";
|
||||
import { getMcpDescriptionCompressionStats } from "../descriptionCompressor.ts";
|
||||
import {
|
||||
getMcpDescriptionCompressionStats,
|
||||
snapshotMcpDescriptionCompressionStats,
|
||||
} from "../descriptionCompressor.ts";
|
||||
|
||||
/**
|
||||
* Handle compression_status tool: return current compression config, analytics, and cache stats
|
||||
@@ -59,6 +62,8 @@ export async function handleCompressionStatus(
|
||||
charsAfter: number;
|
||||
charsSaved: number;
|
||||
estimatedTokensSaved: number;
|
||||
persistedEstimatedTokensSaved: number;
|
||||
persistedSnapshots: number;
|
||||
source: "mcp_metadata_estimate";
|
||||
notProviderUsage: true;
|
||||
};
|
||||
@@ -73,6 +78,7 @@ export async function handleCompressionStatus(
|
||||
const start = Date.now();
|
||||
try {
|
||||
const settings = await getCompressionSettings();
|
||||
await snapshotMcpDescriptionCompressionStats();
|
||||
const analyticsSummary = getCompressionAnalyticsSummary();
|
||||
const mcpDescriptionStats = getMcpDescriptionCompressionStats();
|
||||
const cacheStats = getCacheStatsSummary();
|
||||
@@ -107,6 +113,9 @@ export async function handleCompressionStatus(
|
||||
charsAfter: mcpDescriptionStats.charsAfter,
|
||||
charsSaved: mcpDescriptionStats.charsSaved,
|
||||
estimatedTokensSaved: mcpDescriptionStats.estimatedTokensSaved,
|
||||
persistedEstimatedTokensSaved:
|
||||
analyticsSummary.mcpDescriptionCompression.estimatedTokensSaved,
|
||||
persistedSnapshots: analyticsSummary.mcpDescriptionCompression.snapshots,
|
||||
source: "mcp_metadata_estimate" as const,
|
||||
notProviderUsage: true as const,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,11 @@ import { cavemanCompress } from "../caveman.ts";
|
||||
import { compressAggressive } from "../aggressive.ts";
|
||||
import { ultraCompress } from "../ultra.ts";
|
||||
import { createCompressionStats } from "../stats.ts";
|
||||
import type { CavemanIntensity } from "../types.ts";
|
||||
import {
|
||||
DEFAULT_AGGRESSIVE_CONFIG,
|
||||
DEFAULT_ULTRA_CONFIG,
|
||||
type CavemanIntensity,
|
||||
} from "../types.ts";
|
||||
import type { CompressionEngine, EngineConfigField, EngineValidationResult } from "./types.ts";
|
||||
|
||||
const CAVEMAN_INTENSITIES: CavemanIntensity[] = ["lite", "full", "ultra"];
|
||||
@@ -32,10 +36,116 @@ const CAVEMAN_SCHEMA: EngineConfigField[] = [
|
||||
},
|
||||
];
|
||||
|
||||
const AGGRESSIVE_SCHEMA: EngineConfigField[] = [
|
||||
{
|
||||
key: "summarizerEnabled",
|
||||
type: "boolean",
|
||||
label: "Summarizer enabled",
|
||||
defaultValue: DEFAULT_AGGRESSIVE_CONFIG.summarizerEnabled,
|
||||
},
|
||||
{
|
||||
key: "maxTokensPerMessage",
|
||||
type: "number",
|
||||
label: "Max tokens per message",
|
||||
defaultValue: DEFAULT_AGGRESSIVE_CONFIG.maxTokensPerMessage,
|
||||
min: 256,
|
||||
max: 32768,
|
||||
},
|
||||
{
|
||||
key: "minSavingsThreshold",
|
||||
type: "number",
|
||||
label: "Minimum savings threshold",
|
||||
defaultValue: DEFAULT_AGGRESSIVE_CONFIG.minSavingsThreshold,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
{
|
||||
key: "preserveSystemPrompt",
|
||||
type: "boolean",
|
||||
label: "Preserve system prompt",
|
||||
defaultValue: true,
|
||||
},
|
||||
];
|
||||
|
||||
const ULTRA_SCHEMA: EngineConfigField[] = [
|
||||
{
|
||||
key: "enabled",
|
||||
type: "boolean",
|
||||
label: "Enabled",
|
||||
defaultValue: DEFAULT_ULTRA_CONFIG.enabled,
|
||||
},
|
||||
{
|
||||
key: "compressionRate",
|
||||
type: "number",
|
||||
label: "Compression rate",
|
||||
defaultValue: DEFAULT_ULTRA_CONFIG.compressionRate,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
{
|
||||
key: "minScoreThreshold",
|
||||
type: "number",
|
||||
label: "Minimum score threshold",
|
||||
defaultValue: DEFAULT_ULTRA_CONFIG.minScoreThreshold,
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
{
|
||||
key: "slmFallbackToAggressive",
|
||||
type: "boolean",
|
||||
label: "Fallback to aggressive",
|
||||
defaultValue: DEFAULT_ULTRA_CONFIG.slmFallbackToAggressive,
|
||||
},
|
||||
{
|
||||
key: "modelPath",
|
||||
type: "string",
|
||||
label: "Model path",
|
||||
defaultValue: "",
|
||||
},
|
||||
{
|
||||
key: "maxTokensPerMessage",
|
||||
type: "number",
|
||||
label: "Max tokens per message",
|
||||
defaultValue: DEFAULT_ULTRA_CONFIG.maxTokensPerMessage,
|
||||
min: 0,
|
||||
max: 32768,
|
||||
},
|
||||
{
|
||||
key: "preserveSystemPrompt",
|
||||
type: "boolean",
|
||||
label: "Preserve system prompt",
|
||||
defaultValue: true,
|
||||
},
|
||||
];
|
||||
|
||||
function ok(): EngineValidationResult {
|
||||
return { valid: true, errors: [] };
|
||||
}
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return !!value && typeof value === "object" && !Array.isArray(value);
|
||||
}
|
||||
|
||||
function validateBoolean(config: Record<string, unknown>, key: string, errors: string[]): void {
|
||||
if (config[key] !== undefined && typeof config[key] !== "boolean") {
|
||||
errors.push(`${key} must be a boolean`);
|
||||
}
|
||||
}
|
||||
|
||||
function validateNumberRange(
|
||||
config: Record<string, unknown>,
|
||||
key: string,
|
||||
min: number,
|
||||
max: number,
|
||||
errors: string[]
|
||||
): void {
|
||||
const value = config[key];
|
||||
if (value === undefined) return;
|
||||
if (typeof value !== "number" || !Number.isFinite(value) || value < min || value > max) {
|
||||
errors.push(`${key} must be a number between ${min} and ${max}`);
|
||||
}
|
||||
}
|
||||
|
||||
function validateCavemanLikeConfig(config: Record<string, unknown>): EngineValidationResult {
|
||||
const errors: string[] = [];
|
||||
if (
|
||||
@@ -56,6 +166,50 @@ function validateCavemanLikeConfig(config: Record<string, unknown>): EngineValid
|
||||
return { valid: errors.length === 0, errors };
|
||||
}
|
||||
|
||||
function validateAggressiveConfig(config: Record<string, unknown>): EngineValidationResult {
|
||||
const errors: string[] = [];
|
||||
validateBoolean(config, "summarizerEnabled", errors);
|
||||
validateBoolean(config, "preserveSystemPrompt", errors);
|
||||
validateNumberRange(config, "maxTokensPerMessage", 256, 32768, errors);
|
||||
validateNumberRange(config, "minSavingsThreshold", 0, 1, errors);
|
||||
|
||||
if (config.thresholds !== undefined) {
|
||||
if (!isRecord(config.thresholds)) {
|
||||
errors.push("thresholds must be an object");
|
||||
} else {
|
||||
for (const key of ["fullSummary", "moderate", "light", "verbatim"]) {
|
||||
validateNumberRange(config.thresholds, key, 1, 100, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.toolStrategies !== undefined) {
|
||||
if (!isRecord(config.toolStrategies)) {
|
||||
errors.push("toolStrategies must be an object");
|
||||
} else {
|
||||
for (const key of ["fileContent", "grepSearch", "shellOutput", "json", "errorMessage"]) {
|
||||
validateBoolean(config.toolStrategies, key, errors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: errors.length === 0, errors };
|
||||
}
|
||||
|
||||
function validateUltraConfig(config: Record<string, unknown>): EngineValidationResult {
|
||||
const errors: string[] = [];
|
||||
validateBoolean(config, "enabled", errors);
|
||||
validateBoolean(config, "slmFallbackToAggressive", errors);
|
||||
validateBoolean(config, "preserveSystemPrompt", errors);
|
||||
validateNumberRange(config, "compressionRate", 0, 1, errors);
|
||||
validateNumberRange(config, "minScoreThreshold", 0, 1, errors);
|
||||
validateNumberRange(config, "maxTokensPerMessage", 0, 32768, errors);
|
||||
if (config.modelPath !== undefined && typeof config.modelPath !== "string") {
|
||||
errors.push("modelPath must be a string");
|
||||
}
|
||||
return { valid: errors.length === 0, errors };
|
||||
}
|
||||
|
||||
export const liteEngine: CompressionEngine = {
|
||||
id: "lite",
|
||||
name: "Lite",
|
||||
@@ -83,10 +237,10 @@ export const liteEngine: CompressionEngine = {
|
||||
return this.apply(body, { stepConfig: config });
|
||||
},
|
||||
getConfigSchema() {
|
||||
return [];
|
||||
return AGGRESSIVE_SCHEMA;
|
||||
},
|
||||
validateConfig() {
|
||||
return ok();
|
||||
validateConfig(config) {
|
||||
return validateAggressiveConfig(config);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -189,10 +343,10 @@ export const aggressiveEngine: CompressionEngine = {
|
||||
return this.apply(body, { stepConfig: config });
|
||||
},
|
||||
getConfigSchema() {
|
||||
return [];
|
||||
return AGGRESSIVE_SCHEMA;
|
||||
},
|
||||
validateConfig() {
|
||||
return ok();
|
||||
validateConfig(config) {
|
||||
return validateAggressiveConfig(config);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -246,9 +400,9 @@ export const ultraEngine: CompressionEngine = {
|
||||
return this.apply(body, { stepConfig: config });
|
||||
},
|
||||
getConfigSchema() {
|
||||
return [];
|
||||
return ULTRA_SCHEMA;
|
||||
},
|
||||
validateConfig() {
|
||||
return ok();
|
||||
validateConfig(config) {
|
||||
return validateUltraConfig(config);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -199,6 +199,9 @@ export function applyStackedCompression(
|
||||
const rules = new Set<string>();
|
||||
const breakdown: NonNullable<CompressionStats["engineBreakdown"]> = [];
|
||||
const rtkRawOutputPointers: NonNullable<CompressionStats["rtkRawOutputPointers"]> = [];
|
||||
const validationWarnings = new Set<string>();
|
||||
const validationErrors = new Set<string>();
|
||||
let fallbackApplied = false;
|
||||
const start = performance.now();
|
||||
|
||||
for (const step of steps) {
|
||||
@@ -218,6 +221,9 @@ export function applyStackedCompression(
|
||||
result.stats.rtkRawOutputPointers?.forEach((pointer) => {
|
||||
rtkRawOutputPointers.push(pointer);
|
||||
});
|
||||
result.stats.validationWarnings?.forEach((warning) => validationWarnings.add(warning));
|
||||
result.stats.validationErrors?.forEach((error) => validationErrors.add(error));
|
||||
fallbackApplied = fallbackApplied || result.stats.fallbackApplied === true;
|
||||
breakdown.push({
|
||||
engine: step.engine,
|
||||
originalTokens: result.stats.originalTokens,
|
||||
@@ -246,6 +252,15 @@ export function applyStackedCompression(
|
||||
stats.compressionComboId =
|
||||
options?.compressionComboId ?? options?.config?.compressionComboId ?? null;
|
||||
stats.engineBreakdown = breakdown;
|
||||
if (validationWarnings.size > 0) {
|
||||
stats.validationWarnings = Array.from(validationWarnings);
|
||||
}
|
||||
if (validationErrors.size > 0) {
|
||||
stats.validationErrors = Array.from(validationErrors);
|
||||
}
|
||||
if (fallbackApplied) {
|
||||
stats.fallbackApplied = true;
|
||||
}
|
||||
if (rtkRawOutputPointers.length > 0) {
|
||||
const seenPointers = new Set<string>();
|
||||
stats.rtkRawOutputPointers = rtkRawOutputPointers.filter((pointer) => {
|
||||
|
||||
@@ -251,7 +251,7 @@ export default function CavemanContextPageClient() {
|
||||
{previewPrompt}
|
||||
</pre>
|
||||
<p className="mt-3 text-xs text-text-muted">
|
||||
{t("bypassConditions")}: security, irreversible, clarification, order-sensitive
|
||||
{t("bypassConditions")}: {t("bypassConditionsList")}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -23,6 +23,10 @@ type RtkConfig = {
|
||||
maxLinesPerResult: number;
|
||||
maxCharsPerResult: number;
|
||||
deduplicateThreshold: number;
|
||||
customFiltersEnabled: boolean;
|
||||
trustProjectFilters: boolean;
|
||||
rawOutputRetention: "never" | "failures" | "always";
|
||||
rawOutputMaxBytes: number;
|
||||
};
|
||||
|
||||
type AnalyticsSummary = {
|
||||
@@ -156,7 +160,7 @@ export default function RtkContextPageClient() {
|
||||
|
||||
{config && (
|
||||
<section className="rounded-lg border border-border bg-surface p-4">
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-4">
|
||||
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-5">
|
||||
<label className="flex items-center gap-2 text-sm text-text-main">
|
||||
<input
|
||||
type="checkbox"
|
||||
@@ -205,12 +209,39 @@ export default function RtkContextPageClient() {
|
||||
className="rounded border border-border bg-bg px-2 py-1 text-sm"
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1 text-sm text-text-main">
|
||||
{t("deduplicateThreshold")}
|
||||
<input
|
||||
type="number"
|
||||
min={2}
|
||||
max={100}
|
||||
value={config.deduplicateThreshold}
|
||||
onChange={(event) =>
|
||||
saveConfig({ deduplicateThreshold: Number(event.target.value) || 2 })
|
||||
}
|
||||
className="rounded border border-border bg-bg px-2 py-1 text-sm"
|
||||
/>
|
||||
</label>
|
||||
<label className="flex flex-col gap-1 text-sm text-text-main">
|
||||
{t("rawOutputMaxBytes")}
|
||||
<input
|
||||
type="number"
|
||||
min={1024}
|
||||
value={config.rawOutputMaxBytes}
|
||||
onChange={(event) =>
|
||||
saveConfig({ rawOutputMaxBytes: Number(event.target.value) || 1024 })
|
||||
}
|
||||
className="rounded border border-border bg-bg px-2 py-1 text-sm"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
<div className="mt-4 flex flex-wrap gap-4 text-sm text-text-main">
|
||||
{[
|
||||
["applyToToolResults", t("toolResults")],
|
||||
["applyToAssistantMessages", t("assistantMessages")],
|
||||
["applyToCodeBlocks", t("codeBlocks")],
|
||||
["customFiltersEnabled", t("customFilters")],
|
||||
["trustProjectFilters", t("trustProjectFilters")],
|
||||
].map(([key, label]) => (
|
||||
<label key={key} className="flex items-center gap-2">
|
||||
<input
|
||||
@@ -225,6 +256,25 @@ export default function RtkContextPageClient() {
|
||||
</label>
|
||||
))}
|
||||
</div>
|
||||
<div className="mt-4 max-w-sm text-sm text-text-main">
|
||||
<label className="flex flex-col gap-1">
|
||||
{t("rawOutputRetention")}
|
||||
<select
|
||||
value={config.rawOutputRetention}
|
||||
disabled={saving}
|
||||
onChange={(event) =>
|
||||
saveConfig({
|
||||
rawOutputRetention: event.target.value as RtkConfig["rawOutputRetention"],
|
||||
})
|
||||
}
|
||||
className="rounded border border-border bg-bg px-2 py-1 text-sm"
|
||||
>
|
||||
<option value="never">{t("rawOutputNever")}</option>
|
||||
<option value="failures">{t("rawOutputFailures")}</option>
|
||||
<option value="always">{t("rawOutputAlways")}</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { enforceApiKeyPolicy } from "@/shared/utils/apiKeyPolicy";
|
||||
import { compressionPreviewConfigSchema } from "@/shared/validation/compressionConfigSchemas";
|
||||
import { applyCompression } from "@omniroute/open-sse/services/compression/strategySelector";
|
||||
import type {
|
||||
CompressionConfig,
|
||||
@@ -8,54 +9,7 @@ import type {
|
||||
} from "@omniroute/open-sse/services/compression/types";
|
||||
import { buildCompressionPreviewDiff } from "@omniroute/open-sse/services/compression/diffHelper";
|
||||
|
||||
const previewRtkConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
intensity: z.enum(["minimal", "standard", "aggressive"]).optional(),
|
||||
applyToToolResults: z.boolean().optional(),
|
||||
applyToCodeBlocks: z.boolean().optional(),
|
||||
applyToAssistantMessages: z.boolean().optional(),
|
||||
enabledFilters: z.array(z.string()).optional(),
|
||||
disabledFilters: z.array(z.string()).optional(),
|
||||
maxLinesPerResult: z.number().int().min(0).max(100000).optional(),
|
||||
maxCharsPerResult: z.number().int().min(0).max(1000000).optional(),
|
||||
deduplicateThreshold: z.number().int().min(2).max(100).optional(),
|
||||
customFiltersEnabled: z.boolean().optional(),
|
||||
trustProjectFilters: z.boolean().optional(),
|
||||
rawOutputRetention: z.enum(["never", "failures", "always"]).optional(),
|
||||
rawOutputMaxBytes: z.number().int().min(1024).max(10_000_000).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const previewPipelineStepSchema = z
|
||||
.object({
|
||||
engine: z.enum(["lite", "caveman", "aggressive", "ultra", "rtk"]),
|
||||
intensity: z.string().optional(),
|
||||
config: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const PreviewCompressionConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
defaultMode: z
|
||||
.enum(["off", "lite", "standard", "aggressive", "ultra", "rtk", "stacked"])
|
||||
.optional(),
|
||||
preserveSystemPrompt: z.boolean().optional(),
|
||||
compressionComboId: z.string().nullable().optional(),
|
||||
rtkConfig: previewRtkConfigSchema.optional(),
|
||||
stackedPipeline: z.array(previewPipelineStepSchema).optional(),
|
||||
languageConfig: z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
defaultLanguage: z.string().min(1).optional(),
|
||||
autoDetect: z.boolean().optional(),
|
||||
enabledPacks: z.array(z.string().min(1)).optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
})
|
||||
.strict();
|
||||
export const PreviewCompressionConfigSchema = compressionPreviewConfigSchema;
|
||||
|
||||
export const PreviewRequestSchema = z.object({
|
||||
messages: z
|
||||
|
||||
@@ -8,14 +8,12 @@ import {
|
||||
} from "@/lib/db/compressionCombos";
|
||||
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
||||
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
import {
|
||||
cavemanIntensitySchema,
|
||||
stackedPipelineStepSchema,
|
||||
} from "@/shared/validation/compressionConfigSchemas";
|
||||
|
||||
export const pipelineStepSchema = z
|
||||
.object({
|
||||
engine: z.enum(["lite", "caveman", "aggressive", "ultra", "rtk"]),
|
||||
intensity: z.string().optional(),
|
||||
config: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
.strict();
|
||||
export const pipelineStepSchema = stackedPipelineStepSchema;
|
||||
|
||||
export const compressionComboUpdateSchema = z
|
||||
.object({
|
||||
@@ -24,7 +22,7 @@ export const compressionComboUpdateSchema = z
|
||||
pipeline: z.array(pipelineStepSchema).min(1).optional(),
|
||||
languagePacks: z.array(z.string().trim().min(1)).optional(),
|
||||
outputMode: z.boolean().optional(),
|
||||
outputModeIntensity: z.string().optional(),
|
||||
outputModeIntensity: cavemanIntensitySchema.optional(),
|
||||
isDefault: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
@@ -3,14 +3,12 @@ import { z } from "zod";
|
||||
import { createCompressionCombo, listCompressionCombos } from "@/lib/db/compressionCombos";
|
||||
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
||||
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
import {
|
||||
cavemanIntensitySchema,
|
||||
stackedPipelineStepSchema,
|
||||
} from "@/shared/validation/compressionConfigSchemas";
|
||||
|
||||
export const pipelineStepSchema = z
|
||||
.object({
|
||||
engine: z.enum(["lite", "caveman", "aggressive", "ultra", "rtk"]),
|
||||
intensity: z.string().optional(),
|
||||
config: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
.strict();
|
||||
export const pipelineStepSchema = stackedPipelineStepSchema;
|
||||
|
||||
export const compressionComboCreateSchema = z
|
||||
.object({
|
||||
@@ -20,7 +18,7 @@ export const compressionComboCreateSchema = z
|
||||
pipeline: z.array(pipelineStepSchema).min(1).optional(),
|
||||
languagePacks: z.array(z.string().trim().min(1)).optional(),
|
||||
outputMode: z.boolean().optional(),
|
||||
outputModeIntensity: z.string().optional(),
|
||||
outputModeIntensity: cavemanIntensitySchema.optional(),
|
||||
isDefault: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
@@ -1,27 +1,10 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { getCompressionSettings, updateCompressionSettings } from "@/lib/db/compression";
|
||||
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
||||
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
import { rtkConfigSchema } from "@/shared/validation/compressionConfigSchemas";
|
||||
|
||||
export const rtkConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
intensity: z.enum(["minimal", "standard", "aggressive"]).optional(),
|
||||
applyToToolResults: z.boolean().optional(),
|
||||
applyToCodeBlocks: z.boolean().optional(),
|
||||
applyToAssistantMessages: z.boolean().optional(),
|
||||
enabledFilters: z.array(z.string()).optional(),
|
||||
disabledFilters: z.array(z.string()).optional(),
|
||||
maxLinesPerResult: z.number().int().min(0).max(100000).optional(),
|
||||
maxCharsPerResult: z.number().int().min(0).max(1000000).optional(),
|
||||
deduplicateThreshold: z.number().int().min(2).max(100).optional(),
|
||||
customFiltersEnabled: z.boolean().optional(),
|
||||
trustProjectFilters: z.boolean().optional(),
|
||||
rawOutputRetention: z.enum(["never", "failures", "always"]).optional(),
|
||||
rawOutputMaxBytes: z.number().int().min(1024).max(10_000_000).optional(),
|
||||
})
|
||||
.strict();
|
||||
export { rtkConfigSchema };
|
||||
|
||||
export async function GET(request: Request) {
|
||||
const authError = await requireManagementAuth(request);
|
||||
|
||||
@@ -6,12 +6,13 @@ import {
|
||||
} from "@omniroute/open-sse/services/compression/engines/rtk";
|
||||
import { requireManagementAuth } from "@/lib/api/requireManagementAuth";
|
||||
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
import { rtkConfigSchema } from "@/shared/validation/compressionConfigSchemas";
|
||||
|
||||
export const rtkTestSchema = z
|
||||
.object({
|
||||
text: z.string().min(1),
|
||||
command: z.string().optional(),
|
||||
config: z.record(z.string(), z.unknown()).optional(),
|
||||
config: rtkConfigSchema.optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
|
||||
@@ -1,132 +1,8 @@
|
||||
import { NextRequest, NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
import { getCompressionSettings, updateCompressionSettings } from "@/lib/db/compression";
|
||||
import { isAuthenticated } from "@/shared/utils/apiAuth";
|
||||
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
|
||||
const compressionModeSchema = z.enum([
|
||||
"off",
|
||||
"lite",
|
||||
"standard",
|
||||
"aggressive",
|
||||
"ultra",
|
||||
"rtk",
|
||||
"stacked",
|
||||
]);
|
||||
const cavemanIntensitySchema = z.enum(["lite", "full", "ultra"]);
|
||||
const rtkIntensitySchema = z.enum(["minimal", "standard", "aggressive"]);
|
||||
|
||||
const cavemanConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
compressRoles: z.array(z.enum(["user", "assistant", "system"])).optional(),
|
||||
skipRules: z.array(z.string()).optional(),
|
||||
minMessageLength: z.number().int().min(0).optional(),
|
||||
preservePatterns: z.array(z.string()).optional(),
|
||||
intensity: cavemanIntensitySchema.optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const cavemanOutputModeSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
intensity: cavemanIntensitySchema.optional(),
|
||||
autoClarity: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const rtkConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
intensity: rtkIntensitySchema.optional(),
|
||||
applyToToolResults: z.boolean().optional(),
|
||||
applyToCodeBlocks: z.boolean().optional(),
|
||||
applyToAssistantMessages: z.boolean().optional(),
|
||||
enabledFilters: z.array(z.string()).optional(),
|
||||
disabledFilters: z.array(z.string()).optional(),
|
||||
maxLinesPerResult: z.number().int().min(0).max(100000).optional(),
|
||||
maxCharsPerResult: z.number().int().min(0).max(1000000).optional(),
|
||||
deduplicateThreshold: z.number().int().min(2).max(100).optional(),
|
||||
customFiltersEnabled: z.boolean().optional(),
|
||||
trustProjectFilters: z.boolean().optional(),
|
||||
rawOutputRetention: z.enum(["never", "failures", "always"]).optional(),
|
||||
rawOutputMaxBytes: z.number().int().min(1024).max(10_000_000).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const languageConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
defaultLanguage: z.string().trim().min(1).optional(),
|
||||
autoDetect: z.boolean().optional(),
|
||||
enabledPacks: z.array(z.string().trim().min(1)).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const stackedPipelineStepSchema = z
|
||||
.object({
|
||||
engine: z.enum(["lite", "caveman", "aggressive", "ultra", "rtk"]),
|
||||
intensity: z.string().optional(),
|
||||
config: z.record(z.string(), z.unknown()).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const aggressiveConfigSchema = z
|
||||
.object({
|
||||
thresholds: z
|
||||
.object({
|
||||
fullSummary: z.number().int().min(1).max(100).optional(),
|
||||
moderate: z.number().int().min(1).max(100).optional(),
|
||||
light: z.number().int().min(1).max(100).optional(),
|
||||
verbatim: z.number().int().min(1).max(100).optional(),
|
||||
})
|
||||
.optional(),
|
||||
toolStrategies: z
|
||||
.object({
|
||||
fileContent: z.boolean().optional(),
|
||||
grepSearch: z.boolean().optional(),
|
||||
shellOutput: z.boolean().optional(),
|
||||
json: z.boolean().optional(),
|
||||
errorMessage: z.boolean().optional(),
|
||||
})
|
||||
.optional(),
|
||||
summarizerEnabled: z.boolean().optional(),
|
||||
maxTokensPerMessage: z.number().int().min(256).max(32768).optional(),
|
||||
minSavingsThreshold: z.number().min(0).max(1).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const ultraConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
compressionRate: z.number().min(0).max(1).optional(),
|
||||
minScoreThreshold: z.number().min(0).max(1).optional(),
|
||||
slmFallbackToAggressive: z.boolean().optional(),
|
||||
modelPath: z.string().trim().min(1).optional(),
|
||||
maxTokensPerMessage: z.number().int().min(0).max(32768).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const compressionSettingsUpdateSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
defaultMode: compressionModeSchema.optional(),
|
||||
autoTriggerMode: compressionModeSchema.optional(),
|
||||
autoTriggerTokens: z.number().int().min(0).optional(),
|
||||
cacheMinutes: z.number().int().min(1).max(60).optional(),
|
||||
preserveSystemPrompt: z.boolean().optional(),
|
||||
mcpDescriptionCompressionEnabled: z.boolean().optional(),
|
||||
comboOverrides: z.record(z.string(), compressionModeSchema).optional(),
|
||||
compressionComboId: z.string().trim().min(1).nullable().optional(),
|
||||
stackedPipeline: z.array(stackedPipelineStepSchema).optional(),
|
||||
cavemanConfig: cavemanConfigSchema.optional(),
|
||||
cavemanOutputMode: cavemanOutputModeSchema.optional(),
|
||||
rtkConfig: rtkConfigSchema.optional(),
|
||||
languageConfig: languageConfigSchema.optional(),
|
||||
aggressive: aggressiveConfigSchema.optional(),
|
||||
ultra: ultraConfigSchema.optional(),
|
||||
})
|
||||
.strict();
|
||||
import { compressionSettingsUpdateSchema } from "@/shared/validation/compressionConfigSchemas";
|
||||
|
||||
export async function GET(request: NextRequest) {
|
||||
if (!(await isAuthenticated(request))) {
|
||||
|
||||
@@ -3719,6 +3719,14 @@
|
||||
"codeBlocks": "Code blocks",
|
||||
"maxLines": "Max lines",
|
||||
"maxChars": "Max chars",
|
||||
"deduplicateThreshold": "Deduplicate threshold",
|
||||
"customFilters": "Custom filters",
|
||||
"trustProjectFilters": "Trust project filters",
|
||||
"rawOutputRetention": "Raw output retention",
|
||||
"rawOutputNever": "Never",
|
||||
"rawOutputFailures": "Failures",
|
||||
"rawOutputAlways": "Always",
|
||||
"rawOutputMaxBytes": "Raw output max bytes",
|
||||
"filterTesting": "Filter Testing",
|
||||
"pasteOutput": "Paste tool output to test filtering...",
|
||||
"run": "Run",
|
||||
@@ -3770,6 +3778,7 @@
|
||||
"outputModeDesc": "Instructs the LLM to respond in a terse, compact format.",
|
||||
"autoClarity": "Auto-Clarity Bypass",
|
||||
"bypassConditions": "Bypass conditions",
|
||||
"bypassConditionsList": "security, irreversible, clarification, order-sensitive",
|
||||
"preview": {
|
||||
"lite": "Respond concise. Preserve technical terms, code, errors, URLs, and identifiers.",
|
||||
"full": "Respond terse and compact. Preserve all technical substance.",
|
||||
|
||||
@@ -3421,6 +3421,14 @@
|
||||
"codeBlocks": "Blocos de codigo",
|
||||
"maxLines": "Max linhas",
|
||||
"maxChars": "Max caracteres",
|
||||
"deduplicateThreshold": "Limite de deduplicacao",
|
||||
"customFilters": "Filtros customizados",
|
||||
"trustProjectFilters": "Confiar em filtros do projeto",
|
||||
"rawOutputRetention": "Retencao de raw output",
|
||||
"rawOutputNever": "Nunca",
|
||||
"rawOutputFailures": "Falhas",
|
||||
"rawOutputAlways": "Sempre",
|
||||
"rawOutputMaxBytes": "Max bytes de raw output",
|
||||
"filterTesting": "Teste de filtros",
|
||||
"pasteOutput": "Cole output de ferramenta para testar...",
|
||||
"run": "Executar",
|
||||
@@ -3472,6 +3480,7 @@
|
||||
"outputModeDesc": "Instrui o LLM a responder em formato curto e compacto.",
|
||||
"autoClarity": "Bypass Auto-Clarity",
|
||||
"bypassConditions": "Condicoes de bypass",
|
||||
"bypassConditionsList": "seguranca, irreversivel, clarificacao, sensivel a ordem",
|
||||
"preview": {
|
||||
"lite": "Responda conciso. Preserve termos tecnicos, codigo, erros, URLs e identificadores.",
|
||||
"full": "Responda seco e compacto. Preserve todo conteudo tecnico.",
|
||||
|
||||
@@ -49,6 +49,10 @@ export interface CompressionAnalyticsSummary {
|
||||
estimatedUsdSaved: number;
|
||||
bySource: Record<string, number>;
|
||||
};
|
||||
mcpDescriptionCompression: {
|
||||
snapshots: number;
|
||||
estimatedTokensSaved: number;
|
||||
};
|
||||
}
|
||||
|
||||
let columnsEnsuredForDb: unknown = null;
|
||||
@@ -413,6 +417,15 @@ export function getCompressionAnalyticsSummary(since?: string): CompressionAnaly
|
||||
)
|
||||
.get(...params) as { cnt: number } | undefined;
|
||||
|
||||
const mcpDescriptionRow = db
|
||||
.prepare(
|
||||
`
|
||||
SELECT COUNT(*) as cnt, COALESCE(SUM(mcp_description_tokens_saved), 0) as saved
|
||||
FROM compression_analytics ${appendCondition(whereClause, "mcp_description_tokens_saved > 0")}
|
||||
`
|
||||
)
|
||||
.get(...params) as { cnt: number; saved: number } | undefined;
|
||||
|
||||
return {
|
||||
totalRequests: scalar?.total ?? 0,
|
||||
totalTokensSaved: scalar?.totalSaved ?? 0,
|
||||
@@ -425,5 +438,9 @@ export function getCompressionAnalyticsSummary(since?: string): CompressionAnaly
|
||||
last24h,
|
||||
validationFallbacks: fallbackRow?.cnt ?? 0,
|
||||
realUsage,
|
||||
mcpDescriptionCompression: {
|
||||
snapshots: mcpDescriptionRow?.cnt ?? 0,
|
||||
estimatedTokensSaved: mcpDescriptionRow?.saved ?? 0,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
165
src/shared/validation/compressionConfigSchemas.ts
Normal file
165
src/shared/validation/compressionConfigSchemas.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
import { z } from "zod";
|
||||
|
||||
export const compressionModeSchema = z.enum([
|
||||
"off",
|
||||
"lite",
|
||||
"standard",
|
||||
"aggressive",
|
||||
"ultra",
|
||||
"rtk",
|
||||
"stacked",
|
||||
]);
|
||||
|
||||
export const cavemanIntensitySchema = z.enum(["lite", "full", "ultra"]);
|
||||
export const rtkIntensitySchema = z.enum(["minimal", "standard", "aggressive"]);
|
||||
export const rtkRawOutputRetentionSchema = z.enum(["never", "failures", "always"]);
|
||||
|
||||
export const cavemanConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
compressRoles: z.array(z.enum(["user", "assistant", "system"])).optional(),
|
||||
skipRules: z.array(z.string()).optional(),
|
||||
minMessageLength: z.number().int().min(0).optional(),
|
||||
preservePatterns: z.array(z.string()).optional(),
|
||||
intensity: cavemanIntensitySchema.optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const cavemanOutputModeSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
intensity: cavemanIntensitySchema.optional(),
|
||||
autoClarity: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const rtkConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
intensity: rtkIntensitySchema.optional(),
|
||||
applyToToolResults: z.boolean().optional(),
|
||||
applyToCodeBlocks: z.boolean().optional(),
|
||||
applyToAssistantMessages: z.boolean().optional(),
|
||||
enabledFilters: z.array(z.string()).optional(),
|
||||
disabledFilters: z.array(z.string()).optional(),
|
||||
maxLinesPerResult: z.number().int().min(0).max(100000).optional(),
|
||||
maxCharsPerResult: z.number().int().min(0).max(1000000).optional(),
|
||||
deduplicateThreshold: z.number().int().min(2).max(100).optional(),
|
||||
customFiltersEnabled: z.boolean().optional(),
|
||||
trustProjectFilters: z.boolean().optional(),
|
||||
rawOutputRetention: rtkRawOutputRetentionSchema.optional(),
|
||||
rawOutputMaxBytes: z.number().int().min(1024).max(10_000_000).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const languageConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
defaultLanguage: z.string().trim().min(1).optional(),
|
||||
autoDetect: z.boolean().optional(),
|
||||
enabledPacks: z.array(z.string().trim().min(1)).optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const aggressiveConfigSchema = z
|
||||
.object({
|
||||
thresholds: z
|
||||
.object({
|
||||
fullSummary: z.number().int().min(1).max(100).optional(),
|
||||
moderate: z.number().int().min(1).max(100).optional(),
|
||||
light: z.number().int().min(1).max(100).optional(),
|
||||
verbatim: z.number().int().min(1).max(100).optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
toolStrategies: z
|
||||
.object({
|
||||
fileContent: z.boolean().optional(),
|
||||
grepSearch: z.boolean().optional(),
|
||||
shellOutput: z.boolean().optional(),
|
||||
json: z.boolean().optional(),
|
||||
errorMessage: z.boolean().optional(),
|
||||
})
|
||||
.strict()
|
||||
.optional(),
|
||||
summarizerEnabled: z.boolean().optional(),
|
||||
maxTokensPerMessage: z.number().int().min(256).max(32768).optional(),
|
||||
minSavingsThreshold: z.number().min(0).max(1).optional(),
|
||||
preserveSystemPrompt: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const ultraConfigSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
compressionRate: z.number().min(0).max(1).optional(),
|
||||
minScoreThreshold: z.number().min(0).max(1).optional(),
|
||||
slmFallbackToAggressive: z.boolean().optional(),
|
||||
modelPath: z.string().trim().min(1).optional(),
|
||||
maxTokensPerMessage: z.number().int().min(0).max(32768).optional(),
|
||||
preserveSystemPrompt: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
const noConfigSchema = z.object({}).strict();
|
||||
|
||||
export const stackedPipelineStepSchema = z.discriminatedUnion("engine", [
|
||||
z
|
||||
.object({
|
||||
engine: z.literal("lite"),
|
||||
intensity: z.literal("lite").optional(),
|
||||
config: noConfigSchema.optional(),
|
||||
})
|
||||
.strict(),
|
||||
z
|
||||
.object({
|
||||
engine: z.literal("caveman"),
|
||||
intensity: cavemanIntensitySchema.optional(),
|
||||
config: cavemanConfigSchema.optional(),
|
||||
})
|
||||
.strict(),
|
||||
z
|
||||
.object({
|
||||
engine: z.literal("aggressive"),
|
||||
intensity: z.literal("standard").optional(),
|
||||
config: aggressiveConfigSchema.optional(),
|
||||
})
|
||||
.strict(),
|
||||
z
|
||||
.object({
|
||||
engine: z.literal("ultra"),
|
||||
intensity: z.literal("ultra").optional(),
|
||||
config: ultraConfigSchema.optional(),
|
||||
})
|
||||
.strict(),
|
||||
z
|
||||
.object({
|
||||
engine: z.literal("rtk"),
|
||||
intensity: rtkIntensitySchema.optional(),
|
||||
config: rtkConfigSchema.optional(),
|
||||
})
|
||||
.strict(),
|
||||
]);
|
||||
|
||||
export const compressionSettingsUpdateSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
defaultMode: compressionModeSchema.optional(),
|
||||
autoTriggerMode: compressionModeSchema.optional(),
|
||||
autoTriggerTokens: z.number().int().min(0).optional(),
|
||||
cacheMinutes: z.number().int().min(1).max(60).optional(),
|
||||
preserveSystemPrompt: z.boolean().optional(),
|
||||
mcpDescriptionCompressionEnabled: z.boolean().optional(),
|
||||
comboOverrides: z.record(z.string(), compressionModeSchema).optional(),
|
||||
compressionComboId: z.string().trim().min(1).nullable().optional(),
|
||||
stackedPipeline: z.array(stackedPipelineStepSchema).optional(),
|
||||
cavemanConfig: cavemanConfigSchema.optional(),
|
||||
cavemanOutputMode: cavemanOutputModeSchema.optional(),
|
||||
rtkConfig: rtkConfigSchema.optional(),
|
||||
languageConfig: languageConfigSchema.optional(),
|
||||
aggressive: aggressiveConfigSchema.optional(),
|
||||
ultra: ultraConfigSchema.optional(),
|
||||
})
|
||||
.strict();
|
||||
|
||||
export const compressionPreviewConfigSchema = compressionSettingsUpdateSchema;
|
||||
@@ -180,10 +180,10 @@ test("chatCore integration: disabled prompt compression leaves combo override re
|
||||
"Test body should exceed proactive compression threshold"
|
||||
);
|
||||
|
||||
let capturedBody: any = null;
|
||||
let capturedBody: { messages?: Array<{ role?: string; content?: string }> } | null = null;
|
||||
globalThis.fetch = async (_url: string | URL | Request, init?: RequestInit) => {
|
||||
if (init?.body) {
|
||||
capturedBody = JSON.parse(init.body as string);
|
||||
capturedBody = JSON.parse(init.body as string) as typeof capturedBody;
|
||||
}
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
@@ -660,6 +660,96 @@ test("chatCore integration: assigned compression combo applies language packs an
|
||||
}
|
||||
});
|
||||
|
||||
test("chatCore integration: default compression combo applies for unassigned stacked requests", async () => {
|
||||
const provider = "openai";
|
||||
const model = "gpt-4";
|
||||
|
||||
await compressionDb.updateCompressionSettings({
|
||||
enabled: true,
|
||||
defaultMode: "stacked",
|
||||
autoTriggerTokens: 0,
|
||||
cavemanOutputMode: {
|
||||
enabled: false,
|
||||
intensity: "full",
|
||||
autoClarity: true,
|
||||
},
|
||||
languageConfig: {
|
||||
enabled: false,
|
||||
defaultLanguage: "en",
|
||||
autoDetect: true,
|
||||
enabledPacks: ["en"],
|
||||
},
|
||||
});
|
||||
|
||||
const compressionCombo = compressionCombosDb.createCompressionCombo({
|
||||
name: "Default PT Output Mode",
|
||||
pipeline: [{ engine: "caveman", intensity: "lite" }],
|
||||
languagePacks: ["pt-BR"],
|
||||
outputMode: true,
|
||||
outputModeIntensity: "lite",
|
||||
isDefault: true,
|
||||
});
|
||||
|
||||
const connection = await providersDb.createProviderConnection({
|
||||
provider,
|
||||
apiKey: "test-key",
|
||||
isActive: true,
|
||||
});
|
||||
|
||||
let capturedBody: { messages?: Array<{ role?: string; content?: string }> } | null = null;
|
||||
globalThis.fetch = async (_url: string | URL | Request, init?: RequestInit) => {
|
||||
if (init?.body) {
|
||||
capturedBody = JSON.parse(init.body as string) as typeof capturedBody;
|
||||
}
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
choices: [{ message: { role: "assistant", content: "ok" } }],
|
||||
usage: { prompt_tokens: 10, completion_tokens: 5, total_tokens: 15 },
|
||||
}),
|
||||
{
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await handleChatCore({
|
||||
body: {
|
||||
model,
|
||||
stream: false,
|
||||
messages: [{ role: "user", content: "Resuma esta implementacao." }],
|
||||
},
|
||||
modelInfo: { provider, model },
|
||||
credentials: { apiKey: "test-key" },
|
||||
log: { debug: () => {}, info: () => {}, warn: () => {}, error: () => {} },
|
||||
clientRawRequest: { endpoint: "/v1/chat/completions", headers: new Map() },
|
||||
connectionId: connection.id,
|
||||
});
|
||||
|
||||
assert.ok(result.success, "Request should succeed");
|
||||
assert.ok(capturedBody, "Fetch should receive the request body");
|
||||
const firstMessage = capturedBody.messages?.[0];
|
||||
assert.equal(firstMessage?.role, "system");
|
||||
assert.match(firstMessage?.content ?? "", /OmniRoute Caveman Output Mode/);
|
||||
assert.match(firstMessage?.content ?? "", /Responda conciso/);
|
||||
|
||||
let summary = compressionAnalyticsDb.getCompressionAnalyticsSummary();
|
||||
for (
|
||||
let attempt = 0;
|
||||
attempt < 100 && !summary.byCompressionCombo[compressionCombo.id];
|
||||
attempt += 1
|
||||
) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 20));
|
||||
summary = compressionAnalyticsDb.getCompressionAnalyticsSummary();
|
||||
}
|
||||
|
||||
assert.equal(summary.byCompressionCombo[compressionCombo.id].count, 1);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
});
|
||||
|
||||
test("chatCore integration: modular compression records analytics row best-effort", async () => {
|
||||
const provider = "openai";
|
||||
const model = "gpt-4";
|
||||
|
||||
@@ -49,5 +49,17 @@ describe("compression preview API contract", () => {
|
||||
}).success,
|
||||
false
|
||||
);
|
||||
assert.equal(
|
||||
PreviewCompressionConfigSchema.safeParse({
|
||||
stackedPipeline: [{ engine: "rtk", intensity: "bogus" }],
|
||||
}).success,
|
||||
false
|
||||
);
|
||||
assert.equal(
|
||||
PreviewCompressionConfigSchema.safeParse({
|
||||
stackedPipeline: [{ engine: "rtk", config: { maxLinesPerResult: -1 } }],
|
||||
}).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -68,6 +68,10 @@ describe("compressionAnalytics", () => {
|
||||
estimatedUsdSaved: 0,
|
||||
bySource: {},
|
||||
},
|
||||
mcpDescriptionCompression: {
|
||||
snapshots: 0,
|
||||
estimatedTokensSaved: 0,
|
||||
},
|
||||
});
|
||||
assert.equal(summary.last24h.length, 24);
|
||||
});
|
||||
@@ -335,4 +339,22 @@ describe("compressionAnalytics", () => {
|
||||
assert.equal(summary.realUsage.requestsWithReceipts, 1);
|
||||
assert.equal(summary.realUsage.totalTokens, 1020);
|
||||
});
|
||||
|
||||
it("summarizes MCP description estimates without counting them as provider receipts", () => {
|
||||
insertCompressionAnalyticsRow({
|
||||
timestamp: new Date().toISOString(),
|
||||
mode: "mcp-description",
|
||||
engine: "mcp-description",
|
||||
original_tokens: 20,
|
||||
compressed_tokens: 12,
|
||||
tokens_saved: 8,
|
||||
mcp_description_tokens_saved: 8,
|
||||
});
|
||||
|
||||
const summary = getCompressionAnalyticsSummary();
|
||||
assert.equal(summary.mcpDescriptionCompression.snapshots, 1);
|
||||
assert.equal(summary.mcpDescriptionCompression.estimatedTokensSaved, 8);
|
||||
assert.equal(summary.realUsage.requestsWithReceipts, 0);
|
||||
assert.equal(summary.realUsage.bySource.mcp_metadata_estimate, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,6 +21,8 @@ const {
|
||||
listCompressionCombosTool,
|
||||
compressionComboStatsTool,
|
||||
} = await import("../../../open-sse/mcp-server/schemas/tools.ts");
|
||||
const { maybeCompressMcpDescription, resetMcpDescriptionCompressionStats } =
|
||||
await import("../../../open-sse/mcp-server/descriptionCompressor.ts");
|
||||
|
||||
describe("compression MCP tool schemas", () => {
|
||||
it("uses canonical read/write compression scopes", () => {
|
||||
@@ -83,6 +85,26 @@ describe("handleCompressionStatus", () => {
|
||||
assert.equal(typeof result.analytics.mcpDescriptionCompression.charsBefore, "number");
|
||||
assert.equal(typeof result.analytics.mcpDescriptionCompression.charsAfter, "number");
|
||||
});
|
||||
|
||||
it("snapshots MCP description savings into analytics separately", async () => {
|
||||
resetMcpDescriptionCompressionStats();
|
||||
const compressed = maybeCompressMcpDescription(
|
||||
"The function returns the current weather for a city and the detailed forecast summary.",
|
||||
{ enabled: true }
|
||||
);
|
||||
assert.match(compressed, /weather/i);
|
||||
|
||||
const result = await handleCompressionStatus({});
|
||||
assert.ok(result.analytics.mcpDescriptionCompression.estimatedTokensSaved > 0);
|
||||
assert.ok(result.analytics.mcpDescriptionCompression.persistedEstimatedTokensSaved > 0);
|
||||
assert.ok(result.analytics.mcpDescriptionCompression.persistedSnapshots > 0);
|
||||
|
||||
const stats = await handleCompressionComboStats({ since: "all" });
|
||||
const mcp = stats.mcpDescriptionCompression as { estimatedTokensSaved?: number };
|
||||
const realUsage = stats.realUsage as { bySource?: Record<string, number> };
|
||||
assert.ok((mcp.estimatedTokensSaved ?? 0) > 0);
|
||||
assert.equal(realUsage.bySource?.mcp_metadata_estimate, undefined);
|
||||
});
|
||||
});
|
||||
|
||||
describe("handleCompressionConfigure", () => {
|
||||
|
||||
@@ -23,6 +23,13 @@ describe("context compression API schemas", () => {
|
||||
assert.equal(rtkConfigSchema.safeParse({ rawOutputRetention: "plaintext" }).success, false);
|
||||
assert.equal(rtkTestSchema.safeParse({ text: "" }).success, false);
|
||||
assert.equal(rtkTestSchema.safeParse({ text: "ok", extra: true }).success, false);
|
||||
assert.equal(
|
||||
rtkTestSchema.safeParse({
|
||||
text: "ok",
|
||||
config: { maxLinesPerResult: -1, madeUp: true },
|
||||
}).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects invalid compression combo payloads", () => {
|
||||
@@ -38,5 +45,12 @@ describe("context compression API schemas", () => {
|
||||
assert.equal(compressionComboUpdateSchema.safeParse({ pipeline: [] }).success, false);
|
||||
assert.equal(assignmentsUpdateSchema.safeParse({ routingComboIds: ["combo-a"] }).success, true);
|
||||
assert.equal(assignmentsUpdateSchema.safeParse({ routingComboIds: [""] }).success, false);
|
||||
assert.equal(
|
||||
compressionComboCreateSchema.safeParse({
|
||||
name: "Bad",
|
||||
pipeline: [{ engine: "rtk", intensity: "bogus" }],
|
||||
}).success,
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { describe, it, beforeEach } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { cavemanEngine } from "../../../open-sse/services/compression/engines/cavemanAdapter.ts";
|
||||
import {
|
||||
aggressiveEngine,
|
||||
cavemanEngine,
|
||||
ultraEngine,
|
||||
} from "../../../open-sse/services/compression/engines/cavemanAdapter.ts";
|
||||
import { rtkEngine as realRtkEngine } from "../../../open-sse/services/compression/engines/rtk/index.ts";
|
||||
import {
|
||||
clearCompressionEngineRegistry,
|
||||
@@ -63,11 +67,19 @@ describe("compression engine registry contract", () => {
|
||||
it("exposes schema and validation for built-in adapters", () => {
|
||||
const cavemanSchema = cavemanEngine.getConfigSchema();
|
||||
const rtkSchema = realRtkEngine.getConfigSchema();
|
||||
const aggressiveSchema = aggressiveEngine.getConfigSchema();
|
||||
const ultraSchema = ultraEngine.getConfigSchema();
|
||||
|
||||
assert.ok(cavemanSchema.some((field) => field.key === "intensity"));
|
||||
assert.ok(rtkSchema.some((field) => field.key === "applyToCodeBlocks"));
|
||||
assert.ok(aggressiveSchema.some((field) => field.key === "maxTokensPerMessage"));
|
||||
assert.ok(ultraSchema.some((field) => field.key === "compressionRate"));
|
||||
assert.equal(cavemanEngine.validateConfig({ intensity: "full" }).valid, true);
|
||||
assert.equal(cavemanEngine.validateConfig({ intensity: "bad" }).valid, false);
|
||||
assert.equal(realRtkEngine.validateConfig({ maxLinesPerResult: 20 }).valid, true);
|
||||
assert.equal(aggressiveEngine.validateConfig({ maxTokensPerMessage: 2048 }).valid, true);
|
||||
assert.equal(aggressiveEngine.validateConfig({ maxTokensPerMessage: 10 }).valid, false);
|
||||
assert.equal(ultraEngine.validateConfig({ compressionRate: 0.4 }).valid, true);
|
||||
assert.equal(ultraEngine.validateConfig({ compressionRate: 4 }).valid, false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user