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
15 changes: 13 additions & 2 deletions integrations/langgraph/python/ag_ui_langgraph/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
LangGraphReasoning,
)
from .utils import json_safe_stringify, make_json_safe
from .endpoint import add_langgraph_fastapi_endpoint
from .middlewares.state_streaming import StateStreamingMiddleware, StateItem
from .a2ui_tool import (
get_a2ui_tools,
Expand All @@ -27,6 +26,10 @@
BASIC_CATALOG_ID,
)

# FastAPI is an optional extra. Keep the endpoint import lazy so middleware-only
# installs (`pip install ag-ui-langgraph` without `[fastapi]`) can still import
# LangGraphAgent and helpers without requiring fastapi (#2013).

__all__ = [
"LangGraphAgent",
"get_a2ui_tools",
Expand All @@ -53,5 +56,13 @@
"StateStreamingMiddleware",
"StateItem",
"json_safe_stringify",
"make_json_safe"
"make_json_safe",
]


def __getattr__(name: str):
if name == "add_langgraph_fastapi_endpoint":
from .endpoint import add_langgraph_fastapi_endpoint

return add_langgraph_fastapi_endpoint
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
44 changes: 44 additions & 0 deletions integrations/langgraph/python/tests/test_lazy_fastapi_import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
"""Regression for #2013 — package import must not require fastapi.

`fastapi` is an optional extra, but `__init__.py` used to eagerly import
`endpoint.py`, which imports fastapi at module top. Middleware-only installs
then failed on any `import ag_ui_langgraph`.
"""

from __future__ import annotations

import importlib
import sys
import unittest


class TestLazyFastapiImport(unittest.TestCase):
def test_init_does_not_import_endpoint_module(self):
# Drop package modules so we re-import against current source.
for name in list(sys.modules):
if name == "ag_ui_langgraph" or name.startswith("ag_ui_langgraph."):
del sys.modules[name]

import ag_ui_langgraph # noqa: F401

self.assertNotIn("ag_ui_langgraph.endpoint", sys.modules)

# Core exports still resolve without touching the FastAPI path.
from ag_ui_langgraph import LangGraphAgent # noqa: F401

self.assertNotIn("ag_ui_langgraph.endpoint", sys.modules)

def test_endpoint_export_is_available_lazily(self):
for name in list(sys.modules):
if name == "ag_ui_langgraph" or name.startswith("ag_ui_langgraph."):
del sys.modules[name]

import ag_ui_langgraph

endpoint_fn = ag_ui_langgraph.add_langgraph_fastapi_endpoint
self.assertTrue(callable(endpoint_fn))
self.assertIn("ag_ui_langgraph.endpoint", sys.modules)


if __name__ == "__main__":
unittest.main()