@forbocai/browser 0.5.9 → 0.6.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.
package/dist/index.js CHANGED
@@ -30,17 +30,37 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- createBrowserCortex: () => createBrowserCortex,
34
- createBrowserMemory: () => createBrowserMemory,
35
- createCortex: () => createCortex,
36
- createMemory: () => createMemory,
33
+ browserCortexSlice: () => browserCortexSlice,
34
+ browserMemorySlice: () => browserMemorySlice,
35
+ browserVectorSlice: () => browserVectorSlice,
36
+ clearBrowserMemoryThunk: () => clearBrowserMemoryThunk,
37
+ completeBrowserCortexThunk: () => completeBrowserCortexThunk,
38
+ createBrowserStore: () => createBrowserStore,
39
+ dispatch: () => dispatch,
40
+ generateBrowserEmbeddingThunk: () => generateBrowserEmbeddingThunk,
37
41
  generateEmbedding: () => generateEmbedding,
38
- initVectorEngine: () => initVectorEngine
42
+ getBrowserEngine: () => getBrowserEngine,
43
+ initBrowserCortexThunk: () => initBrowserCortexThunk,
44
+ initBrowserMemoryThunk: () => initBrowserMemoryThunk,
45
+ initBrowserVectorThunk: () => initBrowserVectorThunk,
46
+ recallBrowserMemoryThunk: () => recallBrowserMemoryThunk,
47
+ selectAllBrowserCortexSessions: () => selectAllBrowserCortexSessions,
48
+ selectAllBrowserDBs: () => selectAllBrowserDBs,
49
+ selectBrowserCortexById: () => selectBrowserCortexById,
50
+ selectBrowserDBById: () => selectBrowserDBById,
51
+ setDownloadProgress: () => setDownloadProgress,
52
+ setDownloadState: () => setDownloadState,
53
+ store: () => store,
54
+ storeBrowserMemoryThunk: () => storeBrowserMemoryThunk
39
55
  });
40
56
  module.exports = __toCommonJS(index_exports);
41
57
 
42
- // src/cortex.ts
58
+ // src/browserCortexSlice.ts
59
+ var import_toolkit = require("@reduxjs/toolkit");
60
+ var import_core = require("@forbocai/core");
43
61
  var import_meta = {};
62
+ var _engines = {};
63
+ var browserCortexAdapter = (0, import_toolkit.createEntityAdapter)();
44
64
  var DEFAULT_MODEL = "smollm2-135m";
