mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-06-13 18:31:16 +08:00
Implement vector recovery and refresh docs
This commit is contained in:
66
tests/runtime-history.mjs
Normal file
66
tests/runtime-history.mjs
Normal file
@@ -0,0 +1,66 @@
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
appendBatchJournal,
|
||||
cloneGraphSnapshot,
|
||||
createBatchJournalEntry,
|
||||
detectHistoryMutation,
|
||||
findJournalRecoveryPoint,
|
||||
snapshotProcessedMessageHashes,
|
||||
} from "../runtime-state.js";
|
||||
import { createEmptyGraph } from "../graph.js";
|
||||
|
||||
const chat = [
|
||||
{ is_user: true, mes: "你好" },
|
||||
{ is_user: false, mes: "我记住了。" },
|
||||
{ is_user: true, mes: "继续" },
|
||||
{ is_user: false, mes: "新的回复" },
|
||||
];
|
||||
|
||||
const hashes = snapshotProcessedMessageHashes(chat, 3);
|
||||
const cleanDetection = detectHistoryMutation(chat, {
|
||||
lastProcessedAssistantFloor: 3,
|
||||
processedMessageHashes: hashes,
|
||||
});
|
||||
assert.equal(cleanDetection.dirty, false);
|
||||
|
||||
const editedChat = structuredClone(chat);
|
||||
editedChat[1].mes = "我改过内容了。";
|
||||
const editedDetection = detectHistoryMutation(editedChat, {
|
||||
lastProcessedAssistantFloor: 3,
|
||||
processedMessageHashes: hashes,
|
||||
});
|
||||
assert.equal(editedDetection.dirty, true);
|
||||
assert.equal(editedDetection.earliestAffectedFloor, 1);
|
||||
|
||||
const truncatedChat = chat.slice(0, 2);
|
||||
const truncatedDetection = detectHistoryMutation(truncatedChat, {
|
||||
lastProcessedAssistantFloor: 3,
|
||||
processedMessageHashes: hashes,
|
||||
});
|
||||
assert.equal(truncatedDetection.dirty, true);
|
||||
assert.equal(truncatedDetection.earliestAffectedFloor, 2);
|
||||
|
||||
const graph = createEmptyGraph();
|
||||
graph.historyState.chatId = "chat-history-test";
|
||||
const beforeSnapshot = cloneGraphSnapshot(graph);
|
||||
graph.lastProcessedSeq = 3;
|
||||
graph.historyState.lastProcessedAssistantFloor = 3;
|
||||
const afterSnapshot = cloneGraphSnapshot(graph);
|
||||
appendBatchJournal(
|
||||
graph,
|
||||
createBatchJournalEntry(beforeSnapshot, afterSnapshot, {
|
||||
processedRange: [1, 3],
|
||||
postProcessArtifacts: ["compression"],
|
||||
vectorHashesInserted: [1234],
|
||||
}),
|
||||
);
|
||||
|
||||
const recoveryPoint = findJournalRecoveryPoint(graph, 2);
|
||||
assert.ok(recoveryPoint);
|
||||
assert.equal(recoveryPoint.journal.processedRange[1], 3);
|
||||
assert.equal(
|
||||
recoveryPoint.snapshotBefore.historyState.lastProcessedAssistantFloor,
|
||||
-1,
|
||||
);
|
||||
|
||||
console.log("runtime-history tests passed");
|
||||
71
tests/vector-config.mjs
Normal file
71
tests/vector-config.mjs
Normal file
@@ -0,0 +1,71 @@
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import vm from "node:vm";
|
||||
|
||||
async function loadVectorHelpers() {
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const sourcePath = path.resolve(__dirname, "../vector-index.js");
|
||||
const source = await fs.readFile(sourcePath, "utf8");
|
||||
|
||||
const pieces = [
|
||||
source.match(/export const BACKEND_VECTOR_SOURCES = \[[\s\S]*?\];/m)?.[0],
|
||||
source.match(/export const BACKEND_DEFAULT_MODELS = \{[\s\S]*?\};/m)?.[0],
|
||||
source.match(/const BACKEND_SOURCES_REQUIRING_API_URL = new Set\([\s\S]*?\);/m)?.[0],
|
||||
source.match(/export function normalizeOpenAICompatibleBaseUrl\(value, autoSuffix = true\) \{[\s\S]*?^\}/m)?.[0],
|
||||
source.match(/export function getVectorConfigFromSettings\(settings = \{\}\) \{[\s\S]*?^\}/m)?.[0],
|
||||
source.match(/export function isBackendVectorConfig\(config\) \{[\s\S]*?^\}/m)?.[0],
|
||||
source.match(/export function isDirectVectorConfig\(config\) \{[\s\S]*?^\}/m)?.[0],
|
||||
source.match(/export function validateVectorConfig\(config\) \{[\s\S]*?^\}/m)?.[0],
|
||||
].filter(Boolean);
|
||||
|
||||
if (pieces.length < 8) {
|
||||
throw new Error("无法从 vector-index.js 提取向量配置辅助函数");
|
||||
}
|
||||
|
||||
const context = vm.createContext({});
|
||||
const script = new vm.Script(`
|
||||
${pieces.join("\n\n").replaceAll("export ", "")}
|
||||
this.getVectorConfigFromSettings = getVectorConfigFromSettings;
|
||||
this.validateVectorConfig = validateVectorConfig;
|
||||
`);
|
||||
script.runInContext(context);
|
||||
return {
|
||||
getVectorConfigFromSettings: context.getVectorConfigFromSettings,
|
||||
validateVectorConfig: context.validateVectorConfig,
|
||||
};
|
||||
}
|
||||
|
||||
const { getVectorConfigFromSettings, validateVectorConfig } =
|
||||
await loadVectorHelpers();
|
||||
|
||||
const backendConfig = getVectorConfigFromSettings({
|
||||
embeddingTransportMode: "backend",
|
||||
embeddingBackendSource: "openai",
|
||||
embeddingBackendModel: "",
|
||||
});
|
||||
assert.equal(backendConfig.mode, "backend");
|
||||
assert.equal(backendConfig.source, "openai");
|
||||
assert.equal(backendConfig.model, "text-embedding-3-small");
|
||||
assert.equal(validateVectorConfig(backendConfig).valid, true);
|
||||
|
||||
const directConfig = getVectorConfigFromSettings({
|
||||
embeddingTransportMode: "direct",
|
||||
embeddingApiUrl: "https://example.com/v1/embeddings",
|
||||
embeddingApiKey: "sk-test",
|
||||
embeddingModel: "text-embedding-3-small",
|
||||
});
|
||||
assert.equal(directConfig.mode, "direct");
|
||||
assert.equal(directConfig.apiUrl, "https://example.com/v1");
|
||||
assert.equal(validateVectorConfig(directConfig).valid, true);
|
||||
|
||||
const invalidBackendConfig = getVectorConfigFromSettings({
|
||||
embeddingTransportMode: "backend",
|
||||
embeddingBackendSource: "vllm",
|
||||
embeddingBackendApiUrl: "",
|
||||
embeddingBackendModel: "BAAI/bge-m3",
|
||||
});
|
||||
assert.equal(validateVectorConfig(invalidBackendConfig).valid, false);
|
||||
|
||||
console.log("vector-config tests passed");
|
||||
Reference in New Issue
Block a user