brickly-sdk
Brickly Brick Python runtime 官方 SDK。通过 loopback gRPC 接入 Host Runtime
(invoke / interact),让 Python Brick 专注写命令逻辑。缺少 Host endpoint 时拒绝 BPP fallback。
业务日志请使用 brick.info / brick.warn / brick.error(或兼容旧名 brick.log),经 Host diagnostics.log 进入日志中心。不要手写旧 stdin/stdout 协议帧。平台未连接时这些方法是 no-op,不会抛错。
安装
pip install brickly-sdk==0.11.0
国内环境可以使用 PyPI 镜像:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple brickly-sdk==0.11.0
快速开始
from brickly import BricklyRuntime
brick = BricklyRuntime()
@brick.on_command("hello")
def hello(ctx, input_value):
data = input_value if isinstance(input_value, dict) else {}
name = str(data.get("name") or "Brickly")
brick.info("hello", {"name": name})
return {"message": f"Hello, {name}"}
brick.run()
SDK 会自动处理:
- 连接
BRICKLY_HOST_ENDPOINT并注册 gRPC Runtime invoke/interact命令分发- 取消信号、
ctx.is_cancelled()和ctx.on_cancel(...) invoke一次一结果;interact用ctx.send/on_event/closed- 再跑自己的命令:
invoke/interact/call(已有占用则不 dispose;没有占用则拒绝) - Host 平台 / UI / Resource 客户端
- 可选 shutdown hook
- 子窗口创建、窗口方法调用、
window.*事件路由 - 事件总线
events.publish(...)/events.on(...)(公共事件命名空间:主题;窗口寿命用win.on) - alias-first 跨 Brick 调用与会话:
dependencies.require(alias) - 平台能力
platform.screenshot、platform.screen、platform.input、platform.clipboard、platform.system
跨 Brick 的 invoke / interact 不设置 SDK 本地固定超时,由 Host
调用生命周期统一终止,避免大资源或长任务被误判超时。其他底层 Host API
仍保留默认超时,显式 timeout 的调用方式不变。
进阶:双向 live
ctx.send 只在 interact 里有意义,不要写进 hello。页面用 interact,不要用 call。
@brick.on_command("live")
def live(ctx, input_value):
n = {"value": 0}
def on_event(event):
data = event if isinstance(event, dict) else {}
ctx.send({"type": "reply", "text": "收到" + str(data.get("text") or "")})
ctx.on_event(on_event)
ctx.closed.wait()
return {"n": n["value"]}
会话内 RPC 用 ctx.handle_requests。作者和调用方成对写法见 docs/invoke-interact.md 第 4.6 节。摘要:
from brickly import BppError
def complete(request, req_ctx):
if req_ctx.is_cancelled():
raise BppError("CANCELLED", "request 已取消")
body = request if isinstance(request, dict) else {}
return {"items": suggest(str(body.get("prefix") or ""))}
@brick.on_command("assist")
def assist(ctx, _input):
ctx.handle_requests(complete)
ctx.closed.wait()
return {"ok": True}
调用方(require(alias)、ToolHandle、brick.interact 同一套):必须传 on_event,结果走 end()。
session = await client.interact(
"assist",
{"file": "main.ts"},
on_event=lambda event: None,
)
items = await session.request({"prefix": "con"})
result = await session.end()
破坏性: request() 同步返回句柄,不是结果,也不是 async def。pending = session.request(...) 拿到的是句柄。await session.request(...) 仍可用(句柄可 await),但要 cancel() 必须先拿句柄再 await:
pending = session.request({"prefix": "con"})
pending.cancel()
await pending # 也可 await pending.result()
错误看 error.code:CANCELLED / DEADLINE_EXCEEDED / SESSION_CLOSED。DEADLINE_EXCEEDED 同时也是 TimeoutError。invoke / call 本轮不改成 CallHandle。作者 ctx.cancel_event / is_cancelled() 不改。
call 是 interact + 立刻 end:
poem = await client.call(
"complete",
{"prompt": "写一首诗"},
on_event=lambda event: None,
)
进阶:Call 窗与 Session 窗
ctx.ui 只开 Call 窗:跟这次命令走,命令结束就关。brick.ui 开 Session 窗:跟当前占用走;keepAlive: True 时 Claim 交回后窗仍可保住 Session。不要写 lifetime= 或清单 command.window。后台定时弹窗请 invoke 再在命令里开窗,不要在定时器里直接 create_browser_window。per-call 没有 Session,不能用 brick.ui。
与 Node SDK 的同步关系
Python SDK 的协议语义与 @syllm/brickly-sdk 保持一致:
- Python 使用
snake_case方法名,例如create_browser_window()、set_full_screen()。 - Node 使用
camelCase方法名,例如createBrowserWindow()、setFullScreen()。 - 两者底层走同一套 gRPC Runtime / Host 服务,窗口方法名一致。
- 当前 SDK 包版本为
0.11.0(__version__);生产协议是brickly.runtime.v1。 - Python 不提供 Node 的 TypeScript
CommandMap类型生成能力;Python 侧依赖类型标注和中文 docstring 提供 IDE 补全。
命令上下文
命令处理函数会收到 CommandContext:
@brick.on_command("process")
def process(ctx, input_value):
if ctx.is_cancelled():
return {"cancelled": True}
ctx.send({"type": "status", "step": "start"})
return {"ok": True}
长期占用使用 ToolSdk.start() / ToolHandle。Runtime 里占用依赖用 require(alias).start(),必须先进入自己的命令(brick.invoke 中转);占用跟这次 Call,return 自动放手。异步上下文管理器是 dispose() 语法糖。一次性 invoke 不会 pin 进程。
常用属性和方法:
ctx.request_id:当前请求 IDctx.command_id:当前命令 IDctx.invocation:宿主传入的可信调用来源和依赖 Profile 映射ctx.config:当前 Profile 配置快照ctx.storage:本机持久 KV / collection / secrets;与体验窗共库。看不见路径或_revctx.ui:子窗口 APIctx.events:事件总线 APIctx.platform:平台能力 APIctx.system:ctx.platform.system的快捷别名ctx.send(event):推给调用方(仅 interact)ctx.on_event(handler):收调用方 send(仅 interact)ctx.handle_requests(handler, concurrency=8):注册会话内 request handler;return 就是那条 request 的结果(仅 interact)ctx.closed.wait():等到调用方 closeInput / 断开ctx.dependencies.require(alias):获取绑定当前 command parent、trace 与 Profile 的依赖客户端
子窗口
from brickly import BricklyRuntime
brick = BricklyRuntime()
@brick.on_command("open")
def open_window(ctx, _input):
win = ctx.ui.create_browser_window("ui/index.html", {"width": 640, "height": 480})
win.expose({
"pause": lambda _payload, _session=None: None,
})
win.send("tick", {"remaining": 60})
win.on("closed", lambda payload: brick.info("窗口已关闭", {"payload": payload}))
win.set_title("Hello from Python")
return {"windowId": win.id, "windowKey": win.window_key}
brick.run()
WindowHandle 提供与 Node SDK 对齐的常用窗口方法,例如:
- 几何尺寸:
set_bounds()、get_bounds()、set_position()、set_size() - 内容区域:
set_content_bounds()、get_content_bounds()、set_content_size() - 状态切换:
minimize()、maximize()、restore()、show()、hide()、focus() - 状态查询:
is_visible()、is_focused()、is_minimized()、is_full_screen() - 外观能力:
set_title()、set_opacity()、set_background_color()、set_has_shadow() - webContents:
send()、execute_javascript()、open_dev_tools()、go_back()、set_zoom_factor()、copy()、paste()、undo()
关闭是显式生命周期操作:
result = win.close()
if result["status"] in ("pending", "prevented"):
win.focus() # 句柄仍可用
termination = win.force_close()
close() 返回 closed/prevented/pending/not-found。只有 closed/not-found 和 window.closed 终态事件会把句柄标记为 closed、从 Runtime Map 删除并清空 listener。终态 eventId 有界去重,transport 结束时也会释放全部窗口句柄。
跨 Brick 调用
命令处理函数内部只使用 manifest alias:
result = ctx.dependencies.require("openai").invoke(
"chat",
{"prompt": "hello"},
profile_id="work",
)
from brickly.internal.grpc.client import call
poem = await call(
ctx.dependencies.require("openai"),
"complete",
{"prompt": "写一首诗"},
on_event=lambda event: None,
)
精确来源和版本由 Host 握手绑定。热键依赖 Profile 会按绑定的精确 BrickKey 自动使用,显式
profile_id 始终优先。
没有当前命令时,同一套 invoke / call / interact 就是 root:
result = brick.dependencies.require("openai").invoke(
"chat",
{"prompt": "hello"},
profile_id="work",
)
大载荷与资源
普通 invoke 始终返回直接值,逻辑 JSON 输入和结果上限为 10 MiB,一次传完;
超限抛出 PAYLOAD_TOO_LARGE,不会静默改成资源类型。更大的字节走 Resource。
改限额时先改这张表,再对三语言 README 保持同一组数字:
| 限制 | 值 | 含义 |
|---|---|---|
| 单帧(wire chunk) | 1 MiB | Create/Read 每一帧上限。SDK 自动拆,调用方不必切块 |
| gRPC 单条消息 | 4 MiB | 传输天花板,给 protobuf 信封留余量 |
bytes() / text() / json() |
200 MiB | 整份进内存的上限;更大用 stream() / save_to() |
| 单对象默认配额 | 8 GiB | 真正的「能不能存 1G」 |
| 并发上传 | 8 | 每个 runtime 同时进行的 Create |
str 默认 text/plain; charset=utf-8,bytes 默认 application/octet-stream。
创建受 Host 配额与 Call/Lifetime 治理。finish 前资源不可读。命令内创建会自动带
x-brickly-invocation-id。不要记录 capability token,也不要长期持久化 Ref。
已经在内存里、通常不大——超过 1 MiB 时 SDK 自动改走 Writer,调用方式不变:
note = brick.resources.create("hello", name="note.txt")
bin = brick.resources.create(data, name="input.bin")
文件、流、未知长度或 1G 级对象用 create_from,边读边传,不要先拼完整副本:
with open("large.bin", "rb") as source:
resource = brick.resources.create_from(source, name="large.bin")
resource.save_to("out.bin")
也接受 Iterable[str | bytes] 或带 read() 的文件对象。来源失败会 abort。
边算边写用 Writer。write 接受任意大小;关闭后再写返回 RESOURCE_UPLOAD_CLOSED。
finish 幂等;finish 之后的 abort 不会撤销已发布对象:
writer = brick.resources.create_writer(name="out.bin")
try:
writer.write(chunk)
resource = writer.finish()
except Exception:
writer.abort()
raise
大结果由作者 create 后 return Handle;invoke 交回 ResourceRef,调用方再 open:
ref = ctx.dependencies.require("report").invoke("export", input_value)
resource = brick.resources.open(ref)
if resource.ref["sizeBytes"] <= 200 * 1024 * 1024:
report = resource.json()
else:
resource.save_to(output_path)
ctx.dependencies.require("consumer").invoke(
"analyse",
{"source": resource},
)
open() 只绑定句柄,不立刻访问 Host。ResourceHandle 支持 stream()、bytes()、
text()、json()、save_to()、close()、revoke();再次作为 input 时只传 ResourceRef。
resource = brick.resources.open(payload["attachment"])
try:
resource.save_to(output_path)
finally:
resource.close()
events.on() 回调收到的就是发布时的业务对象,不会再包一层资源,也不会水合成
ResourceHandle。若业务对象里本身带 ResourceRef,需要读内容时再 resources.open。
过程调用用 call,必须传入 on_event:
from brickly.internal.grpc.client import call
return await call(
ctx.dependencies.require("openai"),
"chat",
{"prompt": "hello"},
on_event=lambda event: ctx.send(event),
)
跨 Brick 调用需要在调用方 manifest 的 dependencies 中声明目标 Brick 和命令:
"dependencies": {
"openai": {
"target": {
"brickId": "com.brickly.openai",
"origin": "installed",
"version": "2.1.0"
},
"commands": ["chat"]
}
}
跨 Brick 会话
目标 Brick 有状态时,在 command handler 里 interact,不要另做 open()。收过程只走 on_event,说完用 end:
session = await ctx.dependencies.require("openai").interact(
"chat",
{"prompt": "继续这个话题"},
on_event=lambda event: None,
)
return await session.end()
平台能力
系统 API:
@brick.on_command("show-app-info")
def show_app_info(ctx, _input):
return {
"appName": ctx.system.get_app_name(),
"appVersion": ctx.system.get_app_version(),
"userData": ctx.system.get_path("userData"),
"isWindows": ctx.system.is_windows(),
}
剪贴板 API:
@brick.on_command("replace-clipboard")
def replace_clipboard(ctx, _input):
previous = ctx.platform.clipboard.read_content()
updated = ctx.platform.clipboard.set_content({"kind": "text", "text": "来自 Python"})
return {"previous": previous, "updated": updated}
输入和屏幕 API:
@brick.on_command("screen-info")
def screen_info(ctx, _input):
point = ctx.platform.screen.get_cursor_screen_point()
display = ctx.platform.screen.get_primary_display()
return {"point": point, "display": display}
@brick.on_command("click")
def click(ctx, _input):
ctx.platform.input.mouse_click(100, 100)
ctx.platform.input.keyboard_tap("A", "control")
return {"ok": True}
宿主错误会以 BppError 原样抛出。shell_open_external 仅允许 http / https / mailto。
错误处理
抛出 BppError 可以保留明确错误码:
from brickly import BppError
raise BppError("INVALID_INPUT", "url 不能为空")
普通异常会被 SDK 转换为 INTERNAL_ERROR 并返回给宿主。
日志
brick.info("开始处理", {"id": 1})
debug / info / warn / error 经 Host diagnostics.log 进入日志中心。命令 handler 内会带上当前 invocationId 挂到该 command 节点;on_ready 等无当前 command 时走顶级 diagnostic。handler 返回后的异步日志仍能靠 ContextVar 挂回(与 Node 一致)。不要手写 stdin/stdout 协议帧。
源码结构
实现按领域拆分为 transport / scope / command / session / window / events / platform / runtime,与 Node SDK 对齐。
详情见 brickly/README.md。
AI 对齐框架
本 SDK 是 Follower 实现。用 AI 跟进 Node 时请走:
specs/sdk/AGENT.mdspecs/sdk/capability-matrix.yamlspecs/sdk/api-mapping.yamlspecs/sdk/prompts/follower-agent.md(target=python)
Metadata
Release files for brickly-sdk 0.11.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| brickly_sdk-0.11.0.tar.gz | 312.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| brickly_sdk-0.11.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 424.5 kB
Release files / brickly_sdk-0.11.0.tar.gz
| Download URL | brickly_sdk-0.11.0.tar.gz |
|---|---|
| Size | 312.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
01061e7f8d96fb0e8df39c4be99d932790e967d73e5286ea0d074641ff111faf
|
|
BLAKE2b-256 checksum How to use checksums |
70409c3523be428a1ef7774d0d23cb7433abf20e2dbee1cc291c4b83b2e1dee3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / brickly_sdk-0.11.0-py3-none-any.whl
| Download URL | brickly_sdk-0.11.0-py3-none-any.whl |
|---|---|
| Size | 112.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
9866eff54832a1678c26576032509fac453a61ee2638bc37d5b6fb0709266c7c
|
|
BLAKE2b-256 checksum How to use checksums |
0d3ec483f38040c3cc2d3bb83462c8b032ad7e10e53f87762646f2c27959f5ee
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Security for brickly-sdk 0.11.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.