ccproxy-api 0.1.1__py3-none-any.whl → 0.1.3__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 (107) hide show
  1. ccproxy/_version.py +2 -2
  2. ccproxy/adapters/openai/__init__.py +1 -2
  3. ccproxy/adapters/openai/adapter.py +218 -180
  4. ccproxy/adapters/openai/streaming.py +247 -65
  5. ccproxy/api/__init__.py +0 -3
  6. ccproxy/api/app.py +173 -40
  7. ccproxy/api/dependencies.py +65 -3
  8. ccproxy/api/middleware/errors.py +3 -7
  9. ccproxy/api/middleware/headers.py +0 -2
  10. ccproxy/api/middleware/logging.py +4 -3
  11. ccproxy/api/middleware/request_content_logging.py +297 -0
  12. ccproxy/api/middleware/request_id.py +5 -0
  13. ccproxy/api/middleware/server_header.py +0 -4
  14. ccproxy/api/routes/__init__.py +9 -1
  15. ccproxy/api/routes/claude.py +23 -32
  16. ccproxy/api/routes/health.py +58 -4
  17. ccproxy/api/routes/mcp.py +171 -0
  18. ccproxy/api/routes/metrics.py +4 -8
  19. ccproxy/api/routes/permissions.py +217 -0
  20. ccproxy/api/routes/proxy.py +0 -53
  21. ccproxy/api/services/__init__.py +6 -0
  22. ccproxy/api/services/permission_service.py +368 -0
  23. ccproxy/api/ui/__init__.py +6 -0
  24. ccproxy/api/ui/permission_handler_protocol.py +33 -0
  25. ccproxy/api/ui/terminal_permission_handler.py +593 -0
  26. ccproxy/auth/conditional.py +2 -2
  27. ccproxy/auth/dependencies.py +1 -1
  28. ccproxy/auth/oauth/models.py +0 -1
  29. ccproxy/auth/oauth/routes.py +1 -3
  30. ccproxy/auth/storage/json_file.py +0 -1
  31. ccproxy/auth/storage/keyring.py +0 -3
  32. ccproxy/claude_sdk/__init__.py +2 -0
  33. ccproxy/claude_sdk/client.py +91 -8
  34. ccproxy/claude_sdk/converter.py +405 -210
  35. ccproxy/claude_sdk/options.py +88 -19
  36. ccproxy/claude_sdk/parser.py +200 -0
  37. ccproxy/claude_sdk/streaming.py +286 -0
  38. ccproxy/cli/commands/__init__.py +5 -1
  39. ccproxy/cli/commands/auth.py +2 -4
  40. ccproxy/cli/commands/permission_handler.py +553 -0
  41. ccproxy/cli/commands/serve.py +52 -12
  42. ccproxy/cli/docker/params.py +0 -4
  43. ccproxy/cli/helpers.py +0 -2
  44. ccproxy/cli/main.py +6 -17
  45. ccproxy/cli/options/claude_options.py +41 -1
  46. ccproxy/cli/options/core_options.py +0 -3
  47. ccproxy/cli/options/security_options.py +0 -2
  48. ccproxy/cli/options/server_options.py +3 -2
  49. ccproxy/config/auth.py +0 -1
  50. ccproxy/config/claude.py +78 -2
  51. ccproxy/config/discovery.py +0 -1
  52. ccproxy/config/docker_settings.py +0 -1
  53. ccproxy/config/loader.py +1 -4
  54. ccproxy/config/scheduler.py +20 -0
  55. ccproxy/config/security.py +7 -2
  56. ccproxy/config/server.py +5 -0
  57. ccproxy/config/settings.py +15 -7
  58. ccproxy/config/validators.py +1 -1
  59. ccproxy/core/async_utils.py +1 -4
  60. ccproxy/core/errors.py +45 -1
  61. ccproxy/core/http_transformers.py +4 -3
  62. ccproxy/core/interfaces.py +2 -2
  63. ccproxy/core/logging.py +97 -95
  64. ccproxy/core/middleware.py +1 -1
  65. ccproxy/core/proxy.py +1 -1
  66. ccproxy/core/transformers.py +1 -1
  67. ccproxy/core/types.py +1 -1
  68. ccproxy/docker/models.py +1 -1
  69. ccproxy/docker/protocol.py +0 -3
  70. ccproxy/models/__init__.py +41 -0
  71. ccproxy/models/claude_sdk.py +420 -0
  72. ccproxy/models/messages.py +45 -18
  73. ccproxy/models/permissions.py +115 -0
  74. ccproxy/models/requests.py +1 -1
  75. ccproxy/models/responses.py +64 -1
  76. ccproxy/observability/access_logger.py +1 -2
  77. ccproxy/observability/context.py +17 -1
  78. ccproxy/observability/metrics.py +1 -3
  79. ccproxy/observability/pushgateway.py +0 -2
  80. ccproxy/observability/stats_printer.py +2 -4
  81. ccproxy/observability/storage/duckdb_simple.py +1 -1
  82. ccproxy/observability/storage/models.py +0 -1
  83. ccproxy/pricing/cache.py +0 -1
  84. ccproxy/pricing/loader.py +5 -21
  85. ccproxy/pricing/updater.py +0 -1
  86. ccproxy/scheduler/__init__.py +1 -0
  87. ccproxy/scheduler/core.py +6 -6
  88. ccproxy/scheduler/manager.py +35 -7
  89. ccproxy/scheduler/registry.py +1 -1
  90. ccproxy/scheduler/tasks.py +127 -2
  91. ccproxy/services/claude_sdk_service.py +225 -329
  92. ccproxy/services/credentials/manager.py +0 -1
  93. ccproxy/services/credentials/oauth_client.py +1 -2
  94. ccproxy/services/proxy_service.py +93 -222
  95. ccproxy/testing/config.py +1 -1
  96. ccproxy/testing/mock_responses.py +0 -1
  97. ccproxy/utils/model_mapping.py +197 -0
  98. ccproxy/utils/models_provider.py +150 -0
  99. ccproxy/utils/simple_request_logger.py +284 -0
  100. ccproxy/utils/version_checker.py +184 -0
  101. {ccproxy_api-0.1.1.dist-info → ccproxy_api-0.1.3.dist-info}/METADATA +63 -2
  102. ccproxy_api-0.1.3.dist-info/RECORD +166 -0
  103. {ccproxy_api-0.1.1.dist-info → ccproxy_api-0.1.3.dist-info}/entry_points.txt +1 -0
  104. ccproxy_api-0.1.1.dist-info/RECORD +0 -149
  105. /ccproxy/scheduler/{exceptions.py → errors.py} +0 -0
  106. {ccproxy_api-0.1.1.dist-info → ccproxy_api-0.1.3.dist-info}/WHEEL +0 -0
  107. {ccproxy_api-0.1.1.dist-info → ccproxy_api-0.1.3.dist-info}/licenses/LICENSE +0 -0
ccproxy/core/errors.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Core error types for the proxy system."""
2
2
 
3
- from typing import Any, Optional
3
+ from typing import Any
4
4
 
5
5
  from fastapi import HTTPException
6
6
 
@@ -234,6 +234,45 @@ class DockerError(ClaudeProxyError):
234
234
  )
235
235
 
236
236
 
237
+ class PermissionRequestError(ClaudeProxyError):
238
+ """Base exception for permission request-related errors."""
239
+
240
+ pass
241
+
242
+
243
+ class PermissionNotFoundError(PermissionRequestError):
244
+ """Raised when permission request is not found."""
245
+
246
+ def __init__(self, confirmation_id: str) -> None:
247
+ super().__init__(
248
+ message=f"Permission request '{confirmation_id}' not found",
249
+ error_type="not_found_error",
250
+ status_code=404,
251
+ )
252
+
253
+
254
+ class PermissionExpiredError(PermissionRequestError):
255
+ """Raised when permission request has expired."""
256
+
257
+ def __init__(self, confirmation_id: str) -> None:
258
+ super().__init__(
259
+ message=f"Permission request '{confirmation_id}' has expired",
260
+ error_type="expired_error",
261
+ status_code=410,
262
+ )
263
+
264
+
265
+ class PermissionAlreadyResolvedError(PermissionRequestError):
266
+ """Raised when trying to resolve an already resolved request."""
267
+
268
+ def __init__(self, confirmation_id: str, status: str) -> None:
269
+ super().__init__(
270
+ message=f"Permission request '{confirmation_id}' already resolved with status: {status}",
271
+ error_type="conflict_error",
272
+ status_code=409,
273
+ )
274
+
275
+
237
276
  __all__ = [
238
277
  # Core proxy errors
239
278
  "ProxyError",
@@ -253,4 +292,9 @@ __all__ = [
253
292
  "TimeoutError",
254
293
  "ServiceUnavailableError",
255
294
  "DockerError",
295
+ # Permission errors
296
+ "PermissionRequestError",
297
+ "PermissionNotFoundError",
298
+ "PermissionExpiredError",
299
+ "PermissionAlreadyResolvedError",
256
300
  ]
@@ -1,6 +1,5 @@
1
1
  """HTTP-level transformers for proxy service."""
2
2
 
3
- import json
4
3
  from typing import TYPE_CHECKING, Any
5
4
 
6
5
  import structlog
@@ -18,6 +17,8 @@ logger = structlog.get_logger(__name__)
18
17
  # Claude Code system prompt constants
19
18
  claude_code_prompt = "You are Claude Code, Anthropic's official CLI for Claude."
20
19
 
20
+ # claude_code_prompt = "<system-reminder>\nAs you answer the user's questions, you can use the following context:\n# important-instruction-reminders\nDo what has been asked; nothing more, nothing less.\nNEVER create files unless they're absolutely necessary for achieving your goal.\nALWAYS prefer editing an existing file to creating a new one.\nNEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested by the User.\n\n \n IMPORTANT: this context may or may not be relevant to your tasks. You should not respond to this context unless it is highly relevant to your task.\n</system-reminder>\n"
21
+
21
22
 
22
23
  def get_claude_code_prompt() -> dict[str, Any]:
23
24
  """Get the Claude Code system prompt with cache control."""
@@ -179,7 +180,7 @@ class HTTPRequestTransformer(RequestTransformer):
179
180
 
180
181
  # Claude CLI identity headers
181
182
  proxy_headers["x-app"] = "cli"
182
- proxy_headers["User-Agent"] = "claude-cli/1.0.43 (external, cli)"
183
+ proxy_headers["User-Agent"] = "claude-cli/1.0.60 (external, cli)"
183
184
 
184
185
  # Stainless SDK compatibility headers
185
186
  proxy_headers["X-Stainless-Lang"] = "js"
@@ -189,7 +190,7 @@ class HTTPRequestTransformer(RequestTransformer):
189
190
  proxy_headers["X-Stainless-OS"] = "Linux"
190
191
  proxy_headers["X-Stainless-Arch"] = "x64"
191
192
  proxy_headers["X-Stainless-Runtime"] = "node"
192
- proxy_headers["X-Stainless-Runtime-Version"] = "v22.14.0"
193
+ proxy_headers["X-Stainless-Runtime-Version"] = "v24.3.0"
193
194
 
194
195
  # Standard HTTP headers for proper API interaction
195
196
  proxy_headers["accept-language"] = "*"
@@ -6,10 +6,10 @@ providing a single location for defining contracts and protocols.
6
6
 
7
7
  from abc import ABC, abstractmethod
8
8
  from collections.abc import AsyncIterator
9
- from typing import Any, Optional, Protocol, TypeVar, runtime_checkable
9
+ from typing import Any, Protocol, TypeVar, runtime_checkable
10
10
 
11
11
  from ccproxy.auth.models import ClaudeCredentials
12
- from ccproxy.core.types import ProxyRequest, ProxyResponse, TransformContext
12
+ from ccproxy.core.types import TransformContext
13
13
 
14
14
 
15
15
  __all__ = [
ccproxy/core/logging.py CHANGED
@@ -1,45 +1,25 @@
1
1
  import logging
2
2
  import sys
3
- from collections.abc import MutableMapping
4
- from typing import Any
3
+ from pathlib import Path
5
4
 
6
5
  import structlog
7
6
  from structlog.stdlib import BoundLogger
8
7
  from structlog.typing import Processor
9
8
 
10
9
 
11
- def configure_structlog(json_logs: bool = False, log_level: str = "INFO") -> None:
12
- """Configure structlog with your preferred processors."""
13
- # Use different timestamp format based on log level
14
- # Dev mode (DEBUG): only hours without microseconds
15
- # Info mode: full date without microseconds
16
- if log_level.upper() == "DEBUG":
17
- timestamper = structlog.processors.TimeStamper(fmt="%H:%M:%S")
18
- else:
19
- timestamper = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S")
20
-
21
- # Processors that will be used for structlog loggers
10
+ def configure_structlog(log_level: int = logging.INFO) -> None:
11
+ """Configure structlog with shared processors following canonical pattern."""
12
+ # Shared processors for all structlog loggers
22
13
  processors: list[Processor] = [
14
+ structlog.contextvars.merge_contextvars, # For request context in web apps
23
15
  structlog.stdlib.filter_by_level,
16
+ structlog.stdlib.add_log_level,
17
+ structlog.stdlib.add_logger_name,
24
18
  ]
25
19
 
26
- # Only add logger name if NOT in INFO mode
27
- if log_level.upper() != "INFO":
28
- processors.append(structlog.stdlib.add_logger_name)
29
-
30
- processors.extend(
31
- [
32
- structlog.stdlib.add_log_level,
33
- structlog.stdlib.PositionalArgumentsFormatter(),
34
- timestamper,
35
- structlog.processors.StackInfoRenderer(),
36
- structlog.processors.format_exc_info,
37
- structlog.processors.UnicodeDecoder(),
38
- ]
39
- )
40
-
41
- # Only add CallsiteParameterAdder if NOT in INFO mode
42
- if log_level.upper() != "INFO":
20
+ # Add debug-specific processors
21
+ if log_level < logging.INFO:
22
+ # Dev mode (DEBUG): add callsite information
43
23
  processors.append(
44
24
  structlog.processors.CallsiteParameterAdder(
45
25
  parameters=[
@@ -49,92 +29,115 @@ def configure_structlog(json_logs: bool = False, log_level: str = "INFO") -> Non
49
29
  )
50
30
  )
51
31
 
52
- # This wrapper passes the event dictionary to the ProcessorFormatter
53
- # so we don't double-render
54
- processors.append(structlog.stdlib.ProcessorFormatter.wrap_for_formatter)
32
+ # Common processors for all log levels
33
+ processors.extend(
34
+ [
35
+ # Use human-readable timestamp for structlog logs in debug mode, normal otherwise
36
+ structlog.processors.TimeStamper(
37
+ fmt="%H:%M:%S" if log_level < logging.INFO else "%Y-%m-%d %H:%M:%S"
38
+ ),
39
+ structlog.processors.StackInfoRenderer(),
40
+ structlog.dev.set_exc_info, # Handle exceptions properly
41
+ # This MUST be the last processor - allows different renderers per handler
42
+ structlog.stdlib.ProcessorFormatter.wrap_for_formatter,
43
+ ]
44
+ )
55
45
 
56
46
  structlog.configure(
57
47
  processors=processors,
58
48
  context_class=dict,
59
49
  logger_factory=structlog.stdlib.LoggerFactory(),
60
50
  wrapper_class=structlog.stdlib.BoundLogger,
61
- cache_logger_on_first_use=False, # Don't cache to allow reconfiguration
51
+ cache_logger_on_first_use=True, # Cache for performance
62
52
  )
63
53
 
64
54
 
65
55
  def setup_logging(
66
- json_logs: bool = False, log_level: str = "INFO", log_file: str | None = None
56
+ json_logs: bool = False, log_level_name: str = "DEBUG", log_file: str | None = None
67
57
  ) -> BoundLogger:
68
58
  """
