cube-agent-harness 0.1.0__py3-none-any.whl
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.
- cube/README.md +369 -0
- cube/__init__.py +1 -0
- cube/_optional.py +17 -0
- cube/cluster/README.md +277 -0
- cube/cluster/__init__.py +52 -0
- cube/cluster/_config.py +118 -0
- cube/cluster/_health.py +39 -0
- cube/cluster/_schema.py +51 -0
- cube/cluster/_services.py +25 -0
- cube/cluster/gateway/__init__.py +3 -0
- cube/cluster/gateway/_events.py +58 -0
- cube/cluster/gateway/_gateway.py +365 -0
- cube/cluster/gateway/_pending.py +26 -0
- cube/cluster/mysql/__init__.py +4 -0
- cube/cluster/mysql/_config.py +31 -0
- cube/cluster/mysql/_provider.py +55 -0
- cube/cluster/protocol/__init__.py +16 -0
- cube/cluster/protocol/_codec.py +20 -0
- cube/cluster/protocol/_constants.py +3 -0
- cube/cluster/protocol/_envelope.py +50 -0
- cube/cluster/redis/__init__.py +6 -0
- cube/cluster/redis/_client.py +46 -0
- cube/cluster/redis/_config.py +15 -0
- cube/cluster/redis/_keys.py +44 -0
- cube/cluster/redis/_request_cache.py +31 -0
- cube/cluster/routing/__init__.py +21 -0
- cube/cluster/routing/_directory.py +60 -0
- cube/cluster/routing/_lease.py +129 -0
- cube/cluster/routing/_resolver.py +86 -0
- cube/cluster/routing/_worker_select.py +41 -0
- cube/cluster/worker/__init__.py +3 -0
- cube/cluster/worker/__main__.py +4 -0
- cube/cluster/worker/_drain.py +16 -0
- cube/cluster/worker/_entrypoint.py +19 -0
- cube/cluster/worker/_env.py +98 -0
- cube/cluster/worker/_worker.py +661 -0
- cube/core/README.md +116 -0
- cube/core/__init__.py +168 -0
- cube/core/common/__init__.py +23 -0
- cube/core/common/_cancellation.py +56 -0
- cube/core/common/_event_bus.py +220 -0
- cube/core/common/_event_registry.py +69 -0
- cube/core/common/_id.py +18 -0
- cube/core/common/_status.py +14 -0
- cube/core/common/_time.py +16 -0
- cube/core/hook/README.md +152 -0
- cube/core/hook/__init__.py +136 -0
- cube/core/hook/_core_extensions.py +184 -0
- cube/core/hook/_decorators.py +78 -0
- cube/core/hook/_dispatch.py +96 -0
- cube/core/hook/_extension.py +76 -0
- cube/core/hook/_record.py +260 -0
- cube/core/hook/_reducer.py +138 -0
- cube/core/hook/_registry.py +143 -0
- cube/core/hook/_runtime.py +38 -0
- cube/core/hook/types.py +342 -0
- cube/core/llm/__init__.py +160 -0
- cube/core/llm/_base_client.py +536 -0
- cube/core/llm/_catalog.py +147 -0
- cube/core/llm/_lite_llm_client.py +599 -0
- cube/core/llm/_model.py +362 -0
- cube/core/llm/_router.py +219 -0
- cube/core/llm/_router_client.py +395 -0
- cube/core/llm/_router_compile.py +126 -0
- cube/core/llm/_token.py +55 -0
- cube/core/mcp/__init__.py +27 -0
- cube/core/mcp/_adapter.py +90 -0
- cube/core/mcp/_config.py +117 -0
- cube/core/mcp/_manager.py +319 -0
- cube/core/runtime/README.md +138 -0
- cube/core/runtime/__init__.py +158 -0
- cube/core/runtime/_agent_loop.py +1473 -0
- cube/core/runtime/_agent_runtime.py +1359 -0
- cube/core/runtime/_background_render.py +61 -0
- cube/core/runtime/_compact_contract.py +77 -0
- cube/core/runtime/_events.py +353 -0
- cube/core/runtime/_llm_msg_adapter.py +301 -0
- cube/core/runtime/_message.py +376 -0
- cube/core/runtime/_message_registry.py +87 -0
- cube/core/runtime/_model.py +577 -0
- cube/core/runtime/_shutdown.py +139 -0
- cube/core/tool/__init__.py +173 -0
- cube/core/tool/_base_tool.py +882 -0
- cube/core/tool/_call_executor.py +662 -0
- cube/core/tool/_decorator.py +128 -0
- cube/core/tool/_dynamic.py +201 -0
- cube/core/tool/_file_access.py +173 -0
- cube/core/tool/_file_mutation.py +100 -0
- cube/core/tool/_orchestrator.py +385 -0
- cube/core/tool/_registry.py +192 -0
- cube/core/tool/_result_storage.py +308 -0
- cube/core/tool/_runtime_access.py +142 -0
- cube/core/tool/_tool_runtime.py +1584 -0
- cube/core/tool/_tool_task.py +931 -0
- cube/platform/README.md +163 -0
- cube/platform/__init__.py +77 -0
- cube/platform/agency/__init__.py +67 -0
- cube/platform/agency/_errors.py +2 -0
- cube/platform/agency/_model.py +133 -0
- cube/platform/agency/_service.py +842 -0
- cube/platform/agency/_skill.py +829 -0
- cube/platform/agency/_subagent.py +379 -0
- cube/platform/agent/__init__.py +59 -0
- cube/platform/agent/_agent.py +377 -0
- cube/platform/agent/_option.py +238 -0
- cube/platform/agent/_state.py +28 -0
- cube/platform/agent/_subagent.py +45 -0
- cube/platform/agent/_worker.py +41 -0
- cube/platform/assembly/__init__.py +15 -0
- cube/platform/assembly/_config.py +22 -0
- cube/platform/assembly/_loader.py +49 -0
- cube/platform/assembly/_runtime.py +27 -0
- cube/platform/assembly/_validation.py +74 -0
- cube/platform/channel/__init__.py +13 -0
- cube/platform/channel/_coordinator.py +198 -0
- cube/platform/channel/_errors.py +2 -0
- cube/platform/channel/_model.py +15 -0
- cube/platform/channel/_service.py +197 -0
- cube/platform/channel/_session_gate.py +51 -0
- cube/platform/channel/_state.py +300 -0
- cube/platform/command/__init__.py +37 -0
- cube/platform/command/_executor.py +384 -0
- cube/platform/command/_local.py +29 -0
- cube/platform/command/_model.py +180 -0
- cube/platform/command/_protocol.py +15 -0
- cube/platform/database/README.md +123 -0
- cube/platform/database/__init__.py +58 -0
- cube/platform/database/_config.py +28 -0
- cube/platform/database/_json.py +23 -0
- cube/platform/database/_paths.py +12 -0
- cube/platform/database/_provider.py +21 -0
- cube/platform/database/_providers/__init__.py +3 -0
- cube/platform/database/_providers/_sqlite.py +74 -0
- cube/platform/database/_retry.py +37 -0
- cube/platform/database/_schema.py +364 -0
- cube/platform/database/_service.py +138 -0
- cube/platform/database/_table_config.py +32 -0
- cube/platform/database/_table_init.py +42 -0
- cube/platform/database/_table_loader.py +59 -0
- cube/platform/database/_table_registry.py +132 -0
- cube/platform/database/_table_spec.py +72 -0
- cube/platform/database/_tables.py +22 -0
- cube/platform/database/_types.py +11 -0
- cube/platform/database/repository/__init__.py +47 -0
- cube/platform/database/repository/_agency_user_membership_repository.py +65 -0
- cube/platform/database/repository/_agent_run_state_repository.py +135 -0
- cube/platform/database/repository/_background_tool_task_repository.py +120 -0
- cube/platform/database/repository/_base.py +44 -0
- cube/platform/database/repository/_config_repository.py +43 -0
- cube/platform/database/repository/_llm_call_metric_repository.py +179 -0
- cube/platform/database/repository/_notification_repository.py +95 -0
- cube/platform/database/repository/_session_member_repository.py +106 -0
- cube/platform/database/repository/_session_message_repository.py +168 -0
- cube/platform/database/repository/_session_repository.py +61 -0
- cube/platform/database/table/__init__.py +38 -0
- cube/platform/database/table/_agency_user_membership.py +44 -0
- cube/platform/database/table/_agent_run_state.py +21 -0
- cube/platform/database/table/_background_tool_task.py +63 -0
- cube/platform/database/table/_config.py +23 -0
- cube/platform/database/table/_llm_call_metric.py +93 -0
- cube/platform/database/table/_notification.py +62 -0
- cube/platform/database/table/_session.py +56 -0
- cube/platform/database/table/_session_member.py +33 -0
- cube/platform/database/table/_session_message.py +53 -0
- cube/platform/event/__init__.py +25 -0
- cube/platform/event/_local.py +118 -0
- cube/platform/event/_model.py +18 -0
- cube/platform/event/_protocol.py +36 -0
- cube/platform/event/_snapshot.py +49 -0
- cube/platform/event/_stream.py +94 -0
- cube/platform/host/__init__.py +47 -0
- cube/platform/host/_config.py +38 -0
- cube/platform/host/_health.py +68 -0
- cube/platform/host/_local_app.py +152 -0
- cube/platform/host/_runtime.py +306 -0
- cube/platform/host/_services.py +180 -0
- cube/platform/mcp/__init__.py +15 -0
- cube/platform/mcp/_runtime.py +158 -0
- cube/platform/notification/__init__.py +23 -0
- cube/platform/notification/_events.py +26 -0
- cube/platform/notification/_model.py +68 -0
- cube/platform/notification/_service.py +203 -0
- cube/platform/plugin/README.md +203 -0
- cube/platform/plugin/__init__.py +173 -0
- cube/platform/plugin/_agent.py +50 -0
- cube/platform/plugin/_assembly.py +92 -0
- cube/platform/plugin/_catalog.py +60 -0
- cube/platform/plugin/_channel.py +287 -0
- cube/platform/plugin/_config.py +254 -0
- cube/platform/plugin/_context.py +127 -0
- cube/platform/plugin/_database.py +37 -0
- cube/platform/plugin/_discovery.py +144 -0
- cube/platform/plugin/_errors.py +35 -0
- cube/platform/plugin/_loader.py +30 -0
- cube/platform/plugin/_manager.py +44 -0
- cube/platform/plugin/_materializer.py +166 -0
- cube/platform/plugin/_model.py +169 -0
- cube/platform/plugin/_registration.py +63 -0
- cube/platform/plugin/_registry.py +252 -0
- cube/platform/plugin/_resolver.py +144 -0
- cube/platform/plugin/_selection.py +34 -0
- cube/platform/plugin/_session.py +330 -0
- cube/platform/sandbox/__init__.py +16 -0
- cube/platform/sandbox/_base.py +33 -0
- cube/platform/sandbox/_config.py +93 -0
- cube/platform/sandbox/_srt.py +448 -0
- cube/platform/session/README.md +117 -0
- cube/platform/session/__init__.py +163 -0
- cube/platform/session/_agent_helpers.py +213 -0
- cube/platform/session/_capabilities.py +65 -0
- cube/platform/session/_construction.py +42 -0
- cube/platform/session/_errors.py +35 -0
- cube/platform/session/_file_mounts.py +162 -0
- cube/platform/session/_history.py +30 -0
- cube/platform/session/_manifest/__init__.py +27 -0
- cube/platform/session/_manifest/_registry.py +163 -0
- cube/platform/session/_prompt/__init__.py +109 -0
- cube/platform/session/_prompt/_build.py +68 -0
- cube/platform/session/_prompt/_document.py +118 -0
- cube/platform/session/_prompt/_environment.py +277 -0
- cube/platform/session/_prompt/_errors.py +5 -0
- cube/platform/session/_prompt/_layers.py +672 -0
- cube/platform/session/_prompt/_models.py +74 -0
- cube/platform/session/_prompt/_snapshot.py +269 -0
- cube/platform/session/_prompt/_workspace.py +138 -0
- cube/platform/session/_role.py +50 -0
- cube/platform/session/_runtime/__init__.py +99 -0
- cube/platform/session/_runtime/_async.py +37 -0
- cube/platform/session/_runtime/_background.py +237 -0
- cube/platform/session/_runtime/_background_control.py +71 -0
- cube/platform/session/_runtime/_recovery.py +74 -0
- cube/platform/session/_runtime/_registry.py +638 -0
- cube/platform/session/_runtime/_run_state.py +614 -0
- cube/platform/session/_runtime/_services.py +62 -0
- cube/platform/session/_runtime/_tool_access.py +194 -0
- cube/platform/session/_schema/__init__.py +69 -0
- cube/platform/session/_schema/_events.py +44 -0
- cube/platform/session/_schema/_lifecycle.py +58 -0
- cube/platform/session/_schema/_messages.py +92 -0
- cube/platform/session/_schema/_model.py +176 -0
- cube/platform/session/_schema/_receive.py +71 -0
- cube/platform/session/_schema/_state.py +39 -0
- cube/platform/session/_schema/_workspace.py +30 -0
- cube/platform/session/_service.py +688 -0
- cube/platform/session/_session.py +1264 -0
- cube/platform/session/_transcript/__init__.py +11 -0
- cube/platform/session/_transcript/_codec.py +26 -0
- cube/platform/session/_transcript/_transcript.py +251 -0
- cube/platform/workspace/__init__.py +73 -0
- cube/platform/workspace/_agency_workspace.py +99 -0
- cube/platform/workspace/_agent_workspace.py +82 -0
- cube/platform/workspace/_channel_workspace.py +73 -0
- cube/platform/workspace/_coordination.py +727 -0
- cube/platform/workspace/_file_workspace.py +347 -0
- cube/platform/workspace/_key_file_schema.py +219 -0
- cube/platform/workspace/_key_file_service.py +237 -0
- cube/platform/workspace/_metadata_cache.py +211 -0
- cube/platform/workspace/_vfs.py +110 -0
- cube/platform/workspace/_workspace_service.py +111 -0
- cube/plugins/README.md +56 -0
- cube/plugins/__init__.py +1 -0
- cube/plugins/channels/__init__.py +1 -0
- cube/plugins/channels/chat/__init__.py +14 -0
- cube/plugins/channels/chat/_config.py +14 -0
- cube/plugins/channels/chat/_provider.py +92 -0
- cube/plugins/channels/chat/plugin.py +54 -0
- cube/plugins/channels/project/__init__.py +18 -0
- cube/plugins/channels/project/_config.py +10 -0
- cube/plugins/channels/project/_provider.py +50 -0
- cube/plugins/channels/project/_query_service.py +176 -0
- cube/plugins/channels/project/plugin.py +45 -0
- cube/plugins/hooks/__init__.py +26 -0
- cube/plugins/hooks/compaction/__init__.py +17 -0
- cube/plugins/hooks/compaction/_micro.py +57 -0
- cube/plugins/hooks/compaction/_policy.py +16 -0
- cube/plugins/hooks/compaction/_shared.py +165 -0
- cube/plugins/hooks/compaction/_summary.py +131 -0
- cube/plugins/hooks/internal_tool_policy.py +58 -0
- cube/plugins/hooks/llm_metric_recorder.py +63 -0
- cube/plugins/hooks/normalize_tool_input.py +53 -0
- cube/plugins/hooks/plugin.py +27 -0
- cube/plugins/sessions/__init__.py +3 -0
- cube/plugins/sessions/chat/__init__.py +41 -0
- cube/plugins/sessions/chat/_compaction.py +31 -0
- cube/plugins/sessions/chat/_file_space.py +173 -0
- cube/plugins/sessions/chat/_hooks.py +54 -0
- cube/plugins/sessions/chat/_llm_msg_adapter.py +140 -0
- cube/plugins/sessions/chat/_messages.py +129 -0
- cube/plugins/sessions/chat/_model.py +168 -0
- cube/plugins/sessions/chat/_prompt.py +102 -0
- cube/plugins/sessions/chat/_service.py +151 -0
- cube/plugins/sessions/chat/_session.py +1543 -0
- cube/plugins/sessions/chat/_state.py +15 -0
- cube/plugins/sessions/chat/_workspace.py +54 -0
- cube/plugins/sessions/chat/plugin.py +43 -0
- cube/plugins/sessions/chat/tools/__init__.py +6 -0
- cube/plugins/sessions/chat/tools/send_message.py +309 -0
- cube/plugins/sessions/task/README.md +35 -0
- cube/plugins/sessions/task/__init__.py +228 -0
- cube/plugins/sessions/task/_agent_text.py +18 -0
- cube/plugins/sessions/task/_command_service.py +123 -0
- cube/plugins/sessions/task/_compaction.py +56 -0
- cube/plugins/sessions/task/_event.py +23 -0
- cube/plugins/sessions/task/_executor.py +825 -0
- cube/plugins/sessions/task/_llm_msg_adapter.py +255 -0
- cube/plugins/sessions/task/_messages.py +130 -0
- cube/plugins/sessions/task/_models.py +434 -0
- cube/plugins/sessions/task/_notification_topics.py +15 -0
- cube/plugins/sessions/task/_notifications.py +97 -0
- cube/plugins/sessions/task/_persistence_service.py +48 -0
- cube/plugins/sessions/task/_query_service.py +120 -0
- cube/plugins/sessions/task/_repositories.py +30 -0
- cube/plugins/sessions/task/_state.py +67 -0
- cube/plugins/sessions/task/_tables.py +11 -0
- cube/plugins/sessions/task/_task_file_space.py +114 -0
- cube/plugins/sessions/task/_task_prompt.py +101 -0
- cube/plugins/sessions/task/_task_session.py +2182 -0
- cube/plugins/sessions/task/_task_workspace.py +56 -0
- cube/plugins/sessions/task/_workflow_file_space.py +167 -0
- cube/plugins/sessions/task/_workflow_messages.py +68 -0
- cube/plugins/sessions/task/_workflow_projection.py +305 -0
- cube/plugins/sessions/task/_workflow_prompt.py +98 -0
- cube/plugins/sessions/task/_workflow_session.py +824 -0
- cube/plugins/sessions/task/_workflow_workspace.py +68 -0
- cube/plugins/sessions/task/persistence/__init__.py +17 -0
- cube/plugins/sessions/task/persistence/_task_run.py +96 -0
- cube/plugins/sessions/task/persistence/_task_run_repository.py +118 -0
- cube/plugins/sessions/task/persistence/_task_schedule.py +64 -0
- cube/plugins/sessions/task/persistence/_task_schedule_repository.py +73 -0
- cube/plugins/sessions/task/plugin.py +77 -0
- cube/plugins/sessions/task/tools/__init__.py +48 -0
- cube/plugins/sessions/task/tools/task_cancel.py +143 -0
- cube/plugins/sessions/task/tools/task_create.py +226 -0
- cube/plugins/sessions/task/tools/task_list.py +94 -0
- cube/plugins/sessions/task/tools/task_start.py +139 -0
- cube/plugins/sessions/task/tools/task_status.py +84 -0
- cube/plugins/sessions/task/tools/workflow_run.py +406 -0
- cube/plugins/subagent/__init__.py +44 -0
- cube/plugins/subagent/_assembly.py +121 -0
- cube/plugins/subagent/_compaction.py +34 -0
- cube/plugins/subagent/_definition.py +140 -0
- cube/plugins/subagent/_definition_service.py +94 -0
- cube/plugins/subagent/_file_space.py +106 -0
- cube/plugins/subagent/_models.py +41 -0
- cube/plugins/subagent/_prompt.py +70 -0
- cube/plugins/subagent/_provider.py +40 -0
- cube/plugins/subagent/_session.py +419 -0
- cube/plugins/subagent/_subagent.py +96 -0
- cube/plugins/subagent/_workspace.py +52 -0
- cube/plugins/subagent/plugin.py +48 -0
- cube/plugins/subagent/tools/__init__.py +8 -0
- cube/plugins/subagent/tools/subagent.py +138 -0
- cube/plugins/tools/README.md +178 -0
- cube/plugins/tools/__init__.py +100 -0
- cube/plugins/tools/_media_reader.py +293 -0
- cube/plugins/tools/_text_reader.py +569 -0
- cube/plugins/tools/ask_user.py +243 -0
- cube/plugins/tools/background_tool_cancel.py +84 -0
- cube/plugins/tools/background_tool_status.py +91 -0
- cube/plugins/tools/bash.py +357 -0
- cube/plugins/tools/edit.py +282 -0
- cube/plugins/tools/glob.py +140 -0
- cube/plugins/tools/grep.py +591 -0
- cube/plugins/tools/plugin.py +53 -0
- cube/plugins/tools/read.py +530 -0
- cube/plugins/tools/skill.py +125 -0
- cube/plugins/tools/write.py +196 -0
- cube/py.typed +1 -0
- cube_agent_harness-0.1.0.dist-info/METADATA +331 -0
- cube_agent_harness-0.1.0.dist-info/RECORD +373 -0
- cube_agent_harness-0.1.0.dist-info/WHEEL +4 -0
- cube_agent_harness-0.1.0.dist-info/entry_points.txt +8 -0
- cube_agent_harness-0.1.0.dist-info/licenses/LICENSE +21 -0
cube/README.md
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
# Cube SDK
|
|
2
|
+
|
|
3
|
+
`cube` 是 `cube-agent-harness` 发行包安装后的 Python import 包:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install cube-agent-harness
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
```python
|
|
10
|
+
import cube
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Cube 是一个用于构建可嵌入、可使用工具、可通过插件扩展的 Agent Harness
|
|
14
|
+
Python SDK。它拆成一个轻量 runtime kernel、可选的本地应用宿主、可选的分布式
|
|
15
|
+
runtime,以及官方能力插件。
|
|
16
|
+
|
|
17
|
+
## 包结构
|
|
18
|
+
|
|
19
|
+
| 包 | 适用场景 | 主要入口 |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| `cube.core` | 需要不依赖 platform 的 Agent runtime kernel。 | `AgentRuntime`, `AgentRunRequest`, `ToolRuntime`, `HookRuntime`, LLM/MCP contracts |
|
|
22
|
+
| `cube.platform` | 需要本地可嵌入 Cube 应用,包含 SQLite、sessions、workspaces、plugins 和本地 runtime 激活。 | `CubeLocalApp`, `CubeLocalAppConfig`, `create_cube_local_app`, `PlatformServices`, `PlatformRuntime` |
|
|
23
|
+
| `cube.cluster` | 需要基于 MySQL、Redis 和共享 workspace 的 Backend/Worker 分布式部署。 | `ClusterGateway`, `ClusterGatewayConfig`, `ClusterPlatformServicesConfig`, `ClusterWorker`, `ClusterWorkerConfig` |
|
|
24
|
+
| `cube.plugins` | 需要官方具体 tools、hooks 和 session kinds。 | plugin entry points, bundled tools, chat/task/subagent sessions |
|
|
25
|
+
|
|
26
|
+
独立 CLI 应用通过 `cube-cli` 单独发布,并安装 `cube` 命令。Playground 应用也不属于
|
|
27
|
+
SDK wheel。
|
|
28
|
+
|
|
29
|
+
## 安装配置
|
|
30
|
+
|
|
31
|
+
基础安装保持轻量:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pip install cube-agent-harness
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
按需安装可选能力:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
pip install "cube-agent-harness[llm]" # LiteLLM client/router 支持
|
|
41
|
+
pip install "cube-agent-harness[mcp]" # MCP client 支持
|
|
42
|
+
pip install "cube-agent-harness[platform]" # 本地 platform host 和 SQLite
|
|
43
|
+
pip install "cube-agent-harness[cluster]" # 分布式 runtime:MySQL + Redis
|
|
44
|
+
pip install "cube-agent-harness[all]" # 全部官方 SDK extras
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## 应该使用哪个入口?
|
|
48
|
+
|
|
49
|
+
本地嵌入使用 `cube.platform.CubeLocalApp`:
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from cube.platform import CubeLocalAppConfig, create_cube_local_app
|
|
53
|
+
|
|
54
|
+
app = create_cube_local_app(CubeLocalAppConfig.from_root_dir("./cube-data"))
|
|
55
|
+
|
|
56
|
+
async with app.lifespan():
|
|
57
|
+
services = app.services
|
|
58
|
+
commands = app.commands
|
|
59
|
+
events = app.events
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
分布式 Backend 进程中,显式组合 durable services 和 live Gateway:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from pathlib import Path
|
|
66
|
+
|
|
67
|
+
from cube.cluster import (
|
|
68
|
+
ClusterConfig,
|
|
69
|
+
ClusterGateway,
|
|
70
|
+
ClusterGatewayConfig,
|
|
71
|
+
ClusterPlatformServicesConfig,
|
|
72
|
+
GatewayConfig,
|
|
73
|
+
MySQLConfig,
|
|
74
|
+
RedisConfig,
|
|
75
|
+
create_cluster_platform_services,
|
|
76
|
+
)
|
|
77
|
+
from cube.platform import RuntimeAssemblyConfig
|
|
78
|
+
|
|
79
|
+
assembly = RuntimeAssemblyConfig(rollout_tag="v1")
|
|
80
|
+
services = create_cluster_platform_services(
|
|
81
|
+
ClusterPlatformServicesConfig(
|
|
82
|
+
root_dir=Path("/cube"),
|
|
83
|
+
assembly=assembly,
|
|
84
|
+
mysql=MySQLConfig(
|
|
85
|
+
url="mysql+asyncmy://cube:secret@mysql:3306/cube?charset=utf8mb4",
|
|
86
|
+
),
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
gateway = ClusterGateway(
|
|
90
|
+
config=ClusterGatewayConfig(
|
|
91
|
+
assembly=assembly,
|
|
92
|
+
cluster=ClusterConfig(namespace="cube-prod"),
|
|
93
|
+
redis=RedisConfig(url="redis://:secret@redis:6379/0"),
|
|
94
|
+
gateway=GatewayConfig(),
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
await services.startup()
|
|
99
|
+
await gateway.startup()
|
|
100
|
+
commands = gateway
|
|
101
|
+
events = gateway
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
分布式 Worker 进程使用 `cube.cluster.ClusterWorker`:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
CUBE_ROOT_DIR=/cube \
|
|
108
|
+
CUBE_CLUSTER_NAMESPACE=cube-prod \
|
|
109
|
+
CUBE_ROLLOUT_TAG=v1 \
|
|
110
|
+
CUBE_MYSQL_URL='mysql+asyncmy://cube:secret@mysql:3306/cube?charset=utf8mb4' \
|
|
111
|
+
CUBE_REDIS_URL='redis://:secret@redis:6379/0' \
|
|
112
|
+
CUBE_WORKER_ID=worker-1 \
|
|
113
|
+
python -m cube.cluster.worker
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
只有当你希望自己完整负责 persistence、session 语义、workspace 布局和 UI projection
|
|
117
|
+
时,才直接使用 `cube.core.AgentRuntime`。
|
|
118
|
+
|
|
119
|
+
## 统一应用接入面
|
|
120
|
+
|
|
121
|
+
本地和分布式 composition 都应该向业务代码暴露同一组高层应用接入面:
|
|
122
|
+
|
|
123
|
+
- `services`:durable agency/channel/session/workspace/database services。
|
|
124
|
+
- `commands`:与 transport 无关的 live session command dispatcher。
|
|
125
|
+
- `events`:按 session 订阅的 live event subscriber。
|
|
126
|
+
|
|
127
|
+
这样 application services 可以依赖稳定协议,而不是依赖具体的本地 runtime 或 cluster
|
|
128
|
+
gateway 类。
|
|
129
|
+
|
|
130
|
+
应用后端通常应该在自己的 composition root 中隐藏部署形态选择:
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
from dataclasses import dataclass
|
|
134
|
+
|
|
135
|
+
from cube.platform import PlatformServices
|
|
136
|
+
from cube.platform.command import SessionCommandDispatcher
|
|
137
|
+
from cube.platform.event import SessionEventSubscriber
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@dataclass(frozen=True)
|
|
141
|
+
class CubeComponents:
|
|
142
|
+
services: PlatformServices
|
|
143
|
+
commands: SessionCommandDispatcher
|
|
144
|
+
events: SessionEventSubscriber
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
本地 backend composition 从 `CubeLocalApp` 填充这些字段:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from cube.platform import CubeLocalAppConfig, create_cube_local_app
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
cube_app = create_cube_local_app(
|
|
154
|
+
CubeLocalAppConfig.from_root_dir("./cube-data"),
|
|
155
|
+
)
|
|
156
|
+
cube = CubeComponents(
|
|
157
|
+
services=cube_app.services,
|
|
158
|
+
commands=cube_app.commands,
|
|
159
|
+
events=cube_app.events,
|
|
160
|
+
)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
分布式 backend composition 从 distributed services 和 Gateway 填充同样的字段:
|
|
164
|
+
|
|
165
|
+
```python
|
|
166
|
+
services = create_cluster_platform_services(...)
|
|
167
|
+
gateway = ClusterGateway(...)
|
|
168
|
+
cube = CubeComponents(
|
|
169
|
+
services=services,
|
|
170
|
+
commands=gateway,
|
|
171
|
+
events=gateway,
|
|
172
|
+
)
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Route handlers 和 application services 应该按职责使用这些字段:
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
# Durable 查询和管理不会激活 live sessions。
|
|
179
|
+
channel = await cube.services.channels.get(agency_code, channel_code)
|
|
180
|
+
history = await cube.services.sessions.get_history(address.session_id)
|
|
181
|
+
|
|
182
|
+
# Live commands 通过 transport-neutral command boundary。
|
|
183
|
+
result = await cube.commands.dispatch(command)
|
|
184
|
+
|
|
185
|
+
# Live UI 更新通过 event boundary 订阅。
|
|
186
|
+
async with cube.events.subscribe(address) as stream:
|
|
187
|
+
async for event in stream:
|
|
188
|
+
...
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
分布式模式下,Backend 禁止创建 `PlatformRuntime`,也禁止在同一进程内启动 Worker。
|
|
192
|
+
Worker 是独立的 `ClusterWorker` 进程。
|
|
193
|
+
|
|
194
|
+
## Runtime Assembly
|
|
195
|
+
|
|
196
|
+
Cube 从 runtime-local manifests 加载 plugins 和 tables:
|
|
197
|
+
|
|
198
|
+
```text
|
|
199
|
+
<root_dir>/plugins.toml
|
|
200
|
+
<root_dir>/tables.toml
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
`RuntimeAssembly` 会在注入 `PlatformServices` 前冻结已选择的 plugin registry、table
|
|
204
|
+
registry 和 capability assembly。
|
|
205
|
+
|
|
206
|
+
应用拥有自己的默认 manifests。SDK 不会自动 import 所有已安装 plugins,也不会自动
|
|
207
|
+
import 所有 table modules。
|
|
208
|
+
|
|
209
|
+
## 插件扩展
|
|
210
|
+
|
|
211
|
+
插件通过 `cube.plugins` entry point group 暴露 `PluginSpec`:
|
|
212
|
+
|
|
213
|
+
```python
|
|
214
|
+
from cube.platform.plugin import PluginSpec
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def register(ctx):
|
|
218
|
+
ctx.tools("my_tools", my_tool)
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
plugin = PluginSpec(
|
|
222
|
+
name="my.company.plugin",
|
|
223
|
+
version="0.1.0",
|
|
224
|
+
register=register,
|
|
225
|
+
)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
```toml
|
|
229
|
+
[project.entry-points."cube.plugins"]
|
|
230
|
+
"my.company.plugin" = "my_company.plugin:plugin"
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
插件可以贡献 tools、hooks、hook event contracts 和 session kinds。带持久化能力的
|
|
234
|
+
plugins 需要单独声明 `TableSpec` 对象,并在 `PluginSpec.requires_tables` 中列出
|
|
235
|
+
所需的逻辑 table names。
|
|
236
|
+
|
|
237
|
+
## 本地模式与分布式模式
|
|
238
|
+
|
|
239
|
+
本地模式:
|
|
240
|
+
|
|
241
|
+
- 使用 `cube.platform.CubeLocalApp`
|
|
242
|
+
- 使用 SQLite 和本地 POSIX workspace
|
|
243
|
+
- 在应用进程内激活 sessions
|
|
244
|
+
- 使用进程内 event delivery
|
|
245
|
+
|
|
246
|
+
分布式模式:
|
|
247
|
+
|
|
248
|
+
- Backend 进程使用 `cube.cluster.ClusterGateway`
|
|
249
|
+
- Worker 进程使用 `cube.cluster.ClusterWorker`
|
|
250
|
+
- 使用 MySQL 存储 Cube durable domain data
|
|
251
|
+
- 使用 Redis 承载 worker heartbeat、owner leases、command ACKs 和 live events
|
|
252
|
+
- 要求所有节点可见同一个共享 POSIX workspace
|
|
253
|
+
|
|
254
|
+
分布式模式有意不提供 `CubeApp` facade。本地嵌入仍然使用
|
|
255
|
+
`cube.platform.CubeLocalApp`;分布式 Backend composition 直接连接
|
|
256
|
+
`PlatformServices` 和 `ClusterGateway`。
|
|
257
|
+
|
|
258
|
+
## 本地快捷调试:分布式模式
|
|
259
|
+
|
|
260
|
+
在 Cube 源码仓库中开发 Playground、`cube.cluster`、Worker、tools 或 sessions
|
|
261
|
+
时,推荐使用根目录 `Makefile` 提供的本地多进程模式。它不使用 Docker 包住源码,
|
|
262
|
+
但运行拓扑仍然是完整的分布式模式:
|
|
263
|
+
|
|
264
|
+
```text
|
|
265
|
+
Next.js frontend :3000
|
|
266
|
+
|
|
|
267
|
+
distributed Playground backend :8000
|
|
268
|
+
|
|
|
269
|
+
ClusterGateway -- Redis -- ClusterWorker(s)
|
|
270
|
+
| |
|
|
271
|
+
+------ MySQL ----------+
|
|
272
|
+
+--- shared root_dir ---+
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### macOS 一次性准备
|
|
276
|
+
|
|
277
|
+
本机需要 Git、Make、`uv`、npm,以及 Node.js 20.11 或更高版本;推荐与官方
|
|
278
|
+
Worker 镜像一致使用 Node.js 22。Python 3.12/3.13 和项目 Python 依赖由 `uv`
|
|
279
|
+
根据 workspace 配置准备。
|
|
280
|
+
|
|
281
|
+
把部署配置放在仓库根目录 `.env`。最少需要正确配置:
|
|
282
|
+
|
|
283
|
+
```text
|
|
284
|
+
CUBE_ROOT_DIR
|
|
285
|
+
CUBE_CLUSTER_NAMESPACE
|
|
286
|
+
CUBE_ROLLOUT_TAG
|
|
287
|
+
CUBE_MYSQL_URL
|
|
288
|
+
CUBE_REDIS_URL
|
|
289
|
+
PLAYGROUND_DATABASE_URL
|
|
290
|
+
PLAYGROUND_AUTH_SECRET
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
`CUBE_MYSQL_URL` 使用 Cube runtime 数据库;`PLAYGROUND_DATABASE_URL` 使用
|
|
294
|
+
Playground 自己的 account 数据库。两个 database 需要提前存在并允许对应账号建表。
|
|
295
|
+
本机单套 backend/worker 可以共用一个本地可写的 `CUBE_ROOT_DIR`;跨机器运行时,
|
|
296
|
+
所有 backend/worker 必须看到同一个共享 POSIX/NAS 路径。
|
|
297
|
+
|
|
298
|
+
每位开发者应使用独立的 cluster namespace、worker-id prefix、Cube database、
|
|
299
|
+
Playground database 和本地 root directory。只共享 MySQL/Redis、却各用一份本地
|
|
300
|
+
workspace,会让不同 Worker 看到同一批 sessions 但看到不同文件,不是有效拓扑。
|
|
301
|
+
|
|
302
|
+
### 标准 Make 流程
|
|
303
|
+
|
|
304
|
+
首次拿到代码和 `.env` 后:
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
make doctor
|
|
308
|
+
make local-start
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
`make doctor` 只读检查 macOS host tools、Node.js 版本、root directory 权限,
|
|
312
|
+
以及 MySQL、Redis、Playground database 连通性,不创建表。`make local-start`
|
|
313
|
+
随后会依次:
|
|
314
|
+
|
|
315
|
+
1. 在 `deploy/local/sandbox-tools/` 安装或校验固定版本的 SRT 和 ripgrep;不安装
|
|
316
|
+
全局 npm 包,macOS 也不需要手工安装 Linux 的 `bubblewrap`/`socat`。
|
|
317
|
+
2. 保留已有 `plugins.toml` / `tables.toml`,缺失时写入 Playground defaults。
|
|
318
|
+
3. 创建并校验 Cube MySQL tables。
|
|
319
|
+
4. 启动默认两个 `ClusterWorker`,等待它们在 Redis 发布匹配 namespace/rollout 的
|
|
320
|
+
heartbeat。
|
|
321
|
+
5. 启动 distributed Playground backend 并等待 HTTP readiness。
|
|
322
|
+
6. 启动带热更新的 Next.js frontend 并等待 `http://localhost:3000` 可访问。
|
|
323
|
+
|
|
324
|
+
日常命令:
|
|
325
|
+
|
|
326
|
+
```bash
|
|
327
|
+
make local-status
|
|
328
|
+
make local-logs
|
|
329
|
+
make local-restart # 修改 Worker/cube runtime 代码后使用
|
|
330
|
+
make local-shutdown
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
backend 和 frontend 启用源码热更新;Worker/runtime 代码变更需要
|
|
334
|
+
`make local-restart`。启动任一环节 readiness 失败时,launcher 会输出相关日志、
|
|
335
|
+
停止已记录的本地进程并以失败状态退出。
|
|
336
|
+
|
|
337
|
+
常用覆盖参数:
|
|
338
|
+
|
|
339
|
+
```bash
|
|
340
|
+
make doctor ENV_FILE=config/alice.env
|
|
341
|
+
make local-start ENV_FILE=config/alice.env LOCAL_WORKERS=1
|
|
342
|
+
make local-start LOCAL_CLUSTER_READY_TIMEOUT_SECONDS=120
|
|
343
|
+
make local-prepare LOCAL_OVERWRITE_MANIFESTS=1
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
`make local-prepare` 只用于“初始化但不启动”;它不是 `local-start` 的必需前置。
|
|
347
|
+
`LOCAL_OVERWRITE_MANIFESTS=1` 会有意覆盖已有 runtime manifests,应只在需要恢复
|
|
348
|
+
当前 Playground defaults 时使用。
|
|
349
|
+
|
|
350
|
+
如果目标是验证镜像而不是源码热调试,使用:
|
|
351
|
+
|
|
352
|
+
```bash
|
|
353
|
+
make docker-build
|
|
354
|
+
make docker-up
|
|
355
|
+
make docker-down
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
Docker Compose 会把源码和依赖构建进镜像、把 frontend 导出为静态资源,不挂载
|
|
359
|
+
源码,也不提供本地热更新。完整环境变量、sandbox policy、日志位置和故障处理见
|
|
360
|
+
仓库根目录 `deploy/README.md`。
|
|
361
|
+
|
|
362
|
+
## 更多文档
|
|
363
|
+
|
|
364
|
+
- `cube/core/README.md`:core runtime、tools、hooks、LLM、MCP。
|
|
365
|
+
- `cube/platform/README.md`:local app host、durable services、session command
|
|
366
|
+
和 event protocols。
|
|
367
|
+
- `cube/cluster/README.md`:distributed Backend/Worker runtime。
|
|
368
|
+
- `cube/platform/plugin/README.md`:plugin SPI 和 registration model。
|
|
369
|
+
- `cube/plugins/README.md`:官方 bundled plugin implementations。
|
cube/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Cube package."""
|
cube/_optional.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def import_optional_dependency(module_name: str, *, extra: str, feature: str) -> Any:
|
|
8
|
+
try:
|
|
9
|
+
return importlib.import_module(module_name)
|
|
10
|
+
except ImportError as error:
|
|
11
|
+
raise RuntimeError(
|
|
12
|
+
f"{feature} requires optional dependency {module_name!r}. "
|
|
13
|
+
f"Install Cube with the '{extra}' extra."
|
|
14
|
+
) from error
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
__all__ = ["import_optional_dependency"]
|
cube/cluster/README.md
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
# cube.cluster
|
|
2
|
+
|
|
3
|
+
`cube.cluster` is Cube's distributed runtime SDK surface. It adds the minimum
|
|
4
|
+
coordination layer needed to run Cube sessions across multiple application
|
|
5
|
+
backend and worker processes.
|
|
6
|
+
|
|
7
|
+
The package is intentionally separate from `cube.platform`:
|
|
8
|
+
|
|
9
|
+
- `cube.platform` owns the local app host and durable platform services.
|
|
10
|
+
- `cube.cluster` owns Redis/MySQL-backed distributed command routing, owner
|
|
11
|
+
leases, worker heartbeat, and live event forwarding.
|
|
12
|
+
- `cube.plugins` still owns concrete tools, hooks, and session kinds.
|
|
13
|
+
|
|
14
|
+
Install with the `cluster` extra:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pip install "cube-agent-harness[cluster]"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
In this repository use:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
uv sync --all-packages --all-extras
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Runtime Shape
|
|
27
|
+
|
|
28
|
+
Distributed mode has two process roles.
|
|
29
|
+
|
|
30
|
+
| Role | SDK entry | Owns | Does not own |
|
|
31
|
+
| --- | --- | --- | --- |
|
|
32
|
+
| Backend / Gateway | `PlatformServices` + `cube.cluster.ClusterGateway` | durable services, command dispatch, event subscription | `PlatformRuntime`, live Session execution |
|
|
33
|
+
| Worker | `cube.cluster.ClusterWorker` | `PlatformServices`, `PlatformRuntime`, owner leases, command handling, event forwarding | HTTP routes, accounts, frontend projection |
|
|
34
|
+
|
|
35
|
+
The backend receives application HTTP/WebSocket traffic, performs durable reads,
|
|
36
|
+
and submits live commands through `ClusterGateway`. Workers claim session owner
|
|
37
|
+
leases and execute the actual `PlatformRuntime` work.
|
|
38
|
+
|
|
39
|
+
There is intentionally no distributed `CubeApp` facade. Local embedding still
|
|
40
|
+
uses `cube.platform.CubeLocalApp`; distributed Backend applications compose
|
|
41
|
+
`PlatformServices` and `ClusterGateway` directly.
|
|
42
|
+
|
|
43
|
+
## Public API
|
|
44
|
+
|
|
45
|
+
Top-level exports:
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from cube.cluster import (
|
|
49
|
+
CLUSTER_PROTOCOL_VERSION,
|
|
50
|
+
ClusterConfig,
|
|
51
|
+
ClusterGateway,
|
|
52
|
+
ClusterGatewayConfig,
|
|
53
|
+
ClusterPlatformServicesConfig,
|
|
54
|
+
ClusterWorker,
|
|
55
|
+
ClusterWorkerConfig,
|
|
56
|
+
GatewayConfig,
|
|
57
|
+
MySQLConfig,
|
|
58
|
+
RedisConfig,
|
|
59
|
+
WorkerConfig,
|
|
60
|
+
create_cluster_platform_services,
|
|
61
|
+
init_cluster_schema,
|
|
62
|
+
validate_cluster_schema,
|
|
63
|
+
)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Important config objects:
|
|
67
|
+
|
|
68
|
+
- `ClusterConfig`: shared coordination namespace used in Redis key prefixes and
|
|
69
|
+
envelopes.
|
|
70
|
+
- `ClusterPlatformServicesConfig`: Backend/Worker durable service config for
|
|
71
|
+
MySQL, shared root directory, and runtime assembly.
|
|
72
|
+
- `ClusterGatewayConfig`: Backend-side Gateway config for cluster namespace,
|
|
73
|
+
Redis, rollout tag, request limits, and command timeout.
|
|
74
|
+
- `ClusterWorkerConfig`: worker-side config for `ClusterWorker`.
|
|
75
|
+
- `GatewayConfig`: request timeout, pending request limit, optional gateway id,
|
|
76
|
+
and optional worker active-session capacity filter.
|
|
77
|
+
- `WorkerConfig`: optional worker id, heartbeat interval/TTL, owner lease TTL,
|
|
78
|
+
request ACK TTL, dependency grace, and shutdown grace.
|
|
79
|
+
- `MySQLConfig`: SQLAlchemy async MySQL URL and pool settings.
|
|
80
|
+
- `RedisConfig`: Redis URL and socket timeout settings.
|
|
81
|
+
|
|
82
|
+
## Backend Usage
|
|
83
|
+
|
|
84
|
+
Backend applications usually compose durable services and Gateway at their
|
|
85
|
+
composition root.
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
from pathlib import Path
|
|
89
|
+
|
|
90
|
+
from cube.cluster import (
|
|
91
|
+
ClusterConfig,
|
|
92
|
+
ClusterGateway,
|
|
93
|
+
ClusterGatewayConfig,
|
|
94
|
+
ClusterPlatformServicesConfig,
|
|
95
|
+
GatewayConfig,
|
|
96
|
+
MySQLConfig,
|
|
97
|
+
RedisConfig,
|
|
98
|
+
create_cluster_platform_services,
|
|
99
|
+
)
|
|
100
|
+
from cube.platform import RuntimeAssemblyConfig
|
|
101
|
+
|
|
102
|
+
assembly = RuntimeAssemblyConfig(rollout_tag="v1")
|
|
103
|
+
services = create_cluster_platform_services(
|
|
104
|
+
ClusterPlatformServicesConfig(
|
|
105
|
+
root_dir=Path("/cube"),
|
|
106
|
+
assembly=assembly,
|
|
107
|
+
mysql=MySQLConfig(
|
|
108
|
+
url="mysql+asyncmy://cube:secret@mysql:3306/cube?charset=utf8mb4",
|
|
109
|
+
),
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
gateway = ClusterGateway(
|
|
113
|
+
config=ClusterGatewayConfig(
|
|
114
|
+
assembly=assembly,
|
|
115
|
+
cluster=ClusterConfig(namespace="cube-prod"),
|
|
116
|
+
redis=RedisConfig(url="redis://:secret@redis:6379/0"),
|
|
117
|
+
gateway=GatewayConfig(command_timeout_seconds=30),
|
|
118
|
+
)
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
await services.startup()
|
|
122
|
+
await gateway.startup()
|
|
123
|
+
commands = gateway
|
|
124
|
+
events = gateway
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Use the surfaces by responsibility:
|
|
128
|
+
|
|
129
|
+
- Durable reads and management go through `app.services`.
|
|
130
|
+
- Live session commands go through `app.commands.dispatch(...)`.
|
|
131
|
+
- Live session events go through `app.events.subscribe(...)`.
|
|
132
|
+
|
|
133
|
+
Backend code must not create `PlatformRuntime` in distributed mode.
|
|
134
|
+
|
|
135
|
+
## Worker Usage
|
|
136
|
+
|
|
137
|
+
Workers can be constructed directly:
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
from pathlib import Path
|
|
141
|
+
|
|
142
|
+
from cube.cluster import (
|
|
143
|
+
ClusterConfig,
|
|
144
|
+
ClusterWorker,
|
|
145
|
+
ClusterWorkerConfig,
|
|
146
|
+
MySQLConfig,
|
|
147
|
+
RedisConfig,
|
|
148
|
+
WorkerConfig,
|
|
149
|
+
)
|
|
150
|
+
from cube.platform import RuntimeAssemblyConfig
|
|
151
|
+
|
|
152
|
+
worker = ClusterWorker(
|
|
153
|
+
config=ClusterWorkerConfig(
|
|
154
|
+
root_dir=Path("/cube"),
|
|
155
|
+
assembly=RuntimeAssemblyConfig(rollout_tag="v1"),
|
|
156
|
+
cluster=ClusterConfig(namespace="cube-prod"),
|
|
157
|
+
mysql=MySQLConfig(
|
|
158
|
+
url="mysql+asyncmy://cube:secret@mysql:3306/cube?charset=utf8mb4",
|
|
159
|
+
),
|
|
160
|
+
redis=RedisConfig(url="redis://:secret@redis:6379/0"),
|
|
161
|
+
worker=WorkerConfig(worker_id="worker-1"),
|
|
162
|
+
)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
async with worker.lifespan():
|
|
166
|
+
await worker.run()
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
The built-in worker entrypoint reads environment variables:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
CUBE_ROOT_DIR=/cube \
|
|
173
|
+
CUBE_CLUSTER_NAMESPACE=cube-prod \
|
|
174
|
+
CUBE_ROLLOUT_TAG=v1 \
|
|
175
|
+
CUBE_MYSQL_URL='mysql+asyncmy://cube:secret@mysql:3306/cube?charset=utf8mb4' \
|
|
176
|
+
CUBE_REDIS_URL='redis://:secret@redis:6379/0' \
|
|
177
|
+
CUBE_WORKER_ID=worker-1 \
|
|
178
|
+
python -m cube.cluster.worker
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
`CUBE_WORKER_ID` is optional. If omitted, the SDK generates a process-local
|
|
182
|
+
worker id.
|
|
183
|
+
|
|
184
|
+
## Schema Lifecycle
|
|
185
|
+
|
|
186
|
+
Distributed runtime uses MySQL for Cube durable domain tables. For production,
|
|
187
|
+
DDL is usually owned by deployment or database operations. For a first test
|
|
188
|
+
deployment, the SDK exposes explicit helpers:
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
await init_cluster_schema(config)
|
|
192
|
+
await validate_cluster_schema(config)
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
`init_cluster_schema(...)` creates the tables selected by the current
|
|
196
|
+
`tables.toml`. `validate_cluster_schema(...)` checks compatibility without
|
|
197
|
+
running DDL. Runtime startup validates schema but does not automatically create
|
|
198
|
+
or migrate tables.
|
|
199
|
+
|
|
200
|
+
## Required Runtime Files
|
|
201
|
+
|
|
202
|
+
Both backend and worker processes must see the same runtime assembly files under
|
|
203
|
+
`<root_dir>/`:
|
|
204
|
+
|
|
205
|
+
```text
|
|
206
|
+
<root_dir>/plugins.toml
|
|
207
|
+
<root_dir>/tables.toml
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
The selected plugins, tables, package version, and `rollout_tag` should match
|
|
211
|
+
between backends and workers.
|
|
212
|
+
|
|
213
|
+
`root_dir` must be shared across nodes in distributed mode. For multi-ECS
|
|
214
|
+
deployments this should be a shared POSIX mount such as NAS, not a local
|
|
215
|
+
disk path that differs by machine.
|
|
216
|
+
|
|
217
|
+
## Command And Event Flow
|
|
218
|
+
|
|
219
|
+
The core live path is:
|
|
220
|
+
|
|
221
|
+
```text
|
|
222
|
+
Backend
|
|
223
|
+
-> ClusterGateway
|
|
224
|
+
-> Redis Pub/Sub command request
|
|
225
|
+
-> ClusterWorker
|
|
226
|
+
-> owner lease claim or owner verification
|
|
227
|
+
-> PlatformRuntime / Session
|
|
228
|
+
-> durable state in MySQL and shared workspace
|
|
229
|
+
-> Redis Pub/Sub live event
|
|
230
|
+
-> Backend event subscription
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`CommandStatus.TIMEOUT` means the Gateway did not receive a worker ACK before
|
|
234
|
+
the request deadline. It does not prove the worker did not execute the request.
|
|
235
|
+
V1 does not automatically replay timed-out commands.
|
|
236
|
+
|
|
237
|
+
## Health
|
|
238
|
+
|
|
239
|
+
Backend applications can combine `services.health()` and `gateway.health()` for
|
|
240
|
+
readiness. `ClusterWorker.health()` reports platform service health plus Redis
|
|
241
|
+
heartbeat and command subscriber state.
|
|
242
|
+
|
|
243
|
+
Health snapshots are for readiness/diagnostics. They are not a scheduler or
|
|
244
|
+
durable recovery mechanism.
|
|
245
|
+
|
|
246
|
+
## Playground Integration
|
|
247
|
+
|
|
248
|
+
The Playground backend supports distributed mode through environment variables:
|
|
249
|
+
|
|
250
|
+
```bash
|
|
251
|
+
PLAYGROUND_RUNTIME_PROFILE=distributed
|
|
252
|
+
CUBE_ROOT_DIR=/cube
|
|
253
|
+
CUBE_CLUSTER_NAMESPACE=cube-prod
|
|
254
|
+
CUBE_ROLLOUT_TAG=v1
|
|
255
|
+
CUBE_MYSQL_URL='mysql+asyncmy://cube:secret@mysql:3306/cube?charset=utf8mb4'
|
|
256
|
+
CUBE_REDIS_URL='redis://:secret@redis:6379/0'
|
|
257
|
+
PLAYGROUND_AUTH_SECRET='at-least-32-random-characters'
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
When this profile is selected, the backend creates distributed `PlatformServices`
|
|
261
|
+
and a `ClusterGateway`, then exposes `services`, `commands`, and `events` to
|
|
262
|
+
application routes. Worker processes still run separately.
|
|
263
|
+
|
|
264
|
+
## V1 Boundaries
|
|
265
|
+
|
|
266
|
+
The current V1 skeleton intentionally keeps recovery small:
|
|
267
|
+
|
|
268
|
+
- No Redis Streams/List durable command queue.
|
|
269
|
+
- No transactional outbox or event replay.
|
|
270
|
+
- No automatic retry/reconciliation for timed-out commands.
|
|
271
|
+
- No distributed `WarmupSession`; it is rejected in distributed mode.
|
|
272
|
+
- No local fallback when Redis/MySQL/shared workspace is unavailable.
|
|
273
|
+
- No Session-to-Session routing or mailbox.
|
|
274
|
+
|
|
275
|
+
Frontend or application users should handle clear failures by retrying or
|
|
276
|
+
starting a new request. Durable state remains in MySQL and the shared workspace;
|
|
277
|
+
live events are best effort.
|