@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 +10 -0
- package/dist/errors.js +171 -59
- package/dist/index.js +316 -122
- package/dist/internal/embedder/transformers.d.ts +6 -2
- package/dist/internal/store/topics/index.d.ts +2 -2
- package/dist/internal/store/topics/lifecycle.d.ts +25 -0
- package/dist/internal/workflow/index.d.ts +1 -1
- package/dist/internal/workflow/queue.d.ts +21 -0
- package/package.json +1 -1
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
|
|
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
|
|
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 "${
|
|
3784
|
+
message: `memory.embed: unknown model "${payload.modelKey}"`
|
|
3714
3785
|
}));
|
|
3715
3786
|
}
|
|
3716
|
-
const
|
|
3717
|
-
|
|
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
|
|
3722
|
-
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
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
3765
|
-
INSERT OR REPLACE INTO ${
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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(
|
|
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"),
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
7067
|
+
fromMissing: sql11`NOT EXISTS (
|
|
6966
7068
|
SELECT 1 FROM memories WHERE memories.id = ${artifactEdges.fromMemoryId}
|
|
6967
7069
|
)`,
|
|
6968
|
-
toMissing:
|
|
7070
|
+
toMissing: sql11`NOT EXISTS (
|
|
6969
7071
|
SELECT 1 FROM memories WHERE memories.id = ${artifactEdges.toMemoryId}
|
|
6970
7072
|
)`
|
|
6971
|
-
}).from(artifactEdges).where(or2(
|
|
7073
|
+
}).from(artifactEdges).where(or2(sql11`NOT EXISTS (
|
|
6972
7074
|
SELECT 1 FROM memories WHERE memories.id = ${artifactEdges.fromMemoryId}
|
|
6973
|
-
)`,
|
|
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:
|
|
7098
|
+
memoryMissing: sql11`NOT EXISTS (
|
|
6997
7099
|
SELECT 1 FROM memories WHERE memories.id = ${memoryConcepts.memoryId}
|
|
6998
7100
|
)`
|
|
6999
|
-
}).from(memoryConcepts).where(or2(
|
|
7101
|
+
}).from(memoryConcepts).where(or2(sql11`NOT EXISTS (
|
|
7000
7102
|
SELECT 1 FROM concepts WHERE concepts.id = ${memoryConcepts.conceptId}
|
|
7001
|
-
)`,
|
|
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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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({
|