This release is a pre-release and may not be stable for production use.
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.
Metadata
Release files for agnomux2 0.0.1a0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| agnomux2-0.0.1a0.tar.gz | 18.2 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| agnomux2-0.0.1a0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 34.0 kB
Release files / agnomux2-0.0.1a0.tar.gz
| Download URL | agnomux2-0.0.1a0.tar.gz |
|---|---|
| Size | 18.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
91a4c883d90270fd7053045245e2ee56d33d293964809293ca3acffdf4e9279c
|
|
BLAKE2b-256 checksum How to use checksums |
3b11aee6ef96ff580670559d9c2e6c81bf346c9016f263181ce38412eeec4b82
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Release files / agnomux2-0.0.1a0-py3-none-any.whl
| Download URL | agnomux2-0.0.1a0-py3-none-any.whl |
|---|---|
| Size | 15.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
997bb61606b1d53da03bbad9547fcb6adc935e54596fe678f4456e9db7dca58e
|
|
BLAKE2b-256 checksum How to use checksums |
e3267391ea71d29127ba67cbf99f1f5251aaa7cadc1dd9e4c6999e53c3ea9b9e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.10
|
Security for agnomux2 0.0.1a0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.