CB commited on
Commit
4ff4bd1
·
verified ·
1 Parent(s): 6e43b5c

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +102 -177
streamlit_app.py CHANGED
@@ -353,141 +353,85 @@ if generate_now and not st.session_state.get("busy"):
353
  est_tokens = max_tokens
354
  est_cost_caption = f"Est. max tokens: {est_tokens}"
355
 
356
- # Use Responses API directly (avoid phi.Agent which can raise when candidates is empty)
357
- genai.configure(api_key=key_to_use)
358
- fname = file_name_or_id(processed)
359
- if not fname:
360
- raise RuntimeError("Uploaded file missing name/id")
361
- system_msg = {"role": "system", "content": prompt_text}
362
- user_msg = {"role": "user", "content": "Please summarize the attached video."}
363
-
364
- try:
365
- response = genai.responses.generate(
366
- model=model_used,
367
- messages=[system_msg, user_msg],
368
- files=[{"name": fname}],
369
- safety_settings=safety_settings,
370
- max_output_tokens=max_tokens,
371
- )
372
- except TypeError:
373
- response = genai.responses.generate(
374
- model=model_used,
375
- input=[{"text": prompt_text, "files": [{"name": fname}]}],
376
- safety_settings=safety_settings,
377
- max_output_tokens=max_tokens,
378
- )
379
-
380
- # Normalize response into a safe list of output items
381
- outputs = []
382
- if response is None:
383
- outputs = []
384
- elif isinstance(response, dict):
385
- for key in ("output", "candidates", "items", "responses"):
386
- val = response.get(key)
387
- if isinstance(val, list) and val:
388
- outputs = val
389
- break
390
- if not outputs:
391
- for v in response.values():
392
- if isinstance(v, list) and v:
393
- outputs = v
394
- break
395
- else:
396
- for attr in ("output", "candidates", "items", "responses"):
397
- val = getattr(response, attr, None)
398
- if isinstance(val, list) and val:
399
- outputs = val
400
- break
401
-
402
- # Safely extract text pieces without indexing into empty lists
403
- text_pieces = []
404
- for item in outputs:
405
- if item is None:
406
- continue
407
- # item may be dict or object
408
- if isinstance(item, dict):
409
- # try common fields
410
- txt = item.get("text") or item.get("output_text") or item.get("message")
411
- if isinstance(txt, str) and txt.strip():
412
- text_pieces.append(txt.strip())
413
- continue
414
- contents = item.get("content") or item.get("output")
415
- else:
416
- txt = getattr(item, "text", None) or getattr(item, "output_text", None) or getattr(item, "message", None)
417
- if isinstance(txt, str) and txt.strip():
418
- text_pieces.append(txt.strip())
419
- continue
420
- contents = getattr(item, "content", None) or getattr(item, "output", None)
421
-
422
- # contents may be string or list
423
- if isinstance(contents, str) and contents.strip():
424
- text_pieces.append(contents.strip())
425
- elif isinstance(contents, (list, tuple)):
426
- for c in contents:
427
- if c is None:
428
- continue
429
- if isinstance(c, str) and c.strip():
430
- text_pieces.append(c.strip())
431
- continue
432
- if isinstance(c, dict):
433
- t = c.get("text") or c.get("content")
434
- else:
435
- t = getattr(c, "text", None) or getattr(c, "content", None)
436
- if t:
437
- text_pieces.append(str(t).strip())
438
-
439
- # final fallback: top-level text
440
- if not text_pieces:
441
- top_text = getattr(response, "text", None) if not isinstance(response, dict) else (response.get("text") or response.get("message"))
442
- if top_text:
443
- text_pieces.append(str(top_text).strip())
444
-
445
- # dedupe preserving order
446
- seen = set()
447
- filtered = []
448
- for t in text_pieces:
449
- if not isinstance(t, str):
450
- continue
451
- if t and t not in seen:
452
- filtered.append(t)
453
- seen.add(t)
454
- out = "\n\n".join(filtered)
455
-
456
  try:
457
- response = genai.responses.generate(
458
- model=model_used,
459
- messages=[system_msg, user_msg],
460
- files=[{"name": fname}],
461
- safety_settings=safety_settings,
462
- max_output_tokens=max_tokens,
463
- )
464
- except TypeError:
465
- response = genai.responses.generate(
466
- model=model_used,
467
- input=[{"text": prompt_text, "files": [{"name": fname}]}],
468
- safety_settings=safety_settings,
469
- max_output_tokens=max_tokens,
470
- )
471
-
472
- # record raw shape for debugging
473
- debug_info["response_shape"] = type(response).__name__ if response is not None else "None"
474
-
475
- # SAFE normalization into list
476
- outputs = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  try:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
478
  if response is None:
479
  outputs = []
480
  elif isinstance(response, dict):
481
- # typical dict shapes
482
  for key in ("output", "candidates", "items", "responses"):
483
  val = response.get(key)
484
  if isinstance(val, list) and val:
485
  outputs = val
486
  break
487
  if not outputs:
488
- # pick first list-valued entry if any
489
  for v in response.values():
490
- if isinstance(v, list):
491
  outputs = v
492
  break
493
  else:
