CB commited on
Commit
94c9dfa
·
verified ·
1 Parent(s): c314d98

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +44 -68
streamlit_app.py CHANGED
@@ -1,4 +1,4 @@
1
- # streamlit_app_enhanced.py
2
  import os
3
  import time
4
  import string
@@ -7,7 +7,6 @@ import traceback
7
  from glob import glob
8
  from pathlib import Path
9
  from difflib import SequenceMatcher
10
- import concurrent.futures
11
  import json
12
  import logging
13
 
@@ -18,7 +17,7 @@ from dotenv import load_dotenv
18
 
19
  load_dotenv()
20
 
21
- # Optional phi integration (Agent + Gemini wrapper)
22
  try:
23
  from phi.agent import Agent
24
  from phi.model.google import Gemini
@@ -38,7 +37,6 @@ except Exception:
38
  upload_file = get_file = None
39
  HAS_GENAI = False
40
 
41
- # Logging
42
  logging.basicConfig(level=logging.INFO)
43
  logger = logging.getLogger("video_ai")
44
 
@@ -64,7 +62,16 @@ st.session_state.setdefault("processing_timeout", 900)
64
  st.session_state.setdefault("generation_timeout", 300)
65
  st.session_state.setdefault("preferred_model", "gemini-2.5-flash-lite")
66
 
67
- # Helpers (kept in-file for single-file deliverable)
 
 
 
 
 
 
 
 
 
68
  def sanitize_filename(path_str: str):
69
  name = Path(path_str).name
70
  return name.lower().translate(str.maketrans("", "", string.punctuation)).replace(" ", "_")
@@ -167,7 +174,7 @@ def clear_all_video_state():
167
  except Exception:
168
  pass
169
 
170
- # Reset when URL changes
171
  current_url = st.session_state.get("url", "")
172
  if current_url != st.session_state.get("last_url_value"):
173
  clear_all_video_state()
@@ -178,7 +185,11 @@ st.sidebar.header("Video Input")
178
  st.sidebar.text_input("Video URL", key="url", placeholder="https://")
179
 
180
  settings_exp = st.sidebar.expander("Settings", expanded=False)
181
- model_input = settings_exp.text_input("Preferred Gemini Model (short name)", st.session_state.get("preferred_model", "gemini-2.5-flash-lite"), key="model_input")
 
 
 
 
182
  settings_exp.text_input("Google API Key", key="api_key", value=os.getenv("GOOGLE_API_KEY", ""), type="password")
