mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
208 lines
6.5 KiB
JavaScript
208 lines
6.5 KiB
JavaScript
import { randomUUID } from "node:crypto";
|
|
|
|
function parsePositiveInteger(value, fallback, min = 1, max = Number.MAX_SAFE_INTEGER) {
|
|
const parsed = Number(value);
|
|
if (!Number.isFinite(parsed)) return fallback;
|
|
return Math.min(max, Math.max(min, Math.trunc(parsed)));
|
|
}
|
|
|
|
function parseJsonObject(value, fallback = {}) {
|
|
if (!value) return fallback;
|
|
try {
|
|
const parsed = JSON.parse(String(value));
|
|
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : fallback;
|
|
} catch {
|
|
return fallback;
|
|
}
|
|
}
|
|
|
|
export function resolveAuthorityE2eBaseUrl(value, env = process.env) {
|
|
const normalized = String(value || "").replace(/\/+$/g, "");
|
|
if (/^https?:\/\//i.test(normalized)) return normalized;
|
|
const origin = String(env.AUTHORITY_E2E_ORIGIN || "").replace(/\/+$/g, "");
|
|
if (origin && normalized.startsWith("/")) return `${origin}${normalized}`;
|
|
throw new Error(
|
|
"AUTHORITY_E2E_BASE_URL must be absolute, or set AUTHORITY_E2E_ORIGIN for relative plugin paths",
|
|
);
|
|
}
|
|
|
|
export function createAuthorityE2eHeaderProvider(env = process.env) {
|
|
const staticHeaders = parseJsonObject(env.AUTHORITY_E2E_HEADER_JSON, {});
|
|
const token = String(env.AUTHORITY_E2E_TOKEN || "").trim();
|
|
const cookie = String(env.AUTHORITY_E2E_COOKIE || "").trim();
|
|
return () => ({
|
|
...staticHeaders,
|
|
...(token ? { Authorization: /^Bearer\s+/i.test(token) ? token : `Bearer ${token}` } : {}),
|
|
...(cookie ? { Cookie: cookie } : {}),
|
|
});
|
|
}
|
|
|
|
export function createAuthorityE2eFetchWithTimeout(
|
|
timeoutMs,
|
|
baseFetchImpl = typeof fetch === "function" ? fetch.bind(globalThis) : null,
|
|
) {
|
|
if (typeof baseFetchImpl !== "function") {
|
|
throw new Error("Authority E2E fetch unavailable");
|
|
}
|
|
return async (url, options = {}) => {
|
|
const controller = new AbortController();
|
|
const timer = setTimeout(() => {
|
|
controller.abort(new Error(`Authority E2E request timeout after ${timeoutMs}ms`));
|
|
}, timeoutMs);
|
|
try {
|
|
return await baseFetchImpl(url, {
|
|
...options,
|
|
signal: controller.signal,
|
|
});
|
|
} finally {
|
|
clearTimeout(timer);
|
|
}
|
|
};
|
|
}
|
|
|
|
export function createAuthorityE2eContext(options = {}) {
|
|
const env =
|
|
options.env && typeof options.env === "object" && !Array.isArray(options.env)
|
|
? options.env
|
|
: process.env;
|
|
const skipMessage = String(
|
|
options.skipMessage ||
|
|
"authority E2E skipped: set AUTHORITY_E2E_BASE_URL to run against a real Authority server",
|
|
);
|
|
const baseUrl = String(env.AUTHORITY_E2E_BASE_URL || "").trim();
|
|
if (!baseUrl) {
|
|
return {
|
|
skip: true,
|
|
skipMessage,
|
|
env,
|
|
};
|
|
}
|
|
const resolvedBaseUrl = resolveAuthorityE2eBaseUrl(baseUrl, env);
|
|
const timeoutMs = parsePositiveInteger(env.AUTHORITY_E2E_TIMEOUT_MS, 15000, 1000, 300000);
|
|
const jobWaitTimeoutMs = parsePositiveInteger(
|
|
env.AUTHORITY_E2E_JOB_WAIT_TIMEOUT_MS,
|
|
15000,
|
|
1000,
|
|
300000,
|
|
);
|
|
const fetchImpl = createAuthorityE2eFetchWithTimeout(
|
|
timeoutMs,
|
|
options.fetchImpl || (typeof fetch === "function" ? fetch.bind(globalThis) : null),
|
|
);
|
|
const headerProvider = createAuthorityE2eHeaderProvider(env);
|
|
const runId = String(
|
|
env.AUTHORITY_E2E_RUN_ID || `authority-e2e-${Date.now()}-${randomUUID().slice(0, 8)}`,
|
|
).replace(/[^A-Za-z0-9._:-]+/g, "-");
|
|
const chatId = String(env.AUTHORITY_E2E_CHAT_ID || `st-bme-${runId}`);
|
|
const namespace = String(env.AUTHORITY_E2E_NAMESPACE || `st-bme-e2e-${runId}`);
|
|
const collectionId = String(env.AUTHORITY_E2E_COLLECTION_ID || `${namespace}::${chatId}`);
|
|
const blobPath = String(env.AUTHORITY_E2E_BLOB_PATH || `st-bme/e2e/${runId}/contract.json`);
|
|
const embeddingApiUrl = String(env.AUTHORITY_E2E_EMBEDDING_API_URL || "").trim();
|
|
const embeddingApiKey = String(env.AUTHORITY_E2E_EMBEDDING_API_KEY || "").trim();
|
|
const embeddingModel = String(env.AUTHORITY_E2E_EMBEDDING_MODEL || "").trim();
|
|
return {
|
|
skip: false,
|
|
env,
|
|
baseUrl: resolvedBaseUrl,
|
|
timeoutMs,
|
|
jobWaitTimeoutMs,
|
|
fetchImpl,
|
|
headerProvider,
|
|
runId,
|
|
chatId,
|
|
namespace,
|
|
collectionId,
|
|
blobPath,
|
|
embeddingApiUrl,
|
|
embeddingApiKey,
|
|
embeddingModel,
|
|
};
|
|
}
|
|
|
|
export function createAuthorityE2eContractNode(id, title, nowMs) {
|
|
return {
|
|
id,
|
|
type: "fact",
|
|
fields: {
|
|
title,
|
|
summary: `${title} generated by Authority server-primary E2E contract smoke`,
|
|
},
|
|
seqRange: [1, 1],
|
|
scope: {
|
|
layer: "global",
|
|
ownerType: "system",
|
|
ownerId: "authority-e2e",
|
|
bucket: "contract",
|
|
regionKey: "authority-e2e-region",
|
|
},
|
|
storySegmentId: "authority-e2e-segment",
|
|
importance: 0.8,
|
|
archived: false,
|
|
updatedAt: nowMs,
|
|
};
|
|
}
|
|
|
|
export function createAuthorityE2eContractGraph(chatId, runId, options = {}) {
|
|
const nowMs = Number(options.nowMs || Date.now());
|
|
const revision = Number.isFinite(Number(options.revision))
|
|
? Math.max(1, Math.trunc(Number(options.revision)))
|
|
: 1;
|
|
const nodeA = createAuthorityE2eContractNode(`${runId}-node-a`, "Authority E2E Alpha", nowMs);
|
|
const nodeB = createAuthorityE2eContractNode(`${runId}-node-b`, "Authority E2E Beta", nowMs);
|
|
return {
|
|
meta: {
|
|
schemaVersion: 1,
|
|
chatId,
|
|
deviceId: "authority-e2e",
|
|
revision,
|
|
lastModified: nowMs,
|
|
nodeCount: 2,
|
|
edgeCount: 1,
|
|
tombstoneCount: 0,
|
|
},
|
|
nodes: [nodeA, nodeB],
|
|
edges: [
|
|
{
|
|
id: `${runId}-edge-a-b`,
|
|
fromId: nodeA.id,
|
|
toId: nodeB.id,
|
|
relation: "related",
|
|
type: "semantic",
|
|
strength: 0.7,
|
|
updatedAt: nowMs,
|
|
},
|
|
],
|
|
tombstones: [],
|
|
state: {
|
|
lastProcessedFloor: 1,
|
|
extractionCount: 1,
|
|
},
|
|
};
|
|
}
|
|
|
|
export function buildAuthorityE2eVectorEntries(graph = null) {
|
|
const nodes = Array.isArray(graph?.nodes) ? graph.nodes : [];
|
|
return nodes.map((node, index) => ({
|
|
nodeId: node.id,
|
|
index,
|
|
hash: `${node.id}:hash`,
|
|
text: `${node.fields?.title || node.id}. ${node.fields?.summary || ""}`,
|
|
vector: [1, index + 1, String(node.fields?.title || node.id || "").length / 100],
|
|
}));
|
|
}
|
|
|
|
export async function runAuthorityE2eStep(name, fn) {
|
|
const startedAt = Date.now();
|
|
try {
|
|
const result = await fn();
|
|
const durationMs = Date.now() - startedAt;
|
|
console.log(`authority E2E ${name}: ok (${durationMs}ms)`);
|
|
return { name, ok: true, durationMs, result };
|
|
} catch (error) {
|
|
const durationMs = Date.now() - startedAt;
|
|
console.error(`authority E2E ${name}: failed (${durationMs}ms)`);
|
|
console.error(error?.stack || error?.message || String(error));
|
|
throw error;
|
|
}
|
|
}
|