CB commited on
Commit
a4d27b6
·
verified ·
1 Parent(s): ca2603e

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +160 -128
streamlit_app.py CHANGED
@@ -3,6 +3,7 @@ import os
3
  import time
4
  import string
5
  import hashlib
 
6
  from glob import glob
7
  from pathlib import Path
8
  from difflib import SequenceMatcher
@@ -352,143 +353,172 @@ if generate_now and not st.session_state.get("busy"):
352
  est_tokens = max_tokens
353
  est_cost_caption = f"Est. max tokens: {est_tokens}"
354
 
 
355
  agent = maybe_create_agent(model_used)
 
356
  if agent:
357
- with st.spinner("Generating description via Agent..."):
358
- if not processed:
359
- raise RuntimeError("Processed file missing for agent generation")
360
- response = agent.run(prompt_text, videos=[processed], safety_settings=safety_settings)
361
- out = getattr(response, "content", None) or getattr(response, "outputText", None) or str(response)
362
- else:
363
- if not HAS_GENAI or genai is None:
364
- raise RuntimeError("Responses API not available; install google.generativeai SDK.")
365
- genai.configure(api_key=key_to_use)
366
- fname = file_name_or_id(processed)
367
- if not fname:
368
- raise RuntimeError("Uploaded file missing name/id")
369
- system_msg = {"role": "system", "content": prompt_text}
370
- user_msg = {"role": "user", "content": "Please summarize the attached video."}
371
-
372
- # Try the modern and legacy signatures; fail clearly if both fail
373
  try:
374
- response = genai.responses.generate(
375
- model=model_used,
376
- messages=[system_msg, user_msg],
377
- files=[{"name": fname}],
378
- safety_settings=safety_settings,
379
- max_output_tokens=max_tokens,
380
- )
381
- except TypeError:
382
- response = genai.responses.generate(
383
- model=model_used,
384
- input=[{"text": prompt_text, "files": [{"name": fname}]}],
385
- safety_settings=safety_settings,
386
- max_output_tokens=max_tokens,
387
- )
388
-
389
- # Normalize response into iterable items safely
390
- outputs = []
391
- if response is None:
392
- outputs = []
393
- else:
394
- # response might be object or dict; try known attributes/keys
395
- if isinstance(response, dict):
396
- # common dict keys
397
- if isinstance(response.get("output"), list):
398
- outputs = response.get("output") or []
399
- elif isinstance(response.get("candidates"), list):
400
- outputs = response.get("candidates") or []
401
- elif isinstance(response.get("items"), list):
402
- outputs = response.get("items") or []
403
- elif isinstance(response.get("responses"), list):
404
- outputs = response.get("responses") or []
405
  else:
406
- # fallback: try to find list-valued entries
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
  for v in response.values():
408
- if isinstance(v, list):
409
  outputs = v
410
  break
411
  else:
412
- # try attribute access
413
- attr_candidates = []
414
  for attr in ("output", "candidates", "items", "responses"):
415
  val = getattr(response, attr, None)
416
- if isinstance(val, list):
417
- attr_candidates = val
418
  break
419
- outputs = attr_candidates or []
420
-
421
- # Ensure we have a list
422
- if not isinstance(outputs, list):
423
- outputs = list(outputs) if outputs else []
424
-
425
- text_pieces = []
426
- # Iterate safely through outputs (may be dicts or objects)
427
- for item in outputs:
428
- if item is None:
429
- continue
430
- # attempt to extract a 'content' bag
431
- contents = None
432
- if isinstance(item, dict):
433
- contents = item.get("content") or item.get("text") or item.get("message") or item.get("output")
434
- else:
435
- contents = getattr(item, "content", None) or getattr(item, "text", None) or getattr(item, "message", None) or getattr(item, "output", None)
436
-
437
- # If contents is a single string, take it
438
- if isinstance(contents, str):
439
- if contents.strip():
440
- text_pieces.append(contents.strip())
441
- continue
442
-
443
- # If contents is list-like, iterate
444
- if isinstance(contents, (list, tuple)):
445
- for c in contents:
446
- if c is None:
447
- continue
448
- if isinstance(c, str):
449
- if c.strip():
450
- text_pieces.append(c.strip())
451
- continue
452
- c_text = None
453
- if isinstance(c, dict):
454
- c_text = c.get("text") or c.get("content") or None
455
- else:
456
- c_text = getattr(c, "text", None) or getattr(c, "content", None)
457
- if c_text:
458
- text_pieces.append(str(c_text).strip())
459
- continue
460
-
461
- # If the item itself contains direct text fields
462
- direct_txt = None
463
- if isinstance(item, dict):
464
- direct_txt = item.get("text") or item.get("output_text") or item.get("message")
465
- else:
466
- direct_txt = getattr(item, "text", None) or getattr(item, "output_text", None) or getattr(item, "message", None)
467
- if direct_txt:
468
- text_pieces.append(str(direct_txt).strip())
469
-
470
- # final fallback: top-level text on response
471
- if not text_pieces:
472
- top_text = None
473
- if isinstance(response, dict):
474
- top_text = response.get("text") or response.get("message") or None
475
- else:
476
- top_text = getattr(response, "text", None) or getattr(response, "message", None)
477
- if top_text:
478
- text_pieces.append(str(top_text).strip())
479
-
480
- # dedupe preserving order
481
- seen = set()
482
- filtered = []
483
- for t in text_pieces:
484
- if not isinstance(t, str):
485
- continue
486
- if t and t not in seen:
487
- filtered.append(t)
488
- seen.add(t)
489
- out = "\n\n".join(filtered)
490
-
491
- # post-process output to remove prompt echo or placeholders
 
 
 
 
 
 
492
  if out:
493
  out = remove_prompt_echo(prompt_text, out)
494
  p = prompt_text
@@ -506,9 +536,11 @@ if generate_now and not st.session_state.get("busy"):
506
  st.session_state["last_error"] = ""
507
  st.subheader("Analysis Result")
508
  st.markdown(out if out else "No analysis returned.")
509
- st.caption(est_cost_caption)
 
510
  except Exception as e:
511
- st.session_state["last_error"] = str(e)
 
512
  st.error("An error occurred while generating the story. You can try Generate again; the uploaded video will be reused.")
513
  finally:
514
  st.session_state["busy"] = False
 
3
  import time
4
  import string
5
  import hashlib
6
+ import traceback
7
  from glob import glob
8
  from pathlib import Path
9
  from difflib import SequenceMatcher
 
353
  est_tokens = max_tokens
354
  est_cost_caption = f"Est. max tokens: {est_tokens}"
355
 
356
+ # First try Agent, but guard and FALLBACK to direct genai responses if Agent fails or returns empty.
357
  agent = maybe_create_agent(model_used)
358
+ debug_info = {"agent_attempted": False, "agent_ok": False, "agent_error": None, "agent_response_has_text": False}
359
  if agent:
360
+ debug_info["agent_attempted"] = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
  try:
362
+ with st.spinner("Generating description via Agent..."):
363
+ if not processed:
364
+ raise RuntimeError("Processed file missing for agent generation")
365
+ # call agent.run inside try/except to catch library IndexError
366
+ agent_response = agent.run(prompt_text, videos=[processed], safety_settings=safety_settings)
367
+ # Try to extract text from common attributes; be defensive
368
+ agent_text = getattr(agent_response, "content", None) or getattr(agent_response, "outputText", None) or None
369
+ if not agent_text:
370
+ # try dict-like access
371
+ try:
372
+ if isinstance(agent_response, dict):
373
+ # check common keys
374
+ for k in ("content", "outputText", "text"):
375
+ if k in agent_response and agent_response[k]:
376
+ agent_text = agent_response[k]
377
+ break
378
+ except Exception:
379
+ pass
380
+ if agent_text and str(agent_text).strip():
381
+ out = str(agent_text).strip()
382
+ debug_info["agent_ok"] = True
383
+ debug_info["agent_response_has_text"] = True
 
 
 
 
 
 
 
 
 
384
  else:
385
+ # Agent returned but had no usable text; set a marker to fallback
386
+ debug_info["agent_ok"] = False
387
+ except Exception as ae:
388
+ # Save agent error and continue to fallback path instead of crashing
389
+ debug_info["agent_error"] = f"{ae}"
390
+ # include traceback for debugging
391
+ debug_info["agent_traceback"] = traceback.format_exc()
392
+ # Do not re-raise; we'll fallback to genai.responses.generate below
393
+
394
+ if not out:
395
+ # Fallback to direct Responses API flow
396
+ try:
397
+ if not HAS_GENAI or genai is None:
398
+ raise RuntimeError("Responses API not available; install google.generativeai SDK.")
399
+ genai.configure(api_key=key_to_use)
400
+ fname = file_name_or_id(processed)
401
+ if not fname:
402
+ raise RuntimeError("Uploaded file missing name/id")
403
+ system_msg = {"role": "system", "content": prompt_text}
404
+ user_msg = {"role": "user", "content": "Please summarize the attached video."}
405
+
406
+ try:
407
+ response = genai.responses.generate(
408
+ model=model_used,
409
+ messages=[system_msg, user_msg],
410
+ files=[{"name": fname}],
411
+ safety_settings=safety_settings,
412
+ max_output_tokens=max_tokens,
413
+ )
414
+ except TypeError:
415
+ response = genai.responses.generate(
416
+ model=model_used,
417
+ input=[{"text": prompt_text, "files": [{"name": fname}]}],
418
+ safety_settings=safety_settings,
419
+ max_output_tokens=max_tokens,
420
+ )
421
+
422
+ # Defensive normalization of response -> outputs list
423
+ outputs = []
424
+ if response is None:
425
+ outputs = []
426
+ elif isinstance(response, dict):
427
+ for key in ("output", "candidates", "items", "responses"):
428
+ val = response.get(key)
429
+ if isinstance(val, list) and val:
430
+ outputs = val
431
+ break
432
+ if not outputs:
433
  for v in response.values():
