Add User Model Service and Temporal Recall for MeMind V0.1.
Introduce UMS ingest/snapshot pipeline, Context Planner with multi-source recall, runtime context injection, canonical user mapping, and session snapshot loading on auth/me. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Create RDS database memind_user_model (if missing) and run UMS schema migrate.
|
||||
*
|
||||
* Env (pick one):
|
||||
* UMS_DATABASE_URL=mysql://user:pass@host:3306/memind_user_model
|
||||
* DATABASE_URL / MEINPUT_DATABASE_URL (derives sibling DB on same host)
|
||||
*/
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import mysql from 'mysql2/promise';
|
||||
import path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const root = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
|
||||
function deriveUmsUrl() {
|
||||
if (process.env.UMS_DATABASE_URL) return process.env.UMS_DATABASE_URL;
|
||||
const base = process.env.DATABASE_URL ?? process.env.MEINPUT_DATABASE_URL ?? '';
|
||||
if (!base) return null;
|
||||
const u = new URL(base);
|
||||
u.pathname = '/memind_user_model';
|
||||
return u.toString();
|
||||
}
|
||||
|
||||
function adminUrl(dbUrl) {
|
||||
const u = new URL(dbUrl);
|
||||
u.pathname = '/';
|
||||
return u.toString();
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const umsUrl = deriveUmsUrl();
|
||||
if (!umsUrl) {
|
||||
console.error('Set UMS_DATABASE_URL or DATABASE_URL');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const admin = await mysql.createConnection({ uri: adminUrl(umsUrl) });
|
||||
try {
|
||||
await admin.query(
|
||||
'CREATE DATABASE IF NOT EXISTS memind_user_model CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci',
|
||||
);
|
||||
console.log('Database memind_user_model ready.');
|
||||
} finally {
|
||||
await admin.end();
|
||||
}
|
||||
|
||||
const child = spawnSync('node', ['user-model-service/migrate.mjs'], {
|
||||
cwd: root,
|
||||
stdio: 'inherit',
|
||||
env: { ...process.env, UMS_DATABASE_URL: umsUrl },
|
||||
});
|
||||
process.exit(child.status ?? 1);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Smoke: context plan → temporal recall query
|
||||
*
|
||||
* Env: MEMIND_BASE_URL (default http://127.0.0.1:8081)
|
||||
* MEMIND_USERNAME / MEMIND_PASSWORD
|
||||
*/
|
||||
const MEMIND_PORTAL = process.env.MEMIND_BASE_URL ?? 'http://127.0.0.1:8081';
|
||||
const MEMIND_API = `${MEMIND_PORTAL.replace(/\/$/, '')}/api`;
|
||||
const username = process.env.MEMIND_USERNAME ?? 'admin';
|
||||
const password = process.env.MEMIND_PASSWORD ?? '981122';
|
||||
|
||||
async function login() {
|
||||
const res = await fetch(`${MEMIND_PORTAL}/auth/login`, {
|
||||
method: 'POST',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify({ username, password }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(`login: ${JSON.stringify(data)}`);
|
||||
return { token: data.sessionToken, user: data.user };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const { token, user } = await login();
|
||||
const headers = {
|
||||
'content-type': 'application/json',
|
||||
cookie: `tkmind_user_session=${token}`,
|
||||
};
|
||||
|
||||
const query = process.argv[2] ?? '我昨天有什么重要的事?';
|
||||
const planRes = await fetch(`${MEMIND_API}/v1/context/plan`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({ query, now: new Date().toISOString() }),
|
||||
});
|
||||
const planBody = await planRes.json();
|
||||
if (!planRes.ok) throw new Error(`plan: ${JSON.stringify(planBody)}`);
|
||||
console.log('plan:', {
|
||||
query_type: planBody.plan.query_type,
|
||||
temporal_mode: planBody.plan.temporal_mode,
|
||||
sources: planBody.plan.sources,
|
||||
retrievals: planBody.plan.retrievals?.map((r) => r.source),
|
||||
});
|
||||
|
||||
const recallRes = await fetch(`${MEMIND_API}/v1/temporal-recall/query`, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({ plan: planBody.plan, limit: 10 }),
|
||||
});
|
||||
const recallBody = await recallRes.json();
|
||||
if (!recallRes.ok) throw new Error(`recall: ${JSON.stringify(recallBody)}`);
|
||||
console.log('recall stats:', recallBody.stats);
|
||||
const sample = recallBody.items?.[0] ?? recallBody.groups?.[0]?.items?.[0];
|
||||
if (sample) {
|
||||
console.log('sample:', sample.source, sample.title?.slice(0, 60), 'score=', sample.recall_score);
|
||||
} else {
|
||||
console.log('no items (user may have no data in range)');
|
||||
}
|
||||
console.log(`smoke OK for user ${user.id}`);
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user