File size: 17,423 Bytes
cde1649
9b5b26a
 
 
c19d193
3e34e42
 
 
 
 
6aae614
9b5b26a
 
3e34e42
 
 
 
9b5b26a
3e34e42
 
 
9b5b26a
3e34e42
9b5b26a
3e34e42
 
 
 
 
 
9b5b26a
 
3e34e42
 
 
9b5b26a
3e34e42
 
9b5b26a
 
3e34e42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9b5b26a
3e34e42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8c01ffb
3e34e42
 
 
8c01ffb
3e34e42
8c01ffb
3e34e42
 
 
 
 
 
 
 
8c01ffb
3e34e42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8c01ffb
3e34e42
 
 
9b5b26a
3e34e42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8fe992b
3e34e42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9b5b26a
3e34e42
 
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
from smolagents import CodeAgent, DuckDuckGoSearchTool, InferenceClientModel, load_tool, tool
import datetime
import requests
import pytz
import yaml
import json
import os
import math
import re
from typing import Dict, List, Any, Optional
from tools.final_answer import FinalAnswerTool
from Gradio_UI import GradioUI

# =============================================================================
# CUSTOM TOOLS COLLECTION
# =============================================================================

@tool
def get_current_time_in_timezone(timezone: str) -> str:
    """Get the current local time in a specified timezone.
    
    Args:
        timezone: A string representing a valid timezone (e.g., 'America/New_York', 'Europe/London', 'Asia/Tokyo')
    """
    try:
        tz = pytz.timezone(timezone)
        local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S %Z")
        return f"Current time in {timezone}: {local_time}"
    except Exception as e:
        return f"Error: Invalid timezone '{timezone}'. Please use format like 'America/New_York'"

@tool
def weather_forecast(city: str, country_code: str = "") -> str:
    """Get current weather information for a city.
    
    Args:
        city: Name of the city
        country_code: Optional 2-letter country code (e.g., 'US', 'UK', 'CA')
    """
    try:
        # Using OpenWeatherMap API (you'll need to get a free API key)
        api_key = os.getenv('OPENWEATHER_API_KEY', 'demo_key')
        
        if country_code:
            query = f"{city},{country_code}"
        else:
            query = city
            
        url = f"http://api.openweathermap.org/data/2.5/weather?q={query}&appid={api_key}&units=metric"
        
        if api_key == 'demo_key':
            return f"Weather service unavailable. Please set OPENWEATHER_API_KEY environment variable."
        
        response = requests.get(url, timeout=10)
        data = response.json()
        
        if response.status_code == 200:
            temp = data['main']['temp']
            feels_like = data['main']['feels_like']
            humidity = data['main']['humidity']
            description = data['weather'][0]['description']
            wind_speed = data['wind']['speed']
            
            return f"Weather in {city}: {description.title()}, {temp}°C (feels like {feels_like}°C), Humidity: {humidity}%, Wind: {wind_speed} m/s"
        else:
            return f"Could not fetch weather for {city}. Error: {data.get('message', 'Unknown error')}"
            
    except Exception as e:
        return f"Weather service error: {str(e)}"

@tool
def calculate_advanced_math(expression: str) -> str:
    """Safely evaluate mathematical expressions including advanced functions.
    
    Args:
        expression: Mathematical expression (e.g., 'sqrt(16)', 'sin(pi/2)', '2**3 + log(10)')
    """
    try:
        # Safe math evaluation with common functions
        safe_dict = {
            '__builtins__': {},
            'abs': abs, 'round': round, 'min': min, 'max': max,
            'sum': sum, 'pow': pow,
            'sqrt': math.sqrt, 'sin': math.sin, 'cos': math.cos, 'tan': math.tan,
            'log': math.log, 'log10': math.log10, 'exp': math.exp,
            'pi': math.pi, 'e': math.e,
            'ceil': math.ceil, 'floor': math.floor,
            'factorial': math.factorial,
            'degrees': math.degrees, 'radians': math.radians
        }
        
        result = eval(expression, safe_dict)
        return f"Result: {result}"
        
    except Exception as e:
        return f"Math error: {str(e)}. Please check your expression syntax."

@tool
def text_analyzer(text: str) -> str:
    """Analyze text for various metrics and insights.
    
    Args:
        text: The text to analyze
    """
    try:
        # Basic text statistics
        word_count = len(text.split())
        char_count = len(text)
        char_count_no_spaces = len(text.replace(' ', ''))
        sentence_count = len([s for s in re.split(r'[.!?]+', text) if s.strip()])
        paragraph_count = len([p for p in text.split('\n\n') if p.strip()])
        
        # Average metrics
        avg_words_per_sentence = word_count / max(sentence_count, 1)
        avg_chars_per_word = char_count_no_spaces / max(word_count, 1)
        
        # Reading time estimate (average 200 words per minute)
        reading_time_minutes = word_count / 200
        
        # Most common words (excluding common stop words)
        stop_words = {'the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'of', 'with', 'by', 'is', 'are', 'was', 'were', 'be', 'been', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could', 'should', 'this', 'that', 'these', 'those'}
        words = [word.lower().strip('.,!?";()[]{}') for word in text.split()]
        content_words = [word for word in words if word not in stop_words and len(word) > 2]
        word_freq = {}
        for word in content_words:
            word_freq[word] = word_freq.get(word, 0) + 1
        
        top_words = sorted(word_freq.items(), key=lambda x: x[1], reverse=True)[:5]
        
        analysis = f"""Text Analysis Results:
        
📊 Basic Statistics:
• Words: {word_count}
• Characters: {char_count} (with spaces), {char_count_no_spaces} (without spaces)
• Sentences: {sentence_count}
• Paragraphs: {paragraph_count}

📈 Averages:
• Words per sentence: {avg_words_per_sentence:.1f}
• Characters per word: {avg_chars_per_word:.1f}

⏱️ Reading Time: {reading_time_minutes:.1f} minutes

🔤 Most Common Words:
{chr(10).join([f"• {word}: {count}" for word, count in top_words])}
        """
        
        return analysis
        
    except Exception as e:
        return f"Text analysis error: {str(e)}"

@tool
def url_shortener(url: str, custom_alias: str = "") -> str:
    """Create a shortened URL using TinyURL service.
    
    Args:
        url: The URL to shorten
        custom_alias: Optional custom alias for the shortened URL
    """
    try:
        if not url.startswith(('http://', 'https://')):
            url = 'https://' + url
        
        api_url = "http://tinyurl.com/api-create.php"
        params = {'url': url}
        
        if custom_alias:
            params['alias'] = custom_alias
        
        response = requests.get(api_url, params=params, timeout=10)
        
        if response.status_code == 200:
            short_url = response.text.strip()
            if short_url.startswith('http'):
                return f"Shortened URL: {short_url}"
            else:
                return f"Error: {short_url}"
        else:
            return f"URL shortening failed with status code: {response.status_code}"
            
    except Exception as e:
        return f"URL shortening error: {str(e)}"

@tool
def password_generator(length: int = 12, include_symbols: bool = True, include_numbers: bool = True) -> str:
    """Generate a secure random password.
    
    Args:
        length: Length of the password (default: 12)
        include_symbols: Whether to include special characters (default: True)
        include_numbers: Whether to include numbers (default: True)
    """
    try:
        import random
        import string
        
        if length < 4:
            return "Error: Password length must be at least 4 characters"
        
        if length > 128:
            return "Error: Password length cannot exceed 128 characters"
        
        # Base character set
        chars = string.ascii_letters
        
        if include_numbers:
            chars += string.digits
        
        if include_symbols:
            chars += "!@#$%^&*()_+-=[]{}|;:,.<>?"
        
        # Ensure at least one character from each selected category
        password = []
        
        # Add at least one lowercase and uppercase letter
        password.append(random.choice(string.ascii_lowercase))
        password.append(random.choice(string.ascii_uppercase))
        
        if include_numbers:
            password.append(random.choice(string.digits))
        
        if include_symbols:
            password.append(random.choice("!@#$%^&*()_+-=[]{}|;:,.<>?"))
        
        # Fill the rest with random characters
        for _ in range(length - len(password)):
            password.append(random.choice(chars))
        
        # Shuffle the password
        random.shuffle(password)
        
        generated_password = ''.join(password)
        
        return f"Generated password: {generated_password}\n\nSecurity tips:\n• Store in a password manager\n• Don't reuse across sites\n• Change regularly for sensitive accounts"
        
    except Exception as e:
        return f"Password generation error: {str(e)}"

@tool
def unit_converter(value: float, from_unit: str, to_unit: str) -> str:
    """Convert between different units of measurement.
    
    Args:
        value: The numeric value to convert
        from_unit: Source unit (e.g., 'km', 'miles', 'kg', 'lbs', 'celsius', 'fahrenheit')
        to_unit: Target unit
    """
    try:
        # Conversion factors to base units
        conversions = {
            # Length (to meters)
            'mm': 0.001, 'cm': 0.01, 'm': 1, 'km': 1000,
            'inch': 0.0254, 'ft': 0.3048, 'yard': 0.9144, 'mile': 1609.34,
            
            # Weight (to kg)
            'mg': 0.000001, 'g': 0.001, 'kg': 1,
            'oz': 0.0283495, 'lb': 0.453592, 'stone': 6.35029,
            
            # Temperature (special handling)
            'celsius': 'celsius', 'fahrenheit': 'fahrenheit', 'kelvin': 'kelvin',
            
            # Volume (to liters)
            'ml': 0.001, 'l': 1, 'gallon': 3.78541, 'quart': 0.946353,
            'pint': 0.473176, 'cup': 0.236588, 'fl_oz': 0.0295735
        }
        
        from_unit = from_unit.lower()
        to_unit = to_unit.lower()
        
        # Handle temperature conversions separately
        if from_unit in ['celsius', 'fahrenheit', 'kelvin'] or to_unit in ['celsius', 'fahrenheit', 'kelvin']:
            if from_unit == 'celsius' and to_unit == 'fahrenheit':
                result = (value * 9/5) + 32
            elif from_unit == 'fahrenheit' and to_unit == 'celsius':
                result = (value - 32) * 5/9
            elif from_unit == 'celsius' and to_unit == 'kelvin':
                result = value + 273.15
            elif from_unit == 'kelvin' and to_unit == 'celsius':
                result = value - 273.15
            elif from_unit == 'fahrenheit' and to_unit == 'kelvin':
                result = (value - 32) * 5/9 + 273.15
            elif from_unit == 'kelvin' and to_unit == 'fahrenheit':
                result = (value - 273.15) * 9/5 + 32
            else:
                return f"Error: Cannot convert from {from_unit} to {to_unit}"
            
            return f"{value}° {from_unit.title()} = {result:.2f}° {to_unit.title()}"
        
        # Handle other unit conversions
        if from_unit not in conversions or to_unit not in conversions:
            available_units = list(conversions.keys())
            return f"Error: Unsupported unit. Available units: {', '.join(available_units)}"
        
        # Convert to base unit, then to target unit
        base_value = value * conversions[from_unit]
        result = base_value / conversions[to_unit]
        
        return f"{value} {from_unit} = {result:.6f} {to_unit}"
        
    except Exception as e:
        return f"Unit conversion error: {str(e)}"

@tool
def json_formatter(json_string: str) -> str:
    """Format and validate JSON strings.
    
    Args:
        json_string: The JSON string to format
    """
    try:
        # Parse the JSON to validate it
        parsed = json.loads(json_string)
        
        # Format with proper indentation
        formatted = json.dumps(parsed, indent=2, ensure_ascii=False)
        
        return f"✅ Valid JSON - Formatted:\n\n{formatted}"
        
    except json.JSONDecodeError as e:
        return f"❌ Invalid JSON - Error: {str(e)}"
    except Exception as e:
        return f"JSON formatting error: {str(e)}"

@tool
def base64_encoder_decoder(text: str, operation: str = "encode") -> str:
    """Encode or decode base64 strings.
    
    Args:
        text: The text to encode/decode
        operation: Either 'encode' or 'decode'
    """
    try:
        import base64
        
        if operation.lower() == "encode":
            encoded = base64.b64encode(text.encode('utf-8')).decode('utf-8')
            return f"Base64 Encoded: {encoded}"
        
        elif operation.lower() == "decode":
            decoded = base64.b64decode(text.encode('utf-8')).decode('utf-8')
            return f"Base64 Decoded: {decoded}"
        
        else:
            return "Error: Operation must be 'encode' or 'decode'"
            
    except Exception as e:
        return f"Base64 operation error: {str(e)}"

