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.
- coderouter/__init__.py +17 -0
- coderouter/__main__.py +6 -0
- coderouter/adapters/__init__.py +23 -0
- coderouter/adapters/anthropic_native.py +502 -0
- coderouter/adapters/base.py +220 -0
- coderouter/adapters/openai_compat.py +395 -0
- coderouter/adapters/registry.py +17 -0
- coderouter/cli.py +345 -0
- coderouter/cli_stats.py +751 -0
- coderouter/config/__init__.py +10 -0
- coderouter/config/capability_registry.py +339 -0
- coderouter/config/env_file.py +295 -0
- coderouter/config/loader.py +73 -0
- coderouter/config/schemas.py +515 -0
- coderouter/data/__init__.py +7 -0
- coderouter/data/model-capabilities.yaml +86 -0
- coderouter/doctor.py +1596 -0
- coderouter/env_security.py +434 -0
- coderouter/errors.py +29 -0
- coderouter/ingress/__init__.py +5 -0
- coderouter/ingress/anthropic_routes.py +205 -0
- coderouter/ingress/app.py +144 -0
- coderouter/ingress/dashboard_routes.py +493 -0
- coderouter/ingress/metrics_routes.py +92 -0
- coderouter/ingress/openai_routes.py +153 -0
- coderouter/logging.py +315 -0
- coderouter/metrics/__init__.py +39 -0
- coderouter/metrics/collector.py +471 -0
- coderouter/metrics/prometheus.py +221 -0
- coderouter/output_filters.py +407 -0
- coderouter/routing/__init__.py +13 -0
- coderouter/routing/auto_router.py +244 -0
- coderouter/routing/capability.py +285 -0
- coderouter/routing/fallback.py +611 -0
- coderouter/translation/__init__.py +57 -0
- coderouter/translation/anthropic.py +204 -0
- coderouter/translation/convert.py +1291 -0
- coderouter/translation/tool_repair.py +236 -0
- coderouter_cli-1.7.0.dist-info/METADATA +509 -0
- coderouter_cli-1.7.0.dist-info/RECORD +43 -0
- coderouter_cli-1.7.0.dist-info/WHEEL +4 -0
- coderouter_cli-1.7.0.dist-info/entry_points.txt +2 -0
- 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]
|