Hug0endob commited on
Commit
a6bec5f
·
verified ·
1 Parent(s): 6b83d17

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +17 -19
app.py CHANGED
@@ -31,13 +31,11 @@ def is_remote(src: str) -> bool:
31
  return bool(src) and src.startswith(("http://", "https://"))
32
 
33
  def fetch_bytes(src: str, stream_threshold=20 * 1024 * 1024) -> bytes:
34
- """Fetch bytes. For remote large files stream to a temp file then read."""
35
  if is_remote(src):
36
  with requests.get(src, timeout=60, stream=True) as r:
37
  r.raise_for_status()
38
  content_length = r.headers.get("content-length")
39
  if content_length and int(content_length) > stream_threshold:
40
- # stream to temp file
41
  fd, path = tempfile.mkstemp()
42
  os.close(fd)
43
  with open(path, "wb") as f:
@@ -59,7 +57,6 @@ def fetch_bytes(src: str, stream_threshold=20 * 1024 * 1024) -> bytes:
59
  def convert_to_jpeg_bytes(media_bytes: bytes, base_h=480) -> bytes:
60
  img = Image.open(BytesIO(media_bytes))
61
  try:
62
- # take first frame for animated formats
63
  img.seek(0)
64
  except Exception:
65
  pass
@@ -95,7 +92,6 @@ def choose_model_for_src(src: str):
95
  return DEFAULT_VIDEO_MODEL
96
  if ext in IMAGE_EXTS:
97
  return DEFAULT_IMAGE_MODEL
98
- # fallback: remote URLs likely videos, local files inspect existence
99
  if is_remote(src):
100
  return DEFAULT_VIDEO_MODEL
101
  return DEFAULT_IMAGE_MODEL
@@ -121,12 +117,14 @@ def extract_delta(chunk):
121
  data = getattr(chunk, "data", None) or getattr(chunk, "response", None) or getattr(chunk, "delta", None)
122
  if not data:
123
  return None
 
124
  try:
125
- # try common shapes and coerce to string
126
  c = data.choices[0].delta
127
  if isinstance(c, dict):
128
  txt = c.get("content") or c.get("text")
129
- return str(txt) if txt is not None else None
 
 
130
  except Exception:
131
  pass
132
  try:
@@ -135,7 +133,9 @@ def extract_delta(chunk):
135
  txt = msg.get("content")
136
  else:
137
  txt = getattr(msg, "content", None)
138
- return str(txt) if txt is not None else None
 
 
139
  except Exception:
140
  pass
141
  try:
@@ -147,14 +147,12 @@ def extract_delta(chunk):
147
  def generate_final_text(src: str, custom_prompt: str, api_key: str):
148
  client = get_client(api_key)
149
  prompt = (custom_prompt.strip() if custom_prompt and custom_prompt.strip() else "Please provide a detailed visual review.")
150
- lower = (src or "").lower()
151
  ext = ext_from_src(src)
152
  is_image = ext in IMAGE_EXTS or (not is_remote(src) and os.path.isfile(src) and ext in IMAGE_EXTS)
153
  parts = []
154
 
155
  def stream_and_collect(model, messages):
156
  try:
157
- # prefer streaming if available; fall back to non-streaming
158
  stream_gen = None
159
  try:
160
  stream_gen = client.chat.stream(model=model, messages=messages)
@@ -163,12 +161,14 @@ def generate_final_text(src: str, custom_prompt: str, api_key: str):
163
  if stream_gen:
164
  for chunk in stream_gen:
165
  d = extract_delta(chunk)
166
- if d:
167
- parts.append(d)
 
 
 
 
168
  return
169
- # fallback complete
170
  res = client.chat.complete(model=model, messages=messages, stream=False)
171
- # try to extract text
172
  try:
173
  choices = getattr(res, "choices", None) or res.get("choices", [])
174
  except Exception:
@@ -184,7 +184,6 @@ def generate_final_text(src: str, custom_prompt: str, api_key: str):
184
  if isinstance(content, str):
185
  parts.append(content)
186
  else:
187
- # handle list/dict shaped content
188
  if isinstance(content, list):
189
  for c in content:
190
  if isinstance(c, dict) and c.get("type") == "text":
@@ -200,7 +199,7 @@ def generate_final_text(src: str, custom_prompt: str, api_key: str):
200
  except Exception as e:
201
  parts.append(f"[Model error: {e}]")
202
 
203
- # Image path
204
  if is_image:
205
  try:
206
  raw = fetch_bytes(src)
@@ -212,13 +211,13 @@ def generate_final_text(src: str, custom_prompt: str, api_key: str):
212
  stream_and_collect(choose_model_for_src(src), msgs)
213
  return "".join(parts).strip()
214
 
215
- # Remote video: send URL as plain text (avoid embedding non-text chunk types)
216
  if is_remote(src):
217
  msgs = build_messages_for_text(prompt, f"Video URL: {src}")
218
  stream_and_collect(choose_model_for_src(src), msgs)
219
  return "".join(parts).strip()
220
 
221
- # Local video fallback: extract a frame with ffmpeg
222
  tmp_media = None
