router-maestro 0.1.4__py3-none-any.whl → 0.1.5__py3-none-any.whl

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.
@@ -1,3 +1,3 @@
1
1
  """Router-Maestro: Multi-model routing and load balancing system."""
2
2
 
3
- __version__ = "0.1.4"
3
+ __version__ = "0.1.5"
@@ -139,19 +139,29 @@ def claude_code_config() -> None:
139
139
  )
140
140
  anthropic_url = f"{base_url}/api/anthropic"
141
141
 
142
- config = {
143
- "env": {
144
- "ANTHROPIC_BASE_URL": anthropic_url,
145
- "ANTHROPIC_AUTH_TOKEN": auth_token,
146
- "ANTHROPIC_MODEL": main_model,
147
- "ANTHROPIC_SMALL_FAST_MODEL": fast_model,
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(config, f, indent=2)
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 if hasattr(msg, "content") else msg.get("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) -> 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 if hasattr(msg, "content") else msg.get("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(openai_reason: str | None) -> str | None:
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",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: router-maestro
3
- Version: 0.1.4
3
+ Version: 0.1.5
4
4
  Summary: Multi-model routing and load balancing system with OpenAI-compatible API
5
5
  Author-email: Kanwen Li <likanwen@icloud.com>
6
6
  License-Expression: MIT
@@ -1,4 +1,4 @@
1
- router_maestro/__init__.py,sha256=dQ7VGwTEZyoTDYi_HcMlVOwL7DKJ75prK_l_Qa9eN_4,92
1
+ router_maestro/__init__.py,sha256=E3wx7rchrL5OkqXGemj0n4c8To9tSZh-9KiPk7jpf5o,92
2
2
  router_maestro/__main__.py,sha256=cUHr8B7JBiv5HhnN6l2iayDkGSBpI5Kf4I3jv9I_I3o,121
3
3
  router_maestro/auth/__init__.py,sha256=0JgD1w2gtGSkj809kgSKQanYYkncg6eF-hHoz-jQPgo,353
4
4
  router_maestro/auth/github_oauth.py,sha256=acQlAA2Zh6c8KQYdzXbC4ww0EJ41AgvbI5ixpFuNoRg,5060
@@ -7,7 +7,7 @@ router_maestro/auth/storage.py,sha256=TCLxgQ1lWcWD4xJXJzx5OMpvuAun_LSRItK0zhR6H0
7
7
  router_maestro/cli/__init__.py,sha256=yIAshaHpLL0WrDFmRpoMRM2EUe75x0wmM5NlGW3C89s,37
8
8
  router_maestro/cli/auth.py,sha256=eq5LBUohbMnHS4dZeyvq4OQAjzdrJ-StP2FGuUhkKa0,5940
9
9
  router_maestro/cli/client.py,sha256=mRzpsA_Dxn-Xq7W1_t6EiyddMI0a3cvuTL6-2JuV4mE,9383
10
- router_maestro/cli/config.py,sha256=9e_O0J-cxX2FCqHl8bKFKh897PK9mErTp31SXYmmU5c,5667
10
+ router_maestro/cli/config.py,sha256=SGrWdd59eX1F8KzhHMJ_u2AkYfFuYlyLl6Nfz6u_rNk,6100
11
11
  router_maestro/cli/context.py,sha256=EPbT7fReIW17veU76CSAcv8QjzMsCIPm1QDBlGsV8fQ,4549
12
12
  router_maestro/cli/main.py,sha256=5yiK4Q149goSB2KKzgMuF5EpcC8FBzOUCkEt8wY5NAU,1314
13
13
  router_maestro/cli/model.py,sha256=2IG3IpQWh8Ejdv5Htcgr90O2v2UAa80TU15oOniPdvk,9054
@@ -35,7 +35,7 @@ router_maestro/server/middleware/__init__.py,sha256=PhtP2E04wApnOUBLE76mrOa0sSHp
35
35
  router_maestro/server/middleware/auth.py,sha256=Ak3k5cC8m4qPGUIheuOB--QiFvs6GIAcTRJqtCGCjAA,2018
36
36
  router_maestro/server/routes/__init__.py,sha256=eGEpNCnSRVQC1pFL7_evDmZfkMrviuI-n1okAS-YnhM,397
37
37
  router_maestro/server/routes/admin.py,sha256=oub4hDrYaytuorXkJzmz0YZ4Z2rcyNuwKcK_4IGvcDY,8942
38
- router_maestro/server/routes/anthropic.py,sha256=T5-rHBPDyPxP4Cs0yzm7Kvvn-zgV6jspnZdoSVDeH2w,8041
38
+ router_maestro/server/routes/anthropic.py,sha256=3ILc6m9UlqSsRsWXZYk7DWSu1o5LI7FZgoqPo16D56U,8063
39
39
  router_maestro/server/routes/chat.py,sha256=vyYX1ILhgAb9HYD87h1U3c5btpplqkTaejA81pWg4Oo,4752
40
40
  router_maestro/server/routes/models.py,sha256=PTSXojNFN9j90Bke74ZO6sEsfIc8u_4A69eW1QzFIbc,716
41
41
  router_maestro/server/schemas/__init__.py,sha256=VmJZoTMLb-bF33m79urhbejVdLfjDGMqCJP5QvWbHsU,1176
@@ -44,9 +44,9 @@ router_maestro/server/schemas/anthropic.py,sha256=hNl6rZ7AX-HdLxtsd0cWpZjpIyK1Ak
44
44
  router_maestro/server/schemas/openai.py,sha256=s2487RYIn1h-CIaUpLue9BScDaTsafbVg5yc-kKhfME,2141
45
45
  router_maestro/utils/__init__.py,sha256=oSQyV--FueMPggRfjWWVnAKtjkcZWFOm9hCTymu0oZU,409
46
46
  router_maestro/utils/logging.py,sha256=gJWoRYibAxCWn4VmTmnrwpBRzQ7Uu5YIEk5zDiF9X_k,2393
47
- router_maestro/utils/tokens.py,sha256=t2E5BrrE5X3VCgw-rYFMkic7heJ0huj9rrOXAIlKq8o,1330
48
- router_maestro-0.1.4.dist-info/METADATA,sha256=0f158gqfAjoReykKlaeBee6eraI8fwKqmAW9WifpKNo,12161
49
- router_maestro-0.1.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
50
- router_maestro-0.1.4.dist-info/entry_points.txt,sha256=zoFUxxvNcFe0nTgpRbIdygIDEOla3KbvW6HbOCOlgv4,63
51
- router_maestro-0.1.4.dist-info/licenses/LICENSE,sha256=Ea86BSGu7_tpLAuzif_JmM9zjMoKQEf95VVF9sZw3Jo,1084
52
- router_maestro-0.1.4.dist-info/RECORD,,
47
+ router_maestro/utils/tokens.py,sha256=U5PXJv_6ba5xgMBG0c5qB96Yu6uLscSUjMWYTdNests,1530
48
+ router_maestro-0.1.5.dist-info/METADATA,sha256=xqHRuMfsP0uG-H9Vv89iWmH0-wjaMrdGvuov1WH4juA,12161
49
+ router_maestro-0.1.5.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
50
+ router_maestro-0.1.5.dist-info/entry_points.txt,sha256=zoFUxxvNcFe0nTgpRbIdygIDEOla3KbvW6HbOCOlgv4,63
51
+ router_maestro-0.1.5.dist-info/licenses/LICENSE,sha256=Ea86BSGu7_tpLAuzif_JmM9zjMoKQEf95VVF9sZw3Jo,1084
52
+ router_maestro-0.1.5.dist-info/RECORD,,