forbocai 0.2.0 → 0.3.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/README.md +25 -0
- package/dist/index.d.mts +12 -8
- package/dist/index.d.ts +12 -8
- package/dist/index.js +199 -134
- package/dist/index.mjs +199 -134
- package/package.json +11 -6
package/README.md
CHANGED
|
@@ -78,6 +78,31 @@ npm install forbocai
|
|
|
78
78
|
|
|
79
79
|
---
|
|
80
80
|
|
|
81
|
+
## CLI Reference
|
|
82
|
+
|
|
83
|
+
`CLI_Tóols // Cómmand_Líne`
|
|
84
|
+
|
|
85
|
+
The SDK includes a comprehensive CLI for managing your AI infrastructure.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
# Agents
|
|
89
|
+
npx forbocai agent create "A wise wizard"
|
|
90
|
+
npx forbocai agent list
|
|
91
|
+
npx forbocai agent chat <agentId>
|
|
92
|
+
|
|
93
|
+
# Memory & Bridge
|
|
94
|
+
npx forbocai memory recall <agentId> "battle"
|
|
95
|
+
npx forbocai bridge validate action.json
|
|
96
|
+
|
|
97
|
+
# System
|
|
98
|
+
npx forbocai status
|
|
99
|
+
npx forbocai doctor
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
See [full CLI documentation](https://forbocai.docs.buildwithfern.com/docs/pages/cli).
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
81
106
|
## Quick Start
|
|
82
107
|
|
|
83
108
|
`Quíck_Stárt // Éxample`
|
package/dist/index.d.mts
CHANGED
|
@@ -17,7 +17,7 @@ interface CortexStatus {
|
|
|
17
17
|
id: string;
|
|
18
18
|
model: string;
|
|
19
19
|
ready: boolean;
|
|
20
|
-
engine: '
|
|
20
|
+
engine: 'webllm' | 'mock' | 'remote';
|
|
21
21
|
}
|
|
22
22
|
interface CompletionOptions {
|
|
23
23
|
temperature?: number;
|
|
@@ -107,7 +107,7 @@ interface ICortex {
|
|
|
107
107
|
*/
|
|
108
108
|
declare const createCortex: (config: CortexConfig) => ICortex;
|
|
109
109
|
/**
|
|
110
|
-
* Pure function to generate completion text
|
|
110
|
+
* Pure function to generate completion text (Fallback/Mock)
|
|
111
111
|
* Deterministic output for same input (functional principle)
|
|
112
112
|
*/
|
|
113
113
|
declare const generateCompletion: (prompt: string, options: CompletionOptions, modelId: string) => string;
|
|
@@ -173,14 +173,14 @@ interface IMemory {
|
|
|
173
173
|
recall(query: string, limit?: number): Promise<MemoryItem[]>;
|
|
174
174
|
list(limit?: number, offset?: number): Promise<MemoryItem[]>;
|
|
175
175
|
clear(): Promise<void>;
|
|
176
|
-
export(): MemoryItem[]
|
|
176
|
+
export(): Promise<MemoryItem[]>;
|
|
177
177
|
import(memories: MemoryItem[]): Promise<void>;
|
|
178
178
|
}
|
|
179
179
|
/**
|
|
180
180
|
* Configuration for the memory module
|
|
181
181
|
*/
|
|
182
182
|
interface MemoryModuleConfig extends MemoryConfig {
|
|
183
|
-
/** Storage key for IndexedDB persistence */
|
|
183
|
+
/** Storage key/Database name for IndexedDB persistence */
|
|
184
184
|
storageKey?: string;
|
|
185
185
|
/** API URL for optional cloud sync */
|
|
186
186
|
apiUrl?: string;
|
|
@@ -210,7 +210,7 @@ declare const rankMemoriesByRelevance: (memories: readonly MemoryItem[], query:
|
|
|
210
210
|
*/
|
|
211
211
|
declare const createMemory: (config?: MemoryModuleConfig) => IMemory;
|
|
212
212
|
/**
|
|
213
|
-
* Mock Memory for testing
|
|
213
|
+
* Mock Memory for testing (In-Memory Fallback)
|
|
214
214
|
*/
|
|
215
215
|
declare class MockMemory implements IMemory {
|
|
216
216
|
private memories;
|
|
@@ -218,7 +218,7 @@ declare class MockMemory implements IMemory {
|
|
|
218
218
|
recall(query: string, limit?: number): Promise<MemoryItem[]>;
|
|
219
219
|
list(limit?: number, offset?: number): Promise<MemoryItem[]>;
|
|
220
220
|
clear(): Promise<void>;
|
|
221
|
-
export(): MemoryItem[]
|
|
221
|
+
export(): Promise<MemoryItem[]>;
|
|
222
222
|
import(memories: MemoryItem[]): Promise<void>;
|
|
223
223
|
}
|
|
224
224
|
|
|
@@ -313,6 +313,8 @@ interface SoulExportConfig {
|
|
|
313
313
|
includeMemories?: boolean;
|
|
314
314
|
/** Sign with wallet (for NFT minting) */
|
|
315
315
|
sign?: boolean;
|
|
316
|
+
/** Use local IPFS node (Helia) */
|
|
317
|
+
useLocalNode?: boolean;
|
|
316
318
|
}
|
|
317
319
|
/**
|
|
318
320
|
* Soul export result
|
|
@@ -331,6 +333,8 @@ interface SoulImportConfig {
|
|
|
331
333
|
apiUrl?: string;
|
|
332
334
|
/** Verify signature before import */
|
|
333
335
|
verifySignature?: boolean;
|
|
336
|
+
/** Use local IPFS node (Helia) */
|
|
337
|
+
useLocalNode?: boolean;
|
|
334
338
|
}
|
|
335
339
|
/**
|
|
336
340
|
* Interface for Soul operations
|
|
@@ -359,11 +363,11 @@ declare const validateSoul: (soul: Soul) => {
|
|
|
359
363
|
errors: string[];
|
|
360
364
|
};
|
|
361
365
|
/**
|
|
362
|
-
* Export agent Soul to IPFS
|
|
366
|
+
* Export agent Soul to IPFS (Helia or API)
|
|
363
367
|
*/
|
|
364
368
|
declare const exportSoulToIPFS: (agentId: string, soul: Soul, config?: SoulExportConfig) => Promise<SoulExportResult>;
|
|
365
369
|
/**
|
|
366
|
-
* Import Soul from IPFS by CID
|
|
370
|
+
* Import Soul from IPFS by CID (Helia or API)
|
|
367
371
|
*/
|
|
368
372
|
declare const importSoulFromIPFS: (cid: string, config?: SoulImportConfig) => Promise<Soul>;
|
|
369
373
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ interface CortexStatus {
|
|
|
17
17
|
id: string;
|
|
18
18
|
model: string;
|
|
19
19
|
ready: boolean;
|
|
20
|
-
engine: '
|
|
20
|
+
engine: 'webllm' | 'mock' | 'remote';
|
|
21
21
|
}
|
|
22
22
|
interface CompletionOptions {
|
|
23
23
|
temperature?: number;
|
|
@@ -107,7 +107,7 @@ interface ICortex {
|
|
|
107
107
|
*/
|
|
108
108
|
declare const createCortex: (config: CortexConfig) => ICortex;
|
|
109
109
|
/**
|
|
110
|
-
* Pure function to generate completion text
|
|
110
|
+
* Pure function to generate completion text (Fallback/Mock)
|
|
111
111
|
* Deterministic output for same input (functional principle)
|
|
112
112
|
*/
|
|
113
113
|
declare const generateCompletion: (prompt: string, options: CompletionOptions, modelId: string) => string;
|
|
@@ -173,14 +173,14 @@ interface IMemory {
|
|
|
173
173
|
recall(query: string, limit?: number): Promise<MemoryItem[]>;
|
|
174
174
|
list(limit?: number, offset?: number): Promise<MemoryItem[]>;
|
|
175
175
|
clear(): Promise<void>;
|
|
176
|
-
export(): MemoryItem[]
|
|
176
|
+
export(): Promise<MemoryItem[]>;
|
|
177
177
|
import(memories: MemoryItem[]): Promise<void>;
|
|
178
178
|
}
|
|
179
179
|
/**
|
|
180
180
|
* Configuration for the memory module
|
|
181
181
|
*/
|
|
182
182
|
interface MemoryModuleConfig extends MemoryConfig {
|
|
183
|
-
/** Storage key for IndexedDB persistence */
|
|
183
|
+
/** Storage key/Database name for IndexedDB persistence */
|
|
184
184
|
storageKey?: string;
|
|
185
185
|
/** API URL for optional cloud sync */
|
|
186
186
|
apiUrl?: string;
|
|
@@ -210,7 +210,7 @@ declare const rankMemoriesByRelevance: (memories: readonly MemoryItem[], query:
|
|
|
210
210
|
*/
|
|
211
211
|
declare const createMemory: (config?: MemoryModuleConfig) => IMemory;
|
|
212
212
|
/**
|
|
213
|
-
* Mock Memory for testing
|
|
213
|
+
* Mock Memory for testing (In-Memory Fallback)
|
|
214
214
|
*/
|
|
215
215
|
declare class MockMemory implements IMemory {
|
|
216
216
|
private memories;
|
|
@@ -218,7 +218,7 @@ declare class MockMemory implements IMemory {
|
|
|
218
218
|
recall(query: string, limit?: number): Promise<MemoryItem[]>;
|
|
219
219
|
list(limit?: number, offset?: number): Promise<MemoryItem[]>;
|
|
220
220
|
clear(): Promise<void>;
|
|
221
|
-
export(): MemoryItem[]
|
|
221
|
+
export(): Promise<MemoryItem[]>;
|
|
222
222
|
import(memories: MemoryItem[]): Promise<void>;
|
|
223
223
|
}
|
|
224
224
|
|
|
@@ -313,6 +313,8 @@ interface SoulExportConfig {
|
|
|
313
313
|
includeMemories?: boolean;
|
|
314
314
|
/** Sign with wallet (for NFT minting) */
|
|
315
315
|
sign?: boolean;
|
|
316
|
+
/** Use local IPFS node (Helia) */
|
|
317
|
+
useLocalNode?: boolean;
|
|
316
318
|
}
|
|
317
319
|
/**
|
|
318
320
|
* Soul export result
|
|
@@ -331,6 +333,8 @@ interface SoulImportConfig {
|
|
|
331
333
|
apiUrl?: string;
|
|
332
334
|
/** Verify signature before import */
|
|
333
335
|
verifySignature?: boolean;
|
|
336
|
+
/** Use local IPFS node (Helia) */
|
|
337
|
+
useLocalNode?: boolean;
|
|
334
338
|
}
|
|
335
339
|
/**
|
|
336
340
|
* Interface for Soul operations
|
|
@@ -359,11 +363,11 @@ declare const validateSoul: (soul: Soul) => {
|
|
|
359
363
|
errors: string[];
|
|
360
364
|
};
|
|
361
365
|
/**
|
|
362
|
-
* Export agent Soul to IPFS
|
|
366
|
+
* Export agent Soul to IPFS (Helia or API)
|
|
363
367
|
*/
|
|
364
368
|
declare const exportSoulToIPFS: (agentId: string, soul: Soul, config?: SoulExportConfig) => Promise<SoulExportResult>;
|
|
365
369
|
/**
|
|
366
|
-
* Import Soul from IPFS by CID
|
|
370
|
+
* Import Soul from IPFS by CID (Helia or API)
|
|
367
371
|
*/
|
|
368
372
|
declare const importSoulFromIPFS: (cid: string, config?: SoulImportConfig) => Promise<Soul>;
|
|
369
373
|
/**
|
package/dist/index.js
CHANGED
|
@@ -65,14 +65,18 @@ __export(index_exports, {
|
|
|
65
65
|
module.exports = __toCommonJS(index_exports);
|
|
66
66
|
|
|
67
67
|
// src/cortex.ts
|
|
68
|
+
var import_web_llm = require("@mlc-ai/web-llm");
|
|
69
|
+
var MODEL_CONFIG = {
|
|
70
|
+
"smollm2-135m": "HuggingFaceTB/SmolLM2-135M-Instruct-q4f16_1-MLC",
|
|
71
|
+
"llama3-8b": "Llama-3-8B-Instruct-q4f16_1-MLC",
|
|
72
|
+
"default": "HuggingFaceTB/SmolLM2-135M-Instruct-q4f16_1-MLC"
|
|
73
|
+
};
|
|
68
74
|
var createCortex = (config) => {
|
|
69
75
|
return new WebLLMCortex(config);
|
|
70
76
|
};
|
|
71
77
|
var generateCompletion = (prompt, options, modelId) => {
|
|
72
|
-
const baseResponse = `Response to: ${prompt}`;
|
|
73
78
|
const temperature = options.temperature ?? 0.7;
|
|
74
|
-
|
|
75
|
-
return baseResponse;
|
|
79
|
+
return `[Mock Response] Model: ${modelId}, Temp: ${temperature}. Response to: ${prompt}`;
|
|
76
80
|
};
|
|
77
81
|
var processObservationToDirective = (observation) => {
|
|
78
82
|
return {
|
|
@@ -90,6 +94,7 @@ var generateActionFromDirective = (directive) => {
|
|
|
90
94
|
var WebLLMCortex = class {
|
|
91
95
|
constructor(config) {
|
|
92
96
|
this.status = null;
|
|
97
|
+
this.engine = null;
|
|
93
98
|
this.config = {
|
|
94
99
|
...config,
|
|
95
100
|
temperature: config.temperature ?? 0.7,
|
|
@@ -101,53 +106,70 @@ var WebLLMCortex = class {
|
|
|
101
106
|
if (this.status?.ready) {
|
|
102
107
|
return this.status;
|
|
103
108
|
}
|
|
104
|
-
const apiUrl = this.config.apiUrl || "http://localhost:8080";
|
|
105
109
|
try {
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
const isBrowser = typeof window !== "undefined";
|
|
111
|
+
if (isBrowser && this.config.gpu) {
|
|
112
|
+
console.log(`> Initializing WebLLM with model: ${this.config.model}`);
|
|
113
|
+
const selectedModel = MODEL_CONFIG[this.config.model] || MODEL_CONFIG["default"];
|
|
114
|
+
this.engine = await (0, import_web_llm.CreateMLCEngine)(selectedModel, {
|
|
115
|
+
initProgressCallback: (progress) => {
|
|
116
|
+
console.log(`> Model Loading: ${(progress.progress * 100).toFixed(1)}% - ${progress.text}`);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
this.status = {
|
|
120
|
+
id: "webllm-" + Math.random().toString(36).substring(7),
|
|
121
|
+
model: this.config.model,
|
|
122
|
+
ready: true,
|
|
123
|
+
engine: "webllm"
|
|
124
|
+
};
|
|
125
|
+
return this.status;
|
|
126
|
+
} else {
|
|
127
|
+
console.warn("WebGPU unavailable (Node.js or disabled). Falling back to mock/API.");
|
|
128
|
+
throw new Error("WebGPU unavailable");
|
|
118
129
|
}
|
|
119
|
-
const data = await response.json();
|
|
120
|
-
const newStatus = {
|
|
121
|
-
id: data.cortexId,
|
|
122
|
-
model: this.config.model,
|
|
123
|
-
ready: true,
|
|
124
|
-
engine: "webballm"
|
|
125
|
-
};
|
|
126
|
-
this.status = newStatus;
|
|
127
|
-
return newStatus;
|
|
128
130
|
} catch (e) {
|
|
129
|
-
console.warn("
|
|
130
|
-
|
|
131
|
+
console.warn("Using offline/mock mode due to initialization failure:", e);
|
|
132
|
+
this.status = {
|
|
131
133
|
id: "offline-" + Math.random().toString(36).substring(7),
|
|
132
134
|
model: this.config.model,
|
|
133
135
|
ready: true,
|
|
134
136
|
engine: "mock"
|
|
135
137
|
};
|
|
136
|
-
this.status
|
|
137
|
-
return newStatus;
|
|
138
|
+
return this.status;
|
|
138
139
|
}
|
|
139
140
|
}
|
|
140
141
|
async complete(prompt, options = {}) {
|
|
141
|
-
if (!this.status?.ready)
|
|
142
|
-
|
|
142
|
+
if (!this.status?.ready) await this.init();
|
|
143
|
+
if (this.engine) {
|
|
144
|
+
const response = await this.engine.chat.completions.create({
|
|
145
|
+
messages: [{ role: "user", content: prompt }],
|
|
146
|
+
temperature: options.temperature ?? this.config.temperature,
|
|
147
|
+
max_tokens: options.maxTokens ?? this.config.maxTokens
|
|
148
|
+
});
|
|
149
|
+
return response.choices[0]?.message?.content || "";
|
|
150
|
+
} else {
|
|
151
|
+
return generateCompletion(prompt, options, this.config.model);
|
|
143
152
|
}
|
|
144
|
-
const result = generateCompletion(prompt, options, this.config.model);
|
|
145
|
-
return result;
|
|
146
153
|
}
|
|
147
154
|
async *completeStream(prompt, options = {}) {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
155
|
+
if (!this.status?.ready) await this.init();
|
|
156
|
+
if (this.engine) {
|
|
157
|
+
const stream = await this.engine.chat.completions.create({
|
|
158
|
+
messages: [{ role: "user", content: prompt }],
|
|
159
|
+
temperature: options.temperature ?? this.config.temperature,
|
|
160
|
+
max_tokens: options.maxTokens ?? this.config.maxTokens,
|
|
161
|
+
stream: true
|
|
162
|
+
});
|
|
163
|
+
for await (const chunk of stream) {
|
|
164
|
+
const content = chunk.choices[0]?.delta?.content || "";
|
|
165
|
+
if (content) yield content;
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
const result = generateCompletion(prompt, options, this.config.model);
|
|
169
|
+
for (const char of result) {
|
|
170
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
171
|
+
yield char;
|
|
172
|
+
}
|
|
151
173
|
}
|
|
152
174
|
}
|
|
153
175
|
async processObservation(observation) {
|
|
@@ -167,21 +189,24 @@ var MockCortex = class {
|
|
|
167
189
|
};
|
|
168
190
|
}
|
|
169
191
|
async complete(prompt) {
|
|
170
|
-
return
|
|
192
|
+
return `Mock response to: ${prompt}`;
|
|
171
193
|
}
|
|
172
194
|
async *completeStream(prompt) {
|
|
173
|
-
yield `Mock
|
|
195
|
+
yield `Mock `;
|
|
196
|
+
yield `streaming `;
|
|
197
|
+
yield `response `;
|
|
198
|
+
yield `to: ${prompt}`;
|
|
174
199
|
}
|
|
175
200
|
async processObservation(observation) {
|
|
176
201
|
return {
|
|
177
202
|
type: "system-prompt",
|
|
178
|
-
content: "Mock processing
|
|
203
|
+
content: "Mock processing"
|
|
179
204
|
};
|
|
180
205
|
}
|
|
181
206
|
async generateAction(directive) {
|
|
182
207
|
return {
|
|
183
208
|
type: "mock-action",
|
|
184
|
-
reason: "Mock action
|
|
209
|
+
reason: "Mock action"
|
|
185
210
|
};
|
|
186
211
|
}
|
|
187
212
|
};
|
|
@@ -300,6 +325,7 @@ var fromSoul = (soul, cortex) => {
|
|
|
300
325
|
};
|
|
301
326
|
|
|
302
327
|
// src/memory.ts
|
|
328
|
+
var import_idb = require("idb");
|
|
303
329
|
var createMemoryItem = (text, type = "observation", importance = 0.5) => {
|
|
304
330
|
return {
|
|
305
331
|
id: `mem_${Date.now()}_${Math.random().toString(36).substring(7)}`,
|
|
@@ -337,129 +363,128 @@ var rankMemoriesByRelevance = (memories, query, limit = 5) => {
|
|
|
337
363
|
return scored.sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.memory);
|
|
338
364
|
};
|
|
339
365
|
var createMemory = (config = {}) => {
|
|
340
|
-
return new
|
|
366
|
+
return new IndexedDBMemory(config);
|
|
341
367
|
};
|
|
342
|
-
var
|
|
368
|
+
var IndexedDBMemory = class {
|
|
343
369
|
constructor(config) {
|
|
370
|
+
this.DB_NAME = "forbocai_db";
|
|
371
|
+
this.STORE_NAME = "memories";
|
|
344
372
|
this.config = {
|
|
345
373
|
decay: config.decay ?? "none",
|
|
346
374
|
maxContextWindow: config.maxContextWindow ?? 10,
|
|
347
375
|
storageKey: config.storageKey ?? "forbocai_memory",
|
|
348
376
|
...config
|
|
349
377
|
};
|
|
350
|
-
|
|
351
|
-
|
|
378
|
+
if (typeof window !== "undefined" && window.indexedDB) {
|
|
379
|
+
this.dbPromise = (0, import_idb.openDB)(this.config.storageKey || this.DB_NAME, 1, {
|
|
380
|
+
upgrade(db) {
|
|
381
|
+
if (!db.objectStoreNames.contains("memories")) {
|
|
382
|
+
const store = db.createObjectStore("memories", { keyPath: "id" });
|
|
383
|
+
store.createIndex("timestamp", "timestamp");
|
|
384
|
+
store.createIndex("agentId", "agentId");
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
});
|
|
388
|
+
} else {
|
|
389
|
+
console.warn("IndexedDB not available (Node.js environment). Falling back to in-memory.");
|
|
390
|
+
this.dbPromise = Promise.resolve({
|
|
391
|
+
getAll: async () => [],
|
|
392
|
+
put: async () => {
|
|
393
|
+
},
|
|
394
|
+
clear: async () => {
|
|
395
|
+
},
|
|
396
|
+
getAllFromIndex: async () => [],
|
|
397
|
+
transaction: () => ({ objectStore: () => ({ put: async () => {
|
|
398
|
+
} }), done: Promise.resolve() })
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
async getAllMemories() {
|
|
403
|
+
const db = await this.dbPromise;
|
|
404
|
+
try {
|
|
405
|
+
return await db.getAll(this.STORE_NAME) || [];
|
|
406
|
+
} catch {
|
|
407
|
+
return [];
|
|
408
|
+
}
|
|
352
409
|
}
|
|
353
|
-
/**
|
|
354
|
-
* Store a new memory observation
|
|
355
|
-
*/
|
|
356
410
|
async store(text, type = "observation", importance = 0.5) {
|
|
357
411
|
const memory = createMemoryItem(text, type, importance);
|
|
358
|
-
|
|
359
|
-
|
|
412
|
+
const db = await this.dbPromise;
|
|
413
|
+
try {
|
|
414
|
+
await db.put(this.STORE_NAME, memory);
|
|
415
|
+
} catch (e) {
|
|
416
|
+
}
|
|
360
417
|
if (this.config.apiUrl && this.config.agentId) {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
method: "POST",
|
|
364
|
-
headers: { "Content-Type": "application/json" },
|
|
365
|
-
body: JSON.stringify({
|
|
366
|
-
observation: text,
|
|
367
|
-
importance
|
|
368
|
-
})
|
|
369
|
-
});
|
|
370
|
-
} catch (e) {
|
|
371
|
-
console.warn("Failed to sync memory to API:", e);
|
|
372
|
-
}
|
|
418
|
+
this.syncToApi(text, importance).catch(() => {
|
|
419
|
+
});
|
|
373
420
|
}
|
|
374
421
|
return memory;
|
|
375
422
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
423
|
+
async syncToApi(text, importance) {
|
|
424
|
+
try {
|
|
425
|
+
await fetch(`${this.config.apiUrl}/agents/${this.config.agentId}/memory`, {
|
|
426
|
+
method: "POST",
|
|
427
|
+
headers: { "Content-Type": "application/json" },
|
|
428
|
+
body: JSON.stringify({ observation: text, importance })
|
|
429
|
+
});
|
|
430
|
+
} catch (e) {
|
|
431
|
+
}
|
|
432
|
+
}
|
|
379
433
|
async recall(query, limit = 5) {
|
|
380
|
-
let
|
|
434
|
+
let memories = await this.getAllMemories();
|
|
381
435
|
if (this.config.decay === "temporal") {
|
|
382
436
|
const now = Date.now();
|
|
383
|
-
|
|
384
|
-
(mem) => applyTemporalDecay(mem, now)
|
|
385
|
-
);
|
|
437
|
+
memories = memories.map((mem) => applyTemporalDecay(mem, now));
|
|
386
438
|
}
|
|
387
|
-
const results = rankMemoriesByRelevance(
|
|
388
|
-
processedMemories,
|
|
389
|
-
query,
|
|
390
|
-
limit
|
|
391
|
-
);
|
|
439
|
+
const results = rankMemoriesByRelevance(memories, query, limit);
|
|
392
440
|
if (this.config.apiUrl && this.config.agentId) {
|
|
393
441
|
try {
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
{
|
|
397
|
-
method: "POST",
|
|
398
|
-
headers: { "Content-Type": "application/json" },
|
|
399
|
-
body: JSON.stringify({ query, similarity: 0.5 })
|
|
400
|
-
}
|
|
401
|
-
);
|
|
402
|
-
if (response.ok) {
|
|
403
|
-
const apiMemories = await response.json();
|
|
404
|
-
return [...results, ...apiMemories.slice(0, limit - results.length)];
|
|
405
|
-
}
|
|
442
|
+
const apiResults = await this.recallFromApi(query);
|
|
443
|
+
return [...results, ...apiResults.slice(0, limit - results.length)];
|
|
406
444
|
} catch (e) {
|
|
407
|
-
console.warn("Failed to recall from API:", e);
|
|
408
445
|
}
|
|
409
446
|
}
|
|
410
447
|
return results;
|
|
411
448
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
449
|
+
async recallFromApi(query) {
|
|
450
|
+
const response = await fetch(
|
|
451
|
+
`${this.config.apiUrl}/agents/${this.config.agentId}/memory/recall`,
|
|
452
|
+
{
|
|
453
|
+
method: "POST",
|
|
454
|
+
headers: { "Content-Type": "application/json" },
|
|
455
|
+
body: JSON.stringify({ query, similarity: 0.5 })
|
|
456
|
+
}
|
|
457
|
+
);
|
|
458
|
+
if (response.ok) return await response.json();
|
|
459
|
+
return [];
|
|
460
|
+
}
|
|
415
461
|
async list(limit = 50, offset = 0) {
|
|
416
|
-
|
|
462
|
+
const db = await this.dbPromise;
|
|
463
|
+
try {
|
|
464
|
+
const all = await db.getAllFromIndex(this.STORE_NAME, "timestamp");
|
|
465
|
+
return all.reverse().slice(offset, offset + limit);
|
|
466
|
+
} catch {
|
|
467
|
+
return [];
|
|
468
|
+
}
|
|
417
469
|
}
|
|
418
|
-
/**
|
|
419
|
-
* Clear all memories
|
|
420
|
-
*/
|
|
421
470
|
async clear() {
|
|
422
|
-
|
|
423
|
-
this.saveToStorage();
|
|
424
|
-
}
|
|
425
|
-
/**
|
|
426
|
-
* Export all memories for backup/portability
|
|
427
|
-
*/
|
|
428
|
-
export() {
|
|
429
|
-
return this.memories.map((m) => ({ ...m }));
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Import memories (e.g., from Soul)
|
|
433
|
-
*/
|
|
434
|
-
async import(memories) {
|
|
435
|
-
const existingIds = new Set(this.memories.map((m) => m.id));
|
|
436
|
-
const newMemories = memories.filter((m) => !existingIds.has(m.id));
|
|
437
|
-
this.memories = [...this.memories, ...newMemories];
|
|
438
|
-
this.saveToStorage();
|
|
439
|
-
}
|
|
440
|
-
/**
|
|
441
|
-
* Load memories from localStorage (browser) or skip (Node)
|
|
442
|
-
*/
|
|
443
|
-
loadFromStorage() {
|
|
444
|
-
if (typeof window === "undefined" || !window.localStorage) return;
|
|
471
|
+
const db = await this.dbPromise;
|
|
445
472
|
try {
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
this.memories = JSON.parse(stored);
|
|
449
|
-
}
|
|
450
|
-
} catch (e) {
|
|
451
|
-
console.warn("Failed to load memories from storage:", e);
|
|
473
|
+
await db.clear(this.STORE_NAME);
|
|
474
|
+
} catch {
|
|
452
475
|
}
|
|
453
476
|
}
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
477
|
+
async export() {
|
|
478
|
+
return this.getAllMemories();
|
|
479
|
+
}
|
|
480
|
+
async import(memories) {
|
|
481
|
+
const db = await this.dbPromise;
|
|
459
482
|
try {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
483
|
+
const tx = db.transaction(this.STORE_NAME, "readwrite");
|
|
484
|
+
const store = tx.objectStore(this.STORE_NAME);
|
|
485
|
+
for (const mem of memories) await store.put(mem);
|
|
486
|
+
await tx.done;
|
|
487
|
+
} catch {
|
|
463
488
|
}
|
|
464
489
|
}
|
|
465
490
|
};
|
|
@@ -481,7 +506,7 @@ var MockMemory = class {
|
|
|
481
506
|
async clear() {
|
|
482
507
|
this.memories = [];
|
|
483
508
|
}
|
|
484
|
-
export() {
|
|
509
|
+
async export() {
|
|
485
510
|
return [...this.memories];
|
|
486
511
|
}
|
|
487
512
|
async import(memories) {
|
|
@@ -780,6 +805,24 @@ var validateAction = (action, rules, context = {}) => {
|
|
|
780
805
|
};
|
|
781
806
|
|
|
782
807
|
// src/soul.ts
|
|
808
|
+
var import_helia = require("helia");
|
|
809
|
+
var import_json = require("@helia/json");
|
|
810
|
+
var import_blockstore_core = require("blockstore-core");
|
|
811
|
+
var heliaNode = null;
|
|
812
|
+
var heliaJson = null;
|
|
813
|
+
var getHelia = async () => {
|
|
814
|
+
if (heliaNode) return { node: heliaNode, j: heliaJson };
|
|
815
|
+
try {
|
|
816
|
+
const blockstore = new import_blockstore_core.MemoryBlockstore();
|
|
817
|
+
heliaNode = await (0, import_helia.createHelia)({ blockstore });
|
|
818
|
+
heliaJson = (0, import_json.json)(heliaNode);
|
|
819
|
+
console.log("> Helia IPFS Node Initialized:", heliaNode.libp2p.peerId.toString());
|
|
820
|
+
return { node: heliaNode, j: heliaJson };
|
|
821
|
+
} catch (e) {
|
|
822
|
+
console.warn("Failed to init Helia (IPFS):", e);
|
|
823
|
+
return null;
|
|
824
|
+
}
|
|
825
|
+
};
|
|
783
826
|
var createSoul = (id, name, persona, state, memories = []) => {
|
|
784
827
|
return {
|
|
785
828
|
id,
|
|
@@ -793,8 +836,8 @@ var createSoul = (id, name, persona, state, memories = []) => {
|
|
|
793
836
|
var serializeSoul = (soul) => {
|
|
794
837
|
return JSON.stringify(soul, null, 2);
|
|
795
838
|
};
|
|
796
|
-
var deserializeSoul = (
|
|
797
|
-
const parsed = JSON.parse(
|
|
839
|
+
var deserializeSoul = (json2) => {
|
|
840
|
+
const parsed = JSON.parse(json2);
|
|
798
841
|
if (!parsed.id || !parsed.persona || !parsed.state) {
|
|
799
842
|
throw new Error("Invalid Soul format: missing required fields");
|
|
800
843
|
}
|
|
@@ -820,6 +863,21 @@ var validateSoul = (soul) => {
|
|
|
820
863
|
};
|
|
821
864
|
};
|
|
822
865
|
var exportSoulToIPFS = async (agentId, soul, config = {}) => {
|
|
866
|
+
if (config.useLocalNode !== false) {
|
|
867
|
+
const helia = await getHelia();
|
|
868
|
+
if (helia) {
|
|
869
|
+
try {
|
|
870
|
+
const cid = await helia.j.add(soul);
|
|
871
|
+
return {
|
|
872
|
+
cid: cid.toString(),
|
|
873
|
+
ipfsUrl: `ipfs://${cid.toString()}`,
|
|
874
|
+
soul
|
|
875
|
+
};
|
|
876
|
+
} catch (e) {
|
|
877
|
+
console.warn("Helia add failed, falling back to API", e);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
823
881
|
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
824
882
|
try {
|
|
825
883
|
const response = await fetch(`${apiUrl}/agents/${agentId}/soul/export`, {
|
|
@@ -847,6 +905,14 @@ var exportSoulToIPFS = async (agentId, soul, config = {}) => {
|
|
|
847
905
|
}
|
|
848
906
|
};
|
|
849
907
|
var importSoulFromIPFS = async (cid, config = {}) => {
|
|
908
|
+
if (config.useLocalNode !== false) {
|
|
909
|
+
const helia = await getHelia();
|
|
910
|
+
if (helia) {
|
|
911
|
+
try {
|
|
912
|
+
} catch (e) {
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
850
916
|
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
851
917
|
try {
|
|
852
918
|
const response = await fetch(`${apiUrl}/souls/${cid}`, {
|
|
@@ -862,7 +928,6 @@ var importSoulFromIPFS = async (cid, config = {}) => {
|
|
|
862
928
|
version: "1.0.0",
|
|
863
929
|
name: data.soulName,
|
|
864
930
|
persona: data.dna,
|
|
865
|
-
// DNA contains the persona
|
|
866
931
|
state: { mood: "neutral", inventory: [], skills: {}, relationships: {} },
|
|
867
932
|
memories: []
|
|
868
933
|
};
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
// src/cortex.ts
|
|
2
|
+
import { CreateMLCEngine } from "@mlc-ai/web-llm";
|
|
3
|
+
var MODEL_CONFIG = {
|
|
4
|
+
"smollm2-135m": "HuggingFaceTB/SmolLM2-135M-Instruct-q4f16_1-MLC",
|
|
5
|
+
"llama3-8b": "Llama-3-8B-Instruct-q4f16_1-MLC",
|
|
6
|
+
"default": "HuggingFaceTB/SmolLM2-135M-Instruct-q4f16_1-MLC"
|
|
7
|
+
};
|
|
2
8
|
var createCortex = (config) => {
|
|
3
9
|
return new WebLLMCortex(config);
|
|
4
10
|
};
|
|
5
11
|
var generateCompletion = (prompt, options, modelId) => {
|
|
6
|
-
const baseResponse = `Response to: ${prompt}`;
|
|
7
12
|
const temperature = options.temperature ?? 0.7;
|
|
8
|
-
|
|
9
|
-
return baseResponse;
|
|
13
|
+
return `[Mock Response] Model: ${modelId}, Temp: ${temperature}. Response to: ${prompt}`;
|
|
10
14
|
};
|
|
11
15
|
var processObservationToDirective = (observation) => {
|
|
12
16
|
return {
|
|
@@ -24,6 +28,7 @@ var generateActionFromDirective = (directive) => {
|
|
|
24
28
|
var WebLLMCortex = class {
|
|
25
29
|
constructor(config) {
|
|
26
30
|
this.status = null;
|
|
31
|
+
this.engine = null;
|
|
27
32
|
this.config = {
|
|
28
33
|
...config,
|
|
29
34
|
temperature: config.temperature ?? 0.7,
|
|
@@ -35,53 +40,70 @@ var WebLLMCortex = class {
|
|
|
35
40
|
if (this.status?.ready) {
|
|
36
41
|
return this.status;
|
|
37
42
|
}
|
|
38
|
-
const apiUrl = this.config.apiUrl || "http://localhost:8080";
|
|
39
43
|
try {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
44
|
+
const isBrowser = typeof window !== "undefined";
|
|
45
|
+
if (isBrowser && this.config.gpu) {
|
|
46
|
+
console.log(`> Initializing WebLLM with model: ${this.config.model}`);
|
|
47
|
+
const selectedModel = MODEL_CONFIG[this.config.model] || MODEL_CONFIG["default"];
|
|
48
|
+
this.engine = await CreateMLCEngine(selectedModel, {
|
|
49
|
+
initProgressCallback: (progress) => {
|
|
50
|
+
console.log(`> Model Loading: ${(progress.progress * 100).toFixed(1)}% - ${progress.text}`);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
this.status = {
|
|
54
|
+
id: "webllm-" + Math.random().toString(36).substring(7),
|
|
55
|
+
model: this.config.model,
|
|
56
|
+
ready: true,
|
|
57
|
+
engine: "webllm"
|
|
58
|
+
};
|
|
59
|
+
return this.status;
|
|
60
|
+
} else {
|
|
61
|
+
console.warn("WebGPU unavailable (Node.js or disabled). Falling back to mock/API.");
|
|
62
|
+
throw new Error("WebGPU unavailable");
|
|
52
63
|
}
|
|
53
|
-
const data = await response.json();
|
|
54
|
-
const newStatus = {
|
|
55
|
-
id: data.cortexId,
|
|
56
|
-
model: this.config.model,
|
|
57
|
-
ready: true,
|
|
58
|
-
engine: "webballm"
|
|
59
|
-
};
|
|
60
|
-
this.status = newStatus;
|
|
61
|
-
return newStatus;
|
|
62
64
|
} catch (e) {
|
|
63
|
-
console.warn("
|
|
64
|
-
|
|
65
|
+
console.warn("Using offline/mock mode due to initialization failure:", e);
|
|
66
|
+
this.status = {
|
|
65
67
|
id: "offline-" + Math.random().toString(36).substring(7),
|
|
66
68
|
model: this.config.model,
|
|
67
69
|
ready: true,
|
|
68
70
|
engine: "mock"
|
|
69
71
|
};
|
|
70
|
-
this.status
|
|
71
|
-
return newStatus;
|
|
72
|
+
return this.status;
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
75
|
async complete(prompt, options = {}) {
|
|
75
|
-
if (!this.status?.ready)
|
|
76
|
-
|
|
76
|
+
if (!this.status?.ready) await this.init();
|
|
77
|
+
if (this.engine) {
|
|
78
|
+
const response = await this.engine.chat.completions.create({
|
|
79
|
+
messages: [{ role: "user", content: prompt }],
|
|
80
|
+
temperature: options.temperature ?? this.config.temperature,
|
|
81
|
+
max_tokens: options.maxTokens ?? this.config.maxTokens
|
|
82
|
+
});
|
|
83
|
+
return response.choices[0]?.message?.content || "";
|
|
84
|
+
} else {
|
|
85
|
+
return generateCompletion(prompt, options, this.config.model);
|
|
77
86
|
}
|
|
78
|
-
const result = generateCompletion(prompt, options, this.config.model);
|
|
79
|
-
return result;
|
|
80
87
|
}
|
|
81
88
|
async *completeStream(prompt, options = {}) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
89
|
+
if (!this.status?.ready) await this.init();
|
|
90
|
+
if (this.engine) {
|
|
91
|
+
const stream = await this.engine.chat.completions.create({
|
|
92
|
+
messages: [{ role: "user", content: prompt }],
|
|
93
|
+
temperature: options.temperature ?? this.config.temperature,
|
|
94
|
+
max_tokens: options.maxTokens ?? this.config.maxTokens,
|
|
95
|
+
stream: true
|
|
96
|
+
});
|
|
97
|
+
for await (const chunk of stream) {
|
|
98
|
+
const content = chunk.choices[0]?.delta?.content || "";
|
|
99
|
+
if (content) yield content;
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
const result = generateCompletion(prompt, options, this.config.model);
|
|
103
|
+
for (const char of result) {
|
|
104
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
105
|
+
yield char;
|
|
106
|
+
}
|
|
85
107
|
}
|
|
86
108
|
}
|
|
87
109
|
async processObservation(observation) {
|
|
@@ -101,21 +123,24 @@ var MockCortex = class {
|
|
|
101
123
|
};
|
|
102
124
|
}
|
|
103
125
|
async complete(prompt) {
|
|
104
|
-
return
|
|
126
|
+
return `Mock response to: ${prompt}`;
|
|
105
127
|
}
|
|
106
128
|
async *completeStream(prompt) {
|
|
107
|
-
yield `Mock
|
|
129
|
+
yield `Mock `;
|
|
130
|
+
yield `streaming `;
|
|
131
|
+
yield `response `;
|
|
132
|
+
yield `to: ${prompt}`;
|
|
108
133
|
}
|
|
109
134
|
async processObservation(observation) {
|
|
110
135
|
return {
|
|
111
136
|
type: "system-prompt",
|
|
112
|
-
content: "Mock processing
|
|
137
|
+
content: "Mock processing"
|
|
113
138
|
};
|
|
114
139
|
}
|
|
115
140
|
async generateAction(directive) {
|
|
116
141
|
return {
|
|
117
142
|
type: "mock-action",
|
|
118
|
-
reason: "Mock action
|
|
143
|
+
reason: "Mock action"
|
|
119
144
|
};
|
|
120
145
|
}
|
|
121
146
|
};
|
|
@@ -234,6 +259,7 @@ var fromSoul = (soul, cortex) => {
|
|
|
234
259
|
};
|
|
235
260
|
|
|
236
261
|
// src/memory.ts
|
|
262
|
+
import { openDB } from "idb";
|
|
237
263
|
var createMemoryItem = (text, type = "observation", importance = 0.5) => {
|
|
238
264
|
return {
|
|
239
265
|
id: `mem_${Date.now()}_${Math.random().toString(36).substring(7)}`,
|
|
@@ -271,129 +297,128 @@ var rankMemoriesByRelevance = (memories, query, limit = 5) => {
|
|
|
271
297
|
return scored.sort((a, b) => b.score - a.score).slice(0, limit).map((s) => s.memory);
|
|
272
298
|
};
|
|
273
299
|
var createMemory = (config = {}) => {
|
|
274
|
-
return new
|
|
300
|
+
return new IndexedDBMemory(config);
|
|
275
301
|
};
|
|
276
|
-
var
|
|
302
|
+
var IndexedDBMemory = class {
|
|
277
303
|
constructor(config) {
|
|
304
|
+
this.DB_NAME = "forbocai_db";
|
|
305
|
+
this.STORE_NAME = "memories";
|
|
278
306
|
this.config = {
|
|
279
307
|
decay: config.decay ?? "none",
|
|
280
308
|
maxContextWindow: config.maxContextWindow ?? 10,
|
|
281
309
|
storageKey: config.storageKey ?? "forbocai_memory",
|
|
282
310
|
...config
|
|
283
311
|
};
|
|
284
|
-
|
|
285
|
-
|
|
312
|
+
if (typeof window !== "undefined" && window.indexedDB) {
|
|
313
|
+
this.dbPromise = openDB(this.config.storageKey || this.DB_NAME, 1, {
|
|
314
|
+
upgrade(db) {
|
|
315
|
+
if (!db.objectStoreNames.contains("memories")) {
|
|
316
|
+
const store = db.createObjectStore("memories", { keyPath: "id" });
|
|
317
|
+
store.createIndex("timestamp", "timestamp");
|
|
318
|
+
store.createIndex("agentId", "agentId");
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
} else {
|
|
323
|
+
console.warn("IndexedDB not available (Node.js environment). Falling back to in-memory.");
|
|
324
|
+
this.dbPromise = Promise.resolve({
|
|
325
|
+
getAll: async () => [],
|
|
326
|
+
put: async () => {
|
|
327
|
+
},
|
|
328
|
+
clear: async () => {
|
|
329
|
+
},
|
|
330
|
+
getAllFromIndex: async () => [],
|
|
331
|
+
transaction: () => ({ objectStore: () => ({ put: async () => {
|
|
332
|
+
} }), done: Promise.resolve() })
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
async getAllMemories() {
|
|
337
|
+
const db = await this.dbPromise;
|
|
338
|
+
try {
|
|
339
|
+
return await db.getAll(this.STORE_NAME) || [];
|
|
340
|
+
} catch {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
286
343
|
}
|
|
287
|
-
/**
|
|
288
|
-
* Store a new memory observation
|
|
289
|
-
*/
|
|
290
344
|
async store(text, type = "observation", importance = 0.5) {
|
|
291
345
|
const memory = createMemoryItem(text, type, importance);
|
|
292
|
-
|
|
293
|
-
|
|
346
|
+
const db = await this.dbPromise;
|
|
347
|
+
try {
|
|
348
|
+
await db.put(this.STORE_NAME, memory);
|
|
349
|
+
} catch (e) {
|
|
350
|
+
}
|
|
294
351
|
if (this.config.apiUrl && this.config.agentId) {
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
method: "POST",
|
|
298
|
-
headers: { "Content-Type": "application/json" },
|
|
299
|
-
body: JSON.stringify({
|
|
300
|
-
observation: text,
|
|
301
|
-
importance
|
|
302
|
-
})
|
|
303
|
-
});
|
|
304
|
-
} catch (e) {
|
|
305
|
-
console.warn("Failed to sync memory to API:", e);
|
|
306
|
-
}
|
|
352
|
+
this.syncToApi(text, importance).catch(() => {
|
|
353
|
+
});
|
|
307
354
|
}
|
|
308
355
|
return memory;
|
|
309
356
|
}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
357
|
+
async syncToApi(text, importance) {
|
|
358
|
+
try {
|
|
359
|
+
await fetch(`${this.config.apiUrl}/agents/${this.config.agentId}/memory`, {
|
|
360
|
+
method: "POST",
|
|
361
|
+
headers: { "Content-Type": "application/json" },
|
|
362
|
+
body: JSON.stringify({ observation: text, importance })
|
|
363
|
+
});
|
|
364
|
+
} catch (e) {
|
|
365
|
+
}
|
|
366
|
+
}
|
|
313
367
|
async recall(query, limit = 5) {
|
|
314
|
-
let
|
|
368
|
+
let memories = await this.getAllMemories();
|
|
315
369
|
if (this.config.decay === "temporal") {
|
|
316
370
|
const now = Date.now();
|
|
317
|
-
|
|
318
|
-
(mem) => applyTemporalDecay(mem, now)
|
|
319
|
-
);
|
|
371
|
+
memories = memories.map((mem) => applyTemporalDecay(mem, now));
|
|
320
372
|
}
|
|
321
|
-
const results = rankMemoriesByRelevance(
|
|
322
|
-
processedMemories,
|
|
323
|
-
query,
|
|
324
|
-
limit
|
|
325
|
-
);
|
|
373
|
+
const results = rankMemoriesByRelevance(memories, query, limit);
|
|
326
374
|
if (this.config.apiUrl && this.config.agentId) {
|
|
327
375
|
try {
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
{
|
|
331
|
-
method: "POST",
|
|
332
|
-
headers: { "Content-Type": "application/json" },
|
|
333
|
-
body: JSON.stringify({ query, similarity: 0.5 })
|
|
334
|
-
}
|
|
335
|
-
);
|
|
336
|
-
if (response.ok) {
|
|
337
|
-
const apiMemories = await response.json();
|
|
338
|
-
return [...results, ...apiMemories.slice(0, limit - results.length)];
|
|
339
|
-
}
|
|
376
|
+
const apiResults = await this.recallFromApi(query);
|
|
377
|
+
return [...results, ...apiResults.slice(0, limit - results.length)];
|
|
340
378
|
} catch (e) {
|
|
341
|
-
console.warn("Failed to recall from API:", e);
|
|
342
379
|
}
|
|
343
380
|
}
|
|
344
381
|
return results;
|
|
345
382
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
383
|
+
async recallFromApi(query) {
|
|
384
|
+
const response = await fetch(
|
|
385
|
+
`${this.config.apiUrl}/agents/${this.config.agentId}/memory/recall`,
|
|
386
|
+
{
|
|
387
|
+
method: "POST",
|
|
388
|
+
headers: { "Content-Type": "application/json" },
|
|
389
|
+
body: JSON.stringify({ query, similarity: 0.5 })
|
|
390
|
+
}
|
|
391
|
+
);
|
|
392
|
+
if (response.ok) return await response.json();
|
|
393
|
+
return [];
|
|
394
|
+
}
|
|
349
395
|
async list(limit = 50, offset = 0) {
|
|
350
|
-
|
|
396
|
+
const db = await this.dbPromise;
|
|
397
|
+
try {
|
|
398
|
+
const all = await db.getAllFromIndex(this.STORE_NAME, "timestamp");
|
|
399
|
+
return all.reverse().slice(offset, offset + limit);
|
|
400
|
+
} catch {
|
|
401
|
+
return [];
|
|
402
|
+
}
|
|
351
403
|
}
|
|
352
|
-
/**
|
|
353
|
-
* Clear all memories
|
|
354
|
-
*/
|
|
355
404
|
async clear() {
|
|
356
|
-
|
|
357
|
-
this.saveToStorage();
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Export all memories for backup/portability
|
|
361
|
-
*/
|
|
362
|
-
export() {
|
|
363
|
-
return this.memories.map((m) => ({ ...m }));
|
|
364
|
-
}
|
|
365
|
-
/**
|
|
366
|
-
* Import memories (e.g., from Soul)
|
|
367
|
-
*/
|
|
368
|
-
async import(memories) {
|
|
369
|
-
const existingIds = new Set(this.memories.map((m) => m.id));
|
|
370
|
-
const newMemories = memories.filter((m) => !existingIds.has(m.id));
|
|
371
|
-
this.memories = [...this.memories, ...newMemories];
|
|
372
|
-
this.saveToStorage();
|
|
373
|
-
}
|
|
374
|
-
/**
|
|
375
|
-
* Load memories from localStorage (browser) or skip (Node)
|
|
376
|
-
*/
|
|
377
|
-
loadFromStorage() {
|
|
378
|
-
if (typeof window === "undefined" || !window.localStorage) return;
|
|
405
|
+
const db = await this.dbPromise;
|
|
379
406
|
try {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
this.memories = JSON.parse(stored);
|
|
383
|
-
}
|
|
384
|
-
} catch (e) {
|
|
385
|
-
console.warn("Failed to load memories from storage:", e);
|
|
407
|
+
await db.clear(this.STORE_NAME);
|
|
408
|
+
} catch {
|
|
386
409
|
}
|
|
387
410
|
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
411
|
+
async export() {
|
|
412
|
+
return this.getAllMemories();
|
|
413
|
+
}
|
|
414
|
+
async import(memories) {
|
|
415
|
+
const db = await this.dbPromise;
|
|
393
416
|
try {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
417
|
+
const tx = db.transaction(this.STORE_NAME, "readwrite");
|
|
418
|
+
const store = tx.objectStore(this.STORE_NAME);
|
|
419
|
+
for (const mem of memories) await store.put(mem);
|
|
420
|
+
await tx.done;
|
|
421
|
+
} catch {
|
|
397
422
|
}
|
|
398
423
|
}
|
|
399
424
|
};
|
|
@@ -415,7 +440,7 @@ var MockMemory = class {
|
|
|
415
440
|
async clear() {
|
|
416
441
|
this.memories = [];
|
|
417
442
|
}
|
|
418
|
-
export() {
|
|
443
|
+
async export() {
|
|
419
444
|
return [...this.memories];
|
|
420
445
|
}
|
|
421
446
|
async import(memories) {
|
|
@@ -714,6 +739,24 @@ var validateAction = (action, rules, context = {}) => {
|
|
|
714
739
|
};
|
|
715
740
|
|
|
716
741
|
// src/soul.ts
|
|
742
|
+
import { createHelia } from "helia";
|
|
743
|
+
import { json } from "@helia/json";
|
|
744
|
+
import { MemoryBlockstore } from "blockstore-core";
|
|
745
|
+
var heliaNode = null;
|
|
746
|
+
var heliaJson = null;
|
|
747
|
+
var getHelia = async () => {
|
|
748
|
+
if (heliaNode) return { node: heliaNode, j: heliaJson };
|
|
749
|
+
try {
|
|
750
|
+
const blockstore = new MemoryBlockstore();
|
|
751
|
+
heliaNode = await createHelia({ blockstore });
|
|
752
|
+
heliaJson = json(heliaNode);
|
|
753
|
+
console.log("> Helia IPFS Node Initialized:", heliaNode.libp2p.peerId.toString());
|
|
754
|
+
return { node: heliaNode, j: heliaJson };
|
|
755
|
+
} catch (e) {
|
|
756
|
+
console.warn("Failed to init Helia (IPFS):", e);
|
|
757
|
+
return null;
|
|
758
|
+
}
|
|
759
|
+
};
|
|
717
760
|
var createSoul = (id, name, persona, state, memories = []) => {
|
|
718
761
|
return {
|
|
719
762
|
id,
|
|
@@ -727,8 +770,8 @@ var createSoul = (id, name, persona, state, memories = []) => {
|
|
|
727
770
|
var serializeSoul = (soul) => {
|
|
728
771
|
return JSON.stringify(soul, null, 2);
|
|
729
772
|
};
|
|
730
|
-
var deserializeSoul = (
|
|
731
|
-
const parsed = JSON.parse(
|
|
773
|
+
var deserializeSoul = (json2) => {
|
|
774
|
+
const parsed = JSON.parse(json2);
|
|
732
775
|
if (!parsed.id || !parsed.persona || !parsed.state) {
|
|
733
776
|
throw new Error("Invalid Soul format: missing required fields");
|
|
734
777
|
}
|
|
@@ -754,6 +797,21 @@ var validateSoul = (soul) => {
|
|
|
754
797
|
};
|
|
755
798
|
};
|
|
756
799
|
var exportSoulToIPFS = async (agentId, soul, config = {}) => {
|
|
800
|
+
if (config.useLocalNode !== false) {
|
|
801
|
+
const helia = await getHelia();
|
|
802
|
+
if (helia) {
|
|
803
|
+
try {
|
|
804
|
+
const cid = await helia.j.add(soul);
|
|
805
|
+
return {
|
|
806
|
+
cid: cid.toString(),
|
|
807
|
+
ipfsUrl: `ipfs://${cid.toString()}`,
|
|
808
|
+
soul
|
|
809
|
+
};
|
|
810
|
+
} catch (e) {
|
|
811
|
+
console.warn("Helia add failed, falling back to API", e);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
}
|
|
757
815
|
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
758
816
|
try {
|
|
759
817
|
const response = await fetch(`${apiUrl}/agents/${agentId}/soul/export`, {
|
|
@@ -781,6 +839,14 @@ var exportSoulToIPFS = async (agentId, soul, config = {}) => {
|
|
|
781
839
|
}
|
|
782
840
|
};
|
|
783
841
|
var importSoulFromIPFS = async (cid, config = {}) => {
|
|
842
|
+
if (config.useLocalNode !== false) {
|
|
843
|
+
const helia = await getHelia();
|
|
844
|
+
if (helia) {
|
|
845
|
+
try {
|
|
846
|
+
} catch (e) {
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
784
850
|
const apiUrl = config.apiUrl || "https://forbocai-api.onrender.com";
|
|
785
851
|
try {
|
|
786
852
|
const response = await fetch(`${apiUrl}/souls/${cid}`, {
|
|
@@ -796,7 +862,6 @@ var importSoulFromIPFS = async (cid, config = {}) => {
|
|
|
796
862
|
version: "1.0.0",
|
|
797
863
|
name: data.soulName,
|
|
798
864
|
persona: data.dna,
|
|
799
|
-
// DNA contains the persona
|
|
800
865
|
state: { mood: "neutral", inventory: [], skills: {}, relationships: {} },
|
|
801
866
|
memories: []
|
|
802
867
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "forbocai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "The Infrastructure Layer for Autonomous AI Characters",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -16,15 +16,20 @@
|
|
|
16
16
|
"postinstall": "node postinstall.js"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@
|
|
20
|
-
"
|
|
21
|
-
"axios": "^1.6.2"
|
|
19
|
+
"@helia/json": "^5.0.6",
|
|
20
|
+
"@mlc-ai/web-llm": "^0.2.80",
|
|
21
|
+
"axios": "^1.6.2",
|
|
22
|
+
"blockstore-core": "^6.1.2",
|
|
23
|
+
"datastore-core": "^11.0.2",
|
|
24
|
+
"helia": "^6.0.18",
|
|
25
|
+
"idb": "^8.0.3",
|
|
26
|
+
"zod": "^3.22.4"
|
|
22
27
|
},
|
|
23
28
|
"devDependencies": {
|
|
29
|
+
"@types/node": "^20.0.0",
|
|
24
30
|
"tsup": "^8.5.1",
|
|
25
31
|
"typescript": "^5.9.3",
|
|
26
|
-
"vitest": "^1.0.0"
|
|
27
|
-
"@types/node": "^20.0.0"
|
|
32
|
+
"vitest": "^1.0.0"
|
|
28
33
|
},
|
|
29
34
|
"files": [
|
|
30
35
|
"dist",
|