renderfy commited on
Commit
dfda8d0
·
verified ·
1 Parent(s): 67f54a7

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +39 -39
app.py CHANGED
@@ -1,4 +1,7 @@
1
- # app.py — Diag Studio AI Frontend (TR/EN, retries, warm-up, artifacts download, audio preview)
 
 
 
2
  import os
3
  import json
4
  import time
@@ -7,12 +10,12 @@ import streamlit as st
7
  from requests.adapters import HTTPAdapter
8
  from urllib3.util.retry import Retry
9
 
10
- # ---- container-safe defaults to avoid '/.streamlit' permission errors
11
  os.environ.setdefault("HOME", "/tmp")
12
  os.environ.setdefault("STREAMLIT_BROWSER_GATHER_USAGE_STATS", "false")
13
  os.environ.setdefault("STREAMLIT_SERVER_HEADLESS", "true")
14
 
15
- # ================= Theme (no extra files) =================
16
  try:
17
  st._config.set_option("theme.base", "dark")
18
  st._config.set_option("theme.primaryColor", "#ff7a1a")
@@ -26,6 +29,7 @@ except Exception:
26
  def _env(k: str) -> str:
27
  return (os.getenv(k) or "").strip().strip("'\"")
28
 
 
29
  API_BASE = (
30
  _env("CARDIAG_AI_API") or _env("AI_LIGHTBOX_API") or _env("LUXFIT_API")
31
  ).rstrip("/") if (_env("CARDIAG_AI_API") or _env("AI_LIGHTBOX_API") or _env("LUXFIT_API")) else ""
