// Package anthropic describes the Anthropic Messages API that Claude // Code speaks and converts it to and from Chat Completions. package anthropic import ( "bytes" "encoding/json" ) // MessagesRequest is the Anthropic Messages API request body. type MessagesRequest struct { Model string `json:"model"` MaxTokens int `json:"max_tokens"` Messages []Message `json:"messages"` System json.RawMessage `json:"system,omitempty"` Tools []Tool `json:"tools,omitempty"` ToolChoice json.RawMessage `json:"tool_choice,omitempty"` Temperature *float64 `json:"temperature,omitempty"` TopP *float64 `json:"top_p,omitempty"` TopK *int `json:"top_k,omitempty"` StopSequences []string `json:"stop_sequences,omitempty"` Stream bool `json:"stream,omitempty"` } // Message is one conversation turn. Content is either a plain string or // an array of blocks. type Message struct { Role string `json:"role"` Content json.RawMessage `json:"content"` } // Tool is an Anthropic tool definition. type Tool struct { Name string `json:"name"` Description string `json:"description,omitempty"` InputSchema json.RawMessage `json:"input_schema,omitempty"` } // Block is a single content block inside a message. Only the fields // belonging to Type are populated. type Block struct { Type string `json:"type"` // text Text string `json:"text,omitempty"` // image Source *ImageSource `json:"source,omitempty"` // tool_use ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` Input json.RawMessage `json:"input,omitempty"` // tool_result ToolUseID string `json:"tool_use_id,omitempty"` Content json.RawMessage `json:"content,omitempty"` IsError bool `json:"is_error,omitempty"` // cache_control is accepted but never forwarded upstream. CacheControl json.RawMessage `json:"cache_control,omitempty"` } // ImageSource is an image referenced by a user message. type ImageSource struct { Type string `json:"type"` MediaType string `json:"media_type,omitempty"` Data string `json:"data,omitempty"` URL string `json:"url,omitempty"` } // MessagesResponse is the Anthropic Messages API response body. type MessagesResponse struct { ID string `json:"id"` Type string `json:"type"` Role string `json:"role"` Content []Block `json:"content"` Model string `json:"model"` StopReason string `json:"stop_reason"` StopSequence *string `json:"stop_sequence"` Usage Usage `json:"usage"` } // Usage reports token counts. type Usage struct { InputTokens int `json:"input_tokens"` OutputTokens int `json:"output_tokens"` } // ErrorResponse is the Anthropic error envelope. type ErrorResponse struct { Type string `json:"type"` Error Detail `json:"error"` } // Detail is the error payload. type Detail struct { Type string `json:"type"` Message string `json:"message"` } // CountTokensResponse is the response of /v1/messages/count_tokens. type CountTokensResponse struct { InputTokens int `json:"input_tokens"` } // ParseContent decodes a message content field into blocks. A plain // string becomes a single text block. func ParseContent(raw json.RawMessage) ([]Block, error) { trimmed := bytes.TrimSpace(raw) if len(trimmed) == 0 || string(trimmed) == "null" { return nil, nil } if trimmed[0] == '"' { var s string if err := json.Unmarshal(trimmed, &s); err != nil { return nil, err } if s == "" { return nil, nil } return []Block{{Type: "text", Text: s}}, nil } var blocks []Block if err := json.Unmarshal(trimmed, &blocks); err != nil { return nil, err } return blocks, nil } // SystemText flattens a system field (string or block array) into plain // text. func SystemText(raw json.RawMessage) string { blocks, err := ParseContent(raw) if err != nil { return "" } var parts []string for _, b := range blocks { if b.Type == "text" && b.Text != "" { parts = append(parts, b.Text) } } return joinBlocks(parts) } // TextOf extracts plain text from a tool_result content field. func TextOf(raw json.RawMessage) string { blocks, err := ParseContent(raw) if err != nil { return "" } var sb bytes.Buffer for _, b := range blocks { if b.Type == "text" { sb.WriteString(b.Text) } } return sb.String() } func joinBlocks(parts []string) string { if len(parts) == 0 { return "" } out := parts[0] for _, p := range parts[1:] { out += "\n\n" + p } return out }