Ranoosh / noura_server.py
mrwabnalas40's picture
Upload 70 files
a2cbfcc verified
raw
history blame
5.16 kB
# noura_server.py
import os, time, hmac, traceback
from typing import Any, Dict
from flask import Flask, request, jsonify
from dotenv import load_dotenv
import importlib
from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeoutError
# (اختياري) لو عندك هذه الملفات فعلاً:
try:
import analyzer, brain, core, knowledge_search, learner, media_analyzer, memory, self_learning, self_improvement # noqa: F401
except Exception as e:
print("[WARN] helper modules import issue:", repr(e))
# --- بيئة ---
load_dotenv()
API_KEY = (os.getenv("NOURA_API_KEY", "changeme") or "").strip()
MIN_SIM_DEFAULT = float(os.getenv("MIN_SIMILARITY", "0.82"))
ADAPTER_MODULE = os.getenv("SELF_LEARNING_MODULE", "self_learning_adapter")
# --- الأداپتر ---
sl = importlib.import_module(ADAPTER_MODULE)
sl.init({"min_similarity": MIN_SIM_DEFAULT})
# (اختياري) مولّد ردود مخصص
try:
from telegram_listener import generate_reply as custom_generate_reply
except Exception:
custom_generate_reply = None
# --- Flask ---
app = Flask(__name__)
# --- ThreadPool للمهل ---
EXEC = ThreadPoolExecutor(max_workers=4)
def call_with_timeout(fn, *args, timeout=1.8, default=None, label=""):
fut = EXEC.submit(fn, *args)
try:
return fut.result(timeout=timeout)
except FuturesTimeoutError:
print(f"[TIMEOUT] {label or fn.__name__} > {timeout}s")
return default
except Exception as e:
print(f"[ERR] {label or fn.__name__} crashed:", repr(e))
return default
def verify_auth(req) -> bool:
key = (req.headers.get("x-api-key") or "").strip()
ok = hmac.compare_digest(key, API_KEY)
if not ok:
print(f"[AUTH FAIL] header={repr(key)} expected={repr(API_KEY)}")
return ok
@app.route("/health", methods=["GET"])
def health():
return jsonify({"ok": True}), 200
@app.route("/chat", methods=["POST"], endpoint="chat_api")
def chat():
try:
if not verify_auth(request):
return jsonify({"error": "unauthorized"}), 401
t0 = time.time()
data: Dict[str, Any] = request.get_json(force=True) or {}
user = (data.get("user") or "unknown").strip()
text = (data.get("text") or "").strip()
meta = data.get("meta") or {}
chat_id = str(meta.get("peer_id", "unknown"))
ts = int(time.time())
reply = ""
used_memory = False
# 1) استرجاع من الذاكرة (بمهلة)
if text:
cand, score = call_with_timeout(
sl.find_best, chat_id, text,
timeout=1.8, default=(None, 0.0), label="find_best"
) or (None, 0.0)
if cand and score >= MIN_SIM_DEFAULT:
reply, used_memory = cand, True
# 2) مولد مخصص (اختياري)
if not reply and custom_generate_reply:
r = call_with_timeout(custom_generate_reply, text, timeout=1.5, default="", label="custom_generate_reply")
reply = (r or "").strip()
# 3) fallback
if not reply:
low = text.lower()
if any(w in low for w in ["سهم", "اسهم", "stocks", "stock"]):
reply = "تنبيه: لستُ مستشارًا ماليًا. اكتب سؤالك بدقة وسأساعدك بشكل عام."
else:
reply = f"وصلتني رسالتك: «{text}». ماذا تريد بعدها؟"
# 4) حفظ التعلم (لا يمنع الرد حتى لو تأخر)
if text:
_ = call_with_timeout(sl.add_example, chat_id, user, text, reply, meta, timeout=1.8, label="add_example")
src = "memory" if used_memory else "fallback"
print(f"[CHAT] chat_id={chat_id} src={src} dt={(time.time()-t0)*1000:.1f}ms text={text[:60]}")
return jsonify({"reply": reply, "ts": ts, "used_memory": used_memory})
except Exception:
print("[ERR] /chat top-level:\n", traceback.format_exc())
return jsonify({"error": "internal_error"}), 200
@app.route("/admin/learn", methods=["POST"])
def admin_learn_toggle():
if not verify_auth(request): return jsonify({"error":"unauthorized"}), 401
data = request.get_json(force=True) or {}
chat_id = str(data.get("chat_id"))
enabled = bool(data.get("enabled", True))
min_sim = data.get("min_similarity")
ms = MIN_SIM_DEFAULT if min_sim is None else float(min_sim)
try:
sl.set_chat_config(chat_id, learn_enabled=enabled, min_similarity=ms)
except Exception as e:
print("[ERR] set_chat_config:\n", traceback.format_exc())
return jsonify({"ok": True, "learn_enabled": enabled})
@app.route("/admin/stats", methods=["GET"])
def admin_stats():
if not verify_auth(request): return jsonify({"error":"unauthorized"}), 401
chat_id = request.args.get("chat_id")
try:
c = sl.stats(chat_id)
except Exception:
print("[ERR] stats:\n", traceback.format_exc())
c = 0
return jsonify({"ok": True, "count": c})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=9531)