Skip to content

Conversation

@shentongmartin
Copy link
Contributor

Objective

Replace the current counter-based tool call ID generation (function_name, function_name-2, etc.) with UUID-based IDs to ensure uniqueness across multiple model calls within a session.

Current Implementation Analysis

Current approach:

  • Uses map[string]int to track function call counts
  • Generates IDs: function_name, function_name-2, function_name-3
  • IDs are unique within a single response but may conflict across multiple Generate/Stream calls

Problem:

  • In a multi-turn conversation or agent session with multiple model calls, the same counter-based IDs could be reused
  • This could cause conflicts in tool execution tracking across the session

Proposed Solution

1. Replace Counter with UUID Generation

Changes to convFC() function:

  • Remove the counter-based logic
  • Use github.com/google/uuid (already in dependencies) to generate unique IDs
  • Each tool call gets a fresh UUID regardless of function name

New ID format: 550e8400-e29b-41d4-a716-446655440000 (standard UUID v4)

2. Simplify Function Signatures

Since we no longer need to track counts, we can:

  • Remove toolCallIDs map[string]int parameter from all functions
  • Simplify convFC(), convCandidate(), convResponse() signatures
  • Remove map initialization in Generate() and Stream() methods

3. Update Implementation

Files to modify:

  • gemini.go: Update ID generation logic, remove map tracking
  • gemini_test.go: Update tests to expect UUID format instead of counter format
  • README.md: Update documentation with new UUID-based approach
  • README_zh.md: Update Chinese documentation

Detailed Changes

Code Changes

  1. Add UUID import to gemini.go:

    import "github.com/google/uuid"
  2. Simplify convFC() function:

    func convFC(part *genai.Part) (*schema.ToolCall, error) {
        // ... existing validation ...
        
        // Generate unique UUID for this tool call
        callID := uuid.NewString()
        
        toolCall := &schema.ToolCall{
            ID: callID,
            Function: schema.FunctionCall{
                Name:      tp.Name,
                Arguments: args,
            },
        }
        return toolCall, nil
    }
  3. Update function signatures:

    • convResponse(resp *genai.GenerateContentResponse) (*schema.Message, error)
    • convCandidate(candidate *genai.Candidate) (*schema.Message, error)
    • convFC(part *genai.Part) (*schema.ToolCall, error)
  4. Remove map initialization:

    • In Generate(): Remove make(map[string]int) call
    • In Stream(): Remove toolCallIDs := make(map[string]int) line
  5. Update comments: Replace counter-based explanations with UUID explanations

Test Changes

Update TestDuplicateToolCallIDs to:

  • Rename to TestUniqueToolCallIDs (more accurate name)
  • Verify IDs are valid UUIDs (format validation)
  • Verify IDs are unique across all tool calls
  • Remove assertions about specific ID patterns (func-2, func-3)
  • Add UUID format validation helper

Documentation Changes

README.md & README_zh.md:

  • Update "Tool Call ID Handling" section
  • Change from counter pattern to UUID pattern
  • Update examples to show UUID format
  • Emphasize session-wide uniqueness

Example update:

// Tool Call 1: ID = "550e8400-e29b-41d4-a716-446655440000", Args = {"city": "Paris"}
// Tool Call 2: ID = "6ba7b810-9dad-11d1-80b4-00c04fd430c8", Args = {"city": "London"}
// Tool Call 3: ID = "7c9e6679-7425-40de-944b-e07fc1f90ae7", Args = {"city": "Tokyo"}

Benefits

Session-wide uniqueness: UUIDs are globally unique, preventing conflicts across multiple model calls

Simpler code: No need to maintain counter state or pass maps through function calls

Industry standard: UUIDs are widely recognized and used for unique identifiers

Better traceability: Each tool call has a truly unique identifier for debugging and logging

Breaking Changes

⚠️ Minor breaking change: Tool call IDs will change format from function_name-N to UUID

Impact:

  • Code that parses or expects specific ID patterns will need updates
  • However, most code should only use IDs as opaque identifiers
  • This is still early in the fix lifecycle (just committed), so impact is minimal

Implementation Steps

  1. Update gemini.go with UUID generation
  2. Update all function signatures to remove map parameter
  3. Update test cases to validate UUID format and uniqueness
  4. Update README.md and README_zh.md documentation
  5. Run all tests to ensure compatibility
  6. Commit changes with clear message about UUID adoption
  7. Update PR description to reflect the UUID approach

@github-actions
Copy link

Need to create a new tag

The following modules have changes and may need version updates:

  • components/model/gemini (Current: components/model/gemini/v0.1.27)

⚠️ Please create and push new version tags for these modules after merging this PR.

Replace the previous counter-based ID generation (function_name, function_name-2, etc.)
with UUID v4 to ensure globally unique tool call IDs across multiple model calls
within a session.

Changes:
- Added github.com/google/uuid import to gemini.go and gemini_test.go
- Updated convFC() to generate UUID for each tool call using uuid.NewString()
- Simplified function signatures by removing toolCallIDs map parameter:
  - convFC(part) instead of convFC(part, toolCallIDs)
  - convCandidate(candidate) instead of convCandidate(candidate, toolCallIDs)
  - convResponse(resp) instead of convResponse(resp, toolCallIDs)
- Removed map initialization in Generate() and Stream() methods
- Updated comments to reflect UUID approach
- Renamed TestDuplicateToolCallIDs to TestUniqueToolCallIDs
- Updated all test cases to validate UUID format and uniqueness
- Added isValidUUID() helper function for test validation
- Updated README.md and README_zh.md with UUID documentation

Benefits:
- Session-wide uniqueness: UUIDs prevent ID collisions across multiple model calls
- Simpler code: No need to maintain counter state or pass maps through functions
- Industry standard: UUIDs are widely recognized for unique identifiers
- Better traceability: Each tool call has a truly unique identifier

Breaking Changes:
- Tool call ID format changes from 'function_name-N' to UUID format
- Impact is minimal as IDs should be treated as opaque identifiers

All tests pass ✅~

Change-Id: I28a9ae1938f1c8f40e5fc1d45f19c0e5ae72987a
@shentongmartin shentongmartin force-pushed the fix/gemini_tool_call_id branch from 33e3188 to 65cbe12 Compare January 16, 2026 10:04
@github-actions
Copy link

Need to create a new tag

The following modules have changes and may need version updates:

  • components/model/gemini (Current: components/model/gemini/v0.1.27)

⚠️ Please create and push new version tags for these modules after merging this PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants