mirror of
https://github.com/NicolasBohn/NexQuant.git
synced 2026-07-28 16:07:46 +00:00
239 lines
7.3 KiB
Markdown
239 lines
7.3 KiB
Markdown
|
|
# Predix Parallel Run System
|
|||
|
|
|
|||
|
|
## Overview
|
|||
|
|
|
|||
|
|
The Parallel Run System enables concurrent execution of 5+ factor generation experiments with automatic API key distribution and complete isolation between runs.
|
|||
|
|
|
|||
|
|
## Architecture
|
|||
|
|
|
|||
|
|
### Components
|
|||
|
|
|
|||
|
|
| File | Purpose |
|
|||
|
|
|------|---------|
|
|||
|
|
| `predix.py` | Extended with `--run-id` parameter for isolated single runs |
|
|||
|
|
| `predix_parallel.py` | Parallel runner manager with Rich live dashboard |
|
|||
|
|
| `factor_runner.py` | Modified to use `PARALLEL_RUN_ID` for path isolation |
|
|||
|
|
| `CoSTEER/__init__.py` | Modified to use `PARALLEL_RUN_ID` for intermediate results |
|
|||
|
|
|
|||
|
|
### Directory Structure (Per Run)
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
results/
|
|||
|
|
├── db/ # Shared database
|
|||
|
|
├── runs/
|
|||
|
|
│ ├── run1/ # Run #1 isolated results
|
|||
|
|
│ │ ├── factors/ # Factor JSON files
|
|||
|
|
│ │ ├── logs/ # Run-specific logs
|
|||
|
|
│ │ ├── db/ # Run-specific database
|
|||
|
|
│ │ └── costeer/ # CoSTEER intermediate results
|
|||
|
|
│ ├── run2/ # Run #2 isolated results
|
|||
|
|
│ │ └── ...
|
|||
|
|
│ └── runN/ # Run #N isolated results
|
|||
|
|
│ └── ...
|
|||
|
|
└── logs/ # Default (non-parallel) logs
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Log Files
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
fin_quant.log # Single run (run_id=0)
|
|||
|
|
fin_quant_run1.log # Parallel run #1
|
|||
|
|
fin_quant_run2.log # Parallel run #2
|
|||
|
|
...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Workspaces
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
RD-Agent_workspace/ # Single run (run_id=0)
|
|||
|
|
RD-Agent_workspace_run1/ # Parallel run #1
|
|||
|
|
RD-Agent_workspace_run2/ # Parallel run #2
|
|||
|
|
...
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Usage
|
|||
|
|
|
|||
|
|
### CLI - Single Parallel Run
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Run with isolated results
|
|||
|
|
predix quant --run-id 1 -m openrouter
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### CLI - Parallel Runner (Direct)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Run 5 experiments with 2 API keys
|
|||
|
|
python predix_parallel.py --runs 5 --api-keys 2
|
|||
|
|
|
|||
|
|
# Run 3 experiments with local model
|
|||
|
|
python predix_parallel.py --runs 3 --model local
|
|||
|
|
|
|||
|
|
# Custom configuration
|
|||
|
|
python predix_parallel.py -n 10 -k 2 -m openrouter
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Programmatic Usage
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
from predix_parallel import main
|
|||
|
|
|
|||
|
|
result = main(runs=5, api_keys=2, model="openrouter")
|
|||
|
|
print(f"Success: {result['success']}/{result['total']}")
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## API Key Distribution
|
|||
|
|
|
|||
|
|
The system distributes API keys using round-robin assignment:
|
|||
|
|
|
|||
|
|
| Run ID | API Key | Model |
|
|||
|
|
|--------|---------|-------|
|
|||
|
|
| 1 | Key 1 | openrouter |
|
|||
|
|
| 2 | Key 2 | openrouter |
|
|||
|
|
| 3 | Key 1 | openrouter |
|
|||
|
|
| 4 | Key 2 | openrouter |
|
|||
|
|
| 5 | Key 1 | openrouter |
|
|||
|
|
|
|||
|
|
**With 2 API keys:**
|
|||
|
|
- Runs 1, 3, 5 → Key 1
|
|||
|
|
- Runs 2, 4 → Key 2
|
|||
|
|
|
|||
|
|
**LiteLLM Load Balancing:**
|
|||
|
|
When 2 API keys are available, the system configures LiteLLM for parallel request handling:
|
|||
|
|
```
|
|||
|
|
OPENAI_API_KEY=key1,key2
|
|||
|
|
LITELLM_PARALLEL_CALLS=2
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Isolation Guarantees
|
|||
|
|
|
|||
|
|
Each parallel run is completely isolated:
|
|||
|
|
|
|||
|
|
### Environment Variables
|
|||
|
|
- `PARALLEL_RUN_ID=N` - Identifies the run
|
|||
|
|
- `RD_AGENT_WORKSPACE` - Points to run-specific workspace
|
|||
|
|
- `OPENAI_API_KEY` - Assigned API key for this run
|
|||
|
|
|
|||
|
|
### No Shared State
|
|||
|
|
- ✅ Separate log files
|
|||
|
|
- ✅ Separate result directories
|
|||
|
|
- ✅ Separate workspace directories
|
|||
|
|
- ✅ Separate database files (optional)
|
|||
|
|
- ✅ No race conditions (no shared mutable state)
|
|||
|
|
|
|||
|
|
### Graceful Degradation
|
|||
|
|
- If a run fails, others continue unaffected
|
|||
|
|
- Each run is independently restartable
|
|||
|
|
- Results are persisted immediately after completion
|
|||
|
|
|
|||
|
|
## Live Dashboard
|
|||
|
|
|
|||
|
|
The parallel runner shows a Rich-based live dashboard:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────┐
|
|||
|
|
│ 🔀 Predix Parallel Run Dashboard │
|
|||
|
|
├──────┬──────────┬──────────┬─────────┬──────────┬───────┤
|
|||
|
|
│ Run │ Status │ Elapsed │ API Key │ Model │ Exit │
|
|||
|
|
├──────┼──────────┼──────────┼─────────┼──────────┼───────┤
|
|||
|
|
│ #1 │ ✅ success│ 02:15:30│ 1 │openrouter│ 0 │
|
|||
|
|
│ #2 │ 🔄 running│ 01:45:12│ 2 │openrouter│ -- │
|
|||
|
|
│ #3 │ 🔄 running│ 01:42:08│ 1 │openrouter│ -- │
|
|||
|
|
│ #4 │ ⏳ pending│ --:--:--│ 2 │openrouter│ -- │
|
|||
|
|
│ #5 │ ❌ failed │ 00:05:23│ 1 │openrouter│ 1 │
|
|||
|
|
├──────┴──────────┴──────────┴─────────┴──────────┴───────┤
|
|||
|
|
│ Summary: 5 total | 1 done | 2 running | 1 pending | 1 failed │
|
|||
|
|
└─────────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Signal Handling
|
|||
|
|
|
|||
|
|
- **First Ctrl+C:** Gracefully stops all running subprocesses
|
|||
|
|
- **Second Ctrl+C:** Force kills all remaining processes
|
|||
|
|
- Dashboard updates in real-time during shutdown
|
|||
|
|
|
|||
|
|
## Configuration
|
|||
|
|
|
|||
|
|
### Environment Variables (`.env`)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Required for openrouter mode
|
|||
|
|
OPENROUTER_API_KEY=sk-or-your-first-key
|
|||
|
|
OPENROUTER_API_KEY_2=sk-or-your-second-key # Optional
|
|||
|
|
|
|||
|
|
# Required for local mode
|
|||
|
|
OPENAI_API_KEY=local
|
|||
|
|
OPENAI_API_BASE=http://localhost:8081/v1
|
|||
|
|
CHAT_MODEL=qwen3.5-35b
|
|||
|
|
|
|||
|
|
# Optional: Custom model
|
|||
|
|
OPENROUTER_MODEL=openrouter/qwen/qwen3.6-plus:free
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Performance
|
|||
|
|
|
|||
|
|
**Expected Speedup:**
|
|||
|
|
- 5 runs with 2 API keys ≈ 2.5× faster than sequential
|
|||
|
|
- 5 runs with local model ≈ 5× faster than sequential (no API rate limits)
|
|||
|
|
|
|||
|
|
**Overhead:**
|
|||
|
|
- ~1 second per run for subprocess startup
|
|||
|
|
- Dashboard refresh: 2 Hz (negligible CPU)
|
|||
|
|
|
|||
|
|
## Error Handling
|
|||
|
|
|
|||
|
|
| Scenario | Behavior |
|
|||
|
|
|----------|----------|
|
|||
|
|
| Run fails | Logged, others continue |
|
|||
|
|
| API key exhausted | Retry with next key |
|
|||
|
|
| Ctrl+C pressed | Graceful shutdown of all runs |
|
|||
|
|
| Disk full | Error logged, run marked failed |
|
|||
|
|
| LLM timeout | Run fails, others unaffected |
|
|||
|
|
|
|||
|
|
## Integration with Existing Code
|
|||
|
|
|
|||
|
|
### factor_runner.py Changes
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Before (shared paths)
|
|||
|
|
log_dir = project_root / "results" / "logs"
|
|||
|
|
factors_dir = project_root / "results" / "factors"
|
|||
|
|
|
|||
|
|
# After (parallel-aware)
|
|||
|
|
parallel_run_id = os.getenv("PARALLEL_RUN_ID", "0")
|
|||
|
|
if parallel_run_id != "0":
|
|||
|
|
log_dir = project_root / "results" / "runs" / f"run{parallel_run_id}" / "logs"
|
|||
|
|
factors_dir = project_root / "results" / "runs" / f"run{parallel_run_id}" / "factors"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### CoSTEER/__init__.py Changes
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Intermediate results isolation
|
|||
|
|
parallel_run_id = os.getenv("PARALLEL_RUN_ID", "0")
|
|||
|
|
if parallel_run_id != "0":
|
|||
|
|
results_dir = project_root / "results" / "runs" / f"run{parallel_run_id}" / "costeer"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Testing
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# Run all integration tests
|
|||
|
|
pytest test/integration/test_all_features.py -v
|
|||
|
|
|
|||
|
|
# Test parallel runner imports
|
|||
|
|
python -c "from predix_parallel import ParallelRunner, main; print('✅ OK')"
|
|||
|
|
|
|||
|
|
# Test CLI options
|
|||
|
|
predix quant --help # Should show --run-id option
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Future Enhancements
|
|||
|
|
|
|||
|
|
- [ ] Auto-detect optimal number of parallel runs based on API rate limits
|
|||
|
|
- [ ] Result aggregation and comparison across runs
|
|||
|
|
- [ ] Dynamic API key rebalancing (assign more runs to faster key)
|
|||
|
|
- [ ] Support for >2 API keys
|
|||
|
|
- [ ] Run prioritization (run high-priority experiments first)
|
|||
|
|
- [ ] Slack/email notifications on completion
|