196 lines
5.7 KiB
JavaScript
196 lines
5.7 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const BACKEND_SPECS = [
|
|
{
|
|
name: 'pgvector',
|
|
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_VECTOR_ENABLED', 'MEMORY_PGVECTOR_DATABASE_URL', 'MEMORY_PGVECTOR_EMBEDDING_MODULE'],
|
|
recommended: {
|
|
MEMORY_ENABLED: '1',
|
|
MEMORY_BACKEND: 'pgvector',
|
|
MEMORY_VECTOR_ENABLED: '1',
|
|
MEMORY_PGVECTOR_DATABASE_URL: 'postgresql://john@127.0.0.1:5432/memind_memory',
|
|
MEMORY_PGVECTOR_EMBEDDING_MODULE: './scripts/embed-memory-v2-local-hash.mjs',
|
|
},
|
|
},
|
|
{
|
|
name: 'qdrant',
|
|
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_QDRANT_ENABLED', 'MEMORY_QDRANT_URL', 'MEMORY_QDRANT_EMBEDDING_MODULE'],
|
|
recommended: {
|
|
MEMORY_ENABLED: '1',
|
|
MEMORY_BACKEND: 'qdrant',
|
|
MEMORY_QDRANT_ENABLED: '1',
|
|
MEMORY_QDRANT_URL: 'http://127.0.0.1:6333',
|
|
MEMORY_QDRANT_EMBEDDING_MODULE: './scripts/embed-memory-v2-local-hash.mjs',
|
|
},
|
|
},
|
|
{
|
|
name: 'weaviate',
|
|
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_WEAVIATE_ENABLED', 'MEMORY_WEAVIATE_URL', 'MEMORY_WEAVIATE_EMBEDDING_MODULE'],
|
|
recommended: {
|
|
MEMORY_ENABLED: '1',
|
|
MEMORY_BACKEND: 'weaviate',
|
|
MEMORY_WEAVIATE_ENABLED: '1',
|
|
MEMORY_WEAVIATE_URL: 'http://127.0.0.1:8080',
|
|
MEMORY_WEAVIATE_EMBEDDING_MODULE: './scripts/embed-memory-v2-local-hash.mjs',
|
|
},
|
|
},
|
|
{
|
|
name: 'mem0',
|
|
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_MEM0_ENABLED', 'MEMORY_MEM0_API_KEY'],
|
|
recommended: {
|
|
MEMORY_ENABLED: '1',
|
|
MEMORY_BACKEND: 'mem0',
|
|
MEMORY_MEM0_ENABLED: '1',
|
|
MEMORY_MEM0_API_KEY: 'replace-me',
|
|
},
|
|
},
|
|
{
|
|
name: 'letta',
|
|
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_LETTA_ENABLED', 'MEMORY_LETTA_API_KEY', 'MEMORY_LETTA_AGENT_ID'],
|
|
recommended: {
|
|
MEMORY_ENABLED: '1',
|
|
MEMORY_BACKEND: 'letta',
|
|
MEMORY_LETTA_ENABLED: '1',
|
|
MEMORY_LETTA_API_KEY: 'replace-me',
|
|
MEMORY_LETTA_AGENT_ID: 'agent_1',
|
|
},
|
|
},
|
|
{
|
|
name: 'neo4j',
|
|
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_NEO4J_ENABLED', 'MEMORY_NEO4J_HTTP_URL', 'MEMORY_NEO4J_USER', 'MEMORY_NEO4J_PASSWORD'],
|
|
recommended: {
|
|
MEMORY_ENABLED: '1',
|
|
MEMORY_BACKEND: 'neo4j',
|
|
MEMORY_NEO4J_ENABLED: '1',
|
|
MEMORY_NEO4J_HTTP_URL: 'http://127.0.0.1:7474',
|
|
MEMORY_NEO4J_USER: 'neo4j',
|
|
MEMORY_NEO4J_PASSWORD: 'replace-me',
|
|
},
|
|
},
|
|
{
|
|
name: 'redis-streams',
|
|
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_REDIS_STREAMS_ENABLED', 'MEMORY_REDIS_STREAMS_URL'],
|
|
recommended: {
|
|
MEMORY_ENABLED: '1',
|
|
MEMORY_BACKEND: 'redis-streams',
|
|
MEMORY_REDIS_STREAMS_ENABLED: '1',
|
|
MEMORY_REDIS_STREAMS_URL: 'redis://127.0.0.1:6379',
|
|
},
|
|
},
|
|
{
|
|
name: 'langgraph',
|
|
env: ['MEMORY_ENABLED', 'MEMORY_BACKEND', 'MEMORY_LANGGRAPH_ENABLED', 'MEMORY_LANGGRAPH_URL'],
|
|
recommended: {
|
|
MEMORY_ENABLED: '1',
|
|
MEMORY_BACKEND: 'langgraph',
|
|
MEMORY_LANGGRAPH_ENABLED: '1',
|
|
MEMORY_LANGGRAPH_URL: 'http://127.0.0.1:2024',
|
|
},
|
|
},
|
|
];
|
|
|
|
function usage() {
|
|
return `Usage:
|
|
node scripts/check-memory-v2-config-gaps.mjs [options]
|
|
|
|
Options:
|
|
--backend <name> Limit output to one backend.
|
|
--format <type> json | shell. Default: json.
|
|
-h, --help Show this help.
|
|
`;
|
|
}
|
|
|
|
export function parseMemoryV2ConfigGapArgs(argv = []) {
|
|
const options = {
|
|
backend: null,
|
|
format: 'json',
|
|
help: false,
|
|
};
|
|
for (let i = 0; i < argv.length; i += 1) {
|
|
const arg = argv[i];
|
|
if (arg === '--backend') {
|
|
options.backend = argv[++i] ?? '';
|
|
} else if (arg === '--format') {
|
|
options.format = argv[++i] ?? '';
|
|
} else if (arg === '-h' || arg === '--help') {
|
|
options.help = true;
|
|
} else {
|
|
throw new Error(`Unknown argument: ${arg}`);
|
|
}
|
|
}
|
|
if (options.backend && !BACKEND_SPECS.some((item) => item.name === options.backend)) {
|
|
throw new Error(`--backend must be one of: ${BACKEND_SPECS.map((item) => item.name).join(', ')}`);
|
|
}
|
|
if (!['json', 'shell'].includes(options.format)) {
|
|
throw new Error('--format must be json or shell');
|
|
}
|
|
return options;
|
|
}
|
|
|
|
function inspectBackend(spec, env = process.env) {
|
|
const present = {};
|
|
const missing = [];
|
|
for (const key of spec.env) {
|
|
const value = String(env[key] ?? '').trim();
|
|
if (value) present[key] = value;
|
|
else missing.push(key);
|
|
}
|
|
return {
|
|
backend: spec.name,
|
|
ready: missing.length === 0,
|
|
missing,
|
|
presentKeys: Object.keys(present),
|
|
recommended: spec.recommended,
|
|
};
|
|
}
|
|
|
|
function renderShell(reports) {
|
|
const chunks = [];
|
|
for (const report of reports) {
|
|
chunks.push(`# ${report.backend}`);
|
|
for (const [key, value] of Object.entries(report.recommended)) {
|
|
chunks.push(`export ${key}='${String(value).replaceAll("'", "'\\''")}'`);
|
|
}
|
|
chunks.push('');
|
|
}
|
|
return chunks.join('\n').trimEnd();
|
|
}
|
|
|
|
export async function runMemoryV2ConfigGapCli({
|
|
argv = process.argv.slice(2),
|
|
env = process.env,
|
|
stdout = process.stdout,
|
|
} = {}) {
|
|
const options = parseMemoryV2ConfigGapArgs(argv);
|
|
if (options.help) {
|
|
stdout.write(usage());
|
|
return 0;
|
|
}
|
|
|
|
const reports = BACKEND_SPECS
|
|
.filter((spec) => !options.backend || spec.name === options.backend)
|
|
.map((spec) => inspectBackend(spec, env));
|
|
|
|
if (options.format === 'shell') {
|
|
stdout.write(`${renderShell(reports)}\n`);
|
|
return reports.every((item) => item.ready) ? 0 : 1;
|
|
}
|
|
|
|
const output = {
|
|
ok: reports.every((item) => item.ready),
|
|
checkedAt: new Date().toISOString(),
|
|
reports,
|
|
};
|
|
stdout.write(`${JSON.stringify(output, null, 2)}\n`);
|
|
return output.ok ? 0 : 1;
|
|
}
|
|
|
|
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
runMemoryV2ConfigGapCli().then((code) => {
|
|
process.exitCode = code;
|
|
}).catch((err) => {
|
|
console.error(err instanceof Error ? err.message : err);
|
|
process.exitCode = 1;
|
|
});
|
|
}
|