feat: native multi-agent support via MultiAgentRegistry - #1031
feat: native multi-agent support via MultiAgentRegistry#1031malladinagarjuna2 wants to merge 8 commits into
Conversation
This adds native support for multiple agents in the reference-jsonrpc Quarkus extension.
If a CDI bean implements MultiAgentRegistry, the A2AServerRoutes will automatically
dynamically register endpoints for each agent, mapping '/{agentId}' to its JSONRPCHandler
and '/{agentId}/.well-known/agent-card.json'.
kabir
left a comment
There was a problem hiding this comment.
Thanks for the PR :-) The MultiAgentRegistry approach is interesting.
A few things to address before this can be merged:
- Support for all transports (REST, gRPC), not just JSON-RPC — as noted inline
- The tenant extraction / agent ID path conflict
- Compile issues in the tests
It would also be interesting to see if this could be enhanced with @mescaja's database-driven approach from #898 (comment) — either in this PR or as a follow-up.
| Instance<JSONRPCHandler> jsonRpcHandler; | ||
|
|
||
| @Inject | ||
| Instance<org.a2aproject.sdk.server.apps.quarkus.registry.MultiAgentRegistry> multiAgentRegistry; |
There was a problem hiding this comment.
fixed, now it imports multiagentresgistry instead of using the fully-qualified name
| String pathPrefix = "/" + agentId; | ||
| registerAgentRoutes(router, pathPrefix, entry.getValue()); | ||
| } | ||
| } else if (!jsonRpcHandler.isUnsatisfied()) { |
There was a problem hiding this comment.
Better to use isResolvable()
There was a problem hiding this comment.
replaced both !isUnsatisfied() checks (this one and the jsonRpcHandler one right below it) with isResolvable(), since isUnsatisfied() doesn't catch the ambiguous-resolution case (.get() would throw AmbiguousResolutionException).
| /** | ||
| * @return a map of agent ID (path segment) to their JSONRPCHandler | ||
| */ | ||
| Map<String, JSONRPCHandler> getAgents(); |
There was a problem hiding this comment.
We should also include the other transports
There was a problem hiding this comment.
extended the same MultiAgentRegistry pattern to REST (routes registered under //...) and gRPC (dispatched via a new X-A2A-Agent-Id metadata header, since gRPC has no per-path routing). Also verified live locally ,JSON-RPC calls to two different registered agents each get routed to and served by their own handler correctly.
| } | ||
|
|
||
| private void registerAgentRoutes(Router router, String pathPrefix, JSONRPCHandler handler) { | ||
| String rpcPath = pathPrefix.isEmpty() ? "/" : pathPrefix; |
There was a problem hiding this comment.
The tenant extraction will conflict with multi-agent routing. extractTenant() reads the normalized path, so a request to POST /myagent would return "myagent" as the tenant. The agent ID prefix needs to be stripped before tenant extraction — registerAgentRoutes should pass the pathPrefix length so extractTenant can skip it, or use a Vert.x path parameter (e.g. /:agentId/*) instead of a fixed path.
There was a problem hiding this comment.
went with your suggestion: registerAgentRoutes now stores the reistered path prefix on the routing context, and extractTenant() strips it before computing the tenant from the remaining path. So POST/myagent now correctly resolves to an empty tentant instead of treating "myagent" as the tenant.
- Import MultiAgentRegistry instead of using its fully-qualified name - Use Instance.isResolvable() instead of !isUnsatisfied(), which also matches on ambiguous (multi-bean) resolution and would throw on get() - Strip the registered agent path prefix before computing the tenant, so a request to POST /myagent no longer treats "myagent" as the tenant - Fix pre-existing test compile errors (isUnsatisfied()/get() stubbed on the wrong mock) and the resulting compile break in MultiVersionJSONRPCRoutes, which still called the old 2-arg invokeJSONRPCHandler
Mirrors the JSON-RPC MultiAgentRegistry pattern across REST and gRPC: - REST: MultiAgentRegistry (Map<String, RestHandler>), routes each agent under /<agentId>/ with the ID as a literal regex prefix ahead of the tenant capture group. - gRPC: MultiAgentRegistry (Map<String, GrpcAgent>), dispatches by a new X-A2A-Agent-Id metadata header, falling back to the default single-agent beans when absent/unknown. Also fixes the compile break this causes in MultiVersionRestRoutes.
Signed-off-by: malladi nagarjuna <zombmalladinags69@gmail.com>
a665080 to
72a5380
Compare
|
@kabir thanks for the review. All four points are addressed, details below, plus a note on the database-driven follow-up. Support for all transports
In every transport the registry bean is optional: if no Tenant extraction / agent ID path conflict Fixed as you suggested.
Both Test compile issues Resolved. On @mescaja's database-driven approach, and @omatheusmesmo's capabilities question One thing worth making explicit, since it came up in #898: the capabilities problem doesn't apply to this design. @omatheusmesmo noted that the handlers gate operations on a single resolved card: if (!resolveAgentCard().capabilities().streaming()) { ... }In registry mode each registered agent has its own handler instance carrying its own On the database-driven card store: I'd prefer to keep it out of this PR and do it as a follow-up. It's a different concern , this PR is about routing requests to the right handler, whereas @mescaja's approach and @ehsavoie's suggestion of an |
|
thanks a lot @malladinagarjuna2. It's a nice feature that you put together that will simplify the handling of multiple AgentCard. For more complex enterprise agentic platform that requires deploy agent at scale both corporate and remote one i think other options would be preferred in my view. Sorry to ask you for a favor but it'd appreciate if you could have a look at this ticket i raised yesterday which i believe it's a critical one and have your view on it. Not expecting to pick it up straightaway if it's a bug, just quick little look and let me know your thoughts. |
|
@kabir could you please review it |
Overview
This PR introduces native support for deploying multiple Server Agents within a single Quarkus application instance for the
reference-jsonrpcextension, resolving the operational overhead of 1:1 Kubernetes pod mappings.Fixes #898
Changes
MultiAgentRegistryInterface: Introduced a registry interface that users can implement as a CDI bean to provide a map ofagentIdtoJSONRPCHandler.A2AServerRoutes: The server router now checks for the presence of aMultiAgentRegistry. If found, it iterates over all registered agents and dynamically creates Vert.x routes for/{agentId}and/{agentId}/.well-known/agent-card.json./and/.well-known/agent-card.json).This allows production deployments to scale infinitely to hundreds of agents in a single JVM with zero custom routing code.