-
Notifications
You must be signed in to change notification settings - Fork 46
Map hostedtool.CodeInterpreter to the Anthropic code_execution tool and parse its result blocks #637
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Map hostedtool.CodeInterpreter to the Anthropic code_execution tool and parse its result blocks #637
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,6 +5,7 @@ package anthropicprovider | |
| import ( | ||
| "cmp" | ||
| "context" | ||
| "encoding/base64" | ||
| "encoding/json" | ||
| "fmt" | ||
| "iter" | ||
|
|
@@ -20,6 +21,7 @@ import ( | |
| "github.com/microsoft/agent-framework-go/agent/harness/toolautocall" | ||
| "github.com/microsoft/agent-framework-go/message" | ||
| "github.com/microsoft/agent-framework-go/tool" | ||
| "github.com/microsoft/agent-framework-go/tool/hostedtool" | ||
| ) | ||
|
|
||
| type messageNewParamsOpt anthropic.MessageNewParams | ||
|
|
@@ -295,10 +297,80 @@ func (a *client) buildBlock(index int, v any, contents []message.Content, functi | |
| Name: v.Name, | ||
| Arguments: string(v.Input), | ||
| } | ||
| case anthropic.ServerToolUseBlock: | ||
| if v.Name == anthropic.ServerToolUseBlockNameCodeExecution { | ||
| call := &message.CodeInterpreterToolCallContent{ | ||
| CallID: v.ID, | ||
| ContentHeader: message.ContentHeader{ | ||
| RawRepresentation: v, | ||
| }, | ||
| } | ||
| if code := codeExecutionInput(v.Input); code != "" { | ||
| call.Inputs = message.Contents{ | ||
| &message.DataContent{ | ||
| Data: base64.StdEncoding.EncodeToString([]byte(code)), | ||
| MediaType: "text/x-python", | ||
| }, | ||
| } | ||
| } | ||
| contents = append(contents, call) | ||
| } | ||
| case anthropic.CodeExecutionToolResultBlock: | ||
| result := &message.CodeInterpreterToolResultContent{ | ||
| CallID: v.ToolUseID, | ||
| ContentHeader: message.ContentHeader{ | ||
| RawRepresentation: v, | ||
| }, | ||
| } | ||
| res := v.Content | ||
| if res.Stdout != "" { | ||
| result.Outputs = append(result.Outputs, &message.TextContent{ | ||
| Text: res.Stdout, | ||
| ContentHeader: message.ContentHeader{RawRepresentation: res}, | ||
| }) | ||
| } | ||
| if res.Stderr != "" { | ||
| result.Outputs = append(result.Outputs, &message.TextContent{ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Parity issue: The upstream Python implementation ( Suggestion: use Upstream reference: |
||
| Text: res.Stderr, | ||
| ContentHeader: message.ContentHeader{RawRepresentation: res}, | ||
| }) | ||
| } | ||
| if res.ErrorCode != "" { | ||
| result.Outputs = append(result.Outputs, &message.TextContent{ | ||
| Text: string(res.ErrorCode), | ||
| ContentHeader: message.ContentHeader{RawRepresentation: res}, | ||
| }) | ||
| } | ||
| for _, out := range res.Content { | ||
| result.Outputs = append(result.Outputs, &message.HostedFileContent{ | ||
| FileID: out.FileID, | ||
| ContentHeader: message.ContentHeader{RawRepresentation: out}, | ||
| }) | ||
| } | ||
| contents = append(contents, result) | ||
| } | ||
| return contents | ||
| } | ||
|
|
||
| // codeExecutionInput extracts the source code from an Anthropic server_tool_use | ||
| // code_execution input payload (shaped as {"code": "..."}). | ||
| func codeExecutionInput(input any) string { | ||
| if input == nil { | ||
| return "" | ||
| } | ||
| raw, err := json.Marshal(input) | ||
| if err != nil { | ||
| return "" | ||
| } | ||
| var parsed struct { | ||
| Code string `json:"code"` | ||
| } | ||
| if err := json.Unmarshal(raw, &parsed); err != nil { | ||
| return "" | ||
| } | ||
| return parsed.Code | ||
| } | ||
|
|
||
| // citationAnnotations converts Anthropic text-block citations into | ||
| // [message.CitationAnnotation] values. It returns nil when there are no | ||
| // citations so callers can leave the annotations slice unset. | ||
|
|
@@ -398,6 +470,14 @@ func (a *client) buildMessageParams(messages []*message.Message, opts []agent.Op | |
| toolParam.OfTool.Description = anthropic.String(description) | ||
| } | ||
| tools = append(tools, toolParam) | ||
| continue | ||
| } | ||
| if _, ok := tl.(*hostedtool.CodeInterpreter); ok { | ||
| // Map the hosted code-interpreter marker onto Anthropic's server-side | ||
| // code_execution tool, mirroring the OpenAI Responses provider. | ||
| tools = append(tools, anthropic.ToolUnionParam{ | ||
| OfCodeExecutionTool20250825: &anthropic.CodeExecutionTool20250825Param{}, | ||
| }) | ||
| } | ||
| } | ||
| if len(tools) > 0 { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Parity note:
code_executiontool-call input encoding differs from PythonThe Python implementation (
_chat_client.py, line 1252–1265) wraps the raw input block asContent.from_text(text=str(content_block.input))— a plainTextContentcontaining the string representation of the input dict.This Go implementation extracts the
"code"key from the JSON payload and stores it base64-encoded asDataContentwithmedia_type: text/x-python. While arguably more structured (and analogous to how OpenAI Responses surfaces it), this is a deliberate cross-SDK divergence: a consumer inspectingCodeInterpreterToolCallContent.Inputs[0]will receive aDataContentin Go but aTextContentin Python.If this encoding difference is intentional, please document it (e.g., in a
CHANGELOGentry or an inline comment noting the divergence from Python). If it should align, switch to&message.TextContent{Text: code}or align the Python side to emitDataContent.