router-maestro 0.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. router_maestro-0.1.2/.env.example +20 -0
  2. router_maestro-0.1.2/.gitignore +103 -0
  3. router_maestro-0.1.2/CLAUDE.md +79 -0
  4. router_maestro-0.1.2/Dockerfile +64 -0
  5. router_maestro-0.1.2/LICENSE +21 -0
  6. router_maestro-0.1.2/Makefile +140 -0
  7. router_maestro-0.1.2/PKG-INFO +383 -0
  8. router_maestro-0.1.2/README.md +346 -0
  9. router_maestro-0.1.2/docker-compose.yml +72 -0
  10. router_maestro-0.1.2/docs/deployment.md +210 -0
  11. router_maestro-0.1.2/pyproject.toml +81 -0
  12. router_maestro-0.1.2/src/router_maestro/__init__.py +3 -0
  13. router_maestro-0.1.2/src/router_maestro/__main__.py +6 -0
  14. router_maestro-0.1.2/src/router_maestro/auth/__init__.py +18 -0
  15. router_maestro-0.1.2/src/router_maestro/auth/github_oauth.py +181 -0
  16. router_maestro-0.1.2/src/router_maestro/auth/manager.py +136 -0
  17. router_maestro-0.1.2/src/router_maestro/auth/storage.py +91 -0
  18. router_maestro-0.1.2/src/router_maestro/cli/__init__.py +1 -0
  19. router_maestro-0.1.2/src/router_maestro/cli/auth.py +167 -0
  20. router_maestro-0.1.2/src/router_maestro/cli/client.py +322 -0
  21. router_maestro-0.1.2/src/router_maestro/cli/config.py +132 -0
  22. router_maestro-0.1.2/src/router_maestro/cli/context.py +146 -0
  23. router_maestro-0.1.2/src/router_maestro/cli/main.py +42 -0
  24. router_maestro-0.1.2/src/router_maestro/cli/model.py +288 -0
  25. router_maestro-0.1.2/src/router_maestro/cli/server.py +117 -0
  26. router_maestro-0.1.2/src/router_maestro/cli/stats.py +76 -0
  27. router_maestro-0.1.2/src/router_maestro/config/__init__.py +72 -0
  28. router_maestro-0.1.2/src/router_maestro/config/contexts.py +29 -0
  29. router_maestro-0.1.2/src/router_maestro/config/paths.py +50 -0
  30. router_maestro-0.1.2/src/router_maestro/config/priorities.py +93 -0
  31. router_maestro-0.1.2/src/router_maestro/config/providers.py +34 -0
  32. router_maestro-0.1.2/src/router_maestro/config/server.py +115 -0
  33. router_maestro-0.1.2/src/router_maestro/config/settings.py +76 -0
  34. router_maestro-0.1.2/src/router_maestro/providers/__init__.py +31 -0
  35. router_maestro-0.1.2/src/router_maestro/providers/anthropic.py +203 -0
  36. router_maestro-0.1.2/src/router_maestro/providers/base.py +123 -0
  37. router_maestro-0.1.2/src/router_maestro/providers/copilot.py +346 -0
  38. router_maestro-0.1.2/src/router_maestro/providers/openai.py +188 -0
  39. router_maestro-0.1.2/src/router_maestro/providers/openai_compat.py +175 -0
  40. router_maestro-0.1.2/src/router_maestro/routing/__init__.py +5 -0
  41. router_maestro-0.1.2/src/router_maestro/routing/router.py +526 -0
  42. router_maestro-0.1.2/src/router_maestro/server/__init__.py +5 -0
  43. router_maestro-0.1.2/src/router_maestro/server/app.py +87 -0
  44. router_maestro-0.1.2/src/router_maestro/server/middleware/__init__.py +11 -0
  45. router_maestro-0.1.2/src/router_maestro/server/middleware/auth.py +66 -0
  46. router_maestro-0.1.2/src/router_maestro/server/oauth_sessions.py +159 -0
  47. router_maestro-0.1.2/src/router_maestro/server/routes/__init__.py +8 -0
  48. router_maestro-0.1.2/src/router_maestro/server/routes/admin.py +358 -0
  49. router_maestro-0.1.2/src/router_maestro/server/routes/anthropic.py +228 -0
  50. router_maestro-0.1.2/src/router_maestro/server/routes/chat.py +142 -0
  51. router_maestro-0.1.2/src/router_maestro/server/routes/models.py +34 -0
  52. router_maestro-0.1.2/src/router_maestro/server/schemas/__init__.py +57 -0
  53. router_maestro-0.1.2/src/router_maestro/server/schemas/admin.py +87 -0
  54. router_maestro-0.1.2/src/router_maestro/server/schemas/anthropic.py +246 -0
  55. router_maestro-0.1.2/src/router_maestro/server/schemas/openai.py +107 -0
  56. router_maestro-0.1.2/src/router_maestro/server/translation.py +636 -0
  57. router_maestro-0.1.2/src/router_maestro/stats/__init__.py +14 -0
  58. router_maestro-0.1.2/src/router_maestro/stats/heatmap.py +154 -0
  59. router_maestro-0.1.2/src/router_maestro/stats/storage.py +228 -0
  60. router_maestro-0.1.2/src/router_maestro/stats/tracker.py +73 -0
  61. router_maestro-0.1.2/src/router_maestro/utils/__init__.py +16 -0
  62. router_maestro-0.1.2/src/router_maestro/utils/logging.py +81 -0
  63. router_maestro-0.1.2/src/router_maestro/utils/tokens.py +51 -0
  64. router_maestro-0.1.2/tests/__init__.py +1 -0
  65. router_maestro-0.1.2/tests/test_auth.py +94 -0
  66. router_maestro-0.1.2/tests/test_config.py +86 -0
  67. router_maestro-0.1.2/tests/test_providers.py +115 -0
  68. router_maestro-0.1.2/tests/test_router.py +147 -0
  69. router_maestro-0.1.2/tests/test_stats.py +78 -0
  70. router_maestro-0.1.2/tests/test_translation.py +181 -0
  71. router_maestro-0.1.2/tests/test_utils.py +63 -0
  72. router_maestro-0.1.2/uv.lock +1152 -0
@@ -0,0 +1,20 @@
1
+ # Domain configuration
2
+ DOMAIN=api.example.com
3
+
4
+ # Cloudflare API token for DNS challenge
5
+ # Generate at: https://dash.cloudflare.com/profile/api-tokens
6
+ # Required permissions: Zone:DNS:Edit
7
+ CF_DNS_API_TOKEN=your_cloudflare_api_token
8
+
9
+ # Let's Encrypt email for certificate notifications
10
+ ACME_EMAIL=your-email@example.com
11
+
12
+ # Router-Maestro API key (generate a secure random string)
13
+ ROUTER_MAESTRO_API_KEY=your_secure_api_key_here
14
+
15
+ # Log level (DEBUG, INFO, WARNING, ERROR)
16
+ ROUTER_MAESTRO_LOG_LEVEL=INFO
17
+
18
+ # Traefik dashboard auth (generate with: htpasswd -nB admin)
19
+ # Example: admin:$2y$05$... (user:bcrypt_hash)
20
+ TRAEFIK_DASHBOARD_AUTH=admin:$$2y$$05$$your_bcrypt_hash_here
@@ -0,0 +1,103 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+ *.pyc
6
+ *.pyo
7
+ *.pyd
8
+
9
+ # C extensions
10
+ *.so
11
+
12
+ # Distribution / packaging
13
+ .Python
14
+ build/
15
+ develop-eggs/
16
+ dist/
17
+ downloads/
18
+ eggs/
19
+ .eggs/
20
+ lib/
21
+ lib64/
22
+ parts/
23
+ sdist/
24
+ var/
25
+ wheels/
26
+ *.egg-info/
27
+ .installed.cfg
28
+ *.egg
29
+ *.whl
30
+ *.tar.gz
31
+
32
+ # PyInstaller
33
+ *.manifest
34
+ *.spec
35
+
36
+ # Installer logs
37
+ pip-log.txt
38
+ pip-delete-this-directory.txt
39
+
40
+ # Unit test / coverage reports
41
+ htmlcov/
42
+ .tox/
43
+ .nox/
44
+ .coverage
45
+ .coverage.*
46
+ .cache
47
+ nosetests.xml
48
+ coverage.xml
49
+ *.cover
50
+ *.py,cover
51
+ .hypothesis/
52
+ .pytest_cache/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Environments
59
+ .env
60
+ .venv
61
+ env/
62
+ venv/
63
+ ENV/
64
+ env.bak/
65
+ venv.bak/
66
+
67
+ # IDE
68
+ .idea/
69
+ .vscode/
70
+ .claude/
71
+ .serena/
72
+ *.swp
73
+ *.swo
74
+ *.project
75
+ .pydevproject
76
+
77
+ # UV
78
+ .uv/
79
+
80
+ # Ruff
81
+ .ruff_cache/
82
+
83
+ # MyPy
84
+ .mypy_cache/
85
+ .dmypy.json
86
+ dmypy.json
87
+
88
+ # Logs
89
+ *.log
90
+
91
+ # Project specific
92
+ *.db
93
+ *.sqlite3
94
+
95
+ # macOS
96
+ .DS_Store
97
+ .AppleDouble
98
+ .LSOverride
99
+
100
+ # Temporary files
101
+ *.tmp
102
+ *.bak
103
+ *~
@@ -0,0 +1,79 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ ## Build and Development Commands
6
+
7
+ ```bash
8
+ # Install dependencies
9
+ uv pip install -e ".[dev]"
10
+
11
+ # Run the CLI
12
+ uv run router-maestro --help
13
+
14
+ # Start the API server
15
+ uv run router-maestro server start --port 8080
16
+
17
+ # Run all tests
18
+ uv run pytest tests/ -v
19
+
20
+ # Run a single test file
21
+ uv run pytest tests/test_auth.py -v
22
+
23
+ # Run a specific test
24
+ uv run pytest tests/test_auth.py::TestAuthStorage::test_empty_storage -v
25
+
26
+ # Lint code
27
+ uv run ruff check src/
28
+
29
+ # Format code
30
+ uv run ruff format src/
31
+ ```
32
+
33
+ ## Architecture Overview
34
+
35
+ Router-Maestro is a multi-model routing system that exposes both OpenAI-compatible and Anthropic-compatible APIs. It routes requests to various LLM providers (GitHub Copilot, OpenAI, Anthropic, custom) with priority-based routing and fallback support.
36
+
37
+ ### Core Components
38
+
39
+ **Router (`src/router_maestro/routing/router.py`)**
40
+ - Central routing logic that selects providers based on model priorities
41
+ - Handles auto-routing when model is `router-maestro`
42
+ - Implements fallback to alternative providers on failure
43
+ - Caches model availability from all providers
44
+
45
+ **Providers (`src/router_maestro/providers/`)**
46
+ - `BaseProvider` - Abstract base class defining the provider interface
47
+ - `CopilotProvider` - GitHub Copilot integration with OAuth
48
+ - `OpenAIProvider` - Native OpenAI API
49
+ - `AnthropicProvider` - Native Anthropic API
50
+ - `OpenAICompatibleProvider` - Custom providers with OpenAI-compatible APIs
51
+
52
+ **Server (`src/router_maestro/server/`)**
53
+ - FastAPI application with two API flavors:
54
+ - OpenAI-compatible: `/v1/chat/completions`, `/v1/models`
55
+ - Anthropic-compatible: `/v1/messages`, `/api/anthropic/v1/messages`
56
+ - `translation.py` - Converts between Anthropic and OpenAI request/response formats
57
+ - `schemas/` - Pydantic models for both API formats
58
+
59
+ **CLI (`src/router_maestro/cli/`)**
60
+ - Typer-based CLI with subcommands: `server`, `auth`, `model`, `context`, `config`, `stats`
61
+ - Each subcommand in its own module registered in `main.py`
62
+
63
+ ### Data Flow
64
+
65
+ 1. Request arrives at API endpoint (OpenAI or Anthropic format)
66
+ 2. Anthropic requests are translated to internal OpenAI format
67
+ 3. Router selects provider based on model key and priorities
68
+ 4. Provider makes upstream API call
69
+ 5. Response is translated back if needed (for Anthropic API)
70
+
71
+ ### File Locations
72
+
73
+ Configuration and data files follow XDG conventions:
74
+ - **Config** (`~/.config/router-maestro/`): `providers.json`, `priorities.json`, `contexts.json`
75
+ - **Data** (`~/.local/share/router-maestro/`): `auth.json`, `server.json`, `stats.db`
76
+
77
+ ### Model Identification
78
+
79
+ Models are identified by `provider/model-id` format (e.g., `github-copilot/gpt-4o`). The special model name `router-maestro` triggers auto-routing based on priority configuration.
@@ -0,0 +1,64 @@
1
+ # Multi-stage build for minimal final image
2
+ # Stage 1: Builder with compilation tools
3
+ FROM python:3.11-alpine AS builder
4
+
5
+ WORKDIR /app
6
+
7
+ # Install build dependencies for native extensions
8
+ # - gcc, musl-dev: C compiler for native extensions
9
+ # - libffi-dev: Required by cffi/cryptography
10
+ # - cargo, rust: Required by tiktoken
11
+ RUN apk add --no-cache \
12
+ gcc \
13
+ musl-dev \
14
+ libffi-dev \
15
+ cargo \
16
+ rust
17
+
18
+ # Install uv for fast package installation
19
+ RUN pip install --no-cache-dir uv
20
+
21
+ # Copy project files
22
+ COPY pyproject.toml README.md ./
23
+ COPY src/ src/
24
+
25
+ # Install dependencies into a virtual environment
26
+ RUN python -m venv /opt/venv
27
+ ENV PATH="/opt/venv/bin:$PATH"
28
+ RUN uv pip install --no-cache .
29
+
30
+ # Stage 2: Minimal runtime image
31
+ FROM python:3.11-alpine
32
+
33
+ WORKDIR /app
34
+
35
+ # Install runtime dependencies only (no build tools)
36
+ RUN apk add --no-cache \
37
+ libffi \
38
+ curl
39
+
40
+ # Copy virtual environment from builder
41
+ COPY --from=builder /opt/venv /opt/venv
42
+ ENV PATH="/opt/venv/bin:$PATH"
43
+
44
+ # Create non-root user
45
+ RUN adduser -D -u 1000 maestro
46
+ USER maestro
47
+
48
+ # Create data and logs directories
49
+ RUN mkdir -p /home/maestro/.local/share/router-maestro/logs \
50
+ && mkdir -p /home/maestro/.config/router-maestro
51
+
52
+ # Environment variables
53
+ ENV PYTHONUNBUFFERED=1 \
54
+ PYTHONDONTWRITEBYTECODE=1
55
+
56
+ # Expose default port
57
+ EXPOSE 8080
58
+
59
+ # Health check
60
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
61
+ CMD curl -f http://localhost:8080/health || exit 1
62
+
63
+ # Run the server
64
+ CMD ["router-maestro", "server", "start", "--host", "0.0.0.0", "--port", "8080"]
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Router-Maestro Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,140 @@
1
+ .PHONY: help install dev test lint format clean build push run stop logs shell docker-up docker-down docker-logs buildx-setup build-multiarch dist publish publish-test
2
+
3
+ # Variables
4
+ VERSION := $(shell grep '^version' pyproject.toml | head -1 | cut -d'"' -f2)
5
+ IMAGE_NAME := likanwen/router-maestro
6
+ DOCKER_TAGS := -t $(IMAGE_NAME):$(VERSION) -t $(IMAGE_NAME):latest
7
+ PLATFORMS := linux/amd64,linux/arm64
8
+
9
+ # Default target
10
+ help:
11
+ @echo "Router-Maestro Development Commands"
12
+ @echo ""
13
+ @echo "Development:"
14
+ @echo " make install Install dependencies"
15
+ @echo " make dev Install with dev dependencies"
16
+ @echo " make test Run tests"
17
+ @echo " make lint Run linter (ruff check)"
18
+ @echo " make format Format code (ruff format)"
19
+ @echo " make clean Clean build artifacts"
20
+ @echo ""
21
+ @echo "Build & Publish:"
22
+ @echo " make dist Build Python package (sdist + wheel)"
23
+ @echo " make publish Publish to PyPI (requires PYPI_TOKEN)"
24
+ @echo ""
25
+ @echo "Local Server:"
26
+ @echo " make run Start local server (port 8080)"
27
+ @echo " make run-debug Start with DEBUG logging"
28
+ @echo " make stop Stop local server"
29
+ @echo ""
30
+ @echo "Docker:"
31
+ @echo " make build Build Docker image ($(IMAGE_NAME):$(VERSION))"
32
+ @echo " make push Push image to Docker Hub"
33
+ @echo " make buildx-setup Setup Docker buildx for multi-arch builds"
34
+ @echo " make build-multiarch Build and push multi-arch image (amd64, arm64)"
35
+ @echo " make shell Open shell in container"
36
+ @echo ""
37
+ @echo "Docker Compose (Production):"
38
+ @echo " make docker-up Start services (Traefik + Router-Maestro)"
39
+ @echo " make docker-down Stop services"
40
+ @echo " make docker-logs View logs"
41
+ @echo ""
42
+ @echo "Current version: $(VERSION)"
43
+
44
+ # ============== Development ==============
45
+
46
+ install:
47
+ uv pip install .
48
+
49
+ dev:
50
+ uv pip install -e ".[dev]"
51
+
52
+ test:
53
+ uv run pytest tests/ -v
54
+
55
+ lint:
56
+ uv run ruff check src/ tests/
57
+
58
+ format:
59
+ uv run ruff format src/ tests/
60
+ uv run ruff check --fix src/ tests/
61
+
62
+ clean:
63
+ rm -rf build/ dist/ *.egg-info .pytest_cache .ruff_cache src/*.egg-info
64
+ find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
65
+ find . -type f -name "*.pyc" -delete 2>/dev/null || true
66
+
67
+ # ============== Build & Publish ==============
68
+
69
+ dist: clean
70
+ uv build
71
+
72
+ publish: dist
73
+ @if [ -z "$$PYPI_TOKEN" ]; then \
74
+ echo "Error: PYPI_TOKEN environment variable is not set"; \
75
+ echo "Usage: PYPI_TOKEN=your_token make publish"; \
76
+ exit 1; \
77
+ fi
78
+ uv publish --token $$PYPI_TOKEN
79
+
80
+ publish-test: dist
81
+ @if [ -z "$$PYPI_TEST_TOKEN" ]; then \
82
+ echo "Error: PYPI_TEST_TOKEN environment variable is not set"; \
83
+ echo "Usage: PYPI_TEST_TOKEN=your_token make publish-test"; \
84
+ exit 1; \
85
+ fi
86
+ uv publish --token $$PYPI_TEST_TOKEN --publish-url https://test.pypi.org/legacy/
87
+
88
+ # ============== Local Server ==============
89
+
90
+ run:
91
+ uv run router-maestro server start --port 8080
92
+
93
+ run-debug:
94
+ uv run router-maestro server start --port 8080 --log-level DEBUG
95
+
96
+ stop:
97
+ @echo "Use Ctrl+C to stop the server"
98
+
99
+ # ============== Docker ==============
100
+
101
+ build:
102
+ docker build $(DOCKER_TAGS) .
103
+
104
+ push: build
105
+ docker push $(IMAGE_NAME):$(VERSION)
106
+ docker push $(IMAGE_NAME):latest
107
+
108
+ buildx-setup:
109
+ @docker buildx inspect multiarch > /dev/null 2>&1 || docker buildx create --name multiarch --use
110
+ docker buildx inspect --bootstrap
111
+
112
+ build-multiarch: buildx-setup
113
+ docker buildx build --platform $(PLATFORMS) $(DOCKER_TAGS) --push .
114
+
115
+ shell:
116
+ docker run --rm -it $(IMAGE_NAME):latest /bin/sh
117
+
118
+ # ============== Docker Compose ==============
119
+
120
+ docker-up:
121
+ docker compose up -d
122
+
123
+ docker-down:
124
+ docker compose down
125
+
126
+ docker-logs:
127
+ docker compose logs -f
128
+
129
+ docker-restart:
130
+ docker compose restart router-maestro
131
+
132
+ docker-pull:
133
+ docker compose pull
134
+
135
+ # ============== Release ==============
136
+
137
+ release: lint test build push
138
+ @echo "Released $(IMAGE_NAME):$(VERSION)"
139
+ git tag -a v$(VERSION) -m "Release v$(VERSION)"
140
+ @echo "Don't forget to push tags: git push origin v$(VERSION)"