router-maestro 0.1.4__tar.gz → 0.1.5__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.
- {router_maestro-0.1.4 → router_maestro-0.1.5}/CLAUDE.md +2 -2
- {router_maestro-0.1.4 → router_maestro-0.1.5}/PKG-INFO +1 -1
- {router_maestro-0.1.4 → router_maestro-0.1.5}/pyproject.toml +1 -1
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/__init__.py +1 -1
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/cli/config.py +19 -9
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/routes/anthropic.py +6 -5
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/utils/tokens.py +10 -2
- {router_maestro-0.1.4 → router_maestro-0.1.5}/uv.lock +1 -1
- {router_maestro-0.1.4 → router_maestro-0.1.5}/.env.example +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/.gitignore +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/.markdownlint.json +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/Dockerfile +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/LICENSE +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/Makefile +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/README.md +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/docker-compose.yml +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/docs/deployment.md +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/__main__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/auth/__init__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/auth/github_oauth.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/auth/manager.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/auth/storage.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/cli/__init__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/cli/auth.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/cli/client.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/cli/context.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/cli/main.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/cli/model.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/cli/server.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/config/__init__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/config/contexts.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/config/paths.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/config/priorities.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/config/providers.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/config/server.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/config/settings.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/providers/__init__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/providers/anthropic.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/providers/base.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/providers/copilot.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/providers/openai.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/providers/openai_compat.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/routing/__init__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/routing/router.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/__init__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/app.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/middleware/__init__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/middleware/auth.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/oauth_sessions.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/routes/__init__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/routes/admin.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/routes/chat.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/routes/models.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/schemas/__init__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/schemas/admin.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/schemas/anthropic.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/schemas/openai.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/translation.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/utils/__init__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/utils/logging.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/tests/__init__.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/tests/test_auth.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/tests/test_config.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/tests/test_providers.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/tests/test_router.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/tests/test_translation.py +0 -0
- {router_maestro-0.1.4 → router_maestro-0.1.5}/tests/test_utils.py +0 -0
|
@@ -57,7 +57,7 @@ Router-Maestro is a multi-model routing system that exposes both OpenAI-compatib
|
|
|
57
57
|
- `schemas/` - Pydantic models for both API formats
|
|
58
58
|
|
|
59
59
|
**CLI (`src/router_maestro/cli/`)**
|
|
60
|
-
- Typer-based CLI with subcommands: `server`, `auth`, `model`, `context`, `config
|
|
60
|
+
- Typer-based CLI with subcommands: `server`, `auth`, `model`, `context`, `config`
|
|
61
61
|
- Each subcommand in its own module registered in `main.py`
|
|
62
62
|
|
|
63
63
|
### Data Flow
|
|
@@ -72,7 +72,7 @@ Router-Maestro is a multi-model routing system that exposes both OpenAI-compatib
|
|
|
72
72
|
|
|
73
73
|
Configuration and data files follow XDG conventions:
|
|
74
74
|
- **Config** (`~/.config/router-maestro/`): `providers.json`, `priorities.json`, `contexts.json`
|
|
75
|
-
- **Data** (`~/.local/share/router-maestro/`): `auth.json`, `server.json
|
|
75
|
+
- **Data** (`~/.local/share/router-maestro/`): `auth.json`, `server.json`
|
|
76
76
|
|
|
77
77
|
### Model Identification
|
|
78
78
|
|
|
@@ -139,19 +139,29 @@ def claude_code_config() -> None:
|
|
|
139
139
|
)
|
|
140
140
|
anthropic_url = f"{base_url}/api/anthropic"
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
"
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
|
|
149
|
-
}
|
|
142
|
+
env_config = {
|
|
143
|
+
"ANTHROPIC_BASE_URL": anthropic_url,
|
|
144
|
+
"ANTHROPIC_AUTH_TOKEN": auth_token,
|
|
145
|
+
"ANTHROPIC_MODEL": main_model,
|
|
146
|
+
"ANTHROPIC_SMALL_FAST_MODEL": fast_model,
|
|
147
|
+
"CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC": "1",
|
|
150
148
|
}
|
|
151
149
|
|
|
150
|
+
# Load existing settings to preserve other sections (e.g., MCP servers)
|
|
151
|
+
existing_config: dict = {}
|
|
152
|
+
if settings_path.exists():
|
|
153
|
+
try:
|
|
154
|
+
with open(settings_path, encoding="utf-8") as f:
|
|
155
|
+
existing_config = json.load(f)
|
|
156
|
+
except (json.JSONDecodeError, OSError):
|
|
157
|
+
pass # If file is corrupted, start fresh
|
|
158
|
+
|
|
159
|
+
# Merge: update env section while preserving other sections
|
|
160
|
+
existing_config["env"] = env_config
|
|
161
|
+
|
|
152
162
|
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
153
163
|
with open(settings_path, "w", encoding="utf-8") as f:
|
|
154
|
-
json.dump(
|
|
164
|
+
json.dump(existing_config, f, indent=2)
|
|
155
165
|
|
|
156
166
|
console.print(
|
|
157
167
|
Panel(
|
|
@@ -26,6 +26,7 @@ from router_maestro.utils import (
|
|
|
26
26
|
get_logger,
|
|
27
27
|
map_openai_stop_reason_to_anthropic,
|
|
28
28
|
)
|
|
29
|
+
from router_maestro.utils.tokens import AnthropicStopReason
|
|
29
30
|
|
|
30
31
|
logger = get_logger("server.routes.anthropic")
|
|
31
32
|
|
|
@@ -106,7 +107,7 @@ async def count_tokens(request: AnthropicCountTokensRequest):
|
|
|
106
107
|
|
|
107
108
|
# Count messages
|
|
108
109
|
for msg in request.messages:
|
|
109
|
-
content = msg.content
|
|
110
|
+
content = msg.content
|
|
110
111
|
if isinstance(content, str):
|
|
111
112
|
total_chars += len(content)
|
|
112
113
|
elif isinstance(content, list):
|
|
@@ -115,12 +116,12 @@ async def count_tokens(request: AnthropicCountTokensRequest):
|
|
|
115
116
|
if block.get("type") == "text":
|
|
116
117
|
total_chars += len(block.get("text", ""))
|
|
117
118
|
elif hasattr(block, "text"):
|
|
118
|
-
total_chars += len(block.text)
|
|
119
|
+
total_chars += len(block.text) # type: ignore[union-attr]
|
|
119
120
|
|
|
120
121
|
return {"input_tokens": estimate_tokens_from_char_count(total_chars)}
|
|
121
122
|
|
|
122
123
|
|
|
123
|
-
def _map_finish_reason(reason: str | None) ->
|
|
124
|
+
def _map_finish_reason(reason: str | None) -> AnthropicStopReason | None:
|
|
124
125
|
"""Map OpenAI finish reason to Anthropic stop reason."""
|
|
125
126
|
return map_openai_stop_reason_to_anthropic(reason)
|
|
126
127
|
|
|
@@ -144,7 +145,7 @@ def _estimate_input_tokens(request: AnthropicMessagesRequest) -> int:
|
|
|
144
145
|
|
|
145
146
|
# Count messages
|
|
146
147
|
for msg in request.messages:
|
|
147
|
-
content = msg.content
|
|
148
|
+
content = msg.content
|
|
148
149
|
if isinstance(content, str):
|
|
149
150
|
total_chars += len(content)
|
|
150
151
|
elif isinstance(content, list):
|
|
@@ -161,7 +162,7 @@ def _estimate_input_tokens(request: AnthropicMessagesRequest) -> int:
|
|
|
161
162
|
if isinstance(tc, dict) and tc.get("type") == "text":
|
|
162
163
|
total_chars += len(tc.get("text", ""))
|
|
163
164
|
elif hasattr(block, "text"):
|
|
164
|
-
total_chars += len(block.text)
|
|
165
|
+
total_chars += len(block.text) # type: ignore[union-attr]
|
|
165
166
|
|
|
166
167
|
# Count tools definitions if present
|
|
167
168
|
if request.tools:
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"""Token estimation utilities."""
|
|
2
2
|
|
|
3
|
+
from typing import Literal
|
|
4
|
+
|
|
3
5
|
# Approximate characters per token for English text
|
|
4
6
|
CHARS_PER_TOKEN = 4
|
|
5
7
|
|
|
8
|
+
AnthropicStopReason = Literal[
|
|
9
|
+
"end_turn", "max_tokens", "stop_sequence", "tool_use", "pause_turn", "refusal"
|
|
10
|
+
]
|
|
11
|
+
|
|
6
12
|
|
|
7
13
|
def estimate_tokens(text: str) -> int:
|
|
8
14
|
"""Estimate token count from text.
|
|
@@ -31,7 +37,9 @@ def estimate_tokens_from_char_count(char_count: int) -> int:
|
|
|
31
37
|
return char_count // CHARS_PER_TOKEN
|
|
32
38
|
|
|
33
39
|
|
|
34
|
-
def map_openai_stop_reason_to_anthropic(
|
|
40
|
+
def map_openai_stop_reason_to_anthropic(
|
|
41
|
+
openai_reason: str | None,
|
|
42
|
+
) -> AnthropicStopReason | None:
|
|
35
43
|
"""Map OpenAI finish reason to Anthropic stop reason.
|
|
36
44
|
|
|
37
45
|
Args:
|
|
@@ -42,7 +50,7 @@ def map_openai_stop_reason_to_anthropic(openai_reason: str | None) -> str | None
|
|
|
42
50
|
"""
|
|
43
51
|
if openai_reason is None:
|
|
44
52
|
return None
|
|
45
|
-
mapping = {
|
|
53
|
+
mapping: dict[str, AnthropicStopReason] = {
|
|
46
54
|
"stop": "end_turn",
|
|
47
55
|
"length": "max_tokens",
|
|
48
56
|
"tool_calls": "tool_use",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/middleware/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{router_maestro-0.1.4 → router_maestro-0.1.5}/src/router_maestro/server/schemas/anthropic.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|