Skip to content

fix(plugin): support structured content parts in tool responses - #563

Open
lirenjie95 wants to merge 4 commits into
labring:mainfrom
lirenjie95:fix/plugin-tool-content-parts
Open

lirenjie95 wants to merge 4 commits into
labring:mainfrom
lirenjie95:fix/plugin-tool-content-parts

Conversation

@lirenjie95

Copy link
Copy Markdown

背景

主仓库 labring/FastGPT PR #7923 让工具消息 content 支持 text / image_url parts(对齐 OpenAI tool message 契约)。本 PR 为配套的插件侧联动修改:插件工具处理器允许返回结构化 content parts,并打通工厂、SDK 客户端、CLI 调试、连接网关四个解析点。

联动

改动内容

  • sdk/factory:ToolHandlerReturnType 新增 content 字段(结构同 OpenAI 的 text / image_url parts);ToolHandlerReturnSchema 校验 wire 契约并保留未知字段(catchall);ZodError 统一映射为可读的 path: message 文本,content 类型错误附专项中文提示
  • sdk/client:导出 ToolMessageContentPartSchema,客户端与工厂契约一致
  • CLI 调试与连接网关:解析点与工厂对齐,直接透传 content parts
  • SKILL.md:补充 content parts 用法与「契约优先级」说明(wire 校验先于 outputSchema;outputSchema 的 content 需声明 z.array(ToolMessageContentPartSchema);对旧插件返回字符串 content 属破坏性变更,错误信息已专项提示)

测试

  • tool.vo.test.ts:parts schema(非法 detail 拒收、未知字段剥离)、return schema(空数组、未知顶层字段保留)新增 4 例
  • tool-factory.test.ts:改用真实 schema;新增空 content、未知键透传、非法 detail 错误提示 3 例
  • 全量:374 通过;4 例失败均为 Windows 环境固有(路径分隔符 / 信号差异),与改动无关

兼容性

  • 不返回 content 的插件行为不变(仍按 response 文本处理)
  • 返回 content 为数组时按 parts 逐项透传;字符串 content 会被 wire 校验拒绝,错误信息明确指出需结构化内容数组

@c121914yu

Copy link
Copy Markdown
Collaborator

联查 FastGPT PR #7923,本评论基于当前提交 4621c0a9eb939481c416b8a294809d1762dc1ccc。

[P1] 将 handler 业务返回中的 content 收紧为 parts-only,破坏了旧插件的输出兼容性。

本次预期是让工具响应向后兼容地支持字符串及显式复合内容,已有业务字段及内容应保持原意。原先 handler 返回值允许任意字段;现在 ToolHandlerReturnSchema,32–36 行 将顶层业务 content 变成只接受 text/image_url parts 的保留字段,.catchall(z.unknown()) 不会放宽这个已声明字段。

例如,原本合法的返回值:

{ content: 'ordinary document text', title: 'Result' }

现在会在 tool-factory.ts,133–144 行 被 parse 拒绝,并把一次正常工具执行转换成 error。若旧服务端仍输出这样的 response,新版客户端也会在 tool-stream.ts,123–139 行 因共享 schema 校验失败而丢弃消息,最终可能报 Tool stream closed without terminal event。此影响还会传播到使用同一 stream schema 的 CLI/debug gateway。

已直接调用本 PR 的两个 schema 验证:上述对象的 ToolHandlerReturnSchema.safeParse(...) 和对应 response 事件的 ToolStreamMessageSchema.safeParse(...) 均返回 success: false。

建议保留旧 handler 业务对象的解析路径,通过明确的结构化输出契约承载新增的模型内容,不要将所有业务 content 强制重解释为视觉内容。仅增加 string 分支也不足以保留原先允许的对象、普通数组等业务 content。需要补充旧字符串/对象/数组 content 的服务端与客户端兼容测试,同时保留显式 text+image_url 的传递测试。

相关 schema/factory 局部测试 26 项通过,但目前通过的测试并不能证明旧输出兼容;部分测试正是在要求拒绝旧字符串 content。未修改源码。

@lirenjie95

Copy link
Copy Markdown
Author

按意见改了:

  • ToolHandlerReturnSchema 保持 z.record(z.string(), z.unknown()) 宽松透传,不再收紧业务 content;结构化 parts 通过 outputSchema 显式声明。
  • 旧的字符串/对象/数组 content 在 server(factory)和 client(流解析)两端都加了透传测试,schema 校验失败丢帧的路径也锁了用例。
  • 文档同步更新,写明向后兼容。

@lirenjie95
lirenjie95 force-pushed the fix/plugin-tool-content-parts branch from 90f3655 to d59a252 Compare October 19, 2026 12:48
lirenjie95 and others added 4 commits October 25, 2026 19:46
- add ToolMessageContentPartSchema (text / image_url) in domain tool vo, allow an optional content parts array in ToolHandlerReturnSchema

- sdk factory: validate the content contract before sending the response and report readable validation errors

- sdk client: re-export the part schema and type for tool authors

- docs: document the content parts contract and wire-first priority in the system tool SKILL

- tests: cover part validation, passthrough, unknown fields and error cases
@lirenjie95
lirenjie95 force-pushed the fix/plugin-tool-content-parts branch from d59a252 to f8d99e0 Compare October 25, 2026 11:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants