carlosduplar
Restructure repo: src/, frontend/, modal/ dirs; update app_file paths; update .gitignore
3ab287a
Raw History Blame Contribute Delete
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()