github.com/anthropics/anthropic-sdk-go@v1.70.1/internal/apijson/union.go (about) 1 package apijson 2 3 import ( 4 "errors" 5 "reflect" 6 "sync" 7 8 "github.com/anthropics/anthropic-sdk-go/packages/param" 9 10 "github.com/tidwall/gjson" 11 ) 12 13 var apiUnionType = reflect.TypeOf(param.APIUnion{}) 14 var apiObjectType = reflect.TypeOf(param.APIObject{}) 15 16 var nativeTypeCache sync.Map // map[reflect.Type]bool 17 18 // CustomUnmarshaler indicates that a type is not "apijson native". 19 // apijson native types use the custom [json.Unmarshaler] only as an entry 20 // point, and therefore are skipped when nested. 21 type CustomUnmarshaler interface { 22 UnmarshalAPIJSON([]byte) error 23 } 24 25 var customUnmarshalerType = reflect.TypeOf((*CustomUnmarshaler)(nil)).Elem() 26 27 func implementsCustomUnmarshaler(t reflect.Type) bool { 28 return t.Implements(customUnmarshalerType) || reflect.PointerTo(t).Implements(customUnmarshalerType) 29 } 30 31 // isApijsonNative reports whether t is a generated struct whose 32 // UnmarshalJSON is the trivial delegation to [UnmarshalRoot], therefore 33 // the UnmarshalJSON function can be skipped for nested values. 34 func isApijsonNative(t reflect.Type) bool { 35 if cached, ok := nativeTypeCache.Load(t); ok { 36 return cached.(bool) 37 } 38 native := computeApijsonNative(t) 39 nativeTypeCache.Store(t, native) 40 return native 41 } 42 43 func computeApijsonNative(t reflect.Type) bool { 44 for t.Kind() == reflect.Pointer { 45 t = t.Elem() 46 } 47 if t.Kind() != reflect.Struct { 48 return false 49 } 50 if implementsCustomUnmarshaler(t) { 51 return false 52 } 53 if isParamStruct(t) || isStructUnion(t) { 54 return true 55 } 56 if f, ok := t.FieldByName("JSON"); ok && f.Type.Kind() == reflect.Struct { 57 if _, ok := f.Type.FieldByName("raw"); ok { 58 return true 59 } 60 } 61 return false 62 } 63 64 var paramObjectIdxCache sync.Map // map[reflect.Type]int 65 66 func paramObjectIndex(t reflect.Type) int { 67 return paramEmbedIndex(t, apiObjectType, ¶mObjectIdxCache) 68 } 69 70 func paramEmbedIndex(t reflect.Type, embed reflect.Type, cache *sync.Map) int { 71 if cached, ok := cache.Load(t); ok { 72 return cached.(int) 73 } 74 idx := -1 75 if t.Kind() == reflect.Struct { 76 for i := 0; i < t.NumField(); i++ { 77 f := t.Field(i) 78 if f.Anonymous && f.Type == embed { 79 idx = i 80 break 81 } 82 } 83 } 84 cache.Store(t, idx) 85 return idx 86 } 87 88 // isParamStruct reports whether t is a generated request param struct. 89 func isParamStruct(t reflect.Type) bool { 90 return paramObjectIndex(t) >= 0 91 } 92 93 func isStructUnion(t reflect.Type) bool { 94 if t.Kind() != reflect.Struct { 95 return false 96 } 97 for i := 0; i < t.NumField(); i++ { 98 if t.Field(i).Type == apiUnionType && t.Field(i).Anonymous { 99 return true 100 } 101 } 102 return false 103 } 104 105 func RegisterDiscriminatedUnion[T any](key string, mappings map[string]reflect.Type) { 106 var t T 107 entry := unionEntry{ 108 discriminatorKey: key, 109 variants: []UnionVariant{}, 110 } 111 for k, typ := range mappings { 112 entry.variants = append(entry.variants, UnionVariant{ 113 DiscriminatorValue: k, 114 Type: typ, 115 }) 116 } 117 unionRegistry[reflect.TypeOf(t)] = entry 118 } 119 120 // deriveDiscriminator picks the `default:`-tagged field whose values 121 // route the most variants uniquely, and returns that field's JSON 122 // name plus a value→variant-index map. 123 func deriveDiscriminator(variants []reflect.StructField) (key string, index map[string]int) { 124 byKey := map[string]map[string]int{} 125 for idx, v := range variants { 126 t := v.Type 127 for t.Kind() == reflect.Ptr { 128 t = t.Elem() 129 } 130 if t.Kind() != reflect.Struct { 131 continue 132 } 133 for i := 0; i < t.NumField(); i++ { 134 ptag, ok := parseJSONStructTag(t.Field(i)) 135 if !ok { 136 continue 137 } 138 def, ok := ptag.defaultValue.(string) 139 if !ok { 140 continue 141 } 142 m := byKey[ptag.name] 143 if m == nil { 144 m = map[string]int{} 145 byKey[ptag.name] = m 146 } 147 if _, dup := m[def]; dup { 148 m[def] = -1 149 } else { 150 m[def] = idx 151 } 152 } 153 } 154 for k, m := range byKey { 155 usable := map[string]int{} 156 for v, idx := range m { 157 if idx >= 0 { 158 usable[v] = idx 159 } 160 } 161 if len(usable) > len(index) || (len(usable) == len(index) && k < key) { 162 key, index = k, usable 163 } 164 } 165 return key, index 166 } 167 168 func (d *decoderBuilder) newStructUnionDecoder(t reflect.Type) decoderFunc { 169 type variantDecoder struct { 170 decoder decoderFunc 171 field reflect.StructField 172 } 173 var decoders []variantDecoder 174 var fields []reflect.StructField 175 for i := 0; i < t.NumField(); i++ { 176 field := t.Field(i) 177 if field.Anonymous && field.Type == apiUnionType { 178 continue 179 } 180 decoders = append(decoders, variantDecoder{d.typeDecoder(field.Type), field}) 181 fields = append(fields, field) 182 } 183 184 // To more performantly pick the correct variant, derive a 185 // discriminator from the `default:` tags. 186 discrimKey, discrimIndex := deriveDiscriminator(fields) 187 188 type discriminatedDecoder struct { 189 variantDecoder 190 discriminator any 191 } 192 discriminatedDecoders := []discriminatedDecoder{} 193 unionEntry, discriminated := unionRegistry[t] 194 for _, variant := range unionEntry.variants { 195 // For each union variant, find a matching decoder and save it 196 for _, decoder := range decoders { 197 if decoder.field.Type.Elem() == variant.Type { 198 discriminatedDecoders = append(discriminatedDecoders, discriminatedDecoder{ 199 decoder, 200 variant.DiscriminatorValue, 201 }) 202 break 203 } 204 } 205 } 206 207 return func(n gjson.Result, v reflect.Value, state *decoderState) error { 208 if discriminated && n.Type == gjson.JSON && len(unionEntry.discriminatorKey) != 0 { 209 discriminator := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value() 210 for _, decoder := range discriminatedDecoders { 211 if discriminator == decoder.discriminator { 212 inner := v.FieldByIndex(decoder.field.Index) 213 return decoder.decoder(n, inner, state) 214 } 215 } 216 return errors.New("apijson: was not able to find discriminated union variant") 217 } 218 219 scanned := discrimKey != "" && n.Type == gjson.JSON 220 if scanned { 221 if i, ok := discrimIndex[n.Get(EscapeSJSONKey(discrimKey)).String()]; ok { 222 sub := decoderState{strict: state.strict} 223 inner := v.FieldByIndex(decoders[i].field.Index) 224 if decoders[i].decoder(n, inner, &sub) == nil { 225 state.exactness.absorb(sub.exactness) 226 return nil 227 } 228 } 229 } 230 231 var best exactness 232 bestVariant := -1 233 for i, decoder := range decoders { 234 // Pointers are used to discern JSON object variants from value variants 235 if n.Type != gjson.JSON && decoder.field.Type.Kind() == reflect.Ptr { 236 continue 237 } 238 239 sub := decoderState{strict: state.strict} 240 inner := v.FieldByIndex(decoder.field.Index) 241 if err := decoder.decoder(n, inner, &sub); err != nil { 242 continue 243 } 244 if bestVariant < 0 || sub.exactness.betterThan(best) { 245 best, bestVariant = sub.exactness, i 246 // A perfect fit cannot be beaten once the discriminator 247 // scan (or the fit itself) has ruled out a later hit. 248 if best.perfect() && (scanned || best.constHit()) { 249 break 250 } 251 } 252 } 253 254 if bestVariant < 0 { 255 return errors.New("apijson: was not able to coerce type as union") 256 } 257 258 if state.strict && !best.perfect() { 259 return errors.New("apijson: was not able to coerce type as union strictly") 260 } 261 state.exactness.absorb(best) 262 263 for i := 0; i < len(decoders); i++ { 264 if i == bestVariant { 265 continue 266 } 267 v.FieldByIndex(decoders[i].field.Index).SetZero() 268 } 269 270 return nil 271 } 272 } 273 274 // newUnionDecoder returns a decoderFunc that deserializes into a union using an 275 // algorithm roughly similar to Pydantic's [smart algorithm]. 276 // 277 // Conceptually this is equivalent to choosing the best schema based on how 'exact' 278 // the deserialization is for each of the schemas. 279 // 280 // If there is a tie in the level of exactness, then the tie is broken 281 // left-to-right. 282 // 283 // [smart algorithm]: https://docs.pydantic.dev/latest/concepts/unions/#smart-mode 284 func (d *decoderBuilder) newUnionDecoder(t reflect.Type) decoderFunc { 285 unionEntry, ok := unionRegistry[t] 286 if !ok { 287 panic("apijson: couldn't find union of type " + t.String() + " in union registry") 288 } 289 decoders := []decoderFunc{} 290 for _, variant := range unionEntry.variants { 291 decoder := d.typeDecoder(variant.Type) 292 decoders = append(decoders, decoder) 293 } 294 return func(n gjson.Result, v reflect.Value, state *decoderState) error { 295 // If there is a discriminator match, circumvent the exactness logic entirely 296 for idx, variant := range unionEntry.variants { 297 decoder := decoders[idx] 298 if variant.TypeFilter != n.Type { 299 continue 300 } 301 302 if len(unionEntry.discriminatorKey) != 0 { 303 discriminatorValue := n.Get(EscapeSJSONKey(unionEntry.discriminatorKey)).Value() 304 if discriminatorValue == variant.DiscriminatorValue { 305 inner := reflect.New(variant.Type).Elem() 306 err := decoder(n, inner, state) 307 v.Set(inner) 308 return err 309 } 310 } 311 } 312 313 var bestExactness exactness 314 found := false 315 for idx, variant := range unionEntry.variants { 316 decoder := decoders[idx] 317 if variant.TypeFilter != n.Type { 318 continue 319 } 320 sub := decoderState{strict: state.strict} 321 inner := reflect.New(variant.Type).Elem() 322 err := decoder(n, inner, &sub) 323 if err != nil { 324 continue 325 } 326 if !found || sub.exactness.betterThan(bestExactness) { 327 v.Set(inner) 328 bestExactness = sub.exactness 329 found = true 330 if bestExactness.constHit() && bestExactness.perfect() { 331 return nil 332 } 333 } 334 } 335 336 if !found { 337 return errors.New("apijson: was not able to coerce type as union") 338 } 339 340 if state.strict && !bestExactness.perfect() { 341 return errors.New("apijson: was not able to coerce type as union strictly") 342 } 343 state.exactness.absorb(bestExactness) 344 345 return nil 346 } 347 }