File size: 7,057 Bytes
e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff e05484b 53a78ff |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# KC Robot AI V4.1 – Cloud Brain Intelligent Assistant
# Flask server: Chat (HF), TTS, STT, Telegram poller, REST API cho ESP32
# Features: bilingual greetings, radar detect, OLED lines, TTS/STT, Telegram, HuggingFace brain
import os, io, time, json, threading, logging, requests
from typing import Optional, List, Tuple
from flask import Flask, request, jsonify, send_file, render_template_string
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("kcrobot.v4.1")
app = Flask(__name__)
# ====== Config from env / Secrets ======
HF_API_TOKEN = os.getenv("HF_API_TOKEN", "")
HF_MODEL = os.getenv("HF_MODEL", "google/flan-t5-large")
HF_TTS_MODEL = os.getenv("HF_TTS_MODEL", "facebook/tts_transformer-en-ljspeech")
HF_STT_MODEL = os.getenv("HF_STT_MODEL", "openai/whisper-small")
TELEGRAM_TOKEN = os.getenv("TELEGRAM_TOKEN", "")
PORT = int(os.getenv("PORT", 7860))
HF_HEADERS = {"Authorization": f"Bearer {HF_API_TOKEN}"} if HF_API_TOKEN else {}
if not HF_API_TOKEN:
logger.warning("⚠️ HF_API_TOKEN not set. Put HF_API_TOKEN in Secrets.")
# ====== In-memory storage ======
CONV: List[Tuple[str, str]] = []
DISPLAY_LINES: List[str] = []
def push_display(line: str, limit=6):
global DISPLAY_LINES
DISPLAY_LINES.append(line)
if len(DISPLAY_LINES) > limit:
DISPLAY_LINES = DISPLAY_LINES[-limit:]
# ====== Hugging Face API helpers ======
def hf_text_generate(prompt: str, model: Optional[str] = None, max_new_tokens: int = 256) -> str:
model = model or HF_MODEL
url = f"https://api-inference.huggingface.co/models/{model}"
payload = {
"inputs": prompt,
"parameters": {"max_new_tokens": max_new_tokens, "temperature": 0.7},
"options": {"wait_for_model": True}
}
r = requests.post(url, headers=HF_HEADERS, json=payload, timeout=120)
if r.status_code != 200:
raise RuntimeError(f"HF text generation failed: {r.status_code}: {r.text}")
data = r.json()
if isinstance(data, list) and len(data) and isinstance(data[0], dict):
return data[0].get("generated_text", "")
if isinstance(data, dict) and "generated_text" in data:
return data["generated_text"]
return str(data)
def hf_tts_get_mp3(text: str, model: Optional[str] = None) -> bytes:
model = model or HF_TTS_MODEL
url = f"https://api-inference.huggingface.co/models/{model}"
payload = {"inputs": text}
headers = dict(HF_HEADERS)
headers["Content-Type"] = "application/json"
r = requests.post(url, headers=headers, json=payload, stream=True, timeout=120)
if r.status_code != 200:
raise RuntimeError(f"HF TTS failed: {r.status_code}: {r.text}")
return r.content
def hf_stt_from_bytes(audio_bytes: bytes, model: Optional[str] = None) -> str:
model = model or HF_STT_MODEL
url = f"https://api-inference.huggingface.co/models/{model}"
headers = dict(HF_HEADERS)
headers["Content-Type"] = "application/octet-stream"
r = requests.post(url, headers=headers, data=audio_bytes, timeout=180)
if r.status_code != 200:
raise RuntimeError(f"HF STT failed: {r.status_code}: {r.text}")
j = r.json()
return j.get("text", str(j))
# ====== API cho ESP32 ======
@app.route("/ask", methods=["POST"])
def api_ask():
data = request.get_json(force=True)
text = data.get("text", "").strip()
lang = data.get("lang", "auto")
if not text:
return jsonify({"error": "no text"}), 400
if lang == "vi":
prompt = "Bạn là trợ lý thông minh, trả lời bằng tiếng Việt:\n" + text
elif lang == "en":
prompt = "You are a helpful assistant. Answer in English:\n" + text
else:
prompt = "Bạn là trợ lý song ngữ Việt-Anh, trả lời theo ngôn ngữ người dùng:\n" + text
try:
ans = hf_text_generate(prompt)
except Exception as e:
return jsonify({"error": str(e)}), 500
CONV.append((text, ans))
push_display("YOU: " + text[:40])
push_display("BOT: " + ans[:40])
return jsonify({"answer": ans})
@app.route("/presence", methods=["POST"])
def api_presence():
data = request.get_json(force=True)
note = data.get("note", "Có người tới gần!")
greeting_vi = f"Xin chào! {note}"
greeting_en = "Hello there! Nice to see you."
combined = f"{greeting_vi}\n{greeting_en}"
CONV.append(("__presence__", combined))
push_display("RADAR: " + note[:40])
if TELEGRAM_TOKEN:
try:
send_telegram_message(f"⚠️ Phát hiện có người: {note}")
except Exception as e:
logger.error("Telegram send failed: %s", e)
return jsonify({"greeting": combined})
@app.route("/display", methods=["GET"])
def api_display():
return jsonify({"lines": DISPLAY_LINES, "conv_len": len(CONV)})
# ====== Web UI (simple) ======
@app.route("/")
def index():
return render_template_string("<h3>🤖 KC Robot AI V4.1 - Cloud Brain Running</h3><p>Bilingual & Smart Connected to ESP32</p>")
# ====== Telegram ======
def send_telegram_message(text: str):
if not TELEGRAM_TOKEN:
return
chat_id = os.getenv("TELEGRAM_CHATID", "")
url = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}/sendMessage"
requests.post(url, json={"chat_id": chat_id, "text": text}, timeout=10)
def telegram_poll_loop():
if not TELEGRAM_TOKEN: return
logger.info("Starting Telegram poller...")
offset = None
base = f"https://api.telegram.org/bot{TELEGRAM_TOKEN}"
while True:
try:
params = {"timeout": 30}
if offset: params["offset"] = offset
r = requests.get(base + "/getUpdates", params=params, timeout=35)
j = r.json()
for u in j.get("result", []):
offset = u["update_id"] + 1
msg = u.get("message", {})
text = msg.get("text", "")
chat_id = msg.get("chat", {}).get("id")
if text.lower().startswith("/ask "):
q = text[5:].strip()
ans = hf_text_generate(q)
requests.post(base + "/sendMessage", json={"chat_id": chat_id, "text": ans})
elif text.lower().startswith("/say "):
t = text[5:].strip()
mp3 = hf_tts_get_mp3(t)
files = {"audio": ("robot.mp3", mp3, "audio/mpeg")}
requests.post(base + "/sendAudio", files=files, data={"chat_id": chat_id})
except Exception as e:
logger.error("TG poll error: %s", e)
time.sleep(3)
def start_background():
if TELEGRAM_TOKEN:
threading.Thread(target=telegram_poll_loop, daemon=True).start()
@app.before_first_request
def _startup():
start_background()
if __name__ == "__main__":
start_background()
logger.info(f"🚀 KC Robot AI V4.1 running on port {PORT}")
print("Xin chào chủ nhân! Em là KC Robot — rất vui được gặp bạn.\nHello master! I’m KC Robot, your smart assistant.")
app.run(host="0.0.0.0", port=PORT)
|