cat-llm 0.0.39__py3-none-any.whl → 0.0.41__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cat-llm
3
- Version: 0.0.39
3
+ Version: 0.0.41
4
4
  Summary: A tool for categorizing text data and images using LLMs and vision models
5
5
  Project-URL: Documentation, https://github.com/chrissoria/cat-llm#readme
6
6
  Project-URL: Issues, https://github.com/chrissoria/cat-llm/issues
@@ -20,7 +20,6 @@ Classifier: Programming Language :: Python :: Implementation :: CPython
20
20
  Classifier: Programming Language :: Python :: Implementation :: PyPy
21
21
  Requires-Python: >=3.8
22
22
  Requires-Dist: pandas
23
- Requires-Dist: pkg-resources
24
23
  Requires-Dist: tqdm
25
24
  Description-Content-Type: text/markdown
26
25
 
@@ -1,14 +1,14 @@
1
- catllm/CERAD_functions.py,sha256=O2q36tDyTfx2VTaIb_aj4FZj1kOUOcHbHjXT1QLzFa0,20167
2
- catllm/__about__.py,sha256=b1JodYiWo4VCqcPrzBgK4KiF8M-y2NNQXKs-snhIDlw,404
1
+ catllm/CERAD_functions.py,sha256=luo0CtDBR02pizs1zLw_-C_BGsNC9VLWpBLcNtLqOP4,21825
2
+ catllm/__about__.py,sha256=RCXZpPyKlH4hD8FaFE3Xjf3Ervz7ZuuxG_bnY_ymIg4,404
3
3
  catllm/__init__.py,sha256=BpAG8nPhM3ZQRd0WqkubI_36-VCOs4eCYtGVgzz48Bs,337
4
- catllm/image_functions.py,sha256=3QlOAt1UTn2NQ9rybtP0eIYjIOHNDlJgoql9g7TjI1o,31319
4
+ catllm/image_functions.py,sha256=86EDccwnRVze7uhc-6p7aBxvvh8ozA7FEMtR6ywOTjY,33401
5
5
  catllm/text_functions.py,sha256=K6oetWYk25PwsllWSZP4cFrz7kyxJg0plPRvpmQkCsU,16846
6
6
  catllm/images/circle.png,sha256=JWujAWAh08-TajAoEr_TAeFNLlfbryOLw6cgIBREBuQ,86202
7
7
  catllm/images/cube.png,sha256=nFec3e5bmRe4zrBCJ8QK-HcJLrG7u7dYdKhmdMfacfE,77275
8
8
  catllm/images/diamond.png,sha256=rJDZKtsnBGRO8FPA0iHuA8FvHFGi9PkI_DWSFdw6iv0,99568
9
9
  catllm/images/overlapping_pentagons.png,sha256=VO5plI6eoVRnjfqinn1nNzsCP2WQhuQy71V0EASouW4,71208
10
10
  catllm/images/rectangles.png,sha256=2XM16HO9EYWj2yHgN4bPXaCwPfl7iYQy0tQUGaJX9xg,40692
