-
Notifications
You must be signed in to change notification settings - Fork 131
Description
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 aboveFix: 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:54control-plane/internal/handlers/ui/config.go:24
This is tracked by #119 but included here for completeness.
Acceptance Criteria
-
CapabilityToToolDefinitiondeduplicated — shared helper for Reasoner/Skill -
ToolCallResultwrapper struct added (parity with PythonToolCallResponse) - Tool name sanitization added (
sanitizeToolName/unsanitizeToolName) -
ToolCallConfigextended with optionalSystemPromptfield - All existing tests pass (
go test ./...) -
go vetclean - No new lint warnings
Files
sdk/go/ai/tool_calling.go(primary)sdk/go/ai/tool_calling_test.go(tests to update)
Related Issues
- feat: abstract SDK-injected prompt text into a configurable PromptTemplates layer #229 — PromptTemplates abstraction (parent initiative, add Go
PromptConfig) - [Control Plane] Extract shared ErrorResponse helper #119 — Extract shared
ErrorResponsehelper in control plane handlers - [Go SDK] Add rate limiter with exponential backoff and circuit breaker #97 — Go SDK rate limiter (already implemented)
- [Go SDK] Add AgentRouter for modular agent organization #98 — Go SDK
AgentRouter(Go SDK maturity) - [Go SDK] Port .harness() to Go SDK #207 — Port
.harness()to Go SDK (Go SDK feature parity)
Using AI to solve this issue? Read our AI-Assisted Contributions guide for testing requirements, prompt strategies, and common pitfalls to avoid.