Repository navigation
Conversation
Contributor
There was a problem hiding this comment.
Code Review
This pull request introduces AgentFunction, AgentFunctionContext, and AgentFunctionHandler to support catalog functions that run on the agent starting from protocol version 1.0. It updates Catalog and Catalog.copy_with to handle these agent functions, ensuring they are validated and merged correctly, and adds comprehensive unit tests. The feedback suggests improving the protocol version check using a version-at-least helper, simplifying a redundant fallback expression in copy_with, and adding a defensive type check in AgentFunction.from_api to prevent nested wrappers.
This was referenced Nov 3, 2026
nan-yu
force-pushed
the
feat/python-core-agent-functions
branch
from
November 3, 2026 01:22
60ddda2 to
7fd36e5
Compare
nan-yu
force-pushed
the
feat/python-core-agent-functions
branch
from
November 3, 2026 16:46
7fd36e5 to
5a4f8ce
Compare
Base automatically changed from
test/conformance-agent-function-routing
to
main
November 3, 2026 17:17
nan-yu
force-pushed
the
feat/python-core-agent-functions
branch
2 times, most recently
from
November 3, 2026 18:21
3e5ce21 to
787e4f6
Compare
jacobsimionato
approved these changes
Nov 4, 2026
nan-yu
force-pushed
the
feat/python-core-agent-functions
branch
from
November 4, 2026 18:21
787e4f6 to
7e40330
Compare
Signed-off-by: Nan Yu <nanyu@google.com>
- from_api for a function the catalog doesn't declare is a catalog error - agent functions are added through copy_with or functions, not a constructor parameter - AgentFunction is always rendererOnly; from_api keeps rendererOnly or rendererOrAgent and rejects agentOnly - separate errors for duplicate agent functions and a taken name - add Catalog.renderer_functions - handlers may take (args) or (args, context); add AgentFunction.invoke - from_api raises TypeError for a non-FunctionApi - export AgentFunction, AgentFunctionContext and AgentFunctionHandler from a2ui.core Signed-off-by: Nan Yu <nanyu@google.com>
- v1.0 catalogs check agent function parameter names against UAX #31 - copy_with keeps agent functions given in functions; existing ones go back to their declarations only when functions is omitted - handler arity is checked at construction; a defaulted second parameter gets the context only if named context or ctx - handler is read-only; add with_handler - distinct error for from_api of a different declaration object - renderer_functions keeps the catalog's function type - AgentFunction.__init__ drops is_definition and from_document - from_api rejects requiresUserActivation unless rendererOnly - AgentFunctionContext is keyword-only - shorter CHANGELOG entry Signed-off-by: Nan Yu <nanyu@google.com>
The rebase onto main brought back the first version of the entry next to the shorter one that replaced it. Signed-off-by: Nan Yu <nanyu@google.com>
nan-yu
force-pushed
the
feat/python-core-agent-functions
branch
from
November 4, 2026 19:22
7e40330 to
d1c83d1
Compare
…d schemas Signed-off-by: Nan Yu <nanyu@google.com>
…ith the blueprint Signed-off-by: Nan Yu <nanyu@google.com>
Signed-off-by: Nan Yu <nanyu@google.com>
…ter names Follow a local $ref as a JSON Pointer, so #/$defs/Bundle/allOf/0 reaches the node it names rather than the whole Bundle definition, and merge a whole function definition's own $defs with its args' over the catalog's. This matches the Dart fix from the #3861 review. Signed-off-by: Nan Yu <nanyu@google.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Based on
main(the conformance PR #3742 has merged). The PR has seven commits:feat(python-core): declare agent functions on a catalog;fix(python-core): tighten agent function declarations after review,fix(python-core): address second review of agent functions,fix(python-core): check UAX #31 parameter names in union and shorthand schemas,fix(python-core): align RpcHandler.dispose and the agent call cache with the blueprintandfix(core): merge catalog and local $defs in function validation, which apply the review fixes across the stack; anddocs(python-core): drop the stale agent functions CHANGELOG entry, which removes an entry the rebase brought back.Part of #3536 (step 4 of the SDK-level fix plan).
Description
The renderer side of agent-hosted functions is in #3735 and #3739-#3741: a v1.0 renderer sends a call it can't run to the agent with
callAgentFunction. The agent side had no way to declare such a function. To pass its own validation, an agent had to add a plain schema-only entry to its catalog, and keep the code that answers the call somewhere else.This PR adds the declaration to
a2ui_core, whereCataloglives, so the agent SDK can build on it. AnsweringcallAgentFunction(#3793) and the A2A/ADK wiring (#3795) come in the next PRs of this stack.AgentFunctionis aFunctionApiwith a read-onlyhandler. The handler takes the validated arguments, or the arguments and anAgentFunctionContext(catalog, surface and function call IDs, keyword-only). It returns the value, directly or as an awaitable.AgentFunctionhas noexecute, so a renderer core given a catalog containing one treats it as declared but not implemented, and routes it. AnAgentFunctionbuilt directly is alwaysrendererOnly, and its constructor takes none of the catalog-document parameters (is_definition,from_document).AgentFunctionis built. The context is passed when the handler takes*args, or when its second positional parameter has no default or is namedcontextorctx; a defaulted second parameter with another name, such as an injected dependency, is left untouched. A handler that can be called neither as(args)nor as(args, context)raisesTypeError.with_handler(handler)returns a copy bound to another handler, with the same name, schema, description andallowed_callers. A function built withfrom_apiis rebuilt from its declaration, so a catalog still takes the copy in place of that declaration.AgentFunction.from_api(api, handler)builds an agent function from a function the catalog document already declares, so the definition stays as written; it copies the document fields from the declaration. It keeps the declaration'sallowedCallers(rendererOnlyorrendererOrAgent) and raisesA2uiCatalogErrorfor anagentOnlydeclaration, which renderers never call, and for one withrequiresUserActivation: truethat isn'trendererOnly, which the v1.0 catalog definition doesn't allow. It raisesTypeErrorfor something that isn't aFunctionApi, or that is already anAgentFunction.Catalog.copy_with(agent_functions=...)adds agent functions to a loaded catalog. Withfunctionsomitted, they replace the catalog's agent functions: one built withfrom_apigoes back to its declaration, and any other is dropped. Withfunctionsgiven, its entries are kept as they are, agent functions included, andagent_functionsare added to them. A catalog built in code can also list agent functions infunctions; the constructor has no separate parameter. Agent functions are ordinary entries ofcatalog.functions, so validation,to_json, the prompt generators and the compilers treat them like any other declared function, with no changes there. The catalog document writes them like functions without an implementation and never includes the handler.Catalog.agent_functionsandCatalog.renderer_functionssplitfunctionsby whether an entry is anAgentFunction. Both are computed.renderer_functionskeeps the catalog's function type (dict[str, TFunction]) and returns a new dict, so changing it leaves the catalog as it is._with_agent_functions). Each case has its ownA2uiCatalogErrormessage:from_apifrom that entry, which replaces it in place.from_apifrom a function the catalog doesn't declare is rejected, instead of being added. Otherwise a declaration from another catalog could slip in throughcopy_with(agent_functions=...).from_apifrom a declaration of that name that isn't the catalog's own entry object, for example one from a reloaded or copied catalog, gets a message saying so, and to build it from this catalog's entry.copy_with, for JSON Schema (includingallOf,anyOf,oneOf, local and catalog#/$defs/references, and shorthand maps) and Pydantic schemas.callAgentFunctiononly exists from v1.0.deriveand the pruning transformer keep agent functions consistent withfunctionswithout any special handling, because they carry the same entries.AgentFunction,AgentFunctionContextandAgentFunctionHandlerare exported froma2ui.core. The CHANGELOG entry lists the main exports and methods.RpcHandler.dispose()clearsoutbound_listener, and the settled agent call cache uses strict FIFO eviction (256entries by default, configurable viaSurfaceModel(..., max_agent_call_cache_entries=...)andMessageProcessorOptions.max_agent_call_cache_entries).$defsresolution (_resolved_schemas,_extract_raw_param_schemaand_apply_object_defaults): merges local$defsover catalog$defswithout discarding catalog definitions, validates parameter schemas defined through$ref/allOf/anyOf/oneOfwithout a top-levelpropertieskey, and populates schema defaults acrossproperties,$refandallOf.Changes after the reviews:
agent_functionsconstructor parameter and theallowed_callersparameter ofAgentFunctionare gone;from_apiof an undeclared function, a duplicate name and a taken name are separate errors;renderer_functions, one-parameter handlers and the root exports are new.copy_with(functions=...)keeps the agent functions it is given; the handler's arity is checked at construction;handleris read-only andwith_handleris new;from_apiof a different declaration object has its own error;renderer_functionskeeps the catalog's function type; the constructor dropsis_definitionandfrom_document;from_apirejectsrequiresUserActivationunless the declaration isrendererOnly;AgentFunctionContextis keyword-only; the CHANGELOG entry is shorter.$defsduring schema resolution, validation and default application, and supports top-level combinators ($ref,allOf,anyOf,oneOf) inPayloadValidator.validate_function.#/$defs/...reference as a JSON Pointer, so#/$defs/Bundle/allOf/0reaches only that member, and a whole function definition's own$defsare merged with its args' over the catalog's.Tests
tests/test_agent_functions.py:copy_withandfunctions(including agent functions given infunctions), name collisions and their separate errors,from_apireplacement (with a schema-only declaration),from_apiof an undeclared,agentOnly, user-activation or reloaded declaration,from_apiwith a non-FunctionApi,rendererOnlyfor directly built functions, handler arity (one and two parameters,*args, defaulted parameters, bound methods, wrong arity), the read-onlyhandlerandwith_handler, the keyword-only context, parameter names,renderer_functionsand its type, pre-1.0 rejection,to_jsonoutput,deriveand pruning, and strict validation of calls and components that use an agent function (including combined catalog and local$defs, schema defaults and rejected arguments).uv run pytest python/a2ui_core/tests: 2118 passed, 23 skipped, 5 xfailed.