mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-11 01:32:22 +03:00
* feat(sse): unified media-part detection helper (image+audio, input_image) * refactor(guardrails): extractImageParts/comboStructure delegate to unified media detector * fix(sse): media detector — audio parts no longer shadow sibling/nested image indicators * fix(guardrails): close extract↔replace contract for input_image (allowlist + splice) * perf(guardrails): skip media traversal when bridge disabled; short-circuit combo image check * feat(guardrails): in-memory LRU bridge cache (sha256 keyed) * feat(settings): modalityBridge* schema with legacy visionBridge* fallback * feat(db): migrate visionBridge* settings to modalityBridge* (idempotent) * refactor(guardrails): harden bridge cache key/config + settings resolution (review minors) * feat(guardrails): vision bridge mode selector (auto/describe/reroute) short-circuit * feat(guardrails): task-aware vision description prompt (default on) * feat(guardrails): describe-path cache integration * docs(guardrails): review polish — cache-key coupling notes + helper header * feat(guardrails): in-memory bridge stats + modality-bridge response header * feat(api): modality bridge stats endpoint + header wiring in chat handler * docs(guardrails): document modality bridge mode/task-aware/cache/header + stats endpoint * chore: untrack _tasks symlink (inherited from base tip; blocks pre-commit tracked-artifacts gate) * fix(db): renumber modality bridge migration 139->140 (base renumbered ccr_blocks to 139) * docs(guardrails): migration filename touch-up 139->140 * docs(db): stale comment touch-ups after 139->140 renumber and #9688 landing * fix(db): renumber modality bridge migration 140->141 (base renumbered connection_runtime_state to 140) * test(db): migration test titles 139->141 --------- Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
93 lines
3.4 KiB
TypeScript
93 lines
3.4 KiB
TypeScript
import { test } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import fs from "node:fs";
|
|
import path from "node:path";
|
|
import Database from "better-sqlite3";
|
|
|
|
const MIGRATION_PATH = path.resolve("src/lib/db/migrations/141_modality_bridge_settings.sql");
|
|
const INITIAL_SCHEMA_PATH = path.resolve("src/lib/db/migrations/001_initial_schema.sql");
|
|
|
|
/** Extract the REAL key_value DDL from the initial schema so the seed can never drift. */
|
|
function keyValueTableDdl(): string {
|
|
const schema = fs.readFileSync(INITIAL_SCHEMA_PATH, "utf8");
|
|
const match = schema.match(/CREATE TABLE IF NOT EXISTS key_value \([\s\S]*?\);/);
|
|
assert.ok(match, "key_value CREATE TABLE block not found in 001_initial_schema.sql");
|
|
return match[0];
|
|
}
|
|
|
|
function createSeededDb(): InstanceType<typeof Database> {
|
|
const db = new Database(":memory:");
|
|
db.exec(keyValueTableDdl());
|
|
return db;
|
|
}
|
|
|
|
function getSetting(db: InstanceType<typeof Database>, key: string): string | undefined {
|
|
const row = db
|
|
.prepare("SELECT value FROM key_value WHERE namespace = 'settings' AND key = ?")
|
|
.get(key) as { value: string } | undefined;
|
|
return row?.value;
|
|
}
|
|
|
|
test("migration 141 copies legacy visionBridge* settings to modalityBridge* keys", () => {
|
|
const sql = fs.readFileSync(MIGRATION_PATH, "utf8");
|
|
const db = createSeededDb();
|
|
try {
|
|
const seed = db.prepare(
|
|
"INSERT INTO key_value (namespace, key, value) VALUES ('settings', ?, ?)"
|
|
);
|
|
seed.run("visionBridgeEnabled", "true");
|
|
seed.run("visionBridgeModel", "openai/gpt-4o-mini");
|
|
seed.run("visionBridgePrompt", "legacy prompt");
|
|
seed.run("visionBridgeTimeout", "45000");
|
|
seed.run("visionBridgeMaxImages", "6");
|
|
|
|
db.exec(sql);
|
|
|
|
assert.equal(getSetting(db, "modalityBridgeVisionEnabled"), "true");
|
|
assert.equal(getSetting(db, "modalityBridgeVisionModel"), "openai/gpt-4o-mini");
|
|
assert.equal(getSetting(db, "modalityBridgeVisionPrompt"), "legacy prompt");
|
|
assert.equal(getSetting(db, "modalityBridgeVisionTimeout"), "45000");
|
|
assert.equal(getSetting(db, "modalityBridgeVisionMaxImages"), "6");
|
|
// Legacy keys stay untouched (one-cycle rollback window).
|
|
assert.equal(getSetting(db, "visionBridgeEnabled"), "true");
|
|
} finally {
|
|
db.close();
|
|
}
|
|
});
|
|
|
|
test("migration 141 is idempotent and never overwrites an existing new key", () => {
|
|
const sql = fs.readFileSync(MIGRATION_PATH, "utf8");
|
|
const db = createSeededDb();
|
|
try {
|
|
const seed = db.prepare(
|
|
"INSERT INTO key_value (namespace, key, value) VALUES ('settings', ?, ?)"
|
|
);
|
|
seed.run("visionBridgeModel", "openai/gpt-4o-mini");
|
|
// Operator already set the new key — the migration must not clobber it.
|
|
seed.run("modalityBridgeVisionModel", "gemini/gemini-2.5-flash");
|
|
|
|
db.exec(sql);
|
|
db.exec(sql); // re-run: idempotent
|
|
|
|
assert.equal(getSetting(db, "modalityBridgeVisionModel"), "gemini/gemini-2.5-flash");
|
|
const count = db
|
|
.prepare("SELECT COUNT(*) AS n FROM key_value WHERE namespace = 'settings'")
|
|
.get() as { n: number };
|
|
assert.equal(count.n, 2);
|
|
} finally {
|
|
db.close();
|
|
}
|
|
});
|
|
|
|
test("migration 141 is a no-op on a database without legacy settings", () => {
|
|
const sql = fs.readFileSync(MIGRATION_PATH, "utf8");
|
|
const db = createSeededDb();
|
|
try {
|
|
db.exec(sql);
|
|
const count = db.prepare("SELECT COUNT(*) AS n FROM key_value").get() as { n: number };
|
|
assert.equal(count.n, 0);
|
|
} finally {
|
|
db.close();
|
|
}
|
|
});
|