@@ -37,7 +41,7 @@ HEADERS = {"Authorization": f"Bearer {HF_TOKEN}"} if HF_TOKEN else {}
37
  L = {
38
  "en": {
39
  "title": "Diag Studio AI — Engine Sound Diagnosis",
40
- "about": "Upload an engine sound (mp3/wav/ogg) or paste an audio URL. Backend runs fal.ai audio-understanding with a minimal prompt, then GPT-4o maps to labels and summarizes.",
41
  "backend_url": "Backend URL",
42
  "lang": "Language",
43
  "upload": "Upload audio file",
@@ -46,7 +50,7 @@ L = {
46
  "adv": "Advanced",
47
  "prompt": "Override FAL prompt (optional)",
48
  "use_data_uri": "Send as data:URI (safer for private backends)",
49
- "model": "OpenAI model for labeling/commentary",
50
  "analyze": "Analyze",
51
  "reset": "Reset",
52
  "result": "Result",
@@ -60,23 +64,24 @@ L = {
60
  "health_ok": "Backend is reachable.",
61
  "health_fail": "Backend not reachable.",
62
  "upload_hint": "Max ~200 MB per Streamlit; use mp3/wav/ogg.",
63
- "footer": "Tip: You can also paste a public audio URL if you don’t want to upload the file.",
64
  "auto_retry": "Auto-retry on 429/500/502/503/504",
65
  "warmup": "Warm up backend before analysis",
66
  "max_wait": "Max total wait (sec) for retries",
67
  "warming": "Warming up backend…",
68
  "analyzing": "Analyzing…",
69
- "lang_help": "Output language for GPT-4o commentary",
70
- "ok_false": "Request returned a controlled error from backend.",
71
- "try_again": "Backend might be waking up or rate-limited. Try Analyze again or increase ‘Max total wait’.",
72
  "preview": "Audio preview",
73
  "raw_text": "Raw model text",
74
  "prompt_used": "Prompt used (first 240 chars)",
75
- "api_used": "Backend audio URL (truncated)"
 
76
  },
77
  "tr": {
78
  "title": "Diag Studio AI — Motor Sesi Teşhis",
79
- "about": "Motor sesini (mp3/wav/ogg) yükleyin veya bir ses URL’si girin. Backend minimal bir prompt ile fal.ai ‘audio-understanding’ çalıştırır; GPT-4o etiketlere map eder ve özetler.",
80
  "backend_url": "Backend URL",
81
  "lang": "Dil",
82
  "upload": "Ses dosyası yükle",
@@ -85,7 +90,7 @@ L = {
85
  "adv": "Gelişmiş",
86
  "prompt": "FAL prompt’u geçersiz kıl (opsiyonel)",
87
  "use_data_uri": "data:URI olarak gönder (özel backend için güvenli)",
88
- "model": "OpenAI modeli (labeling/yorum)",
89
  "analyze": "Analiz Et",
90
  "reset": "Sıfırla",
91
  "result": "Sonuç",
@@ -98,20 +103,21 @@ L = {
98
  "no_backend": "Bir backend URL girin (örn: http://127.0.0.1:7860)",
99
  "health_ok": "Backend erişilebilir.",
100
  "health_fail": "Backend’e erişilemedi.",
101
- "upload_hint": "Streamlit varsayılan yükleme sınırı ~200 MB; mp3/wav/ogg kullanın.",
102
- "footer": "İpucu: Dosya yüklemek istemezseniz herkese açık bir ses URL’si de girebilirsiniz.",
103
  "auto_retry": "429/500/502/503/504 için otomatik yeniden dene",
104
  "warmup": "Analizden önce backend’i ısıt",
105
  "max_wait": "Retry için azami bekleme (sn)",
106
  "warming": "Backend ısıtılıyor…",
107
  "analyzing": "Analiz ediliyor…",
108
- "lang_help": "GPT-4o yorum çıktısının dili",
109
  "ok_false": "Backend kontrollü bir hata döndürdü.",
110
- "try_again": "Backend uyanıyor veya sınırlandı. Yeniden Dene’ye basın ya da ‘Azami bekleme’ değerini artırın.",
111
  "preview": "Ses önizleme",
112
  "raw_text": "Ham model metni",
113
  "prompt_used": "Kullanılan prompt (ilk 240 karakter)",
114
- "api_used": "Backend ses URL’si (kısaltılmış)"
 
115
  }
116
  }
117
 
@@ -122,7 +128,7 @@ if "last_response" not in st.session_state:
122
  st.session_state.last_response = None
123
 
124
  # ================= Header =================
125
- col1, col2 = st.columns([0.6, 0.4])
126
  with col1:
127
  st.title(L[st.session_state.lang]["title"])
128
  with col2:
@@ -135,6 +141,7 @@ with col2:
135
 
136
  t = L[st.session_state.lang]
137
  st.caption(t["about"])
 
138
 
139
  backend_default = API_BASE or "http://127.0.0.1:7860"
140
  api_base = st.text_input(t["backend_url"], value=backend_default)
@@ -143,10 +150,10 @@ api_base = st.text_input(t["backend_url"], value=backend_default)
143
  with st.expander(t["adv"], expanded=False):
144
  use_data_uri = st.checkbox(t["use_data_uri"], value=True)
145
  openai_model = st.text_input(t["model"], value="gpt-4o")
146
- prompt_override = st.text_area(t["prompt"], value="", height=120)
147
  auto_retry = st.checkbox(t["auto_retry"], value=True)
148
  warmup = st.checkbox(t["warmup"], value=True)
149
- max_wait = st.slider(t["max_wait"], 10, 180, 60, step=10)
150
  st.caption(t["footer"])
151
 
152
  st.markdown("---")
@@ -176,9 +183,7 @@ def build_session(_auto_retry: bool) -> requests.Session:
176
  s.headers.update(HEADERS)
177
  if _auto_retry:
178
  retry = Retry(
179
- total=6,
180
- connect=6,
181
- read=6,
182
  backoff_factor=0.8,
183
  status_forcelist=[429, 500, 502, 503, 504],
184
  allowed_methods=frozenset(["GET", "POST"]),
@@ -215,7 +220,7 @@ def post_analyze(sess: requests.Session, base: str, _file, _audio_url: str, lang
215
  "use_data_uri": "true" if use_data_uri else "false",
216
  "openai_model": openai_model or "gpt-4o",
217
  "lang": lang, # "tr" or "en"
218
- # backend pins detailed_analysis=True internally; no need to send.
219
  }
220
  if prompt_override.strip():
221
  data["prompt"] = prompt_override.strip()
@@ -231,7 +236,8 @@ def post_analyze(sess: requests.Session, base: str, _file, _audio_url: str, lang
231
  return {"ok": False, "error": "No file or URL provided."}
232
 
233
  deadline = time.time() + max_wait
234
- attempts, last_err = 0, None
 
235
  while True:
236
  attempts += 1
237
  try:
@@ -241,12 +247,7 @@ def post_analyze(sess: requests.Session, base: str, _file, _audio_url: str, lang
241
  except Exception as e:
242
  last_err = e
243
  if not auto_retry or time.time() > deadline:
244
- return {
245
- "ok": False,
246
- "error": f"All attempts failed. Last error: {e}",
247
- "attempts": attempts,
248
- "api_base": base,
249
- }
250
  time.sleep(1.2)
251
 
252
  # ================= Actions =================
@@ -256,10 +257,9 @@ if health_btn:
256
  try:
257
  r = sess.get(f"{api_base.rstrip('/')}/health", timeout=10)
258
  st.success(t["health_ok"] if r.ok else t["health_fail"])
259
- if r.ok:
260
  payload = r.json()
261
- if isinstance(payload, dict):
262
- st.code(json.dumps(payload, ensure_ascii=False, indent=2), language="json")
263
  except Exception:
264
  st.error(t["health_fail"])
265
 
@@ -291,19 +291,19 @@ if res:
291
  else:
292
  st.subheader(t["result"])
293
 
294
- # driver-friendly commentary
295
  comm = res.get("commentary") or ""
296
  if comm:
297
  st.markdown(f"**{t['commentary']}**")
298
  st.write(comm)
299
 
300
- # parsed JSON
301
  parsed = res.get("fal_parsed")
302
  if parsed:
303
  st.markdown(f"**{t['json']}**")
304
  st.json(parsed)
305
 
306
- # prompt used and fal audio url
307
  extras_col1, extras_col2 = st.columns(2)
308
  if res.get("prompt_used"):
309
  with extras_col1:
@@ -314,7 +314,7 @@ if res:
314
  st.caption(t["api_used"])
315
  st.code(res["fal_audio_url"], language="text")
316
 
317
- # backend artifacts
318
  st.markdown(f"**{t['backend_artifacts']}**")
319
  dls = res.get("downloads") or {}
320
  col_a, col_b, col_c = st.columns(3)
@@ -341,4 +341,4 @@ if res:
341
  col_c.error("raw.txt ✖")
342
 
343
  # ===== Footer =====
344
- st.caption("© 2025 Diag Studio AI — Streamlit frontend (TR/EN, retries, warm-up, downloads)")
 
1
+ # app.py — Diag Studio AI Frontend
2
+ # Compatible with backend v1.8.0 (FAL open-world cue-rich → GPT-4o hard-discriminator labeling)
3
+ # TR/EN UI • retries • warm-up • audio preview • artifact downloads
4
+
5
  import os
6
  import json
7
  import time
 
10
  from requests.adapters import HTTPAdapter
11
  from urllib3.util.retry import Retry
12
 
13
+ # ---- container-safe defaults
14
  os.environ.setdefault("HOME", "/tmp")
15
  os.environ.setdefault("STREAMLIT_BROWSER_GATHER_USAGE_STATS", "false")
16
  os.environ.setdefault("STREAMLIT_SERVER_HEADLESS", "true")
17
 
18
+ # ================= Theme =================
19
  try:
20
  st._config.set_option("theme.base", "dark")
21
  st._config.set_option("theme.primaryColor", "#ff7a1a")
 
29
  def _env(k: str) -> str:
30
  return (os.getenv(k) or "").strip().strip("'\"")
31
 
32
+ # Allow reusing existing envs from your other apps
33
  API_BASE = (
34
  _env("CARDIAG_AI_API") or _env("AI_LIGHTBOX_API") or _env("LUXFIT_API")
35
  ).rstrip("/") if (_env("CARDIAG_AI_API") or _env("AI_LIGHTBOX_API") or _env("LUXFIT_API")) else ""
 
41
  L = {
42
  "en": {
43
  "title": "Diag Studio AI — Engine Sound Diagnosis",
44
+ "about": "Upload an engine sound (mp3/wav/ogg) or paste an audio URL. Backend extracts cue-rich acoustics with fal.ai, then GPT-4o applies HARD discriminators to avoid collapsing similar timbres.",
45
  "backend_url": "Backend URL",
46
  "lang": "Language",
47
  "upload": "Upload audio file",
 
50
  "adv": "Advanced",
51
  "prompt": "Override FAL prompt (optional)",
52
  "use_data_uri": "Send as data:URI (safer for private backends)",
53
+ "model": "OpenAI model (labeling/commentary)",
54
  "analyze": "Analyze",
55
  "reset": "Reset",
56
  "result": "Result",
 
64
  "health_ok": "Backend is reachable.",
65
  "health_fail": "Backend not reachable.",
66
  "upload_hint": "Max ~200 MB per Streamlit; use mp3/wav/ogg.",
67
+ "footer": "Tip: You can paste a public audio URL instead of uploading.",
68
  "auto_retry": "Auto-retry on 429/500/502/503/504",
69
  "warmup": "Warm up backend before analysis",
70
  "max_wait": "Max total wait (sec) for retries",
71
  "warming": "Warming up backend…",
72
  "analyzing": "Analyzing…",
73
+ "lang_help": "Commentary language",
74
+ "ok_false": "Backend returned a controlled error.",
75
+ "try_again": "Service may be waking or rate-limited. Try again or increase ‘Max total wait’.",
76
  "preview": "Audio preview",
77
  "raw_text": "Raw model text",
78
  "prompt_used": "Prompt used (first 240 chars)",
79
+ "api_used": "Backend audio URL (truncated)",
80
+ "hard_note": "HARD discriminators active: half-crank vs full-crank, load vs RPM, cold-start confinement, tonal vs broadband, deep vs light."
81
  },
82
  "tr": {
83
  "title": "Diag Studio AI — Motor Sesi Teşhis",
84
+ "about": "Motor sesini (mp3/wav/ogg) yükleyin veya bir ses URL’si girin. Backend fal.ai ile ipucu-zengin akustik özet çıkarır; GPT-4o benzer tınıları karıştırmamak için SERT ayraçlar uygular.",
85
  "backend_url": "Backend URL",
86
  "lang": "Dil",
87
  "upload": "Ses dosyası yükle",
 
90
  "adv": "Gelişmiş",
91
  "prompt": "FAL prompt’u geçersiz kıl (opsiyonel)",
92
  "use_data_uri": "data:URI olarak gönder (özel backend için güvenli)",
93
+ "model": "OpenAI modeli (label/yorum)",
94
  "analyze": "Analiz Et",
95
  "reset": "Sıfırla",
96
  "result": "Sonuç",
 
103
  "no_backend": "Bir backend URL girin (örn: http://127.0.0.1:7860)",
104
  "health_ok": "Backend erişilebilir.",
105
  "health_fail": "Backend’e erişilemedi.",
106
+ "upload_hint": "Streamlit yükleme sınırı ~200 MB; mp3/wav/ogg kullanın.",
107
+ "footer": "İpucu: Dosya yüklemek istemezseniz herkese açık bir ses URL’si girebilirsiniz.",
108
  "auto_retry": "429/500/502/503/504 için otomatik yeniden dene",
109
  "warmup": "Analizden önce backend’i ısıt",
110
  "max_wait": "Retry için azami bekleme (sn)",
111
  "warming": "Backend ısıtılıyor…",
112
  "analyzing": "Analiz ediliyor…",
113
+ "lang_help": "Yorum dilini seçin",
114
  "ok_false": "Backend kontrollü bir hata döndürdü.",
115
+ "try_again": "Servis uyanıyor veya sınırlandı. Tekrar deneyin veya ‘Azami bekleme’ süresini artırın.",
116
  "preview": "Ses önizleme",
117
  "raw_text": "Ham model metni",
118
  "prompt_used": "Kullanılan prompt (ilk 240 karakter)",
119
+ "api_used": "Backend ses URL’si (kısaltılmış)",
120
+ "hard_note": "SERT ayraçlar aktif: yarım-krank vs tam-krank, yük vs RPM, soğuk-başlangıç sınırı, tonal vs geniş bant, derin vs hafif."
121
  }
122
  }
123
 
 
128
  st.session_state.last_response = None
129
 
130
  # ================= Header =================
131
+ col1, col2 = st.columns([0.65, 0.35])
132
  with col1:
133
  st.title(L[st.session_state.lang]["title"])
134
  with col2:
 
141
 
142
  t = L[st.session_state.lang]
143
  st.caption(t["about"])
144
+ st.info(t["hard_note"])
145
 
146
  backend_default = API_BASE or "http://127.0.0.1:7860"
147
  api_base = st.text_input(t["backend_url"], value=backend_default)
 
150
  with st.expander(t["adv"], expanded=False):
151
  use_data_uri = st.checkbox(t["use_data_uri"], value=True)
152
  openai_model = st.text_input(t["model"], value="gpt-4o")
153
+ prompt_override = st.text_area(t["prompt"], value="", height=140, help="Leave empty to use backend’s cue-rich default")
154
  auto_retry = st.checkbox(t["auto_retry"], value=True)
155
  warmup = st.checkbox(t["warmup"], value=True)
156
+ max_wait = st.slider(t["max_wait"], 10, 240, 60, step=10)
157
  st.caption(t["footer"])
158
 
159
  st.markdown("---")
 
183
  s.headers.update(HEADERS)
184
  if _auto_retry:
185
  retry = Retry(
186
+ total=6, connect=6, read=6,
 
 
187
  backoff_factor=0.8,
188
  status_forcelist=[429, 500, 502, 503, 504],
189
  allowed_methods=frozenset(["GET", "POST"]),
 
220
  "use_data_uri": "true" if use_data_uri else "false",
221
  "openai_model": openai_model or "gpt-4o",
222
  "lang": lang, # "tr" or "en"
223
+ # backend pins detailed_analysis=True
224
  }
225
  if prompt_override.strip():
226
  data["prompt"] = prompt_override.strip()
 
236
  return {"ok": False, "error": "No file or URL provided."}
237
 
238
  deadline = time.time() + max_wait
239
+ attempts = 0
240
+ last_err = None
241
  while True:
242
  attempts += 1
243
  try:
 
247
  except Exception as e:
248
  last_err = e
249
  if not auto_retry or time.time() > deadline:
250
+ return {"ok": False, "error": f"All attempts failed. Last error: {e}", "attempts": attempts, "api_base": base}
 
 
 
 
 
251
  time.sleep(1.2)
252
 
253
  # ================= Actions =================
 
257
  try:
258
  r = sess.get(f"{api_base.rstrip('/')}/health", timeout=10)
259
  st.success(t["health_ok"] if r.ok else t["health_fail"])
260
+ if r.ok and r.headers.get("content-type","").startswith("application/json"):
261
  payload = r.json()
262
+ st.code(json.dumps(payload, ensure_ascii=False, indent=2), language="json")
 
263
  except Exception:
264
  st.error(t["health_fail"])
265
 
 
291
  else:
292
  st.subheader(t["result"])
293
 
294
+ # Commentary
295
  comm = res.get("commentary") or ""
296
  if comm:
297
  st.markdown(f"**{t['commentary']}**")
298
  st.write(comm)
299
 
300
+ # Parsed JSON
301
  parsed = res.get("fal_parsed")
302
  if parsed:
303
  st.markdown(f"**{t['json']}**")
304
  st.json(parsed)
305
 
306
+ # Prompt used + fal audio url
307
  extras_col1, extras_col2 = st.columns(2)
308
  if res.get("prompt_used"):
309
  with extras_col1:
 
314
  st.caption(t["api_used"])
315
  st.code(res["fal_audio_url"], language="text")
316
 
317
+ # Backend artifacts
318
  st.markdown(f"**{t['backend_artifacts']}**")
319
  dls = res.get("downloads") or {}
320
  col_a, col_b, col_c = st.columns(3)
 
341
  col_c.error("raw.txt ✖")
342
 
343
  # ===== Footer =====
344
+ st.caption("© 2025 Diag Studio AI — Streamlit frontend (TR/EN, retries, warm-up, cue-rich + hard-discriminator flow)")