@ponraaj/dimple-sdk 0.0.0-alpha.11 → 0.0.0-alpha.12

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/dist/dimple.d.ts CHANGED
@@ -55,6 +55,12 @@ export interface IndexOptions {
55
55
  readonly namespace?: string;
56
56
  /** Embed + topic-route every unit (default true). */
57
57
  readonly embed?: boolean;
58
+ /** Write the units and hand every one that needs a vector to the durable
59
+ * `memory.embed.batch` job instead of embedding inline: units are stored
60
+ * (and lexically searchable) when `index` returns, vectors + topic routing
61
+ * land whenever a worker drains the queue. Ignored when `embed` is
62
+ * `false`. */
63
+ readonly deferEmbed?: boolean;
58
64
  /** Cap for the whole-file memory content (default 64 KiB). */
59
65
  readonly maxFileBytes?: number;
60
66
  /** Cap for each extracted chunk's content (default 8 KiB). */
@@ -83,6 +89,10 @@ export interface IndexResult {
83
89
  readonly skipped: number;
84
90
  /** Files skipped because the extension is not indexable. */
85
91
  readonly unsupported: number;
92
+ /** `memory.embed.batch` jobs enqueued by the deferred path (`deferEmbed`) —
93
+ * units stored without a vector, counted after the enqueue. Absent in the
94
+ * synchronous path, where every unit is embedded before returning. */
95
+ readonly pendingEmbeddings?: number;
86
96
  readonly commit: string | null;
87
97
  /** Files that could not be walked/read/parsed/extracted/written. */
88
98
  readonly failed: readonly {
package/dist/errors.js CHANGED
@@ -33,7 +33,7 @@ import { Effect as Effect39, Layer as Layer15, Schedule as Schedule2, Schema as
33
33
 
34
34
  // ../store/src/store.ts
35
35
  import { Config as Config4, Effect as Effect25, Layer as Layer10, Schema as Schema23 } from "effect";
36
- import { eq as eq8, and as and8, desc as desc2, asc as asc5, inArray as inArray4, ne as ne4, or, isNull as isNull2, like, sql as sql7 } from "drizzle-orm";
36
+ import { eq as eq8, and as and8, desc as desc2, asc as asc5, inArray as inArray4, ne as ne4, or, isNull as isNull2, like, sql as sql8 } from "drizzle-orm";
37
37
 
38
38
  // ../store/src/db-backend.ts
39
39
  import { Context as Context2 } from "effect";
@@ -2736,8 +2736,8 @@ var resolveTopics = Effect11.fn("topics.resolveTopics")(function* (queryVector,
2736
2736
  });
2737
2737
 
2738
2738
  // ../store/src/topics/lifecycle.ts
2739
- import { Clock as Clock3, Effect as Effect20 } from "effect";
2740
- import { and as and3, eq as eq3, inArray, ne } from "drizzle-orm";
2739
+ import { Clock as Clock3, Cause as Cause3, Effect as Effect20, Exit as Exit6 } from "effect";
2740
+ import { and as and3, eq as eq3, inArray, ne, sql as sql3 } from "drizzle-orm";
2741
2741
 
2742
2742
  // ../workflow/src/queue.ts
2743
2743
  import { Effect as Effect12, Schema as Schema13 } from "effect";
@@ -2820,6 +2820,16 @@ var MemoryEmbed = JobDefinition.make("memory.embed", {
2820
2820
  error: Schema13.String,
2821
2821
  idempotencyKey: (p) => `embed:${p.memoryId}:${p.modelKey}`
2822
2822
  });
2823
+ var MemoryEmbedBatch = JobDefinition.make("memory.embed.batch", {
2824
+ payload: {
2825
+ memoryIds: Schema13.Array(Schema13.String),
2826
+ modelKey: Schema13.String,
2827
+ nonce: Schema13.String
2828
+ },
2829
+ success: Schema13.Void,
2830
+ error: Schema13.String,
2831
+ idempotencyKey: (p) => `embed-batch:${p.modelKey}:${p.nonce}`
2832
+ });
2823
2833
  var TopicMerge = JobDefinition.make("topic.merge", {
2824
2834
  payload: { topicId: Schema13.String, modelKey: Schema13.String },
2825
2835
  success: Schema13.Void,
@@ -2877,6 +2887,7 @@ var AuditWrite = JobDefinition.make("audit.write", {
2877
2887
  var queues = [
2878
2888
  TopicSplit,
2879
2889
  MemoryEmbed,
2890
+ MemoryEmbedBatch,
2880
2891
  TopicMerge,
2881
2892
  TopicRebalance,
2882
2893
  ConceptCluster,
@@ -3703,46 +3714,136 @@ var setActiveModel = Effect20.fn("topics.setActiveModel")(function* (model, conf
3703
3714
  }).pipe(routingFailure3("topics.setActiveModel.deactivate"));
3704
3715
  return { routed, alreadyInTree, deactivated };
3705
3716
  });
3717
+ var embedOneMemory = (deps, payload, ctx) => Effect20.gen(function* () {
3718
+ const envelope = payload;
3719
+ const model = deps.getModelConfig(envelope.modelKey);
3720
+ if (!model) {
3721
+ return yield* Effect20.fail(new JobFailure({
3722
+ jobId: ctx.jobId,
3723
+ message: `memory.embed: unknown model "${envelope.modelKey}"`
3724
+ }));
3725
+ }
3726
+ const content = yield* deps.getMemoryContent(envelope.memoryId).pipe(Effect20.catchTag("Store.NotFound", () => Effect20.succeed(undefined)), Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: load content: ${e}` })));
3727
+ if (content === undefined) {
3728
+ yield* Effect20.logDebug("embed.stale-reoffer").pipe(Effect20.annotateLogs({
3729
+ scope: "@dimple/store",
3730
+ event: "embed.stale-reoffer",
3731
+ memoryId: envelope.memoryId,
3732
+ modelKey: envelope.modelKey
3733
+ }));
3734
+ return;
3735
+ }
3736
+ const vector = yield* deps.embedOne(content, model).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: embed: ${e}` })));
3737
+ const backend = yield* Service2;
3738
+ const db = backend.db;
3739
+ const existing = yield* Effect20.tryPromise({
3740
+ try: () => db.select({ memoryId: topicMemberships.memoryId }).from(topicMemberships).where(and3(eq3(topicMemberships.memoryId, envelope.memoryId), eq3(topicMemberships.modelKey, model.id))),
3741
+ catch: (cause) => new StoreError({ message: `memory.embed: membership check: ${cause}` })
3742
+ }).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: membership check: ${e}` })));
3743
+ yield* ensureTopicTree(model, deps.config).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: seed tree: ${e}` })));
3744
+ yield* backend.ensureVectorTable(model).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: vector table: ${e}` })));
3745
+ yield* backend.insertVector(envelope.memoryId, [...vector], model).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: insert vector: ${e}` })));
3746
+ yield* Effect20.tryPromise({
3747
+ try: () => db.update(memories).set({ embedding: [...vector], embeddingModel: model.id }).where(eq3(memories.id, envelope.memoryId)),
3748
+ catch: (cause) => new StoreError({ message: `memory.embed: stamp embedding: ${cause}` })
3749
+ }).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: stamp embedding: ${e}` })));
3750
+ if (existing.length === 0) {
3751
+ yield* routeInsert(envelope.memoryId, [...vector], model, deps.config).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: route: ${e}` })));
3752
+ }
3753
+ yield* Effect20.logInfo("embed.completed").pipe(Effect20.annotateLogs({
3754
+ scope: "@dimple/store",
3755
+ event: "embed.completed",
3756
+ memoryId: envelope.memoryId,
3757
+ modelKey: envelope.modelKey,
3758
+ reroute: existing.length === 0
3759
+ }));
3760
+ return;
3761
+ });
3706
3762
  function memoryEmbedHandler(deps) {
3763
+ const run = (payload, ctx) => embedOneMemory(deps, payload, ctx);
3764
+ return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect20.provideService(Service2, deps.backend)) : run;
3765
+ }
3766
+ var logBatchCompleted = (modelKey, embedded, failed, ctx) => Effect20.logInfo("embed.batch.completed").pipe(Effect20.annotateLogs({
3767
+ scope: "@dimple/store",
3768
+ event: "embed.batch.completed",
3769
+ jobId: ctx.jobId,
3770
+ modelKey,
3771
+ embedded,
3772
+ failed
3773
+ }));
3774
+ var batchFailure = (ctx, failed, total, first) => Effect20.fail(new JobFailure({
3775
+ jobId: ctx.jobId,
3776
+ message: `memory.embed.batch: ${failed}/${total} failed — ${first}`
3777
+ }));
3778
+ function memoryEmbedBatchHandler(deps) {
3707
3779
  const run = (payload, ctx) => Effect20.gen(function* () {
3708
- const envelope = payload;
3709
- const model = deps.getModelConfig(envelope.modelKey);
3780
+ const model = deps.getModelConfig(payload.modelKey);
3710
3781
  if (!model) {
3711
3782
  return yield* Effect20.fail(new JobFailure({
3712
3783
  jobId: ctx.jobId,
3713
- message: `memory.embed: unknown model "${envelope.modelKey}"`
3784
+ message: `memory.embed: unknown model "${payload.modelKey}"`
3714
3785
  }));
3715
3786
  }
3716
- const content = yield* deps.getMemoryContent(envelope.memoryId).pipe(Effect20.catchTag("Store.NotFound", () => Effect20.succeed(undefined)), Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: load content: ${e}` })));
3717
- if (content === undefined) {
3787
+ const backend = yield* Service2;
3788
+ const db = backend.db;
3789
+ let failed = 0;
3790
+ let first;
3791
+ const embedded = [];
3792
+ const stale = [];
3793
+ yield* Effect20.forEach(payload.memoryIds, (memoryId) => Effect20.gen(function* () {
3794
+ const content = yield* deps.getMemoryContent(memoryId).pipe(Effect20.catchTag("Store.NotFound", () => Effect20.succeed(undefined)), Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: load content: ${e}` })));
3795
+ if (content === undefined) {
3796
+ stale.push(memoryId);
3797
+ return;
3798
+ }
3799
+ const vector = yield* deps.embedOne(content, model).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: embed: ${e}` })));
3800
+ embedded.push({ memoryId, vector });
3801
+ }).pipe(Effect20.exit, Effect20.tap((exit) => {
3802
+ if (Exit6.isSuccess(exit))
3803
+ return Effect20.void;
3804
+ failed += 1;
3805
+ first ??= Cause3.pretty(exit.cause);
3806
+ return Effect20.void;
3807
+ })), { concurrency: 1, discard: true });
3808
+ for (const memoryId of stale) {
3718
3809
  yield* Effect20.logDebug("embed.stale-reoffer").pipe(Effect20.annotateLogs({
3719
3810
  scope: "@dimple/store",
3720
3811
  event: "embed.stale-reoffer",
3721
- memoryId: envelope.memoryId,
3722
- modelKey: envelope.modelKey
3812
+ memoryId,
3813
+ modelKey: payload.modelKey
3723
3814
  }));
3815
+ }
3816
+ if (embedded.length === 0) {
3817
+ yield* logBatchCompleted(payload.modelKey, 0, failed, ctx);
3818
+ if (failed > 0) {
3819
+ return yield* batchFailure(ctx, failed, payload.memoryIds.length, first);
3820
+ }
3724
3821
  return;
3725
3822
  }
3726
- const vector = yield* deps.embedOne(content, model).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: embed: ${e}` })));
3727
- const backend = yield* Service2;
3728
- const db = backend.db;
3729
- const existing = yield* Effect20.tryPromise({
3730
- try: () => db.select({ memoryId: topicMemberships.memoryId }).from(topicMemberships).where(and3(eq3(topicMemberships.memoryId, envelope.memoryId), eq3(topicMemberships.modelKey, model.id))),
3731
- catch: (cause) => new StoreError({ message: `memory.embed: membership check: ${cause}` })
3732
- }).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: membership check: ${e}` })));
3733
3823
  yield* ensureTopicTree(model, deps.config).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: seed tree: ${e}` })));
