Spaces:
Sleeping
Sleeping
CB commited on
Update streamlit_app.py
Browse files- streamlit_app.py +52 -23
streamlit_app.py
CHANGED
|
@@ -281,11 +281,20 @@ def get_runtime_api_key():
|
|
| 281 |
|
| 282 |
# ---- Simplified SDK-first + HTTP-fallback layer ----
|
| 283 |
def _normalize_model_for_http(model: str) -> str:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
if not model:
|
| 285 |
-
return "text-bison@001"
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
|
|
|
| 289 |
|
| 290 |
def _messages_to_prompt(messages):
|
| 291 |
if not messages:
|
|
@@ -300,10 +309,11 @@ def _messages_to_prompt(messages):
|
|
| 300 |
def _http_generate(api_key: str, model: str, prompt: str, max_tokens: int):
|
| 301 |
host = "https://generativelanguage.googleapis.com"
|
| 302 |
norm = _normalize_model_for_http(model)
|
|
|
|
| 303 |
candidates = [
|
| 304 |
-
f"{host}/v1/
|
| 305 |
-
f"{host}/v1beta3/
|
| 306 |
-
f"{host}/v1beta2/
|
| 307 |
]
|
| 308 |
payload = {"prompt": {"text": prompt}, "maxOutputTokens": int(max_tokens or 512)}
|
| 309 |
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
|
@@ -316,6 +326,7 @@ def _http_generate(api_key: str, model: str, prompt: str, max_tokens: int):
|
|
| 316 |
return r.json()
|
| 317 |
except Exception:
|
| 318 |
return {"text": r.text}
|
|
|
|
| 319 |
last_exc = RuntimeError(f"HTTP {r.status_code}: {r.text}")
|
| 320 |
except Exception as e:
|
| 321 |
last_exc = e
|
|
@@ -358,10 +369,13 @@ def extract_text_from_response(response):
|
|
| 358 |
return None
|
| 359 |
# dict-like (HTTP)
|
| 360 |
if isinstance(response, dict):
|
| 361 |
-
#
|
|
|
|
| 362 |
if "candidates" in response and isinstance(response["candidates"], list) and response["candidates"]:
|
| 363 |
cand = response["candidates"][0]
|
| 364 |
-
|
|
|
|
|
|
|
| 365 |
if "output" in response and isinstance(response["output"], list):
|
| 366 |
pieces = []
|
| 367 |
for item in response["output"]:
|
|
@@ -373,7 +387,12 @@ def extract_text_from_response(response):
|
|
| 373 |
return "\n\n".join(pieces)
|
| 374 |
if "text" in response and isinstance(response["text"], str):
|
| 375 |
return response["text"]
|
| 376 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
return None
|
| 378 |
# object-like (SDK)
|
| 379 |
try:
|
|
@@ -386,7 +405,7 @@ def extract_text_from_response(response):
|
|
| 386 |
pieces.append(txt)
|
| 387 |
if pieces:
|
| 388 |
return "\n\n".join(pieces)
|
| 389 |
-
txt = getattr(response, "text", None)
|
| 390 |
if txt:
|
| 391 |
return txt
|
| 392 |
except Exception:
|
|
@@ -395,6 +414,7 @@ def extract_text_from_response(response):
|
|
| 395 |
|
| 396 |
# ---- end compatibility layer ----
|
| 397 |
|
|
|
|
| 398 |
if (st.session_state.get("busy") is False) and ('generate_now' in locals() and generate_now):
|
| 399 |
if not st.session_state.get("videos"):
|
| 400 |
st.error("No video loaded. Use 'Load Video' in the sidebar.")
|
|
@@ -418,22 +438,26 @@ if (st.session_state.get("busy") is False) and ('generate_now' in locals() and g
|
|
| 418 |
|
| 419 |
upload_path = current_path
|
| 420 |
uploaded = st.session_state.get("uploaded_file")
|
|
|
|
| 421 |
if reupload_needed:
|
| 422 |
local_path = current_path
|
| 423 |
-
fast_mode = st.session_state.get("fast_mode", False)
|
| 424 |
try:
|
| 425 |
file_size_mb = os.path.getsize(local_path) / (1024 * 1024)
|
| 426 |
except Exception:
|
| 427 |
file_size_mb = 0
|
| 428 |
|
| 429 |
-
use_compression = st.session_state.get("use_compression", True)
|
| 430 |
if use_compression and not fast_mode and file_size_mb > 50:
|
| 431 |
compressed_path = str(Path(local_path).with_name(Path(local_path).stem + "_compressed.mp4"))
|
| 432 |
try:
|
| 433 |
preset = "veryfast" if fast_mode else "fast"
|
| 434 |
upload_path = compress_video(local_path, compressed_path, crf=28, preset=preset)
|
|
|
|
|
|
|
| 435 |
except Exception:
|
| 436 |
upload_path = local_path
|
|
|
|
| 437 |
|
| 438 |
if HAS_GENAI and upload_file is not None:
|
| 439 |
genai.configure(api_key=runtime_key)
|
|
@@ -453,13 +477,13 @@ if (st.session_state.get("busy") is False) and ('generate_now' in locals() and g
|
|
| 453 |
uploaded = st.session_state.get("uploaded_file")
|
| 454 |
processed = st.session_state.get("processed_file")
|
| 455 |
|
| 456 |
-
prompt_text = (analysis_prompt or default_prompt).strip()
|
| 457 |
if st.session_state.get("fast_mode"):
|
| 458 |
model_used = model_arg or "text-bison@001"
|
| 459 |
-
max_tokens = min(st.session_state.get("max_output_tokens", 512), 1024)
|
| 460 |
else:
|
| 461 |
-
model_used = model_arg
|
| 462 |
-
max_tokens = st.session_state.get("max_output_tokens", 1024)
|
| 463 |
|
| 464 |
# Ensure model_used is a short name (SDK accepts it; HTTP will normalize)
|
| 465 |
system_msg = {"role": "system", "content": "You are a helpful assistant that summarizes videos concisely in vivid detail."}
|
|
@@ -506,12 +530,18 @@ if (st.session_state.get("busy") is False) and ('generate_now' in locals() and g
|
|
| 506 |
st.subheader("Analysis Result")
|
| 507 |
st.markdown(out or "_(no text returned)_")
|
| 508 |
|
|
|
|
| 509 |
try:
|
| 510 |
if reupload_needed:
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
except Exception:
|
| 516 |
pass
|
| 517 |
|
|
@@ -560,8 +590,7 @@ with st.sidebar.expander("Manage uploads", expanded=False):
|
|
| 560 |
st.session_state["uploaded_file"] = None
|
| 561 |
st.session_state["processed_file"] = None
|
| 562 |
st.session_state["last_loaded_path"] = ""
|
| 563 |
-
st.session_state["
|
| 564 |
-
st.session_state["file_hash"] = None
|
| 565 |
try:
|
| 566 |
fname = file_name_or_id(st.session_state.get("uploaded_file"))
|
| 567 |
if fname and delete_file and HAS_GENAI:
|
|
|
|
| 281 |
|
| 282 |
# ---- Simplified SDK-first + HTTP-fallback layer ----
|
| 283 |
def _normalize_model_for_http(model: str) -> str:
|
| 284 |
+
"""
|
| 285 |
+
Return a model identifier appropriate for the HTTP path.
|
| 286 |
+
Accept inputs like:
|
| 287 |
+
- "text-bison@001" -> "models/text-bison@001"
|
| 288 |
+
- "models/text-bison@001" -> "models/text-bison@001"
|
| 289 |
+
- "models/foo" -> "models/foo"
|
| 290 |
+
- "foo" -> "models/foo"
|
| 291 |
+
"""
|
| 292 |
if not model:
|
| 293 |
+
return "models/text-bison@001"
|
| 294 |
+
m = model.strip()
|
| 295 |
+
if m.startswith("models/"):
|
| 296 |
+
return m
|
| 297 |
+
return f"models/{m}"
|
| 298 |
|
| 299 |
def _messages_to_prompt(messages):
|
| 300 |
if not messages:
|
|
|
|
| 309 |
def _http_generate(api_key: str, model: str, prompt: str, max_tokens: int):
|
| 310 |
host = "https://generativelanguage.googleapis.com"
|
| 311 |
norm = _normalize_model_for_http(model)
|
| 312 |
+
# Try several API paths. norm already contains "models/..."
|
| 313 |
candidates = [
|
| 314 |
+
f"{host}/v1/{norm}:generate",
|
| 315 |
+
f"{host}/v1beta3/{norm}:generate",
|
| 316 |
+
f"{host}/v1beta2/{norm}:generate",
|
| 317 |
]
|
| 318 |
payload = {"prompt": {"text": prompt}, "maxOutputTokens": int(max_tokens or 512)}
|
| 319 |
headers = {"Authorization": f"Bearer {api_key}", "Content-Type": "application/json"}
|
|
|
|
| 326 |
return r.json()
|
| 327 |
except Exception:
|
| 328 |
return {"text": r.text}
|
| 329 |
+
# Explicit 404 handling: try next candidate
|
| 330 |
last_exc = RuntimeError(f"HTTP {r.status_code}: {r.text}")
|
| 331 |
except Exception as e:
|
| 332 |
last_exc = e
|
|
|
|
| 369 |
return None
|
| 370 |
# dict-like (HTTP)
|
| 371 |
if isinstance(response, dict):
|
| 372 |
+
# Try common shapes
|
| 373 |
+
# 1) "candidates": [ { "content": "...", "text": "..." } ]
|
| 374 |
if "candidates" in response and isinstance(response["candidates"], list) and response["candidates"]:
|
| 375 |
cand = response["candidates"][0]
|
| 376 |
+
if isinstance(cand, dict):
|
| 377 |
+
return cand.get("content") or cand.get("text") or response.get("text")
|
| 378 |
+
# 2) "output": [ { "content": "..."} ] or "output_text"
|
| 379 |
if "output" in response and isinstance(response["output"], list):
|
| 380 |
pieces = []
|
| 381 |
for item in response["output"]:
|
|
|
|
| 387 |
return "\n\n".join(pieces)
|
| 388 |
if "text" in response and isinstance(response["text"], str):
|
| 389 |
return response["text"]
|
| 390 |
+
if "outputText" in response and isinstance(response["outputText"], str):
|
| 391 |
+
return response["outputText"]
|
| 392 |
+
# fallback: try joining string fields
|
| 393 |
+
for k in ("result", "generated_text", "description"):
|
| 394 |
+
if k in response and isinstance(response[k], str):
|
| 395 |
+
return response[k]
|
| 396 |
return None
|
| 397 |
# object-like (SDK)
|
| 398 |
try:
|
|
|
|
| 405 |
pieces.append(txt)
|
| 406 |
if pieces:
|
| 407 |
return "\n\n".join(pieces)
|
| 408 |
+
txt = getattr(response, "text", None) or getattr(response, "output_text", None)
|
| 409 |
if txt:
|
| 410 |
return txt
|
| 411 |
except Exception:
|
|
|
|
| 414 |
|
| 415 |
# ---- end compatibility layer ----
|
| 416 |
|
| 417 |
+
# The main generation block (patched, robust)
|
| 418 |
if (st.session_state.get("busy") is False) and ('generate_now' in locals() and generate_now):
|
| 419 |
if not st.session_state.get("videos"):
|
| 420 |
st.error("No video loaded. Use 'Load Video' in the sidebar.")
|
|
|
|
| 438 |
|
| 439 |
upload_path = current_path
|
| 440 |
uploaded = st.session_state.get("uploaded_file")
|
| 441 |
+
compressed_path = None
|
| 442 |
if reupload_needed:
|
| 443 |
local_path = current_path
|
| 444 |
+
fast_mode = bool(st.session_state.get("fast_mode", False))
|
| 445 |
try:
|
| 446 |
file_size_mb = os.path.getsize(local_path) / (1024 * 1024)
|
| 447 |
except Exception:
|
| 448 |
file_size_mb = 0
|
| 449 |
|
| 450 |
+
use_compression = bool(st.session_state.get("use_compression", True))
|
| 451 |
if use_compression and not fast_mode and file_size_mb > 50:
|
| 452 |
compressed_path = str(Path(local_path).with_name(Path(local_path).stem + "_compressed.mp4"))
|
| 453 |
try:
|
| 454 |
preset = "veryfast" if fast_mode else "fast"
|
| 455 |
upload_path = compress_video(local_path, compressed_path, crf=28, preset=preset)
|
| 456 |
+
if Path(upload_path) == Path(local_path):
|
| 457 |
+
compressed_path = None
|
| 458 |
except Exception:
|
| 459 |
upload_path = local_path
|
| 460 |
+
compressed_path = None
|
| 461 |
|
| 462 |
if HAS_GENAI and upload_file is not None:
|
| 463 |
genai.configure(api_key=runtime_key)
|
|
|
|
| 477 |
uploaded = st.session_state.get("uploaded_file")
|
| 478 |
processed = st.session_state.get("processed_file")
|
| 479 |
|
| 480 |
+
prompt_text = (analysis_prompt or default_prompt or "").strip()
|
| 481 |
if st.session_state.get("fast_mode"):
|
| 482 |
model_used = model_arg or "text-bison@001"
|
| 483 |
+
max_tokens = min(int(st.session_state.get("max_output_tokens", 512)), 1024)
|
| 484 |
else:
|
| 485 |
+
model_used = model_arg or "text-bison@001"
|
| 486 |
+
max_tokens = int(st.session_state.get("max_output_tokens", 1024))
|
| 487 |
|
| 488 |
# Ensure model_used is a short name (SDK accepts it; HTTP will normalize)
|
| 489 |
system_msg = {"role": "system", "content": "You are a helpful assistant that summarizes videos concisely in vivid detail."}
|
|
|
|
| 530 |
st.subheader("Analysis Result")
|
| 531 |
st.markdown(out or "_(no text returned)_")
|
| 532 |
|
| 533 |
+
# Clean up compressed temporary file only (don't remove original unless it was a true temp)
|
| 534 |
try:
|
| 535 |
if reupload_needed:
|
| 536 |
+
try:
|
| 537 |
+
if compressed_path:
|
| 538 |
+
p = Path(compressed_path)
|
| 539 |
+
if p.exists():
|
| 540 |
+
p.unlink(missing_ok=True)
|
| 541 |
+
except Exception:
|
| 542 |
+
pass
|
| 543 |
+
# don't delete original unless it's intended to be removed (conservative)
|
| 544 |
+
# If you want original removed, enable a setting and handle carefully.
|
| 545 |
except Exception:
|
| 546 |
pass
|
| 547 |
|
|
|
|
| 590 |
st.session_state["uploaded_file"] = None
|
| 591 |
st.session_state["processed_file"] = None
|
| 592 |
st.session_state["last_loaded_path"] = ""
|
| 593 |
+
st.session_state[""] = None
|
|
|
|
| 594 |
try:
|
| 595 |
fname = file_name_or_id(st.session_state.get("uploaded_file"))
|
| 596 |
if fname and delete_file and HAS_GENAI:
|