Spaces:
Sleeping
Sleeping
| """Pure FastAPI application factory.""" | |
| from typing import Any | |
| from fastapi import FastAPI, Request | |
| from fastapi.exception_handlers import request_validation_exception_handler | |
| from fastapi.exceptions import RequestValidationError | |
| from fastapi.responses import JSONResponse, Response | |
| from starlette.middleware.base import BaseHTTPMiddleware | |
| from loguru import logger | |
| class ScannerTrapMiddleware(BaseHTTPMiddleware): | |
| async def dispatch(self, request: Request, call_next): | |
| path = request.url.path | |
| if path.startswith("/.env") or path.startswith("/.git") or "wp-" in path: | |
| return Response(content="OK", status_code=200, media_type="text/plain") | |
| return await call_next(request) | |
| from free_claude_code.application.errors import ApplicationError | |
| from free_claude_code.core.anthropic import anthropic_error_payload | |
| from free_claude_code.core.diagnostics import ( | |
| redacted_exception_traceback, | |
| safe_exception_message, | |
| ) | |
| from free_claude_code.core.openai_responses import openai_error_payload | |
| from free_claude_code.core.trace import ( | |
| extract_claude_session_id_from_headers, | |
| trace_event, | |
| ) | |
| from free_claude_code.core.version import package_version | |
| from .admin_cache import AdminNoStoreMiddleware, attach_admin_no_store | |
| from .admin_routes import router as admin_router | |
| from .ports import ApiServices | |
| from .request_errors import ordinary_application_error_response | |
| from .request_ids import ( | |
| RequestCorrelationMiddleware, | |
| attach_request_id_headers, | |
| get_request_id, | |
| ) | |
| from .routes import router | |
| from .validation_log import summarize_request_validation_body | |
| def create_app(services: ApiServices) -> FastAPI: | |
| """Create the HTTP adapter around explicitly supplied runtime services.""" | |
| app = FastAPI(title="Claude Code Proxy", version=package_version()) | |
| app.state.services = services | |
| app.add_middleware(ScannerTrapMiddleware) | |
| app.add_middleware(RequestCorrelationMiddleware) | |
| app.add_middleware(AdminNoStoreMiddleware) | |
| app.include_router(admin_router) | |
| app.include_router(router) | |
| async def validation_error_handler(request: Request, exc: RequestValidationError): | |
| """Log request shape for 422 debugging without content values.""" | |
| body: Any | |
| try: | |
| body = await request.json() | |
| except Exception as error: | |
| body = {"_json_error": type(error).__name__} | |
| message_summary, tool_names = summarize_request_validation_body(body) | |
| trace_event( | |
| stage="ingress", | |
| event="server.request.validation_failed", | |
| source="api", | |
| path=request.url.path, | |
| query=dict(request.query_params), | |
| error_locs=[list(error.get("loc", ())) for error in exc.errors()], | |
| error_types=[str(error.get("type", "")) for error in exc.errors()], | |
| message_summary=message_summary, | |
| tool_names=tool_names, | |
| ) | |
| return await request_validation_exception_handler(request, exc) | |
| async def application_error_handler(request: Request, exc: ApplicationError): | |
| """Serialize defensive application failures in the selected wire protocol.""" | |
| return ordinary_application_error_response( | |
| exc, | |
| wire_api=( | |
| "responses" if request.url.path == "/v1/responses" else "messages" | |
| ), | |
| request_id=get_request_id(request), | |
| ) | |
| async def general_error_handler(request: Request, exc: Exception): | |
| """Handle general errors and return Anthropic format.""" | |
| request_id = get_request_id(request) | |
| claude_sid = extract_claude_session_id_from_headers(request.headers) | |
| settings = services.requests.current_settings() | |
| with logger.contextualize( | |
| http_method=request.method, | |
| http_path=request.url.path, | |
| claude_session_id=claude_sid, | |
| request_id=request_id, | |
| ): | |
| if settings.log_api_error_tracebacks: | |
| logger.error("General Error: {}", safe_exception_message(exc)) | |
| logger.error(redacted_exception_traceback(exc)) | |
| else: | |
| logger.error( | |
| "General Error: path={} method={} exc_type={}", | |
| request.url.path, | |
| request.method, | |
| type(exc).__name__, | |
| ) | |
| message = safe_exception_message(exc) | |
| if request.url.path == "/v1/responses": | |
| content = openai_error_payload(message=message, error_type="api_error") | |
| else: | |
| content = anthropic_error_payload( | |
| error_type="api_error", | |
| message=message, | |
| request_id=request_id, | |
| ) | |
| response = JSONResponse(status_code=500, content=content) | |
| attach_admin_no_store(response, path=request.url.path) | |
| attach_request_id_headers( | |
| response, | |
| request_id=request_id, | |
| path=request.url.path, | |
| ) | |
| return response | |
| return app | |