package shared import ( "fmt" "git.flytoex.net/yuanwei/flyto-agent/pkg/flyto" ) // CheckNoImageBlocks rejects requests whose messages contain flyto.BlockImage // anywhere (top-level blocks OR nested inside BlockToolResult.ResultBlocks) // with an explicit error instead of silently dropping the image block. Used // by providers that have not yet wired vision support -- vision path routes // through Anthropic for now; other providers call this at Stream entry to // surface "your image never reached the model" loudly. // // Recursion note: when path B (tool returns image via Result.Data) wraps // image blocks inside tool_result.ResultBlocks, a naive top-level-only scan // would miss them and let the provider silently drop the nested image. // This function recurses one level into ResultBlocks to catch that path. // Deeper nesting (blocks containing blocks containing blocks) is not expected // in the current Block model, so we stop at one level. // // providerName goes into the error message so the caller can tell which // provider rejected the image. // // CheckNoImageBlocks 扫描请求消息, 顶层 block 或 BlockToolResult.ResultBlocks // 嵌套层一旦出现 flyto.BlockImage 即返 error, 不做 silent drop. 未接 vision // 的 provider 在 Stream 入口调用, 让调用方立刻知道"这张图没送到模型", 而 // 不是静默通过后纳闷为什么模型视若无睹. 当前 vision 路径只走 Anthropic, 其他 // provider 待 per-provider 启用. // // 递归说明: 路径 B (工具经 Result.Data 返图) 会把 image block 包在 // tool_result.ResultBlocks 里, 只扫顶层会漏, 让 provider 静默丢嵌套图. // 本函数递归一层扫 ResultBlocks. 当前 Block 模型无更深嵌套预期, 一层即止. // // providerName 会写进 error message, 调用方可据此定位是谁拒的图. func CheckNoImageBlocks(msgs []flyto.Message, providerName string) error { for _, msg := range msgs { for _, b := range msg.Blocks { if blockHasImage(b) { return fmt.Errorf("%s: BlockImage not yet supported by this provider (vision path currently routes through anthropic)", providerName) } } } return nil } // blockHasImage returns true for BlockImage directly or a BlockToolResult // whose ResultBlocks contains a nested image. Centralizes recursion so // CheckNoImageBlocks and future guards share the traversal. // // blockHasImage 对直接 BlockImage 或 BlockToolResult 内 ResultBlocks 嵌套 // image 返 true. 递归集中于此, 让 CheckNoImageBlocks 和未来其他 guard 共享 // 同一遍历. func blockHasImage(b flyto.Block) bool { if b.Type == flyto.BlockImage { return true } if b.Type == flyto.BlockToolResult { for _, nested := range b.ResultBlocks { if nested.Type == flyto.BlockImage { return true } } } return false }