|
|
|
|
|
""" |
|
|
run.py - FastAPI backend + frontend server for GAKR AI |
|
|
|
|
|
Features: |
|
|
- Serves templates/chat.html at "/" |
|
|
- /api/analyze: |
|
|
- prompt: required (must not be empty) |
|
|
- files: optional list[UploadFile] (0, 1, many; any type) |
|
|
- api_key: required |
|
|
- If files are present β file-analysis mode (different system prompt) |
|
|
- If no files β general assistant mode |
|
|
- Detailed exceptions and logs for easier debugging |
|
|
""" |
|
|
|
|
|
from typing import List, Optional |
|
|
|
|
|
import json |
|
|
import traceback |
|
|
|
|
|
from fastapi import ( |
|
|
FastAPI, |
|
|
HTTPException, |
|
|
Form, |
|
|
UploadFile, |
|
|
File, |
|
|
Request, |
|
|
) |
|
|
from fastapi.middleware.cors import CORSMiddleware |
|
|
from fastapi.responses import HTMLResponse, JSONResponse |
|
|
from fastapi.templating import Jinja2Templates |
|
|
import uvicorn |
|
|
|
|
|
from load_model import init_model |
|
|
from generate import generate_response |
|
|
from file_pipeline import process_files |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app = FastAPI(title="GAKR AI") |
|
|
|
|
|
|
|
|
templates = Jinja2Templates(directory="templates") |
|
|
|
|
|
|
|
|
app.add_middleware( |
|
|
CORSMiddleware, |
|
|
allow_origins=["*"], |
|
|
allow_credentials=True, |
|
|
allow_methods=["*"], |
|
|
allow_headers=["*"], |
|
|
) |
|
|
|
|
|
|
|
|
API_KEY = "gakr-ai-2025-secret" |
|
|
|
|
|
|
|
|
print("π Starting GAKR AI Backend...") |
|
|
try: |
|
|
model, tokenizer = init_model(".") |
|
|
print("β
Model initialized successfully") |
|
|
except Exception as e: |
|
|
print("β Failed to load model at startup:") |
|
|
traceback.print_exc() |
|
|
raise e |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@app.get("/", response_class=HTMLResponse) |
|
|
async def home(request: Request): |
|
|
""" |
|
|
Serve chat.html as homepage. |
|
|
""" |
|
|
try: |
|
|
return templates.TemplateResponse("chat.html", {"request": request}) |
|
|
except Exception as e: |
|
|
|
|
|
traceback.print_exc() |
|
|
raise HTTPException( |
|
|
status_code=500, |
|
|
detail=f"Failed to render chat.html: {str(e)}", |
|
|
) |
|
|
|
|
|
|
|
|
@app.post("/api/analyze") |
|
|
async def analyze_endpoint( |
|
|
prompt: str = Form(...), |
|
|
api_key: str = Form(...), |
|
|
files: Optional[List[UploadFile]] = File(None), |
|
|
): |
|
|
""" |
|
|
Main analysis endpoint. |
|
|
|
|
|
Cases: |
|
|
- prompt only (no files) β general assistant mode |
|
|
- prompt + one/many files β file-analysis mode (uses file context) |
|
|
""" |
|
|
try: |
|
|
|
|
|
if api_key != API_KEY: |
|
|
raise HTTPException(status_code=401, detail="Invalid API key") |
|
|
|
|
|
if prompt is None: |
|
|
raise HTTPException(status_code=400, detail="Prompt is missing") |
|
|
if not prompt.strip(): |
|
|
raise HTTPException(status_code=400, detail="Prompt cannot be empty") |
|
|
|
|
|
files = files or [] |
|
|
|
|
|
|
|
|
if files: |
|
|
|
|
|
try: |
|
|
context = await process_files(files) |
|
|
except Exception as extract_err: |
|
|
traceback.print_exc() |
|
|
raise HTTPException( |
|
|
status_code=500, |
|
|
detail=f"Error while processing uploaded files: {str(extract_err)}", |
|
|
) |
|
|
|
|
|
context_text = json.dumps(context, indent=2, ensure_ascii=False) |
|
|
combined_user_prompt = f""" |
|
|
User question: |
|
|
{prompt} |
|
|
|
|
|
Below is structured information extracted from the user's uploaded files. |
|
|
The extraction was done by automated tools. |
|
|
|
|
|
Your task: |
|
|
1. Answer the user's question as accurately as possible. |
|
|
2. Use the context when it is relevant. |
|
|
3. Highlight important patterns, risks, or opportunities. |
|
|
4. If some information is missing or uncertain, say so honestly. |
|
|
|
|
|
Context: |
|
|
{context_text} |
|
|
""" |
|
|
|
|
|
system_prompt = ( |
|
|
"You are GAKR AI, a careful and honest analysis assistant that works with " |
|
|
"structured summaries of files (tables, PDFs, documents, images, audio, video, etc.). " |
|
|
"You never assume file contents beyond what the provided context states." |
|
|
) |
|
|
|
|
|
else: |
|
|
|
|
|
context = {"files": []} |
|
|
combined_user_prompt = prompt |
|
|
|
|
|
system_prompt = ( |
|
|
"You are GAKR AI, a helpful and honest general-purpose assistant. " |
|
|
"You answer questions, explain concepts, help with reasoning and coding, " |
|
|
"using your knowledge up to 2024. Be clear, concise, and direct." |
|
|
) |
|
|
|
|
|
|
|
|
try: |
|
|
response_text = generate_response( |
|
|
user_prompt=combined_user_prompt, |
|
|
system_prompt=system_prompt, |
|
|
max_tokens=512, |
|
|
temperature=0.2, |
|
|
) |
|
|
except Exception as gen_err: |
|
|
traceback.print_exc() |
|
|
raise HTTPException( |
|
|
status_code=500, |
|
|
detail=f"Error during model generation: {str(gen_err)}", |
|
|
) |
|
|
|
|
|
return JSONResponse( |
|
|
{ |
|
|
"response": response_text, |
|
|
"context": context, |
|
|
"status": "success", |
|
|
} |
|
|
) |
|
|
|
|
|
except HTTPException: |
|
|
|
|
|
raise |
|
|
except Exception as e: |
|
|
|
|
|
traceback.print_exc() |
|
|
raise HTTPException( |
|
|
status_code=500, |
|
|
detail=f"Unexpected backend error: {str(e)}", |
|
|
) |
|
|
|
|
|
|
|
|
@app.get("/health") |
|
|
async def health_check(): |
|
|
""" |
|
|
Simple health check. |
|
|
""" |
|
|
return {"status": "healthy", "model_loaded": True} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
print("\n" + "=" * 60) |
|
|
print("π SERVER & CHAT LOCATION") |
|
|
print("=" * 60) |
|
|
print("π CHAT INTERFACE: http://localhost:8080") |
|
|
print("π± ALTERNATIVE URL: http://127.0.0.1:8080") |
|
|
print("π§ API DOCUMENTATION: http://localhost:8080/docs") |
|
|
print("β
CHAT.HTML SERVED: templates/chat.html") |
|
|
print("π TEMPLATES FOLDER: ./templates/") |
|
|
print("=" * 60 + "\n") |
|
|
|
|
|
uvicorn.run(app, host="0.0.0.0", port=8080) |
|
|
|