@amemhq/core 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/chunk-K6WZTDM7.js +2353 -0
- package/dist/chunk-K6WZTDM7.js.map +1 -0
- package/dist/cli-migrate.cjs +1086 -0
- package/dist/cli-migrate.cjs.map +1 -0
- package/dist/cli-migrate.d.cts +30 -0
- package/dist/cli-migrate.d.ts +30 -0
- package/dist/cli-migrate.js +147 -0
- package/dist/cli-migrate.js.map +1 -0
- package/dist/index.cjs +289 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +181 -28
- package/dist/index.d.ts +181 -28
- package/dist/index.js +82 -2120
- package/dist/index.js.map +1 -1
- package/package.json +4 -1
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
collectionDimRaw,
|
|
4
|
+
countPointsRaw,
|
|
5
|
+
getCollection,
|
|
6
|
+
getEmbeddingDim,
|
|
7
|
+
getEmbeddingModel,
|
|
8
|
+
migrateCollection,
|
|
9
|
+
resolveAliasRaw,
|
|
10
|
+
scrollIdsRaw,
|
|
11
|
+
switchToMigrated
|
|
12
|
+
} from "./chunk-K6WZTDM7.js";
|
|
13
|
+
|
|
14
|
+
// src/cli-migrate.ts
|
|
15
|
+
var USAGE = `amem-migrate \u2014 move a memory store onto a different embedding model
|
|
16
|
+
|
|
17
|
+
amem-migrate what state the store is in, and what comes next
|
|
18
|
+
amem-migrate --apply do the next step; safe to interrupt and re-run
|
|
19
|
+
amem-migrate --switch put the new store behind the old name (irreversible)
|
|
20
|
+
|
|
21
|
+
Options
|
|
22
|
+
--from-collection <name> the store to migrate. Defaults to AMEM_COLLECTION.
|
|
23
|
+
--to-collection <name> where to build it. Derived from the source if omitted.
|
|
24
|
+
--no-refresh-fields skip re-extracting keywords for notes that never had
|
|
25
|
+
them. Makes the run completely offline.
|
|
26
|
+
-h, --help
|
|
27
|
+
|
|
28
|
+
Migrates onto amem's current default unless AMEM_EMBED_MODEL says otherwise:
|
|
29
|
+
|
|
30
|
+
AMEM_EMBED_MODEL=Alibaba-NLP/gte-multilingual-base amem-migrate --apply
|
|
31
|
+
|
|
32
|
+
Nothing before --switch touches the original. If a run looks wrong, delete the
|
|
33
|
+
target and start again.`;
|
|
34
|
+
function parseArgs(argv) {
|
|
35
|
+
const value = (flag) => {
|
|
36
|
+
const i = argv.indexOf(flag);
|
|
37
|
+
return i === -1 ? void 0 : argv[i + 1];
|
|
38
|
+
};
|
|
39
|
+
return {
|
|
40
|
+
help: argv.includes("-h") || argv.includes("--help"),
|
|
41
|
+
apply: argv.includes("--apply"),
|
|
42
|
+
switchOver: argv.includes("--switch"),
|
|
43
|
+
from: value("--from-collection"),
|
|
44
|
+
to: value("--to-collection"),
|
|
45
|
+
refreshFields: !argv.includes("--no-refresh-fields")
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function deriveTarget(source) {
|
|
49
|
+
const m = source.match(/^(.*)_v(\d+)$/);
|
|
50
|
+
return m ? `${m[1]}_v${Number(m[2]) + 1}` : `${source}_v2`;
|
|
51
|
+
}
|
|
52
|
+
function carried(args) {
|
|
53
|
+
return (args.from ? ` --from-collection ${args.from}` : "") + (args.to ? ` --to-collection ${args.to}` : "");
|
|
54
|
+
}
|
|
55
|
+
async function detect(from, to) {
|
|
56
|
+
const alias = await resolveAliasRaw(from);
|
|
57
|
+
if (alias !== null) return { kind: "switched", points: await countPointsRaw(from) };
|
|
58
|
+
const sourceDim = await collectionDimRaw(from);
|
|
59
|
+
if (sourceDim === null) return { kind: "no-source" };
|
|
60
|
+
const modelDim = await getEmbeddingDim();
|
|
61
|
+
if (sourceDim === modelDim) return { kind: "already-current", model: getEmbeddingModel() };
|
|
62
|
+
const notes = await countPointsRaw(from);
|
|
63
|
+
const targetDim = await collectionDimRaw(to);
|
|
64
|
+
if (targetDim === null) return { kind: "not-started", notes };
|
|
65
|
+
const done = (await scrollIdsRaw(to)).size;
|
|
66
|
+
return done >= notes ? { kind: "ready-to-switch", notes } : { kind: "partial", done, notes };
|
|
67
|
+
}
|
|
68
|
+
async function main() {
|
|
69
|
+
const args = parseArgs(process.argv.slice(2));
|
|
70
|
+
if (args.help) {
|
|
71
|
+
console.log(USAGE);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const from = args.from ?? getCollection();
|
|
75
|
+
const to = args.to ?? deriveTarget(from);
|
|
76
|
+
const flags = carried(args);
|
|
77
|
+
const phase = await detect(from, to);
|
|
78
|
+
console.log(`store: ${from}`);
|
|
79
|
+
console.log(`model: ${getEmbeddingModel()}`);
|
|
80
|
+
switch (phase.kind) {
|
|
81
|
+
case "no-source":
|
|
82
|
+
console.error(`
|
|
83
|
+
No collection named "${from}". Nothing to migrate.`);
|
|
84
|
+
process.exitCode = 1;
|
|
85
|
+
return;
|
|
86
|
+
case "switched":
|
|
87
|
+
console.log(`
|
|
88
|
+
"${from}" is already an alias \u2014 ${phase.points} notes, nothing to do.`);
|
|
89
|
+
return;
|
|
90
|
+
case "already-current":
|
|
91
|
+
console.log(`
|
|
92
|
+
Already on ${phase.model}. Nothing to migrate.`);
|
|
93
|
+
return;
|
|
94
|
+
case "not-started":
|
|
95
|
+
if (!args.apply) {
|
|
96
|
+
console.log(`
|
|
97
|
+
${phase.notes} notes to rebuild into "${to}".`);
|
|
98
|
+
console.log(`Run "amem-migrate${flags} --apply" to start. "${from}" is only read.`);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
case "partial":
|
|
103
|
+
if (!args.apply) {
|
|
104
|
+
console.log(`
|
|
105
|
+
${phase.done} of ${phase.notes} rebuilt into "${to}".`);
|
|
106
|
+
console.log(`Run "amem-migrate${flags} --apply" to carry on from there.`);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
break;
|
|
110
|
+
case "ready-to-switch":
|
|
111
|
+
if (!args.switchOver) {
|
|
112
|
+
console.log(`
|
|
113
|
+
All ${phase.notes} notes are in "${to}". "${from}" is untouched.`);
|
|
114
|
+
console.log(`Check it, then run "amem-migrate${flags} --switch" to put "${to}" behind the name "${from}".`);
|
|
115
|
+
console.log(`That drops "${from}" and cannot be undone.`);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
await switchToMigrated({ name: from, to });
|
|
119
|
+
console.log(`
|
|
120
|
+
Done. Nothing to change in your config \u2014 "${from}" now resolves to "${to}".`);
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (args.switchOver) {
|
|
124
|
+
console.error(`
|
|
125
|
+
Not finished yet \u2014 run "amem-migrate${flags} --apply" until it is before switching.`);
|
|
126
|
+
process.exitCode = 1;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const result = await migrateCollection({ from, to, dryRun: false, refreshFields: args.refreshFields });
|
|
130
|
+
console.log(`
|
|
131
|
+
${result.migrated + result.skipped} of ${result.total} rebuilt.`);
|
|
132
|
+
if (result.migrated + result.skipped >= result.total) {
|
|
133
|
+
console.log(`Check "${to}", then run "amem-migrate${flags} --switch".`);
|
|
134
|
+
} else {
|
|
135
|
+
console.log(`Run "amem-migrate${flags} --apply" again to carry on.`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
main().catch((err) => {
|
|
139
|
+
console.error(`amem-migrate: ${err instanceof Error ? err.message : String(err)}`);
|
|
140
|
+
process.exitCode = 1;
|
|
141
|
+
});
|
|
142
|
+
export {
|
|
143
|
+
carried,
|
|
144
|
+
deriveTarget,
|
|
145
|
+
parseArgs
|
|
146
|
+
};
|
|
147
|
+
//# sourceMappingURL=cli-migrate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/cli-migrate.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * amem-migrate — move a memory store onto a different embedding model.\n *\n * Written for the person who got here from an error message, not for someone\n * embedding the engine. A developer using `@amemhq/core` directly has\n * `migrateCollection()` and `switchToMigrated()` and can sequence them however\n * their deployment wants; this is the other audience, who installed a plugin and\n * whose memory has stopped working.\n *\n * So it takes no decisions from the caller that it can take itself. It works out\n * which phase the store is in, does the next safe thing, and prints the one\n * command that comes after. The target collection name is derived; nobody has to\n * know Qdrant has collections at all.\n *\n * Exactly one step is irreversible — dropping the old collection to free its name\n * for the alias — and that one has its own flag rather than being the tail of a\n * run that started out read-only.\n */\nimport { migrateCollection, switchToMigrated } from './migrate.js'\nimport { getCollection, collectionDimRaw, countPointsRaw, scrollIdsRaw, resolveAliasRaw } from './storage.js'\nimport { getEmbeddingModel, getEmbeddingDim } from './embedding.js'\n\nconst USAGE = `amem-migrate — move a memory store onto a different embedding model\n\n amem-migrate what state the store is in, and what comes next\n amem-migrate --apply do the next step; safe to interrupt and re-run\n amem-migrate --switch put the new store behind the old name (irreversible)\n\nOptions\n --from-collection <name> the store to migrate. Defaults to AMEM_COLLECTION.\n --to-collection <name> where to build it. Derived from the source if omitted.\n --no-refresh-fields skip re-extracting keywords for notes that never had\n them. Makes the run completely offline.\n -h, --help\n\nMigrates onto amem's current default unless AMEM_EMBED_MODEL says otherwise:\n\n AMEM_EMBED_MODEL=Alibaba-NLP/gte-multilingual-base amem-migrate --apply\n\nNothing before --switch touches the original. If a run looks wrong, delete the\ntarget and start again.`\n\nexport interface MigrateArgs {\n help: boolean\n apply: boolean\n switchOver: boolean\n from?: string\n to?: string\n refreshFields: boolean\n}\n\nexport function parseArgs(argv: string[]): MigrateArgs {\n const value = (flag: string): string | undefined => {\n const i = argv.indexOf(flag)\n return i === -1 ? undefined : argv[i + 1]\n }\n return {\n help: argv.includes('-h') || argv.includes('--help'),\n apply: argv.includes('--apply'),\n switchOver: argv.includes('--switch'),\n from: value('--from-collection'),\n to: value('--to-collection'),\n refreshFields: !argv.includes('--no-refresh-fields'),\n }\n}\n\n/**\n * `amem_notes` → `amem_notes_v2`, and `amem_notes_v2` → `amem_notes_v3`.\n *\n * Derived rather than asked for: the name is an implementation detail of a\n * mechanism the user is not supposed to have to learn, and a wrong guess at it is\n * how you end up with two half-migrated stores.\n */\nexport function deriveTarget(source: string): string {\n const m = source.match(/^(.*)_v(\\d+)$/)\n return m ? `${m[1]}_v${Number(m[2]) + 1}` : `${source}_v2`\n}\n\n/**\n * The collection flags this run was given, so every \"now run …\" line it prints is\n * copy-pasteable as-is.\n *\n * Without it a mode B operator who passed `--from-collection` would be told to run\n * a bare `amem-migrate --apply`, which falls back to AMEM_COLLECTION and migrates\n * a different store. Only the collection flags are carried: forgetting\n * `--no-refresh-fields` costs an LLM call, forgetting these loses the plot.\n */\nexport function carried(args: MigrateArgs): string {\n return (args.from ? ` --from-collection ${args.from}` : '') + (args.to ? ` --to-collection ${args.to}` : '')\n}\n\ntype Phase =\n | { kind: 'no-source' }\n | { kind: 'already-current'; model: string }\n | { kind: 'not-started'; notes: number }\n | { kind: 'partial'; done: number; notes: number }\n | { kind: 'ready-to-switch'; notes: number }\n | { kind: 'switched'; points: number }\n\nasync function detect(from: string, to: string): Promise<Phase> {\n const alias = await resolveAliasRaw(from)\n if (alias !== null) return { kind: 'switched', points: await countPointsRaw(from) }\n\n const sourceDim = await collectionDimRaw(from)\n if (sourceDim === null) return { kind: 'no-source' }\n\n const modelDim = await getEmbeddingDim()\n if (sourceDim === modelDim) return { kind: 'already-current', model: getEmbeddingModel() }\n\n const notes = await countPointsRaw(from)\n const targetDim = await collectionDimRaw(to)\n if (targetDim === null) return { kind: 'not-started', notes }\n\n const done = (await scrollIdsRaw(to)).size\n return done >= notes ? { kind: 'ready-to-switch', notes } : { kind: 'partial', done, notes }\n}\n\nasync function main(): Promise<void> {\n const args = parseArgs(process.argv.slice(2))\n if (args.help) {\n console.log(USAGE)\n return\n }\n\n const from = args.from ?? getCollection()\n const to = args.to ?? deriveTarget(from)\n const flags = carried(args)\n const phase = await detect(from, to)\n\n console.log(`store: ${from}`)\n console.log(`model: ${getEmbeddingModel()}`)\n\n switch (phase.kind) {\n case 'no-source':\n console.error(`\\nNo collection named \"${from}\". Nothing to migrate.`)\n process.exitCode = 1\n return\n\n case 'switched':\n console.log(`\\n\"${from}\" is already an alias — ${phase.points} notes, nothing to do.`)\n return\n\n case 'already-current':\n console.log(`\\nAlready on ${phase.model}. Nothing to migrate.`)\n return\n\n case 'not-started':\n if (!args.apply) {\n console.log(`\\n${phase.notes} notes to rebuild into \"${to}\".`)\n console.log(`Run \"amem-migrate${flags} --apply\" to start. \"${from}\" is only read.`)\n return\n }\n break\n\n case 'partial':\n if (!args.apply) {\n console.log(`\\n${phase.done} of ${phase.notes} rebuilt into \"${to}\".`)\n console.log(`Run \"amem-migrate${flags} --apply\" to carry on from there.`)\n return\n }\n break\n\n case 'ready-to-switch':\n if (!args.switchOver) {\n console.log(`\\nAll ${phase.notes} notes are in \"${to}\". \"${from}\" is untouched.`)\n console.log(`Check it, then run \"amem-migrate${flags} --switch\" to put \"${to}\" behind the name \"${from}\".`)\n console.log(`That drops \"${from}\" and cannot be undone.`)\n return\n }\n await switchToMigrated({ name: from, to })\n console.log(`\\nDone. Nothing to change in your config — \"${from}\" now resolves to \"${to}\".`)\n return\n }\n\n if (args.switchOver) {\n console.error(`\\nNot finished yet — run \"amem-migrate${flags} --apply\" until it is before switching.`)\n process.exitCode = 1\n return\n }\n\n const result = await migrateCollection({ from, to, dryRun: false, refreshFields: args.refreshFields })\n console.log(`\\n${result.migrated + result.skipped} of ${result.total} rebuilt.`)\n if (result.migrated + result.skipped >= result.total) {\n console.log(`Check \"${to}\", then run \"amem-migrate${flags} --switch\".`)\n } else {\n console.log(`Run \"amem-migrate${flags} --apply\" again to carry on.`)\n }\n}\n\nmain().catch((err: unknown) => {\n console.error(`amem-migrate: ${err instanceof Error ? err.message : String(err)}`)\n process.exitCode = 1\n})\n"],"mappings":"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","names":[]}
|