@game_ryo/lsji 0.1.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/package.json +15 -7
- package/src/cli.js +395 -62
- package/src/execution/budget/circuit-breaker.js +245 -0
- package/src/execution/budget/cost-tracker.js +387 -0
- package/src/execution/budget/index.js +63 -0
- package/src/execution/budget/token-counter.js +159 -0
- package/src/execution/engine.js +428 -0
- package/src/execution/hitl/approval-gate.js +210 -0
- package/src/execution/hitl/index.js +12 -0
- package/src/execution/hitl/notifier.js +151 -0
- package/src/execution/hitl/store.js +311 -0
- package/src/execution/idempotency.js +312 -0
- package/src/execution/index.js +14 -0
- package/src/index.js +80 -4
- package/src/llm/index.js +21 -0
- package/src/llm/llm-agent.js +357 -0
- package/src/llm/memory/conversation.js +271 -0
- package/src/llm/memory/episodic.js +312 -0
- package/src/llm/memory/index.js +12 -0
- package/src/llm/memory/semantic.js +324 -0
- package/src/llm/plugins/index.js +202 -0
- package/src/llm/prompt-manager.js +332 -0
- package/src/llm/providers/anthropic.js +250 -0
- package/src/llm/providers/base.js +116 -0
- package/src/llm/providers/local.js +163 -0
- package/src/llm/providers/openai.js +212 -0
- package/src/llm/tools/registry.js +342 -0
- package/src/server/index.js +416 -0
- package/src/server/ui/index.html +16 -0
- package/src/server/ui/package.json +19 -0
- package/src/server/ui/src/main.jsx +10 -0
- package/src/server/ui/src/styles.css +260 -0
- package/src/server/ui/vite.config.js +27 -0
- package/docs/README.md +0 -43
- package/docs/blog/2019-05-28-first-blog-post.mdx +0 -12
- package/docs/blog/2019-05-29-long-blog-post.mdx +0 -44
- package/docs/blog/2021-08-01-mdx-blog-post.mdx +0 -24
- package/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
- package/docs/blog/2021-08-26-welcome/index.mdx +0 -29
- package/docs/blog/authors.yml +0 -25
- package/docs/blog/tags.yml +0 -19
- package/docs/docs/api/agent.md +0 -151
- package/docs/docs/api/env.md +0 -133
- package/docs/docs/api/environments.md +0 -102
- package/docs/docs/api/qlearning.md +0 -138
- package/docs/docs/api/storage.md +0 -168
- package/docs/docs/architecture.md +0 -155
- package/docs/docs/cli.md +0 -210
- package/docs/docs/contributing.md +0 -162
- package/docs/docs/core-concepts.md +0 -152
- package/docs/docs/examples/advanced-training.md +0 -244
- package/docs/docs/examples/custom-environment.md +0 -198
- package/docs/docs/examples/custom-storage.md +0 -251
- package/docs/docs/getting-started.md +0 -91
- package/docs/docusaurus.config.ts +0 -149
- package/docs/package-lock.json +0 -19522
- package/docs/package.json +0 -49
- package/docs/sidebars.ts +0 -33
- package/docs/src/components/HomepageFeatures/index.tsx +0 -71
- package/docs/src/components/HomepageFeatures/styles.module.css +0 -11
- package/docs/src/css/custom.css +0 -79
- package/docs/src/pages/index.module.css +0 -23
- package/docs/src/pages/index.tsx +0 -44
- package/docs/src/pages/markdown-page.mdx +0 -7
- package/docs/static/.nojekyll +0 -0
- package/docs/static/img/docusaurus-social-card.jpg +0 -0
- package/docs/static/img/docusaurus.png +0 -0
- package/docs/static/img/favicon.ico +0 -0
- package/docs/static/img/logo.png +0 -0
- package/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
- package/docs/static/img/undraw_docusaurus_react.svg +0 -170
- package/docs/static/img/undraw_docusaurus_tree.svg +0 -40
- package/docs/tsconfig.json +0 -12
- package/legacy/worker.js +0 -166
- package/legacy/wrangler.toml +0 -11
package/docs/docs/api/env.md
DELETED
|
@@ -1,133 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Env Interface
|
|
3
|
-
description: Base environment interface for RL problems
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Env Interface
|
|
7
|
-
|
|
8
|
-
All reinforcement learning environments must extend the abstract `Env` class.
|
|
9
|
-
|
|
10
|
-
## Import
|
|
11
|
-
|
|
12
|
-
```typescript
|
|
13
|
-
import { Env } from 'lsji';
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Abstract Methods
|
|
17
|
-
|
|
18
|
-
### `getState()`
|
|
19
|
-
Return current state as a string key.
|
|
20
|
-
|
|
21
|
-
```typescript
|
|
22
|
-
getState(): string;
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
### `step(action)`
|
|
26
|
-
Execute an action and return the result.
|
|
27
|
-
|
|
28
|
-
```typescript
|
|
29
|
-
async step(action: number): Promise<StepResult>;
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
**StepResult:**
|
|
33
|
-
```typescript
|
|
34
|
-
interface StepResult {
|
|
35
|
-
state: string; // New state
|
|
36
|
-
reward: number; // Reward for this step
|
|
37
|
-
done: boolean; // Episode ended
|
|
38
|
-
info?: object; // Additional info
|
|
39
|
-
}
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### `actionSize()`
|
|
43
|
-
Return number of possible actions.
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
actionSize(): number;
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
### `reset()`
|
|
50
|
-
Reset environment to initial state.
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
async reset(): Promise<string>; // Returns initial state
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
## Optional Methods
|
|
57
|
-
|
|
58
|
-
### `render()`
|
|
59
|
-
Human-readable representation for debugging.
|
|
60
|
-
|
|
61
|
-
```typescript
|
|
62
|
-
render(): string;
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## StateEncoder Utility
|
|
66
|
-
|
|
67
|
-
Helper for encoding/decoding complex states:
|
|
68
|
-
|
|
69
|
-
```typescript
|
|
70
|
-
import { StateEncoder } from 'lsji';
|
|
71
|
-
|
|
72
|
-
// Encode object to string
|
|
73
|
-
const key = StateEncoder.encode({ position: 5, inventory: ['sword'] });
|
|
74
|
-
// Returns: '{"position":5,"inventory":["sword"]}'
|
|
75
|
-
|
|
76
|
-
// Decode string to object
|
|
77
|
-
const state = StateEncoder.decode(key);
|
|
78
|
-
// Returns: { position: 5, inventory: ['sword'] }
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Creating a Custom Environment
|
|
82
|
-
|
|
83
|
-
```typescript
|
|
84
|
-
import { Env } from 'lsji';
|
|
85
|
-
|
|
86
|
-
class GridWorldEnv extends Env {
|
|
87
|
-
constructor() {
|
|
88
|
-
super();
|
|
89
|
-
this.position = 0;
|
|
90
|
-
this.gridSize = 10;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
getState() {
|
|
94
|
-
return String(this.position);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
async step(action) {
|
|
98
|
-
// Actions: 0=left, 1=right
|
|
99
|
-
if (action === 0) this.position = Math.max(0, this.position - 1);
|
|
100
|
-
if (action === 1) this.position = Math.min(this.gridSize - 1, this.position + 1);
|
|
101
|
-
|
|
102
|
-
const done = this.position === this.gridSize - 1;
|
|
103
|
-
const reward = done ? 1 : -0.01;
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
state: String(this.position),
|
|
107
|
-
reward,
|
|
108
|
-
done,
|
|
109
|
-
info: { position: this.position }
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
actionSize() {
|
|
114
|
-
return 2;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async reset() {
|
|
118
|
-
this.position = 0;
|
|
119
|
-
return '0';
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
render() {
|
|
123
|
-
return `GridWorld: position ${this.position}/${this.gridSize - 1}`;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
## Best Practices
|
|
129
|
-
|
|
130
|
-
1. **State as string** — Use simple string keys for Q-table indexing
|
|
131
|
-
2. **Deterministic rewards** — Same state-action should give consistent rewards
|
|
132
|
-
3. **Action space** — Keep action space small for tabular Q-learning
|
|
133
|
-
4. **Reset** — Always implement proper reset for episode boundaries
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Built-in Environments
|
|
3
|
-
description: Pre-built environments for quick start
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Built-in Environments
|
|
7
|
-
|
|
8
|
-
LSJI includes a Rock-Paper-Scissors environment for demonstration and testing.
|
|
9
|
-
|
|
10
|
-
## Import
|
|
11
|
-
|
|
12
|
-
```typescript
|
|
13
|
-
import {
|
|
14
|
-
RockPaperScissorsEnv,
|
|
15
|
-
TrainingPattern,
|
|
16
|
-
getTrainingAction
|
|
17
|
-
} from 'lsji';
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## RockPaperScissorsEnv
|
|
21
|
-
|
|
22
|
-
Classic Rock-Paper-Scissors game environment.
|
|
23
|
-
|
|
24
|
-
### Constructor
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
const env = new RockPaperScissorsEnv({
|
|
28
|
-
opponent: 'random' // 'random' | 'always_rock' | 'counter' | 'sequential'
|
|
29
|
-
});
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
### Opponent Strategies
|
|
33
|
-
|
|
34
|
-
| Strategy | Description |
|
|
35
|
-
|----------|-------------|
|
|
36
|
-
| `'random'` | Uniform random actions (default) |
|
|
37
|
-
| `'always_rock'` | Always plays Rock (0) |
|
|
38
|
-
| `'counter'` | Plays counter to agent's previous action |
|
|
39
|
-
| `'sequential'` | Cycles through Rock→Scissors→Paper |
|
|
40
|
-
|
|
41
|
-
### Methods
|
|
42
|
-
|
|
43
|
-
All standard `Env` methods plus:
|
|
44
|
-
|
|
45
|
-
```typescript
|
|
46
|
-
// Static helpers
|
|
47
|
-
RockPaperScissorsEnv.getHandName(0); // 'Rock'
|
|
48
|
-
RockPaperScissorsEnv.getHandName(1); // 'Scissors'
|
|
49
|
-
RockPaperScissorsEnv.getHandName(2); // 'Paper'
|
|
50
|
-
|
|
51
|
-
const { judge, reward, outcome } = RockPaperScissorsEnv.calculateOutcome(0, 2);
|
|
52
|
-
// judge: 2, reward: 1, outcome: 'WIN'
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
### Training Patterns
|
|
56
|
-
|
|
57
|
-
```typescript
|
|
58
|
-
import { TrainingPattern, getTrainingAction } from 'lsji';
|
|
59
|
-
|
|
60
|
-
// Pattern IDs
|
|
61
|
-
TrainingPattern.RANDOM; // 0
|
|
62
|
-
TrainingPattern.ALWAYS_ROCK; // 1
|
|
63
|
-
TrainingPattern.COUNTER; // 2
|
|
64
|
-
TrainingPattern.SEQUENTIAL; // 3
|
|
65
|
-
|
|
66
|
-
// Get action for pattern
|
|
67
|
-
const action = getTrainingAction(TrainingPattern.COUNTER, episode, lastAction);
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
### Example
|
|
71
|
-
|
|
72
|
-
```typescript
|
|
73
|
-
import {
|
|
74
|
-
Agent, QLearning, createStorage,
|
|
75
|
-
RockPaperScissorsEnv, TrainingPattern, getTrainingAction
|
|
76
|
-
} from 'lsji';
|
|
77
|
-
|
|
78
|
-
const storage = await createStorage('sqlite', { path: './rps.db' });
|
|
79
|
-
const qlearning = new QLearning({ alpha: 0.1, gamma: 0.9, epsilon: 0.1, storage });
|
|
80
|
-
|
|
81
|
-
// Train against counter opponent
|
|
82
|
-
const env = new RockPaperScissorsEnv({ opponent: 'counter' });
|
|
83
|
-
const agent = new Agent({ qlearning, storage, env });
|
|
84
|
-
|
|
85
|
-
await agent.train({
|
|
86
|
-
episodes: 1000,
|
|
87
|
-
actionSelector: (ep, last) => getTrainingAction(TrainingPattern.RANDOM, ep, last)
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// Play against random opponent
|
|
91
|
-
const playEnv = new RockPaperScissorsEnv({ opponent: 'random' });
|
|
92
|
-
agent.setEnvironment(playEnv);
|
|
93
|
-
|
|
94
|
-
const result = await agent.play(0); // You play Rock
|
|
95
|
-
console.log(`AI: ${RockPaperScissorsEnv.getHandName(result.action)} | ${result.reward > 0 ? 'WIN' : 'LOSE'}`);
|
|
96
|
-
|
|
97
|
-
await storage.close();
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
## Creating Custom Environments
|
|
101
|
-
|
|
102
|
-
See [Custom Environment Example](/docs/examples/custom-environment) for a complete guide.
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: QLearning API
|
|
3
|
-
description: Tabular Q-Learning engine with TD updates
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# QLearning API
|
|
7
|
-
|
|
8
|
-
The `QLearning` class implements tabular Q-Learning with epsilon-greedy exploration.
|
|
9
|
-
|
|
10
|
-
## Import
|
|
11
|
-
|
|
12
|
-
```typescript
|
|
13
|
-
import { QLearning } from 'lsji';
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Constructor
|
|
17
|
-
|
|
18
|
-
```typescript
|
|
19
|
-
const qlearning = new QLearning({
|
|
20
|
-
alpha: 0.1, // Learning rate [0, 1] (default: 0.1)
|
|
21
|
-
gamma: 0.9, // Discount factor [0, 1] (default: 0.9)
|
|
22
|
-
epsilon: 0.1, // Exploration rate [0, 1] (default: 0.1)
|
|
23
|
-
storage: Storage // Required: Storage backend
|
|
24
|
-
});
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
Values are automatically clamped to [0, 1] range.
|
|
28
|
-
|
|
29
|
-
## Methods
|
|
30
|
-
|
|
31
|
-
### `initialize()`
|
|
32
|
-
Load Q-table from storage. Called automatically by other methods.
|
|
33
|
-
|
|
34
|
-
```typescript
|
|
35
|
-
await qlearning.initialize();
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### `getQValue(state, action)`
|
|
39
|
-
Get Q-value for a state-action pair.
|
|
40
|
-
|
|
41
|
-
```typescript
|
|
42
|
-
const value = qlearning.getQValue('state1', 0);
|
|
43
|
-
// Returns: number (0 if unseen)
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
### `setQValue(state, action, value)`
|
|
47
|
-
Set Q-value and persist to storage.
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
50
|
-
await qlearning.setQValue('state1', 0, 0.5);
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
### `act(state, actionSize)`
|
|
54
|
-
Select action using epsilon-greedy policy.
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
const action = await qlearning.act('state1', 3);
|
|
58
|
-
// Returns: number (0 to actionSize-1)
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
**Behavior:**
|
|
62
|
-
- With probability `epsilon`: random action
|
|
63
|
-
- With probability `1-epsilon`: best known action (ties broken randomly)
|
|
64
|
-
|
|
65
|
-
### `learn(state, action, reward, nextState, nextActionSize)`
|
|
66
|
-
Full TD update: Q(s,a) ← Q(s,a) + α[r + γ·maxₐ' Q(s',a') - Q(s,a)]
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
const newQ = await qlearning.learn('state1', 0, 1, 'state2', 3);
|
|
70
|
-
// Returns: updated Q-value
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### `learnSimple(state, action, reward)`
|
|
74
|
-
Simplified update for terminal states: Q(s,a) ← Q(s,a) + α[r - Q(s,a)]
|
|
75
|
-
|
|
76
|
-
```typescript
|
|
77
|
-
const newQ = await qlearning.learnSimple('state1', 0, 1);
|
|
78
|
-
// Returns: updated Q-value
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
### `reset()`
|
|
82
|
-
Clear in-memory Q-table cache.
|
|
83
|
-
|
|
84
|
-
```typescript
|
|
85
|
-
await qlearning.reset();
|
|
86
|
-
// Note: Does not clear storage
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### `getStateValues(state, actionSize)`
|
|
90
|
-
Get all Q-values for a state.
|
|
91
|
-
|
|
92
|
-
```typescript
|
|
93
|
-
const values = qlearning.getStateValues('state1', 3);
|
|
94
|
-
// Returns: { 0: 0.5, 1: 0.2, 2: -0.1 }
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
### `getFullQTable()`
|
|
98
|
-
Get entire Q-table for inspection.
|
|
99
|
-
|
|
100
|
-
```typescript
|
|
101
|
-
const table = await qlearning.getFullQTable();
|
|
102
|
-
// Returns:
|
|
103
|
-
[
|
|
104
|
-
{ state: 'state1', action: 0, q_value: 0.5 },
|
|
105
|
-
{ state: 'state1', action: 1, q_value: 0.2 },
|
|
106
|
-
...
|
|
107
|
-
]
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Hyperparameters
|
|
111
|
-
|
|
112
|
-
| Parameter | Default | Range | Description |
|
|
113
|
-
|-----------|---------|-------|-------------|
|
|
114
|
-
| `alpha` | 0.1 | [0, 1] | Learning rate — how much new info overrides old |
|
|
115
|
-
| `gamma` | 0.9 | [0, 1] | Discount factor — future reward importance |
|
|
116
|
-
| `epsilon` | 0.1 | [0, 1] | Exploration rate — random action probability |
|
|
117
|
-
|
|
118
|
-
## Example
|
|
119
|
-
|
|
120
|
-
```typescript
|
|
121
|
-
import { QLearning, createStorage } from 'lsji';
|
|
122
|
-
|
|
123
|
-
const storage = await createStorage('memory');
|
|
124
|
-
const ql = new QLearning({ alpha: 0.1, gamma: 0.9, epsilon: 0.1, storage });
|
|
125
|
-
|
|
126
|
-
await ql.initialize();
|
|
127
|
-
|
|
128
|
-
// Train on simple state-action pairs
|
|
129
|
-
await ql.learnSimple('state1', 0, 1); // Win
|
|
130
|
-
await ql.learnSimple('state1', 0, 1); // Win again
|
|
131
|
-
await ql.learnSimple('state1', 1, -1); // Lose
|
|
132
|
-
|
|
133
|
-
// Check learned values
|
|
134
|
-
console.log(ql.getQValue('state1', 0)); // ~0.19
|
|
135
|
-
console.log(ql.getQValue('state1', 1)); // ~-0.1
|
|
136
|
-
|
|
137
|
-
await storage.close();
|
|
138
|
-
```
|
package/docs/docs/api/storage.md
DELETED
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Storage API
|
|
3
|
-
description: Pluggable storage backends
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Storage API
|
|
7
|
-
|
|
8
|
-
LSJI provides a pluggable storage abstraction with three built-in implementations.
|
|
9
|
-
|
|
10
|
-
## Import
|
|
11
|
-
|
|
12
|
-
```typescript
|
|
13
|
-
import { createStorage, Storage, SqliteStorage, BetterSqliteStorage, MemoryStorage } from 'lsji';
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
## Factory Function
|
|
17
|
-
|
|
18
|
-
```typescript
|
|
19
|
-
const storage = await createStorage(type, options);
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
**Types:**
|
|
23
|
-
- `'sqlite'` — Node.js built-in `node:sqlite` (recommended)
|
|
24
|
-
- `'better-sqlite'` — `better-sqlite3` package
|
|
25
|
-
- `'memory'` — In-memory (testing only)
|
|
26
|
-
|
|
27
|
-
**Options:**
|
|
28
|
-
```typescript
|
|
29
|
-
// SQLite
|
|
30
|
-
{ path: './my-agent.db' }
|
|
31
|
-
|
|
32
|
-
// Better-SQLite
|
|
33
|
-
{ path: './my-agent.db' }
|
|
34
|
-
|
|
35
|
-
// Memory
|
|
36
|
-
{} // No options needed
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
## Storage Interface
|
|
40
|
-
|
|
41
|
-
All backends implement this interface:
|
|
42
|
-
|
|
43
|
-
```typescript
|
|
44
|
-
interface Storage {
|
|
45
|
-
initialize(): Promise<void>;
|
|
46
|
-
close(): Promise<void>;
|
|
47
|
-
getSetting(key: string): Promise<{key: string, value: string} | null>;
|
|
48
|
-
setSetting(key: string, value: string | number): Promise<void>;
|
|
49
|
-
getQTable(): Promise<Array<{state: string, action: number, q_value: number}>>;
|
|
50
|
-
updateQ(state: string, action: number, qValue: number): Promise<void>;
|
|
51
|
-
addBattle(record: BattleRecord): Promise<void>;
|
|
52
|
-
getTodayBattleCount(): Promise<number>;
|
|
53
|
-
getPerformanceStats(): Promise<Array<{mode: string, total: number, win_rate: number}>>;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
interface BattleRecord {
|
|
57
|
-
mode: 'train' | 'test';
|
|
58
|
-
handA: number;
|
|
59
|
-
handB: number;
|
|
60
|
-
reward: number;
|
|
61
|
-
createdAt: string; // ISO timestamp
|
|
62
|
-
}
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Backends
|
|
66
|
-
|
|
67
|
-
### SqliteStorage (Recommended)
|
|
68
|
-
|
|
69
|
-
Uses Node.js 22+ built-in `node:sqlite` — **zero dependencies**.
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
import { SqliteStorage } from 'lsji';
|
|
73
|
-
|
|
74
|
-
const storage = new SqliteStorage('./agent.db');
|
|
75
|
-
await storage.initialize();
|
|
76
|
-
// ... use storage
|
|
77
|
-
await storage.close();
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
**Features:**
|
|
81
|
-
- Synchronous API (fast)
|
|
82
|
-
- Automatic schema creation
|
|
83
|
-
- Indexes on battle_history for performance
|
|
84
|
-
- Persistent across process restarts
|
|
85
|
-
|
|
86
|
-
### BetterSqliteStorage
|
|
87
|
-
|
|
88
|
-
Uses `better-sqlite3` for high-performance synchronous access.
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
import { BetterSqliteStorage } from 'lsji';
|
|
92
|
-
|
|
93
|
-
const storage = new BetterSqliteStorage('./agent.db');
|
|
94
|
-
await storage.initialize();
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
**Requires:** `npm install better-sqlite3`
|
|
98
|
-
|
|
99
|
-
**Use when:** You need faster writes or advanced SQLite features.
|
|
100
|
-
|
|
101
|
-
### MemoryStorage
|
|
102
|
-
|
|
103
|
-
Pure in-memory implementation.
|
|
104
|
-
|
|
105
|
-
```typescript
|
|
106
|
-
import { MemoryStorage } from 'lsji';
|
|
107
|
-
|
|
108
|
-
const storage = new MemoryStorage();
|
|
109
|
-
await storage.initialize();
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
**Features:**
|
|
113
|
-
- No persistence (data lost on exit)
|
|
114
|
-
- Fastest for testing/CI
|
|
115
|
-
- Implements `clear()` for test isolation
|
|
116
|
-
|
|
117
|
-
## Database Schema
|
|
118
|
-
|
|
119
|
-
All SQL backends create these tables:
|
|
120
|
-
|
|
121
|
-
```sql
|
|
122
|
-
-- Settings (key-value)
|
|
123
|
-
CREATE TABLE settings (
|
|
124
|
-
key TEXT PRIMARY KEY,
|
|
125
|
-
value TEXT NOT NULL
|
|
126
|
-
);
|
|
127
|
-
|
|
128
|
-
-- Battle history
|
|
129
|
-
CREATE TABLE battle_history (
|
|
130
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
131
|
-
mode TEXT NOT NULL CHECK (mode IN ('train', 'test')),
|
|
132
|
-
hand_a INTEGER NOT NULL,
|
|
133
|
-
hand_b INTEGER NOT NULL,
|
|
134
|
-
reward INTEGER NOT NULL,
|
|
135
|
-
created_at TEXT NOT NULL
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
-- Q-table
|
|
139
|
-
CREATE TABLE q_table (
|
|
140
|
-
state TEXT NOT NULL,
|
|
141
|
-
action INTEGER NOT NULL,
|
|
142
|
-
q_value REAL NOT NULL DEFAULT 0,
|
|
143
|
-
PRIMARY KEY (state, action)
|
|
144
|
-
);
|
|
145
|
-
|
|
146
|
-
-- Indexes
|
|
147
|
-
CREATE INDEX idx_battle_history_date ON battle_history(created_at);
|
|
148
|
-
CREATE INDEX idx_battle_history_mode ON battle_history(mode);
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
## Example
|
|
152
|
-
|
|
153
|
-
```typescript
|
|
154
|
-
import { createStorage, QLearning, Agent, RockPaperScissorsEnv } from 'lsji';
|
|
155
|
-
|
|
156
|
-
// Production: persistent SQLite
|
|
157
|
-
const storage = await createStorage('sqlite', { path: './production.db' });
|
|
158
|
-
|
|
159
|
-
// Testing: in-memory
|
|
160
|
-
const testStorage = await createStorage('memory');
|
|
161
|
-
|
|
162
|
-
const qlearning = new QLearning({ alpha: 0.1, gamma: 0.9, epsilon: 0.1, storage });
|
|
163
|
-
const env = new RockPaperScissorsEnv({ opponent: 'random' });
|
|
164
|
-
const agent = new Agent({ qlearning, storage, env });
|
|
165
|
-
|
|
166
|
-
await agent.train({ episodes: 1000 });
|
|
167
|
-
await storage.close();
|
|
168
|
-
```
|
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Architecture
|
|
3
|
-
description: Design decisions and internals
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Architecture
|
|
7
|
-
|
|
8
|
-
## Design Principles
|
|
9
|
-
|
|
10
|
-
1. **Single Package** — All core functionality in one npm package
|
|
11
|
-
2. **Zero Dependencies** — Core runs on Node.js 22+ built-ins only
|
|
12
|
-
3. **Storage Abstraction** — Easy to swap backends
|
|
13
|
-
4. **ESM Only** — Modern JavaScript modules
|
|
14
|
-
5. **Apache 2.0** — License compatible with Apache Incubator
|
|
15
|
-
|
|
16
|
-
## Project Structure
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
src/
|
|
20
|
-
├── core/
|
|
21
|
-
│ ├── env.ts # Environment interface
|
|
22
|
-
│ ├── qlearning.ts # Q-Learning engine
|
|
23
|
-
│ └── agent.ts # Agent orchestration
|
|
24
|
-
├── storage/
|
|
25
|
-
│ ├── index.ts # Storage interface + factory
|
|
26
|
-
│ ├── sqlite.ts # node:sqlite implementation
|
|
27
|
-
│ ├── better-sqlite.ts # better-sqlite3 implementation
|
|
28
|
-
│ └── memory.ts # In-memory implementation
|
|
29
|
-
├── envs/
|
|
30
|
-
│ └── rps.ts # Rock-Paper-Scissors environment
|
|
31
|
-
├── cli.ts # Command-line interface
|
|
32
|
-
└── index.ts # Public API exports
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
## Core Components
|
|
36
|
-
|
|
37
|
-
### Q-Learning Engine
|
|
38
|
-
|
|
39
|
-
**Algorithm:** Tabular Q-Learning with Temporal Difference updates
|
|
40
|
-
|
|
41
|
-
**Update Rules:**
|
|
42
|
-
- Full TD: `Q(s,a) ← Q(s,a) + α[r + γ·maxₐ' Q(s',a') - Q(s,a)]`
|
|
43
|
-
- Simple: `Q(s,a) ← Q(s,a) + α[r - Q(s,a)]` (terminal states)
|
|
44
|
-
|
|
45
|
-
**Exploration:** Epsilon-greedy with configurable ε
|
|
46
|
-
|
|
47
|
-
**State Representation:** String keys for Q-table indexing
|
|
48
|
-
|
|
49
|
-
### Storage Layer
|
|
50
|
-
|
|
51
|
-
**Interface:** Abstract `Storage` class with 9 required methods
|
|
52
|
-
|
|
53
|
-
**Implementations:**
|
|
54
|
-
1. `SqliteStorage` — Node.js built-in `node:sqlite` (sync API)
|
|
55
|
-
2. `BetterSqliteStorage` — `better-sqlite3` (faster, more features)
|
|
56
|
-
3. `MemoryStorage` — Pure JS Maps/Arrays (testing)
|
|
57
|
-
|
|
58
|
-
**Schema:**
|
|
59
|
-
- `settings` — Key-value configuration
|
|
60
|
-
- `battle_history` — Training/play records with timestamps
|
|
61
|
-
- `q_table` — State-action values with composite primary key
|
|
62
|
-
|
|
63
|
-
**Indexes:** Date and mode indexes on battle_history for fast queries
|
|
64
|
-
|
|
65
|
-
### Agent Orchestration
|
|
66
|
-
|
|
67
|
-
**Responsibilities:**
|
|
68
|
-
- System lifecycle (start/stop)
|
|
69
|
-
- Training loop with batching
|
|
70
|
-
- Play/evaluation loop
|
|
71
|
-
- Statistics aggregation
|
|
72
|
-
|
|
73
|
-
**Training Loop:**
|
|
74
|
-
```
|
|
75
|
-
for episode in episodes:
|
|
76
|
-
state = env.getState()
|
|
77
|
-
action = qlearning.act(state, actionSize) // ε-greedy
|
|
78
|
-
result = env.step(action)
|
|
79
|
-
qlearning.learnSimple(state, action, result.reward)
|
|
80
|
-
storage.addBattle(record)
|
|
81
|
-
batch.flush()
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Environment Interface
|
|
85
|
-
|
|
86
|
-
**Contract:**
|
|
87
|
-
```typescript
|
|
88
|
-
abstract class Env {
|
|
89
|
-
abstract getState(): string;
|
|
90
|
-
abstract step(action: number): Promise<StepResult>;
|
|
91
|
-
abstract actionSize(): number;
|
|
92
|
-
abstract reset(): Promise<string>;
|
|
93
|
-
render(): string; // Optional
|
|
94
|
-
}
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
**Built-in:** `RockPaperScissorsEnv` with 4 opponent strategies
|
|
98
|
-
|
|
99
|
-
## Data Flow
|
|
100
|
-
|
|
101
|
-
```
|
|
102
|
-
┌─────────┐ ┌──────────────┐ ┌─────────┐
|
|
103
|
-
│ Env │────▶│ Agent │────▶│ Storage │
|
|
104
|
-
│ (State) │ │ Orchestrates│ │(Persist)│
|
|
105
|
-
└─────────┘ └──────┬───────┘ └─────────┘
|
|
106
|
-
│
|
|
107
|
-
▼
|
|
108
|
-
┌──────────────┐
|
|
109
|
-
│ QLearning │
|
|
110
|
-
│ (Updates) │
|
|
111
|
-
└──────────────┘
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
## Concurrency Model
|
|
115
|
-
|
|
116
|
-
- **Single-threaded** Node.js event loop
|
|
117
|
-
- **Synchronous SQLite** — No async/await for DB operations
|
|
118
|
-
- **Batch writes** — Reduce I/O overhead
|
|
119
|
-
- **In-memory Q-table cache** — Fast reads, periodic persistence
|
|
120
|
-
|
|
121
|
-
## Migration from Cloudflare Workers
|
|
122
|
-
|
|
123
|
-
**Original:** Worker.js with D1 database, cron triggers, HTTP endpoints
|
|
124
|
-
|
|
125
|
-
**Changes:**
|
|
126
|
-
| Before | After |
|
|
127
|
-
|--------|-------|
|
|
128
|
-
| `env.DB` (D1) | `Storage` abstraction |
|
|
129
|
-
| Cron triggers | Manual/CLI training |
|
|
130
|
-
| HTTP endpoints | Library API + CLI |
|
|
131
|
-
| Global state | Instance-based |
|
|
132
|
-
| `Request/Response` | Function parameters |
|
|
133
|
-
|
|
134
|
-
**Preserved:**
|
|
135
|
-
- Q-Learning algorithm (α=0.1, γ=0.9, ε=0.1)
|
|
136
|
-
- Reward calculation: `(ai - user + 3) % 3`
|
|
137
|
-
- Training patterns (0-3)
|
|
138
|
-
- Battle history schema
|
|
139
|
-
|
|
140
|
-
## Performance Characteristics
|
|
141
|
-
|
|
142
|
-
| Operation | Complexity | Notes |
|
|
143
|
-
|-----------|------------|-------|
|
|
144
|
-
| `act()` | O(actions) | Scans all actions for max Q |
|
|
145
|
-
| `learnSimple()` | O(1) | Single Q-value update |
|
|
146
|
-
| `learn()` | O(actions) | Finds max next Q |
|
|
147
|
-
| `getFullQTable()` | O(states×actions) | Full table scan |
|
|
148
|
-
| Batch insert | O(batch) | Single transaction |
|
|
149
|
-
|
|
150
|
-
## Future Extensibility
|
|
151
|
-
|
|
152
|
-
- **New algorithms:** Extend `QLearning` or add `SARSA`, `DQN` classes
|
|
153
|
-
- **New environments:** Implement `Env` interface
|
|
154
|
-
- **New storage:** Implement `Storage` interface
|
|
155
|
-
- **Function approximation:** Replace tabular Q-table with neural networks
|