carlosduplar
Restructure repo: src/, frontend/, modal/ dirs; update app_file paths; update .gitignore
3ab287a Download src/core.py from build-small-hackathon/patient-virtuel-dentiste: direct link, hf CLI and curl.
- Browser
- Download file 4.34 kB
-
https://huggingface.co/spaces/build-small-hackathon/patient-virtuel-dentiste/resolve/main/src/core.py
- Command line
-
hf download hf://spaces/build-small-hackathon/patient-virtuel-dentiste/src/core.py
-
curl -L -o core.py https://huggingface.co/spaces/build-small-hackathon/patient-virtuel-dentiste/resolve/main/src/core.py
4.34 kB
| import logging | |
| import re | |
| import os | |
| import tempfile | |
| import time | |
| log = logging.getLogger(__name__) | |
| from prompts import SYSTEM_PROMPT, PHASE_SWITCH_REMINDER | |
| from parse_feedback import parse_feedback, render_feedback_table, strip_markdown | |
| from stt_engine import transcribe, warmup as stt_warmup | |
| from llm_engine import chat as llm_chat, warmup as llm_warmup | |
| from tts_engine import synthesize | |
| TERMINATE_RE = re.compile( | |
| r"(fin\s+de\s+(la\s+)?séance|session\s+terminée|on\s+a\s+terminé|c'est\s+fini)", | |
| re.IGNORECASE, | |
| ) | |
| def make_initial_state(): | |
| messages = [{"role": "system", "content": SYSTEM_PROMPT}] | |
| return {"messages": messages, "phase": 1, "turn_count": 0} | |
| def _chat_val(state): | |
| return [m for m in state["messages"] if m.get("content") != PHASE_SWITCH_REMINDER] | |
| def _make_audio(audio_bytes): | |
| if not audio_bytes: | |
| return None | |
| f = tempfile.NamedTemporaryFile(suffix=".wav", delete=False, dir=tempfile.gettempdir()) | |
| f.write(audio_bytes) | |
| f.close() | |
| return os.path.basename(f.name) | |
| def _default_feedback(): | |
| """Return a blank feedback result block.""" | |
| return { | |
| "feedback_intro": "", | |
| "feedback_points_forts": [], | |
| "feedback_table": [], | |
| "feedback_vocabulaire": [], | |
| "feedback_priorite": [], | |
| "feedback_bilan": {}, | |
| "feedback_open": False, | |
| } | |
| def process_turn(audio_path, state): | |
| state = dict(state) | |
| result = { | |
| "chat": _chat_val(state), | |
| "audio_file": None, | |
| "state": state, | |
| **_default_feedback(), | |
| "status": "", | |
| } | |
| if not audio_path: | |
| return result | |
| result["status"] = "🎙 Transcription…" | |
| _t0 = time.monotonic() | |
| user_text = transcribe(audio_path) | |
| _t1 = time.monotonic() | |
| if not user_text or len(user_text.strip()) < 2: | |
| result["status"] = "⛔ Parlez plus fort ou plus longtemps." | |
| return result | |
| state["messages"].append({"role": "user", "content": user_text.strip()}) | |
| if TERMINATE_RE.search(user_text): | |
| return _end_session(state) | |
| result["status"] = "🧠 Réflexion…" | |
| response = llm_chat(state["messages"]) | |
| _t2 = time.monotonic() | |
| if not response: | |
| result["status"] = "⛔ Erreur du modèle. Réessayez." | |
| return result | |
| clean = strip_markdown(response) | |
| state["messages"].append({"role": "assistant", "content": clean}) | |
| result["status"] = "🔊 Synthèse vocale…" | |
| audio_bytes = synthesize(clean) | |
| _t3 = time.monotonic() | |
| result["audio_file"] = _make_audio(audio_bytes) | |
| log.info("LATENCY stt=%.1fs llm=%.1fs tts=%.1fs total=%.1fs", | |
| _t1 - _t0, _t2 - _t1, _t3 - _t2, _t3 - _t0) | |
| result["chat"] = _chat_val(state) | |
| result["status"] = "" | |
| return result | |
| def _end_session(state): | |
| state["messages"].append({"role": "user", "content": PHASE_SWITCH_REMINDER}) | |
| result = { | |
| "chat": _chat_val(state), | |
| "audio_file": None, | |
| "state": state, | |
| **_default_feedback(), | |
| "status": "📝 Génération du récapitulatif…", | |
| } | |
| response = llm_chat(state["messages"]) | |
| _t0_base = time.monotonic() | |
| _t0 = _t0_base | |
| if not response: | |
| result["status"] = "⛔ Erreur lors de la génération du bilan." | |
| return result | |
| clean = strip_markdown(response) | |
| state["phase"] = 2 | |
| fb = parse_feedback(clean) | |
| table = render_feedback_table(fb["erreurs"]) if fb["erreurs"] else [] | |
| intro = fb.get("intro") or clean | |
| state["messages"].append({"role": "assistant", "content": intro}) | |
| audio_bytes = synthesize(intro) | |
| _t1 = time.monotonic() | |
| result["chat"] = _chat_val(state) | |
| result["audio_file"] = _make_audio(audio_bytes) | |
| log.info("LATENCY llm=%.1fs tts=%.1fs total=%.1fs", | |
| _t0 - _t0_base, _t1 - _t0, _t1 - _t0_base) | |
| result["feedback_intro"] = intro | |
| result["feedback_points_forts"] = fb["points_forts"] | |
| result["feedback_table"] = table | |
| result["feedback_vocabulaire"] = fb["vocabulaire"] | |
| result["feedback_priorite"] = fb["priorite"] | |
| result["feedback_bilan"] = fb["bilan"] | |
| result["feedback_open"] = True | |
| result["status"] = "" | |
| return result | |
| def end_session_click(state): | |
| return _end_session(dict(state)) | |
| def reset_session(): | |
| stt_warmup() | |
| llm_warmup() | |
| return make_initial_state() | |