https://github.com/neuron-core/router
Route inference calls to different underlying providers based on a routing strategy you define.
https://github.com/neuron-core/router
Last synced: 12 days ago
JSON representation
Route inference calls to different underlying providers based on a routing strategy you define.
- Host: GitHub
- URL: https://github.com/neuron-core/router
- Owner: neuron-core
- License: mit
- Created: 2026-05-22T10:01:16.000Z (25 days ago)
- Default Branch: main
- Last Pushed: 2026-05-22T12:40:51.000Z (25 days ago)
- Last Synced: 2026-05-22T16:52:45.432Z (25 days ago)
- Language: PHP
- Homepage:
- Size: 7.83 MB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# Neuron AI Router
This package provides you with a `RouterProvider` component. It is a proxy that implements `AIProviderInterface` and routes inference calls (`chat`, `stream`, `structured`) to different underlying providers based on a routing strategy you define.
The agent doesn't know it's talking to a router, it's a drop-in replacement for any Neuron AI provider.
This is possible thanks to the Unified Messaging Layer that Neuron AI provides, with full support for multi-modality. Documentation here: https://docs.neuron-ai.dev/agent/messages

## When to Use It
- Route `structured()` calls to a provider with better structured output support (e.g., OpenAI) while using another provider for `chat()`
- Use different providers depending on the content of the messages (e.g., route image-heavy requests to Gemini)
- Switch providers based on whether tools are present in the request
- Implement cost-based or latency-based routing logic
## Installation
Install the composer package:
```
composer require neuron-ai/router
```
## Quick Start
```php
use NeuronAI\Router\RouterProvider;
use NeuronAI\Router\Rules\MethodRule;
use NeuronAI\Providers\Anthropic\Anthropic;
use NeuronAI\Providers\OpenAI\OpenAI;
class MyAgent extens Agent
{
protected function provider(): AIProviderInterface
{
return RouterProvider::make()
->addProvider('anthropic', new Anthropic(
key: 'ANTHROPIC_API_KEY',
model: 'claude-sonnet-4-20250514',
))
->addProvider('openai', new OpenAI(
key: 'OPENAI_API_KEY',
model: 'gpt-4o',
))
->setRule(
new RoundRobinRule(['anthropic', 'openai'])
);
}
protected function instructions(): string
{...}
protected function tools(): array
{...}
}
```
## Default Provider
The router delegates `messageMapper()` and `toolPayloadMapper()` to an underlying provider. After each inference call, these delegate to whichever provider the routing rule selected. If you need the mappers available before any inference call (e.g., during agent bootstrapping), set a default:
```php
RouterProvider::make()
->addProvider('anthropic', new Anthropic(...))
->addProvider('openai', new OpenAI(...))
->setDefaultProvider('anthropic')
->setRule(new RoundRobinRule(['anthropic', 'openai']));
```
The default is overwritten each time the routing rule resolves a provider, so it only acts as the initial fallback.
## Routing Rules
Routing logic is defined via the `RoutingRuleInterface`. The router calls `resolveProvider()` on the rule, passing context about the current request:
```php
interface RoutingRuleInterface
{
public function resolveProvider(string $method, array $messages, array $tools): string;
}
```
| Parameter | Type | Description |
|------------|----------|--------------------------------------------------------|
| `$method` | `string` | The inference method: `'chat'`, `'stream'`, or `'structured'` |
| `$messages`| `array` | The messages being sent to the provider |
| `$tools` | `array` | The tools configured for this request |
The method must return the name of a registered provider (as a string).
### Built-in Rules
#### MethodRule
Routes based on the inference method. Set a default provider and optionally override specific methods:
```php
use NeuronAI\Router\Rules\MethodRule;
$router = RouterProvider::make()->addProvider(...);
// Use Anthropic for everything, except structured output which goes to OpenAI
$router->setRule(
new MethodRule('anthropic')->structured('openai')
)
// Override each method individually
$router->setRule(
new MethodRule('openai')
->chat('anthropic')
->stream('anthropic')
->structured('openai')
)
```
#### CallbackRule
Wraps a callable for maximum flexibility. Use this when you need to inspect messages or tools:
```php
use NeuronAI\Router\Rules\CallbackRule;
// Route based on tools presence
$router->setRule(new CallbackRule(function (string $method, array $messages, array $tools): string {
if (count($tools) > 0) {
return 'anthropic';
}
return 'openai';
}))
```
#### RoundRobinRule
Distributes requests evenly across providers in sequence. Each call cycles to the next provider:
```php
use NeuronAI\Router\Rules\RoundRobinRule;
// Alternate between Anthropic and OpenAI for each request
$router->setRule(
new RoundRobinRule(['anthropic', 'openai'])
)
```
#### ContentRule
Routes based on the content blocks inside messages (images, files, audio, video). When a message contains a content type that not all providers support, you can route it to one that does:
```php
use NeuronAI\Router\Rules\ContentRule;
// Use Anthropic by default, route images and video to Gemini, files to OpenAI
$router->setRule(
new ContentRule('anthropic')
->image('gemini')
->video('gemini')
->file('openai')
)
```
When multiple content types are present in the same request, precedence is: **video → audio → image → file → default**. Content types without a configured provider are ignored and fall through to the next type in the precedence order.
### Custom Rules
Implement `RoutingRuleInterface` to create your own routing logic:
```php
use NeuronAI\Router\Rules\RoutingRuleInterface;
class ImageAwareRule implements RoutingRuleInterface
{
public function __construct(
private string $defaultProvider,
private string $imageProvider,
) {}
public function resolveProvider(string $method, array $messages, array $tools): string
{
foreach ($messages as $message) {
foreach ($message->getContents() as $content) {
if ($content instanceof ImageContent) {
return $this->imageProvider;
}
}
}
return $this->defaultProvider;
}
}
```
Then use it:
```php
$router->setRule(
new ImageAwareRule(
defaultProvider: 'anthropic',
imageProvider: 'gemini',
)
)
```
## Using with an Agent
Inject the router just like any other provider — either via `setAiProvider()` or by overriding the `provider()` method:
```php
class MyAgent extends Agent
{
protected function provider(): AIProviderInterface
{
return RouterProvider::make()
->addProvider('anthropic', new Anthropic(
key: 'ANTHROPIC_API_KEY',
model: 'claude-sonnet-4-20250514',
))
->addProvider('openai', new OpenAI(
key: 'OPENAI_API_KEY',
model: 'gpt-4o',
))
->setRule(
new RoundRobinRule(['anthropic', 'openai'])
);
}
}
```
## Error Handling
The router throws `ProviderException` with clear messages for misconfiguration:
| Scenario | Error Message |
|----------|--------------|
| No routing rule set | `no routing strategy configured. Call setRule() to set one.` |
| No providers registered | `no providers registered. Call addProvider() to add one.` |
| Rule returns unknown name | `unknown provider 'name'. Available: ...` |
| Unknown default provider | `unknown provider 'name'. Available: ...` |
| Mapper called with no default or prior call | `no provider available for delegation. Call setDefaultProvider() or make an inference call first.` |
## Limitations
- `messageMapper()` and `toolPayloadMapper()` delegate to the last-resolved provider (or the default). These are internal to each concrete provider and are never called by the agent directly.
- `setHttpClient()` is forwarded to **all** registered providers.
- The routing rule is called on every inference request, so keep it fast.