3734
3824
  yield* backend.ensureVectorTable(model).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: vector table: ${e}` })));
3735
- yield* backend.insertVector(envelope.memoryId, [...vector], model).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: insert vector: ${e}` })));
3736
- if (existing.length === 0) {
3737
- yield* routeInsert(envelope.memoryId, [...vector], model, deps.config).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: route: ${e}` })));
3825
+ const ids = embedded.map((e) => e.memoryId);
3826
+ const members = yield* Effect20.tryPromise({
3827
+ try: () => db.select({ memoryId: topicMemberships.memoryId }).from(topicMemberships).where(and3(eq3(topicMemberships.modelKey, model.id), inArray(topicMemberships.memoryId, ids))),
3828
+ catch: (cause) => new StoreError({ message: `memory.embed: membership check: ${cause}` })
3829
+ }).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: membership check: ${e}` })));
3830
+ const memberSet = new Set(members.map((r) => r.memoryId));
3831
+ const entries = embedded.map((e) => ({ memoryId: e.memoryId, vector: [...e.vector] }));
3832
+ yield* backend.insertVectors(entries, model).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: insert vectors: ${e}` })));
3833
+ yield* Effect20.tryPromise({
3834
+ try: () => db.transaction(async (tx) => {
3835
+ await tx.run(sql3`UPDATE ${memories} SET embedding = CASE id ${sql3.join(entries.map((entry) => sql3`WHEN ${entry.memoryId} THEN ${new Float32Array(entry.vector).buffer}`), sql3` `)} END, embedding_model = ${model.id} WHERE id IN (${sql3.join(entries.map((entry) => sql3`${entry.memoryId}`), sql3`, `)})`);
3836
+ }),
3837
+ catch: (cause) => new StoreError({ message: `memory.embed: stamp embeddings: ${cause}` })
3838
+ }).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: stamp embeddings: ${e}` })));
3839
+ const fresh = entries.filter((entry) => !memberSet.has(entry.memoryId));
3840
+ if (fresh.length > 0) {
3841
+ yield* routeInsertMany(fresh, model, deps.config).pipe(Effect20.mapError((e) => new JobFailure({ jobId: ctx.jobId, message: `memory.embed: route: ${e}` })));
3842
+ }
3843
+ yield* logBatchCompleted(payload.modelKey, embedded.length, failed, ctx);
3844
+ if (failed > 0) {
3845
+ return yield* batchFailure(ctx, failed, payload.memoryIds.length, first);
3738
3846
  }
