Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
- **dashboard:** fix the **Add Provider / onboarding wizard** button silently doing nothing. The `/dashboard/providers/new` route was a redirect stub (it bounced straight back to `/dashboard/providers`), so every "Add Provider" button and dashboard widget link opened nothing, and the fully-built `ProviderOnboardingWizard` component stayed orphaned (never rendered by any route). The route now renders the wizard directly; auth is enforced centrally by the `(dashboard)` layout, same as the sibling provider routes. Regression guard in `tests/unit/onboarding-wizard-route-5427.test.ts`. ([#5427](https://github.com/diegosouzapw/OmniRoute/issues/5427))
- **db (import):** fix `EBUSY: resource busy or locked` when importing a database on **Windows**. The import route deleted the live `storage.sqlite` + WAL/`-shm`/`-journal` sidecars with a plain `fs.unlinkSync` immediately after `resetDbInstance()`, but Windows releases the SQLite file handle asynchronously after `close()` (mmap / antivirus), so the unlink raced and threw `EBUSY`. The route now deletes via `unlinkFileWithRetry` (EBUSY/EPERM backoff) — the same helper the restore path already uses. Regression guard in `tests/unit/db-import-ebusy-5406.test.ts`. ([#5406](https://github.com/diegosouzapw/OmniRoute/issues/5406), consolidated under [#5161](https://github.com/diegosouzapw/OmniRoute/issues/5161))

### ✨ New Features

- **compression (caveman):** add a **Chinese (zh / wenyan 文言) input-side rule pack** — the counterpart of the existing output-side `terse-cjk` style. New `rules/zh/{dedup,filler,ultra}.json` collapse repeated context ("如前所述" → "见上。"), drop pleasantries/hedging ("请帮我…/谢谢/我觉得"), strip sentence-final modal particles ("吗/呢/吧"), and abbreviate dense technical terms ("数据库"→"DB", "应用程序"→"app"). Chinese is now auto-detected: `detectCompressionLanguage` distinguishes zh from ja by Han-without-kana (kana is Japanese-exclusive, so a Han-heavy Japanese sentence still resolves to `ja`), and `zh` is listed in `listSupportedCompressionLanguages`. Patterns are ReDoS-safe (bounded literal alternations, no `\b` since CJK has no word boundaries). Regression guard: `tests/unit/caveman-packs-zh-wenyan.test.ts` (packs load + validate + shrink; zh/ja/non-CJK detection). gaps v3.8.42 — T05/C6.

---

## [3.8.41] — 2026-06-29
Expand Down
12 changes: 11 additions & 1 deletion open-sse/services/compression/languageDetector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ const LANGUAGE_HINTS: Record<string, RegExp[]> = {
* zero hits → English. (B-LANG-DETECTOR)
*/
export function detectCompressionLanguage(text: string): string {
// CJK disambiguation: Han ideographs (U+4E00–U+9FFF) are shared by Chinese and Japanese, but
// kana (U+3040–U+30FF) is Japanese-exclusive. Text with Han and no kana is Chinese (zh); text
// with kana falls through to the scorer below, where the `ja` kana hint catches it. Keeping zh
// out of the additive scorer means a Han-heavy Japanese sentence is never misread as Chinese.
if (/[一-鿿]/.test(text) && !/[぀-ヿ]/.test(text)) {
return "zh";
}

let best = "en";
let bestScore = 0;
for (const [language, patterns] of Object.entries(LANGUAGE_HINTS)) {
Expand All @@ -37,5 +45,7 @@ export function detectCompressionLanguage(text: string): string {
}

export function listSupportedCompressionLanguages(): string[] {
return ["en", ...Object.keys(LANGUAGE_HINTS)];
// zh is detected via the CJK Han/kana disambiguation in detectCompressionLanguage rather than a
// keyword hint (so it stays out of the additive scorer), hence it is listed explicitly here.
return ["en", "zh", ...Object.keys(LANGUAGE_HINTS)];
}
38 changes: 38 additions & 0 deletions open-sse/services/compression/rules/zh/dedup.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"language": "zh",
"category": "dedup",
"rules": [
{
"name": "zh_repeated_context",
"pattern": "(?:如前所述|如之前所说|如先前提到|正如我之前所说)",
"replacement": "见上。",
"context": "all",
"category": "dedup",
"minIntensity": "lite"
},
{
"name": "zh_repeated_question",
"pattern": "(?:和之前一样的问题|我之前问过这个问题|这是同一个问题)",
"replacement": "[同问]",
"context": "user",
"category": "dedup",
"minIntensity": "lite"
},
{
"name": "zh_reestablished_context",
"pattern": "(?:回到上面的代码|关于之前提到的内容|回到之前的话题)",
"replacement": "Re: ",
"context": "all",
"category": "dedup",
"minIntensity": "lite"
},
{
"name": "zh_summary_replacement",
"pattern": "(?:总结一下我们讨论的内容|总结我们的对话|概括来说)",
"replacement": "总结:",
"context": "assistant",
"category": "dedup",
"minIntensity": "lite"
}
]
}
54 changes: 54 additions & 0 deletions open-sse/services/compression/rules/zh/filler.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"language": "zh",
"category": "filler",
"rules": [
{
"name": "zh_filler_please",
"pattern": "请(?:你|您)?(?:帮我|帮忙)?",
"replacement": "",
"context": "user",
"category": "filler",
"minIntensity": "lite"
},
{
"name": "zh_filler_thanks",
"pattern": "(?:谢谢|多谢|感谢)(?:你|您|大家)?[。,!]?",
"replacement": "",
"context": "all",
"category": "filler",
"minIntensity": "lite"
},
{
"name": "zh_filler_trouble",
"pattern": "(?:麻烦你|麻烦您|劳驾)[,]?",
"replacement": "",
"context": "user",
"category": "filler",
"minIntensity": "lite"
},
{
"name": "zh_filler_greeting",
"pattern": "(?:你好|您好|大家好)[,,]?",
"replacement": "",
"context": "all",
"category": "filler",
"minIntensity": "lite"
},
{
"name": "zh_filler_hedge_think",
"pattern": "(?:我觉得|我认为|我想说)",
"replacement": "",
"context": "all",
"category": "filler",
"minIntensity": "full"
},
{
"name": "zh_filler_hedge_actually",
"pattern": "(?:其实|说实话|基本上)[,,]?",
"replacement": "",
"context": "all",
"category": "filler",
"minIntensity": "full"
}
]
}
55 changes: 55 additions & 0 deletions open-sse/services/compression/rules/zh/ultra.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"language": "zh",
"category": "ultra",
"rules": [
{
"name": "zh_ultra_modal_particles",
"pattern": "(?:吗|呢|吧|啊|呀|嘛)(?=[。,!?、\\s]|$)",
"flags": "g",
"replacement": "",
"context": "all",
"category": "terse",
"minIntensity": "full"
},
{
"name": "zh_ultra_database_abbreviation",
"pattern": "数据库",
"replacement": "DB",
"context": "all",
"category": "ultra",
"minIntensity": "ultra"
},
{
"name": "zh_ultra_application_abbreviation",
"pattern": "应用程序",
"replacement": "app",
"context": "all",
"category": "ultra",
"minIntensity": "ultra"
},
{
"name": "zh_ultra_dependency_abbreviation",
"pattern": "依赖关系",
"replacement": "dep",
"context": "all",
"category": "ultra",
"minIntensity": "ultra"
},
{
"name": "zh_ultra_config_file_abbreviation",
"pattern": "配置文件",
"replacement": "cfg",
"context": "all",
"category": "ultra",
"minIntensity": "ultra"
},
{
"name": "zh_ultra_function_abbreviation",
"pattern": "函数",
"replacement": "fn",
"context": "all",
"category": "ultra",
"minIntensity": "ultra"
}
]
}
69 changes: 69 additions & 0 deletions tests/unit/caveman-packs-zh-wenyan.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import test from "node:test";
import assert from "node:assert/strict";
import {
loadRulePack,
loadAllRulesForLanguage,
} from "../../open-sse/services/compression/ruleLoader.ts";
import {
detectCompressionLanguage,
listSupportedCompressionLanguages,
} from "../../open-sse/services/compression/languageDetector.ts";

// T05 / C6 — Chinese (zh / wenyan) caveman pack: the input-side counterpart of the existing
// output-side "terse-cjk" (文言) style. Adds dedup + filler + ultra rule packs and zh detection.

type LoadedRule = ReturnType<typeof loadRulePack>[number];

function applyAll(text: string, rules: LoadedRule[]): string {
return rules.reduce((acc, rule) => {
return typeof rule.replacement === "function"
? acc.replace(rule.pattern, rule.replacement)
: acc.replace(rule.pattern, rule.replacement);
}, text);
}

test("zh: dedup + filler + ultra packs load and validate", () => {
// loadRulePack throws on validateRulePack failure, so a non-throwing non-empty load proves
// both presence and schema validity.
for (const category of ["dedup", "filler", "ultra"]) {
const rules = loadRulePack("zh", category, { refresh: true });
assert.ok(rules.length > 0, `zh/${category} should have rules`);
}
});

test("zh: the pack shrinks a representative sample (classical terseness)", () => {
const all = loadAllRulesForLanguage("zh", { refresh: true });
const sample = "请帮我修复数据库的错误,谢谢。如前所述,应用程序需要修复。";
const out = applyAll(sample, all);
assert.ok(
out.length < sample.length,
`zh pack should shrink the sample (${sample.length} -> ${out.length})`
);
});

test("zh: loadAllRulesForLanguage exposes dedup, filler and ultra categories", () => {
const cats = new Set(loadAllRulesForLanguage("zh", { refresh: true }).map((r) => r.category));
assert.ok(cats.has("dedup"), "zh should expose dedup");
assert.ok(cats.has("filler"), "zh should expose filler");
assert.ok(cats.has("ultra"), "zh should expose ultra");
});

test("detectCompressionLanguage: Han without kana → zh", () => {
assert.equal(detectCompressionLanguage("请帮我修复这个文件的错误"), "zh");
assert.equal(detectCompressionLanguage("这个应用程序的数据库配置有问题"), "zh");
});

test("detectCompressionLanguage: kana still wins for Japanese (no zh regression)", () => {
// Han-heavy Japanese with kana must stay ja, not flip to zh.
assert.equal(detectCompressionLanguage("このコードを修正してください"), "ja");
assert.equal(detectCompressionLanguage("実装の認証を修正する必要があります"), "ja");
});

test("detectCompressionLanguage: non-CJK languages are unaffected", () => {
assert.equal(detectCompressionLanguage("necesito corregir este archivo con error"), "es");
assert.equal(detectCompressionLanguage("ich brauche diese konfiguration"), "de");
});

test("zh is reported as a supported compression language", () => {
assert.ok(listSupportedCompressionLanguages().includes("zh"));
});
Loading