Compare commits

...
18 Commits
Author SHA1 Message Date
Mervin Praison faa014b8b0 Merge pull request #333 from MervinPraison/develop
2.0.59
2025-01-24 17:17:53 +00:00
MervinPraison 6d454d0c6e Update PraisonAI to version 2.0.59 and enhance dependencies
- Bumped PraisonAI version from 2.0.58 to 2.0.59 in `Dockerfile`, `pyproject.toml`, and `uv.lock` to reflect the latest updates.
- Updated the `praisonaiagents` dependency version from `0.0.53` to `0.0.56` across relevant files for improved functionality.
- Modified agent task descriptions in `repetitive-agents.py` to clarify expected outputs.
- Enhanced the `process.py` file to improve task handling and expected output management.

These changes aim to refine the PraisonAI framework's capabilities and ensure compatibility with the latest dependencies.
2025-01-24 17:17:32 +00:00
MervinPraison 0ec3fe3510 Add new agent scripts and update version to 0.0.55
- Introduced three new Python scripts: `deepseek-reasoning-output-tokens.py`, `llm-deepseek-reasoning-steps.py`, and `llm-langchain-toolcall.py` to enhance reasoning capabilities and multi-source information retrieval.
- Updated `pyproject.toml` and `uv.lock` to reflect the new version 0.0.55.
- Enhanced `wikipedia-agent.py` to utilize the new `gpt-4o-mini` model for improved performance.
- Improved `agent.py` to support tool calls in chat completions, refining the overall functionality of the agent framework.

These changes aim to expand the capabilities of the PraisonAI framework by integrating new reasoning features and improving user experience with updated versioning.
2025-01-24 13:04:34 +00:00
MervinPraison 707888cdf9 Remove deprecated agent scripts and update version to 0.0.54
- Deleted `any-llm-agents.py` and `any-llm-agents-advanced.py` scripts to streamline the agent framework.
- Updated `pyproject.toml` and `uv.lock` to reflect the new version 0.0.54.
- Enhanced `agent.py` and `llm.py` to improve handling of reasoning steps by ensuring `reasoning_steps` is properly managed in API calls.

These changes aim to simplify the agent framework and improve the overall functionality of the PraisonAI system.
2025-01-24 10:35:42 +00:00
Mervin Praison 42051630bc Merge pull request #331 from MervinPraison/develop
Develop
2025-01-23 17:19:08 +00:00
MervinPraison bfcccb6355 Update PraisonAI to version 2.0.58 and enhance reasoning capabilities
- Bumped PraisonAI version from 2.0.57 to 2.0.58 in `Dockerfile`, `pyproject.toml`, and `uv.lock` to reflect the latest updates.
- Updated the `praisonaiagents` dependency version from `0.0.50` to `0.0.53` across relevant files for improved functionality.
- Enhanced reasoning features by modifying agent initialization parameters to use `reasoning_steps` instead of `show_reasoning`, improving clarity in agent behavior.
- Introduced a new script `deepseek-reasoning-multi-agents.py` for multi-agent reasoning tasks, showcasing the integration of reasoning agents.
- Added documentation for new reasoning features, including a detailed guide on creating reasoning extract agents.

These changes aim to refine the reasoning capabilities and improve the overall user experience within the PraisonAI framework.
2025-01-23 17:18:54 +00:00
MervinPraison e329afc394 Refactor reasoning capabilities and update version to 0.0.52
- Removed the `any-llm-think.py` script to streamline the agent framework.
- Enhanced `deepseek-reasoning-agents.py` to include reasoning display options and updated the method for starting agents.
- Updated `pyproject.toml` and `uv.lock` to reflect the new version 0.0.52.
- Improved `agent.py` and `llm.py` to support reasoning content in both synchronous and asynchronous calls, enhancing the overall functionality of the agent framework.

These changes aim to refine the reasoning features and improve the user experience within the PraisonAI framework.
2025-01-23 13:52:58 +00:00
MervinPraison 9417cc6457 Add new reasoning agents and update version to 0.0.51
- Introduced three new Python scripts: `any-llm-think.py`, `deepseek-reasoning-agents.py`, and `deepseek-think.py` for enhanced reasoning capabilities using the `deepseek-reasoner` model.
- Updated `pyproject.toml` and `uv.lock` to reflect the new version 0.0.51.
- Enhanced the `agent.py` file with new data classes and methods to support reasoning content in chat completions, improving the overall functionality of the agent framework.

These changes aim to expand the capabilities of the PraisonAI framework by integrating reasoning features and improving the user experience with updated versioning.
2025-01-23 10:52:15 +00:00
Mervin Praison 10a3d279ae Merge pull request #329 from MervinPraison/develop
Update index.mdx description to enhance clarity and detail
2025-01-23 06:40:49 +00:00
MervinPraison 2a916208e1 Update index.mdx description to enhance clarity and detail
- Revised the description of PraisonAI to emphasize its capabilities as a production-ready Multi-AI Agents framework with self-reflection.
- Highlighted the integration of PraisonAI Agents, AutoGen, and CrewAI as a low-code solution for automating tasks and solving complex challenges.
- Focused on the framework's strengths in simplicity, customization, and effective human-agent collaboration.

These changes aim to provide users with a clearer understanding of PraisonAI's functionalities and benefits.
2025-01-23 06:40:23 +00:00
Mervin Praison bcd0bbbd24 Merge pull request #328 from MervinPraison/develop
Refactor documentation for agent setup and usage
2025-01-23 06:38:06 +00:00
MervinPraison 5701a40f11 Refactor documentation for agent setup and usage
- Updated the introduction.mdx file to streamline the steps for installing the PraisonAI package and configuring the environment.
- Revised agent creation examples to simplify the process for both single and multiple agents, enhancing clarity and usability.
- Added a new section for creating an `agents.yaml` configuration file for no-code users, including automatic generation instructions.
- Improved the overall structure and readability of the documentation to facilitate a better user experience.

These changes aim to provide clearer guidance for users in setting up and utilizing the PraisonAI framework effectively.
2025-01-23 06:37:49 +00:00
Mervin Praison 3ffac395bb Merge pull request #327 from MervinPraison/develop
Update README and documentation with new video tutorials
2025-01-23 06:34:56 +00:00
MervinPraison 37119aa3ac Update README and documentation with new video tutorials
- Removed the License section from README.md to streamline content.
- Added a comprehensive list of video tutorials to docs/index.mdx, covering various AI agent functionalities such as self-reflection, reasoning, and multimodal capabilities.
- Enhanced the documentation's educational resources, providing users with visual aids to better understand the Praison AI framework.

These changes aim to improve user engagement and learning through accessible video content.
2025-01-23 06:34:36 +00:00
Mervin Praison 8b2d324d3b Merge pull request #326 from MervinPraison/develop
Enhance README with new AI agent video resources
2025-01-23 06:30:01 +00:00
MervinPraison 6d0b29e60d Enhance README with new AI agent video resources
- Added multiple new video links to the README.md, showcasing various AI agent functionalities including self-reflection, reasoning, multimodal capabilities, and more.
- Improved the documentation's educational value by providing visual resources for users to better understand the Praison AI framework.

These changes aim to enrich the user experience by offering accessible learning materials related to AI agents.
2025-01-23 06:29:44 +00:00
Mervin Praison 583cbc5efa Merge pull request #325 from MervinPraison/develop
Update documentation and examples for agent initialization and usage
2025-01-23 06:21:43 +00:00
MervinPraison 90dd84a152 Update documentation and examples for agent initialization and usage
- Revised the README.md to reflect the updated method for starting agents, changing `agent.chat` to `agent.start` for clarity and accuracy.
- Added a new section in the README.md for creating an `agents.yaml` file, enhancing the setup instructions for users.
- Updated the installation instructions in `docs/index.mdx` to clarify package names and improve the overall structure of the documentation.
- Streamlined code examples in `docs/concepts/agents.mdx` to focus on single and multiple agent initialization, ensuring users have clear and concise guidance.

These changes improve the clarity and usability of the documentation, making it easier for users to understand and implement the PraisonAI framework.
2025-01-23 06:21:25 +00:00
32 changed files with 1541 additions and 594 deletions

No files matched your search

