返回 DeepSeek-Reasonix
user_input.go
根目录 / internal / agent / user_input.go
1 package agent
2
3 import (
4 "context"
5
6 "reasonix/internal/attachment"
7 "reasonix/internal/provider"
8 )
9
10 type rawUserInputKey struct{}
11 type subagentImageCandidatesKey struct{}
12 type subagentImageInputsKey struct{}
13 type userImageInputsContextKey struct{}
14 type visionSummaryContextKey struct{}
15
16 // WithRawUserInput keeps user-authored text separate from host-rendered turn
17 // context. Runner implementations can persist the raw text while sending their
18 // composed input to the provider.
19 func WithRawUserInput(ctx context.Context, raw string) context.Context {
20 if ctx == nil {
21 ctx = context.Background()
22 }
23 return context.WithValue(ctx, rawUserInputKey{}, raw)
24 }
25
26 // RawUserInput returns the host-authenticated raw user text when available.
27 // Direct Agent callers that do not use a Controller keep their input unchanged.
28 func RawUserInput(ctx context.Context, fallback string) string {
29 if ctx == nil {
30 return fallback
31 }
32 if raw, ok := ctx.Value(rawUserInputKey{}).(string); ok {
33 return raw
34 }
35 return fallback
36 }
37
38 // WithSubagentImageCandidates carries attachment data resolved by the parent
39 // controller for a child model to opt into when it supports vision. Keeping
40 // this separate from UserImages avoids changing the parent's provider-visible
41 // text-only turn while allowing a vision-capable child to receive the same
42 // authorized attachments.
43 func WithSubagentImageCandidates(ctx context.Context, images []string) context.Context {
44 if ctx == nil {
45 ctx = context.Background()
46 }
47 return context.WithValue(ctx, subagentImageCandidatesKey{}, append([]string(nil), images...))
48 }
49
50 // SubagentImageCandidates returns the parent-resolved attachment data that a
51 // child provider may use when its own model supports vision.
52 func SubagentImageCandidates(ctx context.Context) []string {
53 if ctx == nil {
54 return nil
55 }
56 images, _ := ctx.Value(subagentImageCandidatesKey{}).([]string)
57 return append([]string(nil), images...)
58 }
59
60 // WithSubagentImageInputs carries durable attachment refs from the parent
61 // turn. A vision-capable child resolves them through the inherited request
62 // resolver instead of inheriting already-wired data URLs.
63 func WithSubagentImageInputs(ctx context.Context, inputs []attachment.ImageInput) context.Context {
64 if ctx == nil {
65 ctx = context.Background()
66 }
67 return context.WithValue(ctx, subagentImageInputsKey{}, attachment.CloneImageInputs(inputs))
68 }
69
70 // SubagentImageInputs returns the parent-admitted image refs a child may
71 // persist on its user message. Empty when the parent only had legacy Images.
72 func SubagentImageInputs(ctx context.Context) []attachment.ImageInput {
73 if ctx == nil {
74 return nil
75 }
76 inputs, _ := ctx.Value(subagentImageInputsKey{}).([]attachment.ImageInput)
77 return attachment.CloneImageInputs(inputs)
78 }
79
80 func WithUserImageInputs(ctx context.Context, inputs []attachment.ImageInput) context.Context {
81 if ctx == nil {
82 ctx = context.Background()
83 }
84 return context.WithValue(ctx, userImageInputsContextKey{}, attachment.CloneImageInputs(inputs))
85 }
86
87 func userImageInputs(ctx context.Context) []attachment.ImageInput {
88 inputs, _ := ctx.Value(userImageInputsContextKey{}).([]attachment.ImageInput)
89 return attachment.CloneImageInputs(inputs)
90 }
91
92 func withSubagentTurnImages(ctx context.Context) context.Context {
93 if inputs := SubagentImageInputs(ctx); len(inputs) > 0 {
94 return WithUserImageInputs(ctx, inputs)
95 }
96 return WithUserImages(ctx, SubagentImageCandidates(ctx))
97 }
98
99 // WithVisionSummary carries a hidden image-understanding result into the
100 // foreground turn. The run loop persists it on the user message while keeping
101 // RawContent as the user-visible text.
102 func WithVisionSummary(ctx context.Context, summary *provider.VisionSummary) context.Context {
103 if ctx == nil {
104 ctx = context.Background()
105 }
106 if summary == nil {
107 return ctx
108 }
109 cp := *summary
110 cp.ImageDigests = append([]string(nil), summary.ImageDigests...)
111 return context.WithValue(ctx, visionSummaryContextKey{}, &cp)
112 }
113
114 func VisionSummaryFromContext(ctx context.Context) *provider.VisionSummary {
115 if ctx == nil {
116 return nil
117 }
118 summary, _ := ctx.Value(visionSummaryContextKey{}).(*provider.VisionSummary)
119 if summary == nil {
120 return nil
121 }
122 cp := *summary
123 cp.ImageDigests = append([]string(nil), summary.ImageDigests...)
124 return &cp
125 }
126
126 lines GO