ccproxy-api 0.1.2__py3-none-any.whl → 0.1.4__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/_version.py +2 -2
- ccproxy/adapters/openai/__init__.py +1 -2
- ccproxy/adapters/openai/adapter.py +218 -180
- ccproxy/adapters/openai/streaming.py +247 -65
- ccproxy/api/__init__.py +0 -3
- ccproxy/api/app.py +173 -40
- ccproxy/api/dependencies.py +62 -3
- ccproxy/api/middleware/errors.py +3 -7
- ccproxy/api/middleware/headers.py +0 -2
- ccproxy/api/middleware/logging.py +4 -3
- ccproxy/api/middleware/request_content_logging.py +297 -0
- ccproxy/api/middleware/request_id.py +5 -0
- ccproxy/api/middleware/server_header.py +0 -4
- ccproxy/api/routes/__init__.py +9 -1
- ccproxy/api/routes/claude.py +23 -32
- ccproxy/api/routes/health.py +58 -4
- ccproxy/api/routes/mcp.py +171 -0
- ccproxy/api/routes/metrics.py +4 -8
- ccproxy/api/routes/permissions.py +217 -0
- ccproxy/api/routes/proxy.py +0 -53
- ccproxy/api/services/__init__.py +6 -0
- ccproxy/api/services/permission_service.py +368 -0
- ccproxy/api/ui/__init__.py +6 -0
- ccproxy/api/ui/permission_handler_protocol.py +33 -0
- ccproxy/api/ui/terminal_permission_handler.py +593 -0
- ccproxy/auth/conditional.py +2 -2
- ccproxy/auth/dependencies.py +1 -1
- ccproxy/auth/oauth/models.py +0 -1
- ccproxy/auth/oauth/routes.py +1 -3
- ccproxy/auth/storage/json_file.py +0 -1
- ccproxy/auth/storage/keyring.py +0 -3
- ccproxy/claude_sdk/__init__.py +2 -0
- ccproxy/claude_sdk/client.py +91 -8
- ccproxy/claude_sdk/converter.py +405 -210
- ccproxy/claude_sdk/options.py +76 -29
- ccproxy/claude_sdk/parser.py +200 -0
- ccproxy/claude_sdk/streaming.py +286 -0
- ccproxy/cli/commands/__init__.py +5 -2
- ccproxy/cli/commands/auth.py +2 -4
- ccproxy/cli/commands/permission_handler.py +553 -0
- ccproxy/cli/commands/serve.py +30 -12
- ccproxy/cli/docker/params.py +0 -4
- ccproxy/cli/helpers.py +0 -2
- ccproxy/cli/main.py +5 -16
- ccproxy/cli/options/claude_options.py +19 -1
- ccproxy/cli/options/core_options.py +0 -3
- ccproxy/cli/options/security_options.py +0 -2
- ccproxy/cli/options/server_options.py +3 -2
- ccproxy/config/auth.py +0 -1
- ccproxy/config/claude.py +78 -2
- ccproxy/config/discovery.py +0 -1
- ccproxy/config/docker_settings.py +0 -1
- ccproxy/config/loader.py +1 -4
- ccproxy/config/scheduler.py +20 -0
- ccproxy/config/security.py +7 -2
- ccproxy/config/server.py +5 -0
- ccproxy/config/settings.py +13 -7
- ccproxy/config/validators.py +1 -1
- ccproxy/core/async_utils.py +1 -4
- ccproxy/core/errors.py +45 -1
- ccproxy/core/http_transformers.py +4 -3
- ccproxy/core/interfaces.py +2 -2
- ccproxy/core/logging.py +97 -95
- ccproxy/core/middleware.py +1 -1
- ccproxy/core/proxy.py +1 -1
- ccproxy/core/transformers.py +1 -1
- ccproxy/core/types.py +1 -1
- ccproxy/docker/models.py +1 -1
- ccproxy/docker/protocol.py +0 -3
- ccproxy/models/__init__.py +41 -0
- ccproxy/models/claude_sdk.py +420 -0
- ccproxy/models/messages.py +45 -18
- ccproxy/models/permissions.py +115 -0
- ccproxy/models/requests.py +1 -1
- ccproxy/models/responses.py +29 -2
- ccproxy/observability/access_logger.py +1 -2
- ccproxy/observability/context.py +17 -1
- ccproxy/observability/metrics.py +1 -3
- ccproxy/observability/pushgateway.py +0 -2
- ccproxy/observability/stats_printer.py +2 -4
- ccproxy/observability/storage/duckdb_simple.py +1 -1
- ccproxy/observability/storage/models.py +0 -1
- ccproxy/pricing/cache.py +0 -1
- ccproxy/pricing/loader.py +5 -21
- ccproxy/pricing/updater.py +0 -1
- ccproxy/scheduler/__init__.py +1 -0
- ccproxy/scheduler/core.py +6 -6
- ccproxy/scheduler/manager.py +35 -7
- ccproxy/scheduler/registry.py +1 -1
- ccproxy/scheduler/tasks.py +127 -2
- ccproxy/services/claude_sdk_service.py +220 -328
- ccproxy/services/credentials/manager.py +0 -1
- ccproxy/services/credentials/oauth_client.py +1 -2
- ccproxy/services/proxy_service.py +93 -222
- ccproxy/testing/config.py +1 -1
- ccproxy/testing/mock_responses.py +0 -1
- ccproxy/utils/model_mapping.py +197 -0
- ccproxy/utils/models_provider.py +150 -0
- ccproxy/utils/simple_request_logger.py +284 -0
- ccproxy/utils/version_checker.py +184 -0
- {ccproxy_api-0.1.2.dist-info → ccproxy_api-0.1.4.dist-info}/METADATA +63 -2
- ccproxy_api-0.1.4.dist-info/RECORD +166 -0
- ccproxy/cli/commands/permission.py +0 -128
- ccproxy_api-0.1.2.dist-info/RECORD +0 -150
- /ccproxy/scheduler/{exceptions.py → errors.py} +0 -0
- {ccproxy_api-0.1.2.dist-info → ccproxy_api-0.1.4.dist-info}/WHEEL +0 -0
- {ccproxy_api-0.1.2.dist-info → ccproxy_api-0.1.4.dist-info}/entry_points.txt +0 -0
- {ccproxy_api-0.1.2.dist-info → ccproxy_api-0.1.4.dist-info}/licenses/LICENSE +0 -0
ccproxy/claude_sdk/client.py
CHANGED
|
@@ -4,25 +4,36 @@ from collections.abc import AsyncIterator
|
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
6
|
import structlog
|
|
7
|
+
from pydantic import BaseModel
|
|
7
8
|
|
|
8
9
|
from ccproxy.core.async_utils import patched_typing
|
|
9
10
|
from ccproxy.core.errors import ClaudeProxyError, ServiceUnavailableError
|
|
11
|
+
from ccproxy.models import claude_sdk as sdk_models
|
|
10
12
|
from ccproxy.observability import timed_operation
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
with patched_typing():
|
|
14
16
|
from claude_code_sdk import (
|
|
15
|
-
AssistantMessage,
|
|
17
|
+
AssistantMessage as SDKAssistantMessage,
|
|
18
|
+
)
|
|
19
|
+
from claude_code_sdk import (
|
|
16
20
|
ClaudeCodeOptions,
|
|
17
21
|
CLIConnectionError,
|
|
18
22
|
CLIJSONDecodeError,
|
|
19
23
|
CLINotFoundError,
|
|
20
24
|
ProcessError,
|
|
21
|
-
ResultMessage,
|
|
22
|
-
SystemMessage,
|
|
23
|
-
UserMessage,
|
|
24
25
|
query,
|
|
25
26
|
)
|
|
27
|
+
from claude_code_sdk import (
|
|
28
|
+
ResultMessage as SDKResultMessage,
|
|
29
|
+
)
|
|
30
|
+
from claude_code_sdk import (
|
|
31
|
+
SystemMessage as SDKSystemMessage,
|
|
32
|
+
)
|
|
33
|
+
from claude_code_sdk import (
|
|
34
|
+
UserMessage as SDKUserMessage,
|
|
35
|
+
)
|
|
36
|
+
|
|
26
37
|
|
|
27
38
|
logger = structlog.get_logger(__name__)
|
|
28
39
|
|
|
@@ -53,9 +64,14 @@ class ClaudeSDKClient:
|
|
|
53
64
|
|
|
54
65
|
async def query_completion(
|
|
55
66
|
self, prompt: str, options: ClaudeCodeOptions, request_id: str | None = None
|
|
56
|
-
) -> AsyncIterator[
|
|
67
|
+
) -> AsyncIterator[
|
|
68
|
+
sdk_models.UserMessage
|
|
69
|
+
| sdk_models.AssistantMessage
|
|
70
|
+
| sdk_models.SystemMessage
|
|
71
|
+
| sdk_models.ResultMessage
|
|
72
|
+
]:
|
|
57
73
|
"""
|
|
58
|
-
Execute a query using the Claude Code SDK.
|
|
74
|
+
Execute a query using the Claude Code SDK and yields strongly-typed Pydantic models.
|
|
59
75
|
|
|
60
76
|
Args:
|
|
61
77
|
prompt: The prompt string to send to Claude
|
|
@@ -63,7 +79,7 @@ class ClaudeSDKClient:
|
|
|
63
79
|
request_id: Optional request ID for correlation
|
|
64
80
|
|
|
65
81
|
Yields:
|
|
66
|
-
|
|
82
|
+
Strongly-typed Pydantic messages from ccproxy.claude_sdk.models
|
|
67
83
|
|
|
68
84
|
Raises:
|
|
69
85
|
ClaudeSDKError: If the query fails
|
|
@@ -75,7 +91,74 @@ class ClaudeSDKClient:
|
|
|
75
91
|
message_count = 0
|
|
76
92
|
async for message in query(prompt=prompt, options=options):
|
|
77
93
|
message_count += 1
|
|
78
|
-
|
|
94
|
+
|
|
95
|
+
logger.debug(
|
|
96
|
+
"claude_sdk_raw_message_received",
|
|
97
|
+
message_type=type(message).__name__,
|
|
98
|
+
message_count=message_count,
|
|
99
|
+
request_id=request_id,
|
|
100
|
+
has_content=hasattr(message, "content")
|
|
101
|
+
and bool(getattr(message, "content", None)),
|
|
102
|
+
content_preview=str(message)[:150],
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
model_class: type[BaseModel] | None = None
|
|
106
|
+
if isinstance(message, SDKUserMessage):
|
|
107
|
+
model_class = sdk_models.UserMessage
|
|
108
|
+
elif isinstance(message, SDKAssistantMessage):
|
|
109
|
+
model_class = sdk_models.AssistantMessage
|
|
110
|
+
elif isinstance(message, SDKSystemMessage):
|
|
111
|
+
model_class = sdk_models.SystemMessage
|
|
112
|
+
elif isinstance(message, SDKResultMessage):
|
|
113
|
+
model_class = sdk_models.ResultMessage
|
|
114
|
+
|
|
115
|
+
# Convert Claude SDK message to our Pydantic model
|
|
116
|
+
try:
|
|
117
|
+
if hasattr(message, "__dict__"):
|
|
118
|
+
converted_message = model_class.model_validate(
|
|
119
|
+
vars(message)
|
|
120
|
+
)
|
|
121
|
+
else:
|
|
122
|
+
# For dataclass objects, use dataclass.asdict equivalent
|
|
123
|
+
message_dict = {}
|
|
124
|
+
if hasattr(message, "__dataclass_fields__"):
|
|
125
|
+
message_dict = {
|
|
126
|
+
field: getattr(message, field)
|
|
127
|
+
for field in message.__dataclass_fields__
|
|
128
|
+
}
|
|
129
|
+
else:
|
|
130
|
+
# Try to extract common attributes
|
|
131
|
+
for attr in [
|
|
132
|
+
"content",
|
|
133
|
+
"subtype",
|
|
134
|
+
"data",
|
|
135
|
+
"session_id",
|
|
136
|
+
"stop_reason",
|
|
137
|
+
"usage",
|
|
138
|
+
"total_cost_usd",
|
|
139
|
+
]:
|
|
140
|
+
if hasattr(message, attr):
|
|
141
|
+
message_dict[attr] = getattr(message, attr)
|
|
142
|
+
|
|
143
|
+
converted_message = model_class.model_validate(message_dict)
|
|
144
|
+
|
|
145
|
+
logger.debug(
|
|
146
|
+
"claude_sdk_message_converted_successfully",
|
|
147
|
+
original_type=type(message).__name__,
|
|
148
|
+
converted_type=type(converted_message).__name__,
|
|
149
|
+
message_count=message_count,
|
|
150
|
+
request_id=request_id,
|
|
151
|
+
)
|
|
152
|
+
yield converted_message
|
|
153
|
+
except Exception as e:
|
|
154
|
+
logger.warning(
|
|
155
|
+
"claude_sdk_message_conversion_failed",
|
|
156
|
+
message_type=type(message).__name__,
|
|
157
|
+
model_class=model_class.__name__,
|
|
158
|
+
error=str(e),
|
|
159
|
+
)
|
|
160
|
+
# Skip invalid messages rather than crashing
|
|
161
|
+
continue
|
|
79
162
|
|
|
80
163
|
# Store final metrics
|
|
81
164
|
op["message_count"] = message_count
|