@loreai/core 0.11.1 → 0.13.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 (94) hide show
  1. package/dist/bun/agents-file.d.ts +29 -8
  2. package/dist/bun/agents-file.d.ts.map +1 -1
  3. package/dist/bun/config.d.ts +1 -0
  4. package/dist/bun/config.d.ts.map +1 -1
  5. package/dist/bun/db.d.ts.map +1 -1
  6. package/dist/bun/distillation.d.ts +55 -0
  7. package/dist/bun/distillation.d.ts.map +1 -1
  8. package/dist/bun/embedding.d.ts +15 -1
  9. package/dist/bun/embedding.d.ts.map +1 -1
  10. package/dist/bun/gradient.d.ts +53 -5
  11. package/dist/bun/gradient.d.ts.map +1 -1
  12. package/dist/bun/index.d.ts +4 -4
  13. package/dist/bun/index.d.ts.map +1 -1
  14. package/dist/bun/index.js +799 -256
  15. package/dist/bun/index.js.map +4 -4
  16. package/dist/bun/pattern-extract.d.ts +36 -0
  17. package/dist/bun/pattern-extract.d.ts.map +1 -0
  18. package/dist/bun/recall.d.ts +1 -0
  19. package/dist/bun/recall.d.ts.map +1 -1
  20. package/dist/bun/search.d.ts +13 -1
  21. package/dist/bun/search.d.ts.map +1 -1
  22. package/dist/bun/temporal.d.ts +15 -0
  23. package/dist/bun/temporal.d.ts.map +1 -1
  24. package/dist/bun/types.d.ts +41 -1
  25. package/dist/bun/types.d.ts.map +1 -1
  26. package/dist/bun/worker-model.d.ts +22 -0
  27. package/dist/bun/worker-model.d.ts.map +1 -1
  28. package/dist/node/agents-file.d.ts +29 -8
  29. package/dist/node/agents-file.d.ts.map +1 -1
  30. package/dist/node/config.d.ts +1 -0
  31. package/dist/node/config.d.ts.map +1 -1
  32. package/dist/node/db.d.ts.map +1 -1
  33. package/dist/node/distillation.d.ts +55 -0
  34. package/dist/node/distillation.d.ts.map +1 -1
  35. package/dist/node/embedding.d.ts +15 -1
  36. package/dist/node/embedding.d.ts.map +1 -1
  37. package/dist/node/gradient.d.ts +53 -5
  38. package/dist/node/gradient.d.ts.map +1 -1
  39. package/dist/node/index.d.ts +4 -4
  40. package/dist/node/index.d.ts.map +1 -1
  41. package/dist/node/index.js +799 -256
  42. package/dist/node/index.js.map +4 -4
  43. package/dist/node/pattern-extract.d.ts +36 -0
  44. package/dist/node/pattern-extract.d.ts.map +1 -0
  45. package/dist/node/recall.d.ts +1 -0
  46. package/dist/node/recall.d.ts.map +1 -1
  47. package/dist/node/search.d.ts +13 -1
  48. package/dist/node/search.d.ts.map +1 -1
  49. package/dist/node/temporal.d.ts +15 -0
  50. package/dist/node/temporal.d.ts.map +1 -1
  51. package/dist/node/types.d.ts +41 -1
  52. package/dist/node/types.d.ts.map +1 -1
  53. package/dist/node/worker-model.d.ts +22 -0
  54. package/dist/node/worker-model.d.ts.map +1 -1
  55. package/dist/types/agents-file.d.ts +29 -8
  56. package/dist/types/agents-file.d.ts.map +1 -1
  57. package/dist/types/config.d.ts +1 -0
  58. package/dist/types/config.d.ts.map +1 -1
  59. package/dist/types/db.d.ts.map +1 -1
  60. package/dist/types/distillation.d.ts +55 -0
  61. package/dist/types/distillation.d.ts.map +1 -1
  62. package/dist/types/embedding.d.ts +15 -1
  63. package/dist/types/embedding.d.ts.map +1 -1
  64. package/dist/types/gradient.d.ts +53 -5
  65. package/dist/types/gradient.d.ts.map +1 -1
  66. package/dist/types/index.d.ts +4 -4
  67. package/dist/types/index.d.ts.map +1 -1
  68. package/dist/types/pattern-extract.d.ts +36 -0
  69. package/dist/types/pattern-extract.d.ts.map +1 -0
  70. package/dist/types/recall.d.ts +1 -0
  71. package/dist/types/recall.d.ts.map +1 -1
  72. package/dist/types/search.d.ts +13 -1
  73. package/dist/types/search.d.ts.map +1 -1
  74. package/dist/types/temporal.d.ts +15 -0
  75. package/dist/types/temporal.d.ts.map +1 -1
  76. package/dist/types/types.d.ts +41 -1
  77. package/dist/types/types.d.ts.map +1 -1
  78. package/dist/types/worker-model.d.ts +22 -0
  79. package/dist/types/worker-model.d.ts.map +1 -1
  80. package/package.json +3 -2
  81. package/src/agents-file.ts +111 -28
  82. package/src/config.ts +25 -18
  83. package/src/curator.ts +2 -2
  84. package/src/db.ts +83 -4
  85. package/src/distillation.ts +270 -27
  86. package/src/embedding.ts +158 -14
  87. package/src/gradient.ts +398 -227
  88. package/src/index.ts +13 -5
  89. package/src/pattern-extract.ts +108 -0
  90. package/src/recall.ts +142 -6
  91. package/src/search.ts +37 -1
  92. package/src/temporal.ts +39 -0
  93. package/src/types.ts +41 -1
  94. package/src/worker-model.ts +142 -5
@@ -25,7 +25,11 @@ export type ModelInfo = {
25
25
  providerID: string;
26
26
  cost: { input: number }; // per-token cost
27
27
  status: string;
28
- capabilities: { input: { text: boolean } };
28
+ capabilities: {
29
+ input: { text: boolean };
30
+ /** Whether this model supports extended thinking/reasoning. */
31
+ reasoning?: boolean;
32
+ };
29
33
  };
30
34
 
31
35
  /** Result of a worker model validation stored in kv_meta. */
@@ -64,8 +68,17 @@ export function selectWorkerCandidates(
64
68
 
65
69
  if (eligible.length === 0) return [];
66
70
 
67
- // Sort by cost ascending (cheapest first)
68
- const sorted = [...eligible].sort((a, b) => a.cost.input - b.cost.input);
71
+ // Sort by cost ascending, then prefer non-reasoning models at equal cost.
72
+ // Non-reasoning models don't produce thinking tokens, avoiding wasted spend
73
+ // on tokens that background workers discard.
74
+ const sorted = [...eligible].sort((a, b) => {
75
+ const costDiff = a.cost.input - b.cost.input;
76
+ if (costDiff !== 0) return costDiff;
77
+ // At equal cost, non-reasoning (0) sorts before reasoning (1)
78
+ const aReasoning = a.capabilities.reasoning ? 1 : 0;
79
+ const bReasoning = b.capabilities.reasoning ? 1 : 0;
80
+ return aReasoning - bReasoning;
81
+ });
69
82
 
70
83
  // Cheapest overall
71
84
  const cheapest = sorted[0];
@@ -139,6 +152,11 @@ export function storeValidatedWorkerModel(result: WorkerModelResult): void {
139
152
  .run(key, value, value);
140
153
  }
141
154
 
155
+ /** Clear a stored worker model validation (e.g. when the model is deprecated). */
156
+ export function clearValidatedWorkerModel(providerID: string): void {
157
+ db().query("DELETE FROM kv_meta WHERE key = ?").run(`${KV_PREFIX}${providerID}`);
158
+ }
159
+
142
160
  /**
143
161
  * Check whether the stored validation is stale (fingerprint mismatch).
144
162
  */
@@ -237,6 +255,122 @@ export function parseJudgeScore(response: string): number | null {
237
255
  return parseInt(match[1], 10);
238
256
  }
239
257
 
258
+ // ---------------------------------------------------------------------------
259
+ // Validation orchestration
260
+ // ---------------------------------------------------------------------------
261
+
262
+ import { DISTILLATION_SYSTEM, distillationUser } from "./prompt";
263
+ import type { LLMClient } from "./types";
264
+
265
+ export type ValidationInput = {
266
+ llm: LLMClient;
267
+ providerID: string;
268
+ sessionModelID: string;
269
+ candidates: ModelInfo[];
270
+ /** Recent gen-0 distillation to use as reference (observations text). */
271
+ referenceObservations: string;
272
+ /** Source messages text for re-running distillation with candidates. */
273
+ sourceMessagesText: string;
274
+ /** Date string for the distillation prompt. */
275
+ date: string;
276
+ };
277
+
278
+ /**
279
+ * Run the two-phase quality validation for worker model candidates.
280
+ * Returns the cheapest passing candidate, or null if none pass.
281
+ */
282
+ export async function runValidation(
283
+ input: ValidationInput,
284
+ ): Promise<WorkerModelResult | null> {
285
+ const { llm, candidates, referenceObservations, sourceMessagesText, date } = input;
286
+
287
+ const userPrompt = distillationUser({
288
+ messages: sourceMessagesText,
289
+ date,
290
+ });
291
+
292
+ for (const candidate of candidates) {
293
+ // Skip the session model — it produced the reference, no need to test
294
+ if (candidate.id === input.sessionModelID) continue;
295
+
296
+ // Phase 1: run distillation with candidate model
297
+ let candidateObservations: string | null = null;
298
+ try {
299
+ const raw = await llm.prompt(DISTILLATION_SYSTEM, userPrompt, {
300
+ model: { providerID: candidate.providerID, modelID: candidate.id },
301
+ workerID: "lore-distill",
302
+ thinking: false,
303
+ });
304
+ if (raw) {
305
+ // Parse <observations>...</observations> block
306
+ const match = raw.match(/<observations>([\s\S]*?)<\/observations>/);
307
+ candidateObservations = match ? match[1].trim() : raw.trim();
308
+ }
309
+ } catch (e) {
310
+ log.warn(`worker model validation: candidate ${candidate.id} failed:`, e);
311
+ continue;
312
+ }
313
+
314
+ const structural = structuralCheck(candidateObservations, referenceObservations);
315
+ if (!structural.passed) {
316
+ log.info(
317
+ `worker model validation: ${candidate.id} failed structural check: ${structural.reason}`,
318
+ );
319
+ continue;
320
+ }
321
+
322
+ // Phase 2: LLM judge (using session model)
323
+ let judgeScore: number | null = null;
324
+ try {
325
+ const judgeResponse = await llm.prompt(
326
+ WORKER_JUDGE_SYSTEM,
327
+ workerJudgeUser(referenceObservations, candidateObservations!),
328
+ { workerID: "lore-distill", thinking: false }, // use session model (no model override)
329
+ );
330
+ if (judgeResponse) {
331
+ judgeScore = parseJudgeScore(judgeResponse);
332
+ }
333
+ } catch (e) {
334
+ log.warn(`worker model validation: judge call failed for ${candidate.id}:`, e);
335
+ }
336
+
337
+ if (judgeScore !== null && judgeScore < 3) {
338
+ log.info(
339
+ `worker model validation: ${candidate.id} failed judge (score=${judgeScore})`,
340
+ );
341
+ continue;
342
+ }
343
+
344
+ // Candidate passed both phases
345
+ const fingerprint = computeModelFingerprint(
346
+ input.providerID,
347
+ input.sessionModelID,
348
+ candidates.map((c) => c.id),
349
+ );
350
+
351
+ const result: WorkerModelResult = {
352
+ modelID: candidate.id,
353
+ providerID: candidate.providerID,
354
+ fingerprint,
355
+ validatedAt: Date.now(),
356
+ judgeScore,
357
+ };
358
+ storeValidatedWorkerModel(result);
359
+ log.info(
360
+ `worker model validated: ${candidate.id} (judge=${judgeScore}) for provider ${input.providerID}`,
361
+ );
362
+ return result;
363
+ }
364
+
365
+ // No candidate passed — clear any stale stored result so we don't keep
366
+ // routing worker calls to a potentially-deprecated model.
367
+ clearValidatedWorkerModel(input.providerID);
368
+ log.info(
369
+ `worker model validation: no candidate passed for ${input.providerID} — cleared stale entry`,
370
+ );
371
+ return null;
372
+ }
373
+
240
374
  // ---------------------------------------------------------------------------
241
375
  // Effective worker model resolution
242
376
  // ---------------------------------------------------------------------------
@@ -253,9 +387,12 @@ export function resolveWorkerModel(
253
387
  // Explicit override wins
254
388
  if (configWorkerModel) return configWorkerModel;
255
389
 
256
- // Check for validated auto-selection
390
+ // Check for validated auto-selection.
391
+ // Don't trust entries older than 24h — model may have been deprecated.
392
+ // Validation will re-run on next idle cycle and either re-confirm or clear.
257
393
  const validated = getValidatedWorkerModel(providerID);
258
- if (validated) {
394
+ const MAX_AGE_MS = 24 * 60 * 60 * 1000;
395
+ if (validated && Date.now() - validated.validatedAt <= MAX_AGE_MS) {
259
396
  return { providerID: validated.providerID, modelID: validated.modelID };
260
397
  }
261
398