github-actions[bot] commited on
Commit
d7b38b7
·
1 Parent(s): f092eec

Deploy from Achraf-cyber/hackton-locallang@fd8053796731da45fdee096ea6372897d31048c1

Browse files
Files changed (2) hide show
  1. app/main.py +76 -3
  2. tests/test_api.py +8 -0
app/main.py CHANGED
@@ -12,13 +12,21 @@ from typing import Literal
12
 
13
  from fastapi import FastAPI, File, Form, UploadFile
14
  from fastapi.middleware.cors import CORSMiddleware
 
15
  from fastapi.staticfiles import StaticFiles
16
  from pydantic import BaseModel
17
 
18
  from app.deps import get_settings
19
- from app.services.asr import ASR
20
- from app.services.translator import Translator
21
- from app.services.tts import TTS
 
 
 
 
 
 
 
22
 
23
  logging.basicConfig(level=logging.INFO)
24
  logger = logging.getLogger("model-service")
@@ -76,6 +84,71 @@ class SpeakResponse(BaseModel):
76
  audio_url: str
77
 
78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
  @app.get("/health")
80
  def health() -> dict[str, str]:
81
  return {"status": "ok"}
 
12
 
13
  from fastapi import FastAPI, File, Form, UploadFile
14
  from fastapi.middleware.cors import CORSMiddleware
15
+ from fastapi.responses import HTMLResponse
16
  from fastapi.staticfiles import StaticFiles
17
  from pydantic import BaseModel
18
 
19
  from app.deps import get_settings
20
+ from app.services.asr import (
21
+ HF_API_MODEL_NAME,
22
+ MODEL_NAME as ASR_LOCAL_MODEL_NAME,
23
+ OMNILINGUAL_CTC_MODEL_CARD,
24
+ OMNILINGUAL_LLM_MODEL_CARD,
25
+ OMNILINGUAL_MODEL_CARD,
26
+ ASR,
27
+ )
28
+ from app.services.translator import MODEL_NAME as NLLB_MODEL_NAME, Translator
29
+ from app.services.tts import MMS_TTS_MODEL_NAMES, TTS
30
 
31
  logging.basicConfig(level=logging.INFO)
32
  logger = logging.getLogger("model-service")
 
84
  audio_url: str
85
 
86
 
87
+ _ASR_MODEL_NAMES = {
88
+ "local": ASR_LOCAL_MODEL_NAME,
89
+ "hf_api": HF_API_MODEL_NAME,
90
+ "omnilingual": f"facebook/{OMNILINGUAL_MODEL_CARD}",
91
+ "omnilingual_ctc": f"facebook/{OMNILINGUAL_CTC_MODEL_CARD}",
92
+ "omnilingual_llm": f"facebook/{OMNILINGUAL_LLM_MODEL_CARD}",
93
+ }
94
+
95
+
96
+ def _dashboard_rows() -> list[tuple[str, str, str]]:
97
+ """(composant, modele actif, etat de chargement) pour /."""
98
+ asr_loaded = "chargé" if ASR._instance is not None else "pas encore chargé (lazy)"
99
+ translator_loaded = "chargé" if Translator._instance is not None else "pas encore chargé (lazy)"
100
+ tts_loaded = "chargé" if TTS._instance is not None else "pas encore chargé (lazy)"
101
+
102
+ translation_model = (
103
+ "masakhane/afrimt5_fr_{bam,mos}_news" if settings.TRANSLATION_BACKEND == "afrimt5" else NLLB_MODEL_NAME
104
+ )
105
+ tts_dyu_model = "k2-fsa/OmniVoice" if settings.TTS_BACKEND_DYU == "omnivoice" else MMS_TTS_MODEL_NAMES["dyu"]
106
+
107
+ return [
108
+ ("ASR (dyu/mos/fra)", _ASR_MODEL_NAMES.get(settings.ASR_BACKEND, settings.ASR_BACKEND), asr_loaded),
109
+ ("Traduction", translation_model, translator_loaded),
110
+ ("TTS — dyu", tts_dyu_model, tts_loaded),
111
+ ("TTS — mos", MMS_TTS_MODEL_NAMES["mos"], tts_loaded),
112
+ ]
113
+
114
+
115
+ @app.get("/", response_class=HTMLResponse)
116
+ def dashboard() -> str:
117
+ rows = _dashboard_rows()
118
+ rows_html = "\n".join(
119
+ f"<tr><td>{component}</td><td><code>{model}</code></td><td>{status}</td></tr>"
120
+ for component, model, status in rows
121
+ )
122
+ return f"""<!DOCTYPE html>
123
+ <html lang="fr">
124
+ <head>
125
+ <meta charset="utf-8">
126
+ <title>model-service — état</title>
127
+ <style>
128
+ body {{ font-family: system-ui, sans-serif; background: #0f172a; color: #e2e8f0; padding: 2rem; }}
129
+ h1 {{ font-size: 1.25rem; }}
130
+ table {{ border-collapse: collapse; width: 100%; max-width: 720px; margin-top: 1rem; }}
131
+ td {{ padding: 0.5rem 0.75rem; border-bottom: 1px solid #334155; }}
132
+ td:first-child {{ color: #94a3b8; white-space: nowrap; }}
133
+ code {{ color: #7dd3fc; }}
134
+ .ok {{ color: #4ade80; }}
135
+ </style>
136
+ </head>
137
+ <body>
138
+ <h1>🩺 model-service — <span class="ok">en ligne</span></h1>
139
+ <table>
140
+ <tr><td>Composant</td><td>Modèle actif</td><td>État</td></tr>
141
+ {rows_html}
142
+ </table>
143
+ <p style="color:#64748b; margin-top:1.5rem;">
144
+ Config via variables d'env (ASR_BACKEND / TRANSLATION_BACKEND / TTS_BACKEND_DYU).
145
+ Chaque modèle est chargé au premier appel (singleton paresseux), donc "pas encore chargé"
146
+ juste après un redémarrage est normal.
147
+ </p>
148
+ </body>
149
+ </html>"""
150
+
151
+
152
  @app.get("/health")
153
  def health() -> dict[str, str]:
154
  return {"status": "ok"}
tests/test_api.py CHANGED
@@ -4,6 +4,14 @@ def test_health(client):
4
  assert response.json() == {"status": "ok"}
5
 
6
 
 
 
 
 
 
 
 
 
7
  def test_localize_mocked(client):
8
  response = client.post(
9
  "/localize",
 
4
  assert response.json() == {"status": "ok"}
5
 
6
 
7
+ def test_dashboard(client):
8
+ response = client.get("/")
9
+ assert response.status_code == 200
10
+ assert "text/html" in response.headers["content-type"]
11
+ assert "ASR" in response.text
12
+ assert "Traduction" in response.text
13
+
14
+
15
  def test_localize_mocked(client):
16
  response = client.post(
17
  "/localize",