Skip to content

Commit abfa304

Browse files
committed
chore(release): prepare 0.12.0 — bump version, cut docs snapshot
- Bump pyproject.toml to 0.12.0 - CHANGELOG.md: promote [Unreleased] to [0.12.0] with compressor, sender attribution, deferred tool hint entries - Cut docs version 0.12 (EN + zh-Hans snapshot) - docusaurus.config.ts: flip lastVersion to 0.12, demote 0.11 - Hardcode version in snapshot intro.mdx (no PackageVersion), revert current/ to Next 🚧 - Fix zh-Hans version-0.12.json label, demote 0.11 label
1 parent 50054a3 commit abfa304

114 files changed

Lines changed: 22125 additions & 13 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: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.12.0] - 2026-06-24
11+
12+
### Added
13+
14+
- **`CompactionMiddleware(tool_result_compressor=...)`** — a
15+
`Callable[[ToolResultMessage], str | None]` callback for selective tool
16+
result preservation during compaction. Return a `str` to preserve that
17+
text verbatim in the summary (for grounding/citation); return `None` to
18+
fall through to default pruning. Preserved results are appended to the
19+
summary as a labeled reference section, excluded from the summarizer
20+
input to save budget, and accumulated across compaction rounds via
21+
`CompactionState` persistence.
22+
- **Sender attribution at the provider boundary.** `UserMessage.metadata`
23+
can now carry `sender_user_id` / `sender_display_name`; providers prefix
24+
the first text block with `[Name]:` when converting to the API format.
25+
Keeps stored message content clean while letting the model know who sent
26+
each turn in group-chat scenarios.
27+
- **Deferred tool ordering hint.** The dispatcher description now hints
28+
models to emit `tool_name` before arguments, smoothing streaming UX for
29+
dispatch-mode deferred tools.
30+
31+
### Fixed
32+
33+
- Removed stale `(latest)` labels from Chinese 0.7 and 0.8 version docs.
34+
1035
## [0.11.0] - 2026-06-17
1136

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

613-
[Unreleased]: https://github.com/cubeplexai/cubepi/compare/v0.11.0...HEAD
638+
[Unreleased]: https://github.com/cubeplexai/cubepi/compare/v0.12.0...HEAD
639+
[0.12.0]: https://github.com/cubeplexai/cubepi/compare/v0.11.0...v0.12.0
614640
[0.11.0]: https://github.com/cubeplexai/cubepi/compare/v0.10.0...v0.11.0
615641
[0.10.0]: https://github.com/cubeplexai/cubepi/compare/v0.9.0...v0.10.0
616642
[0.9.0]: https://github.com/cubeplexai/cubepi/compare/v0.8.0...v0.9.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.11.0"
3+
version = "0.12.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 & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +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.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.
74+
version picker, top-right](pathname:///)). CubePi 0.12 adds
75+
`tool_result_compressor` for selective tool result preservation during
76+
compaction, sender attribution at the provider boundary, and deferred
77+
tool ordering hints for smoother streaming UX.
7878

7979
Source, issues, and discussion live on
8080
[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.11',
31+
lastVersion: '0.12',
3232
versions: {
3333
current: { label: 'Next 🚧', path: 'next', banner: 'unreleased', noIndex: true },
34-
'0.11': { label: '0.11 (latest)', path: '' },
34+
'0.12': { label: '0.12 (latest)', path: '' },
35+
'0.11': { label: '0.11', path: '0.11', noIndex: true },
3536
'0.10': { label: '0.10', path: '0.10', noIndex: true },
3637
'0.9': { label: '0.9', path: '0.9', noIndex: true },
3738
'0.8': { label: '0.8', path: '0.8', noIndex: true },
@@ -60,6 +61,7 @@ const classicOptions: ClassicOptions = {
6061
'/docs/0.8/**',
6162
'/docs/0.9/**',
6263
'/docs/0.10/**',
64+
'/docs/0.11/**',
6365
],
6466
},
6567
};

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

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

7070
最新已发布版本是 v<PackageVersion />([从右上角版本选择器切换](pathname:///))。
71-
CubePi 0.11 将 deferred tool group 的默认策略切换为 `dispatch`
72-
新增 `resolve_tool_call` 中间件 hook,以及 `AgentTool.expose_to_model`
73-
(隐藏但可解析的工具)
71+
CubePi 0.12 新增 `tool_result_compressor` 用于压缩时选择性保留 tool result、
72+
provider 层的发送者归属(group-chat 场景),以及 deferred tool 排序提示
73+
以改善流式输出体验
7474

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

website/i18n/zh-Hans/docusaurus-plugin-content-docs/version-0.11.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.11(最新)",
3+
"message": "0.11",
44
"description": "The label for version 0.11"
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.12(最新)",
4+
"description": "The label for version 0.12"
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)