CB commited on
Commit
ae82b95
·
verified ·
1 Parent(s): 251787a

Update streamlit_app.py

Browse files
Files changed (1) hide show
  1. streamlit_app.py +136 -131
streamlit_app.py CHANGED
@@ -7,6 +7,7 @@ import traceback
7
  from glob import glob
8
  from pathlib import Path
9
  from difflib import SequenceMatcher
 
10
 
11
  import yt_dlp
12
  import ffmpeg
@@ -169,7 +170,6 @@ default_prompt = (
169
  analysis_prompt = settings_exp.text_area("Enter analysis", value=default_prompt, height=140)
170
  settings_exp.text_input("Video Password (if needed)", key="video-password", placeholder="password", type="password")
171
 
172
- # Show which key is active
173
  key_source = "session" if st.session_state.get("api_key") else ".env" if os.getenv("GOOGLE_API_KEY") else "none"
174
  settings_exp.caption(f"Using API key from: **{key_source}**")
175
 
@@ -229,6 +229,137 @@ def remove_prompt_echo(prompt: str, text: str, check_len: int = 600, ratio_thres
229
  return b_full[len(ph):].lstrip(" \n:-")
230
  return text
231
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
  col1, col2 = st.columns([1, 3])
233
  with col1:
234
  generate_now = st.button("Generate the story", type="primary", disabled=not bool(get_effective_api_key()))
@@ -315,19 +446,7 @@ if generate_now and not st.session_state.get("busy"):
315
  if not HAS_GENAI:
316
  raise RuntimeError("google.generativeai SDK not available; install it.")
317
  local_path = current_path
318
- upload_path = local_path
319
- try:
320
- file_size_mb = os.path.getsize(local_path) / (1024 * 1024)
321
- except Exception:
322
- file_size_mb = 0
323
-
324
- # If file is over 50MB, attempt compression. If compression fails, fall back to original file.
325
- if file_size_mb > 50:
326
- compressed_path = str(Path(local_path).with_name(Path(local_path).stem + "_compressed.mp4"))
327
- try:
328
- upload_path = compress_video(local_path, compressed_path, crf=28, preset="fast")
329
- except Exception:
330
- upload_path = local_path
331
 
332
  with st.spinner("Uploading video..."):
333
  uploaded = upload_video_sdk(upload_path)
@@ -338,15 +457,12 @@ if generate_now and not st.session_state.get("busy"):
338
  st.session_state["file_hash"] = current_hash
339
 
340
  prompt_text = (analysis_prompt.strip() or default_prompt).strip()
341
-
342
  out = ""
343
  model_used = model_id
344
  max_tokens = 1024
345
-
346
  est_tokens = max_tokens
347
- est_cost_caption = f"Est. max tokens: {est_tokens}"
348
 
349
- # First try Agent, but guard and FALLBACK to direct genai responses if Agent fails or returns empty.
350
  agent = maybe_create_agent(model_used)
351
  debug_info = {"agent_attempted": False, "agent_ok": False, "agent_error": None, "agent_response_has_text": False}
352
  if agent:
@@ -378,125 +494,14 @@ if generate_now and not st.session_state.get("busy"):
378
 
379
  if not out:
380
  try:
381
- if not HAS_GENAI or genai is None:
382
- raise RuntimeError("Responses API not available; install google.generativeai SDK.")
383
- genai.configure(api_key=key_to_use)
384
- fname = file_name_or_id(processed)
385
- if not fname:
386
- raise RuntimeError("Uploaded file missing name/id")
387
- system_msg = {"role": "system", "content": prompt_text}
388
- user_msg = {"role": "user", "content": "Please summarize the attached video."}
389
-
390
- try:
391
- response = genai.responses.generate(
392
- model=model_used,
393
- messages=[system_msg, user_msg],
394
- files=[{"name": fname}],
395
- safety_settings=safety_settings,
396
- max_output_tokens=max_tokens,
397
- )
398
- except TypeError:
399
- response = genai.responses.generate(
400
- model=model_used,
401
- input=[{"text": prompt_text, "files": [{"name": fname}]}],
402
- safety_settings=safety_settings,
403
- max_output_tokens=max_tokens,
404
- )
405
-
406
- outputs = []
407
- if response is None:
408
- outputs = []
409
- elif isinstance(response, dict):
410
- for key in ("output", "candidates", "items", "responses"):
411
- val = response.get(key)
412
- if isinstance(val, list) and val:
413
- outputs = val
414
- break
415
- if not outputs:
416
- for v in response.values():
417
- if isinstance(v, list) and v:
418
- outputs = v
419
- break
420
- else:
421
- for attr in ("output", "candidates", "items", "responses"):
422
- val = getattr(response, attr, None)
423
- if isinstance(val, list) and val:
424
- outputs = val
425
- break
426
-
427
- if not isinstance(outputs, list):
428
- outputs = list(outputs) if outputs else []
429
-
430
- text_pieces = []
431
- for item in outputs:
432
- if item is None:
433
- continue
434
- cand_contents = None
435
- if isinstance(item, dict):
436
- for k in ("content", "text", "message", "output_text", "output"):
437
- if k in item and item[k]:
438
- cand_contents = item[k]
439
- break
440
- else:
441
- for k in ("content", "text", "message", "output", "output_text"):
442
- cand_contents = getattr(item, k, None)
443
- if cand_contents:
444
- break
445
-
446
- if isinstance(cand_contents, str):
447
- if cand_contents.strip():
448
- text_pieces.append(cand_contents.strip())
449
- continue
450
-
451
- if isinstance(cand_contents, (list, tuple)):
452
- for c in cand_contents:
453
- if c is None:
454
- continue
455
- if isinstance(c, str):
456
- if c.strip():
457
- text_pieces.append(c.strip())
458
- continue
459
- if isinstance(c, dict):
460
- t = c.get("text") or c.get("content")
461
- else:
462
- t = getattr(c, "text", None) or getattr(c, "content", None)
463
- if t:
464
- text_pieces.append(str(t).strip())
465
- continue
466
-
467
- direct = None
468
- if isinstance(item, dict):
469
- direct = item.get("text") or item.get("output_text") or item.get("message")
470
- else:
471
- direct = getattr(item, "text", None) or getattr(item, "output_text", None) or getattr(item, "message", None)
472
- if direct:
473
- text_pieces.append(str(direct).strip())
474
-
475
- if not text_pieces:
476
- top_text = None
477
- if isinstance(response, dict):
478
- top_text = response.get("text") or response.get("message")
479
- else:
480
- top_text = getattr(response, "text", None) or getattr(response, "message", None)
481
- if top_text:
482
- text_pieces.append(str(top_text).strip())
483
-
484
- seen = set()
485
- filtered = []
486
- for t in text_pieces:
487
- if not isinstance(t, str):
488
- continue
489
- if t and t not in seen:
490
- filtered.append(t)
491
- seen.add(t)
492
- out = "\n\n".join(filtered)
493
  except Exception as e:
494
  tb = traceback.format_exc()
495
  st.session_state["last_error"] = f"Responses API error: {e}\n\nDebug: {debug_info}\n\nTraceback:\n{tb}"
496
  st.error("An error occurred while generating the story. You can try Generate again; the uploaded video will be reused.")
497
  out = ""
498
 
499
- # post-process output
500
  if out:
501
  out = remove_prompt_echo(prompt_text, out)
502
  p = prompt_text
 
7
  from glob import glob
8
  from pathlib import Path
9
  from difflib import SequenceMatcher
10
+ import concurrent.futures
11
 
12
  import yt_dlp
13
  import ffmpeg
 
170
  analysis_prompt = settings_exp.text_area("Enter analysis", value=default_prompt, height=140)
171
  settings_exp.text_input("Video Password (if needed)", key="video-password", placeholder="password", type="password")
172
 
 
173
  key_source = "session" if st.session_state.get("api_key") else ".env" if os.getenv("GOOGLE_API_KEY") else "none"
174
  settings_exp.caption(f"Using API key from: **{key_source}**")
175
 
 
229
  return b_full[len(ph):].lstrip(" \n:-")
230
  return text
231
 
232
+ def compress_video_if_large(local_path: str, threshold_mb: int = 50):
233
+ try:
234
+ file_size_mb = os.path.getsize(local_path) / (1024 * 1024)
235
+ except Exception:
236
+ return local_path
237
+ if file_size_mb <= threshold_mb:
238
+ return local_path
239
+ compressed_path = str(Path(local_path).with_name(Path(local_path).stem + "_compressed.mp4"))
240
+ try:
241
+ return compress_video(local_path, compressed_path, crf=28, preset="fast")
242
+ except Exception:
243
+ return local_path
244
+
245
+ def generate_via_responses_api(prompt_text: str, processed, model_used: str, max_tokens: int = 1024):
246
+ key = get_effective_api_key()
247
+ if not key:
248
+ raise RuntimeError("No API key provided")
249
+ if not HAS_GENAI or genai is None:
250
+ raise RuntimeError("Responses API not available; install google.generativeai SDK.")
251
+ genai.configure(api_key=key)
252
+ fname = file_name_or_id(processed)
253
+ if not fname:
254
+ raise RuntimeError("Uploaded file missing name/id")
255
+ system_msg = {"role": "system", "content": prompt_text}
256
+ user_msg = {"role": "user", "content": "Please summarize the attached video."}
257
+ try:
258
+ response = genai.responses.generate(
259
+ model=model_used,
260
+ messages=[system_msg, user_msg],
261
+ files=[{"name": fname}],
262
+ safety_settings=safety_settings,
263
+ max_output_tokens=max_tokens,
264
+ )
265
+ except TypeError:
266
+ response = genai.responses.generate(
267
+ model=model_used,
268
+ input=[{"text": prompt_text, "files": [{"name": fname}]}],
269
+ safety_settings=safety_settings,
270
+ max_output_tokens=max_tokens,
271
+ )
272
+
273
+ # Normalize outputs into text pieces
274
+ outputs = []
275
+ if response is None:
276
+ outputs = []
277
+ elif isinstance(response, dict):
278
+ for key in ("output", "candidates", "items", "responses"):
279
+ val = response.get(key)
280
+ if isinstance(val, list) and val:
281
+ outputs = val
282
+ break
283
+ if not outputs:
284
+ for v in response.values():
285
+ if isinstance(v, list) and v:
286
+ outputs = v
287
+ break
288
+ else:
289
+ for attr in ("output", "candidates", "items", "responses"):
290
+ val = getattr(response, attr, None)
291
+ if isinstance(val, list) and val:
292
+ outputs = val
293
+ break
294
+
295
+ if not isinstance(outputs, list):
296
+ outputs = list(outputs) if outputs else []
297
+
298
+ text_pieces = []
299
+ for item in outputs:
300
+ if item is None:
301
+ continue
302
+ cand_contents = None
303
+ if isinstance(item, dict):
304
+ for k in ("content", "text", "message", "output_text", "output"):
305
+ if k in item and item[k]:
306
+ cand_contents = item[k]
307
+ break
308
+ else:
309
+ for k in ("content", "text", "message", "output", "output_text"):
310
+ cand_contents = getattr(item, k, None)
311
+ if cand_contents:
312
+ break
313
+
314
+ if isinstance(cand_contents, str):
315
+ if cand_contents.strip():
316
+ text_pieces.append(cand_contents.strip())
317
+ continue
318
+
319
+ if isinstance(cand_contents, (list, tuple)):
320
+ for c in cand_contents:
321
+ if c is None:
322
+ continue
323
+ if isinstance(c, str):
324
+ if c.strip():
325
+ text_pieces.append(c.strip())
326
+ continue
327
+ if isinstance(c, dict):
328
+ t = c.get("text") or c.get("content")
329
+ else:
330
+ t = getattr(c, "text", None) or getattr(c, "content", None)
331
+ if t:
332
+ text_pieces.append(str(t).strip())
333
+ continue
334
+
335
+ direct = None
336
+ if isinstance(item, dict):
337
+ direct = item.get("text") or item.get("output_text") or item.get("message")
338
+ else:
339
+ direct = getattr(item, "text", None) or getattr(item, "output_text", None) or getattr(item, "message", None)
340
+ if direct:
341
+ text_pieces.append(str(direct).strip())
342
+
343
+ if not text_pieces:
344
+ top_text = None
345
+ if isinstance(response, dict):
346
+ top_text = response.get("text") or response.get("message")
347
+ else:
348
+ top_text = getattr(response, "text", None) or getattr(response, "message", None)
349
+ if top_text:
350
+ text_pieces.append(str(top_text).strip())
351
+
352
+ # Deduplicate, preserve order
353
+ seen = set()
354
+ filtered = []
355
+ for t in text_pieces:
356
+ if not isinstance(t, str):
357
+ continue
358
+ if t and t not in seen:
359
+ filtered.append(t)
360
+ seen.add(t)
361
+ return "\n\n".join(filtered)
362
+
363
  col1, col2 = st.columns([1, 3])
364
  with col1:
365
  generate_now = st.button("Generate the story", type="primary", disabled=not bool(get_effective_api_key()))
 
446
  if not HAS_GENAI:
447
  raise RuntimeError("google.generativeai SDK not available; install it.")
448
  local_path = current_path
449
+ upload_path = compress_video_if_large(local_path)
 
 
 
 
 
 
 
 
 
 
 
 
450
 
451
  with st.spinner("Uploading video..."):
452
  uploaded = upload_video_sdk(upload_path)
 
457
  st.session_state["file_hash"] = current_hash
458
 
459
  prompt_text = (analysis_prompt.strip() or default_prompt).strip()
 
460
  out = ""
461
  model_used = model_id
462
  max_tokens = 1024
 
463
  est_tokens = max_tokens
 
464
 
465
+ # Try Agent first, fallback to Responses API
466
  agent = maybe_create_agent(model_used)
467
  debug_info = {"agent_attempted": False, "agent_ok": False, "agent_error": None, "agent_response_has_text": False}
468
  if agent:
 
494
 
495
  if not out:
496
  try:
497
+ with st.spinner("Generating description via Responses API..."):
498
+ out = generate_via_responses_api(prompt_text, processed, model_used, max_tokens=max_tokens)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
499
  except Exception as e:
500
  tb = traceback.format_exc()
501
  st.session_state["last_error"] = f"Responses API error: {e}\n\nDebug: {debug_info}\n\nTraceback:\n{tb}"
502
  st.error("An error occurred while generating the story. You can try Generate again; the uploaded video will be reused.")
503
  out = ""
504
 
 
505
  if out:
506
  out = remove_prompt_echo(prompt_text, out)
507
  p = prompt_text