@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.
Files changed (75) hide show
  1. package/package.json +15 -7
  2. package/src/cli.js +395 -62
  3. package/src/execution/budget/circuit-breaker.js +245 -0
  4. package/src/execution/budget/cost-tracker.js +387 -0
  5. package/src/execution/budget/index.js +63 -0
  6. package/src/execution/budget/token-counter.js +159 -0
  7. package/src/execution/engine.js +428 -0
  8. package/src/execution/hitl/approval-gate.js +210 -0
  9. package/src/execution/hitl/index.js +12 -0
  10. package/src/execution/hitl/notifier.js +151 -0
  11. package/src/execution/hitl/store.js +311 -0
  12. package/src/execution/idempotency.js +312 -0
  13. package/src/execution/index.js +14 -0
  14. package/src/index.js +80 -4
  15. package/src/llm/index.js +21 -0
  16. package/src/llm/llm-agent.js +357 -0
  17. package/src/llm/memory/conversation.js +271 -0
  18. package/src/llm/memory/episodic.js +312 -0
  19. package/src/llm/memory/index.js +12 -0
  20. package/src/llm/memory/semantic.js +324 -0
  21. package/src/llm/plugins/index.js +202 -0
  22. package/src/llm/prompt-manager.js +332 -0
  23. package/src/llm/providers/anthropic.js +250 -0
  24. package/src/llm/providers/base.js +116 -0
  25. package/src/llm/providers/local.js +163 -0
  26. package/src/llm/providers/openai.js +212 -0
  27. package/src/llm/tools/registry.js +342 -0
  28. package/src/server/index.js +416 -0
  29. package/src/server/ui/index.html +16 -0
  30. package/src/server/ui/package.json +19 -0
  31. package/src/server/ui/src/main.jsx +10 -0
  32. package/src/server/ui/src/styles.css +260 -0
  33. package/src/server/ui/vite.config.js +27 -0
  34. package/docs/README.md +0 -43
  35. package/docs/blog/2019-05-28-first-blog-post.mdx +0 -12
  36. package/docs/blog/2019-05-29-long-blog-post.mdx +0 -44
  37. package/docs/blog/2021-08-01-mdx-blog-post.mdx +0 -24
  38. package/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg +0 -0
  39. package/docs/blog/2021-08-26-welcome/index.mdx +0 -29
  40. package/docs/blog/authors.yml +0 -25
  41. package/docs/blog/tags.yml +0 -19
  42. package/docs/docs/api/agent.md +0 -151
  43. package/docs/docs/api/env.md +0 -133
  44. package/docs/docs/api/environments.md +0 -102
  45. package/docs/docs/api/qlearning.md +0 -138
  46. package/docs/docs/api/storage.md +0 -168
  47. package/docs/docs/architecture.md +0 -155
  48. package/docs/docs/cli.md +0 -210
  49. package/docs/docs/contributing.md +0 -162
  50. package/docs/docs/core-concepts.md +0 -152
  51. package/docs/docs/examples/advanced-training.md +0 -244
  52. package/docs/docs/examples/custom-environment.md +0 -198
  53. package/docs/docs/examples/custom-storage.md +0 -251
  54. package/docs/docs/getting-started.md +0 -91
  55. package/docs/docusaurus.config.ts +0 -149
  56. package/docs/package-lock.json +0 -19522
  57. package/docs/package.json +0 -49
  58. package/docs/sidebars.ts +0 -33
  59. package/docs/src/components/HomepageFeatures/index.tsx +0 -71
  60. package/docs/src/components/HomepageFeatures/styles.module.css +0 -11
  61. package/docs/src/css/custom.css +0 -79
  62. package/docs/src/pages/index.module.css +0 -23
  63. package/docs/src/pages/index.tsx +0 -44
  64. package/docs/src/pages/markdown-page.mdx +0 -7
  65. package/docs/static/.nojekyll +0 -0
  66. package/docs/static/img/docusaurus-social-card.jpg +0 -0
  67. package/docs/static/img/docusaurus.png +0 -0
  68. package/docs/static/img/favicon.ico +0 -0
  69. package/docs/static/img/logo.png +0 -0
  70. package/docs/static/img/undraw_docusaurus_mountain.svg +0 -171
  71. package/docs/static/img/undraw_docusaurus_react.svg +0 -170
  72. package/docs/static/img/undraw_docusaurus_tree.svg +0 -40
  73. package/docs/tsconfig.json +0 -12
  74. package/legacy/worker.js +0 -166
  75. package/legacy/wrangler.toml +0 -11
@@ -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
- ```
@@ -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