183
  default_prompt = (
184
  "Watch the video and provide a detailed behavioral report focusing on human actions, interactions, posture, movement, and apparent intent. Keep language professional. Include a list of observations for notable events."
@@ -210,19 +221,14 @@ safety_settings = [
210
  ]
211
 
212
  # Upload & processing helpers
213
- def upload_video_sdk(filepath: str, progress_callback=None):
214
  key = get_effective_api_key()
215
  if not key:
216
  raise RuntimeError("No API key provided")
217
  if not HAS_GENAI or upload_file is None:
218
  raise RuntimeError("google.generativeai SDK not available; cannot upload")
219
  genai.configure(api_key=key)
220
- # upload_file doesn't offer progress hooks in SDK; attempt best-effort by streaming in chunks if possible
221
- # Fall back to direct upload_file call for compatibility
222
- try:
223
- return upload_file(filepath)
224
- except Exception as e:
225
- raise
226
 
227
  def wait_for_processed(file_obj, timeout: int = None, progress_callback=None):
228
  if timeout is None:
@@ -234,7 +240,6 @@ def wait_for_processed(file_obj, timeout: int = None, progress_callback=None):
234
  if not name:
235
  return file_obj
236
  backoff = 1.0
237
- last_state = None
238
  while True:
239
  try:
240
  obj = get_file(name)
@@ -248,9 +253,8 @@ def wait_for_processed(file_obj, timeout: int = None, progress_callback=None):
248
  state = getattr(obj, "state", None)
249
  state_name = getattr(state, "name", None) if state else None
250
  if progress_callback:
251
- # show a simple heuristic percent while PROCESSING
252
  elapsed = int(time.time() - start)
253
- pct = 100 if not state_name else (50 if state_name == "PROCESSING" else 100)
254
  try:
255
  progress_callback(min(100, pct), elapsed, state_name)
256
  except Exception:
@@ -263,7 +267,6 @@ def wait_for_processed(file_obj, timeout: int = None, progress_callback=None):
263
  raise TimeoutError(f"File processing timed out after {int(time.time() - start)}s")
264
  time.sleep(backoff)
265
  backoff = min(backoff * 2, 8.0)
266
- last_state = state_name
267
 
268
  def remove_prompt_echo(prompt: str, text: str, check_len: int = 600, ratio_threshold: float = 0.68):
269
  if not prompt or not text:
@@ -304,7 +307,7 @@ def compress_video_if_large(local_path: str, threshold_mb: int = 50):
304
  st.session_state["last_error"] = f"Video compression failed: {e}\n{traceback.format_exc()}"
305
  return local_path, False
306
 
307
- # Robust Responses API caller with retries and auto-fallback to older model on certain failures
308
  def generate_via_responses_api(prompt_text: str, processed, model_used: str, max_tokens: int = 1024, timeout: int = 300, progress_callback=None):
309
  key = get_effective_api_key()
310
  if not key:
@@ -318,7 +321,6 @@ def generate_via_responses_api(prompt_text: str, processed, model_used: str, max
318
 
319
  system_msg = {"role": "system", "content": prompt_text}
320
  user_msg = {"role": "user", "content": "Please summarize the attached video."}
321
-
322
  call_variants = [
323
  {"messages": [system_msg, user_msg], "files": [{"name": fname}], "safety_settings": safety_settings, "max_output_tokens": max_tokens},
324
  {"input": [{"text": prompt_text, "files": [{"name": fname}]}], "safety_settings": safety_settings, "max_output_tokens": max_tokens},
@@ -331,56 +333,30 @@ def generate_via_responses_api(prompt_text: str, processed, model_used: str, max
331
  start = time.time()
332
  last_exc = None
333
  backoff = 1.0
334
- max_total = timeout
335
  attempts = 0
336
- tried_models = []
337
- preferred_model = model_used or st.session_state.get("preferred_model", "gemini-2.5-flash-lite")
338
- fallback_model = "gemini-2.0-flash-lite" if "2.5" in preferred_model else None
339
- models_to_try = [preferred_model] + ([fallback_model] if fallback_model else [])
340
- for m in models_to_try:
341
- if not m:
342
- continue
343
- tried_models.append(m)
344
- # per-model attempt window
345
- model_start = time.time()
346
- while True:
347
  attempts += 1
348
- for payload in call_variants:
349
- try:
350
- if progress_callback:
351
- elapsed = int(time.time() - start)
352
- try:
353
- progress_callback("starting_generation", elapsed, {"model": m, "attempt": attempts})
354
- except Exception:
355
- pass
356
- response = genai.responses.generate(model=m, **payload)
357
- text = _normalize_genai_response(response)
358
- if progress_callback:
359
- elapsed = int(time.time() - start)
360
- try:
361
- progress_callback("generation_complete", elapsed, {"model": m})
362
- except Exception:
363
- pass
364
- return text
365
- except Exception as e:
366
- last_exc = e
367
- msg = str(e)
368
- logger.warning("Responses.generate error on model %s attempt %s: %s", m, attempts, msg)
369
- if not is_transient_error(msg):
370
- # Non-transient: rethrow to surface to caller
371
- raise
372
- # transient: will retry for this model up to timeout
373
- if time.time() - start > max_total:
374
- break
375
- time.sleep(backoff)
376
- backoff = min(backoff * 2, 8.0)
377
- if time.time() - model_start > max_total:
378
- break
379
- # try next model (fallback)
380
- raise TimeoutError(f"Responses.generate failed after trying models {tried_models}: last error: {last_exc}")
381
 
382
  def _normalize_genai_response(response):
383
- outputs = []
384
  if response is None:
385
  return ""
386
  if not isinstance(response, dict):
@@ -511,7 +487,8 @@ if generate_now and not st.session_state.get("busy"):
511
  except Exception:
512
  pass
513
 
514
- model_id = (st.session_state.get("model_input") or st.session_state.get("preferred_model") or "gemini-2.5-flash-lite").strip()
 
515
  if st.session_state.get("last_model") != model_id:
516
  st.session_state["last_model"] = ""
517
  maybe_create_agent(model_id)
@@ -534,7 +511,6 @@ if generate_now and not st.session_state.get("busy"):
534
  upload_path, compressed = compress_video_if_large(local_path)
535
 
536
  with st.spinner(f"Uploading video{' (compressed)' if compressed else ''}..."):
537
- upload_progress_placeholder = st.empty()
538
  try:
539
  uploaded = upload_video_sdk(upload_path)
540
  except Exception as e:
 
1
+ # streamlit_app_refined.py
2
  import os
3
  import time
4
  import string
 
7
  from glob import glob
8
  from pathlib import Path
9
  from difflib import SequenceMatcher
 
10
  import json
11
  import logging
12
 
 
17
 
18
  load_dotenv()
19
 
20
+ # Optional phi integration (Agent wrapper)
21
  try:
22
  from phi.agent import Agent
23
  from phi.model.google import Gemini
 
37
  upload_file = get_file = None
38
  HAS_GENAI = False
39
 
 
40
  logging.basicConfig(level=logging.INFO)
41
  logger = logging.getLogger("video_ai")
42
 
 
62
  st.session_state.setdefault("generation_timeout", 300)
63
  st.session_state.setdefault("preferred_model", "gemini-2.5-flash-lite")
64
 
65
+ # Model choices for dropdown
66
+ MODEL_OPTIONS = [
67
+ "gemini-2.5-flash",
68
+ "gemini-2.5-flash-lite",
69
+ "gemini-2.0-flash",
70
+ "gemini-2.0-flash-lite",
71
+ "custom",
72
+ ]
73
+
74
+ # Helpers
75
  def sanitize_filename(path_str: str):
76
  name = Path(path_str).name
77
  return name.lower().translate(str.maketrans("", "", string.punctuation)).replace(" ", "_")
 
174
  except Exception:
175
  pass
176
 
177
+ # Reset on URL change
178
  current_url = st.session_state.get("url", "")
179
  if current_url != st.session_state.get("last_url_value"):
180
  clear_all_video_state()
 
185
  st.sidebar.text_input("Video URL", key="url", placeholder="https://")
186
 
187
  settings_exp = st.sidebar.expander("Settings", expanded=False)
188
+ chosen = settings_exp.selectbox("Gemini model", MODEL_OPTIONS, index=MODEL_OPTIONS.index("gemini-2.5-flash-lite"))
189
+ custom_model = ""
190
+ if chosen == "custom":
191
+ custom_model = settings_exp.text_input("Custom model name", value=st.session_state.get("preferred_model", "gemini-2.5-flash-lite"))
192
+ model_input_value = (custom_model.strip() if chosen == "custom" else chosen).strip()
193
  settings_exp.text_input("Google API Key", key="api_key", value=os.getenv("GOOGLE_API_KEY", ""), type="password")
194
  default_prompt = (
195
  "Watch the video and provide a detailed behavioral report focusing on human actions, interactions, posture, movement, and apparent intent. Keep language professional. Include a list of observations for notable events."
 
221
  ]
222
 
223
  # Upload & processing helpers
224
+ def upload_video_sdk(filepath: str):
225
  key = get_effective_api_key()
226
  if not key:
227
  raise RuntimeError("No API key provided")
228
  if not HAS_GENAI or upload_file is None:
229
  raise RuntimeError("google.generativeai SDK not available; cannot upload")
230
  genai.configure(api_key=key)
231
+ return upload_file(filepath)
 
 
 
 
 
232
 
233
  def wait_for_processed(file_obj, timeout: int = None, progress_callback=None):
234
  if timeout is None:
 
240
  if not name:
241
  return file_obj
242
  backoff = 1.0
 
243
  while True:
244
  try:
245
  obj = get_file(name)
 
253
  state = getattr(obj, "state", None)
254
  state_name = getattr(state, "name", None) if state else None
255
  if progress_callback:
 
256
  elapsed = int(time.time() - start)
257
+ pct = 50 if state_name == "PROCESSING" else 100
258
  try:
259
  progress_callback(min(100, pct), elapsed, state_name)
260
  except Exception:
 
267
  raise TimeoutError(f"File processing timed out after {int(time.time() - start)}s")
268
  time.sleep(backoff)
269
  backoff = min(backoff * 2, 8.0)
 
270
 
271
  def remove_prompt_echo(prompt: str, text: str, check_len: int = 600, ratio_threshold: float = 0.68):
272
  if not prompt or not text:
 
307
  st.session_state["last_error"] = f"Video compression failed: {e}\n{traceback.format_exc()}"
308
  return local_path, False
309
 
310
+ # Responses API caller: robust, but NO automatic model-switching (user-controlled)
311
  def generate_via_responses_api(prompt_text: str, processed, model_used: str, max_tokens: int = 1024, timeout: int = 300, progress_callback=None):
312
  key = get_effective_api_key()
313
  if not key:
 
321
 
322
  system_msg = {"role": "system", "content": prompt_text}
323
  user_msg = {"role": "user", "content": "Please summarize the attached video."}
 
324
  call_variants = [
325
  {"messages": [system_msg, user_msg], "files": [{"name": fname}], "safety_settings": safety_settings, "max_output_tokens": max_tokens},
326
  {"input": [{"text": prompt_text, "files": [{"name": fname}]}], "safety_settings": safety_settings, "max_output_tokens": max_tokens},
 
333
  start = time.time()
334
  last_exc = None
335
  backoff = 1.0
 
336
  attempts = 0
337
+ while True:
338
+ for payload in call_variants:
 
 
 
 
 
 
 
 
 
339
  attempts += 1
340
+ try:
341
+ if progress_callback:
342
+ progress_callback("starting", int(time.time() - start), {"model": model_used, "attempt": attempts})
343
+ response = genai.responses.generate(model=model_used, **payload)
344
+ text = _normalize_genai_response(response)
345
+ if progress_callback:
346
+ progress_callback("done", int(time.time() - start), {"model": model_used, "attempt": attempts})
347
+ return text
348
+ except Exception as e:
349
+ last_exc = e
350
+ msg = str(e)
351
+ logger.warning("Responses.generate error (model=%s attempt=%s): %s", model_used, attempts, msg)
352
+ if not is_transient_error(msg):
353
+ raise
354
+ if time.time() - start > timeout:
355
+ raise TimeoutError(f"Responses.generate timed out after {timeout}s: last error: {last_exc}")
356
+ time.sleep(backoff)
357
+ backoff = min(backoff * 2, 8.0)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
 
359
  def _normalize_genai_response(response):
 
360
  if response is None:
361
  return ""
362
  if not isinstance(response, dict):
 
487
  except Exception:
488
  pass
489
 
490
+ # chosen model
491
+ model_id = model_input_value or st.session_state.get("preferred_model") or "gemini-2.5-flash-lite"
492
  if st.session_state.get("last_model") != model_id:
493
  st.session_state["last_model"] = ""
494
  maybe_create_agent(model_id)
 
511
  upload_path, compressed = compress_video_if_large(local_path)
512
 
513
  with st.spinner(f"Uploading video{' (compressed)' if compressed else ''}..."):
 
514
  try:
515
  uploaded = upload_video_sdk(upload_path)
516
  except Exception as e: