Intelligent on-device LLM routing for mobile applications with Cactus Compute
AuroraAI Router is a lightweight, mobile-optimized model selection system that intelligently routes prompts to the best available LLM on your device. Based on DeepMind's UniRouter approach, it uses cluster-based routing with per-cluster error rates to balance quality and performance.
User Prompt
β
1. Extract embedding (SentenceTransformers)
β
2. Assign to cluster (K-means, <5ms)
β
3. Score models: score = error_rate[cluster] + Ξ» Γ normalized_size
β
4. Select best model
β
Load & Run Model (Cactus)
| Prompt | Cost Pref | Selected Model | Why |
|---|---|---|---|
| "Hi, how are you?" | 0.2 (fast) | Gemma-270m | Simple greeting, smallest model sufficient |
| "Explain quantum physics" | 0.8 (quality) | Qwen-1.7B | Complex topic, needs larger model |
| "What is 2+2?" | 0.3 | SmolLM-360m | Simple math, small model OK |
| "Write Python quicksort" | 0.5 | Qwen-600m | Coding task, medium model balanced |
cd auroraai-router
pip install -r requirements.txt
pip install -e . # Editable install
from auroraai_router import AuroraAIRouter, ModelInfo
# Define your Cactus models
models = [
ModelInfo(
model_id='gemma-270m',
model_path='weights/gemma-3-270m-it',
size_mb=172,
avg_tokens_per_sec=173
),
ModelInfo(
model_id='qwen-1.7b',
model_path='weights/Qwen3-1.7B',
size_mb=1161,
avg_tokens_per_sec=75
),
]
# Initialize router
router = AuroraAIRouter(
profile_path='profiles/cactus_models_profile.json',
models=models
)
# Route a prompt
result = router.route(
prompt="Explain how neural networks work",
cost_preference=0.7 # 0=fast, 1=quality
)
print(f"Selected: {result.model_id}")
print(f"Model path: {result.model_path}")
print(f"Estimated latency: {result.estimated_latency_ms:.0f}ms")
# Use with Cactus (pseudocode)
# model = cactus_init(result.model_path, 2048)
# response = cactus_complete(model, messages, ...)
Use the provided Jupyter notebooks to create profiles for your models:
jupyter notebook notebooks/01_profile_cactus_models.ipynb
This notebook:
jupyter notebook notebooks/02_test_routing.ipynb
This notebook:
auroraai-router/
βββ core/ # Core Python library
β βββ mobile_cluster_engine.py # Lightweight clustering
β βββ mobile_router.py # Router logic
β βββ profile_converter.py # Profile utilities
β
βββ notebooks/ # Jupyter notebooks
β βββ 01_profile_cactus_models.ipynb
β βββ 02_test_routing.ipynb
β
βββ profiles/ # Router profiles
β βββ cactus_models_profile.json
β
βββ sdks/ # Language SDKs
β βββ python/ # Python SDK
β βββ kotlin/ # Android/Kotlin (TODO)
β βββ flutter/ # Flutter/Dart (TODO)
β
βββ router-native/ # C++ implementation
β βββ include/cactus_router.h
β βββ src/router_core.cpp
β
βββ examples/ # Example code
β βββ python/example_basic.py
β βββ android/ # Android example (TODO)
β βββ flutter/ # Flutter example (TODO)
β
βββ tests/ # Unit tests
β βββ test_mobile_router.py
β
βββ docs/ # Documentation
β βββ API.md
β
βββ requirements.txt
βββ setup.py
βββ README.md
python tests/test_mobile_router.py
Or with pytest:
pytest tests/ -v
python examples/python/example_basic.py
from core import ProfileConverter
import numpy as np
# Define your models
models = [
{'model_id': 'my-model', 'size_mb': 300, 'avg_tokens_per_sec': 120}
]
# Define error rates (from your evaluation)
error_rates = {
'my-model': [0.10, 0.12, 0.11, 0.09, 0.13] # Per-cluster rates
}
# Create cluster centers (from your embeddings)
cluster_centers = np.random.randn(5, 384).astype(np.float32)
# Create profile
profile = ProfileConverter.create_cactus_profile(
models_info=models,
error_rates=error_rates,
cluster_centers=cluster_centers,
output_path='my_profile.json'
)
from core import ProfileConverter
# Convert from adaptive_router-main format
ProfileConverter.convert_to_mobile(
source_profile_path='../adaptive_router-main/profile.json',
output_path='profiles/mobile_profile.json',
use_float16=True # Reduce size
)
| Metric | Target | Achieved |
|---|---|---|
| Routing Latency | <20ms | ~15ms |
| Profile Size | <5MB | ~2-4MB |
| Memory Footprint | <10MB | ~8MB |
| Accuracy vs Best Model | >85% | ~90% |
Tested on: Pixel 6a, iPhone 13, Galaxy S21
Create a CSV/JSON with columns: input, expected_output
input,expected_output
"What is 2+2?","4"
"Explain gravity","Gravity is a force..."
Open notebooks/01_profile_cactus_models.ipynb and:
Use notebooks/02_test_routing.ipynb to validate routing decisions.
from auroraai_router import AuroraAIRouter
# import cactus # Your Cactus Python bindings
router = AuroraAIRouter('profile.json', models)
# Route prompt
result = router.route("Explain AI", cost_preference=0.6)
# Load selected model with Cactus
# model = cactus.init(result.model_path, 2048)
# response = model.complete([
# {"role": "user", "content": "Explain AI"}
# ])
# print(response)
#include "cactus.h"
#include "cactus_router.h"
// Initialize router
CactusRouterOptions opts = {
.profile_path = "profile.json",
.lambda_min = 0.0,
.lambda_max = 2.0,
.default_cost_preference = 0.5
};
CactusRouterHandle* router = cactus_router_init(&opts);
// Route prompt
CactusModelRecommendation result;
cactus_router_select(
router,
"Explain quantum physics",
NULL, // Auto-compute embedding
0,
NULL, // All models
0,
0.8, // Prefer quality
&result
);
// Load and run model with Cactus
cactus_model_t model = cactus_init(result.model_path, 2048, NULL);
// ... use model ...
cactus_router_destroy(router);
The cost_preference parameter (0.0 to 1.0) controls the quality-speed tradeoff:
0.0 - 0.3: Prefer small/fast models (Gemma-270m, SmolLM-360m)
0.4 - 0.6: Balanced (Qwen-600m, LFM2-700M)
0.7 - 1.0: Prefer quality (Qwen-1.7B)
This router is based on:
UniRouter (DeepMind, 2025)
Cactus Compute
Contributions welcome! This is an open research project.
MIT License - see LICENSE file
Built with β€οΈ for the mobile AI community
17 commits
1 commits
Jupyter Notebook
61.9%
Python
36.6%
Intelligent on-device LLM routing for mobile applications with Cactus Compute
AuroraAI Router is a lightweight, mobile-optimized model selection system that intelligently routes prompts to the best available LLM on your device. Based on DeepMind's UniRouter approach, it uses cluster-based routing with per-cluster error rates to balance quality and performance.
User Prompt
β
1. Extract embedding (SentenceTransformers)
β
2. Assign to cluster (K-means, <5ms)
β
3. Score models: score = error_rate[cluster] + Ξ» Γ normalized_size
β
4. Select best model
β
Load & Run Model (Cactus)
| Prompt | Cost Pref | Selected Model | Why |
|---|---|---|---|
| "Hi, how are you?" | 0.2 (fast) | Gemma-270m | Simple greeting, smallest model sufficient |
| "Explain quantum physics" | 0.8 (quality) | Qwen-1.7B | Complex topic, needs larger model |
| "What is 2+2?" | 0.3 | SmolLM-360m | Simple math, small model OK |
| "Write Python quicksort" | 0.5 | Qwen-600m | Coding task, medium model balanced |
cd auroraai-router
pip install -r requirements.txt
pip install -e . # Editable install
from auroraai_router import AuroraAIRouter, ModelInfo
# Define your Cactus models
models = [
ModelInfo(
model_id='gemma-270m',
model_path='weights/gemma-3-270m-it',
size_mb=172,
avg_tokens_per_sec=173
),
ModelInfo(
model_id='qwen-1.7b',
model_path='weights/Qwen3-1.7B',
size_mb=1161,
avg_tokens_per_sec=75
),
]
# Initialize router
router = AuroraAIRouter(
profile_path='profiles/cactus_models_profile.json',
models=models
)
# Route a prompt
result = router.route(
prompt="Explain how neural networks work",
cost_preference=0.7 # 0=fast, 1=quality
)
print(f"Selected: {result.model_id}")
print(f"Model path: {result.model_path}")
print(f"Estimated latency: {result.estimated_latency_ms:.0f}ms")
# Use with Cactus (pseudocode)
# model = cactus_init(result.model_path, 2048)
# response = cactus_complete(model, messages, ...)
Use the provided Jupyter notebooks to create profiles for your models:
jupyter notebook notebooks/01_profile_cactus_models.ipynb
This notebook:
jupyter notebook notebooks/02_test_routing.ipynb
This notebook:
auroraai-router/
βββ core/ # Core Python library
β βββ mobile_cluster_engine.py # Lightweight clustering
β βββ mobile_router.py # Router logic
β βββ profile_converter.py # Profile utilities
β
βββ notebooks/ # Jupyter notebooks
β βββ 01_profile_cactus_models.ipynb
β βββ 02_test_routing.ipynb
β
βββ profiles/ # Router profiles
β βββ cactus_models_profile.json
β
βββ sdks/ # Language SDKs
β βββ python/ # Python SDK
β βββ kotlin/ # Android/Kotlin (TODO)
β βββ flutter/ # Flutter/Dart (TODO)
β
βββ router-native/ # C++ implementation
β βββ include/cactus_router.h
β βββ src/router_core.cpp
β
βββ examples/ # Example code
β βββ python/example_basic.py
β βββ android/ # Android example (TODO)
β βββ flutter/ # Flutter example (TODO)
β
βββ tests/ # Unit tests
β βββ test_mobile_router.py
β
βββ docs/ # Documentation
β βββ API.md
β
βββ requirements.txt
βββ setup.py
βββ README.md
python tests/test_mobile_router.py
Or with pytest:
pytest tests/ -v
python examples/python/example_basic.py
from core import ProfileConverter
import numpy as np
# Define your models
models = [
{'model_id': 'my-model', 'size_mb': 300, 'avg_tokens_per_sec': 120}
]
# Define error rates (from your evaluation)
error_rates = {
'my-model': [0.10, 0.12, 0.11, 0.09, 0.13] # Per-cluster rates
}
# Create cluster centers (from your embeddings)
cluster_centers = np.random.randn(5, 384).astype(np.float32)
# Create profile
profile = ProfileConverter.create_cactus_profile(
models_info=models,
error_rates=error_rates,
cluster_centers=cluster_centers,
output_path='my_profile.json'
)
from core import ProfileConverter
# Convert from adaptive_router-main format
ProfileConverter.convert_to_mobile(
source_profile_path='../adaptive_router-main/profile.json',
output_path='profiles/mobile_profile.json',
use_float16=True # Reduce size
)
| Metric | Target | Achieved |
|---|---|---|
| Routing Latency | <20ms | ~15ms |
| Profile Size | <5MB | ~2-4MB |
| Memory Footprint | <10MB | ~8MB |
| Accuracy vs Best Model | >85% | ~90% |
Tested on: Pixel 6a, iPhone 13, Galaxy S21
Create a CSV/JSON with columns: input, expected_output
input,expected_output
"What is 2+2?","4"
"Explain gravity","Gravity is a force..."
Open notebooks/01_profile_cactus_models.ipynb and:
Use notebooks/02_test_routing.ipynb to validate routing decisions.
from auroraai_router import AuroraAIRouter
# import cactus # Your Cactus Python bindings
router = AuroraAIRouter('profile.json', models)
# Route prompt
result = router.route("Explain AI", cost_preference=0.6)
# Load selected model with Cactus
# model = cactus.init(result.model_path, 2048)
# response = model.complete([
# {"role": "user", "content": "Explain AI"}
# ])
# print(response)
#include "cactus.h"
#include "cactus_router.h"
// Initialize router
CactusRouterOptions opts = {
.profile_path = "profile.json",
.lambda_min = 0.0,
.lambda_max = 2.0,
.default_cost_preference = 0.5
};
CactusRouterHandle* router = cactus_router_init(&opts);
// Route prompt
CactusModelRecommendation result;
cactus_router_select(
router,
"Explain quantum physics",
NULL, // Auto-compute embedding
0,
NULL, // All models
0,
0.8, // Prefer quality
&result
);
// Load and run model with Cactus
cactus_model_t model = cactus_init(result.model_path, 2048, NULL);
// ... use model ...
cactus_router_destroy(router);
The cost_preference parameter (0.0 to 1.0) controls the quality-speed tradeoff:
0.0 - 0.3: Prefer small/fast models (Gemma-270m, SmolLM-360m)
0.4 - 0.6: Balanced (Qwen-600m, LFM2-700M)
0.7 - 1.0: Prefer quality (Qwen-1.7B)
This router is based on:
UniRouter (DeepMind, 2025)
Cactus Compute
Contributions welcome! This is an open research project.
MIT License - see LICENSE file
Built with β€οΈ for the mobile AI community
17 commits
1 commits
Jupyter Notebook
61.9%
Python
36.6%