223
  try:
224
  media_bytes = fetch_bytes(src)
@@ -249,7 +248,6 @@ def generate_final_text(src: str, custom_prompt: str, api_key: str):
249
  except Exception:
250
  pass
251
  else:
252
- # ffmpeg failed or produced no frame
253
  try:
254
  if tmp_frame and os.path.exists(tmp_frame):
255
  os.remove(tmp_frame)
@@ -270,7 +268,7 @@ def generate_final_text(src: str, custom_prompt: str, api_key: str):
270
  except Exception:
271
  pass
272
 
273
- # Simple UI: left preview + inputs, right final text only (plain text)
274
  css = """
275
  .preview_column { min-width: 380px; }
276
  .preview_media img, .preview_media video { max-width: 100%; height: auto; }
 
31
  return bool(src) and src.startswith(("http://", "https://"))
32
 
33
  def fetch_bytes(src: str, stream_threshold=20 * 1024 * 1024) -> bytes:
 
34
  if is_remote(src):
35
  with requests.get(src, timeout=60, stream=True) as r:
36
  r.raise_for_status()
37
  content_length = r.headers.get("content-length")
38
  if content_length and int(content_length) > stream_threshold:
 
39
  fd, path = tempfile.mkstemp()
40
  os.close(fd)
41
  with open(path, "wb") as f:
 
57
  def convert_to_jpeg_bytes(media_bytes: bytes, base_h=480) -> bytes:
58
  img = Image.open(BytesIO(media_bytes))
59
  try:
 
60
  img.seek(0)
61
  except Exception:
62
  pass
 
92
  return DEFAULT_VIDEO_MODEL
93
  if ext in IMAGE_EXTS:
94
  return DEFAULT_IMAGE_MODEL
 
95
  if is_remote(src):
96
  return DEFAULT_VIDEO_MODEL
97
  return DEFAULT_IMAGE_MODEL
 
117
  data = getattr(chunk, "data", None) or getattr(chunk, "response", None) or getattr(chunk, "delta", None)
118
  if not data:
119
  return None
120
+ # Try common streaming shapes and coerce to trimmed string
121
  try:
 
122
  c = data.choices[0].delta
123
  if isinstance(c, dict):
124
  txt = c.get("content") or c.get("text")
125
+ if txt is None:
126
+ return None
127
+ return str(txt)
128
  except Exception:
129
  pass
130
  try:
 
133
  txt = msg.get("content")
134
  else:
135
  txt = getattr(msg, "content", None)
136
+ if txt is None:
137
+ return None
138
+ return str(txt)
139
  except Exception:
140
  pass
141
  try:
 
147
  def generate_final_text(src: str, custom_prompt: str, api_key: str):
148
  client = get_client(api_key)
149
  prompt = (custom_prompt.strip() if custom_prompt and custom_prompt.strip() else "Please provide a detailed visual review.")
 
150
  ext = ext_from_src(src)
151
  is_image = ext in IMAGE_EXTS or (not is_remote(src) and os.path.isfile(src) and ext in IMAGE_EXTS)
152
  parts = []
153
 
154
  def stream_and_collect(model, messages):
155
  try:
 
156
  stream_gen = None
157
  try:
158
  stream_gen = client.chat.stream(model=model, messages=messages)
 
161
  if stream_gen:
162
  for chunk in stream_gen:
163
  d = extract_delta(chunk)
164
+ if d is None:
165
+ continue
166
+ # ignore whitespace-only pieces unless parts is empty and meaningful
167
+ if d.strip() == "" and parts:
168
+ continue
169
+ parts.append(d)
170
  return
 
171
  res = client.chat.complete(model=model, messages=messages, stream=False)
 
172
  try:
173
  choices = getattr(res, "choices", None) or res.get("choices", [])
174
  except Exception:
 
184
  if isinstance(content, str):
185
  parts.append(content)
186
  else:
 
187
  if isinstance(content, list):
188
  for c in content:
189
  if isinstance(c, dict) and c.get("type") == "text":
 
199
  except Exception as e:
200
  parts.append(f"[Model error: {e}]")
201
 
202
+ # Image
203
  if is_image:
204
  try:
205
  raw = fetch_bytes(src)
 
211
  stream_and_collect(choose_model_for_src(src), msgs)
212
  return "".join(parts).strip()
213
 
214
+ # Remote video
215
  if is_remote(src):
216
  msgs = build_messages_for_text(prompt, f"Video URL: {src}")
217
  stream_and_collect(choose_model_for_src(src), msgs)
218
  return "".join(parts).strip()
219
 
220
+ # Local video: extract one frame with ffmpeg
221
  tmp_media = None
222
  try:
223
  media_bytes = fetch_bytes(src)
 
248
  except Exception:
249
  pass
250
  else:
 
251
  try:
252
  if tmp_frame and os.path.exists(tmp_frame):
253
  os.remove(tmp_frame)
 
268
  except Exception:
269
  pass
270
 
271
+ # UI
272
  css = """
273
  .preview_column { min-width: 380px; }
274
  .preview_media img, .preview_media video { max-width: 100%; height: auto; }