返回 DeepSeek-Reasonix
submission_sources.go
根目录 / internal / control / submission_sources.go
1 package control
2
3 import (
4 "context"
5 "path/filepath"
6 "reasonix/internal/attachment"
7 "slices"
8 "strings"
9 )
10
11 func submissionSourceFailure(item SubmissionAttachment, index int, err error) []ImageReferenceFailure {
12 failures := imageFailuresFromAttachment(err)
13 name := "image"
14 if item.Path != "" {
15 name = filepath.Base(filepath.FromSlash(item.Path))
16 } else if item.Reference != nil {
17 name = item.Reference.DisplayName
18 }
19 for i := range failures {
20 failures[i].Index = index + 1
21 failures[i].Name = attachment.NormalizeDisplayName(name)
22 }
23 return failures
24 }
25
26 func withoutDraftID(ids []string, remove string) []string {
27 var out []string
28 for _, id := range ids {
29 if id != remove {
30 out = append(out, id)
31 }
32 }
33 return out
34 }
35
36 func canonicalAttachmentToken(input, transport, logicalID string) string {
37 if slices.Contains(parseRefTokens(input), transport) {
38 return strings.ReplaceAll(input, "@"+EscapeRefPath(transport), "@attachment:"+logicalID)
39 }
40 return input
41 }
42
43 func legacyRemoteImageInputs(line string) []attachment.ImageInput {
44 refs := bareVisionRefs(line)
45 for _, token := range parseRefTokens(line) {
46 if r, ok := classifyVisionToken(token); ok {
47 refs = append(refs, r)
48 }
49 }
50 seen := map[string]bool{}
51 var inputs []attachment.ImageInput
52 for _, r := range refs {
53 if seen[r.path] {
54 continue
55 }
56 seen[r.path] = true
57 switch r.kind {
58 case refRemoteImage:
59 inputs = append(inputs, attachment.ImageInput{Kind: attachment.KindURL, URL: r.path})
60 case refFileID:
61 inputs = append(inputs, attachment.ImageInput{Kind: attachment.KindFiles, FilesID: r.path})
62 }
63 }
64 return inputs
65 }
66
67 // Keep ordinary image references when a submission also uses the new explicit
68 // attachment channel. Without this merge, the frozen channel would mask them.
69 func (c *Controller) appendOrdinaryImageSources(line string, sources []attachment.Source) []attachment.Source {
70 seen := make(map[string]bool)
71 for _, source := range sources {
72 seen[normalizedImageReferencePath(source.Path)] = true
73 }
74 for _, r := range c.detectRefsMode(line, true) {
75 imageFile := r.kind == refFile && isImageAttachmentRef(r.path)
76 if (r.kind != refImage && !imageFile) || isAttachmentRef(r.path) || seen[normalizedImageReferencePath(r.path)] {
77 continue
78 }
79 root := c.workspaceRoot
80 if r.baseDir != "" {
81 root = r.baseDir
82 }
83 sources = append(sources, attachment.Source{Path: r.path, WorkspaceRoot: root, Confine: ".", DisplayName: filepath.Base(r.path)})
84 seen[normalizedImageReferencePath(r.path)] = true
85 }
86 return sources
87 }
88
89 func (c *Controller) structuredImageSources(ctx context.Context, items []SubmissionAttachment) ([]attachment.Source, []ImageReferenceFailure) {
90 var sources []attachment.Source
91 svc := c.attachmentService()
92 logical := map[string]bool{}
93 for index, item := range items {
94 if item.ClientAttachmentID == "" || logical[item.ClientAttachmentID] {
95 return nil, submissionSourceFailure(item, index, attachment.Error{Code: attachment.CodeUnsupported, Message: "invalid logical attachment identity"})
96 }
97 logical[item.ClientAttachmentID] = true
98 count := 0
99 if item.DraftID != "" {
100 count++
101 }
102 if item.Path != "" {
103 count++
104 }
105 if item.Reference != nil {
106 count++
107 }
108 if count != 1 {
109 return nil, submissionSourceFailure(item, index, attachment.Error{Code: attachment.CodeUnsupported, Message: "invalid attachment source"})
110 }
111 if item.DraftID != "" {
112 draft, ok := svc.Drafts().Lookup(c.attachmentScope(), item.DraftID)
113 if !ok {
114 return nil, submissionSourceFailure(item, index, attachment.Error{Code: attachment.CodeMissing, Message: "draft credential is not valid"})
115 }
116 sources = append(sources, attachment.Source{Existing: &draft.Ref, DisplayName: draft.DisplayName, Path: "draft:" + item.DraftID})
117 } else if item.Path != "" {
118 sources = append(sources, attachment.Source{Path: item.Path, WorkspaceRoot: c.workspaceRoot, Confine: ".reasonix/attachments"})
119 } else {
120 if _, _, err := c.ReadSessionAttachment(ctx, item.Reference.Content.Digest, 0, 1); err != nil {
121 return nil, submissionSourceFailure(item, index, err)
122 }
123 sources = append(sources, attachment.Source{Existing: item.Reference, DisplayName: item.Reference.DisplayName})
124 }
125 }
126 return sources, nil
127 }
128
128 lines GO