coderouter-cli 1.7.0__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.
Files changed (43) hide show
  1. coderouter/__init__.py +17 -0
  2. coderouter/__main__.py +6 -0
  3. coderouter/adapters/__init__.py +23 -0
  4. coderouter/adapters/anthropic_native.py +502 -0
  5. coderouter/adapters/base.py +220 -0
  6. coderouter/adapters/openai_compat.py +395 -0
  7. coderouter/adapters/registry.py +17 -0
  8. coderouter/cli.py +345 -0
  9. coderouter/cli_stats.py +751 -0
  10. coderouter/config/__init__.py +10 -0
  11. coderouter/config/capability_registry.py +339 -0
  12. coderouter/config/env_file.py +295 -0
  13. coderouter/config/loader.py +73 -0
  14. coderouter/config/schemas.py +515 -0
  15. coderouter/data/__init__.py +7 -0
  16. coderouter/data/model-capabilities.yaml +86 -0
  17. coderouter/doctor.py +1596 -0
  18. coderouter/env_security.py +434 -0
  19. coderouter/errors.py +29 -0
  20. coderouter/ingress/__init__.py +5 -0
  21. coderouter/ingress/anthropic_routes.py +205 -0
  22. coderouter/ingress/app.py +144 -0
  23. coderouter/ingress/dashboard_routes.py +493 -0
  24. coderouter/ingress/metrics_routes.py +92 -0
  25. coderouter/ingress/openai_routes.py +153 -0
  26. coderouter/logging.py +315 -0
  27. coderouter/metrics/__init__.py +39 -0
  28. coderouter/metrics/collector.py +471 -0
  29. coderouter/metrics/prometheus.py +221 -0
  30. coderouter/output_filters.py +407 -0
  31. coderouter/routing/__init__.py +13 -0
  32. coderouter/routing/auto_router.py +244 -0
  33. coderouter/routing/capability.py +285 -0
  34. coderouter/routing/fallback.py +611 -0
  35. coderouter/translation/__init__.py +57 -0
  36. coderouter/translation/anthropic.py +204 -0
  37. coderouter/translation/convert.py +1291 -0
  38. coderouter/translation/tool_repair.py +236 -0
  39. coderouter_cli-1.7.0.dist-info/METADATA +509 -0
  40. coderouter_cli-1.7.0.dist-info/RECORD +43 -0
  41. coderouter_cli-1.7.0.dist-info/WHEEL +4 -0
  42. coderouter_cli-1.7.0.dist-info/entry_points.txt +2 -0
  43. coderouter_cli-1.7.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,204 @@
