local-openai2anthropic 0.2.8__py3-none-any.whl → 0.3.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.
- local_openai2anthropic/router.py +70 -7
- {local_openai2anthropic-0.2.8.dist-info → local_openai2anthropic-0.3.0.dist-info}/METADATA +1 -1
- {local_openai2anthropic-0.2.8.dist-info → local_openai2anthropic-0.3.0.dist-info}/RECORD +6 -6
- {local_openai2anthropic-0.2.8.dist-info → local_openai2anthropic-0.3.0.dist-info}/WHEEL +0 -0
- {local_openai2anthropic-0.2.8.dist-info → local_openai2anthropic-0.3.0.dist-info}/entry_points.txt +0 -0
- {local_openai2anthropic-0.2.8.dist-info → local_openai2anthropic-0.3.0.dist-info}/licenses/LICENSE +0 -0
local_openai2anthropic/router.py
CHANGED
|
@@ -179,6 +179,19 @@ async def _stream_response(
|
|
|
179
179
|
if choice.get("finish_reason"):
|
|
180
180
|
finish_reason = choice["finish_reason"]
|
|
181
181
|
|
|
182
|
+
# When finish_reason is tool_calls, we need to close the current block
|
|
183
|
+
# and prepare to send message_delta
|
|
184
|
+
if finish_reason == "tool_calls" and content_block_started:
|
|
185
|
+
stop_block = {
|
|
186
|
+
"type": "content_block_stop",
|
|
187
|
+
"index": content_block_index,
|
|
188
|
+
}
|
|
189
|
+
logger.debug(
|
|
190
|
+
f"[Anthropic Stream Event] content_block_stop (tool_calls): {json.dumps(stop_block, ensure_ascii=False)}"
|
|
191
|
+
)
|
|
192
|
+
yield f"event: content_block_stop\ndata: {json.dumps(stop_block)}\n\n"
|
|
193
|
+
content_block_started = False
|
|
194
|
+
|
|
182
195
|
# Handle reasoning content (thinking)
|
|
183
196
|
if delta.get("reasoning_content"):
|
|
184
197
|
reasoning = delta["reasoning_content"]
|
|
@@ -253,6 +266,8 @@ async def _stream_response(
|
|
|
253
266
|
if tool_calls:
|
|
254
267
|
tool_call = tool_calls[0]
|
|
255
268
|
|
|
269
|
+
# Handle new tool call (with id) - use separate if, not elif
|
|
270
|
+
# because a chunk may have both id AND arguments
|
|
256
271
|
if tool_call.get("id"):
|
|
257
272
|
if content_block_started:
|
|
258
273
|
yield f"event: content_block_stop\ndata: {json.dumps({'type': 'content_block_stop', 'index': content_block_index})}\n\n"
|
|
@@ -264,7 +279,9 @@ async def _stream_response(
|
|
|
264
279
|
content_block_started = True
|
|
265
280
|
current_block_type = "tool_use"
|
|
266
281
|
|
|
267
|
-
|
|
282
|
+
# Handle tool call arguments - always check separately
|
|
283
|
+
# Note: This is intentionally NOT elif, as a single chunk may contain both
|
|
284
|
+
if (tool_call.get("function") or {}).get("arguments"):
|
|
268
285
|
args = (tool_call.get("function") or {}).get("arguments", "")
|
|
269
286
|
yield f"event: content_block_delta\ndata: {json.dumps({'type': 'content_block_delta', 'index': content_block_index, 'delta': {'type': 'input_json_delta', 'partial_json': args}})}\n\n"
|
|
270
287
|
|
|
@@ -495,8 +512,9 @@ async def _handle_with_server_tools(
|
|
|
495
512
|
)
|
|
496
513
|
|
|
497
514
|
completion_data = response.json()
|
|
498
|
-
|
|
499
|
-
|
|
515
|
+
# Log raw OpenAI response for server tools
|
|
516
|
+
logger.info(
|
|
517
|
+
f"[OpenAI Response (Server Tools)] {json.dumps(completion_data, ensure_ascii=False, indent=2)[:2000]}"
|
|
500
518
|
)
|
|
501
519
|
from openai.types.chat import ChatCompletion
|
|
502
520
|
|
|
@@ -514,7 +532,12 @@ async def _handle_with_server_tools(
|
|
|
514
532
|
if tool_calls:
|
|
515
533
|
for tc in tool_calls:
|
|
516
534
|
func_name = tc.function.name if tc.function else ""
|
|
535
|
+
func_args = tc.function.arguments if tc.function else "{}"
|
|
517
536
|
logger.info(f" Tool call: {func_name}")
|
|
537
|
+
logger.info(f" Tool ID: {tc.id}")
|
|
538
|
+
logger.info(
|
|
539
|
+
f" Arguments: {func_args[:200]}"
|
|
540
|
+
) # Log first 200 chars
|
|
518
541
|
|
|
519
542
|
# Generate Anthropic-style ID for server tools
|
|
520
543
|
is_server = handler.is_server_tool_call(
|
|
@@ -842,20 +865,60 @@ async def create_message(
|
|
|
842
865
|
)
|
|
843
866
|
|
|
844
867
|
openai_completion = response.json()
|
|
845
|
-
|
|
846
|
-
|
|
868
|
+
# Log raw OpenAI response
|
|
869
|
+
logger.info(
|
|
870
|
+
f"[OpenAI Raw Response] {json.dumps(openai_completion, ensure_ascii=False, indent=2)[:2000]}"
|
|
847
871
|
)
|
|
848
872
|
|
|
873
|
+
# Log response details
|
|
874
|
+
if openai_completion.get("choices"):
|
|
875
|
+
choice = openai_completion["choices"][0]
|
|
876
|
+
message = choice.get("message", {})
|
|
877
|
+
finish_reason = choice.get("finish_reason")
|
|
878
|
+
content_preview = (
|
|
879
|
+
message.get("content", "")[:100]
|
|
880
|
+
if message.get("content")
|
|
881
|
+
else ""
|
|
882
|
+
)
|
|
883
|
+
tool_calls_count = (
|
|
884
|
+
len(message.get("tool_calls", []))
|
|
885
|
+
if message.get("tool_calls")
|
|
886
|
+
else 0
|
|
887
|
+
)
|
|
888
|
+
logger.info(
|
|
889
|
+
f"[OpenAI Response Details] finish_reason={finish_reason}, "
|
|
890
|
+
f"content_length={len(message.get('content', ''))}, "
|
|
891
|
+
f"tool_calls={tool_calls_count}, "
|
|
892
|
+
f"content_preview={content_preview[:50]!r}"
|
|
893
|
+
)
|
|
894
|
+
|
|
849
895
|
from openai.types.chat import ChatCompletion
|
|
850
896
|
|
|
851
897
|
completion = ChatCompletion.model_validate(openai_completion)
|
|
852
898
|
anthropic_message = convert_openai_to_anthropic(completion, model)
|
|
853
899
|
|
|
854
900
|
anthropic_response = anthropic_message.model_dump()
|
|
855
|
-
|
|
856
|
-
|
|
901
|
+
# Log converted Anthropic response
|
|
902
|
+
logger.info(
|
|
903
|
+
f"[Anthropic Converted Response] {json.dumps(anthropic_response, ensure_ascii=False, indent=2)[:2000]}"
|
|
857
904
|
)
|
|
858
905
|
|
|
906
|
+
# Log Anthropic response details
|
|
907
|
+
content_blocks = anthropic_response.get("content", [])
|
|
908
|
+
stop_reason = anthropic_response.get("stop_reason")
|
|
909
|
+
usage = anthropic_response.get("usage", {})
|
|
910
|
+
logger.info(
|
|
911
|
+
f"[Anthropic Response Details] stop_reason={stop_reason}, "
|
|
912
|
+
f"content_blocks={len(content_blocks)}, "
|
|
913
|
+
f"input_tokens={usage.get('input_tokens')}, "
|
|
914
|
+
f"output_tokens={usage.get('output_tokens')}"
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
# Log content block types
|
|
918
|
+
if content_blocks:
|
|
919
|
+
block_types = [block.get("type") for block in content_blocks]
|
|
920
|
+
logger.info(f"[Anthropic Content Blocks] types={block_types}")
|
|
921
|
+
|
|
859
922
|
return JSONResponse(content=anthropic_response)
|
|
860
923
|
|
|
861
924
|
except httpx.TimeoutException:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: local-openai2anthropic
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: A lightweight proxy server that converts Anthropic Messages API to OpenAI API
|
|
5
5
|
Project-URL: Homepage, https://github.com/dongfangzan/local-openai2anthropic
|
|
6
6
|
Project-URL: Repository, https://github.com/dongfangzan/local-openai2anthropic
|
|
@@ -7,13 +7,13 @@ local_openai2anthropic/daemon_runner.py,sha256=rguOH0PgpbjqNsKYei0uCQX8JQOQ1wmtQ
|
|
|
7
7
|
local_openai2anthropic/main.py,sha256=FK5JBBpzB_T44y3N16lPl1hK4ht4LEQqRKzVmkIjIoo,9866
|
|
8
8
|
local_openai2anthropic/openai_types.py,sha256=jFdCvLwtXYoo5gGRqOhbHQcVaxcsxNnCP_yFPIv7rG4,3823
|
|
9
9
|
local_openai2anthropic/protocol.py,sha256=vUEgxtRPFll6jEtLc4DyxTLCBjrWIEScZXhEqe4uibk,5185
|
|
10
|
-
local_openai2anthropic/router.py,sha256=
|
|
10
|
+
local_openai2anthropic/router.py,sha256=ci8G3XL3eHzpn3kOko5QhnqLtIQTrlAaIhvzrvuk_Jc,46271
|
|
11
11
|
local_openai2anthropic/tavily_client.py,sha256=QsBhnyF8BFWPAxB4XtWCCpHCquNL5SW93-zjTTi4Meg,3774
|
|
12
12
|
local_openai2anthropic/server_tools/__init__.py,sha256=QlJfjEta-HOCtLe7NaY_fpbEKv-ZpInjAnfmSqE9tbk,615
|
|
13
13
|
local_openai2anthropic/server_tools/base.py,sha256=pNFsv-jSgxVrkY004AHAcYMNZgVSO8ZOeCzQBUtQ3vU,5633
|
|
14
14
|
local_openai2anthropic/server_tools/web_search.py,sha256=1C7lX_cm-tMaN3MsCjinEZYPJc_Hj4yAxYay9h8Zbvs,6543
|
|
15
|
-
local_openai2anthropic-0.
|
|
16
|
-
local_openai2anthropic-0.
|
|
17
|
-
local_openai2anthropic-0.
|
|
18
|
-
local_openai2anthropic-0.
|
|
19
|
-
local_openai2anthropic-0.
|
|
15
|
+
local_openai2anthropic-0.3.0.dist-info/METADATA,sha256=e8dCKWpeT7dth6tqLfW7RvRaJxrUUr4DRg3OJDc3MBI,11240
|
|
16
|
+
local_openai2anthropic-0.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
17
|
+
local_openai2anthropic-0.3.0.dist-info/entry_points.txt,sha256=hdc9tSJUNxyNLXcTYye5SuD2K0bEQhxBhGnWTFup6ZM,116
|
|
18
|
+
local_openai2anthropic-0.3.0.dist-info/licenses/LICENSE,sha256=X3_kZy3lJvd_xp8IeyUcIAO2Y367MXZc6aaRx8BYR_s,11369
|
|
19
|
+
local_openai2anthropic-0.3.0.dist-info/RECORD,,
|
|
File without changes
|
{local_openai2anthropic-0.2.8.dist-info → local_openai2anthropic-0.3.0.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{local_openai2anthropic-0.2.8.dist-info → local_openai2anthropic-0.3.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|