Merge Memory V2 runtime facade
This commit is contained in:
@@ -0,0 +1,152 @@
|
||||
const REQUIRED_STATUS_KEYS = [
|
||||
'enabled',
|
||||
'backend',
|
||||
'selectedBackend',
|
||||
'profileEnabled',
|
||||
'eventLogEnabled',
|
||||
'vectorEnabled',
|
||||
'failOpen',
|
||||
'backends',
|
||||
];
|
||||
|
||||
function pass(name, details = {}) {
|
||||
return { name, ok: true, ...details };
|
||||
}
|
||||
|
||||
function fail(name, message, details = {}) {
|
||||
return { name, ok: false, message, ...details };
|
||||
}
|
||||
|
||||
function findBackend(status, name) {
|
||||
return Array.isArray(status?.backends)
|
||||
? status.backends.find((backend) => backend?.name === name)
|
||||
: null;
|
||||
}
|
||||
|
||||
function missingStatusKeys(status) {
|
||||
return REQUIRED_STATUS_KEYS.filter((key) => !(key in Object(status)));
|
||||
}
|
||||
|
||||
export function evaluateMemoryV2Health({
|
||||
status,
|
||||
writeResult = null,
|
||||
compactResult = null,
|
||||
expectedBackend = null,
|
||||
requireEnabled = false,
|
||||
requireFailOpen = true,
|
||||
} = {}) {
|
||||
const checks = [];
|
||||
const missing = missingStatusKeys(status);
|
||||
|
||||
checks.push(
|
||||
missing.length === 0
|
||||
? pass('status_contract')
|
||||
: fail('status_contract', `Missing status keys: ${missing.join(', ')}`, { missing }),
|
||||
);
|
||||
|
||||
if (requireEnabled) {
|
||||
checks.push(
|
||||
status?.enabled === true
|
||||
? pass('memory_enabled')
|
||||
: fail('memory_enabled', 'MEMORY_ENABLED is not enabled', { enabled: status?.enabled }),
|
||||
);
|
||||
} else {
|
||||
checks.push(pass('memory_enabled_optional', { enabled: Boolean(status?.enabled) }));
|
||||
}
|
||||
|
||||
if (requireFailOpen) {
|
||||
checks.push(
|
||||
status?.failOpen === true
|
||||
? pass('fail_open')
|
||||
: fail('fail_open', 'MEMORY_FAIL_OPEN must remain enabled for release safety', {
|
||||
failOpen: status?.failOpen,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (expectedBackend) {
|
||||
checks.push(
|
||||
status?.backend === expectedBackend
|
||||
? pass('expected_backend', { backend: status?.backend })
|
||||
: fail('expected_backend', `Expected MEMORY_BACKEND=${expectedBackend}`, {
|
||||
backend: status?.backend,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const legacy = findBackend(status, 'legacy-conversation-memory');
|
||||
checks.push(
|
||||
legacy?.available === true
|
||||
? pass('legacy_available')
|
||||
: fail('legacy_available', 'Legacy conversation-memory backend must be available', {
|
||||
legacy,
|
||||
}),
|
||||
);
|
||||
|
||||
checks.push(
|
||||
legacy?.supports?.write === true && legacy?.supports?.compact === true
|
||||
? pass('legacy_write_compact_supported')
|
||||
: fail('legacy_write_compact_supported', 'Legacy backend must support write and compact', {
|
||||
supports: legacy?.supports ?? null,
|
||||
}),
|
||||
);
|
||||
|
||||
const selectedBackend = status?.selectedBackend ?? null;
|
||||
checks.push(
|
||||
selectedBackend
|
||||
? pass('selected_backend_present', { selectedBackend })
|
||||
: fail('selected_backend_present', 'No selected Memory V2 backend for resolve'),
|
||||
);
|
||||
|
||||
const selected = findBackend(status, selectedBackend);
|
||||
checks.push(
|
||||
!selected || selected.available === true
|
||||
? pass('selected_backend_available', { selectedBackend })
|
||||
: fail('selected_backend_available', 'Selected backend is not available', { selected }),
|
||||
);
|
||||
|
||||
const pgvector = findBackend(status, 'pgvector');
|
||||
if (status?.backend === 'pgvector') {
|
||||
checks.push(
|
||||
pgvector?.available === true
|
||||
? pass('pgvector_canary_available')
|
||||
: pass('pgvector_canary_fallback', {
|
||||
reason: pgvector?.reason ?? 'unavailable',
|
||||
selectedBackend,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (writeResult) {
|
||||
checks.push(
|
||||
writeResult.source === 'legacy-conversation-memory'
|
||||
? pass('write_uses_legacy', { source: writeResult.source })
|
||||
: fail('write_uses_legacy', 'Memory V2 write must use legacy backend during semantic canary', {
|
||||
source: writeResult.source ?? null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
if (compactResult) {
|
||||
checks.push(
|
||||
compactResult.source === 'legacy-conversation-memory'
|
||||
? pass('compact_uses_legacy', { source: compactResult.source })
|
||||
: fail('compact_uses_legacy', 'Memory V2 compact must use legacy backend during semantic canary', {
|
||||
source: compactResult.source ?? null,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
ok: checks.every((check) => check.ok),
|
||||
checkedAt: new Date().toISOString(),
|
||||
summary: {
|
||||
enabled: Boolean(status?.enabled),
|
||||
backend: status?.backend ?? null,
|
||||
selectedBackend,
|
||||
failOpen: Boolean(status?.failOpen),
|
||||
backendCount: Array.isArray(status?.backends) ? status.backends.length : 0,
|
||||
},
|
||||
checks,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user