@axiom-lattice/core 2.1.75 → 2.1.77

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 CHANGED
@@ -1,149 +1,390 @@
1
- # Lattice Core
1
+ # @axiom-lattice/core
2
2
 
3
- 这是 Lattice 项目的核心库,提供了基于 LangGraph 的智能代理构建基础设施。
3
+ Core agent framework providing lattice managers for models, tools, agents, memory, stores, and more.
4
4
 
5
- ## 功能特点
5
+ ## Store Configuration
6
6
 
7
- - 模型格子管理 (ModelLatticeManager)
8
- - 工具格子管理 (ToolLatticeManager)
9
- - 代理格子管理 (AgentLatticeManager)
10
- - 记忆格子管理 (MemoryLatticeManager)
11
- - 流式缓冲管理 (ChunkBufferLatticeManager)
12
- - DeepAgent 实现
13
- - 工具类和辅助函数
7
+ ### `configureStores`
14
8
 
15
- ## 安装
9
+ Unified store registration that replaces the manual `new → initialize → remove → register` boilerplate.
10
+ Handles `StoreLatticeManager`, `ScheduleLatticeManager`, and `MemoryLatticeManager` through a single call.
16
11
 
17
- ```bash
18
- pnpm install
12
+ ```typescript
13
+ import { configureStores } from "@axiom-lattice/core";
14
+ import { createPgStoreConfig } from "@axiom-lattice/pg-stores";
15
+ import { PostgresSaver } from "@langchain/langgraph-checkpoint-postgres";
16
+
17
+ // One connection string creates all PG store instances
18
+ const stores = createPgStoreConfig(process.env.DATABASE_URL);
19
+
20
+ // One call registers all stores across three managers
21
+ await configureStores({
22
+ ...stores,
23
+ checkpoint: PostgresSaver.fromConnString(process.env.DATABASE_URL),
24
+ }, { autoDisposeStores: true });
19
25
  ```
20
26
 
21
- ## 构建
27
+ ### Supported store keys
28
+
29
+ | Key | Registered via |
30
+ |-----|---------------|
31
+ | `thread`, `assistant`, `workspace`, `project`, `database`, `metrics`, `mcp`, `user`, `tenant`, `userTenantLink`, `threadMessageQueue`, `workflowTracking`, `eval`, `channelInstallation`, `channelBinding`, `skill` | `StoreLatticeManager` |
32
+ | `schedule` | `ScheduleLatticeManager` (auto-configured as POSTGRES) |
33
+ | `checkpoint` | `MemoryLatticeManager` (accepts any CheckpointSaver) |
34
+
35
+ ### Behavior
36
+
37
+ For each store entry:
38
+
39
+ 1. Calls `store.initialize()` if the method exists (skipped if it requires arguments)
40
+ 2. Removes any existing `"default"` registration
41
+ 3. Registers the new store under `"default"`
42
+ 4. Tracks `store.dispose` for cleanup
22
43
 
23
- ```bash
24
- pnpm build
44
+ ### Options
45
+
46
+ ```typescript
47
+ interface ConfigureStoresOptions {
48
+ autoDisposeStores?: boolean; // Register SIGINT/SIGTERM handlers for cleanup
49
+ customStores?: Record<string, object>; // Register custom store types
50
+ }
25
51
  ```
26
52
 
27
- ## 测试
53
+ Returns a `dispose` function for manual cleanup regardless of `autoDisposeStores`.
54
+
55
+ ### Custom stores
56
+
57
+ Register custom store implementations that follow the same `initialize()` / `dispose()` pattern:
28
58
 
29
- ```bash
30
- pnpm test
59
+ ```ts
60
+ interface MyCustomStore {
61
+ getData(): Promise<string>;
62
+ }
63
+
64
+ class PostgreSQLMyCustomStore implements MyCustomStore {
65
+ async initialize() { /* run migrations */ }
66
+ async dispose() { /* close pool */ }
67
+ async getData() { return "from pg"; }
68
+ }
31
69
  ```
32
70
 
33
- ## 使用示例
71
+ ```ts
72
+ await configureStores({
73
+ thread: new PostgreSQLThreadStore(opts),
74
+ }, {
75
+ customStores: {
76
+ myCustom: new PostgreSQLMyCustomStore({ poolConfig }),
77
+ },
78
+ });
34
79
 