434
+ if isinstance(v, list) and v:
435
  outputs = v
436
  break
437
  else:
 
 
438
  for attr in ("output", "candidates", "items", "responses"):
439
  val = getattr(response, attr, None)
440
+ if isinstance(val, list) and val:
441
+ outputs = val
442
  break
443
+
444
+ # ensure list
445
+ if not isinstance(outputs, list):
446
+ outputs = list(outputs) if outputs else []
447
+
448
+ # extract text pieces safely
449
+ text_pieces = []
450
+ for item in outputs:
451
+ if item is None:
452
+ continue
453
+ # item may be dict or object; attempt to find text-rich fields
454
+ cand_contents = None
455
+ if isinstance(item, dict):
456
+ for k in ("content", "text", "message", "output_text", "output"):
457
+ if k in item and item[k]:
458
+ cand_contents = item[k]
459
+ break
460
+ else:
461
+ for k in ("content", "text", "message", "output", "output_text"):
462
+ cand_contents = getattr(item, k, None)
463
+ if cand_contents:
464
+ break
465
+
466
+ if isinstance(cand_contents, str):
467
+ if cand_contents.strip():
468
+ text_pieces.append(cand_contents.strip())
469
+ continue
470
+
471
+ if isinstance(cand_contents, (list, tuple)):
472
+ for c in cand_contents:
473
+ if c is None:
474
+ continue
475
+ if isinstance(c, str):
476
+ if c.strip():
477
+ text_pieces.append(c.strip())
478
+ continue
479
+ if isinstance(c, dict):
480
+ t = c.get("text") or c.get("content")
481
+ else:
482
+ t = getattr(c, "text", None) or getattr(c, "content", None)
483
+ if t:
484
+ text_pieces.append(str(t).strip())
485
+ continue
486
+
487
+ direct = None
488
+ if isinstance(item, dict):
489
+ direct = item.get("text") or item.get("output_text") or item.get("message")
490
+ else:
491
+ direct = getattr(item, "text", None) or getattr(item, "output_text", None) or getattr(item, "message", None)
492
+ if direct:
493
+ text_pieces.append(str(direct).strip())
494
+
495
+ if not text_pieces:
496
+ top_text = None
497
+ if isinstance(response, dict):
498
+ top_text = response.get("text") or response.get("message")
499
+ else:
500
+ top_text = getattr(response, "text", None) or getattr(response, "message", None)
501
+ if top_text:
502
+ text_pieces.append(str(top_text).strip())
503
+
504
+ # dedupe preserving order
505
+ seen = set()
506
+ filtered = []
507
+ for t in text_pieces:
508
+ if not isinstance(t, str):
509
+ continue
510
+ if t and t not in seen:
511
+ filtered.append(t)
512
+ seen.add(t)
513
+ out = "\n\n".join(filtered)
514
+ except Exception as e:
515
+ # Capture clear error to UI and include debug_info
516
+ tb = traceback.format_exc()
517
+ st.session_state["last_error"] = f"Responses API error: {e}\n\nDebug: {debug_info}\n\nTraceback:\n{tb}"
518
+ st.error("An error occurred while generating the story. You can try Generate again; the uploaded video will be reused.")
519
+ out = ""
520
+
521
+ # post-process output
522
  if out:
523
  out = remove_prompt_echo(prompt_text, out)
524
  p = prompt_text
 
536
  st.session_state["last_error"] = ""
537
  st.subheader("Analysis Result")
538
  st.markdown(out if out else "No analysis returned.")
539
+ st.caption(f"Est. max tokens: {est_tokens}")
540
+
541
  except Exception as e:
542
+ tb = traceback.format_exc()
543
+ st.session_state["last_error"] = f"{e}\n\nDebug: {locals().get('debug_info', debug_info)}\n\nTraceback:\n{tb}"
544
  st.error("An error occurred while generating the story. You can try Generate again; the uploaded video will be reused.")
545
  finally:
546
  st.session_state["busy"] = False