File size: 14,299 Bytes
3469f37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d3b823a
3469f37
 
 
 
 
 
 
 
 
 
d3b823a
3469f37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d3b823a
3469f37
 
 
 
 
 
 
 
 
d3b823a
 
 
 
 
3469f37
 
 
 
 
 
 
 
 
 
 
4729622
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3469f37
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4729622
3469f37
 
4729622
3469f37
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
import os
from typing import Union
import requests
import pandas as pd
import subprocess
import sys
from smolagents import tool


@tool
def reverse_string(text: str) -> str:
  """
  Reverse the order of characters in a given string.

  Args:
      text (str): The input string to be reversed.
      
  Returns:
      str: A new string with characters in reverse order.
      
  Example:
      >>> reverse_string("hello")
      'olleh'
      >>> reverse_string("Python")
      'nohtyP'
  """
  return text[::-1]


def download_file(url: str, filename: str) -> str:
  """
  Download a file from the given URL and save it to a temporary location.

  Args:
      url (str): The URL of the file to download.
      filename (str): The name to use for the saved file.
      
  Returns:
      str: Full path to the downloaded file in temporary directory.
      
  Raises:
      requests.RequestException: If the download fails.
      
  Example:
      >>> path = download_file("https://example.com/data.json", "my_data.json")
      >>> print(path)
      '/tmp/tmpxyz123/my_data.json'
  """
    
  file_path = os.path.join("downloaded_files", filename)
  if not os.path.exists(file_path):
      print(f"Attempting to download file from: {url}")
      try:
          response = requests.get(url, timeout=30)
          response.raise_for_status()
          os.makedirs("downloaded_files", exist_ok=True)
          with open(file_path, "wb") as f:
              f.write(response.content)
      except requests.exceptions.RequestException as e:
          print(f"Error downloading file: {e}")
  return file_path

@tool
def process_excel_file(file_path: str) -> pd.DataFrame:
  """
  Process an Excel file and return its data as Pandas DataFrame.
  Args:
    file_path (str): Path to the Excel file (.xlsx or .xls).
   
  Returns:
    pd.DataFrame: processed DataFrame.

  Example:
    >>> result = process_excel_file(
    ...   "data.xlsx",
    ... )
    >>> print(result.head())
  """
  return pd.read_excel(file_path)


@tool
def is_text_file(file_path: str) -> bool:
  """Check if a file is a text file by attempting to decode it as UTF-8.

  Args:
    file_path (str): Path to the file to check.

  Returns:
    bool: True if the file is likely a text file, False if it is likely binary
          or an error occurs.

  Raises:
    None: All exceptions are caught and handled internally.

  Example:
    >>> is_text_file("example.txt")
    True
    >>> is_text_file("image.png")
    False
  """
  try:
    with open(file_path, 'rb') as file:
      # Read a small chunk of the file (1024 bytes)
      chunk: bytes = file.read(1024)
      # Try decoding as UTF-8 text
      chunk.decode('utf-8')
      return True  # No decoding errors, likely a text file
  except UnicodeDecodeError:
    return False  # Decoding failed, likely a binary file
  except Exception as e:
    print(f"Error reading file: {e}")
    return False


@tool
def execute_python_file(file_path: str) -> str:
  """
  Execute a Python code from file_path in a separate process and return its output as a numeric value.
  
  This function runs the specified Python file using the Python interpreter
  in a separate subprocess with error handling and a 60-second timeout.
  
  Args:
      file_path (str): Path to the Python file to execute.
  
  Returns:
      str: The output from the executed script, or an error message if execution failed.
  
  Raises:
      None: All exceptions are handled internally and returned as error strings.
      
  Examples:
      >>> output = execute_python_file("script.py")
      >>> print(output)
      "Hello, World!"
      
      >>> output = execute_python_file("nonexistent.py")
      >>> print(output)
      "Error: File not found: nonexistent.py"
  """
  # Check if file exists
  if not os.path.exists(file_path):
    return f"Error: File not found: {file_path}"
  
  # Check if file is actually a Python file
  if not file_path.endswith('.py'):
    return f"Error: File is not a Python file: {file_path}"
  
  try:
    # Execute the Python file in a separate process
    result = subprocess.run(
      [sys.executable, file_path],
      capture_output=True,
      text=True,
      timeout=180  # 180 seconds timeout
    )
    
    # If there's stderr output, include it in the result
    if result.stderr and result.returncode != 0:
      return f"Error: {result.stderr.strip()}"
    elif result.stderr:
      # Include stderr even if return code is 0 (warnings, etc.)
      return f"{result.stdout.strip()}\nWarnings/Info: {result.stderr.strip()}"
    else:
        for i in result.stdout.strip().split():
          try:
            return str(int(i.strip()))
          except:
            pass
        return result.stdout.strip() if result.stdout.strip() else "Script executed successfully with no output"
    
  except subprocess.TimeoutExpired:
    return "Error: Execution timed out after 60 seconds"
    
  except subprocess.SubprocessError as e:
    return f"Error: Subprocess error: {str(e)}"
    
  except Exception as e:
    return f"Error: Unexpected error: {str(e)}"


