Spaces:
Sleeping
Sleeping
File size: 14,790 Bytes
2baa302 8c096ca dfda8d0 5292b4a 8c096ca 5292b4a 5382abd 5292b4a 1c0f876 831e1da e487326 8c096ca f7d17d1 a3b116a f7d17d1 8c096ca e31837d 8c096ca e487326 a3b116a 2baa302 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 2a0f99d 2baa302 2a0f99d 2baa302 2a0f99d 8c096ca 2a0f99d 2baa302 8c096ca 2baa302 234d1ce 2baa302 8c096ca 2baa302 8c096ca 2baa302 2a0f99d 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2a0f99d 2baa302 2a0f99d 2baa302 8c096ca 2baa302 5292b4a 6f9f0c2 8c096ca 2baa302 bc1e133 8c096ca 2baa302 831e1da 2baa302 831e1da dfda8d0 831e1da 8c096ca 831e1da 8c096ca 5382abd 6f9f0c2 8c096ca 6f9f0c2 8c096ca 831e1da 2baa302 5292b4a 2baa302 5292b4a 8c096ca 5292b4a 8c096ca 2baa302 2a0f99d 6f9f0c2 2baa302 6f9f0c2 2baa302 8c096ca 6f9f0c2 8c096ca 6f9f0c2 8c096ca 6f9f0c2 8c096ca 6f9f0c2 8c096ca 2baa302 6f9f0c2 2baa302 234d1ce 2baa302 8c096ca 6f9f0c2 2baa302 6f9f0c2 2baa302 6f9f0c2 2a0f99d 8c096ca 2a0f99d 2baa302 2a0f99d 2baa302 2a0f99d 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 e31837d 831e1da 2baa302 6f9f0c2 2baa302 6f9f0c2 831e1da 2baa302 81b698e 8c096ca 2baa302 831e1da 2baa302 831e1da 2a0f99d 8c096ca 1c2f173 831e1da 2baa302 831e1da 2a0f99d 8c096ca 831e1da 2baa302 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 8c096ca 2baa302 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | # app.py — Diag Studio (Customer UI)
# AI Destekli Araç Motor Ses Analizi — sade ticari arayüz
# İstekler: Ses oynatıcı, ilerleme çubuğu, kullanıcı dostu akış.
# Teknik sağlayıcı/kitaplık adları ve loglar UI’da gösterilmez.
# Yönetici ayarları yalnızca DEV_MODE=1 iken görünür.
import os
import io
import json
import time
import requests
import streamlit as st
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# ---------------- Safe defaults ----------------
os.environ.setdefault("HOME", "/tmp")
os.environ.setdefault("STREAMLIT_BROWSER_GATHER_USAGE_STATS", "false")
os.environ.setdefault("STREAMLIT_SERVER_HEADLESS", "true")
# ---------------- Theme ----------------
try:
st._config.set_option("theme.base", "dark")
st._config.set_option("theme.primaryColor", "#ff7a1a")
st._config.set_option("theme.backgroundColor", "#0e0f12")
st._config.set_option("theme.secondaryBackgroundColor", "#171a1f")
st._config.set_option("theme.textColor", "#e6e8ea")
except Exception:
pass
# ---------------- Env helpers ----------------
def _env(k: str) -> str:
return (os.getenv(k) or "").strip().strip("'\"")
DEV_MODE = (_env("DEV_MODE") in {"1", "true", "yes", "on"})
API_BASE_DEFAULT = (_env("CARDIAG_AI_API") or _env("APP_API") or "http://127.0.0.1:7860").rstrip("/")
TOKEN = _env("CARDIAG_AI_TOKEN") or _env("APP_TOKEN")
HEADERS = {"Authorization": f"Bearer {TOKEN}"} if TOKEN else {}
# ---------------- i18n ----------------
LANGS = {
"tr": {
"title": "AI Destekli Araç Motor Ses Analizi",
"about": "Motor ses kaydını yükleyin veya bağlantı girin. Sistem sesi analiz eder, olası arızaları ve önerileri özetler.",
"upload": "Ses dosyası yükle",
"or": "veya",
"paste_url": "Ses bağlantısı (opsiyonel)",
"analyze": "Analiz Et",
"reset": "Sıfırla",
"preview": "Önizleme",
"result": "Sonuç",
"commentary": "Özet ve Öneriler",
"json": "Detaylı Bulgular",
"artifacts": "Çıktılar",
"download_summary": "Özet indir",
"download_parsed": "Detay indir (JSON)",
"download_raw": "Ham metin indir",
"upload_hint": "mp3 / wav / ogg önerilir. İsterseniz herkese açık bir bağlantı da girebilirsiniz.",
"warming": "Hazırlanıyor…",
"analyzing": "Analiz ediliyor…",
"health_ok": "Sistem hazır.",
"health_fail": "Sisteme erişilemedi.",
"need_backend": "Sistem adresi yapılandırılmadı.",
"need_input": "Lütfen bir dosya yükleyin veya bağlantı girin.",
"error_generic": "İşlem başarısız. Lütfen tekrar deneyin.",
"try_again": "Yoğunluk olabilir. Biraz sonra tekrar deneyin.",
"timeline_title": "İşlem Durumu",
"step_prepare": "Hazırlık",
"step_upload": "Yükleme",
"step_process": "Analiz",
"step_summarize": "Özet oluşturma",
"admin": "Yönetici",
"admin_desc": "Bu bölüm yalnızca yetkili kullanım içindir.",
"backend_url": "Sistem adresi",
"use_data_uri": "Gizli yükleme",
"warmup": "Ön ısıtma",
"auto_retry": "Otomatik yeniden dene",
"max_wait": "Azami bekleme (sn)",
"show_debug": "Hata ayıklama bilgileri",
},
"en": {
"title": "AI-Powered Engine Sound Analysis",
"about": "Upload an engine sound or provide a public URL. The system analyzes the audio and summarizes likely issues and next steps.",
"upload": "Upload audio file",
"or": "or",
"paste_url": "Audio URL (optional)",
"analyze": "Analyze",
"reset": "Reset",
"preview": "Preview",
"result": "Result",
"commentary": "Summary & Recommendations",
"json": "Detailed Findings",
"artifacts": "Downloads",
"download_summary": "Download summary",
"download_parsed": "Download details (JSON)",
"download_raw": "Download raw text",
"upload_hint": "mp3 / wav / ogg recommended. You may paste a public URL instead of uploading.",
"warming": "Preparing…",
"analyzing": "Analyzing…",
"health_ok": "Service is ready.",
"health_fail": "Cannot reach the service.",
"need_backend": "Service URL is not configured.",
"need_input": "Please upload a file or enter a URL.",
"error_generic": "Request failed. Please try again.",
"try_again": "Service may be busy. Try again shortly.",
"timeline_title": "Progress",
"step_prepare": "Preparing",
"step_upload": "Uploading",
"step_process": "Processing",
"step_summarize": "Summarizing",
"admin": "Admin",
"admin_desc": "This section is for authorized use only.",
"backend_url": "Service URL",
"use_data_uri": "Private upload",
"warmup": "Warm up",
"auto_retry": "Auto-retry",
"max_wait": "Max wait (sec)",
"show_debug": "Show debug info",
},
}
# ---------------- Session ----------------
if "lang" not in st.session_state: st.session_state.lang = "tr"
if "api_base" not in st.session_state: st.session_state.api_base = API_BASE_DEFAULT
if "use_data_uri" not in st.session_state: st.session_state.use_data_uri = True
if "warmup" not in st.session_state: st.session_state.warmup = True
if "auto_retry" not in st.session_state: st.session_state.auto_retry = True
if "max_wait" not in st.session_state: st.session_state.max_wait = 60
if "show_debug" not in st.session_state: st.session_state.show_debug = False
if "prompt_override" not in st.session_state: st.session_state.prompt_override = ""
if "res" not in st.session_state: st.session_state.res = None
T = LANGS[st.session_state.lang]
# ---------------- Header ----------------
left, right = st.columns([0.7, 0.3])
with left:
st.title(T["title"])
with right:
st.session_state.lang = st.selectbox(
"Language / Dil", ["tr", "en"],
index=(0 if st.session_state.lang == "tr" else 1),
)
T = LANGS[st.session_state.lang]
st.caption(T["about"])
# ---------------- Admin (DEV only) ----------------
if DEV_MODE:
with st.expander(f"🛠 {T['admin']}", expanded=False):
st.write(T["admin_desc"])
st.session_state.api_base = st.text_input(T["backend_url"], value=st.session_state.api_base)
st.session_state.use_data_uri = st.checkbox(T["use_data_uri"], value=st.session_state.use_data_uri)
st.session_state.warmup = st.checkbox(T["warmup"], value=st.session_state.warmup)
st.session_state.auto_retry = st.checkbox(T["auto_retry"], value=st.session_state.auto_retry)
st.session_state.max_wait = st.slider(T["max_wait"], 10, 240, st.session_state.max_wait, step=10)
st.session_state.show_debug = st.checkbox(T["show_debug"], value=st.session_state.show_debug)
st.session_state.prompt_override = st.text_area("Advanced prompt (optional)", value=st.session_state.prompt_override, height=120)
st.markdown("---")
# ---------------- Upload / URL ----------------
st.write(T["upload_hint"])
file = st.file_uploader(T["upload"], type=["mp3", "wav", "ogg"])
st.write(f"**{T['or']}**")
audio_url = st.text_input(T["paste_url"], value="", placeholder="https://...")
# Audio preview block (player)
if file is not None:
st.markdown(f"**{T['preview']}**")
st.audio(file, format=file.type or "audio/mpeg")
elif audio_url.strip():
st.markdown(f"**{T['preview']}**")
st.audio(audio_url)
# ---------------- Controls ----------------
c1, c2, c3 = st.columns([0.22, 0.22, 0.56])
go = c1.button(f"▶ {T['analyze']}", type="primary")
reset = c2.button(f"↩ {T['reset']}")
health = c3.button("🔄 Health")
# ---------------- HTTP helpers ----------------
def build_session(auto_retry: bool) -> requests.Session:
s = requests.Session()
s.headers.update(HEADERS)
if auto_retry:
retry = Retry(
total=6, connect=6, read=6,
backoff_factor=0.8,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=frozenset(["GET", "POST"]),
raise_on_status=False,
)
adapter = HTTPAdapter(max_retries=retry)
s.mount("http://", adapter)
s.mount("https://", adapter)
return s
def warm_up(sess: requests.Session, base: str, progress_cb=None, total_budget: int = 25):
start = time.time()
paths = ["/", "/health"]
for i, path in enumerate(paths, 1):
try:
_ = sess.get(f"{base.rstrip('/')}{path}", timeout=8)
except Exception:
pass
if progress_cb:
progress_cb(min(int(i/len(paths)*18), 18)) # 0-18%
if time.time() - start > total_budget:
break
time.sleep(0.4)
def fetch_bytes(sess: requests.Session, base: str, url: str) -> bytes:
full = url if url.startswith("http") else f"{base.rstrip('/')}{url}"
r = sess.get(full, timeout=90)
r.raise_for_status()
return r.content
def post_analyze(sess: requests.Session, base: str, _file, _url: str, progress_cb=None) -> dict:
data = {
"use_data_uri": "true" if st.session_state.use_data_uri else "false",
"lang": st.session_state.lang,
}
if DEV_MODE and st.session_state.prompt_override.strip():
data["prompt"] = st.session_state.prompt_override.strip()
files = None
if _file is not None:
# read into BytesIO for repeatable upload
content = _file.read()
buf = io.BytesIO(content)
mime = getattr(_file, "type", None) or "application/octet-stream"
files = {"file": (_file.name, buf, mime)}
if progress_cb: progress_cb(30) # after upload prep
elif _url.strip():
data["audio_url"] = _url.strip()
if not files and "audio_url" not in data:
return {"ok": False, "error": T["need_input"]}
url = f"{base.rstrip('/')}/analyze"
deadline = time.time() + st.session_state.max_wait
last_err = None
while True:
try:
if progress_cb: progress_cb(35) # request start
resp = sess.post(url, data=data, files=files, timeout=240)
if progress_cb: progress_cb(78) # server responded
resp.raise_for_status()
if progress_cb: progress_cb(92) # parse
return resp.json()
except Exception as e:
last_err = e
if time.time() > deadline or not st.session_state.auto_retry:
return {"ok": False, "error": f"{T['error_generic']} ({last_err})"}
time.sleep(1.2)
# ---------------- Actions ----------------
sess = build_session(st.session_state.auto_retry)
if health:
try:
r = sess.get(f"{st.session_state.api_base.rstrip('/')}/health", timeout=10)
st.success(T["health_ok"] if r.ok else T["health_fail"])
if DEV_MODE and r.ok:
st.code(json.dumps(r.json(), ensure_ascii=False, indent=2), language="json")
except Exception:
st.error(T["health_fail"])
if reset:
st.session_state.res = None
st.rerun()
# Progress widgets (player stays above)
progress_box = st.container()
timeline = progress_box.expander(T["timeline_title"], expanded=True)
p_text = progress_box.empty()
p_bar = progress_box.progress(0)
def progress_update(pct: int, label: str = ""):
p_bar.progress(min(max(pct, 0), 100))
if label:
p_text.write(label)
if go:
if not st.session_state.api_base:
st.error(T["need_backend"])
elif not file and not audio_url.strip():
st.error(T["need_input"])
else:
# timeline
with timeline:
st.write(f"1) {T['step_prepare']}")
st.write(f"2) {T['step_upload']}")
st.write(f"3) {T['step_process']}")
st.write(f"4) {T['step_summarize']}")
# warmup (optional)
if st.session_state.warmup:
progress_update(5, T["warming"])
warm_up(sess, st.session_state.api_base, progress_cb=lambda v: progress_update(v, T["warming"]))
# analyze
progress_update(22, T["analyzing"])
res = post_analyze(
sess, st.session_state.api_base, file, audio_url,
progress_cb=lambda v: progress_update(v, T["analyzing"])
)
st.session_state.res = res
progress_update(100, "")
# ---------------- Results ----------------
res = st.session_state.res
if res:
if not res.get("ok", True) and res.get("error"):
st.error(T["error_generic"])
st.write(res.get("error"))
st.info(T["try_again"])
if DEV_MODE and st.session_state.show_debug and res.get("fal_debug"):
st.expander("Debug").code(json.dumps(res["fal_debug"], ensure_ascii=False, indent=2), language="json")
else:
st.subheader(T["result"])
# Summary for drivers
comm = (res.get("commentary") or "").strip()
if comm:
st.markdown(f"**{T['commentary']}**")
st.write(comm)
# Detailed findings (JSON). This is the structured diagnosis — no provider names.
parsed = res.get("fal_parsed")
if parsed:
st.markdown(f"**{T['json']}**")
st.json(parsed)
# Downloads
dls = res.get("downloads") or {}
if any(dls.get(k) for k in ("summary_txt", "fal_parsed_json", "fal_raw_txt")):
st.markdown(f"**{T['artifacts']}**")
col_a, col_b, col_c = st.columns(3)
if dls.get("summary_txt"):
try:
data = fetch_bytes(sess, st.session_state.api_base, dls["summary_txt"])
col_a.download_button(label=T["download_summary"], data=data, file_name="ozet.txt" if st.session_state.lang=="tr" else "summary.txt")
except Exception:
col_a.error("✖")
if dls.get("fal_parsed_json"):
try:
data = fetch_bytes(sess, st.session_state.api_base, dls["fal_parsed_json"])
col_b.download_button(label=T["download_parsed"], data=data, file_name="detay.json" if st.session_state.lang=="tr" else "details.json", mime="application/json")
except Exception:
col_b.error("✖")
if dls.get("fal_raw_txt"):
try:
data = fetch_bytes(sess, st.session_state.api_base, dls["fal_raw_txt"])
col_c.download_button(label=T["download_raw"], data=data, file_name="ham.txt" if st.session_state.lang=="tr" else "raw.txt")
except Exception:
col_c.error("✖")
# ---------------- Footer ----------------
st.caption("© 2025 Diag Studio — Araç Motor Ses Analizi")
|