ccproxy-api 0.1.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.
- ccproxy/__init__.py +4 -0
- ccproxy/__main__.py +7 -0
- ccproxy/_version.py +21 -0
- ccproxy/adapters/__init__.py +11 -0
- ccproxy/adapters/base.py +80 -0
- ccproxy/adapters/openai/__init__.py +43 -0
- ccproxy/adapters/openai/adapter.py +915 -0
- ccproxy/adapters/openai/models.py +412 -0
- ccproxy/adapters/openai/streaming.py +449 -0
- ccproxy/api/__init__.py +28 -0
- ccproxy/api/app.py +225 -0
- ccproxy/api/dependencies.py +140 -0
- ccproxy/api/middleware/__init__.py +11 -0
- ccproxy/api/middleware/auth.py +0 -0
- ccproxy/api/middleware/cors.py +55 -0
- ccproxy/api/middleware/errors.py +703 -0
- ccproxy/api/middleware/headers.py +51 -0
- ccproxy/api/middleware/logging.py +175 -0
- ccproxy/api/middleware/request_id.py +69 -0
- ccproxy/api/middleware/server_header.py +62 -0
- ccproxy/api/responses.py +84 -0
- ccproxy/api/routes/__init__.py +16 -0
- ccproxy/api/routes/claude.py +181 -0
- ccproxy/api/routes/health.py +489 -0
- ccproxy/api/routes/metrics.py +1033 -0
- ccproxy/api/routes/proxy.py +238 -0
- ccproxy/auth/__init__.py +75 -0
- ccproxy/auth/bearer.py +68 -0
- ccproxy/auth/credentials_adapter.py +93 -0
- ccproxy/auth/dependencies.py +229 -0
- ccproxy/auth/exceptions.py +79 -0
- ccproxy/auth/manager.py +102 -0
- ccproxy/auth/models.py +118 -0
- ccproxy/auth/oauth/__init__.py +26 -0
- ccproxy/auth/oauth/models.py +49 -0
- ccproxy/auth/oauth/routes.py +396 -0
- ccproxy/auth/oauth/storage.py +0 -0
- ccproxy/auth/storage/__init__.py +12 -0
- ccproxy/auth/storage/base.py +57 -0
- ccproxy/auth/storage/json_file.py +159 -0
- ccproxy/auth/storage/keyring.py +192 -0
- ccproxy/claude_sdk/__init__.py +20 -0
- ccproxy/claude_sdk/client.py +169 -0
- ccproxy/claude_sdk/converter.py +331 -0
- ccproxy/claude_sdk/options.py +120 -0
- ccproxy/cli/__init__.py +14 -0
- ccproxy/cli/commands/__init__.py +8 -0
- ccproxy/cli/commands/auth.py +553 -0
- ccproxy/cli/commands/config/__init__.py +14 -0
- ccproxy/cli/commands/config/commands.py +766 -0
- ccproxy/cli/commands/config/schema_commands.py +119 -0
- ccproxy/cli/commands/serve.py +630 -0
- ccproxy/cli/docker/__init__.py +34 -0
- ccproxy/cli/docker/adapter_factory.py +157 -0
- ccproxy/cli/docker/params.py +278 -0
- ccproxy/cli/helpers.py +144 -0
- ccproxy/cli/main.py +193 -0
- ccproxy/cli/options/__init__.py +14 -0
- ccproxy/cli/options/claude_options.py +216 -0
- ccproxy/cli/options/core_options.py +40 -0
- ccproxy/cli/options/security_options.py +48 -0
- ccproxy/cli/options/server_options.py +117 -0
- ccproxy/config/__init__.py +40 -0
- ccproxy/config/auth.py +154 -0
- ccproxy/config/claude.py +124 -0
- ccproxy/config/cors.py +79 -0
- ccproxy/config/discovery.py +87 -0
- ccproxy/config/docker_settings.py +265 -0
- ccproxy/config/loader.py +108 -0
- ccproxy/config/observability.py +158 -0
- ccproxy/config/pricing.py +88 -0
- ccproxy/config/reverse_proxy.py +31 -0
- ccproxy/config/scheduler.py +89 -0
- ccproxy/config/security.py +14 -0
- ccproxy/config/server.py +81 -0
- ccproxy/config/settings.py +534 -0
- ccproxy/config/validators.py +231 -0
- ccproxy/core/__init__.py +274 -0
- ccproxy/core/async_utils.py +675 -0
- ccproxy/core/constants.py +97 -0
- ccproxy/core/errors.py +256 -0
- ccproxy/core/http.py +328 -0
- ccproxy/core/http_transformers.py +428 -0
- ccproxy/core/interfaces.py +247 -0
- ccproxy/core/logging.py +189 -0
- ccproxy/core/middleware.py +114 -0
- ccproxy/core/proxy.py +143 -0
- ccproxy/core/system.py +38 -0
- ccproxy/core/transformers.py +259 -0
- ccproxy/core/types.py +129 -0
- ccproxy/core/validators.py +288 -0
- ccproxy/docker/__init__.py +67 -0
- ccproxy/docker/adapter.py +588 -0
- ccproxy/docker/docker_path.py +207 -0
- ccproxy/docker/middleware.py +103 -0
- ccproxy/docker/models.py +228 -0
- ccproxy/docker/protocol.py +192 -0
- ccproxy/docker/stream_process.py +264 -0
- ccproxy/docker/validators.py +173 -0
- ccproxy/models/__init__.py +123 -0
- ccproxy/models/errors.py +42 -0
- ccproxy/models/messages.py +243 -0
- ccproxy/models/requests.py +85 -0
- ccproxy/models/responses.py +227 -0
- ccproxy/models/types.py +102 -0
- ccproxy/observability/__init__.py +51 -0
- ccproxy/observability/access_logger.py +400 -0
- ccproxy/observability/context.py +447 -0
- ccproxy/observability/metrics.py +539 -0
- ccproxy/observability/pushgateway.py +366 -0
- ccproxy/observability/sse_events.py +303 -0
- ccproxy/observability/stats_printer.py +755 -0
- ccproxy/observability/storage/__init__.py +1 -0
- ccproxy/observability/storage/duckdb_simple.py +665 -0
- ccproxy/observability/storage/models.py +55 -0
- ccproxy/pricing/__init__.py +19 -0
- ccproxy/pricing/cache.py +212 -0
- ccproxy/pricing/loader.py +267 -0
- ccproxy/pricing/models.py +106 -0
- ccproxy/pricing/updater.py +309 -0
- ccproxy/scheduler/__init__.py +39 -0
- ccproxy/scheduler/core.py +335 -0
- ccproxy/scheduler/exceptions.py +34 -0
- ccproxy/scheduler/manager.py +186 -0
- ccproxy/scheduler/registry.py +150 -0
- ccproxy/scheduler/tasks.py +484 -0
- ccproxy/services/__init__.py +10 -0
- ccproxy/services/claude_sdk_service.py +614 -0
- ccproxy/services/credentials/__init__.py +55 -0
- ccproxy/services/credentials/config.py +105 -0
- ccproxy/services/credentials/manager.py +562 -0
- ccproxy/services/credentials/oauth_client.py +482 -0
- ccproxy/services/proxy_service.py +1536 -0
- ccproxy/static/.keep +0 -0
- ccproxy/testing/__init__.py +34 -0
- ccproxy/testing/config.py +148 -0
- ccproxy/testing/content_generation.py +197 -0
- ccproxy/testing/mock_responses.py +262 -0
- ccproxy/testing/response_handlers.py +161 -0
- ccproxy/testing/scenarios.py +241 -0
- ccproxy/utils/__init__.py +6 -0
- ccproxy/utils/cost_calculator.py +210 -0
- ccproxy/utils/streaming_metrics.py +199 -0
- ccproxy_api-0.1.0.dist-info/METADATA +253 -0
- ccproxy_api-0.1.0.dist-info/RECORD +148 -0
- ccproxy_api-0.1.0.dist-info/WHEEL +4 -0
- ccproxy_api-0.1.0.dist-info/entry_points.txt +2 -0
- ccproxy_api-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""Streaming metrics extraction utilities.
|
|
2
|
+
|
|
3
|
+
This module provides utilities for extracting token usage and calculating costs
|
|
4
|
+
from Anthropic streaming responses in a testable, modular way.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
import structlog
|
|
11
|
+
|
|
12
|
+
from ccproxy.models.types import StreamingTokenMetrics, UsageData
|
|
13
|
+
from ccproxy.utils.cost_calculator import calculate_token_cost
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
logger = structlog.get_logger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def extract_usage_from_streaming_chunk(chunk_data: Any) -> UsageData | None:
|
|
20
|
+
"""Extract usage information from Anthropic streaming response chunk.
|
|
21
|
+
|
|
22
|
+
This function looks for usage information in both message_start and message_delta events
|
|
23
|
+
from Anthropic's streaming API responses. message_start contains initial input tokens,
|
|
24
|
+
message_delta contains final output tokens.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
chunk_data: Streaming response chunk dictionary
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
UsageData with token counts or None if no usage found
|
|
31
|
+
"""
|
|
32
|
+
if not isinstance(chunk_data, dict):
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
chunk_type = chunk_data.get("type")
|
|
36
|
+
|
|
37
|
+
# Look for message_start events with initial usage (input tokens)
|
|
38
|
+
if chunk_type == "message_start" and "message" in chunk_data:
|
|
39
|
+
message = chunk_data["message"]
|
|
40
|
+
if "usage" in message:
|
|
41
|
+
usage = message["usage"]
|
|
42
|
+
return UsageData(
|
|
43
|
+
input_tokens=usage.get("input_tokens"),
|
|
44
|
+
output_tokens=usage.get(
|
|
45
|
+
"output_tokens"
|
|
46
|
+
), # Initial output tokens (usually small)
|
|
47
|
+
cache_read_input_tokens=usage.get("cache_read_input_tokens"),
|
|
48
|
+
cache_creation_input_tokens=usage.get("cache_creation_input_tokens"),
|
|
49
|
+
event_type="message_start",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Look for message_delta events with final usage (output tokens)
|
|
53
|
+
elif chunk_type == "message_delta" and "usage" in chunk_data:
|
|
54
|
+
usage = chunk_data["usage"]
|
|
55
|
+
return UsageData(
|
|
56
|
+
input_tokens=usage.get("input_tokens"), # Usually None in delta
|
|
57
|
+
output_tokens=usage.get("output_tokens"), # Final output token count
|
|
58
|
+
cache_read_input_tokens=usage.get("cache_read_input_tokens"),
|
|
59
|
+
cache_creation_input_tokens=usage.get("cache_creation_input_tokens"),
|
|
60
|
+
event_type="message_delta",
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class StreamingMetricsCollector:
|
|
67
|
+
"""Collects and manages token metrics during streaming responses."""
|
|
68
|
+
|
|
69
|
+
def __init__(self, request_id: str | None = None) -> None:
|
|
70
|
+
"""Initialize the metrics collector.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
request_id: Optional request ID for logging context
|
|
74
|
+
"""
|
|
75
|
+
self.request_id = request_id
|
|
76
|
+
self.metrics = StreamingTokenMetrics(
|
|
77
|
+
tokens_input=None,
|
|
78
|
+
tokens_output=None,
|
|
79
|
+
cache_read_tokens=None,
|
|
80
|
+
cache_write_tokens=None,
|
|
81
|
+
cost_usd=None,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
def process_chunk(self, chunk_str: str) -> bool:
|
|
85
|
+
"""Process a streaming chunk to extract token metrics.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
chunk_str: Raw chunk string from streaming response
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
True if this was the final chunk with complete metrics, False otherwise
|
|
92
|
+
"""
|
|
93
|
+
# Check if this chunk contains usage information
|
|
94
|
+
# Look for usage data in any chunk - the event type will be determined from the JSON
|
|
95
|
+
if "usage" not in chunk_str:
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
logger.debug(
|
|
99
|
+
"Processing chunk with usage",
|
|
100
|
+
chunk_preview=chunk_str[:300],
|
|
101
|
+
request_id=self.request_id,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
# Parse SSE data lines to find usage information
|
|
106
|
+
for line in chunk_str.split("\n"):
|
|
107
|
+
if line.startswith("data: "):
|
|
108
|
+
data_str = line[6:].strip()
|
|
109
|
+
if data_str and data_str != "[DONE]":
|
|
110
|
+
event_data = json.loads(data_str)
|
|
111
|
+
usage_data = extract_usage_from_streaming_chunk(event_data)
|
|
112
|
+
|
|
113
|
+
if usage_data:
|
|
114
|
+
event_type = usage_data.get("event_type")
|
|
115
|
+
|
|
116
|
+
# Handle message_start: get input tokens and initial cache tokens
|
|
117
|
+
if event_type == "message_start":
|
|
118
|
+
self.metrics["tokens_input"] = usage_data.get(
|
|
119
|
+
"input_tokens"
|
|
120
|
+
)
|
|
121
|
+
self.metrics["cache_read_tokens"] = (
|
|
122
|
+
usage_data.get("cache_read_input_tokens")
|
|
123
|
+
or self.metrics["cache_read_tokens"]
|
|
124
|
+
)
|
|
125
|
+
self.metrics["cache_write_tokens"] = (
|
|
126
|
+
usage_data.get("cache_creation_input_tokens")
|
|
127
|
+
or self.metrics["cache_write_tokens"]
|
|
128
|
+
)
|
|
129
|
+
logger.debug(
|
|
130
|
+
"Extracted input tokens from message_start",
|
|
131
|
+
tokens_input=self.metrics["tokens_input"],
|
|
132
|
+
cache_read_tokens=self.metrics["cache_read_tokens"],
|
|
133
|
+
cache_write_tokens=self.metrics[
|
|
134
|
+
"cache_write_tokens"
|
|
135
|
+
],
|
|
136
|
+
request_id=self.request_id,
|
|
137
|
+
)
|
|
138
|
+
return False # Not final yet
|
|
139
|
+
|
|
140
|
+
# Handle message_delta: get final output tokens
|
|
141
|
+
elif event_type == "message_delta":
|
|
142
|
+
self.metrics["tokens_output"] = usage_data.get(
|
|
143
|
+
"output_tokens"
|
|
144
|
+
)
|
|
145
|
+
logger.debug(
|
|
146
|
+
"Extracted output tokens from message_delta",
|
|
147
|
+
tokens_output=self.metrics["tokens_output"],
|
|
148
|
+
request_id=self.request_id,
|
|
149
|
+
)
|
|
150
|
+
return True # This is the final event
|
|
151
|
+
|
|
152
|
+
break # Only process first valid data line
|
|
153
|
+
|
|
154
|
+
except (json.JSONDecodeError, KeyError) as e:
|
|
155
|
+
logger.debug(
|
|
156
|
+
"Failed to parse streaming token metrics",
|
|
157
|
+
error=str(e),
|
|
158
|
+
request_id=self.request_id,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
def calculate_final_cost(self, model: str | None) -> float | None:
|
|
164
|
+
"""Calculate the final cost based on collected metrics.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
model: Model name for pricing lookup
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Final cost in USD or None if calculation fails
|
|
171
|
+
"""
|
|
172
|
+
cost_usd = calculate_token_cost(
|
|
173
|
+
self.metrics["tokens_input"],
|
|
174
|
+
self.metrics["tokens_output"],
|
|
175
|
+
model,
|
|
176
|
+
self.metrics["cache_read_tokens"],
|
|
177
|
+
self.metrics["cache_write_tokens"],
|
|
178
|
+
)
|
|
179
|
+
self.metrics["cost_usd"] = cost_usd
|
|
180
|
+
|
|
181
|
+
logger.debug(
|
|
182
|
+
"Final streaming token metrics",
|
|
183
|
+
tokens_input=self.metrics["tokens_input"],
|
|
184
|
+
tokens_output=self.metrics["tokens_output"],
|
|
185
|
+
cache_read_tokens=self.metrics["cache_read_tokens"],
|
|
186
|
+
cache_write_tokens=self.metrics["cache_write_tokens"],
|
|
187
|
+
cost_usd=cost_usd,
|
|
188
|
+
request_id=self.request_id,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
return cost_usd
|
|
192
|
+
|
|
193
|
+
def get_metrics(self) -> StreamingTokenMetrics:
|
|
194
|
+
"""Get the current collected metrics.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Current token metrics
|
|
198
|
+
"""
|
|
199
|
+
return self.metrics.copy()
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ccproxy-api
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: API server that provides an Anthropic and OpenAI compatible interface over Claude Code, allowing to use your Claude OAuth account or over the API.
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.11
|
|
7
|
+
Requires-Dist: aiosqlite>=0.21.0
|
|
8
|
+
Requires-Dist: claude-code-sdk>=0.0.14
|
|
9
|
+
Requires-Dist: duckdb-engine>=0.17.0
|
|
10
|
+
Requires-Dist: duckdb>=1.1.0
|
|
11
|
+
Requires-Dist: fastapi[standard]>=0.115.14
|
|
12
|
+
Requires-Dist: httpx-sse>=0.4.1
|
|
13
|
+
Requires-Dist: httpx>=0.28.1
|
|
14
|
+
Requires-Dist: jsonschema>=0.33.2
|
|
15
|
+
Requires-Dist: keyring>=25.6.0
|
|
16
|
+
Requires-Dist: openai>=1.93.0
|
|
17
|
+
Requires-Dist: prometheus-client>=0.22.1
|
|
18
|
+
Requires-Dist: pydantic-settings>=2.4.0
|
|
19
|
+
Requires-Dist: pydantic>=2.8.0
|
|
20
|
+
Requires-Dist: rich-toolkit>=0.14.8
|
|
21
|
+
Requires-Dist: rich>=13.0.0
|
|
22
|
+
Requires-Dist: sqlmodel>=0.0.24
|
|
23
|
+
Requires-Dist: structlog>=25.4.0
|
|
24
|
+
Requires-Dist: tomli>=2.0.0; python_version < '3.11'
|
|
25
|
+
Requires-Dist: typer>=0.16.0
|
|
26
|
+
Requires-Dist: typing-extensions>=4.0.0
|
|
27
|
+
Requires-Dist: uvicorn>=0.34.0
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# CCProxy API Server
|
|
31
|
+
|
|
32
|
+
`ccproxy` is a local reverse proxy server for Anthropic Claude LLM at `api.anthropic.com/v1/messages`. It allows you to use your existing Claude Max subscription to interact with the Anthropic API, bypassing the need for separate API key billing.
|
|
33
|
+
|
|
34
|
+
The server provides two primary modes of operation:
|
|
35
|
+
* **SDK Mode (`/sdk`):** Routes requests through the local `claude-code-sdk`. This enables access to tools configured in your Claude environment.
|
|
36
|
+
* **API Mode (`/api`):** Acts as a direct reverse proxy, injecting the necessary authentication headers. This provides full access to the underlying API features and model settings.
|
|
37
|
+
|
|
38
|
+
It includes a translation layer to support both Anthropic and OpenAI-compatible API formats for requests and responses, including streaming.
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
# The official claude-code CLI is required for SDK mode
|
|
44
|
+
npm install -g @anthropic-ai/claude-code
|
|
45
|
+
|
|
46
|
+
# Install ccproxy
|
|
47
|
+
pipx install git+https://github.com/caddyglow/ccproxy-api.git@dev
|
|
48
|
+
|
|
49
|
+
# Optional: Enable shell completion
|
|
50
|
+
eval "$(ccproxy --show-completion zsh)" # For zsh
|
|
51
|
+
eval "$(ccproxy --show-completion bash)" # For bash
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Authentication
|
|
55
|
+
|
|
56
|
+
The proxy uses two different authentication mechanisms depending on the mode.
|
|
57
|
+
|
|
58
|
+
1. **Claude CLI (`sdk` mode):**
|
|
59
|
+
This mode relies on the authentication handled by the `claude-code-sdk`.
|
|
60
|
+
```bash
|
|
61
|
+
claude /login
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
2. **ccproxy (`api` mode):**
|
|
65
|
+
This mode uses its own OAuth2 flow to obtain credentials for direct API access.
|
|
66
|
+
```bash
|
|
67
|
+
ccproxy auth login
|
|
68
|
+
```
|
|
69
|
+
You can check the status of these credentials with `ccproxy auth validate` and `ccproxy auth info`.
|
|
70
|
+
|
|
71
|
+
## Usage
|
|
72
|
+
|
|
73
|
+
### Running the Server
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
# Start the proxy server
|
|
77
|
+
ccproxy
|
|
78
|
+
```
|
|
79
|
+
The server will start on `http://127.0.0.1:8000` by default.
|
|
80
|
+
|
|
81
|
+
### Client Configuration
|
|
82
|
+
|
|
83
|
+
Point your existing tools and applications to the local proxy instance by setting the appropriate environment variables. A dummy API key is required by most client libraries but is not used by the proxy itself.
|
|
84
|
+
|
|
85
|
+
**For OpenAI-compatible clients:**
|
|
86
|
+
```bash
|
|
87
|
+
# For SDK mode
|
|
88
|
+
export OPENAI_BASE_URL="http://localhost:8000/sdk/v1"
|
|
89
|
+
# For API mode
|
|
90
|
+
export OPENAI_BASE_URL="http://localhost:8000/api/v1"
|
|
91
|
+
|
|
92
|
+
export OPENAI_API_KEY="dummy-key"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**For Anthropic-compatible clients:**
|
|
96
|
+
```bash
|
|
97
|
+
# For SDK mode
|
|
98
|
+
export ANTHROPIC_BASE_URL="http://localhost:8000/sdk"
|
|
99
|
+
# For API mode
|
|
100
|
+
export ANTHROPIC_BASE_URL="http://localhost:8000/api"
|
|
101
|
+
|
|
102
|
+
export ANTHROPIC_API_KEY="dummy-key"
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `curl` Example
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
# SDK mode
|
|
109
|
+
curl -X POST http://localhost:8000/sdk/v1/messages \
|
|
110
|
+
-H "Content-Type: application/json" \
|
|
111
|
+
-d '{
|
|
112
|
+
"model": "claude-3-5-sonnet-20241022",
|
|
113
|
+
"messages": [{"role": "user", "content": "Hello!"}],
|
|
114
|
+
"max_tokens": 100
|
|
115
|
+
}'
|
|
116
|
+
|
|
117
|
+
# API mode
|
|
118
|
+
curl -X POST http://localhost:8000/api/v1/messages \
|
|
119
|
+
-H "Content-Type: application/json" \
|
|
120
|
+
-d '{
|
|
121
|
+
"model": "claude-3-5-sonnet-20241022",
|
|
122
|
+
"messages": [{"role": "user", "content": "Hello!"}],
|
|
123
|
+
"max_tokens": 100
|
|
124
|
+
}'
|
|
125
|
+
```
|
|
126
|
+
More examples are available in the `examples/` directory.
|
|
127
|
+
|
|
128
|
+
## Endpoints
|
|
129
|
+
|
|
130
|
+
The proxy exposes endpoints under two prefixes, corresponding to its operating modes.
|
|
131
|
+
|
|
132
|
+
| Mode | URL Prefix | Description | Use Case |
|
|
133
|
+
|------|------------|-------------|----------|
|
|
134
|
+
| **SDK** | `/sdk/` | Uses `claude-code-sdk` with its configured tools. | Accessing Claude with local tools. |
|
|
135
|
+
| **API** | `/api/` | Direct proxy with header injection. | Full API control, direct access. |
|
|
136
|
+
|
|
137
|
+
* **Anthropic:**
|
|
138
|
+
* `POST /sdk/v1/messages`
|
|
139
|
+
* `POST /api/v1/messages`
|
|
140
|
+
* **OpenAI-Compatible:**
|
|
141
|
+
* `POST /sdk/v1/chat/completions`
|
|
142
|
+
* `POST /api/v1/chat/completions`
|
|
143
|
+
* **Utility:**
|
|
144
|
+
* `GET /health`
|
|
145
|
+
* `GET /sdk/models`, `GET /api/models`
|
|
146
|
+
* `GET /sdk/status`, `GET /api/status`
|
|
147
|
+
* `GET /oauth/callback`
|
|
148
|
+
* **Observability (Optional):**
|
|
149
|
+
* `GET /metrics`
|
|
150
|
+
* `GET /logs/status`, `GET /logs/query`
|
|
151
|
+
* `GET /dashboard`
|
|
152
|
+
|
|
153
|
+
## Supported Models
|
|
154
|
+
|
|
155
|
+
CCProxy supports recent Claude models including Opus, Sonnet, and Haiku variants. The specific models available to you will depend on your Claude account and the features enabled for your subscription.
|
|
156
|
+
|
|
157
|
+
* `claude-opus-4-20250514`
|
|
158
|
+
* `claude-sonnet-4-20250514`
|
|
159
|
+
* `claude-3-7-sonnet-20250219`
|
|
160
|
+
* `claude-3-5-sonnet-20241022`
|
|
161
|
+
* `claude-3-5-sonnet-20240620`
|
|
162
|
+
|
|
163
|
+
## Configuration
|
|
164
|
+
|
|
165
|
+
Settings can be configured through (in order of precedence):
|
|
166
|
+
1. Command-line arguments
|
|
167
|
+
2. Environment variables
|
|
168
|
+
3. `.env` file
|
|
169
|
+
4. TOML configuration files (`.ccproxy.toml`, `ccproxy.toml`, or `~/.config/ccproxy/config.toml`)
|
|
170
|
+
5. Default values
|
|
171
|
+
|
|
172
|
+
For complex configurations, you can use a nested syntax for environment variables with `__` as a delimiter:
|
|
173
|
+
|
|
174
|
+
```bash
|
|
175
|
+
# Server settings
|
|
176
|
+
SERVER__HOST=0.0.0.0
|
|
177
|
+
SERVER__PORT=8080
|
|
178
|
+
# etc.
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Securing the Proxy (Optional)
|
|
182
|
+
|
|
183
|
+
You can enable token authentication for the proxy. This supports multiple header formats (`x-api-key` for Anthropic, `Authorization: Bearer` for OpenAI) for compatibility with standard client libraries.
|
|
184
|
+
|
|
185
|
+
**1. Generate a Token:**
|
|
186
|
+
```bash
|
|
187
|
+
ccproxy generate-token
|
|
188
|
+
# Output: AUTH_TOKEN=abc123xyz789...
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
**2. Configure the Token:**
|
|
192
|
+
```bash
|
|
193
|
+
# Set environment variable
|
|
194
|
+
export AUTH_TOKEN=abc123xyz789...
|
|
195
|
+
|
|
196
|
+
# Or add to .env file
|
|
197
|
+
echo "AUTH_TOKEN=abc123xyz789..." >> .env
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
**3. Use in Requests:**
|
|
201
|
+
When authentication is enabled, include the token in your API requests.
|
|
202
|
+
```bash
|
|
203
|
+
# Anthropic Format (x-api-key)
|
|
204
|
+
curl -H "x-api-key: your-token" ...
|
|
205
|
+
|
|
206
|
+
# OpenAI/Bearer Format
|
|
207
|
+
curl -H "Authorization: Bearer your-token" ...
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Observability
|
|
211
|
+
|
|
212
|
+
`ccproxy` includes an optional but powerful observability suite for monitoring and analytics. When enabled, it provides:
|
|
213
|
+
|
|
214
|
+
* **Prometheus Metrics:** A `/metrics` endpoint for real-time operational monitoring.
|
|
215
|
+
* **Access Log Storage:** Detailed request logs, including token usage and costs, are stored in a local DuckDB database.
|
|
216
|
+
* **Analytics API:** Endpoints to query and analyze historical usage data.
|
|
217
|
+
* **Real-time Dashboard:** A live web interface at `/dashboard` to visualize metrics and request streams.
|
|
218
|
+
|
|
219
|
+
These features are disabled by default and can be enabled via configuration. For a complete guide on setting up and using these features, see the [Observability Documentation](docs/observability.md).
|
|
220
|
+
|
|
221
|
+
## Troubleshooting
|
|
222
|
+
|
|
223
|
+
### Common Issues
|
|
224
|
+
|
|
225
|
+
1. **Authentication Error:** Ensure you're using the correct mode (`/sdk` or `/api`) for your authentication method.
|
|
226
|
+
2. **Claude Credentials Expired:** Run `ccproxy auth login` to refresh credentials for API mode. Run `claude /login` for SDK mode.
|
|
227
|
+
3. **Missing API Auth Token:** If you've enabled security, include the token in your request headers.
|
|
228
|
+
4. **Port Already in Use:** Start the server on a different port: `ccproxy --port 8001`.
|
|
229
|
+
5. **Model Not Available:** Check that your Claude subscription includes the requested model.
|
|
230
|
+
|
|
231
|
+
## Contributing
|
|
232
|
+
|
|
233
|
+
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
|
|
234
|
+
|
|
235
|
+
## License
|
|
236
|
+
|
|
237
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
238
|
+
|
|
239
|
+
## Documentation
|
|
240
|
+
|
|
241
|
+
- **[Online Documentation](https://caddyglow.github.io/ccproxy-api)**
|
|
242
|
+
- **[API Reference](https://caddyglow.github.io/ccproxy-api/api-reference/overview/)**
|
|
243
|
+
- **[Developer Guide](https://caddyglow.github.io/ccproxy-api/developer-guide/architecture/)**
|
|
244
|
+
|
|
245
|
+
## Support
|
|
246
|
+
|
|
247
|
+
- Issues: [GitHub Issues](https://github.com/CaddyGlow/ccproxy-api/issues)
|
|
248
|
+
- Documentation: [Project Documentation](https://caddyglow.github.io/ccproxy-api)
|
|
249
|
+
|
|
250
|
+
## Acknowledgments
|
|
251
|
+
|
|
252
|
+
- [Anthropic](https://anthropic.com) for Claude and the Claude Code SDK
|
|
253
|
+
- The open-source community
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
ccproxy/__init__.py,sha256=VrtzUTUA8MwpYqFJAiwKLPxRdMRc7UOzFi2VB_1W9qw,62
|
|
2
|
+
ccproxy/__main__.py,sha256=kcejcfzAaMnFiSxEiVtNl-_TwynpntkupRxqG5XR15s,116
|
|
3
|
+
ccproxy/_version.py,sha256=-LyU5F1uZDjn6Q8_Z6-_FJt_8RE4Kq9zcKdg1abSSps,511
|
|
4
|
+
ccproxy/adapters/__init__.py,sha256=CMr5MPIFigfazoXfhyD2eLqBrutzaSzBaEi8u2i9xJQ,206
|
|
5
|
+
ccproxy/adapters/base.py,sha256=aufx8ho9LhF0kmTsCvw1a9K3lk5YyYymJV8h_wt5TpU,2191
|
|
6
|
+
ccproxy/adapters/openai/__init__.py,sha256=ZBT4y3cHZHrhzsxETOJNDePDT-2rfWwsPOzDKDRZAvY,1125
|
|
7
|
+
ccproxy/adapters/openai/adapter.py,sha256=PuN8YY-T0vk6eSxDwt_oG7TCcoA9odgQHPqFy_Er2dk,35747
|
|
8
|
+
ccproxy/adapters/openai/models.py,sha256=tg5NLFmTAOjLd2-gjqRQPU3IkL9ATftXv4ZugGw-rwc,11645
|
|
9
|
+
ccproxy/adapters/openai/streaming.py,sha256=y3GEGWpd_3Pyg4mclR5RB8xWMq7sYj1T4qx8eOLcJTU,15670
|
|
10
|
+
ccproxy/api/__init__.py,sha256=m3t0z3_agXKDupMjmdFW1pu2_YrOTu6DKi0b1OdR-YM,558
|
|
11
|
+
ccproxy/api/app.py,sha256=Yx1w-KgBaZDKJ2UYVlJVmYqfSrmOwnWpK2CM14gY4sU,7937
|
|
12
|
+
ccproxy/api/dependencies.py,sha256=VRYA3A1Az1qglOT55anGr5WdHq4ZapdcqHK5fAMk9oo,4065
|
|
13
|
+
ccproxy/api/responses.py,sha256=eipdbHLQcrUjBYJd-Q5bEWUoiFlmPiDB0wdySFNubx4,2905
|
|
14
|
+
ccproxy/api/middleware/__init__.py,sha256=S887PXY40Tnb0IFGei4Sgs7sygkcoT0IEOciOO7IDBc,284
|
|
15
|
+
ccproxy/api/middleware/auth.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
|
+
ccproxy/api/middleware/cors.py,sha256=u8bnWmBXRG9J2etbQdNsQOTIQY6PFWawk2Oa0Ei1x3s,1666
|
|
17
|
+
ccproxy/api/middleware/errors.py,sha256=XMpJui7i_OFGfymQTEPx-YqZ98M1W5VrO7fxdemSGMY,21929
|
|
18
|
+
ccproxy/api/middleware/headers.py,sha256=Jkm61_gH7kCfo3WqTQuIXs1tUK8fWT5vnpjOR8GAcU4,1813
|
|
19
|
+
ccproxy/api/middleware/logging.py,sha256=ab_V4eLrIZxqTUFQKkIOC4MMV55L50ies0kUbt-iFBQ,7064
|
|
20
|
+
ccproxy/api/middleware/request_id.py,sha256=4VLbv1xT8gbRzd5z1kkV-YmeMmlwDf_HoldhrnjK9iA,2350
|
|
21
|
+
ccproxy/api/middleware/server_header.py,sha256=G01U8Wlbj6ov5QEJwjf7UpwcNNPLEqQNom48Gzw-phw,2448
|
|
22
|
+
ccproxy/api/routes/__init__.py,sha256=nuULLSLErxclqDEwNgpIxJVMqjMoBIZNyK7HmFwDxf0,519
|
|
23
|
+
ccproxy/api/routes/claude.py,sha256=DK-X_WePR0RHuybp4z0jWwPJ_HXvfccU16JsGv6cPmM,6659
|
|
24
|
+
ccproxy/api/routes/health.py,sha256=0lGKBT0ll5kn0UgtpA9TQwqVu_q07ITa-5Zgs7xhvvc,17254
|
|
25
|
+
ccproxy/api/routes/metrics.py,sha256=38IwtMYGmcconhB8bIGBmjrvQWB7aZuGK_rQy0d7_wU,40985
|
|
26
|
+
ccproxy/api/routes/proxy.py,sha256=plTNf07Zfg9rvs9W7B2qtFxtmuYekzMpVYrAxg51J3M,8778
|
|
27
|
+
ccproxy/auth/__init__.py,sha256=frT0rDZVGs3zVgaI_z1OuEK6VqAccqRJBBmhlx8tlT4,1967
|
|
28
|
+
ccproxy/auth/bearer.py,sha256=4K9y7kG29NNEhuqqpDvNusmDNWQah90nHGRZxBA1H2o,2015
|
|
29
|
+
ccproxy/auth/credentials_adapter.py,sha256=2BlihJ0KLcIq4hLVHPVaimS0JuZFINzGnBmm_SMPP6c,3279
|
|
30
|
+
ccproxy/auth/dependencies.py,sha256=AOEdZ-6ZfzEMmLevKLlQq_j5YadClSqejiAdxeWQqp4,7083
|
|
31
|
+
ccproxy/auth/exceptions.py,sha256=EUaT7qW0D8HHziX7HglZB-WnALQlbj1lt9OKvyZ97IA,1325
|
|
32
|
+
ccproxy/auth/manager.py,sha256=AHVK4iq3LvavZeSyAmON1ExVA3GLdphe2jO4VuMmImA,2425
|
|
33
|
+
ccproxy/auth/models.py,sha256=z2mkTc-Av2trF3fnqoBEQ7IAfgq96pRGtbjdzrXSFUI,3709
|
|
34
|
+
ccproxy/auth/oauth/__init__.py,sha256=PEymCot9_oIj2mKFIyfmIlznoKwoBuSKqfRvH9k_QR0,508
|
|
35
|
+
ccproxy/auth/oauth/models.py,sha256=c1-egh1iIeOMEkyHNmvGJo0XlGcUJvuLF2gZARqjdfY,2035
|
|
36
|
+
ccproxy/auth/oauth/routes.py,sha256=qTazFFsHGLbE6iIFUBE8CpSl7eRQtAx81V-ew3TaUU4,13643
|
|
37
|
+
ccproxy/auth/oauth/storage.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
+
ccproxy/auth/storage/__init__.py,sha256=5thfvXhc8HGJblQWMiG6ZPl594RpbTGdAgyAlpAe1TI,324
|
|
39
|
+
ccproxy/auth/storage/base.py,sha256=Gw20tabOKTwey6YrOm09Bxu-MYJuMIrQULLJT6RQ07E,1357
|
|
40
|
+
ccproxy/auth/storage/json_file.py,sha256=3vOHHIuUrjEQVVMOWJS8GX3EHIvQYeaUuTnlhV7mHRU,5055
|
|
41
|
+
ccproxy/auth/storage/keyring.py,sha256=GS9UZrLEXSZPwGxsik6MiDWRpZoStGs-8o30qE8T4kA,6022
|
|
42
|
+
ccproxy/claude_sdk/__init__.py,sha256=HezOzX8RaTw5MTYXZu27aaQYLX9x7jrpjZuT1kwJoKc,404
|
|
43
|
+
ccproxy/claude_sdk/client.py,sha256=L48FJdY3gDSrYXK88tbV5kH6z6HZsjLS_erqdN3OUFA,5500
|
|
44
|
+
ccproxy/claude_sdk/converter.py,sha256=FaMnmvwKEuxmi-atr6zZUWLk8ILqlg2_OVPSlBPBxec,11326
|
|
45
|
+
ccproxy/claude_sdk/options.py,sha256=IZX4yqGjfNlUUOH3XzUOVMBtgXEvk9r1MFzeeiVZihA,3516
|
|
46
|
+
ccproxy/cli/__init__.py,sha256=_vISKEqzAsAYrTZ-j9ScxTUtFZp3RVRbIvGpCcu--SU,262
|
|
47
|
+
ccproxy/cli/helpers.py,sha256=cEgEeXP5Qpqq7Jxqy7wagAkJH_w9jPhQACt00IzhDdQ,3874
|
|
48
|
+
ccproxy/cli/main.py,sha256=Uf5Omn9xGVc8OC4t_9rS9lTPHF_DzS9Q7WGL40ux2yY,5613
|
|
49
|
+
ccproxy/cli/commands/__init__.py,sha256=k6CqHFSM-gbn-tdfjRgTkvwDhCPipZ8-4lbiRn54LPo,185
|
|
50
|
+
ccproxy/cli/commands/auth.py,sha256=k1cejJqKitoHHM7NI2Phc-RzSS7XVJbkXeU53GSHxVA,20474
|
|
51
|
+
ccproxy/cli/commands/serve.py,sha256=LTQeKGft_uL2ZqpG910c_rhHao-DZDCaVbeZfaQIs9I,21988
|
|
52
|
+
ccproxy/cli/commands/config/__init__.py,sha256=uX8VewtrMu5X8VAx5ym1slwNtlICDBEs3S1HfoFe5Qo,376
|
|
53
|
+
ccproxy/cli/commands/config/commands.py,sha256=rXuNO8PIbdiRfU_RkOi_9idamsJ6RzRPagheXjhg5O4,28897
|
|
54
|
+
ccproxy/cli/commands/config/schema_commands.py,sha256=ZSuK_Q1bWp8WUOSbkuwKz_4TwrxkRsA6MOt_-y1eiVs,4218
|
|
55
|
+
ccproxy/cli/docker/__init__.py,sha256=4x9QO8SF9aloJFs4xrUEhGcSmoosIYmjcMTk4-GnHBE,791
|
|
56
|
+
ccproxy/cli/docker/adapter_factory.py,sha256=dbO_MGrrAsyF0DDZMKbrk4oMx2xAMZPihOAviF7D5zY,5022
|
|
57
|
+
ccproxy/cli/docker/params.py,sha256=2Wjn2WW7Q839QItcLQWQRgFoR8lue3GfikVstBZjTmI,7438
|
|
58
|
+
ccproxy/cli/options/__init__.py,sha256=OiToWXDNOXnO-fuPz2H5qXYwp9ht7uB12-4wxqner1Q,340
|
|
59
|
+
ccproxy/cli/options/claude_options.py,sha256=vR7VZ3TVvkAOXC6sbpmkkySiaDLbtUB7TJIZBgwSn-U,6028
|
|
60
|
+
ccproxy/cli/options/core_options.py,sha256=g0B8Up-cPzjUM810uSXxXgtZBLgUXXjI1WodS0pKdJE,862
|
|
61
|
+
ccproxy/cli/options/security_options.py,sha256=571v9ujDk6LS6IicGVDvMhNweCS7HiE_y03TiHgd9PY,1093
|
|
62
|
+
ccproxy/cli/options/server_options.py,sha256=8mtcNDOAOVbKrCyMWySCrkQEMukGc5fsMbIkTi8ztrg,2898
|
|
63
|
+
ccproxy/config/__init__.py,sha256=RVXg7sdADdX9-Z-67OYoNlQxby6UN9tThNtg_XzjZ1I,976
|
|
64
|
+
ccproxy/config/auth.py,sha256=5ztj17H1465i_pI8wOdvffjZ3BHgB5ELEa3i6V0yNyQ,4663
|
|
65
|
+
ccproxy/config/claude.py,sha256=ToNj7a9F_XX6PRPnXI-31VjsZxFvtljGZG-J3jxBRfY,4249
|
|
66
|
+
ccproxy/config/cors.py,sha256=eNxrNuPN7Ujh9_2fIWu6kmOIeYNuqn0NTuCsBYvctFM,2505
|
|
67
|
+
ccproxy/config/discovery.py,sha256=6-exBQ5FU0z0H7ViNVYSSFPg8qKXtqQU6VDB8aTLJj8,2283
|
|
68
|
+
ccproxy/config/docker_settings.py,sha256=w1lCxKyAwe0Vrof4zNrnXaFzA8zjoID0tnFR4zW9bM4,8409
|
|
69
|
+
ccproxy/config/loader.py,sha256=gX9R6GY9zE733VUIV-4ofPivgR4aftYxgqat5RXPPz4,3152
|
|
70
|
+
ccproxy/config/observability.py,sha256=5AwQFEFxJLUStGP5gjL_5i8Hk8KdrXKY7ocITYg8jZQ,5466
|
|
71
|
+
ccproxy/config/pricing.py,sha256=YnqaP8wvdA2G94S9EkA9ibSBLZoabeV7HqlCv8Yez1g,2607
|
|
72
|
+
ccproxy/config/reverse_proxy.py,sha256=hep4ubV7-4ZgdO1_WqY56b5yrYyHCdQgYayUHKH3tfo,866
|
|
73
|
+
ccproxy/config/scheduler.py,sha256=pF0lNMhzsumEgm24X16xNsRyymE6aMoAREaMrZUO_Y8,2492
|
|
74
|
+
ccproxy/config/security.py,sha256=8e8o2OsYgaOKpZoBcA4qqIcYp3NHYIR0QfM1HNUxvxg,330
|
|
75
|
+
ccproxy/config/server.py,sha256=L42FUX8uEdVYP-3VP74lfTGVkzQgsv6PEOopEfHDb0U,2317
|
|
76
|
+
ccproxy/config/settings.py,sha256=5lNv0IovU6yE7SuHe_o_z43iwwvn_eA0icm7uNGR-rQ,17687
|
|
77
|
+
ccproxy/config/validators.py,sha256=A1AIwQnpuC-FSDaZDYisgx5RgHRS9pHs1feuKQKQKrk,5771
|
|
78
|
+
ccproxy/core/__init__.py,sha256=ZreOdlUlCn7R5GlS3EdpvIj5fO9sySbQKnuR8o6vwXI,6400
|
|
79
|
+
ccproxy/core/async_utils.py,sha256=MQlyuPdeZrvcMN4ZGKnOP7FuQemsAv638TXMo7fRWgk,19943
|
|
80
|
+
ccproxy/core/constants.py,sha256=mmkcOfKNnvu3h5paZ6GL72ecAK4XdBI4tXlmgImGyzI,2735
|
|
81
|
+
ccproxy/core/errors.py,sha256=05tTMlBwNS1AWihS0LFOCsevCjZcOPgSUoH8sGuecy0,7091
|
|
82
|
+
ccproxy/core/http.py,sha256=pj3UM9TDL13_sU0ULGuA2wvVg58uxOCy-ib3zugjelo,9317
|
|
83
|
+
ccproxy/core/http_transformers.py,sha256=kBu4H9tAeiyMAfSlybZnRuCAe_iL4dVxwI8HDEL5L3o,15663
|
|
84
|
+
ccproxy/core/interfaces.py,sha256=WliYsGhdF3-CxfH8XSng0JVaWOhLgJZDAwiZT1gYLLg,6405
|
|
85
|
+
ccproxy/core/logging.py,sha256=UuIjAHU21Sv1yljCfdYEdGiZP934ovOD3xvzlOG0MOI,6841
|
|
86
|
+
ccproxy/core/middleware.py,sha256=ot6rr84h9lLlHhBol7rHBNpB-RWlCEumUcNlv-4thmk,3370
|
|
87
|
+
ccproxy/core/proxy.py,sha256=lsJzNuUnxHN75h6d9TRRcYt2mQ03tZRHwH2a7vQpvIA,4625
|
|
88
|
+
ccproxy/core/system.py,sha256=91rMtlRtY4yxSsnPV5qZJaXHNFzvcrRZ1nQwS4CwG68,1019
|
|
89
|
+
ccproxy/core/transformers.py,sha256=uPT9PsV_moU4dMg6qlOyvnAomrFmT-rKBwmWtEFkkMA,7709
|
|
90
|
+
ccproxy/core/types.py,sha256=qoR8IHcAHnUekml2lotEnxkPqwI-5qhQdJUWgtw7S-Q,3631
|
|
91
|
+
ccproxy/core/validators.py,sha256=k2z71lz-zKhRdtx3tYgJllqFnEcQ-eivj1kf3aVej0s,7367
|
|
92
|
+
ccproxy/docker/__init__.py,sha256=gO9FJepIWneXPANgsAJKx_VL9rt7pcX3hbRcwnSyzJk,1833
|
|
93
|
+
ccproxy/docker/adapter.py,sha256=P-GeLVu5hl4oMHQrQJU3AHnUxrps-iUd06r7IVnncoY,21176
|
|
94
|
+
ccproxy/docker/docker_path.py,sha256=U_Di1bJDxDZNHW0cxGL31PD6RGKS9Sngs6_D7burmd0,6419
|
|
95
|
+
ccproxy/docker/middleware.py,sha256=hFSvTf77zrOWTH_T3lAk_O7jNSnYDPV_YQoQ82deBsM,3282
|
|
96
|
+
ccproxy/docker/models.py,sha256=RdgHpb4Oyzh19RfWZn-R61OX2R7c1cwAh5ZT7-egJuQ,7500
|
|
97
|
+
ccproxy/docker/protocol.py,sha256=4asLeKpP1Su9HzEQVm_AIdMa1nzWMuGpuJ2BHPEkEVA,6561
|
|
98
|
+
ccproxy/docker/stream_process.py,sha256=XahXegGG6B-7RsZAb9J8OA7sz7xi8CmRzEI4vBJLwTI,9071
|
|
99
|
+
ccproxy/docker/validators.py,sha256=OY-dkU88CLIeMBfv4P5QoVB7miQ8BD7kIgaMHxF7TsU,5764
|
|
100
|
+
ccproxy/models/__init__.py,sha256=XOkiHVzKuMSL8v0NNdL4WxBKrgeItnCeu9p9yYphQkY,2568
|
|
101
|
+
ccproxy/models/errors.py,sha256=B2rbL-17ZI9emDOca2A2fLOcH1-_77t_xqDmiPf6fgY,1329
|
|
102
|
+
ccproxy/models/messages.py,sha256=vSr2O57-h74fa9Y75HGgZZ9WEpJcoJpO9ZQ9Z5438xw,7740
|
|
103
|
+
ccproxy/models/requests.py,sha256=bhpxN19rvV5gyZPQxriFotJ9rwtMaZF9astMT4qnMz0,2766
|
|
104
|
+
ccproxy/models/responses.py,sha256=PPZ1ajF8Z9anPbY04krxPnFmj-9lAEEsPgSbaoJePOc,7044
|
|
105
|
+
ccproxy/models/types.py,sha256=27MUg7pVGrK5oWzcrtG_IAoScEcrF20thO94bxZXQww,2729
|
|
106
|
+
ccproxy/observability/__init__.py,sha256=5n9yQBbMLAQVAEJZD-2t4CKfI0Wx2aoDH7d2ynPM4bg,1503
|
|
107
|
+
ccproxy/observability/access_logger.py,sha256=hhP-dgWPmbFhviPqJhrb7AlRcvfF1uONbKlqzDvAvAI,13120
|
|
108
|
+
ccproxy/observability/context.py,sha256=VOFLP_OUDvPeIX45l-hbnbRXBLM1Hr9g0Prb2FSTIA0,13969
|
|
109
|
+
ccproxy/observability/metrics.py,sha256=iVEkAWMXsnWjlAacFcMhl_cVBb5OQTt0f_UzQZ7jpCA,16197
|
|
110
|
+
ccproxy/observability/pushgateway.py,sha256=TlaVhqhzvL4kTeNsnoTfPYMvs1yD5-v0xkhrB2nTVsw,12737
|
|
111
|
+
ccproxy/observability/sse_events.py,sha256=CVumUxVlXV5tpqVozymaLW4pBTEM90KBYWS9jAJ6Dzk,10697
|
|
112
|
+
ccproxy/observability/stats_printer.py,sha256=_-VU86UbnJHJMWJgEulKcazJRlF_s3LFw2jocPNrknE,30207
|
|
113
|
+
ccproxy/observability/storage/__init__.py,sha256=iAnhODA2PD3PxheoRgJv5KFCTUK_QwxxFa2Ne230RTg,47
|
|
114
|
+
ccproxy/observability/storage/duckdb_simple.py,sha256=kQXJhcc8tWtnGXIZkHtia_4CU1oLlehdCl7sBvzQT-w,24799
|
|
115
|
+
ccproxy/observability/storage/models.py,sha256=njN-sY0kQZICo0um25JwQ8JBplDsPZwitG1wSwO-QJM,1451
|
|
116
|
+
ccproxy/pricing/__init__.py,sha256=8EKUTusWT_URBO8_jzHQ2JWIv11JYZxiyk1WoA_slHE,450
|
|
117
|
+
ccproxy/pricing/cache.py,sha256=UBr4iTS3220VAunVmyOp_pJpKJRn6eij2GZO42XJHps,6531
|
|
118
|
+
ccproxy/pricing/loader.py,sha256=b7rdYjMkWp4ImQBQBqq6h0Pp7yAHZWgh5IlzKpnriuU,9486
|
|
119
|
+
ccproxy/pricing/models.py,sha256=um5ggAtT1QxdSPowHcBWk_ZlnaN6yus2Gz5X2a-BW74,3582
|
|
120
|
+
ccproxy/pricing/updater.py,sha256=RFhofp5m4CUc4huEyg5d4a3JRXg52kDsRnm7-IxvQXs,10200
|
|
121
|
+
ccproxy/scheduler/__init__.py,sha256=6eEXMl8B3l69-O4OCwIYeL_DPGV59TJ9n2Q330hyh5g,1139
|
|
122
|
+
ccproxy/scheduler/core.py,sha256=TCBn8DLzPjd6juLUYF3zKmBzw4HVm37zNy01kpOshzM,10343
|
|
123
|
+
ccproxy/scheduler/exceptions.py,sha256=k7dcid0_te7IwwQaad-Jkj7MWFBgIOdgD_y_n5joio0,817
|
|
124
|
+
ccproxy/scheduler/manager.py,sha256=WXREf7nCUqINk5rv--l-xFiecH_qddBfXHe4BhYKDVw,5793
|
|
125
|
+
ccproxy/scheduler/registry.py,sha256=E2636J1Pj5G9cqr3UZO4D8hVCzt3cMd8rkanvXLF72M,4062
|
|
126
|
+
ccproxy/scheduler/tasks.py,sha256=y6FdhkXg7Irgf5CAqXAWlR5j2viZQKuRuG3ydtWpKdc,16058
|
|
127
|
+
ccproxy/services/__init__.py,sha256=ZvnelD15eFLlWHsucYXBFGNrdT7ncdP1KLnqzJNGOCs,251
|
|
128
|
+
ccproxy/services/claude_sdk_service.py,sha256=m1T7tJAVfxI-vV2U1zsTPv1AtDHHfh9PE6ld_w_nPos,22086
|
|
129
|
+
ccproxy/services/proxy_service.py,sha256=2IIR-zAy_MyU9G3IzUiGtECOa4nEApMBxAIihaQFWVw,59232
|
|
130
|
+
ccproxy/services/credentials/__init__.py,sha256=fkCWqxlUyGVA1mxGicn4cvdbYJQo09SG9NoGKzUun3s,1394
|
|
131
|
+
ccproxy/services/credentials/config.py,sha256=97W3GqtxZlBv45oDHJ-plsHiSeFvNI-FTMZEw4CsPes,3221
|
|
132
|
+
ccproxy/services/credentials/manager.py,sha256=p7yLb3nO34nh2yVb3HIFrwDEM2RQE4ym-GKW8tkaZj4,19971
|
|
133
|
+
ccproxy/services/credentials/oauth_client.py,sha256=ldnRl1XQsKEVGiOYc2gI8br92CEY9jBCoPeaJCaZS7M,16605
|
|
134
|
+
ccproxy/static/.keep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
135
|
+
ccproxy/testing/__init__.py,sha256=-W01VLA1RfopmwVaeWH32gstERNsjFcNIIynZjZjzls,1071
|
|
136
|
+
ccproxy/testing/config.py,sha256=TQLEYHCwdyXzlnk5xQOjIUD0cef5QfWd70nAZG0g_xY,4875
|
|
137
|
+
ccproxy/testing/content_generation.py,sha256=in2F_W1RSnQYKXYCavMrYXPLBrqVn_Bogx84oF242iY,8585
|
|
138
|
+
ccproxy/testing/mock_responses.py,sha256=bskAbk2YecbpAoOCVrP4LCkO-lQhspwhAlpC2pXdypY,9212
|
|
139
|
+
ccproxy/testing/response_handlers.py,sha256=TSfBHzHLw8d3Y95zJ8wYewU9f5Wh89bl7DRfa-49ze0,6311
|
|
140
|
+
ccproxy/testing/scenarios.py,sha256=lVTkf1TbqPnWAfY2VbXez6dSFHsohuXerDmh37iC5VU,9200
|
|
141
|
+
ccproxy/utils/__init__.py,sha256=MBEWNgdZh4Ev-LcltUhkMBboR1QHYNeri3oOy1KQeDo,213
|
|
142
|
+
ccproxy/utils/cost_calculator.py,sha256=mHquyA_1vnPVZ2smjdPwThCJtGu-rF9n8ZvIrAwTF78,7276
|
|
143
|
+
ccproxy/utils/streaming_metrics.py,sha256=JkvmWJ9s1fuKi7x1NoSoderUuT-mU6MQfbnN5GmziYE,7761
|
|
144
|
+
ccproxy_api-0.1.0.dist-info/METADATA,sha256=b1qXrdXFhbT_iONZnb24-_IaDp5qSAfDLqXVBlGC2e0,8602
|
|
145
|
+
ccproxy_api-0.1.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
146
|
+
ccproxy_api-0.1.0.dist-info/entry_points.txt,sha256=_7fyE285w0AZtqkRkP6ZIFICeDO3D69RZTiHOVrRSI8,45
|
|
147
|
+
ccproxy_api-0.1.0.dist-info/licenses/LICENSE,sha256=httxSCpTrEOkipisMeGXSrZhTB-4MRIorQU0hS1B6eQ,1066
|
|
148
|
+
ccproxy_api-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 CaddyGlow
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|