45
65
  var MODEL_ALIASES = {
46
66
  "smollm2-135m": "SmolLM2-135M-Instruct-q0f16-MLC",
@@ -49,169 +69,320 @@ var MODEL_ALIASES = {
49
69
  "llama3-8b": "Llama-3.1-8B-Instruct-q4f16_1-MLC"
50
70
  };
51
71
  var resolveModelId = (alias) => MODEL_ALIASES[alias] ?? alias;
52
- var yieldTokens = async function* (iterator) {
53
- const { value, done } = await iterator.next();
54
- if (done) return;
55
- const content = value.choices[0]?.delta?.content || "";
56
- if (content) yield content;
57
- yield* yieldTokens(iterator);
58
- };
59
- var createBrowserCortex = (config = {}) => {
60
- const friendlyModel = config.model || DEFAULT_MODEL;
61
- const modelId = resolveModelId(friendlyModel);
62
- let _state = {
63
- engine: null,
64
- status: {
65
- id: "browser-init",
66
- model: friendlyModel,
67
- ready: false,
68
- engine: "web-llm"
69
- }
70
- };
71
- const init = async (onProgress) => {
72
- if (_state.status.ready) return _state.status;
72
+ var initBrowserCortexThunk = (0, import_toolkit.createAsyncThunk)(
73
+ "browser/cortex/init",
74
+ async ({ model = DEFAULT_MODEL, onProgress }, { dispatch: dispatch2, rejectWithValue }) => {
73
75
  if (typeof window === "undefined") {
74
- throw new Error("BrowserCortex requires a browser environment");
76
+ return rejectWithValue("BrowserCortex requires a browser environment");
75
77
  }
76
- const { CreateWebWorkerMLCEngine } = await import("@mlc-ai/web-llm");
77
- const initProgressCallback = (report) => {
78
- const match = report.text?.match(/(\d+)%/);
79
- if (onProgress && match) onProgress(parseInt(match[1], 10));
80
- };
81
- const engine = await CreateWebWorkerMLCEngine(
82
- new Worker(new URL("./worker.js", import_meta.url), { type: "module" }),
83
- modelId,
84
- { initProgressCallback }
85
- );
86
- _state = {
87
- engine,
88
- status: {
78
+ const modelId = resolveModelId(model);
79
+ dispatch2((0, import_core.cortexInitStart)());
80
+ dispatch2(setDownloadState({ isDownloading: true, progress: 0 }));
81
+ try {
82
+ const { CreateWebWorkerMLCEngine } = await import("@mlc-ai/web-llm");
83
+ const initProgressCallback = (report) => {
84
+ const match = report.text?.match(/(\d+)%/);
85
+ if (match) {
86
+ const progress = parseInt(match[1], 10);
87
+ dispatch2(setDownloadProgress(progress));
88
+ if (onProgress) onProgress(progress);
89
+ }
90
+ };
91
+ const engine = await CreateWebWorkerMLCEngine(
92
+ new Worker(new URL("./worker.js", import_meta.url), { type: "module" }),
93
+ modelId,
94
+ { initProgressCallback }
95
+ );
96
+ _engines[model] = engine;
97
+ const sessionInfo = {
98
+ id: model,
99
+ ready: true,
100
+ engine: "web-llm"
101
+ };
102
+ const newStatus = {
89
103
  id: `ctx_web_${Date.now()}`,
90
104
  model: modelId,
91
105
  ready: true,
92
106
  engine: "web-llm"
93
- }
94
- };
95
- return _state.status;
96
- };
97
- const ensureReady = async () => {
98
- if (!_state.status.ready) await init();
99
- return _state;
100
- };
101
- const complete = async (prompt, options = {}) => {
102
- const { engine } = await ensureReady();
103
- const reply = await engine.chat.completions.create({
104
- messages: [{ role: "user", content: prompt }],
105
- max_gen_len: options.maxTokens,
106
- temperature: options.temperature
107
- });
108
- return reply.choices[0].message.content || "";
109
- };
110
- const completeStream = async function* (prompt, options = {}) {
111
- const { engine } = await ensureReady();
112
- const chunks = await engine.chat.completions.create({
113
- messages: [{ role: "user", content: prompt }],
114
- max_gen_len: options.maxTokens,
115
- temperature: options.temperature,
116
- stream: true
107
+ };
108
+ dispatch2((0, import_core.cortexInitSuccess)(newStatus));
109
+ dispatch2(setDownloadState({ isDownloading: false, progress: 100 }));
110
+ return sessionInfo;
111
+ } catch (e) {
112
+ const error = e.message || "Unknown error";
113
+ dispatch2((0, import_core.cortexInitFailed)(error));
114
+ dispatch2(setDownloadState({ isDownloading: false, progress: 0 }));
115
+ return rejectWithValue(error);
116
+ }
117
+ }
118
+ );
119
+ var completeBrowserCortexThunk = (0, import_toolkit.createAsyncThunk)(
120
+ "browser/cortex/complete",
121
+ async ({ model = DEFAULT_MODEL, prompt, maxTokens, temperature }, { rejectWithValue }) => {
122
+ const engine = _engines[model];
123
+ if (!engine) {
124
+ return rejectWithValue(`Browser Cortex session not found for model: ${model}`);
125
+ }
126
+ try {
127
+ const reply = await engine.chat.completions.create({
128
+ messages: [{ role: "user", content: prompt }],
129
+ max_gen_len: maxTokens,
130
+ temperature
131
+ });
132
+ return reply.choices[0].message.content || "";
133
+ } catch (e) {
134
+ return rejectWithValue(e.message || "Inference failed");
135
+ }
136
+ }
137
+ );
138
+ var browserCortexSlice = (0, import_toolkit.createSlice)({
139
+ name: "browserCortex",
140
+ initialState: browserCortexAdapter.getInitialState({
141
+ progress: 0,
142
+ isDownloading: false,
143
+ error: null
144
+ }),
145
+ reducers: {
146
+ setDownloadProgress: (state, action) => {
147
+ state.progress = action.payload;
148
+ },
149
+ setDownloadState: (state, action) => {
150
+ state.isDownloading = action.payload.isDownloading;
151
+ state.progress = action.payload.progress;
152
+ }
153
+ },
154
+ extraReducers: (builder) => {
155
+ builder.addCase(initBrowserCortexThunk.fulfilled, (state, action) => {
156
+ browserCortexAdapter.upsertOne(state, action.payload);
157
+ state.error = null;
158
+ }).addCase(initBrowserCortexThunk.rejected, (state, action) => {
159
+ state.error = action.payload || "Init failed";
117
160
  });
118
- yield* yieldTokens(chunks[Symbol.asyncIterator]());
119
- };
120
- return { init, complete, completeStream };
121
- };
122
- var createCortex = (config = {}) => createBrowserCortex(config);
161
+ }
162
+ });
163
+ var { setDownloadProgress, setDownloadState } = browserCortexSlice.actions;
164
+ var {
165
+ selectById: selectBrowserCortexById,
166
+ selectAll: selectAllBrowserCortexSessions
167
+ } = browserCortexAdapter.getSelectors((state) => state.browserCortex);
168
+ var getBrowserEngine = (model = DEFAULT_MODEL) => _engines[model];
169
+ var browserCortexSlice_default = browserCortexSlice.reducer;
123
170
 
124
- // src/memory.ts
171
+ // src/browserMemorySlice.ts
172
+ var import_toolkit3 = require("@reduxjs/toolkit");
125
173
  var import_core2 = require("@forbocai/core");
126
174
 
127
- // src/vector.ts
128
- var import_core = require("@forbocai/core");
129
- var getEmbedder = (0, import_core.memoiseAsync)(async () => {
130
- if (typeof window === "undefined") {
131
- throw new Error("BrowserVectorEngine requires a browser environment");
132
- }
133
- const { pipeline } = await import("@huggingface/transformers");
134
- return pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2");
135
- });
136
- var initVectorEngine = async () => {
137
- await getEmbedder();
175
+ // src/browserVectorSlice.ts
176
+ var import_toolkit2 = require("@reduxjs/toolkit");
177
+ var _pipeline = null;
178
+ var initialState = {
179
+ isReady: false,
180
+ error: null
138
181
  };
182
+ var initBrowserVectorThunk = (0, import_toolkit2.createAsyncThunk)(
183
+ "browser/vector/init",
184
+ async (_, { rejectWithValue }) => {
185
+ if (typeof window === "undefined") {
186
+ return rejectWithValue("BrowserVector requires a browser environment");
187
+ }
188
+ try {
189
+ const { pipeline } = await import("@huggingface/transformers");
190
+ _pipeline = await pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2");
191
+ } catch (e) {
192
+ return rejectWithValue(e.message || "Failed to load Transformers.js");
193
+ }
194
+ }
195
+ );
196
+ var generateBrowserEmbeddingThunk = (0, import_toolkit2.createAsyncThunk)(
197
+ "browser/vector/generate",
198
+ async (text, { dispatch: dispatch2, rejectWithValue }) => {
199
+ if (!_pipeline) {
200
+ await dispatch2(initBrowserVectorThunk());
201
+ }
202
+ try {
203
+ const result = await _pipeline(text, { pooling: "mean", normalize: true });
204
+ return Array.from(result.data);
205
+ } catch (e) {
206
+ return rejectWithValue(e.message || "Embedding generation failed");
207
+ }
208
+ }
209
+ );
139
210
  var generateEmbedding = async (text) => {
140
- try {
141
- const embedder = await getEmbedder();
142
- const result = await embedder(text, { pooling: "mean", normalize: true });
143
- return Array.from(result.data);
144
- } catch (e) {
145
- console.error("Browser embedding failed:", e);
146
- return new Array(384).fill(0);
211
+ if (!_pipeline) {
212
+ const { pipeline } = await import("@huggingface/transformers");
213
+ _pipeline = await pipeline("feature-extraction", "Xenova/all-MiniLM-L6-v2");
147
214
  }
215
+ const result = await _pipeline(text, { pooling: "mean", normalize: true });
216
+ return Array.from(result.data);
148
217
  };
218
+ var browserVectorSlice = (0, import_toolkit2.createSlice)({
219
+ name: "browserVector",
220
+ initialState,
221
+ reducers: {},
222
+ extraReducers: (builder) => {
223
+ builder.addCase(initBrowserVectorThunk.fulfilled, (state) => {
224
+ state.isReady = true;
225
+ state.error = null;
226
+ }).addCase(initBrowserVectorThunk.rejected, (state, action) => {
227
+ state.error = action.payload;
228
+ });
229
+ }
230
+ });
231
+ var browserVectorSlice_default = browserVectorSlice.reducer;
149
232
 
150
- // src/memory.ts
151
- var createBrowserMemory = (config = {}) => {
152
- const getDb = (0, import_core2.memoiseAsync)(async () => {
233
+ // src/browserMemorySlice.ts
234
+ var _dbs = {};
235
+ var browserMemoryAdapter = (0, import_toolkit3.createEntityAdapter)();
236
+ var initBrowserMemoryThunk = (0, import_toolkit3.createAsyncThunk)(
237
+ "browser/memory/init",
238
+ async ({ dbName = "default" } = {}, { rejectWithValue }) => {
153
239
  if (typeof window === "undefined") {
154
- throw new Error("BrowserMemory requires a browser environment");
240
+ return rejectWithValue("BrowserMemory requires a browser environment");
155
241
  }
156
- const { create } = await import("@orama/orama");
157
- return create({
158
- schema: {
159
- id: "string",
160
- text: "string",
161
- timestamp: "number",
162
- type: "string",
163
- importance: "number",
164
- embedding: "vector[384]"
165
- }
242
+ try {
243
+ const { create } = await import("@orama/orama");
244
+ const db = await create({
245
+ schema: {
246
+ id: "string",
247
+ text: "string",
248
+ timestamp: "number",
249
+ type: "string",
250
+ importance: "number",
251
+ embedding: "vector[384]"
252
+ }
253
+ });
254
+ _dbs[dbName] = db;
255
+ return { id: dbName, isInitialized: true };
256
+ } catch (e) {
257
+ return rejectWithValue(e.message || "Failed to initialize Orama");
258
+ }
259
+ }
260
+ );
261
+ var storeBrowserMemoryThunk = (0, import_toolkit3.createAsyncThunk)(
262
+ "browser/memory/store",
263
+ async ({ text, type = "observation", importance = 0.5, dbName = "default" }, { dispatch: dispatch2, rejectWithValue }) => {
264
+ let db = _dbs[dbName];
265
+ if (!db) {
266
+ const result = await dispatch2(initBrowserMemoryThunk({ dbName })).unwrap();
267
+ db = _dbs[result.id];
268
+ }
269
+ dispatch2((0, import_core2.memoryStoreStart)());
270
+ try {
271
+ const item = {
272
+ id: `mem_br_${Date.now()}_${Math.random().toString(36).substring(7)}`,
273
+ text,
274
+ timestamp: Date.now(),
275
+ type,
276
+ importance
277
+ };
278
+ const embedding = await generateEmbedding(text);
279
+ const { insert } = await import("@orama/orama");
280
+ await insert(db, { ...item, embedding });
281
+ dispatch2((0, import_core2.memoryStoreSuccess)(item));
282
+ return item;
283
+ } catch (e) {
284
+ const error = e.message || "Memory store failed";
285
+ dispatch2((0, import_core2.memoryStoreFailed)(error));
286
+ return rejectWithValue(error);
287
+ }
288
+ }
289
+ );
290
+ var recallBrowserMemoryThunk = (0, import_toolkit3.createAsyncThunk)(
291
+ "browser/memory/recall",
292
+ async ({ query, limit = 5, threshold = 0.5, dbName = "default" }, { dispatch: dispatch2, rejectWithValue }) => {
293
+ let db = _dbs[dbName];
294
+ if (!db) {
295
+ const result = await dispatch2(initBrowserMemoryThunk({ dbName })).unwrap();
296
+ db = _dbs[result.id];
297
+ }
298
+ dispatch2((0, import_core2.memoryRecallStart)());
299
+ try {
300
+ const embedding = await generateEmbedding(query);
301
+ const { search } = await import("@orama/orama");
302
+ const results = await search(db, {
303
+ mode: "vector",
304
+ vector: { value: embedding, property: "embedding" },
305
+ similarity: threshold,
306
+ limit
307
+ });
308
+ const items = results.hits.map((hit) => hit.document);
309
+ dispatch2((0, import_core2.memoryRecallSuccess)(items));
310
+ return items;
311
+ } catch (e) {
312
+ const error = e.message || "Memory recall failed";
313
+ dispatch2((0, import_core2.memoryRecallFailed)(error));
314
+ return rejectWithValue(error);
315
+ }
316
+ }
317
+ );
318
+ var clearBrowserMemoryThunk = (0, import_toolkit3.createAsyncThunk)(
319
+ "browser/memory/clear",
320
+ async ({ dbName = "default" } = {}, { dispatch: dispatch2 }) => {
321
+ delete _dbs[dbName];
322
+ dispatch2((0, import_core2.memoryClear)());
323
+ }
324
+ );
325
+ var browserMemorySlice = (0, import_toolkit3.createSlice)({
326
+ name: "browserMemory",
327
+ initialState: browserMemoryAdapter.getInitialState({
328
+ error: null
329
+ }),
330
+ reducers: {},
331
+ extraReducers: (builder) => {
332
+ builder.addCase(initBrowserMemoryThunk.fulfilled, (state, action) => {
333
+ browserMemoryAdapter.upsertOne(state, action.payload);
334
+ state.error = null;
335
+ }).addCase(initBrowserMemoryThunk.rejected, (state, action) => {
336
+ state.error = action.payload;
166
337
  });
338
+ }
339
+ });
340
+ var {
341
+ selectById: selectBrowserDBById,
342
+ selectAll: selectAllBrowserDBs
343
+ } = browserMemoryAdapter.getSelectors((state) => state.browserMemory);
344
+ var browserMemorySlice_default = browserMemorySlice.reducer;
345
+
346
+ // src/worker.ts
347
+ var import_web_llm = require("@mlc-ai/web-llm");
348
+ var handler = new import_web_llm.WebWorkerMLCEngineHandler();
349
+ self.onmessage = (msg) => {
350
+ handler.onmessage(msg);
351
+ };
352
+
353
+ // src/store.ts
354
+ var import_core3 = require("@forbocai/core");
355
+ var createBrowserStore = () => {
356
+ return (0, import_core3.createSDKStore)({
357
+ browserCortex: browserCortexSlice_default,
358
+ browserMemory: browserMemorySlice_default,
359
+ browserVector: browserVectorSlice_default
167
360
  });
168
- const store = async (text, type = "observation", importance = 0.5) => {
169
- const instance = await getDb();
170
- const item = {
171
- id: `mem_br_${Date.now()}_${Math.random().toString(36).substring(7)}`,
172
- text,
173
- timestamp: Date.now(),
174
- type,
175
- importance
176
- };
177
- const embedding = await generateEmbedding(text);
178
- const { insert } = await import("@orama/orama");
179
- await insert(instance, { ...item, embedding });
180
- return item;
181
- };
182
- const recall = async (query, limit = 5, threshold) => {
183
- const instance = await getDb();
184
- const embedding = await generateEmbedding(query);
185
- const { search } = await import("@orama/orama");
186
- const results = await search(instance, {
187
- mode: "vector",
188
- vector: { value: embedding, property: "embedding" },
189
- similarity: threshold ?? 0.5,
190
- limit
191
- });
192
- return results.hits.map((hit) => hit.document);
193
- };
194
- const list = async (limit = 50, offset = 0) => {
195
- const instance = await getDb();
196
- const { search } = await import("@orama/orama");
197
- const results = await search(instance, { term: "", limit: limit + offset });
198
- return results.hits.slice(offset).map((hit) => hit.document);
199
- };
200
- const clear = async () => {
201
- };
202
- const exportMemories = async () => list(1e4);
203
- const importMemories = async (memories) => {
204
- await Promise.all(memories.map((m) => store(m.text, m.type, m.importance)));
205
- };
206
- return { store, recall, list, clear, export: exportMemories, import: importMemories };
207
361
  };
208
- var createMemory = (config = {}) => createBrowserMemory(config);
362
+ var store = createBrowserStore();
363
+ var dispatch = store.dispatch;
209
364
  // Annotate the CommonJS export names for ESM import in node:
210
365
  0 && (module.exports = {
211
- createBrowserCortex,
212
- createBrowserMemory,
213
- createCortex,
214
- createMemory,
366
+ browserCortexSlice,
367
+ browserMemorySlice,
368
+ browserVectorSlice,
369
+ clearBrowserMemoryThunk,
370
+ completeBrowserCortexThunk,
371
+ createBrowserStore,
372
+ dispatch,
373
+ generateBrowserEmbeddingThunk,
215
374
  generateEmbedding,
216
- initVectorEngine
375
+ getBrowserEngine,
376
+ initBrowserCortexThunk,
377
+ initBrowserMemoryThunk,
378
+ initBrowserVectorThunk,
379
+ recallBrowserMemoryThunk,
380
+ selectAllBrowserCortexSessions,
381
+ selectAllBrowserDBs,
382
+ selectBrowserCortexById,
383
+ selectBrowserDBById,
384
+ setDownloadProgress,
385
+ setDownloadState,
386
+ store,
387
+ storeBrowserMemoryThunk
217
388
  });