OpenAI ChatKit 动作
动作(action)让ChatKit SDK前端在用户没有提交消息时,也能触发流式响应,还可以触发ChatKit之外的副作用。
触发动作
响应用户与组件的交互
给支持动作的组件节点附加ActionConfig即可触发。例如为按钮绑定点击动作,用户点击后,动作被送到服务器,可更新组件、运行推理或流式返回新的对话线程项目。
来源示例 1(python):
## [中文] 示例1:响应用户与组件的交互。
## [中文] 创建标签为“示例”的按钮,点击发送type=example、载荷id=123的动作,默认交由服务器处理。
button = Button(
label="Example",
onClickAction=ActionConfig(
type="example",
payload={"id": 123},
),
)
前端也可直接调用sendAction()。它适合响应ChatKit之外的界面交互;需要客户端和服务端共同处理时,也可用来串联动作。
来源示例 2(javascript):
// [中文] 示例2:响应用户与组件的交互。
// [中文] 前端直接发送example动作,无需用户输入消息;chatKit实例须由集成代码提供。
await chatKit.sendAction({
type: "example",
payload: { id: 123 },
});
处理动作
服务端
默认将动作发送给服务器,通过实现ChatKitServer.action处理:
来源示例 3(python):
## [中文] 示例3:服务端。
## [中文] 服务器按动作类型读取id并执行应用逻辑,写入隐藏上下文“用户做了一项操作”,随后生成并流式返回事件。
## [中文] 动作与载荷不可信,需验证授权及字段;do_thing、generate、存储和导入均由应用提供。
## [中文] 线程项目ID由store.generate_item_id生成,避免复用固定示意ID;仍须提供持久化store和上下文。
class MyChatKitServer(ChatKitServer[RequestContext]):
async def action(
self,
thread: ThreadMetadata,
action: Action[str, Any],
sender: WidgetItem | None,
context: RequestContext,
) -> AsyncIterator[Event]:
if action.type == "example":
await do_thing(action.payload["id"])
# Often you'll want to add a HiddenContextItem so the model
# can see that the user did something.
await self.store.add_thread_item(
thread.id,
HiddenContextItem(
id=self.store.generate_item_id("message", thread, context),
thread_id=thread.id,
created_at=datetime.now(),
content="<USER_ACTION>The user did a thing</USER_ACTION>",
),
context,
)
# Then you might want to run inference to stream a response
# back to the user.
async for event in self.generate(context, thread):
yield event
动作及其载荷由客户端发来,必须视为不可信数据。
客户端
要在客户端集成中处理动作,在ActionConfig中设置handler="client":
来源示例 4(python):
## [中文] 示例4:客户端。
## [中文] 将按钮动作标记handler=client,使其交给客户端回调;“示例”是按钮标签。
button = Button(
label="Example",
onClickAction=ActionConfig(type="example", payload={"id": 123}, handler="client"),
)
触发时,动作会被传给初始化ChatKit时提供的回调:
来源示例 5(javascript):
// [中文] 示例5:客户端。
// [中文] 客户端处理example后,将结果作为example_complete动作送回服务器;widgets.onAction绑定处理器。
// [中文] doSomething与ChatKit实例由应用提供,客户端处理不代替服务端校验。
async function handleWidgetAction(action) {
if (action.type === "example") {
const res = await doSomething(action);
// You can fire off actions to your server from here as well.
// For example, stream new thread items or update a widget.
await chatKit.sendAction({
type: "example_complete",
payload: res,
});
}
}
chatKit.setOptions({
// Other options...
widgets: { onAction: handleWidgetAction },
});
强类型动作
Action与ActionConfig默认不是强类型,但Action.create辅助方法可从一组强类型动作生成ActionConfig:
来源示例 6(python):
## [中文] 示例6:强类型动作。
## [中文] 用Literal与Pydantic区分example及other载荷,通过TypeAdapter验证,再用Action.create构造动作配置。
## [中文] 处理成功后产生文字“动作完成”;原片段依赖相关导入与应用逻辑,验证失败应由应用补错误处理。
class ExamplePayload(BaseModel):
id: int
ExampleAction = Action[Literal["example"], ExamplePayload]
OtherAction = Action[Literal["other"], None]
AppAction = Annotated[
ExampleAction | OtherAction,
Field(discriminator="type"),
]
ActionAdapter: TypeAdapter[AppAction] = TypeAdapter(AppAction)
def parse_app_action(action: Action[str, Any]) -> AppAction:
return ActionAdapter.validate_python(action)
## Usage in a widget
## Action provides a create helper which makes it easy to generate
## ActionConfigs from strongly typed actions.
button = Button(
label="Example",
onClickAction=ExampleAction.create(ExamplePayload(id=123)),
)
## usage in action handler
class MyChatKitServer(ChatKitServer[RequestContext]):
async def action(
self,
thread: ThreadMetadata,
action: Action[str, Any],
sender: WidgetItem | None,
context: RequestContext,
) -> AsyncIterator[Event]:
# add custom error handling if needed
app_action = parse_app_action(action)
if app_action.type == "example":
await do_thing(app_action.payload.id)
yield ThreadItemDoneEvent(
item=AssistantMessageItem(
id=self.store.generate_item_id("message", thread, context),
thread_id=thread.id,
created_at=datetime.now(),
content=[AssistantMessageContent(text="Action complete.")],
)
)
用组件与动作创建自定义表单
接收用户输入的节点放入Form后,这些字段值会加入表单内部触发的所有动作的payload。
表单字段按name组织载荷:Select(name="title")对应action.payload.title,Select(name="todo.title")对应action.payload.todo.title。
来源示例 7(python):
## [中文] 示例7:用组件与动作创建自定义表单。
## [中文] 编辑待办表单包含标题、描述和“保存”按钮;标题为必填,提交动作带待办ID和收集到的字段。
## [中文] 服务器按update_todo更新并输出“待办已更新”;todo、update_todo和认证、字段校验仍由应用提供。
form = Form(
direction="col",
onSubmitAction=ActionConfig(
type="update_todo",
payload={"id": todo.id},
),
children=[
Title(value="Edit Todo"),
Text(value="Title", color="secondary", size="sm"),
Text(
value=todo.title,
editable=EditableProps(name="title", required=True),
),
Text(value="Description", color="secondary", size="sm"),
Text(
value=todo.description,
editable=EditableProps(name="description"),
),
Button(label="Save", submit=True),
],
)
class MyChatKitServer(ChatKitServer[RequestContext]):
async def action(
self,
thread: ThreadMetadata,
action: Action[str, Any],
sender: WidgetItem | None,
context: RequestContext,
) -> AsyncIterator[Event]:
if action.type == "update_todo":
todo_id = action.payload["id"]
# Any action that originates from within the Form will
# include title and description.
title = action.payload["title"]
description = action.payload["description"]
await update_todo(todo_id, title, description)
yield ThreadItemDoneEvent(
item=AssistantMessageItem(
id=self.store.generate_item_id("message", thread, context),
thread_id=thread.id,
created_at=datetime.now(),
content=[AssistantMessageContent(text="Todo updated.")],
)
)
验证
Form使用基础原生表单验证,对已配置的required与pattern执行检查;存在无效字段时阻止提交。
未来可能加入体验更好、表达更丰富或可定制错误显示的验证模式。目前,复杂且验证条件繁琐的表单不适合仅靠组件实现。可改用客户端动作打开弹窗,在其中显示自定义表单,再用sendAction把结果送回ChatKit。
将Card用作Form
向Card传入asForm=True,它会按Form工作,执行验证,并将收集到的字段传给Card的confirm动作。
载荷键冲突
如果表单字段名称与载荷已有的预定义键冲突,表单值会被忽略。这通常是一个错误,检测到时会发出error事件。
控制组件加载状态下的交互
使用ActionConfig.loadingBehavior控制动作在组件中触发的加载状态:
来源示例 8(python):
## [中文] 示例8:控制组件加载状态下的交互。
## [中文] 按钮文字是“这可能需要一段时间”;loadingBehavior=container使整个组件容器进入加载状态,而非只影响按钮。
button = Button(
label="This may take a while...",
onClickAction=ActionConfig(
type="long_running_action_that_should_block_other_ui_interactions",
loadingBehavior="container",
),
)
| 值 | 行为 |
|---|---|
auto |
根据使用位置调整,默认值 |
self |
在动作绑定的组件节点上显示加载状态 |
container |
整个组件容器进入加载状态,略微淡出并不可交互 |
none |
不显示加载状态 |
使用auto行为
通常建议使用默认的auto,它依据绑定位置决定状态,例如:
Button.onClickAction使用self。Select.onChangeAction使用none。Card.confirm.action使用container。

TRAE-AI编程


更多建议: