CB commited on
Commit
d5297e2
·
verified ·
1 Parent(s): 1dd19a6

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +79 -138
streamlit_app.py CHANGED
@@ -1,4 +1,3 @@
1
- # streamlit_app.py
2
  import os
3
  import time
4
  import string
@@ -7,7 +6,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
 
13
  import yt_dlp
@@ -27,14 +25,15 @@ 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
  st.set_page_config(page_title="Generate the story of videos", layout="wide")
@@ -70,9 +69,9 @@ st.session_state.setdefault("api_key", os.getenv("GOOGLE_API_KEY", ""))
70
  st.session_state.setdefault("last_model", "")
71
  st.session_state.setdefault("upload_progress", {"uploaded": 0, "total": 0})
72
  st.session_state.setdefault("last_url_value", "")
73
- st.session_state.setdefault("processing_timeout", 900) # seconds
74
- st.session_state.setdefault("generation_timeout", 300) # seconds
75
- st.session_state.setdefault("compress_threshold_mb", 200) # optional compression default
76
 
77
  # ---- Helpers ----
78
  def sanitize_filename(path_str: str):
@@ -140,7 +139,8 @@ def configure_genai_if_needed():
140
  if not key:
141
  return False
142
  try:
143
- genai.configure(api_key=key)
 
144
  except Exception:
145
  pass
146
  return True
@@ -156,7 +156,8 @@ def maybe_create_agent(model_id: str):
156
  if _agent and st.session_state.get("last_model") == model_id:
157
  return _agent
158
  try:
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,7 +178,6 @@ def clear_all_video_state():
177
  except Exception:
178
  pass
179
 
180
- # Reset when URL changes
181
  current_url = st.session_state.get("url", "")
182
  if current_url != st.session_state.get("last_url_value"):
183
  clear_all_video_state()
@@ -228,33 +228,34 @@ safety_settings = [
228
  {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "OFF"},
229
  ]
230
 
231
- # ---- Upload & processing helpers ----
232
  def upload_video_sdk(filepath: str):
233
  key = get_effective_api_key()
234
  if not key:
235
  raise RuntimeError("No API key provided")
236
- if not HAS_GENAI or upload_file is None:
237
- raise RuntimeError("google.generativeai SDK not available; cannot upload")
238
- genai.configure(api_key=key)
239
- return upload_file(filepath)
 
 
 
 
240
 
241
  def wait_for_processed(file_obj, timeout: int = None):
242
- """
243
- Poll get_file until file is no longer PROCESSING.
244
- Retries get_file on transient errors with exponential backoff.
245
- """
246
  if timeout is None:
247
  timeout = st.session_state.get("processing_timeout", 900)
248
- if not HAS_GENAI or get_file is None:
249
  return file_obj
250
  start = time.time()
251
- name = file_name_or_id(file_obj)
252
- if not name:
 
253
  return file_obj
254
  backoff = 1.0
255
  while True:
256
  try:
257
- obj = get_file(name)
258
  except Exception as e:
259
  if time.time() - start > timeout:
260
  raise TimeoutError(f"Failed to fetch file status before timeout: {e}")
@@ -262,8 +263,9 @@ def wait_for_processed(file_obj, timeout: int = None):
262
  backoff = min(backoff * 2, 8.0)
263
  continue
264
 
265
- state = getattr(obj, "state", None)
266
- if not state or getattr(state, "name", None) != "PROCESSING":
 
267
  return obj
268
 
269
  if time.time() - start > timeout:
@@ -310,123 +312,67 @@ def compress_video_if_large(local_path: str, threshold_mb: int = 200):
310
  st.session_state["last_error"] = f"Video compression failed: {e}\n{traceback.format_exc()}"
311
  return local_path, False
312
 
313
- # ---- Robust Responses API caller adapted for varying model versions ----
314
  def generate_via_responses_api(prompt_text: str, processed, model_used: str, max_tokens: int = 1024, timeout: int = 300):
315
  key = get_effective_api_key()
316
  if not key:
317
  raise RuntimeError("No API key provided")
318
- if not HAS_GENAI or genai is None:
319
- raise RuntimeError("Responses API not available; install google.generativeai SDK.")
320
- genai.configure(api_key=key)
321
- fname = file_name_or_id(processed)
322
- if not fname:
323
- raise RuntimeError("Uploaded file missing name/id")
324
-
325
- system_msg = {"role": "system", "content": prompt_text}
326
- user_msg = {"role": "user", "content": "Please summarize the attached video."}
327
-
328
- # Some model versions and SDK releases expect messages, some older ones expect input with files.
329
- call_variants = [
330
- {"messages": [system_msg, user_msg], "files": [{"name": fname}], "safety_settings": safety_settings, "max_output_tokens": max_tokens},
331
- {"input": [{"text": prompt_text, "files": [{"name": fname}]}], "safety_settings": safety_settings, "max_output_tokens": max_tokens},
332
- ]
333
-
334
- last_exc = None
335
- start = time.time()
336
- backoff = 1.0
337
- while True:
338
- for payload in call_variants:
339
- try:
340
- response = genai.responses.generate(model=model_used, **payload)
341
- return _normalize_genai_response(response)
342
- except Exception as e:
343
- last_exc = e
344
- msg = str(e).lower()
345
- if any(k in msg for k in ("internal", "unavailable", "deadlineexceeded", "deadline exceeded", "timeout", "rate limit")):
346
- pass
347
- else:
348
- raise
349
- if time.time() - start > timeout:
350
- raise TimeoutError(f"Responses.generate timed out after {timeout}s: last error: {last_exc}")
351
- time.sleep(backoff)
352
- backoff = min(backoff * 2, 8.0)
353
 
354
- def _normalize_genai_response(response):
355
- outputs = []
356
- if response is None:
357
- return ""
358
 
359
- if not isinstance(response, dict):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
  try:
361
- response = json.loads(str(response))
 
 
 
 
 
362
  except Exception:
363
- pass
364
-
365
- candidate_lists = []
366
- if isinstance(response, dict):
367
- for key in ("output", "candidates", "items", "responses", "choices"):
368
- val = response.get(key)
369
- if isinstance(val, list) and val:
370
- candidate_lists.append(val)
371
- if not candidate_lists and isinstance(response, dict):
372
- for v in response.values():
373
- if isinstance(v, list) and v:
374
- candidate_lists.append(v)
375
- break
376
-
377
- text_pieces = []
378
- for lst in candidate_lists:
379
- for item in lst:
380
- if not item:
381
- continue
382
- if isinstance(item, dict):
383
- for k in ("content", "text", "message", "output_text", "output"):
384
- t = item.get(k)
385
- if t:
386
- text_pieces.append(str(t).strip())
387
- break
388
- else:
389
- if "content" in item and isinstance(item["content"], list):
390
- for part in item["content"]:
391
- if isinstance(part, dict):
392
- t = part.get("text") or part.get("content")
393
- if t:
394
- text_pieces.append(str(t).strip())
395
- elif isinstance(part, str):
396
- text_pieces.append(part.strip())
397
- elif isinstance(item, str):
398
- text_pieces.append(item.strip())
399
- else:
400
- try:
401
- t = getattr(item, "text", None) or getattr(item, "content", None)
402
- if t:
403
- text_pieces.append(str(t).strip())
404
- except Exception:
405
- pass
406
-
407
- if not text_pieces and isinstance(response, dict):
408
- for k in ("text", "message", "output_text"):
409
- v = response.get(k)
410
- if v:
411
- text_pieces.append(str(v).strip())
412
- break
413
-
414
- seen = set()
415
- filtered = []
416
- for t in text_pieces:
417
- if not isinstance(t, str):
418
- continue
419
- if t and t not in seen:
420
- filtered.append(t)
421
- seen.add(t)
422
- return "\n\n".join(filtered).strip()
423
 
424
  # ---- Layout ----
425
  col1, col2 = st.columns([1, 3])
426
  with col1:
427
  generate_now = st.button("Generate the story", type="primary", disabled=not bool(get_effective_api_key()))
428
  with col2:
429
- st.write("") # placeholder
430
 
431
  if st.sidebar.button("Load Video", use_container_width=True):
432
  try:
@@ -484,7 +430,8 @@ if generate_now and not st.session_state.get("busy"):
484
  st.session_state["busy"] = True
485
  try:
486
  if HAS_GENAI and genai is not None:
487
- genai.configure(api_key=key_to_use)
 
488
  except Exception:
489
  pass
490
 
@@ -506,12 +453,11 @@ if generate_now and not st.session_state.get("busy"):
506
 
507
  if reupload_needed:
508
  if not HAS_GENAI:
509
- raise RuntimeError("google.generativeai SDK not available; install it.")
510
  local_path = current_path
511
  upload_path, compressed = compress_video_if_large(local_path, threshold_mb=st.session_state.get("compress_threshold_mb", 200))
512
 
513
  with st.spinner(f"Uploading video{' (compressed)' if compressed else ''}..."):
514
- progress_placeholder = st.empty()
515
  try:
516
  uploaded = upload_video_sdk(upload_path)
517
  except Exception as e:
@@ -520,11 +466,7 @@ if generate_now and not st.session_state.get("busy"):
520
  raise
521
 
522
  try:
523
- processing_placeholder = st.empty()
524
- processing_bar = processing_placeholder.progress(0)
525
  processed = wait_for_processed(uploaded, timeout=st.session_state.get("processing_timeout", 900))
526
- processing_bar.progress(100)
527
- processing_placeholder.success("Processing complete")
528
  except Exception as e:
529
  st.session_state["last_error"] = f"Processing failed/wait timeout: {e}\n\nTraceback:\n{traceback.format_exc()}"
530
  st.error("Video processing failed or timed out. See Last Error.")
@@ -541,7 +483,6 @@ if generate_now and not st.session_state.get("busy"):
541
  max_tokens = 2048 if "2.5" in model_used else 1024
542
  est_tokens = max_tokens
543
 
544
- # Try Agent first, fallback to Responses API
545
  agent = maybe_create_agent(model_used)
546
  debug_info = {"agent_attempted": False, "agent_ok": False, "agent_error": None, "agent_response_has_text": False}
547
  if agent:
 
 
1
  import os
2
  import time
3
  import string
 
6
  from glob import glob
7
  from pathlib import Path
8
  from difflib import SequenceMatcher
 
9
  import json
10
 
11
  import yt_dlp
 
25
  Agent = Gemini = DuckDuckGo = None
26
  HAS_PHI = False
27
 
28
+ # google-genai (v1.49.1)
29
  try:
30
+ import google_genai as genai # package name for google-genai
31
+ from google_genai import Files, Responses, configure as genai_configure # convenience
32
  HAS_GENAI = True
33
  except Exception:
34
  genai = None
35
+ Files = Responses = None
36
+ genai_configure = None
37
  HAS_GENAI = False
38
 
39
  st.set_page_config(page_title="Generate the story of videos", layout="wide")
 
69
  st.session_state.setdefault("last_model", "")
70
  st.session_state.setdefault("upload_progress", {"uploaded": 0, "total": 0})
71
  st.session_state.setdefault("last_url_value", "")
72
+ st.session_state.setdefault("processing_timeout", 900)
73
+ st.session_state.setdefault("generation_timeout", 300)
74
+ st.session_state.setdefault("compress_threshold_mb", 200)
75
 
76
  # ---- Helpers ----
77
  def sanitize_filename(path_str: str):
 
139
  if not key:
140
  return False
141
  try:
142
+ if genai_configure:
143
+ genai_configure(api_key=key)
144
  except Exception:
145
  pass
146
  return True
 
156
  if _agent and st.session_state.get("last_model") == model_id:
157
  return _agent
158
  try:
159
+ if genai_configure:
160
+ genai_configure(api_key=key)
161
  _agent = Agent(name="Video AI summarizer", model=Gemini(id=model_id), tools=[DuckDuckGo()], markdown=True)
162
  st.session_state["last_model"] = model_id
163
  except Exception:
 
178
  except Exception:
179
  pass
180
 
 
181
  current_url = st.session_state.get("url", "")
182
  if current_url != st.session_state.get("last_url_value"):
183
  clear_all_video_state()
 
228
  {"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "OFF"},
229
  ]
230
 
231
+ # ---- Upload & processing helpers for google-genai Files ----
232
  def upload_video_sdk(filepath: str):
233
  key = get_effective_api_key()
234
  if not key:
235
  raise RuntimeError("No API key provided")
236
+ if not HAS_GENAI or Files is None:
237
+ raise RuntimeError("google-genai SDK not available; cannot upload")
238
+ if genai_configure:
239
+ genai_configure(api_key=key)
240
+ with open(filepath, "rb") as fh:
241
+ # Files.upload returns a response-like object; adapt as needed
242
+ resp = Files.create(file=fh, purpose="video")
243
+ return resp
244
 
245
  def wait_for_processed(file_obj, timeout: int = None):
 
 
 
 
246
  if timeout is None:
247
  timeout = st.session_state.get("processing_timeout", 900)
248
+ if not HAS_GENAI or Files is None:
249
  return file_obj
250
  start = time.time()
251
+ # file_obj may be a dict or an SDK object; adapt
252
+ file_id = file_obj.get("name") if isinstance(file_obj, dict) else getattr(file_obj, "name", None) or getattr(file_obj, "id", None)
253
+ if not file_id:
254
  return file_obj
255
  backoff = 1.0
256
  while True:
257
  try:
258
+ obj = Files.get(file_id)
259
  except Exception as e:
260
  if time.time() - start > timeout:
261
  raise TimeoutError(f"Failed to fetch file status before timeout: {e}")
 
263
  backoff = min(backoff * 2, 8.0)
264
  continue
265
 
266
+ state = obj.get("state") if isinstance(obj, dict) else getattr(obj, "state", None)
267
+ name = state.get("name") if isinstance(state, dict) else getattr(state, "name", None)
268
+ if not name or name != "PROCESSING":
269
  return obj
270
 
271
  if time.time() - start > timeout:
 
312
  st.session_state["last_error"] = f"Video compression failed: {e}\n{traceback.format_exc()}"
313
  return local_path, False
314
 
315
+ # ---- Responses API caller adapted for google-genai Responses ----
316
  def generate_via_responses_api(prompt_text: str, processed, model_used: str, max_tokens: int = 1024, timeout: int = 300):
317
  key = get_effective_api_key()
318
  if not key:
319
  raise RuntimeError("No API key provided")
320
+ if not HAS_GENAI or Responses is None:
321
+ raise RuntimeError("Responses API not available; install google-genai SDK.")
322
+ if genai_configure:
323
+ genai_configure(api_key=key)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
 
325
+ file_name = file_name_or_id(processed)
326
+ if not file_name:
327
+ raise RuntimeError("Uploaded file missing name/id")
 
328
 
329
+ # Build a minimal Responses.create call that attaches the video file reference.
330
+ # The exact shape depends on google-genai; here we create a simple text + reference instruction.
331
+ request = {
332
+ "model": model_used,
333
+ "input": [
334
+ {"role": "system", "content": prompt_text},
335
+ {"role": "user", "content": "Please summarize the attached video."}
336
+ ],
337
+ "attachments": [{"mime_type": "video/mp4", "uri": f"file:{file_name}"}],
338
+ "max_output_tokens": max_tokens,
339
+ "temperature": 0.2,
340
+ }
341
+
342
+ # Responses.create returns a response object/dict; attempt to extract text
343
+ resp = Responses.create(**request)
344
+ text = ""
345
+ # support multiple response shapes
346
+ if isinstance(resp, dict):
347
+ # common shapes: resp['output'][0]['content'] or resp['candidates'][0]['content']
348
+ out = resp.get("output") or resp.get("candidates")
349
+ if isinstance(out, list) and out:
350
+ first = out[0]
351
+ if isinstance(first, dict):
352
+ text = first.get("content") or first.get("text") or ""
353
+ else:
354
+ text = str(first)
355
+ else:
356
+ text = resp.get("content") or resp.get("text") or ""
357
+ else:
358
+ # SDK object: try attributes
359
  try:
360
+ if hasattr(resp, "outputs"):
361
+ outputs = getattr(resp, "outputs", None)
362
+ if outputs:
363
+ text = outputs[0].get("content") if isinstance(outputs, list) and isinstance(outputs[0], dict) else str(outputs[0])
364
+ elif hasattr(resp, "text"):
365
+ text = getattr(resp, "text", "")
366
  except Exception:
367
+ text = str(resp)
368
+ return text or ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
369
 
370
  # ---- Layout ----
371
  col1, col2 = st.columns([1, 3])
372
  with col1:
373
  generate_now = st.button("Generate the story", type="primary", disabled=not bool(get_effective_api_key()))
374
  with col2:
375
+ st.write("")
376
 
377
  if st.sidebar.button("Load Video", use_container_width=True):
378
  try:
 
430
  st.session_state["busy"] = True
431
  try:
432
  if HAS_GENAI and genai is not None:
433
+ if genai_configure:
434
+ genai_configure(api_key=key_to_use)
435
  except Exception:
436
  pass
437
 
 
453
 
454
  if reupload_needed:
455
  if not HAS_GENAI:
456
+ raise RuntimeError("google-genai SDK not available; install it.")
457
  local_path = current_path
458
  upload_path, compressed = compress_video_if_large(local_path, threshold_mb=st.session_state.get("compress_threshold_mb", 200))
459
 
460
  with st.spinner(f"Uploading video{' (compressed)' if compressed else ''}..."):
 
461
  try:
462
  uploaded = upload_video_sdk(upload_path)
463
  except Exception as e:
 
466
  raise
467
 
468
  try:
 
 
469
  processed = wait_for_processed(uploaded, timeout=st.session_state.get("processing_timeout", 900))
 
 
470
  except Exception as e:
471
  st.session_state["last_error"] = f"Processing failed/wait timeout: {e}\n\nTraceback:\n{traceback.format_exc()}"
472
  st.error("Video processing failed or timed out. See Last Error.")
 
483
  max_tokens = 2048 if "2.5" in model_used else 1024
484
  est_tokens = max_tokens
485
 
 
486
  agent = maybe_create_agent(model_used)
487
  debug_info = {"agent_attempted": False, "agent_ok": False, "agent_error": None, "agent_response_has_text": False}
488
  if agent: