@ponraaj/dimple-sdk 0.0.0-alpha.1 → 0.0.0-alpha.2

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 ADDED
@@ -0,0 +1,334 @@
1
+ # @ponraaj/dimple-sdk
2
+
3
+ A local-first memory system for AI agents. Store text memories (and whole
4
+ codebases), retrieve them with hybrid semantic + lexical search, and let a
5
+ **self-organizing topic tree + artifact graph + concepts** keep everything
6
+ structured as it grows. Everything runs locally: ONNX embeddings on your
7
+ CPU, SQLite files on disk.
8
+
9
+ This is the programmatic SDK — the
10
+ [`@ponraaj/dimple`](https://www.npmjs.com/package/@ponraaj/dimple) CLI is a
11
+ thin facade over it.
12
+
13
+ > **Status**: alpha (`0.0.0-alpha.n`). The API is stabilizing but may change.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install @ponraaj/dimple-sdk
19
+ ```
20
+
21
+ Runtime dependencies are the only things installed (Effect, libSQL,
22
+ Transformers.js, drizzle) — the workspace internals are bundled into the
23
+ package.
24
+
25
+ ## Quickstart
26
+
27
+ ```ts
28
+ import { makeDimple } from "@ponraaj/dimple-sdk";
29
+
30
+ const dimple = await makeDimple({ config: "./dimple.jsonc" });
31
+
32
+ // write a memory (embeds + topic-routes it)
33
+ const mem = await dimple.write({
34
+ logicalKey: "deploy-1",
35
+ content: "the deploy pipeline runs on every push",
36
+ });
37
+
38
+ // hybrid semantic + FTS search
39
+ const results = await dimple.query("how does the pipeline deploy", { limit: 3 });
40
+
41
+ // index real code — files + extracted functions/classes/methods become memories
42
+ const indexed = await dimple.index({ paths: ["./src"] });
43
+
44
+ await dimple.dispose(); // stop the worker, close DBs (idempotent)
45
+ ```
46
+
47
+ ## The problem & the approach
48
+
49
+ **Problem**: naive memory stores scan every vector per query (cost grows
50
+ with memory count), return flat results with no structure, and give the
51
+ agent no notion of _why_ a memory is relevant, what supersedes it, or what
52
+ contradicts it.
53
+
54
+ **Approach** — three structures maintained automatically by a durable
55
+ background worker:
56
+
57
+ 1. **Topic tree** — memories are routed by embedding similarity into tree
58
+ leaves. Queries descend the tree (beam search on centroids) and only
59
+ score the reached leaves: **search cost grows with depth, not with
60
+ memory count**. A durable worker splits overflowing leaves and merges
61
+ underflowing ones with deterministic algorithms (CF-triplet k-means) —
62
+ same embeddings ⇒ same tree, anywhere.
63
+ 2. **Hybrid retrieval** — vector (cosine) + FTS5 lexical legs fused with
64
+ Reciprocal Rank Fusion, optional recency boost, and per-result graph
65
+ context.
66
+ 3. **Artifact graph + concepts** — directed `memory_supports` /
67
+ `memory_supersedes` / `memory_contradicts` edges written by structural
68
+ rules, code indexing, the agent, and the optional **dream** job (LLM
69
+ classification of candidates + deterministic concept clustering).
70
+ Retrieval consumes the graph: superseded memories demote, conflicts
71
+ surface, supports ride along as provenance; concept filters give the
72
+ query sovereignty.
73
+
74
+ All structural decisions are **deterministic**; everything is **durable**
75
+ (two SQLite files, resume-safe jobs).
76
+
77
+ ## Configuration
78
+
79
+ Pass a config file path or an inline object:
80
+
81
+ ```ts
82
+ const dimple = await makeDimple({ config: "./dimple.jsonc" });
83
+ // or
84
+ const dimple = await makeDimple({
85
+ config: {
86
+ storeUrl: "file:./.dimple/dimple.db",
87
+ jobs: { dbUrl: "file:./.dimple/jobs.db" },
88
+ embedding: {
89
+ defaultModel: "minilm",
90
+ models: [
91
+ {
92
+ id: "minilm",
93
+ provider: "transformers",
94
+ model: "Xenova/all-MiniLM-L6-v2",
95
+ dimensions: 384,
96
+ },
97
+ ],
98
+ },
99
+ },
100
+ });
101
+ ```
102
+
103
+ ### Config sections (`dimple.jsonc`)
104
+
105
+ | Section | Fields | Notes |
106
+ | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------ |
107
+ | `storeUrl` | string | memory store DB (`file:` or remote libSQL) |
108
+ | `jobs` | `dbUrl`, `execution` (reserved), `concurrency`, `leaseTimeoutMs` (reserved), `maxAttempts` (1–100), `pollIntervalMs` (reserved) | separate DB for the durable queue |
109
+ | `embedding` | `defaultModel` + `models[]` | **required** — see Models below |
110
+ | `topics` | tree shape (`minLeafItems`, `maxLeafItems`, …) | optional, defaults apply |
111
+ | `graph` | graph config | optional |
112
+ | `llm` | `baseURL`, `apiKey`, `modelId`, `temperature` | enables `dream`/`repair` |
113
+ | `logging` | `enabled`, `level`, `format` (`jsonl`\|`pretty`), `file`, `scopes`, `ringSize` | silent by default; `file` = durable JSONL |
114
+ | `defaultK` / `defaultLimit` | numbers | retrieval defaults |
115
+ | `defaultTtlSecs` | number | write TTL default |
116
+
117
+ ### Embedding models
118
+
119
+ Config-swappable — any Transformers.js ONNX model (the only tested
120
+ provider):
121
+
122
+ ```jsonc
123
+ "embedding": {
124
+ "defaultModel": "bge",
125
+ "models": [
126
+ { "id": "minilm", "provider": "transformers",
127
+ "model": "Xenova/all-MiniLM-L6-v2", "dimensions": 384 },
128
+ { "id": "bge", "provider": "transformers",
129
+ "model": "Xenova/bge-small-en-v1.5", "dimensions": 384 },
130
+ { "id": "bge-base", "provider": "transformers",
131
+ "model": "Xenova/bge-base-en-v1.5", "dimensions": 768 }
132
+ ]
133
+ }
134
+ ```
135
+
136
+ - `dimensions` must match the model's real output — a mismatch fails typed
137
+ on first use
138
+ - Multiple models coexist: each gets its own vector tables
139
+ (`vec_<dims>_<model>`) and its own topic tree; queries route per model
140
+ - **Per-model runtime options** (transformers provider, all optional):
141
+ `device` (`auto` | `gpu` | `cpu` | `wasm` | `webgpu` | `cuda` | `dml` |
142
+ `coreml` | `webnn` — `cpu`/`wasm` tested; GPU/CUDA needs the onnxruntime
143
+ CUDA libs, fails typed without them), `dtype` (`fp32` | `fp16` | `q8` |
144
+ `int8` | `uint8` | `q4` | `bnb4` | `q4f16` | `q2` | `q2f16` | `q1` |
145
+ `q1f16` — quantizes model WEIGHTS only, never the output vectors;
146
+ `fp32`/`q8`/`int8`/`q4`/`fp16` verified end-to-end, others depend on the
147
+ HF repo shipping that ONNX variant), `pooling` (`mean` | `cls` | `none`),
148
+ `normalize` (boolean), `cacheDir`
149
+ - **DB-level vector quantization** via `vectorType` (`float32` |
150
+ `float16` | `float8` | `float1bit`): the per-memory search vectors are
151
+ stored as int8/bfloat16/binary blobs and searched with libSQL's native
152
+ `vector_distance_cos` — `float8` verified end-to-end at **395 B vs
153
+ 1536 B (4× smaller)** for 384-dim. The topic tree's centroid math and
154
+ `memories.embedding` always stay float32.
155
+ - **Switching models**: set `defaultModel` to a configured model (multi-
156
+ model configs coexist — each model gets its own tree + `vec_<dims>_<model>`
157
+ tables). After a restart the new default handles all writes/queries;
158
+ the old model's tree stays intact. Existing memories are backfilled via
159
+ the durable `memory.embed` job (re-embed + re-route into the new tree,
160
+ idempotent/resume-safe) — the store-level switch (`setActiveModel` +
161
+ `findStale`) drives it, reachable through the `{ core: … }` path. Until
162
+ backfilled, old memories remain in the old tree (queries descend the
163
+ ACTIVE model's tree), and the runtime is bound to one active model at a
164
+ time (per-model reads for another model fail typed with
165
+ `topics.ModelMismatch`).
166
+ - Vectors are always stored as float32 (`F32_BLOB`, 4 B/dim: 384 = 1536 B,
167
+ 768 = 3072 B) regardless of `dtype`
168
+
169
+ ### Power users: `{ core: … }`
170
+
171
+ Instead of a config, pass full `CoreOptions` (store/jobs URLs, models,
172
+ `defaultModelId`, `embedderFor` factory, topics/graph/logging/llm,
173
+ `concurrency`, `maxAttempts`, `extraDefs`/`extraHandlers` for custom durable
174
+ jobs — register `{ name: "my.job", handler }` and enqueue it via the
175
+ runtime; `extraHandlers` entries override built-ins by name). This is the
176
+ injection seam used by tests (synthetic embedders).
177
+
178
+ ## API surface
179
+
180
+ ### `write(input, options?)`
181
+
182
+ ```ts
183
+ await dimple.write({
184
+ logicalKey: "deploy-1", // required, non-empty
185
+ content: "…", // required
186
+ namespace?: "default",
187
+ contentType?: "text/plain",
188
+ ttlSecs?: 604800,
189
+ metadata?: "{\"json\":true}", // JSON string
190
+ typedMetadata?: { … },
191
+ }, { embed: true }); // embed + topic-route (default); false for externally-managed embeddings
192
+ ```
193
+
194
+ Returns the persisted `MemoryUnit`: `{ id, logicalKey, content,
195
+ contentType, state: "active" | "superseded" | …, createdAt, ttlSecs,
196
+ namespace, metadata, embedding? }` (re-fetched after embedding). Duplicate
197
+ active `logicalKey` → `Sdk.DuplicateKey` (carries `logicalKey`/`namespace`).
198
+
199
+ **Memory expiry**: `ttlSecs` (or the config `defaultTtlSecs`) schedules
200
+ expiry; a sweep job retires expired memories in the background.
201
+
202
+ ### `get(id)` / `forget(id)`
203
+
204
+ Fetch one memory; soft-delete (cascades edges, rebalances the tree).
205
+
206
+ ### `query(text, options?)`
207
+
208
+ ```ts
209
+ await dimple.query("how does the pipeline deploy", {
210
+ limit?: 10,
211
+ k?: 60, // RRF smoothing
212
+ namespace?: "default",
213
+ profile?: {
214
+ recencyBoost?: 0, // 0 = off; hyperbolically decays with age
215
+ modeWeights?: { vector?: 0.5, fts?: 0.5 },
216
+ conceptFilter?: string[], // concept ids (query sovereignty)
217
+ } | "hybrid" | "semantic" | "keyword",
218
+ topics?: { topicKeys?: string[] },
219
+ });
220
+ ```
221
+
222
+ Returns `AugmentedMemory[]` — each entry carries the memory, the fused
223
+ score, its 1-based `rank`, and the graph context (`supersededBy`,
224
+ `conflicts`, `supportedBy`). Zero-result queries resolve to `[]` (the
225
+ absent-vector-table case is an empty dense leg, never an error).
226
+
227
+ ### `index(options)` — codebase ingestion
228
+
229
+ ```ts
230
+ await dimple.index({
231
+ paths: ["./src", "./packages"], // dirs (recursive) and/or files
232
+ keyPrefix?: "code-",
233
+ namespace?: "default",
234
+ embed?: true,
235
+ maxFileBytes?: 64 * 1024,
236
+ maxChunkBytes?: 8 * 1024,
237
+ extensions?: [".ts", ".tsx", ".js", ".jsx", …],
238
+ });
239
+ // → { files, indexed, skipped, unsupported, failed: [{ filePath, message }] }
240
+ ```
241
+
242
+ - Tree-sitter parse (TypeScript/JavaScript/TSX/JSX) → the extractor
243
+ produces one **file unit** per file (with its import graph in metadata)
244
+ and one **chunk unit** per definition (function/class/method/interface/
245
+ type/enum/variable, with kind/name/line-range/docstring metadata)
246
+ - Every unit embeds + topic-routes (unless `embed: false`)
247
+ - AST-derived graph: relative imports resolve to indexed files and
248
+ class→method containment become **confirmed `memory_supports` edges**
249
+ (`source: "rule"`)
250
+ - **Idempotent**: keys are cwd-anchored + deterministic — re-indexing the
251
+ same tree skips existing units
252
+ - Per-file failures are collected, never fatal
253
+
254
+ ### `updateEmbedding(id, embedding, modelKey?)` / `embed(text)`
255
+
256
+ Attach an externally-computed embedding (completes the `{ embed: false }`
257
+ workflow — the memory becomes semantically retrievable + topic-routed), or
258
+ embed text with the SDK's own pipeline.
259
+
260
+ ### `dream(modelKey)` / `repair(modelKey)`
261
+
262
+ Run the consolidation job (concept clustering + LLM edge classification;
263
+ requires the `llm` config section) or the graph repair pass. Each call
264
+ gets a fresh run id (always executes). Failures are typed:
265
+ `Sdk.LLMNotConfigured`, `Sdk.JobFailed`.
266
+
267
+ ### `explain({ kind: "memory" | "topic" | "concept", id })`
268
+
269
+ Graph neighborhood traversal — `{ nodes: [{ kind, id, depth, edgeKind }] }`.
270
+
271
+ ### `health(modelKey)` / `graph.*`
272
+
273
+ - `health` — topic-tree invariant sweep (`warnCount` + violation strings)
274
+ - `graph.addEdge({ fromMemoryId, toMemoryId, edgeKind: "memory_supports" |
275
+ "memory_supersedes" | "memory_contradicts", source: "rule" | "llm" |
276
+ "agent", weight?, confidence?, confirmed? })` — explicit edges; typed
277
+ validation (no self-loops, known kinds, weight > 0, confidence ∈ [0,1])
278
+ - `graph.candidates()` — pending structural candidates + isolated count
279
+ - `graph.coverage(modelKey)` — coverage %, pending, concept count
280
+
281
+ ### `dispose()`
282
+
283
+ Stops the worker, closes sinks + the store. Idempotent; post-dispose calls
284
+ reject with a closed `Sdk.StoreFailure`.
285
+
286
+ ## Errors — the closed hierarchy
287
+
288
+ Every promise rejects with typed errors from one family (never raw Effect
289
+ internals). Export surface: `ConfigError`, `NotFoundError`,
290
+ `DuplicateKeyError` (with `logicalKey`/`namespace`), `DuplicateEdgeError`,
291
+ `LLMNotConfiguredError`, `JobFailedError`, `StoreFailure`, `InputError`,
292
+ plus the `remapError` helper (maps unknown failures onto the hierarchy).
293
+ Also importable from the `@ponraaj/dimple-sdk/errors` subpath.
294
+
295
+ **Type exports** (all importable from the package root): `Dimple` (the
296
+ facade interface), `MakeDimpleOptions`, `ConfigInput`, `QueryOptions`,
297
+ `IndexOptions`, `IndexResult`, plus the error classes and the `DimpleError`
298
+ union type. Errors also carry stable `_tag` values (`Sdk.DuplicateKey`,
299
+ `Sdk.NotFound`, `Sdk.InputError`, `Sdk.JobFailed`, `Sdk.LLMNotConfigured`,
300
+ `Sdk.StoreFailure`, …) — the recommended discriminator.
301
+
302
+ ## Background jobs (what runs while you work)
303
+
304
+ | Job | Trigger | Work |
305
+ | ------------------------------------------------- | ------------------- | -------------------------------------------------------------------------------------------------------------------------- |
306
+ | `topic.split` / `topic.merge` / `topic.rebalance` | automatic per write | keep every leaf within its size budget (deterministic splits, donor borrow, nearest-sibling merge, uneven-split rebalance) |
307
+ | `memory.embed` | model backfill | re-embed a memory with a target model + route into its tree |
308
+ | `memory.dream` | `dream()` | concept clustering + LLM edge classification |
309
+ | `graph.repair` | `repair()` | graph invariant repair |
310
+ | `audit.write` | query path | async audit-log writes (never blocks queries) — one FUSED row per hybrid query with the per-leg breakdown |
311
+
312
+ Jobs are durable (leased, retried up to `maxAttempts`, resume-safe across
313
+ processes). Every `makeDimple` boots the worker; `dispose` stops it.
314
+
315
+ Every `query` also writes a retrieval audit row (query, namespace, mode,
316
+ per-leg counts/scores, sources) into `retrieval_log` — machine-readable
317
+ query history, persisted asynchronously (never blocks the query).
318
+
319
+ ## Storage & logs
320
+
321
+ | What | Where |
322
+ | ------------------------------ | -------------------------------------------------------------------------------------------- |
323
+ | Memories, tree, graph, audit | `storeUrl` (`file:…`) |
324
+ | Durable queue + workflow state | `jobs.dbUrl` (separate DB) |
325
+ | Logs | stdout + in-memory ring (4096) by default; durable JSONL **only** when `logging.file` is set |
326
+ | Grammar WASMs | `~/.cache/dimple/tree-sitter/` (downloaded once, offline after) |
327
+ | ONNX model | bun/npm cache (~90 MB, first use) |
328
+
329
+ ## Notes
330
+
331
+ - Determinism: same data + same config ⇒ same tree, same clusters, same
332
+ audit hashes — reproducible across machines and runs
333
+ - `migrationFolder` is accepted in `makeDimple` options for advanced users;
334
+ the default path uses migrations embedded at build time (nothing to ship)
package/dist/index.js CHANGED
@@ -659,7 +659,35 @@ var EmbeddingModelConfig = Schema6.Struct({
659
659
  dimensions: Schema6.Number,
660
660
  vectorType: Schema6.optional(Schema6.String),
661
661
  apiKey: Schema6.optional(Schema6.String),
662
- indexConfig: Schema6.optional(IndexConfig)
662
+ indexConfig: Schema6.optional(IndexConfig),
663
+ device: Schema6.optional(Schema6.Literals([
664
+ "auto",
665
+ "gpu",
666
+ "cpu",
667
+ "wasm",
668
+ "webgpu",
669
+ "cuda",
670
+ "dml",
671
+ "coreml",
672
+ "webnn"
673
+ ])),
674
+ dtype: Schema6.optional(Schema6.Literals([
675
+ "fp32",
676
+ "fp16",
677
+ "q8",
678
+ "int8",
679
+ "uint8",
680
+ "q4",
681
+ "bnb4",
682
+ "q4f16",
683
+ "q2",
684
+ "q2f16",
685
+ "q1",
686
+ "q1f16"
687
+ ])),
688
+ pooling: Schema6.optional(Schema6.Literals(["mean", "cls", "none"])),
689
+ normalize: Schema6.optional(Schema6.Boolean),
690
+ cacheDir: Schema6.optional(Schema6.String)
663
691
  });
664
692
  var EmbeddingConfig = Schema6.Struct({
665
693
  defaultModel: Schema6.String,
@@ -1410,13 +1438,11 @@ var makeBackend = (url, client, db, migrationFolder) => ({
1410
1438
  ensureTopicVectorTable: (model) => Effect9.tryPromise({
1411
1439
  try: () => {
1412
1440
  const tableName = topicVectorTableName(model);
1413
- const vecType = model.vectorType ?? "float32";
1414
- const sqlType = VECTOR_SQL_TYPES[vecType] ?? "F32_BLOB";
1415
1441
  const dims = model.dimensions;
1416
1442
  const sql3 = `
1417
1443
  CREATE TABLE IF NOT EXISTS ${tableName} (
1418
1444
  topic_key TEXT PRIMARY KEY,
1419
- sum_vector ${sqlType}(${dims}) NOT NULL,
1445
+ sum_vector F32_BLOB(${dims}) NOT NULL,
1420
1446
  model_key TEXT NOT NULL
1421
1447
  );
1422
1448
  `;
@@ -1428,9 +1454,8 @@ var makeBackend = (url, client, db, migrationFolder) => ({
1428
1454
  try: () => {
1429
1455
  const tableName = topicVectorTableName(model);
1430
1456
  const vecJson = JSON.stringify([...sumVector]);
1431
- const convertFn = VECTOR_CONVERT_FNS[model.vectorType ?? "float32"] ?? "vector32";
1432
1457
  return client.execute({
1433
- sql: `INSERT OR REPLACE INTO ${tableName} (topic_key, sum_vector, model_key) VALUES (?, ${convertFn}(?), ?)`,
1458
+ sql: `INSERT OR REPLACE INTO ${tableName} (topic_key, sum_vector, model_key) VALUES (?, vector32(?), ?)`,
1434
1459
  args: [topicKey, vecJson, model.id]
1435
1460
  });
1436
1461
  },
@@ -1462,16 +1487,18 @@ var makeBackend = (url, client, db, migrationFolder) => ({
1462
1487
  return [];
1463
1488
  const tableName = vectorTableName(model);
1464
1489
  const placeholders = memoryIds.map(() => "?").join(", ");
1490
+ const quantized = (model.vectorType ?? "float32") !== "float32";
1491
+ const extractCol = quantized ? "vector_extract(embedding) AS embedding" : "embedding";
1465
1492
  const rows = yield* Effect9.tryPromise({
1466
1493
  try: () => client.execute({
1467
- sql: `SELECT memory_id, embedding FROM ${tableName} WHERE memory_id IN (${placeholders})`,
1494
+ sql: `SELECT memory_id, ${extractCol} FROM ${tableName} WHERE memory_id IN (${placeholders})`,
1468
1495
  args: [...memoryIds]
1469
1496
  }).then((rs) => rs.rows),
1470
1497
  catch: (cause) => new StoreError({ message: `DbBackend.fetchMemoryVectors: ${cause}` })
1471
1498
  });
1472
1499
  return rows.map((row) => ({
1473
1500
  memoryId: row.memory_id,
1474
- vector: decodeVectorBlob(row.embedding)
1501
+ vector: quantized ? JSON.parse(String(row.embedding)) : decodeVectorBlob(row.embedding)
1475
1502
  }));
1476
1503
  }),
1477
1504
  listMemoryIds: (model) => Effect9.gen(function* () {
@@ -3237,22 +3264,15 @@ import { and as and4, asc as asc3, eq as eq4, inArray as inArray2, sql as sql4 }
3237
3264
  // ../store/src/topics/topic-vectors.ts
3238
3265
  import { sql as sql3 } from "drizzle-orm";
3239
3266
  var topicVectorTableName2 = (model) => `topic_vec_${model.dimensions}_${model.id.replace(/[^a-zA-Z0-9_]/g, "_")}`;
3240
- var VECTOR_CONVERT_FNS2 = {
3241
- float32: "vector32",
3242
- float16: "vector16",
3243
- float8: "vector8",
3244
- float1bit: "vector1bit"
3245
- };
3246
3267
  var deleteTopicVectorRow = async (runner, model, topicKey) => {
3247
3268
  const table = topicVectorTableName2(model);
3248
3269
  await runner.run(sql3`DELETE FROM ${sql3.raw(table)} WHERE topic_key = ${topicKey}`);
3249
3270
  };
3250
3271
  var writeTopicVector = async (runner, model, topicKey, ls) => {
3251
3272
  const table = topicVectorTableName2(model);
3252
- const convertFn = VECTOR_CONVERT_FNS2[model.vectorType ?? "float32"] ?? "vector32";
3253
3273
  await runner.run(sql3`
3254
3274
  INSERT OR REPLACE INTO ${sql3.raw(table)} (topic_key, sum_vector, model_key)
3255
- VALUES (${topicKey}, ${sql3.raw(convertFn)}(${JSON.stringify([...ls])}), ${model.id})
3275
+ VALUES (${topicKey}, vector32(${JSON.stringify([...ls])}), ${model.id})
3256
3276
  `);
3257
3277
  };
3258
3278
 
@@ -6807,7 +6827,15 @@ var DISABLED_LOGGING2 = {
6807
6827
  format: "jsonl",
6808
6828
  ringSize: 4096
6809
6829
  };
6810
- var embedderFromConfig = (model) => model.provider === "transformers" ? Effect39.succeed(makeTransformersModel({ modelId: model.model, dimensions: model.dimensions })) : model.provider === "openai" ? model.apiKey === undefined ? Effect39.fail(new EmbeddingError({ message: `OpenAI model ${model.id} requires an apiKey` })) : Effect39.succeed(makeOpenAIModel({
6830
+ var embedderFromConfig = (model) => model.provider === "transformers" ? Effect39.succeed(makeTransformersModel({
6831
+ modelId: model.model,
6832
+ dimensions: model.dimensions,
6833
+ ...model.device !== undefined ? { device: model.device } : {},
6834
+ ...model.dtype !== undefined ? { dtype: model.dtype } : {},
6835
+ ...model.pooling !== undefined ? { pooling: model.pooling } : {},
6836
+ ...model.normalize !== undefined ? { normalize: model.normalize } : {},
6837
+ ...model.cacheDir !== undefined ? { cacheDir: model.cacheDir } : {}
6838
+ })) : model.provider === "openai" ? model.apiKey === undefined ? Effect39.fail(new EmbeddingError({ message: `OpenAI model ${model.id} requires an apiKey` })) : Effect39.succeed(makeOpenAIModel({
6811
6839
  modelId: model.id,
6812
6840
  model: model.model,
6813
6841
  dimensions: model.dimensions,
@@ -18,6 +18,15 @@ export declare const EmbeddingModelConfig: Schema.Struct<{
18
18
  readonly maxNeighbors: Schema.optional<Schema.Number>;
19
19
  readonly searchL: Schema.optional<Schema.Number>;
20
20
  }>>;
21
+ /** Transformers.js runtime options — per-model, all optional (defaults:
22
+ * device "cpu", dtype "q8", pooling "mean", normalize true). `dtype`
23
+ * quantizes the MODEL WEIGHTS (inference memory/speed), never the
24
+ * output vectors; unsupported devices fail typed at first use. */
25
+ readonly device: Schema.optional<Schema.Literals<readonly ["auto", "gpu", "cpu", "wasm", "webgpu", "cuda", "dml", "coreml", "webnn"]>>;
26
+ readonly dtype: Schema.optional<Schema.Literals<readonly ["fp32", "fp16", "q8", "int8", "uint8", "q4", "bnb4", "q4f16", "q2", "q2f16", "q1", "q1f16"]>>;
27
+ readonly pooling: Schema.optional<Schema.Literals<readonly ["mean", "cls", "none"]>>;
28
+ readonly normalize: Schema.optional<Schema.Boolean>;
29
+ readonly cacheDir: Schema.optional<Schema.String>;
21
30
  }>;
22
31
  export interface EmbeddingModelConfig extends Schema.Schema.Type<typeof EmbeddingModelConfig> {
23
32
  }
@@ -35,6 +44,15 @@ export declare const EmbeddingConfig: Schema.Struct<{
35
44
  readonly maxNeighbors: Schema.optional<Schema.Number>;
36
45
  readonly searchL: Schema.optional<Schema.Number>;
37
46
  }>>;
47
+ /** Transformers.js runtime options — per-model, all optional (defaults:
48
+ * device "cpu", dtype "q8", pooling "mean", normalize true). `dtype`
49
+ * quantizes the MODEL WEIGHTS (inference memory/speed), never the
50
+ * output vectors; unsupported devices fail typed at first use. */
51
+ readonly device: Schema.optional<Schema.Literals<readonly ["auto", "gpu", "cpu", "wasm", "webgpu", "cuda", "dml", "coreml", "webnn"]>>;
52
+ readonly dtype: Schema.optional<Schema.Literals<readonly ["fp32", "fp16", "q8", "int8", "uint8", "q4", "bnb4", "q4f16", "q2", "q2f16", "q1", "q1f16"]>>;
53
+ readonly pooling: Schema.optional<Schema.Literals<readonly ["mean", "cls", "none"]>>;
54
+ readonly normalize: Schema.optional<Schema.Boolean>;
55
+ readonly cacheDir: Schema.optional<Schema.String>;
38
56
  }>>;
39
57
  }>;
40
58
  export interface EmbeddingConfig extends Schema.Schema.Type<typeof EmbeddingConfig> {
@@ -115,6 +133,15 @@ export declare const DimpleConfig: Schema.Struct<{
115
133
  readonly maxNeighbors: Schema.optional<Schema.Number>;
116
134
  readonly searchL: Schema.optional<Schema.Number>;
117
135
  }>>;
136
+ /** Transformers.js runtime options — per-model, all optional (defaults:
137
+ * device "cpu", dtype "q8", pooling "mean", normalize true). `dtype`
138
+ * quantizes the MODEL WEIGHTS (inference memory/speed), never the
139
+ * output vectors; unsupported devices fail typed at first use. */
140
+ readonly device: Schema.optional<Schema.Literals<readonly ["auto", "gpu", "cpu", "wasm", "webgpu", "cuda", "dml", "coreml", "webnn"]>>;
141
+ readonly dtype: Schema.optional<Schema.Literals<readonly ["fp32", "fp16", "q8", "int8", "uint8", "q4", "bnb4", "q4f16", "q2", "q2f16", "q1", "q1f16"]>>;
142
+ readonly pooling: Schema.optional<Schema.Literals<readonly ["mean", "cls", "none"]>>;
143
+ readonly normalize: Schema.optional<Schema.Boolean>;
144
+ readonly cacheDir: Schema.optional<Schema.String>;
118
145
  }>>;
119
146
  }>>;
120
147
  readonly jobs: Schema.optional<Schema.Struct<{
@@ -4,7 +4,7 @@ import { Service, type EmbeddingModel } from "./provider.ts";
4
4
  interface TransformersOptions {
5
5
  readonly modelId?: string;
6
6
  readonly device?: DeviceType;
7
- readonly dtype?: "fp32" | "fp16" | "q8" | "q4";
7
+ readonly dtype?: "fp32" | "fp16" | "q8" | "int8" | "uint8" | "q4" | "bnb4" | "q4f16" | "q2" | "q2f16" | "q1" | "q1f16";
8
8
  readonly pooling?: "mean" | "cls" | "none";
9
9
  readonly normalize?: boolean;
10
10
  readonly cacheDir?: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ponraaj/dimple-sdk",
3
- "version": "0.0.0-alpha.1",
3
+ "version": "0.0.0-alpha.2",
4
4
  "private": false,
5
5
  "files": [
6
6
  "dist"
@@ -39,6 +39,16 @@
39
39
  "web-tree-sitter": "^0.26.9",
40
40
  "zod": "^4.4.3"
41
41
  },
42
+ "devDependencies": {
43
+ "@dimple/config": "0.0.0-alpha.1",
44
+ "@dimple/core": "0.0.0-alpha.1",
45
+ "@dimple/embedder": "0.0.0-alpha.1",
46
+ "@dimple/extractor": "0.0.0-alpha.1",
47
+ "@dimple/parser": "0.0.0-alpha.1",
48
+ "@dimple/retriever": "0.0.0-alpha.1",
49
+ "@dimple/store": "0.0.0-alpha.1",
50
+ "@dimple/workflow": "0.0.0-alpha.1"
51
+ },
42
52
  "peerDependencies": {
43
53
  "typescript": "^5"
44
54
  }