diff --git a/sdk/src/openagents/sdk/topology.py b/sdk/src/openagents/sdk/topology.py index 190a52e86..19e26daf5 100644 --- a/sdk/src/openagents/sdk/topology.py +++ b/sdk/src/openagents/sdk/topology.py @@ -444,7 +444,7 @@ async def initialize(self) -> bool: transport = WebSocketTransport(transport_config.config) elif transport_type == TransportType.GRPC: - from .transports import GRPCTransport + from .transports.grpc import GRPCTransport transport = GRPCTransport(transport_config.config) elif transport_type == TransportType.MCP: @@ -718,7 +718,7 @@ async def initialize(self) -> bool: transport = WebSocketTransport(transport_config.get("config", {})) elif transport_type == TransportType.GRPC: - from .transports import GRPCTransport + from .transports.grpc import GRPCTransport transport = GRPCTransport(transport_config.get("config", {})) else: diff --git a/sdk/src/openagents/sdk/transports/__init__.py b/sdk/src/openagents/sdk/transports/__init__.py index cce81e204..cd4fe89de 100644 --- a/sdk/src/openagents/sdk/transports/__init__.py +++ b/sdk/src/openagents/sdk/transports/__init__.py @@ -5,26 +5,35 @@ Includes WebSocket, gRPC, and base transport abstractions. """ -# Import base classes -from .base import Transport, Message - -# Import transport implementations -from .websocket import WebSocketTransport, create_websocket_transport -from .grpc import GRPCTransport, OpenAgentsGRPCServicer, create_grpc_transport -from .http import HttpTransport -from .mcp import MCPTransport, create_mcp_transport -from .a2a import A2ATransport, create_a2a_transport -from openagents.models.network_context import NetworkContext +from importlib import import_module +from typing import Any # Import transport types and models +from openagents.models.event import Event + +# Import base classes +from openagents.models.network_context import NetworkContext from openagents.models.transport import ( - TransportType, + AgentConnection, + ConnectionInfo, ConnectionState, PeerMetadata, - ConnectionInfo, - AgentConnection, + TransportType, ) -from openagents.models.event import Event + +from .a2a import A2ATransport, create_a2a_transport +from .base import Message, Transport +from .http import HttpTransport +from .mcp import MCPTransport, create_mcp_transport + +# Import transport implementations +from .websocket import WebSocketTransport, create_websocket_transport + +_GRPC_EXPORTS = { + "GRPCTransport", + "OpenAgentsGRPCServicer", + "create_grpc_transport", +} # Simplified exports - only working transports __all__ = [ @@ -39,7 +48,6 @@ "MCPTransport", "A2ATransport", "NetworkContext", - "OpenAgentsGRPCServicer", # Convenience functions "create_websocket_transport", "create_grpc_transport", @@ -51,4 +59,16 @@ "PeerMetadata", "ConnectionInfo", "AgentConnection", + "OpenAgentsGRPCServicer", ] + + +def __getattr__(name: str) -> Any: + """Load gRPC exports only when callers explicitly request them.""" + if name not in _GRPC_EXPORTS: + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + grpc_module = import_module(f"{__name__}.grpc") + value = getattr(grpc_module, name) + globals()[name] = value + return value diff --git a/tests/test_optional_grpc.py b/tests/test_optional_grpc.py new file mode 100644 index 000000000..5fa7b2593 --- /dev/null +++ b/tests/test_optional_grpc.py @@ -0,0 +1,46 @@ +"""Verify non-gRPC transports work without the SDK's grpcio dependency.""" + +import os +import subprocess +import sys +import textwrap +from pathlib import Path + + +def test_http_transport_import_does_not_require_grpcio(): + """A base install can import HTTP transport without grpcio installed.""" + sdk_src = Path(__file__).parents[1] / "sdk" / "src" + script = textwrap.dedent( + """ + import importlib.abc + import sys + + class BlockGrpc(importlib.abc.MetaPathFinder): + def find_spec(self, fullname, path=None, target=None): + if fullname == "grpc" or fullname.startswith("grpc."): + raise ModuleNotFoundError("grpc blocked for optional dependency test") + return None + + sys.meta_path.insert(0, BlockGrpc()) + from openagents.sdk.transports import HttpTransport + assert HttpTransport.__name__ == "HttpTransport" + assert "grpc" not in sys.modules + + try: + from openagents.sdk.transports import GRPCTransport + except ImportError as exc: + assert "pip install openagents[sdk]" in str(exc) + else: + raise AssertionError(f"unexpected gRPC transport: {GRPCTransport}") + """ + ) + env = os.environ.copy() + env["PYTHONPATH"] = str(sdk_src) + result = subprocess.run( + [sys.executable, "-c", script], + env=env, + capture_output=True, + text=True, + check=False, + ) + assert result.returncode == 0, result.stderr