97 lines
3.8 KiB
Python
97 lines
3.8 KiB
Python
"""
|
|
===============================================================================
|
|
FILE: modules/web_api/routers/chat.py
|
|
ROLE: Обработка сообщений веб-чата с поддержкой токенов и гостевого доступа.
|
|
===============================================================================
|
|
"""
|
|
|
|
from fastapi import APIRouter, Header, HTTPException, Request
|
|
from pydantic import BaseModel
|
|
from typing import Optional, List, Dict, Any
|
|
import logging
|
|
|
|
from services.text_reporter import ask_ollama
|
|
from services.knowledge_base import load_knowledge_base
|
|
from core.database import get_connection
|
|
|
|
logger = logging.getLogger("CHAT_API")
|
|
router = APIRouter(prefix="/api/v1", tags=["Chat"])
|
|
|
|
|
|
class ChatMessageRequest(BaseModel):
|
|
message: str
|
|
session_id: Optional[str] = "web_session_main"
|
|
user_id: Optional[int] = 1
|
|
|
|
|
|
def resolve_user_id(authorization: Optional[str] = None, explicit_user_id: Optional[int] = None) -> int:
|
|
"""
|
|
Извлекает ID пользователя из Bearer-токена.
|
|
Если токен не передан или сессия новая — использует user_id=1 по умолчанию,
|
|
не блокируя работу ошибкой 403 Forbidden.
|
|
"""
|
|
if explicit_user_id and explicit_user_id > 0:
|
|
return explicit_user_id
|
|
|
|
if authorization and authorization.startswith("Bearer "):
|
|
token = authorization.replace("Bearer ", "").strip()
|
|
# Если используется простой токен вида 'user_1' или JWT
|
|
if token.isdigit():
|
|
return int(token)
|
|
elif token.startswith("dev_token_"):
|
|
try:
|
|
return int(token.replace("dev_token_", ""))
|
|
except ValueError:
|
|
pass
|
|
|
|
# Дефолтный пользователь (гостевой / основной аккаунт)
|
|
return 1
|
|
|
|
|
|
@router.post("/chat")
|
|
async def chat_endpoint(payload: ChatMessageRequest, authorization: Optional[str] = Header(None)):
|
|
user_id = resolve_user_id(authorization, payload.user_id)
|
|
session_id = payload.session_id or "web_session_main"
|
|
user_msg = payload.message.strip()
|
|
|
|
if not user_msg:
|
|
raise HTTPException(status_code=400, detail="Пустое сообщение")
|
|
|
|
logger.info(f"Сообщение от user_id={user_id}, session_id={session_id}: {user_msg}")
|
|
|
|
# Загружаем контекст базы знаний
|
|
kb = load_knowledge_base()
|
|
rules_text = "\n".join([f"- {r}" for r in kb.get("rules", [])])
|
|
|
|
system_prompt = (
|
|
"Ты — ИИ-ассистент системы кадровой безопасности и контроллинга СКУД Orion AI.\n"
|
|
"Отвечай четко, профессионально и на русском языке.\n"
|
|
f"Актуальные правила системы:\n{rules_text}"
|
|
)
|
|
|
|
try:
|
|
reply_text = ask_ollama(user_msg, system_prompt=system_prompt)
|
|
|
|
# Сохранение истории в SQLite при необходимости
|
|
try:
|
|
with get_connection() as conn:
|
|
conn.execute(
|
|
"INSERT INTO chat_messages (session_id, role, content) VALUES (?, ?, ?), (?, ?, ?)",
|
|
(session_id, "user", user_msg, session_id, "assistant", reply_text)
|
|
)
|
|
conn.commit()
|
|
except Exception:
|
|
pass
|
|
|
|
return {
|
|
"status": "success",
|
|
"user_id": user_id,
|
|
"session_id": session_id,
|
|
"response": reply_text
|
|
}
|
|
except Exception as e:
|
|
logger.error(f"Ошибка вызова нейросети: {e}")
|
|
return {
|
|
"status": "error",
|
|
"response": f"⚠️ Ошибка обработки запроса: {str(e)}"
|
|
} |