diff --git a/internal/llminternal/functions.go b/internal/llminternal/functions.go index b1771bdea..98db0dfb0 100644 --- a/internal/llminternal/functions.go +++ b/internal/llminternal/functions.go @@ -15,8 +15,6 @@ package llminternal import ( - "time" - "google.golang.org/genai" "google.golang.org/adk/agent" @@ -78,18 +76,15 @@ func generateRequestConfirmationEvent( return nil } - return &session.Event{ - InvocationID: invocationContext.InvocationID(), - Author: invocationContext.Agent().Name(), - Branch: invocationContext.Branch(), - LLMResponse: model.LLMResponse{ - Content: &genai.Content{ - Parts: parts, - Role: genai.RoleModel, - }, + ev := session.NewEvent(invocationContext.InvocationID()) + ev.Author = invocationContext.Agent().Name() + ev.Branch = invocationContext.Branch() + ev.LLMResponse = model.LLMResponse{ + Content: &genai.Content{ + Parts: parts, + Role: genai.RoleModel, }, - Timestamp: time.Now(), - LongRunningToolIDs: longRunningToolIDs, - Actions: session.EventActions{}, } + ev.LongRunningToolIDs = longRunningToolIDs + return ev } diff --git a/internal/llminternal/functions_test.go b/internal/llminternal/functions_test.go index 63aa27169..de11da566 100644 --- a/internal/llminternal/functions_test.go +++ b/internal/llminternal/functions_test.go @@ -146,6 +146,9 @@ func TestGenerateRequestConfirmationEvent(t *testing.T) { InvocationID: "inv_1", Author: "agent_1", Branch: "main", + Actions: session.EventActions{ + StateDelta: map[string]any{}, + }, LLMResponse: model.LLMResponse{ Content: &genai.Content{ Role: genai.RoleModel, @@ -189,3 +192,62 @@ func TestGenerateRequestConfirmationEvent(t *testing.T) { }) } } + +// TestGenerateRequestConfirmationEventHasID verifies that the event returned +// by generateRequestConfirmationEvent always has a non-empty ID. +// +// In Python ADK, every Event gets a UUID via model_post_init: +// +// def model_post_init(self, __context): +// if not self.id: +// self.id = Event.new_id() # str(uuid.uuid4()) +// +// In Go ADK, events must be created with session.NewEvent() to get an ID. +// A raw &session.Event{} literal leaves ID as "" which breaks features +// that rely on event IDs (e.g. time-travel restart_from_event_id). +func TestGenerateRequestConfirmationEventHasID(t *testing.T) { + confirmingFunctionCall := &genai.FunctionCall{ + ID: "call_1", + Name: "test_tool", + Args: map[string]any{"arg": "val"}, + } + + ctx := &mockInvocationContext{ + invocationID: "inv_1", + agentName: "agent_1", + branch: "main", + } + + functionCallEvent := &session.Event{ + LLMResponse: model.LLMResponse{ + Content: &genai.Content{ + Parts: []*genai.Part{ + {FunctionCall: confirmingFunctionCall}, + }, + }, + }, + } + + functionResponseEvent := &session.Event{ + Actions: session.EventActions{ + RequestedToolConfirmations: map[string]toolconfirmation.ToolConfirmation{ + "call_1": { + Hint: "Are you sure?", + }, + }, + }, + } + + got := generateRequestConfirmationEvent(ctx, functionCallEvent, functionResponseEvent) + if got == nil { + t.Fatal("expected non-nil event") + } + + if got.ID == "" { + t.Error("event ID is empty; events must have a UUID for time-travel and session lookup") + } + + if got.InvocationID != "inv_1" { + t.Errorf("expected InvocationID=\"inv_1\", got %q", got.InvocationID) + } +}