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

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +104 -66
streamlit_app.py CHANGED
@@ -1,4 +1,4 @@
1
- # streamlit_app_refined.py
2
  import os
3
  import time
4
  import string
@@ -27,14 +27,19 @@ except Exception:
27
  Agent = Gemini = DuckDuckGo = None
28
  HAS_PHI = False
29
 
30
- # google.generativeai SDK
31
  try:
32
  import google.generativeai as genai
33
- from google.generativeai import upload_file, get_file # type: ignore
 
 
 
34
  HAS_GENAI = True
35
  except Exception:
36
  genai = None
37
- upload_file = get_file = None
 
 
38
  HAS_GENAI = False
39
 
40
  logging.basicConfig(level=logging.INFO)
@@ -44,7 +49,6 @@ st.set_page_config(page_title="Generate the story of videos", layout="wide")
44
  DATA_DIR = Path("./data")
45
  DATA_DIR.mkdir(exist_ok=True)
46
 
47
- # Session defaults
48
  st.session_state.setdefault("videos", "")
49
  st.session_state.setdefault("loop_video", False)
50
  st.session_state.setdefault("uploaded_file", None)
@@ -62,7 +66,6 @@ st.session_state.setdefault("processing_timeout", 900)
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",
@@ -71,7 +74,6 @@ MODEL_OPTIONS = [
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(" ", "_")
@@ -137,12 +139,12 @@ def configure_genai_if_needed():
137
  if not key:
138
  return False
139
  try:
140
- genai.configure(api_key=key)
 
141
  except Exception:
142
  pass
143
  return True
144
 
145
- # Agent management
146
  _agent = None
147
  def maybe_create_agent(model_id: str):
148
  global _agent
@@ -153,7 +155,8 @@ def maybe_create_agent(model_id: str):
153
  if _agent and st.session_state.get("last_model") == model_id:
154
  return _agent
155
  try:
156
- genai.configure(api_key=key)
 
157
  _agent = Agent(name="Video AI summarizer", model=Gemini(id=model_id), tools=[DuckDuckGo()], markdown=True)
158
  st.session_state["last_model"] = model_id
159
  except Exception:
@@ -174,13 +177,11 @@ def clear_all_video_state():
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()
181
  st.session_state["last_url_value"] = current_url
182
 
183
- # Sidebar UI
184
  st.sidebar.header("Video Input")
185
  st.sidebar.text_input("Video URL", key="url", placeholder="https://")
186
 
@@ -220,14 +221,14 @@ safety_settings = [
220
  {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "OFF"},
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):
@@ -307,55 +308,6 @@ def compress_video_if_large(local_path: str, threshold_mb: int = 50):
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:
314
- raise RuntimeError("No API key provided")
315
- if not HAS_GENAI or genai is None:
316
- raise RuntimeError("Responses API not available; install google.generativeai SDK.")
317
- genai.configure(api_key=key)
318
- fname = file_name_or_id(processed)
319
- if not fname:
320
- raise RuntimeError("Uploaded file missing name/id")
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},
327
- ]
328
-
329
- def is_transient_error(e_text: str):
330
- txt = str(e_text).lower()
331
- return any(k in txt for k in ("internal", "unavailable", "deadlineexceeded", "deadline exceeded", "timeout", "rate limit", "503", "502", "500"))
332
-
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 ""
@@ -420,7 +372,95 @@ def _normalize_genai_response(response):
420
  seen.add(t)
421
  return "\n\n".join(filtered).strip()
422
 
423
- # Layout
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
424
  col1, col2 = st.columns([1, 3])
425
  with col1:
426
  generate_now = st.button("Generate the story", type="primary", disabled=not bool(get_effective_api_key()))
@@ -470,7 +510,6 @@ if st.session_state["videos"]:
470
  except Exception:
471
  pass
472
 
473
- # Main generation flow
474
  if generate_now and not st.session_state.get("busy"):
475
  if not st.session_state.get("videos"):
476
  st.error("No video loaded. Use 'Load Video' in the sidebar.")
@@ -487,7 +526,6 @@ if generate_now and not st.session_state.get("busy"):
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"] = ""
 
1
+ # streamlit_app.py
2
  import os
3
  import time
4
  import string
 
27
  Agent = Gemini = DuckDuckGo = None
28
  HAS_PHI = False
29
 
30
+ # google.generativeai SDK (try both legacy and newer patterns)
31
  try:
32
  import google.generativeai as genai
