Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion go/adk/pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
"os"
"strings"
"time"

"github.com/go-logr/logr"
"github.com/kagent-dev/kagent/go/adk/pkg/mcp"
Expand Down Expand Up @@ -57,7 +59,11 @@ func CreateGoogleADKAgentWithSubagentSessionIDs(ctx context.Context, agentConfig
log.Info("Skipping remote agent with empty URL", "name", remoteAgent.Name)
continue
}
remoteTool, sessionID, err := tools.NewKAgentRemoteA2ATool(remoteAgent.Name, remoteAgent.Description, remoteAgent.Url, nil, remoteAgent.Headers)
var httpClient *http.Client
if remoteAgent.Timeout != nil {
httpClient = &http.Client{Timeout: time.Duration(*remoteAgent.Timeout * float64(time.Second))}
}
remoteTool, sessionID, err := tools.NewKAgentRemoteA2ATool(remoteAgent.Name, remoteAgent.Description, remoteAgent.Url, httpClient, remoteAgent.Headers)
if err != nil {
return nil, nil, fmt.Errorf("failed to create remote A2A tool for %s: %w", remoteAgent.Name, err)
}
Expand Down
1 change: 1 addition & 0 deletions go/api/adk/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ type RemoteAgentConfig struct {
Name string `json:"name"`
Url string `json:"url"`
Headers map[string]string `json:"headers,omitempty"`
Timeout *float64 `json:"timeout,omitempty"`
Description string `json:"description,omitempty"`
}

Expand Down
4 changes: 4 additions & 0 deletions go/api/v1alpha2/agent_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,10 @@ type TypedReference struct {
Name string `json:"name"`
// +optional
Namespace string `json:"namespace,omitempty"`
// Timeout specifies the A2A client read timeout in seconds for calls to this agent.
// Defaults to 600s if unset.
// +optional
Timeout *float64 `json:"timeout,omitempty"`
Comment on lines +448 to +451
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TypedReference struct has been modified with a new Timeout field, but the generated deepcopy code in zz_generated.deepcopy.go was not regenerated. While the current simple *out = *in assignment works correctly for this case (all fields are scalar types), it's important that make generate is run to properly update all generated code. The PR description mentions controller-gen wasn't available during development, but this must be done before merging.

Copilot uses AI. Check for mistakes.
}

func (t *TypedReference) GroupKind() schema.GroupKind {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -745,12 +745,16 @@ func (a *adkApiTranslator) translateInlineAgent(ctx context.Context, agent *v1al
}
}

cfg.RemoteAgents = append(cfg.RemoteAgents, adk.RemoteAgentConfig{
remoteAgent := adk.RemoteAgentConfig{
Name: utils.ConvertToPythonIdentifier(utils.GetObjectRef(toolAgent)),
Url: targetURL,
Headers: headers,
Description: toolAgent.Spec.Description,
})
}
if tool.Agent.Timeout != nil {
remoteAgent.Timeout = tool.Agent.Timeout
}
cfg.RemoteAgents = append(cfg.RemoteAgents, remoteAgent)
Comment on lines +754 to +757
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new timeout field for agent tools lacks test coverage. There are existing tests for agent tool translation (e.g., Test_AdkApiTranslator_CrossNamespaceAgentTool), but none verify that the timeout value is correctly propagated from the Tool.Agent reference to the RemoteAgentConfig. Consider adding test cases to verify timeout handling, including when timeout is set and when it's nil.

Copilot uses AI. Check for mistakes.
Comment on lines +754 to +757
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The timeout field is being correctly propagated to RemoteAgentConfig by the translator (line 755). However, note that this timeout value must still be consumed by the ADK runtime when creating the HTTP client for the remote agent tool (in go/adk/pkg/agent/agent.go around line 60, where NewKAgentRemoteA2ATool is called with nil for httpClient). If the timeout is not consumed by the runtime, the configuration will have no effect.

Copilot uses AI. Check for mistakes.
default:
return nil, nil, nil, fmt.Errorf("unknown agent type: %s", toolAgent.Spec.Type)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,94 @@ func Test_AdkApiTranslator_CrossNamespaceAgentTool(t *testing.T) {
}
}

