renderfy commited on
Commit
e487326
·
verified ·
1 Parent(s): 8c3ccf1

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +22 -21
app.py CHANGED
@@ -1,12 +1,16 @@
1
  # app.py — DiagStudio AI (Streamlit frontend)
2
- # Works with the FastAPI backend you just built (single composite image + JSON mode).
3
- # Configure the backend URL via:
4
- # CARDIAG_AI_API (or AI_LIGHTBOX_API / LUXFIT_API)
5
- # Optional auth header passthrough:
6
- # CARDIAG_AI_TOKEN (or AI_LIGHTBOX_TOKEN / LUXFIT_TOKEN)
 
 
 
7
 
8
  import os, io, json, time, base64, requests, hashlib
9
- # Prevent /.streamlit permission issues on HF Spaces/containers
 
10
  os.environ.setdefault("HOME", "/tmp")
11
  os.environ.setdefault("STREAMLIT_BROWSER_GATHER_USAGE_STATS", "false")
12
  os.environ.setdefault("STREAMLIT_SERVER_HEADLESS", "true")
@@ -24,7 +28,8 @@ except Exception:
24
  pass
25
 
26
  # ================= Env & API =================
27
- def _env(k): 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")
@@ -38,15 +43,15 @@ st.title("DiagStudio AI")
38
  st.caption("Upload a short engine recording and get a clear diagnosis with visuals and next steps.")
39
 
40
  # ================= Helpers =================
41
- @st.cache_data(show_spinner=False, ttl=20)
42
- def _health(url, headers):
43
  try:
44
  r = requests.get(f"{url}/health", headers=headers, timeout=10)
45
  return r.status_code == 200
46
  except Exception:
47
  return False
48
 
49
- def _nice_conf(x):
50
  try:
51
  return min(max(float(x), 0.0), 1.0)
52
  except Exception:
