package main import ( "encoding/base64" "errors" "fmt" "strings" ) // imageUserMessage attaches pixels only to messages selected as new by a turn. func imageUserMessage(store *ImageStore, text string) (any, error) { names := imageFiles(text) if len(names) == 0 { return modelUserMessage(text), nil } parts, err := imageParts(store, text, names) if err != nil { return nil, fmt.Errorf("make image message: %w", err) } return modelUserParts(parts), nil } // imageParts is the shared wire format for image messages and zoom results. // Tool results use this list directly as function_call_output.output. func imageParts(store *ImageStore, text string, names []string) ([]ModelInputPart, error) { parts := []ModelInputPart{{ Type: "input_text", Text: text, }} for _, name := range names { if store == nil { return nil, errors.New("image store is not available") } data, err := store.Read(name) if err != nil { return nil, fmt.Errorf("read image part: %w", err) } mime := "image/" + strings.TrimPrefix(name[64:], ".") if strings.HasSuffix(name, ".jpg") { mime = "image/jpeg" } parts = append(parts, ModelInputPart{ Type: "input_image", ImageURL: "data:" + mime + ";base64," + base64.StdEncoding.EncodeToString(data), }) } return parts, nil } // imageToolOutput leaves all results as text except zoom of one image message. // Only the text goes to the log, never the data URLs. func imageToolOutput(box *Toolbox, name, arguments, text string) (any, error) { if name != "zoom" || strings.HasPrefix(text, "error:") { return text, nil } var args ToolZoomArguments err := toolDecodeArguments(arguments, &args) if err != nil { return nil, fmt.Errorf("decode image zoom: %w", err) } if args.Name != nil || args.N != 1 { return text, nil } message, err := box.chat.Message(args.ID) if err != nil { return nil, fmt.Errorf("read image zoom: %w", err) } names := imageFiles(message.Text) if len(names) == 0 { return text, nil } parts, err := imageParts(box.images, text, names) if err != nil { return nil, fmt.Errorf("make image zoom: %w", err) } return parts, nil }