// Test_AdkApiTranslator_AgentToolTimeout tests that the timeout field on a
// TypedReference is correctly propagated into the RemoteAgentConfig produced
// by the translator.
func Test_AdkApiTranslator_AgentToolTimeout(t *testing.T) {
scheme := schemev1.Scheme
require.NoError(t, v1alpha2.AddToScheme(scheme))

timeout1800 := float64(1800)

modelConfig := &v1alpha2.ModelConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test-model", Namespace: "default"},
Spec: v1alpha2.ModelConfigSpec{Model: "gpt-4", Provider: v1alpha2.ModelProviderOpenAI},
}
toolAgent := &v1alpha2.Agent{
ObjectMeta: metav1.ObjectMeta{Name: "slow-agent", Namespace: "default"},
Spec: v1alpha2.AgentSpec{
Type: v1alpha2.AgentType_Declarative,
Description: "Slow agent",
Declarative: &v1alpha2.DeclarativeAgentSpec{
SystemMessage: "You are slow",
ModelConfig: "test-model",
},
},
}

tests := []struct {
name string
timeout *float64
wantTimeout *float64
}{
{
name: "timeout set - propagated into RemoteAgentConfig",
timeout: &timeout1800,
wantTimeout: &timeout1800,
},
{
name: "timeout nil - RemoteAgentConfig has no timeout",
timeout: nil,
wantTimeout: nil,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sourceAgent := &v1alpha2.Agent{
ObjectMeta: metav1.ObjectMeta{Name: "source-agent", Namespace: "default"},
Spec: v1alpha2.AgentSpec{
Type: v1alpha2.AgentType_Declarative,
Description: "Source agent",
Declarative: &v1alpha2.DeclarativeAgentSpec{
SystemMessage: "You are the source",
ModelConfig: "test-model",
Tools: []*v1alpha2.Tool{
{
Type: v1alpha2.ToolProviderType_Agent,
Agent: &v1alpha2.TypedReference{
Name: "slow-agent",
Namespace: "default",
Timeout: tt.timeout,
},
},
},
},
},
}

kubeClient := fake.NewClientBuilder().
WithScheme(scheme).
WithObjects(modelConfig, toolAgent, sourceAgent).
Build()

trans := translator.NewAdkApiTranslator(kubeClient, types.NamespacedName{Namespace: "default", Name: "test-model"}, nil, "")
outputs, err := trans.TranslateAgent(context.Background(), sourceAgent)
require.NoError(t, err)
require.NotNil(t, outputs.Config)
require.Len(t, outputs.Config.RemoteAgents, 1)

got := outputs.Config.RemoteAgents[0].Timeout
if tt.wantTimeout == nil {
assert.Nil(t, got)
} else {
require.NotNil(t, got)
assert.Equal(t, *tt.wantTimeout, *got)
}
})
}
}

// Test_AdkApiTranslator_CrossNamespaceRemoteMCPServer tests that the translator
// can handle cross-namespace RemoteMCPServer references. Note that cross-namespace
// validation (AllowedNamespaces checks) is now done in the reconciler,
Expand Down
5 changes: 5 additions & 0 deletions helm/kagent-crds/templates/kagent.dev_agents.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2238,6 +2238,11 @@ spec:
Can either be a reference to the name of an Agent in the same namespace as the referencing Agent, or a reference to the name of an Agent in a different namespace in the form <namespace>/<name>
minLength: 1
type: string
timeout:
description: A2A client read timeout in seconds for calls
to this agent. Defaults to 600s if unset.
format: double
type: number
Comment on lines +2241 to +2245
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The CRD schema for the agent tool reference does not match the Go struct definition. The CRD shows only "ref" and "timeout" fields, but the Go TypedReference struct has "kind", "apiGroup", "name", "namespace", and "timeout" fields. The code at line 723 uses tool.Agent.NamespacedName() which expects "name" and "namespace" fields separately. Users following the CRD schema would specify only a "ref" string, which would be incompatible with the Go struct deserialization. The CRD should be regenerated with make generate manifests to match the actual struct definition.

Copilot uses AI. Check for mistakes.
type: object
mcpServer:
properties:
Expand Down