refactor: разбил routes/db/protocol на отдельные модули
This commit is contained in:
+5
-119
@@ -1,12 +1,14 @@
|
||||
"""Эндпоинты для толстого клиента: скрипты и батчевая загрузка.
|
||||
"""Эндпоинты: скрипт, загрузка сессии, чат.
|
||||
|
||||
GET /api/v1/script — выдача скрипта диагностики
|
||||
POST /api/v1/session/upload — приём батча, LLM-анализ, возврат диагноза
|
||||
POST /api/v1/session/upload — приём батча + LLM
|
||||
POST /api/v1/chat — свободный вопрос к LLM
|
||||
|
||||
См. также: api/dtc.py (DTC), api/ping.py (ping)
|
||||
"""
|
||||
|
||||
import logging
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from flask import jsonify, request
|
||||
|
||||
@@ -19,9 +21,6 @@ from brain.prompts import SYSTEM_PROMPT
|
||||
|
||||
logger = logging.getLogger("elmer.script")
|
||||
|
||||
# Кэш для /ping-llm (60 секунд)
|
||||
_ping_llm_cache: dict = {}
|
||||
|
||||
|
||||
def _build_diagnosis_prompt(data: dict, car_info: str = "") -> str:
|
||||
"""Строит промпт для LLM из распарсенных данных."""
|
||||
@@ -65,20 +64,6 @@ def _build_diagnosis_prompt(data: dict, car_info: str = "") -> str:
|
||||
# Загрузка справочника DTC
|
||||
_DTC_CODES: dict[str, str] = {}
|
||||
|
||||
def _load_dtc_dict():
|
||||
global _DTC_CODES
|
||||
if _DTC_CODES:
|
||||
return _DTC_CODES
|
||||
path = Path(__file__).parent.parent / "doc" / "dtc_codes.txt"
|
||||
if path.exists():
|
||||
for line in path.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if "=" in line and not line.startswith("#"):
|
||||
code, desc = line.split("=", 1)
|
||||
_DTC_CODES[code.strip()] = desc.strip()
|
||||
return _DTC_CODES
|
||||
|
||||
|
||||
def register(app):
|
||||
"""Регистрирует эндпоинты скриптов на Flask-приложении."""
|
||||
|
||||
@@ -209,105 +194,6 @@ def register(app):
|
||||
|
||||
return jsonify({"answer": answer})
|
||||
|
||||
@app.route("/api/v1/ping", methods=["GET"])
|
||||
def ping():
|
||||
"""Быстрая проверка доступности сервера."""
|
||||
return {"ok": True}
|
||||
|
||||
@app.route("/api/v1/ping-llm", methods=["GET"])
|
||||
def ping_llm():
|
||||
"""Проверка доступности LLM с адаптивным кэшем.
|
||||
|
||||
- Успех → кэш 60с (нет смысла переспрашивать)
|
||||
- Ошибка → кэш 7с (возможно LLM уже ожил)
|
||||
"""
|
||||
global _ping_llm_cache
|
||||
now = time.time()
|
||||
if _ping_llm_cache:
|
||||
ttl = _ping_llm_cache.get("ttl", 7)
|
||||
if (now - _ping_llm_cache.get("ts", 0)) < ttl:
|
||||
return jsonify(_ping_llm_cache["data"])
|
||||
|
||||
cfg = load()
|
||||
api_key = cfg["llm"]["api_key"]
|
||||
if not api_key:
|
||||
result = {"ok": False, "error": "no API key"}
|
||||
else:
|
||||
t0 = time.time()
|
||||
try:
|
||||
diagnoser = Diagnoser(
|
||||
api_key=api_key,
|
||||
model=cfg["llm"].get("model", "gpt-oss-120b"),
|
||||
base_url=cfg["llm"].get("base_url", "https://api.aillm.ru/v1"),
|
||||
)
|
||||
diagnoser.diagnose("Отвечай одним словом.", "OK")
|
||||
ms = int((time.time() - t0) * 1000)
|
||||
result = {"ok": True, "ms": ms}
|
||||
except Exception as e:
|
||||
ms = int((time.time() - t0) * 1000)
|
||||
result = {"ok": False, "ms": ms, "error": "LLM unavailable"}
|
||||
|
||||
ttl = 60 if result.get("ok") else 7
|
||||
_ping_llm_cache = {"ts": now, "data": result, "ttl": ttl}
|
||||
return jsonify(result)
|
||||
|
||||
@app.route("/api/v1/dtc/decode", methods=["POST"])
|
||||
def dtc_decode():
|
||||
"""Расшифровка кодов DTC."""
|
||||
data = request.get_json(silent=True) or {}
|
||||
codes = data.get("codes", [])
|
||||
if not codes:
|
||||
return jsonify({"codes": []})
|
||||
dtc = _load_dtc_dict()
|
||||
decoded = []
|
||||
for c in codes:
|
||||
c = c.strip().upper()
|
||||
decoded.append({"code": c, "desc": dtc.get(c, c)})
|
||||
return jsonify({"codes": decoded})
|
||||
|
||||
@app.route("/api/v1/dtc/upload", methods=["POST"])
|
||||
def dtc_upload():
|
||||
"""Приём кодов ошибок без полной диагностики."""
|
||||
data = request.get_json(silent=True)
|
||||
if not data:
|
||||
return jsonify({"error": "empty"}), 400
|
||||
codes = data.get("dtc_codes", [])
|
||||
client_info = data.get("client_info", {})
|
||||
client_info["client_ip"] = request.remote_addr
|
||||
|
||||
# Идемпотентность
|
||||
request_id = (data.get("request_id") or "").strip()
|
||||
if request_id:
|
||||
with Database() as db:
|
||||
cached = db.get_cached_response(request_id)
|
||||
if cached is not None:
|
||||
return jsonify(cached), 200
|
||||
|
||||
try:
|
||||
with Database() as db:
|
||||
db.save_dtc_scan(client_info, codes)
|
||||
except Exception as e:
|
||||
logger.error(f"DTC upload failed: {e}")
|
||||
|
||||
dtc = _load_dtc_dict()
|
||||
decoded = [{"code": c, "desc": dtc.get(c.strip().upper(), c)} for c in codes]
|
||||
response = {"codes": decoded, "count": len(decoded)}
|
||||
|
||||
# Сохраняем кэш для идемпотентности
|
||||
if request_id:
|
||||
try:
|
||||
with Database() as db:
|
||||
db.save_session(
|
||||
client_info=client_info,
|
||||
responses=[{"decoded": f"DTC stored: {c}"} for c in codes],
|
||||
request_id=request_id,
|
||||
response_json=response,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"DTC idempotency cache failed: {e}")
|
||||
|
||||
return jsonify(response)
|
||||
|
||||
|
||||
def _summary(p: dict) -> dict:
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user