agent-routers 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Agent Routers
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-routers
3
+ Version: 0.1.0
4
+ Summary: Priority-based async/sync routing engine for multi-agent systems
5
+ Author: Michael Douglas Barbosa Araujo
6
+ License: MIT
7
+ Project-URL: Homepage, https://agentrouters.github.io/docs/
8
+ Project-URL: Issues, https://github.com/seuusuario/agent-routers/issues
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Dynamic: license-file
13
+
14
+ # Agent Routers
15
+
16
+ > Deterministic, priority-based routing engine for multi-agent systems.
17
+
18
+ Agent Routers is a lightweight Python library for building rule-based routing layers.
19
+ It allows you to register sync or async handlers and dispatch inputs using
20
+ priority-aware regex matching — without requiring LLMs or web frameworks.
21
+
22
+ ---
23
+
24
+ ## Why Agent Routers?
25
+
26
+ - Supports **sync and async handlers**
27
+ - Deterministic, priority-based rule resolution
28
+ - Perfect for multi-agent supervisors and orchestration layers
29
+ - Minimal overhead, zero external dependencies
30
+
31
+ ---
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install agent-routers
37
+ ```
@@ -0,0 +1,24 @@
1
+ # Agent Routers
2
+
3
+ > Deterministic, priority-based routing engine for multi-agent systems.
4
+
5
+ Agent Routers is a lightweight Python library for building rule-based routing layers.
6
+ It allows you to register sync or async handlers and dispatch inputs using
7
+ priority-aware regex matching — without requiring LLMs or web frameworks.
8
+
9
+ ---
10
+
11
+ ## Why Agent Routers?
12
+
13
+ - Supports **sync and async handlers**
14
+ - Deterministic, priority-based rule resolution
15
+ - Perfect for multi-agent supervisors and orchestration layers
16
+ - Minimal overhead, zero external dependencies
17
+
18
+ ---
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ pip install agent-routers
24
+ ```
@@ -0,0 +1,29 @@
1
+ [project]
2
+ name = "agent-routers"
3
+ version = "0.1.0"
4
+ description = "Priority-based async/sync routing engine for multi-agent systems"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = [ ]
8
+
9
+
10
+ [project.license]
11
+ text = "MIT"
12
+
13
+
14
+ [[project.authors]]
15
+ name = "Michael Douglas Barbosa Araujo"
16
+
17
+
18
+ [project.urls]
19
+ Homepage = "https://agentrouters.github.io/docs/"
20
+ Issues = "https://github.com/seuusuario/agent-routers/issues"
21
+
22
+
23
+ [build-system]
24
+ requires = [ "setuptools>=61.0", "wheel" ]
25
+ build-backend = "setuptools.build_meta"
26
+
27
+
28
+ [dependency-groups]
29
+ dev = [ "pytest-asyncio>=1.3.0" ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,13 @@
1
+ from .router import AgentRouter
2
+ from .exceptions import (
3
+ AgentRouterError,
4
+ NoAgentMatchedError,
5
+ AgentAlreadyRegisteredError,
6
+ )
7
+
8
+ __all__ = [
9
+ "AgentRouter",
10
+ "AgentRouterError",
11
+ "NoAgentMatchedError",
12
+ "AgentAlreadyRegisteredError",
13
+ ]
@@ -0,0 +1,50 @@
1
+ """
2
+ Custom exceptions for agent_routers.
3
+
4
+ This module centralizes all library-specific errors,
5
+ making error handling predictable and explicit.
6
+ """
7
+
8
+
9
+ class AgentRouterError(Exception):
10
+ """Base exception for all agent_routers errors."""
11
+ pass
12
+
13
+
14
+ class NoAgentMatchedError(AgentRouterError):
15
+ """
16
+ Raised when no registered agent matches the given input.
17
+
18
+ Attributes:
19
+ input_text (str): The input that failed routing.
20
+ """
21
+
22
+ def __init__(self, input_text: str):
23
+ self.input_text = input_text
24
+ super().__init__(f"No agent matched the input: '{input_text}'")
25
+
26
+
27
+ class AgentAlreadyRegisteredError(AgentRouterError):
28
+ """
29
+ Raised when trying to register an agent that already exists.
30
+
31
+ Attributes:
32
+ agent_name (str): The duplicate agent name.
33
+ """
34
+
35
+ def __init__(self, agent_name: str):
36
+ self.agent_name = agent_name
37
+ super().__init__(f"Agent '{agent_name}' is already registered.")
38
+
39
+
40
+ class InvalidRuleError(AgentRouterError):
41
+ """
42
+ Raised when a rule definition is invalid (e.g., malformed regex).
43
+
44
+ Attributes:
45
+ rule (str): The invalid rule definition.
46
+ """
47
+
48
+ def __init__(self, rule: str):
49
+ self.rule = rule
50
+ super().__init__(f"Invalid rule provided: '{rule}'")
@@ -0,0 +1,10 @@
1
+ import time
2
+
3
+
4
+ def log_decision(input_text: str, agent_name: str, layer: str):
5
+ print({
6
+ "input": input_text,
7
+ "agent": agent_name,
8
+ "layer": layer,
9
+ "timestamp": time.time()
10
+ })
@@ -0,0 +1,26 @@
1
+ from dataclasses import dataclass, field
2
+ from typing import Callable, Awaitable, Union, Any
3
+ import re
4
+
5
+
6
+ Handler = Union[
7
+ Callable[[str], Any],
8
+ Callable[[str], Awaitable[Any]],
9
+ ]
10
+
11
+
12
+ @dataclass(order=True)
13
+ class Rule:
14
+ """
15
+ Represents a routing rule.
16
+
17
+ Rules are ordered by priority (descending).
18
+ """
19
+ priority: int
20
+ name: str = field(compare=False)
21
+ pattern: str = field(compare=False)
22
+ handler: Handler = field(compare=False)
23
+ compiled_pattern: re.Pattern = field(init=False, compare=False)
24
+
25
+ def __post_init__(self):
26
+ self.compiled_pattern = re.compile(self.pattern)
@@ -0,0 +1,21 @@
1
+ from typing import Dict, List
2
+ from .models import Agent
3
+
4
+
5
+ class AgentRegistry:
6
+
7
+ def __init__(self):
8
+ self._agents: Dict[str, Agent] = {}
9
+
10
+ def register(self, agent: Agent):
11
+ if agent.name in self._agents:
12
+ raise ValueError(f"Agent '{agent.name}' already registered.")
13
+ self._agents[agent.name] = agent
14
+
15
+ def get(self, name: str) -> Agent:
16
+ if name not in self._agents:
17
+ raise ValueError(f"Agent '{name}' not found.")
18
+ return self._agents[name]
19
+
20
+ def list_names(self) -> List[str]:
21
+ return list(self._agents.keys())
@@ -0,0 +1,54 @@
1
+ import inspect
2
+ from typing import List, Any
3
+
4
+ from .models import Rule
5
+ from .exceptions import (
6
+ NoAgentMatchedError,
7
+ AgentAlreadyRegisteredError,
8
+ )
9
+
10
+
11
+ class AgentRouter:
12
+ """
13
+ Async-first agent router with priority-based matching.
14
+ """
15
+
16
+ def __init__(self) -> None:
17
+ self._rules: List[Rule] = []
18
+ self._registered_names = set()
19
+
20
+ def register(
21
+ self,
22
+ name: str,
23
+ pattern: str,
24
+ handler,
25
+ priority: int = 0,
26
+ ) -> None:
27
+
28
+ if name in self._registered_names:
29
+ raise AgentAlreadyRegisteredError(name)
30
+
31
+ rule = Rule(
32
+ priority=priority,
33
+ name=name,
34
+ pattern=pattern,
35
+ handler=handler,
36
+ )
37
+
38
+ self._rules.append(rule)
39
+ self._registered_names.add(name)
40
+
41
+ # Sort once when registering (descending priority)
42
+ self._rules.sort(reverse=True)
43
+
44
+ async def route(self, input_text: str) -> Any:
45
+
46
+ for rule in self._rules:
47
+ if rule.compiled_pattern.search(input_text):
48
+
49
+ if inspect.iscoroutinefunction(rule.handler):
50
+ return await rule.handler(input_text)
51
+
52
+ return rule.handler(input_text)
53
+
54
+ raise NoAgentMatchedError(input_text)
@@ -0,0 +1,17 @@
1
+ import re
2
+ from typing import Optional, Dict
3
+
4
+
5
+ class RegexClassifier:
6
+
7
+ def __init__(self):
8
+ self._rules: Dict[str, str] = {}
9
+
10
+ def add_rule(self, pattern: str, agent_name: str):
11
+ self._rules[pattern] = agent_name
12
+
13
+ def classify(self, text: str) -> Optional[str]:
14
+ for pattern, agent_name in self._rules.items():
15
+ if re.search(pattern, text, re.IGNORECASE):
16
+ return agent_name
17
+ return None
@@ -0,0 +1,37 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-routers
3
+ Version: 0.1.0
4
+ Summary: Priority-based async/sync routing engine for multi-agent systems
5
+ Author: Michael Douglas Barbosa Araujo
6
+ License: MIT
7
+ Project-URL: Homepage, https://agentrouters.github.io/docs/
8
+ Project-URL: Issues, https://github.com/seuusuario/agent-routers/issues
9
+ Requires-Python: >=3.10
10
+ Description-Content-Type: text/markdown
11
+ License-File: LICENSE
12
+ Dynamic: license-file
13
+
14
+ # Agent Routers
15
+
16
+ > Deterministic, priority-based routing engine for multi-agent systems.
17
+
18
+ Agent Routers is a lightweight Python library for building rule-based routing layers.
19
+ It allows you to register sync or async handlers and dispatch inputs using
20
+ priority-aware regex matching — without requiring LLMs or web frameworks.
21
+
22
+ ---
23
+
24
+ ## Why Agent Routers?
25
+
26
+ - Supports **sync and async handlers**
27
+ - Deterministic, priority-based rule resolution
28
+ - Perfect for multi-agent supervisors and orchestration layers
29
+ - Minimal overhead, zero external dependencies
30
+
31
+ ---
32
+
33
+ ## Installation
34
+
35
+ ```bash
36
+ pip install agent-routers
37
+ ```
@@ -0,0 +1,15 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ src/agent_routers/__init__.py
5
+ src/agent_routers/exceptions.py
6
+ src/agent_routers/logging.py
7
+ src/agent_routers/models.py
8
+ src/agent_routers/registry.py
9
+ src/agent_routers/router.py
10
+ src/agent_routers/rules.py
11
+ src/agent_routers.egg-info/PKG-INFO
12
+ src/agent_routers.egg-info/SOURCES.txt
13
+ src/agent_routers.egg-info/dependency_links.txt
14
+ src/agent_routers.egg-info/top_level.txt
15
+ tests/test_router.py
@@ -0,0 +1 @@
1
+ agent_routers
@@ -0,0 +1,162 @@
1
+ import pytest
2
+ from agent_routers import AgentRouter
3
+ from agent_routers.exceptions import (
4
+ NoAgentMatchedError,
5
+ AgentAlreadyRegisteredError,
6
+ )
7
+
8
+
9
+ # ---------------------------
10
+ # Async Handler
11
+ # ---------------------------
12
+
13
+ @pytest.mark.asyncio
14
+ async def test_async_handler_routing():
15
+
16
+ async def support_handler(text: str):
17
+ return "support called"
18
+
19
+ router = AgentRouter()
20
+ router.register("support", r"error|bug", support_handler)
21
+
22
+ result = await router.route("There is an error")
23
+
24
+ assert result == "support called"
25
+
26
+
27
+ # ---------------------------
28
+ # Sync Handler
29
+ # ---------------------------
30
+
31
+ @pytest.mark.asyncio
32
+ async def test_sync_handler_routing():
33
+
34
+ def greet_handler(text: str):
35
+ return "hello"
36
+
37
+ router = AgentRouter()
38
+ router.register("greet", r"hello|hi", greet_handler)
39
+
40
+ result = await router.route("hello world")
41
+
42
+ assert result == "hello"
43
+
44
+
45
+ # ---------------------------
46
+ # No Match
47
+ # ---------------------------
48
+
49
+ @pytest.mark.asyncio
50
+ async def test_no_agent_matched():
51
+
52
+ router = AgentRouter()
53
+
54
+ with pytest.raises(NoAgentMatchedError):
55
+ await router.route("nothing matches this")
56
+
57
+
58
+ # ---------------------------
59
+ # Duplicate Agent Registration
60
+ # ---------------------------
61
+
62
+ def test_duplicate_agent_registration():
63
+
64
+ def handler(text: str):
65
+ return "x"
66
+
67
+ router = AgentRouter()
68
+ router.register("agent1", r"test", handler)
69
+
70
+ with pytest.raises(AgentAlreadyRegisteredError):
71
+ router.register("agent1", r"other", handler)
72
+
73
+
74
+ # ---------------------------
75
+ # Same Priority → First Registered Wins
76
+ # ---------------------------
77
+
78
+ @pytest.mark.asyncio
79
+ async def test_same_priority_first_registered_wins():
80
+
81
+ def handler1(text: str):
82
+ return "first"
83
+
84
+ def handler2(text: str):
85
+ return "second"
86
+
87
+ router = AgentRouter()
88
+
89
+ # Same priority (default 0)
90
+ router.register("agent1", r"hello", handler1)
91
+ router.register("agent2", r"hello world", handler2)
92
+
93
+ result = await router.route("hello world")
94
+
95
+ # Same priority → insertion order preserved
96
+ assert result == "first"
97
+
98
+
99
+ # ---------------------------
100
+ # Higher Priority Wins
101
+ # ---------------------------
102
+
103
+ @pytest.mark.asyncio
104
+ async def test_higher_priority_wins():
105
+
106
+ def low_priority(text: str):
107
+ return "low"
108
+
109
+ def high_priority(text: str):
110
+ return "high"
111
+
112
+ router = AgentRouter()
113
+
114
+ router.register("low", r"hello", low_priority, priority=1)
115
+ router.register("high", r"hello world", high_priority, priority=10)
116
+
117
+ result = await router.route("hello world")
118
+
119
+ assert result == "high"
120
+
121
+
122
+ # ---------------------------
123
+ # Multiple Agents Different Patterns
124
+ # ---------------------------
125
+
126
+ @pytest.mark.asyncio
127
+ async def test_multiple_agents():
128
+
129
+ def support_handler(text: str):
130
+ return "support"
131
+
132
+ def sales_handler(text: str):
133
+ return "sales"
134
+
135
+ router = AgentRouter()
136
+
137
+ router.register("support", r"error|bug", support_handler)
138
+ router.register("sales", r"price|buy", sales_handler)
139
+
140
+ result_support = await router.route("There is a bug")
141
+ result_sales = await router.route("I want to buy")
142
+
143
+ assert result_support == "support"
144
+ assert result_sales == "sales"
145
+
146
+
147
+ # ---------------------------
148
+ # Regex Behavior
149
+ # ---------------------------
150
+
151
+ @pytest.mark.asyncio
152
+ async def test_regex_matching():
153
+
154
+ def handler(text: str):
155
+ return "matched"
156
+
157
+ router = AgentRouter()
158
+ router.register("test", r"\d+", handler)
159
+
160
+ result = await router.route("Order 1234")
161
+
162
+ assert result == "matched"