@tool
def plural_to_singular(word: str) -> str:
  """
  Convert a plural word to its singular form.
  
  This function handles common English pluralization patterns including:
  - Regular plurals ending in 's' (cats -> cat)
  - Words ending in 'ies' (flies -> fly)
  - Words ending in 'ves' (knives -> knife)
  - Words ending in 'es' after sibilants (boxes -> box)
  - Irregular plurals (children -> child, feet -> foot, etc.)
  
  Args:
    word (str): The plural word to convert to singular form.
    
  Returns:
    str: The singular form of the word.
    
  Examples:
    >>> plural_to_singular("cats")
    'cat'
    >>> plural_to_singular("flies")
    'fly'
    >>> plural_to_singular("children")
    'child'
    >>> plural_to_singular("boxes")
    'box'
  """
  if not word or not isinstance(word, str):
    return word
  
  word = word.lower().strip()
  
  # Handle irregular plurals
  irregular_plurals = {
    'children': 'child',
    'feet': 'foot',
    'teeth': 'tooth',
    'geese': 'goose',
    'mice': 'mouse',
    'men': 'man',
    'women': 'woman',
    'people': 'person',
    'oxen': 'ox',
    'sheep': 'sheep',
    'deer': 'deer',
    'fish': 'fish',
    'species': 'species',
    'series': 'series'
  }
  
  if word in irregular_plurals:
    return irregular_plurals[word]
  
  # Handle words ending in 'ies' -> 'y'
  if word.endswith('ies') and len(word) > 3:
    return word[:-3] + 'y'
  
  # Handle words ending in 'ves' -> 'f' or 'fe'
  if word.endswith('ves'):
    if word[:-3] + 'f' in ['leaf', 'loaf', 'thief', 'shelf', 'knife', 'life', 'wife']:
      return word[:-3] + 'f'
    elif word == 'wolves':
      return 'wolf'
    elif word == 'calves':
      return 'calf'
    elif word == 'halves':
      return 'half'
  
  # Handle words ending in 'es' after sibilants (s, ss, sh, ch, x, z)
  if word.endswith('es') and len(word) > 2:
    if word[-3:-2] in ['s', 'x', 'z'] or word[-4:-2] in ['sh', 'ch', 'ss']:
      return word[:-2]
  
  # Handle words ending in 'oes' -> 'o'
  if word.endswith('oes') and len(word) > 3:
    return word[:-2]
  
  # Handle regular plurals ending in 's'
  if word.endswith('s') and len(word) > 1:
    return word[:-1]
  
  # If no pattern matches, return the original word
  return word
  
@tool
def is_fruit(item: str) -> bool:
  """
  Check if the given item is a recognized fruit.
  
  This function determines whether the provided string matches one of the
  predefined fruits in the internal fruit list. The input is automatically
  converted to singular form and stripped of whitespace before comparison.
  
  The recognized fruits include common varieties such as:
  - Tree fruits: apple, orange, peach, pear, plum, cherry, lemon, lime
  - Berries: strawberry, blueberry, raspberry, grape
  - Tropical fruits: mango, pineapple, kiwi, avocado, pomegranate
  - Dried fruits: fig, date
  - And more: banana
  
  Args:
    item (str): The item name to check against the fruit list. Can be
               singular or plural form.
    
  Returns:
    bool: True if the item is a recognized fruit, False otherwise.
    
  Examples:
    >>> is_fruit("apple")
    True
    >>> is_fruit("apples")
    True
    >>> is_fruit("  Strawberries  ")
    True
    >>> is_fruit("carrot")
    False
    >>> is_fruit("banana")
    True
    
  Note:
    The function uses plural_to_singular() to handle both singular and
    plural forms of fruit names automatically.
  """
  item = plural_to_singular(item.strip())
  fruits = {
    'apple', 'banana', 'orange', 'strawberry', 'blueberry', 'raspberry',
    'mango', 'pineapple', 'grape', 'kiwi', 'peach', 'pear', 'plum',
    'cherry', 'lemon', 'lime', 'avocado', 'pomegranate', 'fig', 'date'
  }
  return item in fruits

