| from fastapi import FastAPI, Request, HTTPException |
| from fastapi.responses import StreamingResponse |
| import httpx |
| import logging |
|
|
| app = FastAPI() |
| OLLAMA_BASE_URL = "http://localhost:11434" |
| client = httpx.AsyncClient(base_url=OLLAMA_BASE_URL) |
|
|
| |
| logging.basicConfig(level=logging.INFO) |
| logger = logging.getLogger("uvicorn") |
|
|
| @app.middleware("http") |
| async def log_requests(request: Request, call_next): |
| logger.info(f"Request: {request.method} {request.url}") |
| response = await call_next(request) |
| return response |
|
|
| @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE"]) |
| async def reverse_proxy(request: Request, path: str): |
| try: |
| |
| url = f"{OLLAMA_BASE_URL}/{path}" |
| |
| |
| headers = { |
| key: value |
| for key, value in request.headers.items() |
| if key.lower() not in ["host", "content-length"] |
| } |
| |
| |
| req = client.build_request( |
| method=request.method, |
| url=url, |
| headers=headers, |
| content=await request.body(), |
| params=request.query_params |
| ) |
| |
| response = await client.send(req, stream=True) |
| |
| |
| if "text/event-stream" in response.headers.get("content-type", ""): |
| return StreamingResponse( |
| response.aiter_bytes(), |
| media_type=response.headers.get("content-type"), |
| headers=dict(response.headers) |
| ) |
| |
| return StreamingResponse( |
| response.aiter_bytes(), |
| media_type=response.headers.get("content-type"), |
| headers=dict(response.headers) |
| ) |
| |
| except httpx.ConnectError: |
| raise HTTPException( |
| status_code=503, |
| detail="Ollama server unavailable" |
| ) |
| except Exception as e: |
| logger.error(f"Proxy error: {str(e)}") |
| raise HTTPException( |
| status_code=500, |
| detail=f"Proxy error: {str(e)}" |
| ) |
|
|
| if __name__ == "__main__": |
| import uvicorn |
| uvicorn.run(app, host="0.0.0.0", port=7860) |