Skip to content
Closed
32 changes: 28 additions & 4 deletions internal/toolinternal/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package toolinternal

import (
"context"
"fmt"

"github.com/google/uuid"
"google.golang.org/adk/agent"
Expand All @@ -33,6 +34,9 @@ type internalArtifacts struct {
}

func (ia *internalArtifacts) Save(ctx context.Context, name string, data *genai.Part) (*artifact.SaveResponse, error) {
if ia == nil || ia.Artifacts == nil {
Comment thread
ShammiAnand marked this conversation as resolved.
Outdated
return nil, fmt.Errorf("artifact service not configured")
}
resp, err := ia.Artifacts.Save(ctx, name, data)
if err != nil {
return resp, err
Expand All @@ -47,6 +51,20 @@ func (ia *internalArtifacts) Save(ctx context.Context, name string, data *genai.
return resp, nil
}

func (ia *internalArtifacts) List(ctx context.Context) (*artifact.ListResponse, error) {
if ia == nil || ia.Artifacts == nil {
return nil, fmt.Errorf("artifact service not configured")
}
return ia.Artifacts.List(ctx)
}

func (ia *internalArtifacts) Load(ctx context.Context, name string) (*artifact.LoadResponse, error) {
if ia == nil || ia.Artifacts == nil {
return nil, fmt.Errorf("artifact service not configured")
}
return ia.Artifacts.Load(ctx, name)
}

func NewToolContext(ctx agent.InvocationContext, functionCallID string, actions *session.EventActions) tool.Context {
if functionCallID == "" {
functionCallID = uuid.NewString()
Expand All @@ -59,15 +77,21 @@ func NewToolContext(ctx agent.InvocationContext, functionCallID string, actions
}
cbCtx := contextinternal.NewCallbackContextWithDelta(ctx, actions.StateDelta)

// Only create internalArtifacts if the underlying Artifacts service is configured
var artifacts *internalArtifacts
if ctx.Artifacts() != nil {
artifacts = &internalArtifacts{
Artifacts: ctx.Artifacts(),
eventActions: actions,
}
}

return &toolContext{
CallbackContext: cbCtx,
invocationContext: ctx,
functionCallID: functionCallID,
eventActions: actions,
artifacts: &internalArtifacts{
Artifacts: ctx.Artifacts(),
eventActions: actions,
},
artifacts: artifacts,
}
}

Expand Down
46 changes: 46 additions & 0 deletions internal/toolinternal/context_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,49 @@ func TestToolContext(t *testing.T) {
t.Errorf("ToolContext(%+T) is unexpectedly an InvocationContext", got)
}
}

func TestInternalArtifacts_NilSafe(t *testing.T) {
// Create invocation context without artifact service
inv := contextinternal.NewInvocationContext(t.Context(), contextinternal.InvocationContextParams{
Artifacts: nil,
})
toolCtx := NewToolContext(inv, "fn1", &session.EventActions{})

artifacts := toolCtx.Artifacts()
// artifacts will be nil when service not configured

// Attempting to call methods on nil should be safe (won't panic)
// but will return errors
t.Run("List returns error", func(t *testing.T) {
_, err := artifacts.List(t.Context())
if err == nil {
t.Error("Expected error from List(), got nil")
}
expectedMsg := "artifact service not configured"
if err != nil && err.Error() != expectedMsg {
t.Errorf("Expected error %q, got: %v", expectedMsg, err)
}
})

t.Run("Load returns error", func(t *testing.T) {
_, err := artifacts.Load(t.Context(), "test.txt")
if err == nil {
t.Error("Expected error from Load(), got nil")
}
expectedMsg := "artifact service not configured"
if err != nil && err.Error() != expectedMsg {
t.Errorf("Expected error %q, got: %v", expectedMsg, err)
}
})

t.Run("Save returns error", func(t *testing.T) {
_, err := artifacts.Save(t.Context(), "test.txt", nil)
if err == nil {
t.Error("Expected error from Save(), got nil")
}
expectedMsg := "artifact service not configured"
if err != nil && err.Error() != expectedMsg {
t.Errorf("Expected error %q, got: %v", expectedMsg, err)
}
})
Comment thread
ShammiAnand marked this conversation as resolved.
Outdated
}
27 changes: 27 additions & 0 deletions tool/loadartifactstool/load_artifacts_tool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,33 @@ func TestLoadArtifactsTool_ProcessRequest_Artifacts_OtherFunctionCall(t *testing
}
}

func TestLoadArtifactsTool_ProcessRequest_NoArtifactService(t *testing.T) {
loadArtifactsTool := loadartifactstool.New()

// Create tool context WITHOUT artifact service configured
ctx := icontext.NewInvocationContext(t.Context(), icontext.InvocationContextParams{
Artifacts: nil, // No artifact service
})
tc := toolinternal.NewToolContext(ctx, "", nil)

llmRequest := &model.LLMRequest{}

requestProcessor, ok := loadArtifactsTool.(toolinternal.RequestProcessor)
if !ok {
t.Fatal("loadArtifactsTool does not implement RequestProcessor")
}

err := requestProcessor.ProcessRequest(tc, llmRequest)
if err == nil {
t.Fatal("Expected error when artifact service not configured, got nil")
}

expectedErr := "artifact service not configured"
if !strings.Contains(err.Error(), expectedErr) {
t.Errorf("Expected error containing %q, got: %v", expectedErr, err)
}
}

func createToolContext(t *testing.T) tool.Context {
t.Helper()

Expand Down