@tool
def is_vegetable(item: str) -> bool:
  """
  Check if the given item is a recognized vegetable.
  
  This function determines whether the provided string matches one of the
  predefined vegetables in the internal vegetable list. The input is
  automatically converted to singular form and stripped of whitespace
  before comparison.
  
  The recognized vegetables include various categories:
  - Root vegetables: carrot, onion, garlic, potato, sweet potato
  - Leafy greens: spinach, lettuce, kale, cabbage
  - Cruciferous: broccoli, cauliflower, cabbage, kale
  - Nightshades: tomato, pepper, eggplant
  - Squash family: zucchini
  - Other: cucumber, celery, mushroom
  
  Args:
    item (str): The item name to check against the vegetable list. Can be
               singular or plural form.
    
  Returns:
    bool: True if the item is a recognized vegetable, False otherwise.
    
  Examples:
    >>> is_vegetable("carrot")
    True
    >>> is_vegetable("carrots")
    True
    >>> is_vegetable("  BROCCOLI  ")
    True
    >>> is_vegetable("apple")
    False
    >>> is_vegetable("mushrooms")
    True
    
  Note:
    The function uses plural_to_singular() to handle both singular and
    plural forms of vegetable names automatically. Note that some items
    like tomatoes are botanically fruits but classified as vegetables
    in culinary contexts.
  """
  item = plural_to_singular(item.strip())
  vegetables = {
    'carrot', 'broccoli', 'spinach', 'tomato', 'cucumber', 'lettuce',
    'onion', 'garlic', 'potato', 'sweet potato', 'zucchini', 'pepper',
    'eggplant', 'cauliflower', 'cabbage', 'kale', 'mushroom', 'celery'
  }
  return item in vegetables

@tool
def is_product(item: str) -> bool:
  """
  Check if the given item is a recognized food product or ingredient.
  
  This function determines whether the provided string matches one of the
  predefined food products in the internal product list. The input is
  automatically converted to singular form and stripped of whitespace
  before comparison.
  
  The recognized products include various categories:
  - Baking ingredients: flour, sugar, salt, baking powder, baking soda, yeast
  - Spices and flavorings: pepper, cinnamon, vanilla, honey
  - Dairy products: milk, cream, cheese, yogurt, butter
  - Cooking essentials: oil, vinegar, egg
  - Beverages and treats: juice, ice cream
  - Specialty items: cocoa
  
  Args:
    item (str): The item name to check against the product list. Can be
               singular or plural form.
    
  Returns:
    bool: True if the item is a recognized food product, False otherwise.
    
  Examples:
    >>> is_product("flour")
    True
    >>> is_product("eggs")
    True
    >>> is_product("  Baking Powder  ")
    True
    >>> is_product("carrot")
    False
    >>> is_product("ice cream")
    True
    
  Note:
    The function uses plural_to_singular() to handle both singular and
    plural forms of product names automatically. Some items like 
    "ice cream" are treated as compound terms and matched exactly.
  """
  item = plural_to_singular(item.strip())
  products = {
    'vanilla', 'sugar', 'flour', 'salt', 'pepper', 'oil', 'butter',
    'milk', 'cream', 'cheese', 'yogurt', 'egg', 'honey', 'vinegar',
    'baking powder', 'baking soda', 'yeast', 'cinnamon', 'cocoa', 'juice', 'ice cream'
  }
  return item in products

@tool
def is_food(item: str) -> bool:
  """
  Check if the given item is a recognized food item.
  
  This function determines whether the provided string matches one of the
  predefined food items in the internal food list. The comparison is
  case-insensitive and ignores leading/trailing whitespace.
  
  The recognized food items are:
  - burgers
  - hot dogs
  - salads
  - fries
  - ice cream
  
  Args:
    item (str): The item name to check against the food list.
    
  Returns:
    bool: True if the item is a recognized food item, False otherwise.
    
  Examples:
    >>> is_food("burgers")
    True
    >>> is_food("FRIES")
    True
    >>> is_food("  Ice Cream  ")
    True
    >>> is_food("pizza")
    False
    >>> is_food("books")
    False
    
  Note:
    The function performs case-insensitive matching and automatically
    strips leading and trailing whitespace from the input.
  """  
  return item.lower().strip() in ('burgers', 'hot dogs', 'salads', 'fries', 'ice cream')

@tool
def get_ingredients(item: str) -> str:
  """
  Extract known ingredients from a given text string.
  
  This function identifies and extracts recognized ingredients (fruits, vegetables, 
  and common cooking products) from the input text and returns them as a 
  comma-separated string.
  
  Args:
      item (str): Input text containing potential ingredient names separated by spaces.
  
  Returns:
      str: Comma-separated string of recognized ingredients in alphabetical order.
            Returns empty string if no recognized ingredients are found.
  
  Examples:
      >>> get_ingredients("apple banana flour")
      "apple,banana,flour"
      
      >>> get_ingredients("I need tomato and onion for cooking")
      "onion,tomato"
      
      >>> get_ingredients("car house table")
      ""
      
      >>> get_ingredients("APPLE Carrot SUGAR")
      "apple,carrot,sugar"
  """   

  
  def is_ingredient(ingredient: str) -> bool:
    return is_fruit(ingredient)  or is_vegetable(ingredient) or is_product(ingredient)
  
  items = set([x.lower().strip() for x in item.split() if is_ingredient(x)])
  return ','.join(sorted(items))