Compare commits

...
42 Commits
Author SHA1 Message Date
Mervin Praison 9f62e9201a Merge pull request #339 from MervinPraison/develop
2.0.60
2025-01-29 05:59:25 +00:00
MervinPraison 1b69360d9f Bump PraisonAI version to 2.0.60 and update dependencies
- Updated version to 2.0.60 in Homebrew formula, pyproject.toml, and Docker files
- Upgraded praisonaiagents dependency from 0.0.56 to 0.0.60
- Added new documentation for generating reasoning data
- Included example script for chain-of-thought data generation
- Enhanced workflow task management and logging in process modules

The changes improve the framework's version management, documentation, and provide new tools for synthetic reasoning data generation.
2025-01-29 05:58:58 +00:00
MervinPraison 9bdc6545ad Enhance Workflow Task Management with Workflow Finished Flag and Improved Routing
- Added `workflow_finished` flag to `Process` class to enable early workflow termination
- Implemented more robust task routing logic in `process.py` for decision and loop tasks
- Updated task reset mechanism to respect `rerun` parameter
- Improved context clearing and task status management
- Enhanced logging for workflow progression and task transitions

The changes provide more flexible and controlled workflow execution, with better handling of task conditions and early termination scenarios.
2025-01-28 16:40:22 +00:00
MervinPraison 06cd909367 Add max_iter parameter and enhanced workflow task management with fallback mechanism
- Updated `orchestrator-workers.py` to include `max_iter=10` parameter in workflow initialization
- Enhanced `process.py` with a new `_find_next_not_started_task()` method to provide fallback task selection
- Implemented comprehensive logging for workflow cycles, task status, and iteration tracking
- Added retry counter and fallback mechanism to handle scenarios with incomplete or stalled tasks
- Improved workflow termination logic with detailed final summary logging

The changes improve workflow reliability, prevent potential infinite loops, and provide more robust task management in complex multi-agent scenarios.
2025-01-28 11:26:32 +00:00
MervinPraison 67aeece7fc Bump praisonaiagents package version to 0.0.58
- Updated version in `pyproject.toml` and `uv.lock`
- Incremented package version for continued development and improvements
2025-01-27 19:19:40 +00:00
MervinPraison ef67d0753c Enhance Workflow Task Management with Detailed Logging and Loop Task Handling
- Updated `.cursorrules` with comprehensive technical overview of workflow execution challenges
- Refactored `cot-agents.py` to:
  - Add Pydantic model for structured output
  - Improve task description and condition handling
  - Add output validation for chain of thought generation
- Enhanced `process.py` with:
  - Extensive debug logging for task execution
  - Improved loop task and subtask management
  - More detailed logging of task status and progression

The changes improve workflow reliability, provide deeper insights into task execution, and prevent infinite loops in complex task workflows.
2025-01-27 19:18:49 +00:00
MervinPraison f374f9f791 Enhance COT Agent Workflow with Improved Task Management and Logging
- Refactored `cot-agents.py` to include:
  - New `write_csv` and `count_questions` functions
  - Added `TotalQuestionsEvaluator` agent for tracking question count
  - Improved task workflow with decision and loop task types
- Updated `process.py` to support:
  - Enhanced logging for task execution
  - Better handling of loop and decision tasks
  - Improved subtask creation and management
- Modified `csv_tools.py` to support more flexible CSV writing with string input
- Implemented more robust task progression and status tracking

The changes improve the flexibility, logging, and task management of the Chain of Thought agent workflow.
2025-01-27 13:59:59 +00:00
MervinPraison 492fb29af9 Enhance Chain of Thought (COT) agent workflow with batch processing and Hugging Face integration
- Added `cot-agents-batch.py` to support batch processing of Q&A pairs
- Updated `cot-agents.py` with a more comprehensive workflow including:
  - Question generation
  - Chain of Thought solution generation
  - Hugging Face dataset upload
- Refined `generatecot.py` with improvements to solution generation:
  - Reduced max attempts
  - Adjusted scoring thresholds
  - Added direct QA pair tracking

The changes improve the flexibility and automation of Chain of Thought solution generation and dataset management.
2025-01-27 02:52:14 +00:00
MervinPraison cd56ebd5cf Add Chain of Thought (COT) generation tools and update project structure
- Introduced comprehensive `generatecot.py` in `praisonaiagents/tools/train/data/` with advanced Chain of Thought solution generation capabilities
- Added multiple COT-related tools and methods:
  - Solution generation and improvement
  - Answer verification
  - Error detection and correction
  - Export to various formats (JSON, CSV, Alpaca)
  - HuggingFace dataset upload support
- Updated tool mappings in `__init__.py` to include COT-related tools
- Moved existing `generatecot.py` from `train/data/` to `tools/train/data/`
- Bumped project version to 0.0.57 in relevant files

The changes enhance the framework's capabilities for generating and managing step-by-step problem-solving solutions with AI-powered reasoning.
2025-01-26 07:22:32 +00:00
MervinPraison 08e99f6ef7 Enhance GenerateCOT with dictionary-based solution generation and Alpaca format export
- Added new methods to generate solutions in dictionary format:
  - `get_solution_dict()` for step-by-step problem solving
  - `improve_solution_dict()` to refine existing solutions
  - `start_dict()` to generate and validate solutions
- Implemented `export_alpaca_format()` to convert solutions to Alpaca training format
- Updated type hints and added example usage for new methods
- Extended the script with additional QA data processing and export demonstration

The changes improve the flexibility and usability of the GenerateCOT class for solution generation and data preparation.
2025-01-25 08:22:08 +00:00
MervinPraison 670d653ae9 Add GenerateCOT class for step-by-step solution generation and verification
- Created a new Python script `generatecot.py` in the train/data directory
- Implemented a comprehensive solution generation class with features like:
  - Step-by-step problem solving
  - Answer verification
  - Solution improvement
  - Error detection and correction
- Added support for loading and saving solutions with JSON
- Included an example usage demonstrating the class functionality

The GenerateCOT (Chain of Thought) class provides a robust mechanism for generating and refining solutions to various problems using AI-powered reasoning.
2025-01-25 07:47:20 +00:00
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
Mervin Praison ef7fa66e33 Merge pull request #324 from MervinPraison/develop
Update PraisonAI to version 2.0.57 and enhance documentation
2025-01-23 06:06:43 +00:00
MervinPraison 1659ed61bc Update PraisonAI to version 2.0.57 and enhance documentation
- Bumped PraisonAI version from 2.0.56 to 2.0.57 in `Dockerfile`, `pyproject.toml`, and `uv.lock` to reflect the latest updates.
- Updated the `praisonaiagents` dependency version from `0.0.49` to `0.0.50` across relevant files for improved functionality.
- Added a new `docker-compose.yml` file and multiple Dockerfiles for different services (UI, chat, development) to streamline deployment.
- Enhanced the README.md with new sections on AI agents flow, tools, memory, and various processing types, improving clarity and usability.
- Introduced a new test script `cl-test.py` for validating chat functionalities with the Gemini API.

These changes improve the overall functionality, usability, and deployment processes of the PraisonAI framework.
2025-01-23 06:06:23 +00:00
Mervin Praison b8939ef439 Merge pull request #323 from MervinPraison/develop
adding chat with pdf
2025-01-22 18:37:48 +00:00
MervinPraison 4bada9e5fc adding chat with pdf 2025-01-22 18:37:27 +00:00
Mervin Praison 51c4cb00b0 Merge pull request #322 from MervinPraison/develop
updating docs
2025-01-22 18:32:20 +00:00
MervinPraison ad96549ea3 updating docs 2025-01-22 18:32:03 +00:00
Mervin Praison 183d10d7cc Merge pull request #321 from MervinPraison/develop
Develop
2025-01-22 18:29:04 +00:00
MervinPraison bce1559ab5 docs update 2025-01-22 18:28:46 +00:00
MervinPraison 6f3fb87ba3 Update README and local development documentation for improved clarity and logging instructions
- Revised section headings in README.md to number the agent categories for better organization.
- Added logging configuration instructions to the local-development.mdx file, detailing how to set log levels for both standard and advanced logging.
- Removed outdated logging instructions from README.md to streamline the documentation.

These changes enhance the clarity of the documentation and provide users with essential logging setup information for local development.
2025-01-22 18:24:24 +00:00
Mervin Praison e7f6b2df67 Merge pull request #320 from MervinPraison/develop
Update README and agent initialization for enhanced clarity and funct…
2025-01-22 18:07:29 +00:00
MervinPraison 08d72eeb80 Update README and agent initialization for enhanced clarity and functionality
- Revised the README.md to better describe PraisonAI as a production-ready Multi-AI Agents framework, emphasizing its capabilities in automating tasks and problem-solving.
- Updated the list of features to include new agent types and functionalities, such as Code Interpreter Agents and Async & Parallel Processing.
- Simplified the agent initialization examples in the README, providing clearer instructions for users to create and run agents.
- Modified the agent client initialization in main.py to handle missing API keys more gracefully.
- Added "developers/local-development" to mint.json for improved documentation structure.

These changes enhance the user experience by providing clearer documentation and improving the robustness of agent initialization.
2025-01-22 18:07:15 +00:00
Mervin Praison 11aefae90f Merge pull request #319 from MervinPraison/develop
Enhance Streamlit UIs and Add New Model Integrations
2025-01-22 13:07:45 +00:00
MervinPraison 10d5158d27 Enhance Streamlit UIs and Add New Model Integrations
- Updated `mint.json` to include new Streamlit UIs for Deepseek, Gemini, and Ollama models, improving model accessibility.
- Expanded documentation in `models.mdx` and `google.mdx` to provide detailed setup instructions for integrating OpenAI, Litellm, and Gemini models.
- Introduced new example scripts for Gemini and Ollama Streamlit applications, showcasing interactive chat capabilities.
- Added comprehensive features and prerequisites sections in the new UI documentation to guide users in setting up their environments.

These changes enhance the usability and functionality of the PraisonAI framework, particularly in model integration and user interface development.
2025-01-22 13:07:31 +00:00
62 changed files with 5703 additions and 1262 deletions

No files matched your search

