Summary
The Go SDK's ai/tool_calling.go has several code quality issues and parity gaps with the Python and TypeScript SDKs. This issue tracks the Go-specific findings from a cross-SDK audit.
Issues Found
1. Duplicated CapabilityToToolDefinition logic (L49-99)
The CapabilityToToolDefinition function has two nearly identical code blocks for ReasonerCapability (L49-73) and SkillCapability (L74-99):
case types.ReasonerCapability:
desc := ""
if c.Description != nil { desc = *c.Description }
if desc == "" { desc = "Call " + c.InvocationTarget }
params := c.InputSchema
if params == nil { params = map[string]interface{}{...} }
if _, ok := params["type"]; !ok { params = map[string]interface{}{...} }
return ToolDefinition{...}
case types.SkillCapability:
// EXACT SAME CODE as above
Fix: Extract a shared helper:
func capabilityToTool(invocationTarget string, description *string, inputSchema map[string]interface{}) ToolDefinition {
desc := ""
if description != nil { desc = *description }
if desc == "" { desc = "Call " + invocationTarget }
params := inputSchema
if params == nil { params = map[string]interface{}{"type": "object", "properties": map[string]interface{}{}} }
if _, ok := params["type"]; !ok { params = map[string]interface{}{"type": "object", "properties": params} }
return ToolDefinition{
Type: "function",
Function: ToolFunction{Name: invocationTarget, Description: desc, Parameters: params},
}
}
2. No ToolCallResponse wrapper (API parity gap)
The Python SDK has a ToolCallResponse class (tool_calling.py:85-124) that wraps the LLM response with the ToolCallTrace, provides a .text property, and delegates attribute access to the underlying response for backward compatibility.
The Go SDK's ExecuteToolCallLoop returns (*Response, *ToolCallTrace, error) as separate values — there's no unified wrapper.
Fix: Add a ToolCallResult struct:
type ToolCallResult struct {
Response *Response
Trace *ToolCallTrace
}
func (r *ToolCallResult) Text() string {
return r.Trace.FinalResponse
}
3. No PromptConfig / PromptTemplates (per #229)
Issue #229 proposes a PromptConfig struct for Go but doesn't exist yet. The Go SDK has the same hardcoded strings as the other SDKs:
| Hardcoded String |
Location |
"Tool call limit reached. Please provide a final response." |
tool_calling.go:167 |
{"error": err.Error(), "tool": tc.Function.Name} |
tool_calling.go:193-196 |
Raw json.Marshal(result) with no framing |
tool_calling.go:204 |
These should be extracted into a PromptConfig struct (tracked by #229, but included here for Go-specific tracking).
4. No tool system prompt
All 3 SDKs send tools to the LLM without any system-level instructions on how to use them. The Python and TS SDKs also lack this, but the Go SDK's ExecuteToolCallLoop doesn't even accept an optional system message parameter — it only takes messages and tools.
Fix: Add an optional system prompt to ToolCallConfig:
type ToolCallConfig struct {
MaxTurns int
MaxToolCalls int
SystemPrompt string // Optional system prompt for tool usage guidance
}
5. No tool name sanitization (parity gap)
Python SDK has _sanitize_tool_name() / _unsanitize_tool_name() to replace colons with double-underscores for LLM provider compatibility (many providers reject colons in function names).
TS SDK has sanitizeToolName() / unsanitizeToolName() — same logic.
Go SDK passes c.InvocationTarget directly as the tool function name (tool_calling.go:69, 93) — no sanitization. This will break with providers like Google that reject colons.
Fix: Add sanitization matching the Python/TS pattern:
func sanitizeToolName(name string) string {
return strings.ReplaceAll(name, ":", "__")
}
func unsanitizeToolName(name string) string {
return strings.ReplaceAll(name, "__", ":")
}
6. Duplicate ErrorResponse in control plane (cross-ref with #119)
Found 2 duplicate ErrorResponse struct definitions:
control-plane/internal/handlers/memory.go:54
control-plane/internal/handlers/ui/config.go:24
This is tracked by #119 but included here for completeness.
Acceptance Criteria
Files
sdk/go/ai/tool_calling.go (primary)
sdk/go/ai/tool_calling_test.go (tests to update)
Related Issues
Using AI to solve this issue? Read our AI-Assisted Contributions guide for testing requirements, prompt strategies, and common pitfalls to avoid.
Summary
The Go SDK's
ai/tool_calling.gohas several code quality issues and parity gaps with the Python and TypeScript SDKs. This issue tracks the Go-specific findings from a cross-SDK audit.Issues Found
1. Duplicated
CapabilityToToolDefinitionlogic (L49-99)The
CapabilityToToolDefinitionfunction has two nearly identical code blocks forReasonerCapability(L49-73) andSkillCapability(L74-99):Fix: Extract a shared helper:
2. No
ToolCallResponsewrapper (API parity gap)The Python SDK has a
ToolCallResponseclass (tool_calling.py:85-124) that wraps the LLM response with theToolCallTrace, provides a.textproperty, and delegates attribute access to the underlying response for backward compatibility.The Go SDK's
ExecuteToolCallLoopreturns(*Response, *ToolCallTrace, error)as separate values — there's no unified wrapper.Fix: Add a
ToolCallResultstruct:3. No
PromptConfig/PromptTemplates(per #229)Issue #229 proposes a
PromptConfigstruct for Go but doesn't exist yet. The Go SDK has the same hardcoded strings as the other SDKs:"Tool call limit reached. Please provide a final response."tool_calling.go:167{"error": err.Error(), "tool": tc.Function.Name}tool_calling.go:193-196json.Marshal(result)with no framingtool_calling.go:204These should be extracted into a
PromptConfigstruct (tracked by #229, but included here for Go-specific tracking).4. No tool system prompt
All 3 SDKs send tools to the LLM without any system-level instructions on how to use them. The Python and TS SDKs also lack this, but the Go SDK's
ExecuteToolCallLoopdoesn't even accept an optional system message parameter — it only takesmessagesandtools.Fix: Add an optional system prompt to
ToolCallConfig:5. No tool name sanitization (parity gap)
Python SDK has
_sanitize_tool_name()/_unsanitize_tool_name()to replace colons with double-underscores for LLM provider compatibility (many providers reject colons in function names).TS SDK has
sanitizeToolName()/unsanitizeToolName()— same logic.Go SDK passes
c.InvocationTargetdirectly as the tool function name (tool_calling.go:69, 93) — no sanitization. This will break with providers like Google that reject colons.Fix: Add sanitization matching the Python/TS pattern:
6. Duplicate
ErrorResponsein control plane (cross-ref with #119)Found 2 duplicate
ErrorResponsestruct definitions:control-plane/internal/handlers/memory.go:54control-plane/internal/handlers/ui/config.go:24This is tracked by #119 but included here for completeness.
Acceptance Criteria
CapabilityToToolDefinitiondeduplicated — shared helper for Reasoner/SkillToolCallResultwrapper struct added (parity with PythonToolCallResponse)sanitizeToolName/unsanitizeToolName)ToolCallConfigextended with optionalSystemPromptfieldgo test ./...)go vetcleanFiles
sdk/go/ai/tool_calling.go(primary)sdk/go/ai/tool_calling_test.go(tests to update)Related Issues
PromptConfig)ErrorResponsehelper in control plane handlersAgentRouter(Go SDK maturity).harness()to Go SDK (Go SDK feature parity)