High-performance AI voice assistant with dual-model streaming response
Loro is a high-performance AI voice assistant API service built in Rust, implementing a dual-model strategy to optimize response latency. The system uses a small model to generate immediate acknowledgment responses while a large model processes the complete response in parallel, significantly improving user experience in voice interactions.
Clone the repository
git clone <repository-url>
cd loro
Configure environment variables
# Create environment configuration
export SMALL_MODEL_API_KEY="your-small-model-api-key"
export LARGE_MODEL_API_KEY="your-large-model-api-key"
# Optional: customize endpoints and models
export SMALL_MODEL_BASE_URL="https://api.siliconflow.cn/v1"
export SMALL_MODEL_NAME="Qwen/Qwen2-1.5B-Instruct"
export LARGE_MODEL_BASE_URL="https://api.siliconflow.cn/v1"
export LARGE_MODEL_NAME="deepseek-ai/DeepSeek-V2.5"
Build and run
# Development mode
cargo run
# Production mode (optimized)
cargo run --release
Verify installation
# Run comprehensive test suite
cargo test
# Run example client
cargo run --example client
The service supports extensive configuration through environment variables:
# Required: Model API Keys
SMALL_MODEL_API_KEY=your-small-model-key
LARGE_MODEL_API_KEY=your-large-model-key
# Optional: Model Endpoints
SMALL_MODEL_BASE_URL=https://api.siliconflow.cn/v1 # Default (use http://127.0.0.1:11434 for Ollama)
LARGE_MODEL_BASE_URL=https://api.siliconflow.cn/v1 # Default (use http://127.0.0.1:11434 for Ollama)
SMALL_MODEL_NAME=Qwen/Qwen2-1.5B-Instruct # Default
LARGE_MODEL_NAME=deepseek-ai/DeepSeek-V2.5 # Default
# Optional: Server Configuration
HOST=0.0.0.0 # Default: 0.0.0.0
PORT=8000 # Default: 8000
LOG_LEVEL=info # Default: info
# Optional: Performance Tuning
HTTP_TIMEOUT_SECS=30 # Default: 30 (5-300)
SMALL_MODEL_TIMEOUT_SECS=5 # Default: 5 (1-30)
MAX_RETRIES=3 # Default: 3 (0-10)
STATS_MAX_ENTRIES=10000 # Default: 10000 (100-100000)
Notes:
*_BASE_URL=http://127.0.0.1:11434 and use *_API_KEY=none. In this case, the service will not send the Authorization header./chat/completions), Ollama uses JSONL over /api/chat. Loro normalizes both into OpenAI-style streaming chunks on the server side.POST /v1/chat/completions - OpenAI-compatible chat completion (supports streaming)GET / - Service information and statusGET /health - Health check endpointGET /metrics - Performance metrics and statisticsPOST /metrics/reset - Reset performance metricsQuick Response Mode (Default):
curl -X POST "http://localhost:8000/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "loro-voice-assistant",
"messages": [{"role": "user", "content": "Hello, how are you?"}],
"stream": true
}'
Direct Mode (Bypass Quick Response):
curl -X POST "http://localhost:8000/v1/chat/completions" \
-H "Content-Type: application/json" \
-d '{
"model": "loro-voice-assistant",
"messages": [{"role": "user", "content": "Hello, how are you?"}],
"stream": true,
"disable_quick_response": true
}'
Request Parameters:
model: Model identifier (any string, ignored in current implementation)messages: Array of message objects with role and contentstream: Boolean, defaults to true (non-streaming mode not implemented)max_tokens: Integer, 1-8192 (optional)temperature: Float, 0.0-2.0 (default: 0.7)disable_quick_response: Boolean, bypasses dual-model strategy (optional)tokio::join!*_BASE_URL is a standard OpenAI-compatible endpoint, requests use /chat/completions with SSE streaming. Auth header Bearer <API_KEY> is sent if *_API_KEY is not none.*_BASE_URL contains 11434, requests use /api/chat with streaming JSON lines. Set *_API_KEY=none. Loro parses Ollamaβs JSON line stream and converts it to OpenAI-style streaming chunks for clients.User Input β Request Validation β Dual Model Strategy
β
Small Model (Quick Response) β tokio::join! β Large Model (Complete Response)
β
Quick Response Sent β Stream Merger β Complete Response Streamed
β
Performance Metrics Updated
# Run all tests (30 total)
cargo test
# Run with single thread (avoids environment variable conflicts)
cargo test -- --test-threads=1
# Run specific test categories
cargo test --test integration_test # Integration tests
cargo test --test end_to_end_test # End-to-end tests
# Run with output
cargo test -- --nocapture
# Terminal 1: Start the service
cargo run --release
# Terminal 2: Run benchmark client
cargo run --example client
# View metrics
curl http://localhost:8000/metrics
Access detailed performance data via /metrics endpoint:
{
"quick_response_mode": {
"total_requests": 100,
"first_response_latency": {
"avg": 0.045, "min": 0.028, "max": 0.089,
"p50": 0.041, "p95": 0.076
},
"total_response_latency": {
"avg": 1.234, "min": 0.867, "max": 2.145,
"p50": 1.156, "p95": 1.987
}
},
"direct_mode": {
"total_requests": 50,
"first_response_latency": {
"avg": 0.678, "min": 0.445, "max": 1.234,
"p50": 0.634, "p95": 1.087
}
},
"comparison": {
"quick_mode_requests": 100,
"direct_mode_requests": 50,
"avg_first_response_improvement": 0.633
}
}
loro/
βββ src/
β βββ main.rs # Server entry point and HTTP handlers
β βββ lib.rs # Library exports
β βββ config.rs # Environment configuration management
β βββ models.rs # OpenAI-compatible data structures
β βββ service.rs # Core dual-model service logic
β βββ stats.rs # Performance statistics collection
β βββ errors.rs # Structured error types
βββ tests/
β βββ integration_test.rs # Integration and unit tests
β βββ end_to_end_test.rs # End-to-end system tests
βββ examples/
β βββ client.rs # Example client with benchmarking
βββ Cargo.toml # Project dependencies and metadata
βββ LICENSE # AGPL-3.0 license file
βββ README.md # Project documentation
Code Quality:
cargo fmt # Format code
cargo clippy # Lint and suggestions
cargo test # Run test suite
cargo doc --open # Generate documentation
Performance Profiling:
cargo run --release # Optimized build
cargo bench # Benchmarks (if implemented)
Debugging:
LOG_LEVEL=debug cargo run # Verbose logging (unified)
RUST_BACKTRACE=1 cargo run # Stack traces
errors.rsTo integrate additional AI model providers:
config.rsservice.rs to normalize provider streams to OpenAI-style chunksLOG_LEVEL=info for production logging (unified)/health endpointuse tower_http::cors::{Any, CorsLayer};
let cors = CorsLayer::new()
.allow_origin(["https://your.app".parse().unwrap()])
.allow_methods([http::Method::GET, http::Method::POST])
.allow_headers(Any);
// Router::new().layer(cors)
In current sample we use CorsLayer::permissive() for development convenience; tighten it in production.Contributions are welcome! Please feel free to submit issues and pull requests.
cargo testThis project is licensed under the AGPL-3.0 License - see the LICENSE file for details.
This project is a Rust reimplementation of the original Python BlastOff LLM voice assistant, designed to achieve significantly better performance and reliability while maintaining the innovative dual-model strategy for optimized voice interactions.
For questions, issues, or contributions: