diff --git a/go/core/internal/controller/translator/agent/adk_api_translator.go b/go/core/internal/controller/translator/agent/adk_api_translator.go index 6c7870aa3..4755bb167 100644 --- a/go/core/internal/controller/translator/agent/adk_api_translator.go +++ b/go/core/internal/controller/translator/agent/adk_api_translator.go @@ -544,9 +544,10 @@ func (a *adkApiTranslator) buildManifest( Spec: corev1.ServiceSpec{ Selector: selectorLabels, Ports: []corev1.ServicePort{{ - Name: "http", - Port: dep.Port, - TargetPort: intstr.FromInt(int(dep.Port)), + Name: "http", + Port: dep.Port, + TargetPort: intstr.FromInt(int(dep.Port)), + AppProtocol: ptr.To("kgateway.dev/a2a"), }}, Type: corev1.ServiceTypeClusterIP, }, diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_allowed_headers.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_allowed_headers.json index 17a868b62..6c8a6edda 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_allowed_headers.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_allowed_headers.json @@ -282,6 +282,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -298,4 +299,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_code.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_code.json index bb75cf120..123f6390e 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_code.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_code.json @@ -277,6 +277,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -293,4 +294,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_cross_namespace_tools.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_cross_namespace_tools.json index 2095ab315..6b59ae292 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_cross_namespace_tools.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_cross_namespace_tools.json @@ -288,6 +288,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -304,4 +305,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_custom_sa.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_custom_sa.json index 02ac211d7..f5fffef2f 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_custom_sa.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_custom_sa.json @@ -241,6 +241,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -257,4 +258,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_http_toolserver.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_http_toolserver.json index 695b5b010..0f264c459 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_http_toolserver.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_http_toolserver.json @@ -281,6 +281,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -297,4 +298,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_mcp_service.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_mcp_service.json index 7b3090d1e..32e3289a4 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_mcp_service.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_mcp_service.json @@ -277,6 +277,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -293,4 +294,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_nested_agent.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_nested_agent.json index a3179403b..55b2ec0a2 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_nested_agent.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_nested_agent.json @@ -275,6 +275,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -291,4 +292,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_passthrough.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_passthrough.json index 62460dec1..fd69e00fb 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_passthrough.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_passthrough.json @@ -259,6 +259,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy.json index fa5a39d3a..f5e038e70 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy.json @@ -288,6 +288,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -304,4 +305,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_external_remotemcp.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_external_remotemcp.json index da2358625..5a74d1268 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_external_remotemcp.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_external_remotemcp.json @@ -277,6 +277,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -293,4 +294,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver.json index 8601d3b58..52d02cc13 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver.json @@ -280,6 +280,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -296,4 +297,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver_custom_timeout.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver_custom_timeout.json index 7615272da..8f767a266 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver_custom_timeout.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver_custom_timeout.json @@ -280,6 +280,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -296,4 +297,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_service.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_service.json index 0d242811f..be6b4d79e 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_service.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_service.json @@ -279,6 +279,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -295,4 +296,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_scheduling_attributes.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_scheduling_attributes.json index 356e42492..37fb36676 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_scheduling_attributes.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_scheduling_attributes.json @@ -300,6 +300,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -316,4 +317,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_security_context.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_security_context.json index 714386157..0ec2c03c2 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_security_context.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_security_context.json @@ -297,6 +297,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -313,4 +314,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_skills.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_skills.json index 2f40ff749..e4d9a0176 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_skills.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_skills.json @@ -307,6 +307,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -323,4 +324,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_streaming.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_streaming.json index ceb8f896d..714ae29f7 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_streaming.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_streaming.json @@ -273,6 +273,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -289,4 +290,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_system_message_from_configmap.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_system_message_from_configmap.json index bb4e20737..7fff216d1 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_system_message_from_configmap.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_system_message_from_configmap.json @@ -266,6 +266,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -282,4 +283,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_system_message_from_secret.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_system_message_from_secret.json index 7c1a4ca58..e6a2051a1 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_system_message_from_secret.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_system_message_from_secret.json @@ -266,6 +266,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -282,4 +283,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/anthropic_agent.json b/go/core/internal/controller/translator/agent/testdata/outputs/anthropic_agent.json index bbd9050d5..a293e7d13 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/anthropic_agent.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/anthropic_agent.json @@ -265,6 +265,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -281,4 +282,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/basic_agent.json b/go/core/internal/controller/translator/agent/testdata/outputs/basic_agent.json index 314fbe008..c235d462a 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/basic_agent.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/basic_agent.json @@ -273,6 +273,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -289,4 +290,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/bedrock_agent.json b/go/core/internal/controller/translator/agent/testdata/outputs/bedrock_agent.json index 824e552a4..10129aa38 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/bedrock_agent.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/bedrock_agent.json @@ -279,6 +279,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -295,4 +296,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/ollama_agent.json b/go/core/internal/controller/translator/agent/testdata/outputs/ollama_agent.json index 724c17d2c..253652c42 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/ollama_agent.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/ollama_agent.json @@ -268,6 +268,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -284,4 +285,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-custom-ca.json b/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-custom-ca.json index b71d59178..081f4d45d 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-custom-ca.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-custom-ca.json @@ -283,6 +283,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -299,4 +300,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-disabled-verify.json b/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-disabled-verify.json index dcdf6b411..72db6cebb 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-disabled-verify.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-disabled-verify.json @@ -270,6 +270,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -286,4 +287,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-system-cas-disabled.json b/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-system-cas-disabled.json index 0438e5bb6..0e964432b 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-system-cas-disabled.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/tls-with-system-cas-disabled.json @@ -283,6 +283,7 @@ "spec": { "ports": [ { + "appProtocol": "kgateway.dev/a2a", "name": "http", "port": 8080, "targetPort": 8080 @@ -299,4 +300,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/httpserver/auth/authn.go b/go/core/internal/httpserver/auth/authn.go index ef5292f58..408fef4e8 100644 --- a/go/core/internal/httpserver/auth/authn.go +++ b/go/core/internal/httpserver/auth/authn.go @@ -6,6 +6,7 @@ import ( "net/http" "net/url" + "github.com/kagent-dev/kagent/go/core/internal/tracecontext" "github.com/kagent-dev/kagent/go/core/pkg/auth" "k8s.io/apimachinery/pkg/types" ) @@ -70,7 +71,21 @@ type A2AAuthenticator struct { } func (p *A2AAuthenticator) Wrap(next http.Handler) http.Handler { - return auth.AuthnMiddleware(p.provider)(next) + authn := auth.AuthnMiddleware(p.provider)(next) + // Capture W3C trace context headers from the incoming request into the + // context before the A2A server strips the *http.Request. These headers + // are later injected into outgoing requests by A2ARequestHandler. + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if tp := r.Header.Get("traceparent"); tp != "" { + traceHeaders := make(http.Header, 2) + traceHeaders.Set("traceparent", tp) + if ts := r.Header.Get("tracestate"); ts != "" { + traceHeaders.Set("tracestate", ts) + } + r = r.WithContext(tracecontext.HeadersTo(r.Context(), traceHeaders)) + } + authn.ServeHTTP(w, r) + }) } type handler func(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) @@ -107,6 +122,15 @@ func A2ARequestHandler(authProvider auth.AuthProvider, agentNns types.Namespaced } } + // Propagate W3C trace context headers captured from the incoming request. + if traceHeaders, ok := tracecontext.HeadersFrom(ctx); ok { + for key, values := range traceHeaders { + for _, v := range values { + req.Header.Set(key, v) + } + } + } + resp, err = client.Do(req) if err != nil { return nil, fmt.Errorf("a2aClient.httpRequestHandler: http request failed: %w", err) diff --git a/go/core/internal/tracecontext/tracecontext.go b/go/core/internal/tracecontext/tracecontext.go new file mode 100644 index 000000000..b35c98089 --- /dev/null +++ b/go/core/internal/tracecontext/tracecontext.go @@ -0,0 +1,23 @@ +package tracecontext + +import ( + "context" + "net/http" +) + +type traceHeadersKeyType struct{} + +var traceHeadersKey = traceHeadersKeyType{} + +// HeadersFrom retrieves W3C trace context headers (traceparent, tracestate) +// that were captured from an incoming request. +func HeadersFrom(ctx context.Context) (http.Header, bool) { + v, ok := ctx.Value(traceHeadersKey).(http.Header) + return v, ok && v != nil +} + +// HeadersTo stores W3C trace context headers in the context so they can +// be propagated to outgoing requests (e.g., from the controller to agent pods). +func HeadersTo(ctx context.Context, headers http.Header) context.Context { + return context.WithValue(ctx, traceHeadersKey, headers) +} diff --git a/go/core/pkg/auth/auth.go b/go/core/pkg/auth/auth.go index 469d8619d..3ea3d423f 100644 --- a/go/core/pkg/auth/auth.go +++ b/go/core/pkg/auth/auth.go @@ -59,9 +59,7 @@ type Authorizer interface { type sessionKeyType struct{} -var ( - sessionKey = sessionKeyType{} -) +var sessionKey = sessionKeyType{} func AuthSessionFrom(ctx context.Context) (Session, bool) { v, ok := ctx.Value(sessionKey).(Session) diff --git a/python/packages/kagent-core/pyproject.toml b/python/packages/kagent-core/pyproject.toml index c792c2240..9832ab9c9 100644 --- a/python/packages/kagent-core/pyproject.toml +++ b/python/packages/kagent-core/pyproject.toml @@ -15,6 +15,7 @@ dependencies = [ "opentelemetry-exporter-otlp-proto-grpc>=1.38.0,<1.39.0", "opentelemetry-instrumentation-openai>=0.52.5", "opentelemetry-instrumentation-anthropic>=0.52.5", + "opentelemetry-instrumentation-aiohttp-client>=0.52.0", "opentelemetry-instrumentation-httpx >= 0.52.0", "opentelemetry-instrumentation-fastapi>=0.52.0", "opentelemetry-instrumentation-google-generativeai>=0.52.5", diff --git a/python/packages/kagent-core/src/kagent/core/tracing/_utils.py b/python/packages/kagent-core/src/kagent/core/tracing/_utils.py index 9a0838ad3..4729cff78 100644 --- a/python/packages/kagent-core/src/kagent/core/tracing/_utils.py +++ b/python/packages/kagent-core/src/kagent/core/tracing/_utils.py @@ -32,6 +32,20 @@ def _instrument_anthropic(event_logger_provider=None): pass +def _instrument_aiohttp_client(): + """Instrument aiohttp client if available. + + This ensures outbound HTTP calls made via aiohttp (e.g. litellm's default + transport) create OTEL spans and propagate trace context headers. + """ + try: + from opentelemetry.instrumentation.aiohttp_client import AioHttpClientInstrumentor + + AioHttpClientInstrumentor().instrument() + except ImportError: + pass + + def _instrument_google_generativeai(): """Instrument Google GenerativeAI SDK if available.""" try: @@ -91,6 +105,7 @@ def configure(name: str = "kagent", namespace: str = "kagent", fastapi_app: Fast logging.info("Created new TracerProvider") HTTPXClientInstrumentor().instrument() + _instrument_aiohttp_client() if fastapi_app: FastAPIInstrumentor().instrument_app(fastapi_app) # Configure logging if enabled