feat: декомпозиция JS на модули (auth, tasks, chat, app) и добавление гостевого режима с локальной нейросетью
This commit is contained in:
@@ -1,166 +1,73 @@
|
||||
# === ANCHOR: IMPORTS_START ===
|
||||
import os
|
||||
import json
|
||||
import sqlite3
|
||||
from typing import List, Optional, Dict, Any
|
||||
|
||||
from fastapi import FastAPI, HTTPException, Depends, Security
|
||||
import urllib.request
|
||||
from typing import List, Dict, Any, Optional
|
||||
from fastapi import FastAPI, Depends, HTTPException, status
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
from fastapi.responses import FileResponse, HTMLResponse
|
||||
from fastapi.responses import FileResponse
|
||||
from pydantic import BaseModel
|
||||
|
||||
from llm.agent import process_chat_message
|
||||
# === ANCHOR: IMPORTS_END ===
|
||||
from llm.db_tools import db_get_tasks
|
||||
|
||||
API_TOKEN = "scud_secret_token_2026"
|
||||
OLLAMA_URL = "http://192.168.11.3:11434/api/chat"
|
||||
MODEL_NAME = "qwen2.5:14b"
|
||||
|
||||
# === ANCHOR: APP_INIT_START ===
|
||||
app = FastAPI(
|
||||
title="SCUD Orion AI Context & Task Tracker API",
|
||||
version="2.0.0",
|
||||
description="REST API локального контекста, памяти и трекинга задач для ИИ-аудитора"
|
||||
)
|
||||
|
||||
DB_NAME = "/home/puh/scud_orion_ai_v2/data/scud_orion_ai.db"
|
||||
SECURITY_TOKEN = os.getenv("API_BEARER_TOKEN", "scud_secret_token_2026")
|
||||
security = HTTPBearer()
|
||||
# === ANCHOR: APP_INIT_END ===
|
||||
|
||||
|
||||
# === ANCHOR: DATABASE_HELPERS_START ===
|
||||
def init_chat_table():
|
||||
"""Создает таблицу истории чата, если она не существует."""
|
||||
conn = sqlite3.connect(DB_NAME)
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("""
|
||||
CREATE TABLE IF NOT EXISTS chat_sessions (
|
||||
session_id TEXT PRIMARY KEY,
|
||||
history_json TEXT NOT NULL,
|
||||
updated_at TEXT DEFAULT CURRENT_TIMESTAMP
|
||||
)
|
||||
""")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
init_chat_table()
|
||||
|
||||
|
||||
def verify_token(credentials: HTTPAuthorizationCredentials = Security(security)):
|
||||
if credentials.credentials != SECURITY_TOKEN:
|
||||
raise HTTPException(status_code=403, detail="Недействительный токен доступа")
|
||||
def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):
|
||||
if credentials.credentials != API_TOKEN:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
detail="Неверный токен доступа",
|
||||
headers={"WWW-Authenticate": "Bearer"},
|
||||
)
|
||||
return credentials.credentials
|
||||
|
||||
app = FastAPI(title="SCUD Orion AI Context API")
|
||||
|
||||
def get_db_connection():
|
||||
conn = sqlite3.connect(DB_NAME)
|
||||
conn.row_factory = sqlite3.Row
|
||||
return conn
|
||||
# === ANCHOR: DATABASE_HELPERS_END ===
|
||||
|
||||
|
||||
# === ANCHOR: SCHEMAS_START ===
|
||||
class TaskSchema(BaseModel):
|
||||
task_id: str
|
||||
module: str
|
||||
title: str
|
||||
status: str
|
||||
priority: str
|
||||
completed_at: Optional[str] = None
|
||||
|
||||
|
||||
class RuleSchema(BaseModel):
|
||||
id: int
|
||||
category: str
|
||||
rule_text: str
|
||||
is_active: int
|
||||
|
||||
|
||||
class ChatRequest(BaseModel):
|
||||
session_id: str = "default"
|
||||
message: str
|
||||
|
||||
|
||||
class ChatResponse(BaseModel):
|
||||
session_id: str
|
||||
reply: str
|
||||
# === ANCHOR: SCHEMAS_END ===
|
||||
|
||||
|
||||
# === ANCHOR: STATIC_ROUTES_START ===
|
||||
# Подключаем папку static для отдачи HTML-интерфейса
|
||||
app.mount("/static", StaticFiles(directory="static"), name="static")
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
class ChatRequest(BaseModel):
|
||||
session_id: str
|
||||
message: str
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return FileResponse("static/index.html")
|
||||
# === ANCHOR: STATIC_ROUTES_END ===
|
||||
|
||||
@app.get("/api/v1/tasks")
|
||||
def get_tasks(token: str = Depends(verify_token)):
|
||||
return db_get_tasks()
|
||||
|
||||
# === ANCHOR: TASKS_ENDPOINTS_START ===
|
||||
@app.get("/api/v1/tasks", response_model=List[TaskSchema], dependencies=[Depends(verify_token)])
|
||||
def get_all_tasks(status: Optional[str] = None):
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
if status:
|
||||
cursor.execute("SELECT * FROM tasks WHERE status = ? ORDER BY task_id", (status.upper(),))
|
||||
else:
|
||||
cursor.execute("SELECT * FROM tasks ORDER BY task_id")
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
return [dict(r) for r in rows]
|
||||
@app.post("/api/v1/chat")
|
||||
def chat_endpoint(req: ChatRequest, token: str = Depends(verify_token)):
|
||||
reply, _ = process_chat_message(req.message)
|
||||
return {"reply": reply}
|
||||
|
||||
|
||||
@app.post("/api/v1/tasks/{task_id}/complete", dependencies=[Depends(verify_token)])
|
||||
def mark_task_completed(task_id: str):
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute(
|
||||
"UPDATE tasks SET status = 'COMPLETED', completed_at = CURRENT_TIMESTAMP WHERE task_id = ?",
|
||||
(task_id.upper(),)
|
||||
)
|
||||
if cursor.rowcount == 0:
|
||||
conn.close()
|
||||
raise HTTPException(status_code=404, detail=f"Задача {task_id} не найдена")
|
||||
conn.commit()
|
||||
conn.close()
|
||||
return {"status": "success", "message": f"Задача {task_id} успешно выполнена"}
|
||||
# === ANCHOR: TASKS_ENDPOINTS_END ===
|
||||
|
||||
|
||||
# === ANCHOR: RULES_AND_CHAT_ENDPOINTS_START ===
|
||||
@app.get("/api/v1/rules", response_model=List[RuleSchema], dependencies=[Depends(verify_token)])
|
||||
def get_active_rules():
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT * FROM architecture_memory WHERE is_active = 1 ORDER BY id")
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
return [dict(r) for r in rows]
|
||||
|
||||
|
||||
@app.post("/api/v1/chat", response_model=ChatResponse, dependencies=[Depends(verify_token)])
|
||||
def chat_endpoint(req: ChatRequest):
|
||||
"""Принимает сообщение, подтягивает историю из БД, выполняет вызовы функций к БД и сохраняет историю."""
|
||||
conn = get_db_connection()
|
||||
cursor = conn.cursor()
|
||||
cursor.execute("SELECT history_json FROM chat_sessions WHERE session_id = ?", (req.session_id,))
|
||||
row = cursor.fetchone()
|
||||
# Эндпоинт для гостевого режима (без авторизации и без привязки к проекту)
|
||||
@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}
|
||||
}
|
||||
|
||||
chat_history = json.loads(row["history_json"]) if row else []
|
||||
|
||||
reply, updated_history = process_chat_message(req.message, chat_history)
|
||||
|
||||
cursor.execute(
|
||||
"INSERT OR REPLACE INTO chat_sessions (session_id, history_json, updated_at) VALUES (?, ?, CURRENT_TIMESTAMP)",
|
||||
(req.session_id, json.dumps(updated_history, ensure_ascii=False))
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return ChatResponse(session_id=req.session_id, reply=reply)
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
def health_check():
|
||||
return {"status": "healthy", "service": "scud_context_api", "version": "2.0.0"}
|
||||
# === ANCHOR: RULES_AND_CHAT_ENDPOINTS_END ===
|
||||
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}"}
|
||||
|
||||
Reference in New Issue
Block a user