35
- ### Model Lattice
80
+ // Consume via StoreLatticeManager
81
+ const { store } = getStoreLattice("default", "myCustom");
82
+ const customStore = store as MyCustomStore;
83
+ ```
84
+
85
+ ### Consumer pattern
86
+
87
+ Components that need stores read them directly from `StoreLatticeManager` — no `setConfigStore` required:
36
88
 
37
89
  ```typescript
38
- import { ModelLatticeManager } from "@axiom-lattice/core";
90
+ // SqlDatabaseManager reads database configs lazily from the store lattice
91
+ const db = await sqlDatabaseManager.getDatabase(tenantId, "main-db");
39
92
 
40
- // 创建模型格子管理器
41
- const modelLattice = ModelLatticeManager.getInstance();
93
+ // MetricsServerManager loads configs on first access
94
+ const client = await metricsServerManager.getClient(tenantId, "prometheus");
95
+ const servers = await metricsServerManager.getServerKeys(tenantId);
42
96
  ```
43
97
 
44
- ### ChunkBuffer - Streaming Chunk Management
98
+ ---
99
+
100
+ ## Other Lattice Managers
101
+
102
+ ### Model Lattice
45
103
 
46
104
  ```typescript
47
- import {
48
- InMemoryChunkBuffer,
49
- registerChunkBuffer
50
- } from "@axiom-lattice/core";
51
-
52
- // Create buffer with 30-minute TTL and optional periodic cleanup
53
- const buffer = new InMemoryChunkBuffer({
54
- ttl: 30 * 60 * 1000, // 30 minutes
55
- cleanupInterval: 5 * 60 * 1000 // Clean every 5 minutes (optional)
105
+ import { registerModelLattice } from "@axiom-lattice/core";
106
+
107
+ registerModelLattice("default", {
108
+ model: "gpt-4o",
109
+ provider: "openai",
110
+ streaming: true,
111
+ apiKeyEnvName: "OPENAI_API_KEY",
56
112
  });
113
+ ```
114
+
115
+ ### ChunkBuffer
57
116
 
58
- // Register in Lattice system
59
- registerChunkBuffer('default', buffer);
117
+ ```typescript
118
+ import { InMemoryChunkBuffer, registerChunkBuffer } from "@axiom-lattice/core";
119
+
120
+ const buffer = new InMemoryChunkBuffer({ ttl: 30 * 60 * 1000 });
121
+ registerChunkBuffer("default", buffer);
122
+ ```
60
123
 
61
- // Add chunks as they arrive
62
- await buffer.addChunk('thread-123', 'msg-1', 'Hello ');
63
- await buffer.addChunk('thread-123', 'msg-1', 'world!');
64
- await buffer.addChunk('thread-123', 'msg-2', ' How are you?');
124
+ ## Directory Structure
65
125
 
66
- // Get accumulated content
67
- const content = await buffer.getAccumulatedContent('thread-123');
68
- // => "Hello world! How are you?"
126
+ - `src/store_lattice/` Store lattice manager + configureStores
127
+ - `src/model_lattice/` LLM provider abstractions
128
+ - `src/tool_lattice/` Tool implementations (SQL, metrics, etc.)
129
+ - `src/agent_lattice/` — Agent definitions and builders
130
+ - `src/memory_lattice/` — Context/memory management
131
+ - `src/schedule_lattice/` — Scheduled task management
132
+ - `src/sandbox_lattice/` — Code execution sandbox providers
69
133
 
70
- // Check thread status
71
- const isActive = await buffer.isThreadActive('thread-123'); // true
134
+ ---
72
135
 
73
- // Explicitly complete the thread
74
- await buffer.completeThread('thread-123');
136
+ ## Custom Middleware Registry
75
137
 
76
- // Get buffer statistics
77
- const stats = buffer.getStats();
78
- console.log(stats);
138
+ Applications can write their own middleware and register it at runtime without modifying core source code. The registered middleware can then be enabled via agent config stored in the database.
79
139
 
80
- // Cleanup
81
- buffer.dispose();
140
+ ### Quick start
141
+
142
+ ```typescript
143
+ import { createMiddleware, CustomMiddlewareRegistry } from "@axiom-lattice/core";
144
+
145
+ CustomMiddlewareRegistry.register("my-logger", (config) =>
146
+ createMiddleware({
147
+ name: "MyLogger",
148
+ wrapModelCall: async (request, handler) => {
149
+ console.log(`[${config.logLevel}] Model call`);
150
+ return handler(request);
151
+ },
152
+ })
153
+ );
82
154
  ```
83
155
 
84
- ## 目录结构
156
+ ### Database config format
157
+
158
+ In your agent's `graphDefinition.middleware` array, add a `"custom"` type entry:
159
+
160
+ ```json
161
+ {
162
+ "id": "mw-001",
163
+ "type": "custom",
164
+ "name": "Audit Logger",
165
+ "description": "Logs model calls for audit",
166
+ "enabled": true,
167
+ "config": {
168
+ "key": "my-logger",
169
+ "logLevel": "debug"
170
+ }
171
+ }
172
+ ```
173
+
174
+ The `config.key` must match the key passed to `register()`. All other config fields are forwarded to your factory function.
85
175
 
86
- - `src/base/`: 基础类和接口
87
- - `src/model_lattice/`: 模型格子相关实现
88
- - `src/tool_lattice/`: 工具格子相关实现
89
- - `src/agent_lattice/`: 代理格子相关实现
90
- - `src/memory_lattice/`: 记忆格子相关实现
91
- - `src/chunk_buffer_lattice/`: 流式缓冲管理实现
92
- - `src/deep_agent/`: DeepAgent 实现
93
- - `src/util/`: 工具类和辅助函数
176
+ ### Full example — permission-check middleware
177
+
178
+ ```typescript
179
+ import { createMiddleware, CustomMiddlewareRegistry } from "@axiom-lattice/core";
180
+
181
+ CustomMiddlewareRegistry.register("permission-check", (config) =>
182
+ createMiddleware({
183
+ name: "PermissionCheck",
184
+ wrapToolCall: async (request, handler) => {
185
+ const toolName = request.toolCall?.name;
186
+ if (toolName && !config.allowedTools.includes(toolName)) {
187
+ return { content: `Tool "${toolName}" is not permitted.` };
188
+ }
189
+ return handler(request);
190
+ },
191
+ })
192
+ );
193
+ ```
194
+
195
+ Database config:
196
+
197
+ ```json
198
+ {
199
+ "id": "perm-001",
200
+ "type": "custom",
201
+ "name": "Tool Permissions",
202
+ "description": "Restrict tool access",
203
+ "enabled": true,
204
+ "config": {
205
+ "key": "permission-check",
206
+ "allowedTools": ["read_file", "write_file"]
207
+ }
208
+ }
209
+ ```
94
210
 
95
- ## ChunkBuffer 详细说明
211
+ ### API reference
96
212
 
97
- ChunkBuffer 模块提供了一个高效的流式数据缓冲解决方案,用于管理按线程(thread)组织的消息块(chunk)。
213
+ | Method | Signature | Description |
214
+ |--------|-----------|-------------|
215
+ | `register` | `(key: string, factory: (config: Record<string, any>) => AgentMiddleware \| Promise<AgentMiddleware>) => void` | Register a factory by key |
216
+ | `unregister` | `(key: string) => boolean` | Remove a registration |
217
+ | `get` | `(key: string) => AgentMiddlewareFactory \| undefined` | Look up a factory |
218
+ | `has` | `(key: string) => boolean` | Check if key is registered |
219
+ | `list` | `() => string[]` | Get all registered keys |
98
220
 
99
- ### 核心特性
221
+ ### Important
100
222
 
101
- 1. **Thread-based Organization**: 每个 thread 维护独立的 chunk 缓冲区
102
- 2. **Sequential Chunk Storage**: Chunks 按到达顺序存储,无需唯一 ID
103
- 3. **Explicit Status Management**: Thread 状态通过显式调用管理(active/completed/aborted)
104
- 4. **Hybrid Cleanup Strategy**:
105
- - 懒清理(Lazy Cleanup): 访问时自动清除过期 thread
106
- - 可选周期清理: 后台定时器定期清理过期 thread
107
- 5. **TTL Auto-Extension**: 有新 chunk 加入时自动延长 TTL
223
+ - Register factories **before** building agents (typically at app startup)
224
+ - Duplicate keys overwrite previous registrations
225
+ - Unregistered keys are skipped with a console warning at build time
226
+ - Factory functions receive the raw `config` object (minus `key`) — validate it yourself
108
227
 
109
- ### API Overview
228
+ ---
229
+
230
+ ## External Channel Integration
231
+
232
+ core provides the storage infrastructure and binding system that external channel adapters (Lark, Slack, Email, etc.) depend on. The actual HTTP routes and message dispatch live in `packages/gateway`, but all persistence and sender resolution are handled here.
233
+
234
+ ### What core provides
235
+
236
+ | Component | Location | Purpose |
237
+ |-----------|----------|---------|
238
+ | `InMemoryChannelInstallationStore` | `store_lattice/` | In-memory `ChannelInstallationStore` implementation |
239
+ | `InMemoryBindingStore` | `store_lattice/` | In-memory `BindingRegistry` implementation |
240
+ | `BindingRegistryHolder` | `bindings_lattice/` | Global accessor for the active `BindingRegistry` |
241
+ | `manage_binding` tool | `tool_lattice/manage_binding/` | Agent tool for CRUD on sender-to-agent bindings |
242
+
243
+ ### Architecture overview
244
+
245
+ ```
246
+ External Platform (Lark, Slack, Email)
247
+ → Gateway HTTP route (packages/gateway)
248
+ → ChannelAdapter.receive(rawPayload) → InboundMessage
249
+ → MessageRouter.dispatch() (packages/gateway)
250
+ → BindingRegistry.resolve() ←── core
251
+ → Thread creation / reuse ←── core
252
+ → Agent.addMessage() ←── core
253
+ → ChannelAdapter.sendReply() (packages/gateway)
254
+ ```
255
+
256
+ Agents remain **channel-agnostic** — they only see standard thread/message execution.
257
+
258
+ ### Setup for custom channel development
259
+
260
+ #### 1. Register channel stores
110
261
 
111
262
  ```typescript
112
- // Core operations
113
- addChunk(threadId, messageId, content) // Add chunk to thread
114
- getChunks(threadId) // Get all chunks for a thread
115
- getAccumulatedContent(threadId) // Get concatenated content
116
- getChunksByMessageId(threadId, messageId) // Get chunks for specific message
263
+ import { configureStores } from "@axiom-lattice/core";
264
+ import { PostgreSQLChannelInstallationStore, PostgreSQLBindingStore } from "@axiom-lattice/pg-stores";
117
265
 
118
- // Thread status management
119
- completeThread(threadId) // Mark thread as completed
120
- abortThread(threadId) // Mark thread as aborted
121
- isThreadActive(threadId) // Check if thread is active
122
- getThreadStatus(threadId) // Get thread status
266
+ await configureStores({
267
+ channelInstallation: new PostgreSQLChannelInstallationStore({ pool }),
268
+ channelBinding: new PostgreSQLBindingStore({ pool }),
269
+ // ... other stores
270
+ });
271
+ ```
123
272
 
124
- // Thread lifecycle
125
- clearThread(threadId) // Remove specific thread
126
- cleanupExpiredThreads() // Manual cleanup of expired threads
127
- extendThreadTTL(threadId, additionalMs) // Extend thread expiration time
273
+ #### 2. Set the global BindingRegistry
274
+
275
+ ```typescript
276
+ import { setBindingRegistry } from "@axiom-lattice/core";
128
277
 
129
- // Query operations
130
- getActiveThreads() // Get all active thread IDs
131
- getAllThreads() // Get all thread IDs
132
- getStats() // Get buffer statistics
278
+ const bindingStore = getStoreLattice("default", "channelBinding").store;
279
+ setBindingRegistry(bindingStore);
133
280
  ```
134
281
 
135
- ### Configuration Options
282
+ #### 3. Implement ChannelAdapter (in gateway)
136
283
 
137
284
  ```typescript
138
- interface ThreadBufferConfig {
139
- ttl?: number; // Time-to-live in ms (default: 1 hour)
140
- cleanupInterval?: number; // Optional periodic cleanup interval in ms
285
+ import type { ChannelAdapter, InboundMessage, OutboundMessage, ReplyTarget } from "@axiom-lattice/protocols";
286
+ import { z } from "zod";
287
+
288
+ const slackConfigSchema = z.object({
289
+ botToken: z.string(),
290
+ signingSecret: z.string(),
291
+ });
292
+
293
+ export const slackAdapter: ChannelAdapter = {
294
+ channel: "slack",
295
+ configSchema: slackConfigSchema,
296
+
297
+ async receive(rawPayload, installation): Promise<InboundMessage | null> {
298
+ // 1. Parse Slack-specific event
299
+ // 2. Return normalized InboundMessage or null to ignore
300
+ return {
301
+ channel: "slack",
302
+ channelInstallationId: installation.id,
303
+ tenantId: installation.tenantId,
304
+ sender: { id: userId, displayName: userName },
305
+ content: { text: messageText },
306
+ replyTarget: {
307
+ adapterChannel: "slack",
308
+ channelInstallationId: installation.id,
309
+ rawTarget: { channelId, threadTs },
310
+ },
311
+ };
312
+ },
313
+
314
+ async sendReply(replyTarget, message, installation): Promise<void> {
315
+ // Use Slack API to send reply
316
+ // replyTarget.rawTarget contains channel-specific context
317
+ },
318
+ };
319
+ ```
320
+
321
+ #### 4. Register routes and adapter (in gateway)
322
+
323
+ ```typescript
324
+ // packages/gateway/src/channels/registry.ts
325
+ adapterRegistry.register(slackAdapter);
326
+
327
+ // packages/gateway/src/channels/routes.ts
328
+ app.post("/api/channels/slack/installations/:id/events", async (req, reply) => {
329
+ const installation = await installationStore.getInstallationById(req.params.id);
330
+ const message = await slackAdapter.receive(req.body, installation);
331
+ if (message) await messageRouter.dispatch(message);
332
+ });
333
+ ```
334
+
335
+ ### Binding resolution flow
336
+
337
+ When `MessageRouter.dispatch()` receives an `InboundMessage`:
338
+
339
+ 1. Calls `BindingRegistry.resolve({ channel, senderId, channelInstallationId, tenantId })`
340
+ 2. If **no binding found**:
341
+ - If `installation.rejectWhenNoBinding` → throw `BindingNotFoundError`
342
+ - If `installation.fallbackAgentId` → create temporary fallback binding
343
+ 3. If **binding disabled** → reject
344
+ 4. **Thread resolution** (based on `binding.threadMode`):
345
+ - `"fixed"` → reuse `binding.threadId`
346
+ - `"per_conversation"` → always create new thread
347
+ 5. Execute agent via `agent.addMessage()`
348
+
349
+ ### Agent-side binding management
350
+
351
+ Agents can manage bindings dynamically via the `manage_binding` tool:
352
+
353
+ ```typescript
354
+ // Agent can call this tool to:
355
+ // - list_installations: List available channel installations
356
+ // - create: Bind a sender to an agent (channel, senderId, agentId)
357
+ // - update: Change agent or threadMode for a binding
358
+ // - delete: Remove a binding
359
+ // - list: List all bindings with optional filters
360
+ ```
361
+
362
+ ### Key types (from @axiom-lattice/protocols)
363
+
364
+ ```typescript
365
+ interface ChannelAdapter<TConfig = unknown> {
366
+ readonly channel: string;
367
+ readonly configSchema: z.ZodSchema<TConfig>;
368
+ receive(rawPayload: unknown, installation: ChannelInstallation): Promise<InboundMessage | null>;
369
+ sendReply(replyTarget: ReplyTarget, message: OutboundMessage, installation: ChannelInstallation): Promise<void>;
370
+ }
371
+
372
+ interface Binding {
373
+ id: string;
374
+ channel: string;
375
+ channelInstallationId: string;
376
+ tenantId: string;
377
+ senderId: string;
378
+ agentId: string;
379
+ threadMode: "fixed" | "per_conversation";
380
+ enabled: boolean;
141
381
  }
142
382
  ```
143
383
 
144
- ### Use Cases
384
+ ### Reference implementation
145
385
 
146
- - **Streaming AI Responses**: Buffer streaming responses from LLM models
147
- - **Real-time Data Processing**: Collect and organize real-time data streams
148
- - **Message Aggregation**: Aggregate fragmented messages by thread
149
- - **Temporary Cache**: Short-term caching with automatic expiration
386
+ See `packages/gateway/src/channels/lark/` for a complete production channel adapter including:
387
+ - Webhook verification and decryption
388
+ - Event parsing and normalization
389
+ - Thread mapping (`user` / `group` / `hybrid` modes)
390
+ - Reply delivery via official SDK