package flyto // message.go - 引擎与 ModelProvider 之间的消息模型. // // 设计原则: // - 最大公约数:只包含所有 LLM provider 通用的消息结构 // - 类型安全:用具名字段而非 json.RawMessage,错误在编译期暴露 // - 可扩展:provider 特有的内容(如 cache_control) // 由 provider 在内部处理,不污染公共类型 // Role 是消息发送方角色. type Role string const ( RoleUser Role = "user" RoleAssistant Role = "assistant" ) // Message 是对话消息(用户或助手发出). type Message struct { Role Role Blocks []Block // 一条消息可包含多个内容块 } // UserText 创建纯文本用户消息的便捷函数. func UserText(text string) Message { return Message{Role: RoleUser, Blocks: []Block{TextBlock(text)}} } // AssistantText 创建纯文本助手消息的便捷函数. func AssistantText(text string) Message { return Message{Role: RoleAssistant, Blocks: []Block{TextBlock(text)}} } // Block 是消息内容块. // // 精妙之处(CLEVER): 用单一结构体 + BlockType 判断,而非 interface + 多个子类型-- // 避免了消费者写 type switch + 类型断言的样板代码, // 同时保持零 interface 分配开销(结构体值语义). type Block struct { Type BlockType // --- TextBlock 字段 --- Text string // --- ToolUseBlock 字段(模型请求调用工具)--- ToolUseID string ToolName string ToolInput map[string]any // --- ToolResultBlock 字段(工具执行结果回传)--- // ToolUseID 复用上方同名字段(关联请求) ResultText string IsError bool // ResultBlocks carries tool_result content as an array of blocks when // the result includes non-text payloads (e.g. image). Populated by the // engine when a tool returns *tools/builtin.ImageResult via Result.Data. // Nil when ResultText is sufficient (plain text result). Provider // layer emits "content":[...] when non-nil; otherwise falls back to // "content":"string" using ResultText. // // Anthropic API spec (verified 2026-04-21, docs.anthropic.com): a // tool_result content field accepts either string or array. Array form // supports text + image mixed blocks so the model can ground its // response in the returned image. // // ResultBlocks 以 block 数组形式承载 tool_result 内容, 当结果含非文本 // 载荷 (如图片) 时填入. 引擎在工具经 Result.Data 返回 // *tools/builtin.ImageResult 时填. 纯文本结果 ResultText 够用时为 nil. // Provider 层据此决定 wire 形态: 非 nil 发 "content":[...] array, // 否则用 "content":"string" 字符串 (fallback 到 ResultText). // // Anthropic spec (2026-04-21 verify): tool_result content 支持 string // 或 array. Array 形式允许 text + image 混排, 让模型基于返回图片应答. ResultBlocks []Block // --- ThinkingBlock 字段(模型扩展思考内容)--- ThinkingText string // 思考内容文本 // --- ImageBlock 字段 (BlockImage 类型消息内容) --- // ImageSource carries image data in either base64-inline or URL form. // Nil for non-image blocks. Populated by engine when the user inlines // an image path or a tool returns image data via tools.Result.Data. // // ImageSource 携带图片数据, base64 内联或 URL 两种形式. 非图片块为 nil. // 用户粘贴图片路径或工具经 tools.Result.Data 返图时由引擎填入. ImageSource *ImageSource // ProviderMetadata 存储 Provider 特有的不透明数据(如签名,ID 等). // 引擎不解读此字段,原样回传给 Provider. ProviderMetadata map[string]string } // ImageSource carries a single image as either base64-inline data or a URL. // Mirrors Anthropic's image source shape (provider layer reshapes per API). // // ImageSource 携带单张图片, base64 内联数据或 URL 二选一. shape 对齐 Anthropic // image source, provider 层按具体 API 适配. type ImageSource struct { // SourceType is "base64" or "url". // SourceType 为 "base64" 或 "url". SourceType string // MediaType is the IANA media type (e.g. "image/png", "image/jpeg"). // Required when SourceType is "base64". // // MediaType 是 IANA 媒体类型 (如 "image/png" / "image/jpeg"). SourceType // 为 "base64" 时必填. MediaType string // Data is the base64-encoded image bytes (no data-URL prefix). Required // when SourceType is "base64". // // Data 是图片 bytes 的 base64 编码 (不带 data-URL 前缀). SourceType 为 // "base64" 时必填. Data string // URL points to an image served over HTTPS. Required when SourceType is // "url". // // URL 是 HTTPS 图片链接. SourceType 为 "url" 时必填. URL string } // BlockType 标识内容块类型. type BlockType string const ( // BlockText 是普通文本内容. BlockText BlockType = "text" // BlockToolUse 表示模型请求调用一个工具. BlockToolUse BlockType = "tool_use" // BlockToolResult 表示工具执行结果(回传给模型). BlockToolResult BlockType = "tool_result" // BlockThinking 表示模型的扩展思考内容(extended thinking). BlockThinking BlockType = "thinking" // BlockImage carries image data (base64 or URL) for multimodal models. // Provider compatibility varies -- Anthropic and compatible endpoints // consume this; other providers (openai, gemini, minimax native) // currently return an error when encountering BlockImage. Callers // routing images must select a vision-capable provider. // // BlockImage 携带图片数据 (base64 或 URL) 给多模态模型. Provider 兼容性 // 有差异 -- Anthropic 及兼容端点已支持, 其他 (openai / gemini / minimax // native) 目前遇 BlockImage 返 error. 需要传图的调用方应选用有 vision // 能力的 provider. BlockImage BlockType = "image" ) // TextBlock 创建文本内容块. func TextBlock(text string) Block { return Block{Type: BlockText, Text: text} } // ToolUseBlock 创建工具调用块. func ToolUseBlock(id, name string, input map[string]any) Block { return Block{Type: BlockToolUse, ToolUseID: id, ToolName: name, ToolInput: input} } // ToolResultBlock 创建工具结果块. func ToolResultBlock(toolUseID, result string, isError bool) Block { return Block{Type: BlockToolResult, ToolUseID: toolUseID, ResultText: result, IsError: isError} } // ToolResultBlocks constructs a tool_result block whose content is an array of // nested blocks (typically text + image mixed). Use when the tool returns an // image (via Result.Data) that should be surfaced to the model; for plain // text results prefer ToolResultBlock. // // ToolResultBlocks 构造 content 为 block 数组形式的 tool_result 块 (典型是 // text + image 混排). 工具经 Result.Data 返图且要让模型看见时使用; 纯文本 // 结果优先 ToolResultBlock. func ToolResultBlocks(toolUseID string, blocks []Block, isError bool) Block { return Block{Type: BlockToolResult, ToolUseID: toolUseID, ResultBlocks: blocks, IsError: isError} } // ThinkingBlock 创建扩展思考内容块. // providerMeta 是 provider 返回的不透明元数据(如签名),回传时必须原样携带. // 传 nil 表示无额外元数据. func ThinkingBlock(text string, providerMeta map[string]string) Block { return Block{Type: BlockThinking, ThinkingText: text, ProviderMetadata: providerMeta} } // ImageBlockBase64 constructs an image block carrying base64-encoded data. // mediaType is required (e.g. "image/png"). data must NOT include the // "data:image/...;base64," prefix -- pass raw base64 bytes only. // // ImageBlockBase64 构造含 base64 编码数据的图片块. mediaType 必填 (如 // "image/png"). data 必须是纯 base64 字节, 不带 "data:image/...;base64," // 前缀. func ImageBlockBase64(mediaType, data string) Block { return Block{ Type: BlockImage, ImageSource: &ImageSource{ SourceType: "base64", MediaType: mediaType, Data: data, }, } } // ImageBlockURL constructs an image block referencing an HTTPS image URL. // Provider layer may fetch or pass through; check provider docs. // // ImageBlockURL 构造引用 HTTPS 图片链接的图片块. Provider 层可能抓取或透传, // 详见 provider 文档. func ImageBlockURL(url string) Block { return Block{ Type: BlockImage, ImageSource: &ImageSource{ SourceType: "url", URL: url, }, } }