@tool
def hash_generator(text: str, algorithm: str = "sha256") -> str:
    """Generate hash for text using various algorithms.
    
    Args:
        text: The text to hash
        algorithm: Hash algorithm ('md5', 'sha1', 'sha256', 'sha512')
    """
    try:
        import hashlib
        
        algorithm = algorithm.lower()
        
        if algorithm == "md5":
            hash_obj = hashlib.md5(text.encode())
        elif algorithm == "sha1":
            hash_obj = hashlib.sha1(text.encode())
        elif algorithm == "sha256":
            hash_obj = hashlib.sha256(text.encode())
        elif algorithm == "sha512":
            hash_obj = hashlib.sha512(text.encode())
        else:
            return "Error: Supported algorithms are 'md5', 'sha1', 'sha256', 'sha512'"
        
        hash_value = hash_obj.hexdigest()
        return f"{algorithm.upper()} hash: {hash_value}"
        
    except Exception as e:
        return f"Hash generation error: {str(e)}"

# =============================================================================
# MAIN APPLICATION SETUP
# =============================================================================

def main():
    """Initialize and launch the AI agent application."""
    
    # Initialize core tools
    final_answer = FinalAnswerTool()
    search_tool = DuckDuckGoSearchTool()
    
    # Initialize the language model
    model = InferenceClientModel(
        max_tokens=2096,
        temperature=0.5,
        model_id='Qwen/Qwen2.5-Coder-32B-Instruct',
        custom_role_conversions=None,
    )
    
    # Try to load image generation tool
    try:
        image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True)
        print("✅ Image generation tool loaded successfully")
    except Exception as e:
        print(f"⚠️ Could not load image generation tool: {e}")
        image_generation_tool = None
    
    # Collect all tools
    tools = [
        final_answer,
        search_tool,
        get_current_time_in_timezone,
        weather_forecast,
        calculate_advanced_math,
        text_analyzer,
        url_shortener,
        password_generator,
        unit_converter,
        json_formatter,
        base64_encoder_decoder,
        hash_generator,
    ]
    
    # Add image generation tool if available
    if image_generation_tool:
        tools.append(image_generation_tool)
    
    # Load system prompt templates
    try:
        with open("prompts.yaml", 'r') as stream:
            prompt_templates = yaml.safe_load(stream)
        print("✅ System prompts loaded successfully")
    except FileNotFoundError:
        print("⚠️ prompts.yaml not found, using default prompts")
        prompt_templates = None
    except Exception as e:
        print(f"⚠️ Error loading prompts.yaml: {e}")
        prompt_templates = None
    
    # Initialize the CodeAgent
    agent = CodeAgent(
        model=model,
        tools=tools,
        max_steps=10,  # Increased for more complex tasks
        verbosity_level=1,
        grammar=None,
        planning_interval=3,  # Plan every 3 steps
        name="SuperAgent",
        description="An advanced AI agent capable of solving complex tasks using code and various tools",
        prompt_templates=prompt_templates
    )
    
    print("🚀 SuperAgent initialized with the following capabilities:")
    print("   • Web search and information retrieval")
    print("   • Advanced mathematical calculations")
    print("   • Text analysis and processing")
    print("   • Time zone and weather information")
    print("   • URL shortening and web utilities")
    print("   • Security tools (password generation, hashing)")
    print("   • Data conversion and formatting")
    if image_generation_tool:
        print("   • Image generation from text")
    print("   • Code execution and problem solving")
    print()
    
    # Launch the Gradio interface
    try:
        ui = GradioUI(agent)
        ui.launch(
            share=False,  # Set to True to create a public link
            inbrowser=True,  # Open in browser automatically
            server_name="0.0.0.0",  # Allow access from other devices on network
            server_port=7860,  # Default Gradio port
        )
    except Exception as e:
        print(f"❌ Failed to launch Gradio UI: {e}")
        print("Make sure you have gradio installed and Gradio_UI module is available")

if __name__ == "__main__":
    main()