github.com/anthropics/anthropic-sdk-go@v1.70.1/messageutil.go (about) 1 package anthropic 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "strings" 7 8 "github.com/tidwall/gjson" 9 "github.com/tidwall/sjson" 10 11 "github.com/anthropics/anthropic-sdk-go/internal/apijson" 12 "github.com/anthropics/anthropic-sdk-go/internal/paramutil" 13 "github.com/anthropics/anthropic-sdk-go/packages/param" 14 ) 15 16 // Accumulate builds up the Message incrementally from a MessageStreamEvent. The Message then can be used as 17 // any other Message, including the Message.JSON field, which holds the wire JSON with the streamed deltas 18 // applied and is complete once message_stop arrives. 19 // 20 // message := anthropic.Message{} 21 // for stream.Next() { 22 // event := stream.Current() 23 // message.Accumulate(event) 24 // } 25 func (acc *Message) Accumulate(event MessageStreamEventUnion) error { 26 if acc == nil { 27 return fmt.Errorf("accumulate: cannot accumulate into nil Message") 28 } 29 30 switch event.Type { 31 case "message_start": 32 *acc = event.Message 33 case "message_delta": 34 // stop_reason, stop_sequence and stop_details are always sent and null is 35 // their final value when the turn carries no such detail. 36 acc.StopReason = event.Delta.StopReason 37 acc.StopSequence = event.Delta.StopSequence 38 acc.StopDetails = event.Delta.StopDetails 39 if event.Delta.JSON.Container.Valid() { 40 acc.Container = event.Delta.Container 41 } 42 // Every usage count here is a cumulative whole-message total, so it 43 // overwrites rather than adds; the ones that do not apply are omitted, and 44 // message_start keeps the last word on those. 45 acc.Usage.OutputTokens = event.Usage.OutputTokens 46 if event.Usage.JSON.InputTokens.Valid() { 47 acc.Usage.InputTokens = event.Usage.InputTokens 48 } 49 if event.Usage.JSON.CacheCreationInputTokens.Valid() { 50 acc.Usage.CacheCreationInputTokens = event.Usage.CacheCreationInputTokens 51 } 52 if event.Usage.JSON.CacheReadInputTokens.Valid() { 53 acc.Usage.CacheReadInputTokens = event.Usage.CacheReadInputTokens 54 } 55 if event.Usage.JSON.ServerToolUse.Valid() { 56 acc.Usage.ServerToolUse = event.Usage.ServerToolUse 57 } 58 if event.Usage.JSON.OutputTokensDetails.Valid() { 59 acc.Usage.OutputTokensDetails = event.Usage.OutputTokensDetails 60 } 61 acc.JSON.raw = mergeRaw(acc.JSON.raw, "", event.Delta.JSON.raw) 62 acc.JSON.raw = mergeRaw(acc.JSON.raw, "usage", event.Usage.RawJSON()) 63 case "content_block_start": 64 // Content blocks start in index order with no gaps: a start event always 65 // addresses the slot right after the previous block, even when deltas and 66 // stops for still-open blocks interleave after it. 67 if event.Index != int64(len(acc.Content)) { 68 return fmt.Errorf("received event of type %s for content block at index %d, expected index %d", event.Type, event.Index, len(acc.Content)) 69 } 70 acc.Content = append(acc.Content, ContentBlockUnion{}) 71 err := acc.Content[event.Index].UnmarshalJSON([]byte(event.ContentBlock.RawJSON())) 72 if err != nil { 73 return err 74 } 75 case "content_block_delta": 76 if err := checkContentBlockIndex(event.Type, event.Index, len(acc.Content)); err != nil { 77 return err 78 } 79 cb := &acc.Content[event.Index] 80 switch event.Delta.Type { 81 case "text_delta": 82 cb.Text += event.Delta.Text 83 case "input_json_delta": 84 if len(event.Delta.PartialJSON) != 0 { 85 if string(cb.Input) == "{}" { 86 cb.Input = []byte(event.Delta.PartialJSON) 87 } else { 88 cb.Input = append(cb.Input, event.Delta.PartialJSON...) 89 } 90 } 91 case "thinking_delta": 92 cb.Thinking += event.Delta.Thinking 93 case "signature_delta": 94 cb.Signature += event.Delta.Signature 95 case "citations_delta": 96 citation := TextCitationUnion{} 97 err := citation.UnmarshalJSON([]byte(event.Delta.Citation.RawJSON())) 98 if err != nil { 99 return fmt.Errorf("could not unmarshal citation delta into citation type: %w", err) 100 } 101 cb.Citations = append(cb.Citations, citation) 102 } 103 case "message_stop": 104 // A block whose stop event never arrived has not been refreshed yet. 105 for i := range acc.Content { 106 refreshContentBlockRaw(&acc.Content[i]) 107 } 108 acc.JSON.raw, _ = sjson.SetRaw(acc.JSON.raw, "content", rawArray(acc.Content)) 109 case "content_block_stop": 110 if err := checkContentBlockIndex(event.Type, event.Index, len(acc.Content)); err != nil { 111 return err 112 } 113 refreshContentBlockRaw(&acc.Content[event.Index]) 114 } 115 116 return nil 117 } 118 119 // refreshContentBlockRaw overlays the delta-mutated fields onto the block's 120 // wire JSON, leaving a block that received no deltas byte for byte. 121 func refreshContentBlockRaw(cb *ContentBlockUnion) { 122 raw := cb.JSON.raw 123 if cb.Text != "" { 124 raw, _ = sjson.SetRaw(raw, "text", jsonString(cb.Text)) 125 } 126 if cb.Thinking != "" { 127 raw, _ = sjson.SetRaw(raw, "thinking", jsonString(cb.Thinking)) 128 } 129 if cb.Signature != "" { 130 raw, _ = sjson.SetRaw(raw, "signature", jsonString(cb.Signature)) 131 } 132 if json.Valid(cb.Input) { 133 raw, _ = sjson.SetRaw(raw, "input", string(cb.Input)) 134 } else if len(cb.Input) > 0 { 135 // A cut-off tool call left non-JSON input; empty it so the block marshals. 136 cb.Input = json.RawMessage(`{}`) 137 } 138 if len(cb.Citations) > 0 { 139 raw, _ = sjson.SetRaw(raw, "citations", rawArray(cb.Citations)) 140 } 141 cb.JSON.raw = raw 142 } 143 144 // jsonString encodes s as the server does, leaving <, > and & literal, which 145 // sjson.Set does inconsistently. 146 func jsonString(s string) string { 147 var b strings.Builder 148 enc := json.NewEncoder(&b) 149 enc.SetEscapeHTML(false) 150 enc.Encode(s) 151 return strings.TrimSuffix(b.String(), "\n") 152 } 153 154 // mergeRaw merges the wire object src into dst at path, skipping the nulls 155 // message_delta sends for values it is not updating. 156 func mergeRaw(dst, path, src string) string { 157 if path != "" { 158 path += "." 159 } 160 gjson.Parse(src).ForEach(func(key, value gjson.Result) bool { 161 if value.Type == gjson.Null { 162 return true 163 } 164 dst, _ = sjson.SetRaw(dst, path+apijson.EscapeSJSONKey(key.String()), value.Raw) 165 return true 166 }) 167 return dst 168 } 169 170 // rawArray renders the wire JSON of each item as an array, skipping any item 171 // with none, such as a block whose start event carried no content_block. 172 func rawArray[T interface{ RawJSON() string }](items []T) string { 173 raws := make([]string, 0, len(items)) 174 for _, item := range items { 175 if raw := item.RawJSON(); raw != "" { 176 raws = append(raws, raw) 177 } 178 } 179 return "[" + strings.Join(raws, ",") + "]" 180 } 181 182 // checkContentBlockIndex reports an error if a stream event's index does not 183 // address one of the numBlocks content blocks accumulated so far. Delta and 184 // stop events may interleave across open content blocks, so they address 185 // blocks by index rather than applying to the most recently started block. 186 func checkContentBlockIndex(eventType string, index int64, numBlocks int) error { 187 if index < 0 || index >= int64(numBlocks) { 188 return fmt.Errorf("received event of type %s for content block at index %d but there are only %d content blocks", eventType, index, numBlocks) 189 } 190 return nil 191 } 192 193 // ToParam converters 194 195 func (r Message) ToParam() MessageParam { 196 var p MessageParam 197 p.Role = MessageParamRole(r.Role) 198 p.Content = make([]ContentBlockParamUnion, len(r.Content)) 199 for i, c := range r.Content { 200 p.Content[i] = c.ToParam() 201 } 202 return p 203 } 204 205 func (r ContentBlockUnion) ToParam() ContentBlockParamUnion { 206 return r.AsAny().toParamUnion() 207 } 208 209 func (variant TextBlock) toParamUnion() ContentBlockParamUnion { 210 p := variant.ToParam() 211 return ContentBlockParamUnion{OfText: &p} 212 } 213 214 func (variant ToolUseBlock) toParamUnion() ContentBlockParamUnion { 215 p := variant.ToParam() 216 return ContentBlockParamUnion{OfToolUse: &p} 217 } 218 219 func (variant WebSearchToolResultBlock) toParamUnion() ContentBlockParamUnion { 220 p := variant.ToParam() 221 return ContentBlockParamUnion{OfWebSearchToolResult: &p} 222 } 223 224 func (variant ServerToolUseBlock) toParamUnion() ContentBlockParamUnion { 225 p := variant.ToParam() 226 return ContentBlockParamUnion{OfServerToolUse: &p} 227 } 228 229 func (variant ThinkingBlock) toParamUnion() ContentBlockParamUnion { 230 p := variant.ToParam() 231 return ContentBlockParamUnion{OfThinking: &p} 232 } 233 234 func (variant RedactedThinkingBlock) toParamUnion() ContentBlockParamUnion { 235 p := variant.ToParam() 236 return ContentBlockParamUnion{OfRedactedThinking: &p} 237 } 238 239 func (r RedactedThinkingBlock) ToParam() RedactedThinkingBlockParam { 240 var p RedactedThinkingBlockParam 241 p.Type = r.Type 242 p.Data = r.Data 243 return p 244 } 245 246 func (r ToolUseBlock) ToParam() ToolUseBlockParam { 247 var toolUse ToolUseBlockParam 248 toolUse.Type = r.Type 249 toolUse.ID = r.ID 250 toolUse.Input = r.Input 251 toolUse.Name = r.Name 252 toolUse.ToolsetName = paramutil.ToOpt(r.ToolsetName, r.JSON.ToolsetName) 253 if r.JSON.Caller.Valid() { 254 toolUse.Caller = r.Caller.toParam() 255 } 256 return toolUse 257 } 258 259 // toParam converts a caller. The other three caller unions share this layout 260 // and convert through it. 261 func (r ToolUseBlockCallerUnion) toParam() ToolUseBlockParamCallerUnion { 262 var p ToolUseBlockParamCallerUnion 263 switch v := r.AsAny().(type) { 264 case DirectCaller: 265 c := v.ToParam() 266 p.OfDirect = &c 267 case ServerToolCaller: 268 c := v.ToParam() 269 p.OfCodeExecution20250825 = &c 270 case ServerToolCaller20260120: 271 c := v.ToParam() 272 p.OfCodeExecution20260120 = &c 273 default: 274 p = param.Override[ToolUseBlockParamCallerUnion](json.RawMessage(r.RawJSON())) 275 } 276 return p 277 } 278 279 func (r ServerToolUseBlockCallerUnion) toParam() ServerToolUseBlockParamCallerUnion { 280 return ServerToolUseBlockParamCallerUnion(ToolUseBlockCallerUnion(r).toParam()) 281 } 282 283 func (r WebSearchToolResultBlockCallerUnion) toParam() WebSearchToolResultBlockParamCallerUnion { 284 return WebSearchToolResultBlockParamCallerUnion(ToolUseBlockCallerUnion(r).toParam()) 285 } 286 287 func (r WebFetchToolResultBlockCallerUnion) toParam() WebFetchToolResultBlockParamCallerUnion { 288 return WebFetchToolResultBlockParamCallerUnion(ToolUseBlockCallerUnion(r).toParam()) 289 } 290 291 func (citationVariant CitationCharLocation) toParamUnion() TextCitationParamUnion { 292 var citationParam CitationCharLocationParam 293 citationParam.Type = citationVariant.Type 294 citationParam.DocumentTitle = paramutil.ToOpt(citationVariant.DocumentTitle, citationVariant.JSON.DocumentTitle) 295 citationParam.CitedText = citationVariant.CitedText 296 citationParam.DocumentIndex = citationVariant.DocumentIndex 297 citationParam.EndCharIndex = citationVariant.EndCharIndex 298 citationParam.StartCharIndex = citationVariant.StartCharIndex 299 return TextCitationParamUnion{OfCharLocation: &citationParam} 300 } 301 302 func (citationVariant CitationPageLocation) toParamUnion() TextCitationParamUnion { 303 var citationParam CitationPageLocationParam 304 citationParam.Type = citationVariant.Type 305 citationParam.DocumentTitle = paramutil.ToOpt(citationVariant.DocumentTitle, citationVariant.JSON.DocumentTitle) 306 citationParam.CitedText = citationVariant.CitedText 307 citationParam.DocumentIndex = citationVariant.DocumentIndex 308 citationParam.EndPageNumber = citationVariant.EndPageNumber 309 citationParam.StartPageNumber = citationVariant.StartPageNumber 310 return TextCitationParamUnion{OfPageLocation: &citationParam} 311 } 312 313 func (citationVariant CitationContentBlockLocation) toParamUnion() TextCitationParamUnion { 314 var citationParam CitationContentBlockLocationParam 315 citationParam.Type = citationVariant.Type 316 citationParam.DocumentTitle = paramutil.ToOpt(citationVariant.DocumentTitle, citationVariant.JSON.DocumentTitle) 317 citationParam.CitedText = citationVariant.CitedText 318 citationParam.DocumentIndex = citationVariant.DocumentIndex 319 citationParam.EndBlockIndex = citationVariant.EndBlockIndex 320 citationParam.StartBlockIndex = citationVariant.StartBlockIndex 321 return TextCitationParamUnion{OfContentBlockLocation: &citationParam} 322 } 323 324 func (citationVariant CitationsSearchResultLocation) toParamUnion() TextCitationParamUnion { 325 var citationParam CitationSearchResultLocationParam 326 citationParam.Type = citationVariant.Type 327 citationParam.CitedText = citationVariant.CitedText 328 citationParam.Title = paramutil.ToOpt(citationVariant.Title, citationVariant.JSON.Title) 329 citationParam.EndBlockIndex = citationVariant.EndBlockIndex 330 citationParam.SearchResultIndex = citationVariant.SearchResultIndex 331 citationParam.Source = citationVariant.Source 332 citationParam.StartBlockIndex = citationVariant.StartBlockIndex 333 return TextCitationParamUnion{OfSearchResultLocation: &citationParam} 334 } 335 336 func (citationVariant CitationsWebSearchResultLocation) toParamUnion() TextCitationParamUnion { 337 var citationParam CitationWebSearchResultLocationParam 338 citationParam.Type = citationVariant.Type 339 citationParam.CitedText = citationVariant.CitedText 340 citationParam.Title = paramutil.ToOpt(citationVariant.Title, citationVariant.JSON.Title) 341 citationParam.EncryptedIndex = citationVariant.EncryptedIndex 342 citationParam.URL = citationVariant.URL 343 return TextCitationParamUnion{OfWebSearchResultLocation: &citationParam} 344 } 345 346 func (r TextBlock) ToParam() TextBlockParam { 347 var p TextBlockParam 348 p.Type = r.Type 349 p.Text = r.Text 350 351 // Distinguish between a nil and zero length slice, since some compatible 352 // APIs may not require citations. 353 if r.Citations != nil { 354 p.Citations = make([]TextCitationParamUnion, 0, len(r.Citations)) 355 } 356 357 for _, citation := range r.Citations { 358 p.Citations = append(p.Citations, citation.AsAny().toParamUnion()) 359 } 360 361 return p 362 } 363 364 func (r ThinkingBlock) ToParam() ThinkingBlockParam { 365 var p ThinkingBlockParam 366 p.Type = r.Type 367 p.Signature = r.Signature 368 p.Thinking = r.Thinking 369 return p 370 } 371 372 func (r ServerToolUseBlock) ToParam() ServerToolUseBlockParam { 373 var p ServerToolUseBlockParam 374 p.Type = r.Type 375 p.ID = r.ID 376 p.Input = r.Input 377 p.Name = ServerToolUseBlockParamName(r.Name) 378 if r.JSON.Caller.Valid() { 379 p.Caller = r.Caller.toParam() 380 } 381 return p 382 } 383 384 func (r WebSearchToolResultBlock) ToParam() WebSearchToolResultBlockParam { 385 var p WebSearchToolResultBlockParam 386 p.Type = r.Type 387 p.ToolUseID = r.ToolUseID 388 p.Content = r.Content.ToParam() 389 if r.JSON.Caller.Valid() { 390 p.Caller = r.Caller.toParam() 391 } 392 return p 393 } 394 395 func (r WebSearchResultBlock) ToParam() WebSearchResultBlockParam { 396 var p WebSearchResultBlockParam 397 p.Type = r.Type 398 p.EncryptedContent = r.EncryptedContent 399 p.Title = r.Title 400 p.URL = r.URL 401 p.PageAge = paramutil.ToOpt(r.PageAge, r.JSON.PageAge) 402 return p 403 } 404 405 func (r WebSearchToolResultBlockContentUnion) ToParam() WebSearchToolResultBlockParamContentUnion { 406 var p WebSearchToolResultBlockParamContentUnion 407 408 if r.JSON.OfWebSearchResultBlockArray.Valid() { 409 // content is required, so send [] rather than omitting it. 410 p.OfWebSearchToolResultBlockItem = make([]WebSearchResultBlockParam, 0, len(r.OfWebSearchResultBlockArray)) 411 for _, block := range r.OfWebSearchResultBlockArray { 412 p.OfWebSearchToolResultBlockItem = append(p.OfWebSearchToolResultBlockItem, block.ToParam()) 413 } 414 return p 415 } 416 417 p.OfRequestWebSearchToolResultError = &WebSearchToolRequestErrorParam{ 418 ErrorCode: WebSearchToolResultErrorCode(r.ErrorCode), 419 } 420 return p 421 } 422 423 func (variant WebFetchToolResultBlock) toParamUnion() ContentBlockParamUnion { 424 p := variant.ToParam() 425 return ContentBlockParamUnion{OfWebFetchToolResult: &p} 426 } 427 428 func (variant CodeExecutionToolResultBlock) toParamUnion() ContentBlockParamUnion { 429 p := variant.ToParam() 430 return ContentBlockParamUnion{OfCodeExecutionToolResult: &p} 431 } 432 433 func (variant BashCodeExecutionToolResultBlock) toParamUnion() ContentBlockParamUnion { 434 p := variant.ToParam() 435 return ContentBlockParamUnion{OfBashCodeExecutionToolResult: &p} 436 } 437 438 func (variant TextEditorCodeExecutionToolResultBlock) toParamUnion() ContentBlockParamUnion { 439 p := variant.ToParam() 440 return ContentBlockParamUnion{OfTextEditorCodeExecutionToolResult: &p} 441 } 442 443 func (variant ToolSearchToolResultBlock) toParamUnion() ContentBlockParamUnion { 444 p := variant.ToParam() 445 return ContentBlockParamUnion{OfToolSearchToolResult: &p} 446 } 447 448 func (variant ContainerUploadBlock) toParamUnion() ContentBlockParamUnion { 449 p := variant.ToParam() 450 return ContentBlockParamUnion{OfContainerUpload: &p} 451 } 452 453 func (r WebFetchToolResultBlock) ToParam() WebFetchToolResultBlockParam { 454 var p WebFetchToolResultBlockParam 455 p.Type = r.Type 456 p.ToolUseID = r.ToolUseID 457 if r.Content.JSON.ErrorCode.Valid() { 458 p.Content.OfRequestWebFetchToolResultError = &WebFetchToolResultErrorBlockParam{ 459 ErrorCode: r.Content.ErrorCode, 460 } 461 } else { 462 p.Content = param.Override[WebFetchToolResultBlockParamContentUnion](json.RawMessage(r.Content.RawJSON())) 463 } 464 if r.JSON.Caller.Valid() { 465 p.Caller = r.Caller.toParam() 466 } 467 return p 468 } 469 470 func (r ContainerUploadBlock) ToParam() ContainerUploadBlockParam { 471 var p ContainerUploadBlockParam 472 p.Type = r.Type 473 p.FileID = r.FileID 474 return p 475 } 476 477 func (r BashCodeExecutionToolResultBlock) ToParam() BashCodeExecutionToolResultBlockParam { 478 var p BashCodeExecutionToolResultBlockParam 479 p.Type = r.Type 480 p.ToolUseID = r.ToolUseID 481 482 if r.Content.JSON.ErrorCode.Valid() { 483 p.Content.OfRequestBashCodeExecutionToolResultError = &BashCodeExecutionToolResultErrorParam{ 484 ErrorCode: BashCodeExecutionToolResultErrorCode(r.Content.ErrorCode), 485 } 486 } else { 487 requestBashContentResult := &BashCodeExecutionResultBlockParam{ 488 // content is required, so send [] rather than omitting it. 489 Content: make([]BashCodeExecutionOutputBlockParam, 0, len(r.Content.Content)), 490 ReturnCode: r.Content.ReturnCode, 491 Stderr: r.Content.Stderr, 492 Stdout: r.Content.Stdout, 493 } 494 for _, block := range r.Content.Content { 495 requestBashContentResult.Content = append(requestBashContentResult.Content, block.ToParam()) 496 } 497 p.Content.OfRequestBashCodeExecutionResultBlock = requestBashContentResult 498 } 499 500 return p 501 } 502 503 func (r BashCodeExecutionOutputBlock) ToParam() BashCodeExecutionOutputBlockParam { 504 var p BashCodeExecutionOutputBlockParam 505 p.Type = r.Type 506 p.FileID = r.FileID 507 return p 508 } 509 510 func (r CodeExecutionToolResultBlock) ToParam() CodeExecutionToolResultBlockParam { 511 var p CodeExecutionToolResultBlockParam 512 p.Type = r.Type 513 p.ToolUseID = r.ToolUseID 514 // content is required, so send [] rather than omitting it. 515 files := make([]CodeExecutionOutputBlockParam, 0, len(r.Content.Content)) 516 for _, block := range r.Content.Content { 517 files = append(files, block.ToParam()) 518 } 519 switch { 520 case r.Content.JSON.ErrorCode.Valid(): 521 p.Content.OfRequestCodeExecutionToolResultError = &CodeExecutionToolResultErrorParam{ 522 ErrorCode: r.Content.ErrorCode, 523 } 524 case r.Content.JSON.EncryptedStdout.Valid(): 525 p.Content.OfRequestEncryptedCodeExecutionResultBlock = &EncryptedCodeExecutionResultBlockParam{ 526 Content: files, 527 EncryptedStdout: r.Content.EncryptedStdout, 528 ReturnCode: r.Content.ReturnCode, 529 Stderr: r.Content.Stderr, 530 } 531 default: 532 p.Content.OfRequestCodeExecutionResultBlock = &CodeExecutionResultBlockParam{ 533 Content: files, 534 ReturnCode: r.Content.ReturnCode, 535 Stderr: r.Content.Stderr, 536 Stdout: r.Content.Stdout, 537 } 538 } 539 return p 540 } 541 542 func (r CodeExecutionOutputBlock) ToParam() CodeExecutionOutputBlockParam { 543 var p CodeExecutionOutputBlockParam 544 p.Type = r.Type 545 p.FileID = r.FileID 546 return p 547 } 548 549 func (r TextEditorCodeExecutionToolResultBlock) ToParam() TextEditorCodeExecutionToolResultBlockParam { 550 var p TextEditorCodeExecutionToolResultBlockParam 551 p.Type = r.Type 552 p.ToolUseID = r.ToolUseID 553 if r.Content.JSON.ErrorCode.Valid() { 554 p.Content.OfRequestTextEditorCodeExecutionToolResultError = &TextEditorCodeExecutionToolResultErrorParam{ 555 ErrorCode: TextEditorCodeExecutionToolResultErrorCode(r.Content.ErrorCode), 556 ErrorMessage: paramutil.ToOpt(r.Content.ErrorMessage, r.Content.JSON.ErrorMessage), 557 } 558 } else { 559 p.Content = param.Override[TextEditorCodeExecutionToolResultBlockParamContentUnion](json.RawMessage(r.Content.RawJSON())) 560 } 561 return p 562 } 563 564 func (r ToolSearchToolResultBlock) ToParam() ToolSearchToolResultBlockParam { 565 var p ToolSearchToolResultBlockParam 566 p.Type = r.Type 567 p.ToolUseID = r.ToolUseID 568 if r.Content.JSON.ErrorCode.Valid() { 569 p.Content.OfRequestToolSearchToolResultError = &ToolSearchToolResultErrorParam{ 570 ErrorCode: ToolSearchToolResultErrorCode(r.Content.ErrorCode), 571 ErrorMessage: paramutil.ToOpt(r.Content.ErrorMessage, r.Content.JSON.ErrorMessage), 572 } 573 } else { 574 p.Content.OfRequestToolSearchToolSearchResultBlock = &ToolSearchToolSearchResultBlockParam{ 575 // tool_references is required, so send [] rather than omitting it. 576 ToolReferences: make([]ToolReferenceBlockParam, 0, len(r.Content.ToolReferences)), 577 } 578 for _, block := range r.Content.ToolReferences { 579 p.Content.OfRequestToolSearchToolSearchResultBlock.ToolReferences = append( 580 p.Content.OfRequestToolSearchToolSearchResultBlock.ToolReferences, 581 block.ToParam(), 582 ) 583 } 584 } 585 return p 586 } 587 588 func (r ToolReferenceBlock) ToParam() ToolReferenceBlockParam { 589 var p ToolReferenceBlockParam 590 p.Type = r.Type 591 p.ToolName = r.ToolName 592 return p 593 }