33
+ # some installs expose a top-level `responses` object, others require attribute access
34
+ genai_responses = getattr(genai, "responses", None) or getattr(genai, "Responses", None)
35
+ upload_file = getattr(genai, "upload_file", None)
36
+ get_file = getattr(genai, "get_file", None)
37
  HAS_GENAI = True
38
  except Exception:
39
  genai = None
40
+ genai_responses = None
41
+ upload_file = None
42
+ get_file = None
43
  HAS_GENAI = False
44
 
45
  logging.basicConfig(level=logging.INFO)
 
49
  DATA_DIR = Path("./data")
50
  DATA_DIR.mkdir(exist_ok=True)
51
 
 
52
  st.session_state.setdefault("videos", "")
53
  st.session_state.setdefault("loop_video", False)
54
  st.session_state.setdefault("uploaded_file", None)
 
66
  st.session_state.setdefault("generation_timeout", 300)
67
  st.session_state.setdefault("preferred_model", "gemini-2.5-flash-lite")
68
 
 
69
  MODEL_OPTIONS = [
70
  "gemini-2.5-flash",
71
  "gemini-2.5-flash-lite",
 
74
  "custom",
75
  ]
76
 
 
77
  def sanitize_filename(path_str: str):
78
  name = Path(path_str).name
79
  return name.lower().translate(str.maketrans("", "", string.punctuation)).replace(" ", "_")
 
139
  if not key:
140
  return False
141
  try:
142
+ if genai is not None and hasattr(genai, "configure"):
143
+ genai.configure(api_key=key)
144
  except Exception:
145
  pass
146
  return True
147
 
 
148
  _agent = None
149
  def maybe_create_agent(model_id: str):
150
  global _agent
 
155
  if _agent and st.session_state.get("last_model") == model_id:
156
  return _agent
157
  try:
158
+ if genai is not None and hasattr(genai, "configure"):
159
+ genai.configure(api_key=key)
160
  _agent = Agent(name="Video AI summarizer", model=Gemini(id=model_id), tools=[DuckDuckGo()], markdown=True)
161
  st.session_state["last_model"] = model_id
162
  except Exception:
 
177
  except Exception:
178
  pass
179
 
 
180
  current_url = st.session_state.get("url", "")
181
  if current_url != st.session_state.get("last_url_value"):
182
  clear_all_video_state()
183
  st.session_state["last_url_value"] = current_url
184
 
 
185
  st.sidebar.header("Video Input")
186
  st.sidebar.text_input("Video URL", key="url", placeholder="https://")
187
 
 
221
  {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "OFF"},
222
  ]
223
 
 
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
+ if genai is not None and hasattr(genai, "configure"):
231
+ genai.configure(api_key=key)
232
  return upload_file(filepath)
233
 
234
  def wait_for_processed(file_obj, timeout: int = None, progress_callback=None):
 
308
  st.session_state["last_error"] = f"Video compression failed: {e}\n{traceback.format_exc()}"
309
  return local_path, False
310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  def _normalize_genai_response(response):
312
  if response is None:
313
  return ""
 
372
  seen.add(t)
373
  return "\n\n".join(filtered).strip()
374
 
