local-openai2anthropic 0.2.3__py3-none-any.whl → 0.2.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.
- local_openai2anthropic/__init__.py +1 -1
- local_openai2anthropic/router.py +97 -0
- {local_openai2anthropic-0.2.3.dist-info → local_openai2anthropic-0.2.4.dist-info}/METADATA +1 -1
- {local_openai2anthropic-0.2.3.dist-info → local_openai2anthropic-0.2.4.dist-info}/RECORD +7 -7
- {local_openai2anthropic-0.2.3.dist-info → local_openai2anthropic-0.2.4.dist-info}/WHEEL +0 -0
- {local_openai2anthropic-0.2.3.dist-info → local_openai2anthropic-0.2.4.dist-info}/entry_points.txt +0 -0
- {local_openai2anthropic-0.2.3.dist-info → local_openai2anthropic-0.2.4.dist-info}/licenses/LICENSE +0 -0
local_openai2anthropic/router.py
CHANGED
|
@@ -786,6 +786,103 @@ async def list_models(
|
|
|
786
786
|
)
|
|
787
787
|
|
|
788
788
|
|
|
789
|
+
@router.post("/v1/messages/count_tokens")
|
|
790
|
+
async def count_tokens(
|
|
791
|
+
request: Request,
|
|
792
|
+
settings: Settings = Depends(get_request_settings),
|
|
793
|
+
) -> JSONResponse:
|
|
794
|
+
"""
|
|
795
|
+
Count tokens in messages without creating a message.
|
|
796
|
+
Uses tiktoken for local token counting.
|
|
797
|
+
"""
|
|
798
|
+
try:
|
|
799
|
+
body_bytes = await request.body()
|
|
800
|
+
body_json = json.loads(body_bytes.decode("utf-8"))
|
|
801
|
+
logger.debug(f"[Count Tokens Request] {json.dumps(body_json, ensure_ascii=False, indent=2)}")
|
|
802
|
+
except json.JSONDecodeError as e:
|
|
803
|
+
error_response = AnthropicErrorResponse(
|
|
804
|
+
error=AnthropicError(type="invalid_request_error", message=f"Invalid JSON: {e}")
|
|
805
|
+
)
|
|
806
|
+
return JSONResponse(status_code=422, content=error_response.model_dump())
|
|
807
|
+
except Exception as e:
|
|
808
|
+
error_response = AnthropicErrorResponse(
|
|
809
|
+
error=AnthropicError(type="invalid_request_error", message=str(e))
|
|
810
|
+
)
|
|
811
|
+
return JSONResponse(status_code=400, content=error_response.model_dump())
|
|
812
|
+
|
|
813
|
+
# Validate required fields
|
|
814
|
+
if not isinstance(body_json, dict):
|
|
815
|
+
error_response = AnthropicErrorResponse(
|
|
816
|
+
error=AnthropicError(type="invalid_request_error", message="Request body must be a JSON object")
|
|
817
|
+
)
|
|
818
|
+
return JSONResponse(status_code=422, content=error_response.model_dump())
|
|
819
|
+
|
|
820
|
+
messages = body_json.get("messages", [])
|
|
821
|
+
if not isinstance(messages, list):
|
|
822
|
+
error_response = AnthropicErrorResponse(
|
|
823
|
+
error=AnthropicError(type="invalid_request_error", message="messages must be a list")
|
|
824
|
+
)
|
|
825
|
+
return JSONResponse(status_code=422, content=error_response.model_dump())
|
|
826
|
+
|
|
827
|
+
model = body_json.get("model", "")
|
|
828
|
+
system = body_json.get("system")
|
|
829
|
+
tools = body_json.get("tools", [])
|
|
830
|
+
|
|
831
|
+
try:
|
|
832
|
+
# Use tiktoken for token counting
|
|
833
|
+
import tiktoken
|
|
834
|
+
|
|
835
|
+
# Map model names to tiktoken encoding
|
|
836
|
+
# Claude models don't have direct tiktoken encodings, so we use cl100k_base as approximation
|
|
837
|
+
encoding = tiktoken.get_encoding("cl100k_base")
|
|
838
|
+
|
|
839
|
+
total_tokens = 0
|
|
840
|
+
|
|
841
|
+
# Count system prompt tokens if present
|
|
842
|
+
if system:
|
|
843
|
+
if isinstance(system, str):
|
|
844
|
+
total_tokens += len(encoding.encode(system))
|
|
845
|
+
elif isinstance(system, list):
|
|
846
|
+
for block in system:
|
|
847
|
+
if isinstance(block, dict) and block.get("type") == "text":
|
|
848
|
+
total_tokens += len(encoding.encode(block.get("text", "")))
|
|
849
|
+
|
|
850
|
+
# Count message tokens
|
|
851
|
+
for msg in messages:
|
|
852
|
+
content = msg.get("content", "")
|
|
853
|
+
if isinstance(content, str):
|
|
854
|
+
total_tokens += len(encoding.encode(content))
|
|
855
|
+
elif isinstance(content, list):
|
|
856
|
+
for block in content:
|
|
857
|
+
if isinstance(block, dict):
|
|
858
|
+
if block.get("type") == "text":
|
|
859
|
+
total_tokens += len(encoding.encode(block.get("text", "")))
|
|
860
|
+
elif block.get("type") == "image":
|
|
861
|
+
# Images are typically counted as a fixed number of tokens
|
|
862
|
+
# This is an approximation
|
|
863
|
+
total_tokens += 85 # Standard approximation for images
|
|
864
|
+
|
|
865
|
+
# Count tool definitions tokens
|
|
866
|
+
if tools:
|
|
867
|
+
for tool in tools:
|
|
868
|
+
tool_def = tool if isinstance(tool, dict) else tool.model_dump()
|
|
869
|
+
# Rough approximation for tool definitions
|
|
870
|
+
total_tokens += len(encoding.encode(json.dumps(tool_def)))
|
|
871
|
+
|
|
872
|
+
logger.debug(f"[Count Tokens Response] input_tokens: {total_tokens}")
|
|
873
|
+
|
|
874
|
+
return JSONResponse(content={
|
|
875
|
+
"input_tokens": total_tokens
|
|
876
|
+
})
|
|
877
|
+
|
|
878
|
+
except Exception as e:
|
|
879
|
+
logger.error(f"Token counting error: {e}")
|
|
880
|
+
error_response = AnthropicErrorResponse(
|
|
881
|
+
error=AnthropicError(type="internal_error", message=f"Failed to count tokens: {str(e)}")
|
|
882
|
+
)
|
|
883
|
+
return JSONResponse(status_code=500, content=error_response.model_dump())
|
|
884
|
+
|
|
885
|
+
|
|
789
886
|
@router.get("/health")
|
|
790
887
|
async def health_check() -> dict[str, str]:
|
|
791
888
|
"""Health check endpoint."""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: local-openai2anthropic
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
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
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
local_openai2anthropic/__init__.py,sha256=
|
|
1
|
+
local_openai2anthropic/__init__.py,sha256=xPWXxEgbns2l2aiZzW0BDbNBkjcfXK-Ee-2ukgjQPKc,1059
|
|
2
2
|
local_openai2anthropic/__main__.py,sha256=K21u5u7FN8-DbO67TT_XDF0neGqJeFrVNkteRauCRQk,179
|
|
3
3
|
local_openai2anthropic/config.py,sha256=bnM7p5htd6rHgLn7Z0Ukmm2jVImLuVjIB5Cnfpf2ClY,1918
|
|
4
4
|
local_openai2anthropic/converter.py,sha256=qp0LPJBTP0uAb_5l9VINZ03RAjmumxdquP6JqWXiZkQ,15779
|
|
@@ -7,13 +7,13 @@ local_openai2anthropic/daemon_runner.py,sha256=rguOH0PgpbjqNsKYei0uCQX8JQOQ1wmtQ
|
|
|
7
7
|
local_openai2anthropic/main.py,sha256=5tdgPel8RSCn1iK0d7hYAmcTM9vYHlepgQujaEXA2ic,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=KDIsckdQLx78z5rmVX8Zhr5zWO9m_qB-BjQbTwWjj0s,40224
|
|
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.2.
|
|
16
|
-
local_openai2anthropic-0.2.
|
|
17
|
-
local_openai2anthropic-0.2.
|
|
18
|
-
local_openai2anthropic-0.2.
|
|
19
|
-
local_openai2anthropic-0.2.
|
|
15
|
+
local_openai2anthropic-0.2.4.dist-info/METADATA,sha256=nWz75h6XmZzWk3BdkMhTZNT0xlUmUSNmx2jgyFONS10,10040
|
|
16
|
+
local_openai2anthropic-0.2.4.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
17
|
+
local_openai2anthropic-0.2.4.dist-info/entry_points.txt,sha256=hdc9tSJUNxyNLXcTYye5SuD2K0bEQhxBhGnWTFup6ZM,116
|
|
18
|
+
local_openai2anthropic-0.2.4.dist-info/licenses/LICENSE,sha256=X3_kZy3lJvd_xp8IeyUcIAO2Y367MXZc6aaRx8BYR_s,11369
|
|
19
|
+
local_openai2anthropic-0.2.4.dist-info/RECORD,,
|
|
File without changes
|
{local_openai2anthropic-0.2.3.dist-info → local_openai2anthropic-0.2.4.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{local_openai2anthropic-0.2.3.dist-info → local_openai2anthropic-0.2.4.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|