返回 DeepSeek-Reasonix
image_replay.go
根目录 / internal / control / image_replay.go
1 package control
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "slices"
8
9 "reasonix/internal/agent"
10 "reasonix/internal/attachment"
11 "reasonix/internal/i18n"
12 "reasonix/internal/provider"
13 )
14
15 // imageReadError distinguishes local attachment loss from upload/auth failures.
16 // Only the former can be omitted when replaying an earlier user turn.
17 type imageReadError struct{ error }
18
19 func (e imageReadError) Unwrap() error { return e.error }
20
21 func lastImageRequestTurn(messages []provider.Message) int {
22 for i, m := range slices.Backward(messages) {
23 if m.Role == provider.RoleUser && !m.LocalOnly && !agent.IsHostGeneratedUserMessage(m) {
24 return i
25 }
26 }
27 return 0 // no known user boundary: keep all inputs required
28 }
29
30 // Resolve independently so a missing old attachment does not discard its
31 // healthy siblings. The durable references and UI transcript remain intact.
32 func (c *Controller) resolveReplayImages(ctx context.Context, inputs []attachment.ImageInput, route ImageRequestRoute, historical bool) ([]string, []int, error) {
33 var images []string
34 var missing []int
35 for i, input := range inputs {
36 if err := ctx.Err(); err != nil {
37 return nil, nil, err
38 }
39 resolved, err := c.resolveImageInputsForRoute(ctx, []attachment.ImageInput{input}, route)
40 if err == nil {
41 images = append(images, resolved...)
42 continue
43 }
44 if ctx.Err() != nil || errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
45 return nil, nil, err
46 }
47 var readErr imageReadError
48 if historical && (errors.As(err, &readErr) || input.Validate() != nil) {
49 missing = append(missing, i+1)
50 continue
51 }
52 var item attachment.Error
53 if errors.As(err, &item) {
54 item.Index = i + 1
55 return nil, nil, item
56 }
57 return nil, nil, err
58 }
59 return images, missing, nil
60 }
61
62 func noteUnavailableImages(m *provider.Message, positions []int) {
63 if len(positions) == 0 {
64 return
65 }
66 m.Content += fmt.Sprintf("\n[Historical image inputs %v are unavailable. Their contents have not been supplied in this request. Continue with available text and images; if those missing images are needed, ask the user to attach them again. Do not infer their contents.]", positions)
67 }
68
69 func imageRequestFailure(ctx context.Context, err error) error {
70 if ctx.Err() != nil || errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
71 return err
72 }
73 return fmt.Errorf("%s: %w", i18n.M.ImageRequestRecovery, err)
74 }
75
75 lines GO