om-memory-system 6.2.0-next.243 → 6.3.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.
Files changed (41) hide show
  1. package/dist/config.js +3 -0
  2. package/dist/core/memorisation-operations.js +2 -0
  3. package/dist/importer/external-api-test.d.ts +4 -2
  4. package/dist/importer/external-api-test.js +3 -2
  5. package/dist/services/ai/provider-config.d.ts +1 -1
  6. package/dist/services/ai/provider-error-kind.d.ts +7 -0
  7. package/dist/services/ai/provider-error-kind.js +9 -0
  8. package/dist/services/ai/providers/anthropic-messages.js +3 -2
  9. package/dist/services/ai/providers/base-provider.d.ts +2 -0
  10. package/dist/services/ai/providers/base-provider.js +4 -0
  11. package/dist/services/ai/providers/google-gemini.js +3 -2
  12. package/dist/services/ai/providers/openai-chat-completion.js +3 -2
  13. package/dist/services/ai/providers/openai-responses.js +3 -2
  14. package/dist/services/api-handlers.d.ts +3 -24
  15. package/dist/services/api-handlers.js +4 -114
  16. package/dist/services/client.js +1 -0
  17. package/dist/services/global-config-writer.d.ts +12 -5
  18. package/dist/services/global-config-writer.js +23 -8
  19. package/dist/services/linked-search.d.ts +2 -0
  20. package/dist/services/openrouter-models.d.ts +25 -0
  21. package/dist/services/openrouter-models.js +58 -0
  22. package/dist/services/openrouter-providers.d.ts +37 -0
  23. package/dist/services/openrouter-providers.js +147 -0
  24. package/dist/services/openrouter-routes.d.ts +9 -0
  25. package/dist/services/openrouter-routes.js +77 -0
  26. package/dist/services/openrouter-routing-config.d.ts +48 -0
  27. package/dist/services/openrouter-routing-config.js +143 -0
  28. package/dist/services/retrieval-rerank.d.ts +2 -0
  29. package/dist/services/retrieval-rerank.js +4 -1
  30. package/dist/services/settings-snapshot.d.ts +1 -0
  31. package/dist/services/settings-snapshot.js +5 -0
  32. package/dist/services/tag-migration.d.ts +48 -0
  33. package/dist/services/tag-migration.js +183 -0
  34. package/dist/services/web-route-modules.js +4 -0
  35. package/dist/web/assets/index-4epIq1xy.js +171 -0
  36. package/dist/web/assets/index-Tc6q6ju1.css +2 -0
  37. package/dist/web/assets/react-SIfiwpqq.js +1 -0
  38. package/dist/web/index.html +3 -2
  39. package/package.json +1 -1
  40. package/dist/web/assets/index-CoYuPYCS.css +0 -2
  41. package/dist/web/assets/index-jbZuE9HV.js +0 -171