11
- cat_llm-0.0.39.dist-info/METADATA,sha256=d8V4zqaBfZqOjN1KKdQ-PK_uJQzCrWzheVoPVLjdKHA,17543
12
- cat_llm-0.0.39.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
- cat_llm-0.0.39.dist-info/licenses/LICENSE,sha256=Vje2sS5WV4TnIwY5uQHrF4qnBAM3YOk1pGpdH0ot-2o,34969
14
- cat_llm-0.0.39.dist-info/RECORD,,
11
+ cat_llm-0.0.41.dist-info/METADATA,sha256=kiCX2N0wJkvYqBqd4cm34M88FBysYAKHY3tfqsGI1LQ,17514
12
+ cat_llm-0.0.41.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
13
+ cat_llm-0.0.41.dist-info/licenses/LICENSE,sha256=Vje2sS5WV4TnIwY5uQHrF4qnBAM3YOk1pGpdH0ot-2o,34969
14
+ cat_llm-0.0.41.dist-info/RECORD,,
catllm/CERAD_functions.py CHANGED
@@ -67,8 +67,8 @@ def cerad_drawn_score(
67
67
  categories = ["The image contains a drawing that clearly represents overlapping rectangles",
68
68
  "The image does NOT contain any drawing that resembles overlapping rectangles",
69
69
  "The image contains a drawing that resembles overlapping rectangles",
70
- "If rectangle 1 is present it has 4 sides",
71
- "If rectablge 2 is present it has 4 sides",
70
+ "If rectangle 1 is present and it has 4 sides",
71
+ "If rectangle 2 is present and it has 4 sides",
72
72
  "The drawn rectangles are overlapping",
73
73
  "The drawn rectangles overlap to form a longer vertical rectangle with top and bottom sticking out",
74
74
  "None of the above descriptions apply"]
@@ -131,19 +131,30 @@ def cerad_drawn_score(
131
131
  continue # Skip the rest of the loop iteration
132
132
 
133
133
  # Only open the file if path is valid
134
- with open(img_path, "rb") as f:
135
- encoded = base64.b64encode(f.read()).decode("utf-8")
136
-
134
+ if os.path.isdir(img_path):
135
+ encoded = "Not a Valid Image, contains file path"
136
+ else:
137
+ try:
138
+ with open(img_path, "rb") as f:
139
+ encoded = base64.b64encode(f.read()).decode("utf-8")
140
+ except Exception as e:
141
+ encoded = f"Error: {str(e)}"
137
142
  # Handle extension safely
138
- ext = Path(img_path).suffix.lstrip(".").lower()
139
- encoded_image = f"data:image/{ext};base64,{encoded}"
143
+ if encoded.startswith("Error:") or encoded == "Not a Valid Image, contains file path":
144
+ encoded_image = encoded
145
+ valid_image = False
146
+
147
+ else:
148
+ ext = Path(img_path).suffix.lstrip(".").lower()
149
+ encoded_image = f"data:image/{ext};base64,{encoded}"
150
+ valid_image = True
140
151
 
141
152
  if reference_in_image:
142
153
  reference_text = f"This image contains a perfect reference image of a {shape}. Next to is a drawing that is meant to be similar to the reference {shape}.\n\n"
143
154
  else:
144
155
  reference_text = f"Image is expected to show within it a drawing of a {shape}.\n\n"
145
156
 
146
- if model_source == "OpenAI":
157
+ if model_source == "OpenAI" and valid_image:
147
158
  prompt = [
148
159
  {
149
160
  "type": "text",
@@ -154,8 +165,8 @@ def cerad_drawn_score(
154
165
  f"{reference_text}"
155
166
  f"Categories:\n{categories_str}\n\n"
156
167
  f"Output format ► Respond with **only** a JSON object whose keys are the "
157
- f"quoted category numbers ('1', '2', …) and whose values are 1 or 0. "
158
- f"No additional keys, comments, or text.\n\n"
168
+ f"quoted category numbers ('1', '2', …) and whose values are 1 if present or 0 if not present. "
169
+ f"No additional keys, comments, numbers beyond 0 or 1, or text.\n\n"
159
170
  f"Example:\n"
160
171
  f"{example_JSON}"
161
172
  )
@@ -173,7 +184,7 @@ def cerad_drawn_score(
173
184
  "image_url": {"url": encoded_image, "detail": "high"}
174
185
  })
175
186
 
176
- elif model_source == "Anthropic":
187
+ elif model_source == "Anthropic" and valid_image:
177
188
  prompt = [
178
189
  {
179
190
  "type": "text",
@@ -184,8 +195,8 @@ def cerad_drawn_score(
184
195
  f"{reference_text}"
185
196
  f"Categories:\n{categories_str}\n\n"
186
197
  f"Output format ► Respond with **only** a JSON object whose keys are the "
187
- f"quoted category numbers ('1', '2', …) and whose values are 1 or 0. "
188
- f"No additional keys, comments, or text.\n\n"
198
+ f"quoted category numbers ('1', '2', …) and whose values are 1 if present or 0 if not present. "
199
+ f"No additional keys, comments, numbers beyond 0 or 1, or text.\n\n"
189
200
  f"Example:\n"
190
201
  f"{example_JSON}"
191
202
  ),
@@ -213,7 +224,7 @@ def cerad_drawn_score(
213
224
  }
214
225
  )
215
226
 
216
- elif model_source == "Mistral":
227
+ elif model_source == "Mistral" and valid_image:
217
228
  prompt = [
218
229
  {
219
230
  "type": "text",
@@ -224,8 +235,8 @@ def cerad_drawn_score(
224
235
  f"{reference_text}"
225
236
  f"Categories:\n{categories_str}\n\n"
226
237
  f"Output format ► Respond with **only** a JSON object whose keys are the "
227
- f"quoted category numbers ('1', '2', …) and whose values are 1 or 0. "
228
- f"No additional keys, comments, or text.\n\n"
238
+ f"quoted category numbers ('1', '2', …) and whose values are 1 if present or 0 if not present. "
239
+ f"No additional keys, comments, numbers beyond 0 or 1, or text.\n\n"
229
240
  f"Example:\n"
230
241
  f"{example_JSON}"
231
242
  ),
@@ -241,8 +252,8 @@ def cerad_drawn_score(
241
252
  "type": "image_url",
242
253
  "image_url": f"data:image/{ext};base64,{encoded_image}"
243
254
  })
244
-
245
- if model_source == "OpenAI":
255
+
256
+ if model_source == "OpenAI" and valid_image:
246
257
  from openai import OpenAI
247
258
  client = OpenAI(api_key=api_key)
248
259
  try:
@@ -254,10 +265,10 @@ def cerad_drawn_score(
254
265
  reply = response_obj.choices[0].message.content
255
266
  link1.append(reply)
256
267
  except Exception as e:
257
- print(f"An error occurred: {e}")
258
- link1.append(f"Error processing input: {e}")
268
+ print("An error occurred: {e}")
269
+ link1.append("Error processing input: {e}")
259
270
 
260
- elif model_source == "Anthropic":
271
+ elif model_source == "Anthropic" and valid_image:
261
272
  import anthropic
262
273
  client = anthropic.Anthropic(api_key=api_key)
263
274
  try:
@@ -270,10 +281,10 @@ def cerad_drawn_score(
270
281
  reply = message.content[0].text # Anthropic returns content as list
271
282
  link1.append(reply)
272
283
  except Exception as e:
273
- print(f"An error occurred: {e}")
274
- link1.append(f"Error processing input: {e}")
284
+ print("An error occurred: {e}")
285
+ link1.append("Error processing input: {e}")
275
286
 
276
- elif model_source == "Mistral":
287
+ elif model_source == "Mistral" and valid_image:
277
288
  from mistralai import Mistral
278
289
  reply = None
279
290
  client = Mistral(api_key=api_key)
@@ -288,25 +299,34 @@ def cerad_drawn_score(
288
299
  reply = response.choices[0].message.content
289
300
  link1.append(reply)
290
301
  except Exception as e:
291
- print(f"An error occurred: {e}")
292
- link1.append(f"Error processing input: {e}")
302
+ reply = None
303
+ print("An error occurred: {e}")
304
+ link1.append("Error processing input: {e}")
305
+ #if no valid image path is provided
306
+ elif valid_image == False:
307
+ reply = "invalid image path"
308
+ print("Skipped NaN input or invalid path")
309
+ #extracted_jsons.append("""{"no_valid_path": 1}""")
310
+ link1.append("Error processing input: {e}")
293
311
  else:
294
312
  raise ValueError("Unknown source! Choose from OpenAI, Perplexity, or Mistral")
295
313
  # in situation that no JSON is found
296
314
  if reply is not None:
297
- extracted_json = regex.findall(r'\{(?:[^{}]|(?R))*\}', reply, regex.DOTALL)
298
- if extracted_json:
299
- cleaned_json = extracted_json[0].replace('[', '').replace(']', '').replace('\n', '').replace(" ", '').replace(" ", '')
300
- extracted_jsons.append(cleaned_json)
301
- #print(cleaned_json)
315
+ if reply == "invalid image path":
316
+ extracted_jsons.append("""{"no_valid_path": 1}""")
302
317
  else:
303
- error_message = """{"1":"e"}"""
304
- extracted_jsons.append(error_message)
305
- print(error_message)
318
+ extracted_json = regex.findall(r'\{(?:[^{}]|(?R))*\}', reply, regex.DOTALL)
319
+ if extracted_json:
320
+ cleaned_json = extracted_json[0].replace('[', '').replace(']', '').replace('\n', '').replace(" ", '').replace(" ", '')
321
+ extracted_jsons.append(cleaned_json)
322
+ else:
323
+ error_message = """{"1":"e"}"""
324
+ extracted_jsons.append(error_message)
325
+ print(error_message)
306
326
  else:
307
327
  error_message = """{"1":"e"}"""
308
328
  extracted_jsons.append(error_message)
309
- #print(error_message)
329
+ print(error_message)
310
330
 
311
331
  # --- Safety Save ---
312
332
  if safety:
@@ -369,6 +389,8 @@ def cerad_drawn_score(
369
389
  categorized_data['score'] = categorized_data['cir_almost_closed'] + categorized_data['cir_closed'] + categorized_data['cir_round'] + categorized_data['cir_almost_round']
370
390
  categorized_data.loc[categorized_data['none'] == 1, 'score'] = 0
371
391
  categorized_data.loc[(categorized_data['drawing_present'] == 0) & (categorized_data['score'] == 0), 'score'] = 0
392
+ #this score should never be greater than 2
393
+ categorized_data.loc[categorized_data['score'] > 2, 'score'] = 2
372
394
 
373
395
  elif shape == "diamond":
374
396
 
@@ -382,11 +404,12 @@ def cerad_drawn_score(
382
404
  "7": "complex_diamond",
383
405
  "8": "none"
384
406
  })
385
-
386
- categorized_data['score'] = categorized_data['diamond_4_sides'] + categorized_data['diamond_equal_sides'] + categorized_data['similar']
407
+ categorized_data['diamond_4_sides'] = np.where(categorized_data['diamond_4_sides'] > 1, 1, categorized_data['diamond_4_sides'])
408
+ categorized_data['score'] = categorized_data['diamond_4_sides'] + categorized_data['diamond_equal_sides'] + categorized_data['similar'] + categorized_data['diamond_square']
387
409
 
388
410
  categorized_data.loc[categorized_data['none'] == 1, 'score'] = 0
389
- #categorized_data.loc[(categorized_data['diamond_square'] == 1) & (categorized_data['score'] == 0), 'score'] = 2
411
+ #this score should never be greater than 3
412
+ categorized_data.loc[categorized_data['score'] > 3, 'score'] = 3
390
413
 
391
414
  elif shape == "rectangles" or shape == "overlapping rectangles":
392
415
 
@@ -401,11 +424,13 @@ def cerad_drawn_score(
401
424
  "8": "none"
402
425
  })
403
426
 
404
- categorized_data['score'] = 0
405
- categorized_data.loc[(categorized_data['r1_4_sides'] == 1) & (categorized_data['r2_4_sides'] == 1), 'score'] = 1
406
- categorized_data.loc[(categorized_data['rectangles_overlap'] == 1) & (categorized_data['rectangles_cross'] == 1), 'score'] += 1
427
+ #TODO: check to this logic, it might be skewing scores to be more often 2 than should be
428
+ categorized_data['score'] = categorized_data['rectangles_overlap'] + categorized_data['similar'] + categorized_data['rectangles_cross']
407
429
  categorized_data.loc[categorized_data['none'] == 1, 'score'] = 0
408
430
 
431
+ #this score should never be greater than 2
432
+ categorized_data.loc[categorized_data['score'] > 2, 'score'] = 2
433
+
409
434
  elif shape == "cube":
410
435
 
411
436
  categorized_data = categorized_data.rename(columns={
@@ -424,12 +449,14 @@ def cerad_drawn_score(
424
449
  categorized_data.loc[categorized_data['none'] == 1, 'score'] = 0
425
450
  categorized_data.loc[(categorized_data['drawing_present'] == 0) & (categorized_data['score'] == 0), 'score'] = 0
426
451
  categorized_data.loc[(categorized_data['not_similar'] == 1) & (categorized_data['score'] == 0), 'score'] = 0
452
+ #this score should never be greater than 4
427
453
  categorized_data.loc[categorized_data['score'] > 4, 'score'] = 4
428
454
 
429
455
  else:
430
456
  raise ValueError("Invalid shape! Choose from 'circle', 'diamond', 'rectangles', or 'cube'.")
431
457
 
432
458
  categorized_data.loc[categorized_data['no_valid_image'] == 1, 'score'] = None
459
+ categorized_data['image_file'] = categorized_data['image_input'].apply(lambda x: Path(x).name)
433
460
 
434
461
  if filename is not None:
435
462
  categorized_data.to_csv(filename, index=False)
catllm/__about__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # SPDX-FileCopyrightText: 2025-present Christopher Soria <chrissoria@berkeley.edu>
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
- __version__ = "0.0.39"
4
+ __version__ = "0.0.41"
5
5
  __author__ = "Chris Soria"
6
6
  __email__ = "chrissoria@berkeley.edu"
7
7
  __title__ = "cat-llm"
catllm/image_functions.py CHANGED
@@ -66,8 +66,23 @@ def image_multi_class(
66
66
  continue # Skip the rest of the loop iteration
67
67
 
68
68
  # Only open the file if path is valid
69
- with open(img_path, "rb") as f:
70
- encoded = base64.b64encode(f.read()).decode("utf-8")
69
+ if os.path.isdir(img_path):
70
+ encoded = "Not a Valid Image, contains file path"
71
+ else:
72
+ try:
73
+ with open(img_path, "rb") as f:
74
+ encoded = base64.b64encode(f.read()).decode("utf-8")
75
+ except Exception as e:
76
+ encoded = f"Error: {str(e)}"
77
+ # Handle extension safely
78
+ if encoded.startswith("Error:") or encoded == "Not a Valid Image, contains file path":
79
+ encoded_image = encoded
80
+ valid_image = False
81
+ extracted_jsons.append("""{"no_valid_path": 1}""")
82
+ else:
83
+ ext = Path(img_path).suffix.lstrip(".").lower()
84
+ encoded_image = f"data:image/{ext};base64,{encoded}"
85
+ valid_image = True
71
86
 
72
87
  # Handle extension safely
73
88
  ext = Path(img_path).suffix.lstrip(".").lower()
@@ -169,23 +184,31 @@ def image_multi_class(
169
184
  except Exception as e:
170
185
  print(f"An error occurred: {e}")
171
186
  link1.append(f"Error processing input: {e}")
187
+ #if no valid image path is provided
188
+ elif valid_image == False:
189
+ reply = "invalid image path"
190
+ print("Skipped NaN input or invalid path")
191
+ #extracted_jsons.append("""{"no_valid_path": 1}""")
192
+ link1.append("Error processing input: {e}")
172
193
  else:
173
- raise ValueError("Unknown source! Choose from OpenAI, Anthropic, or Mistral")
194
+ raise ValueError("Unknown source! Choose from OpenAI, Perplexity, or Mistral")
174
195
  # in situation that no JSON is found
175
196
  if reply is not None:
176
- extracted_json = regex.findall(r'\{(?:[^{}]|(?R))*\}', reply, regex.DOTALL)
177
- if extracted_json:
178
- cleaned_json = extracted_json[0].replace('[', '').replace(']', '').replace('\n', '').replace(" ", '').replace(" ", '')
179
- extracted_jsons.append(cleaned_json)
180
- #print(cleaned_json)
197
+ if reply == "invalid image path":
198
+ extracted_jsons.append("""{"no_valid_path": 1}""")
181
199
  else:
182
- error_message = """{"1":"e"}"""
183
- extracted_jsons.append(error_message)
184
- print(error_message)
200
+ extracted_json = regex.findall(r'\{(?:[^{}]|(?R))*\}', reply, regex.DOTALL)
201
+ if extracted_json:
202
+ cleaned_json = extracted_json[0].replace('[', '').replace(']', '').replace('\n', '').replace(" ", '').replace(" ", '')
203
+ extracted_jsons.append(cleaned_json)
204
+ else:
205
+ error_message = """{"1":"e"}"""
206
+ extracted_jsons.append(error_message)
207
+ print(error_message)
185
208
  else:
186
209
  error_message = """{"1":"e"}"""
187
210
  extracted_jsons.append(error_message)
188
- #print(error_message)
211
+ print(error_message)
189
212
 
190
213
  # --- Safety Save ---
191
214
  if safety:
@@ -227,9 +250,6 @@ def image_multi_class(
227
250
  'json': pd.Series(extracted_jsons).reset_index(drop=True)
228
251
  })
229
252
  categorized_data = pd.concat([categorized_data, normalized_data], axis=1)
230
-
231
- if columns != "numbered": #if user wants text columns
232
- categorized_data.columns = list(categorized_data.columns[:3]) + categories[:len(categorized_data.columns) - 3]
233
253
 
234
254
  if to_csv:
235
255
  if save_directory is None:
@@ -301,8 +321,23 @@ def image_score_drawing(
301
321
  continue # Skip the rest of the loop iteration
302
322
 
303
323
  # Only open the file if path is valid
304
- with open(img_path, "rb") as f:
305
- encoded = base64.b64encode(f.read()).decode("utf-8")
324
+ if os.path.isdir(img_path):
325
+ encoded = "Not a Valid Image, contains file path"
326
+ else:
327
+ try:
328
+ with open(img_path, "rb") as f:
329
+ encoded = base64.b64encode(f.read()).decode("utf-8")
330
+ except Exception as e:
331
+ encoded = f"Error: {str(e)}"
332
+ # Handle extension safely
333
+ if encoded.startswith("Error:") or encoded == "Not a Valid Image, contains file path":
334
+ encoded_image = encoded
335
+ valid_image = False
336
+
337
+ else:
338
+ ext = Path(img_path).suffix.lstrip(".").lower()
339
+ encoded_image = f"data:image/{ext};base64,{encoded}"
340
+ valid_image = True
306
341
 
307
342
  # Handle extension safely
308
343
  ext = Path(img_path).suffix.lstrip(".").lower()
@@ -436,23 +471,31 @@ def image_score_drawing(
436
471
  except Exception as e:
437
472
  print(f"An error occurred: {e}")
438
473
  link1.append(f"Error processing input: {e}")
474
+ #if no valid image path is provided
475
+ elif valid_image == False:
476
+ reply = "invalid image path"
477
+ print("Skipped NaN input or invalid path")
478
+ #extracted_jsons.append("""{"no_valid_path": 1}""")
479
+ link1.append("Error processing input: {e}")
439
480
  else:
440
- raise ValueError("Unknown source! Choose from OpenAI, Anthropic, Perplexity, or Mistral")
481
+ raise ValueError("Unknown source! Choose from OpenAI, Perplexity, or Mistral")
441
482
  # in situation that no JSON is found
442
483
  if reply is not None:
443
- extracted_json = regex.findall(r'\{(?:[^{}]|(?R))*\}', reply, regex.DOTALL)
444
- if extracted_json:
445
- cleaned_json = extracted_json[0].replace('[', '').replace(']', '').replace('\n', '').replace(" ", '').replace(" ", '')
446
- extracted_jsons.append(cleaned_json)
447
- #print(cleaned_json)
484
+ if reply == "invalid image path":
485
+ extracted_jsons.append("""{"no_valid_path": 1}""")
448
486
  else:
449
- error_message = """{"1":"e"}"""
450
- extracted_jsons.append(error_message)
451
- print(error_message)
487
+ extracted_json = regex.findall(r'\{(?:[^{}]|(?R))*\}', reply, regex.DOTALL)
488
+ if extracted_json:
489
+ cleaned_json = extracted_json[0].replace('[', '').replace(']', '').replace('\n', '').replace(" ", '').replace(" ", '')
490
+ extracted_jsons.append(cleaned_json)
491
+ else:
492
+ error_message = """{"1":"e"}"""
493
+ extracted_jsons.append(error_message)
494
+ print(error_message)
452
495
  else:
453
496
  error_message = """{"1":"e"}"""
454
497
  extracted_jsons.append(error_message)
455
- #print(error_message)
498
+ print(error_message)
456
499
 
457
500
  # --- Safety Save ---
458
501
  if safety:
@@ -697,6 +740,10 @@ def image_features(
697
740
  except Exception as e:
698
741
  print(f"An error occurred: {e}")
699
742
  link1.append(f"Error processing input: {e}")
743
+ elif valid_image == False:
744
+ print("Skipped NaN input or invalid path")
745
+ reply = None
746
+ link1.append("Error processing input: {e}")
700
747
  else:
701
748
  raise ValueError("Unknown source! Choose from OpenAI, Anthropic, Perplexity, or Mistral")
702
749
  # in situation that no JSON is found