omnimux 0.1.0__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 (58) hide show
  1. omnimux-0.1.0/.github/workflows/ci.yml +38 -0
  2. omnimux-0.1.0/.gitignore +52 -0
  3. omnimux-0.1.0/.pre-commit-config.yaml +21 -0
  4. omnimux-0.1.0/.python-version +1 -0
  5. omnimux-0.1.0/Makefile +15 -0
  6. omnimux-0.1.0/PKG-INFO +169 -0
  7. omnimux-0.1.0/README.md +154 -0
  8. omnimux-0.1.0/assets/omnimux-banner.png +0 -0
  9. omnimux-0.1.0/example.models.json +83 -0
  10. omnimux-0.1.0/install_service.sh +79 -0
  11. omnimux-0.1.0/pyproject.toml +69 -0
  12. omnimux-0.1.0/src/omnimux/__init__.py +0 -0
  13. omnimux-0.1.0/src/omnimux/__main__.py +211 -0
  14. omnimux-0.1.0/src/omnimux/app.py +101 -0
  15. omnimux-0.1.0/src/omnimux/config.py +16 -0
  16. omnimux-0.1.0/src/omnimux/exceptions.py +25 -0
  17. omnimux-0.1.0/src/omnimux/models/__init__.py +0 -0
  18. omnimux-0.1.0/src/omnimux/models/schemas.py +123 -0
  19. omnimux-0.1.0/src/omnimux/py.typed +1 -0
  20. omnimux-0.1.0/src/omnimux/routes/__init__.py +0 -0
  21. omnimux-0.1.0/src/omnimux/routes/proxy.py +313 -0
  22. omnimux-0.1.0/src/omnimux/services/__init__.py +0 -0
  23. omnimux-0.1.0/src/omnimux/services/adapters.py +166 -0
  24. omnimux-0.1.0/src/omnimux/services/cache.py +78 -0
  25. omnimux-0.1.0/src/omnimux/services/compressor.py +166 -0
  26. omnimux-0.1.0/src/omnimux/services/db.py +324 -0
  27. omnimux-0.1.0/src/omnimux/services/gatekeeper.py +32 -0
  28. omnimux-0.1.0/src/omnimux/services/gemini_client.py +138 -0
  29. omnimux-0.1.0/src/omnimux/services/model_discovery.py +153 -0
  30. omnimux-0.1.0/src/omnimux/services/multiplexer.py +212 -0
  31. omnimux-0.1.0/src/omnimux/services/router_core.py +202 -0
  32. omnimux-0.1.0/src/omnimux/services/sync.py +48 -0
  33. omnimux-0.1.0/src/omnimux/services/unloader.py +68 -0
  34. omnimux-0.1.0/src/omnimux/utils/__init__.py +0 -0
  35. omnimux-0.1.0/src/omnimux/utils/cli_checklist.py +76 -0
  36. omnimux-0.1.0/src/omnimux/utils/html.py +29 -0
  37. omnimux-0.1.0/tests/__init__.py +0 -0
  38. omnimux-0.1.0/tests/conftest.py +21 -0
  39. omnimux-0.1.0/tests/e2e/test_multi_turn.py +142 -0
  40. omnimux-0.1.0/tests/unit/test_adapters.py +305 -0
  41. omnimux-0.1.0/tests/unit/test_app.py +427 -0
  42. omnimux-0.1.0/tests/unit/test_benchmark.py +74 -0
  43. omnimux-0.1.0/tests/unit/test_cache.py +160 -0
  44. omnimux-0.1.0/tests/unit/test_cli_checklist.py +92 -0
  45. omnimux-0.1.0/tests/unit/test_compressor.py +219 -0
  46. omnimux-0.1.0/tests/unit/test_config.py +15 -0
  47. omnimux-0.1.0/tests/unit/test_db.py +182 -0
  48. omnimux-0.1.0/tests/unit/test_exceptions.py +20 -0
  49. omnimux-0.1.0/tests/unit/test_gatekeeper.py +26 -0
  50. omnimux-0.1.0/tests/unit/test_gemini_client.py +235 -0
  51. omnimux-0.1.0/tests/unit/test_html.py +52 -0
  52. omnimux-0.1.0/tests/unit/test_main.py +281 -0
  53. omnimux-0.1.0/tests/unit/test_model_discovery.py +187 -0
  54. omnimux-0.1.0/tests/unit/test_multiplexer.py +336 -0
  55. omnimux-0.1.0/tests/unit/test_router_core.py +203 -0
  56. omnimux-0.1.0/tests/unit/test_schemas.py +76 -0
  57. omnimux-0.1.0/tests/unit/test_sync.py +112 -0
  58. omnimux-0.1.0/tests/unit/test_unloader.py +108 -0
