219 lines
9.3 KiB
JavaScript
219 lines
9.3 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import {
|
|
createMemoryV2AdminConfigService,
|
|
memoryV2AdminConfigInternals,
|
|
} from './memory-v2-admin-config.mjs';
|
|
|
|
function createPool(seedRow = null) {
|
|
const state = { row: seedRow };
|
|
return {
|
|
state,
|
|
async query(sql, params) {
|
|
if (sql.includes('CREATE TABLE')) return [[], []];
|
|
if (sql.includes('SELECT config_json')) {
|
|
return [state.row ? [state.row] : [], []];
|
|
}
|
|
if (sql.includes('INSERT INTO h5_memory_v2_admin_config')) {
|
|
state.row = {
|
|
config_json: params[1],
|
|
secrets_ciphertext: params[2],
|
|
secrets_iv: params[3],
|
|
secrets_tag: params[4],
|
|
updated_by: params[5],
|
|
updated_at: params[6],
|
|
};
|
|
return [[], []];
|
|
}
|
|
throw new Error(`Unexpected query: ${sql}`);
|
|
},
|
|
};
|
|
}
|
|
|
|
test('memory v2 admin config service loads defaults from env and masks secrets', async () => {
|
|
const service = createMemoryV2AdminConfigService(createPool(), {
|
|
env: {
|
|
MEMORY_ENABLED: '1',
|
|
MEMORY_BACKEND: 'pgvector',
|
|
MEMIND_CHAT_LLM_ROUTER_ENABLED: '1',
|
|
MEMIND_CHAT_ROUTER_MODEL_PROVIDER_KEY_ID: 'key-router',
|
|
MEMIND_CHAT_ROUTER_MODEL: 'deepseek-chat',
|
|
MEMORY_PGVECTOR_DATABASE_URL: 'postgresql://user:pass@127.0.0.1:5432/memory',
|
|
MEMORY_QDRANT_API_KEY: 'secret-qdrant',
|
|
},
|
|
});
|
|
|
|
const result = await service.getAdminConfig();
|
|
|
|
assert.equal(result.config.global.enabled, true);
|
|
assert.equal(result.config.global.backend, 'pgvector');
|
|
assert.equal(result.config.chatIntentRouter.enabled, true);
|
|
assert.equal(result.config.chatIntentRouter.modelProviderKeyId, 'key-router');
|
|
assert.equal(result.config.chatIntentRouter.model, 'deepseek-chat');
|
|
assert.equal(result.config.pgvector.databaseUrlConfigured, true);
|
|
assert.match(result.config.pgvector.databaseUrlMasked, /^post\*+/);
|
|
assert.equal(result.config.qdrant.apiKeyConfigured, true);
|
|
});
|
|
|
|
test('memory v2 admin config service persists non-secret and secret patches', async () => {
|
|
const pool = createPool();
|
|
const service = createMemoryV2AdminConfigService(pool, {
|
|
env: {
|
|
MEMORY_ENABLED: '0',
|
|
MEMORY_BACKEND: 'legacy',
|
|
},
|
|
});
|
|
|
|
const updated = await service.updateAdminConfig({
|
|
global: { enabled: true, backend: 'qdrant' },
|
|
chatIntentRouter: {
|
|
enabled: true,
|
|
modelProviderKeyId: 'key-router',
|
|
model: 'deepseek-chat',
|
|
minConfidence: '0.7',
|
|
memoryResolveEnabled: true,
|
|
memoryResolveLimit: '8',
|
|
timeoutMs: '1500',
|
|
fallbackRoute: 'agent_orchestration',
|
|
},
|
|
qdrant: {
|
|
enabled: true,
|
|
url: 'http://127.0.0.1:6333',
|
|
apiKey: 'secret-qdrant',
|
|
},
|
|
candidateMemory: {
|
|
enabled: true,
|
|
mode: 'shadow',
|
|
minImportance: '0.75',
|
|
minConfidence: '0.85',
|
|
maxPending: '500',
|
|
persistenceEnabled: true,
|
|
},
|
|
runtimeControl: {
|
|
agentResolveEnabled: true,
|
|
agentInjectionMode: 'shadow',
|
|
agentCanaryUserIds: 'user-1,user-2',
|
|
agentResolveLimit: '3',
|
|
agentResolveTimeoutMs: '1200',
|
|
promotionEnabled: false,
|
|
compactionV2Enabled: false,
|
|
reflectionEnabled: false,
|
|
},
|
|
policy: {
|
|
enabled: true,
|
|
saveExplicit: true,
|
|
rejectSensitive: true,
|
|
requireEvidence: true,
|
|
retentionDays: '365',
|
|
},
|
|
retriever: {
|
|
enabled: true,
|
|
episodicEnabled: true,
|
|
episodicMode: 'canary',
|
|
episodicCanaryUserIds: 'user-1,user-2',
|
|
semanticEnabled: true,
|
|
preferenceEnabled: true,
|
|
goalEnabled: true,
|
|
limit: '12',
|
|
tokenBudget: '1800',
|
|
timeoutMs: '1200',
|
|
},
|
|
lifecycle: { enabled: true, dedupeEnabled: true, conflictReview: true },
|
|
persona: { enabled: true, provider: 'ai-mind', shadowMode: true },
|
|
graph: { enabled: true, provider: 'postgres', maxDepth: '2' },
|
|
userMemory: { enabled: true, reviewEnabled: true, forgetEnabled: true, deletePropagation: true },
|
|
pluginHealth: { enabled: true, intervalSeconds: '60', timeoutMs: '1500', failureThreshold: '3', autoFallback: true },
|
|
}, { updatedBy: 'admin-1' });
|
|
|
|
assert.equal(updated.config.global.enabled, true);
|
|
assert.equal(updated.config.global.backend, 'qdrant');
|
|
assert.equal(updated.config.chatIntentRouter.enabled, true);
|
|
assert.equal(updated.config.chatIntentRouter.modelProviderKeyId, 'key-router');
|
|
assert.equal(updated.config.chatIntentRouter.minConfidence, '0.7');
|
|
assert.equal(updated.config.qdrant.enabled, true);
|
|
assert.equal(updated.config.qdrant.url, 'http://127.0.0.1:6333');
|
|
assert.equal(updated.config.qdrant.apiKeyConfigured, true);
|
|
assert.equal(updated.config.candidateMemory.mode, 'shadow');
|
|
assert.equal(updated.config.candidateMemory.persistenceEnabled, true);
|
|
assert.equal(updated.config.runtimeControl.agentResolveEnabled, true);
|
|
assert.equal(updated.config.runtimeControl.agentInjectionMode, 'shadow');
|
|
assert.equal(updated.config.runtimeControl.agentCanaryUserIds, 'user-1,user-2');
|
|
assert.equal(updated.config.runtimeControl.agentResolveLimit, '3');
|
|
assert.equal(updated.config.policy.requireEvidence, true);
|
|
assert.equal(updated.config.retriever.tokenBudget, '1800');
|
|
assert.equal(updated.config.retriever.episodicMode, 'canary');
|
|
assert.equal(updated.config.retriever.episodicCanaryUserIds, 'user-1,user-2');
|
|
assert.equal(updated.config.lifecycle.dedupeEnabled, true);
|
|
assert.equal(updated.config.persona.provider, 'ai-mind');
|
|
assert.equal(updated.config.graph.provider, 'postgres');
|
|
assert.equal(updated.config.userMemory.deletePropagation, true);
|
|
assert.equal(updated.config.pluginHealth.autoFallback, true);
|
|
assert.equal(updated.updatedBy, 'admin-1');
|
|
|
|
const runtimeState = await service.getRuntimeState();
|
|
assert.equal(runtimeState.overrides.MEMORY_ENABLED, '1');
|
|
assert.equal(runtimeState.overrides.MEMORY_BACKEND, 'qdrant');
|
|
assert.equal(runtimeState.overrides.MEMIND_CHAT_LLM_ROUTER_ENABLED, '1');
|
|
assert.equal(runtimeState.overrides.MEMIND_CHAT_ROUTER_MODEL_PROVIDER_KEY_ID, 'key-router');
|
|
assert.equal(runtimeState.overrides.MEMIND_CHAT_ROUTER_MODEL, 'deepseek-chat');
|
|
assert.equal(runtimeState.overrides.MEMIND_CHAT_ROUTER_MIN_CONFIDENCE, '0.7');
|
|
assert.equal(runtimeState.overrides.MEMIND_CHAT_ROUTER_MEMORY_ENABLED, '1');
|
|
assert.equal(runtimeState.overrides.MEMIND_CHAT_ROUTER_MEMORY_LIMIT, '8');
|
|
assert.equal(runtimeState.overrides.MEMIND_CHAT_ROUTER_TIMEOUT_MS, '1500');
|
|
assert.equal(runtimeState.overrides.MEMIND_CHAT_ROUTER_FALLBACK_ROUTE, 'agent_orchestration');
|
|
assert.equal(runtimeState.overrides.MEMORY_QDRANT_ENABLED, '1');
|
|
assert.equal(runtimeState.overrides.MEMORY_QDRANT_URL, 'http://127.0.0.1:6333');
|
|
assert.equal(runtimeState.overrides.MEMORY_QDRANT_API_KEY, 'secret-qdrant');
|
|
assert.equal(runtimeState.overrides.MEMORY_CANDIDATE_ENABLED, '1');
|
|
assert.equal(runtimeState.overrides.MEMORY_CANDIDATE_MODE, 'shadow');
|
|
assert.equal(runtimeState.overrides.MEMORY_CANDIDATE_PERSISTENCE_ENABLED, '1');
|
|
assert.equal(runtimeState.overrides.MEMORY_AGENT_RESOLVE_ENABLED, '1');
|
|
assert.equal(runtimeState.overrides.MEMORY_AGENT_INJECTION_MODE, 'shadow');
|
|
assert.equal(runtimeState.overrides.MEMORY_AGENT_CANARY_USER_IDS, 'user-1,user-2');
|
|
assert.equal(runtimeState.overrides.MEMORY_AGENT_RESOLVE_LIMIT, '3');
|
|
assert.equal(runtimeState.overrides.MEMORY_AGENT_RESOLVE_TIMEOUT_MS, '1200');
|
|
assert.equal(runtimeState.overrides.MEMORY_PROMOTION_ENABLED, '0');
|
|
assert.equal(runtimeState.overrides.MEMORY_POLICY_REQUIRE_EVIDENCE, '1');
|
|
assert.equal(runtimeState.overrides.MEMORY_RETRIEVER_TOKEN_BUDGET, '1800');
|
|
assert.equal(runtimeState.overrides.MEMORY_RETRIEVER_EPISODIC_MODE, 'canary');
|
|
assert.equal(runtimeState.overrides.MEMORY_RETRIEVER_EPISODIC_CANARY_USER_IDS, 'user-1,user-2');
|
|
assert.equal(runtimeState.overrides.MEMORY_LIFECYCLE_DEDUPE_ENABLED, '1');
|
|
assert.equal(runtimeState.overrides.MEMORY_PERSONA_PROVIDER, 'ai-mind');
|
|
assert.equal(runtimeState.overrides.MEMORY_GRAPH_PROVIDER, 'postgres');
|
|
assert.equal(runtimeState.overrides.MEMORY_USER_DELETE_PROPAGATION, '1');
|
|
assert.equal(runtimeState.overrides.MEMORY_PLUGIN_HEALTH_AUTO_FALLBACK, '1');
|
|
});
|
|
|
|
test('memory v2 admin config internals flatten booleans and numbers consistently', () => {
|
|
const env = memoryV2AdminConfigInternals.flattenConfig(
|
|
{
|
|
...memoryV2AdminConfigInternals.defaultConfigShape(),
|
|
global: { enabled: true, backend: 'pgvector', vectorEnabled: true },
|
|
chatIntentRouter: {
|
|
enabled: true,
|
|
minConfidence: '0.65',
|
|
memoryResolveEnabled: true,
|
|
memoryResolveLimit: '8',
|
|
timeoutMs: '1500',
|
|
},
|
|
pgvector: { enabled: true, poolMax: '5', limit: '8' },
|
|
},
|
|
{
|
|
...memoryV2AdminConfigInternals.defaultConfigShape(),
|
|
pgvector: { databaseUrl: 'postgresql://local/memory' },
|
|
},
|
|
);
|
|
|
|
assert.equal(env.MEMORY_ENABLED, '1');
|
|
assert.equal(env.MEMORY_BACKEND, 'pgvector');
|
|
assert.equal(env.MEMIND_CHAT_LLM_ROUTER_ENABLED, '1');
|
|
assert.equal(env.MEMIND_CHAT_ROUTER_MIN_CONFIDENCE, '0.65');
|
|
assert.equal(env.MEMIND_CHAT_ROUTER_MEMORY_ENABLED, '1');
|
|
assert.equal(env.MEMIND_CHAT_ROUTER_MEMORY_LIMIT, '8');
|
|
assert.equal(env.MEMIND_CHAT_ROUTER_TIMEOUT_MS, '1500');
|
|
assert.equal(env.MEMORY_VECTOR_ENABLED, '1');
|
|
assert.equal(env.MEMORY_PGVECTOR_ENABLED, '1');
|
|
assert.equal(env.MEMORY_PGVECTOR_POOL_MAX, '5');
|
|
assert.equal(env.MEMORY_PGVECTOR_LIMIT, '8');
|
|
});
|