Coverage for src / agent_contracts / router.py: 47%

17 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-09 00:42 +0900

1"""BaseActionRouter - Action-based routing. 

2 

3Parses action parameters and routes to appropriate subgraphs. 

4Uses rule-based decisions, no LLM. 

5""" 

6from __future__ import annotations 

7 

8from abc import ABC, abstractmethod 

9 

10from agent_contracts.utils.logging import get_logger 

11 

12logger = get_logger("agent_contracts.router") 

13 

14 

15class BaseActionRouter(ABC): 

16 """Base class for action-based routing. 

17  

18 All requests are routed here first based on action. 

19 Uses only parameters, no LLM. 

20  

21 Example: 

22 class MyRouter(BaseActionRouter): 

23 def route(self, action: str, state: dict | None = None) -> str: 

24 if action == "create": 

25 return "create_supervisor" 

26 elif action == "search": 

27 return "search_supervisor" 

28 return "default_supervisor" 

29 """ 

30 

31 @abstractmethod 

32 def route(self, action: str, state: dict | None = None) -> str: 

33 """Determine routing target based on action. 

34  

35 Args: 

36 action: Request action 

37 state: Agent state (optional) 

38  

39 Returns: 

40 Routing target node name 

41  

42 Raises: 

43 ValueError: For unknown actions 

44 """ 

45 ... 

46 

47 def __call__(self, state: dict) -> dict: 

48 """Execute as LangGraph node. 

49  

50 Args: 

51 state: Agent state 

52  

53 Returns: 

54 Updated state (includes _internal.next_node) 

55 """ 

56 request = state.get("request", {}) 

57 action = request.get("action", "") 

58 

59 try: 

60 next_node = self.route(action, state) 

61 logger.info(f"Routed: action={action} -> {next_node}") 

62 # LangGraph reducer merges this into existing _internal slice 

63 return {"_internal": {"next_node": next_node}} 

64 except ValueError as e: 

65 logger.error(f"Routing failed: {e}") 

66 # LangGraph reducer merges these updates into existing slices 

67 return { 

68 "_internal": {"next_node": None, "error": str(e)}, 

69 "response": { 

70 "response_type": "error", 

71 "response_data": {"code": "UNKNOWN_ACTION", "message": str(e)}, 

72 }, 

73 }