Lightweight Universal API Router
Project description
agnomux2 - Universal API Multiplexer
โ ๏ธ Alpha Release - This is the debut release of agnomux2. While functional, the API may change in future versions.
Lightweight Universal API Router - Route requests to any API through configuration instead of hardcoded clients. Works with any data type and transport protocol. Plug in your own transports and parsers as needed.
Part of the Agnosweaverโข project suite.
๐ฅ Why agnomux2?
Tired of maintaining dozens of API client libraries? Frustrated with hardcoded HTTP clients, GraphQL clients, gRPC clients, and custom API wrappers? agnomux2 eliminates this complexity by providing a single, configurable router for all your API needs.
# Configure once, route anywhere
router = Router()
router.add_endpoint('weather', {
'transport': 'http',
'parser': 'json',
'request_template': {'url': 'https://api.weather.com/{city}'}
})
router.add_endpoint('payment', {
'transport': 'grpc',
'parser': 'protobuf',
'request_template': {'service': 'PaymentService'}
})
# Same interface for different APIs and protocols
weather = router.route('weather', city="London")
payment = router.route('payment', amount=100, currency="USD")
โจ Features
- ๐ Transport Agnostic: HTTP, gRPC, WebSockets, message queues, or any transport protocol
- ๐ Data Type Agnostic: Handle JSON, XML, binary, protobuf, or any data format seamlessly
- โ๏ธ Configuration-Driven: Define endpoints through config instead of hardcoded clients
- ๐๏ธ Extensible Architecture: Bring-your-own transports and parsers
- โก Sync & Async: Full support for both synchronous and asynchronous operations
- ๐ Streaming Support: Real-time streaming responses when supported by transports
- ๐ฆ Minimal Core: Lightweight router without bloat
- ๐ฏ Standardized Responses: Consistent response format across all endpoints and data types
- ๐ Plugin System: Registry-based component discovery and registration
- ๐ก๏ธ Error Transparency: Exceptions bubble up naturally - handle errors your way
๐ฆ Installation
# PyPI
pip install agnomux2
# GitHub
pip install git+https://github.com/agnosweaver/agnomux2.git
Requirements: Python 3.11 or higher
๐ Quick Start
1. Basic Setup
from agnomux2 import Router, TransportRegistry, ParserRegistry
# Create router
router = Router()
2. Implement Transport & Parser
Since agnomux2 provides the core routing logic, you implement transports and parsers for your specific needs:
import requests
import json
from agnomux2 import BaseTransport, BaseParser, RawResponse
class HTTPTransport(BaseTransport):
def send(self, data, **kwargs):
response = requests.post(data['url'], json=data.get('payload'))
return RawResponse(response.text, response.headers, response.status_code)
async def asend(self, data, **kwargs):
# Your async HTTP implementation
pass
class JSONParser(BaseParser):
def parse_request(self, data, template):
# Build request from template
return {
'url': template['url'].format(**data),
'payload': data
}
def parse_response(self, raw_response):
return json.loads(raw_response.data)
def get_content_type(self, raw_response):
return raw_response.headers.get('content-type', 'application/json')
# Register your components
TransportRegistry.register('http', HTTPTransport)
ParserRegistry.register('json', JSONParser)
3. Configure & Route!
# Add endpoints
router.add_endpoint('weather', {
'transport': 'http',
'parser': 'json',
'request_template': {
'url': 'https://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}'
}
})
router.add_endpoint('users', {
'transport': 'http',
'parser': 'json',
'request_template': {
'url': 'https://jsonplaceholder.typicode.com/users/{user_id}'
}
})
# Route requests
weather_response = router.route('weather', city="London", api_key="your-key")
user_response = router.route('users', user_id=1)
print(f"Weather: {weather_response.content}")
print(f"User: {user_response.content}")
๐ฏ Common Use Cases
API Gateway Pattern
# Route to different backends based on endpoint
router.add_endpoint('v1/payments', {
'transport': 'http',
'parser': 'json',
'request_template': {'url': 'https://payments-v1.internal.com/{endpoint}'}
})
router.add_endpoint('v2/payments', {
'transport': 'grpc',
'parser': 'protobuf',
'request_template': {'service': 'PaymentsV2Service'}
})
# Same interface, different backend implementations
old_payment = router.route('v1/payments', endpoint='charge', amount=100)
new_payment = router.route('v2/payments', amount=100, currency='USD')
Multi-Protocol Integration
# HTTP REST API
router.add_endpoint('rest_users', {
'transport': 'http',
'parser': 'json'
})
# GraphQL API
router.add_endpoint('graphql_users', {
'transport': 'http',
'parser': 'graphql'
})
# gRPC Service
router.add_endpoint('grpc_users', {
'transport': 'grpc',
'parser': 'protobuf'
})
# Same application logic, different protocols
for endpoint in ['rest_users', 'graphql_users', 'grpc_users']:
users = router.route(endpoint, query="active_users")
process_users(users.content) # Same processing logic
Database Abstraction
# Route to different databases with same interface
router.add_endpoint('postgres_users', {
'transport': 'sql',
'parser': 'json',
'request_template': {'query': 'SELECT * FROM users WHERE id = {user_id}'}
})
router.add_endpoint('mongodb_users', {
'transport': 'mongo',
'parser': 'bson',
'request_template': {'collection': 'users', 'filter': {'_id': '{user_id}'}}
})
# Same interface for different databases
pg_user = router.route('postgres_users', user_id=123)
mongo_user = router.route('mongodb_users', user_id=123)
Streaming Data Processing
# Stream from any endpoint that supports it
for chunk in router.route_stream('live_metrics', metric_type='cpu'):
print(f"CPU: {chunk.content['value']}%")
if chunk.is_final:
break
# Handle different streaming data types
for chunk in router.route_stream('video_feed', camera_id=1):
if chunk.content_type == 'video/mp4':
process_video_frame(chunk.content)
๐ API Reference
Core Classes
Router
Main interface for configuring endpoints and routing requests.
Methods:
add_endpoint(name, config)- Configure a new endpointroute(endpoint, **data)- Route request synchronouslyaroute(endpoint, **data)- Route request asynchronouslyroute_stream(endpoint, **data)- Stream from endpoint synchronouslyaroute_stream(endpoint, **data)- Stream from endpoint asynchronously
BaseTransport
Abstract base class for transport implementations.
Must Implement:
send(data, **kwargs)- Send data synchronouslyasend(data, **kwargs)- Send data asynchronously
Optional Override:
send_stream(data, **kwargs)- Send with streaming responseasend_stream(data, **kwargs)- Async send with streaming response
BaseParser
Abstract base class for parser implementations.
Must Implement:
parse_request(data, template)- Build request from data and templateparse_response(raw_response)- Parse transport response
Optional Override:
get_content_type(raw_response)- Extract content typeparse_stream_chunk(raw_chunk)- Parse streaming chunk
Data Structures
Response
@dataclass
class Response:
content: Any # Can be any data type
endpoint_name: str
content_type: str
timestamp: str
metadata: Optional[Dict[str, Any]] = None
StreamChunk
@dataclass
class StreamChunk:
content: Any # Can be any data type
endpoint_name: str
content_type: str
timestamp: str
metadata: Optional[Dict[str, Any]] = None
is_final: bool = False
RawResponse
class RawResponse:
def __init__(self, data: Any, headers: Optional[Dict] = None, status: Optional[Any] = None):
self.data = data
self.headers = headers or {}
self.status = status
๐๏ธ Architecture
agnomux2 follows a clean, modular architecture with clear separation of concerns:
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ Router โโโโโโ Registry โโโโโโ BaseTransport โ
โ (Orchestrator) โ โ (Discovery) โ โ (Communication) โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโ
โ โ โ
โ โโโโโโโโโโดโโโโโโโโโ โ
โ โ โ โ
โผ โผ โผ โผ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ Your App Code โ โ HTTP โ โ gRPC โ โ WebSocket โ
โ โ โTransport โ โ Transport โ โ Transport โ
โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ
โโโโโโโโโโโโโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโ
โ โ โ
โผ โผ โผ
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
โ JSON โ โ Protobuf โ โ XML โ
โ Parser โ โ Parser โ โ Parser โ
โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ
Key Principles:
- Configuration over Code: Define endpoints through config, not hardcoded clients
- Transport Abstraction: Same interface works with any protocol
- Data Type Flexibility: Handle any data format through pluggable parsers
- Error Transparency: Transport/parser exceptions bubble up naturally (by design)
- Registry Pattern: Components register themselves for discovery
โก Advanced Features
Template Engines
from agnomux2 import BaseTemplateEngine, TemplateRegistry
class JinjaTemplateEngine(BaseTemplateEngine):
def render_template(self, template, variables):
from jinja2 import Template
return Template(template).render(**variables)
TemplateRegistry.register('jinja', JinjaTemplateEngine)
# Use in endpoint config
router.add_endpoint('dynamic_api', {
'transport': 'http',
'parser': 'json',
'template_engine': 'jinja',
'request_template': {
'url': 'https://api.service.com/{{ endpoint }}/{{ resource_id }}',
'headers': {'Authorization': 'Bearer {{ token }}'}
}
})
Custom Transport Protocols
import websocket
from agnomux2 import BaseTransport
class WebSocketTransport(BaseTransport):
def send(self, data, **kwargs):
ws = websocket.create_connection(data['url'])
ws.send(json.dumps(data['payload']))
result = ws.recv()
ws.close()
return RawResponse(result)
class MessageQueueTransport(BaseTransport):
def send(self, data, **kwargs):
# Your message queue implementation
pass
Binary Data Handling
class ProtobufParser(BaseParser):
def parse_request(self, data, template):
# Serialize to protobuf
message = MyProtoMessage(**data)
return {'payload': message.SerializeToString()}
def parse_response(self, raw_response):
# Deserialize protobuf response
message = MyProtoMessage()
message.ParseFromString(raw_response.data)
return message
def get_content_type(self, raw_response):
return "application/x-protobuf"
๐ค Contributing
Coming Soon: A dedicated repository for community contributions and ideas is being set up. Stay tuned!
For now, if you have suggestions or find issues, please reach out via email.
๐ License
Licensed under the PolyForm Noncommercial License 1.0.0.
โ ๏ธ Important: This license prohibits commercial use. If you need to use agnomux2 in a commercial project, please contact agnosweaver@gmail.com for a commercial license.
๐ Contact
- Email: agnosweaver@gmail.com
- Project: Part of the Agnosweaverโข project suite
- Repository: https://github.com/agnosweaver/agnomux2
One interface. Many APIs.
agnomux2 keeps it clean and flexible.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file agnomux2-0.0.1a0.tar.gz.
File metadata
- Download URL: agnomux2-0.0.1a0.tar.gz
- Upload date:
- Size: 18.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
91a4c883d90270fd7053045245e2ee56d33d293964809293ca3acffdf4e9279c
|
|
| MD5 |
ab83b8c2464c22715e8c27cb09c131d0
|
|
| BLAKE2b-256 |
3b11aee6ef96ff580670559d9c2e6c81bf346c9016f263181ce38412eeec4b82
|
File details
Details for the file agnomux2-0.0.1a0-py3-none-any.whl.
File metadata
- Download URL: agnomux2-0.0.1a0-py3-none-any.whl
- Upload date:
- Size: 15.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
997bb61606b1d53da03bbad9547fcb6adc935e54596fe678f4456e9db7dca58e
|
|
| MD5 |
d256e51e6fe5e7ba1832654efa3f960d
|
|
| BLAKE2b-256 |
e3267391ea71d29127ba67cbf99f1f5251aaa7cadc1dd9e4c6999e53c3ea9b9e
|