package engine import ( "testing" "git.flytoex.net/yuanwei/flyto-agent/pkg/flyto" "git.flytoex.net/yuanwei/flyto-agent/pkg/query" ) // TestQueryMessagesToFlyto_ToolResultWithImage locks path B conversion: // query.Content.ResultBlocks containing text + image nested blocks must // translate to flyto.Block.ResultBlocks preserving image source fields. // Session replay and engine tool_result construction both feed through this // conversion; shortcutting a level here silently breaks the full chain. // // TestQueryMessagesToFlyto_ToolResultWithImage 锁路径 B conversion: // query.Content.ResultBlocks 含 text + image 嵌套 block 时, 转 flyto.Block // .ResultBlocks 需保留 image source 各字段. session replay 和 engine // tool_result 构造都经这里, 少翻一层整条链就静默断. func TestQueryMessagesToFlyto_ToolResultWithImage(t *testing.T) { msgs := []query.Message{ { Role: query.RoleUser, Content: []query.Content{ { Type: query.ContentToolResult, ToolUseID: "toolu_r1", Text: "screenshot fallback", ResultBlocks: []query.Content{ {Type: query.ContentText, Text: "Dashboard view:"}, { Type: query.ContentImage, Source: &query.ContentSource{ Type: "base64", MediaType: "image/png", Data: "pngbytes==", }, }, }, }, }, }, } out := queryMessagesToFlyto(msgs) if len(out) != 1 || len(out[0].Blocks) != 1 { t.Fatalf("expected 1 message, 1 block, got %+v", out) } tr := out[0].Blocks[0] if tr.Type != flyto.BlockToolResult { t.Errorf("block type = %q, want %q", tr.Type, flyto.BlockToolResult) } if tr.ToolUseID != "toolu_r1" { t.Errorf("tool_use_id = %q", tr.ToolUseID) } if len(tr.ResultBlocks) != 2 { t.Fatalf("ResultBlocks len = %d, want 2 (text + image)", len(tr.ResultBlocks)) } if tr.ResultBlocks[0].Type != flyto.BlockText || tr.ResultBlocks[0].Text != "Dashboard view:" { t.Errorf("nested text block malformed: %+v", tr.ResultBlocks[0]) } img := tr.ResultBlocks[1] if img.Type != flyto.BlockImage { t.Errorf("nested block[1] type = %q, want %q", img.Type, flyto.BlockImage) } if img.ImageSource == nil { t.Fatal("nested image missing ImageSource") } if img.ImageSource.SourceType != "base64" || img.ImageSource.MediaType != "image/png" || img.ImageSource.Data != "pngbytes==" { t.Errorf("nested image source malformed: %+v", img.ImageSource) } } // TestQueryMessagesToFlyto_ToolResultTextFallback ensures plain-text // tool_result (no ResultBlocks) still takes the original string path, to // prevent regressing text-only flows when introducing the array path. // // TestQueryMessagesToFlyto_ToolResultTextFallback 纯文本 tool_result (无 // ResultBlocks) 仍走原 string 路径, 防引入 array 路径时误杀 text-only 流. func TestQueryMessagesToFlyto_ToolResultTextFallback(t *testing.T) { msgs := []query.Message{ { Role: query.RoleUser, Content: []query.Content{ { Type: query.ContentToolResult, ToolUseID: "toolu_r2", Text: "plain text only", }, }, }, } out := queryMessagesToFlyto(msgs) if len(out[0].Blocks) != 1 { t.Fatal("expected 1 block") } tr := out[0].Blocks[0] if tr.ResultBlocks != nil { t.Errorf("text-only tool_result must not populate ResultBlocks, got %+v", tr.ResultBlocks) } if tr.ResultText != "plain text only" { t.Errorf("ResultText = %q, want plain text only", tr.ResultText) } }