3739
- yield* Effect20.logInfo("embed.completed").pipe(Effect20.annotateLogs({
3740
- scope: "@dimple/store",
3741
- event: "embed.completed",
3742
- memoryId: envelope.memoryId,
3743
- modelKey: envelope.modelKey,
3744
- reroute: existing.length === 0
3745
- }));
3746
3847
  return;
3747
3848
  });
3748
3849
  return deps.backend ? (payload, ctx) => run(payload, ctx).pipe(Effect20.provideService(Service2, deps.backend)) : run;
@@ -3750,19 +3851,19 @@ function memoryEmbedHandler(deps) {
3750
3851
 
3751
3852
  // ../store/src/topics/jobs.ts
3752
3853
  import { Clock as Clock4, Effect as Effect21 } from "effect";
3753
- import { and as and4, asc as asc3, eq as eq4, inArray as inArray2, sql as sql4 } from "drizzle-orm";
3854
+ import { and as and4, asc as asc3, eq as eq4, inArray as inArray2, sql as sql5 } from "drizzle-orm";
3754
3855
 
3755
3856
  // ../store/src/topics/topic-vectors.ts
3756
- import { sql as sql3 } from "drizzle-orm";
3857
+ import { sql as sql4 } from "drizzle-orm";
3757
3858
  var topicVectorTableName2 = (model) => `topic_vec_${model.dimensions}_${model.id.replace(/[^a-zA-Z0-9_]/g, "_")}`;
3758
3859
  var deleteTopicVectorRow = async (runner, model, topicKey) => {
3759
3860
  const table = topicVectorTableName2(model);
3760
- await runner.run(sql3`DELETE FROM ${sql3.raw(table)} WHERE topic_key = ${topicKey}`);
3861
+ await runner.run(sql4`DELETE FROM ${sql4.raw(table)} WHERE topic_key = ${topicKey}`);
3761
3862
  };
3762
3863
  var writeTopicVector = async (runner, model, topicKey, ls) => {
3763
3864
  const table = topicVectorTableName2(model);
3764
- await runner.run(sql3`
3765
- INSERT OR REPLACE INTO ${sql3.raw(table)} (topic_key, sum_vector, model_key)
3865
+ await runner.run(sql4`
3866
+ INSERT OR REPLACE INTO ${sql4.raw(table)} (topic_key, sum_vector, model_key)
3766
3867
  VALUES (${topicKey}, vector32(${JSON.stringify([...ls])}), ${model.id})
3767
3868
  `);
3768
3869
  };
@@ -3853,7 +3954,7 @@ var splitTopic = Effect21.fn("TopicSplit.splitTopic")(function* (topicKey, model
3853
3954
  }).onConflictDoUpdate({
3854
3955
  target: [topicGrowthState.topicKey, topicGrowthState.modelKey],
3855
3956
  set: {
3856
- splitRejectedCount: sql4`${topicGrowthState.splitRejectedCount} + 1`,
3957
+ splitRejectedCount: sql5`${topicGrowthState.splitRejectedCount} + 1`,
3857
3958
  splitDeferred: 1
3858
3959
  }
3859
3960
  });
@@ -4543,7 +4644,7 @@ var staleNoOp = (notFound, model) => Effect22.gen(function* () {
4543
4644
 
4544
4645
  // ../store/src/topics/observability.ts
4545
4646
  import { Effect as Effect23 } from "effect";
4546
- import { and as and6, desc, eq as eq6, inArray as inArray3, ne as ne3, sql as sql5 } from "drizzle-orm";
4647
+ import { and as and6, desc, eq as eq6, inArray as inArray3, ne as ne3, sql as sql6 } from "drizzle-orm";
4547
4648
  var DEFAULT_MAX_LEAF_ITEMS3 = 128;
4548
4649
  var DEFAULT_SPLIT_DEFERRED_HARD_LIMIT2 = 512;
4549
4650
  var DEFAULT_CHANGE_LOG_LIMIT = 50;
@@ -4587,7 +4688,7 @@ var topicMetrics = Effect23.fn("topics.topicMetrics")(function* (modelKey, confi
4587
4688
  }
4588
4689
  leafMemberCounts.sort((a, b) => a - b);
4589
4690
  const growth = yield* Effect23.tryPromise({
4590
- try: () => db.select({ total: sql5`coalesce(sum(${topicGrowthState.splitRejectedCount}), 0)` }).from(topicGrowthState).where(eq6(topicGrowthState.modelKey, modelKey)),
4691
+ try: () => db.select({ total: sql6`coalesce(sum(${topicGrowthState.splitRejectedCount}), 0)` }).from(topicGrowthState).where(eq6(topicGrowthState.modelKey, modelKey)),
4591
4692
  catch: (cause) => cause
4592
4693
  }).pipe(routingFailure4("topics.topicMetrics.growth"));
4593
4694
  const splitRejectedTotal = Number(growth[0]?.total ?? 0);
@@ -4643,7 +4744,7 @@ var topicHealth = Effect23.fn("topics.topicHealth")(function* (modelKey, config)
4643
4744
  catch: (cause) => cause
4644
4745
  }).pipe(routingFailure4("topics.topicHealth.topics"));
4645
4746
  const countRows = yield* Effect23.tryPromise({
4646
- try: () => db.select({ topicKey: topicMemberships.topicKey, count: sql5`count(*)` }).from(topicMemberships).where(eq6(topicMemberships.modelKey, modelKey)).groupBy(topicMemberships.topicKey),
4747
+ try: () => db.select({ topicKey: topicMemberships.topicKey, count: sql6`count(*)` }).from(topicMemberships).where(eq6(topicMemberships.modelKey, modelKey)).groupBy(topicMemberships.topicKey),
4647
4748
  catch: (cause) => cause
4648
4749
  }).pipe(routingFailure4("topics.topicHealth.memberships"));
4649
4750
  const membershipCount = new Map(countRows.map((r) => [r.topicKey, Number(r.count)]));
@@ -4668,7 +4769,7 @@ var topicHealth = Effect23.fn("topics.topicHealth")(function* (modelKey, config)
4668
4769
 
4669
4770
  // ../store/src/graph/observability.ts
4670
4771
  import { Effect as Effect24 } from "effect";
4671
- import { and as and7, eq as eq7, isNull, sql as sql6 } from "drizzle-orm";
4772
+ import { and as and7, eq as eq7, isNull, sql as sql7 } from "drizzle-orm";
4672
4773
  var graphCoverage = Effect24.fn("GraphCoverage.run")(function* (modelKey) {
4673
4774
  const backend = yield* Service2;
4674
4775
  const db = backend.db;
@@ -4682,7 +4783,7 @@ var graphCoverage = Effect24.fn("GraphCoverage.run")(function* (modelKey) {
4682
4783
  const coveredMemories = totalMemories - isolatedMemories;
4683
4784
  const coveragePercent = totalMemories === 0 ? 0 : Math.round(coveredMemories / totalMemories * 1000) / 10;
4684
4785
  const pendingRows = yield* Effect24.tryPromise({
4685
- try: () => db.select({ count: sql6`count(*)` }).from(artifactEdges).where(and7(eq7(artifactEdges.status, "pending"), isNull(artifactEdges.expiredAt))),
4786
+ try: () => db.select({ count: sql7`count(*)` }).from(artifactEdges).where(and7(eq7(artifactEdges.status, "pending"), isNull(artifactEdges.expiredAt))),
4686
4787
  catch: (cause) => new StoreError({ message: `graphCoverage: pending: ${cause}` })
4687
4788
  });
4688
4789
  const pendingCandidates = totalMemories === 0 ? 0 : Number(pendingRows[0]?.count ?? 0);
@@ -5303,7 +5404,7 @@ var makeStore = (backend, modelConfig, topicsOptions, graphConfig, auditOptions,
5303
5404
  const relRows = await tx.select({ parentKey: topicRelations.parentKey }).from(topicRelations).where(and8(eq8(topicRelations.childKey, membership.topicKey), eq8(topicRelations.modelKey, membership.modelKey), eq8(topicRelations.relationKind, "parent_child")));
5304
5405
  let ancestorKey = relRows[0]?.parentKey;
5305
5406
  while (ancestorKey) {
5306
- await tx.update(topics).set({ memberCount: sql7`max(0, ${topics.memberCount} - 1)`, updatedAt: Date.now() }).where(and8(eq8(topics.topicKey, ancestorKey), eq8(topics.modelKey, membership.modelKey)));
5407
+ await tx.update(topics).set({ memberCount: sql8`max(0, ${topics.memberCount} - 1)`, updatedAt: Date.now() }).where(and8(eq8(topics.topicKey, ancestorKey), eq8(topics.modelKey, membership.modelKey)));
5307
5408
  const up = await tx.select({ parentKey: topicRelations.parentKey }).from(topicRelations).where(and8(eq8(topicRelations.childKey, ancestorKey), eq8(topicRelations.modelKey, membership.modelKey), eq8(topicRelations.relationKind, "parent_child")));
5308
5409
  ancestorKey = up[0]?.parentKey;
5309
5410
  }
@@ -5597,7 +5698,7 @@ var makeStore = (backend, modelConfig, topicsOptions, graphConfig, auditOptions,
5597
5698
  const now = Date.now();
5598
5699
  yield* Effect25.tryPromise({
5599
5700
  try: () => db.transaction(async (tx) => {
5600
- await tx.run(sql7`UPDATE ${memories2} SET embedding = CASE id ${sql7.join(entries.map((entry) => sql7`WHEN ${entry.id} THEN ${new Float32Array(entry.embedding).buffer}`), sql7` `)} END, embedding_model = ${modelName} WHERE id IN (${sql7.join(entries.map((entry) => entry.id), sql7`, `)})`);
5701
+ await tx.run(sql8`UPDATE ${memories2} SET embedding = CASE id ${sql8.join(entries.map((entry) => sql8`WHEN ${entry.id} THEN ${new Float32Array(entry.embedding).buffer}`), sql8` `)} END, embedding_model = ${modelName} WHERE id IN (${sql8.join(entries.map((entry) => entry.id), sql8`, `)})`);
5601
5702
  await tx.insert(memoryEvents2).values(entries.map((entry) => ({
5602
5703
  memoryId: entry.id,
5603
5704
  eventType: "embedding_updated",
@@ -5646,7 +5747,7 @@ var makeStore = (backend, modelConfig, topicsOptions, graphConfig, auditOptions,
5646
5747
  });
5647
5748
  const findStale = Effect25.fn("Store.findStale")(function* (model) {
5648
5749
  const rows = yield* Effect25.tryPromise({
5649
- try: () => db.select({ id: memories2.id }).from(memories2).where(and8(eq8(memories2.state, "active"), sql7`(embedding IS NULL OR embedding_model IS NULL OR embedding_model != ${model})`)),
5750
+ try: () => db.select({ id: memories2.id }).from(memories2).where(and8(eq8(memories2.state, "active"), sql8`(embedding IS NULL OR embedding_model IS NULL OR embedding_model != ${model})`)),
5650
5751
  catch: storeError("Store.findStale")
5651
5752
  });
5652
5753
  return rows.map((row) => row.id);
@@ -6284,6 +6385,7 @@ __export(exports_topics, {
6284
6385
  radiusOf: () => radiusOf,
6285
6386
  mergeOrBorrow: () => mergeOrBorrow,
6286
6387
  memoryEmbedHandler: () => memoryEmbedHandler,
6388
+ memoryEmbedBatchHandler: () => memoryEmbedBatchHandler,
6287
6389
  lloydRefine: () => lloydRefine,
6288
6390
  isActive: () => isActive,
6289
6391
  farthestPair: () => farthestPair,
@@ -6358,7 +6460,7 @@ __export(exports_graph, {
6358
6460
 
6359
6461
  // ../store/src/graph/concepts.ts
6360
6462
  import { Clock as Clock6, Effect as Effect27 } from "effect";
6361
- import { and as and9, asc as asc6, eq as eq9, isNotNull, sql as sql8 } from "drizzle-orm";
6463
+ import { and as and9, asc as asc6, eq as eq9, isNotNull, sql as sql9 } from "drizzle-orm";
6362
6464
  var DEFAULT_CLUSTER_K = 8;
6363
6465
  var DEFAULT_CLUSTER_MIN_SIZE = 3;
6364
6466
  var DEFAULT_CLUSTER_PASSES = 2;
@@ -6551,7 +6653,7 @@ var persistClusters = (db, members, config, now) => Effect27.tryPromise({
6551
6653
  }).onConflictDoUpdate({
6552
6654
  target: concepts.id,
6553
6655
  set: {
6554
- occurrenceCount: sql8`${concepts.occurrenceCount} + 1`,
6656
+ occurrenceCount: sql9`${concepts.occurrenceCount} + 1`,
6555
6657
  lastSeen: now
6556
6658
  }
6557
6659
  });
@@ -6565,7 +6667,7 @@ var persistClusters = (db, members, config, now) => Effect27.tryPromise({
6565
6667
  };
6566
6668
  })).onConflictDoUpdate({
6567
6669
  target: [memoryConcepts.conceptId, memoryConcepts.memoryId],
6568
- set: { confidence: sql8`excluded.confidence`, assignedAt: now }
6670
+ set: { confidence: sql9`excluded.confidence`, assignedAt: now }
6569
6671
  });
6570
6672
  conceptCount++;
6571
6673
  annotated += memberIndices.length;
@@ -6601,7 +6703,7 @@ function conceptClusterHandler(deps) {
6601
6703
  }
6602
6704
  // ../store/src/graph/dream.ts
6603
6705
  import { Clock as Clock7, Effect as Effect28 } from "effect";
6604
- import { and as and10, asc as asc7, eq as eq10, gt, inArray as inArray5, isNull as isNull3, sql as sql9 } from "drizzle-orm";
6706
+ import { and as and10, asc as asc7, eq as eq10, gt, inArray as inArray5, isNull as isNull3, sql as sql10 } from "drizzle-orm";
6605
6707
  var dreamStateDefaults = {
6606
6708
  minHours: 24,
6607
6709
  minNewMemories: 20,
@@ -6732,7 +6834,7 @@ var runDream = Effect28.fn("MemoryDream.run")(function* (model, classify) {
6732
6834
  };
6733
6835
  }
6734
6836
  const sinceCountRows = yield* Effect28.tryPromise({
6735
- try: () => db.select({ count: sql9`count(*)` }).from(memories).where(gt(memories.createdAt, lastDreamAt)),
6837
+ try: () => db.select({ count: sql10`count(*)` }).from(memories).where(gt(memories.createdAt, lastDreamAt)),
6736
6838
  catch: (cause) => new StoreError({ message: `memory.dream: count new: ${cause}` })
6737
6839
  });
6738
6840
  const memoriesSinceWatermark = Number(sinceCountRows[0]?.count ?? 0);
@@ -6911,7 +7013,7 @@ var runDream = Effect28.fn("MemoryDream.run")(function* (model, classify) {
6911
7013
  if (outcome.inserted)
6912
7014
  created++;
6913
7015
  }
6914
- const countRows = await tx.select({ count: sql9`count(*)` }).from(memories);
7016
+ const countRows = await tx.select({ count: sql10`count(*)` }).from(memories);
6915
7017
  const totalMemories = Number(countRows[0]?.count ?? 0);
6916
7018
  await tx.insert(dreamState).values({
6917
7019
  modelKey: model.id,
@@ -6950,7 +7052,7 @@ function graphDreamHandler(deps) {
6950
7052
  }
6951
7053
  // ../store/src/graph/repair.ts
6952
7054
  import { Clock as Clock8, Effect as Effect29 } from "effect";
6953
- import { and as and11, eq as eq11, isNotNull as isNotNull2, lt, or as or2, sql as sql10 } from "drizzle-orm";
7055
+ import { and as and11, eq as eq11, isNotNull as isNotNull2, lt, or as or2, sql as sql11 } from "drizzle-orm";
6954
7056
  var DEFAULT_EXPIRED_GRACE_MS = 7 * 24 * 60 * 60 * 1000;
6955
7057
  var runRepair = (db, includePurgeExpired, expiredGraceMs, now) => Effect29.tryPromise({
6956
7058
  try: () => db.transaction(async (tx) => {
@@ -6962,15 +7064,15 @@ var runRepair = (db, includePurgeExpired, expiredGraceMs, now) => Effect29.tryPr
6962
7064
  edgeKind: artifactEdges.edgeKind,
6963
7065
  fromMemoryId: artifactEdges.fromMemoryId,
6964
7066
  toMemoryId: artifactEdges.toMemoryId,
6965
- fromMissing: sql10`NOT EXISTS (
7067
+ fromMissing: sql11`NOT EXISTS (
6966
7068
  SELECT 1 FROM memories WHERE memories.id = ${artifactEdges.fromMemoryId}
6967
7069
  )`,
6968
- toMissing: sql10`NOT EXISTS (
7070
+ toMissing: sql11`NOT EXISTS (
6969
7071
  SELECT 1 FROM memories WHERE memories.id = ${artifactEdges.toMemoryId}
6970
7072
  )`
6971
- }).from(artifactEdges).where(or2(sql10`NOT EXISTS (
7073
+ }).from(artifactEdges).where(or2(sql11`NOT EXISTS (
6972
7074
  SELECT 1 FROM memories WHERE memories.id = ${artifactEdges.fromMemoryId}
6973
- )`, sql10`NOT EXISTS (
7075
+ )`, sql11`NOT EXISTS (
6974
7076
  SELECT 1 FROM memories WHERE memories.id = ${artifactEdges.toMemoryId}
6975
7077
  )`));
6976
7078
  for (const row of orphanRows) {
@@ -6993,12 +7095,12 @@ var runRepair = (db, includePurgeExpired, expiredGraceMs, now) => Effect29.tryPr
6993
7095
  const staleRows = await tx.select({
6994
7096
  conceptId: memoryConcepts.conceptId,
6995
7097
  memoryId: memoryConcepts.memoryId,
6996
- memoryMissing: sql10`NOT EXISTS (
7098
+ memoryMissing: sql11`NOT EXISTS (
6997
7099
  SELECT 1 FROM memories WHERE memories.id = ${memoryConcepts.memoryId}
6998
7100
  )`
6999
- }).from(memoryConcepts).where(or2(sql10`NOT EXISTS (
7101
+ }).from(memoryConcepts).where(or2(sql11`NOT EXISTS (
7000
7102
  SELECT 1 FROM concepts WHERE concepts.id = ${memoryConcepts.conceptId}
7001
- )`, sql10`NOT EXISTS (
7103
+ )`, sql11`NOT EXISTS (
7002
7104
  SELECT 1 FROM memories WHERE memories.id = ${memoryConcepts.memoryId}
7003
7105
  )`));
7004
7106
  for (const row of staleRows) {
@@ -7375,7 +7477,10 @@ var make6 = (options) => {
7375
7477
  pipe = yield* Effect32.tryPromise(() => transformers.pipeline("feature-extraction", options.modelId ?? defaults2.modelId, {
7376
7478
  device: options.device ?? defaults2.device,
7377
7479
  dtype: options.dtype ?? defaults2.dtype,
7378
- ...options.sessionOptions !== undefined ? { sessionOptions: buildSessionOptions(options.sessionOptions) } : undefined
7480
+ session_options: {
7481
+ enableCpuMemArena: false,
7482
+ ...options.sessionOptions !== undefined ? buildSessionOptions(options.sessionOptions) : undefined
7483
+ }
7379
7484
  })).pipe(Effect32.mapError((cause) => new EmbeddingError({ message: `load model: ${cause}` })));
7380
7485
  return pipe;
7381
7486
  });
@@ -7676,7 +7781,7 @@ var configured8 = (options, deps) => make({
7676
7781
  deps
7677
7782
  });
7678
7783
  // ../llm/src/llm.ts
7679
- import { Cause as Cause3, Effect as Effect36, Option as Option12, Schema as Schema28 } from "effect";
7784
+ import { Cause as Cause4, Effect as Effect36, Option as Option12, Schema as Schema28 } from "effect";
7680
7785
  import {
7681
7786
  generateText as generateTextCall,
7682
7787
  NoSuchToolError,
@@ -7733,7 +7838,7 @@ var makeLLM = (model, defaults3 = {}) => {
7733
7838
  ...input.system !== undefined ? { system: input.system } : undefined,
7734
7839
  ...callOptions(input)
7735
7840
  }).then((result) => result.text),
7736
- catch: (cause) => new LLMError({ message: "LLM text generation failed", cause: Cause3.die(cause) })
7841
+ catch: (cause) => new LLMError({ message: "LLM text generation failed", cause: Cause4.die(cause) })
7737
7842
  });
7738
7843
  yield* Effect36.logDebug("llm.generate-text").pipe(Effect36.annotateLogs({
7739
7844
  scope: "@dimple/llm",
@@ -7785,7 +7890,7 @@ ${schemaJson}`,
7785
7890
  return Promise.reject(extracted.error);
7786
7891
  return input.schema.parse(extracted.value);
7787
7892
  },
7788
- catch: (cause) => new LLMError({ message: "LLM structured generation failed", cause: Cause3.die(cause) })
7893
+ catch: (cause) => new LLMError({ message: "LLM structured generation failed", cause: Cause4.die(cause) })
7789
7894
  });
7790
7895
  yield* Effect36.logDebug("llm.generate-object").pipe(Effect36.annotateLogs({
7791
7896
  scope: "@dimple/llm",
@@ -7987,6 +8092,13 @@ var layer13 = (options) => {
7987
8092
  config: topicConfig,
7988
8093
  backend
7989
8094
  }));
8095
+ yield* register("memory.embed.batch", exports_topics.memoryEmbedBatchHandler({
8096
+ getModelConfig,
8097
+ embedOne: (text4, model) => options.embedderFor(model).pipe(Effect39.flatMap((m) => m.embedSingle(text4))),
8098
+ getMemoryContent: (memoryId) => store2.get(memoryId).pipe(Effect39.map((m) => m.content)),
8099
+ config: topicConfig,
8100
+ backend
8101
+ }));
7990
8102
  yield* register("concept.cluster", exports_graph.conceptClusterHandler({ getModelConfig, config: graphConfig, backend }));
7991
8103
  if (options.llm !== undefined) {
7992
8104
  yield* register("memory.dream", exports_graph.graphDreamHandler({