текст из pdf распознается, все работает, добавлены все функции из файла работы с базой данных db_cli.py. Готовность к оптимизации интерфейса.

This commit is contained in:
2026-08-11 16:19:07 +03:00
parent 286355e4c0
commit a622fd98d2
13 changed files with 641 additions and 78 deletions
+82 -31
View File
@@ -8,7 +8,7 @@ from typing import List, Dict, Any, Optional
import jwt
from passlib.context import CryptContext
from fastapi import FastAPI, Depends, HTTPException, status
from fastapi import FastAPI, Depends, HTTPException, status, UploadFile, File, Form
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
@@ -16,9 +16,10 @@ from pydantic import BaseModel
from llm.agent import process_chat_message
from llm.db_tools import db_get_tasks, DB_PATH
from llm.file_parser import extract_text_from_file
logging.basicConfig(
level=logging.INFO,
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[logging.StreamHandler()]
)
@@ -65,6 +66,16 @@ app = FastAPI(title="SCUD Orion AI Context API")
if os.path.exists("static"):
app.mount("/static", StaticFiles(directory="static"), name="static")
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
logging.error(f"❌ ОШИБКА ВАЛИДАЦИИ 422 НА {request.url}: {exc.errors()}")
return JSONResponse(
status_code=422,
content={"detail": exc.errors(), "body": str(exc)}
)
class AuthRequest(BaseModel):
username: str
@@ -80,16 +91,42 @@ class ChangePasswordRequest(BaseModel):
old_password: str
new_password: str
class ChatRequest(BaseModel):
session_id: str
message: str
# === API МАРШРУТЫ ===
@app.get("/")
def read_root():
return FileResponse("static/index.html")
@app.get("/favicon.ico")
async def favicon():
file_path = os.path.join("static", "favicon.ico")
if os.path.exists(file_path):
return FileResponse(file_path)
raise HTTPException(status_code=404)
@app.get("/{file_path:path}")
def serve_static_fallback(file_path: str):
clean_path = file_path.lstrip("/")
# Игнорируем сканеры WordPress / PHP
if any(clean_path.startswith(prefix) for prefix in ["wp-", "wordpress", "php", "cms", "shop"]):
raise HTTPException(status_code=404, detail="Not Found")
target = os.path.join("static", clean_path)
if os.path.isfile(target):
return FileResponse(target)
filename = os.path.basename(clean_path)
target_js = os.path.join("static/js", filename)
if filename.endswith(".js") and os.path.isfile(target_js):
return FileResponse(target_js, media_type="application/javascript")
target_css = os.path.join("static/css", filename)
if filename.endswith(".css") and os.path.isfile(target_css):
return FileResponse(target_css, media_type="text/css")
raise HTTPException(status_code=404, detail="File not found")
@app.post("/api/v1/auth/login")
def login(req: AuthRequest):
username = req.username.strip().lower()
@@ -200,34 +237,48 @@ def delete_user(user_id: int, current_user: Dict[str, Any] = Depends(get_current
def get_tasks(user: Dict[str, Any] = Depends(get_current_user)):
return db_get_tasks(user_id=user["id"])
# ЧАТ С ПОДДЕРЖКОЙ ФАЙЛОВ И АВТОРИЗАЦИИ
@app.post("/api/v1/chat")
def chat_endpoint(req: ChatRequest, user: Dict[str, Any] = Depends(get_current_user)):
reply, _ = process_chat_message(user_id=user["id"], user_message=req.message)
return {"reply": reply}
async def chat_endpoint(
session_id: str = Form("web_session_main"),
message: str = Form(""),
file: Optional[UploadFile] = File(default=None),
current_user: dict = Depends(get_current_user)
):
logging.info(f"=== [CHAT API] Входящий запрос от user_id={current_user['id']}, file={file.filename if file else 'None'} ===")
file_content_text = ""
if file and file.filename:
file_bytes = await file.read()
file_content_text = extract_text_from_file(file_bytes, file.filename)
reply, history = process_chat_message(
user_id=current_user["id"],
user_message=message,
file_context=file_content_text,
session_id=session_id
)
return {"reply": reply, "history": history}
# ЕДИНЫЙ ГОСТЕВОЙ ЧАТ (FormData + Файлы)
@app.post("/api/v1/chat/guest")
def guest_chat_endpoint(req: ChatRequest):
payload = {
"model": MODEL_NAME,
"messages": [
{"role": "system", "content": "Ты — полезный ИИ-ассистент. Отвечай на вопросы пользователя четко и по существу."},
{"role": "user", "content": req.message}
],
"stream": False,
"options": {"num_predict": 2048, "temperature": 0.3}
}
try:
req_ollama = urllib.request.Request(
OLLAMA_URL,
data=json.dumps(payload).encode("utf-8"),
headers={"Content-Type": "application/json"}
)
with urllib.request.urlopen(req_ollama) as response:
res_data = json.loads(response.read().decode("utf-8"))
reply = res_data.get("message", {}).get("content", "").strip()
return {"reply": reply}
except Exception as e:
return {"reply": f"Ошибка связи с локальной нейросетью: {e}"}
async def guest_chat_endpoint(
session_id: str = Form("web_session_main"),
message: str = Form(""),
file: Optional[UploadFile] = File(default=None)
):
logging.info(f"=== [GUEST CHAT API] Входящий запрос, file={file.filename if file else 'None'} ===")
file_content_text = ""
if file and file.filename:
file_bytes = await file.read()
file_content_text = extract_text_from_file(file_bytes, file.filename)
reply, history = process_chat_message(
user_id=0,
user_message=message,
file_context=file_content_text,
session_id=session_id
)
return {"reply": reply, "history": history}
# === СТРОГО В КОНЦЕ: ФОЛЛБЭК СТАТИКИ ===