+434 -443
View File
@@ -20,7 +20,7 @@
</div>
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.
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.
<div align="center">
<a href="https://docs.praison.ai">
@@ -37,35 +37,24 @@ PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application
- 🧠 Reasoning AI Agents
- 👁️ Multi Modal AI Agents
- 🤝 Multi Agent Collaboration
- ⚡ AI Agent Workflow
- 🔄 Use CrewAI or AutoGen Framework
- 🎭 AI Agent Workflow
- 📚 Add Custom Knowledge
- 🧠 Agents with Short and Long Term Memory
- 📄 Chat with PDF Agents
- 💻 Code Interpreter Agents
- 📚 RAG Agents
- 🤔 Async & Parallel Processing
- 🔄 Auto Agents
- 🔢 Math Agents
- 🎯 Structured Output Agents
- 🔗 LangChain Integrated Agents
- 📞 Callback Agents
- 🤏 Mini AI Agents
- 🛠️ 100+ Custom Tools
- 📄 YAML Configuration
- 💯 100+ LLM Support
- 💻 Chat with ENTIRE Codebase
- 🎨 Interactive UIs
- 📄 YAML-based Configuration
- 🛠️ Custom Tool Integration
- 🔍 Internet Search Capability (using Crawl4AI and Tavily)
- 🖼️ Vision Language Model (VLM) Support
- 🎙️ Real-time Voice Interaction
## Using No Code
### Auto Mode:
```bash
pip install praisonai
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
praisonai --auto create a movie script about Robots in Mars
```
### Initialise Mode:
```bash
pip install praisonai
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
praisonai --init create a movie script about Robots in Mars
praisonai
```
## Using Coding
## Using Code
Light weight package dedicated for coding:
```bash
@@ -76,58 +65,13 @@ pip install praisonaiagents
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
```
### 1. Single Agent
Create app.py file and add the code below:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
# 1. Create agents
researcher = Agent(
name="Researcher",
role="Senior Research Analyst",
goal="Uncover cutting-edge developments in AI and data science",
backstory="""You are an expert at a technology research group,
skilled in identifying trends and analyzing complex data.""",
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 known for
making complex tech topics interesting and easy to understand.""",
llm="gpt-4o",
markdown=True
)
# 2. Define Tasks
task1 = Task(
name="research_task",
description="""Analyze 2024's AI advancements.
Find major trends, new technologies, and their effects.""",
expected_output="""A detailed report on 2024 AI advancements""",
agent=researcher
)
task2 = Task(
name="writing_task",
description="""Create a blog post about major AI advancements using the insights you have.
Make it interesting, clear, and suited for tech enthusiasts.
It should be at least 4 paragraphs long.""",
expected_output="A blog post of at least 4 paragraphs",
agent=writer,
)
agents = PraisonAIAgents(
agents=[researcher, writer],
tasks=[task1, task2],
verbose=False,
process="hierarchical",
manager_llm="gpt-4o"
)
result = agents.start()
from praisonaiagents import Agent
agent = Agent(instructions="Your are a helpful AI assistant")
agent.start("Write a movie script about a robot in Mars")
```
Run:
@@ -135,6 +79,381 @@ Run:
python app.py
```
### 2. Multi Agents
Create app.py file and add the code below:
```python
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()
```
Run:
```bash
python app.py
```
## Using No Code
### Auto Mode:
```bash
pip install praisonai
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
praisonai --auto create a movie script about Robots in Mars
```
## 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
```
## Adding Models
<div align="center">
<a href="https://docs.praison.ai/models">
<p align="center">
<img src="https://img.shields.io/badge/%F0%9F%93%9A_Models-Visit_docs.praison.ai-blue?style=for-the-badge&logo=bookstack&logoColor=white" alt="Models" />
</p>
</a>
</div>
## Ollama Integration
```bash
export OPENAI_BASE_URL=http://localhost:11434/v1
@@ -147,267 +466,16 @@ export OPENAI_API_KEY=xxxxxxxxxxx
export OPENAI_BASE_URL=https://api.groq.com/openai/v1
```
## Logging
```bash
export LOGLEVEL=info
```
Advanced logging:
```bash
export LOGLEVEL=debug
```
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="docs/images/architecture-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="docs/images/architecture-light.png" />
<img alt="PraisonAI Architecture" src="docs/images/architecture-light.png" />
</picture>
</div>
## Different User Interfaces:
| Interface | Description | URL |
|---|---|---|
| **UI** | Multi Agents such as CrewAI or AutoGen | [https://docs.praison.ai/ui/ui](https://docs.praison.ai/ui/ui) |
| **Chat** | Chat with 100+ LLMs, single AI Agent | [https://docs.praison.ai/ui/chat](https://docs.praison.ai/ui/chat) |
| **Code** | Chat with entire Codebase, single AI Agent | [https://docs.praison.ai/ui/code](https://docs.praison.ai/ui/code) |
| **Realtime** | Real-time voice interaction with AI | [https://docs.praison.ai/ui/realtime](https://docs.praison.ai/ui/realtime) |
| Other Features | Description | Docs |
|---|---|---|
| **Train** | Fine-tune LLMs using your custom data | [https://docs.praison.ai/train](https://docs.praison.ai/train) |
## Google Colab Multi Agents
| | Cookbook | Open in Colab |
| ------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Basic | PraisonAI | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" /></a> |
| Include Tools | PraisonAI Tools | <a target="_blank" href="https://colab.research.google.com/github/MervinPraison/PraisonAI/blob/main/cookbooks/praisonai-tools-googlecolab.ipynb"><img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab" /></a> |
## Installation Options
### Using pip
```bash
pip install praisonai
```
### Framework-specific Installation
```bash
# Install with CrewAI support
pip install "praisonai[crewai]"
# Install with AutoGen support
pip install "praisonai[autogen]"
# Install with both frameworks
pip install "praisonai[crewai,autogen]"
```
### UI and Additional Features
```bash
# Install UI support
pip install "praisonai[ui]"
# Install Chat interface
pip install "praisonai[chat]"
# Install Code interface
pip install "praisonai[code]"
# Install Realtime voice interaction
pip install "praisonai[realtime]"
# Install Call feature
pip install "praisonai[call]"
```
## Quick Start
```bash
# Set your OpenAI API key
export OPENAI_API_KEY="Enter your API key"
# Initialize with CrewAI (default)
praisonai --init "create a movie script about dog in moon"
# Or initialize with AutoGen
praisonai --framework autogen --init "create a movie script about dog in moon"
# Run the agents
praisonai
```
## Full Automatic Mode
```bash
# With CrewAI (default)
praisonai --auto "create a movie script about Dog in Moon"
# With AutoGen
praisonai --framework autogen --auto "create a movie script about Dog in Moon"
```
## Framework-specific Features
### CrewAI
When installing with `pip install "praisonai[crewai]"`, you get:
- CrewAI framework support
- PraisonAI tools integration
- Task delegation capabilities
- Sequential and parallel task execution
### AutoGen
When installing with `pip install "praisonai[autogen]"`, you get:
- AutoGen framework support
- PraisonAI tools integration
- Multi-agent conversation capabilities
- Code execution environment
## TL;DR Multi Agents
```bash
pip install praisonai
export OPENAI_API_KEY="Enter your API key"
praisonai --init create a movie script about dog in moon
praisonai
```
## Table of Contents
- [Installation](#installation)
- [Initialise](#initialise)
- [Run](#run)
- [Full Automatic Mode](#full-automatic-mode)
- [User Interface](#user-interface)
- [Praison AI Chat](#praison-ai-chat)
- [Create Custom Tools](#create-custom-tools)
- [Agents Playbook](#agents-playbook)
- [Include praisonai package in your project](#include-praisonai-package-in-your-project)
- [Commands to Install Dev Dependencies](#commands-to-install-dependencies)
- [Other Models](#other-models)
- [Contributing](#contributing)
- [Star History](#star-history)
## Installation Multi Agents
```bash
pip install praisonai
```
## Initialise
```bash
export OPENAI_API_KEY="Enter your API key"
```
Generate your OPENAI API KEY from here: https://platform.openai.com/api-keys
Note: You can use other providers such as Ollama, Mistral ... etc. Details are provided at the bottom.
```bash
praisonai --init create a movie script about dog in moon
```
This will automatically create agents.yaml file in the current directory.
### To initialise with a specific agent framework (Optional):
```bash
praisonai --framework autogen --init create movie script about cat in mars
```
## Run
```bash
praisonai
```
or
```bash
python -m praisonai
```
### Specify the agent framework (Optional):
```bash
praisonai --framework autogen
```
### Full Automatic Mode
```bash
praisonai --auto create a movie script about Dog in Moon
```
## User Interface
## PraisonAI User Interfaces:
| Interface | Description | URL |
| --------- | ------------------------------------------ | --------------------------------------------------------------------- |
| **UI** | Multi Agents such as CrewAI or AutoGen | [https://docs.praisonai.com/ui/ui](https://docs.praison.ai/ui/ui) |
| **Chat** | Chat with 100+ LLMs, single AI Agent | [https://docs.praisonai.com/ui/chat](https://docs.praison.ai/ui/chat) |
| **Code** | Chat with entire Codebase, single AI Agent | [https://docs.praisonai.com/ui/code](https://docs.praison.ai/ui/code) |
```bash
pip install -U "praisonai[ui]"
export OPENAI_API_KEY="Enter your API key"
chainlit create-secret
export CHAINLIT_AUTH_SECRET=xxxxxxxx
praisonai ui
```
or
```
python -m praisonai ui
```
## Praison AI Chat
- https://docs.praison.ai/chat/
```bash
pip install "praisonai[chat]"
export OPENAI_API_KEY="Enter your API key"
praisonai chat
```
### Internet Search
Praison AI Chat and Praison AI Code now includes internet search capabilities using Crawl4AI and Tavily, allowing you to retrieve up-to-date information during your conversations.
### Vision Language Model Support
You can now upload images and ask questions based on them using Vision Language Models. This feature enables visual understanding and analysis within your chat sessions.
## Praison AI Code
```bash
pip install "praisonai[code]"
export OPENAI_API_KEY="Enter your API key"
praisonai code
```
### Internet Search
Praison AI Code also includes internet search functionality, enabling you to find relevant code snippets and programming information online.
## Create Custom Tools
- https://docs.praison.ai/tools/custom/
## No Code Options
## Agents Playbook
### Simple Playbook Example
Create `agents.yaml` file and add the code below:
```yaml
framework: crewai
framework: praisonai
topic: Artificial Intelligence
roles:
screenwriter:
@@ -420,62 +488,25 @@ roles:
expected_output: "Complete script ready for production."
```
*To run the playbook:*
```bash
praisonai agents.yaml
```
## Use 100+ Models
- https://docs.praison.ai/models/
<div align="center">
<a href="https://docs.praison.ai">
<p align="center">
<img src="https://img.shields.io/badge/📚_Documentation-Visit_docs.praison.ai-blue?style=for-the-badge&logo=bookstack&logoColor=white" alt="Documentation" />
</p>
</a>
</div>
## Include praisonai package in your project
## Development:
- https://docs.praison.ai/developers/wrapper
- https://docs.praison.ai/developers/wrapper-tools/
## Option 1: Using RAW YAML
```python
from praisonai import PraisonAI
# Example agent_yaml content
agent_yaml = """
framework: "crewai"
topic: "Space Exploration"
roles:
astronomer:
role: "Space Researcher"
goal: "Discover new insights about {topic}"
backstory: "You are a curious and dedicated astronomer with a passion for unraveling the mysteries of the cosmos."
tasks:
investigate_exoplanets:
description: "Research and compile information about exoplanets discovered in the last decade."
expected_output: "A summarized report on exoplanet discoveries, including their size, potential habitability, and distance from Earth."
"""
# Create a PraisonAI instance with the agent_yaml content
praisonai = PraisonAI(agent_yaml=agent_yaml)
# Run PraisonAI
result = praisonai.run()
# Print the result
print(result)
```
## Option 2: Using separate agents.yaml file
Note: Please create agents.yaml file before hand.
```python
from praisonai import PraisonAI
def basic(): # Basic Mode
praisonai = PraisonAI(agent_file="agents.yaml")
praisonai.run()
if __name__ == "__main__":
basic()
```
## Commands to Install Dependencies:
Below is used for development only.
### Using uv
```bash
@@ -500,18 +531,33 @@ uv pip install -r pyproject.toml --extra "crewai,autogen"
- Submit a pull request via GitHub's web interface.
- Await feedback from project maintainers.
## Other Features
- 🔄 Use CrewAI or AutoGen Framework
- 💻 Chat with ENTIRE Codebase
- 🎨 Interactive UIs
- 📄 YAML-based Configuration
- 🛠️ Custom Tool Integration
- 🔍 Internet Search Capability (using Crawl4AI and Tavily)
- 🖼️ Vision Language Model (VLM) Support
- 🎙️ Real-time Voice Interaction
## Star History
[![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) |
@@ -525,58 +571,3 @@ Praison AI is an open-sourced software licensed under the **[MIT license](https:
| 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) |
## License
Praison AI is an open-sourced software licensed under the **[MIT license](https://opensource.org/licenses/MIT)**.
## Local Docker Development with Live Reload
To facilitate local development with live reload, you can use Docker. Follow the steps below:
1. **Create a `Dockerfile.dev`**:
```dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask praisonai==2.0.18 watchdog
EXPOSE 5555
ENV FLASK_ENV=development
CMD ["flask", "run", "--host=0.0.0.0"]
```
2. **Create a `docker-compose.yml`**:
```yaml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app
ports:
- "5555:5555"
environment:
FLASK_ENV: development
command: flask run --host=0.0.0.0
watch:
image: alpine:latest
volumes:
- .:/app
command: sh -c "apk add --no-cache inotify-tools && while inotifywait -r -e modify,create,delete /app; do kill -HUP 1; done"
```
3. **Run Docker Compose**:
```bash
docker-compose up
```
This setup will allow you to develop locally with live reload, making it easier to test and iterate on your code.
+143 -1
View File
@@ -5,4 +5,146 @@
5. Make it minimal change as possible
6. Firstly try not to make any modification to the existing code as possible
7. Only modify the existing code if its highly required, without that if it cant be done, then add new code section.
8. If you are adding new code, make sure to add it in a way that it can be easily integrated with the existing codebase.
8. If you are adding new code, make sure to add it in a way that it can be easily integrated with the existing codebase.
Below is a **detailed technical overview** of the issues that have been coming up in the workflow execution (specifically around loops, resetting tasks, and continuing on to subsequent tasks). It includes:
1. **How the workflow currently flows**
2. **What logic marks a task as “completed”**
3. **What issues arose in `loop` tasks**
4. **Why the workflow can end up “stuck” or “looping indefinitely”**
---
## 1. Overall Workflow Flow
### a. Building relationships among tasks
- At startup, the code iterates through all `tasks`.
- For each `task`:
- It looks at `task.next_tasks`, and for each `next_task_name`:
- Finds the corresponding `Task` object
- Appends the current task’s name to the found `next_task`'s `previous_tasks` list.
- This means if Task A has `next_tasks=["B"]`, then Task B’s `previous_tasks` will include `"A"`.
### b. Finding and starting with a “start task”
- The workflow code tries to locate a task with `is_start=True`.
- If no such task is found, it uses the first item in the tasks dictionary instead.
- That “start task” is what the workflow tries to run first.
### c. Execution loop in the method (e.g., `workflow()` or `aworkflow()`)
- There is a `while current_task:` loop that processes tasks in sequence, or conditionally, based on `task.condition`.
- Each time a task runs (if non-loop), it yields the `task_id` or triggers an agent to run. Once the agent finishes (with or without a final result), the workflow picks up again to see what to do next:
- If the `task` is a `loop` type, the code tries to create or manage sub-tasks for each row/line in an “input_file.”
- If the `task` is a normal (“decision” or “task” or “some-other-type”), it just executes once, sets `status="completed"`, and the code moves on.
### d. Condition-based branching
- If a task has a result that includes a “decision” (like `{"decision":"more"}` or `"done"`), the code checks `task.condition`. For example:
```python
condition = {
"more": "generate_task",
"done": "evaluate_total_questions"
}
```
- If the result’s decision is `"done"`, it jumps to the task named `"evaluate_total_questions"`.
- If the result’s decision is `"more"`, it jumps right back to `"generate_task"`.
- If no condition matches, it can fallback to the first item in `task.next_tasks`.
### e. Marking tasks as “completed”
- After a task’s execution (like a typical “non-loop” task), the code sets `task.status = "completed"`.
- Then the code has a snippet that says:
```python
if self.tasks[task_id].status == "completed":
# Possibly reset to "not started" so we can re-run if needed
```
- By default, the system tries to “reset” tasks to `"not started"`, **unless** it is a loop task or a subtask of a loop.
---
## 2. How a “completed” task is marked
Generally, tasks are marked `status="completed"` in two primary ways:
1. **Non-Loop Execution**
A normal task (like a “decision” or “task”) is executed once the code calls the agent, the agent returns a final result, and the system sets `task.status = "completed"`.
2. **Loop Execution**
A loop-type task is *programmatically* set to `status="completed"` when all of its sub-tasks have finished. That is:
- The code checks: “Have all child tasks of this loop finished?”
- If `True`, the loop task is set to `completed`.
---
## 3. Issues Specifically in `loop` Tasks
### a. Re-Entering the Loop
- Before, the same snippet that “resets completed tasks to ‘not started’ so they can re-run if needed” **also** tried to reset loop tasks or their subtasks.
- If a loop task got reset to `"not started"`, the code would eventually pick it back up again, leading to repeated creation of sub-tasks (or repeated attempts to re-run them).
- This caused an **infinite loop** or repeating the same steps in the workflow, never truly exiting the loop stage.
### b. Subtasks Not Marked or Overwritten
- Another tricky scenario: If sub-tasks themselves got reset, the parent loop would see them as “not started” again, and might wait for them to “complete,” or might re-run them. That can lead to indefinite re-running of sub-tasks.
### c. Not Proceeding to Next (e.g., “upload_to_huggingface”)
- If the loop kept “restarting,” the workflow never ended up hitting the next tasks. For example, if your workflow is:
1. `generate_task`
2. `evaluate_total_questions`
3. `generate_cot` (loop)
4. `upload_to_huggingface`
- The system might get stuck in step #3 indefinitely (the sub-tasks keep getting reset, so it never actually transitions to step #4).
### d. Condition Logic vs. Next Tasks
- Another subtlety: If the loop tasks had a `condition` that pointed them back to a prior step, it might cause unintentional re-entry. Typically, you only want loop tasks to proceed once, **unless** you explicitly want to re-visit. But if it’s a data ingestion process, you usually want to do it once, then move on to the next step.
---
## 4. Why the System Can End Up Stuck or “Looping Indefinitely”
1. **Reset Mechanism**
- The code tries to “reset tasks to ‘not started’ once they complete,” so they can be re-run in some dynamic multi-run scenario.
- But that same logic can cause loop tasks to revert back to “not started” the moment they end. The system sees “Oh, a task is ‘not started’? Let’s run it!” and you’re in a cycle.
2. **No Condition for Exit**
- If the loop has a condition that leads back to a prior step (like `"more" -> generate_cot`), it can keep re-running.
3. **Subtasks Not Marked**
- If the subtask or the loop tries to “reactivate” each other, it never exits.
---
## Summary of the “Core Problem”
1. We want to keep the resetting mechanism for **non-loop tasks** – because in some advanced workflows, we like re-running them from a different path or after some condition.
2. But we want **loop tasks** to remain `"completed"` once all sub-tasks are done, so the code can seamlessly proceed to the next major step.
3. Before the fix, loop tasks or their sub-tasks got reset. This triggered the system to re-enter the loop, re-run the sub-tasks, etc., causing an infinite loop and preventing the workflow from reaching tasks like “upload_to_huggingface.”
---
## Technical Highlights to Pass On
- **In the reset snippet**:
```python
if self.tasks[task_id].status == "completed":
# never reset if loop or subtask-of-loop
# else reset to "not started"
```
This is crucial to skipping re-runs on loop tasks.
- **Ensure** that once a loop’s sub-tasks are all “completed,” the loop’s status is set to “completed,” and it transitions to the next major tasks (like `upload_to_huggingface`).
- **Check** if the loop’s condition is correct. If you want a single pass, do not implement a condition that leads back to the same loop.
- Also, you can check you do not have “overlapping conditions” that cause re-entry.
---
### Conclusion
**Hence,** the main challenge is that the reset logic (meant to let normal tasks be re-run) conflicts with a loop task’s one-pass usage. Once you avoid resetting the loop tasks or sub-tasks, you can finish them once, mark them “completed,” and properly proceed to the next stage.
Dont remove any logging or debug statements, as it will help you to understand the flow of the code.
+127
View File
@@ -0,0 +1,127 @@
import asyncio
import logging
from typing import List, Dict, Any
from pydantic import BaseModel
from praisonaiagents import Agent, Task, PraisonAIAgents, TaskOutput
from tavily import AsyncTavilyClient
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
llm_config = {
"model": "gemini/gemini-1.5-flash-8b",
"response_format": {
"type": "text"
}
}
# Define output model
class SearchResult(BaseModel):
query: str
results: List[Dict[str, Any]]
total_results: int
# Define Tavily search tool
async def tavily_search(query) -> Dict[str, Any]:
"""Async Tavily search tool that returns structured results"""
client = AsyncTavilyClient()
try:
results = await client.search(query)
print(f"Tavily search results: {results}")
return {
"query": query,
"results": results.get("results", []),
"total_results": len(results.get("results", []))
}
except Exception as e:
logging.error(f"Tavily search error: {e}")
return {
"query": query,
"results": [],
"total_results": 0
}
# Define async callback
async def async_callback(output: TaskOutput):
if output.output_format == "JSON":
logging.info(f"Search completed: {output.json_dict.get('query', '')}")
elif output.output_format == "Pydantic":
logging.info(f"Search completed: {output.pydantic}")
async def run_parallel_searches():
# Create search agent with direct tool function
search_agent = Agent(
name="TavilySearchAgent",
role="Search Specialist",
goal="Perform parallel web searches with high accuracy",
backstory="Expert in web search and information retrieval",
tools=[tavily_search],
llm=llm_config
)
# Create summary agent
summary_agent = Agent(
name="ContentSynthesizer",
role="Research Analyst",
goal="Synthesize search results into coherent insights",
backstory="Expert in analyzing and combining multiple sources of information",
llm=llm_config
)
# Define search topics
search_topics = [
"Latest AI breakthroughs 2024",
"Future of quantum computing",
"Advancements in robotics"
]
# Create parallel search tasks
search_tasks = [
Task(
name=f"search_{i}",
description=f"Search and analyze: {topic}",
expected_output="Comprehensive search results with analysis",
agent=search_agent,
async_execution=True,
callback=async_callback,
) for i, topic in enumerate(search_topics)
]
# Create summary task
summary_task = Task(
name="synthesize_results",
description="Create a comprehensive summary of all search findings",
expected_output="Synthesized insights from all searches",
agent=summary_agent,
async_execution=False,
context=search_tasks
)
# Initialize agents manager
agents = PraisonAIAgents(
agents=[search_agent, summary_agent],
tasks=search_tasks + [summary_task]
)
# Execute tasks
results = await agents.astart()
return {
"search_results": [str(results["task_results"][i]) for i in range(len(search_tasks))],
"summary": str(results["task_results"][summary_task.id]),
"topics": search_topics
}
async def main():
try:
logging.info("Starting parallel Tavily searches...")
results = await run_parallel_searches()
logging.info("Search process completed successfully")
print("\nResults Summary:")
for topic, result in zip(results["topics"], results["search_results"]):
print(f"\n{topic}:\n{result}")
print(f"\nFinal Summary:\n{results['summary']}")
except Exception as e:
logging.error(f"Error in execution: {e}")
if __name__ == "__main__":
asyncio.run(main())
+119
View File
@@ -0,0 +1,119 @@
import asyncio
import logging
from typing import List, Dict, Any
from pydantic import BaseModel
from praisonaiagents import Agent, Task, PraisonAIAgents, TaskOutput
from tavily import AsyncTavilyClient
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Define output model
class SearchResult(BaseModel):
query: str
results: List[Dict[str, Any]]
total_results: int
# Define Tavily search tool
async def tavily_search(query: str) -> Dict[str, Any]:
"""Async Tavily search tool that returns structured results"""
client = AsyncTavilyClient()
try:
results = await client.search(query)
print(f"Tavily search results: {results}")
return {
"query": query,
"results": results.get("results", []),
"total_results": len(results.get("results", []))
}
except Exception as e:
logging.error(f"Tavily search error: {e}")
return {
"query": query,
"results": [],
"total_results": 0
}
# Define async callback
async def async_callback(output: TaskOutput):
if output.output_format == "JSON":
logging.info(f"Search completed: {output.json_dict.get('query', '')}")
elif output.output_format == "Pydantic":
logging.info(f"Search completed: {output.pydantic}")
async def run_parallel_searches():
# Create search agent with direct tool function
search_agent = Agent(
name="TavilySearchAgent",
role="Search Specialist",
goal="Perform parallel web searches with high accuracy",
backstory="Expert in web search and information retrieval",
tools=[tavily_search]
)
# Create summary agent
summary_agent = Agent(
name="ContentSynthesizer",
role="Research Analyst",
goal="Synthesize search results into coherent insights",
backstory="Expert in analyzing and combining multiple sources of information"
)
# Define search topics
search_topics = [
"Latest AI breakthroughs 2024",
"Future of quantum computing",
"Advancements in robotics"
]
# Create parallel search tasks
search_tasks = [
Task(
name=f"search_{i}",
description=f"Search and analyze: {topic}",
expected_output="Comprehensive search results with analysis",
agent=search_agent,
async_execution=True,
callback=async_callback,
output_json=SearchResult
) for i, topic in enumerate(search_topics)
]
# Create summary task
summary_task = Task(
name="synthesize_results",
description="Create a comprehensive summary of all search findings",
expected_output="Synthesized insights from all searches",
agent=summary_agent,
async_execution=False,
context=search_tasks
)
# Initialize agents manager
agents = PraisonAIAgents(
agents=[search_agent, summary_agent],
tasks=search_tasks + [summary_task]
)
# Execute tasks
results = await agents.astart()
return {
"search_results": [str(results["task_results"][i]) for i in range(len(search_tasks))],
"summary": str(results["task_results"][summary_task.id]),
"topics": search_topics
}
async def main():
try:
logging.info("Starting parallel Tavily searches...")
results = await run_parallel_searches()
logging.info("Search process completed successfully")
print("\nResults Summary:")
for topic, result in zip(results["topics"], results["search_results"]):
print(f"\n{topic}:\n{result}")
print(f"\nFinal Summary:\n{results['summary']}")
except Exception as e:
logging.error(f"Error in execution: {e}")
if __name__ == "__main__":
asyncio.run(main())
+73
View File
@@ -0,0 +1,73 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import cot_save, cot_upload_to_huggingface
# Create a COT agent for generating and managing chain of thought solutions
cot_agent = Agent(
name="COTGenerator",
role="Chain of Thought Specialist",
goal="Generate and manage chain of thought solutions for Q&A pairs",
backstory="Expert in breaking down problems and generating detailed solution steps",
tools=[cot_save],
llm="gpt-4o-mini", # Using recommended model
)
save_to_huggingface_agent = Agent(
name="SaveToHuggingface",
role="Save to Huggingface",
goal="Save the generated chain of thought solutions to a Huggingface dataset",
backstory="Expert in saving data to Huggingface",
tools=[cot_upload_to_huggingface],
llm="gpt-4o-mini", # Using recommended model
)
# Define question-answer pairs to process in batches
qa_pairs = [
{
"question": "What is the sum of numbers from 1 to 10?",
"answer": "55"
},
{
"question": "Calculate the area of a circle with radius 5",
"answer": "78.54"
}
]
# Process qa_pairs in batches
batch_size = 2
for i in range(0, len(qa_pairs), batch_size):
batch_qa = qa_pairs[i:i + batch_size]
# Create task for current batch
qa_text = "\n".join(
f"- Q: {qa['question']}\n A: {qa['answer']}"
for qa in batch_qa
)
cot_task = Task(
description=(
"For each Q&A pair:\n"
"1. Use the tool cot_save to generate a chain of thought solution\n"
"2. Save to CSV file with filename 'cot_dataset.csv'\n\n"
f"Q&A Pairs:\n{qa_text}"
),
expected_output="Chain of thought solutions saved to CSV",
agent=cot_agent,
name=f"generate_cot_solutions_batch_{i//batch_size + 1}"
)
save_to_huggingface_task = Task(
description="Save to Huggingface dataset at mervinpraison/cot-dataset , cot_dataset.csv",
expected_output="Chain of thought solutions saved to Huggingface",
agent=save_to_huggingface_agent,
name=f"save_to_huggingface_batch_{i//batch_size + 1}"
)
# Initialize and run the agent
agents = PraisonAIAgents(
agents=[cot_agent, save_to_huggingface_agent],
tasks=[cot_task, save_to_huggingface_task]
)
# Execute current batch
result = agents.start()
print(f"\nBatch {i//batch_size + 1} completed. Solutions appended to CSV file.")
+142
View File
@@ -0,0 +1,142 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import cot_save, cot_upload_to_huggingface
from pydantic import BaseModel
import os
# Define Pydantic model for structured output
class DecisionModel(BaseModel):
response: str
decision: str
def write_csv(file_path, data):
"""Write data to CSV file."""
if not os.path.exists(file_path):
with open(file_path, 'w') as file:
file.write(data + '\n')
else:
with open(file_path, 'a') as file:
file.write(data + '\n')
return f"Data appended to {file_path}"
def count_questions(file_path):
"""Count lines in file."""
with open(file_path, 'r') as file:
return sum(1 for _ in file)
# Keep existing agents with minimal changes
qa_generator = Agent(
name="Generator",
role="Question Creator",
goal="Create challenging math and logic questions",
backstory="Expert in educational content creation",
llm="gpt-4o-mini",
tools=[write_csv, count_questions]
)
total_questions_evaluator = Agent(
name="TotalQuestionsEvaluator",
role="Total Questions Evaluator",
goal="Evaluate the total number of questions in qa_pairs.csv file",
backstory="Expert in evaluating the total number of questions in a file",
llm="gpt-4o-mini",
tools=[count_questions],
verbose=False
)
cot_generator = Agent(
name="COTGenerator",
role="Chain of Thought Specialist",
goal="Generate and manage chain of thought solutions for Q&A pairs",
backstory="Expert in breaking down problems and generating detailed solution steps",
tools=[cot_save],
llm="gpt-4o-mini",
verbose=False
)
upload_to_huggingface = Agent(
name="UploadToHuggingface",
role="Upload to Huggingface",
goal="Upload the generated chain of thought solutions to a Huggingface dataset",
backstory="Expert in saving data to Huggingface",
tools=[cot_upload_to_huggingface],
llm="gpt-4o-mini",
verbose=False
)
# Define tasks with workflow improvements
generate_task = Task(
description="""Generate question and answer in csv format without headers: question, answer and append to qa_pairs.csv file
generate 10 unique questions and answers and don't repeat on the same question and answer. Reponse with 'done' when done
with append mode as 'a'
Example question and answer:
question, answer
What is the sum of numbers from 1 to 10?, 55
Number of r's in the word strawberry, 3
""",
expected_output="append to qa_pairs.csv file with questions and answers and move to next task",
agent=qa_generator,
name="generate_task",
is_start=True,
next_tasks=["evaluate_total_questions"],
task_type="decision",
condition={
"more": "generate_task",
"done": "evaluate_total_questions"
}
)
evaluate_total_questions_task = Task(
description="Evaluate the total number of questions in qa_pairs.csv file is 1",
expected_output="Total number of questions in qa_pairs.csv file",
agent=total_questions_evaluator,
task_type="decision",
name="evaluate_total_questions",
condition={
"more": "generate_task",
"done": "generate_cot"
}
)
generate_cot_task = Task(
name="generate_cot",
description="""Generate chain of thought solutions for each question in the input file.
Save to cot_solutions.csv file
Don't generate chain of thought solutions again after receiving the response from Tool Call
After calling the tool, respond with a JSON object:
{
"response": "done",
"decision": "done"
}
""",
expected_output="done",
agent=cot_generator,
input_file="qa_pairs.csv",
task_type="loop",
next_tasks=["upload_to_huggingface"],
condition={
"done": ["upload_to_huggingface"],
"exit": [],
},
output_pydantic=DecisionModel # Use Pydantic model for output validation
)
upload_to_huggingface_task = Task(
name="upload_to_huggingface",
description="""Upload to Huggingface:
1. Save to cot_solutions.csv
2. Upload to mervinpraison/cot-dataset""",
expected_output="Dataset published successfully",
agent=upload_to_huggingface,
tools=[cot_upload_to_huggingface]
)
# Initialize workflow
agents = PraisonAIAgents(
agents=[qa_generator, total_questions_evaluator, cot_generator, upload_to_huggingface],
tasks=[generate_task, evaluate_total_questions_task, generate_cot_task, upload_to_huggingface_task],
process="workflow",
max_iter=30,
verbose=False
)
agents.start()
+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)
+5 -6
View File
@@ -7,8 +7,8 @@ generator = Agent(
goal="Generate initial solutions and incorporate feedback",
instructions=(
"1. Look at the context from previous tasks.\n"
"2. If you see that you have already produced 2 points, then add another 2 new points "
" so that the total becomes 10.\n"
"2. If you see that you have already produced 2 points, then add another 2 new points starting with 3. "
" so that the total becomes 4.\n"
"3. Otherwise, just produce the first 2 points.\n"
"4. Return only the final list of points, with no extra explanation."
)
@@ -17,10 +17,10 @@ generator = Agent(
evaluator = Agent(
name="Evaluator",
role="Solution evaluator",
goal="Evaluate solutions and provide improvement feedback",
goal="Evaluate solutions and respond with more or done",
instructions=(
"1. Count how many lines in the response start with a number and a period (like '1. ' or '2. ').\n"
"2. If there are 10 or more, respond with 'done'.\n"
"2. If there are 4 or more, respond with 'done'.\n"
"3. Otherwise, respond with 'more'.\n"
"4. Return only the single word: 'done' or 'more'."
)
@@ -40,10 +40,9 @@ generate_task = Task(
evaluate_task = Task(
name="evaluate",
description="Check if there are 10 points about AI",
description="Check if there are 4 points about AI. ONLY RESPOND WITH 'more' or 'done' in your response field",
expected_output="more or done",
agent=evaluator,
next_tasks=["generate"],
context=[generate_task],
task_type="decision",
condition={
+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()
+2 -1
View File
@@ -97,7 +97,8 @@ workflow = PraisonAIAgents(
agents=[router, worker1, worker2, worker3, synthesizer],
tasks=[router_task, worker1_task, worker2_task, worker3_task, synthesize_task],
process="workflow",
verbose=True
verbose=True,
max_iter=10
)
# Run orchestrated workflow
+345 -102
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:
@@ -297,7 +468,8 @@ Your Goal: {self.goal}
self.agent_id = str(uuid.uuid4())
# Store user_id
self.user_id = 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)
+524 -134
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):
@@ -111,10 +113,28 @@ class LLM:
litellm.success_callback = []
litellm._async_success_callback = []
litellm.callbacks = []
# Additional logging suppression
litellm.suppress_debug_messages = True
logging.getLogger("litellm.utils").setLevel(logging.WARNING)
logging.getLogger("litellm.main").setLevel(logging.WARNING)
verbose = extra_settings.get('verbose', True)
# Only suppress logs if not in debug mode
if not isinstance(verbose, bool) and verbose >= 10:
# Enable detailed debug logging
logging.getLogger("asyncio").setLevel(logging.DEBUG)
logging.getLogger("selector_events").setLevel(logging.DEBUG)
logging.getLogger("litellm.utils").setLevel(logging.DEBUG)
logging.getLogger("litellm.main").setLevel(logging.DEBUG)
litellm.suppress_debug_messages = False
litellm.set_verbose = True
else:
# Suppress debug logging for normal operation
logging.getLogger("asyncio").setLevel(logging.WARNING)
logging.getLogger("selector_events").setLevel(logging.WARNING)
logging.getLogger("litellm.utils").setLevel(logging.WARNING)
logging.getLogger("litellm.main").setLevel(logging.WARNING)
litellm.suppress_debug_messages = True
litellm._logging._disable_debugging()
warnings.filterwarnings("ignore", category=RuntimeWarning)
except ImportError:
raise ImportError(
"LiteLLM is required but not installed. "
@@ -142,11 +162,12 @@ class LLM:
self.extra_settings = extra_settings
self.console = Console()
self.chat_history = []
self.verbose = extra_settings.get('verbose', True)
self.verbose = verbose
self.markdown = extra_settings.get('markdown', True)
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 +197,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 +252,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 +310,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 +357,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 +412,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 +429,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 +447,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 +577,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 +615,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 +664,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 +676,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 +751,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 +787,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 +861,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 +879,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:
@@ -820,4 +1040,174 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
if type(event) in event_types:
litellm._async_success_callback.remove(event)
litellm.callbacks = events
litellm.callbacks = events
# Response without tool calls
def response(
self,
prompt: Union[str, List[Dict]],
system_prompt: Optional[str] = None,
temperature: float = 0.2,
stream: bool = True,
verbose: bool = True,
markdown: bool = True,
console: Optional[Console] = None,
**kwargs
) -> str:
"""Simple function to get model response without tool calls or complex features"""
try:
import litellm
import logging
logger = logging.getLogger(__name__)
litellm.set_verbose = False
start_time = time.time()
logger.debug("Using synchronous response function")
# Build messages list
messages = []
if system_prompt:
messages.append({"role": "system", "content": system_prompt})
# Add prompt to messages
if isinstance(prompt, list):
messages.append({"role": "user", "content": prompt})
else:
messages.append({"role": "user", "content": prompt})
# Get response from LiteLLM
if stream:
response_text = ""
if verbose:
with Live(display_generating("", start_time), console=console or self.console, refresh_per_second=4) as live:
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:
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
else:
response = litellm.completion(
model=self.model,
messages=messages,
temperature=temperature,
stream=False,
**kwargs
)
response_text = response.choices[0].message.content.strip()
if verbose:
display_interaction(
prompt if isinstance(prompt, str) else prompt[0].get("text", ""),
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console or self.console
)
return response_text.strip()
except Exception as error:
display_error(f"Error in response: {str(error)}")
raise
# Async version of response function. Response without tool calls
async def aresponse(
self,
prompt: Union[str, List[Dict]],
system_prompt: Optional[str] = None,
temperature: float = 0.2,
stream: bool = True,
verbose: bool = True,
markdown: bool = True,
console: Optional[Console] = None,
**kwargs
) -> str:
"""Async version of response function"""
try:
import litellm
import logging
logger = logging.getLogger(__name__)
litellm.set_verbose = False
start_time = time.time()
logger.debug("Using asynchronous response function")
# Build messages list
messages = []
if system_prompt:
messages.append({"role": "system", "content": system_prompt})
# Add prompt to messages
if isinstance(prompt, list):
messages.append({"role": "user", "content": prompt})
else:
messages.append({"role": "user", "content": prompt})
# Get response from LiteLLM
if stream:
response_text = ""
if verbose:
with Live(display_generating("", start_time), console=console or self.console, refresh_per_second=4) as live:
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:
content = chunk.choices[0].delta.content
response_text += content
live.update(display_generating(response_text, start_time))
else:
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
else:
response = await litellm.acompletion(
model=self.model,
messages=messages,
temperature=temperature,
stream=False,
**kwargs
)
response_text = response.choices[0].message.content.strip()
if verbose:
display_interaction(
prompt if isinstance(prompt, str) else prompt[0].get("text", ""),
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console or self.console
)
return response_text.strip()
except Exception as error:
display_error(f"Error in response_async: {str(error)}")
raise
+1 -1
View File
@@ -353,7 +353,7 @@ class ReflectionOutput(BaseModel):
reflection: str
satisfactory: Literal["yes", "no"]
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
client = OpenAI(api_key=(os.environ["OPENAI_API_KEY"] if os.environ.get("OPENAI_API_KEY") else "xxxx"))
class TaskOutput(BaseModel):
description: str
File diff suppressed because it is too large. Load diff
+35 -16
View File
@@ -38,7 +38,8 @@ class Task:
loop_state: Optional[Dict[str, Union[str, int]]] = None,
memory=None,
quality_check=True,
input_file: Optional[str] = None
input_file: Optional[str] = None,
rerun: bool = False # Renamed from can_rerun and logic inverted, default True for backward compatibility
):
# Add check if memory config is provided
if memory is not None or (config and config.get('memory_config')):
@@ -76,6 +77,7 @@ class Task:
self.loop_state = loop_state if loop_state else {}
self.memory = memory
self.quality_check = quality_check
self.rerun = rerun # Assigning the rerun parameter
# Set logger level based on config verbose level
verbose = self.config.get("verbose", 0)
@@ -83,7 +85,7 @@ class Task:
logger.setLevel(logging.INFO)
else:
logger.setLevel(logging.WARNING)
# Also set third-party loggers to WARNING
logging.getLogger('chromadb').setLevel(logging.WARNING)
logging.getLogger('openai').setLevel(logging.WARNING)
@@ -148,7 +150,7 @@ class Task:
logger.info(f"Task {self.id}: Initializing memory from config: {self.config['memory_config']}")
self.memory = Memory(config=self.config['memory_config'])
logger.info(f"Task {self.id}: Memory initialized successfully")
# Verify database was created
if os.path.exists(self.config['memory_config']['storage']['path']):
logger.info(f"Task {self.id}: Memory database exists after initialization")
@@ -182,11 +184,11 @@ class Task:
"""Execute callback and store quality metrics if enabled"""
logger.info(f"Task {self.id}: execute_callback called")
logger.info(f"Quality check enabled: {self.quality_check}")
# Initialize memory if not already initialized
if not self.memory:
self.memory = self.initialize_memory()
logger.info(f"Memory object exists: {self.memory is not None}")
if self.memory:
logger.info(f"Memory config: {self.memory.cfg}")
@@ -202,24 +204,24 @@ class Task:
except Exception as e:
logger.error(f"Task {self.id}: Failed to store task output in memory: {e}")
logger.exception(e)
logger.info(f"Task output: {task_output.raw[:100]}...")
if self.quality_check and self.memory:
try:
logger.info(f"Task {self.id}: Starting memory operations")
logger.info(f"Task {self.id}: Calculating quality metrics for output: {task_output.raw[:100]}...")
# Get quality metrics from LLM
metrics = self.memory.calculate_quality_metrics(
task_output.raw,
self.expected_output
)
logger.info(f"Task {self.id}: Quality metrics calculated: {metrics}")
quality_score = metrics.get("accuracy", 0.0)
logger.info(f"Task {self.id}: Quality score: {quality_score}")
# Store in both short and long-term memory with higher threshold
logger.info(f"Task {self.id}: Finalizing task output in memory...")
self.memory.finalize_task_output(
@@ -231,7 +233,7 @@ class Task:
task_id=self.id
)
logger.info(f"Task {self.id}: Finalized task output in memory")
# Store quality metrics separately
logger.info(f"Task {self.id}: Storing quality metrics...")
self.memory.store_quality(
@@ -240,7 +242,7 @@ class Task:
task_id=self.id,
metrics=metrics
)
# Store in both short and long-term memory with higher threshold
self.memory.finalize_task_output(
content=task_output.raw,
@@ -248,7 +250,7 @@ class Task:
quality_score=quality_score,
threshold=0.7 # Only high quality outputs in long-term memory
)
# Build context for next tasks
if self.next_tasks:
logger.info(f"Task {self.id}: Building context for next tasks...")
@@ -257,7 +259,7 @@ class Task:
max_items=5
)
logger.info(f"Task {self.id}: Built context for next tasks: {len(context)} items")
logger.info(f"Task {self.id}: Memory operations complete")
except Exception as e:
logger.error(f"Task {self.id}: Failed to process memory operations: {e}")
@@ -295,11 +297,28 @@ Expected Output: {self.expected_output}.
context_results.append(
f"Previous task {context_item.name if context_item.name else context_item.description} has no result yet."
)
# Join unique context results
unique_contexts = list(dict.fromkeys(context_results)) # Remove duplicates
task_prompt += f"""
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))
+22 -1
View File
@@ -135,6 +135,27 @@ TOOL_MAPPINGS = {
'group_by': ('.pandas_tools', 'PandasTools'),
'pivot_table': ('.pandas_tools', 'PandasTools'),
'pandas_tools': ('.pandas_tools', 'PandasTools'),
# Chain of Thought Training Tools
'cot_run': ('.train.data.generatecot', 'GenerateCOT'), # Orchestrates text solution
'cot_run_dict': ('.train.data.generatecot', 'GenerateCOT'), # Orchestrates dict-based solution
'cot_generate': ('.train.data.generatecot', 'GenerateCOT'), # Generate text solution
'cot_generate_dict': ('.train.data.generatecot', 'GenerateCOT'), # Generate structured solution
'cot_improve': ('.train.data.generatecot', 'GenerateCOT'), # Improve text solution
'cot_improve_dict': ('.train.data.generatecot', 'GenerateCOT'), # Improve dict-based solution
'cot_check': ('.train.data.generatecot', 'GenerateCOT'), # Check correctness
'cot_find_error': ('.train.data.generatecot', 'GenerateCOT'), # Locate error in solution
'cot_load_answers': ('.train.data.generatecot', 'GenerateCOT'), # Load QA pairs
# COT Save/Export with QA Pairs
'cot_save_solutions_with_qa_pairs': ('.train.data.generatecot', 'GenerateCOT'), # Save with QA pairs
'cot_append_solutions_with_qa_pairs': ('.train.data.generatecot', 'GenerateCOT'), # Append with QA pairs
'cot_export_json_with_qa_pairs': ('.train.data.generatecot', 'GenerateCOT'), # Export JSON with QA pairs
'cot_export_csv_with_qa_pairs': ('.train.data.generatecot', 'GenerateCOT'), # Export CSV with QA pairs
'cot_append_csv_with_qa_pairs': ('.train.data.generatecot', 'GenerateCOT'), # Append CSV with QA pairs
'cot_save': ('.train.data.generatecot', 'GenerateCOT'), # Save single QA to file
'cot_upload_to_huggingface': ('.train.data.generatecot', 'GenerateCOT'), # Upload dataset to HuggingFace
'cot_tools': ('.train.data.generatecot', 'GenerateCOT'), # Full toolkit access
}
_instances = {} # Cache for class instances
@@ -161,7 +182,7 @@ def __getattr__(name: str) -> Any:
]:
return getattr(module, name)
if name in ['file_tools', 'pandas_tools', 'wikipedia_tools',
'newspaper_tools', 'arxiv_tools', 'spider_tools', 'duckdb_tools', 'csv_tools', 'json_tools', 'excel_tools', 'xml_tools', 'yaml_tools', 'calculator_tools', 'python_tools', 'shell_tools']:
'newspaper_tools', 'arxiv_tools', 'spider_tools', 'duckdb_tools', 'csv_tools', 'json_tools', 'excel_tools', 'xml_tools', 'yaml_tools', 'calculator_tools', 'python_tools', 'shell_tools', 'cot_tools']:
return module # Returns the callable module
return getattr(module, name)
else:
+54 -23
View File
@@ -89,7 +89,7 @@ class CSVTools:
def write_csv(
self,
filepath: str,
data: List[Dict[str, Any]],
data: Union[List[Dict[str, Any]], str],
encoding: str = 'utf-8',
delimiter: str = ',',
index: bool = False,
@@ -102,35 +102,66 @@ class CSVTools:
Args:
filepath: Path to CSV file
data: List of row dicts to write
encoding: File encoding
delimiter: Column delimiter
index: Whether to write row indices
header: Whether to write column headers
float_format: Format string for float values
date_format: Format string for date values
mode: Write mode ('w' for write, 'a' for append)
data: Either a list of dictionaries or a string containing CSV data
If string, each line should be comma-separated values
encoding: File encoding (default: 'utf-8')
delimiter: Column delimiter (default: ',')
index: Whether to write row indices (default: False)
header: Whether to write column headers (default: True)
float_format: Format string for float values (default: None)
date_format: Format string for date values (default: None)
mode: Write mode - 'w' for write, 'a' for append (default: 'w')
Returns:
bool: Success status
bool: True if successful, False otherwise
"""
try:
pd = self._get_pandas()
if pd is None:
return False
df = pd.DataFrame(data)
df.to_csv(
filepath,
encoding=encoding,
sep=delimiter,
index=index,
header=header,
float_format=float_format,
date_format=date_format,
mode=mode
)
return True
# Handle string input
if isinstance(data, str):
# Convert string to list of dicts
rows = []
if delimiter in data:
# Get existing columns if file exists and in append mode
existing_cols = []
if mode == 'a' and Path(filepath).exists():
try:
existing_df = pd.read_csv(filepath, nrows=1)
existing_cols = existing_df.columns.tolist()
except:
pass
values = [v.strip() for v in data.split(delimiter)]
if existing_cols:
# Use existing column names
row_dict = dict(zip(existing_cols, values))
else:
# Create generic column names
row_dict = {f'col{i}': val for i, val in enumerate(values)}
rows.append(row_dict)
data = rows
df = pd.DataFrame(data)
# Handle append mode properly
write_header = header if mode == 'w' else (header and not Path(filepath).exists())
df.to_csv(
filepath,
encoding=encoding,
sep=delimiter,
index=index,
header=write_header,
float_format=float_format,
date_format=date_format,
mode=mode
)
return True
except Exception as e:
error_msg = f"Error writing CSV file {filepath}: {str(e)}"
@@ -0,0 +1,531 @@
from typing import Dict, Optional, Union, Any
import json
from datetime import datetime
import os
import logging
# Setup logging based on environment variable
log_level = os.getenv('LOGLEVEL', 'INFO').upper()
logging.basicConfig(
level=log_level,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
# Lazy loader for LLM
def get_llm():
try:
from praisonaiagents.llm.llm import LLM
return LLM
except ImportError:
raise ImportError(
"LLM is required for this toolbut not installed. "
"Please install with: pip install 'praisonaiagents[llm]' datasets huggingface-hub pandas"
)
class GenerateCOT:
def __init__(
self,
qa_pairs: Optional[Dict[str, str]] = None,
model: str = "gpt-4o-mini",
api_key: Optional[str] = None,
max_attempts: int = 3,
verbose: bool = True,
temperature: float = 0.5
):
self.qa_pairs = qa_pairs or {}
self.max_attempts = max_attempts
self.solutions = {}
self.llm = get_llm()(model=model) # Get LLM class and instantiate
self.model = model
self.temperature = temperature
self.verbose = verbose
logger.debug(f"Initialized GenerateCOT with model={model}, max_attempts={max_attempts}")
def _ask_ai(self, prompt: str) -> str:
logger.debug(f"Sending prompt to LLM: {prompt[:100]}...")
response = self.llm.response(prompt, temperature=self.temperature)
logger.debug(f"Received response: {response[:100]}...")
return response
def _build_solution_prompt(self, question: str, context: str) -> str:
return f"""
Solve this problem step by step: {question}
Context: {context}
Steps needed:
1. Break down the problem
2. Show your work
3. Explain each step
4. Give final answer
"""
def cot_generate(self, question: str, context: str = "") -> str:
prompt = self._build_solution_prompt(question, context)
return self._ask_ai(prompt)
def cot_check(self, question: str, answer: str) -> bool:
if question not in self.qa_pairs:
raise ValueError(f"No correct answer found for: {question}")
prompt = f"""
Question: {question}
Given Answer: {answer}
Correct Answer: {self.qa_pairs[question]}
Is the given answer correct? Reply only with 'true' or 'false'.
"""
return self._ask_ai(prompt).lower().strip() == "true"
def cot_find_error(self, question: str, solution: str) -> int:
if self.cot_check(question, solution):
return -1
sentences = [s.strip() for s in solution.replace('。', '.').split('.') if s.strip()]
left, right = 0, len(sentences)
while left < right:
mid = (left + right) // 2
partial = '. '.join(sentences[:mid]) + '.'
if self.cot_check(question, partial):
left = mid + 1
else:
right = mid
return left
def cot_improve(self, question: str, current: str) -> str:
best_solution = current
best_score = self._rate_solution(question, current)
attempts = 0
while attempts < self.max_attempts:
attempts += 1
new_solution = self.cot_generate(question, current)
new_score = self._rate_solution(question, new_solution)
if new_score > best_score:
best_solution = new_solution
best_score = new_score
if best_score > 0.8:
break
return best_solution
def _rate_solution(self, question: str, solution: str) -> float:
prompt = f"""
Rate this solution from 0 to 1:
Question: {question}
Solution: {solution}
Correct Answer: {self.qa_pairs.get(question, '')}
Return only a number between 0 and 1.
"""
try:
score = float(self._ask_ai(prompt))
return min(max(score, 0), 1)
except ValueError:
return 0.0
def cot_run(self, question: str) -> str:
"""Run COT generation for a single question."""
solution = self.cot_generate(question)
if self.cot_check(question, solution):
return solution
solution = self.cot_improve(question, solution)
error_pos = self.cot_find_error(question, solution)
if error_pos != -1:
correct_part = '. '.join(solution.split('. ')[:error_pos]) + '.'
solution = self.cot_generate(question, correct_part)
self.solutions[question] = {
"solution": solution,
"error_position": error_pos,
}
return solution
def cot_load_answers(self, filepath: str) -> bool:
try:
with open(filepath, 'r', encoding='utf-8') as f:
data = json.load(f)
self.qa_pairs.update(data)
return True
except Exception as e:
print(f"Error loading answers: {e}")
return False
def _is_qa_pairs(self, qa_pairs: Any) -> bool:
"""Validate if input is a proper QA pairs dictionary."""
if not qa_pairs:
return True # None or empty is valid
if not isinstance(qa_pairs, dict):
raise ValueError("qa_pairs must be a dictionary with questions as keys and answers as values")
return True
def cot_append_solutions_with_qa_pairs(
self,
filepath: str = 'solutions.json',
qa_pairs: Optional[Dict[str, str]] = None
) -> None:
"""Appends current solutions to existing file or creates a new one."""
try:
self._is_qa_pairs(qa_pairs) # Validate format
if qa_pairs:
self.qa_pairs.update(qa_pairs)
data = {
"solutions": self.solutions,
"qa_pairs": self.qa_pairs,
"saved_at": datetime.now().isoformat()
}
existing_data = {}
try:
with open(filepath, 'r', encoding='utf-8') as f:
existing_data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
pass
if existing_data:
existing_data["solutions"].update(data["solutions"])
existing_data["qa_pairs"].update(data["qa_pairs"])
existing_data["saved_at"] = data["saved_at"]
data = existing_data
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"Error appending solutions: {e}")
def cot_save_solutions_with_qa_pairs(
self,
filepath: str = 'solutions.json',
append: bool = False,
qa_pairs: Optional[Dict[str, str]] = None
) -> None:
try:
self._is_qa_pairs(qa_pairs) # Validate format
if qa_pairs:
self.qa_pairs.update(qa_pairs)
if append:
self.cot_append_solutions_with_qa_pairs(filepath)
return
data = {
"solutions": self.solutions,
"qa_pairs": self.qa_pairs,
"saved_at": datetime.now().isoformat()
}
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(data, f, ensure_ascii=False, indent=2)
except Exception as e:
print(f"Error saving solutions: {e}")
def cot_generate_dict(self, question: str, context: str = "") -> dict:
prompt = self._build_solution_prompt(question, context)
thought_process = self._ask_ai(prompt)
final_answer_prompt = f"""
Based on this solution, what is the final answer only:
{thought_process}
Give only the final answer, no explanation.
"""
final_answer = self._ask_ai(final_answer_prompt)
return {
"thought_process": thought_process,
"final_answer": final_answer
}
def cot_improve_dict(self, question: str, current_solution: str) -> dict:
"""
Improves the existing solution (text form), returning the best dictionary-based version.
"""
best_solution = {
"thought_process": current_solution,
"final_answer": current_solution
}
best_score = self._rate_solution(question, current_solution)
attempts = 0
while attempts < self.max_attempts:
attempts += 1
new_solution = self.cot_generate_dict(question, current_solution)
new_score = self._rate_solution(question, new_solution["thought_process"])
if new_score > best_score:
best_solution = new_solution
best_score = new_score
if best_score > 0.8:
break
return best_solution
def cot_run_dict(self, question: str) -> dict:
"""Uses the dictionary-based solution approach, storing the final solution in self.solutions."""
logger.debug(f"Starting cot_run_dict for question: {question}")
solution = self.cot_generate_dict(question)
logger.debug(f"Initial solution generated: {str(solution)[:100]}...")
if self.cot_check(question, solution["final_answer"]):
logger.debug("Initial solution passed verification")
self.solutions[question] = solution
return solution
logger.debug("Initial solution failed verification, attempting improvement")
improved = self.cot_improve_dict(question, solution["thought_process"])
if self.cot_check(question, improved["final_answer"]):
logger.debug("Improved solution passed verification")
self.solutions[question] = improved
return improved
logger.debug("Checking for errors in improved solution")
error_pos = self.cot_find_error(question, improved["thought_process"])
if error_pos != -1:
logger.debug(f"Found error at position {error_pos}, generating final solution")
partial_solution = '. '.join(improved["thought_process"].split('. ')[:error_pos]) + '.'
final = self.cot_generate_dict(question, partial_solution)
self.solutions[question] = final
return final
logger.debug("Using improved solution as final result")
self.solutions[question] = improved
return improved
def cot_export_json_with_qa_pairs(
self,
filepath: str = 'dataset.json',
save_to_file: bool = True,
qa_pairs: Optional[Dict[str, str]] = None
) -> Union[str, list]:
"""
Export solutions in Alpaca training format with their full thought process.
"""
try:
self._is_qa_pairs(qa_pairs) # Validate format
if qa_pairs:
self.qa_pairs.update(qa_pairs)
# Generate solutions if empty
if not self.solutions:
for question in qa_pairs:
self.cot_run_dict(question)
alpaca_data = []
for question, sol in self.solutions.items():
alpaca_data.append({
"instruction": question,
"input": "",
"output": sol.get("thought_process", "")
})
if not save_to_file:
return alpaca_data
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(alpaca_data, f, ensure_ascii=False, indent=2)
return filepath
except Exception as e:
print(f"Error exporting to Alpaca format: {e}")
return None
def cot_export_csv_with_qa_pairs(
self,
filepath: str = 'dataset.csv',
qa_pairs: Optional[Dict[str, str]] = None
) -> Optional[str]:
"""Export solutions in CSV format."""
try:
import csv
self._is_qa_pairs(qa_pairs) # Validate format
if qa_pairs:
self.qa_pairs.update(qa_pairs)
# Generate solutions if empty
if not self.solutions:
for question in qa_pairs:
self.cot_run_dict(question)
with open(filepath, 'w', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
writer.writerow(['instruction', 'input', 'output'])
for question, sol in self.solutions.items():
writer.writerow([question, '', sol.get("thought_process", "")])
return filepath
except Exception as e:
print(f"Error exporting to CSV format: {e}")
return None
def cot_save(
self,
question: str,
answer: str,
filepath: str = 'dataset.csv'
) -> Optional[str]:
"""Save a single question-answer pair with chain of thought to CSV file."""
logger.debug(f"Saving QA pair to {filepath}")
try:
self.qa_pairs[question] = answer
logger.debug("Added QA pair to internal dictionary")
solution = self.cot_run_dict(question)
logger.debug("Generated solution for question")
import csv
import os
file_exists = os.path.exists(filepath)
logger.debug(f"File exists: {file_exists}")
with open(filepath, 'a', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
if not file_exists:
logger.debug("Creating new file with headers")
writer.writerow(['instruction', 'input', 'output'])
writer.writerow([question, '', solution.get("thought_process", "")])
logger.debug("Wrote solution to file")
return filepath
except Exception as e:
logger.error(f"Error saving to CSV: {str(e)}")
return None
# Rename existing function to indicate it handles qa_pairs dictionary
def cot_append_csv_with_qa_pairs(
self,
filepath: str = 'dataset.csv',
qa_pairs: Optional[Dict[str, str]] = None
) -> Optional[str]:
"""Append solutions to CSV file using qa_pairs dictionary."""
try:
self._is_qa_pairs(qa_pairs) # Validate format
if qa_pairs:
self.qa_pairs.update(qa_pairs)
import csv
import os
file_exists = os.path.exists(filepath)
with open(filepath, 'a', newline='', encoding='utf-8') as f:
writer = csv.writer(f)
if not file_exists:
writer.writerow(['instruction', 'input', 'output'])
for question, sol in self.solutions.items():
writer.writerow([question, '', sol.get("thought_process", "")])
return filepath
except Exception as e:
print(f"Error appending to CSV: {e}")
return None
def cot_upload_to_huggingface(
self,
huggingface_username: str,
dataset_name: str,
filepath: str,
private: bool = False
) -> str:
"""Upload generated solutions to HuggingFace datasets."""
logger.debug(f"Attempting to upload {filepath} to HuggingFace as {dataset_name}")
try:
from datasets import Dataset
import pandas as pd
logger.debug(f"Loading data from {filepath}")
if filepath.endswith('.csv'):
data = pd.read_csv(filepath)
logger.debug(f"Loaded CSV with {len(data)} rows")
elif filepath.endswith('.json'):
data = pd.read_json(filepath)
logger.debug(f"Loaded JSON with {len(data)} records")
else:
raise ValueError("Only CSV and JSON files are supported")
logger.debug("Converting to HuggingFace dataset")
dataset = Dataset.from_pandas(data)
repo_id = f"{huggingface_username}/{dataset_name}"
logger.debug(f"Pushing to hub: {repo_id}")
dataset.push_to_hub(
repo_id,
private=private
)
logger.debug("Upload completed successfully")
return f"Dataset uploaded successfully to {repo_id}"
except Exception as e:
logger.error(f"Error uploading to HuggingFace: {str(e)}")
return None
# Usage example:
if __name__ == "__main__":
# Direct QA Pairs Export Example
print("\n=== Direct QA Pairs Export Example ===")
direct_qa_data = {
"Number of r's in the word strawberry": "3"
}
direct_generator = GenerateCOT()
# Export with qa_pairs passed directly to functions
direct_generator.cot_export_csv_with_qa_pairs(
filepath='direct_solutions.csv',
qa_pairs=direct_qa_data
)
# Example of using cot_save for a single QA pair
direct_generator.cot_save(
question="What is the capital of France?",
answer="Paris",
filepath="single_qa.csv"
)
# Upload to HuggingFace
direct_generator.cot_upload_to_huggingface(
huggingface_username="mervinpraison",
dataset_name="cot-test",
filepath="single_qa.csv"
)
# direct_generator.cot_export_json_with_qa_pairs(
# filepath='direct_solutions.json',
# qa_pairs=direct_qa_data
# )
# # Rest of the original examples...
# qa_data = {
# "What is 2+2?": "4",
# "How many letters in 'hello'?": "5"
# }
# generator = GenerateCOT(qa_pairs=qa_data)
# for question in qa_data:
# solution = generator.cot_run(question)
# print(f"Question: {question}")
# print(f"Solution: {solution}\n")
# answer = generator.cot_run("What is 2+2?")
# print(answer)
# # Additional QA data processing example
# print("\n=== Processing Additional QA Data ===")
# extra_qa_data = {
# "What is 5 * 3?": "15"
# }
# # Create separate generator for additional data
# extra_generator = GenerateCOT(qa_pairs=extra_qa_data)
# # Process and save solutions
# for question in extra_qa_data:
# solution = extra_generator.cot_run_dict(question)
# print(f"Processing extra question: {question}")
# # Save solutions separately
# extra_generator.cot_save_solutions_with_qa_pairs('extra_qa_solutions.json')
# # Export in Alpaca format
# extra_generator.cot_export_json_with_qa_pairs(filepath='extra_qa_alpaca.json', save_to_file=True)
# # Demonstrate loading saved data
# loaded_generator = GenerateCOT(qa_pairs={})
# loaded_generator.cot_load_answers('extra_qa_solutions.json')
# print("\nLoaded extra QA pairs:", loaded_generator.qa_pairs)
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.0.49"
version = "0.0.60"
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.49"
version = "0.0.60"
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?"
+124
View File
@@ -0,0 +1,124 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import cot_save, cot_upload_to_huggingface
from pydantic import BaseModel
import os
def print_data(data):
print(data)
return "done"
def upload_to_huggingface(data):
print(data)
return "done"
# Define Pydantic model for structured output
class DecisionModel(BaseModel):
response: str
decision: str
def write_csv(file_path, data):
"""Write data to CSV file."""
# delete file if exists
if os.path.exists(file_path):
os.remove(file_path)
with open(file_path, 'w') as file:
file.write(data + '\n')
return f"Data appended to {file_path}"
def count_questions(file_path):
"""Count lines in file."""
with open(file_path, 'r') as file:
return sum(1 for _ in file)
# Keep existing agents with minimal changes
qa_generator = Agent(
name="Generator",
role="Question Creator",
goal="Create challenging math and logic questions",
backstory="Expert in educational content creation",
llm="gpt-4o-mini",
tools=[write_csv, count_questions]
)
cot_generator = Agent(
name="COTGenerator",
role="Chain of Thought Specialist",
goal="Generate and manage chain of thought solutions for Q&A pairs",
backstory="Expert in breaking down problems and generating detailed solution steps",
tools=[print_data],
llm="gpt-4o-mini",
verbose=False
)
upload_to_huggingface = Agent(
name="UploadToHuggingface",
role="Upload to Huggingface",
goal="Upload the generated chain of thought solutions to a Huggingface dataset",
backstory="Expert in saving data to Huggingface",
tools=[upload_to_huggingface],
llm="gpt-4o-mini",
verbose=False
)
# Define tasks with workflow improvements
generate_task = Task(
description="""Generate question and answer in csv format without headers: question, answer and append to qa_pairs.csv file
generate 1 unique questions and answers and don't repeat on the same question and answer. Reponse with 'done' when done
with append mode as 'a'
Example question and answer:
question, answer
What is the sum of numbers from 1 to 10?, 55
Number of r's in the word strawberry, 3
""",
expected_output="append to qa_pairs.csv file with questions and answers and move to next task",
agent=qa_generator,
name="generate_task",
is_start=True,
next_tasks=["evaluate_total_questions"],
task_type="decision",
condition={
"more": "generate_task",
"done": "generate_cot"
}
)
generate_cot_task = Task(
name="generate_cot",
description="""Generate chain of thought solutions for each question in the input file.
After processing all questions, respond with a JSON object:
{
"response": "done",
"decision": "done"
}
""",
expected_output="done",
agent=cot_generator,
input_file="qa_pairs.csv",
task_type="loop",
next_tasks=["upload_to_huggingface"],
condition={
"done": ["upload_to_huggingface"],
"exit": [],
},
output_pydantic=DecisionModel # Use Pydantic model for output validation
)
upload_to_huggingface_task = Task(
name="upload_to_huggingface",
description="""Upload to Huggingface:
1. Save to cot_solutions.csv
2. Upload to mervinpraison/cot-dataset""",
expected_output="Dataset published successfully",
agent=upload_to_huggingface
)
# Initialize workflow
agents = PraisonAIAgents(
agents=[qa_generator, cot_generator, upload_to_huggingface],
tasks=[generate_task, generate_cot_task, upload_to_huggingface_task],
process="workflow",
max_iter=10,
verbose=False
)
agents.start()
+1 -1
View File
@@ -1,6 +1,6 @@
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask praisonai==2.0.56 gunicorn markdown
RUN pip install flask praisonai==2.0.60 gunicorn markdown
EXPOSE 8080
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+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.56 gunicorn markdown\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.0.60 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>
+75
View File
@@ -0,0 +1,75 @@
---
title: 'Local Development'
description: 'Set up local development environment with Docker and live reload'
---
## Logging
```bash
export LOGLEVEL=info
```
Advanced logging:
```bash
export LOGLEVEL=debug
```
## Local Docker Development with Live Reload
To facilitate local development with live reload, you can use Docker. Follow the steps below:
### 1. Create Dockerfile.dev
<CodeGroup>
```dockerfile Dockerfile.dev
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask praisonai==2.0.18 watchdog
EXPOSE 5555
ENV FLASK_ENV=development
CMD ["flask", "run", "--host=0.0.0.0"]
```
</CodeGroup>
### 2. Create docker-compose.yml
<CodeGroup>
```yaml docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
volumes:
- .:/app
ports:
- "5555:5555"
environment:
FLASK_ENV: development
command: flask run --host=0.0.0.0
watch:
image: alpine:latest
volumes:
- .:/app
command: sh -c "apk add --no-cache inotify-tools && while inotifywait -r -e modify,create,delete /app; do kill -HUP 1; done"
```
</CodeGroup>
### 3. Run Docker Compose
<CodeGroup>
```bash Terminal
docker-compose up
```
</CodeGroup>
This setup will allow you to develop locally with live reload, making it easier to test and iterate on your code.
+244
View File
@@ -0,0 +1,244 @@
---
title: "Chat with PDF Agents"
sidebarTitle: "Chat with PDF"
description: "Learn how to create AI agents that can intelligently chat with PDF documents using vector databases for efficient information retrieval."
icon: "file-pdf"
---
```mermaid
flowchart LR
PDF[PDF Document] --> PDFAgent[("PDF Chat Agent")]
VDB[(Vector DB)] --> PDFAgent
PDFAgent --> Chat[Chat Interface]
Chat --> |Query| VDB
Chat --> Out[Response]
style PDF fill:#8B0000,color:#fff
style PDFAgent fill:#2E8B57,color:#fff,shape:circle
style VDB fill:#4169E1,color:#fff,shape:cylinder
style Chat fill:#2E8B57,color:#fff
style Out fill:#8B0000,color:#fff
```
A PDF-centric workflow where Chat agents interact with vector databases to store and retrieve information from PDF documents, enabling natural conversations and intelligent question-answering capabilities.
## Quick Start
<Steps>
<Step title="Install Package">
Install PraisonAI Agents with PDF chat support:
```bash
pip install "praisonaiagents[knowledge]"
```
</Step>
<Step title="Set API Key">
Set your OpenAI API key:
```bash
export OPENAI_API_KEY=xxxxx
```
</Step>
<Step title="Create Script">
Create a new file `chat_with_pdf.py`:
```python
from praisonaiagents import Agent
agent = Agent(
name="PDF Chat Agent",
instructions="You answer questions based on the provided PDF document.",
knowledge=["document.pdf"], # PDF Indexing
)
agent.start("What is the main topic of this PDF?") # Chat Query
```
</Step>
</Steps>
## PDF Processing and Chat Agents
<Note>
PDF processing involves indexing the document content for efficient retrieval during chat.
</Note>
```mermaid
flowchart LR
subgraph In[Input]
PDF[PDF Documents]
end
subgraph Router[Vector Store]
DB[(Vector DB)]
end
subgraph Out[Chat Agents]
A1[Chat Agent 1]
A2[Chat Agent 2]
A3[Chat Agent 3]
end
In --> Router
Router --> A1
Router --> A2
Router --> A3
style In fill:#8B0000,color:#fff
style Router fill:#2E8B57,color:#fff
style Out fill:#8B0000,color:#fff
```
The simplest way to create a PDF chat agent is without any configuration:
```python
from praisonaiagents import Agent
agent = Agent(
name="PDF Chat Agent",
instructions="You answer questions based on the provided PDF document.",
knowledge=["document.pdf"] # PDF Indexing
)
agent.start("What are the key points in this document?") # Chat Query
```
### Advanced Configuration
For more control over the knowledge base, you can specify a configuration:
```python
from praisonaiagents import Agent
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison",
}
}
}
agent = Agent(
name="PDF Chat Agent",
instructions="You answer questions based on the provided PDF document.",
knowledge=["document.pdf"], # PDF Indexing
knowledge_config=config # Configuration
)
agent.start("What is the main topic of this PDF?") # Chat Query
```
### Multi-Agent Knowledge System
For more complex scenarios, you can create a knowledge-based system with multiple agents:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
import logging
import os
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
logger = logging.getLogger(__name__)
# Define the configuration for the Knowledge instance
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison",
}
}
}
# Create an agent with knowledge capabilities
knowledge_agent = Agent(
name="KnowledgeAgent",
role="Information Specialist",
goal="Store and retrieve knowledge efficiently",
backstory="Expert in managing and utilizing stored knowledge",
knowledge=["sample.pdf"], # Indexing
knowledge_config=config, # Configuration
verbose=True
)
# Define a task for the agent
knowledge_task = Task(
name="knowledge_task",
description="Who is Mervin Praison?",
expected_output="Answer to the question",
agent=knowledge_agent # Agent
)
# Create and start the agents
agents = PraisonAIAgents(
agents=[knowledge_agent],
tasks=[knowledge_task],
process="sequential",
user_id="user1" # User ID
)
# Start execution
result = agents.start() # Retrieval
```
## Understanding PDF Chat Agents
<Card title="What are PDF Chat Agents?" icon="question">
PDF Chat agents enable:
- Natural conversation with PDF documents
- Intelligent information extraction
- Context-aware document understanding
- Quick answers to document-specific questions
</Card>
## Features
<CardGroup cols={2}>
<Card title="PDF Processing" icon="file-pdf">
Process and index PDF documents efficiently.
</Card>
<Card title="Natural Chat" icon="comments">
Have natural conversations about PDF content.
</Card>
<Card title="Smart Retrieval" icon="brain">
Intelligently retrieve relevant information from PDFs.
</Card>
<Card title="Context Awareness" icon="layer-group">
Maintain context throughout the conversation.
</Card>
</CardGroup>
## Troubleshooting
<CardGroup cols={2}>
<Card title="PDF Issues" icon="triangle-exclamation">
If PDF processing isn't working:
- Check PDF file format and encoding
- Verify document accessibility
- Enable verbose mode for debugging
</Card>
<Card title="Chat Issues" icon="gauge-high">
If chat responses aren't accurate:
- Check PDF indexing quality
- Verify question clarity
- Monitor context retention
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="AutoAgents" icon="robot" href="./autoagents">
Learn about automatically created and managed AI agents
</Card>
<Card title="Mini Agents" icon="microchip" href="./mini">
Explore lightweight, focused AI agents
</Card>
</CardGroup>
<Note>
For optimal chat experience, ensure your PDFs are properly formatted and text-searchable.
</Note>
+289
View File
@@ -0,0 +1,289 @@
---
title: "Generate Synthetic Reasoning Data Agents"
sidebarTitle: "Generate Reasoning Data"
description: "Learn how to generate chain-of-thought reasoning data using PraisonAI Agents."
icon: "brain"
---
```mermaid
flowchart TD
Start[Start] --> Generator[Question Answer Generator Agent]
Generator --> Evaluator[Evaluator Agent]
Evaluator --> COT[Reasoning Steps / COT Generator Agent]
COT --> COT
COT --> Upload[HuggingFace Uploader Agent]
Upload --> End[End]
style Start fill:#8B0000,color:#fff
style Generator fill:#2E8B57,color:#fff
style Evaluator fill:#2E8B57,color:#fff
style COT fill:#2E8B57,color:#fff
style Upload fill:#2E8B57,color:#fff
style End fill:#8B0000,color:#fff
```
## What is Chain-of-Thought Generation?
Chain-of-Thought (CoT) Generation is a process where AI agents create detailed, step-by-step reasoning paths for solving problems. This involves generating questions, evaluating them, producing detailed solution steps, and making the data available for training and analysis.
## Quick Start
<Steps>
<Step title="Install Package">
First, install the PraisonAI Agents package:
```bash
pip install "praisonaiagents[llm]" datasets huggingface-hub pandas
```
</Step>
<Step title="Set API Key">
Set your OpenAI API key as an environment variable in your terminal:
```bash
export OPENAI_API_KEY=your_api_key_here
```
</Step>
<Step title="Create a file">
Create a new file `app.py` with the basic setup:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import cot_save, cot_upload_to_huggingface
from pydantic import BaseModel
import os
# Define Pydantic model for structured output
class DecisionModel(BaseModel):
response: str
decision: str
def write_csv(file_path, data):
"""Write data to CSV file."""
if not os.path.exists(file_path):
with open(file_path, 'w') as file:
file.write(data + '\n')
else:
with open(file_path, 'a') as file:
file.write(data + '\n')
return f"Data appended to {file_path}"
def count_questions(file_path):
"""Count lines in file."""
with open(file_path, 'r') as file:
return sum(1 for _ in file)
# Create specialized agents
qa_generator = Agent(
name="Generator",
role="Question Creator",
goal="Create challenging math and logic questions",
backstory="Expert in educational content creation",
llm="gpt-4o-mini",
tools=[write_csv, count_questions]
)
total_questions_evaluator = Agent(
name="TotalQuestionsEvaluator",
role="Total Questions Evaluator",
goal="Evaluate the total number of questions in qa_pairs.csv file",
backstory="Expert in evaluating the total number of questions in a file",
llm="gpt-4o-mini",
tools=[count_questions],
verbose=False
)
cot_generator = Agent(
name="COTGenerator",
role="Chain of Thought Specialist",
goal="Generate and manage chain of thought solutions for Q&A pairs",
backstory="Expert in breaking down problems and generating detailed solution steps",
tools=[cot_save],
llm="gpt-4o-mini",
verbose=False
)
upload_to_huggingface = Agent(
name="UploadToHuggingface",
role="Upload to Huggingface",
goal="Upload the generated chain of thought solutions to a Huggingface dataset",
backstory="Expert in saving data to Huggingface",
tools=[cot_upload_to_huggingface],
llm="gpt-4o-mini",
verbose=False
)
# Define tasks with workflow
generate_task = Task(
description="""Generate question and answer in csv format without headers: question, answer and append to qa_pairs.csv file
generate 10 unique questions and answers and don't repeat on the same question and answer. Reponse with 'done' when done
with append mode as 'a'
Example question and answer:
question, answer
What is the sum of numbers from 1 to 10?, 55
Number of r's in the word strawberry, 3
""",
expected_output="append to qa_pairs.csv file with questions and answers and move to next task",
agent=qa_generator,
name="generate_task",
is_start=True,
next_tasks=["evaluate_total_questions"],
task_type="decision",
condition={
"more": "generate_task",
"done": "evaluate_total_questions"
}
)
evaluate_total_questions_task = Task(
description="Evaluate the total number of questions in qa_pairs.csv file is 1",
expected_output="Total number of questions in qa_pairs.csv file",
agent=total_questions_evaluator,
task_type="decision",
name="evaluate_total_questions",
condition={
"more": "generate_task",
"done": "generate_cot"
}
)
generate_cot_task = Task(
name="generate_cot",
description="""Generate chain of thought solutions for each question in the input file.
Save to cot_solutions.csv file
Don't generate chain of thought solutions again after receiving the response from Tool Call
After calling the tool, respond with a JSON object:
{
"response": "done",
"decision": "done"
}
""",
expected_output="done",
agent=cot_generator,
input_file="qa_pairs.csv",
task_type="loop",
next_tasks=["upload_to_huggingface"],
condition={
"done": ["upload_to_huggingface"],
"exit": [],
},
output_pydantic=DecisionModel # Use Pydantic model for output validation
)
upload_to_huggingface_task = Task(
name="upload_to_huggingface",
description="""Upload to Huggingface:
1. Save to cot_solutions.csv
2. Upload to mervinpraison/cot-dataset""",
expected_output="Dataset published successfully",
agent=upload_to_huggingface,
tools=[cot_upload_to_huggingface]
)
# Initialize workflow
agents = PraisonAIAgents(
agents=[qa_generator, total_questions_evaluator, cot_generator, upload_to_huggingface],
tasks=[generate_task, evaluate_total_questions_task, generate_cot_task, upload_to_huggingface_task],
process="workflow",
max_iter=30,
verbose=False
)
agents.start()
```
</Step>
</Steps>
## Features
<CardGroup cols={2}>
<Card title="Question Generation" icon="question">
Create challenging math and logic questions with answers.
</Card>
<Card title="Question Evaluation" icon="check-double">
Evaluate and validate generated questions for quality.
</Card>
<Card title="CoT Solutions" icon="diagram-project">
Generate detailed chain-of-thought solutions for each question.
</Card>
<Card title="Data Management" icon="database">
Save and manage generated data in structured formats.
</Card>
<Card title="HuggingFace Integration" icon="cloud-arrow-up">
Upload datasets directly to HuggingFace for sharing.
</Card>
</CardGroup>
## Understanding the Workflow
<AccordionGroup>
<Accordion title="Key Components">
<CardGroup cols={2}>
<Card title="Question Generator" icon="robot">
Creates unique math and logic questions with answers. Uses `write_csv` and `count_questions` tools.
</Card>
<Card title="Questions Evaluator" icon="magnifying-glass-chart">
Validates the total number of generated questions. Uses `count_questions` tool.
</Card>
<Card title="CoT Generator" icon="diagram-project">
Produces detailed step-by-step solutions. Uses `cot_save` tool for solution management.
</Card>
<Card title="HuggingFace Uploader" icon="cloud-arrow-up">
Publishes datasets to HuggingFace. Uses `cot_upload_to_huggingface` tool.
</Card>
</CardGroup>
</Accordion>
<Accordion title="Task Types and Flow Control">
<Tabs>
<Tab title="Decision Tasks">
Used in question generation and evaluation phases.
```python Decision Task Example
generate_task = Task(
task_type="decision",
condition={
"more": "generate_task",
"done": "evaluate_total_questions"
}
)
```
<Note>
Conditions determine whether to continue generating or move forward. The task can loop back to itself or proceed to the next task.
</Note>
</Tab>
<Tab title="Loop Tasks">
Used in Chain-of-Thought generation phase.
```python Loop Task Example
generate_cot_task = Task(
task_type="loop",
input_file="qa_pairs.csv",
output_pydantic=DecisionModel
)
```
<Warning>
Always use Pydantic models for output validation in loop tasks to ensure data consistency.
</Warning>
</Tab>
</Tabs>
<Info>
Each task type serves a specific purpose in the workflow:
- **Decision Tasks**: Control flow and branching logic
- **Loop Tasks**: Process data iteratively with validation
</Info>
</Accordion>
</AccordionGroup>
## Next Steps
<CardGroup>
<Card title="Introduction" icon="book" href="/introduction">
Learn more about PraisonAI and its core concepts
</Card>
<Card title="Quick Start" icon="bolt" href="/quickstart">
Get started with the basics of PraisonAI
</Card>
</CardGroup>
+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>
+9 -2
View File
@@ -118,15 +118,18 @@
"features/autoagents",
"features/selfreflection",
"features/rag",
"features/reasoning-extract",
"features/reasoning",
"features/multimodal",
"features/langchain",
"features/async",
"features/mini",
"features/generate-reasoning",
"features/codeagent",
"features/mathagent",
"features/structured",
"features/callbacks"
"features/callbacks",
"features/chat-with-pdf"
]
},
{
@@ -251,7 +254,10 @@
"pages": [
"ui/streamlit",
"ui/streamlit/code-analysis-streamlit",
"ui/streamlit/hackathon-judge-streamlit"
"ui/streamlit/hackathon-judge-streamlit",
"ui/streamlit/ollama-streamlit",
"ui/streamlit/deepseek-streamlit",
"ui/streamlit/gemini-streamlit"
]
},
"ui/ui",
@@ -272,6 +278,7 @@
"developers/wrapper-tools",
"developers/googlecolab",
"developers/googlecolab-tools",
"developers/local-development",
"deploy"
]
},
+178 -1
View File
@@ -4,7 +4,184 @@ description: "Overview of supported language models in PraisonAI, including Open
icon: "brain"
---
## Supported Models for Code
# Code
## Set model by 3 ways
### 1. OpenAI Compatible Endpoints
<Note>By Default it uses OPENAI_BASE_URL https://api.openai.com/v1 </Note>
Example Groq Implementation:
```bash
export OPENAI_API_KEY=groq-api-key
export OPENAI_BASE_URL=https://api.groq.com/openai/v1
```
```python
from praisonaiagents import Agent
agent = Agent(
instructions="You are a helpful assistant",
llm="llama-3.1-8b-instant",
)
agent.start("Why sky is Blue?")
```
### 2. Litellm Compatible model names (eg: gemini/gemini-1.5-flash-8b)
```bash
pip install "praisonaiagents[llm]"
```
```python
from praisonaiagents import Agent
agent = Agent(
instructions="You are a helpful assistant",
llm="gemini/gemini-1.5-flash-8b",
self_reflect=True,
verbose=True
)
agent.start("Why sky is Blue?")
```
### 3. Litellm Compatible Configuration
```bash
pip install "praisonaiagents[llm]"
```
```python
from praisonaiagents import Agent
llm_config = {
"model": "gemini/gemini-1.5-flash-latest", # Model name without provider prefix
# Core settings
"temperature": 0.7, # Controls randomness (like temperature)
"timeout": 30, # Timeout in seconds
"top_p": 0.9, # Nucleus sampling parameter
"max_tokens": 1000, # Max tokens in response
# Advanced parameters
"presence_penalty": 0.1, # Penalize repetition of topics (-2.0 to 2.0)
"frequency_penalty": 0.1, # Penalize token repetition (-2.0 to 2.0)
# API settings (optional)
"api_key": None, # Your API key (or use environment variable)
"base_url": None, # Custom API endpoint if needed
# Response formatting
"response_format": { # Force specific response format
"type": "text" # Options: "text", "json_object"
},
# Additional controls
"seed": 42, # For reproducible responses
"stop_phrases": ["##", "END"], # Custom stop sequences
}
agent = Agent(
instructions="You are a helpful Assistant."
llm=llm_config
)
agent.start()
```
## Advanced Configuration (Litellm Support)
<Note>This uses Litellm</Note>
<Steps>
<Step title="Install Package">
Install required packages:
```bash
pip install "praisonaiagents[llm]"
```
</Step>
<Step title="Setup Environment">
Configure environment:
```bash
export GOOGLE_API_KEY=your-api-key
```
<Note>
Get your API key from [Google AI Studio](https://makersuite.google.com/app/apikey)
</Note>
</Step>
<Step title="Create Agent">
Create `app.py`:
<CodeGroup>
```python Basic
# if json_object is supported by the model
from praisonaiagents import Agent
agent = Agent(
instructions="You are a helpful assistant",
llm="gemini/gemini-1.5-flash-8b",
self_reflect=True,
verbose=True
)
agent.start("Why sky is Blue?")
```
```python Advanced
# if json_object is not supported by the model
from praisonaiagents import Agent
# Detailed LLM configuration
llm_config = {
"model": "gemini/gemini-1.5-flash-latest", # Model name without provider prefix
# Core settings
"temperature": 0.7, # Controls randomness (like temperature)
"timeout": 30, # Timeout in seconds
"top_p": 0.9, # Nucleus sampling parameter
"max_tokens": 1000, # Max tokens in response
# Advanced parameters
"presence_penalty": 0.1, # Penalize repetition of topics (-2.0 to 2.0)
"frequency_penalty": 0.1, # Penalize token repetition (-2.0 to 2.0)
# API settings (optional)
"api_key": None, # Your API key (or use environment variable)
"base_url": None, # Custom API endpoint if needed
# Response formatting
"response_format": { # Force specific response format
"type": "text" # Options: "text", "json_object"
},
# Additional controls
"seed": 42, # For reproducible responses
"stop_phrases": ["##", "END"], # Custom stop sequences
}
agent = Agent(
instructions="You are a helpful Assistant specialized in scientific explanations. "
"Provide clear, accurate, and engaging responses.",
llm=llm_config, # Pass the detailed configuration
verbose=True, # Enable detailed output
markdown=True, # Format responses in markdown
self_reflect=True, # Enable self-reflection
max_reflect=3, # Maximum reflection iterations
min_reflect=1 # Minimum reflection iterations
)
# Test the agent
response = agent.start("Why is the sky blue? Please explain in simple terms.")
```
</CodeGroup>
</Step>
</Steps>
<AccordionGroup>
<Accordion title="Ollama Integration" defaultOpen>
+43
View File
@@ -115,6 +115,49 @@ agents.start()
This uses Litellm to connect to Google Gemini.
</Note>
## Gemini 2.0 Flash Thinking
### LLM Configuration
```python
llm_config = {
"model": "gemini/gemini-2.0-flash-thinking-exp-01-21",
"response_format": {"type": "text"} # type is text, because json_object is not supported
}
```
<Steps>
<Step title="Install Package">
Install required packages:
```bash
pip install "praisonaiagents[llm]"
```
</Step>
<Step title="Set API Key">
Set your Gemini API key:
```bash
export GEMINI_API_KEY=xxxxxxxxxxx
```
</Step>
<Step title="Code">
```python
from praisonaiagents import Agent
llm_config = {"model": "gemini/gemini-2.0-flash-thinking-exp-01-21","response_format": {"type": "text"}}
agent = Agent(
instructions="You are a helpful assistant",
llm=llm_config
)
result = agent.start("Why sky is Blue?")
print(result)
```
</Step>
</Steps>
## Alternative Setup
### Using Environment Variables
+158
View File
@@ -0,0 +1,158 @@
---
title: "Deepseek Streamlit UI"
description: "Create interactive chat interfaces with Deepseek models using Streamlit"
icon: "window"
---
```mermaid
flowchart LR
In[Input] --> UI[("Streamlit UI")]
VDB[(Vector DB)] --> Agent
DS[("Deepseek")] --> Agent
UI --> Agent[("Knowledge Agent")]
Agent --> |Query| VDB
Agent --> Out[Output]
Out --> UI
style In fill:#8B0000,color:#fff
style UI fill:#FF4B4B,color:#fff,shape:circle
style DS fill:#4169E1,color:#fff,shape:circle
style Agent fill:#2E8B57,color:#fff,shape:circle
style VDB fill:#4169E1,color:#fff,shape:cylinder
style Out fill:#8B0000,color:#fff
```
## Prerequisites
<Steps>
<Step title="Install Package">
Install required packages:
```bash
pip install "praisonaiagents[knowledge]" streamlit ollama
```
<Note>
streamlit for UI
ollama for Deepseek model hosting
praisonaiagents[knowledge] for RAG capabilities
</Note>
</Step>
<Step title="Setup Model">
Pull Deepseek model:
```bash
# Large Language Model
ollama pull deepseek-r1
# Embedding Model
ollama pull nomic-embed-text
```
</Step>
<Step title="Setup Environment">
Configure environment:
```bash
export OPENAI_BASE_URL=http://localhost:11434/v1
export OPENAI_API_KEY=fake-key
```
</Step>
<Step title="Create File">
Create a new file called `app.py` and add the following code:
</Step>
<Step title="Run Application">
Start the Streamlit application:
```bash
streamlit run app.py
```
</Step>
</Steps>
## Code
```python
import streamlit as st
from praisonaiagents import Agent
def init_agent():
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison"
}
},
"llm": {
"provider": "ollama",
"config": {
"model": "deepseek-r1:latest",
"temperature": 0,
"max_tokens": 8000,
"ollama_base_url": "http://localhost:11434",
},
},
"embedder": {
"provider": "ollama",
"config": {
"model": "nomic-embed-text:latest",
"ollama_base_url": "http://localhost:11434",
"embedding_dims": 1536
},
},
}
return Agent(
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["kag-research-paper.pdf"],
knowledge_config=config,
user_id="user1",
llm="deepseek-r1"
)
st.title("Knowledge Agent Chat")
if "agent" not in st.session_state:
st.session_state.agent = init_agent()
st.session_state.messages = []
if "messages" in st.session_state:
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
prompt = st.chat_input("Ask a question...")
if prompt:
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
with st.chat_message("assistant"):
response = st.session_state.agent.start(prompt)
st.markdown(response)
st.session_state.messages.append({"role": "assistant", "content": response})
```
## Features
<CardGroup cols={2}>
<Card title="Interactive Chat" icon="comments">
Real-time chat interface with message history.
</Card>
<Card title="Knowledge Base" icon="database">
RAG capabilities with ChromaDB integration.
</Card>
<Card title="Model Integration" icon="server">
Uses Deepseek through Ollama.
</Card>
<Card title="Session Management" icon="clock-rotate-left">
Maintains chat history in session state.
</Card>
</CardGroup>
<Note>
Make sure your system meets the requirements for running Deepseek models locally through Ollama.
</Note>
+119
View File
@@ -0,0 +1,119 @@
---
title: "Gemini Streamlit UI"
description: "Create interactive chat interfaces with Google's Gemini models using Streamlit"
icon: "window"
---
```mermaid
flowchart LR
In[Input] --> UI[("Streamlit UI")]
VDB[(Vector DB)] --> Agent
Gemini[("Gemini")] --> Agent
UI --> Agent[("Knowledge Agent")]
Agent --> |Query| VDB
Agent --> Out[Output]
Out --> UI
style In fill:#8B0000,color:#fff
style UI fill:#FF4B4B,color:#fff,shape:circle
style Gemini fill:#4169E1,color:#fff,shape:circle
style Agent fill:#2E8B57,color:#fff,shape:circle
style VDB fill:#4169E1,color:#fff,shape:cylinder
style Out fill:#8B0000,color:#fff
```
## Prerequisites
<Steps>
<Step title="Install Package">
Install required packages:
```bash
pip install "praisonaiagents[llm]" streamlit
```
<Note>
streamlit for UI<br />
praisonaiagents[llm] for Gemini model access (It uses Litellm)
</Note>
</Step>
<Step title="Setup Environment">
Configure environment:
```bash
export GOOGLE_API_KEY=your-api-key
```
<Note>
Get your API key from [Google AI Studio](https://makersuite.google.com/app/apikey)
</Note>
</Step>
<Step title="Create File">
Create a new file called `app.py` and add the following code:
</Step>
<Step title="Run Application">
Start the Streamlit application:
```bash
streamlit run app.py
```
</Step>
</Steps>
## Code
```python
import streamlit as st
from praisonaiagents import Agent
st.title("Gemini 2.0 Thinking AI Agent")
# Initialize the agent
@st.cache_resource
def get_agent():
llm_config = {
"model": "gemini/gemini-2.0-flash-thinking-exp-01-21",
"response_format": {"type": "text"}
}
return Agent(
instructions="You are a helpful assistant",
llm=llm_config
)
agent = get_agent()
# Create text area input field
user_question = st.text_area("Ask your question:", height=150)
# Add ask button
if st.button("Ask"):
if user_question:
with st.spinner('Thinking...'):
result = agent.start(user_question)
st.write("### Answer")
st.write(result)
else:
st.warning("Please enter a question")
```
## Features
<CardGroup cols={2}>
<Card title="Interactive Chat" icon="comments">
Real-time chat interface with message history.
</Card>
<Card title="Knowledge Base" icon="database">
RAG capabilities with ChromaDB integration.
</Card>
<Card title="Model Integration" icon="server">
Uses Google's Gemini Pro model.
</Card>
<Card title="Session Management" icon="clock-rotate-left">
Maintains chat history in session state.
</Card>
</CardGroup>
<Note>
Make sure you have a valid Google API key and sufficient quota for using Gemini models.
</Note>
+158
View File
@@ -0,0 +1,158 @@
---
title: "Ollama Streamlit UI"
description: "Create interactive chat interfaces with Ollama models using Streamlit"
icon: "window"
---
```mermaid
flowchart LR
In[Input] --> UI[("Streamlit UI")]
VDB[(Vector DB)] --> Agent
Ollama[("Ollama")] --> Agent
UI --> Agent[("Knowledge Agent")]
Agent --> |Query| VDB
Agent --> Out[Output]
Out --> UI
style In fill:#8B0000,color:#fff
style UI fill:#FF4B4B,color:#fff,shape:circle
style Ollama fill:#4169E1,color:#fff,shape:circle
style Agent fill:#2E8B57,color:#fff,shape:circle
style VDB fill:#4169E1,color:#fff,shape:cylinder
style Out fill:#8B0000,color:#fff
```
## Prerequisites
<Steps>
<Step title="Install Package">
Install required packages:
```bash
pip install "praisonaiagents[knowledge]" streamlit ollama
```
<Note>
streamlit for UI
ollama for model hosting
praisonaiagents[knowledge] for RAG capabilities
</Note>
</Step>
<Step title="Setup Model">
Pull Ollama models:
```bash
# Large Language Model
ollama pull deepseek-r1
# Embedding Model
ollama pull nomic-embed-text
```
</Step>
<Step title="Setup Environment">
Configure environment:
```bash
export OPENAI_BASE_URL=http://localhost:11434/v1
export OPENAI_API_KEY=fake-key
```
</Step>
<Step title="Create File">
Create a new file called `app.py` and add the following code:
</Step>
<Step title="Run Application">
Start the Streamlit application:
```bash
streamlit run app.py
```
</Step>
</Steps>
## Code
```python
import streamlit as st
from praisonaiagents import Agent
def init_agent():
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison"
}
},
"llm": {
"provider": "ollama",
"config": {
"model": "deepseek-r1:latest",
"temperature": 0,
"max_tokens": 8000,
"ollama_base_url": "http://localhost:11434",
},
},
"embedder": {
"provider": "ollama",
"config": {
"model": "nomic-embed-text:latest",
"ollama_base_url": "http://localhost:11434",
"embedding_dims": 1536
},
},
}
return Agent(
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["kag-research-paper.pdf"],
knowledge_config=config,
user_id="user1",
llm="deepseek-r1"
)
st.title("Knowledge Agent Chat")
if "agent" not in st.session_state:
st.session_state.agent = init_agent()
st.session_state.messages = []
if "messages" in st.session_state:
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
prompt = st.chat_input("Ask a question...")
if prompt:
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
with st.chat_message("assistant"):
response = st.session_state.agent.start(prompt)
st.markdown(response)
st.session_state.messages.append({"role": "assistant", "content": response})
```
## Features
<CardGroup cols={2}>
<Card title="Interactive Chat" icon="comments">
Real-time chat interface with message history.
</Card>
<Card title="Knowledge Base" icon="database">
RAG capabilities with ChromaDB integration.
</Card>
<Card title="Model Integration" icon="server">
Uses Ollama for local model hosting.
</Card>
<Card title="Session Management" icon="clock-rotate-left">
Maintains chat history in session state.
</Card>
</CardGroup>
<Note>
Make sure your system meets the requirements for running models locally through Ollama.
</Note>
+142
View File
@@ -0,0 +1,142 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import cot_save, cot_upload_to_huggingface
from pydantic import BaseModel
import os
# Define Pydantic model for structured output
class DecisionModel(BaseModel):
response: str
decision: str
def write_csv(file_path, data):
"""Write data to CSV file."""
if not os.path.exists(file_path):
with open(file_path, 'w') as file:
file.write(data + '\n')
else:
with open(file_path, 'a') as file:
file.write(data + '\n')
return f"Data appended to {file_path}"
def count_questions(file_path):
"""Count lines in file."""
with open(file_path, 'r') as file:
return sum(1 for _ in file)
# Keep existing agents with minimal changes
qa_generator = Agent(
name="Generator",
role="Question Creator",
goal="Create challenging math and logic questions",
backstory="Expert in educational content creation",
llm="gpt-4o-mini",
tools=[write_csv, count_questions]
)
total_questions_evaluator = Agent(
name="TotalQuestionsEvaluator",
role="Total Questions Evaluator",
goal="Evaluate the total number of questions in qa_pairs.csv file",
backstory="Expert in evaluating the total number of questions in a file",
llm="gpt-4o-mini",
tools=[count_questions],
verbose=False
)
cot_generator = Agent(
name="COTGenerator",
role="Chain of Thought Specialist",
goal="Generate and manage chain of thought solutions for Q&A pairs",
backstory="Expert in breaking down problems and generating detailed solution steps",
tools=[cot_save],
llm="gpt-4o-mini",
verbose=False
)
upload_to_huggingface = Agent(
name="UploadToHuggingface",
role="Upload to Huggingface",
goal="Upload the generated chain of thought solutions to a Huggingface dataset",
backstory="Expert in saving data to Huggingface",
tools=[cot_upload_to_huggingface],
llm="gpt-4o-mini",
verbose=False
)
# Define tasks with workflow improvements
generate_task = Task(
description="""Generate question and answer in csv format without headers: question, answer and append to qa_pairs.csv file
generate 10 unique questions and answers and don't repeat on the same question and answer. Reponse with 'done' when done
with append mode as 'a'
Example question and answer:
question, answer
What is the sum of numbers from 1 to 10?, 55
Number of r's in the word strawberry, 3
""",
expected_output="append to qa_pairs.csv file with questions and answers and move to next task",
agent=qa_generator,
name="generate_task",
is_start=True,
next_tasks=["evaluate_total_questions"],
task_type="decision",
condition={
"more": "generate_task",
"done": "evaluate_total_questions"
}
)
evaluate_total_questions_task = Task(
description="Evaluate the total number of questions in qa_pairs.csv file is 1",
expected_output="Total number of questions in qa_pairs.csv file",
agent=total_questions_evaluator,
task_type="decision",
name="evaluate_total_questions",
condition={
"more": "generate_task",
"done": "generate_cot"
}
)
generate_cot_task = Task(
name="generate_cot",
description="""Generate chain of thought solutions for each question in the input file.
Save to cot_solutions.csv file
Don't generate chain of thought solutions again after receiving the response from Tool Call
After calling the tool, respond with a JSON object:
{
"response": "done",
"decision": "done"
}
""",
expected_output="done",
agent=cot_generator,
input_file="qa_pairs.csv",
task_type="loop",
next_tasks=["upload_to_huggingface"],
condition={
"done": ["upload_to_huggingface"],
"exit": [],
},
output_pydantic=DecisionModel # Use Pydantic model for output validation
)
upload_to_huggingface_task = Task(
name="upload_to_huggingface",
description="""Upload to Huggingface:
1. Save to cot_solutions.csv
2. Upload to mervinpraison/cot-dataset""",
expected_output="Dataset published successfully",
agent=upload_to_huggingface,
tools=[cot_upload_to_huggingface]
)
# Initialize workflow
agents = PraisonAIAgents(
agents=[qa_generator, total_questions_evaluator, cot_generator, upload_to_huggingface],
tasks=[generate_task, evaluate_total_questions_task, generate_cot_task, upload_to_huggingface_task],
process="workflow",
max_iter=30,
verbose=False
)
agents.start()
+32
View File
@@ -0,0 +1,32 @@
import streamlit as st
from praisonaiagents import Agent
st.title("Gemini 2.0 Thinking AI Agent")
# Initialize the agent
@st.cache_resource
def get_agent():
llm_config = {
"model": "gemini/gemini-2.0-flash-thinking-exp-01-21",
"response_format": {"type": "text"}
}
return Agent(
instructions="You are a helpful assistant",
llm=llm_config
)
agent = get_agent()
# Create text area input field
user_question = st.text_area("Ask your question:", height=150)
# Add ask button
if st.button("Ask"):
if user_question:
with st.spinner('Thinking...'):
result = agent.start(user_question)
st.write("### Answer")
st.write(result)
else:
st.warning("Please enter a question")
@@ -0,0 +1,62 @@
import streamlit as st
from praisonaiagents import Agent
def init_agent():
config = {
"vector_store": {
"provider": "chroma",
"config": {
"collection_name": "praison",
"path": ".praison"
}
},
"llm": {
"provider": "ollama",
"config": {
"model": "deepseek-r1:latest",
"temperature": 0,
"max_tokens": 8000,
"ollama_base_url": "http://localhost:11434",
},
},
"embedder": {
"provider": "ollama",
"config": {
"model": "nomic-embed-text:latest",
"ollama_base_url": "http://localhost:11434",
"embedding_dims": 1536
},
},
}
return Agent(
name="Knowledge Agent",
instructions="You answer questions based on the provided knowledge.",
knowledge=["kag-research-paper.pdf"],
knowledge_config=config,
user_id="user1",
llm="deepseek-r1"
)
st.title("Knowledge Agent Chat")
if "agent" not in st.session_state:
st.session_state.agent = init_agent()
st.session_state.messages = []
if "messages" in st.session_state:
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
prompt = st.chat_input("Ask a question...")
if prompt:
st.session_state.messages.append({"role": "user", "content": prompt})
with st.chat_message("user"):
st.markdown(prompt)
with st.chat_message("assistant"):
response = st.session_state.agent.start(prompt)
st.markdown(response)
st.session_state.messages.append({"role": "assistant", "content": response})
+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.56.tar.gz"
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/2.0.60.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.56 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.0.60 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.56"
version = "2.0.60"
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.49",
"praisonaiagents>=0.0.60",
"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.56"
version = "2.0.60"
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.49"
praisonaiagents = ">=0.0.60"
python-dotenv = ">=0.19.0"
instructor = ">=1.3.3"
PyYAML = ">=6.0"
View File
File renamed without changes.
Generated
+5 -5
View File
@@ -3060,7 +3060,7 @@ wheels = [
[[package]]
name = "praisonai"
version = "2.0.56"
version = "2.0.60"
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.49" },
{ name = "praisonaiagents", specifier = ">=0.0.60" },
{ 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.49"
version = "0.0.60"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "openai" },
{ name = "pydantic" },
{ name = "rich" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3c/8a/521d13124d338c5b9d397214656b7f76377ef65c885bcb3859a1484997c5/praisonaiagents-0.0.49.tar.gz", hash = "sha256:257e8572866edb989ed8c3808afec66f65ccdd47f47b5e8916a319863a01cc28", size = 85081 }
sdist = { url = "https://files.pythonhosted.org/packages/ba/31/19a0226dda6c6a367294b613296852e89294f5b839482157e8e7b8b6985a/praisonaiagents-0.0.60.tar.gz", hash = "sha256:38404fc976c086bde71ad51fff7b417a105574ba7877f71403625cef90494fc0", size = 100110 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/59/64/a80c2cbb760d4fa73383e661301cfacacb49598686fe0da79da3deed9d05/praisonaiagents-0.0.49-py3-none-any.whl", hash = "sha256:b5b1abb9dc668e0f238282c546b76ddbfc51a6d463c6cbc8cf8da4b829a14ff1", size = 103198 },
{ url = "https://files.pythonhosted.org/packages/13/b0/ae8e2b0b3d0dc1e259e670506530db46cc18ab8300e98323788299e62a95/praisonaiagents-0.0.60-py3-none-any.whl", hash = "sha256:a3a73b60bb8fd760bdeeeed3a7dded49223dc3a29044e73d2af174a0179f710d", size = 118558 },
]
[[package]]