@@ -496,24 +440,19 @@ if generate_now and not st.session_state.get("busy"):
496
  if isinstance(val, list) and val:
497
  outputs = val
498
  break
499
- except Exception as e:
500
- # unexpected structure -> capture for debug and continue with empty outputs
501
- st.session_state["last_error"] = f"Response parsing error: {e}\n{traceback.format_exc()}"
502
- outputs = []
503
 
504
- debug_info["outputs_len"] = len(outputs)
505
- debug_info["outputs_types"] = [type(o).__name__ for o in outputs]
 
506
 
507
- # iterate without indexing
508
- text_pieces = []
509
- try:
510
  for item in outputs:
511
  if item is None:
512
  continue
513
- # get potential content container(s)
514
  cand_contents = None
515
  if isinstance(item, dict):
516
- # common keys that may hold text
517
  for k in ("content", "text", "message", "output_text", "output"):
518
  if k in item and item[k]:
519
  cand_contents = item[k]
@@ -524,13 +463,11 @@ if generate_now and not st.session_state.get("busy"):
524
  if cand_contents:
525
  break
526
 
527
- # handle string content
528
  if isinstance(cand_contents, str):
529
  if cand_contents.strip():
530
  text_pieces.append(cand_contents.strip())
531
  continue
532
 
533
- # handle list-like content
534
  if isinstance(cand_contents, (list, tuple)):
535
  for c in cand_contents:
536
  if c is None:
@@ -547,7 +484,6 @@ if generate_now and not st.session_state.get("busy"):
547
  text_pieces.append(str(t).strip())
548
  continue
549
 
550
- # fallback to direct text on item
551
  direct = None
552
  if isinstance(item, dict):
553
  direct = item.get("text") or item.get("output_text") or item.get("message")
@@ -555,31 +491,34 @@ if generate_now and not st.session_state.get("busy"):
555
  direct = getattr(item, "text", None) or getattr(item, "output_text", None) or getattr(item, "message", None)
556
  if direct:
557
  text_pieces.append(str(direct).strip())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
  except Exception as e:
559
- st.session_state["last_error"] = f"Error while extracting text pieces: {e}\n{traceback.format_exc()}"
 
 
 
 
560
 
561
- # last resort: top-level text
562
- if not text_pieces:
563
- top_text = None
564
- if isinstance(response, dict):
565
- top_text = response.get("text") or response.get("message")
566
- else:
567
- top_text = getattr(response, "text", None) or getattr(response, "message", None)
568
- if top_text:
569
- text_pieces.append(str(top_text).strip())
570
-
571
- # dedupe preserving order
572
- seen = set()
573
- filtered = []
574
- for t in text_pieces:
575
- if not isinstance(t, str):
576
- continue
577
- if t and t not in seen:
578
- filtered.append(t)
579
- seen.add(t)
580
- out = "\n\n".join(filtered)
581
-
582
- # post-process output to remove prompt echo or placeholders
583
  if out:
584
  out = remove_prompt_echo(prompt_text, out)
585
  p = prompt_text
@@ -597,35 +536,21 @@ if generate_now and not st.session_state.get("busy"):
597
  st.session_state["last_error"] = ""
598
  st.subheader("Analysis Result")
599
  st.markdown(out if out else "No analysis returned.")
600
- st.caption(est_cost_caption)
601
 
602
  except Exception as e:
603
- # Build improved error info to display in the UI
604
  tb = traceback.format_exc()
605
- # If we have debug_info, include it
606
- dbg = locals().get("debug_info") or {}
607
- # Save a concise message + trace to last_error so UI shows it
608
- st.session_state["last_error"] = f"{str(e)}\n\nDebug: {dbg}\n\nTraceback:\n{tb}"
609
  st.error("An error occurred while generating the story. You can try Generate again; the uploaded video will be reused.")
610
  finally:
611
  st.session_state["busy"] = False
612
 
613
- # show analysis if present
614
  if st.session_state.get("analysis_out"):
615
  just_loaded_same = (st.session_state.get("last_loaded_path") == st.session_state.get("videos"))
616
  if not just_loaded_same:
617
  st.subheader("Analysis Result")
618
  st.markdown(st.session_state.get("analysis_out"))
619
 
620
- # show last error and debug helper
621
  if st.session_state.get("last_error"):
622
- with st.expander("Last Error (click to expand)", expanded=True):
623
  st.write(st.session_state.get("last_error"))
624
- # If we extracted debug_info earlier, show short diagnostics
625
- try:
626
- di = locals().get("debug_info") or {}
627
- if di:
628
- st.write("Debug info (if available):")
629
- st.write(di)
630
- except Exception:
631
- pass
 
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:
 
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]
 
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:
 
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")
 
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
547
 
 
548
  if st.session_state.get("analysis_out"):
549
  just_loaded_same = (st.session_state.get("last_loaded_path") == st.session_state.get("videos"))
550
  if not just_loaded_same:
551
  st.subheader("Analysis Result")
552
  st.markdown(st.session_state.get("analysis_out"))
553
 
 
554
  if st.session_state.get("last_error"):
555
+ with st.expander("Last Error", expanded=False):
556
  st.write(st.session_state.get("last_error"))