Files
2026-07-13 13:22:34 +08:00

69 lines
1.8 KiB
Python

# /// script
# requires-python = ">=3.10"
# dependencies = ["fastapi>=0.115.0,<1", "uvicorn[standard]>=0.30.0,<1"]
# ///
"""Fake OpenAI-compatible server for benchmarking.
Returns synthetic responses after a configurable delay so benchmarks measure
MLflow overhead rather than provider latency.
Run standalone:
uv run fake_server.py
PORT=9200 uv run fake_server.py
Or with multiple workers (as launched by run.py):
uvicorn fake_server:app --workers 8 --port 9137
"""
import asyncio
import os
import time
from typing import Any
import uvicorn
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
DELAY_MS = int(os.environ.get("FAKE_RESPONSE_DELAY_MS", "50"))
class ChatRequest(BaseModel):
model: str = "gpt-4o-mini"
messages: list[dict[str, str]] = Field(min_length=1)
stream: bool = False
temperature: float = 1.0
max_tokens: int = 50
@app.post("/v1/chat/completions")
async def chat_completions(req: ChatRequest) -> dict[str, Any]:
await asyncio.sleep(DELAY_MS / 1000)
return {
"id": "chatcmpl-fake",
"object": "chat.completion",
"created": int(time.time()),
"model": req.model,
"choices": [
{
"index": 0,
"message": {"role": "assistant", "content": "Hello!"},
"finish_reason": "stop",
}
],
"usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15},
}
@app.get("/health")
async def health() -> dict[str, str]:
# Polled by run.py's _wait_for_port to detect when the server is ready.
return {"status": "ok"}
if __name__ == "__main__":
port = int(os.environ.get("PORT", "9137"))
host = os.environ.get("FAKE_SERVER_HOST", "127.0.0.1")
uvicorn.run("fake_server:app", host=host, port=port, log_level="warning")