@@ -58,7 +63,6 @@ def _post_analyze(audio_file, rpm_file, sr_target, n_fft, hop, env_band, run_llm
58
  files["audio"] = (audio_file.name, audio_file.getvalue(), audio_file.type or "application/octet-stream")
59
  if rpm_file is not None:
60
  files["rpm_csv"] = (rpm_file.name, rpm_file.getvalue(), "text/csv")
61
-
62
  data = {
63
  "sr_target": str(sr_target),
64
  "n_fft": str(n_fft),
@@ -66,14 +70,13 @@ def _post_analyze(audio_file, rpm_file, sr_target, n_fft, hop, env_band, run_llm
66
  "env_band": env_band,
67
  "run_llm": "true" if run_llm else "false",
68
  "extra_text": notes or "",
69
- # cache-bypass hint for intermediaries (backend already sets no-store)
70
- "nonce": str(time.time())
71
  }
72
  r = requests.post(f"{API_BASE}/analyze", headers=HEADERS, files=files, data=data, timeout=240)
73
  r.raise_for_status()
74
  return r.json()
75
 
76
- def _post_analyze_zip(audio_file, rpm_file, sr_target, n_fft, hop, env_band, run_llm, notes):
77
  files = {}
78
  if audio_file is not None:
79
  files["audio"] = (audio_file.name, audio_file.getvalue(), audio_file.type or "application/octet-stream")
@@ -99,7 +102,7 @@ with st.sidebar:
99
  rpm_up = st.file_uploader("Optional RPM CSV (time_sec,rpm)", type=["csv"])
100
 
101
  st.header("Options")
102
- run_llm = st.toggle("Explain with AI", value=True, help="Adds an easy-to-read diagnosis and next steps.")
103
  user_notes = st.text_area("Context (optional)", placeholder="Vehicle, engine type, driving condition, symptoms…")
104
 
105
  with st.expander("Advanced (defaults are fine)"):
@@ -112,10 +115,10 @@ with st.sidebar:
112
  st.error("Backend URL is not set. Define CARDIAG_AI_API (or AI_LIGHTBOX_API / LUXFIT_API).")
113
  ok = _health(API_BASE, HEADERS) if API_BASE else False
114
  if not ok and API_BASE:
115
- st.warning("Could not reach the backend. Verify your private Space URL.")
116
  analyze_btn = st.button("Analyze now", type="primary", disabled=(audio_up is None or not ok))
117
 
118
- # ================= Clear old result on file change =================
119
  if "last_audio_id" not in st.session_state:
120
  st.session_state["last_audio_id"] = None
121
  if audio_up is not None:
@@ -148,11 +151,10 @@ if res:
148
  params = res.get("params", {})
149
  llm = res.get("llm_report") or {}
150
 
151
- # Model name is known from backend params (JSON mode removes auto-added model field)
152
  model_name = params.get("vision_model", "gpt-4o")
153
  st.caption(f"AI model: **{model_name}**")
154
 
155
- # Show audio hash and basic features to prove input is unique
156
  if params.get("audio_id"):
157
  st.caption(f"Audio ID: **{params['audio_id']}**")
158
  if params.get("features"):
@@ -166,12 +168,11 @@ if res:
166
  except Exception:
167
  st.caption(f"Features — {feat}")
168
 
169
- # If model returned meta, show it (proof it saw the image)
170
  if isinstance(llm, dict) and llm.get("meta"):
171
  meta = llm["meta"]
172
  st.caption(f"Vision meta — seen_images: {meta.get('seen_images')}, views: {meta.get('views')}")
173
 
174
- # Layout
175
  col1, col2 = st.columns([1, 1])
176
  diag_list = llm.get("diagnosis", [])
177
  notes = llm.get("notes", "")
 
1
  # app.py — DiagStudio AI (Streamlit frontend)
2
+ # Pairs with the FastAPI backend you built (composite image + JSON mode).
3
+ # Configure the backend URL via one of:
4
+ # CARDIAG_AI_API (preferred) | AI_LIGHTBOX_API | LUXFIT_API
5
+ # Optional bearer auth passthrough:
6
+ # CARDIAG_AI_TOKEN | AI_LIGHTBOX_TOKEN | LUXFIT_TOKEN
7
+ #
8
+ # Run (example):
9
+ # CARDIAG_AI_API="https://<your-private-backend>" streamlit run app.py --server.port 7860
10
 
11
  import os, io, json, time, base64, requests, hashlib
12
+
13
+ # Avoid /.streamlit permission errors on containers/HF
14
  os.environ.setdefault("HOME", "/tmp")
15
  os.environ.setdefault("STREAMLIT_BROWSER_GATHER_USAGE_STATS", "false")
16
  os.environ.setdefault("STREAMLIT_SERVER_HEADLESS", "true")
 
28
  pass
29
 
30
  # ================= Env & API =================
31
+ def _env(k: str) -> str:
32
+ return (os.getenv(k) or "").strip().strip("'\"")
33
 
34
  API_BASE = (
35
  _env("CARDIAG_AI_API") or _env("AI_LIGHTBOX_API") or _env("LUXFIT_API")
 
43
  st.caption("Upload a short engine recording and get a clear diagnosis with visuals and next steps.")
44
 
45
  # ================= Helpers =================
46
+ @st.cache_data(show_spinner=False, ttl=30)
47
+ def _health(url: str, headers: dict) -> bool:
48
  try:
49
  r = requests.get(f"{url}/health", headers=headers, timeout=10)
50
  return r.status_code == 200
51
  except Exception:
52
  return False
53
 
54
+ def _nice_conf(x) -> float:
55
  try:
56
  return min(max(float(x), 0.0), 1.0)
57
  except Exception:
 
63
  files["audio"] = (audio_file.name, audio_file.getvalue(), audio_file.type or "application/octet-stream")
64
  if rpm_file is not None:
65
  files["rpm_csv"] = (rpm_file.name, rpm_file.getvalue(), "text/csv")
 
66
  data = {
67
  "sr_target": str(sr_target),
68
  "n_fft": str(n_fft),
 
70
  "env_band": env_band,
71
  "run_llm": "true" if run_llm else "false",
72
  "extra_text": notes or "",
73
+ "nonce": str(time.time()) # cache-bypass hint; backend already sets no-store
 
74
  }
75
  r = requests.post(f"{API_BASE}/analyze", headers=HEADERS, files=files, data=data, timeout=240)
76
  r.raise_for_status()
77
  return r.json()
78
 
79
+ def _post_analyze_zip(audio_file, rpm_file, sr_target, n_fft, hop, env_band, run_llm, notes) -> bytes:
80
  files = {}
81
  if audio_file is not None:
82
  files["audio"] = (audio_file.name, audio_file.getvalue(), audio_file.type or "application/octet-stream")
 
102
  rpm_up = st.file_uploader("Optional RPM CSV (time_sec,rpm)", type=["csv"])
103
 
104
  st.header("Options")
105
+ run_llm = st.toggle("Explain with AI", value=True, help="Get a readable diagnosis and next steps.")
106
  user_notes = st.text_area("Context (optional)", placeholder="Vehicle, engine type, driving condition, symptoms…")
107
 
108
  with st.expander("Advanced (defaults are fine)"):
 
115
  st.error("Backend URL is not set. Define CARDIAG_AI_API (or AI_LIGHTBOX_API / LUXFIT_API).")
116
  ok = _health(API_BASE, HEADERS) if API_BASE else False
117
  if not ok and API_BASE:
118
+ st.warning("Could not reach the backend. Check your private Space URL.")
119
  analyze_btn = st.button("Analyze now", type="primary", disabled=(audio_up is None or not ok))
120
 
121
+ # ================= Reset state on file change (prevents stale results) =================
122
  if "last_audio_id" not in st.session_state:
123
  st.session_state["last_audio_id"] = None
124
  if audio_up is not None:
 
151
  params = res.get("params", {})
152
  llm = res.get("llm_report") or {}
153
 
 
154
  model_name = params.get("vision_model", "gpt-4o")
155
  st.caption(f"AI model: **{model_name}**")
156
 
157
+ # Input uniqueness proof + quick features
158
  if params.get("audio_id"):
159
  st.caption(f"Audio ID: **{params['audio_id']}**")
160
  if params.get("features"):
 
168
  except Exception:
169
  st.caption(f"Features — {feat}")
170
 
171
+ # Show meta proving Vision read the image
172
  if isinstance(llm, dict) and llm.get("meta"):
173
  meta = llm["meta"]
174
  st.caption(f"Vision meta — seen_images: {meta.get('seen_images')}, views: {meta.get('views')}")
175
 
 
176
  col1, col2 = st.columns([1, 1])
177
  diag_list = llm.get("diagnosis", [])
178
  notes = llm.get("notes", "")