Skip to content

refactor(a2a.helpers): Expose routing helpers from a2a.helpers - #1143

Open
msampathkumar wants to merge 5 commits into
a2aproject:mainfrom
msampathkumar:sampathm_a2a_helperfuns
Open

refactor(a2a.helpers): Expose routing helpers from a2a.helpers#1143
msampathkumar wants to merge 5 commits into
a2aproject:mainfrom
msampathkumar:sampathm_a2a_helperfuns

Conversation

@msampathkumar

Copy link
Copy Markdown
Member

This change exports several route creation functions from the top-level a2a.helpers package, making them directly importable.

This simplifies the process of adding A2A routes to a FastAPI application and improves the overall modularity of the routing setup.

Description

Thank you for opening a Pull Request!
Before submitting your PR, there are a few things you can do to make sure it goes smoothly:

  • Follow the CONTRIBUTING Guide.
  • Make your Pull Request title in the https://www.conventionalcommits.org/ specification.
    • Important Prefixes for release-please:
      • fix: which represents bug fixes, and correlates to a SemVer patch.
      • feat: represents a new feature, and correlates to a SemVer minor.
      • feat!:, or fix!:, refactor!:, etc., which represent a breaking change (indicated by the !) and will result in a SemVer major.
  • Ensure the tests and linter pass (Run bash scripts/format.sh from the repository root to format)
  • Appropriate docs were updated (if necessary)

Fixes #<issue_number_goes_here> 🦕

This change exports several route creation functions from the top-level a2a.helpers package, making them directly importable.

This simplifies the process of adding A2A routes to a FastAPI application and improves the overall modularity of the routing setup.
@msampathkumar
msampathkumar requested a review from a team as a code owner July 22, 2026 14:21
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@chopmob-cloud

Copy link
Copy Markdown

Reproduced the red checks against this branch. They trace back to one change: the new eager from a2a.server.routes.* import ... lines added to a2a/helpers/__init__.py. That top-level import pulls the whole server and routes package in whenever a2a.helpers is imported, and it shows up two ways.

First, base installs break. Installing the branch without the http server extra and importing the package fails:

import a2a.helpers
  File ".../a2a/helpers/__init__.py", line 34, in <module>
    from a2a.server.routes.agent_card_routes import create_agent_card_routes
  ...
  File ".../a2a/compat/v0_3/jsonrpc_adapter.py", line 6, in <module>
    from sse_starlette.sse import EventSourceResponse
ModuleNotFoundError: No module named 'sse_starlette'

On current main the same base install imports a2a.helpers cleanly, so this is a regression, and it matches the Verify install job failing on every Python version. sse_starlette is an http server dependency, so exporting the route factories eagerly makes plain import a2a.helpers require the http extra.

Second, the same early import creates a cycle. The Test with Python jobs fail starting the server subprocess with:

ImportError: cannot import name 'RequestHandler03' from partially initialized module
'a2a.compat.v0_3.request_handler' (most likely due to a circular import)

The import path changes in request_context_builder.py and simple_request_context_builder.py look like an attempt to break that cycle, but it is not fully resolved. This traces to the same root. Importing the failing integration server directly (import server_1_0 from tests/integration/cross_version/client_server) reproduces the RequestHandler03 error, and removing the four eager imports from a2a/helpers/__init__.py makes that same import succeed. With the eager imports present, loading a2a.helpers pulls the route and JSON-RPC compat stack in, so in that server's import order the compat imports run while a2a.compat.v0_3.request_handler is still initialising and the cycle triggers; without them it does not.

A lazy export keeps the nicer top-level names without either problem. Dropping the four eager imports and resolving them on access via a module __getattr__ works:

_LAZY = {
    'add_a2a_routes_to_fastapi': 'a2a.server.routes.fastapi_routes',
    'create_agent_card_routes': 'a2a.server.routes.agent_card_routes',
    'create_jsonrpc_routes': 'a2a.server.routes.jsonrpc_routes',
    'create_rest_routes': 'a2a.server.routes.rest_routes',
}

def __getattr__(name):
    if name in _LAZY:
        import importlib
        return getattr(importlib.import_module(_LAZY[name]), name)
    raise AttributeError(name)

I tried this against the branch to confirm it. With the four eager imports removed and the __getattr__ above in place, base import a2a.helpers succeeds again without the http extra, from a2a.helpers import add_a2a_routes_to_fastapi still works and only pulls the http modules when actually used, and import server_1_0 no longer hits the RequestHandler03 cycle. The four names can stay in __all__, with the one caveat that from a2a.helpers import * would still resolve them and so still need the http extra, which seems fine for a star import.

The remaining red check, Lint Code Base, is separate and trivial: ruff I001 on simple_request_context_builder.py (the import block there needs sorting and formatting). With the cycle removed by the lazy export, the import path edits in that file and request_context_builder.py are no longer needed and could revert to the original, or just run ruff format on them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants