mirror of
https://github.com/volcengine/OpenViking.git
synced 2026-09-30 17:28:07 +08:00
* fix(retrieval): honor tier ceilings and stop cooling unserved recalls Follow-up to #3534, from its post-merge review round. - The abstract-to-overview substitute now applies only to categories whose stored abstract is the whole file body. A resource or skill whose abstract is missing (`processing_mode=vectors_only`) or over the per-entry cap read its body and returned an overview instead, which for a short file is the body almost verbatim — crossing the opt-in deepening boundary those categories are documented to have, and doing it even under an explicit `detail="abstract"`. They now degrade to a bare URI and their body is never read. - A digest reporting `no_relevant` blanks `rendered`, so the client injects nothing, yet those URIs still entered the dedup ledger and were cooled for `dedup_turns` turns. That contradicted the ledger's own bare-URI grace rule and held memories back from the later turn they were relevant to. - Flat retrieval reaches built-in memory types outside the four named ones (`cases`, `patterns`, `tools`, `trajectories`, skill-usage memories) and reported them as an undeclared `memories` category that no tier or penalty table covered, so other-peer hits skipped the score penalty and callers could not pin their tier. The catch-all is now a declared category with both; it stays out of `quotas`, whose buckets it would overlap. Skill-usage memories also stop being misread as the `skills` category. - ZCode, OpenCode and pi own an OV session id but did not forward it, so their recalls silently ran without query expansion or cross-turn dedup. - The context-request deadline covered only the server's 30s rewrite fuse, but the pipeline is serial: expansion, retrieval and budgeting all precede it. 45s covers both fuses and the work between them. - `plugin` config scope and the `/recall` successor example now match what the code actually does. * fix(retrieval): make the context deadline and expansion opt-out reachable Forwarding a session id turns on server-side query expansion, an LLM call with its own 5s fuse, but neither the deadline that was supposed to cover it nor the switch that turns it off reached the two harnesses this PR newly enabled it for. - `contextRequestTimeoutMs()` now derives the deadline from the request body rather than from `cfg` plus a rewrite flag. The body is what states which server stages will run: a session takes the expansion fuse, `rewrite` takes the digest fuse, and a bare retrieval takes neither and keeps the caller's own budget. Reading `cfg` alone could not tell those apart. - OpenCode pinned `timeoutMs: 5000` after spreading the helper's options and pi ignored them entirely, so the helper's deadline was dead code in both. Their own budgets are now defaults rather than ceilings. OpenCode's 5s in particular was shorter than the expansion fuse it had just enabled, so a legal request would have been aborted client-side and dropped back to the path with neither dedup nor expansion. - OpenCode and pi read `OPENVIKING_RECALL_QUERY_EXPANSION` (and `recallQueryExpansion` in their own config files) and set the `configured` flag the shared body builder requires, so the documented opt-out exists where the cost was introduced. - The integration overview no longer implies every harness reads the same environment knobs, and describes the deadline as per-stage rather than rewrite-only.
91 lines
3.1 KiB
JavaScript
91 lines
3.1 KiB
JavaScript
// GENERATED FROM examples/memory-plugin-shared/lib. DO NOT EDIT.
|
|
/**
|
|
* Per-harness plugin settings from ovcli.conf.
|
|
*
|
|
* ovcli.conf carried connection fields only, so every harness had to keep its
|
|
* tuning knobs in ov.conf's harness section — a server-side file that a
|
|
* client-side plugin has no business editing. The `plugin` section fixes that:
|
|
* shared keys apply to every harness that reads them, and a per-harness object
|
|
* overrides them.
|
|
*
|
|
* {
|
|
* "url": "...", "api_key": "...",
|
|
* "plugin": {
|
|
* "recallQueryExpansion": "off",
|
|
* "recallCompress": "auto"
|
|
* }
|
|
* }
|
|
*
|
|
* Resolution stays env → ovcli.conf plugin.<harness> → ovcli.conf plugin →
|
|
* ov.conf harness section (legacy) → defaults.
|
|
*
|
|
* Consumers: Claude Code and Codex only. The other harnesses ship this module
|
|
* through `sync.mjs` but still read their knobs from the environment, so a
|
|
* `plugin` entry named after them is inert. `HARNESS_KEYS` lists what a harness
|
|
* loader actually consumes today — add a key here as its loader starts calling
|
|
* `loadPluginSettings`, not before, so the section never promises a knob that
|
|
* silently does nothing.
|
|
*/
|
|
|
|
import { readFileSync } from "node:fs";
|
|
import { homedir } from "node:os";
|
|
import { join, resolve as resolvePath } from "node:path";
|
|
|
|
const DEFAULT_OVCLI_CONF_PATH = join(homedir(), ".openviking", "ovcli.conf");
|
|
|
|
export const HARNESS_KEYS = {
|
|
claudeCode: "claude_code",
|
|
codex: "codex",
|
|
};
|
|
|
|
function tryLoadJson(path) {
|
|
try {
|
|
return JSON.parse(readFileSync(path, "utf-8"));
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Merge the shared plugin section with the harness-specific override.
|
|
* Returns a flat settings object; unknown keys pass through untouched so a
|
|
* harness can add its own knobs without touching this module.
|
|
*/
|
|
export function loadPluginSettings(harness, env = process.env) {
|
|
const path = resolvePath(
|
|
(env.OPENVIKING_CLI_CONFIG_FILE || DEFAULT_OVCLI_CONF_PATH).replace(/^~/, homedir()),
|
|
);
|
|
const file = tryLoadJson(path);
|
|
const plugin = file && typeof file.plugin === "object" && file.plugin ? file.plugin : {};
|
|
|
|
const shared = {};
|
|
for (const [key, value] of Object.entries(plugin)) {
|
|
if (value && typeof value === "object" && !Array.isArray(value)) continue;
|
|
shared[key] = value;
|
|
}
|
|
const scoped = harness && plugin[harness] && typeof plugin[harness] === "object"
|
|
? plugin[harness]
|
|
: {};
|
|
|
|
return { ...shared, ...scoped };
|
|
}
|
|
|
|
const REWRITE_MODES = new Set(["off", "client", "server", "auto"]);
|
|
|
|
/**
|
|
* Normalize the tri-state rewrite knob.
|
|
*
|
|
* off — never compress
|
|
* client — always compress locally through the host CLI
|
|
* server — always ask the server for a digest
|
|
* auto — prefer local (cost lands on the user's own subscription), fall back
|
|
* to the server when no healthy host CLI is available
|
|
*/
|
|
export function normalizeRewriteMode(value, fallback = "off") {
|
|
const raw = String(value ?? "").trim().toLowerCase();
|
|
if (REWRITE_MODES.has(raw)) return raw;
|
|
if (raw === "1" || raw === "true" || raw === "yes") return "auto";
|
|
if (raw === "0" || raw === "false" || raw === "no") return "off";
|
|
return fallback;
|
|
}
|