from smolagents import CodeAgent, Tool, DuckDuckGoSearchTool, load_tool, tool from models import OpenAIModel # Import our local model import datetime import requests import pytz import yaml from tools.final_answer import FinalAnswerTool import re import os import logging from jinja2 import Template, StrictUndefined from Gradio_UI import GradioUI # Configure logging logging.basicConfig(level=logging.DEBUG) logger = logging.getLogger(__name__) # Below is an example of a tool that does nothing. Amaze us with your creativity ! @tool def calculate_min_price(prices: list[float])-> str: #it's import to specify the return type """A tool that calculates the min price from list of product prices Args: prices: list of product prices of """ min_price =min(prices) return f"The minimum price is {min_price}" @tool def extract_price_from_snippet(snippet: str) -> list[str]: """ A simple function to extract prices from a text snippet using regex. You can enhance this function for more complex price extraction. Args: snippet: text of all prices """ # A basic regular expression to detect common price formats like $29.99, 29.99 USD, etc. price_pattern = r'\$\d+(?:,\d{3})*(?:\.\d{2})?|\d+(?:,\d{3})*(?:\.\d{2})?\s*(USD|EUR|GBP|INR|AUD|CAD)?' matches = re.findall(price_pattern, snippet) matches = [str(x) for x in matches] return matches @tool def get_current_time_in_timezone(timezone: str) -> str: """A tool that fetches the current local time in a specified timezone. Args: timezone: A string representing a valid timezone (e.g., 'America/New_York'). """ try: # Create timezone object tz = pytz.timezone(timezone) # Get current time in that timezone local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S") return f"The current local time in {timezone} is: {local_time}" except Exception as e: return f"Error fetching time for timezone '{timezone}': {str(e)}" final_answer = FinalAnswerTool() # Load configuration from agent.json with open("agent.json", 'r') as f: config = yaml.safe_load(f) # Initialize OpenAI model using the configuration model = OpenAIModel( model=config["model"]["data"]["model_id"], max_tokens=config["model"]["data"]["max_tokens"], temperature=config["model"]["data"]["temperature"], api_key=os.getenv("OPENAI_API_KEY") ) # Import tool from Hub image_generation_tool = load_tool("agents-course/text-to-image", trust_remote_code=True) # Load and validate templates from prompts.yaml with open("prompts.yaml", 'r') as stream: yaml_content = yaml.safe_load(stream) if not isinstance(yaml_content, dict): raise ValueError("YAML content must be a dictionary") if 'prompt_templates' not in yaml_content: raise ValueError("YAML must contain 'prompt_templates' key") prompt_templates = yaml_content['prompt_templates'] if not isinstance(prompt_templates, dict): raise ValueError("prompt_templates must be a dictionary") logger.debug("Starting template validation...") try: # Create test tools for validation class TestTool(Tool): name = "test_tool" description = "A test tool" inputs = {'input': {'type': 'string', 'description': 'Test input'}} output_type = "string" def forward(self, input: str) -> str: return "test output" test_tools = [TestTool()] # Create a list of tools # Validate templates for key, value in prompt_templates.items(): if isinstance(value, dict): for subkey, subvalue in value.items(): if isinstance(subvalue, str): logger.debug(f"Validating template: {key}.{subkey}") logger.debug(f"Template content: {subvalue[:200]}...") # Validate template try: # Create a template with at least one template node template_str = subvalue if '{{' not in template_str and '{%' not in template_str: template_str = "{{ task }}\n" + template_str # Create a template with the task variable template = Template(template_str, undefined=StrictUndefined) rendered = template.render( tools=test_tools, # Pass list of tools task="test", name="test", final_answer="test", remaining_steps=1, answer_facts="test" ) logger.debug(f"Template render test successful for {key}.{subkey}") except Exception as e: logger.error(f"Template render test failed for {key}.{subkey}: {str(e)}") raise elif isinstance(value, str): logger.debug(f"Validating template: {key}") logger.debug(f"Template content: {value[:200]}...") # Validate template try: # Create a template with at least one template node template_str = value if '{{' not in template_str and '{%' not in template_str: template_str = "{{ task }}\n" + template_str # Create a template with the task variable template = Template(template_str, undefined=StrictUndefined) rendered = template.render( tools=test_tools, # Pass list of tools task="test", name="test", final_answer="test", remaining_steps=1, answer_facts="test" ) logger.debug(f"Template render test successful for {key}") except Exception as e: logger.error(f"Template render test failed for {key}: {str(e)}") raise logger.debug("Template validation completed successfully") except Exception as e: logger.error(f"Error during template validation: {str(e)}") raise # Create the agent with the templates class CalculateMinPriceTool(Tool): name = "calculate_min_price" description = "Calculate the minimum price from a list of prices" inputs = {'prices': {'type': 'array', 'description': 'List of product prices (numbers)'}} output_type = "string" def forward(self, prices: list[float]) -> str: min_price = min(prices) return f"The minimum price is {min_price}" class ExtractPriceFromSnippetTool(Tool): name = "extract_price_from_snippet" description = "Extract prices from a text snippet" inputs = {'snippet': {'type': 'string', 'description': 'Text containing prices'}} output_type = "array" def forward(self, snippet: str) -> list[str]: price_pattern = r'\$\d+(?:,\d{3})*(?:\.\d{2})?|\d+(?:,\d{3})*(?:\.\d{2})?\s*(USD|EUR|GBP|INR|AUD|CAD)?' matches = re.findall(price_pattern, snippet) matches = [str(x) for x in matches] return matches class GetCurrentTimeInTimezoneTool(Tool): name = "get_current_time_in_timezone" description = "Get the current time in a specified timezone" inputs = {'timezone': {'type': 'string', 'description': 'A valid timezone (e.g., America/New_York)'}} output_type = "string" def forward(self, timezone: str) -> str: try: tz = pytz.timezone(timezone) local_time = datetime.datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S") return f"The current local time in {timezone} is: {local_time}" except Exception as e: return f"Error fetching time for timezone '{timezone}': {str(e)}" # Create the agent with the templates tools = [ final_answer, # This is already a Tool instance DuckDuckGoSearchTool(), # This is already a Tool instance CalculateMinPriceTool(), # This is a Tool subclass ExtractPriceFromSnippetTool(), # This is a Tool subclass GetCurrentTimeInTimezoneTool() # This is a Tool subclass ] # Debug prints to inspect tools logger.debug("Inspecting tools list:") for i, tool in enumerate(tools): logger.debug(f"Tool {i}: {tool}") logger.debug(f" Type: {type(tool)}") logger.debug(f" Is Tool instance: {isinstance(tool, Tool)}") logger.debug(f" Has name: {hasattr(tool, 'name')}") logger.debug(f" Has description: {hasattr(tool, 'description')}") if hasattr(tool, 'name'): logger.debug(f" Name: {tool.name}") if hasattr(tool, 'description'): logger.debug(f" Description: {tool.description}") # Verify all tools are proper Tool instances and have required attributes for tool in tools: if not isinstance(tool, Tool): raise TypeError(f"Tool {tool} is not an instance of Tool or its subclass") if not hasattr(tool, 'name') or not hasattr(tool, 'description'): raise AttributeError(f"Tool {tool} is missing required attributes (name or description)") # Create a dictionary of tools for template rendering tools_dict = {tool.name: tool for tool in tools} logger.debug("Created tools dictionary:") for name, tool in tools_dict.items(): logger.debug(f" {name}: {tool}") print("[DEBUG] Starting app.py initialization...") # Create a custom CodeAgent class that handles both list and dictionary requirements class CustomCodeAgent(CodeAgent): def __init__(self, *args, **kwargs): print("[DEBUG] Initializing CustomCodeAgent...") # Store the tools dictionary for template rendering self.tools_dict = kwargs.pop('tools_dict', {}) # Initialize parent class first super().__init__(*args, **kwargs) def initialize_system_prompt(self): print("[DEBUG] Initializing system prompt...") # Override to use tools_dict for template rendering template = self.prompt_templates["system_prompt"] # Convert tools_dict to list for template rendering tools_list = list(self.tools_dict.values()) # Use Jinja2 template rendering return Template(template, undefined=StrictUndefined).render( tools=tools_list, # Pass tools as a list task=getattr(self, 'task', ''), managed_agents=getattr(self, 'managed_agents', []), authorized_imports=getattr(self, 'authorized_imports', []) ) print("[DEBUG] Creating agent instance...") agent = CustomCodeAgent( model=model, tools=tools, # Pass tools as a list for _setup_tools tools_dict=tools_dict, # Pass tools as a dictionary for template rendering max_steps=15, verbosity_level=2, grammar=None, planning_interval=1, name="question_answering_agent", description="An agent specialized in answering various types of questions using available tools. The agent must use the final_answer tool to submit its answer.", prompt_templates=prompt_templates ) print("[DEBUG] Building Gradio demo...") demo = GradioUI(agent).build_blocks() print("[DEBUG] Gradio demo built and exposed as 'demo'.") print("[DEBUG] About to assign app = demo") app = demo print("[DEBUG] app assigned successfully")