返回 DeepSeek-Reasonix
content_fields.go
根目录 / internal / transcript / content_fields.go
1 package transcript
2
3 import (
4 "fmt"
5 "reflect"
6 "strconv"
7 "strings"
8 )
9
10 // mapContentStrings copies metadata while sharing immutable strings. Large
11 // bodies are replaced before JSON encoding, so page/content reads never
12 // serialize an entire tool payload just to return one small chunk.
13 func mapContentStrings(value reflect.Value, path []string, visit func(string, []string) string) reflect.Value {
14 switch value.Kind() {
15 case reflect.String:
16 out := reflect.New(value.Type()).Elem()
17 out.SetString(visit(value.String(), path))
18 return out
19 case reflect.Pointer, reflect.Interface:
20 if value.IsNil() {
21 return value
22 }
23 child := mapContentStrings(value.Elem(), path, visit)
24 out := reflect.New(value.Type()).Elem()
25 if value.Kind() == reflect.Pointer {
26 pointer := reflect.New(value.Type().Elem())
27 pointer.Elem().Set(child)
28 out.Set(pointer)
29 } else {
30 out.Set(child)
31 }
32 return out
33 case reflect.Struct:
34 out := reflect.New(value.Type()).Elem()
35 out.Set(value)
36 for i := range value.NumField() {
37 field := value.Type().Field(i)
38 name := strings.Split(field.Tag.Get("json"), ",")[0]
39 if field.PkgPath != "" || name == "-" {
40 continue
41 }
42 if name == "" {
43 name = field.Name
44 }
45 out.Field(i).Set(mapContentStrings(value.Field(i), appendPath(path, name), visit))
46 }
47 return out
48 case reflect.Slice, reflect.Array:
49 if value.Kind() == reflect.Slice && value.IsNil() {
50 return value
51 }
52 var out reflect.Value
53 if value.Kind() == reflect.Slice {
54 out = reflect.MakeSlice(value.Type(), value.Len(), value.Len())
55 } else {
56 out = reflect.New(value.Type()).Elem()
57 }
58 for i := range value.Len() {
59 out.Index(i).Set(mapContentStrings(value.Index(i), appendPath(path, strconv.Itoa(i)), visit))
60 }
61 return out
62 case reflect.Map:
63 if value.IsNil() {
64 return value
65 }
66 out := reflect.MakeMapWithSize(value.Type(), value.Len())
67 for iterator := value.MapRange(); iterator.Next(); {
68 out.SetMapIndex(iterator.Key(), mapContentStrings(iterator.Value(), appendPath(path, fmt.Sprint(iterator.Key().Interface())), visit))
69 }
70 return out
71 default:
72 return value
73 }
74 }
75
76 func appendPath(path []string, key string) []string {
77 return append(append([]string(nil), path...), key)
78 }
79
80 func contentStringAt(message Message, path []string) (string, bool) {
81 value := reflect.ValueOf(message)
82 for _, part := range path {
83 for value.IsValid() && (value.Kind() == reflect.Pointer || value.Kind() == reflect.Interface) {
84 if value.IsNil() {
85 return "", false
86 }
87 value = value.Elem()
88 }
89 if !value.IsValid() {
90 return "", false
91 }
92 switch value.Kind() {
93 case reflect.Struct:
94 var child reflect.Value
95 for i := range value.NumField() {
96 field := value.Type().Field(i)
97 name := strings.Split(field.Tag.Get("json"), ",")[0]
98 if name == "" {
99 name = field.Name
100 }
101 if field.PkgPath == "" && name != "-" && name == part {
102 child = value.Field(i)
103 break
104 }
105 }
106 value = child
107 case reflect.Slice, reflect.Array:
108 index, err := strconv.Atoi(part)
109 if err != nil || index < 0 || index >= value.Len() {
110 return "", false
111 }
112 value = value.Index(index)
113 case reflect.Map:
114 if value.Type().Key().Kind() != reflect.String {
115 return "", false
116 }
117 key := reflect.New(value.Type().Key()).Elem()
118 key.SetString(part)
119 value = value.MapIndex(key)
120 default:
121 return "", false
122 }
123 }
124 if !value.IsValid() || value.Kind() != reflect.String {
125 return "", false
126 }
127 return value.String(), true
128 }
129
130 func retainedBytes(value reflect.Value) int {
131 if !value.IsValid() {
132 return 0
133 }
134 size := int(value.Type().Size())
135 switch value.Kind() {
136 case reflect.String:
137 return size + value.Len()
138 case reflect.Pointer, reflect.Interface:
139 if !value.IsNil() {
140 size += retainedBytes(value.Elem())
141 }
142 case reflect.Struct:
143 for i := range value.NumField() {
144 size += retainedBytes(value.Field(i))
145 }
146 case reflect.Slice, reflect.Array:
147 for i := range value.Len() {
148 size += retainedBytes(value.Index(i))
149 }
150 case reflect.Map:
151 for iterator := value.MapRange(); iterator.Next(); {
152 size += 64 + retainedBytes(iterator.Key()) + retainedBytes(iterator.Value())
153 }
154 }
155 return size
156 }
157
157 lines GO