ccproxy-api 0.2.0a4__py3-none-any.whl → 0.2.2__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/core/_version.py CHANGED
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.2.0a4'
32
- __version_tuple__ = version_tuple = (0, 2, 0, 'a4')
31
+ __version__ = version = '0.2.2'
32
+ __version_tuple__ = version_tuple = (0, 2, 2)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -0,0 +1,44 @@
1
+ """Shared helpers for Anthropic to OpenAI formatting."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from typing import Any
7
+
8
+ from ccproxy.llms.models import openai as openai_models
9
+
10
+
11
+ def serialize_tool_arguments(tool_input: Any) -> str:
12
+ if isinstance(tool_input, str):
13
+ return tool_input
14
+ try:
15
+ return json.dumps(tool_input, ensure_ascii=False)
16
+ except Exception:
17
+ return json.dumps({"arguments": str(tool_input)})
18
+
19
+
20
+ def build_openai_tool_call(
21
+ *,
22
+ tool_id: str | None,
23
+ tool_name: str | None,
24
+ tool_input: Any,
25
+ arguments: Any = None,
26
+ fallback_index: int = 0,
27
+ ) -> openai_models.ToolCall:
28
+ args_str = (
29
+ arguments
30
+ if isinstance(arguments, str) and arguments
31
+ else serialize_tool_arguments(tool_input)
32
+ )
33
+ call_id = (
34
+ tool_id if isinstance(tool_id, str) and tool_id else f"call_{fallback_index}"
35
+ )
36
+ name = tool_name if isinstance(tool_name, str) and tool_name else "function"
37
+
38
+ return openai_models.ToolCall(
39
+ id=str(call_id),
40
+ function=openai_models.FunctionCall(
41
+ name=str(name),
42
+ arguments=str(args_str),
43
+ ),
44
+ )
@@ -14,6 +14,8 @@ from ccproxy.llms.formatters.constants import ANTHROPIC_TO_OPENAI_FINISH_REASON
14
14
  from ccproxy.llms.models import anthropic as anthropic_models
15
15
  from ccproxy.llms.models import openai as openai_models
16
16
 
17
+ from ._helpers import build_openai_tool_call
18
+
17
19
 
18
20
  logger = ccproxy.core.logging.get_logger(__name__)
19
21
 
@@ -101,6 +103,8 @@ def convert__anthropic_message_to_openai_chat__response(
101
103
  """Convert Anthropic MessageResponse to an OpenAI ChatCompletionResponse."""
102
104
  content_blocks = response.content
103
105
  parts: list[str] = []
106
+ tool_calls: list[openai_models.ToolCall] = []
107
+
104
108
  for block in content_blocks:
105
109
  btype = getattr(block, "type", None)
106
110
  if btype == "text":
@@ -117,8 +121,17 @@ def convert__anthropic_message_to_openai_chat__response(
117
121
  else ""
118
122
  )
119
123
  parts.append(f"<thinking{sig_attr}>{thinking}</thinking>")
124
+ elif btype == "tool_use":
125
+ tool_calls.append(
126
+ build_openai_tool_call(
127
+ tool_id=getattr(block, "id", None),
128
+ tool_name=getattr(block, "name", None),
129
+ tool_input=getattr(block, "input", {}) or {},
130
+ fallback_index=len(tool_calls),
131
+ )
132
+ )
120
133
 
121
- content_text = "".join(parts)
134
+ content_text = "".join(parts) if parts else None
122
135
 
123
136
  stop_reason = response.stop_reason
124
137
  finish_reason = ANTHROPIC_TO_OPENAI_FINISH_REASON.get(
@@ -127,12 +140,16 @@ def convert__anthropic_message_to_openai_chat__response(
127
140
 
128
141
  usage_model = convert__anthropic_usage_to_openai_completion__usage(response.usage)
129
142
 
143
+ message_dict: dict[str, Any] = {"role": "assistant", "content": content_text}
144
+ if tool_calls:
145
+ message_dict["tool_calls"] = [call.model_dump() for call in tool_calls]
146
+
130
147
  payload = {
131
148
  "id": response.id,
132
149
  "choices": [
133
150
  {
134
151
  "index": 0,
135
- "message": {"role": "assistant", "content": content_text},
152
+ "message": message_dict,
136
153
  "finish_reason": finish_reason,
137
154
  }
138
155
  ],
@@ -27,10 +27,9 @@ from ccproxy.llms.models import anthropic as anthropic_models
27
27
  from ccproxy.llms.models import openai as openai_models
28
28
  from ccproxy.llms.streaming.accumulators import ClaudeAccumulator
29
29
 
30
+ from ._helpers import build_openai_tool_call
30
31
  from .requests import _build_responses_payload_from_anthropic_request
31
- from .responses import (
32
- convert__anthropic_usage_to_openai_responses__usage,
33
- )
32
+ from .responses import convert__anthropic_usage_to_openai_responses__usage
34
33
 
35
34
 
36
35
  logger = ccproxy.core.logging.get_logger(__name__)
@@ -100,22 +99,15 @@ def _build_openai_tool_call(
100
99
  function_payload = (
101
100
  tool_call.get("function", {}) if isinstance(tool_call, dict) else {}
102
101
  )
103
- name = function_payload.get("name") or tool_call.get("name") or "function"
102
+ tool_name = function_payload.get("name") or tool_call.get("name")
104
103
  arguments = function_payload.get("arguments")
105
- if not isinstance(arguments, str) or not arguments:
106
- try:
107
- arguments = json.dumps(tool_call.get("input", {}), ensure_ascii=False)
108
- except Exception:
109
- arguments = json.dumps(tool_call.get("input", {}))
110
-
111
- tool_id = tool_call.get("id") or f"call_{block_index}"
112
104
 
113
- return openai_models.ToolCall(
114
- id=str(tool_id),
115
- function=openai_models.FunctionCall(
116
- name=str(name),
117
- arguments=str(arguments),
118
- ),
105
+ return build_openai_tool_call(
106
+ tool_id=tool_call.get("id"),
107
+ tool_name=tool_name,
108
+ tool_input=tool_call.get("input", {}),
109
+ arguments=arguments,
110
+ fallback_index=block_index,
119
111
  )
120
112
 
121
113
  return None
@@ -85,6 +85,11 @@ class ClaudeAPIAdapter(BaseHTTPAdapter):
85
85
  # Always set Authorization from OAuth-managed access token
86
86
  filtered_headers["authorization"] = f"Bearer {token_value}"
87
87
 
88
+ # PATCH: Add Computer Use beta headers for Anthropic API
89
+ # These are required for browser automation tools to work
90
+ filtered_headers["anthropic-version"] = "2023-06-01"
91
+ filtered_headers["anthropic-beta"] = "computer-use-2025-01-24"
92
+
88
93
  # Add CLI headers if available, but never allow overriding auth
89
94
  cli_headers = self._collect_cli_headers()
90
95
  if cli_headers:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ccproxy-api
3
- Version: 0.2.0a4
3
+ Version: 0.2.2
4
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
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
@@ -196,14 +196,14 @@ To install the latest stable release without cloning the repository, use `uvx`
196
196
  to grab the published wheel and launch the CLI:
197
197
 
198
198
  ```bash
199
- uvx --with "ccproxy-api[all]==0.2.0" ccproxy serve --port 8000
199
+ uvx --with "ccproxy-api[all]" ccproxy serve --port 8000
200
200
  ```
201
201
 
202
202
  If you prefer `pipx`, install the package (optionally with extras) and use the
203
203
  local shim:
204
204
 
205
205
  ```bash
206
- pipx install "ccproxy-api[all]==0.2.0"
206
+ pipx install "ccproxy-api[all]"
207
207
  ccproxy serve # default on localhost:8000
208
208
  ```
209
209
 
@@ -67,7 +67,7 @@ ccproxy/config/settings.py,sha256=uva0RV4KfIvv7VApDr7w3oARft7OoBCWf7ZSM4oM2VM,19
67
67
  ccproxy/config/toml_generator.py,sha256=_txCYDHI8lXWl-mwOK8P1_TsX1TNiLgkG4iryxOruZc,10034
68
68
  ccproxy/config/utils.py,sha256=tuvOPUsMGgznz94MRwuSWw6sZi_AGkB_ri7VWKMVg8Y,11877
69
69
  ccproxy/core/__init__.py,sha256=hQgrBogZjdt8ZQlQyZtbL91I3gX9YUTWrenqTPRfwbM,236
70
- ccproxy/core/_version.py,sha256=fqfgUu99l0Y7LMwL27QHnVn-raAKZVZ_9iTiD4_KO5A,712
70
+ ccproxy/core/_version.py,sha256=o3ZTescp-19Z9cvBGq9dQnbppljgzdUYUf98Nov0spY,704
71
71
  ccproxy/core/async_task_manager.py,sha256=zf_mbbDwomh8q0E-oMNSPzFecHwLRi-ZPbhqsb6IPgM,16888
72
72
  ccproxy/core/async_utils.py,sha256=OFCJT8xbgZJO757iDPMAKY5c1Ildyk0PbwuktVF19UI,21676
73
73
  ccproxy/core/constants.py,sha256=FSLlbdNqCmZgZC4VAgvmovwXJh4C9WaUf_YBqDbYXXM,1837
@@ -121,10 +121,11 @@ ccproxy/llms/formatters/context.py,sha256=ULZl3sIcGbYKqzqo-W4HNpYoWD03vPVU-akahg
121
121
  ccproxy/llms/formatters/mapping.py,sha256=Tskj43bbDQAZWjqKEfnoivStwAycPXDGn1vXjuqTaic,1014
122
122
  ccproxy/llms/formatters/utils.py,sha256=7TKOraamcc_TXAxYkTv_HUFXr-fkHnqyBWb7WU9eZn4,10224
123
123
  ccproxy/llms/formatters/anthropic_to_openai/__init__.py,sha256=BR4ZAbaPRPd3q7Urb22ylyUgWaVi7UQ2zJzzGBam_Lw,1992
124
+ ccproxy/llms/formatters/anthropic_to_openai/_helpers.py,sha256=mX2HTsmcofkZ-ocBUUr62q0IxHpG2n5oS_cryAY6yvs,1166
124
125
  ccproxy/llms/formatters/anthropic_to_openai/errors.py,sha256=PCGB7PKr6x1jElIKxqDye0o1mI0_MUdTNMtk_djWbLA,2066
125
126
  ccproxy/llms/formatters/anthropic_to_openai/requests.py,sha256=e7BanFju5owBD64gtd835YIv1T18XYshp-DP-7I4y1o,14660
126
- ccproxy/llms/formatters/anthropic_to_openai/responses.py,sha256=yNjYdt2mor0t66kqjWyHu-LT9PYDmPgZqJalCgIUdAs,5199
127
- ccproxy/llms/formatters/anthropic_to_openai/streams.py,sha256=FWauNClQrF8E8EQLdPfkUi3gkGG3FDGbi_g7VVpnGxo,65989
127
+ ccproxy/llms/formatters/anthropic_to_openai/responses.py,sha256=uDq5NAvfinPDC75pY54j8BJzltb5r1kAeytToz0InFM,5834
128
+ ccproxy/llms/formatters/anthropic_to_openai/streams.py,sha256=CGW74eS-TZ9_0pM9ff2MfdI9yUhfmqXlwP7jS96obZQ,65722
128
129
  ccproxy/llms/formatters/common/__init__.py,sha256=Lnsz81M4P91Cex7t0_oM_hD1Tak7oHAWuykQkWg4b38,1524
129
130
  ccproxy/llms/formatters/common/identifiers.py,sha256=rzTynHqcvmPKhog64YAtxLDcQH27u38kZH8aCI3HIOA,1400
130
131
  ccproxy/llms/formatters/common/streams.py,sha256=kP_QQViwki1dH9IIMtigQLOtTY8wM2KZoQMFMl8vE_Y,8042
@@ -170,7 +171,7 @@ ccproxy/plugins/analytics/routes.py,sha256=RZLbaRkvo1vFvOasnzlqKyyTIm01dLYTg-T4a
170
171
  ccproxy/plugins/analytics/service.py,sha256=9aqS0sNZVsKsbrhYi62jdkkDgeoifcC3ARM3d4dweJ0,11699
171
172
  ccproxy/plugins/claude_api/README.md,sha256=kXpPt1NMbKdQiG0D4UmJKreLytWJKr0FwS9pSrxEgTE,1072
172
173
  ccproxy/plugins/claude_api/__init__.py,sha256=2n3Kw6EGmvEyoSgQzT2DRwLe5Zb1XET7kvvt0mwG3Mg,304
173
- ccproxy/plugins/claude_api/adapter.py,sha256=anX45r4gbeULAUaCzA7W5HlLmcfSbGrWuawCFNOdEu0,30857
174
+ ccproxy/plugins/claude_api/adapter.py,sha256=T1y9lAxJEj5yqiyo210IlZeRfSmuqyi4Gt1Jx6q5Bew,31121
174
175
  ccproxy/plugins/claude_api/config.py,sha256=R-8w5yOYcN42tx4cR3eyzyz99Ldt4B1aTDj_vEy9NdQ,1681
175
176
  ccproxy/plugins/claude_api/detection_service.py,sha256=mJeYvSskPS8mN-RTvwEVkPqN-91LZVfD7BsfYw7mDR8,15975
176
177
  ccproxy/plugins/claude_api/health.py,sha256=s1Vb3JuHWaWslLKOyFJXq6PrDAkzjEhhHXAR2cVXgQ8,5990
@@ -410,8 +411,8 @@ ccproxy/utils/id_generator.py,sha256=k6R_W40lJSPi_it4M99EVg9eRD138oC4bv_8Ua3X8ms
410
411
  ccproxy/utils/model_mapper.py,sha256=hnIWc528x8oBuk1y1HuyGHbnwe6dsSxZ2UgA1OYrcJs,3731
411
412
  ccproxy/utils/startup_helpers.py,sha256=u1okOVbm2OeSqrNrbhWco_sXBR0usNo7Wv8zvhBLPhc,7492
412
413
  ccproxy/utils/version_checker.py,sha256=cGRgjD0PUB3MDZDSAdKPQwYIyqnlzFWue0ROyfGngNE,13452
413
- ccproxy_api-0.2.0a4.dist-info/METADATA,sha256=TiTWxEXyms9TVVIWQo7ANRPR41vCIH9yHdAEAa7LfsA,8296
414
- ccproxy_api-0.2.0a4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
415
- ccproxy_api-0.2.0a4.dist-info/entry_points.txt,sha256=bibqQtPpKZJhOY_j5TFvcYzHuR-w7tNovV2i7UcPlU4,1147
416
- ccproxy_api-0.2.0a4.dist-info/licenses/LICENSE,sha256=httxSCpTrEOkipisMeGXSrZhTB-4MRIorQU0hS1B6eQ,1066
417
- ccproxy_api-0.2.0a4.dist-info/RECORD,,
414
+ ccproxy_api-0.2.2.dist-info/METADATA,sha256=5rWBdjovufqSnrAZSnIIy1HwiwWgP2a9R3xf7yV5WCE,8280
415
+ ccproxy_api-0.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
416
+ ccproxy_api-0.2.2.dist-info/entry_points.txt,sha256=bibqQtPpKZJhOY_j5TFvcYzHuR-w7tNovV2i7UcPlU4,1147
417
+ ccproxy_api-0.2.2.dist-info/licenses/LICENSE,sha256=httxSCpTrEOkipisMeGXSrZhTB-4MRIorQU0hS1B6eQ,1066
418
+ ccproxy_api-0.2.2.dist-info/RECORD,,