@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
@@ -0,0 +1,312 @@
1
+ /**
2
+ * Idempotency System
3
+ *
4
+ * Prevents duplicate operations on retries using idempotency keys.
5
+ * Integrates with existing storage abstraction.
6
+ */
7
+
8
+ import { createStorage } from '../index.js';
9
+ import { v4 as uuidv4 } from 'uuid';
10
+
11
+ /**
12
+ * Idempotency record
13
+ * @typedef {Object} IdempotencyRecord
14
+ * @property {string} key - Idempotency key
15
+ * @property {string} operation - Operation identifier
16
+ * @property {Object} request - Request payload (hashed)
17
+ * @property {Object} response - Response payload
18
+ * @property {string} status - 'pending' | 'completed' | 'failed'
19
+ * @property {Date} createdAt
20
+ * @property {Date} completedAt
21
+ * @property {Date} expiresAt
22
+ */
23
+
24
+ /**
25
+ * Idempotency Store - Manages idempotency keys
26
+ */
27
+ export class IdempotencyStore {
28
+ constructor(storage, options = {}) {
29
+ this.storage = storage;
30
+ this.ttl = options.ttl || 86400000; // 24 hours default
31
+ this.initialized = false;
32
+ }
33
+
34
+ /**
35
+ * Check if storage is SQL-based
36
+ */
37
+ isSqlStorage() {
38
+ return this.storage.db && typeof this.storage.db.all === 'function';
39
+ }
40
+
41
+ /**
42
+ * Initialize the idempotency table
43
+ */
44
+ async initialize() {
45
+ if (this.initialized) return;
46
+
47
+ if (this.isSqlStorage()) {
48
+ await this.storage.db.exec(`
49
+ CREATE TABLE IF NOT EXISTS idempotency_keys (
50
+ key TEXT PRIMARY KEY,
51
+ operation TEXT NOT NULL,
52
+ request_hash TEXT NOT NULL,
53
+ request_data TEXT,
54
+ response_data TEXT,
55
+ status TEXT NOT NULL DEFAULT 'pending',
56
+ created_at TEXT NOT NULL,
57
+ completed_at TEXT,
58
+ expires_at TEXT NOT NULL
59
+ )
60
+ `);
61
+
62
+ await this.storage.db.exec(`
63
+ CREATE INDEX IF NOT EXISTS idx_idempotency_expires ON idempotency_keys(expires_at)
64
+ `);
65
+
66
+ await this.storage.db.exec(`
67
+ CREATE INDEX IF NOT EXISTS idx_idempotency_operation ON idempotency_keys(operation)
68
+ `);
69
+ }
70
+
71
+ this.initialized = true;
72
+ }
73
+
74
+ /**
75
+ * Generate a new idempotency key
76
+ */
77
+ generateKey(prefix = 'idem') {
78
+ return `${prefix}_${uuidv4()}`;
79
+ }
80
+
81
+ /**
82
+ * Create hash of request for deduplication
83
+ */
84
+ hashRequest(request) {
85
+ const crypto = require('crypto');
86
+ return crypto.createHash('sha256').update(JSON.stringify(request)).digest('hex');
87
+ }
88
+
89
+ /**
90
+ * Check if key exists and return cached response
91
+ */
92
+ async check(key) {
93
+ await this.initialize();
94
+
95
+ if (this.isSqlStorage()) {
96
+ const row = await this.storage.db.get(
97
+ 'SELECT * FROM idempotency_keys WHERE key = ? AND expires_at > ?',
98
+ [key, new Date().toISOString()]
99
+ );
100
+
101
+ if (row) {
102
+ return {
103
+ key: row.key,
104
+ operation: row.operation,
105
+ requestHash: row.request_hash,
106
+ request: row.request_data ? JSON.parse(row.request_data) : null,
107
+ response: row.response_data ? JSON.parse(row.response_data) : null,
108
+ status: row.status,
109
+ createdAt: row.created_at,
110
+ completedAt: row.completed_at,
111
+ expiresAt: row.expires_at,
112
+ };
113
+ }
114
+ } else {
115
+ // Memory fallback
116
+ if (!this.memoryKeys) this.memoryKeys = new Map();
117
+ const record = this.memoryKeys.get(key);
118
+ if (record && new Date(record.expiresAt) > new Date()) {
119
+ return record;
120
+ }
121
+ }
122
+
123
+ return null;
124
+ }
125
+
126
+ /**
127
+ * Reserve an idempotency key (mark as pending)
128
+ */
129
+ async reserve(key, operation, request) {
130
+ await this.initialize();
131
+
132
+ const requestHash = this.hashRequest(request);
133
+ const createdAt = new Date().toISOString();
134
+ const expiresAt = new Date(Date.now() + this.ttl).toISOString();
135
+
136
+ const record = {
137
+ key,
138
+ operation,
139
+ requestHash,
140
+ requestData: JSON.stringify(request),
141
+ responseData: null,
142
+ status: 'pending',
143
+ createdAt,
144
+ completedAt: null,
145
+ expiresAt,
146
+ };
147
+
148
+ if (this.isSqlStorage()) {
149
+ await this.storage.db.run(
150
+ `INSERT INTO idempotency_keys (key, operation, request_hash, request_data, response_data, status, created_at, completed_at, expires_at)
151
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
152
+ [key, operation, requestHash, record.requestData, null, 'pending', createdAt, null, expiresAt]
153
+ );
154
+ } else {
155
+ if (!this.memoryKeys) this.memoryKeys = new Map();
156
+ this.memoryKeys.set(key, record);
157
+ }
158
+
159
+ return record;
160
+ }
161
+
162
+ /**
163
+ * Complete an idempotency key with response
164
+ */
165
+ async complete(key, response) {
166
+ await this.initialize();
167
+
168
+ const completedAt = new Date().toISOString();
169
+ const responseData = JSON.stringify(response);
170
+
171
+ if (this.isSqlStorage()) {
172
+ await this.storage.db.run(
173
+ `UPDATE idempotency_keys SET status = ?, response_data = ?, completed_at = ? WHERE key = ?`,
174
+ ['completed', responseData, completedAt, key]
175
+ );
176
+ } else {
177
+ const record = this.memoryKeys?.get(key);
178
+ if (record) {
179
+ record.status = 'completed';
180
+ record.responseData = responseData;
181
+ record.completedAt = completedAt;
182
+ this.memoryKeys.set(key, record);
183
+ }
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Mark key as failed
189
+ */
190
+ async fail(key, error) {
191
+ await this.initialize();
192
+
193
+ const completedAt = new Date().toISOString();
194
+ const errorData = JSON.stringify({ error: error.message || String(error) });
195
+
196
+ if (this.isSqlStorage()) {
197
+ await this.storage.db.run(
198
+ `UPDATE idempotency_keys SET status = ?, response_data = ?, completed_at = ? WHERE key = ?`,
199
+ ['failed', errorData, completedAt, key]
200
+ );
201
+ } else {
202
+ const record = this.memoryKeys?.get(key);
203
+ if (record) {
204
+ record.status = 'failed';
205
+ record.responseData = errorData;
206
+ record.completedAt = completedAt;
207
+ this.memoryKeys.set(key, record);
208
+ }
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Execute operation with idempotency
214
+ */
215
+ async execute(key, operation, request, executor) {
216
+ // Check for existing
217
+ const existing = await this.check(key);
218
+ if (existing) {
219
+ if (existing.status === 'completed') {
220
+ return { ...existing.response, idempotent: true };
221
+ }
222
+ if (existing.status === 'failed') {
223
+ throw new Error(`Previous execution failed: ${existing.response?.error}`);
224
+ }
225
+ if (existing.status === 'pending') {
226
+ throw new Error(`Operation already in progress: ${key}`);
227
+ }
228
+ }
229
+
230
+ // Reserve key
231
+ await this.reserve(key, operation, request);
232
+
233
+ try {
234
+ const response = await executor();
235
+ await this.complete(key, response);
236
+ return { ...response, idempotent: false };
237
+ } catch (error) {
238
+ await this.fail(key, error);
239
+ throw error;
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Execute with auto-generated key
245
+ */
246
+ async executeAuto(operation, request, executor, prefix = 'auto') {
247
+ const key = this.generateKey(prefix);
248
+ return this.execute(key, operation, request, executor);
249
+ }
250
+
251
+ /**
252
+ * Clean up expired keys
253
+ */
254
+ async cleanup() {
255
+ await this.initialize();
256
+
257
+ const now = new Date().toISOString();
258
+
259
+ if (this.isSqlStorage()) {
260
+ await this.storage.db.run(
261
+ 'DELETE FROM idempotency_keys WHERE expires_at < ?',
262
+ [now]
263
+ );
264
+ } else {
265
+ for (const [key, record] of this.memoryKeys?.entries() || []) {
266
+ if (new Date(record.expiresAt) < new Date()) {
267
+ this.memoryKeys.delete(key);
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Get all keys for an operation
275
+ */
276
+ async getByOperation(operation, limit = 100) {
277
+ await this.initialize();
278
+
279
+ if (this.isSqlStorage()) {
280
+ const rows = await this.storage.db.all(
281
+ 'SELECT * FROM idempotency_keys WHERE operation = ? ORDER BY created_at DESC LIMIT ?',
282
+ [operation, limit]
283
+ );
284
+ return rows.map(r => ({
285
+ key: r.key,
286
+ operation: r.operation,
287
+ requestHash: r.request_hash,
288
+ request: r.request_data ? JSON.parse(r.request_data) : null,
289
+ response: r.response_data ? JSON.parse(r.response_data) : null,
290
+ status: r.status,
291
+ createdAt: r.created_at,
292
+ completedAt: r.completed_at,
293
+ expiresAt: r.expires_at,
294
+ }));
295
+ }
296
+
297
+ return [];
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Create idempotency store from config
303
+ */
304
+ export async function createIdempotencyStore(config = {}) {
305
+ const storage = await createStorage(
306
+ config.type || 'sqlite',
307
+ config.options || {}
308
+ );
309
+ const store = new IdempotencyStore(storage, { ttl: config.ttl });
310
+ await store.initialize();
311
+ return store;
312
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Execution System
3
+ *
4
+ * Exports all execution components:
5
+ * - ExecutionEngine: Durable workflow execution with checkpointing
6
+ * - IdempotencyStore: Prevents duplicate operations
7
+ * - Budget: Token counting, cost tracking, circuit breaker
8
+ * - HITL: Human-in-the-Loop approval system
9
+ */
10
+
11
+ export { ExecutionEngine, createExecutionEngine } from './engine.js';
12
+ export { IdempotencyStore, createIdempotencyStore } from './idempotency.js';
13
+ export * from './budget/index.js';
14
+ export * from './hitl/index.js';
package/src/index.js CHANGED
@@ -2,21 +2,97 @@
2
2
  * LSJI - Main Entry Point
3
3
  *
4
4
  * Public API exports for the reinforcement learning framework.
5
+ * Now includes production-grade LLM agent framework with:
6
+ * - HITL (Human-in-the-Loop) approval gates
7
+ * - Durability with checkpointing and recovery
8
+ * - Idempotency for duplicate prevention
9
+ * - Budget controls with circuit breaker
10
+ * - Runtime server with WebSocket control panel
11
+ * - Tool plugin system
5
12
  */
6
13
 
7
- // Core
14
+ // Core RL (existing)
8
15
  export { Env, StateEncoder } from './core/env.js';
9
16
  export { QLearning } from './core/qlearning.js';
10
17
  export { Agent } from './core/agent.js';
11
18
 
12
- // Storage
19
+ // Storage (existing)
13
20
  export { Storage, createStorage } from './storage/index.js';
14
21
  export { SqliteStorage } from './storage/sqlite.js';
15
22
  export { BetterSqliteStorage } from './storage/better-sqlite.js';
16
23
  export { MemoryStorage } from './storage/memory.js';
17
24
 
18
- // Environments
25
+ // Environments (existing)
19
26
  export { RockPaperScissorsEnv, TrainingPattern, getTrainingAction } from './envs/rps.js';
20
27
 
28
+ // Execution System (NEW)
29
+ export {
30
+ ExecutionEngine,
31
+ createExecutionEngine,
32
+ IdempotencyStore,
33
+ createIdempotencyStore
34
+ } from './execution/index.js';
35
+
36
+ // Budget Control (NEW)
37
+ export {
38
+ TokenCounter,
39
+ globalTokenCounter,
40
+ CostTracker,
41
+ globalCostTracker,
42
+ CircuitBreaker,
43
+ CircuitState,
44
+ globalCircuitBreaker,
45
+ createBudgetController
46
+ } from './execution/budget/index.js';
47
+
48
+ // HITL (NEW)
49
+ export {
50
+ ApprovalStore,
51
+ createApprovalStore,
52
+ Notifier,
53
+ NotificationChannel,
54
+ createNotifier,
55
+ ApprovalGate,
56
+ createApprovalGate
57
+ } from './execution/hitl/index.js';
58
+
59
+ // LLM Agent System (NEW)
60
+ export {
61
+ LLMAgent,
62
+ createLLMAgent,
63
+ LLMProvider,
64
+ createProvider,
65
+ OpenAIProvider,
66
+ AnthropicProvider,
67
+ LocalProvider,
68
+ ToolRegistry,
69
+ createToolRegistry,
70
+ ConversationMemory,
71
+ createConversationMemory,
72
+ SemanticMemory,
73
+ createSemanticMemory,
74
+ EpisodicMemory,
75
+ createEpisodicMemory,
76
+ PromptManager,
77
+ createPromptManager,
78
+ BUILTIN_PROMPTS
79
+ } from './llm/index.js';
80
+
81
+ // Runtime Server (NEW)
82
+ export {
83
+ createApp,
84
+ startServer,
85
+ stopServer,
86
+ activeRuns
87
+ } from './server/index.js';
88
+
89
+ // Tool Plugin System (NEW)
90
+ export {
91
+ loadPlugins,
92
+ createPluginTemplate,
93
+ PluginRegistry,
94
+ globalPluginRegistry
95
+ } from './llm/plugins/index.js';
96
+
21
97
  // Version
22
- export const VERSION = '0.1.0';
98
+ export const VERSION = '0.3.0';
@@ -0,0 +1,21 @@
1
+ /**
2
+ * LLM Agent System
3
+ *
4
+ * Exports all LLM agent components:
5
+ * - LLMAgent: Main agent class
6
+ * - Providers: OpenAI, Anthropic, Local
7
+ * - Tools: Registry and built-in tools
8
+ * - Memory: Conversation, Semantic, Episodic
9
+ * - PromptManager: Template management
10
+ */
11
+
12
+ export { LLMAgent, createLLMAgent } from './llm-agent.js';
13
+ export { LLMProvider, createProvider } from './providers/base.js';
14
+ export { OpenAIProvider } from './providers/openai.js';
15
+ export { AnthropicProvider } from './providers/anthropic.js';
16
+ export { LocalProvider } from './providers/local.js';
17
+ export { ToolRegistry, createToolRegistry } from './tools/registry.js';
18
+ export { ConversationMemory, createConversationMemory } from './memory/conversation.js';
19
+ export { SemanticMemory, createSemanticMemory } from './memory/semantic.js';
20
+ export { EpisodicMemory, createEpisodicMemory } from './memory/episodic.js';
21
+ export { PromptManager, createPromptManager, BUILTIN_PROMPTS } from './prompt-manager.js';