freellm / api.py
bankrlebhai's picture
Upload 8 files
5803f54 verified
Raw History Blame Contribute Delete
4.24 kB
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse, JSONResponse
from llm_manager import LLMManager
import json
import time
import asyncio
from typing import Optional
app = FastAPI()
llm = None
@app.on_event("startup")
async def startup_event():
global llm
llm = LLMManager()
@app.get("/")
async def root():
return {"status": "running", "model": llm.model_file if llm else "loading"}
@app.post("/api/generate")
async def generate(request: Request):
data = await request.json()
prompt = data.get("prompt")
stream = data.get("stream", True)
model_name = data.get("model", "qwen3")
if not prompt:
return JSONResponse({"error": "Prompt is required"}, status_code=400)
def stream_response():
response = llm.generate(prompt, stream=True)
for chunk in response:
yield json.dumps({
"model": model_name,
"created_at": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
"response": chunk["choices"][0]["text"],
"done": False
}) + "\n"
yield json.dumps({
"model": model_name,
"created_at": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
"done": True,
"context": [], # Placeholder
"total_duration": 0,
"load_duration": 0,
"prompt_eval_count": 0,
"prompt_eval_duration": 0,
"eval_count": 0,
"eval_duration": 0
}) + "\n"
if stream:
return StreamingResponse(stream_response(), media_type="application/x-ndjson")
else:
response = llm.generate(prompt, stream=False)
return {
"model": model_name,
"created_at": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
"response": response["choices"][0]["text"],
"done": True,
"context": [],
"total_duration": 0,
"load_duration": 0,
"prompt_eval_count": 0,
"prompt_eval_duration": 0,
"eval_count": 0,
"eval_duration": 0
}
@app.post("/api/chat")
async def chat(request: Request):
data = await request.json()
messages = data.get("messages", [])
stream = data.get("stream", True)
model_name = data.get("model", "qwen3")
def stream_chat():
response = llm.chat_completion(messages, stream=True)
for chunk in response:
if "choices" in chunk and len(chunk["choices"]) > 0:
delta = chunk["choices"][0].get("delta", {})
content = delta.get("content", "")
yield json.dumps({
"model": model_name,
"created_at": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
"message": {"role": "assistant", "content": content},
"done": False
}) + "\n"
yield json.dumps({
"model": model_name,
"created_at": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
"done": True
}) + "\n"
if stream:
return StreamingResponse(stream_chat(), media_type="application/x-ndjson")
else:
response = llm.chat_completion(messages, stream=False)
return {
"model": model_name,
"created_at": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
"message": response["choices"][0]["message"],
"done": True
}
@app.get("/api/tags")
async def tags():
return {
"models": [
{
"name": "qwen3",
"modified_at": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime()),
"size": 0, # TBD
"digest": "qwen3-digest",
"details": {
"format": "gguf",
"family": "qwen",
"families": ["qwen"],
"parameter_size": "14B",
"quantization_level": "Q4_K_M"
}
}
]
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)