@@ -0,0 +1,183 @@
1
+ /**
2
+ * AI tag backfill for memories saved without tags. Moved out of api-handlers.ts,
3
+ * which sits at its file-size ceiling. The caller passes the config, so this
4
+ * module never imports CONFIG.
5
+ */
6
+ import { embeddingService } from "./embedding.js";
7
+ import { tursoShardManager } from "./turso/shard-manager.js";
8
+ import { tursoVectorSearch } from "./turso/vector-search.js";
9
+ import { tursoConnectionManager } from "./turso/connection-manager.js";
10
+ import { ensureTursoReady } from "./turso/ready.js";
11
+ import { formatTagsForEmbedding } from "./turso/vector-utils.js";
12
+ import { UNTAGGED_NORMAL_PREDICATE } from "./memory-kind.js";
13
+ import { log } from "./logger.js";
14
+ import { resettable } from "./spaces/store-handles.js";
15
+ import { providerErrorKind } from "./ai/provider-error-kind.js";
16
+ const idleMigration = () => ({
17
+ processed: 0,
18
+ total: 0,
19
+ currentBatch: 0,
20
+ totalBatches: 0,
21
+ isComplete: true,
22
+ tagged: 0,
23
+ errors: [],
24
+ });
25
+ let migrationProgress = idleMigration();
26
+ /** The untagged memories this migration run covers, fixed when the run starts. */
27
+ let migrationQueue = [];
28
+ /** Goes up on a memory space switch, so a batch still running for the old space stops. */
29
+ let spaceGeneration = 0;
30
+ /** The batch now running. Batches run one at a time, so two never take the same memories. */
31
+ let batchInFlight = Promise.resolve();
32
+ // The queue holds paths in the active space, so a space switch drops the run.
33
+ // The next batch then builds a new queue for the new space.
34
+ resettable("tag-migration", null, () => {
35
+ spaceGeneration++;
36
+ migrationProgress = idleMigration();
37
+ });
38
+ /** The longest error text sent to the page. */
39
+ const ERROR_TEXT_LIMIT = 300;
40
+ export async function handleDetectTagMigration() {
41
+ try {
42
+ await ensureTursoReady();
43
+ let untaggedCount = 0;
44
+ for (const shard of await tursoShardManager.getAllShards("project", "")) {
45
+ const db = await tursoConnectionManager.getConnection(shard.dbPath);
46
+ const row = await db.get(`SELECT COUNT(*) as count FROM memories WHERE ${UNTAGGED_NORMAL_PREDICATE}`);
47
+ untaggedCount += Number(row?.count ?? 0);
48
+ }
49
+ return { success: true, data: { needsMigration: untaggedCount > 0, count: untaggedCount } };
50
+ }
51
+ catch (error) {
52
+ return { success: false, error: String(error) };
53
+ }
54
+ }
55
+ export async function handleGetTagMigrationProgress() {
56
+ return { success: true, data: migrationProgress };
57
+ }
58
+ /** The error as the page may show it: short, and with the API key removed. */
59
+ function pageErrorText(error, apiKey) {
60
+ let text = error instanceof Error ? error.message : String(error);
61
+ if (apiKey)
62
+ text = text.split(apiKey).join("[redacted]");
63
+ return text.length > ERROR_TEXT_LIMIT ? `${text.slice(0, ERROR_TEXT_LIMIT)}…` : text;
64
+ }
65
+ /**
66
+ * Tag the memories that have no tags, a few per request. Only untagged
67
+ * memories are touched: tagged ones keep their vectors. A memory that fails
68
+ * is recorded and passed over, so one bad row cannot stall the run. An error
69
+ * that fails every memory the same way (no credit, a rejected key) ends the
70
+ * run at once, so a later run starts over with every untagged memory.
71
+ */
72
+ export function runTagMigrationBatch(config, batchSize = 5) {
73
+ const run = batchInFlight.then(() => runOneBatch(config, batchSize));
74
+ batchInFlight = run.catch(() => undefined);
75
+ return run;
76
+ }
77
+ async function runOneBatch(config, batchSize) {
78
+ const generation = spaceGeneration;
79
+ try {
80
+ await ensureTursoReady();
81
+ const { AIProviderFactory } = await import("./ai/ai-provider-factory.js");
82
+ const { buildMemoryProviderConfig } = await import("./ai/provider-config.js");
83
+ const providerConfig = buildMemoryProviderConfig(config, {
84
+ maxIterations: 1,
85
+ iterationTimeout: 30000,
86
+ });
87
+ const provider = AIProviderFactory.createProvider(config.memoryProvider, providerConfig);
88
+ if (migrationProgress.isComplete) {
89
+ migrationQueue = [];
90
+ for (const shard of await tursoShardManager.getAllShards("project", "")) {
91
+ const db = await tursoConnectionManager.getConnection(shard.dbPath);
92
+ const rows = await db.all(`SELECT id FROM memories WHERE ${UNTAGGED_NORMAL_PREDICATE} ORDER BY id`);
93
+ for (const row of rows)
94
+ migrationQueue.push({ id: String(row.id), dbPath: shard.dbPath });
95
+ }
96
+ migrationProgress = {
97
+ ...idleMigration(),
98
+ total: migrationQueue.length,
99
+ totalBatches: Math.ceil(migrationQueue.length / batchSize),
100
+ isComplete: migrationQueue.length === 0,
101
+ };
102
+ }
103
+ const batch = migrationQueue.slice(migrationProgress.processed, migrationProgress.processed + batchSize);
104
+ let stopped = false;
105
+ for (const item of batch) {
106
+ if (generation !== spaceGeneration) {
107
+ return { success: false, error: "The memory space changed during tagging" };
108
+ }
109
+ try {
110
+ const db = await tursoConnectionManager.getConnection(item.dbPath);
111
+ const m = await db.get("SELECT * FROM memories WHERE id = ?", [item.id]);
112
+ // Tagged or deleted since the run started: nothing to do.
113
+ if (!m || (m.tags && String(m.tags).trim()))
114
+ continue;
115
+ const prompt = `Generate 2-4 short technical tags for this memory content:\n\n${m.content}\n\nReturn ONLY a comma-separated list of tags.`;
116
+ const result = await provider.executeToolCall("You are a technical tagger.", prompt, {
117
+ type: "function",
118
+ function: {
119
+ name: "save_tags",
120
+ description: "Save generated tags",
121
+ parameters: {
122
+ type: "object",
123
+ properties: { tags: { type: "array", items: { type: "string" } } },
124
+ required: ["tags"],
125
+ },
126
+ },
127
+ }, `migration_${m.id}`);
128
+ const tags = Array.isArray(result.data?.tags)
129
+ ? result.data.tags.map((t) => String(t).trim().toLowerCase()).filter(Boolean)
130
+ : [];
131
+ if (!result.success || tags.length === 0) {
132
+ throw new Error(result.error ?? "The model returned no tags");
133
+ }
134
+ const vector = await embeddingService.embedWithTimeout(m.content, { task: "document" });
135
+ const tagsVector = await embeddingService.embedWithTimeout(formatTagsForEmbedding(tags), {
136
+ task: "document",
137
+ });
138
+ // One statement, so a memory never ends up tagged with stale vectors.
139
+ await tursoVectorSearch.updateVector(db, m.id, vector, tagsVector, tags.join(","));
140
+ migrationProgress.tagged++;
141
+ }
142
+ catch (e) {
143
+ const errorMsg = pageErrorText(e, config.memoryApiKey);
144
+ const kind = providerErrorKind(errorMsg);
145
+ log("Migration error for memory", { id: item.id, error: errorMsg, kind }, "error");
146
+ if (kind === "other") {
147
+ migrationProgress.errors.push(errorMsg);
148
+ migrationProgress.errorKind ??= kind;
149
+ }
150
+ else {
151
+ // This error ends the run, so it is the one the page reports.
152
+ migrationProgress.errors.unshift(errorMsg);
153
+ migrationProgress.errorKind = kind;
154
+ stopped = true;
155
+ }
156
+ }
157
+ finally {
158
+ migrationProgress.processed++;
159
+ }
160
+ if (stopped)
161
+ break;
162
+ }
163
+ migrationProgress.currentBatch++;
164
+ const hasMore = !stopped && migrationProgress.processed < migrationProgress.total;
165
+ if (!hasMore)
166
+ migrationProgress.isComplete = true;
167
+ const { processed, total, tagged, errors, errorKind } = migrationProgress;
168
+ return {
169
+ success: true,
170
+ data: {
171
+ processed,
172
+ total,
173
+ hasMore,
174
+ tagged,
175
+ failed: errors.length,
176
+ ...(errors.length > 0 ? { errorKind, error: errors[0] } : {}),
177
+ },
178
+ };
179
+ }
180
+ catch (error) {
181
+ return { success: false, error: String(error) };
182
+ }
183
+ }
@@ -2,6 +2,7 @@ import { routeChat } from "./chat/chat-routes.js";
2
2
  import { linkedSettingsFrom } from "./linked-search.js";
3
3
  import { routeMemorisations } from "./memorisation-routes.js";
4
4
  import { routeMemoryHosts } from "./memory-host-routes.js";
5
+ import { routeOpenRouter } from "./openrouter-routes.js";
5
6
  import { routeProfileCleanup } from "./profile-cleanup-routes.js";
6
7
  import { routeSpaceTransfers } from "./space-transfer-routes.js";
7
8
  import { routeSpaces } from "./space-routes.js";
@@ -21,6 +22,9 @@ export async function routeModules(path, method, url, req, ctx) {
21
22
  const hosts = await routeMemoryHosts(path, method, url, readBody, config);
22
23
  if (hosts !== undefined)
23
24
  return hosts;
25
+ const openRouter = await routeOpenRouter(path, method, url, readBody, ctx);
26
+ if (openRouter !== undefined)
27
+ return openRouter;
24
28
  if (path === "/api/skills" || path.startsWith("/api/skills/")) {
25
29
  const { routeSkills } = await import("./skill-routes.js");
26
30
  return routeSkills(path, method, url, readBody, ctx);