Files

149 lines
3.8 KiB
Python

"""
API principale - FastAPI
"""
from pathlib import Path
from fastapi import FastAPI, HTTPException
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import asyncio
import sse_starlette.sse as sse
import config
from backend import wiki_ops, search, ingest, llm
app = FastAPI()
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
class IngestRequest(BaseModel):
source: str
class ChatRequest(BaseModel):
message: str
history: list = []
@app.get("/")
async def root():
return FileResponse("static/index.html")
@app.get("/api/wiki/list")
async def list_wiki():
return wiki_ops.list_wiki_pages(config.WIKI_ROOT)
@app.get("/api/wiki/page/{path:path}")
async def get_page(path: str):
file_path = config.WIKI_ROOT / "wiki" / path
if not file_path.exists():
# Prova con .md
file_path = config.WIKI_ROOT / "wiki" / f"{path}.md"
page_data = wiki_ops.read_page(file_path)
if "error" in page_data:
raise HTTPException(status_code=404, detail=page_data["error"])
return page_data
@app.post("/api/ingest")
async def ingest_source_endpoint(req: IngestRequest):
source_path = config.WIKI_ROOT / "raw" / req.source
if not source_path.exists():
raise HTTPException(status_code=404, detail=f"Source not found: {req.source}")
result = await ingest.ingest_source(source_path)
return result
@app.get("/api/ingest/stream/{source}")
async def ingest_stream(source: str):
source_path = config.WIKI_ROOT / "raw" / source
if not source_path.exists():
raise HTTPException(status_code=404, detail=f"Source not found: {source}")
async def event_generator():
async for line in ingest.ingest_streaming(source_path):
yield {"event": "message", "data": line}
return sse.EventServerResponse(event_generator())
@app.get("/api/search")
async def search_wiki(q: str = ""):
if not q:
return {"results": []}
name_results = search.search_by_name(q)
content_results = search.search_wiki(q)
return {
"by_name": name_results,
"by_content": content_results
}
@app.get("/api/wiki/recent")
async def recent_pages(limit: int = 10):
return search.get_recent_pages(limit)
@app.post("/api/chat")
async def chat(req: ChatRequest):
# Costruisci context dalla wiki
wiki_index = wiki_ops.read_page(config.WIKI_ROOT / "wiki" / "index.md")
overview = wiki_ops.read_page(config.WIKI_ROOT / "wiki" / "overview.md")
system_prompt = f"""Sei un assistente che risponde basandosi sulla wiki.
USA SEMPRE [[pagina]] per citare le fonti.
Wiki Index:
{wiki_index.get('content', '')}
Overview:
{overview.get('content', '')}
Rispondi in italiano. Cita le fonti con [[nome-pagina]]."""
messages = [{"role": "system", "content": system_prompt}]
messages.extend(req.history)
messages.append({"role": "user", "content": req.message})
async def event_generator():
async for token in llm.llm_chat(messages):
yield {"event": "message", "data": token}
return sse.EventServerResponse(event_generator())
@app.get("/api/raw/list")
async def list_raw():
"""Lista i file nella directory raw."""
raw_dir = config.WIKI_ROOT / "raw"
result = {}
for category in ["articles", "papers", "repos", "assets"]:
cat_dir = raw_dir / category
if cat_dir.exists():
files = [f.name for f in cat_dir.iterdir() if f.is_file()]
result[category] = files
else:
result[category] = []
return result
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=config.WEB_PORT)