mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 20:32:20 +03:00
Smaller fixes from the 2nd code-review pass on plan 21. Backend / CLI / DB: - memoryTools.ts: error-path fallback no longer hardcodes retrievalStrategy:"exact"; uses DEFAULT_MEMORY_SETTINGS via toMemoryRetrievalConfig (D16 / Bug #7). - memory.mjs: applyLegacyTypeMap also runs on search / list / clear (was only on add); legacy user/feedback/project/reference remap to canonical types with a stderr warning (D17 / Bug #4). - migrationRunner.ts: case "073" guards via hasColumn(memories, needs_reindex) so an unmarked re-run of 073_memory_vec.sql is skipped cleanly (D27). UI: - MemoryEngineStatus: optional onConfigure callback; "Configurar →" CTAs on the Embedding / Qdrant / Rerank rows when those components are off or missing (matches §4.3 wireframe). - EngineTab: scroll IDs on config cards + handleConfigure wired to the status panel. Providers fetch moved from render body (setState-during-render anti-pattern) into useEffect. - RerankConfigCard: toggle is disabled when no provider has a key — blocks turning rerank ON without a provider, still allows turning it OFF (D13). - MemoriesTab: Import validates each entry against the canonical type enum before POST so invalid types are caught locally with a clear skipped count. Tooling: - package.json: test:all includes test:vitest:ui so the UI suite is no longer orphaned in CI. Tests: - cli-memory-commands: asserts updated for the new legacy->canonical remap on search/clear. - memory-embedding-resolve: drop always-true `|| reason.length > 0` clauses that neutralized two assertions. - memory-embedding-static-potion: model_load_failed test forces a real load failure via MEMORY_STATIC_CACHE_DIR=/dev/null/<subdir> and asserts EmbeddingError shape + reason + sanitized message (replaces the previous `assert.ok(true)`). - rerank-config-card.test.tsx: happy-path now uses a provider with hasKey; new test covers the disabled-toggle guard. Full memory suite green: 331/331 unit tests, 46/46 UI tests. typecheck:core, typecheck:noimplicit:core, check:cycles clean.
143 lines
5.0 KiB
TypeScript
143 lines
5.0 KiB
TypeScript
import { describe, it } from "node:test";
|
|
import assert from "node:assert/strict";
|
|
import { resolveEmbeddingSource } from "../../src/lib/memory/embedding/index";
|
|
import type { MemorySettingsExtended } from "../../src/shared/schemas/memory";
|
|
|
|
function makeSettings(overrides: Partial<MemorySettingsExtended> = {}): MemorySettingsExtended {
|
|
return {
|
|
embeddingSource: "auto",
|
|
embeddingProviderModel: null,
|
|
transformersEnabled: false,
|
|
staticEnabled: false,
|
|
rerankEnabled: false,
|
|
rerankProviderModel: null,
|
|
vectorStore: "auto",
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("resolveEmbeddingSource", () => {
|
|
it("auto + no key + no static + no transformers => source null", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({ embeddingSource: "auto" }));
|
|
assert.strictEqual(res.source, null);
|
|
// The reason must indicate the lack of any source — not just be non-empty.
|
|
assert.ok(
|
|
res.reason.toLowerCase().includes("nenhuma"),
|
|
`expected reason to mention "nenhuma", got: ${res.reason}`
|
|
);
|
|
});
|
|
|
|
it("auto + embeddingProviderModel set to openai/... => source remote", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({
|
|
embeddingSource: "auto",
|
|
embeddingProviderModel: "openai/text-embedding-3-small",
|
|
}));
|
|
assert.strictEqual(res.source, "remote");
|
|
assert.strictEqual(res.model, "openai/text-embedding-3-small");
|
|
});
|
|
|
|
it("auto + no model + staticEnabled=true => source static", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({
|
|
embeddingSource: "auto",
|
|
embeddingProviderModel: null,
|
|
staticEnabled: true,
|
|
}));
|
|
assert.strictEqual(res.source, "static");
|
|
assert.ok(res.model !== null);
|
|
});
|
|
|
|
it("auto + no model + staticEnabled=false + transformersEnabled=true => source transformers", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({
|
|
embeddingSource: "auto",
|
|
embeddingProviderModel: null,
|
|
staticEnabled: false,
|
|
transformersEnabled: true,
|
|
}));
|
|
assert.strictEqual(res.source, "transformers");
|
|
});
|
|
|
|
it("explicit 'remote' + no model => source null with no_key reason", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({
|
|
embeddingSource: "remote",
|
|
embeddingProviderModel: null,
|
|
}));
|
|
assert.strictEqual(res.source, null);
|
|
// The reason must reference the missing key, not just be non-empty.
|
|
assert.ok(
|
|
res.reason.includes("no_key") || res.reason.includes("configurado"),
|
|
`expected reason to mention "no_key" or "configurado", got: ${res.reason}`
|
|
);
|
|
});
|
|
|
|
it("explicit 'remote' + model set => source remote (no fallback)", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({
|
|
embeddingSource: "remote",
|
|
embeddingProviderModel: "openai/text-embedding-3-small",
|
|
}));
|
|
assert.strictEqual(res.source, "remote");
|
|
assert.strictEqual(res.model, "openai/text-embedding-3-small");
|
|
});
|
|
|
|
it("explicit 'static' + staticEnabled=true => source static", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({
|
|
embeddingSource: "static",
|
|
staticEnabled: true,
|
|
}));
|
|
assert.strictEqual(res.source, "static");
|
|
});
|
|
|
|
it("explicit 'static' + staticEnabled=false => source null", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({
|
|
embeddingSource: "static",
|
|
staticEnabled: false,
|
|
}));
|
|
assert.strictEqual(res.source, null);
|
|
});
|
|
|
|
it("explicit 'transformers' + transformersEnabled=true => source transformers", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({
|
|
embeddingSource: "transformers",
|
|
transformersEnabled: true,
|
|
}));
|
|
assert.strictEqual(res.source, "transformers");
|
|
});
|
|
|
|
it("explicit 'transformers' + transformersEnabled=false => source null", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({
|
|
embeddingSource: "transformers",
|
|
transformersEnabled: false,
|
|
}));
|
|
assert.strictEqual(res.source, null);
|
|
});
|
|
|
|
it("signature is deterministic for same inputs", () => {
|
|
const settings = makeSettings({
|
|
embeddingSource: "auto",
|
|
staticEnabled: true,
|
|
});
|
|
const res1 = resolveEmbeddingSource(settings);
|
|
const res2 = resolveEmbeddingSource(settings);
|
|
assert.strictEqual(res1.signature, res2.signature);
|
|
});
|
|
|
|
it("signature contains source:model:dim components", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({
|
|
embeddingSource: "static",
|
|
staticEnabled: true,
|
|
}));
|
|
assert.ok(res.signature.includes("static"), `signature should contain 'static': ${res.signature}`);
|
|
assert.ok(res.signature.includes(":"), "signature should contain colons");
|
|
});
|
|
|
|
it("signature for null source is null:null:null", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({ embeddingSource: "auto" }));
|
|
assert.strictEqual(res.signature, "null:null:null");
|
|
});
|
|
|
|
it("reason field is non-empty string", () => {
|
|
const res = resolveEmbeddingSource(makeSettings({ embeddingSource: "auto" }));
|
|
assert.ok(typeof res.reason === "string");
|
|
assert.ok(res.reason.length > 0);
|
|
});
|
|
});
|