+11 -5
View File
@@ -71,7 +71,7 @@ Create app.py file and add the code below:
```python
from praisonaiagents import Agent
agent = Agent(instructions="Your are a helpful AI assistant")
agent.chat("Write a movie script about a robot in Mars")
agent.start("Write a movie script about a robot in Mars")
```
Run:
@@ -472,6 +472,8 @@ export OPENAI_BASE_URL=https://api.groq.com/openai/v1
### Simple Playbook Example
Create `agents.yaml` file and add the code below:
```yaml
framework: praisonai
topic: Artificial Intelligence
@@ -544,14 +546,18 @@ uv pip install -r pyproject.toml --extra "crewai,autogen"
[![Star History Chart](https://api.star-history.com/svg?repos=MervinPraison/PraisonAI&type=Date)](https://docs.praison.ai)
## License
Praison AI is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
## Video Tutorials
| Topic | Video |
|-------|--------|
| AI Agents with Self Reflection | [![Self Reflection](https://img.youtube.com/vi/vLXobEN2Vc8/0.jpg)](https://www.youtube.com/watch?v=vLXobEN2Vc8) |
| AI Agents with Reasoning | [![Reasoning](https://img.youtube.com/vi/KNDVWGN3TpM/0.jpg)](https://www.youtube.com/watch?v=KNDVWGN3TpM) |
| Multimodal AI Agents | [![Multimodal](https://img.youtube.com/vi/hjAWmUT1qqY/0.jpg)](https://www.youtube.com/watch?v=hjAWmUT1qqY) |
| AI Agents Workflow | [![Workflow](https://img.youtube.com/vi/yWTH44QPl2A/0.jpg)](https://www.youtube.com/watch?v=yWTH44QPl2A) |
| Async AI Agents | [![Async](https://img.youtube.com/vi/VhVQfgo00LE/0.jpg)](https://www.youtube.com/watch?v=VhVQfgo00LE) |
| Mini AI Agents | [![Mini](https://img.youtube.com/vi/OkvYp5aAGSg/0.jpg)](https://www.youtube.com/watch?v=OkvYp5aAGSg) |
| AI Agents with Memory | [![Memory](https://img.youtube.com/vi/1hVfVxvPnnQ/0.jpg)](https://www.youtube.com/watch?v=1hVfVxvPnnQ) |
| Repetitive Agents | [![Repetitive](https://img.youtube.com/vi/dAYGxsjDOPg/0.jpg)](https://www.youtube.com/watch?v=dAYGxsjDOPg) |
| Introduction | [![Introduction](https://img.youtube.com/vi/Fn1lQjC0GO0/0.jpg)](https://www.youtube.com/watch?v=Fn1lQjC0GO0) |
| Tools Overview | [![Tools Overview](https://img.youtube.com/vi/XaQRgRpV7jo/0.jpg)](https://www.youtube.com/watch?v=XaQRgRpV7jo) |
| Custom Tools | [![Custom Tools](https://img.youtube.com/vi/JSU2Rndh06c/0.jpg)](https://www.youtube.com/watch?v=JSU2Rndh06c) |
+6
View File
@@ -0,0 +1,6 @@
from praisonaiagents import Agent
agent = Agent(instructions="You are helpful Assisant", llm="deepseek-reasoner", reasoning_steps=True)
result = agent.start("Why sky is Blue?")
print(result)
+14
View File
@@ -0,0 +1,14 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
reasoning_agent = Agent(role="Helpful Assistant", reasoning_steps=True, llm="deepseek/deepseek-reasoner")
small_agent = Agent(role="Helpful Assistant", llm="gpt-3.5-turbo")
reasoning_task = Task(description="How many r's in the word 'Strawberry'?", agent=reasoning_agent)
small_task = Task(description="With the provided reasoning tell me how many r's in the word 'Strawberry'?", agent=small_agent)
agents = PraisonAIAgents(
agents=[reasoning_agent, small_agent],
tasks=[reasoning_task, small_task]
)
agents.start()
@@ -0,0 +1,19 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
llm_config = {
"model": "deepseek/deepseek-reasoner",
"max_tokens": 1
}
reasoning_agent = Agent(role="Helpful Assistant", llm=llm_config, reasoning_steps=True)
small_agent = Agent(role="Helpful Assistant", llm="openai/gpt-3.5-turbo")
reasoning_task = Task(description="How many r's in the word 'Strawberry'?", agent=reasoning_agent)
small_task = Task(description="With the provided reasoning tell me how many r's in the word 'Strawberry'?", agent=small_agent)
agents = PraisonAIAgents(
agents=[reasoning_agent, small_agent],
tasks=[reasoning_task, small_task]
)
agents.start()
+18
View File
@@ -0,0 +1,18 @@
from openai import OpenAI
client = OpenAI()
response = client.chat.completions.create(
model="deepseek-reasoner",
messages=[
{"role": "system", "content": "You are a helpful assistant"},
{"role": "user", "content": "Why sky is Blue?"},
],
stream=True
)
for chunk in response:
if chunk.choices[0].delta.content is not None:
print(chunk.choices[0].delta.content, end="", flush=True)
if chunk.choices[0].delta.reasoning_content is not None:
print(chunk.choices[0].delta.reasoning_content, end="", flush=True)
+14
View File
@@ -0,0 +1,14 @@
from litellm import completion
messages = [{"role": "user", "content": "What is 1+1?"}]
resp = completion(
model="deepseek/deepseek-reasoner",
messages=messages,
stream=False
)
reasoning_content = resp.choices[0].message.provider_specific_fields["reasoning_content"]
content = resp.choices[0].message.content
print(reasoning_content)
print(content)
+9
View File
@@ -0,0 +1,9 @@
from praisonaiagents import Agent
agent = Agent(
instructions="You are helpful Assisant",
llm="deepseek/deepseek-reasoner"
)
result = agent.start("Why sky is Blue?")
print(result)
+10
View File
@@ -0,0 +1,10 @@
from praisonaiagents import Agent
agent = Agent(
instructions="You are helpful Assisant",
llm="deepseek/deepseek-reasoner",
reasoning_steps=True
)
result = agent.start("Why sky is Blue?")
print(result)
+14
View File
@@ -0,0 +1,14 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
reasoning_agent = Agent(role="Helpful Assistant", reasoning_steps=True, llm="deepseek/deepseek-reasoner")
small_agent = Agent(role="Helpful Assistant", llm="openai/gpt-3.5-turbo")
reasoning_task = Task(description="How many r's in the word 'Strawberry'?", agent=reasoning_agent)
small_task = Task(description="With the provided reasoning tell me how many r's in the word 'Strawberry'?", agent=small_agent)
agents = PraisonAIAgents(
agents=[reasoning_agent, small_agent],
tasks=[reasoning_task, small_task]
)
agents.start()
File renamed without changes.
File renamed without changes.
+30
View File
@@ -0,0 +1,30 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
from langchain_community.tools import YouTubeSearchTool
from langchain_community.utilities import WikipediaAPIWrapper
# Create an agent with both tools
agent = Agent(
name="SearchAgent",
role="Research Assistant",
goal="Search for information from multiple sources",
backstory="I am an AI assistant that can search YouTube and Wikipedia.",
tools=[YouTubeSearchTool, WikipediaAPIWrapper],
llm="openai/gpt-4o-mini"
)
# Create tasks to demonstrate both tools
task = Task(
name="search_task",
description="Search for information about 'AI advancements' on both YouTube and Wikipedia",
expected_output="Combined information from YouTube videos and Wikipedia articles",
agent=agent
)
# Create and start the workflow
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
verbose=True
)
agents.start()
+344 -101
View File
@@ -21,10 +21,180 @@ from ..main import (
)
import inspect
import uuid
from dataclasses import dataclass
if TYPE_CHECKING:
from ..task.task import Task
@dataclass
class ChatCompletionMessage:
content: str
role: str = "assistant"
refusal: Optional[str] = None
audio: Optional[str] = None
function_call: Optional[dict] = None
tool_calls: Optional[List] = None
reasoning_content: Optional[str] = None
@dataclass
class Choice:
finish_reason: Optional[str]
index: int
message: ChatCompletionMessage
logprobs: Optional[dict] = None
@dataclass
class CompletionTokensDetails:
accepted_prediction_tokens: Optional[int] = None
audio_tokens: Optional[int] = None
reasoning_tokens: Optional[int] = None
rejected_prediction_tokens: Optional[int] = None
@dataclass
class PromptTokensDetails:
audio_tokens: Optional[int] = None
cached_tokens: int = 0
@dataclass
class CompletionUsage:
completion_tokens: int = 0
prompt_tokens: int = 0
total_tokens: int = 0
completion_tokens_details: Optional[CompletionTokensDetails] = None
prompt_tokens_details: Optional[PromptTokensDetails] = None
prompt_cache_hit_tokens: int = 0
prompt_cache_miss_tokens: int = 0
@dataclass
class ChatCompletion:
id: str
choices: List[Choice]
created: int
model: str
object: str = "chat.completion"
system_fingerprint: Optional[str] = None
service_tier: Optional[str] = None
usage: Optional[CompletionUsage] = None
def process_stream_chunks(chunks):
"""Process streaming chunks into combined response"""
if not chunks:
return None
try:
first_chunk = chunks[0]
last_chunk = chunks[-1]
# Basic metadata
id = getattr(first_chunk, "id", None)
created = getattr(first_chunk, "created", None)
model = getattr(first_chunk, "model", None)
system_fingerprint = getattr(first_chunk, "system_fingerprint", None)
# Track usage
completion_tokens = 0
prompt_tokens = 0
content_list = []
reasoning_list = []
tool_calls = []
current_tool_call = None
# First pass: Get initial tool call data
for chunk in chunks:
if not hasattr(chunk, "choices") or not chunk.choices:
continue
delta = getattr(chunk.choices[0], "delta", None)
if not delta:
continue
# Handle content and reasoning
if hasattr(delta, "content") and delta.content:
content_list.append(delta.content)
if hasattr(delta, "reasoning_content") and delta.reasoning_content:
reasoning_list.append(delta.reasoning_content)
# Handle tool calls
if hasattr(delta, "tool_calls") and delta.tool_calls:
for tool_call_delta in delta.tool_calls:
if tool_call_delta.index is not None and tool_call_delta.id:
# Found the initial tool call
current_tool_call = {
"id": tool_call_delta.id,
"type": "function",
"function": {
"name": tool_call_delta.function.name,
"arguments": ""
}
}
while len(tool_calls) <= tool_call_delta.index:
tool_calls.append(None)
tool_calls[tool_call_delta.index] = current_tool_call
current_tool_call = tool_calls[tool_call_delta.index]
elif current_tool_call is not None and hasattr(tool_call_delta.function, "arguments"):
if tool_call_delta.function.arguments:
current_tool_call["function"]["arguments"] += tool_call_delta.function.arguments
# Remove any None values and empty tool calls
tool_calls = [tc for tc in tool_calls if tc and tc["id"] and tc["function"]["name"]]
combined_content = "".join(content_list) if content_list else ""
combined_reasoning = "".join(reasoning_list) if reasoning_list else None
finish_reason = getattr(last_chunk.choices[0], "finish_reason", None) if hasattr(last_chunk, "choices") and last_chunk.choices else None
# Create ToolCall objects
processed_tool_calls = []
if tool_calls:
try:
from openai.types.chat import ChatCompletionMessageToolCall
for tc in tool_calls:
tool_call = ChatCompletionMessageToolCall(
id=tc["id"],
type=tc["type"],
function={
"name": tc["function"]["name"],
"arguments": tc["function"]["arguments"]
}
)
processed_tool_calls.append(tool_call)
except Exception as e:
print(f"Error processing tool call: {e}")
message = ChatCompletionMessage(
content=combined_content,
role="assistant",
reasoning_content=combined_reasoning,
tool_calls=processed_tool_calls if processed_tool_calls else None
)
choice = Choice(
finish_reason=finish_reason or "tool_calls" if processed_tool_calls else None,
index=0,
message=message
)
usage = CompletionUsage(
completion_tokens=completion_tokens,
prompt_tokens=prompt_tokens,
total_tokens=completion_tokens + prompt_tokens,
completion_tokens_details=CompletionTokensDetails(),
prompt_tokens_details=PromptTokensDetails()
)
return ChatCompletion(
id=id,
choices=[choice],
created=created,
model=model,
system_fingerprint=system_fingerprint,
usage=usage
)
except Exception as e:
print(f"Error processing chunks: {e}")
return None
class Agent:
def _generate_tool_definition(self, function_name):
"""
@@ -190,7 +360,8 @@ class Agent:
max_reflect: int = 3,
min_reflect: int = 1,
reflect_llm: Optional[str] = None,
user_id: Optional[str] = None
user_id: Optional[str] = None,
reasoning_steps: bool = False
):
# Add check at start if memory is requested
if memory is not None:
@@ -298,6 +469,7 @@ Your Goal: {self.goal}
# Store user_id
self.user_id = user_id or "praison"
self.reasoning_steps = reasoning_steps
# Check if knowledge parameter has any values
if not knowledge:
@@ -399,7 +571,54 @@ Your Goal: {self.goal}
def __str__(self):
return f"Agent(name='{self.name}', role='{self.role}', goal='{self.goal}')"
def _chat_completion(self, messages, temperature=0.2, tools=None, stream=True):
def _process_stream_response(self, messages, temperature, start_time, formatted_tools=None, reasoning_steps=False):
"""Process streaming response and return final response"""
try:
# Create the response stream
response_stream = client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
tools=formatted_tools if formatted_tools else None,
stream=True
)
full_response_text = ""
reasoning_content = ""
chunks = []
# Create Live display with proper configuration
with Live(
display_generating("", start_time),
console=self.console,
refresh_per_second=4,
transient=True,
vertical_overflow="ellipsis",
auto_refresh=True
) as live:
for chunk in response_stream:
chunks.append(chunk)
if chunk.choices[0].delta.content:
full_response_text += chunk.choices[0].delta.content
live.update(display_generating(full_response_text, start_time))
# Update live display with reasoning content if enabled
if reasoning_steps and hasattr(chunk.choices[0].delta, "reasoning_content"):
rc = chunk.choices[0].delta.reasoning_content
if rc:
reasoning_content += rc
live.update(display_generating(f"{full_response_text}\n[Reasoning: {reasoning_content}]", start_time))
# Clear the last generating display with a blank line
self.console.print()
final_response = process_stream_chunks(chunks)
return final_response
except Exception as e:
display_error(f"Error in stream processing: {e}")
return None
def _chat_completion(self, messages, temperature=0.2, tools=None, stream=True, reasoning_steps=False):
start_time = time.time()
logging.debug(f"{self.name} sending messages to LLM: {messages}")
@@ -425,20 +644,31 @@ Your Goal: {self.goal}
logging.warning(f"Tool {tool} not recognized")
try:
initial_response = client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
tools=formatted_tools if formatted_tools else None,
stream=False
)
if stream:
# Process as streaming response with formatted tools
final_response = self._process_stream_response(
messages,
temperature,
start_time,
formatted_tools=formatted_tools if formatted_tools else None,
reasoning_steps=reasoning_steps
)
else:
# Process as regular non-streaming response
final_response = client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
tools=formatted_tools if formatted_tools else None,
stream=False
)
tool_calls = getattr(initial_response.choices[0].message, 'tool_calls', None)
tool_calls = getattr(final_response.choices[0].message, 'tool_calls', None)
if tool_calls:
messages.append({
"role": "assistant",
"content": initial_response.choices[0].message.content,
"role": "assistant",
"content": final_response.choices[0].message.content,
"tool_calls": tool_calls
})
@@ -461,56 +691,31 @@ Your Goal: {self.goal}
"content": results_str
})
if stream:
response_stream = client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
stream=True
)
full_response_text = ""
# Create Live display with proper configuration
with Live(
display_generating("", start_time),
console=self.console,
refresh_per_second=4,
transient=True, # Changed to False to preserve output
vertical_overflow="ellipsis",
auto_refresh=True
) as live:
for chunk in response_stream:
if chunk.choices[0].delta.content:
full_response_text += chunk.choices[0].delta.content
live.update(display_generating(full_response_text, start_time))
# Clear the last generating display with a blank line
self.console.print()
final_response = client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
stream=False
)
return final_response
else:
if tool_calls:
# Get final response after tool calls
if stream:
final_response = self._process_stream_response(
messages,
temperature,
start_time,
formatted_tools=formatted_tools if formatted_tools else None,
reasoning_steps=reasoning_steps
)
else:
final_response = client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
stream=False
)
return final_response
else:
return initial_response
return final_response
except Exception as e:
display_error(f"Error in chat completion: {e}")
return None
def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None):
def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False):
reasoning_steps = reasoning_steps or self.reasoning_steps
# Search for existing knowledge if any knowledge is provided
if self.knowledge:
search_results = self.knowledge.search(prompt, agent_id=self.agent_id)
@@ -546,7 +751,8 @@ Your Goal: {self.goal}
agent_name=self.name,
agent_role=self.role,
agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in self.tools],
execute_tool_fn=self.execute_tool # Pass tool execution function
execute_tool_fn=self.execute_tool, # Pass tool execution function
reasoning_steps=reasoning_steps
)
self.chat_history.append({"role": "user", "content": prompt})
@@ -616,14 +822,13 @@ Your Goal: {self.goal}
agent_tools=agent_tools
)
response = self._chat_completion(messages, temperature=temperature, tools=tools if tools else None)
response = self._chat_completion(messages, temperature=temperature, tools=tools if tools else None, reasoning_steps=reasoning_steps)
if not response:
return None
tool_calls = getattr(response.choices[0].message, 'tool_calls', None)
response_text = response.choices[0].message.content.strip()
if tool_calls:
if tool_calls: ## TODO: Most likely this tool call is already called in _chat_completion, so maybe we can remove this.
messages.append({
"role": "assistant",
"content": response_text,
@@ -675,6 +880,9 @@ Your Goal: {self.goal}
if self.verbose:
logging.debug(f"Agent {self.name} final response: {response_text}")
display_interaction(original_prompt, response_text, markdown=self.markdown, generation_time=time.time() - start_time, console=self.console)
# Return only reasoning content if reasoning_steps is True
if reasoning_steps and hasattr(response.choices[0].message, 'reasoning_content'):
return response.choices[0].message.reasoning_content
return response_text
reflection_prompt = f"""
@@ -749,8 +957,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
cleaned = cleaned[:-3].strip()
return cleaned
async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None, output_pydantic=None):
async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False):
"""Async version of chat method. TODO: Requires Syncing with chat method."""
reasoning_steps = reasoning_steps or self.reasoning_steps
try:
# Search for existing knowledge if any knowledge is provided
if self.knowledge:
@@ -781,7 +990,8 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
agent_name=self.name,
agent_role=self.role,
agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in self.tools],
execute_tool_fn=self.execute_tool_async
execute_tool_fn=self.execute_tool_async,
reasoning_steps=reasoning_steps
)
self.chat_history.append({"role": "user", "content": prompt})
@@ -847,48 +1057,48 @@ Your Goal: {self.goal}
)
# Format tools if provided
formatted_tools = []
if tools:
for tool in tools:
if isinstance(tool, str):
tool_def = self._generate_tool_definition(tool)
if tool_def:
formatted_tools.append(tool_def)
elif isinstance(tool, dict):
formatted_tools.append(tool)
elif hasattr(tool, "to_openai_tool"):
formatted_tools.append(tool.to_openai_tool())
elif callable(tool):
formatted_tools.append(self._generate_tool_definition(tool.__name__))
formatted_tools = []
if tools:
for tool in tools:
if isinstance(tool, str):
tool_def = self._generate_tool_definition(tool)
if tool_def:
formatted_tools.append(tool_def)
elif isinstance(tool, dict):
formatted_tools.append(tool)
elif hasattr(tool, "to_openai_tool"):
formatted_tools.append(tool.to_openai_tool())
elif callable(tool):
formatted_tools.append(self._generate_tool_definition(tool.__name__))
# Create async OpenAI client
async_client = AsyncOpenAI()
# Create async OpenAI client
async_client = AsyncOpenAI()
# Make the API call based on the type of request
if tools:
response = await async_client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
tools=formatted_tools
)
return await self._achat_completion(response, tools)
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"}
)
# Return the raw response
return response.choices[0].message.content
else:
response = await async_client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature
)
return response.choices[0].message.content
# Make the API call based on the type of request
if tools:
response = await async_client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature,
tools=formatted_tools
)
return await self._achat_completion(response, tools)
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"}
)
# Return the raw response
return response.choices[0].message.content
else:
response = await async_client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=temperature
)
return response.choices[0].message.content
except Exception as e:
display_error(f"Error in chat completion: {e}")
return None
@@ -896,7 +1106,7 @@ Your Goal: {self.goal}
display_error(f"Error in achat: {e}")
return None
async def _achat_completion(self, response, tools):
async def _achat_completion(self, response, tools, reasoning_steps=False):
"""Async version of _chat_completion method"""
try:
message = response.choices[0].message
@@ -942,9 +1152,42 @@ Your Goal: {self.goal}
final_response = await async_client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=0.2
temperature=0.2,
stream=True
)
return final_response.choices[0].message.content
full_response_text = ""
reasoning_content = ""
chunks = []
start_time = time.time()
with Live(
display_generating("", start_time),
console=self.console,
refresh_per_second=4,
transient=True,
vertical_overflow="ellipsis",
auto_refresh=True
) as live:
async for chunk in final_response:
chunks.append(chunk)
if chunk.choices[0].delta.content:
full_response_text += chunk.choices[0].delta.content
live.update(display_generating(full_response_text, start_time))
if reasoning_steps and hasattr(chunk.choices[0].delta, "reasoning_content"):
rc = chunk.choices[0].delta.reasoning_content
if rc:
reasoning_content += rc
live.update(display_generating(f"{full_response_text}\n[Reasoning: {reasoning_content}]", start_time))
self.console.print()
final_response = process_stream_chunks(chunks)
# Return only reasoning content if reasoning_steps is True
if reasoning_steps and hasattr(final_response.choices[0].message, 'reasoning_content'):
return final_response.choices[0].message.reasoning_content
return final_response.choices[0].message.content if final_response else full_response_text
except Exception as e:
display_error(f"Error in final chat completion: {e}")
return formatted_results
@@ -952,7 +1195,7 @@ Your Goal: {self.goal}
return None
except Exception as e:
display_error(f"Error in _achat_completion: {e}")
return None
return None
async def astart(self, prompt: str, **kwargs):
"""Async version of start method"""
+4 -18
View File
@@ -390,15 +390,8 @@ Context:
task.status = "completed"
# Run execute_callback for memory operations
try:
try:
# If a loop is already running, just create the task
loop = asyncio.get_running_loop()
loop.create_task(task.execute_callback(task_output))
except RuntimeError:
# Otherwise, create and set a new loop, and run the callback
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.create_task(task.execute_callback(task_output))
# Use the new sync wrapper to avoid pending coroutine issues
task.execute_callback_sync(task_output)
except Exception as e:
logger.error(f"Error executing memory callback for task {task_id}: {e}")
logger.exception(e)
@@ -720,15 +713,8 @@ Context:
task.status = "completed"
# Run execute_callback for memory operations
try:
try:
# If a loop is already running, just create the task
loop = asyncio.get_running_loop()
loop.create_task(task.execute_callback(task_output))
except RuntimeError:
# Otherwise, create and set a new loop, and run the callback
loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop)
loop.create_task(task.execute_callback(task_output))
# Use the new sync wrapper to avoid pending coroutine issues
task.execute_callback_sync(task_output)
except Exception as e:
logger.error(f"Error executing memory callback for task {task_id}: {e}")
logger.exception(e)
+331 -128
View File
@@ -17,6 +17,8 @@ from ..main import (
from rich.console import Console
from rich.live import Live
# TODO: Include in-build tool calling in LLM class
# TODO: Restructure so that duplicate calls are not made (Sync with agent.py)
class LLMContextLengthExceededException(Exception):
"""Raised when LLM context length is exceeded"""
def __init__(self, message: str):
@@ -113,6 +115,7 @@ class LLM:
litellm.callbacks = []
# Additional logging suppression
litellm.suppress_debug_messages = True
litellm._logging._disable_debugging()
logging.getLogger("litellm.utils").setLevel(logging.WARNING)
logging.getLogger("litellm.main").setLevel(logging.WARNING)
except ImportError:
@@ -147,6 +150,7 @@ class LLM:
self.self_reflect = extra_settings.get('self_reflect', False)
self.max_reflect = extra_settings.get('max_reflect', 3)
self.min_reflect = extra_settings.get('min_reflect', 1)
self.reasoning_steps = extra_settings.get('reasoning_steps', False)
# Enable error dropping for cleaner output
litellm.drop_params = True
@@ -176,7 +180,8 @@ class LLM:
"""Enhanced get_response with all OpenAI-like features"""
try:
import litellm
# This below **kwargs** is passed to .completion() directly. so reasoning_steps has to be popped. OR find alternate best way of handling this.
reasoning_steps = kwargs.pop('reasoning_steps', self.reasoning_steps)
# Disable litellm debug messages
litellm.set_verbose = False
@@ -230,8 +235,55 @@ class LLM:
# Get response from LiteLLM
start_time = time.time()
if verbose:
with Live(display_generating("", start_time), console=console, refresh_per_second=4) as live:
# If reasoning_steps is True, do a single non-streaming call
if reasoning_steps:
resp = litellm.completion(
model=self.model,
messages=messages,
temperature=temperature,
stream=False, # force non-streaming
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
)
reasoning_content = resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
response_text = resp["choices"][0]["message"]["content"]
# Optionally display reasoning if present
if verbose and reasoning_content:
display_interaction(
original_prompt,
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
else:
display_interaction(
original_prompt,
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
# Otherwise do the existing streaming approach
else:
if verbose:
with Live(display_generating("", start_time), console=console, refresh_per_second=4) as live:
response_text = ""
for chunk in litellm.completion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
**kwargs
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
response_text += content
live.update(display_generating(response_text, start_time))
else:
# Non-verbose mode, just collect the response
response_text = ""
for chunk in litellm.completion(
model=self.model,
@@ -241,23 +293,9 @@ class LLM:
**kwargs
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
response_text += content
live.update(display_generating(response_text, start_time))
else:
# Non-verbose mode, just collect the response
response_text = ""
for chunk in litellm.completion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
**kwargs
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
response_text += chunk.choices[0].delta.content
response_text += chunk.choices[0].delta.content
response_text = response_text.strip()
response_text = response_text.strip()
# Get final completion to check for tool calls
final_response = litellm.completion(
@@ -302,9 +340,53 @@ class LLM:
"content": "Function returned an empty output"
})
# Get response after tool calls with streaming
if verbose:
with Live(display_generating("", start_time), console=console, refresh_per_second=4) as live:
# If reasoning_steps is True, do a single non-streaming call
if reasoning_steps:
resp = litellm.completion(
model=self.model,
messages=messages,
temperature=temperature,
stream=False, # force non-streaming
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
)
reasoning_content = resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
response_text = resp["choices"][0]["message"]["content"]
# Optionally display reasoning if present
if verbose and reasoning_content:
display_interaction(
original_prompt,
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
else:
display_interaction(
original_prompt,
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
# Otherwise do the existing streaming approach
else:
# Get response after tool calls with streaming
if verbose:
with Live(display_generating("", start_time), console=console, refresh_per_second=4) as live:
response_text = ""
for chunk in litellm.completion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
response_text += content
live.update(display_generating(response_text, start_time))
else:
response_text = ""
for chunk in litellm.completion(
model=self.model,
@@ -313,21 +395,9 @@ class LLM:
stream=True
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
response_text += content
live.update(display_generating(response_text, start_time))
else:
response_text = ""
for chunk in litellm.completion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
response_text += chunk.choices[0].delta.content
response_text += chunk.choices[0].delta.content
response_text = response_text.strip()
response_text = response_text.strip()
# Handle output formatting
if output_json or output_pydantic:
@@ -342,6 +412,9 @@ class LLM:
if verbose:
display_interaction(original_prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
# Return reasoning content if reasoning_steps is True
if reasoning_steps and reasoning_content:
return reasoning_content
return response_text
# Handle self-reflection
@@ -357,32 +430,66 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
{"role": "user", "content": reflection_prompt}
]
# Get reflection response with streaming
if verbose:
with Live(display_generating("", start_time), console=console, refresh_per_second=4) as live:
# If reasoning_steps is True, do a single non-streaming call to capture reasoning
if reasoning_steps:
reflection_resp = litellm.completion(
model=self.model,
messages=reflection_messages,
temperature=temperature,
stream=False, # Force non-streaming
response_format={"type": "json_object"},
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
)
# Grab reflection text and optional reasoning
reasoning_content = reflection_resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
reflection_text = reflection_resp["choices"][0]["message"]["content"]
# Optionally display reasoning if present
if verbose and reasoning_content:
display_interaction(
"Reflection reasoning:",
f"{reasoning_content}\n\nReflection result:\n{reflection_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
elif verbose:
display_interaction(
"Self-reflection (non-streaming):",
reflection_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
else:
# Existing streaming approach
if verbose:
with Live(display_generating("", start_time), console=console, refresh_per_second=4) as live:
reflection_text = ""
for chunk in litellm.completion(
model=self.model,
messages=reflection_messages,
temperature=temperature,
stream=True,
response_format={"type": "json_object"},
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
reflection_text += content
live.update(display_generating(reflection_text, start_time))
else:
reflection_text = ""
for chunk in litellm.completion(
model=self.model,
messages=reflection_messages,
temperature=temperature,
stream=True,
response_format={"type": "json_object"}
response_format={"type": "json_object"},
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
reflection_text += content
live.update(display_generating(reflection_text, start_time))
else:
reflection_text = ""
for chunk in litellm.completion(
model=self.model,
messages=reflection_messages,
temperature=temperature,
stream=True,
response_format={"type": "json_object"}
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
reflection_text += chunk.choices[0].delta.content
reflection_text += chunk.choices[0].delta.content
try:
reflection_data = json.loads(reflection_text)
@@ -453,6 +560,7 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
"""Async version of get_response with identical functionality."""
try:
import litellm
reasoning_steps = kwargs.pop('reasoning_steps', self.reasoning_steps)
litellm.set_verbose = False
# Build messages list
@@ -490,10 +598,10 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
# Format tools for LiteLLM
formatted_tools = None
if tools:
logging.info(f"Starting tool formatting for {len(tools)} tools")
logging.debug(f"Starting tool formatting for {len(tools)} tools")
formatted_tools = []
for tool in tools:
logging.info(f"Processing tool: {tool.__name__ if hasattr(tool, '__name__') else str(tool)}")
logging.debug(f"Processing tool: {tool.__name__ if hasattr(tool, '__name__') else str(tool)}")
if hasattr(tool, '__name__'):
tool_name = tool.__name__
tool_doc = tool.__doc__ or "No description available"
@@ -539,10 +647,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
}
}
# Ensure tool definition is JSON serializable
print(f"Generated tool definition: {tool_def}")
try:
json.dumps(tool_def) # Test serialization
logging.info(f"Generated tool definition: {tool_def}")
logging.debug(f"Generated tool definition: {tool_def}")
formatted_tools.append(tool_def)
except TypeError as e:
logging.error(f"Tool definition not JSON serializable: {e}")
@@ -552,38 +659,67 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
if formatted_tools:
try:
json.dumps(formatted_tools) # Final serialization check
logging.info(f"Final formatted tools: {json.dumps(formatted_tools, indent=2)}")
logging.debug(f"Final formatted tools: {json.dumps(formatted_tools, indent=2)}")
except TypeError as e:
logging.error(f"Final tools list not JSON serializable: {e}")
formatted_tools = None
response_text = ""
if verbose:
# ----------------------------------------------------
# 1) Make the streaming call WITHOUT tools
# ----------------------------------------------------
async for chunk in await litellm.acompletion(
if reasoning_steps:
# Non-streaming call to capture reasoning
resp = await litellm.acompletion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
**kwargs
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
response_text += chunk.choices[0].delta.content
print("\033[K", end="\r")
print(f"Generating... {time.time() - start_time:.1f}s", end="\r")
stream=False, # force non-streaming
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
)
reasoning_content = resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
response_text = resp["choices"][0]["message"]["content"]
if verbose and reasoning_content:
display_interaction(
"Initial reasoning:",
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
elif verbose:
display_interaction(
"Initial response:",
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
else:
# Non-verbose streaming call, still no tools
async for chunk in await litellm.acompletion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
**kwargs
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
response_text += chunk.choices[0].delta.content
if verbose:
# ----------------------------------------------------
# 1) Make the streaming call WITHOUT tools
# ----------------------------------------------------
async for chunk in await litellm.acompletion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
**kwargs
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
response_text += chunk.choices[0].delta.content
print("\033[K", end="\r")
print(f"Generating... {time.time() - start_time:.1f}s", end="\r")
else:
# Non-verbose streaming call, still no tools
async for chunk in await litellm.acompletion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
**kwargs
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
response_text += chunk.choices[0].delta.content
response_text = response_text.strip()
@@ -598,7 +734,7 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
temperature=temperature,
stream=False,
tools=formatted_tools, # We safely pass tools here
**kwargs
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
)
# handle tool_calls from tool_response as usual...
tool_calls = tool_response.choices[0].message.get("tool_calls")
@@ -634,35 +770,66 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
"content": "Function returned an empty output"
})
# Get response after tool calls with streaming
# Get response after tool calls
response_text = ""
if verbose:
async for chunk in await litellm.acompletion(
if reasoning_steps:
# Non-streaming call to capture reasoning
resp = await litellm.acompletion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
tools=formatted_tools,
**kwargs
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
response_text += content
print("\033[K", end="\r")
print(f"Reflecting... {time.time() - start_time:.1f}s", end="\r")
else:
response_text = ""
for chunk in litellm.completion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
**kwargs
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
response_text += chunk.choices[0].delta.content
stream=False, # force non-streaming
tools=formatted_tools, # Include tools
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
)
reasoning_content = resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
response_text = resp["choices"][0]["message"]["content"]
if verbose and reasoning_content:
display_interaction(
"Tool response reasoning:",
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
elif verbose:
display_interaction(
"Tool response:",
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
else:
# Get response after tool calls with streaming
if verbose:
async for chunk in await litellm.acompletion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
tools=formatted_tools,
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
response_text += content
print("\033[K", end="\r")
print(f"Reflecting... {time.time() - start_time:.1f}s", end="\r")
else:
response_text = ""
for chunk in litellm.completion(
model=self.model,
messages=messages,
temperature=temperature,
stream=True,
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
response_text += chunk.choices[0].delta.content
response_text = response_text.strip()
response_text = response_text.strip()
# Handle output formatting
if output_json or output_pydantic:
@@ -677,6 +844,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
if verbose:
display_interaction(original_prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
# Return reasoning content if reasoning_steps is True
if reasoning_steps and reasoning_content:
return reasoning_content
return response_text
# Handle self-reflection
@@ -692,33 +862,66 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
{"role": "user", "content": reflection_prompt}
]
# Get reflection response
reflection_text = ""
if verbose:
async for chunk in await litellm.acompletion(
# If reasoning_steps is True, do a single non-streaming call to capture reasoning
if reasoning_steps:
reflection_resp = litellm.completion(
model=self.model,
messages=reflection_messages,
temperature=temperature,
stream=True,
stream=False, # Force non-streaming
response_format={"type": "json_object"},
**kwargs
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
reflection_text += content
print("\033[K", end="\r")
print(f"Reflecting... {time.time() - start_time:.1f}s", end="\r")
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
)
# Grab reflection text and optional reasoning
reasoning_content = reflection_resp["choices"][0]["message"].get("provider_specific_fields", {}).get("reasoning_content")
reflection_text = reflection_resp["choices"][0]["message"]["content"]
# Optionally display reasoning if present
if verbose and reasoning_content:
display_interaction(
"Reflection reasoning:",
f"{reasoning_content}\n\nReflection result:\n{reflection_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
elif verbose:
display_interaction(
"Self-reflection (non-streaming):",
reflection_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
)
else:
async for chunk in await litellm.acompletion(
model=self.model,
messages=reflection_messages,
temperature=temperature,
stream=True,
response_format={"type": "json_object"},
**kwargs
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
reflection_text += chunk.choices[0].delta.content
# Existing streaming approach
if verbose:
with Live(display_generating("", start_time), console=console, refresh_per_second=4) as live:
reflection_text = ""
for chunk in litellm.completion(
model=self.model,
messages=reflection_messages,
temperature=temperature,
stream=True,
response_format={"type": "json_object"},
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
content = chunk.choices[0].delta.content
reflection_text += content
live.update(display_generating(reflection_text, start_time))
else:
reflection_text = ""
for chunk in litellm.completion(
model=self.model,
messages=reflection_messages,
temperature=temperature,
stream=True,
response_format={"type": "json_object"},
**{k:v for k,v in kwargs.items() if k != 'reasoning_steps'}
):
if chunk and chunk.choices and chunk.choices[0].delta.content:
reflection_text += chunk.choices[0].delta.content
while True: # Add loop for reflection handling
try:
+8 -4
View File
@@ -44,6 +44,8 @@ class Process:
current_task = start_task
visited_tasks = set()
loop_data = {} # Store loop-specific data
# TODO: start task with loop feature is not available in aworkflow method
while current_task:
current_iter += 1
@@ -350,9 +352,10 @@ Provide a JSON with the structure:
if row: # Skip empty rows
task_desc = row[0] # Take first column
row_task = Task(
description=task_desc, # Keep full row as description
description=f"{start_task.description}\n{task_desc}" if start_task.description else task_desc,
agent=start_task.agent,
name=task_desc, # Use first column as name
name=f"{start_task.name}_{i+1}" if start_task.name else task_desc,
expected_output=getattr(start_task, 'expected_output', None),
is_start=(i == 0),
task_type="task",
condition={
@@ -374,9 +377,10 @@ Provide a JSON with the structure:
previous_task = None
for i, line in enumerate(lines):
row_task = Task(
description=line.strip(),
description=f"{start_task.description}\n{line.strip()}" if start_task.description else line.strip(),
agent=start_task.agent,
name=line.strip(),
name=f"{start_task.name}_{i+1}" if start_task.name else line.strip(),
expected_output=getattr(start_task, 'expected_output', None),
is_start=(i == 0),
task_type="task",
condition={
+18 -1
View File
@@ -302,4 +302,21 @@ Expected Output: {self.expected_output}.
Context:
{' '.join(unique_contexts)}
"""
"""
def execute_callback_sync(self, task_output: TaskOutput) -> None:
"""
Synchronous wrapper to ensure that execute_callback is awaited,
preventing 'Task was destroyed but pending!' warnings if called
from non-async code.
"""
import asyncio
try:
loop = asyncio.get_running_loop()
if loop.is_running():
loop.create_task(self.execute_callback(task_output))
else:
loop.run_until_complete(self.execute_callback(task_output))
except RuntimeError:
# If no loop is running in this context
asyncio.run(self.execute_callback(task_output))
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.0.50"
version = "0.0.56"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
authors = [
{ name="Mervin Praison" }
+2 -1
View File
@@ -6,7 +6,8 @@ agent = Agent(
)
task = Task(
description="Create the list of tasks to be looped through.",
description="complete the task",
expected_output="task completed",
agent=agent,
task_type="loop",
input_file="tasks.csv"
+1 -1
View File
@@ -1868,7 +1868,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.50"
version = "0.0.56"
source = { editable = "." }
dependencies = [
{ name = "openai" },
+1
View File
@@ -7,6 +7,7 @@ agent = Agent(
self_reflect=True,
min_reflect=3,
max_reflect=5,
llm="gpt-4o-mini"
)
agent.start(
"What is the history of AI?"
+1 -1
View File
@@ -1,6 +1,6 @@
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask praisonai==2.0.57 gunicorn markdown
RUN pip install flask praisonai==2.0.59 gunicorn markdown
EXPOSE 8080
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
+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.57 gunicorn markdown\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.0.59 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;)
+12 -76
View File
@@ -112,86 +112,22 @@ graph LR
<Step title="Create Agent">
Create `app.py`:
<CodeGroup>
```python Single Agent
from praisonaiagents import Agent, Task, PraisonAIAgents
```python Single Agent
from praisonaiagents import Agent
# Create an agent
researcher = Agent(
name="Researcher",
role="Senior Research Analyst",
goal="Uncover cutting-edge developments in AI",
backstory="You are an expert at a technology research group",
verbose=True,
llm="gpt-4o"
)
agent = Agent(instructions="Your are a helpful AI assistant")
agent.start("Write a movie script about a robot in Mars")
```
# Define a task
task = Task(
name="research_task",
description="Analyze 2024's AI advancements",
expected_output="A detailed report",
agent=researcher
)
```python Multi Agents
from praisonaiagents import Agent, PraisonAIAgents
# Run the agents
agents = PraisonAIAgents(
agents=[researcher],
tasks=[task],
verbose=False
)
research_agent = Agent(instructions="Research about AI")
summarise_agent = Agent(instructions="Summarise research agent's findings")
result = agents.start()
```
```python Multiple Agents
from praisonaiagents import Agent, Task, PraisonAIAgents
# Create multiple agents
researcher = Agent(
name="Researcher",
role="Senior Research Analyst",
goal="Uncover cutting-edge developments in AI",
backstory="You are an expert at a technology research group",
verbose=True,
llm="gpt-4o",
markdown=True
)
writer = Agent(
name="Writer",
role="Tech Content Strategist",
goal="Craft compelling content on tech advancements",
backstory="You are a content strategist",
llm="gpt-4o",
markdown=True
)
# Define multiple tasks
task1 = Task(
name="research_task",
description="Analyze 2024's AI advancements",
expected_output="A detailed report",
agent=researcher
)
task2 = Task(
name="writing_task",
description="Create a blog post about AI advancements",
expected_output="A blog post",
agent=writer
)
# Run with hierarchical process
agents = PraisonAIAgents(
agents=[researcher, writer],
tasks=[task1, task2],
verbose=False,
process="hierarchical",
manager_llm="gpt-4o"
)
result = agents.start()
```
agents = PraisonAIAgents(agents=[research_agent, summarise_agent])
agents.start()
```
</CodeGroup>
</Step>
+137
View File
@@ -0,0 +1,137 @@
---
title: "Reasoning Extract Agents"
description: "Learn how to create AI agents that can perform step-by-step reasoning and extract information"
icon: "brain-circuit"
---
```mermaid
flowchart LR
In[Input] --> RA[("Reasoning Agent")]
RA --> Steps[Reasoning Steps]
Steps --> SA[("Small Agent")]
SA --> Out[Output]
style In fill:#8B0000,color:#fff
style RA fill:#2E8B57,color:#fff,shape:circle
style Steps fill:#4169E1,color:#fff
style SA fill:#2E8B57,color:#fff,shape:circle
style Out fill:#8B0000,color:#fff
```
A workflow where a reasoning agent breaks down complex problems into steps, followed by a smaller agent that processes these steps to provide concise answers.
## Prerequisites
<Steps>
<Step title="Install Package">
Install required packages:
```bash
pip install "praisonaiagents[llm]"
```
<Note>
praisonaiagents[llm] includes all necessary dependencies for reasoning agents
</Note>
</Step>
<Step title="Set API Key">
Configure your API key:
```bash
export OPENAI_API_KEY=your_api_key_here
```
</Step>
<Step title="Create File">
Create a new file called `app.py` and add the following code:
</Step>
<Step title="Run Application">
Execute the script:
```bash
python app.py
```
</Step>
</Steps>
## Code
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
reasoning_agent = Agent(
role="Helpful Assistant",
reasoning_steps=True,
llm="deepseek/deepseek-reasoner"
)
small_agent = Agent(
role="Helpful Assistant",
llm="gpt-3.5-turbo"
)
reasoning_task = Task(
description="How many r's in the word 'Strawberry'?",
agent=reasoning_agent
)
small_task = Task(
description="With the provided reasoning tell me how many r's in the word 'Strawberry'?",
agent=small_agent
)
agents = PraisonAIAgents(
agents=[reasoning_agent, small_agent],
tasks=[reasoning_task, small_task]
)
agents.start()
```
## Features
<CardGroup cols={2}>
<Card title="Step-by-Step Reasoning" icon="list-check">
Break down complex problems into logical steps.
</Card>
<Card title="Multi-Agent Collaboration" icon="users">
Combine reasoning and processing agents.
</Card>
<Card title="Flexible Models" icon="arrows-split-up-and-left">
Use different models for reasoning and processing.
</Card>
<Card title="Task Chaining" icon="link">
Connect reasoning output to processing tasks.
</Card>
</CardGroup>
## Understanding Reasoning Agents
<Card title="What are Reasoning Agents?" icon="question">
Reasoning agents enable:
- Detailed step-by-step analysis
- Logical problem decomposition
- Clear reasoning paths
- Enhanced decision making
</Card>
## Troubleshooting
<CardGroup cols={2}>
<Card title="Reasoning Issues" icon="triangle-exclamation">
If reasoning steps aren't clear:
- Check reasoning_steps parameter
- Verify model compatibility
- Review task descriptions
</Card>
<Card title="Agent Communication" icon="messages">
If agents aren't coordinating:
- Ensure task order is correct
- Check task dependencies
- Verify agent configurations
</Card>
</CardGroup>
<Note>
The reasoning agent uses a specialized model (deepseek-reasoner) optimized for step-by-step analysis, while the small agent can use a more general-purpose model for processing the reasoning output.
</Note>
+424 -124
View File
@@ -1,7 +1,7 @@
---
title: "Praison AI"
sidebarTitle: "Home"
description: "Comprehensive guide to PraisonAI - an AI Agents Framework with Self Reflection, combining PraisonAI Agents, AutoGen, and CrewAI for building multi-agent LLM systems"
description: "PraisonAI is a production-ready Multi-AI Agents framework with self-reflection, designed to create AI Agents to automate and solve problems ranging from simple tasks to complex challenges. By integrating PraisonAI Agents, AutoGen, and CrewAI into a low-code solution, it streamlines the building and management of multi-agent LLM systems, emphasising simplicity, customisation, and effective human-agent collaboration."
icon: "house"
openGraph:
url: "/"
@@ -66,24 +66,53 @@ seo:
Internet search using Crawl4AI and Tavily
</Card>
</CardGroup>
<br />
<div className="relative w-full aspect-video overflow-hidden rounded-lg shadow-lg mb-8">
<iframe
className="absolute top-0 left-0 w-full h-full border-0"
src="https://www.youtube.com/embed/Fn1lQjC0GO0"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
style={{
maxWidth: '100vw',
margin: '0 auto'
}}
></iframe>
</div>
## Installation Options
## Install
<Tabs>
<Tab title="Code">
<Steps>
<Step title="Install Package">
```bash
pip install praisonaiagents
```
</Step>
<Step title="Set API Key">
```bash
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
```
</Step>
<Step title="Create File">
Create `app.py` file
## Code Example
<CodeGroup>
```python Single Agent
from praisonaiagents import Agent
agent = Agent(instructions="Your are a helpful AI assistant")
agent.start("Write a movie script about a robot in Mars")
```
```python Multi Agents
from praisonaiagents import Agent, PraisonAIAgents
research_agent = Agent(instructions="Research about AI")
summarise_agent = Agent(instructions="Summarise research agent's findings")
agents = PraisonAIAgents(agents=[research_agent, summarise_agent])
agents.start()
```
</CodeGroup>
</Step>
<Step title="Run Script">
```bash
python app.py
```
</Step>
</Steps>
</Tab>
<Tab title="No Code">
<Tip>
PraisonAI combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customization, and efficient human-agent collaboration.
@@ -109,115 +138,361 @@ seo:
</Step>
</Steps>
</Tab>
<Tab title="Code">
<Steps>
<Step title="Install Package">
```bash
pip install praisonaiagents
```
</Step>
<Step title="Set API Key">
```bash
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
```
</Step>
<Step title="Create File">
Create `app.py` file
## Code Example
<CodeGroup>
```python Basic
from praisonaiagents import Agent, Task, PraisonAIAgents
# Create an agent
researcher = Agent(
name="Researcher",
role="Senior Research Analyst",
goal="Uncover cutting-edge developments in AI",
backstory="You are an expert at a technology research group",
verbose=True,
llm="gpt-4o"
)
# Define a task
task = Task(
name="research_task",
description="Analyze 2024's AI advancements",
expected_output="A detailed report",
agent=researcher
)
# Run the agents
agents = PraisonAIAgents(
agents=[researcher],
tasks=[task],
verbose=False
)
result = agents.start()
```
```python Advanced
from praisonaiagents import Agent, Task, PraisonAIAgents
# Create multiple agents
researcher = Agent(
name="Researcher",
role="Senior Research Analyst",
goal="Uncover cutting-edge developments in AI",
backstory="You are an expert at a technology research group",
verbose=True,
llm="gpt-4o",
markdown=True
)
writer = Agent(
name="Writer",
role="Tech Content Strategist",
goal="Craft compelling content on tech advancements",
backstory="You are a content strategist",
llm="gpt-4o",
markdown=True
)
# Define multiple tasks
task1 = Task(
name="research_task",
description="Analyze 2024's AI advancements",
expected_output="A detailed report",
agent=researcher
)
task2 = Task(
name="writing_task",
description="Create a blog post about AI advancements",
expected_output="A blog post",
agent=writer
)
# Run with hierarchical process
agents = PraisonAIAgents(
agents=[researcher, writer],
tasks=[task1, task2],
verbose=False,
process="hierarchical",
manager_llm="gpt-4o"
)
result = agents.start()
```
</CodeGroup>
</Step>
<Step title="Run Script">
```bash
python app.py
```
</Step>
</Steps>
</Tab>
</Tabs>
## AI Agents Flow
```mermaid
graph LR
%% Define the main flow
Start([▶ Start]) --> Agent1
Agent1 --> Process[⚙ Process]
Process --> Agent2
Agent2 --> Output([✓ Output])
Process -.-> Agent1
%% Define subgraphs for agents and their tasks
subgraph Agent1[ ]
Task1[📋 Task]
AgentIcon1[🤖 AI Agent]
Tools1[🔧 Tools]
Task1 --- AgentIcon1
AgentIcon1 --- Tools1
end
subgraph Agent2[ ]
Task2[📋 Task]
AgentIcon2[🤖 AI Agent]
Tools2[🔧 Tools]
Task2 --- AgentIcon2
AgentIcon2 --- Tools2
end
classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
classDef tools fill:#2E8B57,stroke:#7C90A0,color:#fff
classDef transparent fill:none,stroke:none
class Start,Output,Task1,Task2 input
class Process,AgentIcon1,AgentIcon2 process
class Tools1,Tools2 tools
class Agent1,Agent2 transparent
```
## AI Agents with Tools
Create AI agents that can use tools to interact with external systems and perform actions.
```mermaid
flowchart TB
subgraph Tools
direction TB
T3[Internet Search]
T1[Code Execution]
T2[Formatting]
end
Input[Input] ---> Agents
subgraph Agents
direction LR
A1[Agent 1]
A2[Agent 2]
A3[Agent 3]
end
Agents ---> Output[Output]
T3 --> A1
T1 --> A2
T2 --> A3
style Tools fill:#189AB4,color:#fff
style Agents fill:#8B0000,color:#fff
style Input fill:#8B0000,color:#fff
style Output fill:#8B0000,color:#fff
```
## AI Agents with Memory
Create AI agents with memory capabilities for maintaining context and information across tasks.
```mermaid
flowchart TB
subgraph Memory
direction TB
STM[Short Term]
LTM[Long Term]
end
subgraph Store
direction TB
DB[(Vector DB)]
end
Input[Input] ---> Agents
subgraph Agents
direction LR
A1[Agent 1]
A2[Agent 2]
A3[Agent 3]
end
Agents ---> Output[Output]
Memory <--> Store
Store <--> A1
Store <--> A2
Store <--> A3
style Memory fill:#189AB4,color:#fff
style Store fill:#2E8B57,color:#fff
style Agents fill:#8B0000,color:#fff
style Input fill:#8B0000,color:#fff
style Output fill:#8B0000,color:#fff
```
## AI Agents with Different Processes
### Sequential Process
The simplest form of task execution where tasks are performed one after another.
```mermaid
graph LR
Input[Input] --> A1
subgraph Agents
direction LR
A1[Agent 1] --> A2[Agent 2] --> A3[Agent 3]
end
A3 --> Output[Output]
classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
classDef transparent fill:none,stroke:none
class Input,Output input
class A1,A2,A3 process
class Agents transparent
```
### Hierarchical Process
Uses a manager agent to coordinate task execution and agent assignments.
```mermaid
graph TB
Input[Input] --> Manager
subgraph Agents
Manager[Manager Agent]
subgraph Workers
direction LR
W1[Worker 1]
W2[Worker 2]
W3[Worker 3]
end
Manager --> W1
Manager --> W2
Manager --> W3
end
W1 --> Manager
W2 --> Manager
W3 --> Manager
Manager --> Output[Output]
classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
classDef transparent fill:none,stroke:none
class Input,Output input
class Manager,W1,W2,W3 process
class Agents,Workers transparent
```
### Workflow Process
Advanced process type supporting complex task relationships and conditional execution.
```mermaid
graph LR
Input[Input] --> Start
subgraph Workflow
direction LR
Start[Start] --> C1{Condition}
C1 --> |Yes| A1[Agent 1]
C1 --> |No| A2[Agent 2]
A1 --> Join
A2 --> Join
Join --> A3[Agent 3]
end
A3 --> Output[Output]
classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
classDef decision fill:#2E8B57,stroke:#7C90A0,color:#fff
classDef transparent fill:none,stroke:none
class Input,Output input
class Start,A1,A2,A3,Join process
class C1 decision
class Workflow transparent
```
#### Agentic Routing Workflow
Create AI agents that can dynamically route tasks to specialized LLM instances.
```mermaid
flowchart LR
In[In] --> Router[LLM Call Router]
Router --> LLM1[LLM Call 1]
Router --> LLM2[LLM Call 2]
Router --> LLM3[LLM Call 3]
LLM1 --> Out[Out]
LLM2 --> Out
LLM3 --> Out
style In fill:#8B0000,color:#fff
style Router fill:#2E8B57,color:#fff
style LLM1 fill:#2E8B57,color:#fff
style LLM2 fill:#2E8B57,color:#fff
style LLM3 fill:#2E8B57,color:#fff
style Out fill:#8B0000,color:#fff
```
#### Agentic Orchestrator Worker
Create AI agents that orchestrate and distribute tasks among specialized workers.
```mermaid
flowchart LR
In[In] --> Router[LLM Call Router]
Router --> LLM1[LLM Call 1]
Router --> LLM2[LLM Call 2]
Router --> LLM3[LLM Call 3]
LLM1 --> Synthesizer[Synthesizer]
LLM2 --> Synthesizer
LLM3 --> Synthesizer
Synthesizer --> Out[Out]
style In fill:#8B0000,color:#fff
style Router fill:#2E8B57,color:#fff
style LLM1 fill:#2E8B57,color:#fff
style LLM2 fill:#2E8B57,color:#fff
style LLM3 fill:#2E8B57,color:#fff
style Synthesizer fill:#2E8B57,color:#fff
style Out fill:#8B0000,color:#fff
```
#### Agentic Autonomous Workflow
Create AI agents that can autonomously monitor, act, and adapt based on environment feedback.
```mermaid
flowchart LR
Human[Human] <--> LLM[LLM Call]
LLM -->|ACTION| Environment[Environment]
Environment -->|FEEDBACK| LLM
LLM --> Stop[Stop]
style Human fill:#8B0000,color:#fff
style LLM fill:#2E8B57,color:#fff
style Environment fill:#8B0000,color:#fff
style Stop fill:#333,color:#fff
```
#### Agentic Parallelization
Create AI agents that can execute tasks in parallel for improved performance.
```mermaid
flowchart LR
In[In] --> LLM2[LLM Call 2]
In --> LLM1[LLM Call 1]
In --> LLM3[LLM Call 3]
LLM1 --> Aggregator[Aggregator]
LLM2 --> Aggregator
LLM3 --> Aggregator
Aggregator --> Out[Out]
style In fill:#8B0000,color:#fff
style LLM1 fill:#2E8B57,color:#fff
style LLM2 fill:#2E8B57,color:#fff
style LLM3 fill:#2E8B57,color:#fff
style Aggregator fill:#fff,color:#000
style Out fill:#8B0000,color:#fff
```
#### Agentic Prompt Chaining
Create AI agents with sequential prompt chaining for complex workflows.
```mermaid
flowchart LR
In[In] --> LLM1[LLM Call 1] --> Gate{Gate}
Gate -->|Pass| LLM2[LLM Call 2] -->|Output 2| LLM3[LLM Call 3] --> Out[Out]
Gate -->|Fail| Exit[Exit]
style In fill:#8B0000,color:#fff
style LLM1 fill:#2E8B57,color:#fff
style LLM2 fill:#2E8B57,color:#fff
style LLM3 fill:#2E8B57,color:#fff
style Out fill:#8B0000,color:#fff
style Exit fill:#8B0000,color:#fff
```
#### Agentic Evaluator Optimizer
Create AI agents that can generate and optimize solutions through iterative feedback.
```mermaid
flowchart LR
In[In] --> Generator[LLM Call Generator]
Generator -->|SOLUTION| Evaluator[LLM Call Evaluator] -->|ACCEPTED| Out[Out]
Evaluator -->|REJECTED + FEEDBACK| Generator
style In fill:#8B0000,color:#fff
style Generator fill:#2E8B57,color:#fff
style Evaluator fill:#2E8B57,color:#fff
style Out fill:#8B0000,color:#fff
```
#### Repetitive Agents
Create AI agents that can efficiently handle repetitive tasks through automated loops.
```mermaid
flowchart LR
In[Input] --> LoopAgent[("Looping Agent")]
LoopAgent --> Task[Task]
Task --> |Next iteration| LoopAgent
Task --> |Done| Out[Output]
style In fill:#8B0000,color:#fff
style LoopAgent fill:#2E8B57,color:#fff,shape:circle
style Task fill:#2E8B57,color:#fff
style Out fill:#8B0000,color:#fff
```
<br />
<div className="relative w-full aspect-video overflow-hidden rounded-lg shadow-lg mb-8">
<iframe
className="absolute top-0 left-0 w-full h-full border-0"
src="https://www.youtube.com/embed/Fn1lQjC0GO0"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
style={{
maxWidth: '100vw',
margin: '0 auto'
}}
></iframe>
</div>
## Integration Options
<AccordionGroup>
@@ -338,4 +613,29 @@ seo:
<div className="hover:opacity-80 transition-opacity">
<img src="https://img.shields.io/github/forks/MervinPraison/PraisonAI?style=social" alt="GitHub Forks" />
</div>
</div>
</div>
## Video Tutorials
| Topic | Video |
|-------|--------|
| AI Agents with Self Reflection | [![Self Reflection](https://img.youtube.com/vi/vLXobEN2Vc8/0.jpg)](https://www.youtube.com/watch?v=vLXobEN2Vc8) |
| AI Agents with Reasoning | [![Reasoning](https://img.youtube.com/vi/KNDVWGN3TpM/0.jpg)](https://www.youtube.com/watch?v=KNDVWGN3TpM) |
| Multimodal AI Agents | [![Multimodal](https://img.youtube.com/vi/hjAWmUT1qqY/0.jpg)](https://www.youtube.com/watch?v=hjAWmUT1qqY) |
| AI Agents Workflow | [![Workflow](https://img.youtube.com/vi/yWTH44QPl2A/0.jpg)](https://www.youtube.com/watch?v=yWTH44QPl2A) |
| Async AI Agents | [![Async](https://img.youtube.com/vi/VhVQfgo00LE/0.jpg)](https://www.youtube.com/watch?v=VhVQfgo00LE) |
| Mini AI Agents | [![Mini](https://img.youtube.com/vi/OkvYp5aAGSg/0.jpg)](https://www.youtube.com/watch?v=OkvYp5aAGSg) |
| AI Agents with Memory | [![Memory](https://img.youtube.com/vi/1hVfVxvPnnQ/0.jpg)](https://www.youtube.com/watch?v=1hVfVxvPnnQ) |
| Repetitive Agents | [![Repetitive](https://img.youtube.com/vi/dAYGxsjDOPg/0.jpg)](https://www.youtube.com/watch?v=dAYGxsjDOPg) |
| Introduction | [![Introduction](https://img.youtube.com/vi/Fn1lQjC0GO0/0.jpg)](https://www.youtube.com/watch?v=Fn1lQjC0GO0) |
| Tools Overview | [![Tools Overview](https://img.youtube.com/vi/XaQRgRpV7jo/0.jpg)](https://www.youtube.com/watch?v=XaQRgRpV7jo) |
| Custom Tools | [![Custom Tools](https://img.youtube.com/vi/JSU2Rndh06c/0.jpg)](https://www.youtube.com/watch?v=JSU2Rndh06c) |
| Firecrawl Integration | [![Firecrawl](https://img.youtube.com/vi/UoqUDcLcOYo/0.jpg)](https://www.youtube.com/watch?v=UoqUDcLcOYo) |
| User Interface | [![UI](https://img.youtube.com/vi/tg-ZjNl3OCg/0.jpg)](https://www.youtube.com/watch?v=tg-ZjNl3OCg) |
| Crawl4AI Integration | [![Crawl4AI](https://img.youtube.com/vi/KAvuVUh0XU8/0.jpg)](https://www.youtube.com/watch?v=KAvuVUh0XU8) |
| Chat Interface | [![Chat](https://img.youtube.com/vi/sw3uDqn2h1Y/0.jpg)](https://www.youtube.com/watch?v=sw3uDqn2h1Y) |
| Code Interface | [![Code](https://img.youtube.com/vi/_5jQayO-MQY/0.jpg)](https://www.youtube.com/watch?v=_5jQayO-MQY) |
| Mem0 Integration | [![Mem0](https://img.youtube.com/vi/KIGSgRxf1cY/0.jpg)](https://www.youtube.com/watch?v=KIGSgRxf1cY) |
| Training | [![Training](https://img.youtube.com/vi/aLawE8kwCrI/0.jpg)](https://www.youtube.com/watch?v=aLawE8kwCrI) |
| Realtime Voice Interface | [![Realtime](https://img.youtube.com/vi/frRHfevTCSw/0.jpg)](https://www.youtube.com/watch?v=frRHfevTCSw) |
| Call Interface | [![Call](https://img.youtube.com/vi/m1cwrUG2iAk/0.jpg)](https://www.youtube.com/watch?v=m1cwrUG2iAk) |
+99 -121
View File
@@ -89,143 +89,121 @@ graph LR
## Getting Started is Easy
<Tabs>
<Tab title="Code">
<Steps>
<Steps>
<Step title="Install Package">
Install the PraisonAI Agents package:
```bash
pip install praisonaiagents
```
</Step>
<Step title="Install PraisonAI">
Install the core package:
```bash Terminal
pip install praisonaiagents
```
</Step>
<Step title="Set API Key">
```bash
export OPENAI_API_KEY=your_openai_key
```
Generate your OpenAI API key from [OpenAI](https://platform.openai.com/api-keys).
Use other LLM providers like Ollama, Anthropic, Groq, Google, etc. Please refer to the [Models](/models) for more information.
</Step>
<Step title="Configure Environment">
```bash Terminal
export OPENAI_API_KEY=your_openai_key
```
Generate your OpenAI API key from [OpenAI](https://platform.openai.com/api-keys)
Use other LLM providers like Ollama, Anthropic, Groq, Google, etc. Please refer to the [Models](/models) for more information.
</Step>
<Step title="Create Agents">
Create `app.py`:
<CodeGroup>
```python Single Agent
from praisonaiagents import Agent, PraisonAIAgents
<Step title="Create Agent">
Create `app.py`:
<CodeGroup>
```python Single Agent
from praisonaiagents import Agent, Task, PraisonAIAgents
# Create a simple agent
summarise_agent = Agent(instructions="Summarise Photosynthesis")
# Create an agent
researcher = Agent(
name="Researcher",
role="Senior Research Analyst",
goal="Uncover cutting-edge developments in AI",
backstory="You are an expert at a technology research group",
verbose=True,
llm="gpt-4o"
)
# Run the agent
agents = PraisonAIAgents(agents=[summarise_agent])
agents.start()
```
# Define a task
task = Task(
name="research_task",
description="Analyze 2024's AI advancements",
expected_output="A detailed report",
agent=researcher
)
```python Multiple Agents
from praisonaiagents import Agent, PraisonAIAgents
# Run the agents
agents = PraisonAIAgents(
agents=[researcher],
tasks=[task],
verbose=False
)
# Create agents with specific roles
diet_agent = Agent(
instructions="Give me 5 healthy food recipes",
)
result = agents.start()
```
blog_agent = Agent(
instructions="Write a blog post about the food recipes",
)
```python Multiple Agents
from praisonaiagents import Agent, Task, PraisonAIAgents
# Run multiple agents
agents = PraisonAIAgents(agents=[diet_agent, blog_agent])
agents.start()
```
</CodeGroup>
</Step>
# Create multiple agents
researcher = Agent(
name="Researcher",
role="Senior Research Analyst",
goal="Uncover cutting-edge developments in AI",
backstory="You are an expert at a technology research group",
verbose=True,
llm="gpt-4o",
markdown=True
)
<Step title="Run Agents">
Execute your script:
```bash
python app.py
```
writer = Agent(
name="Writer",
role="Tech Content Strategist",
goal="Craft compelling content on tech advancements",
backstory="You are a content strategist",
llm="gpt-4o",
markdown=True
)
# Define multiple tasks
task1 = Task(
name="research_task",
description="Analyze 2024's AI advancements",
expected_output="A detailed report",
agent=researcher
)
task2 = Task(
name="writing_task",
description="Create a blog post about AI advancements",
expected_output="A blog post",
agent=writer
)
# Run with hierarchical process
agents = PraisonAIAgents(
agents=[researcher, writer],
tasks=[task1, task2],
verbose=False,
process="hierarchical",
manager_llm="gpt-4o"
)
result = agents.start()
```
</CodeGroup>
</Step>
<Step title="Start Agents">
Execute your script:
```bash Terminal
python app.py
```
</Step>
</Steps>
</Tab>
You'll see:
- Agent initialization
- Task execution progress
- Final results
<Tip>
You have successfully CreatedAI Agents and made them work for you!
</Tip>
</Step>
</Steps>
</Tab>
<Tab title="No Code">
<Steps>
<Step title="Install Package">
Install the No Code PraisonAI Package:
```bash
pip install praisonaiagents
```
</Step>
<Step title="Install PraisonAI">
Install the PraisonAI package:
```bash
pip install praisonai
```
</Step>
<Step title="Set API Key">
```bash
export OPENAI_API_KEY=your_openai_key
```
</Step>
<Step title="Set API Key">
Set your OpenAI API key as an environment variable in your terminal:
```bash Terminal
export OPENAI_API_KEY=your_openai_key
```
Generate your OpenAI API key from [OpenAI](https://platform.openai.com/api-keys)
Use other LLM providers like Ollama, Anthropic, Groq, Google, etc. Please refer to the [Models](/models) for more information.
</Step>
<Step title="Create Config">
Create `agents.yaml`:
<Step title="Start AutoAgents">
Run your AutoAgents:
```bash
praisonai --auto "Create a movie script about a robot in Mars"
```
</Step>
<CodeGroup>
```yaml Single Agent
roles:
summarise_agent:
instructions: Summarise Photosynthesis
```
```yaml Multiple Agents
roles:
diet_agent:
instructions: Give me 5 healthy food recipes
blog_agent:
instructions: Write a blog post about the food recipes
```
</CodeGroup>
<Note>
You can automatically create `agents.yaml` using:
```bash
praisonai --init "your task description"
```
</Note>
</Step>
<Step title="Run Agents">
Execute your config:
```bash
praisonai agents.yaml
```
</Step>
</Steps>
</Tab>
</Tabs>
+1
View File
@@ -118,6 +118,7 @@
"features/autoagents",
"features/selfreflection",
"features/rag",
"features/reasoning-extract",
"features/reasoning",
"features/multimodal",
"features/langchain",
+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.57.tar.gz"
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/2.0.59.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.57 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.0.59 gunicorn markdown\n")
file.write("EXPOSE 8080\n")
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
+4 -4
View File
@@ -1,6 +1,6 @@
[project]
name = "PraisonAI"
version = "2.0.57"
version = "2.0.59"
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.50",
"praisonaiagents>=0.0.56",
"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.57"
version = "2.0.59"
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.50"
praisonaiagents = ">=0.0.56"
python-dotenv = ">=0.19.0"
instructor = ">=1.3.3"
PyYAML = ">=6.0"
Generated
+5 -5
View File
@@ -3060,7 +3060,7 @@ wheels = [
[[package]]
name = "praisonai"
version = "2.0.57"
version = "2.0.59"
source = { editable = "." }
dependencies = [
{ name = "instructor" },
@@ -3197,7 +3197,7 @@ requires-dist = [
{ name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" },
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.7" },
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.7" },
{ name = "praisonaiagents", specifier = ">=0.0.50" },
{ name = "praisonaiagents", specifier = ">=0.0.56" },
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = ">=0.2.19" },
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
@@ -3250,16 +3250,16 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.50"
version = "0.0.56"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "openai" },
{ name = "pydantic" },
{ name = "rich" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e9/44/52c6e7a0c8bef2cc861e35206c0a5fee350b07134d0c4745dfb62ddfc09d/praisonaiagents-0.0.50.tar.gz", hash = "sha256:3cb6447422a76aff6846df6047e885c7552516ec6a36321bb0c0f663dfcffa24", size = 85120 }
sdist = { url = "https://files.pythonhosted.org/packages/1f/d0/5b3ee6832c4797cb6dd47496485789e13113014d501856c2b5b286e7108c/praisonaiagents-0.0.56.tar.gz", hash = "sha256:148b6400403a7ad05b105084640e7140799e4e3bfb23a844bb79e10ea98bf859", size = 88236 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/96/b5/5433a5611a0798063fd1e3fdbd74f90171f2a9b593adfe448865b30b8c8b/praisonaiagents-0.0.50-py3-none-any.whl", hash = "sha256:3a0657d4bfd1ad70d319ab741b87e9b81b0ffd7aa57499afdc9bd914bdf7f4cd", size = 103242 },
{ url = "https://files.pythonhosted.org/packages/1a/7e/1da42b51510d3bef28f4d450824d7d14fdd2358981adf7c5b8251e586526/praisonaiagents-0.0.56-py3-none-any.whl", hash = "sha256:b198c60a8909c39b2ced09e0c7cc1ad6224af15b8c743f6890ef3985fae4d036", size = 106340 },
]
[[package]]