Skip to content

Commit 1cb0527

Browse files
committed
release: cut docs version 0.11 and bump to 0.11.0
- pyproject.toml → 0.11.0 - CHANGELOG: promote [Unreleased] → [0.11.0] - 2026-06-17, add 2 missing fixes (HITL resume event, resolve_tool_call composition) - Snapshot docs via `pnpm docusaurus docs:version 0.11` - docusaurus.config.ts: lastVersion=0.11, demote 0.10 (noIndex), sitemap ignores /docs/0.10/** - Hardcode version in snapshot intro.mdx (EN + zh-Hans), revert current/ to Next 🚧 - zh-Hans version-0.11.json label → 0.11(最新), demote 0.10
1 parent 1e61e4e commit 1cb0527

114 files changed

Lines changed: 22045 additions & 15 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.11.0] - 2026-06-17
11+
1012
### Changed (BREAKING)
1113

1214
- **Deferred tool groups default to the new `dispatch` strategy.** Tool
@@ -39,6 +41,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3941
**full schema appended to the error result**, so the model can
4042
self-correct in one round trip.
4143

44+
### Fixed
45+
46+
- **HITL resume short-circuit now emits `HitlAnswerEvent`.** Previously,
47+
`_await_answer`'s resume path returned the pre-loaded answer without
48+
emitting the event, so subscribers (e.g. IM outbound tailers) never
49+
learned the question was answered.
50+
- **Explicit `resolve_tool_call` composes with middleware resolvers**
51+
instead of replacing the chain. An explicit resolver passed to
52+
`Agent(resolve_tool_call=...)` becomes the chain head
53+
(first-non-None-wins) rather than silently disabling middleware-provided
54+
resolvers like the deferred dispatcher.
55+
4256
## [0.10.0] - 2026-06-10
4357

4458
### Removed (BREAKING)
@@ -596,7 +610,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
596610
- **[0.2.0]** - 2026-05-10 — see the [release notes](https://github.com/cubeplexai/cubepi/releases/tag/v0.2.0).
597611
- **[0.1.0]** - 2026-05-09 — initial release. See the [release notes](https://github.com/cubeplexai/cubepi/releases/tag/v0.1.0).
598612

599-
[Unreleased]: https://github.com/cubeplexai/cubepi/compare/v0.10.0...HEAD
613+
[Unreleased]: https://github.com/cubeplexai/cubepi/compare/v0.11.0...HEAD
614+
[0.11.0]: https://github.com/cubeplexai/cubepi/compare/v0.10.0...v0.11.0
600615
[0.10.0]: https://github.com/cubeplexai/cubepi/compare/v0.9.0...v0.10.0
601616
[0.9.0]: https://github.com/cubeplexai/cubepi/compare/v0.8.0...v0.9.0
602617
[0.8.0]: https://github.com/cubeplexai/cubepi/compare/v0.7.0...v0.8.0

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "cubepi"
3-
version = "0.10.0"
3+
version = "0.11.0"
44
description = "Pythonic async-native agent framework"
55
readme = "README.md"
66
license = "MIT"

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

website/docs/intro.mdx

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,10 @@ the `main` branch. APIs documented here can still change before they
7171
ship in a tagged release.
7272

7373
The latest released version is v<PackageVersion /> ([switch to it from the
74-
version picker, top-right](pathname:///)). CubePi 0.10 adds
75-
`DeferredToolGroup` for progressive MCP tool disclosure,
76-
`BoundModel.generate_structured()` for typed structured output, a
77-
`tool_choice` parameter on the `Provider` protocol, and `GoalMiddleware`
78-
for autonomous goal-driven runs.
74+
version picker, top-right](pathname:///)). CubePi 0.11 switches deferred
75+
tool groups to the `dispatch` strategy by default, adds the
76+
`resolve_tool_call` middleware hook, and introduces
77+
`AgentTool.expose_to_model` for hidden-but-resolvable tools.
7978

8079
Source, issues, and discussion live on
8180
[GitHub](https://github.com/cubeplexai/cubepi).

website/docusaurus.config.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ const classicOptions: ClassicOptions = {
2828
docs: {
2929
sidebarPath: './sidebars.ts',
3030
editUrl: 'https://github.com/cubeplexai/cubepi/edit/main/website/',
31-
lastVersion: '0.10',
31+
lastVersion: '0.11',
3232
versions: {
3333
current: { label: 'Next 🚧', path: 'next', banner: 'unreleased', noIndex: true },
34-
'0.10': { label: '0.10 (latest)', path: '' },
34+
'0.11': { label: '0.11 (latest)', path: '' },
35+
'0.10': { label: '0.10', path: '0.10', noIndex: true },
3536
'0.9': { label: '0.9', path: '0.9', noIndex: true },
3637
'0.8': { label: '0.8', path: '0.8', noIndex: true },
3738
'0.7': { label: '0.7', path: '0.7', noIndex: true },
@@ -58,6 +59,7 @@ const classicOptions: ClassicOptions = {
5859
'/docs/0.7/**',
5960
'/docs/0.8/**',
6061
'/docs/0.9/**',
62+
'/docs/0.10/**',
6163
],
6264
},
6365
};

website/i18n/zh-Hans/docusaurus-plugin-content-docs/current/intro.mdx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,9 @@ pip install cubepi
6868
这里描述的 API 在打 tag 发布之前还可能变动。
6969

7070
最新已发布版本是 v<PackageVersion />([从右上角版本选择器切换](pathname:///))。
71-
CubePi 0.10 新增 `DeferredToolGroup`(渐进式 MCP 工具披露)、
72-
`BoundModel.generate_structured()`(强类型结构化输出)、
73-
`Provider` 协议上的 `tool_choice` 参数、以及 `GoalMiddleware`
74-
(自主目标驱动 run)。
71+
CubePi 0.11 将 deferred tool group 的默认策略切换为 `dispatch`
72+
新增 `resolve_tool_call` 中间件 hook,以及 `AgentTool.expose_to_model`
73+
(隐藏但可解析的工具)。
7574

7675
源代码、Issue 和讨论都在
7776
[GitHub](https://github.com/cubeplexai/cubepi)

website/i18n/zh-Hans/docusaurus-plugin-content-docs/version-0.10.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"version.label": {
3-
"message": "0.10(最新)",
3+
"message": "0.10",
44
"description": "The label for version 0.10"
55
},
66
"sidebar.docs.category.Getting Started": {
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"version.label": {
3+
"message": "0.11(最新)",
4+
"description": "The label for version 0.11"
5+
},
6+
"sidebar.docs.category.Getting Started": {
7+
"message": "快速入门",
8+
"description": "The label for category 'Getting Started' in sidebar 'docs'"
9+
},
10+
"sidebar.docs.category.Guides": {
11+
"message": "指南",
12+
"description": "The label for category 'Guides' in sidebar 'docs'"
13+
},
14+
"sidebar.docs.category.Agents": {
15+
"message": "Agent",
16+
"description": "The label for category 'Agents' in sidebar 'docs'"
17+
},
18+
"sidebar.docs.category.Providers": {
19+
"message": "Provider",
20+
"description": "The label for category 'Providers' in sidebar 'docs'"
21+
},
22+
"sidebar.docs.category.Checkpointing": {
23+
"message": "检查点",
24+
"description": "The label for category 'Checkpointing' in sidebar 'docs'"
25+
},
26+
"sidebar.docs.category.Middlewares": {
27+
"message": "中间件",
28+
"description": "The label for category 'Middlewares' in sidebar 'docs'"
29+
},
30+
"sidebar.docs.category.Human-in-the-Loop": {
31+
"message": "人机协同",
32+
"description": "The label for category 'Human-in-the-Loop' in sidebar 'docs'"
33+
},
34+
"sidebar.docs.category.MCP": {
35+
"message": "MCP",
36+
"description": "The label for category 'MCP' in sidebar 'docs'"
37+
},
38+
"sidebar.docs.category.Tracing": {
39+
"message": "Tracing",
40+
"description": "The label for category 'Tracing' in sidebar 'docs'"
41+
},
42+
"sidebar.docs.category.Recipes": {
43+
"message": "示例",
44+
"description": "The label for category 'Recipes' in sidebar 'docs'"
45+
},
46+
"sidebar.docs.category.Migration": {
47+
"message": "迁移指南",
48+
"description": "The label for category 'Migration' in sidebar 'docs'"
49+
}
50+
}
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
---
2+
title: 核心概念
3+
description: "了解 CubePi 的六个核心概念:Agent、Tool、Provider、Stream & Events、Middleware 和 Checkpointer。"
4+
---
5+
6+
# 核心概念
7+
8+
CubePi 的全部能力可以归纳为六个概念。这一页读一遍,后面的文档基本就
9+
变成查表了。
10+
11+
## Agent
12+
13+
`Agent` 是有状态的门面:你用 provider、model、可选的工具、可选的
14+
middleware/checkpointer 构造它。然后通过三个方法驱动它:
15+
16+
- `await agent.prompt(message)` —— 用一条 user 消息开启新一轮。
17+
- `await agent.resume()` —— 从最后一条已持久化的消息继续(配合
18+
checkpointer 使用)。
19+
- `agent.steer(message)` / `agent.follow_up(message)` —— 在正在跑的
20+
过程中插入消息,或为当前运行结束后排队下一条。
21+
22+
Agent 持有一个 `AgentState`(system prompt、tools、model、消息历史、
23+
未结束的 tool call、流式标志)和一个 subscribers 列表:
24+
25+
```python
26+
unsubscribe = agent.subscribe(my_listener)
27+
# ...
28+
unsubscribe()
29+
```
30+
31+
Subscriber 会收到循环发出的每一个 `AgentEvent`。可以是同步或异步函数。
32+
33+
## Tool
34+
35+
工具就是一个模型可以调用的 async 函数。用 `@tool` 装饰它,CubePi 会从
36+
参数生成输入 schema:
37+
38+
```python
39+
from cubepi import tool
40+
41+
@tool
42+
async def search(query: str, limit: int = 10) -> str:
43+
"搜索语料库"
44+
# 干活;需要的话在签名里声明 signal / on_update
45+
return ""
46+
```
47+
48+
`@tool` 会构建一个 `AgentTool`(name + description + Pydantic 参数模型 +
49+
异步 `execute`);schema 自动转成 JSON Schema 喂给模型。参数解析、错误
50+
包装、并行执行都由框架处理。长写法 `AgentTool(...)``execution_mode`
51+
`on_update`(增量进度)、`terminate`(在工具里结束本轮)见
52+
[工具使用](../guides/agents/tool-use)
53+
54+
## Provider
55+
56+
任何匹配下面 Protocol 的对象就是 Provider:
57+
58+
```python
59+
class Provider(Protocol):
60+
async def stream(
61+
self,
62+
model: Model,
63+
messages: list[Message],
64+
*,
65+
system_prompt: str = "",
66+
tools: list[ToolDefinition] | None = None,
67+
options: StreamOptions | None = None,
68+
) -> MessageStream: ...
69+
```
70+
71+
它返回一个 `MessageStream` —— 一个统一的异步迭代器,产出 `StreamEvent`,
72+
并通过 `await stream.result()` 暴露最终的 `AssistantMessage`。内置 Provider:
73+
74+
- `AnthropicProvider` —— Claude(Messages API,支持思考、缓存、工具使用)。
75+
- `OpenAIProvider` —— GPT 家族(Chat Completions API)。
76+
- `OpenAIResponsesProvider` —— GPT 家族(Responses API,服务端状态)。
77+
- `FauxProvider` —— 确定性测试替身(不发任何网络请求)。
78+
79+
实现一个方法就能写自己的。见 [Providers / 自定义](../guides/providers/custom)
80+
81+
## Stream 和事件
82+
83+
流和事件分两层:
84+
85+
- **Provider 流** —— `MessageStream` 产出的是 *provider* 事件:
86+
`start``text_start``text_delta``text_end``thinking_*`
87+
`toolcall_*``done``error`。原始 token 流。
88+
- **Agent 事件** —— `agent.subscribe(...)` 收到的内容。十四种类型
89+
覆盖整个循环 + HITL:`agent_start``agent_end``turn_start`
90+
`turn_end``message_start``message_update``message_end`
91+
`tool_execution_start``tool_execution_update`
92+
`tool_execution_end``hitl_request``hitl_answer`
93+
`agent_suspended``agent_aborted`
94+
95+
做 UI 订阅 Agent 事件;做底层 token 路由就钻 `event.stream_event`
96+
[流式事件](../guides/agents/streaming)
97+
98+
## Middleware
99+
100+
`Middleware` 是有最多九个类型化 hook 的类:
101+
102+
| Hook | 何时触发 | 组合规则 |
103+
|---|---|---|
104+
| `transform_context` | 每次调模型之前,处理消息列表 | 链式 —— 每个收到上一个的输出 |
105+
| `convert_to_llm` | provider 序列化之前 | 最后一个实现生效 |
106+
| `transform_system_prompt` | 每次调模型之前,处理 system prompt | 链式 |
107+
| `resolve_tool_call` | 每个工具调用之前(参数校验之前,可改写调用) | 第一个非 `None` 胜出 |
108+
| `before_tool_call` | 每个工具调用之前(在参数校验后) | 第一个 `block=True` 短路 |
109+
| `after_tool_call` | 每个工具调用之后(在 `execute` 之后) | 后写覆盖先写 |
110+
| `after_model_response` | assistant 消息落定之后 | 返回 `TurnAction` 控制流向 |
111+
| `should_stop_after_turn` | 每个轮次结束时 | 任一返回 `True` 即停 |
112+
| `on_run_end` | 所有轮次完成后一次,`agent_end` 之前 | 消息拼接;非空触发一轮额外调用 |
113+
114+
通过 `Agent(middleware=[...])` 传入。见
115+
[Middleware → 组合规则](../guides/middleware/composition)
116+
117+
## Checkpointer
118+
119+
任何匹配下面 Protocol 的对象就是 Checkpointer:
120+
121+
```python
122+
class Checkpointer(Protocol):
123+
async def load(self, thread_id: str) -> CheckpointData | None: ...
124+
async def append(self, thread_id: str, messages: list[Message]) -> None: ...
125+
async def save_extra(self, thread_id: str, extra: dict) -> None: ...
126+
```
127+
128+
通过 `Agent(checkpointer=cp, thread_id="…")` 绑定到 Agent,循环就会在
129+
每条消息落定时追加一行,并在第一次 `prompt()` 时恢复历史。内置后端:
130+
`MemoryCheckpointer``SQLiteCheckpointer``PostgresCheckpointer``MySQLCheckpointer`
131+
[Checkpointing → SQLite](../guides/checkpointing/sqlite)
132+
133+
HITL 为跨进程挂起/恢复新增了两个可选方法:`save_pending_request` /
134+
`load_pending_request`。所有第一方后���都已实现。见 [HITL 指南](../guides/hitl/overview)
135+
136+
## HITL(人机协同)
137+
138+
CubePi 内置了 `cubepi.hitl` 模块,用于 agent 需要**暂停并等待人类输入**
139+
场景:
140+
141+
- **沙箱确认** —— 危险工具(bash、写入文件)在执行前需要人类
142+
approve / deny / edit。
143+
- **运行中提问** —— agent 在运行中途向用户弹出一个结构化表单,等待回答。
144+
145+
```python
146+
from cubepi.hitl import InMemoryChannel, ConfirmToolCallMiddleware, ask_user_tool
147+
148+
channel = InMemoryChannel()
149+
150+
agent = Agent(
151+
model=…,
152+
tools=[bash_tool, ask_user_tool(channel)],
153+
middleware=[ConfirmToolCallMiddleware(channel, require_confirm={"bash"})],
154+
channel=channel,
155+
)
156+
```
157+
158+
Channel 是一个可 `await` 的协程协作者:工具和中间件作者写
159+
`await channel.ask(...)``await channel.confirm(...)`,channel
160+
处理暂停。宿主代码(你的 web 应用 / TUI)订阅 `channel.subscribe()`
161+
轮询 `channel.pending`,把请求渲染给用户,然后通过
162+
`channel.answer(qid, answer)` 回填答案。
163+
164+
内置两种 channel 后端:
165+
- **`InMemoryChannel`** —— 单进程(CLI、notebook、测试)。
166+
- **`CheckpointedChannel`** —— 跨进程(web 服务)。挂起的请求持久化到
167+
checkpointer;另一个进程可以在数小时后通过
168+
`Agent.respond(question_id=, answer=)` 回答。
169+
170+
完整细节——三种 HITL 动词、两套内置中间件、跨进程挂起/恢复协议、事件、
171+
追踪 span 和错误参考——见 [HITL 指南](../guides/hitl/overview)
172+
173+
## Tracer(可选)
174+
175+
`Tracer` 输出符合 [OpenTelemetry GenAI 语义约定](https://opentelemetry.io/docs/specs/semconv/gen-ai/)
176+
的 span,任何 OTLP 后端(Jaeger、Tempo、Honeycomb、Datadog、AWS
177+
X-Ray 等)都能直接接收,无需额外 instrumentation。先装 extra:
178+
179+
```bash
180+
pip install "cubepi[tracing]" # OTel SDK
181+
pip install "cubepi[tracing-otlp]" # + OTLP/HTTP 导出器
182+
```
183+
184+
然后用 `async with` 包住 Agent:
185+
186+
```python
187+
from cubepi.tracing import Tracer
188+
from cubepi.tracing.exporters import JsonlSpanExporter
189+
190+
async with (
191+
Tracer(
192+
service_name="my-bot",
193+
agent_name="assistant",
194+
exporters=[JsonlSpanExporter(directory="./cubepi-traces")],
195+
) as tracer,
196+
tracer.attached(agent),
197+
):
198+
await agent.prompt("")
199+
```
200+
201+
每次 run 会发出一个 `invoke_agent` 根 span,其下每轮 LLM 往返对应
202+
一个 `cubepi.turn`,再嵌套 `chat`(CLIENT)和 `execute_tool`
203+
span。**默认不记录任何 prompt 内容或模型输出** —— 需要的话用
204+
`Tracer(record_content=True)` 显式打开,搭配 `redact` 回调脱敏。配
205+
`Meter(...)` 还能拿到 token / 时延 / TTFC 直方图。完整指南:
206+
[追踪 → 概览](../guides/tracing/overview)
207+
208+
## 拼起来
209+
210+
```
211+
用户代码
212+
213+
214+
┌──────────────────────────────────────────┐
215+
│ Agent │
216+
│ ├─ AgentState (messages, tools, …) │
217+
│ ├─ Middleware ── compose_middleware() │
218+
│ ├─ Checkpointer ── message_end 时追加 │
219+
│ └─ run_agent_loop ◀──── 真实的循环 │
220+
│ │ │
221+
│ ▼ │
222+
│ Provider.stream() → MessageStream │
223+
│ │ │
224+
│ └─ events → emit → subscribers │
225+
└──────────────────────────────────────────┘
226+
```
227+
228+
这张图就是整个框架。文档站的其余部分,都是细节而已。

0 commit comments

Comments
 (0)