diff --git a/docker/Dockerfile b/docker/Dockerfile index 05825c1eb..0355d09c4 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-slim WORKDIR /app COPY . . -RUN pip install flask praisonai==2.0.76 gunicorn markdown +RUN pip install flask praisonai==2.0.77 gunicorn markdown EXPOSE 8080 CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"] diff --git a/docs/api/praisonai/deploy.html b/docs/api/praisonai/deploy.html index ccba7368c..a88b1fc5e 100644 --- a/docs/api/praisonai/deploy.html +++ b/docs/api/praisonai/deploy.html @@ -110,7 +110,7 @@

Raises

file.write("FROM python:3.11-slim\n") file.write("WORKDIR /app\n") file.write("COPY . .\n") - file.write("RUN pip install flask praisonai==2.0.76 gunicorn markdown\n") + file.write("RUN pip install flask praisonai==2.0.77 gunicorn markdown\n") file.write("EXPOSE 8080\n") file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n') diff --git a/praisonai.rb b/praisonai.rb index bfad617e7..0bde49f13 100644 --- a/praisonai.rb +++ b/praisonai.rb @@ -3,7 +3,7 @@ class Praisonai < Formula desc "AI tools for various AI applications" homepage "https://github.com/MervinPraison/PraisonAI" - url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/2.0.76.tar.gz" + url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/2.0.77.tar.gz" sha256 "1828fb9227d10f991522c3f24f061943a254b667196b40b1a3e4a54a8d30ce32" # Replace with actual SHA256 checksum license "MIT" diff --git a/praisonai/deploy.py b/praisonai/deploy.py index a549ff054..07d18618f 100644 --- a/praisonai/deploy.py +++ b/praisonai/deploy.py @@ -56,7 +56,7 @@ def create_dockerfile(self): file.write("FROM python:3.11-slim\n") file.write("WORKDIR /app\n") file.write("COPY . .\n") - file.write("RUN pip install flask praisonai==2.0.76 gunicorn markdown\n") + file.write("RUN pip install flask praisonai==2.0.77 gunicorn markdown\n") file.write("EXPOSE 8080\n") file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n') diff --git a/pyproject.toml b/pyproject.toml index eeae44f4b..bcce0585c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "PraisonAI" -version = "2.0.76" +version = "2.0.77" description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration." readme = "README.md" license = "" @@ -12,7 +12,7 @@ dependencies = [ "rich>=13.7", "markdown>=3.5", "pyparsing>=3.0.0", - "praisonaiagents>=0.0.61", + "praisonaiagents>=0.0.62", "python-dotenv>=0.19.0", "instructor>=1.3.3", "PyYAML>=6.0", @@ -84,7 +84,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.7", "crewai"] [tool.poetry] name = "PraisonAI" -version = "2.0.76" +version = "2.0.77" description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human–agent collaboration." authors = ["Mervin Praison"] license = "" @@ -102,7 +102,7 @@ python = ">=3.10,<3.13" rich = ">=13.7" markdown = ">=3.5" pyparsing = ">=3.0.0" -praisonaiagents = ">=0.0.61" +praisonaiagents = ">=0.0.62" python-dotenv = ">=0.19.0" instructor = ">=1.3.3" PyYAML = ">=6.0" diff --git a/src/praisonai-agents/llm-tool-call.py b/src/praisonai-agents/llm-tool-call.py new file mode 100644 index 000000000..e56c63b83 --- /dev/null +++ b/src/praisonai-agents/llm-tool-call.py @@ -0,0 +1,18 @@ +from praisonaiagents import Agent +from praisonaiagents.tools import wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language + +agent1 = Agent( + instructions="You are a Wikipedia Agent", + tools=[wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language], + llm="openai/gpt-4o-mini", + verbose=10 +) +agent1.start("history of AI in 1 line") + +agent2 = Agent( + instructions="You are a Wikipedia Agent", + tools=[wiki_search, wiki_summary, wiki_page, wiki_random, wiki_language], + llm="gpt-4o-mini", + verbose=10 +) +agent2.start("history of AI in 1 line") \ No newline at end of file diff --git a/src/praisonai-agents/praisonaiagents/agent/agent.py b/src/praisonai-agents/praisonaiagents/agent/agent.py index 721251185..261fc61f0 100644 --- a/src/praisonai-agents/praisonaiagents/agent/agent.py +++ b/src/praisonai-agents/praisonaiagents/agent/agent.py @@ -714,6 +714,22 @@ def _chat_completion(self, messages, temperature=0.2, tools=None, stream=True, r return None def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False): + # Log all parameter values when in debug mode + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + param_info = { + "prompt": str(prompt)[:100] + "..." if isinstance(prompt, str) and len(str(prompt)) > 100 else str(prompt), + "temperature": temperature, + "tools": [t.__name__ if hasattr(t, "__name__") else str(t) for t in tools] if tools else None, + "output_json": str(output_json.__class__.__name__) if output_json else None, + "output_pydantic": str(output_pydantic.__class__.__name__) if output_pydantic else None, + "reasoning_steps": reasoning_steps, + "agent_name": self.name, + "agent_role": self.role, + "agent_goal": self.goal + } + logging.debug(f"Agent.chat parameters: {json.dumps(param_info, indent=2, default=str)}") + + start_time = time.time() reasoning_steps = reasoning_steps or self.reasoning_steps # Search for existing knowledge if any knowledge is provided if self.knowledge: @@ -738,7 +754,7 @@ def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pyd system_prompt=f"{self.backstory}\n\nYour Role: {self.role}\n\nYour Goal: {self.goal}" if self.use_system_prompt else None, chat_history=self.chat_history, temperature=temperature, - tools=tools, + tools=self.tools if tools is None else tools, output_json=output_json, output_pydantic=output_pydantic, verbose=self.verbose, @@ -749,7 +765,7 @@ def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pyd console=self.console, agent_name=self.name, agent_role=self.role, - agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in self.tools], + agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in (tools if tools is not None else self.tools)], execute_tool_fn=self.execute_tool, # Pass tool execution function reasoning_steps=reasoning_steps ) @@ -757,6 +773,11 @@ def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pyd self.chat_history.append({"role": "user", "content": prompt}) self.chat_history.append({"role": "assistant", "content": response_text}) + # Log completion time if in debug mode + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + total_time = time.time() - start_time + logging.debug(f"Agent.chat completed in {total_time:.2f} seconds") + return response_text except Exception as e: display_error(f"Error in LLM chat: {e}") @@ -944,6 +965,13 @@ def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pyd display_error(f"Error in chat: {e}", console=self.console) return None + # Log completion time if in debug mode + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + total_time = time.time() - start_time + logging.debug(f"Agent.chat completed in {total_time:.2f} seconds") + + return response_text + def clean_json_output(self, output: str) -> str: """Clean and extract JSON from response text.""" cleaned = output.strip() @@ -958,6 +986,22 @@ def clean_json_output(self, output: str) -> str: async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False): """Async version of chat method. TODO: Requires Syncing with chat method.""" + # Log all parameter values when in debug mode + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + param_info = { + "prompt": str(prompt)[:100] + "..." if isinstance(prompt, str) and len(str(prompt)) > 100 else str(prompt), + "temperature": temperature, + "tools": [t.__name__ if hasattr(t, "__name__") else str(t) for t in tools] if tools else None, + "output_json": str(output_json.__class__.__name__) if output_json else None, + "output_pydantic": str(output_pydantic.__class__.__name__) if output_pydantic else None, + "reasoning_steps": reasoning_steps, + "agent_name": self.name, + "agent_role": self.role, + "agent_goal": self.goal + } + logging.debug(f"Agent.achat parameters: {json.dumps(param_info, indent=2, default=str)}") + + start_time = time.time() reasoning_steps = reasoning_steps or self.reasoning_steps try: # Search for existing knowledge if any knowledge is provided @@ -996,9 +1040,15 @@ async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None self.chat_history.append({"role": "user", "content": prompt}) self.chat_history.append({"role": "assistant", "content": response_text}) + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + total_time = time.time() - start_time + logging.debug(f"Agent.achat completed in {total_time:.2f} seconds") return response_text except Exception as e: display_error(f"Error in LLM chat: {e}") + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + total_time = time.time() - start_time + logging.debug(f"Agent.achat failed in {total_time:.2f} seconds: {str(e)}") return None # For OpenAI client @@ -1081,7 +1131,11 @@ async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None temperature=temperature, tools=formatted_tools ) - return await self._achat_completion(response, tools) + result = await self._achat_completion(response, tools) + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + total_time = time.time() - start_time + logging.debug(f"Agent.achat completed in {total_time:.2f} seconds") + return result elif output_json or output_pydantic: response = await async_client.chat.completions.create( model=self.llm, @@ -1090,6 +1144,9 @@ async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None response_format={"type": "json_object"} ) # Return the raw response + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + total_time = time.time() - start_time + logging.debug(f"Agent.achat completed in {total_time:.2f} seconds") return response.choices[0].message.content else: response = await async_client.chat.completions.create( @@ -1097,12 +1154,21 @@ async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None messages=messages, temperature=temperature ) + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + total_time = time.time() - start_time + logging.debug(f"Agent.achat completed in {total_time:.2f} seconds") return response.choices[0].message.content except Exception as e: display_error(f"Error in chat completion: {e}") + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + total_time = time.time() - start_time + logging.debug(f"Agent.achat failed in {total_time:.2f} seconds: {str(e)}") return None except Exception as e: display_error(f"Error in achat: {e}") + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + total_time = time.time() - start_time + logging.debug(f"Agent.achat failed in {total_time:.2f} seconds: {str(e)}") return None async def _achat_completion(self, response, tools, reasoning_steps=False): diff --git a/src/praisonai-agents/praisonaiagents/llm/llm.py b/src/praisonai-agents/praisonaiagents/llm/llm.py index 8bd7e763a..f1e126608 100644 --- a/src/praisonai-agents/praisonaiagents/llm/llm.py +++ b/src/praisonai-agents/praisonaiagents/llm/llm.py @@ -172,6 +172,36 @@ def __init__( # Enable error dropping for cleaner output litellm.drop_params = True self._setup_event_tracking(events) + + # Log all initialization parameters when in debug mode + if not isinstance(verbose, bool) and verbose >= 10: + debug_info = { + "model": self.model, + "timeout": self.timeout, + "temperature": self.temperature, + "top_p": self.top_p, + "n": self.n, + "max_tokens": self.max_tokens, + "presence_penalty": self.presence_penalty, + "frequency_penalty": self.frequency_penalty, + "logit_bias": self.logit_bias, + "response_format": self.response_format, + "seed": self.seed, + "logprobs": self.logprobs, + "top_logprobs": self.top_logprobs, + "api_version": self.api_version, + "stop_phrases": self.stop_phrases, + "api_key": "***" if self.api_key else None, # Mask API key for security + "base_url": self.base_url, + "verbose": self.verbose, + "markdown": self.markdown, + "self_reflect": self.self_reflect, + "max_reflect": self.max_reflect, + "min_reflect": self.min_reflect, + "reasoning_steps": self.reasoning_steps, + "extra_settings": {k: v for k, v in self.extra_settings.items() if k not in ["api_key"]} + } + logging.debug(f"LLM instance initialized with: {json.dumps(debug_info, indent=2, default=str)}") def get_response( self, @@ -195,6 +225,56 @@ def get_response( **kwargs ) -> str: """Enhanced get_response with all OpenAI-like features""" + logging.info(f"Getting response from {self.model}") + # Log all self values when in debug mode + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + debug_info = { + "model": self.model, + "timeout": self.timeout, + "temperature": self.temperature, + "top_p": self.top_p, + "n": self.n, + "max_tokens": self.max_tokens, + "presence_penalty": self.presence_penalty, + "frequency_penalty": self.frequency_penalty, + "logit_bias": self.logit_bias, + "response_format": self.response_format, + "seed": self.seed, + "logprobs": self.logprobs, + "top_logprobs": self.top_logprobs, + "api_version": self.api_version, + "stop_phrases": self.stop_phrases, + "api_key": "***" if self.api_key else None, # Mask API key for security + "base_url": self.base_url, + "verbose": self.verbose, + "markdown": self.markdown, + "self_reflect": self.self_reflect, + "max_reflect": self.max_reflect, + "min_reflect": self.min_reflect, + "reasoning_steps": self.reasoning_steps + } + logging.debug(f"LLM instance configuration: {json.dumps(debug_info, indent=2, default=str)}") + + # Log the parameter values passed to get_response + param_info = { + "prompt": str(prompt)[:100] + "..." if isinstance(prompt, str) and len(str(prompt)) > 100 else str(prompt), + "system_prompt": system_prompt[:100] + "..." if system_prompt and len(system_prompt) > 100 else system_prompt, + "chat_history": f"[{len(chat_history)} messages]" if chat_history else None, + "temperature": temperature, + "tools": [t.__name__ if hasattr(t, "__name__") else str(t) for t in tools] if tools else None, + "output_json": str(output_json.__class__.__name__) if output_json else None, + "output_pydantic": str(output_pydantic.__class__.__name__) if output_pydantic else None, + "verbose": verbose, + "markdown": markdown, + "self_reflect": self_reflect, + "max_reflect": max_reflect, + "min_reflect": min_reflect, + "agent_name": agent_name, + "agent_role": agent_role, + "agent_tools": agent_tools, + "kwargs": str(kwargs) + } + logging.debug(f"get_response parameters: {json.dumps(param_info, indent=2, default=str)}") try: import litellm # This below **kwargs** is passed to .completion() directly. so reasoning_steps has to be popped. OR find alternate best way of handling this. @@ -202,6 +282,23 @@ def get_response( # Disable litellm debug messages litellm.set_verbose = False + # Format tools if provided + formatted_tools = None + if tools: + formatted_tools = [] + for tool in tools: + if callable(tool): + tool_def = self._generate_tool_definition(tool.__name__) + elif isinstance(tool, str): + tool_def = self._generate_tool_definition(tool) + else: + continue + + if tool_def: + formatted_tools.append(tool_def) + if not formatted_tools: + formatted_tools = None + # Build messages list messages = [] if system_prompt: @@ -260,6 +357,7 @@ def get_response( messages=messages, temperature=temperature, stream=False, # force non-streaming + tools=formatted_tools, **{k:v for k,v in kwargs.items() if k != 'reasoning_steps'} ) reasoning_content = resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content") @@ -291,6 +389,7 @@ def get_response( for chunk in litellm.completion( model=self.model, messages=messages, + tools=formatted_tools, temperature=temperature, stream=True, **kwargs @@ -305,6 +404,7 @@ def get_response( for chunk in litellm.completion( model=self.model, messages=messages, + tools=formatted_tools, temperature=temperature, stream=True, **kwargs @@ -318,6 +418,7 @@ def get_response( final_response = litellm.completion( model=self.model, messages=messages, + tools=formatted_tools, temperature=temperature, stream=False, # No streaming for tool call check **kwargs @@ -552,6 +653,11 @@ def get_response( except Exception as error: display_error(f"Error in get_response: {str(error)}") raise + + # Log completion time if in debug mode + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + total_time = time.time() - start_time + logging.debug(f"get_response completed in {total_time:.2f} seconds") async def get_response_async( self, @@ -577,6 +683,56 @@ async def get_response_async( """Async version of get_response with identical functionality.""" try: import litellm + logging.info(f"Getting async response from {self.model}") + # Log all self values when in debug mode + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + debug_info = { + "model": self.model, + "timeout": self.timeout, + "temperature": self.temperature, + "top_p": self.top_p, + "n": self.n, + "max_tokens": self.max_tokens, + "presence_penalty": self.presence_penalty, + "frequency_penalty": self.frequency_penalty, + "logit_bias": self.logit_bias, + "response_format": self.response_format, + "seed": self.seed, + "logprobs": self.logprobs, + "top_logprobs": self.top_logprobs, + "api_version": self.api_version, + "stop_phrases": self.stop_phrases, + "api_key": "***" if self.api_key else None, # Mask API key for security + "base_url": self.base_url, + "verbose": self.verbose, + "markdown": self.markdown, + "self_reflect": self.self_reflect, + "max_reflect": self.max_reflect, + "min_reflect": self.min_reflect, + "reasoning_steps": self.reasoning_steps + } + logging.debug(f"LLM async instance configuration: {json.dumps(debug_info, indent=2, default=str)}") + + # Log the parameter values passed to get_response_async + param_info = { + "prompt": str(prompt)[:100] + "..." if isinstance(prompt, str) and len(str(prompt)) > 100 else str(prompt), + "system_prompt": system_prompt[:100] + "..." if system_prompt and len(system_prompt) > 100 else system_prompt, + "chat_history": f"[{len(chat_history)} messages]" if chat_history else None, + "temperature": temperature, + "tools": [t.__name__ if hasattr(t, "__name__") else str(t) for t in tools] if tools else None, + "output_json": str(output_json.__class__.__name__) if output_json else None, + "output_pydantic": str(output_pydantic.__class__.__name__) if output_pydantic else None, + "verbose": verbose, + "markdown": markdown, + "self_reflect": self_reflect, + "max_reflect": max_reflect, + "min_reflect": min_reflect, + "agent_name": agent_name, + "agent_role": agent_role, + "agent_tools": agent_tools, + "kwargs": str(kwargs) + } + logging.debug(f"get_response_async parameters: {json.dumps(param_info, indent=2, default=str)}") reasoning_steps = kwargs.pop('reasoning_steps', self.reasoning_steps) litellm.set_verbose = False @@ -983,6 +1139,11 @@ async def get_response_async( raise LLMContextLengthExceededException(str(error)) display_error(f"Error in get_response_async: {str(error)}") raise + + # Log completion time if in debug mode + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + total_time = time.time() - start_time + logging.debug(f"get_response_async completed in {total_time:.2f} seconds") def can_use_tools(self) -> bool: """Check if this model can use tool functions""" @@ -1065,6 +1226,24 @@ def response( logger.debug("Using synchronous response function") + # Log all self values when in debug mode + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + debug_info = { + "model": self.model, + "timeout": self.timeout, + "temperature": temperature, + "top_p": self.top_p, + "n": self.n, + "max_tokens": self.max_tokens, + "presence_penalty": self.presence_penalty, + "frequency_penalty": self.frequency_penalty, + "stream": stream, + "verbose": verbose, + "markdown": markdown, + "kwargs": str(kwargs) + } + logger.debug(f"Response method configuration: {json.dumps(debug_info, indent=2, default=str)}") + # Build messages list messages = [] if system_prompt: @@ -1150,6 +1329,24 @@ async def aresponse( logger.debug("Using asynchronous response function") + # Log all self values when in debug mode + if logging.getLogger().getEffectiveLevel() == logging.DEBUG: + debug_info = { + "model": self.model, + "timeout": self.timeout, + "temperature": temperature, + "top_p": self.top_p, + "n": self.n, + "max_tokens": self.max_tokens, + "presence_penalty": self.presence_penalty, + "frequency_penalty": self.frequency_penalty, + "stream": stream, + "verbose": verbose, + "markdown": markdown, + "kwargs": str(kwargs) + } + logger.debug(f"Async response method configuration: {json.dumps(debug_info, indent=2, default=str)}") + # Build messages list messages = [] if system_prompt: @@ -1210,4 +1407,117 @@ async def aresponse( except Exception as error: display_error(f"Error in response_async: {str(error)}") - raise \ No newline at end of file + raise + + def _generate_tool_definition(self, function_name: str) -> Optional[Dict]: + """Generate a tool definition from a function name.""" + logging.debug(f"Attempting to generate tool definition for: {function_name}") + + # First try to get the tool definition if it exists + tool_def_name = f"{function_name}_definition" + tool_def = globals().get(tool_def_name) + logging.debug(f"Looking for {tool_def_name} in globals: {tool_def is not None}") + + if not tool_def: + import __main__ + tool_def = getattr(__main__, tool_def_name, None) + logging.debug(f"Looking for {tool_def_name} in __main__: {tool_def is not None}") + + if tool_def: + logging.debug(f"Found tool definition: {tool_def}") + return tool_def + + # Try to find the function + func = globals().get(function_name) + logging.debug(f"Looking for {function_name} in globals: {func is not None}") + + if not func: + import __main__ + func = getattr(__main__, function_name, None) + logging.debug(f"Looking for {function_name} in __main__: {func is not None}") + + if not func or not callable(func): + logging.debug(f"Function {function_name} not found or not callable") + return None + + import inspect + # Handle Langchain and CrewAI tools + if inspect.isclass(func) and hasattr(func, 'run') and not hasattr(func, '_run'): + original_func = func + func = func.run + function_name = original_func.__name__ + elif inspect.isclass(func) and hasattr(func, '_run'): + original_func = func + func = func._run + function_name = original_func.__name__ + + sig = inspect.signature(func) + logging.debug(f"Function signature: {sig}") + + # Skip self, *args, **kwargs + parameters_list = [] + for name, param in sig.parameters.items(): + if name == "self": + continue + if param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD): + continue + parameters_list.append((name, param)) + + parameters = { + "type": "object", + "properties": {}, + "required": [] + } + + # Parse docstring for parameter descriptions + docstring = inspect.getdoc(func) + logging.debug(f"Function docstring: {docstring}") + + param_descriptions = {} + if docstring: + import re + param_section = re.split(r'\s*Args:\s*', docstring) + logging.debug(f"Param section split: {param_section}") + if len(param_section) > 1: + param_lines = param_section[1].split('\n') + for line in param_lines: + line = line.strip() + if line and ':' in line: + param_name, param_desc = line.split(':', 1) + param_descriptions[param_name.strip()] = param_desc.strip() + + logging.debug(f"Parameter descriptions: {param_descriptions}") + + for name, param in parameters_list: + param_type = "string" # Default type + if param.annotation != inspect.Parameter.empty: + if param.annotation == int: + param_type = "integer" + elif param.annotation == float: + param_type = "number" + elif param.annotation == bool: + param_type = "boolean" + elif param.annotation == list: + param_type = "array" + elif param.annotation == dict: + param_type = "object" + + parameters["properties"][name] = { + "type": param_type, + "description": param_descriptions.get(name, "Parameter description not available") + } + + if param.default == inspect.Parameter.empty: + parameters["required"].append(name) + + logging.debug(f"Generated parameters: {parameters}") + tool_def = { + "type": "function", + "function": { + "name": function_name, + "description": docstring.split('\n\n')[0] if docstring else "No description available", + "parameters": parameters + } + } + logging.debug(f"Generated tool definition: {tool_def}") + return tool_def \ No newline at end of file diff --git a/src/praisonai-agents/pyproject.toml b/src/praisonai-agents/pyproject.toml index 80b934ddd..f2057021c 100644 --- a/src/praisonai-agents/pyproject.toml +++ b/src/praisonai-agents/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "praisonaiagents" -version = "0.0.61" +version = "0.0.62" description = "Praison AI agents for completing complex tasks with Self Reflection Agents" authors = [ { name="Mervin Praison" } diff --git a/src/praisonai-agents/uv.lock b/src/praisonai-agents/uv.lock index 110b7226f..ad597e722 100644 --- a/src/praisonai-agents/uv.lock +++ b/src/praisonai-agents/uv.lock @@ -1868,7 +1868,7 @@ wheels = [ [[package]] name = "praisonaiagents" -version = "0.0.61" +version = "0.0.62" source = { editable = "." } dependencies = [ { name = "openai" }, diff --git a/uv.lock b/uv.lock index 77f29ed9b..4c78b3778 100644 --- a/uv.lock +++ b/uv.lock @@ -3060,7 +3060,7 @@ wheels = [ [[package]] name = "praisonai" -version = "2.0.76" +version = "2.0.77" source = { editable = "." } dependencies = [ { name = "instructor" }, @@ -3197,7 +3197,7 @@ requires-dist = [ { name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" }, { name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.7" }, { name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.7" }, - { name = "praisonaiagents", specifier = ">=0.0.61" }, + { name = "praisonaiagents", specifier = ">=0.0.62" }, { name = "pyautogen", marker = "extra == 'autogen'", specifier = ">=0.2.19" }, { name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" }, { name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" }, @@ -3250,16 +3250,16 @@ wheels = [ [[package]] name = "praisonaiagents" -version = "0.0.61" +version = "0.0.62" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "openai" }, { name = "pydantic" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/50/be/09af17ea9eee5135c7b091bca77e1fc7a8ff732a6148497276ff5a06bd2e/praisonaiagents-0.0.61.tar.gz", hash = "sha256:9e178b99a1d2ffe23acd8413810e5d937d910a72fbd851e04d10b0bcb129b48a", size = 102057 } +sdist = { url = "https://files.pythonhosted.org/packages/26/44/528286f187d8be35839382251407a3b5ec7ecf5fc9b1fafd7e4197b436cb/praisonaiagents-0.0.62.tar.gz", hash = "sha256:d461553d7dc64441544a6ad92e6e446531f1be8e5cbb95261407540756b2cb8a", size = 104636 } wheels = [ - { url = "https://files.pythonhosted.org/packages/12/1b/5656b79ae6c9f4c4e9e7b22db6800841c3bbab7fbd73c6c7f3381c936e64/praisonaiagents-0.0.61-py3-none-any.whl", hash = "sha256:835ec8a6fb1b2805a150db3d38bd4a1fe88333f078ee00190b102a3a4b2687a6", size = 121044 }, + { url = "https://files.pythonhosted.org/packages/42/34/c389ba1a9a45b0f7637b073e25717d2f9e0e6e727cc4946182fcf86e8c12/praisonaiagents-0.0.62-py3-none-any.whl", hash = "sha256:f6883ba12642bb3a7af277bfc1c0020276c3ac31bc251ed381f0388b3a61e73b", size = 123702 }, ] [[package]]