MCPcopy
hub / github.com/docker/docker-agent / processFileAttachment

Method processFileAttachment

pkg/app/app.go:536–615  ·  view source on GitHub ↗

processFileAttachment reads a file from disk, classifies it, and either appends its text content to textBuilder or adds a binary part to binaryParts. Returns true when the path resolved to a real, regular file that we attempted to surface to the model — even if the content itself was rejected (too l

(ctx context.Context, att messages.Attachment, textBuilder *strings.Builder, binaryParts *[]chat.MessagePart)

Source from the content-addressed store, hash-verified

534// missing files, but we do want them to cover "the agent has bigger tools
535// than us" cases.
536func (a *App) processFileAttachment(ctx context.Context, att messages.Attachment, textBuilder *strings.Builder, binaryParts *[]chat.MessagePart) bool {
537 absPath := att.FilePath
538
539 fi, err := os.Stat(absPath)
540 if err != nil {
541 var reason string
542 switch {
543 case os.IsNotExist(err):
544 reason = "file does not exist"
545 case os.IsPermission(err):
546 reason = "permission denied"
547 default:
548 reason = fmt.Sprintf("cannot access file: %v", err)
549 }
550 slog.WarnContext(ctx, "skipping attachment", "path", absPath, "reason", reason)
551 a.sendEvent(ctx, runtime.Warning(fmt.Sprintf("Skipped attachment %s: %s", att.Name, reason), ""))
552 return false
553 }
554
555 if !fi.Mode().IsRegular() {
556 slog.WarnContext(ctx, "skipping attachment: not a regular file", "path", absPath, "mode", fi.Mode().String())
557 a.sendEvent(ctx, runtime.Warning(fmt.Sprintf("Skipped attachment %s: not a regular file", att.Name), ""))
558 return false
559 }
560
561 const maxAttachmentSize = 100 * 1024 * 1024 // 100MB
562 if fi.Size() > maxAttachmentSize {
563 slog.WarnContext(ctx, "skipping attachment: file too large", "path", absPath, "size", fi.Size(), "max", maxAttachmentSize)
564 a.sendEvent(ctx, runtime.Warning(fmt.Sprintf("Skipped attachment %s: file too large (max 100MB)", att.Name), ""))
565 return true
566 }
567
568 mimeType := chat.DetectMimeType(absPath)
569
570 switch {
571 case chat.IsTextFile(absPath):
572 if fi.Size() > chat.MaxInlineFileSize {
573 slog.WarnContext(ctx, "skipping attachment: text file too large to inline", "path", absPath, "size", fi.Size(), "max", chat.MaxInlineFileSize)
574 a.sendEvent(ctx, runtime.Warning(fmt.Sprintf("Skipped attachment %s: text file too large to inline (max 5MB)", att.Name), ""))
575 return true
576 }
577 content, err := chat.ReadFileForInline(absPath)
578 if err != nil {
579 slog.WarnContext(ctx, "skipping attachment: failed to read file", "path", absPath, "error", err)
580 a.sendEvent(ctx, runtime.Warning(fmt.Sprintf("Skipped attachment %s: failed to read file", att.Name), ""))
581 return true
582 }
583 textBuilder.WriteString("\n\n")
584 textBuilder.WriteString(content)
585
586 case chat.IsSupportedMimeType(mimeType):
587 // Route through ProcessAttachmentWithMetadata for normalised Document output.
588 // For images this also returns resize metadata used to emit a dimension note.
589 doc, resizeMeta, procErr := chat.ProcessAttachmentWithMetadata(chat.MessagePart{
590 Type: chat.MessagePartTypeFile,
591 File: &chat.MessageFile{Path: absPath, MimeType: mimeType},
592 })
593 if procErr != nil {

Callers 1

RunMethod · 0.95

Calls 10

sendEventMethod · 0.95
WarningFunction · 0.92
DetectMimeTypeFunction · 0.92
IsTextFileFunction · 0.92
ReadFileForInlineFunction · 0.92
IsSupportedMimeTypeFunction · 0.92
FormatDimensionNoteFunction · 0.92
StringMethod · 0.45
SizeMethod · 0.45

Tested by

no test coverage detected