@game_ryo/lsji 1.2.0 → 1.2.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/AGENTS.md +90 -21
- package/README.md +15 -15
- package/package.json +3 -2
- package/src/cli.js +11 -4
- package/src/execution/budget/index.js +16 -0
- package/src/execution/hitl/approval-gate.js +3 -1
- package/src/execution/idempotency.js +4 -2
- package/src/index.js +1 -1
- package/src/llm/llm-agent.js +30 -7
- package/src/llm/memory/conversation.js +11 -10
- package/src/llm/prompt-manager.js +58 -139
- package/src/server/index.js +14 -16
- package/src/server/ui/dist/assets/{main-v74qobjz.js → main-49ugfY-s.js} +8 -8
- package/src/server/ui/dist/index.html +1 -1
- package/src/server/ui/src/components/NewRunModal.jsx +61 -35
- package/src/storage/better-sqlite.js +31 -0
- package/src/storage/index.js +50 -0
- package/src/storage/memory.js +188 -0
- package/src/storage/sqlite.js +31 -0
package/AGENTS.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
**LSJI** (Learning System for JavaScript Intelligence) is a general-purpose Reinforcement Learning agent framework for Node.js. It provides a clean abstraction for building RL agents with pluggable storage backends, environments, and learning algorithms.
|
|
6
6
|
|
|
7
|
-
Originally migrated from a Cloudflare Workers implementation (Rock-Paper-Scissors AI), now redesigned as a standalone npm package with
|
|
7
|
+
Originally migrated from a Cloudflare Workers implementation (Rock-Paper-Scissors AI), now redesigned as a standalone npm package with MIT license, targeting MIT licensed.
|
|
8
8
|
|
|
9
9
|
## Architecture
|
|
10
10
|
|
|
@@ -19,6 +19,23 @@ src/
|
|
|
19
19
|
│ ├── sqlite.js # node:sqlite implementation (Node 22+)
|
|
20
20
|
│ ├── better-sqlite.js # better-sqlite3 implementation
|
|
21
21
|
│ └── memory.js # In-memory implementation
|
|
22
|
+
├── execution/ # Production execution systems
|
|
23
|
+
│ ├── budget/ # TokenCounter, CostTracker, CircuitBreaker
|
|
24
|
+
│ ├── hitl/ # ApprovalGate, ApprovalStore, Notifier
|
|
25
|
+
│ ├── idempotency.js # Duplicate prevention
|
|
26
|
+
│ └── engine.js # Durable execution with checkpoints
|
|
27
|
+
├── llm/ # LLM Agent Framework
|
|
28
|
+
│ ├── providers/ # OpenAI, Anthropic, Gemini, Local (Ollama)
|
|
29
|
+
│ ├── llm-agent.js # ReAct agent with tools & memory
|
|
30
|
+
│ ├── tools/ # Built-in tools (web_search, file_read, file_write, code_exec, api_call, send_email, db_query)
|
|
31
|
+
│ ├── memory/ # Conversation, Semantic, Episodic
|
|
32
|
+
│ ├── plugins/ # Dynamic plugin system
|
|
33
|
+
│ └── prompt-manager.js
|
|
34
|
+
├── server/ # Runtime server + Web UI
|
|
35
|
+
│ ├── index.js # Express + Socket.io
|
|
36
|
+
│ └── ui/ # React + Vite control panel
|
|
37
|
+
├── envs/
|
|
38
|
+
│ └── rps.js # Rock-Paper-Scissors environment
|
|
22
39
|
├── cli.js # Command-line interface
|
|
23
40
|
└── index.js # Public API exports
|
|
24
41
|
```
|
|
@@ -59,9 +76,9 @@ Pluggable backends:
|
|
|
59
76
|
|
|
60
77
|
## Usage
|
|
61
78
|
|
|
62
|
-
### As Library
|
|
79
|
+
### As Library (RL)
|
|
63
80
|
```javascript
|
|
64
|
-
import { Agent, QLearning, createStorage, Env } from 'lsji';
|
|
81
|
+
import { Agent, QLearning, createStorage, Env } from '@game_ryo/lsji';
|
|
65
82
|
|
|
66
83
|
// Create custom environment
|
|
67
84
|
class MyEnv extends Env {
|
|
@@ -77,23 +94,60 @@ await agent.train({ episodes: 1000 });
|
|
|
77
94
|
const result = await agent.play(userAction);
|
|
78
95
|
```
|
|
79
96
|
|
|
97
|
+
### As Library (LLM Agent)
|
|
98
|
+
```javascript
|
|
99
|
+
import { createLLMAgent, startServer } from '@game_ryo/lsji';
|
|
100
|
+
|
|
101
|
+
// Create an agent with full production features
|
|
102
|
+
const agent = await createLLMAgent({
|
|
103
|
+
llm: { provider: 'gemini', model: 'gemini-1.5-flash', apiKey: process.env.GEMINI_API_KEY },
|
|
104
|
+
budget: { maxCostPerRun: 5, maxCostPerDay: 50 },
|
|
105
|
+
hitl: { enabled: true, defaultTimeout: 300000 }, // 5 min approval timeout
|
|
106
|
+
memory: { conversation: true, episodic: true, semantic: true },
|
|
107
|
+
storage: { type: 'sqlite', options: { path: './lsji.db' } },
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const result = await agent.run("Research TypeScript best practices and create a summary");
|
|
111
|
+
console.log(result.answer);
|
|
112
|
+
|
|
113
|
+
// Or start the full runtime server
|
|
114
|
+
const server = await startServer({ port: 3456 });
|
|
115
|
+
// Visit http://localhost:3456 for the web control panel
|
|
116
|
+
```
|
|
117
|
+
|
|
80
118
|
### CLI
|
|
81
119
|
```bash
|
|
82
120
|
# Install globally or use npx
|
|
83
121
|
npm link # for local development
|
|
84
122
|
|
|
85
|
-
#
|
|
86
|
-
lsji train --episodes 500 --pattern 0
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
lsji
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
lsji
|
|
96
|
-
|
|
123
|
+
# RL Commands
|
|
124
|
+
npx @game_ryo/lsji train --episodes 500 --pattern 0
|
|
125
|
+
npx @game_ryo/lsji play --hand 0 # 0=Rock, 1=Scissors, 2=Paper
|
|
126
|
+
npx @game_ryo/lsji status --json
|
|
127
|
+
npx @game_ryo/lsji start
|
|
128
|
+
npx @game_ryo/lsji stop
|
|
129
|
+
|
|
130
|
+
# LLM Agent Commands
|
|
131
|
+
npx @game_ryo/lsji agent run --task "Search for latest AI news and summarize" --provider gemini --model gemini-1.5-flash
|
|
132
|
+
npx @game_ryo/lsji agent run-durable --task "Analyze codebase and create report" --workflow-id my-analysis
|
|
133
|
+
npx @game_ryo/lsji agent status
|
|
134
|
+
|
|
135
|
+
# Budget & Approvals
|
|
136
|
+
npx @game_ryo/lsji budget status --budgetId my-project
|
|
137
|
+
npx @game_ryo/lsji hitl list # Pending approvals
|
|
138
|
+
npx @game_ryo/lsji hitl approve --id <id> --reason "Approved"
|
|
139
|
+
|
|
140
|
+
# Durability
|
|
141
|
+
npx @game_ryo/lsji checkpoint list
|
|
142
|
+
npx @game_ryo/lsji checkpoint show --workflowId my-analysis
|
|
143
|
+
npx @game_ryo/lsji checkpoint recover --workflowId my-analysis
|
|
144
|
+
|
|
145
|
+
# Plugins
|
|
146
|
+
npx @game_ryo/lsji plugin list
|
|
147
|
+
npx @game_ryo/lsji plugin create --name my-tools
|
|
148
|
+
|
|
149
|
+
# Server
|
|
150
|
+
npx @game_ryo/lsji serve --port 3456
|
|
97
151
|
```
|
|
98
152
|
|
|
99
153
|
## Development
|
|
@@ -102,20 +156,20 @@ lsji stop
|
|
|
102
156
|
```bash
|
|
103
157
|
npm test # Run tests (vitest)
|
|
104
158
|
npm run build # No build step (ESM)
|
|
159
|
+
npm run build:ui # Build web UI
|
|
160
|
+
npm run serve # Start runtime server
|
|
105
161
|
```
|
|
106
162
|
|
|
107
163
|
### Testing
|
|
108
|
-
- Tests use `vitest` with `
|
|
109
|
-
- Run `npm test` to verify all functionality
|
|
110
|
-
|
|
111
|
-
## Key Design Decisions
|
|
164
|
+
- Tests use `vitest` with `Memo## Key Design Decisions
|
|
112
165
|
|
|
113
166
|
1. **ESM only** - Uses `"type": "module"` in package.json
|
|
114
167
|
2. **Node 22+** - Requires Node 22 for built-in `node:sqlite`
|
|
115
|
-
3. **
|
|
168
|
+
3. **MIT** - License compatible with open source
|
|
116
169
|
4. **Single package** - All core functionality in one npm package
|
|
117
170
|
5. **Storage abstraction** - Easy to swap backends
|
|
118
171
|
6. **Worker.js compatibility** - Training patterns and reward logic match original
|
|
172
|
+
7. **Production-grade** - HITL, durability, budget controls, idempotency built-in
|
|
119
173
|
|
|
120
174
|
## Common Tasks
|
|
121
175
|
|
|
@@ -129,6 +183,11 @@ npm run build # No build step (ESM)
|
|
|
129
183
|
2. Implement all abstract methods
|
|
130
184
|
3. Add to `createStorage` factory
|
|
131
185
|
|
|
186
|
+
### Adding a New LLM Tool
|
|
187
|
+
1. Create tool definition in `src/llm/tools/registry.js` or plugin
|
|
188
|
+
2. Implement `execute` function
|
|
189
|
+
3. Add to tool registry
|
|
190
|
+
|
|
132
191
|
### Modifying Learning Algorithm
|
|
133
192
|
- Extend `QLearning` class or create new algorithm in `src/core/`
|
|
134
193
|
|
|
@@ -140,4 +199,14 @@ npm run build # No build step (ESM)
|
|
|
140
199
|
|
|
141
200
|
## License
|
|
142
201
|
|
|
143
|
-
|
|
202
|
+
MIT - see LICENSE file
|
|
203
|
+
|
|
204
|
+
## Git Workflow
|
|
205
|
+
|
|
206
|
+
- Commit messages in English
|
|
207
|
+
- Format: `<type>: <subject>` (e.g., `feat: add new environment base class`)
|
|
208
|
+
- Types: feat, fix, docs, refactor, test, chore
|
|
209
|
+
|
|
210
|
+
## License
|
|
211
|
+
|
|
212
|
+
MIT - see LICENSE file
|
package/README.md
CHANGED
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
27
|
# Install
|
|
28
|
-
npm install lsji
|
|
28
|
+
npm install @game_ryo/lsji
|
|
29
29
|
|
|
30
30
|
# Set your API key (Gemini, OpenAI, or Anthropic)
|
|
31
31
|
export GEMINI_API_KEY="your-gemini-key"
|
|
32
32
|
# or: export OPENAI_API_KEY="your-openai-key"
|
|
33
33
|
|
|
34
34
|
# Start the runtime server with web UI
|
|
35
|
-
npx lsji serve
|
|
35
|
+
npx @game_ryo/npx @game_ryo/lsji serve
|
|
36
36
|
|
|
37
37
|
# Open http://localhost:3456 — create runs, watch thought logs, approve actions
|
|
38
38
|
```
|
|
@@ -41,25 +41,25 @@ npx lsji serve
|
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
43
|
# Runtime server with control panel
|
|
44
|
-
lsji serve --port 3456 # Start server + UI
|
|
45
|
-
lsji serve --no-ui # Headless mode
|
|
44
|
+
npx @game_ryo/lsji serve --port 3456 # Start server + UI
|
|
45
|
+
npx @game_ryo/lsji serve --no-ui # Headless mode
|
|
46
46
|
|
|
47
47
|
# Agent operations
|
|
48
|
-
lsji agent run --task "Write a report on AI trends" --provider gemini --model gemini-1.5-flash
|
|
49
|
-
lsji agent run-durable --task "Analyze codebase" --workflow-id my-analysis
|
|
48
|
+
npx @game_ryo/lsji agent run --task "Write a report on AI trends" --provider gemini --model gemini-1.5-flash
|
|
49
|
+
npx @game_ryo/lsji agent run-durable --task "Analyze codebase" --workflow-id my-analysis
|
|
50
50
|
|
|
51
51
|
# Budget & approvals
|
|
52
|
-
lsji budget status --budgetId my-project
|
|
53
|
-
lsji hitl list # Pending approvals
|
|
54
|
-
lsji hitl approve --id <id> --reason "Approved"
|
|
52
|
+
npx @game_ryo/lsji budget status --budgetId my-project
|
|
53
|
+
npx @game_ryo/lsji hitl list # Pending approvals
|
|
54
|
+
npx @game_ryo/lsji hitl approve --id <id> --reason "Approved"
|
|
55
55
|
|
|
56
56
|
# Plugins
|
|
57
|
-
lsji plugin list
|
|
58
|
-
lsji plugin create --name my-tools # Generate template
|
|
57
|
+
npx @game_ryo/lsji plugin list
|
|
58
|
+
npx @game_ryo/lsji plugin create --name my-tools # Generate template
|
|
59
59
|
|
|
60
60
|
# RL (legacy)
|
|
61
|
-
lsji train --episodes 500
|
|
62
|
-
lsji play --hand 0
|
|
61
|
+
npx @game_ryo/lsji train --episodes 500
|
|
62
|
+
npx @game_ryo/lsji play --hand 0
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
## Programmatic Usage
|
|
@@ -85,7 +85,7 @@ const server = await startServer({ port: 3456 });
|
|
|
85
85
|
|
|
86
86
|
## Web Control Panel
|
|
87
87
|
|
|
88
|
-
When you run `lsji serve`, you get a real-time dashboard at **http://localhost:3456**:
|
|
88
|
+
When you run `npx @game_ryo/lsji serve`, you get a real-time dashboard at **http://localhost:3456**:
|
|
89
89
|
|
|
90
90
|
| Panel | Features |
|
|
91
91
|
|-------|----------|
|
|
@@ -117,7 +117,7 @@ export default {
|
|
|
117
117
|
```
|
|
118
118
|
|
|
119
119
|
```bash
|
|
120
|
-
lsji plugin list # Shows: my-tools (1 tools)
|
|
120
|
+
npx @game_ryo/lsji plugin list # Shows: my-tools (1 tools)
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
## Architecture
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@game_ryo/lsji",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "A general-purpose reinforcement learning agent framework (Node.js) with LLM agent capabilities",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"vitest": "^2.0.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
+
"better-sqlite3": "^13.0.3",
|
|
26
27
|
"cors": "^2.8.5",
|
|
27
28
|
"express": "^4.19.2",
|
|
28
29
|
"openai": "^7.8.0",
|
|
@@ -32,4 +33,4 @@
|
|
|
32
33
|
"publishConfig": {
|
|
33
34
|
"access": "public"
|
|
34
35
|
}
|
|
35
|
-
}
|
|
36
|
+
}
|
package/src/cli.js
CHANGED
|
@@ -215,6 +215,7 @@ export async function main() {
|
|
|
215
215
|
|
|
216
216
|
console.log(`Running agent on task: ${task}`);
|
|
217
217
|
|
|
218
|
+
const storageConfig = { type: storageType, options: { path: dbPath } };
|
|
218
219
|
const agent = await createLLMAgent({
|
|
219
220
|
llm: {
|
|
220
221
|
provider: options.provider || 'openai',
|
|
@@ -234,6 +235,7 @@ export async function main() {
|
|
|
234
235
|
semantic: options.semantic === 'true',
|
|
235
236
|
episodic: options.episodic !== 'false',
|
|
236
237
|
},
|
|
238
|
+
storage: storageConfig,
|
|
237
239
|
});
|
|
238
240
|
|
|
239
241
|
const result = await agent.run(task, {
|
|
@@ -257,6 +259,7 @@ export async function main() {
|
|
|
257
259
|
|
|
258
260
|
console.log(`Running durable agent on task: ${task}`);
|
|
259
261
|
|
|
262
|
+
const storageConfig = { type: storageType, options: { path: dbPath } };
|
|
260
263
|
const agent = await createLLMAgent({
|
|
261
264
|
llm: {
|
|
262
265
|
provider: options.provider || 'openai',
|
|
@@ -266,6 +269,7 @@ export async function main() {
|
|
|
266
269
|
execution: {
|
|
267
270
|
checkpointInterval: parseInt(options.checkpointInterval) || 3,
|
|
268
271
|
},
|
|
272
|
+
storage: storageConfig,
|
|
269
273
|
});
|
|
270
274
|
|
|
271
275
|
const result = await agent.runDurable(task, {
|
|
@@ -280,13 +284,16 @@ export async function main() {
|
|
|
280
284
|
}
|
|
281
285
|
|
|
282
286
|
case 'status': {
|
|
283
|
-
const
|
|
284
|
-
|
|
287
|
+
const storageConfig = { type: storageType, options: { path: dbPath } };
|
|
288
|
+
// Create agent without initializing LLM for status check
|
|
289
|
+
const { LLMAgent } = await import('./llm/llm-agent.js');
|
|
290
|
+
const agent = new LLMAgent({
|
|
291
|
+
llm: { provider: 'local', model: 'test', baseUrl: 'http://localhost:11434/v1' },
|
|
292
|
+
storage: storageConfig,
|
|
285
293
|
});
|
|
286
|
-
|
|
294
|
+
// Don't initialize - just get status
|
|
287
295
|
const status = agent.getStatus();
|
|
288
296
|
output(status, jsonOutput);
|
|
289
|
-
await agent.shutdown();
|
|
290
297
|
break;
|
|
291
298
|
}
|
|
292
299
|
|
|
@@ -53,6 +53,22 @@ export function createBudgetController(config = {}) {
|
|
|
53
53
|
};
|
|
54
54
|
},
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Record cost/usage after operation (alias for recordUsage)
|
|
58
|
+
* Supports both recordCost(budgetId, usage) and recordCost({ budgetId, ...usage })
|
|
59
|
+
*/
|
|
60
|
+
recordCost(budgetId, usage) {
|
|
61
|
+
// Handle both calling conventions:
|
|
62
|
+
// 1. recordCost(budgetId, usageObj)
|
|
63
|
+
// 2. recordCost({ budgetId, ...usageObj })
|
|
64
|
+
if (usage === undefined && budgetId && typeof budgetId === 'object') {
|
|
65
|
+
// Called as recordCost({ budgetId, ...usage })
|
|
66
|
+
const obj = budgetId;
|
|
67
|
+
return this.recordUsage(obj.budgetId, obj);
|
|
68
|
+
}
|
|
69
|
+
return this.recordUsage(budgetId, usage);
|
|
70
|
+
},
|
|
71
|
+
|
|
56
72
|
/**
|
|
57
73
|
* Reset run budget
|
|
58
74
|
*/
|
|
@@ -199,7 +199,9 @@ export class ApprovalGate {
|
|
|
199
199
|
* Create approval gate from config
|
|
200
200
|
*/
|
|
201
201
|
export async function createApprovalGate(config = {}) {
|
|
202
|
-
|
|
202
|
+
// Support both config.store and config.storage for storage configuration
|
|
203
|
+
const storageConfig = config.storage || config.store || { type: 'sqlite', options: {} };
|
|
204
|
+
const store = await createApprovalStore(storageConfig);
|
|
203
205
|
const notifier = new Notifier(config.notifier || {});
|
|
204
206
|
|
|
205
207
|
return new ApprovalGate({
|
|
@@ -323,9 +323,11 @@ export class IdempotencyStore {
|
|
|
323
323
|
* Create idempotency store from config
|
|
324
324
|
*/
|
|
325
325
|
export async function createIdempotencyStore(config = {}) {
|
|
326
|
+
// Support both config.storage and config.type/config.options for storage configuration
|
|
327
|
+
const storageConfig = config.storage || { type: config.type || 'sqlite', options: config.options || {} };
|
|
326
328
|
const storage = await createStorage(
|
|
327
|
-
|
|
328
|
-
|
|
329
|
+
storageConfig.type || 'sqlite',
|
|
330
|
+
storageConfig.options || {}
|
|
329
331
|
);
|
|
330
332
|
const store = new IdempotencyStore(storage, { ttl: config.ttl });
|
|
331
333
|
await store.initialize();
|
package/src/index.js
CHANGED
package/src/llm/llm-agent.js
CHANGED
|
@@ -14,7 +14,7 @@ import { createPromptManager } from './prompt-manager.js';
|
|
|
14
14
|
import { createExecutionEngine } from '../execution/engine.js';
|
|
15
15
|
import { createApprovalGate } from '../execution/hitl/approval-gate.js';
|
|
16
16
|
import { createBudgetController } from '../execution/budget/index.js';
|
|
17
|
-
import {
|
|
17
|
+
import { createIdempotencyStore } from '../execution/idempotency.js';
|
|
18
18
|
import { v4 as uuidv4 } from 'uuid';
|
|
19
19
|
|
|
20
20
|
/**
|
|
@@ -27,6 +27,8 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
27
27
|
* @property {Object} [hitl] - HITL approval config
|
|
28
28
|
* @property {Object} [budget] - Budget control config
|
|
29
29
|
* @property {Object} [idempotency] - Idempotency config
|
|
30
|
+
* @property {Object} [prompts] - Prompt manager config
|
|
31
|
+
* @property {string} [sessionId] - Session ID for conversation memory
|
|
30
32
|
*/
|
|
31
33
|
|
|
32
34
|
/**
|
|
@@ -61,6 +63,9 @@ export class LLMAgent {
|
|
|
61
63
|
throw new Error(`LLM validation failed: ${valid.error}`);
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
// Get storage config
|
|
67
|
+
const storageConfig = this.config.storage || { type: 'sqlite', options: {} };
|
|
68
|
+
|
|
64
69
|
// Initialize tool registry
|
|
65
70
|
this.tools = createToolRegistry({
|
|
66
71
|
idempotencyStore: this.idempotency,
|
|
@@ -69,24 +74,39 @@ export class LLMAgent {
|
|
|
69
74
|
|
|
70
75
|
// Initialize memory systems
|
|
71
76
|
if (this.config.memory?.conversation !== false) {
|
|
72
|
-
this.memory.conversation = await createConversationMemory(
|
|
77
|
+
this.memory.conversation = await createConversationMemory({
|
|
78
|
+
...this.config.memory?.conversation,
|
|
79
|
+
storage: storageConfig
|
|
80
|
+
});
|
|
73
81
|
await this.memory.conversation.startSession(this.config.sessionId);
|
|
74
82
|
}
|
|
75
83
|
|
|
76
84
|
if (this.config.memory?.semantic) {
|
|
77
|
-
this.memory.semantic = await createSemanticMemory(
|
|
85
|
+
this.memory.semantic = await createSemanticMemory({
|
|
86
|
+
...this.config.memory.semantic,
|
|
87
|
+
storage: storageConfig
|
|
88
|
+
});
|
|
78
89
|
}
|
|
79
90
|
|
|
80
91
|
if (this.config.memory?.episodic) {
|
|
81
|
-
this.memory.episodic = await createEpisodicMemory(
|
|
92
|
+
this.memory.episodic = await createEpisodicMemory({
|
|
93
|
+
...this.config.memory.episodic,
|
|
94
|
+
storage: storageConfig
|
|
95
|
+
});
|
|
82
96
|
}
|
|
83
97
|
|
|
84
98
|
// Initialize execution engine
|
|
85
|
-
this.execution = await createExecutionEngine(
|
|
99
|
+
this.execution = await createExecutionEngine({
|
|
100
|
+
...this.config.execution,
|
|
101
|
+
storage: storageConfig
|
|
102
|
+
});
|
|
86
103
|
|
|
87
104
|
// Initialize HITL
|
|
88
105
|
if (this.config.hitl?.enabled !== false) {
|
|
89
|
-
this.hitl = await createApprovalGate(
|
|
106
|
+
this.hitl = await createApprovalGate({
|
|
107
|
+
...this.config.hitl,
|
|
108
|
+
store: storageConfig
|
|
109
|
+
});
|
|
90
110
|
// Update tool registry with HITL
|
|
91
111
|
this.tools.approvalGate = this.hitl;
|
|
92
112
|
}
|
|
@@ -95,7 +115,10 @@ export class LLMAgent {
|
|
|
95
115
|
this.budget = createBudgetController(this.config.budget);
|
|
96
116
|
|
|
97
117
|
// Initialize idempotency
|
|
98
|
-
this.idempotency = await
|
|
118
|
+
this.idempotency = await createIdempotencyStore({
|
|
119
|
+
...this.config.idempotency,
|
|
120
|
+
...storageConfig
|
|
121
|
+
});
|
|
99
122
|
this.tools.idempotencyStore = this.idempotency;
|
|
100
123
|
|
|
101
124
|
// Initialize prompt manager
|
|
@@ -37,8 +37,9 @@ export class ConversationMemory {
|
|
|
37
37
|
async initialize() {
|
|
38
38
|
if (this.initialized) return;
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
// Use the new storage interface method
|
|
41
|
+
if (typeof this.storage.exec === 'function') {
|
|
42
|
+
await this.storage.exec(`
|
|
42
43
|
CREATE TABLE IF NOT EXISTS conversations (
|
|
43
44
|
id TEXT PRIMARY KEY,
|
|
44
45
|
session_id TEXT NOT NULL,
|
|
@@ -52,11 +53,11 @@ export class ConversationMemory {
|
|
|
52
53
|
)
|
|
53
54
|
`);
|
|
54
55
|
|
|
55
|
-
await this.storage.
|
|
56
|
+
await this.storage.exec(`
|
|
56
57
|
CREATE INDEX IF NOT EXISTS idx_conversations_session ON conversations(session_id)
|
|
57
58
|
`);
|
|
58
59
|
|
|
59
|
-
await this.storage.
|
|
60
|
+
await this.storage.exec(`
|
|
60
61
|
CREATE INDEX IF NOT EXISTS idx_conversations_created ON conversations(created_at)
|
|
61
62
|
`);
|
|
62
63
|
}
|
|
@@ -86,8 +87,8 @@ export class ConversationMemory {
|
|
|
86
87
|
async loadSession(sessionId) {
|
|
87
88
|
await this.initialize();
|
|
88
89
|
|
|
89
|
-
if (this.storage.
|
|
90
|
-
const rows = await this.storage.
|
|
90
|
+
if (typeof this.storage.all === 'function') {
|
|
91
|
+
const rows = await this.storage.all(
|
|
91
92
|
'SELECT * FROM conversations WHERE session_id = ? ORDER BY created_at ASC',
|
|
92
93
|
[sessionId]
|
|
93
94
|
);
|
|
@@ -123,12 +124,12 @@ export class ConversationMemory {
|
|
|
123
124
|
this.messages.push(msg);
|
|
124
125
|
|
|
125
126
|
// Persist to storage
|
|
126
|
-
if (this.storage.
|
|
127
|
+
if (typeof this.storage.run === 'function' && this.sessionId) {
|
|
127
128
|
const tokens = this.tokenCounter
|
|
128
129
|
? await this.tokenCounter.estimateTokens([msg], { provider: 'openai', model: 'gpt-4o-mini' })
|
|
129
130
|
: 0;
|
|
130
131
|
|
|
131
|
-
await this.storage.
|
|
132
|
+
await this.storage.run(
|
|
132
133
|
`INSERT INTO conversations (id, session_id, role, content, name, tool_call_id, tool_calls, tokens, created_at)
|
|
133
134
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
134
135
|
[
|
|
@@ -229,8 +230,8 @@ export class ConversationMemory {
|
|
|
229
230
|
async clear() {
|
|
230
231
|
this.messages = [];
|
|
231
232
|
|
|
232
|
-
if (this.storage.
|
|
233
|
-
await this.storage.
|
|
233
|
+
if (typeof this.storage.run === 'function' && this.sessionId) {
|
|
234
|
+
await this.storage.run(
|
|
234
235
|
'DELETE FROM conversations WHERE session_id = ?',
|
|
235
236
|
[this.sessionId]
|
|
236
237
|
);
|