mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
committed by
GitHub
parent
9a03840adf
commit
ccbfca84dd
@@ -14,7 +14,7 @@
|
||||
|
||||
- **mcp (RTK):** expose the RTK tool-output **learn/discover** workflow as two new MCP tools so an agent can grow the RTK filter catalog without leaving the protocol. `omniroute_rtk_discover` analyzes recently captured raw tool output (`discoverRepeatedNoise` / `suggestFilter`) and returns candidate noise patterns plus a suggested filter; `omniroute_rtk_learn` lists the captured command samples (`listRtkCommandSamples`) and resolves a command to its RTK filter id (`commandToId`). Both are read-only (scope `read:compression`), wrap the existing RTK discovery primitives (no new logic in the engine), and log to the MCP audit trail. Regression guard: `tests/unit/compression/rtk-mcp-tools.test.ts` (4). gaps v3.8.42 — T07.
|
||||
|
||||
- **compression (LLM tier):** add an **opt-in, default-off LLM-tier compression engine** (`llm`) that condenses the prose of non-system messages via a pluggable chat-completion backend. It mirrors the `llmlingua` engine's contract but is **safe by construction**: the default backend is a **no-op pass-through** (the engine never mutates the payload until an operator both enables it *and* wires a real backend via `setLlmCompressorBackend()`), it is **not** part of the default stacked pipeline, `enabled` defaults to `false`, fenced code blocks and `system` messages are never sent to the model, and every backend error **fails open** (the original segment/body is kept, never thrown). A `minTokens` floor skips small prompts. The real production backend is intentionally a VPS-validated follow-up (Hard Rule #18), exactly as the `llmlingua` worker backend is gated. New `open-sse/services/compression/engines/llm/index.ts`. Regression guard: `tests/unit/compression/llm-compressor-engine.test.ts` (8). gaps v3.8.42 — T05/C3.
|
||||
- **compression (LLM tier):** add an **opt-in, default-off LLM-tier compression engine** (`llm`) that condenses the prose of non-system messages via a pluggable chat-completion backend. It mirrors the `llmlingua` engine's contract but is **safe by construction**: the default backend is a **no-op pass-through** (the engine never mutates the payload until an operator both enables it _and_ wires a real backend via `setLlmCompressorBackend()`), it is **not** part of the default stacked pipeline, `enabled` defaults to `false`, fenced code blocks and `system` messages are never sent to the model, and every backend error **fails open** (the original segment/body is kept, never thrown). A `minTokens` floor skips small prompts. The real production backend is intentionally a VPS-validated follow-up (Hard Rule #18), exactly as the `llmlingua` worker backend is gated. New `open-sse/services/compression/engines/llm/index.ts`. Regression guard: `tests/unit/compression/llm-compressor-engine.test.ts` (8). gaps v3.8.42 — T05/C3.
|
||||
|
||||
- **memory (typed decay):** add **opt-in typed memory decay** (TV6) so the conversational memory store stops accumulating stale `episodic` noise. Each injected memory now tracks an `access_count` + `last_accessed_at` (always-on, non-destructive telemetry; migration `111_memory_typed_decay`), and an **opt-in, default-off** sweep (`MEMORY_TYPED_DECAY_ENABLED`, default `false`) deletes memories that are past a **per-type TTL** and not immune. Only `episodic` decays by default (30d, env-tunable); `factual`/`procedural`/`semantic` are immune, and any memory accessed `>= 3` times earns access immunity (mirroring "guardrail/convention/decision never decay"). The decay clock re-bases on the last access, so used memories survive. Deletions reuse `deleteMemory` (SQLite + sqlite-vec + Qdrant stay in sync) and fail open; an optional periodic sweep is doubly opt-in (also needs `MEMORY_TYPED_DECAY_SWEEP_INTERVAL>0`). With the flag off nothing is ever deleted (Rule #20 spirit). New `src/lib/memory/typedDecay.ts`. Regression guard: `tests/unit/memory/typed-decay.test.ts` (15). gaps v3.8.42 — T10/TV6.
|
||||
|
||||
@@ -24,12 +24,14 @@
|
||||
|
||||
- **compression (CCR):** the CCR retrieval-feedback (H8) is now **graduated** instead of a binary cliff. Previously a block retrieved `>= 3` times was flagged do-not-compress and everything below that stayed fully compressible. Now each prior retrieval raises a block's **effective `minChars` linearly** (`effectiveMinChars`), so frequently-retrieved content is compressed progressively less; the `>= 3` exclusion is preserved (as `Infinity`). The ramp is controlled by a `retrievalRampFactor` (default `2`, per-combo config or `COMPRESSION_CCR_RETRIEVAL_RAMP_FACTOR`); `1` reproduces the exact legacy binary behavior. Per-`(principal, hash)` isolation is unchanged. Regression guard: `tests/unit/compression/ccr-retrieval-ramp.test.ts` (12); existing CCR suites (51) stay green. gaps v3.8.42 — T08/H8.
|
||||
|
||||
- **compression (cache-aware):** add an **opt-in, default-off usage-observed prefix freeze** (H5). The cache-aware guard previously preserved the system prompt only for providers a *static* heuristic recognized as caching. It now also **learns** which system prompts actually recur: once a system prompt has been observed `>=` a threshold across requests, it is treated as a stable cacheable prefix and preserved from compression **even for providers the static check misses** — recovering prompt-cache hits that a prefix-compressing mode would otherwise bust. Content-addressed by a hash of the system prompt (OpenAI / Claude / Gemini shapes), in-memory + bounded, zero DB/IO; a "freeze" only *preserves* the prefix, so it never mutates a payload. Default OFF (`COMPRESSION_PREFIX_FREEZE_ENABLED`, threshold `_THRESHOLD`); respects the `never` preserve-mode (never freezes). New `open-sse/services/compression/prefixFreeze.ts`, wired into `resolveCacheAwareConfig`. Regression guard: `tests/unit/compression/prefix-freeze.test.ts` (10); 44 existing cache-aware / preserve-mode tests stay green. gaps v3.8.42 — T08/H5.
|
||||
- **compression (cache-aware):** add an **opt-in, default-off usage-observed prefix freeze** (H5). The cache-aware guard previously preserved the system prompt only for providers a _static_ heuristic recognized as caching. It now also **learns** which system prompts actually recur: once a system prompt has been observed `>=` a threshold across requests, it is treated as a stable cacheable prefix and preserved from compression **even for providers the static check misses** — recovering prompt-cache hits that a prefix-compressing mode would otherwise bust. Content-addressed by a hash of the system prompt (OpenAI / Claude / Gemini shapes), in-memory + bounded, zero DB/IO; a "freeze" only _preserves_ the prefix, so it never mutates a payload. Default OFF (`COMPRESSION_PREFIX_FREEZE_ENABLED`, threshold `_THRESHOLD`); respects the `never` preserve-mode (never freezes). New `open-sse/services/compression/prefixFreeze.ts`, wired into `resolveCacheAwareConfig`. Regression guard: `tests/unit/compression/prefix-freeze.test.ts` (10); 44 existing cache-aware / preserve-mode tests stay green. gaps v3.8.42 — T08/H5.
|
||||
|
||||
- **compression (read-lifecycle):** add a new **opt-in, default-off `read-lifecycle` engine** (H7) that collapses **stale/superseded file-Read tool results**. In agentic conversations the same file is Read repeatedly; an earlier Read becomes stale once the same path is **re-read** (superseded by a newer view) or **modified** by a later Write/Edit. The engine replaces those earlier Read results with a short stub — keeping only the current (last, un-superseded) Read intact — recovering the tokens the model no longer needs. Unlike `session-dedup` (identical-content) or `ccr` (reversible markers), this is semantic + **lossy**, so it is opt-in (`enabled` defaults `false`). Conservative by construction: matches only well-known Read/Write tool names, compares exact paths, collapses a Read only when a strictly-later invocation touches the same path, and **fail-opens** on any unexpected shape. Supports both the **Anthropic** (`tool_use`/`tool_result`) and **OpenAI** (`tool_calls` + `role:"tool"`) shapes. New `open-sse/services/compression/engines/readLifecycle/index.ts`. Regression guard: `tests/unit/compression/read-lifecycle.test.ts` (10). gaps v3.8.42 — T08/H7.
|
||||
|
||||
### 🔧 Bug Fixes
|
||||
|
||||
- **providers (DashScope/Alibaba setup link):** the "Get API key" link for the **Alibaba** and **Alibaba (China)** providers pointed at the bare API host (`dashscope-intl.aliyuncs.com` / `dashscope.aliyuncs.com`), which returns **404** in a browser — API hostnames have no homepage. Repointed to the consoles where keys are actually issued: `bailian.console.alibabacloud.com` (international) and `dashscope.console.aliyun.com` (China). Same class as #5572/#5574/#5576; regression guard added to `tests/unit/provider-setup-links-5572.test.ts`. ([#5665](https://github.com/diegosouzapw/OmniRoute/issues/5665))
|
||||
|
||||
- **thinking / runtime-config (module-graph fix):** operator-configured proxy settings that are hydrated at **boot** but read **per-request** were silently ignored in production. Next.js compiles `instrumentation.ts` (boot hydration via `applyRuntimeSettings` / restore hooks) as a **separate webpack module graph** from the app-route / open-sse executors, so a module-local `let _config` singleton is **duplicated** — the boot copy is hydrated but the request path reads a different, un-hydrated copy. Live VPS validation proved the Thinking-Budget hydration ran to completion at boot yet `base.ts` still saw the `passthrough` default (this is why #5312 fix A stayed broken even after the boot-wiring fix). Fixed by backing the singletons with `globalThis` (the pattern `systemPrompt.ts` already uses for the Global System Prompt, #2470), so all module-graph copies share one instance: **`thinkingBudget.ts`** (the dashboard Thinking-Budget mode now reaches the executor), **`backgroundTaskDetector.ts`** (the opt-in background-model degradation now actually fires on requests), and **`systemTransforms.ts`** (operator pipeline overrides now reach the request path). `payloadRules.ts` was already safe (it lazily self-loads from the DB per request, #2986). Regression guards: `tests/unit/thinking-budget-globalthis-5312.test.ts` + `tests/unit/runtime-config-globalthis-5312.test.ts` (assert globalThis-backed sharing; a module-local `let` fails them). ([#5312](https://github.com/diegosouzapw/OmniRoute/issues/5312))
|
||||
|
||||
- **thinking (Claude OAuth):** restore the proxy-level **Thinking-Budget** config on startup. The dashboard mode (`auto`/`custom`/`adaptive`) is persisted under `settings.thinkingBudget`, but the boot-time hydration (`hydrateThinkingBudgetConfig`) was only wired into `src/server-init.ts` — an **unused module that never runs in production** — so the operator's choice silently reverted to the `passthrough` default on every restart (#5312 fix A was non-functional, even though its direct unit test passed). The hydration now runs in the real boot path (`src/instrumentation-node.ts`), alongside the Global System Prompt restore. Surfaced by live Anthropic-OAuth validation on the VPS. Regression guard: `tests/unit/thinking-budget-boot-wiring-5312.test.ts` (asserts the production boot module calls the hydration, not just the function in isolation). ([#5312](https://github.com/diegosouzapw/OmniRoute/issues/5312))
|
||||
|
||||
@@ -115,7 +115,7 @@ export const APIKEY_PROVIDERS_REGIONAL = {
|
||||
icon: "cloud_queue",
|
||||
color: "#FF6600",
|
||||
textIcon: "AL",
|
||||
website: "https://dashscope-intl.aliyuncs.com",
|
||||
website: "https://bailian.console.alibabacloud.com/",
|
||||
hasFree: false,
|
||||
},
|
||||
"alibaba-cn": {
|
||||
@@ -125,7 +125,7 @@ export const APIKEY_PROVIDERS_REGIONAL = {
|
||||
icon: "cloud_queue",
|
||||
color: "#FF6600",
|
||||
textIcon: "AL",
|
||||
website: "https://dashscope.aliyuncs.com",
|
||||
website: "https://dashscope.console.aliyun.com/",
|
||||
hasFree: false,
|
||||
},
|
||||
longcat: {
|
||||
|
||||
@@ -5,9 +5,10 @@ import assert from "node:assert/strict";
|
||||
// This guards the corrected (verified-live) URLs so the dead paths cannot
|
||||
// silently return.
|
||||
const { SEARCH_PROVIDERS } = await import("../../src/shared/constants/providers/search.ts");
|
||||
const { APIKEY_PROVIDERS_INFERENCE } = await import(
|
||||
"../../src/shared/constants/providers/apikey/inference-hosts.ts"
|
||||
);
|
||||
const { APIKEY_PROVIDERS_INFERENCE } =
|
||||
await import("../../src/shared/constants/providers/apikey/inference-hosts.ts");
|
||||
const { APIKEY_PROVIDERS_REGIONAL } =
|
||||
await import("../../src/shared/constants/providers/apikey/regional.ts");
|
||||
|
||||
const websites = () =>
|
||||
[
|
||||
@@ -20,7 +21,7 @@ const websites = () =>
|
||||
test("#5572/#5574/#5576 provider setup links avoid the known-404 paths", () => {
|
||||
const deadPaths = [
|
||||
"ollama.com/settings/api-keys", // #5572 — Ollama key page moved to /settings/keys
|
||||
"searchapi.io/docs\"", // #5574 — bare /docs 404s; use /docs/google
|
||||
'searchapi.io/docs"', // #5574 — bare /docs 404s; use /docs/google
|
||||
"you.com/docs/search/overview", // #5576 — moved to /business/api/
|
||||
];
|
||||
for (const dead of deadPaths) {
|
||||
@@ -38,3 +39,21 @@ test("#5572/#5574/#5576 corrected provider setup links", () => {
|
||||
assert.equal(search["ollama-search"]?.website, "https://ollama.com/settings/keys");
|
||||
assert.equal(inference["ollama-cloud"]?.website, "https://ollama.com/settings/keys");
|
||||
});
|
||||
|
||||
// #5665 — DashScope/Alibaba "Get API key" links pointed at the bare API host
|
||||
// (dashscope[-intl].aliyuncs.com), which returns 404 in a browser. Keys are
|
||||
// issued from the Model Studio / DashScope consoles. Same class as #5572/#5574/#5576.
|
||||
test("#5665 DashScope/Alibaba setup links use the console, not the bare API host", () => {
|
||||
const regional = APIKEY_PROVIDERS_REGIONAL as Record<string, { website?: string }>;
|
||||
const deadHosts = ["dashscope-intl.aliyuncs.com", "dashscope.aliyuncs.com"];
|
||||
for (const dead of deadHosts) {
|
||||
for (const p of Object.values(regional)) {
|
||||
assert.ok(
|
||||
!(typeof p.website === "string" && p.website.includes(dead)),
|
||||
`regional provider link still points at the bare API host: ${p.website}`
|
||||
);
|
||||
}
|
||||
}
|
||||
assert.equal(regional["alibaba"]?.website, "https://bailian.console.alibabacloud.com/");
|
||||
assert.equal(regional["alibaba-cn"]?.website, "https://dashscope.console.aliyun.com/");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user