renderfy commited on
Commit
bc1e133
·
verified ·
1 Parent(s): 6f9f0c2

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +91 -30
app.py CHANGED
@@ -1,5 +1,6 @@
1
- # app.py — Diag Studio AI Frontend (TR/EN, retries, warm-up, artifacts download)
2
  import os
 
3
  import json
4
  import time
5
  import requests
@@ -48,6 +49,8 @@ L = {
48
  "detailed": "Detailed analysis (always on)",
49
  "use_data_uri": "Send as data:URI (safer for private backends)",
50
  "model": "Commentary model (OpenAI)",
 
 
51
  "analyze": "Analyze",
52
  "reset": "Reset",
53
  "result": "Result",
@@ -69,7 +72,11 @@ L = {
69
  "analyzing": "Analyzing…",
70
  "lang_help": "Output language for GPT-4o commentary",
71
  "ok_false": "Request returned a controlled error from backend.",
72
- "try_again": "Backend might be waking up or rate-limited. Try Analyze again or increase ‘Max total wait’."
 
 
 
 
73
  },
74
  "tr": {
75
  "title": "Diag Studio AI — Motor Sesi Teşhis",
@@ -84,6 +91,8 @@ L = {
84
  "detailed": "Ayrıntılı analiz (her zaman açık)",
85
  "use_data_uri": "data:URI olarak gönder (özel backend için güvenli)",
86
  "model": "Yorum modeli (OpenAI)",
 
 
87
  "analyze": "Analiz Et",
88
  "reset": "Sıfırla",
89
  "result": "Sonuç",
@@ -96,7 +105,7 @@ L = {
96
  "no_backend": "Bir backend URL girin (örn: http://127.0.0.1:7860)",
97
  "health_ok": "Backend erişilebilir.",
98
  "health_fail": "Backend’e erişilemedi.",
99
- "upload_hint": "Streamlit varsayılan yükleme sınırı ~200 MB; mp3/wav/ogg kullanın.",
100
  "footer": "İpucu: Dosya yüklemek istemezseniz herkese açık bir ses URL’si de girebilirsiniz.",
101
  "auto_retry": "429/500/502/503/504 için otomatik yeniden dene",
102
  "warmup": "Analizden önce backend’i ısıt",
@@ -105,7 +114,11 @@ L = {
105
  "analyzing": "Analiz ediliyor…",
106
  "lang_help": "GPT-4o yorum çıktısının dili",
107
  "ok_false": "Backend kontrollü bir hata döndürdü.",
108
- "try_again": "Backend uyanıyor veya sınırlandı. Yeniden Dene’ye basın ya da ‘Azami bekleme’ değerini artırın."
 
 
 
 
109
  }
110
  }
111
 
@@ -114,6 +127,8 @@ if "lang" not in st.session_state:
114
  st.session_state.lang = "tr"
115
  if "last_response" not in st.session_state:
116
  st.session_state.last_response = None
 
 
117
 
118
  # ================= Header =================
119
  col1, col2 = st.columns([0.6, 0.4])
@@ -135,11 +150,12 @@ api_base = st.text_input(t["backend_url"], value=backend_default)
135
 
136
  # ================= Advanced options =================
137
  with st.expander(t["adv"], expanded=False):
138
- # Detailed analysis backend’te zorunlu True; burada yalnızca bilgi amaçlı gösteriyoruz.
139
  st.checkbox(t["detailed"], value=True, disabled=True)
140
  use_data_uri = st.checkbox(t["use_data_uri"], value=True)
141
  openai_model = st.text_input(t["model"], value="gpt-4o")
142
  prompt_override = st.text_area(t["prompt"], value="", height=120)
 
 
143
  auto_retry = st.checkbox(t["auto_retry"], value=True)
144
  warmup = st.checkbox(t["warmup"], value=True)
145
  max_wait = st.slider(t["max_wait"], 10, 180, 60, step=10)
@@ -153,6 +169,19 @@ file = st.file_uploader(t["upload"], type=["mp3", "wav", "ogg"])
153
  st.write(f"**{t['or']}**")
154
  audio_url = st.text_input(t["paste_url"], value="", placeholder="https://...")
155
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  # ================= Controls =================
157
  c1, c2, c3 = st.columns([0.2, 0.2, 0.6])
158
  go = c1.button(f"▶ {t['analyze']}", type="primary")
@@ -198,13 +227,14 @@ def fetch_bytes(sess: requests.Session, base: str, url: str) -> bytes:
198
  r.raise_for_status()
199
  return r.content
200
 
201
- def post_analyze(sess: requests.Session, base: str, _file, _audio_url: str, lang: str) -> dict:
202
  url = f"{base.rstrip('/')}/analyze"
203
  data = {
204
  "detailed_analysis": "true", # backend yine de True’ya sabitliyor
205
  "use_data_uri": "true" if use_data_uri else "false",
206
  "openai_model": openai_model or "gpt-4o",
207
- "lang": lang, # <<< DİL backend’e gidiyor (tr/en)
 
208
  }
209
  if prompt_override.strip():
210
  data["prompt"] = prompt_override.strip()
@@ -225,7 +255,6 @@ def post_analyze(sess: requests.Session, base: str, _file, _audio_url: str, lang
225
  attempts += 1
226
  try:
227
  resp = sess.post(url, data=data, files=files, timeout=180)
228
- # Başarısız statüleri Retry mekanizması yönetir; yine de raise edelim:
229
  resp.raise_for_status()
230
  return resp.json()
231
  except Exception as e:
@@ -266,7 +295,13 @@ if go:
266
  if warmup:
267
  warm_up_backend(sess, api_base, total_budget=min(max_wait, 40))
268
  with st.spinner(t["analyzing"]):
269
- res = post_analyze(sess, api_base, file, audio_url, st.session_state.lang)
 
 
 
 
 
 
270
  st.session_state.last_response = res
271
 
272
  # ================= Results =================
@@ -281,40 +316,66 @@ if res:
281
  st.expander("Debug").code(json.dumps(res["fal_debug"], ensure_ascii=False, indent=2), language="json")
282
  else:
283
  st.subheader(t["result"])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  comm = res.get("commentary") or ""
285
  if comm:
286
  st.markdown(f"**{t['commentary']}**")
287
  st.write(comm)
288
 
289
- parsed = res.get("fal_parsed")
290
  if parsed:
291
  st.markdown(f"**{t['json']}**")
292
  st.json(parsed)
293
 
 
294
  st.markdown(f"**{t['backend_artifacts']}**")
295
  dls = res.get("downloads") or {}
296
  col_a, col_b, col_c = st.columns(3)
297
 
298
- if dls.get("summary_txt"):
299
- try:
300
- data = fetch_bytes(sess, api_base, dls["summary_txt"])
301
- col_a.download_button(label=t["download_summary"], data=data, file_name="summary.txt")
302
- except Exception:
303
- col_a.error("summary.txt ✖")
304
-
305
- if dls.get("fal_parsed_json"):
306
- try:
307
- data = fetch_bytes(sess, api_base, dls["fal_parsed_json"])
308
- col_b.download_button(label=t["download_parsed"], data=data, file_name="parsed.json", mime="application/json")
309
- except Exception:
310
- col_b.error("parsed.json ✖")
311
 
312
- if dls.get("fal_raw_txt"):
313
- try:
314
- data = fetch_bytes(sess, api_base, dls["fal_raw_txt"])
315
- col_c.download_button(label=t["download_raw"], data=data, file_name="raw.txt")
316
- except Exception:
317
- col_c.error("raw.txt ✖")
318
 
319
  # ===== Footer =====
320
- st.caption("© 2025 Diag Studio AI — Streamlit frontend (TR/EN, retries, warm-up)")
 
1
+ # app.py — Diag Studio AI Frontend (TR/EN, retries, warm-up, artifacts, Strict/Full, anomaly gate, adjudication)
2
  import os
3
+ import io
4
  import json
5
  import time
6
  import requests
 
49
  "detailed": "Detailed analysis (always on)",
50
  "use_data_uri": "Send as data:URI (safer for private backends)",
51
  "model": "Commentary model (OpenAI)",
52
+ "mode": "Prompt Mode",
53
+ "mode_help": "Strict groups similar timbres to reduce mislabels; Full uses detailed taxonomy.",
54
  "analyze": "Analyze",
55
  "reset": "Reset",
56
  "result": "Result",
 
72
  "analyzing": "Analyzing…",
73
  "lang_help": "Output language for GPT-4o commentary",
74
  "ok_false": "Request returned a controlled error from backend.",
75
+ "try_again": "Backend might be waking up or rate-limited. Try Analyze again or increase ‘Max total wait’.",
76
+ "gate": "Anomaly Gate",
77
+ "taxonomy": "Taxonomy",
78
+ "applied_rules": "Adjudication rules applied",
79
+ "confusions": "Common confusion pairs",
80
  },
81
  "tr": {
82
  "title": "Diag Studio AI — Motor Sesi Teşhis",
 
91
  "detailed": "Ayrıntılı analiz (her zaman açık)",
92
  "use_data_uri": "data:URI olarak gönder (özel backend için güvenli)",
93
  "model": "Yorum modeli (OpenAI)",
94
+ "mode": "Prompt Modu",
95
+ "mode_help": "Strict benzer tınıları gruplayıp karışmayı azaltır; Full detaylı taksonomi kullanır.",
96
  "analyze": "Analiz Et",
97
  "reset": "Sıfırla",
98
  "result": "Sonuç",
 
105
  "no_backend": "Bir backend URL girin (örn: http://127.0.0.1:7860)",
106
  "health_ok": "Backend erişilebilir.",
107
  "health_fail": "Backend’e erişilemedi.",
108
+ "upload_hint": "Streamlit yükleme sınırı ~200 MB; mp3/wav/ogg kullanın.",
109
  "footer": "İpucu: Dosya yüklemek istemezseniz herkese açık bir ses URL’si de girebilirsiniz.",
110
  "auto_retry": "429/500/502/503/504 için otomatik yeniden dene",
111
  "warmup": "Analizden önce backend’i ısıt",
 
114
  "analyzing": "Analiz ediliyor…",
115
  "lang_help": "GPT-4o yorum çıktısının dili",
116
  "ok_false": "Backend kontrollü bir hata döndürdü.",
117
+ "try_again": "Backend uyanıyor veya sınırlandı. Yeniden Dene’ye basın ya da ‘Azami bekleme’ değerini artırın.",
118
+ "gate": "Arıza Kapısı (Anomaly Gate)",
119
+ "taxonomy": "Taksonomi",
120
+ "applied_rules": "Uygulanan adjudication kuralları",
121
+ "confusions": "Sık karışan çiftler",
122
  }
123
  }
124
 
 
127
  st.session_state.lang = "tr"
128
  if "last_response" not in st.session_state:
129
  st.session_state.last_response = None
130
+ if "audio_mem" not in st.session_state:
131
+ st.session_state.audio_mem = None # keep last uploaded audio bytes for player
132
 
133
  # ================= Header =================
134
  col1, col2 = st.columns([0.6, 0.4])
 
150
 
151
  # ================= Advanced options =================
152
  with st.expander(t["adv"], expanded=False):
 
153
  st.checkbox(t["detailed"], value=True, disabled=True)
154
  use_data_uri = st.checkbox(t["use_data_uri"], value=True)
155
  openai_model = st.text_input(t["model"], value="gpt-4o")
156
  prompt_override = st.text_area(t["prompt"], value="", height=120)
157
+ # NEW: Full/Strict selector
158
+ mode = st.selectbox(t["mode"], ["FULL", "STRICT"], index=0, help=t["mode_help"])
159
  auto_retry = st.checkbox(t["auto_retry"], value=True)
160
  warmup = st.checkbox(t["warmup"], value=True)
161
  max_wait = st.slider(t["max_wait"], 10, 180, 60, step=10)
 
169
  st.write(f"**{t['or']}**")
170
  audio_url = st.text_input(t["paste_url"], value="", placeholder="https://...")
171
 
172
+ # If a file was uploaded, stash bytes for inline audio preview
173
+ if file is not None:
174
+ try:
175
+ b = file.read()
176
+ st.session_state.audio_mem = (b, file.type or "audio/mpeg")
177
+ # Reset file pointer for requests
178
+ file.seek(0)
179
+ except Exception:
180
+ st.session_state.audio_mem = None
181
+
182
+ if st.session_state.audio_mem:
183
+ st.audio(st.session_state.audio_mem[0], format=st.session_state.audio_mem[1])
184
+
185
  # ================= Controls =================
186
  c1, c2, c3 = st.columns([0.2, 0.2, 0.6])
187
  go = c1.button(f"▶ {t['analyze']}", type="primary")
 
227
  r.raise_for_status()
228
  return r.content
229
 
230
+ def post_analyze(sess: requests.Session, base: str, _file, _audio_url: str, lang: str, mode: str) -> dict:
231
  url = f"{base.rstrip('/')}/analyze"
232
  data = {
233
  "detailed_analysis": "true", # backend yine de True’ya sabitliyor
234
  "use_data_uri": "true" if use_data_uri else "false",
235
  "openai_model": openai_model or "gpt-4o",
236
+ "lang": lang, # "tr" | "en"
237
+ "prompt_mode": mode, # "FULL" | "STRICT"
238
  }
239
  if prompt_override.strip():
240
  data["prompt"] = prompt_override.strip()
 
255
  attempts += 1
256
  try:
257
  resp = sess.post(url, data=data, files=files, timeout=180)
 
258
  resp.raise_for_status()
259
  return resp.json()
260
  except Exception as e:
 
295
  if warmup:
296
  warm_up_backend(sess, api_base, total_budget=min(max_wait, 40))
297
  with st.spinner(t["analyzing"]):
298
+ # streamlit's UploadedFile can be used directly in requests; make sure pointer is at 0
299
+ if file is not None:
300
+ try:
301
+ file.seek(0)
302
+ except Exception:
303
+ pass
304
+ res = post_analyze(sess, api_base, file, audio_url, st.session_state.lang, mode)
305
  st.session_state.last_response = res
306
 
307
  # ================= Results =================
 
316
  st.expander("Debug").code(json.dumps(res["fal_debug"], ensure_ascii=False, indent=2), language="json")
317
  else:
318
  st.subheader(t["result"])
319
+
320
+ # Anomaly Gate & Taxonomy quick view
321
+ parsed = res.get("fal_parsed") or {}
322
+ if isinstance(parsed, dict):
323
+ gate = parsed.get("anomaly_gate") or {}
324
+ colg1, colg2, colg3 = st.columns([0.33, 0.33, 0.34])
325
+ with colg1:
326
+ st.markdown(f"**{t['gate']}**")
327
+ is_anom = gate.get("is_anomaly")
328
+ score = gate.get("score")
329
+ ev = gate.get("evidence")
330
+ st.write(f"• is_anomaly: `{is_anom}`")
331
+ st.write(f"• score: `{score}`")
332
+ if ev: st.write(f"• evidence: `{ev}`")
333
+ with colg2:
334
+ st.markdown(f"**{t['taxonomy']}**")
335
+ tax = parsed.get("taxonomy_version") or res.get("taxonomy_version")
336
+ st.write(f"`{tax or '—'}`")
337
+ pmode = res.get("prompt_mode")
338
+ if pmode:
339
+ st.write(f"mode: `{pmode}`")
340
+ with colg3:
341
+ rules = (parsed.get("adjudication") or {}).get("applied_rules") or (res.get("adjudication") or {}).get("applied_rules")
342
+ st.markdown(f"**{t['applied_rules']}**")
343
+ if rules:
344
+ st.code(json.dumps(rules, ensure_ascii=False, indent=2), language="json")
345
+ conf_pairs = (parsed.get("notes") or {}).get("confusion_pairs")
346
+ if conf_pairs:
347
+ st.markdown(f"**{t['confusions']}**")
348
+ st.code(json.dumps(conf_pairs, ensure_ascii=False, indent=2), language="json")
349
+
350
+ # Driver-friendly commentary
351
  comm = res.get("commentary") or ""
352
  if comm:
353
  st.markdown(f"**{t['commentary']}**")
354
  st.write(comm)
355
 
356
+ # Raw parsed JSON
357
  if parsed:
358
  st.markdown(f"**{t['json']}**")
359
  st.json(parsed)
360
 
361
+ # Backend artifacts
362
  st.markdown(f"**{t['backend_artifacts']}**")
363
  dls = res.get("downloads") or {}
364
  col_a, col_b, col_c = st.columns(3)
365
 
366
+ def fetch_and_button(label: str, key: str, file_name: str, column):
367
+ if dls.get(key):
368
+ try:
369
+ data = fetch_bytes(sess, api_base, dls[key])
370
+ column.download_button(label=label, data=data, file_name=file_name)
371
+ except Exception:
372
+ column.error(f"{file_name} ✖")
373
+ else:
374
+ column.write("—")
 
 
 
 
375
 
376
+ fetch_and_button(t["download_summary"], "summary_txt", "summary.txt", col_a)
377
+ fetch_and_button(t["download_parsed"], "fal_parsed_json", "parsed.json", col_b)
378
+ fetch_and_button(t["download_raw"], "fal_raw_txt", "raw.txt", col_c)
 
 
 
379
 
380
  # ===== Footer =====
381
+ st.caption("© 2025 Diag Studio AI — Streamlit frontend (TR/EN, retries, warm-up, Strict/Full, anomaly gate, adjudication)")