1
+ """Pydantic models for the Anthropic Messages API wire format.
2
+
3
+ Reference: https://docs.anthropic.com/en/api/messages
4
+
5
+ v0.2 scope: text, image, tool_use, tool_result content blocks + streaming.
6
+ Out of scope (v0.3+): thinking blocks, cache_control, documents, citations.
7
+ These remaining shapes are represented with extra="allow" so they pass
8
+ through unchanged if a client sends them.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any, Literal
14
+
15
+ from pydantic import BaseModel, ConfigDict, Field
16
+
17
+ # ============================================================
18
+ # Content blocks
19
+ # ============================================================
20
+
21
+
22
+ class AnthropicTextBlock(BaseModel):
23
+ """Anthropic ``text`` content block — the common plain-prose case."""
24
+
25
+ model_config = ConfigDict(extra="allow")
26
+
27
+ type: Literal["text"] = "text"
28
+ text: str
29
+
30
+
31
+ class AnthropicImageBlock(BaseModel):
32
+ """Anthropic image block.
33
+
34
+ `source` shape varies by type:
35
+ - base64: {"type": "base64", "media_type": "image/png", "data": "<b64>"}
36
+ - url: {"type": "url", "url": "https://..."}
37
+ """
38
+
39
+ model_config = ConfigDict(extra="allow")
40
+
41
+ type: Literal["image"] = "image"
42
+ source: dict[str, Any]
43
+
44
+
45
+ class AnthropicToolUseBlock(BaseModel):
46
+ """Emitted by assistant when the model decides to call a tool."""
47
+
48
+ model_config = ConfigDict(extra="allow")
49
+
50
+ type: Literal["tool_use"] = "tool_use"
51
+ id: str
52
+ name: str
53
+ input: dict[str, Any] = Field(default_factory=dict)
54
+
55
+
56
+ class AnthropicToolResultBlock(BaseModel):
57
+ """Sent by user/client after executing a tool call the assistant requested."""
58
+
59
+ model_config = ConfigDict(extra="allow")
60
+
61
+ type: Literal["tool_result"] = "tool_result"
62
+ tool_use_id: str
63
+ # Anthropic accepts str OR list of blocks (text/image) as content.
64
+ content: str | list[dict[str, Any]] | None = None
65
+ is_error: bool | None = None
66
+
67
+
68
+ # Discriminated-union style isn't strictly required here — we union-type at
69
+ # the parsing boundary (AnthropicMessage.content) and dispatch on `type`.
70
+ AnthropicContentBlock = (
71
+ AnthropicTextBlock
72
+ | AnthropicImageBlock
73
+ | AnthropicToolUseBlock
74
+ | AnthropicToolResultBlock
75
+ # forward-compat for unknown block types (thinking, document, etc.)
76
+ | dict[str, Any]
77
+ )
78
+
79
+
80
+ # ============================================================
81
+ # Messages + Tools
82
+ # ============================================================
83
+
84
+
85
+ class AnthropicMessage(BaseModel):
86
+ """A single message in the Anthropic messages array.
87
+
88
+ `content` may be a string (short form) or a list of content blocks.
89
+ """
90
+
91
+ model_config = ConfigDict(extra="allow")
92
+
93
+ role: Literal["user", "assistant"]
94
+ content: str | list[dict[str, Any]]
95
+
96
+
97
+ class AnthropicTool(BaseModel):
98
+ """Tool definition as sent by the client in Anthropic format."""
99
+
100
+ model_config = ConfigDict(extra="allow")
101
+
102
+ name: str
103
+ description: str | None = None
104
+ # Anthropic's field name (OpenAI calls it `parameters`).
105
+ input_schema: dict[str, Any] = Field(default_factory=dict)
106
+
107
+
108
+ # ============================================================
109
+ # Request
110
+ # ============================================================
111
+
112
+
113
+ class AnthropicRequest(BaseModel):
114
+ """Inbound request body for POST /v1/messages.
115
+
116
+ Required fields per Anthropic spec: model, max_tokens, messages.
117
+ Everything else is optional.
118
+
119
+ CodeRouter specifics:
120
+ - `model` is ignored for routing decisions (same rule as OpenAI ingress).
121
+ - `profile` is a CodeRouter extension (same as OpenAI ingress).
122
+ """
123
+
124
+ model_config = ConfigDict(extra="allow")
125
+
126
+ model: str | None = None # ignored for routing (see docstring)
127
+ max_tokens: int
128
+ messages: list[AnthropicMessage]
129
+ system: str | list[dict[str, Any]] | None = None
130
+ tools: list[AnthropicTool] | None = None
131
+ tool_choice: dict[str, Any] | None = None
132
+ temperature: float | None = None
133
+ top_p: float | None = None
134
+ top_k: int | None = None
135
+ stop_sequences: list[str] | None = None
136
+ stream: bool = False
137
+ metadata: dict[str, Any] | None = None
138
+
139
+ # CodeRouter-specific extension, not sent upstream.
140
+ profile: str | None = Field(default=None, exclude=True)
141
+
142
+ # Populated from the `anthropic-beta` HTTP header by the Anthropic
143
+ # ingress (v0.4-D). Not a wire field — it's a header passthrough
144
+ # hop, not part of the JSON body. When set, the native adapter
145
+ # forwards it to `api.anthropic.com` verbatim. This is what unlocks
146
+ # beta-gated body fields like `context_management`, `cache_control`,
147
+ # `thinking` beyond what the default minor version accepts.
148
+ anthropic_beta: str | None = Field(default=None, exclude=True)
149
+
150
+
151
+ # ============================================================
152
+ # Response
153
+ # ============================================================
154
+
155
+
156
+ class AnthropicUsage(BaseModel):
157
+ """Token accounting on an Anthropic response / ``message_delta`` event.
158
+
159
+ Cache-hit / cache-creation tokens aren't modeled explicitly —
160
+ ``extra="allow"`` lets them round-trip when present so the
161
+ Anthropic ⇄ OpenAI translation preserves them verbatim.
162
+ """
163
+
164
+ model_config = ConfigDict(extra="allow")
165
+
166
+ input_tokens: int = 0
167
+ output_tokens: int = 0
168
+
169
+
170
+ class AnthropicResponse(BaseModel):
171
+ """Non-streaming response for POST /v1/messages."""
172
+
173
+ model_config = ConfigDict(extra="allow")
174
+
175
+ id: str
176
+ type: Literal["message"] = "message"
177
+ role: Literal["assistant"] = "assistant"
178
+ model: str
179
+ content: list[dict[str, Any]]
180
+ stop_reason: Literal["end_turn", "max_tokens", "stop_sequence", "tool_use"] | None = None
181
+ stop_sequence: str | None = None
182
+ usage: AnthropicUsage = Field(default_factory=AnthropicUsage)
183
+
184
+ # Routing metadata — added by CodeRouter, not from upstream.
185
+ coderouter_provider: str | None = None
186
+
187
+
188
+ # ============================================================
189
+ # Streaming events
190
+ # ============================================================
191
+
192
+
193
+ class AnthropicStreamEvent(BaseModel):
194
+ """Generic envelope for an SSE event.
195
+
196
+ The actual wire emission is `event: <type>\\ndata: <json>\\n\\n`; we store
197
+ the event type separately for routing and the payload as a plain dict so
198
+ the translator can build any event shape without a matrix of subclasses.
199
+ """
200
+
201
+ model_config = ConfigDict(extra="allow")
202
+
203
+ type: str
204
+ data: dict[str, Any]