From ec780ef2dc668dbc24646a1e591595db41c85aeb Mon Sep 17 00:00:00 2001 From: "Bob.Hou" Date: Thu, 17 Sep 2026 17:46:45 -0400 Subject: [PATCH] feat(compression): make Lite tool-result truncation length configurable (#13915) * feat(compression): make Lite tool-result truncation length configurable Lite truncated tool results at a hardcoded 2000 characters. Coding-agent payloads (file reads, crash dumps) lost the middle of the content with no supported way to raise the cap. Honor lite.maxToolLength from settings, then OMNIROUTE_LITE_MAX_TOOL_LENGTH, then 2000. Existing installs keep the old length. Related to #13178. Signed-off-by: Minxi Hou #13178 stays open. * compression/lite: keep a stored cap when a step or toggle write is incomplete An out-of-range step maxToolLength was still a number, so it hid a valid global cap and fell through to env. A toggle-only settings PUT replaced the whole lite row and dropped the stored cap. Save treated an out-of-range number like a cleared field. Reject the bad Save, merge omitted caps, and use null to clear. Related to #13178. Signed-off-by: Minxi Hou * compression/lite: stop dashboard copy from hard-coding a 2000-char cap The page overlays schema descriptions from i18n. Updating only LITE_SCHEMA left operators seeing "over 2,000 characters" after the cap became configurable. Also assert the Save error string, not the Save button. Related to #13178. Signed-off-by: Minxi Hou * chore(changelog): move #13915 entry to a changelog.d fragment --------- Signed-off-by: Minxi Hou Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --- .env.example | 5 + .../features/13915-lite-max-tool-length.md | 1 + docs/reference/ENVIRONMENT.md | 1 + .../compression/engines/cavemanAdapter.ts | 22 +- open-sse/services/compression/lite.ts | 36 +- open-sse/services/compression/types.ts | 4 +- src/i18n/messages/am.json | 6 +- src/i18n/messages/ar.json | 6 +- src/i18n/messages/az.json | 6 +- src/i18n/messages/bg.json | 6 +- src/i18n/messages/bn.json | 6 +- src/i18n/messages/cs.json | 6 +- src/i18n/messages/da.json | 6 +- src/i18n/messages/de.json | 6 +- src/i18n/messages/el.json | 6 +- src/i18n/messages/en.json | 6 +- src/i18n/messages/es.json | 6 +- src/i18n/messages/et.json | 6 +- src/i18n/messages/fa.json | 6 +- src/i18n/messages/fi.json | 6 +- src/i18n/messages/fr.json | 6 +- src/i18n/messages/ga.json | 6 +- src/i18n/messages/gu.json | 6 +- src/i18n/messages/ha.json | 6 +- src/i18n/messages/he.json | 6 +- src/i18n/messages/hi.json | 6 +- src/i18n/messages/hr.json | 6 +- src/i18n/messages/hu.json | 6 +- src/i18n/messages/hy.json | 6 +- src/i18n/messages/id.json | 6 +- src/i18n/messages/ig.json | 6 +- src/i18n/messages/it.json | 6 +- src/i18n/messages/ja.json | 6 +- src/i18n/messages/ka.json | 6 +- src/i18n/messages/km.json | 6 +- src/i18n/messages/kn.json | 6 +- src/i18n/messages/ko.json | 6 +- src/i18n/messages/lt.json | 6 +- src/i18n/messages/lv.json | 6 +- src/i18n/messages/ml.json | 6 +- src/i18n/messages/mr.json | 6 +- src/i18n/messages/ms.json | 6 +- src/i18n/messages/mt.json | 6 +- src/i18n/messages/my.json | 6 +- src/i18n/messages/ne.json | 6 +- src/i18n/messages/nl.json | 6 +- src/i18n/messages/no.json | 6 +- src/i18n/messages/or.json | 6 +- src/i18n/messages/pa.json | 6 +- src/i18n/messages/phi.json | 6 +- src/i18n/messages/pl.json | 6 +- src/i18n/messages/pt-BR.json | 6 +- src/i18n/messages/pt.json | 6 +- src/i18n/messages/ro.json | 6 +- src/i18n/messages/ru.json | 6 +- src/i18n/messages/si.json | 6 +- src/i18n/messages/sk.json | 6 +- src/i18n/messages/sl.json | 6 +- src/i18n/messages/sr.json | 6 +- src/i18n/messages/sv.json | 6 +- src/i18n/messages/sw.json | 6 +- src/i18n/messages/ta.json | 6 +- src/i18n/messages/te.json | 6 +- src/i18n/messages/th.json | 6 +- src/i18n/messages/tr.json | 6 +- src/i18n/messages/uk-UA.json | 6 +- src/i18n/messages/ur.json | 6 +- src/i18n/messages/uz.json | 6 +- src/i18n/messages/vi.json | 6 +- src/i18n/messages/yo.json | 6 +- src/i18n/messages/zh-CN.json | 6 +- src/i18n/messages/zh-TW.json | 6 +- src/lib/db/compression.ts | 54 ++- .../compression/EngineConfigForm.tsx | 16 +- .../compression/EngineConfigPage.tsx | 27 +- .../validation/compressionConfigSchemas.ts | 2 + .../api/compression-engines-route.test.ts | 18 + tests/unit/compression/db.test.ts | 40 ++ .../unit/compression/engine-registry.test.ts | 8 + .../headroom-minrows-persist-8056.test.ts | 28 ++ tests/unit/compression/lite.test.ts | 71 ++++ tests/unit/ui/engineConfigPage.test.tsx | 355 ++++++++++++++++++ 82 files changed, 1004 insertions(+), 80 deletions(-) create mode 100644 changelog.d/features/13915-lite-max-tool-length.md diff --git a/.env.example b/.env.example index ea11129215..c8e2883135 100644 --- a/.env.example +++ b/.env.example @@ -1102,6 +1102,11 @@ PROVIDER_LIMITS_SYNC_SPACING_MS=1500 # Used by: open-sse/services/compression/engines/rtk/filterLoader.ts. Default: 0. #OMNIROUTE_RTK_TRUST_PROJECT_FILTERS=0 +# Character cap for Lite proactive tool-result truncation when lite.maxToolLength +# is unset. Range 256-1000000. Dashboard setting wins over this env. +# Used by: open-sse/services/compression/lite.ts. Default: 2000. +#OMNIROUTE_LITE_MAX_TOOL_LENGTH=2000 + # Maximum concurrent synchronous compression workers. Excess jobs wait FIFO. # Used by: open-sse/services/compression/compressionWorkerPool.ts. Default: 2. #OMNI_COMPRESSION_WORKERS=2 diff --git a/changelog.d/features/13915-lite-max-tool-length.md b/changelog.d/features/13915-lite-max-tool-length.md new file mode 100644 index 0000000000..0622253f1f --- /dev/null +++ b/changelog.d/features/13915-lite-max-tool-length.md @@ -0,0 +1 @@ +- **feat(compression):** Lite tool-result truncation length is configurable (`lite.maxToolLength`, env `OMNIROUTE_LITE_MAX_TOOL_LENGTH`). Default stays 2000. An out-of-range step cap no longer hides a valid global cap; a toggle-only settings write keeps a stored cap; `maxToolLength: null` clears it. Dashboard copy no longer hard-codes 2,000 characters. ([#13915](https://github.com/diegosouzapw/OmniRoute/pull/13915) — refs [#13178](https://github.com/diegosouzapw/OmniRoute/issues/13178)) diff --git a/docs/reference/ENVIRONMENT.md b/docs/reference/ENVIRONMENT.md index a1897e9de7..c6cf79813e 100644 --- a/docs/reference/ENVIRONMENT.md +++ b/docs/reference/ENVIRONMENT.md @@ -558,6 +558,7 @@ detection above). | `OMNIROUTE_CONFIG_HOT_RELOAD_MS` | `5000` | `src/lib/config/hotReload.ts` | Polling interval (ms) for config hot-reload. Lower than `1000` is rejected. | | `OMNIROUTE_DISABLE_REDIS_AUTH_CACHE` | _(enabled)_ | `src/lib/db/apiKeys.ts` | Set `1` to bypass the Redis-backed API-key auth cache (forces DB reads). | | `OMNIROUTE_RTK_TRUST_PROJECT_FILTERS` | `0` | `open-sse/services/compression/engines/rtk/filterLoader.ts` | Trust user-managed RTK project filter rules without strict signature checks. | +| `OMNIROUTE_LITE_MAX_TOOL_LENGTH` | `2000` | `open-sse/services/compression/lite.ts` | Character cap for Lite proactive tool-result truncation when `lite.maxToolLength` is unset. Range 256–1000000. Dashboard setting wins over this env. | | `OMNI_COMPRESSION_WORKERS` | `2` | `open-sse/services/compression/compressionWorkerPool.ts` | Maximum concurrent synchronous RTK/Caveman workers; excess jobs wait FIFO. | | `OMNI_COMPRESSION_WORKER_TIMEOUT_MS` | `120000` | `open-sse/services/compression/compressionWorkerPool.ts` | Per-job timeout in milliseconds. Timed-out workers are terminated and the request fails open unchanged. | | `OMNI_COMPRESSION_WORKER_IDLE_MS` | `60000` | `open-sse/services/compression/compressionWorkerPool.ts` | Idle lifetime in milliseconds before an unused compression worker is terminated. | diff --git a/open-sse/services/compression/engines/cavemanAdapter.ts b/open-sse/services/compression/engines/cavemanAdapter.ts index d07e0b0c91..3b0cd4e116 100644 --- a/open-sse/services/compression/engines/cavemanAdapter.ts +++ b/open-sse/services/compression/engines/cavemanAdapter.ts @@ -1,4 +1,4 @@ -import { applyLiteCompression } from "../lite.ts"; +import { applyLiteCompression, isUsableLiteMaxToolLength } from "../lite.ts"; import { cavemanCompress } from "../caveman.ts"; import { compressAggressive } from "../aggressive.ts"; import { ultraCompressHeuristic } from "../ultra.ts"; @@ -226,9 +226,19 @@ const LITE_SCHEMA: EngineConfigField[] = [ type: "boolean", label: "Proactively truncate long tool results", description: - "Truncates tool results over 2,000 characters during Lite compression. Emergency overflow protection may still trim content when the context exceeds the model budget.", + "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget.", defaultValue: true, }, + { + key: "maxToolLength", + type: "number", + label: "Maximum tool-result length", + description: + "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset.", + defaultValue: 2000, + min: 256, + max: 1_000_000, + }, ]; function validateLiteConfig(config: Record): EngineValidationResult { @@ -240,6 +250,7 @@ function validateLiteConfig(config: Record): EngineValidationRe errors.push("preserveSystemPrompt must be a boolean"); } validateBoolean(config, "compressToolResults", errors); + validateNumberRange(config, "maxToolLength", 256, 1_000_000, errors); return { valid: errors.length === 0, errors }; } @@ -267,6 +278,8 @@ export const liteEngine: CompressionEngine = { // to global config.lite, then the default (keeps the type `boolean`, and a malformed // step value can no longer leak through the `??` chain as `{}`). const stepCompressToolResults = options?.stepConfig?.compressToolResults; + const stepMaxToolLength = options?.stepConfig?.maxToolLength; + const configMaxToolLength = options?.config?.lite?.maxToolLength; const result = applyLiteCompression(adapter.body, { ...options, preserveSystemPrompt: options?.config?.preserveSystemPrompt !== false, @@ -277,6 +290,11 @@ export const liteEngine: CompressionEngine = { typeof stepCompressToolResults === "boolean" ? stepCompressToolResults : (options?.config?.lite?.compressToolResults ?? true), + maxToolLength: isUsableLiteMaxToolLength(stepMaxToolLength) + ? Math.floor(stepMaxToolLength) + : isUsableLiteMaxToolLength(configMaxToolLength) + ? Math.floor(configMaxToolLength) + : undefined, }); return adapter.adapted ? { ...result, body: adapter.restore(result.body) } : result; }, diff --git a/open-sse/services/compression/lite.ts b/open-sse/services/compression/lite.ts index 644fd300a8..e4f270339d 100644 --- a/open-sse/services/compression/lite.ts +++ b/open-sse/services/compression/lite.ts @@ -18,6 +18,33 @@ interface LiteCompressionOptions { supportsVision?: boolean | null; preserveSystemPrompt?: boolean; compressToolResults?: boolean; + maxToolLength?: number; +} + +const DEFAULT_MAX_TOOL_LENGTH = 2000; +const MIN_MAX_TOOL_LENGTH = 256; +const MAX_MAX_TOOL_LENGTH = 1_000_000; + +function envInt(name: string, fallback: number): number { + const raw = process.env[name]; + if (raw === undefined || raw === null || raw === "") return fallback; + const parsed = Number(raw); + if (!Number.isFinite(parsed)) return fallback; + const n = Math.floor(parsed); + if (n < MIN_MAX_TOOL_LENGTH || n > MAX_MAX_TOOL_LENGTH) return fallback; + return n; +} + +/** True when a value can be used as a Lite tool-result cap (not merely `typeof number`). */ +export function isUsableLiteMaxToolLength(value: unknown): value is number { + if (typeof value !== "number" || !Number.isFinite(value)) return false; + const n = Math.floor(value); + return n >= MIN_MAX_TOOL_LENGTH && n <= MAX_MAX_TOOL_LENGTH; +} + +export function resolveLiteMaxToolLength(maxToolLength?: number): number { + if (isUsableLiteMaxToolLength(maxToolLength)) return Math.floor(maxToolLength); + return envInt("OMNIROUTE_LITE_MAX_TOOL_LENGTH", DEFAULT_MAX_TOOL_LENGTH); } function normalizeMessageWhitespace(content: string): string { @@ -118,12 +145,15 @@ function backOffToWordBoundary(content: string, cutIndex: number): number { return cutIndex; } -export function compressToolResults(body: ChatBody): { +export function compressToolResults( + body: ChatBody, + options: LiteCompressionOptions = {} +): { body: ChatBody; applied: boolean; } { if (!body.messages) return { body, applied: false }; - const MAX_TOOL_LENGTH = 2000; + const MAX_TOOL_LENGTH = resolveLiteMaxToolLength(options.maxToolLength); let applied = false; const messages = body.messages.map((msg) => { if (msg.role !== "tool" || typeof msg.content !== "string") return msg; @@ -227,7 +257,7 @@ export function applyLiteCompression( if (r2.applied) techniquesApplied.push("system-dedup"); if (options?.compressToolResults !== false) { - const r3 = compressToolResults(current); + const r3 = compressToolResults(current, options); current = r3.body; if (r3.applied) techniquesApplied.push("tool-compress"); } diff --git a/open-sse/services/compression/types.ts b/open-sse/services/compression/types.ts index 3af8dc5a87..c7c68033dc 100644 --- a/open-sse/services/compression/types.ts +++ b/open-sse/services/compression/types.ts @@ -180,7 +180,9 @@ export interface OmniglyphConfig { /** Lite detail settings for proactive request-time transformations. */ export interface LiteConfig { - /** Truncate tool-result strings over 2,000 characters before provider dispatch. */ + /** Truncate tool-result strings over this many characters before provider dispatch. */ + maxToolLength?: number; + /** When false, skip proactive tool-result truncation. */ compressToolResults: boolean; } diff --git a/src/i18n/messages/am.json b/src/i18n/messages/am.json index 20b07658d7..cbbfd682c3 100644 --- a/src/i18n/messages/am.json +++ b/src/i18n/messages/am.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "ረጅም የመሣሪያ ውጤቶችን አስቀድሞ ቀንስ", - "description": "በቀላል ጭመቃ ጊዜ ከ2,000 ቁምፊዎች በላይ የሆኑ የመሣሪያ ውጤቶችን ይቀንሳል። ዐውዱ ከሞዴሉ በጀት ሲያልፍ የአደጋ ጊዜ የመጠን ማለፍ መከላከያው አሁንም ይዘትን ሊቀንስ ይችላል።" + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index cd12ea084e..f565fdffb7 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "قم بقطع نتائج الأدوات الطويلة بشكل استباقي", - "description": "يقطع نتائج الأداة التي تتجاوز 2000 حرف أثناء ضغط Lite. قد تقوم حماية الفائض الطارئة بتقليص المحتوى عندما يتجاوز السياق ميزانية النموذج." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/az.json b/src/i18n/messages/az.json index 5b9d5dded3..555f4562f5 100644 --- a/src/i18n/messages/az.json +++ b/src/i18n/messages/az.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Uzun alət nəticələrini proaktiv şəkildə qısaldın", - "description": "Lite sıxma zamanı 2,000 simvoldan çox olan alət nəticələrini qısaldır. Fövqəladə daşma qorunması kontekst model büdcəsini aşdıqda hələ də məzmunu qısalda bilər." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/bg.json b/src/i18n/messages/bg.json index f6a672f905..24feb4b346 100644 --- a/src/i18n/messages/bg.json +++ b/src/i18n/messages/bg.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Проактивно съкратете дългите резултати от инструменти", - "description": "Обрязва резултатите от инструмента над 2,000 знака по време на Lite компресия. Защитата от извънреден преливник все още може да отреже съдържание, когато контекстът надвишава бюджета на модела." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/bn.json b/src/i18n/messages/bn.json index 8e881c9ee6..d48cc176ee 100644 --- a/src/i18n/messages/bn.json +++ b/src/i18n/messages/bn.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "সক্রিয়ভাবে দীর্ঘ টুলের ফলাফল কেটে ফেলুন", - "description": "লাইট সংকোচনের সময় ২,০০০ অক্ষরের বেশি টুলের ফলাফল কেটে ফেলা হয়। জরুরি ওভারফ্লো সুরক্ষা এখনও কনটেন্ট কেটে ফেলতে পারে যখন প্রসঙ্গ মডেল বাজেট অতিক্রম করে।" + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/cs.json b/src/i18n/messages/cs.json index 7e38854b5f..7335b53e64 100644 --- a/src/i18n/messages/cs.json +++ b/src/i18n/messages/cs.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktivně zkracujte dlouhé výsledky nástrojů", - "description": "Zkracuje výsledky nástroje na více než 2 000 znaků během Lite komprese. Ochrana proti přetečení v nouzi může stále oříznout obsah, když kontext překročí rozpočet modelu." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/da.json b/src/i18n/messages/da.json index 844b3f7e43..69733164bb 100644 --- a/src/i18n/messages/da.json +++ b/src/i18n/messages/da.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktivt afkort lange værktøjsresultater", - "description": "Afkorter værktøjsresultater over 2.000 tegn under Lite-komprimering. Nødoverløbsbeskyttelse kan stadig trimme indhold, når konteksten overskrider modelbudgettet." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 92199483a0..622ba90021 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktiv lange Tool-Ergebnisse kürzen", - "description": "Trimmt die Toolergebnisse über 2.000 Zeichen während der Lite-Kompression. Der Notüberlauf-Schutz kann den Inhalt weiterhin kürzen, wenn der Kontext das Modellbudget überschreitet." + "description": "Kürzt lange Tool-Ergebnisse während der Lite-Kompression. Die Obergrenze setzt das Feld „Maximale Tool-Ergebnis-Länge“ (oder OMNIROUTE_LITE_MAX_TOOL_LENGTH, wenn das Feld leer ist). Der Notüberlauf-Schutz kann den Inhalt weiter kürzen, wenn der Kontext das Modellbudget überschreitet." + }, + "maxToolLength": { + "label": "Maximale Tool-Ergebnis-Länge", + "description": "Zeichenobergrenze für die vorausschauende Kürzung von Tool-Ergebnissen. Standard 2000. Überschreiben mit OMNIROUTE_LITE_MAX_TOOL_LENGTH, wenn das Feld leer ist." } }, "engineFields": { diff --git a/src/i18n/messages/el.json b/src/i18n/messages/el.json index a5065de386..8d83d7282e 100644 --- a/src/i18n/messages/el.json +++ b/src/i18n/messages/el.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Προληπτική περικοπή μακρών αποτελεσμάτων εργαλείων", - "description": "Περικόπτει αποτελέσματα εργαλείων άνω των 2.000 χαρακτήρων κατά τη συμπίεση Lite. Η έκτακτη προστασία υπερχείλισης ενδέχεται να περικόψει περαιτέρω περιεχόμενο όταν το πλαίσιο υπερβαίνει τον προϋπολογισμό του μοντέλου." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/en.json b/src/i18n/messages/en.json index f14bcb121d..18a937ffdf 100644 --- a/src/i18n/messages/en.json +++ b/src/i18n/messages/en.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proactively truncate long tool results", - "description": "Truncates tool results over 2,000 characters during Lite compression. Emergency overflow protection may still trim content when the context exceeds the model budget." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/es.json b/src/i18n/messages/es.json index 7e29d330bf..89515102df 100644 --- a/src/i18n/messages/es.json +++ b/src/i18n/messages/es.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Truncar proactivamente los resultados largos de la herramienta", - "description": "Trunca los resultados de la herramienta a más de 2,000 caracteres durante la compresión Lite. La protección de desbordamiento de emergencia aún puede recortar contenido cuando el contexto excede el presupuesto del modelo." + "description": "Trunca resultados de herramienta largos durante la compresión Lite. El tope lo fija el campo Longitud máxima del resultado de herramienta (o OMNIROUTE_LITE_MAX_TOOL_LENGTH si está vacío). La protección de desbordamiento de emergencia aún puede recortar contenido cuando el contexto excede el presupuesto del modelo." + }, + "maxToolLength": { + "label": "Longitud máxima del resultado de herramienta", + "description": "Tope de caracteres para el recorte proactivo. Predeterminado 2000. Se puede sustituir con OMNIROUTE_LITE_MAX_TOOL_LENGTH si el campo está vacío." } }, "engineFields": { diff --git a/src/i18n/messages/et.json b/src/i18n/messages/et.json index b745f32b4b..78b3f977fb 100644 --- a/src/i18n/messages/et.json +++ b/src/i18n/messages/et.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Kärbi pikad tööriistatulemused ennetavalt", - "description": "Kärbib Lite-tihendamise ajal üle 2000 tähemärgi pikkuseid tööriistatulemusi. Konteksti mudeli mahu ületamisel võib hädaolukorra ületäitumiskaitse sisu siiski kärpida." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/fa.json b/src/i18n/messages/fa.json index 64fd8caf91..b3ae8074c1 100644 --- a/src/i18n/messages/fa.json +++ b/src/i18n/messages/fa.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "به‌طور پیش‌گیرانه نتایج طولانی ابزار را کوتاه کنید", - "description": "نتایج ابزار را در طول فشرده‌سازی Lite بیش از ۲,۰۰۰ کاراکتر کوتاه می‌کند. حفاظت از سرریز اضطراری ممکن است هنوز محتوا را زمانی که زمینه از بودجه مدل فراتر رود، کوتاه کند." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/fi.json b/src/i18n/messages/fi.json index 1716731fca..a672d91117 100644 --- a/src/i18n/messages/fi.json +++ b/src/i18n/messages/fi.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktiivisesti lyhennä pitkiä työkalutuloksia", - "description": "Lyhentää työkalun tuloksia yli 2 000 merkkiä Lite-puristuksen aikana. Hätätilanteen ylivuotosuojaus voi silti leikata sisältöä, kun konteksti ylittää mallin budjetin." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 7fb68c6b46..39c6894543 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Tronquer proactivement les longs résultats d'outils", - "description": "Tronque les résultats des outils à plus de 2 000 caractères lors de la compression Lite. La protection d'urgence contre les débordements peut toujours réduire le contenu lorsque le contexte dépasse le budget du modèle." + "description": "Tronque les longs résultats d'outils pendant la compression Lite. Le plafond est défini par le champ Longueur maximale des résultats d'outil (ou OMNIROUTE_LITE_MAX_TOOL_LENGTH s'il n'est pas renseigné). La protection d'urgence contre le débordement peut encore couper le contenu lorsque le contexte dépasse le budget du modèle." + }, + "maxToolLength": { + "label": "Longueur maximale des résultats d'outil", + "description": "Plafond de caractères pour la troncature proactive des résultats d'outil. Défaut 2000. Surcharge via OMNIROUTE_LITE_MAX_TOOL_LENGTH si le champ est vide." } }, "engineFields": { diff --git a/src/i18n/messages/ga.json b/src/i18n/messages/ga.json index 3d6ad8adbc..285ba8d17d 100644 --- a/src/i18n/messages/ga.json +++ b/src/i18n/messages/ga.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Bearr torthaí fada uirlisí go réamhghníomhach", - "description": "Gearrann sé torthaí uirlisí os cionn 2,000 carachtar le linn comhbhrú Éadrom. Féadfaidh cosaint éigeandála ró-shreafa fós bearradh a dhéanamh ar ábhar nuair a sháraíonn an comhthéacs buiséad an mhúnla." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/gu.json b/src/i18n/messages/gu.json index 03adc49bae..5de8f4da72 100644 --- a/src/i18n/messages/gu.json +++ b/src/i18n/messages/gu.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "પ્રતિસાદી રીતે લાંબા ટૂલ પરિણામોને ટૂંકા કરો", - "description": "લાઇટ સંકોચન દરમિયાન 2,000 અક્ષરોની ઉપર ટૂલ પરિણામોને કાપે છે. તાત્કાલિક ઓવરફ્લો સુરક્ષા ત્યારે પણ સામગ્રીને કાપી શકે છે જ્યારે સંદર્ભ મોડેલ બજેટને પાર કરે છે." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/ha.json b/src/i18n/messages/ha.json index b86d4ba375..af1a132542 100644 --- a/src/i18n/messages/ha.json +++ b/src/i18n/messages/ha.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Datse dogayen sakamakon kayan aiki tun da wuri", - "description": "Yana datse sakamakon kayan aiki da ya wuce haruffa 2,000 yayin matsawar Mai Sauƙi. Kariyar gaggawa daga cikar mahalli na iya ci gaba da rage abun ciki idan mahallin ya wuce kasafin samfurin." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/he.json b/src/i18n/messages/he.json index 9b94566f83..e0025d3932 100644 --- a/src/i18n/messages/he.json +++ b/src/i18n/messages/he.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "בצורה פרואקטיבית לקצר תוצאות כלי ארוכות", - "description": "מקטין את תוצאות הכלים מעל 2,000 תווים במהלך דחיסת Lite. הגנה על עודף חירום עשויה עדיין לקצץ תוכן כאשר ההקשר חורג מתקציב המודל." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/hi.json b/src/i18n/messages/hi.json index 1dc209309f..8287ff889d 100644 --- a/src/i18n/messages/hi.json +++ b/src/i18n/messages/hi.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "सक्रिय रूप से लंबे टूल परिणामों को संक्षिप्त करें", - "description": "Lite संकुचन के दौरान 2,000 वर्णों से अधिक के उपकरण परिणामों को काटता है। आपातकालीन ओवरफ्लो सुरक्षा तब भी सामग्री को काट सकती है जब संदर्भ मॉडल बजट से अधिक हो जाता है।" + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/hr.json b/src/i18n/messages/hr.json index 10dbe9dfaf..f59cdd6f9d 100644 --- a/src/i18n/messages/hr.json +++ b/src/i18n/messages/hr.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktivno skraćivanje dugih rezultata alata", - "description": "Skraćuje rezultate alata dulje od 2 000 znakova tijekom Lite kompresije. Zaštita od hitnog prelijevanja može i dalje smanjiti sadržaj kada kontekst premaši proračun modela." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/hu.json b/src/i18n/messages/hu.json index 89e0c89d20..63ce266d7e 100644 --- a/src/i18n/messages/hu.json +++ b/src/i18n/messages/hu.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktívan vágja le a hosszú eszközeredményeket", - "description": "A Lite tömörítés során a szerszám eredményeit 2,000 karakter felett levágja. Vészhelyzeti túlfolyás elleni védelem esetén a tartalom még mindig csökkenhet, ha a kontextus meghaladja a modell költségvetését." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/hy.json b/src/i18n/messages/hy.json index 5397903138..256191f103 100644 --- a/src/i18n/messages/hy.json +++ b/src/i18n/messages/hy.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Կանխարգելիչ կերպով կրճատել գործիքների երկար արդյունքները", - "description": "Թեթև սեղմման ընթացքում կրճատում է 2,000 նիշը գերազանցող գործիքների արդյունքները։ Գերբեռնվածությունից արտակարգ պաշտպանությունը կարող է նաև կրճատել բովանդակությունը, երբ համատեքստը գերազանցում է մոդելի սահմանաչափը։" + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/id.json b/src/i18n/messages/id.json index e5a6753af4..e5cfec94c2 100644 --- a/src/i18n/messages/id.json +++ b/src/i18n/messages/id.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Secara proaktif memotong hasil alat yang panjang", - "description": "Memotong hasil alat lebih dari 2.000 karakter selama kompresi Lite. Perlindungan overflow darurat mungkin masih memangkas konten ketika konteks melebihi anggaran model." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/ig.json b/src/i18n/messages/ig.json index 875420600d..1fc5839dc2 100644 --- a/src/i18n/messages/ig.json +++ b/src/i18n/messages/ig.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Buru ụzọ belata nsonaazụ ngwaọrụ ndị toro ogologo", - "description": "Na-ebelata nsonaazụ ngwaọrụ karịrị mkpụrụedemede 2,000 n'oge mkpokọta Lite. Nchedo mberede megide njupụta ka nwere ike ibelata ọdịnaya mgbe konteks gafere oke model." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/it.json b/src/i18n/messages/it.json index 751f8e5765..1f5244dbcf 100644 --- a/src/i18n/messages/it.json +++ b/src/i18n/messages/it.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Truncare proattivamente i risultati degli strumenti lunghi", - "description": "Tronca i risultati degli strumenti oltre 2.000 caratteri durante la compressione Lite. La protezione di overflow di emergenza potrebbe comunque ridurre il contenuto quando il contesto supera il budget del modello." + "description": "Tronca i risultati lunghi degli strumenti durante la compressione Lite. Il tetto è impostato dal campo Lunghezza massima del risultato strumento (o OMNIROUTE_LITE_MAX_TOOL_LENGTH se vuoto). La protezione di overflow di emergenza può comunque tagliare il contenuto quando il contesto supera il budget del modello." + }, + "maxToolLength": { + "label": "Lunghezza massima del risultato strumento", + "description": "Limite di caratteri per il troncamento proattivo. Predefinito 2000. Sovrascrivibile con OMNIROUTE_LITE_MAX_TOOL_LENGTH se il campo è vuoto." } }, "engineFields": { diff --git a/src/i18n/messages/ja.json b/src/i18n/messages/ja.json index a9b9fc3cba..a98deb8c63 100644 --- a/src/i18n/messages/ja.json +++ b/src/i18n/messages/ja.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "長いツールの結果を積極的に切り詰める", - "description": "Lite圧縮中に2,000文字を超えるツール結果を切り捨てます。コンテキストがモデルの予算を超えると、緊急オーバーフロープロテクションがコンテンツをトリムする場合があります。" + "description": "Lite圧縮中に長いツール結果を切り捨てます。上限は「最大ツール結果長」フィールド(未設定時は OMNIROUTE_LITE_MAX_TOOL_LENGTH)で決まります。コンテキストがモデルの予算を超えると、緊急オーバーフロープロテクションがコンテンツをトリムする場合があります。" + }, + "maxToolLength": { + "label": "最大ツール結果長", + "description": "ツール結果の先行切り捨て文字数上限。デフォルト 2000。未設定時は OMNIROUTE_LITE_MAX_TOOL_LENGTH で上書きできます。" } }, "engineFields": { diff --git a/src/i18n/messages/ka.json b/src/i18n/messages/ka.json index 377be50574..60a4c7c007 100644 --- a/src/i18n/messages/ka.json +++ b/src/i18n/messages/ka.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "ხელსაწყოს გრძელი შედეგების წინასწარ შეკვეცა", - "description": "მსუბუქი შეკუმშვისას კვეცავს ხელსაწყოს შედეგებს, რომელთა სიგრძეც 2,000 სიმბოლოს აღემატება. გადავსებისგან გადაუდებელმა დაცვამ შესაძლოა მაინც შეკვეცოს შიგთავსი, როდესაც კონტექსტი მოდელის ლიმიტს აჭარბებს." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/km.json b/src/i18n/messages/km.json index 19fbd2801f..178aead3c7 100644 --- a/src/i18n/messages/km.json +++ b/src/i18n/messages/km.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "កាត់លទ្ធផលឧបករណ៍វែងៗជាមុន", - "description": "កាត់លទ្ធផលឧបករណ៍ដែលលើសពី 2,000 តួអក្សរ អំឡុងពេលបង្រួមកម្រិត ស្រាល។ ការការពារការលើសចំណុះបន្ទាន់អាចនៅតែកាត់ខ្លឹមសារ នៅពេលបរិបទលើសថវិកាថូខិនរបស់ម៉ូដែល។" + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/kn.json b/src/i18n/messages/kn.json index a36edb73a4..63e2029438 100644 --- a/src/i18n/messages/kn.json +++ b/src/i18n/messages/kn.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "ಉದ್ದವಾದ ಪರಿಕರ ಫಲಿತಾಂಶಗಳನ್ನು ಪೂರ್ವಭಾವಿಯಾಗಿ ಮೊಟಕುಗೊಳಿಸಿ", - "description": "ಹಗುರ ಸಂಕುಚನದ ಸಮಯದಲ್ಲಿ 2,000 ಅಕ್ಷರಗಳನ್ನು ಮೀರಿದ ಪರಿಕರ ಫಲಿತಾಂಶಗಳನ್ನು ಮೊಟಕುಗೊಳಿಸುತ್ತದೆ. ಸಂದರ್ಭವು ಮಾದರಿಯ ಮಿತಿಯನ್ನು ಮೀರಿದಾಗ ತುರ್ತು ಓವರ್ಫ್ಲೋ ರಕ್ಷಣೆಯು ಇನ್ನೂ ವಿಷಯವನ್ನು ಕಡಿತಗೊಳಿಸಬಹುದು." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/ko.json b/src/i18n/messages/ko.json index 6cf7956f8e..916d6ef02d 100644 --- a/src/i18n/messages/ko.json +++ b/src/i18n/messages/ko.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "긴 도구 결과를 사전적으로 잘라내기", - "description": "Lite 압축 중 2,000자를 초과하는 도구 결과를 잘라냅니다. 모델 예산을 초과하는 경우 긴급 오버플로우 보호가 여전히 콘텐츠를 잘라낼 수 있습니다." + "description": "Lite 압축 중 긴 도구 결과를 잘라냅니다. 상한은 '최대 도구 결과 길이' 필드(미설정 시 OMNIROUTE_LITE_MAX_TOOL_LENGTH)로 정합니다. 모델 예산을 초과하는 경우 긴급 오버플로우 보호가 여전히 콘텐츠를 잘라낼 수 있습니다." + }, + "maxToolLength": { + "label": "최대 도구 결과 길이", + "description": "도구 결과 선행 잘라내기 문자 상한. 기본값 2000. 이 필드를 비우면 OMNIROUTE_LITE_MAX_TOOL_LENGTH로 덮어씁니다." } }, "engineFields": { diff --git a/src/i18n/messages/lt.json b/src/i18n/messages/lt.json index a053447827..6494498947 100644 --- a/src/i18n/messages/lt.json +++ b/src/i18n/messages/lt.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Iš anksto trumpinti ilgus įrankių rezultatus", - "description": "Naudojant „Lite“ glaudinimą, sutrumpinami ilgesni nei 2 000 simbolių įrankių rezultatai. Avarinė apsauga nuo perpildymo vis tiek gali apkarpyti turinį, kai kontekstas viršija modelio limitą." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/lv.json b/src/i18n/messages/lv.json index 367ff9aa8a..1e4979838e 100644 --- a/src/i18n/messages/lv.json +++ b/src/i18n/messages/lv.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktīvi saīsināt garus rīka rezultātus", - "description": "Saīsina rīka rezultātus, kas pārsniedz 2,000 rakstzīmes Lite saspiešanas laikā. Avārijas pārsnieguma aizsardzība var joprojām samazināt saturu, kad konteksts pārsniedz modeļa budžetu." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/ml.json b/src/i18n/messages/ml.json index d13ec5d345..29cb762410 100644 --- a/src/i18n/messages/ml.json +++ b/src/i18n/messages/ml.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "ദൈർഘ്യമേറിയ ടൂൾ ഫലങ്ങൾ മുൻകൂട്ടി വെട്ടിച്ചുരുക്കുക", - "description": "ലഘു കംപ്രഷൻ സമയത്ത് 2,000 പ്രതീകങ്ങളിൽ കൂടുതലുള്ള ടൂൾ ഫലങ്ങൾ വെട്ടിച്ചുരുക്കുന്നു. കോൺടെക്സ്റ്റ് മോഡലിന്റെ ബജറ്റ് കവിയുമ്പോൾ അടിയന്തര ഓവർഫ്ലോ പരിരക്ഷ ഉള്ളടക്കം വീണ്ടും വെട്ടിക്കുറച്ചേക്കാം." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/mr.json b/src/i18n/messages/mr.json index 2cc544ebad..12744fcd84 100644 --- a/src/i18n/messages/mr.json +++ b/src/i18n/messages/mr.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "सक्रियपणे लांब साधन परिणाम कमी करा", - "description": "Lite संकुचनादरम्यान 2,000 वर्णांपेक्षा जास्त टूल परिणाम कमी करतो. आपत्कालीन ओव्हरफ्लो संरक्षणाने मॉडेल बजेट ओलांडल्यास सामग्री कमी केली जाऊ शकते." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/ms.json b/src/i18n/messages/ms.json index e280b1a8b9..6f8a0d5157 100644 --- a/src/i18n/messages/ms.json +++ b/src/i18n/messages/ms.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Secara proaktif memendekkan hasil alat yang panjang", - "description": "Memendekkan hasil alat melebihi 2,000 aksara semasa pemampatan Lite. Perlindungan limpahan kecemasan mungkin masih memotong kandungan apabila konteks melebihi bajet model." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/mt.json b/src/i18n/messages/mt.json index 72b8d5b630..091c1333c2 100644 --- a/src/i18n/messages/mt.json +++ b/src/i18n/messages/mt.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Qassar b'mod proattiv ir-riżultati twal tal-għodda", - "description": "Iqassar ir-riżultati tal-għodda li jaqbżu l-2,000 karattru waqt il-kompressjoni Lite. Il-protezzjoni ta' emerġenza kontra t-tagħbija żejda xorta tista' tqassar il-kontenut meta l-kuntest jaqbeż il-limitu tal-mudell." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/my.json b/src/i18n/messages/my.json index 79bb989c16..0d2b8e94e7 100644 --- a/src/i18n/messages/my.json +++ b/src/i18n/messages/my.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "ရှည်လျားသော tool ရလဒ်များကို ကြိုတင်ဖြတ်တောက်ပါ", - "description": "ပေါ့ပါးသော ချုံ့ခြင်းအတွင်း စာလုံးရေ 2,000 ကျော်သည့် tool ရလဒ်များကို ဖြတ်တောက်သည်။ ဆက်စပ်အကြောင်းအရာသည် မော်ဒယ်၏ သတ်မှတ်ပမာဏကို ကျော်လွန်ပါက အရေးပေါ် လျှံထွက်မှုကာကွယ်ရေးက အကြောင်းအရာကို ဆက်လက်ဖြတ်တောက်နိုင်သည်။" + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/ne.json b/src/i18n/messages/ne.json index 4e8099c7cb..f8d91d3ebb 100644 --- a/src/i18n/messages/ne.json +++ b/src/i18n/messages/ne.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "लामा उपकरण नतिजाहरू सक्रिय रूपमा काट्नुहोस्", - "description": "हल्का सङ्कुचनका क्रममा 2,000 वर्णभन्दा लामा उपकरण नतिजाहरू काट्छ। कन्टेक्स्टले मोडेलको बजेट नाघ्दा आपत्कालीन ओभरफ्लो सुरक्षाले अझै पनि सामग्री काट्न सक्छ।" + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/nl.json b/src/i18n/messages/nl.json index e6aebe1b29..48139bb139 100644 --- a/src/i18n/messages/nl.json +++ b/src/i18n/messages/nl.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proactief lange toolresultaten inkorten", - "description": "Truncateert toolresultaten boven de 2.000 tekens tijdens Lite-compressie. Noodoverloopbescherming kan nog steeds inhoud inkorten wanneer de context het modelbudget overschrijdt." + "description": "Kort lange toolresultaten in tijdens Lite-compressie. De limiet wordt gezet door het veld Maximale toolresultaatlengte (of OMNIROUTE_LITE_MAX_TOOL_LENGTH als het veld leeg is). Noodoverloopbescherming kan nog steeds inhoud inkorten wanneer de context het modelbudget overschrijdt." + }, + "maxToolLength": { + "label": "Maximale toolresultaatlengte", + "description": "Tekenlimiet voor proactief inkorten. Standaard 2000. Overschrijf met OMNIROUTE_LITE_MAX_TOOL_LENGTH als het veld leeg is." } }, "engineFields": { diff --git a/src/i18n/messages/no.json b/src/i18n/messages/no.json index df3ec1866e..6d66caffe7 100644 --- a/src/i18n/messages/no.json +++ b/src/i18n/messages/no.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktivt forkorte lange verktøyresultater", - "description": "Trimmerer verktøyresultater over 2 000 tegn under Lite-komprimering. Nødsituasjonsbeskyttelse kan fortsatt trimme innhold når konteksten overskrider modellbudsjettet." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/or.json b/src/i18n/messages/or.json index 5cb132f886..98fe359367 100644 --- a/src/i18n/messages/or.json +++ b/src/i18n/messages/or.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "ଲମ୍ବା ଟୁଲ୍ ଫଳାଫଳଗୁଡ଼ିକୁ ଆଗୁଆ କାଟଛାଣ୍ଟ କରନ୍ତୁ", - "description": "ଲାଇଟ୍ ସଂକୁଚନ ସମୟରେ 2,000 ଅକ୍ଷରରୁ ଅଧିକ ଟୁଲ୍ ଫଳାଫଳକୁ କାଟଛାଣ୍ଟ କରେ। ପ୍ରସଙ୍ଗ ମଡେଲ୍ର ବଜେଟ୍ ଅତିକ୍ରମ କଲେ ଜରୁରୀ ଓଭରଫ୍ଲୋ ସୁରକ୍ଷା ତଥାପି ବିଷୟବସ୍ତୁକୁ ଛୋଟ କରିପାରେ।" + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/pa.json b/src/i18n/messages/pa.json index 89aeb8312c..5cd8dc6380 100644 --- a/src/i18n/messages/pa.json +++ b/src/i18n/messages/pa.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "ਲੰਬੇ ਟੂਲ ਨਤੀਜਿਆਂ ਨੂੰ ਪਹਿਲਾਂ ਹੀ ਕੱਟੋ", - "description": "Lite ਸੰਕੁਚਨ ਦੌਰਾਨ 2,000 ਅੱਖਰਾਂ ਤੋਂ ਵੱਧ ਦੇ ਟੂਲ ਨਤੀਜਿਆਂ ਨੂੰ ਕੱਟਦਾ ਹੈ। ਜਦੋਂ ਸੰਦਰਭ ਮਾਡਲ ਬਜਟ ਤੋਂ ਵੱਧ ਜਾਂਦਾ ਹੈ, ਤਾਂ ਐਮਰਜੈਂਸੀ ਓਵਰਫ਼ਲੋ ਸੁਰੱਖਿਆ ਹਾਲੇ ਵੀ ਸਮੱਗਰੀ ਨੂੰ ਕੱਟ ਸਕਦੀ ਹੈ।" + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/phi.json b/src/i18n/messages/phi.json index 37de91309b..80727be740 100644 --- a/src/i18n/messages/phi.json +++ b/src/i18n/messages/phi.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktibong putulin ang mahahabang resulta ng tool", - "description": "Pinaputol ang mga resulta ng tool na higit sa 2,000 na karakter sa panahon ng Lite compression. Ang emergency overflow protection ay maaari pa ring magbawas ng nilalaman kapag ang konteksto ay lumampas sa budget ng modelo." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/pl.json b/src/i18n/messages/pl.json index c52a2b7900..2464f6a925 100644 --- a/src/i18n/messages/pl.json +++ b/src/i18n/messages/pl.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktywnie skracaj długie wyniki narzędzi", - "description": "Obcina wyniki narzędzia powyżej 2 000 znaków podczas kompresji Lite. Ochrona przed przepełnieniem w trybie awaryjnym może nadal przycinać treść, gdy kontekst przekracza budżet modelu." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/pt-BR.json b/src/i18n/messages/pt-BR.json index 185a24637e..76ed5b833f 100644 --- a/src/i18n/messages/pt-BR.json +++ b/src/i18n/messages/pt-BR.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Truncar proativamente resultados longos de ferramentas", - "description": "Trunca os resultados da ferramenta com mais de 2.000 caracteres durante a compressão Lite. A proteção de transbordo de emergência ainda pode cortar o conteúdo quando o contexto excede o orçamento do modelo." + "description": "Trunca resultados longos de ferramenta durante a compressão Lite. O limite é definido pelo campo Comprimento máximo do resultado da ferramenta (ou OMNIROUTE_LITE_MAX_TOOL_LENGTH se estiver vazio). A proteção de transbordo de emergência ainda pode recortar conteúdo quando o contexto excede o orçamento do modelo." + }, + "maxToolLength": { + "label": "Comprimento máximo do resultado da ferramenta", + "description": "Limite de caracteres para o recorte proativo. Padrão 2000. Substitua com OMNIROUTE_LITE_MAX_TOOL_LENGTH se o campo estiver vazio." } }, "engineFields": { diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 30d8bfb267..2f7c586d95 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Truncar proativamente resultados longos de ferramentas", - "description": "Trunca os resultados da ferramenta com mais de 2.000 caracteres durante a compressão Lite. A proteção de transbordo de emergência pode ainda cortar conteúdo quando o contexto excede o orçamento do modelo." + "description": "Trunca resultados longos de ferramenta durante a compressão Lite. O limite é definido pelo campo Comprimento máximo do resultado da ferramenta (ou OMNIROUTE_LITE_MAX_TOOL_LENGTH se estiver vazio). A proteção de transbordo de emergência ainda pode recortar conteúdo quando o contexto excede o orçamento do modelo." + }, + "maxToolLength": { + "label": "Comprimento máximo do resultado da ferramenta", + "description": "Limite de caracteres para o recorte proativo. Padrão 2000. Substitua com OMNIROUTE_LITE_MAX_TOOL_LENGTH se o campo estiver vazio." } }, "engineFields": { diff --git a/src/i18n/messages/ro.json b/src/i18n/messages/ro.json index f2a08d3911..630f313304 100644 --- a/src/i18n/messages/ro.json +++ b/src/i18n/messages/ro.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Trunchiați proactiv rezultatele lungi ale instrumentului", - "description": "Trunchiază rezultatele instrumentului peste 2.000 de caractere în timpul compresiei Lite. Protecția de suprasarcină de urgență poate tăia în continuare conținutul atunci când contextul depășește bugetul modelului." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index ce418e6f77..e1f44db7d8 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Проактивно обрезать длинные результаты инструментов", - "description": "Обрезает результаты инструмента, превышающие 2,000 символов, во время легкой компрессии. Защита от переполнения в экстренных ситуациях может все еще обрезать содержимое, когда контекст превышает бюджет модели." + "description": "Обрезает длинные результаты инструментов во время Lite-сжатия. Предел задаёт поле «Максимальная длина результата инструмента» (или OMNIROUTE_LITE_MAX_TOOL_LENGTH, если поле пусто). Аварийная защита от переполнения всё ещё может обрезать содержимое, когда контекст превышает бюджет модели." + }, + "maxToolLength": { + "label": "Максимальная длина результата инструмента", + "description": "Лимит символов для упреждающего обрезания. По умолчанию 2000. Переопределяется OMNIROUTE_LITE_MAX_TOOL_LENGTH, если поле не задано." } }, "engineFields": { diff --git a/src/i18n/messages/si.json b/src/i18n/messages/si.json index ed1a9b3f6b..51902c0ba8 100644 --- a/src/i18n/messages/si.json +++ b/src/i18n/messages/si.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "දිගු මෙවලම් ප්රතිඵල කල්තියා කප්පාදු කරන්න", - "description": "Lite සම්පීඩනය අතරතුර අක්ෂර 2,000 ඉක්මවන මෙවලම් ප්රතිඵල කප්පාදු කරයි. සන්දර්භය ආකෘතියේ සීමාව ඉක්මවන විට හදිසි අතිප්රවාහ ආරක්ෂණය මඟින් තවදුරටත් අන්තර්ගතය කප්පාදු කළ හැක." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/sk.json b/src/i18n/messages/sk.json index 19ae53d789..05b0143790 100644 --- a/src/i18n/messages/sk.json +++ b/src/i18n/messages/sk.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktívne skrátiť dlhé výsledky nástroja", - "description": "Zrezáva výsledky nástroja nad 2 000 znakov počas Lite kompresie. Núdzová ochrana pred pretečením môže stále skrátiť obsah, keď kontext presiahne rozpočet modelu." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/sl.json b/src/i18n/messages/sl.json index ad1564b997..3ed2454787 100644 --- a/src/i18n/messages/sl.json +++ b/src/i18n/messages/sl.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Vnaprej skrajšaj dolge rezultate orodij", - "description": "Med lahkim stiskanjem skrajša rezultate orodij, daljše od 2.000 znakov. Zaščita pred prekoračitvijo v sili lahko še vedno obreže vsebino, ko kontekst preseže proračun modela." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/sr.json b/src/i18n/messages/sr.json index 83431e4f33..7bd7a0f3dd 100644 --- a/src/i18n/messages/sr.json +++ b/src/i18n/messages/sr.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Проактивно скраћивање дугих резултата алата", - "description": "Скраћује резултате алата дуже од 2.000 карактера током Lite компресије. Заштита од хитног прекорачења може и даље скраћивати садржај када контекст премаши буџет модела." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/sv.json b/src/i18n/messages/sv.json index e281a0819a..54316c6c49 100644 --- a/src/i18n/messages/sv.json +++ b/src/i18n/messages/sv.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Proaktivt trunkera långa verktygsresultat", - "description": "Avskär resultat från verktyget över 2 000 tecken under Lite-komprimering. Nödfallsöversvämningsskydd kan fortfarande trimma innehåll när kontexten överskrider modellens budget." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/sw.json b/src/i18n/messages/sw.json index bae241e28b..2664835357 100644 --- a/src/i18n/messages/sw.json +++ b/src/i18n/messages/sw.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Kukata matokeo marefu ya zana kwa njia ya kujiandaa", - "description": "Hupunguza matokeo ya zana zaidi ya herufi 2,000 wakati wa kufinya Lite. Ulinzi wa dharura wa kupita kiasi bado unaweza kukata maudhui wakati muktadha unazidi bajeti ya mfano." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/ta.json b/src/i18n/messages/ta.json index d8f104b636..41b26d3c40 100644 --- a/src/i18n/messages/ta.json +++ b/src/i18n/messages/ta.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "முன்னெச்சரிக்கையாக நீண்ட கருவி முடிவுகளை குறைக்கவும்", - "description": "Lite சுருக்கத்தின் போது 2,000 எழுத்துக்களை மீறிய கருவி முடிவுகளை குறுக்குகிறது. சூடான overflow பாதுகாப்பு, மாடல் பட்ஜெட்டை மீறும் போது உள்ளடக்கத்தை இன்னும் குறைக்கலாம்." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/te.json b/src/i18n/messages/te.json index 1555236638..8de6611300 100644 --- a/src/i18n/messages/te.json +++ b/src/i18n/messages/te.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "సక్రియంగా పొడవైన సాధన ఫలితాలను కత్తిరించండి", - "description": "Lite కంప్రెషన్ సమయంలో 2,000 అక్షరాల కంటే ఎక్కువ టూల్ ఫలితాలను కత్తిరిస్తుంది. సందర్భం మోడల్ బడ్జెట్‌ను మించితే అత్యవసర ఓవర్‌ఫ్లో రక్షణ ఇంకా కంటెంట్‌ను కత్తిరించవచ్చు." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/th.json b/src/i18n/messages/th.json index 025717a298..71bae9e4a9 100644 --- a/src/i18n/messages/th.json +++ b/src/i18n/messages/th.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "ตัดผลลัพธ์เครื่องมือที่ยาวเกินไปอย่างมีประสิทธิภาพ", - "description": "ตัดผลลัพธ์ของเครื่องมือที่มีความยาวเกิน 2,000 ตัวอักษรในระหว่างการบีบอัดแบบ Lite การป้องกันการล débordement ฉุกเฉินอาจยังคงตัดเนื้อหาเมื่อบริบทเกินงบประมาณของโมเดล" + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/tr.json b/src/i18n/messages/tr.json index 0603cfb5a7..455cd0ea2f 100644 --- a/src/i18n/messages/tr.json +++ b/src/i18n/messages/tr.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Uzun araç sonuçlarını proaktif olarak kısaltın", - "description": "Lite sıkıştırma sırasında 2.000 karakterin üzerindeki araç sonuçlarını keser. Acil taşma koruması, bağlam model bütçesini aştığında içeriği hala kesebilir." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/uk-UA.json b/src/i18n/messages/uk-UA.json index af7d1a8f1b..aad66d387b 100644 --- a/src/i18n/messages/uk-UA.json +++ b/src/i18n/messages/uk-UA.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Проактивно обрізати довгі результати інструментів", - "description": "Обрізає результати інструменту понад 2,000 символів під час Lite стиснення. Захист від надмірного переповнення може все ще обрізати вміст, коли контекст перевищує бюджет моделі." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/ur.json b/src/i18n/messages/ur.json index c74fb4c0e0..4f63292e72 100644 --- a/src/i18n/messages/ur.json +++ b/src/i18n/messages/ur.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "پیشگی طور پر طویل ٹول کے نتائج کو کاٹ دیں", - "description": "Lite کمپریشن کے دوران 2,000 کرداروں سے زیادہ کے ٹول کے نتائج کو کاٹ دیتا ہے۔ ایمرجنسی اوورفلو پروٹیکشن اب بھی مواد کو کم کر سکتا ہے جب سیاق و سباق ماڈل کے بجٹ سے تجاوز کر جائے۔" + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/uz.json b/src/i18n/messages/uz.json index bfbdcb6c71..765ef76772 100644 --- a/src/i18n/messages/uz.json +++ b/src/i18n/messages/uz.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Uzun vosita natijalarini oldindan qisqartirish", - "description": "Yengil siqish vaqtida 2 000 belgidan uzun vosita natijalarini qisqartiradi. Kontekst model budjetidan oshganda, favqulodda to‘lib ketishdan himoya mexanizmi kontentni baribir qisqartirishi mumkin." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/vi.json b/src/i18n/messages/vi.json index 2548fa5102..b7ea4490ea 100644 --- a/src/i18n/messages/vi.json +++ b/src/i18n/messages/vi.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Chủ động rút gọn kết quả công cụ dài", - "description": "Rút gọn các kết quả công cụ dài hơn 2.000 ký tự trong quá trình nén Lite. Cơ chế bảo vệ tràn khẩn cấp vẫn có thể cắt bớt nội dung khi ngữ cảnh vượt quá ngân sách của mô hình." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/yo.json b/src/i18n/messages/yo.json index d2f485a2cb..26d40240a3 100644 --- a/src/i18n/messages/yo.json +++ b/src/i18n/messages/yo.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "Gé àwọn àbájáde irinṣẹ́ gígùn kúrú ṣáájú", - "description": "Ó ń gé àwọn àbájáde irinṣẹ́ tó ju ohun kikọ 2,000 lọ kúrú nígbà ìfúnpọ̀ Fúyẹ́. Ààbò pajawiri fún àkúnwọ́sílẹ̀ ṣì lè gé àkóónú kúrú nígbà tí ọ̀rọ̀ àyíká bá kọjá iye tí mọ́dẹ̀lì gbà." + "description": "Truncates long tool results during Lite compression. The Maximum tool-result length field (or OMNIROUTE_LITE_MAX_TOOL_LENGTH when that field is unset) sets the cap. Emergency overflow protection may still trim content when the context exceeds the model budget." + }, + "maxToolLength": { + "label": "Maximum tool-result length", + "description": "Character cap for proactive tool-result truncation. Default 2000. Override with OMNIROUTE_LITE_MAX_TOOL_LENGTH when this field is unset." } }, "engineFields": { diff --git a/src/i18n/messages/zh-CN.json b/src/i18n/messages/zh-CN.json index 92eacf37af..4cf50b1831 100644 --- a/src/i18n/messages/zh-CN.json +++ b/src/i18n/messages/zh-CN.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "主动截断过长的工具结果", - "description": "在 Lite 压缩期间截断超过 2,000 个字符的工具结果。当上下文超出模型预算时,紧急溢出保护仍可能裁剪内容。" + "description": "在 Lite 压缩期间截断过长的工具结果。截断上限由「最大工具结果长度」字段决定;该字段未设置时使用 OMNIROUTE_LITE_MAX_TOOL_LENGTH。当上下文超出模型预算时,紧急溢出保护仍可能裁剪内容。" + }, + "maxToolLength": { + "label": "最大工具结果长度", + "description": "主动截断工具结果的字符上限。默认 2000。该字段未设置时可用 OMNIROUTE_LITE_MAX_TOOL_LENGTH 覆盖。" } }, "engineFields": { diff --git a/src/i18n/messages/zh-TW.json b/src/i18n/messages/zh-TW.json index 31c600a34d..624a5af4c6 100644 --- a/src/i18n/messages/zh-TW.json +++ b/src/i18n/messages/zh-TW.json @@ -8596,7 +8596,11 @@ }, "compressToolResults": { "label": "主動截斷長工具結果", - "description": "在輕量壓縮期間,截斷超過 2,000 個字符的工具結果。當上下文超過模型預算時,緊急溢出保護仍可能修剪內容。" + "description": "在輕量壓縮期間截斷過長的工具結果。截斷上限由「最大工具結果長度」欄位決定;該欄位未設定時使用 OMNIROUTE_LITE_MAX_TOOL_LENGTH。當上下文超過模型預算時,緊急溢出保護仍可能修剪內容。" + }, + "maxToolLength": { + "label": "最大工具結果長度", + "description": "主動截斷工具結果的字元上限。預設 2000。該欄位未設定時可用 OMNIROUTE_LITE_MAX_TOOL_LENGTH 覆寫。" } }, "engineFields": { diff --git a/src/lib/db/compression.ts b/src/lib/db/compression.ts index 6799478532..f95cb88131 100644 --- a/src/lib/db/compression.ts +++ b/src/lib/db/compression.ts @@ -42,6 +42,7 @@ import { normalizePreserveSystemPromptMode, } from "@omniroute/open-sse/services/compression/preserveSystemPromptMode.ts"; import { maybePrewarmUltraSlmOnConfig } from "@omniroute/open-sse/services/compression/ultra.ts"; +import { isUsableLiteMaxToolLength } from "@omniroute/open-sse/services/compression/lite.ts"; import { applyDetailConfigUpdate, buildDetailConfigDefaults } from "./compressionDetailNormalizers"; const NAMESPACE = "compression"; @@ -374,6 +375,10 @@ function boundedInt(value: unknown, fallback: number, min: number, max: number): return Math.min(max, Math.max(min, Math.floor(value))); } +function usableLiteMaxToolLength(value: unknown): number | undefined { + return isUsableLiteMaxToolLength(value) ? Math.floor(value) : undefined; +} + function boundedNumber(value: unknown, fallback: number, min: number, max: number): number { if (typeof value !== "number" || !Number.isFinite(value)) return fallback; return Math.min(max, Math.max(min, value)); @@ -526,6 +531,41 @@ function sanitizeEnginesForWrite(value: unknown): Record { return out; } +// Partial lite writes replace the whole JSON row. Keep a stored cap unless the +// caller sends maxToolLength: null (clear) or a new in-range integer. +function mergeLiteSettingsForWrite( + db: ReturnType, + value: unknown +): { compressToolResults: boolean; maxToolLength?: number } { + const incoming = toRecord(value); + const existingRow = db + .prepare("SELECT value FROM key_value WHERE namespace = ? AND key = ?") + .get(NAMESPACE, "lite") as { value: string } | undefined; + const existing = toRecord(parseJsonSafe(existingRow?.value ?? null)); + const existingCap = usableLiteMaxToolLength(existing.maxToolLength); + const compressToolResults = + typeof incoming.compressToolResults === "boolean" + ? incoming.compressToolResults + : existing.compressToolResults !== false; + if (!Object.prototype.hasOwnProperty.call(incoming, "maxToolLength")) { + return { + compressToolResults, + ...(existingCap !== undefined ? { maxToolLength: existingCap } : {}), + }; + } + if (incoming.maxToolLength === null) { + return { compressToolResults }; + } + const nextCap = usableLiteMaxToolLength(incoming.maxToolLength); + if (nextCap !== undefined) { + return { compressToolResults, maxToolLength: nextCap }; + } + return { + compressToolResults, + ...(existingCap !== undefined ? { maxToolLength: existingCap } : {}), + }; +} + // Read the stored `engines` JSON row, keeping only well-formed `{enabled, level?}` entries for // known engine ids. Returns null when no usable row exists so the caller falls back to deriving // the map from the legacy fields (B-backfill, migration 102). @@ -761,9 +801,15 @@ export async function getCompressionSettings(): Promise { case "ultraConfig": config.ultra = normalizeUltraConfig(parsed); break; - case "lite": - config.lite = { compressToolResults: toRecord(parsed).compressToolResults !== false }; + case "lite": { + const liteRecord = toRecord(parsed); + const storedCap = usableLiteMaxToolLength(liteRecord.maxToolLength); + config.lite = { + compressToolResults: liteRecord.compressToolResults !== false, + ...(storedCap !== undefined ? { maxToolLength: storedCap } : {}), + }; break; + } case "headroom": case "headroomConfig": config.headroom = normalizeHeadroomConfig(parsed); @@ -878,6 +924,10 @@ export async function updateCompressionSettings( insert.run(NAMESPACE, key, JSON.stringify(sanitizeEnginesForWrite(value))); continue; } + if (key === "lite") { + insert.run(NAMESPACE, key, JSON.stringify(mergeLiteSettingsForWrite(db, value))); + continue; + } insert.run(NAMESPACE, key, JSON.stringify(value)); } }); diff --git a/src/shared/components/compression/EngineConfigForm.tsx b/src/shared/components/compression/EngineConfigForm.tsx index 0a6ff2771a..8be71aede4 100644 --- a/src/shared/components/compression/EngineConfigForm.tsx +++ b/src/shared/components/compression/EngineConfigForm.tsx @@ -23,10 +23,22 @@ export function EngineConfigForm({ schema, value, onChange }: EngineConfigFormPr {f.type === "number" && ( set(f.key, Number(e.target.value))} + onChange={(e) => + set( + f.key, + f.key === "maxToolLength" && e.target.value === "" + ? Number.NaN + : Number(e.target.value) + ) + } className="border border-border rounded px-2 py-1" /> )} diff --git a/src/shared/components/compression/EngineConfigPage.tsx b/src/shared/components/compression/EngineConfigPage.tsx index 36e45f5541..4c05af2caa 100644 --- a/src/shared/components/compression/EngineConfigPage.tsx +++ b/src/shared/components/compression/EngineConfigPage.tsx @@ -178,6 +178,12 @@ export function EngineConfigPage({ engineId }: { engineId: string }) { for (const field of foundEngine?.configSchema ?? []) { defaults[field.key] = field.defaultValue; } + // Do not seed lite.maxToolLength from the schema default. Persisting 2000 + // would freeze the cap in settings and hide OMNIROUTE_LITE_MAX_TOOL_LENGTH. + // The form still shows 2000 via field.defaultValue until the operator edits it. + if (engineId === "lite" && currentConfig.maxToolLength === undefined) { + delete defaults.maxToolLength; + } setConfigState({ ...defaults, ...currentConfig }); setLoading(false); } @@ -204,10 +210,23 @@ export function EngineConfigPage({ engineId }: { engineId: string }) { // Strip the `enabled` key — engine on/off is the panel's responsibility. const { enabled: _ignored, ...formDetail } = configState; void _ignored; - const detail = - engineId === "lite" - ? { compressToolResults: formDetail.compressToolResults !== false } - : formDetail; + let detail: Record = formDetail; + if (engineId === "lite") { + const raw = formDetail.maxToolLength; + const compressToolResults = formDetail.compressToolResults !== false; + if (!Object.prototype.hasOwnProperty.call(formDetail, "maxToolLength")) { + detail = { compressToolResults }; + } else if (typeof raw === "number" && Number.isFinite(raw)) { + const n = Math.floor(raw); + if (n < 256 || n > 1_000_000) { + setSaveError(t("saveFailed")); + return; + } + detail = { compressToolResults, maxToolLength: n }; + } else { + detail = { compressToolResults, maxToolLength: null }; + } + } setSaving(true); setSaveError(null); try { diff --git a/src/shared/validation/compressionConfigSchemas.ts b/src/shared/validation/compressionConfigSchemas.ts index e0ba0b6fe5..7219b53fd9 100644 --- a/src/shared/validation/compressionConfigSchemas.ts +++ b/src/shared/validation/compressionConfigSchemas.ts @@ -314,6 +314,8 @@ export const STACKED_PIPELINE_ENGINE_INTENSITIES: Record { `headroom configSchema should contain a field with key 'minRows', got keys: ${headroom.configSchema.map((f) => f.key).join(", ")}` ); }); + + test("lite configSchema includes the 'maxToolLength' field key", async () => { + const req = await makeManagementSessionRequest("http://localhost/api/compression/engines"); + const res = await enginesRoute.GET(req); + const body = (await res.json()) as { + engines: Array<{ + id: string; + configSchema: Array<{ key: string }>; + }>; + }; + const lite = body.engines.find((e) => e.id === "lite"); + assert.ok(lite, "lite engine should be present"); + const hasMaxToolLength = lite.configSchema.some((f) => f.key === "maxToolLength"); + assert.ok( + hasMaxToolLength, + `lite configSchema should contain a field with key 'maxToolLength', got keys: ${lite.configSchema.map((f) => f.key).join(", ")}` + ); + }); }); diff --git a/tests/unit/compression/db.test.ts b/tests/unit/compression/db.test.ts index 957cdbf899..621612998b 100644 --- a/tests/unit/compression/db.test.ts +++ b/tests/unit/compression/db.test.ts @@ -56,6 +56,7 @@ describe("getCompressionSettings", () => { assert.deepEqual(settings.liveZone, { enabled: false }); assert.deepEqual(settings.comboOverrides, {}); assert.equal(settings.lite?.compressToolResults, true); + assert.equal(settings.lite?.maxToolLength, undefined); assert.equal(settings.ultra?.enabled, false); assert.equal(settings.ultra?.compressionRate, 0.5); assert.equal(settings.ultra?.minScoreThreshold, 0.3); @@ -81,6 +82,45 @@ describe("updateCompressionSettings", () => { assert.equal(settings.lite?.compressToolResults, false); }); + it("persists Lite maxToolLength across reload and keeps the truncation switch", async () => { + await updateCompressionSettings({ + lite: { compressToolResults: true, maxToolLength: 8000 }, + }); + core.resetDbInstance(); + + const settings = await getCompressionSettings(); + assert.equal(settings.lite?.compressToolResults, true); + assert.equal(settings.lite?.maxToolLength, 8000); + }); + + it("keeps a stored Lite maxToolLength when a later write only toggles truncation", async () => { + await updateCompressionSettings({ + lite: { compressToolResults: true, maxToolLength: 8000 }, + }); + core.resetDbInstance(); + await updateCompressionSettings({ lite: { compressToolResults: false } }); + core.resetDbInstance(); + + const settings = await getCompressionSettings(); + assert.equal(settings.lite?.compressToolResults, false); + assert.equal(settings.lite?.maxToolLength, 8000); + }); + + it("clears a stored Lite maxToolLength when the write sends null", async () => { + await updateCompressionSettings({ + lite: { compressToolResults: true, maxToolLength: 8000 }, + }); + core.resetDbInstance(); + await updateCompressionSettings({ + lite: { compressToolResults: true, maxToolLength: null }, + } as Parameters[0]); + core.resetDbInstance(); + + const settings = await getCompressionSettings(); + assert.equal(settings.lite?.compressToolResults, true); + assert.equal(settings.lite?.maxToolLength, undefined); + }); + it("updates defaultMode", async () => { await updateCompressionSettings({ defaultMode: "lite" } as any); const settings = await getCompressionSettings(); diff --git a/tests/unit/compression/engine-registry.test.ts b/tests/unit/compression/engine-registry.test.ts index f70571a826..e47e7f6625 100644 --- a/tests/unit/compression/engine-registry.test.ts +++ b/tests/unit/compression/engine-registry.test.ts @@ -83,6 +83,11 @@ describe("compression engine registry contract", () => { assert.ok( liteSchema.some((field) => field.key === "compressToolResults" && field.defaultValue === true) ); + assert.ok( + liteSchema.some((field) => field.key === "maxToolLength" && field.defaultValue === 2000) + ); + const truncateField = liteSchema.find((field) => field.key === "compressToolResults"); + assert.ok(truncateField?.description && !/2,000/.test(truncateField.description)); assert.ok(!liteSchema.some((field) => field.key === "maxTokensPerMessage")); assert.ok(!liteSchema.some((field) => field.key === "summarizerEnabled")); assert.equal( @@ -94,6 +99,9 @@ describe("compression engine registry contract", () => { ); assert.equal(liteEngine.validateConfig({ preserveSystemPrompt: "yes" }).valid, false); assert.equal(liteEngine.validateConfig({ compressToolResults: "no" }).valid, false); + assert.equal(liteEngine.validateConfig({ maxToolLength: 8000 }).valid, true); + assert.equal(liteEngine.validateConfig({ maxToolLength: 10 }).valid, false); + assert.equal(liteEngine.validateConfig({ maxToolLength: 256 }).valid, true); assert.equal(cavemanEngine.validateConfig({ intensity: "full" }).valid, true); assert.equal(cavemanEngine.validateConfig({ intensity: "bad" }).valid, false); assert.equal(realRtkEngine.validateConfig({ maxLinesPerResult: 20 }).valid, true); diff --git a/tests/unit/compression/headroom-minrows-persist-8056.test.ts b/tests/unit/compression/headroom-minrows-persist-8056.test.ts index 717af14673..d0beba0501 100644 --- a/tests/unit/compression/headroom-minrows-persist-8056.test.ts +++ b/tests/unit/compression/headroom-minrows-persist-8056.test.ts @@ -81,6 +81,34 @@ describe("#8056 headroom minRows persistence", () => { assert.equal(result.success, false); }); + it("schema accepts Lite maxToolLength", () => { + const result = compressionSettingsUpdateSchema.safeParse({ + lite: { compressToolResults: true, maxToolLength: 8000 }, + }); + assert.equal(result.success, true, JSON.stringify(result.error?.issues)); + }); + + it("schema rejects a string Lite maxToolLength", () => { + const result = compressionSettingsUpdateSchema.safeParse({ + lite: { maxToolLength: "8000" }, + }); + assert.equal(result.success, false); + }); + + it("schema rejects a Lite maxToolLength below 256", () => { + const result = compressionSettingsUpdateSchema.safeParse({ + lite: { maxToolLength: 10 }, + }); + assert.equal(result.success, false); + }); + + it("schema accepts null Lite maxToolLength to clear a stored cap", () => { + const result = compressionSettingsUpdateSchema.safeParse({ + lite: { compressToolResults: true, maxToolLength: null }, + }); + assert.equal(result.success, true, JSON.stringify(result.error?.issues)); + }); + it("schema accepts headroom.minRows=5", () => { const result = compressionSettingsUpdateSchema.safeParse({ headroom: { minRows: 5 }, diff --git a/tests/unit/compression/lite.test.ts b/tests/unit/compression/lite.test.ts index 74a87307d8..f8756ea249 100644 --- a/tests/unit/compression/lite.test.ts +++ b/tests/unit/compression/lite.test.ts @@ -98,6 +98,52 @@ describe("compressToolResults", () => { const result = compressToolResults(body); assert.equal(result.applied, false); }); + + it("honors an explicit maxToolLength instead of the 2000-char default", () => { + const body = { messages: [{ role: "tool", content: "x".repeat(800) }] }; + const result = compressToolResults(body, { maxToolLength: 500 }); + assert.equal(result.applied, true); + const content = result.body.messages![0].content as string; + assert.ok(content.endsWith("\n...[truncated]")); + assert.ok(content.length < 800); + assert.ok(content.length <= 500 + "\n...[truncated]".length); + }); + + it("keeps a 1500-char tool result when maxToolLength is 4000", () => { + const body = { messages: [{ role: "tool", content: "x".repeat(1500) }] }; + const result = compressToolResults(body, { maxToolLength: 4000 }); + assert.equal(result.applied, false); + assert.equal(result.body.messages![0].content, "x".repeat(1500)); + }); + + it("reads OMNIROUTE_LITE_MAX_TOOL_LENGTH when no option is passed", () => { + const previous = process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH; + process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH = "400"; + try { + const body = { messages: [{ role: "tool", content: "x".repeat(800) }] }; + const result = compressToolResults(body); + assert.equal(result.applied, true); + const content = result.body.messages![0].content as string; + assert.ok(content.endsWith("\n...[truncated]")); + assert.ok(content.length <= 400 + "\n...[truncated]".length); + } finally { + if (previous === undefined) delete process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH; + else process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH = previous; + } + }); + + it("an explicit maxToolLength wins over the env fallback", () => { + const previous = process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH; + process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH = "400"; + try { + const body = { messages: [{ role: "tool", content: "x".repeat(800) }] }; + const result = compressToolResults(body, { maxToolLength: 4000 }); + assert.equal(result.applied, false); + } finally { + if (previous === undefined) delete process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH; + else process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH = previous; + } + }); }); describe("removeRedundantContent", () => { @@ -292,6 +338,31 @@ describe("stacked Lite precedence (global config vs explicit step)", () => { const messages = result.body.messages as Array<{ content: string }>; assert.match(messages[0].content, /\.\.\.\[truncated\]$/); }); + + it("an out-of-range step maxToolLength does not hide a valid global cap", () => { + const previous = process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH; + process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH = "400"; + try { + const longTool = "x".repeat(3000); + const result = applyCompression( + { messages: [{ role: "tool", content: longTool }] }, + "stacked", + { + config: { + ...baseConfig, + lite: { compressToolResults: true, maxToolLength: 8000 }, + stackedPipeline: [{ engine: "lite", config: { maxToolLength: 10 } }], + }, + } + ); + const messages = result.body.messages as Array<{ content: string }>; + assert.equal(messages[0].content, longTool); + assert.ok(!result.stats?.techniquesUsed.includes("tool-compress")); + } finally { + if (previous === undefined) delete process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH; + else process.env.OMNIROUTE_LITE_MAX_TOOL_LENGTH = previous; + } + }); }); describe("applyLiteCompression", () => { diff --git a/tests/unit/ui/engineConfigPage.test.tsx b/tests/unit/ui/engineConfigPage.test.tsx index 824a806d09..f4a3c84b7a 100644 --- a/tests/unit/ui/engineConfigPage.test.tsx +++ b/tests/unit/ui/engineConfigPage.test.tsx @@ -485,6 +485,8 @@ describe("EngineConfigPage", () => { expect(toggle).not.toBeNull(); expect(toggle?.checked).toBe(false); expect(container.textContent).toContain("Emergency overflow protection may still trim content"); + expect(container.textContent).not.toContain("2,000 characters"); + expect(container.textContent).toContain("Maximum tool-result length field"); const saveButton = Array.from(container.querySelectorAll("button")).find((button) => button.textContent?.includes("Save") @@ -498,6 +500,359 @@ describe("EngineConfigPage", () => { expect(settingsPuts).toContainEqual({ lite: { compressToolResults: false } }); }); + it("saves Lite maxToolLength next to the truncation switch", async () => { + const settingsPuts: Array> = []; + vi.spyOn(globalThis, "fetch").mockImplementation( + async (input: RequestInfo | URL, init?: RequestInit) => { + const url = input.toString(); + if (url.includes("/api/compression/engines")) { + return new Response( + JSON.stringify({ + engines: [ + { + id: "lite", + name: "Lite", + description: "Lite engine", + icon: "compress", + stackable: true, + stackPriority: 5, + metadata: { description: "Lite metadata" }, + configSchema: [ + { + key: "compressToolResults", + type: "boolean", + label: "Proactively truncate long tool results", + defaultValue: true, + }, + { + key: "maxToolLength", + type: "number", + label: "Maximum tool-result length", + defaultValue: 2000, + min: 256, + max: 1_000_000, + }, + ], + }, + ], + }), + { status: 200, headers: { "Content-Type": "application/json" } } + ); + } + if (url.includes("/api/settings/compression")) { + if (init?.method === "PUT") { + settingsPuts.push(JSON.parse(init.body as string) as Record); + } + return new Response( + JSON.stringify({ lite: { compressToolResults: true, maxToolLength: 8000 } }), + { + status: 200, + headers: { "Content-Type": "application/json" }, + } + ); + } + if (url.includes("/api/context/analytics/engine")) { + return new Response(JSON.stringify(ANALYTICS_PAYLOAD), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } + return new Response(JSON.stringify({}), { status: 404 }); + } + ); + + const { EngineConfigPage } = + await import("../../../src/shared/components/compression/EngineConfigPage"); + let container!: HTMLElement; + await act(async () => { + container = mountInContainer(); + await Promise.resolve(); + }); + + const numberInput = container.querySelector("input[type='number']") as HTMLInputElement | null; + expect(numberInput).not.toBeNull(); + expect(numberInput?.value).toBe("8000"); + + const saveButton = Array.from(container.querySelectorAll("button")).find((button) => + button.textContent?.includes("Save") + ); + expect(saveButton).toBeTruthy(); + await act(async () => { + saveButton?.click(); + await Promise.resolve(); + }); + + expect(settingsPuts).toContainEqual({ + lite: { compressToolResults: true, maxToolLength: 8000 }, + }); + }); + + it("does not persist the schema default maxToolLength when settings omit it", async () => { + const settingsPuts: Array> = []; + vi.spyOn(globalThis, "fetch").mockImplementation( + async (input: RequestInfo | URL, init?: RequestInit) => { + const url = input.toString(); + if (url.includes("/api/compression/engines")) { + return new Response( + JSON.stringify({ + engines: [ + { + id: "lite", + name: "Lite", + description: "Lite engine", + icon: "compress", + stackable: true, + stackPriority: 5, + metadata: { description: "Lite metadata" }, + configSchema: [ + { + key: "compressToolResults", + type: "boolean", + label: "Proactively truncate long tool results", + defaultValue: true, + }, + { + key: "maxToolLength", + type: "number", + label: "Maximum tool-result length", + defaultValue: 2000, + min: 256, + max: 1_000_000, + }, + ], + }, + ], + }), + { status: 200, headers: { "Content-Type": "application/json" } } + ); + } + if (url.includes("/api/settings/compression")) { + if (init?.method === "PUT") { + settingsPuts.push(JSON.parse(init.body as string) as Record); + } + return new Response(JSON.stringify({ lite: { compressToolResults: true } }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } + if (url.includes("/api/context/analytics/engine")) { + return new Response(JSON.stringify(ANALYTICS_PAYLOAD), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } + return new Response(JSON.stringify({}), { status: 404 }); + } + ); + + const { EngineConfigPage } = + await import("../../../src/shared/components/compression/EngineConfigPage"); + let container!: HTMLElement; + await act(async () => { + container = mountInContainer(); + await Promise.resolve(); + }); + + const saveButton = Array.from(container.querySelectorAll("button")).find((button) => + button.textContent?.includes("Save") + ); + expect(saveButton).toBeTruthy(); + await act(async () => { + saveButton?.click(); + await Promise.resolve(); + }); + + expect(settingsPuts).toContainEqual({ lite: { compressToolResults: true } }); + expect(settingsPuts.some((body) => "maxToolLength" in ((body.lite as object) ?? {}))).toBe( + false + ); + }); + + it("omits maxToolLength from Save when the number input is cleared", async () => { + const settingsPuts: Array> = []; + vi.spyOn(globalThis, "fetch").mockImplementation( + async (input: RequestInfo | URL, init?: RequestInit) => { + const url = input.toString(); + if (url.includes("/api/compression/engines")) { + return new Response( + JSON.stringify({ + engines: [ + { + id: "lite", + name: "Lite", + description: "Lite engine", + icon: "compress", + stackable: true, + stackPriority: 5, + metadata: { description: "Lite metadata" }, + configSchema: [ + { + key: "compressToolResults", + type: "boolean", + label: "Proactively truncate long tool results", + defaultValue: true, + }, + { + key: "maxToolLength", + type: "number", + label: "Maximum tool-result length", + defaultValue: 2000, + min: 256, + max: 1_000_000, + }, + ], + }, + ], + }), + { status: 200, headers: { "Content-Type": "application/json" } } + ); + } + if (url.includes("/api/settings/compression")) { + if (init?.method === "PUT") { + settingsPuts.push(JSON.parse(init.body as string) as Record); + } + return new Response( + JSON.stringify({ lite: { compressToolResults: true, maxToolLength: 8000 } }), + { + status: 200, + headers: { "Content-Type": "application/json" }, + } + ); + } + if (url.includes("/api/context/analytics/engine")) { + return new Response(JSON.stringify(ANALYTICS_PAYLOAD), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } + return new Response(JSON.stringify({}), { status: 404 }); + } + ); + + const { EngineConfigPage } = + await import("../../../src/shared/components/compression/EngineConfigPage"); + let container!: HTMLElement; + await act(async () => { + container = mountInContainer(); + await Promise.resolve(); + }); + + const numberInput = container.querySelector("input[type='number']") as HTMLInputElement | null; + expect(numberInput).not.toBeNull(); + await act(async () => { + const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set; + setter?.call(numberInput, ""); + numberInput!.dispatchEvent(new Event("input", { bubbles: true })); + await Promise.resolve(); + }); + + const saveButton = Array.from(container.querySelectorAll("button")).find((button) => + button.textContent?.includes("Save") + ); + expect(saveButton).toBeTruthy(); + await act(async () => { + saveButton?.click(); + await Promise.resolve(); + }); + + expect(settingsPuts.length).toBeGreaterThan(0); + const lite = settingsPuts[0]?.lite as Record | undefined; + expect(lite).toBeTruthy(); + expect(lite?.compressToolResults).toBe(true); + expect(lite?.maxToolLength).toBeNull(); + }); + + it("rejects Save when maxToolLength is a finite value outside the allowed range", async () => { + const settingsPuts: Array> = []; + vi.spyOn(globalThis, "fetch").mockImplementation( + async (input: RequestInfo | URL, init?: RequestInit) => { + const url = input.toString(); + if (url.includes("/api/compression/engines")) { + return new Response( + JSON.stringify({ + engines: [ + { + id: "lite", + name: "Lite", + description: "Lite engine", + icon: "compress", + stackable: true, + stackPriority: 5, + metadata: { description: "Lite metadata" }, + configSchema: [ + { + key: "compressToolResults", + type: "boolean", + label: "Proactively truncate long tool results", + defaultValue: true, + }, + { + key: "maxToolLength", + type: "number", + label: "Maximum tool-result length", + defaultValue: 2000, + min: 256, + max: 1_000_000, + }, + ], + }, + ], + }), + { status: 200, headers: { "Content-Type": "application/json" } } + ); + } + if (url.includes("/api/settings/compression")) { + if (init?.method === "PUT") { + settingsPuts.push(JSON.parse(init.body as string) as Record); + } + return new Response( + JSON.stringify({ lite: { compressToolResults: true, maxToolLength: 8000 } }), + { + status: 200, + headers: { "Content-Type": "application/json" }, + } + ); + } + if (url.includes("/api/context/analytics/engine")) { + return new Response(JSON.stringify(ANALYTICS_PAYLOAD), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); + } + return new Response(JSON.stringify({}), { status: 404 }); + } + ); + + const { EngineConfigPage } = + await import("../../../src/shared/components/compression/EngineConfigPage"); + let container!: HTMLElement; + await act(async () => { + container = mountInContainer(); + await Promise.resolve(); + }); + + const numberInput = container.querySelector("input[type='number']") as HTMLInputElement | null; + expect(numberInput).not.toBeNull(); + await act(async () => { + const setter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, "value")?.set; + setter?.call(numberInput, "100"); + numberInput!.dispatchEvent(new Event("input", { bubbles: true })); + await Promise.resolve(); + }); + + const saveButton = Array.from(container.querySelectorAll("button")).find((button) => + button.textContent?.includes("Save") + ); + expect(saveButton).toBeTruthy(); + await act(async () => { + saveButton?.click(); + await Promise.resolve(); + }); + + expect(settingsPuts).toEqual([]); + expect(container.textContent).toContain("Failed to save configuration."); + }); + it("#8056: headroom minRows is persistable — Save PUTs headroom:{minRows:5}", async () => { const settingsPuts: { body: Record }[] = []; vi.spyOn(globalThis, "fetch").mockImplementation(