Compare commits

...
20 Commits
Author SHA1 Message Date
Mervin Praison c460c9da60 Merge pull request #268 from MervinPraison/develop
adding structured agents and code agents
2025-01-06 18:38:51 +00:00
MervinPraison 656887c700 adding structured agents and code agents 2025-01-06 18:26:56 +00:00
Mervin Praison e0089cac41 Merge pull request #267 from MervinPraison/develop
fixing bug setting openai_model_name env variable
2025-01-06 10:06:33 +00:00
MervinPraison bf5e069c7b fixing bug setting openai_model_name env variable 2025-01-06 10:06:11 +00:00
Mervin Praison 94cf264b4c Merge pull request #266 from MervinPraison/develop
setting openai_model_name env variable to set
2025-01-06 09:52:09 +00:00
MervinPraison ab44fd4b27 setting openai_model_name env variable to set 2025-01-06 09:51:50 +00:00
Mervin Praison 7bad6cf7e9 Merge pull request #265 from MervinPraison/develop
adding mini agents
2025-01-06 09:31:54 +00:00
MervinPraison 4ffbe48c58 adding mini agents 2025-01-06 09:31:39 +00:00
Mervin Praison 476eeb1184 Merge pull request #264 from MervinPraison/develop
bug fix ui load
2025-01-06 01:07:03 +00:00
MervinPraison 550769017a bug fix ui load 2025-01-06 01:06:24 +00:00
Mervin Praison cccd75ca75 Merge pull request #263 from MervinPraison/develop
Develop
2025-01-06 00:18:12 +00:00
MervinPraison 395f8fb5ef praisonai agents ui working with tools 2025-01-06 00:17:54 +00:00
MervinPraison e4893ad948 working with other callbacks 2025-01-05 23:08:20 +00:00
Mervin Praison e717b625d2 Merge pull request #262 from MervinPraison/develop
Develop
2025-01-05 07:39:54 +00:00
MervinPraison 84638ad759 praisonai ui working 2025-01-05 07:39:36 +00:00
MervinPraison 626bc963f7 ui working together 2025-01-05 07:11:06 +00:00
Mervin Praison efa7110b38 Merge pull request #260 from MervinPraison/develop
adding async callbacks
2025-01-05 00:20:03 +00:00
MervinPraison 539c10796c adding ui with instant messages working 2025-01-05 00:19:26 +00:00
MervinPraison 815becb6f2 async callback fixes for main display 2025-01-04 22:48:21 +00:00
MervinPraison 87790695db adding async callbacks 2025-01-04 17:19:19 +00:00
32 changed files with 2946 additions and 789 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask praisonai==2.0.24 gunicorn markdown
RUN pip install flask praisonai==2.0.32 gunicorn markdown
EXPOSE 8080
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
+2 -10
View File
@@ -7,12 +7,8 @@ roles:
information.
goal: Gather information about Mars and cats
role: Researcher
llm:
model: "gpt-4o"
reflect_llm:
model: "gpt-4o"
min_reflect: 2
max_reflect: 4
min_reflect: 1
max_reflect: 2
tasks:
research_task:
description: Research about Mars, its environment, and the feasibility of
@@ -25,8 +21,6 @@ roles:
stories.
goal: Create a story concept for a movie about a cat in Mars
role: Narrative Designer
llm:
model: "gpt-4o"
tasks:
story_concept_development:
description: Use the research findings to craft a unique story concept for
@@ -39,8 +33,6 @@ roles:
scripts.
goal: Write a movie script about a cat in Mars
role: Scriptwriter
llm:
model: "gpt-4o"
tasks:
scriptwriting_task:
description: Turn the story concept into a production-ready movie script,
View File
Whitespace-only changes.
+50
View File
@@ -0,0 +1,50 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
import json
from e2b_code_interpreter import Sandbox
def code_interpreter(code: str):
"""
A function to demonstrate running Python code dynamically using e2b_code_interpreter.
"""
print(f"\n{'='*50}\n> Running following AI-generated code:\n{code}\n{'='*50}")
exec_result = Sandbox().run_code(code)
if exec_result.error:
print("[Code Interpreter error]", exec_result.error)
return {"error": str(exec_result.error)}
else:
results = []
for result in exec_result.results:
if hasattr(result, '__iter__'):
results.extend(list(result))
else:
results.append(str(result))
logs = {"stdout": list(exec_result.logs.stdout), "stderr": list(exec_result.logs.stderr)}
return json.dumps({"results": results, "logs": logs})
code_agent = Agent(
name="code_agent",
llm="gpt-4o-mini",
backstory="Expert in writing Python scripts",
self_reflect=False
)
execution_agent = Agent(
name="execution_agent",
llm="gpt-4o-mini",
backstory="Expert in executing Python scripts",
self_reflect=False,
tools=[code_interpreter]
)
code_agent_task = Task(
description="Write a simple Python script to print 'Hello, World!'",
expected_output="A Python script that prints 'Hello, World!'",
agent=code_agent
)
execution_agent_task = Task(
description="Execute the Python script",
expected_output="The output of the Python script",
agent=execution_agent
)
agents = PraisonAIAgents(agents=[code_agent, execution_agent], tasks=[code_agent_task, execution_agent_task])
agents.start()
+9 -1
View File
@@ -1,4 +1,12 @@
from praisonaiagents import Agent, Task, PraisonAIAgents, error_logs, register_display_callback
from praisonaiagents import (
Agent,
Task,
PraisonAIAgents,
error_logs,
register_display_callback,
sync_display_callbacks,
async_display_callbacks
)
from duckduckgo_search import DDGS
from rich.console import Console
import json
+6
View File
@@ -0,0 +1,6 @@
from praisonaiagents import Agent, Agents, Tools
research_agent = Agent(instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
summarise_agent = Agent(instructions="You are a summarize agent to summarise in points")
agents = Agents(agents=[research_agent, summarise_agent])
agents.start()
+10 -2
View File
@@ -5,6 +5,7 @@ Praison AI Agents - A package for hierarchical AI agent task execution
from .agent.agent import Agent
from .agents.agents import PraisonAIAgents
from .task.task import Task
from .tools.tools import Tools
from .main import (
TaskOutput,
ReflectionOutput,
@@ -17,12 +18,18 @@ from .main import (
clean_triple_backticks,
error_logs,
register_display_callback,
display_callbacks,
sync_display_callbacks,
async_display_callbacks,
)
# Add Agents as an alias for PraisonAIAgents
Agents = PraisonAIAgents
__all__ = [
'Agent',
'PraisonAIAgents',
'Agents',
'Tools',
'Task',
'TaskOutput',
'ReflectionOutput',
@@ -35,5 +42,6 @@ __all__ = [
'clean_triple_backticks',
'error_logs',
'register_display_callback',
'display_callbacks',
'sync_display_callbacks',
'async_display_callbacks',
]
+74 -47
View File
@@ -3,7 +3,7 @@ import time
import json
import logging
import asyncio
from typing import List, Optional, Any, Dict, Union, Literal
from typing import List, Optional, Any, Dict, Union, Literal, TYPE_CHECKING
from rich.console import Console
from rich.live import Live
from openai import AsyncOpenAI
@@ -19,6 +19,9 @@ from ..main import (
error_logs
)
if TYPE_CHECKING:
from ..task.task import Task
class Agent:
def _generate_tool_definition(self, function_name):
"""
@@ -132,11 +135,12 @@ class Agent:
def __init__(
self,
name: str,
role: str,
goal: str,
backstory: str,
llm: Optional[Union[str, Any]] = "gpt-4o",
name: Optional[str] = None,
role: Optional[str] = None,
goal: Optional[str] = None,
backstory: Optional[str] = None,
instructions: Optional[str] = None,
llm: Optional[Union[str, Any]] = None,
tools: Optional[List[Any]] = None,
function_calling_llm: Optional[Any] = None,
max_iter: int = 20,
@@ -158,16 +162,35 @@ class Agent:
knowledge_sources: Optional[List[Any]] = None,
use_system_prompt: Optional[bool] = True,
markdown: bool = True,
self_reflect: bool = True,
self_reflect: Optional[bool] = None,
max_reflect: int = 3,
min_reflect: int = 1,
reflect_llm: Optional[str] = None
):
self.name = name
self.role = role
self.goal = goal
self.backstory = backstory
self.llm = llm
# Handle backward compatibility for required fields
if all(x is None for x in [name, role, goal, backstory, instructions]):
raise ValueError("At least one of name, role, goal, backstory, or instructions must be provided")
# If instructions are provided, use them to set role, goal, and backstory
if instructions:
self.name = name or "Agent"
self.role = role or "Assistant"
self.goal = goal or instructions
self.backstory = backstory or instructions
# Set self_reflect to False by default for instruction-based agents
self.self_reflect = False if self_reflect is None else self_reflect
else:
# Use provided values or defaults
self.name = name or "Agent"
self.role = role or "Assistant"
self.goal = goal or "Help the user with their tasks"
self.backstory = backstory or "I am an AI assistant"
# Default to True for traditional agents if not specified
self.self_reflect = True if self_reflect is None else self_reflect
self.instructions = instructions
# Check for model name in environment variable if not provided
self.llm = llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
self.tools = tools if tools else [] # Store original tools
self.function_calling_llm = function_calling_llm
self.max_iter = max_iter
@@ -190,10 +213,10 @@ class Agent:
self.use_system_prompt = use_system_prompt
self.chat_history = []
self.markdown = markdown
self.self_reflect = self_reflect
self.max_reflect = max_reflect
self.min_reflect = min_reflect
self.reflect_llm = reflect_llm
# Use the same model selection logic for reflect_llm
self.reflect_llm = reflect_llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
self.console = Console() # Create a single console instance for the agent
# Initialize system prompt
@@ -202,6 +225,21 @@ Your Role: {self.role}\n
Your Goal: {self.goal}
"""
def generate_task(self) -> 'Task':
"""Generate a Task object from the agent's instructions"""
from ..task.task import Task
description = self.instructions if self.instructions else f"Execute task as {self.role} with goal: {self.goal}"
expected_output = "Complete the assigned task successfully"
return Task(
name=self.name,
description=description,
expected_output=expected_output,
agent=self,
tools=self.tools
)
def execute_tool(self, function_name, arguments):
"""
Execute a tool dynamically based on the function name and arguments.
@@ -356,7 +394,7 @@ Your Goal: {self.goal}
display_error(f"Error in chat completion: {e}")
return None
def chat(self, prompt, temperature=0.2, tools=None, output_json=None):
def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None):
if self.use_system_prompt:
system_prompt = f"""{self.backstory}\n
Your Role: {self.role}\n
@@ -364,6 +402,8 @@ Your Goal: {self.goal}
"""
if output_json:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_json.schema_json()}"
elif output_pydantic:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_pydantic.schema_json()}"
else:
system_prompt = None
@@ -372,9 +412,9 @@ Your Goal: {self.goal}
messages.append({"role": "system", "content": system_prompt})
messages.extend(self.chat_history)
# Modify prompt if output_json is specified
# Modify prompt if output_json or output_pydantic is specified
original_prompt = prompt
if output_json:
if output_json or output_pydantic:
if isinstance(prompt, str):
prompt += "\nReturn ONLY a valid JSON object. No other text or explanation."
elif isinstance(prompt, list):
@@ -449,23 +489,15 @@ Your Goal: {self.goal}
return None
response_text = response.choices[0].message.content.strip()
# Handle output_json if specified
if output_json:
try:
# Clean the response text to get only JSON
cleaned_json = self.clean_json_output(response_text)
# Parse into Pydantic model
parsed_model = output_json.model_validate_json(cleaned_json)
# Add to chat history and return
self.chat_history.append({"role": "user", "content": original_prompt})
self.chat_history.append({"role": "assistant", "content": response_text})
if self.verbose:
display_interaction(original_prompt, response_text, markdown=self.markdown,
generation_time=time.time() - start_time, console=self.console)
return parsed_model
except Exception as e:
display_error(f"Failed to parse response as {output_json.__name__}: {e}")
return None
# Handle output_json or output_pydantic if specified
if output_json or output_pydantic:
# Add to chat history and return raw response
self.chat_history.append({"role": "user", "content": original_prompt})
self.chat_history.append({"role": "assistant", "content": response_text})
if self.verbose:
display_interaction(original_prompt, response_text, markdown=self.markdown,
generation_time=time.time() - start_time, console=self.console)
return response_text
if not self.self_reflect:
self.chat_history.append({"role": "user", "content": original_prompt})
@@ -547,19 +579,21 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
cleaned = cleaned[:-3].strip()
return cleaned
async def achat(self, prompt, temperature=0.2, tools=None, output_json=None):
async def achat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None):
"""Async version of chat method"""
try:
# Build system prompt
system_prompt = self.system_prompt
if output_json:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_json.schema_json()}"
elif output_pydantic:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_pydantic.schema_json()}"
# Build messages
if isinstance(prompt, str):
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt + ("\nReturn ONLY a valid JSON object. No other text or explanation." if output_json else "")}
{"role": "user", "content": prompt + ("\nReturn ONLY a valid JSON object. No other text or explanation." if (output_json or output_pydantic) else "")}
]
else:
# For multimodal prompts
@@ -567,7 +601,7 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt}
]
if output_json:
if output_json or output_pydantic:
# Add JSON instruction to text content
for item in messages[-1]["content"]:
if item["type"] == "text":
@@ -601,22 +635,15 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
tools=formatted_tools
)
return await self._achat_completion(response, tools)
elif output_json:
elif output_json or output_pydantic:
response = await async_client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
response_format={"type": "json_object"}
)
result = response.choices[0].message.content
# Clean and parse the JSON response
cleaned_json = self.clean_json_output(result)
try:
parsed = json.loads(cleaned_json)
return output_json(**parsed)
except Exception as e:
display_error(f"Error parsing JSON response: {e}")
return None
# Return the raw response
return response.choices[0].message.content
else:
response = await async_client.chat.completions.create(
model=self.llm,
+61 -15
View File
@@ -41,7 +41,10 @@ def process_video(video_path: str, seconds_per_frame=2):
return base64_frames
class PraisonAIAgents:
def __init__(self, agents, tasks, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None):
def __init__(self, agents, tasks=None, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None):
if not agents:
raise ValueError("At least one agent must be provided")
self.agents = agents
self.tasks = {}
if max_retries < 3:
@@ -51,12 +54,41 @@ class PraisonAIAgents:
self.verbose = verbose
self.max_retries = max_retries
self.process = process
if not manager_llm:
logging.debug("No manager_llm provided. Using OPENAI_MODEL_NAME environment variable or defaulting to 'gpt-4o'")
self.manager_llm = manager_llm if manager_llm else os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
# Check for manager_llm in environment variable if not provided
self.manager_llm = manager_llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
if self.verbose:
logging.info(f"Using model {self.manager_llm} for manager")
# If no tasks provided, generate them from agents
if tasks is None:
tasks = []
for agent in self.agents:
task = agent.generate_task()
tasks.append(task)
logging.info(f"Auto-generated {len(tasks)} tasks from agents")
else:
# Validate tasks for backward compatibility
if not tasks:
raise ValueError("If tasks are provided, at least one task must be present")
logging.info(f"Using {len(tasks)} provided tasks")
# Add tasks and set their status
for task in tasks:
self.add_task(task)
task.status = "not started"
# If tasks were auto-generated from agents or process is sequential, set up sequential flow
if len(tasks) > 1 and (process == "sequential" or all(task.next_tasks == [] for task in tasks)):
for i in range(len(tasks) - 1):
# Set up next task relationship
tasks[i].next_tasks = [tasks[i + 1].name]
# Set up context for the next task to include the current task
if tasks[i + 1].context is None:
tasks[i + 1].context = []
tasks[i + 1].context.append(tasks[i])
logging.info("Set up sequential flow with automatic context passing")
self._state = {} # Add state storage at PraisonAIAgents level
def add_task(self, task):
@@ -119,9 +151,9 @@ Expected Output: {task.expected_output}.
else:
context_results += f"Previous task {context_task.name if context_task.name else context_task.description} had no result.\n"
task_prompt += f"""
Here are the results of previous tasks that might be useful:\n
{context_results}
"""
Here are the results of previous tasks that might be useful:\n
{context_results}
"""
task_prompt += "Please provide only the final result of your work. Do not add any conversation or extra explanation."
if self.verbose >= 2:
@@ -163,10 +195,17 @@ Expected Output: {task.expected_output}.
agent_output = await executor_agent.achat(
_get_multimodal_message(task_prompt, task.images),
tools=task.tools
tools=task.tools,
output_json=task.output_json,
output_pydantic=task.output_pydantic
)
else:
agent_output = await executor_agent.achat(task_prompt, tools=task.tools)
agent_output = await executor_agent.achat(
task_prompt,
tools=task.tools,
output_json=task.output_json,
output_pydantic=task.output_pydantic
)
if agent_output:
task_output = TaskOutput(
@@ -320,7 +359,7 @@ Expected Output: {task.expected_output}.
task_prompt = f"""
You need to do the following task: {task.description}.
Expected Output: {task.expected_output}.
"""
"""
if task.context:
context_results = ""
for context_task in task.context:
@@ -329,9 +368,9 @@ Expected Output: {task.expected_output}.
else:
context_results += f"Previous task {context_task.name if context_task.name else context_task.description} had no result.\n"
task_prompt += f"""
Here are the results of previous tasks that might be useful:\n
{context_results}
"""
Here are the results of previous tasks that might be useful:\n
{context_results}
"""
task_prompt += "Please provide only the final result of your work. Do not add any conversation or extra explanation."
if self.verbose >= 2:
@@ -373,10 +412,17 @@ Expected Output: {task.expected_output}.
agent_output = executor_agent.chat(
_get_multimodal_message(task_prompt, task.images),
tools=task.tools
tools=task.tools,
output_json=task.output_json,
output_pydantic=task.output_pydantic
)
else:
agent_output = executor_agent.chat(task_prompt, tools=task.tools)
agent_output = executor_agent.chat(
task_prompt,
tools=task.tools,
output_json=task.output_json,
output_pydantic=task.output_pydantic
)
if agent_output:
task_output = TaskOutput(
+164 -21
View File
@@ -12,6 +12,7 @@ from rich.text import Text
from rich.markdown import Markdown
from rich.logging import RichHandler
from rich.live import Live
import asyncio
LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO').upper()
@@ -25,12 +26,49 @@ logging.basicConfig(
# Global list to store error logs
error_logs = []
# Global callback registry
display_callbacks = {}
# Separate registries for sync and async callbacks
sync_display_callbacks = {}
async_display_callbacks = {}
def register_display_callback(display_type: str, callback_fn):
"""Register a callback function for a specific display type."""
display_callbacks[display_type] = callback_fn
# At the top of the file, add display_callbacks to __all__
__all__ = [
'error_logs',
'register_display_callback',
'sync_display_callbacks',
'async_display_callbacks',
# ... other exports
]
def register_display_callback(display_type: str, callback_fn, is_async: bool = False):
"""Register a synchronous or asynchronous callback function for a specific display type.
Args:
display_type (str): Type of display event ('interaction', 'self_reflection', etc.)
callback_fn: The callback function to register
is_async (bool): Whether the callback is asynchronous
"""
if is_async:
async_display_callbacks[display_type] = callback_fn
else:
sync_display_callbacks[display_type] = callback_fn
async def execute_callback(display_type: str, **kwargs):
"""Execute both sync and async callbacks for a given display type.
Args:
display_type (str): Type of display event
**kwargs: Arguments to pass to the callback functions
"""
# Execute synchronous callback if registered
if display_type in sync_display_callbacks:
callback = sync_display_callbacks[display_type]
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, lambda: callback(**kwargs))
# Execute asynchronous callback if registered
if display_type in async_display_callbacks:
callback = async_display_callbacks[display_type]
await callback(**kwargs)
def _clean_display_content(content: str, max_length: int = 20000) -> str:
"""Helper function to clean and truncate content for display."""
@@ -53,11 +91,10 @@ def _clean_display_content(content: str, max_length: int = 20000) -> str:
return content.strip()
def display_interaction(message, response, markdown=True, generation_time=None, console=None):
"""Display the interaction between user and assistant."""
"""Synchronous version of display_interaction."""
if console is None:
console = Console()
# Handle multimodal content (list)
if isinstance(message, list):
text_content = next((item["text"] for item in message if item["type"] == "text"), "")
message = text_content
@@ -65,16 +102,16 @@ def display_interaction(message, response, markdown=True, generation_time=None,
message = _clean_display_content(str(message))
response = _clean_display_content(str(response))
# Execute callback if registered
if 'interaction' in display_callbacks:
display_callbacks['interaction'](
# Execute synchronous callback if registered
if 'interaction' in sync_display_callbacks:
sync_display_callbacks['interaction'](
message=message,
response=response,
markdown=markdown,
generation_time=generation_time
)
# Existing display logic...
# Rest of the display logic...
if generation_time:
console.print(Text(f"Response generated in {generation_time:.1f}s", style="dim"))
@@ -93,8 +130,8 @@ def display_self_reflection(message: str, console=None):
message = _clean_display_content(str(message))
# Execute callback if registered
if 'self_reflection' in display_callbacks:
display_callbacks['self_reflection'](message=message)
if 'self_reflection' in sync_display_callbacks:
sync_display_callbacks['self_reflection'](message=message)
console.print(Panel.fit(Text(message, style="bold yellow"), title="Self Reflection", border_style="magenta"))
@@ -106,8 +143,8 @@ def display_instruction(message: str, console=None):
message = _clean_display_content(str(message))
# Execute callback if registered
if 'instruction' in display_callbacks:
display_callbacks['instruction'](message=message)
if 'instruction' in sync_display_callbacks:
sync_display_callbacks['instruction'](message=message)
console.print(Panel.fit(Text(message, style="bold blue"), title="Instruction", border_style="cyan"))
@@ -119,8 +156,8 @@ def display_tool_call(message: str, console=None):
message = _clean_display_content(str(message))
# Execute callback if registered
if 'tool_call' in display_callbacks:
display_callbacks['tool_call'](message=message)
if 'tool_call' in sync_display_callbacks:
sync_display_callbacks['tool_call'](message=message)
console.print(Panel.fit(Text(message, style="bold cyan"), title="Tool Call", border_style="green"))
@@ -132,8 +169,8 @@ def display_error(message: str, console=None):
message = _clean_display_content(str(message))
# Execute callback if registered
if 'error' in display_callbacks:
display_callbacks['error'](message=message)
if 'error' in sync_display_callbacks:
sync_display_callbacks['error'](message=message)
console.print(Panel.fit(Text(message, style="bold red"), title="Error", border_style="red"))
error_logs.append(message)
@@ -150,8 +187,114 @@ def display_generating(content: str = "", start_time: Optional[float] = None):
content = _clean_display_content(str(content))
# Execute callback if registered
if 'generating' in display_callbacks:
display_callbacks['generating'](
if 'generating' in sync_display_callbacks:
sync_display_callbacks['generating'](
content=content,
elapsed_time=elapsed_str.strip() if elapsed_str else None
)
return Panel(Markdown(content), title=f"Generating...{elapsed_str}", border_style="green")
# Async versions with 'a' prefix
async def adisplay_interaction(message, response, markdown=True, generation_time=None, console=None):
"""Async version of display_interaction."""
if console is None:
console = Console()
if isinstance(message, list):
text_content = next((item["text"] for item in message if item["type"] == "text"), "")
message = text_content
message = _clean_display_content(str(message))
response = _clean_display_content(str(response))
# Execute callbacks
await execute_callback(
'interaction',
message=message,
response=response,
markdown=markdown,
generation_time=generation_time
)
# Rest of the display logic...
if generation_time:
console.print(Text(f"Response generated in {generation_time:.1f}s", style="dim"))
if markdown:
console.print(Panel.fit(Markdown(message), title="Message", border_style="cyan"))
console.print(Panel.fit(Markdown(response), title="Response", border_style="cyan"))
else:
console.print(Panel.fit(Text(message, style="bold green"), title="Message", border_style="cyan"))
console.print(Panel.fit(Text(response, style="bold blue"), title="Response", border_style="cyan"))
async def adisplay_self_reflection(message: str, console=None):
"""Async version of display_self_reflection."""
if not message or not message.strip():
return
if console is None:
console = Console()
message = _clean_display_content(str(message))
if 'self_reflection' in async_display_callbacks:
await async_display_callbacks['self_reflection'](message=message)
console.print(Panel.fit(Text(message, style="bold yellow"), title="Self Reflection", border_style="magenta"))
async def adisplay_instruction(message: str, console=None):
"""Async version of display_instruction."""
if not message or not message.strip():
return
if console is None:
console = Console()
message = _clean_display_content(str(message))
if 'instruction' in async_display_callbacks:
await async_display_callbacks['instruction'](message=message)
console.print(Panel.fit(Text(message, style="bold blue"), title="Instruction", border_style="cyan"))
async def adisplay_tool_call(message: str, console=None):
"""Async version of display_tool_call."""
if not message or not message.strip():
return
if console is None:
console = Console()
message = _clean_display_content(str(message))
if 'tool_call' in async_display_callbacks:
await async_display_callbacks['tool_call'](message=message)
console.print(Panel.fit(Text(message, style="bold cyan"), title="Tool Call", border_style="green"))
async def adisplay_error(message: str, console=None):
"""Async version of display_error."""
if not message or not message.strip():
return
if console is None:
console = Console()
message = _clean_display_content(str(message))
if 'error' in async_display_callbacks:
await async_display_callbacks['error'](message=message)
console.print(Panel.fit(Text(message, style="bold red"), title="Error", border_style="red"))
error_logs.append(message)
async def adisplay_generating(content: str = "", start_time: Optional[float] = None):
"""Async version of display_generating."""
if not content or not str(content).strip():
return Panel("", title="", border_style="green")
elapsed_str = ""
if start_time is not None:
elapsed = time.time() - start_time
elapsed_str = f" {elapsed:.1f}s"
content = _clean_display_content(str(content))
if 'generating' in async_display_callbacks:
await async_display_callbacks['generating'](
content=content,
elapsed_time=elapsed_str.strip() if elapsed_str else None
)
+4
View File
@@ -0,0 +1,4 @@
"""Tools package for PraisonAI Agents"""
from .tools import Tools
__all__ = ['Tools']
+40
View File
@@ -0,0 +1,40 @@
"""Tools module for PraisonAI Agents"""
from typing import List, Dict
import logging
import importlib
class Tools:
@staticmethod
def internet_search(query: str) -> List[Dict]:
"""
Perform a search using DuckDuckGo.
Args:
query (str): The search query.
Returns:
list: A list of search result titles, URLs, and snippets.
"""
# Check if duckduckgo_search is installed
if importlib.util.find_spec("duckduckgo_search") is None:
error_msg = "DuckDuckGo search is not available. Please install duckduckgo_search package using: pip install duckduckgo_search"
logging.error(error_msg)
return [{"error": error_msg}]
try:
# Import only when needed
from duckduckgo_search import DDGS
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=5):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return results
except Exception as e:
error_msg = f"Error during DuckDuckGo search: {e}"
logging.error(error_msg)
return [{"error": error_msg}]
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.0.18"
version = "0.0.23"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
authors = [
{ name="Mervin Praison" }
+56
View File
@@ -0,0 +1,56 @@
from praisonaiagents import Agent, Task, PraisonAIAgents, Tools
from pydantic import BaseModel
class AnalysisReport(BaseModel):
title: str
findings: str
summary: str
# Create a researcher agent
researcher = Agent(
name="AIResearcher",
role="Technology Research Analyst",
goal="Analyze and structure information about AI developments",
backstory="Expert analyst specializing in AI technology trends",
verbose=True,
llm="gpt-4o-mini",
tools=[Tools.internet_search],
self_reflect=False
)
# Create an analyst agent
analyst = Agent(
name="DataAnalyst",
role="Data Insights Specialist",
goal="Structure and analyze research findings",
backstory="Senior data analyst with expertise in pattern recognition",
verbose=True,
llm="gpt-4o-mini",
self_reflect=False
)
# Define structured tasks
research_task = Task(
name="gather_research",
description="Research recent AI developments in 2024",
agent=researcher,
expected_output="Research findings"
)
analysis_task = Task(
name="analyze_findings",
description="Analyze research findings and create structured report. No additional text or explanation.",
agent=analyst,
output_json=AnalysisReport,
expected_output="JSON object"
)
# Initialize and run agents
agents = PraisonAIAgents(
agents=[researcher, analyst],
tasks=[research_task, analysis_task],
process="sequential",
verbose=True
)
result = agents.start()
print(result)
+1 -1
View File
@@ -186,7 +186,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.18"
version = "0.0.23"
source = { editable = "." }
dependencies = [
{ name = "openai" },
-1
View File
@@ -1 +0,0 @@
# Welcome to Praison AI! 🚀🤖
+1 -1
View File
@@ -110,7 +110,7 @@ Loads environment variables from .env file or system and sets them.</p>
file.write(&#34;FROM python:3.11-slim\n&#34;)
file.write(&#34;WORKDIR /app\n&#34;)
file.write(&#34;COPY . .\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.0.24 gunicorn markdown\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.0.32 gunicorn markdown\n&#34;)
file.write(&#34;EXPOSE 8080\n&#34;)
file.write(&#39;CMD [&#34;gunicorn&#34;, &#34;-b&#34;, &#34;0.0.0.0:8080&#34;, &#34;api:app&#34;]\n&#39;)
+280 -6
View File
@@ -1,12 +1,13 @@
---
title: "Callbacks"
title: "AI Agents with Callbacks"
sidebarTitle: "Callbacks"
description: "Learn how to implement callbacks to monitor and log AI agent interactions, errors, and task completions."
icon: "bell"
---
<Note>
**Prerequisites**
- Python 3.8 or higher
- Python 3.10 or higher
- PraisonAI Agents package installed
- Basic understanding of Python functions
</Note>
@@ -40,8 +41,17 @@ icon: "bell"
print(f"Received message: {message}")
print(f"Got response: {response}")
# Register the callback
register_display_callback('interaction', simple_callback)
# Register as synchronous callback
register_display_callback('interaction', simple_callback, is_async=False)
# For async callbacks
async def async_simple_callback(message=None, response=None, **kwargs):
await asyncio.sleep(0) # Non-blocking pause
print(f"Received message: {message}")
print(f"Got response: {response}")
# Register as async callback
register_display_callback('interaction', async_simple_callback, is_async=True)
```
</Step>
@@ -75,6 +85,10 @@ icon: "bell"
</Step>
</Steps>
<Note>
PraisonAI Agents supports both synchronous and asynchronous callbacks. When registering a callback, specify `is_async=True` for async callbacks. Both sync and async callbacks for the same display type can be executed simultaneously.
</Note>
## Understanding Callbacks
<Card title="What are Callbacks?" icon="question">
@@ -124,8 +138,16 @@ logging.basicConfig(level=logging.INFO)
def log_callback(message=None, **kwargs):
logging.info(f"Agent message: {message}")
# Register callback
register_display_callback('interaction', log_callback)
# Register synchronous callback
register_display_callback('interaction', log_callback, is_async=False)
# Register asynchronous callback
async def async_log_callback(message=None, **kwargs):
await asyncio.sleep(0)
logging.info(f"Agent message: {message}")
# Register as async callback
register_display_callback('interaction', async_log_callback, is_async=True)
```
```python Advanced
@@ -475,8 +497,260 @@ def main():
if __name__ == "__main__":
main()
## Async Callbacks
Async callbacks allow you to handle events asynchronously, which is particularly useful for long-running operations or when dealing with multiple agents simultaneously.
### Basic Async Callback Implementation
```python
import asyncio
from praisonaiagents import register_display_callback
async def async_interaction_callback(message=None, response=None, **kwargs):
"""Async callback for handling interactions"""
await asyncio.sleep(0) # Non-blocking pause
print(f"Async processing - Message: {message}")
print(f"Response: {response}")
# Register the async callback
register_display_callback('interaction', async_interaction_callback)
```
### Complete Async Example
```python
import asyncio
from praisonaiagents import Agent, Task, PraisonAIAgents, register_display_callback
import logging
from datetime import datetime
# Setup async logging
async def setup_async_logging():
logging.basicConfig(
filename='async_ai_interactions.log',
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Async callbacks
async def async_interaction_callback(message=None, response=None, markdown=None, generation_time=None):
await asyncio.sleep(0)
logging.info(f"""
=== ASYNC INTERACTION ===
Time: {datetime.now()}
Generation Time: {generation_time}s
Message: {message}
Response: {response}
""")
async def async_error_callback(message=None):
await asyncio.sleep(0)
logging.error(f"""
=== ASYNC ERROR ===
Time: {datetime.now()}
Message: {message}
""")
# Register async callbacks
register_display_callback('interaction', async_interaction_callback, is_async=True)
register_display_callback('error', async_error_callback, is_async=True)
# Create async task callback
async def async_task_callback(output):
await asyncio.sleep(0)
logging.info(f"""
=== ASYNC TASK COMPLETED ===
Time: {datetime.now()}
Description: {output.description}
Agent: {output.agent}
Output: {output.raw[:200]}...
""")
async def main():
await setup_async_logging()
# Create agent with async capabilities
agent = Agent(
name="AsyncAgent",
role="Assistant",
goal="Demonstrate async callbacks",
backstory="I am an async-capable assistant",
verbose=True
)
# Create task with async callback
task = Task(
name="async_task",
description="Demonstrate async callbacks",
agent=agent,
expected_output="Async demonstration complete",
callback=async_task_callback
)
# Create and run agents with async support
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
verbose=True
)
await agents.start_async()
if __name__ == "__main__":
asyncio.run(main())
```
### Available Async Functions
PraisonAI Agents provides several async versions of display functions, prefixed with 'a'. Here's the complete list:
<CardGroup cols={2}>
<Card title="adisplay_interaction" icon="comments">
```python
async def adisplay_interaction(
message,
response,
markdown=True,
generation_time=None,
console=None
)
```
Async version of display_interaction for showing conversations.
</Card>
<Card title="adisplay_self_reflection" icon="brain">
```python
async def adisplay_self_reflection(
message: str,
console=None
)
```
Async version for displaying agent's self-reflection moments.
</Card>
<Card title="adisplay_instruction" icon="list-check">
```python
async def adisplay_instruction(
message: str,
console=None
)
```
Async version for showing instructions.
</Card>
<Card title="adisplay_tool_call" icon="wrench">
```python
async def adisplay_tool_call(
message: str,
console=None
)
```
Async version for displaying tool calls.
</Card>
<Card title="adisplay_error" icon="triangle-exclamation">
```python
async def adisplay_error(
message: str,
console=None
)
```
Async version for error messages.
</Card>
<Card title="adisplay_generating" icon="spinner">
```python
async def adisplay_generating(
content: str = "",
start_time: Optional[float] = None
)
```
Async version for showing generation progress.
</Card>
</CardGroup>
#### Example Usage
```python
import asyncio
from praisonaiagents import (
adisplay_interaction,
adisplay_error,
adisplay_tool_call
)
async def main():
# Display an interaction
await adisplay_interaction(
message="What's the weather?",
response="Let me check that for you.",
generation_time=0.5
)
# Display a tool call
await adisplay_tool_call(
"Calling weather API for location data..."
)
# Handle an error
try:
raise Exception("API connection failed")
except Exception as e:
await adisplay_error(str(e))
if __name__ == "__main__":
asyncio.run(main())
```
### Best Practices for Async Callbacks
<AccordionGroup>
<Accordion title="Error Handling">
Implement proper error handling in async callbacks:
```python
async def safe_async_callback(message=None, **kwargs):
try:
await asyncio.sleep(0)
# Process the callback
await process_message(message)
except Exception as e:
logging.error(f"Async callback error: {e}")
```
</Accordion>
<Accordion title="Performance">
Optimize async callbacks for better performance:
```python
async def optimized_async_callback(message=None, **kwargs):
# Use asyncio.gather for concurrent operations
results = await asyncio.gather(
process_message(message),
update_status(),
log_interaction(message)
)
return results
```
</Accordion>
</AccordionGroup>
### Key Considerations
<CardGroup cols={2}>
<Card title="Concurrency" icon="arrows-split-up-and-left">
- Use asyncio.gather for parallel operations
- Avoid blocking calls in async callbacks
- Handle concurrent access to shared resources
</Card>
<Card title="Resource Management" icon="server">
- Implement proper cleanup in async contexts
- Use async context managers when appropriate
- Monitor memory usage in long-running callbacks
</Card>
</CardGroup>
<Note>
When using async callbacks, ensure all dependent operations are also async-compatible to prevent blocking the event loop.
</Note>
## Troubleshooting
+287
View File
@@ -0,0 +1,287 @@
---
title: "Code Execution Agent"
sidebarTitle: "Code Agent"
description: "Learn how to create AI agents that can write and execute Python code safely using e2b code interpreter."
icon: "code"
---
<Note>
**Prerequisites**
- Python 3.10 or higher
- PraisonAI Agents package installed
- e2b_code_interpreter package installed
- Basic understanding of Python
</Note>
## Quick Start
Create AI agents that can write and execute Python code in a sandboxed environment!
<Steps>
<Step title="Install Packages">
First, install the required packages:
```bash
pip install praisonaiagents e2b_code_interpreter
```
</Step>
<Step title="Import Required Components">
Import the necessary components:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
from e2b_code_interpreter import Sandbox
```
</Step>
<Step title="Define Code Interpreter">
Create a code interpreter function:
```python
def code_interpreter(code: str):
print(f"\n{'='*50}\n> Running following AI-generated code:\n{code}\n{'='*50}")
exec_result = Sandbox().run_code(code)
if exec_result.error:
print("[Code Interpreter error]", exec_result.error)
return {"error": str(exec_result.error)}
else:
results = []
for result in exec_result.results:
if hasattr(result, '__iter__'):
results.extend(list(result))
else:
results.append(str(result))
logs = {"stdout": list(exec_result.logs.stdout), "stderr": list(exec_result.logs.stderr)}
return json.dumps({"results": results, "logs": logs})
```
</Step>
<Step title="Create Code Agents">
Set up your code writing and execution agents:
```python
code_agent = Agent(
name="code_agent",
backstory="Expert in writing Python scripts",
self_reflect=False
)
execution_agent = Agent(
name="execution_agent",
backstory="Expert in executing Python scripts",
self_reflect=False,
tools=[code_interpreter]
)
```
</Step>
</Steps>
## Understanding Code Agents
<Card title="What are Code Agents?" icon="question">
Code agents are specialized AI agents that can:
- Write Python code based on requirements
- Execute code safely in a sandboxed environment
- Handle code execution results and errors
- Work together in a pipeline (writer → executor)
</Card>
## Key Components
<CardGroup cols={2}>
<Card title="Code Writer Agent" icon="pencil">
Specialized in writing Python code
```python
Agent(
name="code_agent",
backstory="Expert in writing Python scripts"
)
```
</Card>
<Card title="Code Executor Agent" icon="play">
Executes code using sandbox
```python
Agent(
name="execution_agent",
tools=[code_interpreter]
)
```
</Card>
<Card title="Sandbox Environment" icon="box">
Safe code execution environment using e2b
</Card>
<Card title="Execution Results" icon="output">
Structured output with results and logs
</Card>
</CardGroup>
## Examples
### Simple Hello World Example
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
from e2b_code_interpreter import Sandbox
# Create agents
code_agent = Agent(
name="code_agent",
backstory="Expert in writing Python scripts",
self_reflect=False
)
execution_agent = Agent(
name="execution_agent",
backstory="Expert in executing Python scripts",
self_reflect=False,
tools=[code_interpreter]
)
# Define tasks
code_task = Task(
description="Write a simple Python script to print 'Hello, World!'",
expected_output="A Python script that prints 'Hello, World!'",
agent=code_agent
)
execution_task = Task(
description="Execute the Python script",
expected_output="The output of the Python script",
agent=execution_agent
)
# Initialize and run agents
agents = PraisonAIAgents(
agents=[code_agent, execution_agent],
tasks=[code_task, execution_task]
)
result = agents.start()
```
### Advanced Code Generation
<CodeGroup>
```python Data Analysis
# Create a data analysis script
code_task = Task(
description="""
Write a Python script that:
1. Creates a sample dataset
2. Performs basic statistical analysis
3. Generates a plot
""",
agent=code_agent
)
```
```python Web Scraping
# Create a web scraping script
code_task = Task(
description="""
Write a Python script that:
1. Scrapes a website
2. Extracts specific data
3. Saves to CSV
""",
agent=code_agent
)
```
</CodeGroup>
## Best Practices
<AccordionGroup>
<Accordion title="Code Safety">
Ensure safe code execution:
```python
# Always use sandbox environment
exec_result = Sandbox().run_code(code)
# Handle errors gracefully
if exec_result.error:
return {"error": str(exec_result.error)}
```
</Accordion>
<Accordion title="Task Description">
Write clear code requirements:
```python
# Good
Task(description="""
Write a Python script that:
1. Takes user input
2. Validates the input
3. Processes the data
4. Returns formatted output
""")
# Too vague
Task(description="Write some Python code")
```
</Accordion>
</AccordionGroup>
## Common Patterns
### Code Generation and Testing
```python
# Generate code
code_task = Task(
description="Write a function to calculate fibonacci numbers",
agent=code_agent
)
# Generate test cases
test_task = Task(
description="Write test cases for the fibonacci function",
agent=code_agent
)
# Execute tests
execution_task = Task(
description="Run the test cases",
agent=execution_agent
)
```
### Interactive Code Development
```python
# Write initial code
initial_code_task = Task(
description="Write initial version of the script",
agent=code_agent
)
# Test and improve
improvement_task = Task(
description="Analyze execution results and improve the code",
agent=code_agent
)
```
## Troubleshooting
<CardGroup cols={2}>
<Card title="Execution Errors" icon="triangle-exclamation">
Check sandbox execution results:
```python
if exec_result.error:
print("[Error]", exec_result.error)
```
</Card>
<Card title="Code Generation" icon="code">
Provide clear requirements and constraints in task description
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="Advanced Code Generation" icon="graduation-cap" href="./advanced-code">
Learn about complex code generation patterns
</Card>
<Card title="Custom Tools" icon="wrench" href="./custom-tools">
Create custom code execution tools
</Card>
</CardGroup>
<Note>
Code execution agents provide a powerful way to generate and run Python code safely. They're perfect for automated code generation, testing, and execution in a controlled environment.
</Note>
+322
View File
@@ -0,0 +1,322 @@
---
title: "Structured AI Agents"
description: "Learn how to create AI agents that return structured, type-safe outputs using Pydantic models and JSON."
icon: "cube"
---
<Note>
**Prerequisites**
- Python 3.10 or higher
- PraisonAI Agents package installed
- Basic understanding of Python and Pydantic
</Note>
## Quick Start
Create AI agents that return structured, validated outputs in just a few lines of code!
<Steps>
<Step title="Install Package">
First, install the PraisonAI Agents package:
```bash
pip install praisonaiagents
```
</Step>
<Step title="Import Required Components">
Import the necessary components:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents, Tools
from pydantic import BaseModel
```
</Step>
<Step title="Define Your Data Model">
Create a Pydantic model to define your output structure:
```python
class AnalysisReport(BaseModel):
title: str
findings: str
summary: str
```
</Step>
<Step title="Create Structured Tasks">
Create tasks with structured outputs:
```python
research_task = Task(
description="Research recent AI developments",
agent=researcher,
expected_output="Research findings"
)
analysis_task = Task(
description="Analyze research findings",
agent=analyst,
output_pydantic=AnalysisReport, # For Pydantic output
# or output_json=AnalysisReport # For JSON output
expected_output="Structured analysis report"
)
```
</Step>
</Steps>
## Understanding Structured Outputs
<Card title="What are Structured Outputs?" icon="question">
Structured outputs allow you to:
- Define exact shape of data using Pydantic models
- Get type-safe, validated responses
- Choose between Pydantic objects or JSON
- Ensure consistent output format across agent responses
</Card>
## Key Components
<CardGroup cols={2}>
<Card title="Pydantic Models" icon="cube">
Define your data structure
```python
class ResearchReport(BaseModel):
title: str
content: str
```
</Card>
<Card title="Task Configuration" icon="gear">
Configure output format
```python
Task(output_pydantic=Model)
# or
Task(output_json=Model)
```
</Card>
<Card title="Output Validation" icon="check">
Automatic validation of responses against your model
</Card>
<Card title="Format Options" icon="code">
Choose between Pydantic objects or JSON output
</Card>
</CardGroup>
## Output Types
<CardGroup cols={2}>
<Card title="Pydantic Output" icon="cube">
```python
task = Task(
output_pydantic=AnalysisReport
)
# Returns Pydantic object
```
Get type-safe Pydantic model instances
</Card>
<Card title="JSON Output" icon="brackets-curly">
```python
task = Task(
output_json=AnalysisReport
)
# Returns JSON dict
```
Get JSON dictionary matching model schema
</Card>
</CardGroup>
## Examples
### Research Analysis with Structured Output
```python
from praisonaiagents import Agent, Task, PraisonAIAgents, Tools
from pydantic import BaseModel
# Define output structure
class AnalysisReport(BaseModel):
title: str
findings: str
summary: str
# Create agents
researcher = Agent(
name="AIResearcher",
role="Technology Research Analyst",
tools=[Tools.internet_search]
)
analyst = Agent(
name="DataAnalyst",
role="Data Insights Specialist"
)
# Define structured tasks
research_task = Task(
name="gather_research",
description="Research recent AI developments in 2024",
agent=researcher,
expected_output="Research findings"
)
analysis_task = Task(
name="analyze_findings",
description="Analyze research findings and create structured report",
agent=analyst,
output_pydantic=AnalysisReport,
expected_output="Structured analysis report"
)
# Initialize and run agents
agents = PraisonAIAgents(
agents=[researcher, analyst],
tasks=[research_task, analysis_task]
)
result = agents.start()
```
### Different Output Formats
<CodeGroup>
```python Pydantic
# Get Pydantic model instance
analysis_task = Task(
description="Analyze findings",
agent=analyst,
output_pydantic=AnalysisReport
)
# Result will be AnalysisReport instance
result.pydantic.title
result.pydantic.findings
result.pydantic.summary
```
```python JSON
# Get JSON dictionary
analysis_task = Task(
description="Analyze findings",
agent=analyst,
output_json=AnalysisReport
)
# Result will be dictionary
result.json_dict['title']
result.json_dict['findings']
result.json_dict['summary']
```
</CodeGroup>
## Best Practices
<AccordionGroup>
<Accordion title="Model Design">
Design clear, focused Pydantic models:
```python
# Good
class ResearchReport(BaseModel):
title: str
findings: str
summary: str
# Too complex
class Report(BaseModel):
title: str
subtitle: Optional[str]
findings: List[Dict[str, Any]]
```
</Accordion>
<Accordion title="Output Format Selection">
Choose the right output format:
```python
# Use Pydantic when you need:
# - Type validation
# - Model methods
task = Task(output_pydantic=Model)
# Use JSON when you need:
# - Simple dictionary access
# - Serialization
task = Task(output_json=Model)
```
</Accordion>
</AccordionGroup>
## Common Patterns
### Research and Structured Analysis
```python
class ResearchReport(BaseModel):
topic: str
findings: str
sources: list[str]
class Analysis(BaseModel):
key_points: list[str]
implications: str
recommendations: str
research_task = Task(
description="Research quantum computing",
agent=researcher,
output_pydantic=ResearchReport
)
analysis_task = Task(
description="Analyze research implications",
agent=analyst,
output_pydantic=Analysis
)
```
### Data Collection and Summary
```python
class DataPoint(BaseModel):
timestamp: str
value: float
source: str
class Summary(BaseModel):
average: float
trends: str
insights: str
collection_task = Task(
description="Collect data points",
output_pydantic=DataPoint
)
summary_task = Task(
description="Summarize data",
output_pydantic=Summary
)
```
## Troubleshooting
<CardGroup cols={2}>
<Card title="Validation Errors" icon="triangle-exclamation">
Check if your model matches the expected output structure
```python
# Print model schema
print(Model.schema_json())
```
</Card>
<Card title="Output Format" icon="code">
Verify you're accessing the right output field:
- `result.pydantic` for Pydantic
- `result.json_dict` for JSON
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="Advanced Models" icon="graduation-cap" href="./advanced-models">
Learn about advanced Pydantic model configurations
</Card>
<Card title="Custom Validation" icon="check-double" href="./validation">
Add custom validation to your models
</Card>
</CardGroup>
<Note>
Structured outputs make your AI agents more reliable and their responses more predictable. They're perfect for production systems where data consistency is crucial.
</Note>
+3
View File
@@ -103,6 +103,9 @@
"features/reasoning",
"framework/multimodalagents",
"features/async",
"features/mini",
"features/codeagent",
"features/structured",
"features/callbacks",
"train",
"framework/praisonaiagents",
Generated
+189 -182
View File
@@ -244,13 +244,13 @@ vertex = ["google-auth (>=2,<3)"]
[[package]]
name = "anyio"
version = "4.7.0"
version = "4.8.0"
description = "High level compatibility layer for multiple asynchronous event loop implementations"
optional = false
python-versions = ">=3.9"
files = [
{file = "anyio-4.7.0-py3-none-any.whl", hash = "sha256:ea60c3723ab42ba6fff7e8ccb0488c898ec538ff4df1f1d5e642c3601d07e352"},
{file = "anyio-4.7.0.tar.gz", hash = "sha256:2f834749c602966b7d456a7567cafcb309f96482b5081d14ac93ccd457f9dd48"},
{file = "anyio-4.8.0-py3-none-any.whl", hash = "sha256:b5011f270ab5eb0abf13385f851315585cc37ef330dd88e27ec3d34d651fd47a"},
{file = "anyio-4.8.0.tar.gz", hash = "sha256:1d9fe889df5212298c0c0723fa20479d1b94883a2df44bd3897aa91083316f7a"},
]
[package.dependencies]
@@ -261,7 +261,7 @@ typing_extensions = {version = ">=4.5", markers = "python_version < \"3.13\""}
[package.extras]
doc = ["Sphinx (>=7.4,<8.0)", "packaging", "sphinx-autodoc-typehints (>=1.2.0)", "sphinx_rtd_theme"]
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"]
test = ["anyio[trio]", "coverage[toml] (>=7)", "exceptiongroup (>=1.2.0)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "trustme", "truststore (>=0.9.1)", "uvloop (>=0.21)"]
trio = ["trio (>=0.26.1)"]
[[package]]
@@ -480,6 +480,7 @@ files = [
]
[package.dependencies]
tinycss2 = {version = ">=1.1.0,<1.5", optional = true, markers = "extra == \"css\""}
webencodings = "*"
[package.extras]
@@ -1091,13 +1092,13 @@ colorama = {version = "*", markers = "platform_system == \"Windows\""}
[[package]]
name = "cohere"
version = "5.13.4"
version = "5.13.5"
description = ""
optional = false
python-versions = "<4.0,>=3.9"
files = [
{file = "cohere-5.13.4-py3-none-any.whl", hash = "sha256:f3d81cef93ded8ea57dde6d4a547598846c9e836c18834fe0c6d2bb3da1d8e8f"},
{file = "cohere-5.13.4.tar.gz", hash = "sha256:ade5e817368a73dbdaa2bf5a812ad0952d59bdf3e583928e5207774b964d7d6b"},
{file = "cohere-5.13.5-py3-none-any.whl", hash = "sha256:cce9754003239f7a1c862ed6ebb54aad7996da2c1225688e8af9b91b0845cab5"},
{file = "cohere-5.13.5.tar.gz", hash = "sha256:036ffe2da54a2928412d5895c79f0bf11c91c51f6f562cb2b8fb85ed48c68ed7"},
]
[package.dependencies]
@@ -1483,19 +1484,19 @@ files = [
[[package]]
name = "duckduckgo-search"
version = "7.1.1"
version = "7.2.0"
description = "Search for words, documents, images, news, maps and text translation using the DuckDuckGo.com search engine."
optional = true
python-versions = ">=3.8"
files = [
{file = "duckduckgo_search-7.1.1-py3-none-any.whl", hash = "sha256:753b2237fd8a9a4cbf06c788aaf0cfc7e00f4623e8f854d0525682ff31770093"},
{file = "duckduckgo_search-7.1.1.tar.gz", hash = "sha256:1874e55aa6eac3de2d0e9b556b70f35ed0cd7a4544d7417cf38216275b3de009"},
{file = "duckduckgo_search-7.2.0-py3-none-any.whl", hash = "sha256:511abfa5e3e98592ae6a155d819b981adaac9e66c1ee2bb2eb0d7086e29316e2"},
{file = "duckduckgo_search-7.2.0.tar.gz", hash = "sha256:2163d4b78388ab70731094fdc86f4474d9fb9027c975e5aa2f0638e596a7e7b8"},
]
[package.dependencies]
click = ">=8.1.7"
lxml = ">=5.3.0"
primp = ">=0.9.2"
primp = ">=0.9.3"
[package.extras]
dev = ["mypy (>=1.13.0)", "pytest (>=8.3.4)", "pytest-dependency (>=0.6.0)", "ruff (>=0.8.3)"]
@@ -2086,13 +2087,13 @@ grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"]
[[package]]
name = "google-api-python-client"
version = "2.156.0"
version = "2.157.0"
description = "Google API Client Library for Python"
optional = false
python-versions = ">=3.7"
files = [
{file = "google_api_python_client-2.156.0-py2.py3-none-any.whl", hash = "sha256:6352185c505e1f311f11b0b96c1b636dcb0fec82cd04b80ac5a671ac4dcab339"},
{file = "google_api_python_client-2.156.0.tar.gz", hash = "sha256:b809c111ded61716a9c1c7936e6899053f13bae3defcdfda904bd2ca68065b9c"},
{file = "google_api_python_client-2.157.0-py2.py3-none-any.whl", hash = "sha256:0b0231db106324c659bf8b85f390391c00da57a60ebc4271e33def7aac198c75"},
{file = "google_api_python_client-2.157.0.tar.gz", hash = "sha256:2ee342d0967ad1cedec43ccd7699671d94bff151e1f06833ea81303f9a6d86fd"},
]
[package.dependencies]
@@ -2539,13 +2540,13 @@ colorama = ">=0.4"
[[package]]
name = "grpc-google-iam-v1"
version = "0.13.1"
version = "0.14.0"
description = "IAM API client library"
optional = true
python-versions = ">=3.7"
files = [
{file = "grpc-google-iam-v1-0.13.1.tar.gz", hash = "sha256:3ff4b2fd9d990965e410965253c0da6f66205d5a8291c4c31c6ebecca18a9001"},
{file = "grpc_google_iam_v1-0.13.1-py2.py3-none-any.whl", hash = "sha256:c3e86151a981811f30d5e7330f271cee53e73bb87755e88cc3b6f0c7b5fe374e"},
{file = "grpc_google_iam_v1-0.14.0-py2.py3-none-any.whl", hash = "sha256:fb4a084b30099ba3ab07d61d620a0d4429570b13ff53bd37bac75235f98b7da4"},
{file = "grpc_google_iam_v1-0.14.0.tar.gz", hash = "sha256:c66e07aa642e39bb37950f9e7f491f70dad150ac9801263b42b2814307c2df99"},
]
[package.dependencies]
@@ -2555,70 +2556,70 @@ protobuf = ">=3.20.2,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4
[[package]]
name = "grpcio"
version = "1.68.1"
version = "1.69.0"
description = "HTTP/2-based RPC framework"
optional = false
python-versions = ">=3.8"
files = [
{file = "grpcio-1.68.1-cp310-cp310-linux_armv7l.whl", hash = "sha256:d35740e3f45f60f3c37b1e6f2f4702c23867b9ce21c6410254c9c682237da68d"},
{file = "grpcio-1.68.1-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:d99abcd61760ebb34bdff37e5a3ba333c5cc09feda8c1ad42547bea0416ada78"},
{file = "grpcio-1.68.1-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:f8261fa2a5f679abeb2a0a93ad056d765cdca1c47745eda3f2d87f874ff4b8c9"},
{file = "grpcio-1.68.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0feb02205a27caca128627bd1df4ee7212db051019a9afa76f4bb6a1a80ca95e"},
{file = "grpcio-1.68.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:919d7f18f63bcad3a0f81146188e90274fde800a94e35d42ffe9eadf6a9a6330"},
{file = "grpcio-1.68.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:963cc8d7d79b12c56008aabd8b457f400952dbea8997dd185f155e2f228db079"},
{file = "grpcio-1.68.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ccf2ebd2de2d6661e2520dae293298a3803a98ebfc099275f113ce1f6c2a80f1"},
{file = "grpcio-1.68.1-cp310-cp310-win32.whl", hash = "sha256:2cc1fd04af8399971bcd4f43bd98c22d01029ea2e56e69c34daf2bf8470e47f5"},
{file = "grpcio-1.68.1-cp310-cp310-win_amd64.whl", hash = "sha256:ee2e743e51cb964b4975de572aa8fb95b633f496f9fcb5e257893df3be854746"},
{file = "grpcio-1.68.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:55857c71641064f01ff0541a1776bfe04a59db5558e82897d35a7793e525774c"},
{file = "grpcio-1.68.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:4b177f5547f1b995826ef529d2eef89cca2f830dd8b2c99ffd5fde4da734ba73"},
{file = "grpcio-1.68.1-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:3522c77d7e6606d6665ec8d50e867f13f946a4e00c7df46768f1c85089eae515"},
{file = "grpcio-1.68.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9d1fae6bbf0816415b81db1e82fb3bf56f7857273c84dcbe68cbe046e58e1ccd"},
{file = "grpcio-1.68.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:298ee7f80e26f9483f0b6f94cc0a046caf54400a11b644713bb5b3d8eb387600"},
{file = "grpcio-1.68.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:cbb5780e2e740b6b4f2d208e90453591036ff80c02cc605fea1af8e6fc6b1bbe"},
{file = "grpcio-1.68.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:ddda1aa22495d8acd9dfbafff2866438d12faec4d024ebc2e656784d96328ad0"},
{file = "grpcio-1.68.1-cp311-cp311-win32.whl", hash = "sha256:b33bd114fa5a83f03ec6b7b262ef9f5cac549d4126f1dc702078767b10c46ed9"},
{file = "grpcio-1.68.1-cp311-cp311-win_amd64.whl", hash = "sha256:7f20ebec257af55694d8f993e162ddf0d36bd82d4e57f74b31c67b3c6d63d8b2"},
{file = "grpcio-1.68.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:8829924fffb25386995a31998ccbbeaa7367223e647e0122043dfc485a87c666"},
{file = "grpcio-1.68.1-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:3aed6544e4d523cd6b3119b0916cef3d15ef2da51e088211e4d1eb91a6c7f4f1"},
{file = "grpcio-1.68.1-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:4efac5481c696d5cb124ff1c119a78bddbfdd13fc499e3bc0ca81e95fc573684"},
{file = "grpcio-1.68.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ab2d912ca39c51f46baf2a0d92aa265aa96b2443266fc50d234fa88bf877d8e"},
{file = "grpcio-1.68.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95c87ce2a97434dffe7327a4071839ab8e8bffd0054cc74cbe971fba98aedd60"},
{file = "grpcio-1.68.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:e4842e4872ae4ae0f5497bf60a0498fa778c192cc7a9e87877abd2814aca9475"},
{file = "grpcio-1.68.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:255b1635b0ed81e9f91da4fcc8d43b7ea5520090b9a9ad9340d147066d1d3613"},
{file = "grpcio-1.68.1-cp312-cp312-win32.whl", hash = "sha256:7dfc914cc31c906297b30463dde0b9be48e36939575eaf2a0a22a8096e69afe5"},
{file = "grpcio-1.68.1-cp312-cp312-win_amd64.whl", hash = "sha256:a0c8ddabef9c8f41617f213e527254c41e8b96ea9d387c632af878d05db9229c"},
{file = "grpcio-1.68.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:a47faedc9ea2e7a3b6569795c040aae5895a19dde0c728a48d3c5d7995fda385"},
{file = "grpcio-1.68.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:390eee4225a661c5cd133c09f5da1ee3c84498dc265fd292a6912b65c421c78c"},
{file = "grpcio-1.68.1-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:66a24f3d45c33550703f0abb8b656515b0ab777970fa275693a2f6dc8e35f1c1"},
{file = "grpcio-1.68.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c08079b4934b0bf0a8847f42c197b1d12cba6495a3d43febd7e99ecd1cdc8d54"},
{file = "grpcio-1.68.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8720c25cd9ac25dd04ee02b69256d0ce35bf8a0f29e20577427355272230965a"},
{file = "grpcio-1.68.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:04cfd68bf4f38f5bb959ee2361a7546916bd9a50f78617a346b3aeb2b42e2161"},
{file = "grpcio-1.68.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c28848761a6520c5c6071d2904a18d339a796ebe6b800adc8b3f474c5ce3c3ad"},
{file = "grpcio-1.68.1-cp313-cp313-win32.whl", hash = "sha256:77d65165fc35cff6e954e7fd4229e05ec76102d4406d4576528d3a3635fc6172"},
{file = "grpcio-1.68.1-cp313-cp313-win_amd64.whl", hash = "sha256:a8040f85dcb9830d8bbb033ae66d272614cec6faceee88d37a88a9bd1a7a704e"},
{file = "grpcio-1.68.1-cp38-cp38-linux_armv7l.whl", hash = "sha256:eeb38ff04ab6e5756a2aef6ad8d94e89bb4a51ef96e20f45c44ba190fa0bcaad"},
{file = "grpcio-1.68.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:8a3869a6661ec8f81d93f4597da50336718bde9eb13267a699ac7e0a1d6d0bea"},
{file = "grpcio-1.68.1-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:2c4cec6177bf325eb6faa6bd834d2ff6aa8bb3b29012cceb4937b86f8b74323c"},
{file = "grpcio-1.68.1-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:12941d533f3cd45d46f202e3667be8ebf6bcb3573629c7ec12c3e211d99cfccf"},
{file = "grpcio-1.68.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80af6f1e69c5e68a2be529990684abdd31ed6622e988bf18850075c81bb1ad6e"},
{file = "grpcio-1.68.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:e8dbe3e00771bfe3d04feed8210fc6617006d06d9a2679b74605b9fed3e8362c"},
{file = "grpcio-1.68.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:83bbf5807dc3ee94ce1de2dfe8a356e1d74101e4b9d7aa8c720cc4818a34aded"},
{file = "grpcio-1.68.1-cp38-cp38-win32.whl", hash = "sha256:8cb620037a2fd9eeee97b4531880e439ebfcd6d7d78f2e7dcc3726428ab5ef63"},
{file = "grpcio-1.68.1-cp38-cp38-win_amd64.whl", hash = "sha256:52fbf85aa71263380d330f4fce9f013c0798242e31ede05fcee7fbe40ccfc20d"},
{file = "grpcio-1.68.1-cp39-cp39-linux_armv7l.whl", hash = "sha256:cb400138e73969eb5e0535d1d06cae6a6f7a15f2cc74add320e2130b8179211a"},
{file = "grpcio-1.68.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a1b988b40f2fd9de5c820f3a701a43339d8dcf2cb2f1ca137e2c02671cc83ac1"},
{file = "grpcio-1.68.1-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:96f473cdacfdd506008a5d7579c9f6a7ff245a9ade92c3c0265eb76cc591914f"},
{file = "grpcio-1.68.1-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:37ea3be171f3cf3e7b7e412a98b77685eba9d4fd67421f4a34686a63a65d99f9"},
{file = "grpcio-1.68.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ceb56c4285754e33bb3c2fa777d055e96e6932351a3082ce3559be47f8024f0"},
{file = "grpcio-1.68.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:dffd29a2961f3263a16d73945b57cd44a8fd0b235740cb14056f0612329b345e"},
{file = "grpcio-1.68.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:025f790c056815b3bf53da850dd70ebb849fd755a4b1ac822cb65cd631e37d43"},
{file = "grpcio-1.68.1-cp39-cp39-win32.whl", hash = "sha256:1098f03dedc3b9810810568060dea4ac0822b4062f537b0f53aa015269be0a76"},
{file = "grpcio-1.68.1-cp39-cp39-win_amd64.whl", hash = "sha256:334ab917792904245a028f10e803fcd5b6f36a7b2173a820c0b5b076555825e1"},
{file = "grpcio-1.68.1.tar.gz", hash = "sha256:44a8502dd5de653ae6a73e2de50a401d84184f0331d0ac3daeb044e66d5c5054"},
{file = "grpcio-1.69.0-cp310-cp310-linux_armv7l.whl", hash = "sha256:2060ca95a8db295ae828d0fc1c7f38fb26ccd5edf9aa51a0f44251f5da332e97"},
{file = "grpcio-1.69.0-cp310-cp310-macosx_12_0_universal2.whl", hash = "sha256:2e52e107261fd8fa8fa457fe44bfadb904ae869d87c1280bf60f93ecd3e79278"},
{file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:316463c0832d5fcdb5e35ff2826d9aa3f26758d29cdfb59a368c1d6c39615a11"},
{file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:26c9a9c4ac917efab4704b18eed9082ed3b6ad19595f047e8173b5182fec0d5e"},
{file = "grpcio-1.69.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:90b3646ced2eae3a0599658eeccc5ba7f303bf51b82514c50715bdd2b109e5ec"},
{file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3b75aea7c6cb91b341c85e7c1d9db1e09e1dd630b0717f836be94971e015031e"},
{file = "grpcio-1.69.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:5cfd14175f9db33d4b74d63de87c64bb0ee29ce475ce3c00c01ad2a3dc2a9e51"},
{file = "grpcio-1.69.0-cp310-cp310-win32.whl", hash = "sha256:9031069d36cb949205293cf0e243abd5e64d6c93e01b078c37921493a41b72dc"},
{file = "grpcio-1.69.0-cp310-cp310-win_amd64.whl", hash = "sha256:cc89b6c29f3dccbe12d7a3b3f1b3999db4882ae076c1c1f6df231d55dbd767a5"},
{file = "grpcio-1.69.0-cp311-cp311-linux_armv7l.whl", hash = "sha256:8de1b192c29b8ce45ee26a700044717bcbbd21c697fa1124d440548964328561"},
{file = "grpcio-1.69.0-cp311-cp311-macosx_10_14_universal2.whl", hash = "sha256:7e76accf38808f5c5c752b0ab3fd919eb14ff8fafb8db520ad1cc12afff74de6"},
{file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:d5658c3c2660417d82db51e168b277e0ff036d0b0f859fa7576c0ffd2aec1442"},
{file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5494d0e52bf77a2f7eb17c6da662886ca0a731e56c1c85b93505bece8dc6cf4c"},
{file = "grpcio-1.69.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ed866f9edb574fd9be71bf64c954ce1b88fc93b2a4cbf94af221e9426eb14d6"},
{file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c5ba38aeac7a2fe353615c6b4213d1fbb3a3c34f86b4aaa8be08baaaee8cc56d"},
{file = "grpcio-1.69.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:f79e05f5bbf551c4057c227d1b041ace0e78462ac8128e2ad39ec58a382536d2"},
{file = "grpcio-1.69.0-cp311-cp311-win32.whl", hash = "sha256:bf1f8be0da3fcdb2c1e9f374f3c2d043d606d69f425cd685110dd6d0d2d61258"},
{file = "grpcio-1.69.0-cp311-cp311-win_amd64.whl", hash = "sha256:fb9302afc3a0e4ba0b225cd651ef8e478bf0070cf11a529175caecd5ea2474e7"},
{file = "grpcio-1.69.0-cp312-cp312-linux_armv7l.whl", hash = "sha256:fc18a4de8c33491ad6f70022af5c460b39611e39578a4d84de0fe92f12d5d47b"},
{file = "grpcio-1.69.0-cp312-cp312-macosx_10_14_universal2.whl", hash = "sha256:0f0270bd9ffbff6961fe1da487bdcd594407ad390cc7960e738725d4807b18c4"},
{file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:dc48f99cc05e0698e689b51a05933253c69a8c8559a47f605cff83801b03af0e"},
{file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e925954b18d41aeb5ae250262116d0970893b38232689c4240024e4333ac084"},
{file = "grpcio-1.69.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:87d222569273720366f68a99cb62e6194681eb763ee1d3b1005840678d4884f9"},
{file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:b62b0f41e6e01a3e5082000b612064c87c93a49b05f7602fe1b7aa9fd5171a1d"},
{file = "grpcio-1.69.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:db6f9fd2578dbe37db4b2994c94a1d9c93552ed77dca80e1657bb8a05b898b55"},
{file = "grpcio-1.69.0-cp312-cp312-win32.whl", hash = "sha256:b192b81076073ed46f4b4dd612b8897d9a1e39d4eabd822e5da7b38497ed77e1"},
{file = "grpcio-1.69.0-cp312-cp312-win_amd64.whl", hash = "sha256:1227ff7836f7b3a4ab04e5754f1d001fa52a730685d3dc894ed8bc262cc96c01"},
{file = "grpcio-1.69.0-cp313-cp313-linux_armv7l.whl", hash = "sha256:a78a06911d4081a24a1761d16215a08e9b6d4d29cdbb7e427e6c7e17b06bcc5d"},
{file = "grpcio-1.69.0-cp313-cp313-macosx_10_14_universal2.whl", hash = "sha256:dc5a351927d605b2721cbb46158e431dd49ce66ffbacb03e709dc07a491dde35"},
{file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_aarch64.whl", hash = "sha256:3629d8a8185f5139869a6a17865d03113a260e311e78fbe313f1a71603617589"},
{file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9a281878feeb9ae26db0622a19add03922a028d4db684658f16d546601a4870"},
{file = "grpcio-1.69.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cc614e895177ab7e4b70f154d1a7c97e152577ea101d76026d132b7aaba003b"},
{file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:1ee76cd7e2e49cf9264f6812d8c9ac1b85dda0eaea063af07292400f9191750e"},
{file = "grpcio-1.69.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:0470fa911c503af59ec8bc4c82b371ee4303ececbbdc055f55ce48e38b20fd67"},
{file = "grpcio-1.69.0-cp313-cp313-win32.whl", hash = "sha256:b650f34aceac8b2d08a4c8d7dc3e8a593f4d9e26d86751ebf74ebf5107d927de"},
{file = "grpcio-1.69.0-cp313-cp313-win_amd64.whl", hash = "sha256:028337786f11fecb5d7b7fa660475a06aabf7e5e52b5ac2df47414878c0ce7ea"},
{file = "grpcio-1.69.0-cp38-cp38-linux_armv7l.whl", hash = "sha256:b7f693db593d6bf285e015d5538bf1c86cf9c60ed30b6f7da04a00ed052fe2f3"},
{file = "grpcio-1.69.0-cp38-cp38-macosx_10_14_universal2.whl", hash = "sha256:8b94e83f66dbf6fd642415faca0608590bc5e8d30e2c012b31d7d1b91b1de2fd"},
{file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:b634851b92c090763dde61df0868c730376cdb73a91bcc821af56ae043b09596"},
{file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bf5f680d3ed08c15330d7830d06bc65f58ca40c9999309517fd62880d70cb06e"},
{file = "grpcio-1.69.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:200e48a6e7b00f804cf00a1c26292a5baa96507c7749e70a3ec10ca1a288936e"},
{file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:45a4704339b6e5b24b0e136dea9ad3815a94f30eb4f1e1d44c4ac484ef11d8dd"},
{file = "grpcio-1.69.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:85d347cb8237751b23539981dbd2d9d8f6e9ff90082b427b13022b948eb6347a"},
{file = "grpcio-1.69.0-cp38-cp38-win32.whl", hash = "sha256:60e5de105dc02832dc8f120056306d0ef80932bcf1c0e2b4ca3b676de6dc6505"},
{file = "grpcio-1.69.0-cp38-cp38-win_amd64.whl", hash = "sha256:282f47d0928e40f25d007f24eb8fa051cb22551e3c74b8248bc9f9bea9c35fe0"},
{file = "grpcio-1.69.0-cp39-cp39-linux_armv7l.whl", hash = "sha256:dd034d68a2905464c49479b0c209c773737a4245d616234c79c975c7c90eca03"},
{file = "grpcio-1.69.0-cp39-cp39-macosx_10_14_universal2.whl", hash = "sha256:01f834732c22a130bdf3dc154d1053bdbc887eb3ccb7f3e6285cfbfc33d9d5cc"},
{file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:a7f4ed0dcf202a70fe661329f8874bc3775c14bb3911d020d07c82c766ce0eb1"},
{file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cd7ea241b10bc5f0bb0f82c0d7896822b7ed122b3ab35c9851b440c1ccf81588"},
{file = "grpcio-1.69.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1f03dc9b4da4c0dc8a1db7a5420f575251d7319b7a839004d8916257ddbe4816"},
{file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:ca71d73a270dff052fe4edf74fef142d6ddd1f84175d9ac4a14b7280572ac519"},
{file = "grpcio-1.69.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5ccbed100dc43704e94ccff9e07680b540d64e4cc89213ab2832b51b4f68a520"},
{file = "grpcio-1.69.0-cp39-cp39-win32.whl", hash = "sha256:1514341def9c6ec4b7f0b9628be95f620f9d4b99331b7ef0a1845fd33d9b579c"},
{file = "grpcio-1.69.0-cp39-cp39-win_amd64.whl", hash = "sha256:c1fea55d26d647346acb0069b08dca70984101f2dc95066e003019207212e303"},
{file = "grpcio-1.69.0.tar.gz", hash = "sha256:936fa44241b5379c5afc344e1260d467bee495747eaf478de825bab2791da6f5"},
]
[package.extras]
protobuf = ["grpcio-tools (>=1.68.1)"]
protobuf = ["grpcio-tools (>=1.69.0)"]
[[package]]
name = "grpcio-status"
@@ -2805,13 +2806,13 @@ files = [
[[package]]
name = "huggingface-hub"
version = "0.27.0"
version = "0.27.1"
description = "Client library to download and publish models, datasets and other repos on the huggingface.co hub"
optional = false
python-versions = ">=3.8.0"
files = [
{file = "huggingface_hub-0.27.0-py3-none-any.whl", hash = "sha256:8f2e834517f1f1ddf1ecc716f91b120d7333011b7485f665a9a412eacb1a2a81"},
{file = "huggingface_hub-0.27.0.tar.gz", hash = "sha256:902cce1a1be5739f5589e560198a65a8edcfd3b830b1666f36e4b961f0454fac"},
{file = "huggingface_hub-0.27.1-py3-none-any.whl", hash = "sha256:1c5155ca7d60b60c2e2fc38cbb3ffb7f7c3adf48f824015b219af9061771daec"},
{file = "huggingface_hub-0.27.1.tar.gz", hash = "sha256:c004463ca870283909d715d20f066ebd6968c2207dae9393fdffb3c1d4d8f98b"},
]
[package.dependencies]
@@ -2853,13 +2854,13 @@ pyreadline3 = {version = "*", markers = "sys_platform == \"win32\" and python_ve
[[package]]
name = "identify"
version = "2.6.4"
version = "2.6.5"
description = "File identification library for Python"
optional = false
python-versions = ">=3.9"
files = [
{file = "identify-2.6.4-py2.py3-none-any.whl", hash = "sha256:993b0f01b97e0568c179bb9196391ff391bfb88a99099dbf5ce392b68f42d0af"},
{file = "identify-2.6.4.tar.gz", hash = "sha256:285a7d27e397652e8cafe537a6cc97dd470a970f48fb2e9d979aa38eae5513ac"},
{file = "identify-2.6.5-py2.py3-none-any.whl", hash = "sha256:14181a47091eb75b337af4c23078c9d09225cd4c48929f521f3bf16b09d02566"},
{file = "identify-2.6.5.tar.gz", hash = "sha256:c10b33f250e5bba374fae86fb57f3adcebf1161bce7cdf92031915fd480c13bc"},
]
[package.extras]
@@ -2900,13 +2901,13 @@ test = ["flufl.flake8", "importlib-resources (>=1.3)", "jaraco.test (>=5.4)", "p
[[package]]
name = "importlib-resources"
version = "6.4.5"
version = "6.5.2"
description = "Read resources from Python packages"
optional = true
python-versions = ">=3.8"
python-versions = ">=3.9"
files = [
{file = "importlib_resources-6.4.5-py3-none-any.whl", hash = "sha256:ac29d5f956f01d5e4bb63102a5a19957f1b9175e45649977264a1416783bb717"},
{file = "importlib_resources-6.4.5.tar.gz", hash = "sha256:980862a1d16c9e147a59603677fa2aa5fd82b87f223b6cb870695bcfce830065"},
{file = "importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec"},
{file = "importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c"},
]
[package.extras]
@@ -3975,13 +3976,13 @@ files = [
[[package]]
name = "marshmallow"
version = "3.23.2"
version = "3.24.0"
description = "A lightweight library for converting complex datatypes to and from native Python datatypes."
optional = false
python-versions = ">=3.9"
files = [
{file = "marshmallow-3.23.2-py3-none-any.whl", hash = "sha256:bcaf2d6fd74fb1459f8450e85d994997ad3e70036452cbfa4ab685acb19479b3"},
{file = "marshmallow-3.23.2.tar.gz", hash = "sha256:c448ac6455ca4d794773f00bae22c2f351d62d739929f761dce5eacb5c468d7f"},
{file = "marshmallow-3.24.0-py3-none-any.whl", hash = "sha256:459922b7a1fd3d29d5082ddcadfcea0efd98985030e71d3ef0dd8f44f406e41d"},
{file = "marshmallow-3.24.0.tar.gz", hash = "sha256:378572f727e52123d00de1bdd9b7ea7bed18bbfedc7f9bfbcddaf78925a8d602"},
]
[package.dependencies]
@@ -3989,7 +3990,7 @@ packaging = ">=17.0"
[package.extras]
dev = ["marshmallow[tests]", "pre-commit (>=3.5,<5.0)", "tox"]
docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.14)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)", "sphinx-version-warning (==1.1.2)"]
docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.14)", "sphinx (==8.1.3)", "sphinx-issues (==5.0.0)"]
tests = ["pytest", "simplejson"]
[[package]]
@@ -4548,18 +4549,18 @@ test = ["flaky", "ipykernel (>=6.19.3)", "ipython", "ipywidgets", "nbconvert (>=
[[package]]
name = "nbconvert"
version = "7.16.4"
version = "7.16.5"
description = "Converting Jupyter Notebooks (.ipynb files) to other formats. Output formats include asciidoc, html, latex, markdown, pdf, py, rst, script. nbconvert can be used both as a Python library (`import nbconvert`) or as a command line tool (invoked as `jupyter nbconvert ...`)."
optional = false
python-versions = ">=3.8"
files = [
{file = "nbconvert-7.16.4-py3-none-any.whl", hash = "sha256:05873c620fe520b6322bf8a5ad562692343fe3452abda5765c7a34b7d1aa3eb3"},
{file = "nbconvert-7.16.4.tar.gz", hash = "sha256:86ca91ba266b0a448dc96fa6c5b9d98affabde2867b363258703536807f9f7f4"},
{file = "nbconvert-7.16.5-py3-none-any.whl", hash = "sha256:e12eac052d6fd03040af4166c563d76e7aeead2e9aadf5356db552a1784bd547"},
{file = "nbconvert-7.16.5.tar.gz", hash = "sha256:c83467bb5777fdfaac5ebbb8e864f300b277f68692ecc04d6dab72f2d8442344"},
]
[package.dependencies]
beautifulsoup4 = "*"
bleach = "!=5.0.0"
bleach = {version = "!=5.0.0", extras = ["css"]}
defusedxml = "*"
jinja2 = ">=3.0"
jupyter-core = ">=4.7"
@@ -4571,7 +4572,6 @@ nbformat = ">=5.7"
packaging = "*"
pandocfilters = ">=1.4.1"
pygments = ">=2.4.1"
tinycss2 = "*"
traitlets = ">=5.1"
[package.extras]
@@ -4727,13 +4727,13 @@ sympy = "*"
[[package]]
name = "openai"
version = "1.58.1"
version = "1.59.3"
description = "The official Python library for the openai API"
optional = false
python-versions = ">=3.8"
files = [
{file = "openai-1.58.1-py3-none-any.whl", hash = "sha256:e2910b1170a6b7f88ef491ac3a42c387f08bd3db533411f7ee391d166571d63c"},
{file = "openai-1.58.1.tar.gz", hash = "sha256:f5a035fd01e141fc743f4b0e02c41ca49be8fab0866d3b67f5f29b4f4d3c0973"},
{file = "openai-1.59.3-py3-none-any.whl", hash = "sha256:b041887a0d8f3e70d1fc6ffbb2bf7661c3b9a2f3e806c04bf42f572b9ac7bc37"},
{file = "openai-1.59.3.tar.gz", hash = "sha256:7f7fff9d8729968588edf1524e73266e8593bb6cab09298340efb755755bb66f"},
]
[package.dependencies]
@@ -5438,13 +5438,13 @@ testing = ["pytest", "pytest-benchmark"]
[[package]]
name = "posthog"
version = "3.7.4"
version = "3.7.5"
description = "Integrate PostHog into any python application."
optional = true
python-versions = "*"
files = [
{file = "posthog-3.7.4-py2.py3-none-any.whl", hash = "sha256:21c18c6bf43b2de303ea4cd6e95804cc0f24c20cb2a96a8fd09da2ed50b62faa"},
{file = "posthog-3.7.4.tar.gz", hash = "sha256:19384bd09d330f9787a7e2446aba14c8057ece56144970ea2791072d4e40cd36"},
{file = "posthog-3.7.5-py2.py3-none-any.whl", hash = "sha256:022132c17069dde03c5c5904e2ae1b9bd68d5059cbc5a8dffc5c1537a1b71cb5"},
{file = "posthog-3.7.5.tar.gz", hash = "sha256:8ba40ab623da35db72715fc87fe7dccb7fc272ced92581fe31db2d4dbe7ad761"},
]
[package.dependencies]
@@ -5488,13 +5488,13 @@ selenium = ">=4.18.1,<5.0.0"
[[package]]
name = "praisonaiagents"
version = "0.0.14"
version = "0.0.23"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
optional = false
python-versions = "*"
files = [
{file = "praisonaiagents-0.0.14-py3-none-any.whl", hash = "sha256:6d239d91911ca289aa7be0badebc7c18e187bdae7dc9248072a8c570fe0fbeef"},
{file = "praisonaiagents-0.0.14.tar.gz", hash = "sha256:1acb94dc5e2fa677f922e0d3d23df0748f02982ebb52d3309c5f2295e49b39cc"},
{file = "praisonaiagents-0.0.23-py3-none-any.whl", hash = "sha256:d6aaa406fc993ccd0a763aa426b7139c955e1cd99e6b204b06817ebc1a5855e3"},
{file = "praisonaiagents-0.0.23.tar.gz", hash = "sha256:1814d537b2625a5c7d4b61d07f5d5bd043f0ad8afc31fe9d4eb99a5ca54101bf"},
]
[package.dependencies]
@@ -5522,20 +5522,20 @@ virtualenv = ">=20.10.0"
[[package]]
name = "primp"
version = "0.9.2"
version = "0.9.3"
description = "HTTP client that can impersonate web browsers, mimicking their headers and `TLS/JA3/JA4/HTTP2` fingerprints"
optional = true
python-versions = ">=3.8"
files = [
{file = "primp-0.9.2-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:a3179640e633be843ed5daba5c4e3086ad91f77c7bb40a9db06326f28d56b12b"},
{file = "primp-0.9.2-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:94a5da8ba25f74152b43bc16a7591dfb5d7d30a5827dc0a0f96a956f7d3616be"},
{file = "primp-0.9.2-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0668c0abb6d56fc8b0a918179b1d0f68e7267c1dc632e2b683c618317e13143f"},
{file = "primp-0.9.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:a9c29a4b8eabfc28a1746d2fe93d33b9fcf2e81e642dd0e3eaecede60cc36b7d"},
{file = "primp-0.9.2-cp38-abi3-manylinux_2_34_armv7l.whl", hash = "sha256:04d499308a101b06b40f5fda1bdc795db5731cd0dfbb1a8873f4acd07c085b1d"},
{file = "primp-0.9.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4cd5daf39034a0a8c96cdc0c4c306184c6f2b1b2a0b39dc3294d79ed28a6f7fe"},
{file = "primp-0.9.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8d14653434837eb431b3cf7ca006647d7a196906e48bba96bb600ba2ba70bcdc"},
{file = "primp-0.9.2-cp38-abi3-win_amd64.whl", hash = "sha256:80d9f07564dc9b25b1a9676df770561418557c124fedecae84f6491a1974b61d"},
{file = "primp-0.9.2.tar.gz", hash = "sha256:5b95666c25b9107eab3c05a89cb7b1748d5122e57c57b25bfc3249d525c45300"},
{file = "primp-0.9.3-cp38-abi3-macosx_10_12_x86_64.whl", hash = "sha256:30d5c4f03abeab8fd307ddf4c615d8d093367f3be1a5299078f811982db7cb6f"},
{file = "primp-0.9.3-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:3e3bff0bcffdf746b68486db24ac6dc6db2f202f048f82e0303385ef76dc5c1e"},
{file = "primp-0.9.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15325ec0b916e48a65929f647d7dd12fb1400e76a6aeba72f7e205c94c4aaa82"},
{file = "primp-0.9.3-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:de97cc10130a68edcd4e173cf01c628a10b9b6a6e19393200b12f1a1ddcb863d"},
{file = "primp-0.9.3-cp38-abi3-manylinux_2_34_armv7l.whl", hash = "sha256:cba94e48c9b245a9daac96807aa020525c1a66c1141d4c2b11db4e2ccb69da33"},
{file = "primp-0.9.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1d1df5a9808b82b960df1f72ff53a9ab9c77dce61fcbe5776a11cf5f59dd7f28"},
{file = "primp-0.9.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:484fc69dbed5ca14561d54aecd17f011c8ffa495023134bb9a94edb808ae3e1b"},
{file = "primp-0.9.3-cp38-abi3-win_amd64.whl", hash = "sha256:824a5606ad67cb842ccc5158f19f9ccd1387e25e1374edd69f87cd7616cf3ea9"},
{file = "primp-0.9.3.tar.gz", hash = "sha256:bc6490c659c741c6586bd160ed51b2e04f9dbc3b61bc1dd1af1ce66eefea663e"},
]
[package.extras]
@@ -6161,13 +6161,13 @@ requests = ">=2.14.0"
[[package]]
name = "pygments"
version = "2.18.0"
version = "2.19.1"
description = "Pygments is a syntax highlighting package written in Python."
optional = false
python-versions = ">=3.8"
files = [
{file = "pygments-2.18.0-py3-none-any.whl", hash = "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"},
{file = "pygments-2.18.0.tar.gz", hash = "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199"},
{file = "pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c"},
{file = "pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f"},
]
[package.extras]
@@ -7168,29 +7168,29 @@ pyasn1 = ">=0.1.3"
[[package]]
name = "ruff"
version = "0.8.4"
version = "0.8.6"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
{file = "ruff-0.8.4-py3-none-linux_armv6l.whl", hash = "sha256:58072f0c06080276804c6a4e21a9045a706584a958e644353603d36ca1eb8a60"},
{file = "ruff-0.8.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ffb60904651c00a1e0b8df594591770018a0f04587f7deeb3838344fe3adabac"},
{file = "ruff-0.8.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:6ddf5d654ac0d44389f6bf05cee4caeefc3132a64b58ea46738111d687352296"},
{file = "ruff-0.8.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e248b1f0fa2749edd3350a2a342b67b43a2627434c059a063418e3d375cfe643"},
{file = "ruff-0.8.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf197b98ed86e417412ee3b6c893f44c8864f816451441483253d5ff22c0e81e"},
{file = "ruff-0.8.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c41319b85faa3aadd4d30cb1cffdd9ac6b89704ff79f7664b853785b48eccdf3"},
{file = "ruff-0.8.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:9f8402b7c4f96463f135e936d9ab77b65711fcd5d72e5d67597b543bbb43cf3f"},
{file = "ruff-0.8.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e4e56b3baa9c23d324ead112a4fdf20db9a3f8f29eeabff1355114dd96014604"},
{file = "ruff-0.8.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:736272574e97157f7edbbb43b1d046125fce9e7d8d583d5d65d0c9bf2c15addf"},
{file = "ruff-0.8.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5fe710ab6061592521f902fca7ebcb9fabd27bc7c57c764298b1c1f15fff720"},
{file = "ruff-0.8.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:13e9ec6d6b55f6da412d59953d65d66e760d583dd3c1c72bf1f26435b5bfdbae"},
{file = "ruff-0.8.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:97d9aefef725348ad77d6db98b726cfdb075a40b936c7984088804dfd38268a7"},
{file = "ruff-0.8.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:ab78e33325a6f5374e04c2ab924a3367d69a0da36f8c9cb6b894a62017506111"},
{file = "ruff-0.8.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8ef06f66f4a05c3ddbc9121a8b0cecccd92c5bf3dd43b5472ffe40b8ca10f0f8"},
{file = "ruff-0.8.4-py3-none-win32.whl", hash = "sha256:552fb6d861320958ca5e15f28b20a3d071aa83b93caee33a87b471f99a6c0835"},
{file = "ruff-0.8.4-py3-none-win_amd64.whl", hash = "sha256:f21a1143776f8656d7f364bd264a9d60f01b7f52243fbe90e7670c0dfe0cf65d"},
{file = "ruff-0.8.4-py3-none-win_arm64.whl", hash = "sha256:9183dd615d8df50defa8b1d9a074053891ba39025cf5ae88e8bcb52edcc4bf08"},
{file = "ruff-0.8.4.tar.gz", hash = "sha256:0d5f89f254836799af1615798caa5f80b7f935d7a670fad66c5007928e57ace8"},
{file = "ruff-0.8.6-py3-none-linux_armv6l.whl", hash = "sha256:defed167955d42c68b407e8f2e6f56ba52520e790aba4ca707a9c88619e580e3"},
{file = "ruff-0.8.6-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:54799ca3d67ae5e0b7a7ac234baa657a9c1784b48ec954a094da7c206e0365b1"},
{file = "ruff-0.8.6-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e88b8f6d901477c41559ba540beeb5a671e14cd29ebd5683903572f4b40a9807"},
{file = "ruff-0.8.6-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0509e8da430228236a18a677fcdb0c1f102dd26d5520f71f79b094963322ed25"},
{file = "ruff-0.8.6-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:91a7ddb221779871cf226100e677b5ea38c2d54e9e2c8ed847450ebbdf99b32d"},
{file = "ruff-0.8.6-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:248b1fb3f739d01d528cc50b35ee9c4812aa58cc5935998e776bf8ed5b251e75"},
{file = "ruff-0.8.6-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:bc3c083c50390cf69e7e1b5a5a7303898966be973664ec0c4a4acea82c1d4315"},
{file = "ruff-0.8.6-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52d587092ab8df308635762386f45f4638badb0866355b2b86760f6d3c076188"},
{file = "ruff-0.8.6-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:61323159cf21bc3897674e5adb27cd9e7700bab6b84de40d7be28c3d46dc67cf"},
{file = "ruff-0.8.6-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ae4478b1471fc0c44ed52a6fb787e641a2ac58b1c1f91763bafbc2faddc5117"},
{file = "ruff-0.8.6-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0c000a471d519b3e6cfc9c6680025d923b4ca140ce3e4612d1a2ef58e11f11fe"},
{file = "ruff-0.8.6-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:9257aa841e9e8d9b727423086f0fa9a86b6b420fbf4bf9e1465d1250ce8e4d8d"},
{file = "ruff-0.8.6-py3-none-musllinux_1_2_i686.whl", hash = "sha256:45a56f61b24682f6f6709636949ae8cc82ae229d8d773b4c76c09ec83964a95a"},
{file = "ruff-0.8.6-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:496dd38a53aa173481a7d8866bcd6451bd934d06976a2505028a50583e001b76"},
{file = "ruff-0.8.6-py3-none-win32.whl", hash = "sha256:e169ea1b9eae61c99b257dc83b9ee6c76f89042752cb2d83486a7d6e48e8f764"},
{file = "ruff-0.8.6-py3-none-win_amd64.whl", hash = "sha256:f1d70bef3d16fdc897ee290d7d20da3cbe4e26349f62e8a0274e7a3f4ce7a905"},
{file = "ruff-0.8.6-py3-none-win_arm64.whl", hash = "sha256:7d7fc2377a04b6e04ffe588caad613d0c460eb2ecba4c0ccbbfe2bc973cbc162"},
{file = "ruff-0.8.6.tar.gz", hash = "sha256:dcad24b81b62650b0eb8814f576fc65cfee8674772a6e24c9b747911801eeaa5"},
]
[[package]]
@@ -7263,53 +7263,60 @@ tests = ["black (>=24.3.0)", "matplotlib (>=3.3.4)", "mypy (>=1.9)", "numpydoc (
[[package]]
name = "scipy"
version = "1.14.1"
version = "1.15.0"
description = "Fundamental algorithms for scientific computing in Python"
optional = true
python-versions = ">=3.10"
files = [
{file = "scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389"},
{file = "scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3"},
{file = "scipy-1.14.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0"},
{file = "scipy-1.14.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3"},
{file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d"},
{file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69"},
{file = "scipy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad"},
{file = "scipy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5"},
{file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"},
{file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"},
{file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"},
{file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"},
{file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"},
{file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"},
{file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"},
{file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"},
{file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"},
{file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"},
{file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"},
{file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"},
{file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"},
{file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"},
{file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"},
{file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"},
{file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"},
{file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"},
{file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"},
{file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"},
{file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"},
{file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"},
{file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"},
{file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"},
{file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"},
{file = "scipy-1.15.0-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:aeac60d3562a7bf2f35549bdfdb6b1751c50590f55ce7322b4b2fc821dc27fca"},
{file = "scipy-1.15.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:5abbdc6ede5c5fed7910cf406a948e2c0869231c0db091593a6b2fa78be77e5d"},
{file = "scipy-1.15.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:eb1533c59f0ec6c55871206f15a5c72d1fae7ad3c0a8ca33ca88f7c309bbbf8c"},
{file = "scipy-1.15.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:de112c2dae53107cfeaf65101419662ac0a54e9a088c17958b51c95dac5de56d"},
{file = "scipy-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2240e1fd0782e62e1aacdc7234212ee271d810f67e9cd3b8d521003a82603ef8"},
{file = "scipy-1.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d35aef233b098e4de88b1eac29f0df378278e7e250a915766786b773309137c4"},
{file = "scipy-1.15.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:1b29e4fc02e155a5fd1165f1e6a73edfdd110470736b0f48bcbe48083f0eee37"},
{file = "scipy-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:0e5b34f8894f9904cc578008d1a9467829c1817e9f9cb45e6d6eeb61d2ab7731"},
{file = "scipy-1.15.0-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:46e91b5b16909ff79224b56e19cbad65ca500b3afda69225820aa3afbf9ec020"},
{file = "scipy-1.15.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:82bff2eb01ccf7cea8b6ee5274c2dbeadfdac97919da308ee6d8e5bcbe846443"},
{file = "scipy-1.15.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:9c8254fe21dd2c6c8f7757035ec0c31daecf3bb3cffd93bc1ca661b731d28136"},
{file = "scipy-1.15.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:c9624eeae79b18cab1a31944b5ef87aa14b125d6ab69b71db22f0dbd962caf1e"},
{file = "scipy-1.15.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d13bbc0658c11f3d19df4138336e4bce2c4fbd78c2755be4bf7b8e235481557f"},
{file = "scipy-1.15.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdca4c7bb8dc41307e5f39e9e5d19c707d8e20a29845e7533b3bb20a9d4ccba0"},
{file = "scipy-1.15.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6f376d7c767731477bac25a85d0118efdc94a572c6b60decb1ee48bf2391a73b"},
{file = "scipy-1.15.0-cp311-cp311-win_amd64.whl", hash = "sha256:61513b989ee8d5218fbeb178b2d51534ecaddba050db949ae99eeb3d12f6825d"},
{file = "scipy-1.15.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:5beb0a2200372b7416ec73fdae94fe81a6e85e44eb49c35a11ac356d2b8eccc6"},
{file = "scipy-1.15.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fde0f3104dfa1dfbc1f230f65506532d0558d43188789eaf68f97e106249a913"},
{file = "scipy-1.15.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:35c68f7044b4e7ad73a3e68e513dda946989e523df9b062bd3cf401a1a882192"},
{file = "scipy-1.15.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:52475011be29dfcbecc3dfe3060e471ac5155d72e9233e8d5616b84e2b542054"},
{file = "scipy-1.15.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5972e3f96f7dda4fd3bb85906a17338e65eaddfe47f750e240f22b331c08858e"},
{file = "scipy-1.15.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fe00169cf875bed0b3c40e4da45b57037dc21d7c7bf0c85ed75f210c281488f1"},
{file = "scipy-1.15.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:161f80a98047c219c257bf5ce1777c574bde36b9d962a46b20d0d7e531f86863"},
{file = "scipy-1.15.0-cp312-cp312-win_amd64.whl", hash = "sha256:327163ad73e54541a675240708244644294cb0a65cca420c9c79baeb9648e479"},
{file = "scipy-1.15.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0fcb16eb04d84670722ce8d93b05257df471704c913cb0ff9dc5a1c31d1e9422"},
{file = "scipy-1.15.0-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:767e8cf6562931f8312f4faa7ddea412cb783d8df49e62c44d00d89f41f9bbe8"},
{file = "scipy-1.15.0-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:37ce9394cdcd7c5f437583fc6ef91bd290014993900643fdfc7af9b052d1613b"},
{file = "scipy-1.15.0-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:6d26f17c64abd6c6c2dfb39920f61518cc9e213d034b45b2380e32ba78fde4c0"},
{file = "scipy-1.15.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e2448acd79c6374583581a1ded32ac71a00c2b9c62dfa87a40e1dd2520be111"},
{file = "scipy-1.15.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:36be480e512d38db67f377add5b759fb117edd987f4791cdf58e59b26962bee4"},
{file = "scipy-1.15.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ccb6248a9987193fe74363a2d73b93bc2c546e0728bd786050b7aef6e17db03c"},
{file = "scipy-1.15.0-cp313-cp313-win_amd64.whl", hash = "sha256:952d2e9eaa787f0a9e95b6e85da3654791b57a156c3e6609e65cc5176ccfe6f2"},
{file = "scipy-1.15.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:b1432102254b6dc7766d081fa92df87832ac25ff0b3d3a940f37276e63eb74ff"},
{file = "scipy-1.15.0-cp313-cp313t-macosx_12_0_arm64.whl", hash = "sha256:4e08c6a36f46abaedf765dd2dfcd3698fa4bd7e311a9abb2d80e33d9b2d72c34"},
{file = "scipy-1.15.0-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:ec915cd26d76f6fc7ae8522f74f5b2accf39546f341c771bb2297f3871934a52"},
{file = "scipy-1.15.0-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:351899dd2a801edd3691622172bc8ea01064b1cada794f8641b89a7dc5418db6"},
{file = "scipy-1.15.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e9baff912ea4f78a543d183ed6f5b3bea9784509b948227daaf6f10727a0e2e5"},
{file = "scipy-1.15.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cd9d9198a7fd9a77f0eb5105ea9734df26f41faeb2a88a0e62e5245506f7b6df"},
{file = "scipy-1.15.0-cp313-cp313t-win_amd64.whl", hash = "sha256:129f899ed275c0515d553b8d31696924e2ca87d1972421e46c376b9eb87de3d2"},
{file = "scipy-1.15.0.tar.gz", hash = "sha256:300742e2cc94e36a2880ebe464a1c8b4352a7b0f3e36ec3d2ac006cdbe0219ac"},
]
[package.dependencies]
numpy = ">=1.23.5,<2.3"
numpy = ">=1.23.5,<2.5"
[package.extras]
dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"]
doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<=7.3.7)", "sphinx-design (>=0.4.0)"]
test = ["Cython", "array-api-strict (>=2.0)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
doc = ["intersphinx_registry", "jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.16.5)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<8.0.0)", "sphinx-copybutton", "sphinx-design (>=0.4.0)"]
test = ["Cython", "array-api-strict (>=2.0,<2.1.1)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"]
[[package]]
name = "selenium"
@@ -7358,23 +7365,23 @@ files = [
[[package]]
name = "setuptools"
version = "75.6.0"
version = "75.7.0"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
optional = false
python-versions = ">=3.9"
files = [
{file = "setuptools-75.6.0-py3-none-any.whl", hash = "sha256:ce74b49e8f7110f9bf04883b730f4765b774ef3ef28f722cce7c273d253aaf7d"},
{file = "setuptools-75.6.0.tar.gz", hash = "sha256:8199222558df7c86216af4f84c30e9b34a61d8ba19366cc914424cdbd28252f6"},
{file = "setuptools-75.7.0-py3-none-any.whl", hash = "sha256:84fb203f278ebcf5cd08f97d3fb96d3fbed4b629d500b29ad60d11e00769b183"},
{file = "setuptools-75.7.0.tar.gz", hash = "sha256:886ff7b16cd342f1d1defc16fc98c9ce3fde69e087a4e1983d7ab634e5f41f4f"},
]
[package.extras]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.7.0)"]
check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)", "ruff (>=0.8.0)"]
core = ["importlib_metadata (>=6)", "jaraco.collections", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1)", "wheel (>=0.43.0)"]
cover = ["pytest-cov"]
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"]
enabler = ["pytest-enabler (>=2.2)"]
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (>=1.12,<1.14)", "pytest-mypy"]
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"]
type = ["importlib_metadata (>=7.0.2)", "jaraco.develop (>=7.21)", "mypy (==1.14.*)", "pytest-mypy"]
[[package]]
name = "shapely"
@@ -8271,13 +8278,13 @@ test = ["aiohttp (>=3.10.5)", "flake8 (>=5.0,<6.0)", "mypy (>=0.800)", "psutil",
[[package]]
name = "virtualenv"
version = "20.28.0"
version = "20.28.1"
description = "Virtual Python Environment builder"
optional = false
python-versions = ">=3.8"
files = [
{file = "virtualenv-20.28.0-py3-none-any.whl", hash = "sha256:23eae1b4516ecd610481eda647f3a7c09aea295055337331bb4e6892ecce47b0"},
{file = "virtualenv-20.28.0.tar.gz", hash = "sha256:2c9c3262bb8e7b87ea801d715fae4495e6032450c71d2309be9550e7364049aa"},
{file = "virtualenv-20.28.1-py3-none-any.whl", hash = "sha256:412773c85d4dab0409b83ec36f7a6499e72eaf08c80e81e9576bca61831c71cb"},
{file = "virtualenv-20.28.1.tar.gz", hash = "sha256:5d34ab240fdb5d21549b76f9e8ff3af28252f5499fb6d6f031adac4e5a8c5329"},
]
[package.dependencies]
@@ -8822,9 +8829,9 @@ gradio = ["gradio"]
openai = ["langchain-openai"]
realtime = ["aiosqlite", "chainlit", "crawl4ai", "duckduckgo_search", "greenlet", "litellm", "plotly", "sqlalchemy", "tavily-python", "websockets", "yfinance"]
train = []
ui = ["chainlit"]
ui = ["aiosqlite", "chainlit", "greenlet", "pydantic", "sqlalchemy"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.10,<3.13"
content-hash = "eb9727e6cb210753564b5c109a9e353f65d5f4c79889e0a3c4e8ce5080ee23e4"
content-hash = "596dee1ab307caacfd8041e13a29b60d0b793274e7ea45890b827a92e8c2dd40"
+1 -1
View File
@@ -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.24.tar.gz"
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/2.0.32.tar.gz"
sha256 "1828fb9227d10f991522c3f24f061943a254b667196b40b1a3e4a54a8d30ce32" # Replace with actual SHA256 checksum
license "MIT"
+1 -1
View File
@@ -56,7 +56,7 @@ class CloudDeployer:
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.24 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.0.32 gunicorn markdown\n")
file.write("EXPOSE 8080\n")
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
+628 -488
View File
File diff suppressed because it is too large. Load diff
+57
View File
@@ -0,0 +1,57 @@
import logging
from typing import Dict, Any, Callable, Optional, Union
import asyncio
logger = logging.getLogger(__name__)
class CallbackManager:
"""Manages callbacks for the PraisonAI UI"""
def __init__(self):
self._callbacks: Dict[str, Dict[str, Union[Callable, bool]]] = {}
def register(self, name: str, callback: Callable, is_async: bool = False) -> None:
"""Register a callback function"""
self._callbacks[name] = {
'func': callback,
'is_async': is_async
}
async def call(self, name: str, **kwargs) -> None:
"""Call a registered callback"""
if name not in self._callbacks:
logger.warning(f"No callback registered for {name}")
return
callback_info = self._callbacks[name]
func = callback_info['func']
is_async = callback_info['is_async']
try:
if is_async:
await func(**kwargs)
else:
if asyncio.iscoroutinefunction(func):
await func(**kwargs)
else:
await asyncio.get_event_loop().run_in_executor(None, lambda: func(**kwargs))
except Exception as e:
logger.error(f"Error in callback {name}: {str(e)}")
# Global callback manager instance
callback_manager = CallbackManager()
def register_callback(name: str, callback: Callable, is_async: bool = False) -> None:
"""Register a callback with the global callback manager"""
callback_manager.register(name, callback, is_async)
async def trigger_callback(name: str, **kwargs) -> None:
"""Trigger a callback from the global callback manager"""
await callback_manager.call(name, **kwargs)
# Decorator for registering callbacks
def callback(name: str, is_async: bool = False):
def decorator(func):
register_callback(name, func, is_async)
return func
return decorator
+474
View File
@@ -0,0 +1,474 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
import os
import importlib
import inspect
import yaml
import logging
from .callbacks import trigger_callback
import asyncio
import chainlit as cl
from queue import Queue
logger = logging.getLogger(__name__)
agent_file = "agents.yaml"
with open(agent_file, 'r') as f:
config = yaml.safe_load(f)
topic = "get from the message content from the chainlit user message"
# Create a message queue
message_queue = Queue()
async def process_message_queue():
"""Process messages in the queue and send them to Chainlit"""
while True:
try:
if not message_queue.empty():
msg_data = message_queue.get()
await cl.Message(**msg_data).send()
await asyncio.sleep(0.1) # Small delay to prevent busy waiting
except Exception as e:
logger.error(f"Error processing message queue: {e}")
def load_tools_from_tools_py():
"""
Imports and returns all contents from tools.py file.
Also adds the tools to the global namespace.
Returns:
list: A list of callable functions with proper formatting
"""
tools_list = []
try:
# Try to import tools.py from current directory
spec = importlib.util.spec_from_file_location("tools", "tools.py")
logger.info(f"Spec: {spec}")
if spec is None:
logger.info("tools.py not found in current directory")
return tools_list
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
# Get all module attributes except private ones and classes
for name, obj in inspect.getmembers(module):
if (not name.startswith('_') and
callable(obj) and
not inspect.isclass(obj)):
# Add the function to global namespace
globals()[name] = obj
# Format the tool as an OpenAI function
tool = {
"type": "function",
"function": {
"name": name,
"description": obj.__doc__ or f"Function to {name.replace('_', ' ')}",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The search query to look up information about"
}
},
"required": ["query"]
}
}
}
# Add formatted tool to tools list
tools_list.append(tool)
logger.info(f"Loaded and globalized tool function: {name}")
logger.info(f"Loaded {len(tools_list)} tool functions from tools.py")
logger.info(f"Tools list: {tools_list}")
except Exception as e:
logger.warning(f"Error loading tools from tools.py: {e}")
return tools_list
async def step_callback(step_details):
"""Callback for agent steps"""
logger.info(f"[CALLBACK DEBUG] Step callback triggered with details: {step_details}")
try:
# Queue message for agent response
if step_details.get("response"):
message_queue.put({
"content": f"Agent Response: {step_details.get('response')}",
"author": step_details.get("agent_name", "Agent")
})
logger.info("[CALLBACK DEBUG] Queued agent response message")
# Queue message for tool usage
if step_details.get("tool_name"):
message_queue.put({
"content": f"🛠️ Using tool: {step_details.get('tool_name')}",
"author": "System"
})
logger.info("[CALLBACK DEBUG] Queued tool usage message")
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error in step callback: {str(e)}", exc_info=True)
async def task_callback(task_output):
"""Callback for task completion"""
logger.info(f"[CALLBACK DEBUG] Task callback triggered with output: {task_output}")
try:
# Create message content
if hasattr(task_output, 'raw'):
content = task_output.raw
elif hasattr(task_output, 'content'):
content = task_output.content
else:
content = str(task_output)
# Queue the message
message_queue.put({
"content": f"Task Output: {content}",
"author": "Task"
})
logger.info("[CALLBACK DEBUG] Queued task completion message")
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error in task callback: {str(e)}", exc_info=True)
async def step_callback_wrapper(step_details):
logger.info(f"[CALLBACK DEBUG] Step callback wrapper triggered with details: {step_details}")
try:
# Check if we have a Chainlit context
if not cl.context.context_var.get():
logger.warning("[CALLBACK DEBUG] No Chainlit context available in wrapper")
return
logger.info("[CALLBACK DEBUG] Chainlit context found in wrapper")
# Create a message for the agent's response
if step_details.get("response"):
logger.info(f"[CALLBACK DEBUG] Sending agent response from wrapper: {step_details.get('response')}")
try:
await cl.Message(
content=f"{role_name}: {step_details.get('response')}",
author=role_name,
).send()
logger.info("[CALLBACK DEBUG] Successfully sent agent response message from wrapper")
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error sending agent response message from wrapper: {str(e)}")
# Create a message for any tool usage
if step_details.get("tool_name"):
logger.info(f"[CALLBACK DEBUG] Sending tool usage from wrapper: {step_details.get('tool_name')}")
try:
await cl.Message(
content=f"🛠️ {role_name} is using tool: {step_details.get('tool_name')}",
author="System",
).send()
logger.info("[CALLBACK DEBUG] Successfully sent tool usage message from wrapper")
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error sending tool usage message from wrapper: {str(e)}")
# Create a message for any thoughts or reasoning
if step_details.get("thought"):
logger.info(f"[CALLBACK DEBUG] Sending thought from wrapper: {step_details.get('thought')}")
try:
await cl.Message(
content=f"💭 {role_name}'s thought: {step_details.get('thought')}",
author=role_name,
).send()
logger.info("[CALLBACK DEBUG] Successfully sent thought message from wrapper")
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error sending thought message from wrapper: {str(e)}")
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error in step callback wrapper: {str(e)}", exc_info=True)
try:
await cl.Message(
content=f"Error in step callback: {str(e)}",
author="System",
).send()
except Exception as send_error:
logger.error(f"[CALLBACK DEBUG] Error sending error message: {str(send_error)}")
async def task_callback_wrapper(task_output):
logger.info(f"[CALLBACK DEBUG] Task callback wrapper triggered with output type: {type(task_output)}")
try:
# Check if we have a Chainlit context
if not cl.context.context_var.get():
logger.warning("[CALLBACK DEBUG] No Chainlit context available in task wrapper")
return
logger.info("[CALLBACK DEBUG] Chainlit context found in task wrapper")
# Create a message for task completion
if hasattr(task_output, 'raw'):
content = task_output.raw
logger.info("[CALLBACK DEBUG] Using raw output")
elif hasattr(task_output, 'content'):
content = task_output.content
logger.info("[CALLBACK DEBUG] Using content output")
else:
content = str(task_output)
logger.info("[CALLBACK DEBUG] Using string representation of output")
logger.info(f"[CALLBACK DEBUG] Sending task completion message from wrapper: {content[:100]}...")
try:
await cl.Message(
content=f"✅ {role_name} completed task:\n{content}",
author=role_name,
).send()
logger.info("[CALLBACK DEBUG] Successfully sent task completion message from wrapper")
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error sending task completion message from wrapper: {str(e)}")
# If there are any additional task details
if hasattr(task_output, 'details'):
logger.info("[CALLBACK DEBUG] Task has additional details")
try:
await cl.Message(
content=f"📝 Additional details:\n{task_output.details}",
author=role_name,
).send()
logger.info("[CALLBACK DEBUG] Successfully sent additional details message")
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error sending additional details message: {str(e)}")
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error in task callback wrapper: {str(e)}", exc_info=True)
try:
await cl.Message(
content=f"Error in task callback: {str(e)}",
author="System",
).send()
except Exception as send_error:
logger.error(f"[CALLBACK DEBUG] Error sending error message: {str(send_error)}")
def sync_task_callback_wrapper(task_output):
logger.info("[CALLBACK DEBUG] Sync task callback wrapper triggered")
try:
# Create a new event loop for this thread if there isn't one
try:
loop = asyncio.get_event_loop()
logger.info("[CALLBACK DEBUG] Got existing event loop")
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
logger.info("[CALLBACK DEBUG] Created new event loop")
if loop.is_running():
# If loop is running, schedule the callback
logger.info("[CALLBACK DEBUG] Loop is running, scheduling callback")
asyncio.run_coroutine_threadsafe(
task_callback_wrapper(task_output),
loop
)
else:
# If loop is not running, run it directly
logger.info("[CALLBACK DEBUG] Loop is not running, running callback directly")
loop.run_until_complete(task_callback_wrapper(task_output))
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error in sync task callback: {str(e)}", exc_info=True)
def sync_step_callback_wrapper(step_details):
logger.info("[CALLBACK DEBUG] Sync step callback wrapper triggered")
try:
# Create a new event loop for this thread if there isn't one
try:
loop = asyncio.get_event_loop()
logger.info("[CALLBACK DEBUG] Got existing event loop")
except RuntimeError:
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
logger.info("[CALLBACK DEBUG] Created new event loop")
if loop.is_running():
# If loop is running, schedule the callback
logger.info("[CALLBACK DEBUG] Loop is running, scheduling callback")
asyncio.run_coroutine_threadsafe(
step_callback_wrapper(step_details),
loop
)
else:
# If loop is not running, run it directly
logger.info("[CALLBACK DEBUG] Loop is not running, running callback directly")
loop.run_until_complete(step_callback_wrapper(step_details))
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error in sync step callback: {str(e)}", exc_info=True)
async def ui_run_praisonai(config, topic, tools_dict):
"""Run PraisonAI with the given configuration and topic."""
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
agents = {}
tasks = []
tasks_dict = {}
try:
# Start message queue processor
queue_processor = asyncio.create_task(process_message_queue())
# Create agents for each role
for role, details in config['roles'].items():
# Format the role name and other details
role_name = details.get('name', role).format(topic=topic)
role_filled = details.get('role', role).format(topic=topic)
goal_filled = details['goal'].format(topic=topic)
backstory_filled = details['backstory'].format(topic=topic)
# Test message to verify Chainlit is working
await cl.Message(
content=f"[DEBUG] Creating agent: {role_name}",
author="System"
).send()
# Create a sync wrapper for the step callback
def step_callback_sync(step_details):
try:
# Create a new event loop for this thread
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# Add agent name to step details
step_details["agent_name"] = role_name
# Run the callback
loop.run_until_complete(step_callback(step_details))
loop.close()
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error in step callback: {str(e)}", exc_info=True)
agent = Agent(
name=role_name,
role=role_filled,
goal=goal_filled,
backstory=backstory_filled,
llm=details.get('llm', 'gpt-4o'),
verbose=True,
allow_delegation=details.get('allow_delegation', False),
max_iter=details.get('max_iter', 15),
max_rpm=details.get('max_rpm'),
max_execution_time=details.get('max_execution_time'),
cache=details.get('cache', True),
step_callback=step_callback_sync
)
agents[role] = agent
# Create tasks for each role
for role, details in config['roles'].items():
agent = agents[role]
tools_list = []
# Get tools for this role
for tool_name in details.get('tools', []):
if tool_name in tools_dict:
tool_func = tools_dict[tool_name]
tools_list.append(tool_func)
# Create tasks for the agent
for task_name, task_details in details.get('tasks', {}).items():
description_filled = task_details['description'].format(topic=topic)
expected_output_filled = task_details['expected_output'].format(topic=topic)
# Test message to verify task creation
await cl.Message(
content=f"[DEBUG] Created task: {task_name} for agent {role_name}",
author="System"
).send()
# Create a sync wrapper for the task callback
def task_callback_sync(task_output):
try:
# Create a new event loop for this thread
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
# Run the callback
loop.run_until_complete(task_callback(task_output))
loop.close()
except Exception as e:
logger.error(f"[CALLBACK DEBUG] Error in task callback: {str(e)}", exc_info=True)
task = Task(
description=description_filled,
expected_output=expected_output_filled,
agent=agent,
tools=tools_list,
async_execution=True,
context=[],
config=task_details.get('config', {}),
output_json=task_details.get('output_json'),
output_pydantic=task_details.get('output_pydantic'),
output_file=task_details.get('output_file', ""),
callback=task_callback_sync,
create_directory=task_details.get('create_directory', False)
)
tasks.append(task)
tasks_dict[task_name] = task
# Set up task contexts
for role, details in config['roles'].items():
for task_name, task_details in details.get('tasks', {}).items():
task = tasks_dict[task_name]
context_tasks = [tasks_dict[ctx] for ctx in task_details.get('context', [])
if ctx in tasks_dict]
task.context = context_tasks
# Send the start message
await cl.Message(
content="Starting PraisonAI agents execution...",
author="System"
).send()
# Create and run the PraisonAI agents
if config.get('process') == 'hierarchical':
crew = PraisonAIAgents(
agents=list(agents.values()),
tasks=tasks,
verbose=True,
process="hierarchical",
manager_llm=config.get('manager_llm', 'gpt-4o')
)
else:
crew = PraisonAIAgents(
agents=list(agents.values()),
tasks=tasks,
verbose=2
)
# Store the crew in the user session
cl.user_session.set("crew", crew)
# Run the agents in a separate thread
loop = asyncio.get_event_loop()
response = await loop.run_in_executor(None, crew.start)
logger.debug(f"[CALLBACK DEBUG] Result: {response}")
# Convert response to string if it's not already
if hasattr(response, 'raw'):
result = response.raw
elif hasattr(response, 'content'):
result = response.content
else:
result = str(response)
# Send the completion message
await cl.Message(
content="PraisonAI agents execution completed.",
author="System"
).send()
# After getting the response, wait a bit for remaining messages
await asyncio.sleep(1) # Give time for final messages to be processed
queue_processor.cancel() # Stop the queue processor
return result
except Exception as e:
error_msg = f"Error in ui_run_praisonai: {str(e)}"
logger.error(error_msg, exc_info=True)
await cl.Message(
content=error_msg,
author="System"
).send()
raise
+81
View File
@@ -0,0 +1,81 @@
import os
import logging
from dotenv import load_dotenv
import chainlit as cl
from chainlit.types import ThreadDict
import yaml
import sys
import os
from datetime import datetime
# Add the parent directory to sys.path to allow imports
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
from praisonai.ui.colab import ui_run_praisonai, load_tools_from_tools_py
from praisonai.ui.callbacks import callback, trigger_callback
# Load environment variables
load_dotenv()
# Set up logging
logger = logging.getLogger(__name__)
log_level = os.getenv("LOGLEVEL", "INFO").upper()
logger.setLevel(log_level)
# Load agent configuration
agent_file = "agents.yaml"
with open(agent_file, 'r') as f:
config = yaml.safe_load(f)
# Load tools
tools_dict = load_tools_from_tools_py()
@cl.on_message
async def main(message: cl.Message):
"""Main message handler for Chainlit"""
try:
logger.info(f"Processing message: {message.content}")
await cl.Message(
content=f"🔄 Processing your request about: {message.content}...",
author="System"
).send()
await cl.Message(
content="Using Running PraisonAI Agents...",
author="System"
).send()
# Run PraisonAI with the message content as the topic
result = await ui_run_praisonai(config, message.content, tools_dict)
# Send the final result
await cl.Message(
content=result,
author="System"
).send()
except Exception as e:
error_msg = f"Error running PraisonAI agents: {str(e)}"
logger.error(error_msg, exc_info=True)
await cl.Message(
content=error_msg,
author="System"
).send()
@cl.on_chat_start
async def start():
"""Handler for chat start"""
await cl.Message(
content="👋 Welcome! I'm your AI assistant. What would you like to work on?",
author="System"
).send()
# Authentication setup (optional)
if os.getenv("CHAINLIT_AUTH_SECRET"):
@cl.password_auth_callback
def auth_callback(username: str, password: str) -> cl.User:
# Replace with your authentication logic
if username == os.getenv("CHAINLIT_USERNAME", "admin") and \
password == os.getenv("CHAINLIT_PASSWORD", "admin"):
return cl.User(identifier=username, metadata={"role": "user"})
return None
+1 -1
View File
@@ -1 +1 @@
# Welcome to Praison AI! 🚀🤖
# Welcome to PraisonAI! 🚀
+133
View File
@@ -0,0 +1,133 @@
# Understanding Tool Integration in AI Agents - A Beginner's Guide
## Overview
This guide explains how to properly integrate tools (functions) that an AI agent can use, making them both understandable to the OpenAI API and executable by your code.
## Key Components
### 1. Tool Definition Structure
```python
# Example tool definition in tools.py
def search_tool(query: str) -> list:
"""
Perform a web search using DuckDuckGo.
Args:
query (str): The search query string.
Returns:
list: Search results with title, url, and snippet.
"""
# Function implementation...
```
### 2. Tool Dictionary Format
```python
tools_dict = {
'search_tool': {
'type': 'function',
'function': {
'name': 'search_tool',
'description': '...',
'parameters': {
'type': 'object',
'properties': {
'query': {'type': 'string'}
}
}
},
'callable': search_tool # The actual Python function
}
}
```
## The Two-Part System
### Part 1: OpenAI API Communication
```python
# task_tools: What OpenAI understands
task_tools = []
tool_def = tools_dict[tool_name].copy()
callable_func = tool_def.pop("callable") # Remove the Python function
task_tools.append(tool_def) # Add clean JSON-serializable definition
```
### Part 2: Function Execution
```python
# role_tools: What your code executes
role_tools = []
role_tools.append(callable_func) # Store the actual function
agent.tools = role_tools # Give agent access to executable functions
```
## Putting It All Together
```python
# Initialize empty lists
role_tools = [] # For executable functions
task_tools = [] # For OpenAI API definitions
# Process each tool
for tool_name in tools_list:
if tool_name in tools_dict:
# 1. Get the tool definition
tool_def = tools_dict[tool_name].copy()
# 2. Separate the callable function
callable_func = tool_def.pop("callable")
# 3. Store the function for execution
role_tools.append(callable_func)
# 4. Store the API definition
task_tools.append(tool_def)
# 5. Give agent access to functions
agent.tools = role_tools
# Create task with API definitions
task = Task(
description="...",
tools=task_tools, # OpenAI API will use these
agent=agent, # Agent has access to callable functions
# ... other parameters ...
)
```
## Why This Works
1. **API Communication**
- OpenAI API receives clean JSON tool definitions
- No Python functions that would cause serialization errors
2. **Function Execution**
- Agent has access to actual Python functions
- Can execute tools when OpenAI decides to use them
3. **Separation of Concerns**
- `task_tools`: Describes what tools can do (for OpenAI)
- `role_tools`: Actually does the work (for Python)
## Common Errors and Solutions
1. **"Invalid type for 'tools[0]'"**
- Cause: Sending null or invalid tool definition to OpenAI
- Solution: Use proper tool definition format in `task_tools`
2. **"Object of type function is not JSON serializable"**
- Cause: Trying to send Python function to OpenAI API
- Solution: Remove callable function from API definition
3. **"Tool is not callable"**
- Cause: Agent doesn't have access to executable functions
- Solution: Set `agent.tools = role_tools`
## Best Practices
1. Always initialize both `task_tools` and `role_tools` lists
2. Make clean copies of tool definitions to avoid modifying originals
3. Keep tool definitions JSON-serializable for API communication
4. Ensure agents have access to callable functions
5. Document tool parameters and return values clearly
This structure maintains clean separation between API communication and actual function execution, making your AI agent system both reliable and maintainable.
+4 -4
View File
@@ -1,6 +1,6 @@
[project]
name = "PraisonAI"
version = "2.0.24"
version = "2.0.32"
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.18",
"praisonaiagents>=0.0.23",
"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.24"
version = "2.0.32"
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.18"
praisonaiagents = ">=0.0.23"
python-dotenv = ">=0.19.0"
instructor = ">=1.3.3"
PyYAML = ">=6.0"
Generated
+5 -5
View File
@@ -3061,7 +3061,7 @@ wheels = [
[[package]]
name = "praisonai"
version = "2.0.24"
version = "2.0.32"
source = { editable = "." }
dependencies = [
{ name = "instructor" },
@@ -3198,7 +3198,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.18" },
{ name = "praisonaiagents", specifier = ">=0.0.23" },
{ 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" },
@@ -3251,16 +3251,16 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.18"
version = "0.0.23"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "openai" },
{ name = "pydantic" },
{ name = "rich" },
]
sdist = { url = "https://files.pythonhosted.org/packages/09/e4/9bf056c4a9ecb5cf47766b9442b3b608881f530c4c28e233cbc6adb1f2d1/praisonaiagents-0.0.18.tar.gz", hash = "sha256:56744f82f806999fca328f9f9f8ae8b5c40cf312cf4fb6c0c34c30661f206d23", size = 23337 }
sdist = { url = "https://files.pythonhosted.org/packages/a8/87/08ff2ac7c365251fa07973fded3e68c2edc1173d8514e6fd90fc8ee7b131/praisonaiagents-0.0.23.tar.gz", hash = "sha256:1814d537b2625a5c7d4b61d07f5d5bd043f0ad8afc31fe9d4eb99a5ca54101bf", size = 25375 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b8/abfccfc0e82832721987b9c58d703f163a5573c5615ae5b5f3609f8877a0/praisonaiagents-0.0.18-py3-none-any.whl", hash = "sha256:fa9374f065ade18535ce05e27d2bb1ca326810ed6bbec5421597526f31add792", size = 29334 },
{ url = "https://files.pythonhosted.org/packages/56/75/c3a1cf2adaf0811ab538029054fa993e82caffba2853546f41766b7e0aab/praisonaiagents-0.0.23-py3-none-any.whl", hash = "sha256:d6aaa406fc993ccd0a763aa426b7139c955e1cd99e6b204b06817ebc1a5855e3", size = 31734 },
]
[[package]]