| |
| """ |
| 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, StreamingResponse |
| 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. " |
| "You are a reasoning-focused AI assistant. " |
| "Think carefully before answering. " |
| "Provide clear, correct answers with brief explanations when helpful. " |
| ) |
|
|
| else: |
| |
| context = {"files": []} |
| combined_user_prompt = prompt |
|
|
| system_prompt = ( |
| "You are GAKR AI, a reasoning-focused AI assistant. " |
| "Think carefully before answering. " |
| "Provide clear, correct answers with brief explanations when helpful. " |
| ) |
|
|
| |
| try: |
| response_generator = generate_response( |
| user_prompt=combined_user_prompt, |
| system_prompt=system_prompt, |
| max_tokens=32768, |
| stream=True, |
| ) |
| except Exception as gen_err: |
| traceback.print_exc() |
| raise HTTPException( |
| status_code=500, |
| detail=f"Error during model generation: {str(gen_err)}", |
| ) |
|
|
| return StreamingResponse( |
| response_generator, |
| media_type="text/plain", |
| ) |
|
|
| 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) |
|
|