diff --git a/app/main.py b/app/main.py index 1218f2c..79066f5 100644 --- a/app/main.py +++ b/app/main.py @@ -149,6 +149,10 @@ def auth_guard(request: Request): require_bearer(request, settings.api_keys) +def anthropic_auth_guard(request: Request): + require_anthropic_key(request, settings.api_keys) + + def metrics_auth_guard(request: Request): require_metrics_access( request, @@ -314,7 +318,7 @@ def _last_user_text(messages: list[dict]) -> str: return "" -@app.get("/v1/models", dependencies=[Depends(auth_guard)]) +@app.get("/v1/models", dependencies=[Depends(anthropic_auth_guard)]) async def v1_models(): p = _require_pool() inst = p.pick() @@ -642,6 +646,22 @@ def _anthropic_stop_reason(completion_tokens: int, max_tokens: int) -> str: return "end_turn" +@app.post("/v1/messages/count_tokens") +async def v1_messages_count_tokens(req: AnthropicMessagesRequest, request: Request): + """Anthropic-compatible token counting endpoint. + + Claude Code may probe this endpoint; return Anthropic-shaped response. + """ + try: + require_anthropic_key(request, settings.api_keys) + except AnthropicAuthError as exc: + return _anthropic_error(exc.status_code, exc.error_type, exc.message) + + messages_dump = anthropic_to_internal_messages(req) + prompt = _messages_to_prompt(messages_dump) + return JSONResponse(content={"input_tokens": estimate_tokens(prompt)}) + + @app.post("/v1/messages") async def v1_messages(req: AnthropicMessagesRequest, request: Request): """Anthropic Messages API compatible endpoint.