loadImageAttachment reports whether path is a single image file and, if so, reads it into an ImageContent. Directories, oversized files, unreadable paths, and non-image content all return ok=false so the caller falls back to the normal text-scanning flow.
(path string)
| 296 | // paths, and non-image content all return ok=false so the caller falls back |
| 297 | // to the normal text-scanning flow. |
| 298 | func (cli *ChatCLI) loadImageAttachment(path string) (models.ImageContent, bool) { |
| 299 | expanded, err := utils.ExpandPath(path) |
| 300 | if err != nil { |
| 301 | expanded = path |
| 302 | } |
| 303 | |
| 304 | info, err := os.Stat(expanded) |
| 305 | if err != nil || info.IsDir() { |
| 306 | return models.ImageContent{}, false |
| 307 | } |
| 308 | |
| 309 | // Fast reject by extension only when it is clearly non-image AND large; |
| 310 | // otherwise we still sniff, so an extensionless image is detected. |
| 311 | ext := strings.ToLower(filepath.Ext(expanded)) |
| 312 | if info.Size() > maxImageAttachmentBytes { |
| 313 | if imageFileExtensions[ext] { |
| 314 | cli.logger.Warn("imagem excede o limite de anexo, ignorando", |
| 315 | zap.String("path", expanded), zap.Int64("size", info.Size())) |
| 316 | } |
| 317 | return models.ImageContent{}, false |
| 318 | } |
| 319 | |
| 320 | data, err := os.ReadFile(expanded) //#nosec G304 -- user-specified @file path to attach as a vision image |
| 321 | if err != nil { |
| 322 | return models.ImageContent{}, false |
| 323 | } |
| 324 | |
| 325 | mime, ok := models.DetectImageMediaType(data) |
| 326 | if !ok { |
| 327 | // Not a recognized image; let the text path handle it. |
| 328 | return models.ImageContent{}, false |
| 329 | } |
| 330 | |
| 331 | cli.logger.Debug("imagem anexada via @file", |
| 332 | zap.String("path", expanded), zap.String("mime", mime), zap.Int("bytes", len(data))) |
| 333 | |
| 334 | return models.ImageContent{ |
| 335 | MediaType: mime, |
| 336 | Data: data, |
| 337 | FileName: filepath.Base(expanded), |
| 338 | }, true |
| 339 | } |
| 340 | |
| 341 | // extractFileCommandOptions extrai caminhos e opções do comando @file |
| 342 | func extractFileCommandOptions(input string) ([]string, map[string]string, error) { |