69
- Setup logging for the entire application including uvicorn and fastapi.
59
+ Setup logging for the entire application using canonical structlog pattern.
70
60
  Returns a structlog logger instance.
71
61
  """
72
- # Set the log level for the root logger first so structlog can see it
62
+ log_level = getattr(logging, log_level_name.upper(), logging.INFO)
63
+
64
+ # Get root logger and set level BEFORE configuring structlog
73
65
  root_logger = logging.getLogger()
74
- root_logger.setLevel(getattr(logging, log_level.upper(), logging.INFO))
66
+ root_logger.setLevel(log_level)
75
67
 
76
- # Configure structlog after setting the log level
77
- configure_structlog(json_logs=json_logs, log_level=log_level)
68
+ # 1. Configure structlog with shared processors
69
+ configure_structlog(log_level=log_level)
78
70
 
79
- # Create a handler that will format stdlib logs through structlog
80
- handler = logging.StreamHandler(sys.stdout)
81
- handler.setLevel(getattr(logging, log_level.upper(), logging.INFO))
71
+ # 2. Setup root logger handlers
72
+ root_logger.handlers = [] # Clear any existing handlers
82
73
 
83
- # Use the appropriate renderer based on json_logs setting
84
- renderer = (
85
- structlog.processors.JSONRenderer()
86
- if json_logs
87
- else structlog.dev.ConsoleRenderer()
88
- )
74
+ # 3. Create shared processors for foreign (stdlib) logs
75
+ shared_processors = [
76
+ structlog.contextvars.merge_contextvars,
77
+ structlog.stdlib.add_log_level,
78
+ structlog.stdlib.add_logger_name,
79
+ structlog.dev.set_exc_info,
80
+ ]
89
81
 
90
- # Use ProcessorFormatter to handle both structlog and stdlib logs
91
- # Use the same timestamp format for foreign logs
92
- if log_level.upper() == "DEBUG":
93
- foreign_timestamper = structlog.processors.TimeStamper(fmt="%H:%M:%S")
94
- else:
95
- foreign_timestamper = structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S")
82
+ # Add debug processors if needed
83
+ if log_level < logging.INFO:
84
+ shared_processors.append(
85
+ structlog.processors.CallsiteParameterAdder( # type: ignore[arg-type]
86
+ parameters=[
87
+ structlog.processors.CallsiteParameter.FILENAME,
88
+ structlog.processors.CallsiteParameter.LINENO,
89
+ ]
90
+ )
91
+ )
96
92
 
97
- # Build foreign_pre_chain conditionally
98
- foreign_pre_chain: list[Processor] = [structlog.stdlib.add_log_level]
93
+ # Add appropriate timestamper for console vs file
94
+ console_timestamper = (
95
+ structlog.processors.TimeStamper(fmt="%H:%M:%S")
96
+ if log_level < logging.INFO
97
+ else structlog.processors.TimeStamper(fmt="%Y-%m-%d %H:%M:%S")
98
+ )
99
99
 
100
- # Only add logger name if NOT in INFO mode
101
- if log_level.upper() != "INFO":
102
- foreign_pre_chain.append(structlog.stdlib.add_logger_name)
100
+ file_timestamper = structlog.processors.TimeStamper(fmt="iso")
103
101
 
104
- foreign_pre_chain.append(foreign_timestamper)
102
+ # 4. Setup console handler with ConsoleRenderer
103
+ console_handler = logging.StreamHandler(sys.stdout)
104
+ console_handler.setLevel(log_level)
105
+ console_renderer = (
106
+ structlog.processors.JSONRenderer()
107
+ if json_logs
108
+ else structlog.dev.ConsoleRenderer()
109
+ )
105
110
 
106
- handler.setFormatter(
111
+ # Console gets human-readable timestamps for both structlog and stdlib logs
112
+ console_processors = shared_processors + [console_timestamper]
113
+ console_handler.setFormatter(
107
114
  structlog.stdlib.ProcessorFormatter(
108
- processor=renderer,
109
- foreign_pre_chain=foreign_pre_chain,
115
+ foreign_pre_chain=console_processors,
116
+ processor=console_renderer,
110
117
  )
111
118
  )
119
+ root_logger.addHandler(console_handler)
112
120
 
113
- # Configure root logger (level already set above)
114
- handlers: list[logging.Handler] = [handler]
115
-
116
- # Add file handler if log_file is specified
121
+ # 5. Setup file handler with JSONRenderer (if log_file provided)
117
122
  if log_file:
118
- from pathlib import Path
119
-
120
123
  # Ensure parent directory exists
121
124
  log_path = Path(log_file)
122
125
  log_path.parent.mkdir(parents=True, exist_ok=True)
123
126
 
124
- # Create a file handler that always outputs JSON
125
- file_handler = logging.FileHandler(log_file, encoding="utf-8")
126
- file_handler.setLevel(getattr(logging, log_level.upper(), logging.INFO))
127
+ file_handler = logging.FileHandler(log_file, encoding="utf-8", delay=True)
128
+ file_handler.setLevel(log_level)
129
+
130
+ # File gets ISO timestamps for both structlog and stdlib logs
131
+ file_processors = shared_processors + [file_timestamper]
127
132
  file_handler.setFormatter(
128
133
  structlog.stdlib.ProcessorFormatter(
134
+ foreign_pre_chain=file_processors,
129
135
  processor=structlog.processors.JSONRenderer(),
130
- foreign_pre_chain=foreign_pre_chain,
131
136
  )
132
137
  )
133
- handlers.append(file_handler)
138
+ root_logger.addHandler(file_handler)
134
139
 
135
- root_logger.handlers = handlers
136
-
137
- # Make sure uvicorn and fastapi loggers use our configuration
140
+ # 6. Configure stdlib loggers to propagate to our handlers
138
141
  for logger_name in [
139
142
  "uvicorn",
140
143
  "uvicorn.access",
@@ -146,27 +149,23 @@ def setup_logging(
146
149
  logger.handlers = [] # Remove default handlers
147
150
  logger.propagate = True # Use root logger's handlers
148
151
 
149
- # Set uvicorn loggers to WARNING when app log level is INFO to reduce noise
150
- if logger_name.startswith("uvicorn") and log_level.upper() == "INFO":
152
+ # In DEBUG mode, let all logs through at DEBUG level
153
+ # Otherwise, reduce uvicorn noise by setting to WARNING
154
+ if log_level == logging.DEBUG:
155
+ logger.setLevel(logging.DEBUG)
156
+ elif logger_name.startswith("uvicorn"):
151
157
  logger.setLevel(logging.WARNING)
152
158
  else:
153
- logger.setLevel(getattr(logging, log_level.upper(), logging.INFO))
159
+ logger.setLevel(log_level)
154
160
 
155
161
  # Configure httpx logger separately - INFO when app is DEBUG, WARNING otherwise
156
162
  httpx_logger = logging.getLogger("httpx")
157
- httpx_logger.handlers = [] # Remove default handlers
158
- httpx_logger.propagate = True # Use root logger's handlers
159
- if log_level.upper() == "DEBUG":
160
- httpx_logger.setLevel(logging.INFO)
161
- else:
162
- httpx_logger.setLevel(logging.WARNING)
163
-
164
- # Set noisy HTTP-related loggers to WARNING when app log level >= WARNING, else use app log level
165
- app_log_level = getattr(logging, log_level.upper(), logging.INFO)
166
- noisy_log_level = (
167
- logging.WARNING if app_log_level <= logging.WARNING else app_log_level
168
- )
163
+ httpx_logger.handlers = []
164
+ httpx_logger.propagate = True
165
+ httpx_logger.setLevel(logging.INFO if log_level < logging.INFO else logging.WARNING)
169
166
 
167
+ # Set noisy HTTP-related loggers to WARNING
168
+ noisy_log_level = logging.WARNING if log_level <= logging.WARNING else log_level
170
169
  for noisy_logger_name in [
171
170
  "urllib3",
172
171
  "urllib3.connectionpool",
@@ -174,10 +173,13 @@ def setup_logging(
174
173
  "aiohttp",
175
174
  "httpcore",
176
175
  "httpcore.http11",
176
+ "fastapi_mcp",
177
+ "sse_starlette",
178
+ "mcp",
177
179
  ]:
178
180
  noisy_logger = logging.getLogger(noisy_logger_name)
179
- noisy_logger.handlers = [] # Remove default handlers
180
- noisy_logger.propagate = True # Use root logger's handlers
181
+ noisy_logger.handlers = []
182
+ noisy_logger.propagate = True
181
183
  noisy_logger.setLevel(noisy_log_level)
182
184
 
183
185
  return structlog.get_logger() # type: ignore[no-any-return]
@@ -2,7 +2,7 @@
2
2
 
3
3
  from abc import ABC, abstractmethod
4
4
  from collections.abc import Awaitable, Callable
5
- from typing import Any, Optional, Protocol, runtime_checkable
5
+ from typing import Protocol, runtime_checkable
6
6
 
7
7
  from ccproxy.core.types import ProxyRequest, ProxyResponse
8
8
 
ccproxy/core/proxy.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """Core proxy abstractions for handling HTTP and WebSocket connections."""
2
2
 
3
3
  from abc import ABC, abstractmethod
4
- from typing import TYPE_CHECKING, Any, Optional, Protocol, runtime_checkable
4
+ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
5
5
 
6
6
  from ccproxy.core.types import ProxyRequest, ProxyResponse
7
7
 
@@ -1,7 +1,7 @@
1
1
  """Core transformer abstractions for request/response transformation."""
2
2
 
3
3
  from abc import ABC, abstractmethod
4
- from typing import TYPE_CHECKING, Any, Optional, Protocol, TypeVar, runtime_checkable
4
+ from typing import TYPE_CHECKING, Any, Protocol, TypeVar, runtime_checkable
5
5
 
6
6
  from structlog import get_logger
7
7
 
ccproxy/core/types.py CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  from dataclasses import dataclass, field
4
4
  from enum import Enum
5
- from typing import Any, Optional, Union
5
+ from typing import Any
6
6
 
7
7
  from pydantic import BaseModel, ConfigDict, Field
8
8
 
ccproxy/docker/models.py CHANGED
@@ -3,7 +3,7 @@
3
3
  import os
4
4
  import platform
5
5
  from pathlib import Path
6
- from typing import ClassVar, Literal
6
+ from typing import ClassVar
7
7
 
8
8
  from pydantic import BaseModel, Field, field_validator
9
9
 
@@ -3,11 +3,8 @@
3
3
  from collections.abc import Awaitable
4
4
  from pathlib import Path
5
5
  from typing import (
6
- TYPE_CHECKING,
7
- Any,
8
6
  Protocol,
9
7
  TypeAlias,
10
- TypeVar,
11
8
  runtime_checkable,
12
9
  )
13
10
 
@@ -1,5 +1,26 @@
1
1
  """Pydantic models for Claude Proxy API Server."""
2
2
 
3
+ from .claude_sdk import (
4
+ AssistantMessage,
5
+ ContentBlock,
6
+ ExtendedContentBlock,
7
+ ResultMessage,
8
+ ResultMessageBlock,
9
+ SDKContentBlock,
10
+ SDKMessageMode,
11
+ TextBlock,
12
+ ToolResultBlock,
13
+ ToolResultSDKBlock,
14
+ ToolUseBlock,
15
+ ToolUseSDKBlock,
16
+ UserMessage,
17
+ convert_sdk_result_message,
18
+ convert_sdk_system_message,
19
+ convert_sdk_text_block,
20
+ convert_sdk_tool_result_block,
21
+ convert_sdk_tool_use_block,
22
+ to_sdk_variant,
23
+ )
3
24
  from .messages import (
4
25
  MessageContentBlock,
5
26
  MessageCreateParams,
@@ -67,6 +88,26 @@ __all__ = [
67
88
  "StreamEventType",
68
89
  "ToolChoiceType",
69
90
  "ToolType",
91
+ # Claude SDK models
92
+ "AssistantMessage",
93
+ "ContentBlock",
94
+ "ExtendedContentBlock",
95
+ "ResultMessage",
96
+ "ResultMessageBlock",
97
+ "SDKContentBlock",
98
+ "SDKMessageMode",
99
+ "TextBlock",
100
+ "ToolResultBlock",
101
+ "ToolResultSDKBlock",
102
+ "ToolUseBlock",
103
+ "ToolUseSDKBlock",
104
+ "UserMessage",
105
+ "convert_sdk_result_message",
106
+ "convert_sdk_system_message",
107
+ "convert_sdk_text_block",
108
+ "convert_sdk_tool_result_block",
109
+ "convert_sdk_tool_use_block",
110
+ "to_sdk_variant",
70
111
  # Message models
71
112
  "MessageContentBlock",
72
113
  "MessageCreateParams",