375
+ def generate_via_responses_api(prompt_text: str, processed, model_used: str, max_tokens: int = 1024, timeout: int = 300, progress_callback=None):
376
+ key = get_effective_api_key()
377
+ if not key:
378
+ raise RuntimeError("No API key provided")
379
+ if not HAS_GENAI or genai is None:
380
+ raise RuntimeError("Responses API not available; install google-generativeai SDK.")
381
+ if genai is not None and hasattr(genai, "configure"):
382
+ genai.configure(api_key=key)
383
+ fname = file_name_or_id(processed)
384
+ if not fname:
385
+ raise RuntimeError("Uploaded file missing name/id")
386
+
387
+ system_msg = {"role": "system", "content": prompt_text}
388
+ user_msg = {"role": "user", "content": "Please summarize the attached video."}
389
+ call_variants = []
390
+
391
+ # Two common payload shapes: `genai.responses.generate(model=..., **payload)`
392
+ # and legacy `genai.Responses.create(...)` or model/chat wrappers.
393
+ # Build payloads for both styles.
394
+ call_variants.append({"method": "responses.generate", "payload": {"model": model_used, "messages": [system_msg, user_msg], "files": [{"name": fname}], "safety_settings": safety_settings, "max_output_tokens": max_tokens}})
395
+ call_variants.append({"method": "responses.generate_alt", "payload": {"model": model_used, "input": [{"text": prompt_text, "files": [{"name": fname}]}], "safety_settings": safety_settings, "max_output_tokens": max_tokens}})
396
+ call_variants.append({"method": "legacy_responses_create", "payload": {"model": model_used, "input": prompt_text, "file": fname, "max_output_tokens": max_tokens}})
397
+
398
+ def is_transient_error(e_text: str):
399
+ txt = str(e_text).lower()
400
+ return any(k in txt for k in ("internal", "unavailable", "deadlineexceeded", "deadline exceeded", "timeout", "rate limit", "503", "502", "500"))
401
+
402
+ start = time.time()
403
+ last_exc = None
404
+ backoff = 1.0
405
+ attempts = 0
406
+ while True:
407
+ for attempt_payload in call_variants:
408
+ attempts += 1
409
+ method = attempt_payload["method"]
410
+ payload = attempt_payload["payload"]
411
+ try:
412
+ if progress_callback:
413
+ progress_callback("starting", int(time.time() - start), {"model": model_used, "attempt": attempts, "method": method})
414
+ # Preferred new API style if available
415
+ if genai_responses is not None and hasattr(genai_responses, "generate"):
416
+ response = genai_responses.generate(**payload) if method.startswith("responses.generate") else genai_responses.generate(**payload)
417
+ text = _normalize_genai_response(response)
418
+ if progress_callback:
419
+ progress_callback("done", int(time.time() - start), {"model": model_used, "attempt": attempts, "method": method})
420
+ return text
421
+ # Some versions expose a top-level Responses class/or function `genai.Responses.create`
422
+ if hasattr(genai, "Responses") and hasattr(genai.Responses, "create"):
423
+ response = genai.Responses.create(**payload) # type: ignore
424
+ text = _normalize_genai_response(response)
425
+ if progress_callback:
426
+ progress_callback("done", int(time.time() - start), {"model": model_used, "attempt": attempts, "method": method})
427
+ return text
428
+ # Legacy model object style (older gemini SDK wrappers)
429
+ if hasattr(genai, "GenerativeModel"):
430
+ try:
431
+ model_obj = genai.GenerativeModel(model_name=model_used)
432
+ # try chat pattern
433
+ if hasattr(model_obj, "start_chat"):
434
+ chat = model_obj.start_chat()
435
+ resp = chat.send_message(prompt_text, timeout=timeout)
436
+ text = getattr(resp, "text", None) or str(resp)
437
+ text = text if text else _normalize_genai_response(resp)
438
+ if progress_callback:
439
+ progress_callback("done", int(time.time() - start), {"model": model_used, "attempt": attempts, "method": "GenerativeModel.chat"})
440
+ return text
441
+ except Exception:
442
+ pass
443
+ # If none matched, raise to be caught below and trigger helpful error
444
+ raise RuntimeError("No supported response generation method available in installed google-generativeai package.")
445
+ except Exception as e:
446
+ last_exc = e
447
+ msg = str(e)
448
+ logger.warning("Responses.generate error (model=%s attempt=%s method=%s): %s", model_used, attempts, method, msg)
449
+ if not is_transient_error(msg):
450
+ # non-transient -> surface meaningful hint for common misconfig issues
451
+ if "No supported response generation method" in msg or "has no attribute" in msg or "module 'google.generativeai' has no attribute" in msg:
452
+ raise RuntimeError(
453
+ "Installed google-generativeai package does not expose a compatible Responses API. "
454
+ "Please upgrade to a recent release or install the Google GenAI SDK. "
455
+ "Run: pip install --upgrade google-generativeai"
456
+ ) from e
457
+ raise
458
+ if time.time() - start > timeout:
459
+ raise TimeoutError(f"Responses.generate timed out after {timeout}s: last error: {last_exc}")
460
+ time.sleep(backoff)
461
+ backoff = min(backoff * 2, 8.0)
462
+
463
+ # UI layout
464
  col1, col2 = st.columns([1, 3])
465
  with col1:
466
  generate_now = st.button("Generate the story", type="primary", disabled=not bool(get_effective_api_key()))
 
510
  except Exception:
511
  pass
512
 
 
513
  if generate_now and not st.session_state.get("busy"):
514
  if not st.session_state.get("videos"):
515
  st.error("No video loaded. Use 'Load Video' in the sidebar.")
 
526
  except Exception:
527
  pass
528
 
 
529
  model_id = model_input_value or st.session_state.get("preferred_model") or "gemini-2.5-flash-lite"
530
  if st.session_state.get("last_model") != model_id:
531
  st.session_state["last_model"] = ""