@@ -0,0 +1,38 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v4
13
+
14
+ - name: Install uv
15
+ uses: astral-sh/setup-uv@v5
16
+ with:
17
+ enable-cache: true
18
+ cache-dependency-glob: "uv.lock"
19
+
20
+ - name: Setup Python
21
+ uses: actions/setup-python@v5
22
+ with:
23
+ python-version-file: "pyproject.toml"
24
+
25
+ - name: Install dependencies
26
+ run: uv sync --all-groups
27
+
28
+ - name: Lint (Ruff Format)
29
+ run: uv run ruff format --check .
30
+
31
+ - name: Lint (Ruff Check)
32
+ run: uv run ruff check .
33
+
34
+ - name: Type Check (Mypy)
35
+ run: uv run mypy .
36
+
37
+ - name: Test (Pytest with Coverage)
38
+ run: uv run pytest tests/ --cov=src/omnimux
@@ -0,0 +1,52 @@
1
+ # Agent
2
+ .agent/
3
+
4
+ # Documentation (ERD, Testing, Plan)
5
+ .docs/
6
+
7
+ # Cache
8
+ .ruff_cache/
9
+ .mypy_cache/
10
+ .pytest_cache/
11
+
12
+ # Python
13
+ __pycache__/
14
+ *.py[cod]
15
+ *$py.class
16
+ .coverage
17
+ htmlcov/
18
+ .venv/
19
+ env/
20
+ venv/
21
+ .env
22
+
23
+ # uv
24
+ uv.lock
25
+
26
+ # IDEs
27
+ .vscode/
28
+ .idea/
29
+ .cursor/
30
+ .logs/
31
+
32
+ # Keys
33
+ models.json
34
+
35
+
36
+ # Local DB
37
+ nexus_llm_state.db
38
+ omnimux_state.db
39
+
40
+ # Tests
41
+ e2e_out.log
42
+
43
+ # Logs
44
+ nexus-llm.err.log
45
+ nexus-llm.log
46
+ nexus.log
47
+ omnimux.log
48
+ omnimux.err.log
49
+
50
+ # PID files
51
+ .nexus.pid
52
+ .omnimux.pid
@@ -0,0 +1,21 @@
1
+ repos:
2
+ - repo: local
3
+ hooks:
4
+ - id: ruff-format
5
+ name: ruff format
6
+ entry: uv run ruff format .
7
+ language: system
8
+ types: [python]
9
+ pass_filenames: false
10
+ - id: ruff-check
11
+ name: ruff check
12
+ entry: uv run ruff check . --fix
13
+ language: system
14
+ types: [python]
15
+ pass_filenames: false
16
+ - id: mypy
17
+ name: mypy
18
+ entry: uv run mypy .
19
+ language: system
20
+ types: [python]
21
+ pass_filenames: false
@@ -0,0 +1 @@
1
+ 3.12
omnimux-0.1.0/Makefile ADDED
@@ -0,0 +1,15 @@
1
+ .PHONY: lint typecheck test check format
2
+
3
+ lint:
4
+ uv run ruff check .
5
+
6
+ format:
7
+ uv run ruff check . --fix
8
+
9
+ typecheck:
10
+ uv run mypy .
11
+
12
+ test:
13
+ uv run pytest --cov=omnimux
14
+
15
+ check: format typecheck test
omnimux-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,169 @@
1
+ Metadata-Version: 2.5
2
+ Name: omnimux
3
+ Version: 0.1.0
4
+ Summary: Local intelligent LLM multiplexer, proxy, and router
5
+ Requires-Python: >=3.12
6
+ Requires-Dist: aiosqlite>=0.20.0
7
+ Requires-Dist: beautifulsoup4>=4.14.3
8
+ Requires-Dist: fastapi>=0.136.3
9
+ Requires-Dist: httpx>=0.28.1
10
+ Requires-Dist: markdownify>=1.2.2
11
+ Requires-Dist: pydantic-settings>=2.14.1
12
+ Requires-Dist: python-dotenv>=1.2.2
13
+ Requires-Dist: uvicorn>=0.48.0
14
+ Description-Content-Type: text/markdown
15
+
16
+ <p align="center">
17
+ <img src="assets/omnimux-banner.png" alt="omnimux banner" width="100%">
18
+ </p>
19
+
20
+ <p align="center">
21
+ <strong>The adaptive local multiplexer and fallback gateway for LLM coding agents.</strong><br>
22
+ One endpoint. Any agent. Zero downtime.
23
+ </p>
24
+
25
+ <p align="center">
26
+ <a href="https://pypi.org/project/omnimux/"><img src="https://img.shields.io/badge/pypi-omnimux-blue.svg?style=flat-square" alt="PyPI"></a>
27
+ <a href="https://python.org"><img src="https://img.shields.io/badge/python-3.12%2B-blue.svg?style=flat-square" alt="Python 3.12+"></a>
28
+ <a href="https://github.com/lucikritz/nexus-llm/actions"><img src="https://img.shields.io/badge/coverage-100%25-brightgreen.svg?style=flat-square" alt="Coverage"></a>
29
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License"></a>
30
+ </p>
31
+
32
+ ---
33
+
34
+ ## What is omnimux?
35
+
36
+ When coding agents (Claude Code, Cursor, RooCode, Continue, Open WebUI, Aider) hit rate limits (`429`), credit exhaustion (`402`), or local VRAM out-of-memory errors, tasks break mid-refactor.
37
+
38
+ **omnimux** is a lightweight, database-first local gateway that sits between your coding tools and your LLM backends. It translates between OpenAI and Anthropic protocols, multiplexes API keys across providers, automatically hot-swaps to fallbacks upon failure, and unloads idle local models before loading new ones.
39
+
40
+ ```
41
+ Coding Agents (Claude Code, Cursor, RooCode, Continue, Aider)
42
+ │
43
+ [ omnimux gateway ]
44
+ │
45
+ ┌───────────────┬──────────┴────────┬─────────────┬──────────┐
46
+ ▼ ▼ ▼ ▼ ▼
47
+ Anthropic OpenRouter Gemini Groq Ollama
48
+ (Claude 3.5) (Failover pool) (Long context) (Fast) (Local zero-OOM)
49
+ ```
50
+
51
+ ---
52
+
53
+ ## Compatible Extensions & Agents
54
+
55
+ omnimux exposes standard OpenAI (`/v1/chat/completions`) and Anthropic (`/v1/messages`) endpoints, making it a drop-in replacement across tools:
56
+
57
+ | Agent / Tool | Protocol | Configuration |
58
+ | :--- | :--- | :--- |
59
+ | **Claude Code** | Native Anthropic (`/v1/messages`) | `export ANTHROPIC_BASE_URL="http://localhost:11444"`<br>`export ANTHROPIC_API_KEY="om_your_client_token"` |
60
+ | **Cursor** | OpenAI Compatible (`/v1`) | Base URL: `http://localhost:11444/v1`<br>API Key: `om_your_client_token` |
61
+ | **RooCode / Cline** | OpenAI / Anthropic format | Base URL: `http://localhost:11444/v1` or Anthropic Base URL |
62
+ | **Continue.dev** | OpenAI / Anthropic format | `apiBase: "http://localhost:11444/v1"` in `config.yaml` |
63
+ | **Open WebUI** | OpenAI Compatible (`/v1`) | Connections &rarr; OpenAI API: `http://localhost:11444/v1` |
64
+ | **Aider** | OpenAI Compatible (`/v1`) | `export OPENAI_API_BASE="http://localhost:11444/v1"`<br>`export OPENAI_API_KEY="om_your_client_token"` |
65
+
66
+ ---
67
+
68
+ ## Core Features
69
+
70
+ - 🔄 **Autonomous 429 Hot-Swapping**: Automatically cycles through backup API keys and provider fallbacks with intelligent backoff cooldowns when an upstream service rate-limits or fails.
71
+ - 🦙 **Zero-OOM Ollama Engine**: Issues `keep_alive: 0` unloads for idle local models prior to spinning up fresh models, preventing out-of-memory crashes on unified-memory Apple Silicon and CUDA GPUs.
72
+ - ⚡ **Dual Protocol Translation**: Complete bidirectional translation between Anthropic `/v1/messages` and OpenAI `/v1/chat/completions` with full SSE streaming and token count endpoints.
73
+ - 🗜️ **Context Optimizer**: Converts bulky HTML pages from agent web searches into lean markdown and deduplicates multimodal vision images with local SHA-256 caching.
74
+ - 🔐 **Scoped Client Keys**: Generate isolated client tokens (`om_...`) per IDE extension with local database tracking, usage timestamps, and instant revocation.
75
+ - 📦 **Daemon & Single-Binary Simplicity**: Run seamlessly as a background service via `omnimux start -d` or inspect active logs with `omnimux status`.
76
+
77
+ ---
78
+
79
+ ## Quickstart
80
+
81
+ ### 1. Installation
82
+
83
+ Run via `uvx` (no installation required) or install with `pip`/`uv`:
84
+
85
+ ```bash
86
+ # Run immediately via uvx
87
+ uvx omnimux --help
88
+
89
+ # Or install globally into your environment
90
+ uv pip install omnimux
91
+ ```
92
+
93
+ *(Note: `omni`, `nexus`, and `nexus-llm` are registered aliases for all CLI commands.)*
94
+
95
+ ### 2. Discover & Register Models
96
+
97
+ Interactively discover and register models from local Ollama or upstream providers:
98
+
99
+ ```bash
100
+ omnimux model add
101
+ ```
102
+
103
+ ### 3. Start the Gateway
104
+
105
+ ```bash
106
+ # Start in the foreground
107
+ omnimux start --port 11444
108
+
109
+ # Or start as a background daemon
110
+ omnimux start -d --port 11444
111
+
112
+ # Check status or stop
113
+ omnimux status
114
+ omnimux stop
115
+ ```
116
+
117
+ ### 4. Create an Agent Token
118
+
119
+ Create an isolated client key for your coding agent:
120
+
121
+ ```bash
122
+ omnimux keys create claude-code
123
+ # Output: om_k9A2x...
124
+ ```
125
+
126
+ Then point your agent:
127
+
128
+ ```bash
129
+ export ANTHROPIC_BASE_URL="http://localhost:11444"
130
+ export ANTHROPIC_API_KEY="om_k9A2x..."
131
+ claude
132
+ ```
133
+
134
+ ---
135
+
136
+ ## CLI Reference
137
+
138
+ | Command | Description |
139
+ | :--- | :--- |
140
+ | `omnimux start [--port PORT] [-d]` | Start the gateway server (foreground or daemon) |
141
+ | `omnimux stop` | Terminate running daemon process |
142
+ | `omnimux status` | Inspect health, PID, and port of running daemon |
143
+ | `omnimux model add` | Interactively query and register upstream models |
144
+ | `omnimux model list` | List registered platform models and endpoints |
145
+ | `omnimux keys create [name]` | Generate a client API token (`om_...`) |
146
+ | `omnimux keys list` | List active client keys and last used timestamps |
147
+ | `omnimux keys revoke <id>` | Revoke a client key |
148
+ | `omnimux export <file.json>` | Export entire SQLite routing state to JSON |
149
+ | `omnimux cache --clear` | Purge local image and request cache |
150
+
151
+ ---
152
+
153
+ ## Configuration
154
+
155
+ Settings can be overridden via environment variables or a local `.env` file:
156
+
157
+ ```env
158
+ PORT=11444
159
+ SQLITE_DB_PATH=omnimux_state.db
160
+ PROXY_PASSWORD=optional_fallback_password
161
+ ```
162
+
163
+ Legacy `nx_...` keys and existing SQLite states are automatically recognized and supported for backwards compatibility.
164
+
165
+ ---
166
+
167
+ ## License
168
+
169
+ MIT &copy; LuciKritZ
@@ -0,0 +1,154 @@
1
+ <p align="center">
2
+ <img src="assets/omnimux-banner.png" alt="omnimux banner" width="100%">
3
+ </p>
4
+
5
+ <p align="center">
6
+ <strong>The adaptive local multiplexer and fallback gateway for LLM coding agents.</strong><br>
7
+ One endpoint. Any agent. Zero downtime.
8
+ </p>
9
+
10
+ <p align="center">
11
+ <a href="https://pypi.org/project/omnimux/"><img src="https://img.shields.io/badge/pypi-omnimux-blue.svg?style=flat-square" alt="PyPI"></a>
12
+ <a href="https://python.org"><img src="https://img.shields.io/badge/python-3.12%2B-blue.svg?style=flat-square" alt="Python 3.12+"></a>
13
+ <a href="https://github.com/lucikritz/nexus-llm/actions"><img src="https://img.shields.io/badge/coverage-100%25-brightgreen.svg?style=flat-square" alt="Coverage"></a>
14
+ <a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-green.svg?style=flat-square" alt="License"></a>
15
+ </p>
16
+
17
+ ---
18
+
19
+ ## What is omnimux?
20
+
21
+ When coding agents (Claude Code, Cursor, RooCode, Continue, Open WebUI, Aider) hit rate limits (`429`), credit exhaustion (`402`), or local VRAM out-of-memory errors, tasks break mid-refactor.
22
+
23
+ **omnimux** is a lightweight, database-first local gateway that sits between your coding tools and your LLM backends. It translates between OpenAI and Anthropic protocols, multiplexes API keys across providers, automatically hot-swaps to fallbacks upon failure, and unloads idle local models before loading new ones.
24
+
25
+ ```
26
+ Coding Agents (Claude Code, Cursor, RooCode, Continue, Aider)
27
+ │
28
+ [ omnimux gateway ]
29
+ │
30
+ ┌───────────────┬──────────┴────────┬─────────────┬──────────┐
31
+ ▼ ▼ ▼ ▼ ▼
32
+ Anthropic OpenRouter Gemini Groq Ollama
33
+ (Claude 3.5) (Failover pool) (Long context) (Fast) (Local zero-OOM)
34
+ ```
35
+
36
+ ---
37
+
38
+ ## Compatible Extensions & Agents
39
+
40
+ omnimux exposes standard OpenAI (`/v1/chat/completions`) and Anthropic (`/v1/messages`) endpoints, making it a drop-in replacement across tools:
41
+
42
+ | Agent / Tool | Protocol | Configuration |
43
+ | :--- | :--- | :--- |
44
+ | **Claude Code** | Native Anthropic (`/v1/messages`) | `export ANTHROPIC_BASE_URL="http://localhost:11444"`<br>`export ANTHROPIC_API_KEY="om_your_client_token"` |
45
+ | **Cursor** | OpenAI Compatible (`/v1`) | Base URL: `http://localhost:11444/v1`<br>API Key: `om_your_client_token` |
46
+ | **RooCode / Cline** | OpenAI / Anthropic format | Base URL: `http://localhost:11444/v1` or Anthropic Base URL |
47
+ | **Continue.dev** | OpenAI / Anthropic format | `apiBase: "http://localhost:11444/v1"` in `config.yaml` |
48
+ | **Open WebUI** | OpenAI Compatible (`/v1`) | Connections &rarr; OpenAI API: `http://localhost:11444/v1` |
49
+ | **Aider** | OpenAI Compatible (`/v1`) | `export OPENAI_API_BASE="http://localhost:11444/v1"`<br>`export OPENAI_API_KEY="om_your_client_token"` |
50
+
51
+ ---
52
+
53
+ ## Core Features
54
+
55
+ - 🔄 **Autonomous 429 Hot-Swapping**: Automatically cycles through backup API keys and provider fallbacks with intelligent backoff cooldowns when an upstream service rate-limits or fails.
56
+ - 🦙 **Zero-OOM Ollama Engine**: Issues `keep_alive: 0` unloads for idle local models prior to spinning up fresh models, preventing out-of-memory crashes on unified-memory Apple Silicon and CUDA GPUs.
57
+ - ⚡ **Dual Protocol Translation**: Complete bidirectional translation between Anthropic `/v1/messages` and OpenAI `/v1/chat/completions` with full SSE streaming and token count endpoints.
58
+ - 🗜️ **Context Optimizer**: Converts bulky HTML pages from agent web searches into lean markdown and deduplicates multimodal vision images with local SHA-256 caching.
59
+ - 🔐 **Scoped Client Keys**: Generate isolated client tokens (`om_...`) per IDE extension with local database tracking, usage timestamps, and instant revocation.
60
+ - 📦 **Daemon & Single-Binary Simplicity**: Run seamlessly as a background service via `omnimux start -d` or inspect active logs with `omnimux status`.
61
+
62
+ ---
63
+
64
+ ## Quickstart
65
+
66
+ ### 1. Installation
67
+
68
+ Run via `uvx` (no installation required) or install with `pip`/`uv`:
69
+
70
+ ```bash
71
+ # Run immediately via uvx
72
+ uvx omnimux --help
73
+
74
+ # Or install globally into your environment
75
+ uv pip install omnimux
76
+ ```
77
+
78
+ *(Note: `omni`, `nexus`, and `nexus-llm` are registered aliases for all CLI commands.)*
79
+
80
+ ### 2. Discover & Register Models
81
+
82
+ Interactively discover and register models from local Ollama or upstream providers:
83
+
84
+ ```bash
85
+ omnimux model add
86
+ ```
87
+
88
+ ### 3. Start the Gateway
89
+
90
+ ```bash
91
+ # Start in the foreground
92
+ omnimux start --port 11444
93
+
94
+ # Or start as a background daemon
95
+ omnimux start -d --port 11444
96
+
97
+ # Check status or stop
98
+ omnimux status
99
+ omnimux stop
100
+ ```
101
+
102
+ ### 4. Create an Agent Token
103
+
104
+ Create an isolated client key for your coding agent:
105
+
106
+ ```bash
107
+ omnimux keys create claude-code
108
+ # Output: om_k9A2x...
109
+ ```
110
+
111
+ Then point your agent:
112
+
113
+ ```bash
114
+ export ANTHROPIC_BASE_URL="http://localhost:11444"
115
+ export ANTHROPIC_API_KEY="om_k9A2x..."
116
+ claude
117
+ ```
118
+
119
+ ---
120
+
121
+ ## CLI Reference
122
+
123
+ | Command | Description |
124
+ | :--- | :--- |
125
+ | `omnimux start [--port PORT] [-d]` | Start the gateway server (foreground or daemon) |
126
+ | `omnimux stop` | Terminate running daemon process |
127
+ | `omnimux status` | Inspect health, PID, and port of running daemon |
128
+ | `omnimux model add` | Interactively query and register upstream models |
129
+ | `omnimux model list` | List registered platform models and endpoints |
130
+ | `omnimux keys create [name]` | Generate a client API token (`om_...`) |
131
+ | `omnimux keys list` | List active client keys and last used timestamps |
132
+ | `omnimux keys revoke <id>` | Revoke a client key |
133
+ | `omnimux export <file.json>` | Export entire SQLite routing state to JSON |
134
+ | `omnimux cache --clear` | Purge local image and request cache |
135
+
136
+ ---
137
+
138
+ ## Configuration
139
+
140
+ Settings can be overridden via environment variables or a local `.env` file:
141
+
142
+ ```env
143
+ PORT=11444
144
+ SQLITE_DB_PATH=omnimux_state.db
145
+ PROXY_PASSWORD=optional_fallback_password
146
+ ```
147
+
148
+ Legacy `nx_...` keys and existing SQLite states are automatically recognized and supported for backwards compatibility.
149
+
150
+ ---
151
+
152
+ ## License
153
+
154
+ MIT &copy; LuciKritZ
Binary file
@@ -0,0 +1,83 @@
1
+ {
2
+ "keys": {
3
+ "openrouter": [
4
+ {
5
+ "key_value": "sk-or-v1-abcdef1234567890",
6
+ "priority": 1,
7
+ "meta": {
8
+ "description": "Free tier account 1",
9
+ "models": [
10
+ "openrouter/openrouter/free",
11
+ "openrouter/openrouter/auto"
12
+ ]
13
+ }
14
+ },
15
+ {
16
+ "key_value": "sk-or-v1-0987654321fedcba",
17
+ "priority": 2,
18
+ "meta": {
19
+ "description": "Free tier account 2",
20
+ "models": [
21
+ "openrouter/openrouter/free"
22
+ ]
23
+ }
24
+ }
25
+ ],
26
+ "gemini": [
27
+ {
28
+ "key_value": "AIzaSyB1234567890abcdefghijklmnopqrstuv",
29
+ "priority": 1,
30
+ "meta": {
31
+ "description": "Google AI Studio Credits"
32
+ }
33
+ }
34
+ ],
35
+ "groq": [
36
+ {
37
+ "key_value": "gsk_1234567890abcdefghijklmnopqrstuvwxyz",
38
+ "priority": 1
39
+ }
40
+ ],
41
+ "tokenharbor": [
42
+ {
43
+ "key_value": "th-sk-sample1234567890",
44
+ "priority": 1,
45
+ "meta": {
46
+ "api_base": "https://api.tokenharbor.com",
47
+ "protocol": "anthropic",
48
+ "models": {
49
+ "opus": "inclusionai/ling-3.1-flash",
50
+ "sonnet": "nvidia/nemotron-3-super-120b-a12b:free",
51
+ "haiku": "nvidia/nemotron-3-ultra-550b-a55b:free"
52
+ }
53
+ }
54
+ }
55
+ ]
56
+ },
57
+ "models": {
58
+ "taken-from": "https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json",
59
+ "gemini/gemini-2.5-flash-lite": {
60
+ "max_input_tokens": 1048576,
61
+ "max_output_tokens": 65535,
62
+ "mode": "chat",
63
+ "supports_vision": true
64
+ },
65
+ "groq/llama-3.1-8b-instant": {
66
+ "max_input_tokens": 128000,
67
+ "max_output_tokens": 8192,
68
+ "mode": "chat",
69
+ "supports_vision": false
70
+ },
71
+ "openrouter/openrouter/free": {
72
+ "max_input_tokens": 200000,
73
+ "mode": "chat",
74
+ "supports_vision": true
75
+ },
76
+ "ollama/qwen2.5:1.5b": {
77
+ "max_input_tokens": 32768,
78
+ "max_output_tokens": 8192,
79
+ "mode": "chat",
80
+ "supports_vision": false
81
+ }
82
+ }
83
+ }
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+
5
+ echo "This script will install omnimux as a background macOS service (LaunchAgent)."
6
+ read -p "Do you want to proceed? (y/n) " -n 1 -r
7
+ echo
8
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
9
+ echo "Aborted."
10
+ exit 1
11
+ fi
12
+
13
+ if [[ "$(uname)" != "Darwin" ]]; then
14
+ echo "Error: This service installation is only supported on macOS."
15
+ exit 1
16
+ fi
17
+
18
+ PLIST_PATH="$HOME/Library/LaunchAgents/com.lucikritz.omnimux.plist"
19
+ PROJECT_DIR="$(pwd)"
20
+ UV_PATH="$(which uv)"
21
+
22
+ # Create the logs directory
23
+ mkdir -p "$PROJECT_DIR/.logs"
24
+
25
+ if [ -z "$UV_PATH" ]; then
26
+ echo "Error: uv command not found in PATH."
27
+ exit 1
28
+ fi
29
+
30
+ # Create the plist
31
+ cat <<EOF > "$PLIST_PATH"
32
+ <?xml version="1.0" encoding="UTF-8"?>
33
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
34
+ <plist version="1.0">
35
+ <dict>
36
+ <key>Label</key>
37
+ <string>com.lucikritz.omnimux</string>
38
+ <key>ProgramArguments</key>
39
+ <array>
40
+ <string>$UV_PATH</string>
41
+ <string>run</string>
42
+ <string>python</string>
43
+ <string>-m</string>
44
+ <string>omnimux</string>
45
+ </array>
46
+ <key>WorkingDirectory</key>
47
+ <string>$PROJECT_DIR</string>
48
+ <key>RunAtLoad</key>
49
+ <true/>
50
+ <key>KeepAlive</key>
51
+ <true/>
52
+ <key>StandardOutPath</key>
53
+ <string>$PROJECT_DIR/.logs/omnimux.log</string>
54
+ <key>StandardErrorPath</key>
55
+ <string>$PROJECT_DIR/.logs/omnimux.err.log</string>
56
+ </dict>
57
+ </plist>
58
+ EOF
59
+
60
+ echo "Created LaunchAgent at $PLIST_PATH"
61
+
62
+ # Setup aliases
63
+ SHELL_RC="$HOME/.zshrc"
64
+ if [[ "$SHELL" == *"bash"* ]]; then
65
+ SHELL_RC="$HOME/.bashrc"
66
+ fi
67
+
68
+ if ! grep -q "omnimux-start" "$SHELL_RC"; then
69
+ echo "" >> "$SHELL_RC"
70
+ echo "# omnimux aliases" >> "$SHELL_RC"
71
+ echo "alias omnimux-start=\"launchctl bootstrap gui/\\\$(id -u) $PLIST_PATH\"" >> "$SHELL_RC"
72
+ echo "alias omnimux-stop=\"launchctl bootout gui/\\\$(id -u) $PLIST_PATH\"" >> "$SHELL_RC"
73
+ echo "alias omnimux-log=\"tail -f $PROJECT_DIR/.logs/omnimux.log\"" >> "$SHELL_RC"
74
+ echo "Aliases added to $SHELL_RC. Run 'source $SHELL_RC' to load them."
75
+ else
76
+ echo "Aliases already exist in $SHELL_RC"
77
+ fi
78
+
79
+ echo "Installation complete! Run 'source $SHELL_RC' then 'omnimux-start' to launch the service."
@@ -0,0 +1,69 @@
1
+ [project]
2
+ name = "omnimux"
3
+ version = "0.1.0"
4
+ description = "Local intelligent LLM multiplexer, proxy, and router"
5
+ readme = "README.md"
6
+ requires-python = ">=3.12"
7
+ dependencies = [
8
+ "aiosqlite>=0.20.0",
9
+ "beautifulsoup4>=4.14.3",
10
+ "fastapi>=0.136.3",
11
+ "httpx>=0.28.1",
12
+ "markdownify>=1.2.2",
13
+ "pydantic-settings>=2.14.1",
14
+ "python-dotenv>=1.2.2",
15
+ "uvicorn>=0.48.0",
16
+ ]
17
+
18
+ [project.scripts]
19
+ omnimux = "omnimux.__main__:main"
20
+ omni = "omnimux.__main__:main"
21
+ nexus = "omnimux.__main__:main"
22
+ nexus-llm = "omnimux.__main__:main"
23
+
24
+ [dependency-groups]
25
+ dev = [
26
+ "mypy>=2.1.0",
27
+ "pre-commit>=4.6.0",
28
+ "pytest>=9.0.3",
29
+ "pytest-asyncio>=1.4.0",
30
+ "pytest-benchmark>=5.2.3",
31
+ "pytest-cov>=7.1.0",
32
+ "ruff>=0.15.14",
33
+ ]
34
+
35
+ [build-system]
36
+ requires = ["hatchling"]
37
+ build-backend = "hatchling.build"
38
+
39
+ [tool.ruff]
40
+ target-version = "py312"
41
+ line-length = 100
42
+
43
+ [tool.ruff.lint]
44
+ select = ["E", "W", "F", "I", "N", "UP", "B", "SIM", "RUF"]
45
+
46
+ [tool.ruff.lint.isort]
47
+ known-first-party = ["omnimux"]
48
+
49
+ [tool.mypy]
50
+ python_version = "3.12"
51
+ strict = true
52
+ warn_return_any = true
53
+ warn_unused_configs = true
54
+ disallow_untyped_defs = true
55
+
56
+ [tool.coverage.run]
57
+ source = ["omnimux"]
58
+
59
+ [tool.coverage.report]
60
+ fail_under = 100
61
+ show_missing = true
62
+ exclude_lines = [
63
+ "pragma: no cover",
64
+ "if TYPE_CHECKING:",
65
+ "if __name__ == .__main__.:",
66
+ ]
67
+
68
+ [tool.pytest.ini_options]
69
+ asyncio_mode = "auto"
File without changes