Skip to content

[Go SDK] Code quality: deduplicate tool_calling.go, add ToolCallResponse wrapper, add PromptConfig parity #234

@santoshkumarradha

Description

@santoshkumarradha

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

  • CapabilityToToolDefinition deduplicated — shared helper for Reasoner/Skill
  • ToolCallResult wrapper struct added (parity with Python ToolCallResponse)
  • Tool name sanitization added (sanitizeToolName/unsanitizeToolName)
  • ToolCallConfig extended with optional SystemPrompt field
  • All existing tests pass (go test ./...)
  • go vet clean
  • No new lint warnings

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestrefactorCode quality and refactoring improvementssdk:goGo SDK related

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions