Files
memind/scripts/smoke-memory-v2-qdrant.mjs
2026-07-03 09:46:03 +08:00

128 lines
3.8 KiB
JavaScript

#!/usr/bin/env node
import { createQdrantHttpClient } from '../memory-v2-qdrant.mjs';
const DEFAULT_URL_ENV = 'MEMORY_QDRANT_URL';
function usage() {
return `
Usage:
node scripts/smoke-memory-v2-qdrant.mjs [options]
Runs a read-only Qdrant search smoke test. It does not create collections or write points.
Options:
--collection <name> Collection name. Default: MEMORY_QDRANT_COLLECTION or memind_memory.
--vector <csv> Query vector CSV. Default: 1,0,0.
--limit <number> Result limit. Default: 1.
--url-env <name> Env var containing Qdrant URL. Default: ${DEFAULT_URL_ENV}.
--api-key-env <name> Env var containing Qdrant API key. Default: MEMORY_QDRANT_API_KEY.
--timeout-ms <number> Request timeout. Default: 3000.
-h, --help Show this help.
`.trim();
}
function parseVector(value) {
const vector = String(value ?? '1,0,0')
.split(',')
.map((item) => Number(item.trim()));
if (!vector.length || vector.some((item) => !Number.isFinite(item))) {
throw new Error('--vector must be a comma-separated list of numbers');
}
return vector;
}
export function parseMemoryQdrantSmokeArgs(argv = [], env = process.env) {
const options = {
collection: env.MEMORY_QDRANT_COLLECTION || 'memind_memory',
vector: [1, 0, 0],
limit: 1,
urlEnv: DEFAULT_URL_ENV,
apiKeyEnv: 'MEMORY_QDRANT_API_KEY',
timeoutMs: 3000,
help: false,
};
for (let i = 0; i < argv.length; i += 1) {
const arg = argv[i];
switch (arg) {
case '--collection':
options.collection = argv[++i] ?? '';
break;
case '--vector':
options.vector = parseVector(argv[++i]);
break;
case '--limit':
options.limit = Number(argv[++i]);
break;
case '--url-env':
options.urlEnv = argv[++i] ?? '';
break;
case '--api-key-env':
options.apiKeyEnv = argv[++i] ?? '';
break;
case '--timeout-ms':
options.timeoutMs = Number(argv[++i]);
break;
case '-h':
case '--help':
options.help = true;
break;
default:
throw new Error(`Unknown argument: ${arg}`);
}
}
if (!options.collection) throw new Error('--collection requires a value');
if (!Number.isInteger(options.limit) || options.limit < 1) {
throw new Error('--limit requires a positive integer');
}
if (!options.urlEnv) throw new Error('--url-env requires a value');
if (!options.apiKeyEnv) throw new Error('--api-key-env requires a value');
if (!Number.isFinite(options.timeoutMs) || options.timeoutMs < 1) {
throw new Error('--timeout-ms requires a positive number');
}
return options;
}
export async function runMemoryQdrantSmokeCli({
argv = process.argv.slice(2),
env = process.env,
stdout = process.stdout,
fetchImpl = globalThis.fetch,
} = {}) {
const options = parseMemoryQdrantSmokeArgs(argv, env);
if (options.help) {
stdout.write(`${usage()}\n`);
return { ok: true, mode: 'help' };
}
const url = env[options.urlEnv];
if (!url) throw new Error(`Qdrant smoke requires ${options.urlEnv} to be set`);
const client = createQdrantHttpClient({
url,
apiKey: env[options.apiKeyEnv],
fetchImpl,
timeoutMs: options.timeoutMs,
});
const points = await client.search({
collection: options.collection,
vector: options.vector,
limit: options.limit,
});
const report = {
ok: true,
checkedAt: new Date().toISOString(),
collection: options.collection,
resultCount: points.length,
readOnly: true,
};
stdout.write(`${JSON.stringify(report, null, 2)}\n`);
return report;
}
if (import.meta.url === `file://${process.argv[1]}`) {
runMemoryQdrantSmokeCli().then((result) => {
process.exitCode = result.ok ? 0 : 1;
}).catch((err) => {
console.error(err instanceof Error ? err.message : err);
process.exitCode = 1;
});
}