Hug0endob commited on
Commit
5bcd967
·
verified ·
1 Parent(s): a6bec5f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +31 -24
app.py CHANGED
@@ -22,7 +22,9 @@ SYSTEM_INSTRUCTION = (
22
  "sensory information not present in the media."
23
  )
24
 
25
- # Helpers
 
 
26
  def get_client(key: str = None):
27
  api_key = (key or "").strip() or DEFAULT_KEY
28
  return Mistral(api_key=api_key)
@@ -30,12 +32,16 @@ def get_client(key: str = None):
30
  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
  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:
@@ -49,8 +55,7 @@ def fetch_bytes(src: str, stream_threshold=20 * 1024 * 1024) -> bytes:
49
  except Exception:
50
  pass
51
  return data
52
- else:
53
- return r.content
54
  with open(src, "rb") as f:
55
  return f.read()
56
 
@@ -79,22 +84,13 @@ def save_bytes_to_temp(b: bytes, suffix: str):
79
  f.write(b)
80
  return path
81
 
82
- IMAGE_EXTS = (".jpg", ".jpeg", ".png", ".webp", ".gif")
83
- VIDEO_EXTS = (".mp4", ".mov", ".webm", ".mkv")
84
-
85
- def ext_from_src(src: str) -> str:
86
- _, ext = os.path.splitext((src or "").split("?")[0])
87
- return ext.lower()
88
-
89
  def choose_model_for_src(src: str):
90
  ext = ext_from_src(src)
91
  if ext in VIDEO_EXTS:
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
98
 
99
  def build_messages_for_image(prompt: str, b64_jpg: str):
100
  return [
@@ -114,11 +110,20 @@ def build_messages_for_text(prompt: str, extra_text: str):
114
  def extract_delta(chunk):
115
  if not chunk:
116
  return None
 
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")
@@ -128,14 +133,15 @@ def extract_delta(chunk):
128
  except Exception:
129
  pass
130
  try:
 
131
  msg = data.choices[0].message
132
  if isinstance(msg, dict):
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:
@@ -143,7 +149,6 @@ def extract_delta(chunk):
143
  except Exception:
144
  return None
145
 
146
- # Core: produce final concatenated text
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.")
@@ -153,6 +158,7 @@ def generate_final_text(src: str, custom_prompt: str, api_key: str):
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)
@@ -163,11 +169,12 @@ def generate_final_text(src: str, custom_prompt: str, api_key: str):
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", [])
@@ -211,13 +218,13 @@ def generate_final_text(src: str, custom_prompt: str, api_key: str):
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)
 
22
  "sensory information not present in the media."
23
  )
24
 
25
+ IMAGE_EXTS = (".jpg", ".jpeg", ".png", ".webp", ".gif")
26
+ VIDEO_EXTS = (".mp4", ".mov", ".webm", ".mkv")
27
+
28
  def get_client(key: str = None):
29
  api_key = (key or "").strip() or DEFAULT_KEY
30
  return Mistral(api_key=api_key)
 
32
  def is_remote(src: str) -> bool:
33
  return bool(src) and src.startswith(("http://", "https://"))
34
 
35
+ def ext_from_src(src: str) -> str:
36
+ _, ext = os.path.splitext((src or "").split("?")[0])
37
+ return ext.lower()
38
+
39
  def fetch_bytes(src: str, stream_threshold=20 * 1024 * 1024) -> bytes:
40
  if is_remote(src):
41
  with requests.get(src, timeout=60, stream=True) as r:
42
  r.raise_for_status()
43
+ cl = r.headers.get("content-length")
44
+ if cl and int(cl) > stream_threshold:
45
  fd, path = tempfile.mkstemp()
46
  os.close(fd)
47
  with open(path, "wb") as f:
 
55
  except Exception:
56
  pass
57
  return data
58
+ return r.content
 
59
  with open(src, "rb") as f:
60
  return f.read()
61
 
 
84
  f.write(b)
85
  return path
86
 
 
 
 
 
 
 
 
87
  def choose_model_for_src(src: str):
88
  ext = ext_from_src(src)
89
  if ext in VIDEO_EXTS:
90
  return DEFAULT_VIDEO_MODEL
91
  if ext in IMAGE_EXTS:
92
  return DEFAULT_IMAGE_MODEL
93
+ return DEFAULT_VIDEO_MODEL if is_remote(src) else DEFAULT_IMAGE_MODEL
 
 
94
 
95
  def build_messages_for_image(prompt: str, b64_jpg: str):
96
  return [
 
110
  def extract_delta(chunk):
111
  if not chunk:
112
  return None
113
+ # chunk.data.choices[0].delta.content is the typical shape from Mistral streaming
114
  data = getattr(chunk, "data", None) or getattr(chunk, "response", None) or getattr(chunk, "delta", None)
115
  if not data:
116
  return None
 
117
  try:
118
+ # common streaming shape: data.choices[0].delta.content
119
+ content = data.choices[0].delta.content
120
+ if content is None:
121
+ return None
122
+ return str(content)
123
+ except Exception:
124
+ pass
125
+ try:
126
+ # fallback: delta may be dict-like
127
  c = data.choices[0].delta
128
  if isinstance(c, dict):
129
  txt = c.get("content") or c.get("text")
 
133
  except Exception:
134
  pass
135
  try:
136
+ # non-stream full message shape
137
  msg = data.choices[0].message
138
  if isinstance(msg, dict):
139
+ content = msg.get("content")
140
  else:
141
+ content = getattr(msg, "content", None)
142
+ if content is None:
143
  return None
144
+ return str(content)
145
  except Exception:
146
  pass
147
  try:
 
149
  except Exception:
150
  return None
151
 
 
152
  def generate_final_text(src: str, custom_prompt: str, api_key: str):
153
  client = get_client(api_key)
154
  prompt = (custom_prompt.strip() if custom_prompt and custom_prompt.strip() else "Please provide a detailed visual review.")
 
158
 
159
  def stream_and_collect(model, messages):
160
  try:
161
+ # try streaming API
162
  stream_gen = None
163
  try:
164
  stream_gen = client.chat.stream(model=model, messages=messages)
 
169
  d = extract_delta(chunk)
170
  if d is None:
171
  continue
172
+ # drop pure-whitespace pieces unless result empty
173
  if d.strip() == "" and parts:
174
  continue
175
  parts.append(d)
176
  return
177
+ # fallback to non-streaming complete
178
  res = client.chat.complete(model=model, messages=messages, stream=False)
179
  try:
180
  choices = getattr(res, "choices", None) or res.get("choices", [])
 
218
  stream_and_collect(choose_model_for_src(src), msgs)
219
  return "".join(parts).strip()
220
 
221
+ # Remote video: send URL as text (avoid streaming non-text types)
222
  if is_remote(src):
223
  msgs = build_messages_for_text(prompt, f"Video URL: {src}")
224
  stream_and_collect(choose_model_for_src(src), msgs)
225
  return "".join(parts).strip()
226
 
227
+ # Local video: try extract frame with ffmpeg and send as image
228
  tmp_media = None
229
  try:
230
  media_bytes = fetch_bytes(src)