Compare commits

...
72 Commits
Author SHA1 Message Date
Mervin Praison 57040333a7 Merge pull request #275 from MervinPraison/develop
Develop
2025-01-07 18:19:17 +00:00
MervinPraison 8c6800f876 adding langchain with uv 2025-01-07 18:18:46 +00:00
MervinPraison 0d701ae43c adding langchain 2025-01-07 14:15:32 +00:00
Mervin Praison 7a8c25621c Merge pull request #273 from MervinPraison/develop
fixing internet search tool
2025-01-07 06:55:38 +00:00
MervinPraison 218c5443b1 fixing internet search tool 2025-01-07 06:55:17 +00:00
Mervin Praison f56810341c Merge pull request #272 from MervinPraison/develop
updating docs
2025-01-07 06:09:35 +00:00
MervinPraison 4f601a573f updating docs 2025-01-07 06:09:14 +00:00
Mervin Praison 43c03f4813 Merge pull request #271 from MervinPraison/develop
fixing tools
2025-01-07 05:46:04 +00:00
MervinPraison 03856b74bf fixing tools 2025-01-07 05:45:42 +00:00
Mervin Praison eadfc0cff0 Merge pull request #270 from MervinPraison/develop
adding tools
2025-01-07 04:54:36 +00:00
MervinPraison f33c44d4c5 adding tools 2025-01-07 04:54:05 +00:00
Mervin Praison 3f7885289d Merge pull request #269 from MervinPraison/develop
adding mini docs
2025-01-07 00:55:43 +00:00
MervinPraison 833c795e9a adding mini docs 2025-01-07 00:55:29 +00:00
Mervin Praison c460c9da60 Merge pull request #268 from MervinPraison/develop
adding structured agents and code agents
2025-01-06 18:38:51 +00:00
MervinPraison 656887c700 adding structured agents and code agents 2025-01-06 18:26:56 +00:00
Mervin Praison e0089cac41 Merge pull request #267 from MervinPraison/develop
fixing bug setting openai_model_name env variable
2025-01-06 10:06:33 +00:00
MervinPraison bf5e069c7b fixing bug setting openai_model_name env variable 2025-01-06 10:06:11 +00:00
Mervin Praison 94cf264b4c Merge pull request #266 from MervinPraison/develop
setting openai_model_name env variable to set
2025-01-06 09:52:09 +00:00
MervinPraison ab44fd4b27 setting openai_model_name env variable to set 2025-01-06 09:51:50 +00:00
Mervin Praison 7bad6cf7e9 Merge pull request #265 from MervinPraison/develop
adding mini agents
2025-01-06 09:31:54 +00:00
MervinPraison 4ffbe48c58 adding mini agents 2025-01-06 09:31:39 +00:00
Mervin Praison 476eeb1184 Merge pull request #264 from MervinPraison/develop
bug fix ui load
2025-01-06 01:07:03 +00:00
MervinPraison 550769017a bug fix ui load 2025-01-06 01:06:24 +00:00
Mervin Praison cccd75ca75 Merge pull request #263 from MervinPraison/develop
Develop
2025-01-06 00:18:12 +00:00
MervinPraison 395f8fb5ef praisonai agents ui working with tools 2025-01-06 00:17:54 +00:00
MervinPraison e4893ad948 working with other callbacks 2025-01-05 23:08:20 +00:00
Mervin Praison e717b625d2 Merge pull request #262 from MervinPraison/develop
Develop
2025-01-05 07:39:54 +00:00
MervinPraison 84638ad759 praisonai ui working 2025-01-05 07:39:36 +00:00
MervinPraison 626bc963f7 ui working together 2025-01-05 07:11:06 +00:00
Mervin Praison efa7110b38 Merge pull request #260 from MervinPraison/develop
adding async callbacks
2025-01-05 00:20:03 +00:00
MervinPraison 539c10796c adding ui with instant messages working 2025-01-05 00:19:26 +00:00
MervinPraison 815becb6f2 async callback fixes for main display 2025-01-04 22:48:21 +00:00
MervinPraison 87790695db adding async callbacks 2025-01-04 17:19:19 +00:00
Mervin Praison 00b25bb195 Merge pull request #259 from MervinPraison/develop
adding callbacks
2025-01-04 12:32:23 +00:00
MervinPraison e46ca3248d adding callbacks 2025-01-04 12:32:02 +00:00
Mervin Praison 04f6beaa56 Merge pull request #258 from MervinPraison/develop
adding async examples
2025-01-04 07:43:40 +00:00
MervinPraison 3d99326192 adding async examples 2025-01-04 07:43:18 +00:00
Mervin Praison 70ced6acbb Merge pull request #257 from MervinPraison/develop
updating agents ui
2025-01-04 07:10:16 +00:00
MervinPraison dc14c06e41 updating agents ui 2025-01-04 07:09:57 +00:00
Mervin Praison deb1148c8a Merge pull request #256 from MervinPraison/develop
updating async docs
2025-01-04 05:22:24 +00:00
MervinPraison 3d6d7c0b02 updating async docs 2025-01-04 05:22:02 +00:00
Mervin Praison 1a5a25073e Merge pull request #255 from MervinPraison/develop
Adding async feature
2025-01-04 05:10:08 +00:00
MervinPraison 7b743e6982 Adding async feature 2025-01-04 05:09:52 +00:00
Mervin Praison 77a3512779 Merge pull request #253 from MervinPraison/develop
fixing project file
2025-01-03 16:16:13 +00:00
MervinPraison 2a4ec38077 fixing project file 2025-01-03 16:14:26 +00:00
Mervin Praison 25f59b5c26 Merge pull request #252 from MervinPraison/develop
fixing bug in ui
2025-01-03 15:54:53 +00:00
MervinPraison 7596ed252d fixing bug in ui 2025-01-03 15:54:38 +00:00
Mervin Praison 14bea558fb Merge pull request #251 from MervinPraison/develop
Updating docs
2025-01-03 05:50:30 +00:00
MervinPraison b5b3d8abd0 Updating docs 2025-01-03 05:50:16 +00:00
Mervin Praison 6f69067450 Merge pull request #250 from MervinPraison/develop
fixing a loopitems bug
2025-01-03 02:05:02 +00:00
MervinPraison 0b06a1c3ed fixing a loopitems bug 2025-01-03 02:04:05 +00:00
Mervin Praison 8907344649 Merge pull request #249 from MervinPraison/selective-pr-242
Retain original author: Refactor Docker setup and environment configu…
2025-01-03 01:47:24 +00:00
Jason Kneen 1221bf26e4 Retain original author: Refactor Docker setup and environment configuration for PraisonAI 2025-01-03 01:46:35 +00:00
Mervin Praison 92263bbb90 Merge pull request #248 from MervinPraison/develop
Develop
2025-01-03 01:33:35 +00:00
MervinPraison 067769cefb Selective inclusion of files from PR #242 2025-01-03 01:32:42 +00:00
Mervin Praison 947d301cb5 Merge pull request #235 from tosin2013/release-sync
fix: Enhance release workflow and setup script reliability
2025-01-02 17:55:57 +00:00
MervinPraison 3c96d50fbd adding dedicated process 2025-01-02 17:54:17 +00:00
Mervin Praison b744c4fbf5 Merge pull request #245 from MervinPraison/develop
adding agents docs
2025-01-02 17:15:48 +00:00
MervinPraison 2b79201f0f adding agents docs 2025-01-02 17:15:33 +00:00
Mervin Praison 5bf86cd8bf Merge pull request #244 from MervinPraison/develop
adding docs
2025-01-02 07:38:26 +00:00
MervinPraison 0f2bb67e5f adding docs 2025-01-02 07:38:09 +00:00
Mervin Praison b58b7d6680 Merge pull request #241 from MervinPraison/develop
Develop
2024-12-31 18:01:58 +00:00
MervinPraison 756f5ecebd adding workflow 2024-12-31 18:01:43 +00:00
MervinPraison 4341fdda1c adding features 2024-12-31 11:11:27 +00:00
Tosin Akinosho fd3511200d Update setup.sh 2024-12-29 18:09:19 -05:00
Tosin Akinosho cdb1da765c Update release.yml 2024-12-29 17:30:28 -05:00
Tosin Akinosho b5fb9051ac Update release.yml 2024-12-29 17:25:47 -05:00
Tosin Akinosho 8414c9d0d8 Update release.yml 2024-12-29 17:00:26 -05:00
Tosin Akinosho ef82448569 Update release.yml 2024-12-29 16:36:02 -05:00
Tosin Akinosho e5830de8ad Update release.yml 2024-12-29 16:32:35 -05:00
Tosin Akinosho fb4035437c Update release.yml 2024-12-29 16:26:40 -05:00
Tosin Akinosho 2e3a37ed52 adding release pipeline 2024-12-29 15:22:30 -05:00
182 changed files with 24071 additions and 9415 deletions

No files matched your search

+524
View File
@@ -0,0 +1,524 @@
name: Release Workflow
on:
workflow_dispatch:
inputs:
tag:
description: 'Tag to create release with (e.g., v2.0.10)'
required: true
fork_owner:
description: 'GitHub username of the fork owner'
required: true
default: 'tosin2013'
jobs:
test-ubuntu:
strategy:
matrix:
os: [ubuntu-22.04, ubuntu-24.04]
framework: ['crewai', 'autogen']
fail-fast: false
runs-on: ${{ matrix.os }}
timeout-minutes: 90
steps:
# First checkout the upstream repo at the specified tag
- name: Checkout upstream repo
uses: actions/checkout@v4
with:
repository: MervinPraison/PraisonAI
ref: ${{ github.event.inputs.tag }}
fetch-depth: 0
# Get the commit hash from the tag
- name: Get commit hash
id: get_commit
run: |
COMMIT_HASH=$(git rev-parse HEAD)
echo "commit_hash=$COMMIT_HASH" >> $GITHUB_OUTPUT
echo "Found commit: $COMMIT_HASH for tag ${{ github.event.inputs.tag }}"
# Now checkout the fork at main branch
- name: Checkout fork
uses: actions/checkout@v4
with:
repository: ${{ github.event.inputs.fork_owner }}/PraisonAI
ref: main
fetch-depth: 0
clean: true
# Sync with upstream commit
- name: Sync with upstream
run: |
git config --global user.name "GitHub Actions"
git config --global user.email "[email protected]"
# Add upstream remote
git remote add upstream https://github.com/MervinPraison/PraisonAI.git
# Fetch from upstream
git fetch upstream --tags --force
# Reset to upstream commit
git reset --hard ${{ steps.get_commit.outputs.commit_hash }}
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
cache: 'pip'
- name: Install Poetry
run: |
curl -sSL https://install.python-poetry.org | python3 -
echo "$HOME/.local/bin" >> $GITHUB_PATH
poetry config virtualenvs.in-project true
poetry config virtualenvs.create true
poetry --version
poetry config --list
- name: Install Dependencies
run: |
poetry lock #--no-update
poetry install --all-extras
poetry env info
# Verify venv exists
test -d .venv || { echo "Virtual environment not created!"; exit 1; }
test -f .venv/bin/activate || { echo "Activation script not found!"; exit 1; }
- name: Run setup script
timeout-minutes: 20
run: |
set -e # Exit on any error
curl -OL https://raw.githubusercontent.com/tosin2013/PraisonAI/refs/heads/main/setup.sh
chmod +x setup.sh
./setup.sh --all --cicd || { echo "Setup script failed"; exit 1; }
env:
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
PRAISONAI_NON_INTERACTIVE: "true"
- name: Test Basic Functionality
run: |
set -e # Exit on any error
# Random sleep between 60-120 seconds to avoid API rate limits
SLEEP_TIME=$((RANDOM % 61 + 60))
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
sleep $SLEEP_TIME
lsof -ti:8084 | xargs -r kill -9 || true
source .venv/bin/activate || { echo "Failed to activate virtual environment"; exit 1; }
mkdir -p test_output || { echo "Failed to create test_output directory"; exit 1; }
echo "Testing basic CLI functionality..."
# Test auto generation with explicit framework
praisonai --framework ${{ matrix.framework }} --auto "create a short test script" 2>&1 | tee test_output/auto_test.log
# Verify the output file exists and has content
if [[ ! -s test_output/auto_test.log ]]; then
echo "Error: No output generated from auto test"
exit 1
fi
# Test auto generation without framework
echo "Basic functionality tests passed!"
env:
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
LOGLEVEL: "debug"
PRAISONAI_NON_INTERACTIVE: "true"
- name: Test Framework-specific Features
continue-on-error: false
run: |
source .venv/bin/activate
# Random sleep between 60-120 seconds to avoid API rate limits
SLEEP_TIME=$((RANDOM % 61 + 60))
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
sleep $SLEEP_TIME
lsof -ti:8084 | xargs -r kill -9 || true
praisonai --framework ${{ matrix.framework }} --auto "create a movie script about Robots in Mars" 2>&1 | tee test_output/framework_output.txt || echo "Framework-specific test failed" >> test_output/errors.log
env:
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
PRAISONAI_NON_INTERACTIVE: "true"
- name: Test Core Functionality
continue-on-error: false
run: |
source .venv/bin/activate
# Random sleep between 60-120 seconds to avoid API rate limits
SLEEP_TIME=$((RANDOM % 61 + 60))
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
sleep $SLEEP_TIME
echo "Testing core functionality..."
# Test package imports
python3 -c "from praisonai.cli import PraisonAI; from praisonai.auto import AutoGenerator; from praisonai.agents_generator import AgentsGenerator" || exit 1
# Test configuration loading
python3 -c "from praisonai.inc.config import generate_config; print(generate_config())" || exit 1
# Test tools loading
python3 -c "from praisonai.inbuilt_tools import *" || exit 1
echo "Core functionality tests passed!"
env:
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
LOGLEVEL: "debug"
PRAISONAI_NON_INTERACTIVE: "true"
- name: Test Agent Generation
continue-on-error: false
run: |
source .venv/bin/activate
# Random sleep between 60-120 seconds to avoid API rate limits
SLEEP_TIME=$((RANDOM % 61 + 60))
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
sleep $SLEEP_TIME
echo "Testing agent generation..."
# Create a test YAML file
cat > test_agents.yaml <<EOF
topic: "Test automation task"
framework: "${{ matrix.framework }} "
roles:
- name: "Tester"
goal: "Test the system"
backstory: "Expert system tester"
tasks:
- description: "Verify system functionality"
expected_output: "Test results"
EOF
# Test YAML loading and agent generation
python3 -c '
from praisonai.agents_generator import AgentsGenerator
config = [{"model": "llama-3.3-70b-versatile", "base_url": "https://api.groq.com/openai/v1/"}]
generator = AgentsGenerator("test_agents.yaml", "${{ matrix.framework }} ", config)
print("Agent generation test passed!")
' || exit 1
echo "Agent generation tests passed!"
env:
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
LOGLEVEL: "debug"
PRAISONAI_NON_INTERACTIVE: "true"
- name: Test Custom Tools Integration
continue-on-error: false
run: |
source .venv/bin/activate
# Random sleep between 60-120 seconds to avoid API rate limits
SLEEP_TIME=$((RANDOM % 61 + 60))
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
sleep $SLEEP_TIME
echo "Testing custom tools integration..."
# Create a test tool
cat > test_tool.py <<EOF
from duckduckgo_search import DDGS
from praisonai_tools import BaseTool
class InternetSearchTool(BaseTool):
name: str = "Internet Search Tool"
description: str = "Search Internet for relevant information"
def _run(self, query: str):
return {"status": "success", "query": query}
def test(self):
"""Test method to verify tool setup"""
test_query = "test query"
result = self._run(test_query)
assert result["status"] == "success"
assert result["query"] == test_query
return True
EOF
# Test tool loading and initialization
python3 -c '
import importlib.util
import sys
# Load the test tool
spec = importlib.util.spec_from_file_location("test_tool", "test_tool.py")
module = importlib.util.module_from_spec(spec)
sys.modules["test_tool"] = module
spec.loader.exec_module(module)
# Initialize and test the tool
tool = module.InternetSearchTool()
assert tool.test(), "Tool test failed"
print("Custom tool test passed!")
' || exit 1
echo "Custom tools integration test passed!"
env:
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
LOGLEVEL: "debug"
PRAISONAI_NON_INTERACTIVE: "true"
- name: Test PraisonAI Agents Configuration
continue-on-error: false
run: |
source .venv/bin/activate
# Random sleep between 60-120 seconds to avoid API rate limits
SLEEP_TIME=$((RANDOM % 61 + 60))
echo "Sleeping for $SLEEP_TIME seconds to avoid API rate limits..."
sleep $SLEEP_TIME
echo "Testing agents configuration..."
mkdir -p test_output || { echo "Failed to create test_output directory"; exit 1; }
# Create test script
cat > test_agents.py <<EOF
from langchain_openai import ChatOpenAI
from crewai import Agent, Task, Crew
import os
import logging
import time
import random
logging.basicConfig(level="DEBUG")
logger = logging.getLogger(__name__)
def random_sleep(min_seconds=15, max_seconds=30):
"""Sleep for a random duration to prevent API rate limiting"""
duration = random.uniform(min_seconds, max_seconds)
logger.info(f"Rate limit protection: Sleeping for {duration:.2f} seconds...")
time.sleep(duration)
class RateLimitedChatOpenAI(ChatOpenAI):
"""ChatOpenAI with built-in rate limiting"""
def __call__(self, *args, **kwargs):
random_sleep()
return super().__call__(*args, **kwargs)
def run_test():
logger.info("Initializing test components...")
# Initialize the LLM with rate limiting
llm = RateLimitedChatOpenAI(
model=os.getenv('OPENAI_MODEL_NAME', 'llama-3.3-70b-versatile'),
base_url=os.getenv('OPENAI_API_BASE', 'https://api.groq.com/openai/v1/'),
api_key=os.getenv('OPENAI_API_KEY'),
temperature=0.7
)
random_sleep()
logger.info("Creating researcher agent...")
researcher = Agent(
role="Test Automation Expert",
goal="Verify system functionality and performance",
backstory="""You are an expert in test automation and system verification,
skilled in identifying potential issues and validating functionality.""",
verbose=True,
allow_delegation=False,
llm=llm
)
random_sleep()
logger.info("Creating validator agent...")
validator = Agent(
role="Quality Assurance Specialist",
goal="Validate test results and ensure system reliability",
backstory="""You are a QA specialist focused on ensuring
system reliability and proper functionality.""",
verbose=True,
allow_delegation=False,
llm=llm
)
random_sleep()
logger.info("Creating test tasks...")
test_task = Task(
description="Run a basic system verification test",
expected_output="A report on system functionality verification",
agent=researcher
)
random_sleep()
validation_task = Task(
description="Validate the test results and compile findings",
expected_output="A validation report of test findings",
agent=validator
)
random_sleep()
logger.info("Initializing crew...")
crew = Crew(
agents=[researcher, validator],
tasks=[test_task, validation_task],
verbose=2
)
logger.info("Starting crew execution...")
result = crew.kickoff()
logger.info("Test execution completed.")
logger.info(f"Result: {result}")
return result
if __name__ == "__main__":
try:
run_test()
except Exception as e:
logger.error(f"Test failed with error: {str(e)}", exc_info=True)
raise
EOF
# Run the test script with increased timeout
echo "Testing with Python API..."
timeout 1800 python3 test_agents.py 2>&1 | tee test_output/agents_test.log
# Verify the output
if [[ ! -s test_output/agents_test.log ]]; then
echo "Error: No output generated from agents test"
exit 1
fi
echo "Agents configuration tests passed!"
env:
OPENAI_API_KEY: ${{ secrets.GROQ_API_KEY }}
GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
OPENAI_MODEL_NAME: "llama-3.3-70b-versatile"
OPENAI_API_BASE: "https://api.groq.com/openai/v1/"
OPENAI_API_BASE_URL: "https://api.groq.com/openai/v1/"
LOGLEVEL: "debug"
PRAISONAI_NON_INTERACTIVE: "true"
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v3
with:
name: praisonai-output-${{ matrix.os }}-${{ matrix.framework }}
path: test_output/
retention-days: 14
create-release:
needs: [test-ubuntu]
runs-on: ubuntu-latest
if: success()
steps:
# First checkout the fork's main branch
- name: Checkout fork repo
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.PAT_TOKEN }}
repository: ${{ github.event.inputs.fork_owner }}/PraisonAI
ref: main
# Add upstream and get commit from tag
- name: Get upstream tag commit
id: get_upstream_commit
run: |
# Add upstream remote
git remote add upstream https://github.com/MervinPraison/PraisonAI.git
# Delete local tag if it exists
git tag -d ${{ github.event.inputs.tag }} || true
# Fetch from upstream including tags with force
git fetch upstream --tags --force
# Verify tag exists in upstream
if ! git ls-remote --tags upstream | grep -q "refs/tags/${{ github.event.inputs.tag }}$"; then
echo "::error::Tag ${{ github.event.inputs.tag }} not found in upstream repository"
exit 1
fi
# Get the commit hash from the upstream tag
UPSTREAM_COMMIT=$(git rev-list -n 1 refs/tags/${{ github.event.inputs.tag }})
if [ -z "$UPSTREAM_COMMIT" ]; then
echo "::error::Could not find commit for tag ${{ github.event.inputs.tag }}"
exit 1
fi
echo "upstream_commit=$UPSTREAM_COMMIT" >> $GITHUB_OUTPUT
echo "Found commit: $UPSTREAM_COMMIT for tag ${{ github.event.inputs.tag }}"
# Create tag at the same commit
- name: Create tag at commit
run: |
git config --global user.name "GitHub Actions"
git config --global user.email "[email protected]"
# Remove tag if it exists locally
git tag -d ${{ github.event.inputs.tag }} || true
# Remove tag if it exists remotely
git push origin :refs/tags/${{ github.event.inputs.tag }} || true
# Fetch the specific commit from upstream
git fetch upstream ${{ steps.get_upstream_commit.outputs.upstream_commit }} --force
# Reset to upstream commit
git reset --hard ${{ steps.get_upstream_commit.outputs.upstream_commit }}
# Create and push tag
git tag -a ${{ github.event.inputs.tag }} -m "Release ${{ github.event.inputs.tag }} (synced from upstream MervinPraison/PraisonAI@${{ github.event.inputs.tag }})"
git push origin ${{ github.event.inputs.tag }}
env:
GIT_ASKPASS: /bin/echo
PAT_TOKEN: ${{ secrets.PAT_TOKEN }}
# Create the release
- name: Create Release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.PAT_TOKEN }}
with:
tag_name: ${{ github.event.inputs.tag }}
release_name: Release ${{ github.event.inputs.tag }}
draft: false
prerelease: false
body: |
Release ${{ github.event.inputs.tag }} (synced from upstream)
This release is synchronized with MervinPraison/PraisonAI@${{ github.event.inputs.tag }}
Commit: ${{ steps.get_upstream_commit.outputs.upstream_commit }}
For installation:
```bash
pip install praisonai
```
Changes from upstream v2.0.10:
- Updated agent configuration tests
- Added rate limiting protection
- Improved test reliability
Forked from: MervinPraison/PraisonAI
+2 -1
View File
@@ -64,4 +64,5 @@ node_modules
outputs
agents/praisonaiagents/build
agents/praisonaiagents/dist
agents/praisonaiagents/praisonaiagents.egg-info
agents/praisonaiagents/praisonaiagents.egg-info
.codegpt
+1 -1
View File
@@ -1,6 +1,6 @@
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask praisonai==2.0.17 gunicorn markdown
RUN pip install flask praisonai==2.0.36 gunicorn markdown
EXPOSE 8080
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
+24
View File
@@ -0,0 +1,24 @@
FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
python3-dev \
curl \
&& rm -rf /var/lib/apt/lists/*
# Install Python packages
RUN pip install --no-cache-dir \
praisonaiagents>=0.0.4 \
praisonai_tools \
"praisonai==2.0.18" \
"praisonai[chat]" \
"embedchain[github,youtube]"
# Copy application code
COPY . .
# Default command (will be overridden by docker-compose)
CMD ["praisonai", "chat"]
+28
View File
@@ -0,0 +1,28 @@
FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
python3-dev \
portaudio19-dev \
python3-pyaudio \
curl \
&& rm -rf /var/lib/apt/lists/*
# Install Python packages
RUN pip install --no-cache-dir \
praisonaiagents>=0.0.4 \
praisonai_tools \
"praisonai==2.0.18" \
"praisonai[ui]" \
"praisonai[chat]" \
"praisonai[realtime]" \
"embedchain[github,youtube]"
# Copy application code
COPY . .
# Default command (will be overridden by docker-compose)
CMD ["praisonai", "ui"]
+24
View File
@@ -0,0 +1,24 @@
FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc \
python3-dev \
curl \
&& rm -rf /var/lib/apt/lists/*
# Install Python packages
RUN pip install --no-cache-dir \
praisonaiagents>=0.0.4 \
praisonai_tools \
"praisonai==2.0.18" \
"praisonai[ui]" \
"praisonai[crewai]"
# Copy application code
COPY . .
# Default command (will be overridden by docker-compose)
CMD ["praisonai", "ui"]
+67 -2
View File
@@ -22,17 +22,30 @@
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.
<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>
## Key Features
- 🤖 Automated AI Agents Creation
- 🔄 Self Reflection AI Agents
- 🧠 Reasoning AI Agents
- 👁️ Multi Modal AI Agents
- 🤝 Multi Agent Collaboration
- ⚡ AI Agent Workflow
- 🔄 Use CrewAI or AutoGen Framework
- 💯 100+ LLM Support
- 💻 Chat with ENTIRE Codebase
- 🖥️ Interactive UIs
- 🎨 Interactive UIs
- 📄 YAML-based Configuration
- 🛠️ Custom Tool Integration
- 🔍 Internet Search Capability (using Crawl4AI and Tavily)
- 👁️ Vision Language Model (VLM) Support
- 🖼️ Vision Language Model (VLM) Support
- 🎙️ Real-time Voice Interaction
## Using No Code
@@ -515,3 +528,55 @@ Praison AI is an open-sourced software licensed under the **[MIT license](https:
## 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.
+3 -11
View File
@@ -7,12 +7,8 @@ roles:
information.
goal: Gather information about Mars and cats
role: Researcher
llm:
model: "gpt-4o"
reflect_llm:
model: "gpt-4o"
min_reflect: 2
max_reflect: 4
min_reflect: 1
max_reflect: 2
tasks:
research_task:
description: Research about Mars, its environment, and the feasibility of
@@ -25,8 +21,6 @@ roles:
stories.
goal: Create a story concept for a movie about a cat in Mars
role: Narrative Designer
llm:
model: "gpt-4o"
tasks:
story_concept_development:
description: Use the research findings to craft a unique story concept for
@@ -39,8 +33,6 @@ roles:
scripts.
goal: Write a movie script about a cat in Mars
role: Scriptwriter
llm:
model: "gpt-4o"
tasks:
scriptwriting_task:
description: Turn the story concept into a production-ready movie script,
@@ -48,4 +40,4 @@ roles:
expected_output: Final movie script with dialogue and scene details.
tools:
- ''
dependencies: []
dependencies: []
+6
View File
@@ -0,0 +1,6 @@
Make it minimal change as possible
Firstly try not to make any modification to the existing code as possible
Only if its highly required, without that if it can't be done, then modify the existing.
So by default try to add new code section and not modify the existing.
+3 -1
View File
@@ -81,4 +81,6 @@ praisonaiagents/dist
praisonaiagents/praisonaiagents.egg-info
dist
build
*.mp4
*.mp4
*.png
graph.py
+233
View File
@@ -0,0 +1,233 @@
import asyncio
import time
from typing import List, Dict
from praisonaiagents import Agent, Task, PraisonAIAgents, TaskOutput
from praisonaiagents.main import (
display_error,
display_interaction,
display_tool_call,
display_instruction,
error_logs,
Console
)
from duckduckgo_search import DDGS
from pydantic import BaseModel
console = Console()
# 1. Define output model for structured results
class SearchResult(BaseModel):
query: str
results: List[Dict[str, str]]
total_results: int
# 2. Define both sync and async tools
def sync_search_tool(query: str) -> List[Dict]:
"""
Synchronous search using DuckDuckGo.
Args:
query (str): The search query.
Returns:
list: Search results
"""
display_tool_call(f"Running sync search for: {query}", console)
time.sleep(1) # Simulate network delay
try:
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=5):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return results
except Exception as e:
error_msg = f"Error during sync search: {e}"
display_error(error_msg, console)
error_logs.append(error_msg)
return []
async def async_search_tool(query: str) -> List[Dict]:
"""
Asynchronous search using DuckDuckGo.
Args:
query (str): The search query.
Returns:
list: Search results
"""
display_tool_call(f"Running async search for: {query}", console)
await asyncio.sleep(1) # Simulate network delay
try:
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=5):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return results
except Exception as e:
error_msg = f"Error during async search: {e}"
display_error(error_msg, console)
error_logs.append(error_msg)
return []
# 3. Define both sync and async callbacks
def sync_callback(output: TaskOutput):
display_interaction("Sync Callback", f"Processing output: {output.raw[:100]}...", markdown=True, console=console)
time.sleep(1) # Simulate processing
if output.output_format == "JSON":
display_tool_call(f"Processed JSON result: {output.json_dict}", console)
elif output.output_format == "Pydantic":
display_tool_call(f"Processed Pydantic result: {output.pydantic}", console)
async def async_callback(output: TaskOutput):
display_interaction("Async Callback", f"Processing output: {output.raw[:100]}...", markdown=True, console=console)
await asyncio.sleep(1) # Simulate processing
if output.output_format == "JSON":
display_tool_call(f"Processed JSON result: {output.json_dict}", console)
elif output.output_format == "Pydantic":
display_tool_call(f"Processed Pydantic result: {output.pydantic}", console)
# 4. Create agents with different tools
sync_agent = Agent(
name="SyncAgent",
role="Synchronous Search Specialist",
goal="Perform synchronous searches and return structured results",
backstory="Expert in sync operations and data organization",
tools=[sync_search_tool],
self_reflect=False,
verbose=True,
markdown=True
)
async_agent = Agent(
name="AsyncAgent",
role="Asynchronous Search Specialist",
goal="Perform asynchronous searches and return structured results",
backstory="Expert in async operations and data organization",
tools=[async_search_tool],
self_reflect=False,
verbose=True,
markdown=True
)
# 5. Create tasks with different configurations
sync_task = Task(
name="sync_search",
description="Search for 'Python programming' using sync tool and return structured results",
expected_output="SearchResult model with query details and results",
agent=sync_agent,
async_execution=False,
callback=sync_callback,
output_pydantic=SearchResult
)
async_task = Task(
name="async_search",
description="Search for 'Async programming' using async tool and return structured results",
expected_output="SearchResult model with query details and results",
agent=async_agent,
async_execution=True,
callback=async_callback,
output_pydantic=SearchResult
)
# 6. Create workflow tasks
workflow_sync_task = Task(
name="workflow_sync",
description="Workflow sync search for 'AI trends' with structured output",
expected_output="SearchResult model with AI trends data",
agent=sync_agent,
async_execution=False,
is_start=True,
next_tasks=["workflow_async"],
output_pydantic=SearchResult
)
workflow_async_task = Task(
name="workflow_async",
description="Workflow async search for 'Future of AI' with structured output",
expected_output="SearchResult model with Future of AI data",
agent=async_agent,
async_execution=True,
output_pydantic=SearchResult
)
# 7. Example usage functions
def run_sync_example():
"""Run synchronous example"""
display_instruction("\nRunning Synchronous Example...", console)
agents = PraisonAIAgents(
agents=[sync_agent],
tasks=[sync_task],
verbose=1,
process="sequential"
)
result = agents.start()
display_interaction("Sync Example", f"Result: {result}", markdown=True, console=console)
async def run_async_example():
"""Run asynchronous example"""
display_instruction("\nRunning Asynchronous Example...", console)
agents = PraisonAIAgents(
agents=[async_agent],
tasks=[async_task],
verbose=1,
process="sequential"
)
result = await agents.astart()
display_interaction("Async Example", f"Result: {result}", markdown=True, console=console)
async def run_mixed_example():
"""Run mixed sync/async example"""
display_instruction("\nRunning Mixed Sync/Async Example...", console)
agents = PraisonAIAgents(
agents=[sync_agent, async_agent],
tasks=[sync_task, async_task],
verbose=1,
process="sequential"
)
result = await agents.astart()
display_interaction("Mixed Example", f"Result: {result}", markdown=True, console=console)
async def run_workflow_example():
"""Run workflow example with both sync and async tasks"""
display_instruction("\nRunning Workflow Example...", console)
agents = PraisonAIAgents(
agents=[sync_agent, async_agent],
tasks=[workflow_sync_task, workflow_async_task],
verbose=1,
process="workflow"
)
result = await agents.astart()
display_interaction("Workflow Example", f"Result: {result}", markdown=True, console=console)
# 8. Main execution
async def main():
"""Main execution function"""
display_instruction("Starting PraisonAI Agents Examples...", console)
try:
# Run sync example in a separate thread to not block the event loop
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, run_sync_example)
# Run async examples
await run_async_example()
await run_mixed_example()
await run_workflow_example()
if error_logs:
display_error("\nErrors encountered during execution:", console)
for error in error_logs:
display_error(error, console)
except Exception as e:
display_error(f"Error in main execution: {e}", console)
error_logs.append(str(e))
if __name__ == "__main__":
# Run the main function
asyncio.run(main())
+152
View File
@@ -0,0 +1,152 @@
import asyncio
import time
from typing import List, Dict
from praisonaiagents import Agent, Task, PraisonAIAgents, TaskOutput
from duckduckgo_search import DDGS
from pydantic import BaseModel
# 1. Define output model for structured results
class SearchResult(BaseModel):
query: str
results: List[Dict[str, str]]
total_results: int
# 2. Define async tool
async def async_search_tool(query: str) -> Dict:
"""Perform asynchronous search and return structured results."""
await asyncio.sleep(1) # Simulate network delay
try:
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=5):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return {
"query": query,
"results": results,
"total_results": len(results)
}
except Exception as e:
print(f"Error during async search: {e}")
return {
"query": query,
"results": [],
"total_results": 0
}
# 3. Define async callback
async def async_callback(output: TaskOutput):
await asyncio.sleep(1) # Simulate processing
if output.output_format == "JSON":
print(f"Processed JSON result: {output.json_dict}")
elif output.output_format == "Pydantic":
print(f"Processed Pydantic result: {output.pydantic}")
# 4. Create specialized agents
async_agent = Agent(
name="AsyncSearchAgent",
role="Search Specialist",
goal="Perform fast parallel searches with structured results",
backstory="Expert in efficient data retrieval and parallel search operations",
tools=[async_search_tool],
self_reflect=False,
verbose=True,
markdown=True
)
summary_agent = Agent(
name="SummaryAgent",
role="Research Synthesizer",
goal="Create concise summaries from multiple search results",
backstory="Expert in analyzing and synthesizing information from multiple sources",
self_reflect=True,
verbose=True,
markdown=True
)
# 5. Create async tasks
async_task = Task(
name="async_search",
description="Search for 'Async programming' and return results in JSON format with query, results array, and total_results count.",
expected_output="SearchResult model with structured data",
agent=async_agent,
async_execution=True,
callback=async_callback,
output_json=SearchResult
)
async def run_parallel_tasks():
"""Run multiple async tasks in parallel"""
print("\nRunning Parallel Async Tasks...")
# Define different search topics
search_topics = [
"Latest AI Developments 2024",
"Machine Learning Best Practices",
"Neural Networks Architecture"
]
# Create tasks for different topics
parallel_tasks = [
Task(
name=f"search_task_{i}",
description=f"Search for '{topic}' and return structured results with query details and findings.",
expected_output="SearchResult model with search data",
agent=async_agent,
async_execution=True,
callback=async_callback,
output_json=SearchResult
) for i, topic in enumerate(search_topics)
]
# Create summarization task
summary_task = Task(
name="summary_task",
description="Analyze all search results and create a concise summary highlighting key findings, patterns, and implications.",
expected_output="Structured summary with key findings and insights",
agent=summary_agent,
async_execution=False,
callback=async_callback,
context=parallel_tasks
)
# Create a single PraisonAIAgents instance with both agents
agents = PraisonAIAgents(
agents=[async_agent, summary_agent],
tasks=parallel_tasks + [summary_task],
verbose=1,
process="sequential"
)
# Run all tasks
results = await agents.astart()
print(f"Tasks Results: {results}")
# Return results in a serializable format
return {
"search_results": {
"task_status": {k: v for k, v in results["task_status"].items() if k != summary_task.id},
"task_results": [str(results["task_results"][i]) if results["task_results"][i] else None
for i in range(len(parallel_tasks))]
},
"summary": str(results["task_results"][summary_task.id]) if results["task_results"].get(summary_task.id) else None,
"topics": search_topics
}
# 6. Main execution
async def main():
"""Main execution function"""
print("Starting Async AI Agents Examples...")
try:
await run_parallel_tasks()
except Exception as e:
print(f"Error in main execution: {e}")
if __name__ == "__main__":
# Run the main function
asyncio.run(main())
+248
View File
@@ -0,0 +1,248 @@
import asyncio
import time
from typing import List, Dict
from praisonaiagents import Agent, Task, PraisonAIAgents, TaskOutput
from duckduckgo_search import DDGS
from pydantic import BaseModel
# 1. Define output model for structured results
class SearchResult(BaseModel):
query: str
results: List[Dict[str, str]]
total_results: int
# 2. Define async tool
async def async_search_tool(query: str) -> Dict:
"""
Asynchronous search using DuckDuckGo.
Args:
query (str): The search query.
Returns:
dict: Search results in SearchResult model format
"""
await asyncio.sleep(1) # Simulate network delay
try:
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=5):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
# Format response to match SearchResult model
return {
"query": query,
"results": results,
"total_results": len(results)
}
except Exception as e:
print(f"Error during async search: {e}")
return {
"query": query,
"results": [],
"total_results": 0
}
# 3. Define async callback
async def async_callback(output: TaskOutput):
await asyncio.sleep(1) # Simulate processing
if output.output_format == "JSON":
print(f"Processed JSON result: {output.json_dict}")
elif output.output_format == "Pydantic":
print(f"Processed Pydantic result: {output.pydantic}")
# 4. Create specialized agents
async_agent = Agent(
name="AsyncSearchAgent",
role="Asynchronous Search Specialist",
goal="Perform fast and efficient asynchronous searches with structured results",
backstory="Expert in parallel search operations and data retrieval",
tools=[async_search_tool],
self_reflect=False,
verbose=True,
markdown=True
)
summary_agent = Agent(
name="SummaryAgent",
role="Research Synthesizer",
goal="Create comprehensive summaries and identify patterns across multiple search results",
backstory="""Expert in analyzing and synthesizing information from multiple sources.
Skilled at identifying patterns, trends, and connections between different topics.
Specializes in creating clear, structured summaries that highlight key insights.""",
self_reflect=True, # Enable self-reflection for better summary quality
verbose=True,
markdown=True
)
# 5. Create async tasks
async_task = Task(
name="async_search",
description="""Search for 'Async programming' and return results in the following JSON format:
{
"query": "the search query",
"results": [
{
"title": "result title",
"url": "result url",
"snippet": "result snippet"
}
],
"total_results": number of results
}""",
expected_output="SearchResult model with query details and results",
agent=async_agent,
async_execution=True,
callback=async_callback,
output_json=SearchResult
)
# 6. Example usage functions
async def run_single_task():
"""Run single async task"""
print("\nRunning Single Async Task...")
agents = PraisonAIAgents(
agents=[async_agent],
tasks=[async_task],
verbose=1,
process="sequential"
)
result = await agents.astart()
print(f"Single Task Result: {result}")
async def run_parallel_tasks():
"""Run multiple async tasks in parallel"""
print("\nRunning Parallel Async Tasks...")
# Define different search topics
search_topics = [
"Latest AI Developments 2024",
"Machine Learning Best Practices",
"Neural Networks Architecture"
]
# Create tasks for different topics
parallel_tasks = [
Task(
name=f"search_task_{i}",
description=f"""Search for '{topic}' and return results in the following JSON format:
{{
"query": "{topic}",
"results": [
{{
"title": "result title",
"url": "result url",
"snippet": "result snippet"
}}
],
"total_results": number of results
}}""",
expected_output="SearchResult model with detailed information",
agent=async_agent,
async_execution=True,
callback=async_callback,
output_json=SearchResult
) for i, topic in enumerate(search_topics)
]
# Create summarization task with the specialized summary agent
summary_task = Task(
name="summary_task",
description="""As a Research Synthesizer, analyze the search results and create a comprehensive summary. Your task:
1. Analyze Results:
- Review all search results thoroughly
- Extract key findings from each topic
- Identify main themes and concepts
2. Find Connections:
- Identify relationships between topics
- Spot common patterns or contradictions
- Note emerging trends across sources
3. Create Structured Summary:
- Main findings per topic
- Cross-cutting themes
- Emerging trends
- Practical implications
- Future directions
4. Quality Checks:
- Ensure all topics are covered
- Verify accuracy of connections
- Confirm clarity of insights
- Validate practical relevance
Present the summary in a clear, structured format with sections for findings, patterns, trends, and implications.""",
expected_output="""A comprehensive research synthesis containing:
- Detailed findings from each search topic
- Cross-topic patterns and relationships
- Emerging trends and their implications
- Practical applications and future directions""",
agent=summary_agent, # Use the specialized summary agent
async_execution=False, # Run synchronously after search tasks
callback=async_callback
)
# First run parallel search tasks
agents = PraisonAIAgents(
agents=[async_agent],
tasks=parallel_tasks, # Only run search tasks first
verbose=1,
process="sequential"
)
search_results = await agents.astart()
print(f"Search Tasks Results: {search_results}")
# Create task objects with results for context
completed_tasks = []
for i, topic in enumerate(search_topics):
task = Task(
name=f"search_task_{i}_result",
description=f"Search results for: {topic}",
expected_output="Search results from previous task",
agent=async_agent,
result=search_results["task_results"][i]
)
completed_tasks.append(task)
# Update summary task with context from search results
summary_task.context = completed_tasks
# Run summarization task with summary agent
summary_agents = PraisonAIAgents(
agents=[summary_agent], # Use summary agent for synthesis
tasks=[summary_task],
verbose=1,
process="sequential"
)
summary_result = await summary_agents.astart()
print(f"Summary Task Result: {summary_result}")
# Return results in a serializable format
return {
"search_results": {
"task_status": search_results["task_status"],
"task_results": [str(result) if result else None for result in search_results["task_results"]]
},
"summary": str(summary_result),
"topics": search_topics
}
# 7. Main execution
async def main():
"""Main execution function"""
print("Starting Async AI Agents Examples...")
try:
# Run different async patterns
await run_single_task()
await run_parallel_tasks()
except Exception as e:
print(f"Error in main execution: {e}")
if __name__ == "__main__":
# Run the main function
asyncio.run(main())
View File
Whitespace-only changes.
+50
View File
@@ -0,0 +1,50 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
import json
from e2b_code_interpreter import Sandbox
def code_interpreter(code: str):
"""
A function to demonstrate running Python code dynamically using e2b_code_interpreter.
"""
print(f"\n{'='*50}\n> Running following AI-generated code:\n{code}\n{'='*50}")
exec_result = Sandbox().run_code(code)
if exec_result.error:
print("[Code Interpreter error]", exec_result.error)
return {"error": str(exec_result.error)}
else:
results = []
for result in exec_result.results:
if hasattr(result, '__iter__'):
results.extend(list(result))
else:
results.append(str(result))
logs = {"stdout": list(exec_result.logs.stdout), "stderr": list(exec_result.logs.stderr)}
return json.dumps({"results": results, "logs": logs})
code_agent = Agent(
name="code_agent",
llm="gpt-4o-mini",
backstory="Expert in writing Python scripts",
self_reflect=False
)
execution_agent = Agent(
name="execution_agent",
llm="gpt-4o-mini",
backstory="Expert in executing Python scripts",
self_reflect=False,
tools=[code_interpreter]
)
code_agent_task = Task(
description="Write a simple Python script to print 'Hello, World!'",
expected_output="A Python script that prints 'Hello, World!'",
agent=code_agent
)
execution_agent_task = Task(
description="Execute the Python script",
expected_output="The output of the Python script",
agent=execution_agent
)
agents = PraisonAIAgents(agents=[code_agent, execution_agent], tasks=[code_agent_task, execution_agent_task])
agents.start()
+204
View File
@@ -0,0 +1,204 @@
from praisonaiagents import (
Agent,
Task,
PraisonAIAgents,
error_logs,
register_display_callback,
sync_display_callbacks,
async_display_callbacks
)
from duckduckgo_search import DDGS
from rich.console import Console
import json
from datetime import datetime
import logging
# Setup logging
logging.basicConfig(
filename='ai_interactions.log',
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Callback functions for different display types
def interaction_callback(message=None, response=None, markdown=None, generation_time=None):
"""Callback for display_interaction"""
logging.info(f"""
=== INTERACTION ===
Time: {datetime.now()}
Generation Time: {generation_time}s
Message: {message}
Response: {response}
Markdown: {markdown}
""")
def error_callback(message=None):
"""Callback for display_error"""
logging.error(f"""
=== ERROR ===
Time: {datetime.now()}
Message: {message}
""")
def tool_call_callback(message=None):
"""Callback for display_tool_call"""
logging.info(f"""
=== TOOL CALL ===
Time: {datetime.now()}
Message: {message}
""")
def instruction_callback(message=None):
"""Callback for display_instruction"""
logging.info(f"""
=== INSTRUCTION ===
Time: {datetime.now()}
Message: {message}
""")
def self_reflection_callback(message=None):
"""Callback for display_self_reflection"""
logging.info(f"""
=== SELF REFLECTION ===
Time: {datetime.now()}
Message: {message}
""")
def generating_callback(content=None, elapsed_time=None):
"""Callback for display_generating"""
logging.info(f"""
=== GENERATING ===
Time: {datetime.now()}
Content: {content}
Elapsed Time: {elapsed_time}
""")
# Register all callbacks
register_display_callback('interaction', interaction_callback)
register_display_callback('error', error_callback)
register_display_callback('tool_call', tool_call_callback)
register_display_callback('instruction', instruction_callback)
register_display_callback('self_reflection', self_reflection_callback)
# register_display_callback('generating', generating_callback)
def task_callback(output):
"""Callback for task completion"""
logging.info(f"""
=== TASK COMPLETED ===
Time: {datetime.now()}
Description: {output.description}
Agent: {output.agent}
Output: {output.raw[:200]}...
""")
def internet_search_tool(query) -> list:
"""
Perform a search using DuckDuckGo.
Args:
query (str): The search query.
Returns:
list: A list of search result titles and URLs.
"""
try:
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=10):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return results
except Exception as e:
print(f"Error during DuckDuckGo search: {e}")
return []
def main():
# 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,
allow_delegation=False,
tools=[internet_search_tool],
llm="gpt-4o",
markdown=True,
reflect_llm="gpt-4o",
min_reflect=2,
max_reflect=4
)
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.""",
verbose=True,
allow_delegation=True,
llm="gpt-4o",
tools=[],
markdown=True
)
# Create tasks with callbacks
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,
tools=[internet_search_tool],
callback=task_callback
)
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,
context=[task1],
callback=task_callback,
tools=[]
)
task3 = Task(
name="json_task",
description="""Create a json object with a title of "My Task" and content of "My content".""",
expected_output="""JSON output with title and content""",
agent=researcher,
callback=task_callback
)
task4 = Task(
name="save_output_task",
description="""Save the AI blog post to a file""",
expected_output="""File saved successfully""",
agent=writer,
context=[task2],
output_file='test.txt',
create_directory=True,
callback=task_callback
)
# Create and run agents manager
agents = PraisonAIAgents(
agents=[researcher, writer],
tasks=[task1, task2, task3, task4],
verbose=True,
process="sequential",
manager_llm="gpt-4o"
)
agents.start()
if __name__ == "__main__":
main()
+151
View File
@@ -0,0 +1,151 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
from typing import List, Dict, Union
from duckduckgo_search import DDGS
from langchain_community.tools import YouTubeSearchTool
from langchain_community.utilities import WikipediaAPIWrapper
# 1. Tool
def internet_search_tool(query: str) -> List[Dict]:
"""
Perform a search using DuckDuckGo.
Args:
query (str): The search query.
Returns:
list: A list of search result titles, URLs, and snippets.
"""
try:
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=10):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return results
except Exception as e:
print(f"Error during DuckDuckGo search: {e}")
return []
def youtube_search_tool(query: str, inspect: bool = False, max_results: int = 2):
"""
Provide a custom wrapper around the YouTubeSearchTool.
Args:
query (str): The search query for YouTube.
inspect (bool): If True, returns tool inspection info instead of search results.
max_results (int): Maximum number of results to return (default: 2).
Returns:
Union[List[str], dict]: List of YouTube video URLs or tool inspection info.
"""
yt = YouTubeSearchTool()
if inspect:
inspection_info = {
"type": type(yt),
"attributes": [attr for attr in dir(yt) if not attr.startswith('_')],
"methods": {
"run": getattr(yt, 'run', None),
"arun": getattr(yt, 'arun', None)
},
"properties": {
"name": getattr(yt, 'name', 'youtube_search'),
"description": getattr(yt, 'description', 'Search YouTube videos'),
"return_direct": getattr(yt, 'return_direct', False)
}
}
return inspection_info
# Format query with max_results
formatted_query = f"{query}, {max_results}"
return yt.run(formatted_query)
def wikipedia_search_tool(query: str, inspect: bool = False, max_chars: int = 4000, top_k: int = 3):
"""
Provide a custom wrapper around langchain_community's WikipediaAPIWrapper.
Args:
query (str): A search query for Wikipedia.
inspect (bool): If True, returns tool inspection info instead of search results.
max_chars (int): Maximum characters to return (default: 4000).
top_k (int): Number of top results to consider (default: 3).
Returns:
Union[str, dict]: Summary from Wikipedia or tool inspection info if inspect=True.
"""
w = WikipediaAPIWrapper(
top_k_results=top_k,
doc_content_chars_max=max_chars,
lang='en'
)
if inspect:
inspection_info = {
"type": type(w),
"attributes": [attr for attr in dir(w) if not attr.startswith('_')],
"methods": {
"run": getattr(w, 'run', None),
"arun": getattr(w, 'arun', None)
},
"properties": {
"name": "wikipedia",
"description": "Search and get summaries from Wikipedia",
"top_k": w.top_k_results,
"lang": w.lang,
"max_chars": w.doc_content_chars_max
}
}
return inspection_info
try:
result = w.run(query)
return result
except Exception as e:
return f"Error searching Wikipedia: {str(e)}"
# 2. Agent
data_agent = Agent(
name="DataCollector",
role="Search Specialist",
goal="Perform internet searches to collect relevant information.",
backstory="Expert in finding and organizing internet data from multiple sources.",
tools=[internet_search_tool, youtube_search_tool, wikipedia_search_tool],
self_reflect=False
)
# 3. Tasks
collect_task = Task(
description="Perform an internet search using the query: 'AI job trends in 2024'. Return results as a list of title, URL, and snippet.",
expected_output="List of search results with titles, URLs, and snippets.",
agent=data_agent,
name="collect_data",
is_start=True,
next_tasks=["validate_data"]
)
validate_task = Task(
description="""Validate the collected data. Check if:
1. At least 5 results are returned.
2. Each result contains a title and a URL.
Return validation_result as 'valid' or 'invalid' only no other text.""",
expected_output="Validation result indicating if data is valid or invalid.",
agent=data_agent,
name="validate_data",
task_type="decision",
condition={
"valid": [], # End the workflow on valid data
"invalid": ["collect_data"] # Retry data collection on invalid data
},
)
# 4. Workflow
agents = PraisonAIAgents(
agents=[data_agent],
tasks=[collect_task, validate_task],
verbose=1,
process="workflow"
)
agents.start()
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],
self_reflect=False
)
# 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()
+73
View File
@@ -0,0 +1,73 @@
from langchain_community.tools import YouTubeSearchTool
from langchain_community.utilities import WikipediaAPIWrapper
def test_tools():
"""Test both YouTube and Wikipedia tools"""
print("\n=== Testing YouTube Search ===")
# Test YouTube
yt = YouTubeSearchTool()
# Test basic search
print("\nBasic YouTube Search:")
query = "AI advancements 2024"
print(f"Query: {query}")
result = yt.run(query)
print(f"Results: {result}")
# Test with max results
print("\nYouTube Search with max results:")
query = "AI advancements 2024, 3" # Format: query, max_results
print(f"Query: {query}")
result = yt.run(query)
print(f"Results: {result}")
print("\n=== Testing Wikipedia Search ===")
# Test Wikipedia
wiki = WikipediaAPIWrapper(
top_k_results=2,
doc_content_chars_max=500
)
# Test basic search
print("\nBasic Wikipedia Search:")
query = "Artificial Intelligence"
print(f"Query: {query}")
result = wiki.run(query)
print(f"Results: {result[:200]}..." if result else "No result")
# Test with different parameters
print("\nWikipedia Search with different language:")
wiki_fr = WikipediaAPIWrapper(
lang='fr',
top_k_results=1,
doc_content_chars_max=300
)
result = wiki_fr.run("Intelligence Artificielle")
print(f"French Results: {result[:200]}..." if result else "No result")
def print_tool_info():
"""Print detailed information about the tools"""
print("\n=== Tool Information ===")
# YouTube Tool Info
print("\nYouTube Tool:")
yt = YouTubeSearchTool()
print("Type:", type(yt))
print("Name:", getattr(yt, 'name', None))
print("Description:", getattr(yt, 'description', None))
print("Arguments:", getattr(yt, 'args', None))
# Wikipedia Tool Info
print("\nWikipedia Tool:")
wiki = WikipediaAPIWrapper()
print("Type:", type(wiki))
print("Available Settings:")
print("- Language:", wiki.lang)
print("- Top K Results:", wiki.top_k_results)
print("- Max Characters:", wiki.doc_content_chars_max)
if __name__ == "__main__":
print_tool_info()
test_tools()
+6
View File
@@ -0,0 +1,6 @@
from praisonaiagents import Agent, Agents, Tools
research_agent = Agent(instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
summarise_agent = Agent(instructions="You are a summarize agent to summarise in points")
agents = Agents(agents=[research_agent, summarise_agent])
agents.start()
+12
View File
@@ -5,6 +5,7 @@ Praison AI Agents - A package for hierarchical AI agent task execution
from .agent.agent import Agent
from .agents.agents import PraisonAIAgents
from .task.task import Task
from .tools.tools import Tools
from .main import (
TaskOutput,
ReflectionOutput,
@@ -16,11 +17,19 @@ from .main import (
display_generating,
clean_triple_backticks,
error_logs,
register_display_callback,
sync_display_callbacks,
async_display_callbacks,
)
# Add Agents as an alias for PraisonAIAgents
Agents = PraisonAIAgents
__all__ = [
'Agent',
'PraisonAIAgents',
'Agents',
'Tools',
'Task',
'TaskOutput',
'ReflectionOutput',
@@ -32,4 +41,7 @@ __all__ = [
'display_generating',
'clean_triple_backticks',
'error_logs',
'register_display_callback',
'sync_display_callbacks',
'async_display_callbacks',
]
+277 -36
View File
@@ -1,9 +1,12 @@
import logging
import json
import os
import time
from typing import List, Optional, Any, Dict, Union, Literal
import json
import logging
import asyncio
from typing import List, Optional, Any, Dict, Union, Literal, TYPE_CHECKING
from rich.console import Console
from rich.live import Live
from openai import AsyncOpenAI
from ..main import (
display_error,
display_tool_call,
@@ -15,6 +18,10 @@ from ..main import (
client,
error_logs
)
import inspect
if TYPE_CHECKING:
from ..task.task import Task
class Agent:
def _generate_tool_definition(self, function_name):
@@ -61,9 +68,24 @@ class Agent:
return None
import inspect
# Langchain tools
if inspect.isclass(func) and hasattr(func, 'run'):
original_func = func
func = func.run
function_name = original_func.__name__
sig = inspect.signature(func)
logging.debug(f"Function signature: {sig}")
# Skip self, *args, **kwargs, so they don't get passed in arguments
parameters_list = []
for name, param in sig.parameters.items():
if name == "self":
continue
if param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD):
continue
parameters_list.append((name, param))
parameters = {
"type": "object",
"properties": {},
@@ -89,7 +111,7 @@ class Agent:
logging.debug(f"Parameter descriptions: {param_descriptions}")
for name, param in sig.parameters.items():
for name, param in parameters_list:
param_type = "string" # Default type
if param.annotation != inspect.Parameter.empty:
if param.annotation == int:
@@ -129,11 +151,12 @@ class Agent:
def __init__(
self,
name: str,
role: str,
goal: str,
backstory: str,
llm: Optional[Union[str, Any]] = "gpt-4o",
name: Optional[str] = None,
role: Optional[str] = None,
goal: Optional[str] = None,
backstory: Optional[str] = None,
instructions: Optional[str] = None,
llm: Optional[Union[str, Any]] = None,
tools: Optional[List[Any]] = None,
function_calling_llm: Optional[Any] = None,
max_iter: int = 20,
@@ -155,16 +178,35 @@ class Agent:
knowledge_sources: Optional[List[Any]] = None,
use_system_prompt: Optional[bool] = True,
markdown: bool = True,
self_reflect: bool = True,
self_reflect: Optional[bool] = None,
max_reflect: int = 3,
min_reflect: int = 1,
reflect_llm: Optional[str] = None
):
self.name = name
self.role = role
self.goal = goal
self.backstory = backstory
self.llm = llm
# Handle backward compatibility for required fields
if all(x is None for x in [name, role, goal, backstory, instructions]):
raise ValueError("At least one of name, role, goal, backstory, or instructions must be provided")
# If instructions are provided, use them to set role, goal, and backstory
if instructions:
self.name = name or "Agent"
self.role = role or "Assistant"
self.goal = goal or instructions
self.backstory = backstory or instructions
# Set self_reflect to False by default for instruction-based agents
self.self_reflect = False if self_reflect is None else self_reflect
else:
# Use provided values or defaults
self.name = name or "Agent"
self.role = role or "Assistant"
self.goal = goal or "Help the user with their tasks"
self.backstory = backstory or "I am an AI assistant"
# Default to True for traditional agents if not specified
self.self_reflect = True if self_reflect is None else self_reflect
self.instructions = instructions
# Check for model name in environment variable if not provided
self.llm = llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
self.tools = tools if tools else [] # Store original tools
self.function_calling_llm = function_calling_llm
self.max_iter = max_iter
@@ -187,11 +229,32 @@ class Agent:
self.use_system_prompt = use_system_prompt
self.chat_history = []
self.markdown = markdown
self.self_reflect = self_reflect
self.max_reflect = max_reflect
self.min_reflect = min_reflect
self.reflect_llm = reflect_llm
# Use the same model selection logic for reflect_llm
self.reflect_llm = reflect_llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
self.console = Console() # Create a single console instance for the agent
# Initialize system prompt
self.system_prompt = f"""{self.backstory}\n
Your Role: {self.role}\n
Your Goal: {self.goal}
"""
def generate_task(self) -> 'Task':
"""Generate a Task object from the agent's instructions"""
from ..task.task import Task
description = self.instructions if self.instructions else f"Execute task as {self.role} with goal: {self.goal}"
expected_output = "Complete the assigned task successfully"
return Task(
name=self.name,
description=description,
expected_output=expected_output,
agent=self,
tools=self.tools
)
def execute_tool(self, function_name, arguments):
"""
@@ -202,25 +265,31 @@ class Agent:
# Try to find the function in the agent's tools list first
func = None
for tool in self.tools:
if callable(tool) and getattr(tool, '__name__', '') == function_name:
if (callable(tool) and getattr(tool, '__name__', '') == function_name) or \
(inspect.isclass(tool) and tool.__name__ == function_name):
func = tool
break
logging.debug(f"Looking for {function_name} in agent tools: {func is not None}")
# If not found in tools, try globals and main
if not func:
if func is None:
# If not found in tools, try globals and main
func = globals().get(function_name)
logging.debug(f"Looking for {function_name} in globals: {func is not None}")
if not func:
import __main__
func = getattr(__main__, function_name, None)
logging.debug(f"Looking for {function_name} in __main__: {func is not None}")
if func and callable(func):
if func:
try:
return func(**arguments)
# If it's a class with run method, instantiate and call run
if inspect.isclass(func) and hasattr(func, 'run'):
instance = func()
# Extract only the parameters that run() expects
run_params = {k: v for k, v in arguments.items()
if k in inspect.signature(instance.run).parameters
and k != 'self'}
return instance.run(**run_params)
# Otherwise treat as regular function
elif callable(func):
return func(**arguments)
except Exception as e:
error_msg = str(e)
logging.error(f"Error executing tool {function_name}: {error_msg}")
@@ -347,19 +416,36 @@ class Agent:
display_error(f"Error in chat completion: {e}")
return None
def chat(self, prompt, temperature=0.2, tools=None, output_json=None):
def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None):
if self.use_system_prompt:
system_prompt = f"""{self.backstory}\n
Your Role: {self.role}\n
Your Goal: {self.goal}
"""
if output_json:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_json.schema_json()}"
elif output_pydantic:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_pydantic.schema_json()}"
else:
system_prompt = None
messages = []
if system_prompt:
messages.append({"role": "system", "content": system_prompt})
messages.extend(self.chat_history)
# Modify prompt if output_json or output_pydantic is specified
original_prompt = prompt
if output_json or output_pydantic:
if isinstance(prompt, str):
prompt += "\nReturn ONLY a valid JSON object. No other text or explanation."
elif isinstance(prompt, list):
# For multimodal prompts, append to the text content
for item in prompt:
if item["type"] == "text":
item["text"] += "\nReturn ONLY a valid JSON object. No other text or explanation."
break
if isinstance(prompt, list):
# If we receive a multimodal prompt list, place it directly in the user message
messages.append({"role": "user", "content": prompt})
@@ -385,13 +471,14 @@ Your Goal: {self.goal}
response = self._chat_completion(messages, temperature=temperature, tools=tools if tools else None)
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:
messages.append({
"role": "assistant",
"content": response.choices[0].message.content,
"content": response_text,
"tool_calls": tool_calls
})
@@ -423,15 +510,23 @@ Your Goal: {self.goal}
if not response:
return None
response_text = response.choices[0].message.content.strip()
else:
response_text = response.choices[0].message.content.strip()
# Handle output_json or output_pydantic if specified
if output_json or output_pydantic:
# Add to chat history and return raw response
self.chat_history.append({"role": "user", "content": original_prompt})
self.chat_history.append({"role": "assistant", "content": response_text})
if self.verbose:
display_interaction(original_prompt, response_text, markdown=self.markdown,
generation_time=time.time() - start_time, console=self.console)
return response_text
if not self.self_reflect:
self.chat_history.append({"role": "user", "content": prompt})
self.chat_history.append({"role": "user", "content": original_prompt})
self.chat_history.append({"role": "assistant", "content": response_text})
if self.verbose:
logging.info(f"Agent {self.name} final response: {response_text}")
display_interaction(prompt, response_text, markdown=self.markdown, generation_time=time.time() - start_time, console=self.console)
display_interaction(original_prompt, response_text, markdown=self.markdown, generation_time=time.time() - start_time, console=self.console)
return response_text
reflection_prompt = f"""
@@ -492,4 +587,150 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
except Exception as e:
display_error(f"Error in chat: {e}", console=self.console)
return None
return None
def clean_json_output(self, output: str) -> str:
"""Clean and extract JSON from response text."""
cleaned = output.strip()
# Remove markdown code blocks if present
if cleaned.startswith("```json"):
cleaned = cleaned[len("```json"):].strip()
if cleaned.startswith("```"):
cleaned = cleaned[len("```"):].strip()
if cleaned.endswith("```"):
cleaned = cleaned[:-3].strip()
return cleaned
async def achat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None):
"""Async version of chat method"""
try:
# Build system prompt
system_prompt = self.system_prompt
if output_json:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_json.schema_json()}"
elif output_pydantic:
system_prompt += f"\nReturn ONLY a JSON object that matches this Pydantic model: {output_pydantic.schema_json()}"
# Build messages
if isinstance(prompt, str):
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt + ("\nReturn ONLY a valid JSON object. No other text or explanation." if (output_json or output_pydantic) else "")}
]
else:
# For multimodal prompts
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": prompt}
]
if output_json or output_pydantic:
# Add JSON instruction to text content
for item in messages[-1]["content"]:
if item["type"] == "text":
item["text"] += "\nReturn ONLY a valid JSON object. No other text or explanation."
break
# 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__))
# 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
except Exception as e:
display_error(f"Error in chat completion: {e}")
return None
async def _achat_completion(self, response, tools):
"""Async version of _chat_completion method"""
try:
message = response.choices[0].message
if not hasattr(message, 'tool_calls') or not message.tool_calls:
return message.content
results = []
for tool_call in message.tool_calls:
try:
function_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
# Find the matching tool
tool = next((t for t in tools if t.__name__ == function_name), None)
if not tool:
display_error(f"Tool {function_name} not found")
continue
# Check if the tool is async
if asyncio.iscoroutinefunction(tool):
result = await tool(**arguments)
else:
# Run sync function in executor to avoid blocking
loop = asyncio.get_event_loop()
result = await loop.run_in_executor(None, lambda: tool(**arguments))
results.append(result)
except Exception as e:
display_error(f"Error executing tool {function_name}: {e}")
results.append(None)
# If we have results, format them into a response
if results:
formatted_results = "\n".join([str(r) for r in results if r is not None])
if formatted_results:
messages = [
{"role": "system", "content": self.system_prompt},
{"role": "assistant", "content": "Here are the tool results:"},
{"role": "user", "content": formatted_results + "\nPlease process these results and provide a final response."}
]
try:
async_client = AsyncOpenAI()
final_response = await async_client.chat.completions.create(
model=self.llm,
messages=messages,
temperature=0.2
)
return final_response.choices[0].message.content
except Exception as e:
display_error(f"Error in final chat completion: {e}")
return formatted_results
return formatted_results
return None
except Exception as e:
display_error(f"Error in _achat_completion: {e}")
return None
+307 -139
View File
@@ -2,7 +2,7 @@ import os
import time
import json
import logging
from typing import Any, Dict, Optional
from typing import Any, Dict, Optional, List
from pydantic import BaseModel
from rich.text import Text
from rich.panel import Panel
@@ -10,6 +10,8 @@ from rich.console import Console
from ..main import display_error, TaskOutput, error_logs, client
from ..agent.agent import Agent
from ..task.task import Task
from ..process.process import Process, LoopItems
import asyncio
def encode_file_to_base64(file_path: str) -> str:
"""Base64-encode a file."""
@@ -39,7 +41,10 @@ def process_video(video_path: str, seconds_per_frame=2):
return base64_frames
class PraisonAIAgents:
def __init__(self, agents, tasks, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None):
def __init__(self, agents, tasks=None, verbose=0, completion_checker=None, max_retries=5, process="sequential", manager_llm=None):
if not agents:
raise ValueError("At least one agent must be provided")
self.agents = agents
self.tasks = {}
if max_retries < 3:
@@ -49,12 +54,42 @@ class PraisonAIAgents:
self.verbose = verbose
self.max_retries = max_retries
self.process = process
if not manager_llm:
logging.debug("No manager_llm provided. Using OPENAI_MODEL_NAME environment variable or defaulting to 'gpt-4o'")
self.manager_llm = manager_llm if manager_llm else os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
# Check for manager_llm in environment variable if not provided
self.manager_llm = manager_llm or os.getenv('OPENAI_MODEL_NAME', 'gpt-4o')
if self.verbose:
logging.info(f"Using model {self.manager_llm} for manager")
# If no tasks provided, generate them from agents
if tasks is None:
tasks = []
for agent in self.agents:
task = agent.generate_task()
tasks.append(task)
logging.info(f"Auto-generated {len(tasks)} tasks from agents")
else:
# Validate tasks for backward compatibility
if not tasks:
raise ValueError("If tasks are provided, at least one task must be present")
logging.info(f"Using {len(tasks)} provided tasks")
# Add tasks and set their status
for task in tasks:
self.add_task(task)
task.status = "not started"
# If tasks were auto-generated from agents or process is sequential, set up sequential flow
if len(tasks) > 1 and (process == "sequential" or all(task.next_tasks == [] for task in tasks)):
for i in range(len(tasks) - 1):
# Set up next task relationship
tasks[i].next_tasks = [tasks[i + 1].name]
# Set up context for the next task to include the current task
if tasks[i + 1].context is None:
tasks[i + 1].context = []
tasks[i + 1].context.append(tasks[i])
logging.info("Set up sequential flow with automatic context passing")
self._state = {} # Add state storage at PraisonAIAgents level
def add_task(self, task):
task_id = self.task_id_counter
@@ -80,7 +115,8 @@ class PraisonAIAgents:
return True
return len(agent_output.strip()) > 0
def execute_task(self, task_id):
async def aexecute_task(self, task_id):
"""Async version of execute_task method"""
if task_id not in self.tasks:
display_error(f"Error: Task with ID {task_id} does not exist")
return
@@ -115,9 +151,226 @@ Expected Output: {task.expected_output}.
else:
context_results += f"Previous task {context_task.name if context_task.name else context_task.description} had no result.\n"
task_prompt += f"""
Here are the results of previous tasks that might be useful:\n
{context_results}
"""
Here are the results of previous tasks that might be useful:\n
{context_results}
"""
task_prompt += "Please provide only the final result of your work. Do not add any conversation or extra explanation."
if self.verbose >= 2:
logging.info(f"Executing task {task_id}: {task.description} using {executor_agent.name}")
logging.debug(f"Starting execution of task {task_id} with prompt:\n{task_prompt}")
if task.images:
def _get_multimodal_message(text_prompt, images):
content = [{"type": "text", "text": text_prompt}]
for img in images:
# If local file path for a valid image
if os.path.exists(img):
ext = os.path.splitext(img)[1].lower()
# If it's a .mp4, convert to frames
if ext == ".mp4":
frames = process_video(img, seconds_per_frame=1)
content.append({"type": "text", "text": "These are frames from the video."})
for f in frames:
content.append({
"type": "image_url",
"image_url": {"url": f"data:image/jpg;base64,{f}"}
})
else:
encoded = encode_file_to_base64(img)
content.append({
"type": "image_url",
"image_url": {
"url": f"data:image/{ext.lstrip('.')};base64,{encoded}"
}
})
else:
# Treat as a remote URL
content.append({
"type": "image_url",
"image_url": {"url": img}
})
return content
agent_output = await executor_agent.achat(
_get_multimodal_message(task_prompt, task.images),
tools=task.tools,
output_json=task.output_json,
output_pydantic=task.output_pydantic
)
else:
agent_output = await executor_agent.achat(
task_prompt,
tools=task.tools,
output_json=task.output_json,
output_pydantic=task.output_pydantic
)
if agent_output:
task_output = TaskOutput(
description=task.description,
summary=task.description[:10],
raw=agent_output,
agent=executor_agent.name,
output_format="RAW"
)
if task.output_json:
cleaned = self.clean_json_output(agent_output)
try:
parsed = json.loads(cleaned)
task_output.json_dict = parsed
task_output.output_format = "JSON"
except:
logging.warning(f"Warning: Could not parse output of task {task_id} as JSON")
logging.debug(f"Output that failed JSON parsing: {agent_output}")
if task.output_pydantic:
cleaned = self.clean_json_output(agent_output)
try:
parsed = json.loads(cleaned)
pyd_obj = task.output_pydantic(**parsed)
task_output.pydantic = pyd_obj
task_output.output_format = "Pydantic"
except:
logging.warning(f"Warning: Could not parse output of task {task_id} as Pydantic Model")
logging.debug(f"Output that failed Pydantic parsing: {agent_output}")
task.result = task_output
return task_output
else:
task.status = "failed"
return None
async def arun_task(self, task_id):
"""Async version of run_task method"""
if task_id not in self.tasks:
display_error(f"Error: Task with ID {task_id} does not exist")
return
task = self.tasks[task_id]
if task.status == "completed":
logging.info(f"Task with ID {task_id} is already completed")
return
retries = 0
while task.status != "completed" and retries < self.max_retries:
logging.debug(f"Attempt {retries+1} for task {task_id}")
if task.status in ["not started", "in progress"]:
task_output = await self.aexecute_task(task_id)
if task_output and self.completion_checker(task, task_output.raw):
task.status = "completed"
if task.callback:
await task.execute_callback(task_output)
self.save_output_to_file(task, task_output)
if self.verbose >= 1:
logging.info(f"Task {task_id} completed successfully.")
else:
task.status = "in progress"
if self.verbose >= 1:
logging.info(f"Task {task_id} not completed, retrying")
await asyncio.sleep(1)
retries += 1
else:
if task.status == "failed":
logging.info("Task is failed, resetting to in-progress for another try...")
task.status = "in progress"
else:
logging.info("Invalid Task status")
break
if retries == self.max_retries and task.status != "completed":
logging.info(f"Task {task_id} failed after {self.max_retries} retries.")
async def arun_all_tasks(self):
"""Async version of run_all_tasks method"""
process = Process(
tasks=self.tasks,
agents=self.agents,
manager_llm=self.manager_llm,
verbose=self.verbose
)
if self.process == "workflow":
async for task_id in process.aworkflow():
if self.tasks[task_id].async_execution:
await self.arun_task(task_id)
else:
self.run_task(task_id)
elif self.process == "sequential":
async for task_id in process.asequential():
if self.tasks[task_id].async_execution:
await self.arun_task(task_id)
else:
self.run_task(task_id)
elif self.process == "hierarchical":
async for task_id in process.ahierarchical():
if isinstance(task_id, Task):
task_id = self.add_task(task_id)
if self.tasks[task_id].async_execution:
await self.arun_task(task_id)
else:
self.run_task(task_id)
async def astart(self):
"""Async version of start method"""
await self.arun_all_tasks()
return {
"task_status": self.get_all_tasks_status(),
"task_results": {task_id: self.get_task_result(task_id) for task_id in self.tasks}
}
def save_output_to_file(self, task, task_output):
if task.output_file:
try:
if task.create_directory:
os.makedirs(os.path.dirname(task.output_file), exist_ok=True)
with open(task.output_file, "w") as f:
f.write(str(task_output))
if self.verbose >= 1:
logging.info(f"Task output saved to {task.output_file}")
except Exception as e:
display_error(f"Error saving task output to file: {e}")
def execute_task(self, task_id):
"""Synchronous version of execute_task method"""
if task_id not in self.tasks:
display_error(f"Error: Task with ID {task_id} does not exist")
return
task = self.tasks[task_id]
# Only import multimodal dependencies if task has images
if task.images and task.status == "not started":
try:
import cv2
import base64
from moviepy import VideoFileClip
except ImportError as e:
display_error(f"Error: Missing required dependencies for image/video processing: {e}")
display_error("Please install with: pip install opencv-python moviepy")
task.status = "failed"
return None
if task.status == "not started":
task.status = "in progress"
executor_agent = task.agent
task_prompt = f"""
You need to do the following task: {task.description}.
Expected Output: {task.expected_output}.
"""
if task.context:
context_results = ""
for context_task in task.context:
if context_task.result:
context_results += f"Result of previous task {context_task.name if context_task.name else context_task.description}: {context_task.result.raw}\n"
else:
context_results += f"Previous task {context_task.name if context_task.name else context_task.description} had no result.\n"
task_prompt += f"""
Here are the results of previous tasks that might be useful:\n
{context_results}
"""
task_prompt += "Please provide only the final result of your work. Do not add any conversation or extra explanation."
if self.verbose >= 2:
@@ -159,10 +412,17 @@ Expected Output: {task.expected_output}.
agent_output = executor_agent.chat(
_get_multimodal_message(task_prompt, task.images),
tools=task.tools
tools=task.tools,
output_json=task.output_json,
output_pydantic=task.output_pydantic
)
else:
agent_output = executor_agent.chat(task_prompt, tools=task.tools)
agent_output = executor_agent.chat(
task_prompt,
tools=task.tools,
output_json=task.output_json,
output_pydantic=task.output_pydantic
)
if agent_output:
task_output = TaskOutput(
@@ -200,19 +460,8 @@ Expected Output: {task.expected_output}.
task.status = "failed"
return None
def save_output_to_file(self, task, task_output):
if task.output_file:
try:
if task.create_directory:
os.makedirs(os.path.dirname(task.output_file), exist_ok=True)
with open(task.output_file, "w") as f:
f.write(str(task_output))
if self.verbose >= 1:
logging.info(f"Task output saved to {task.output_file}")
except Exception as e:
display_error(f"Error saving task output to file: {e}")
def run_task(self, task_id):
"""Synchronous version of run_task method"""
if task_id not in self.tasks:
display_error(f"Error: Task with ID {task_id} does not exist")
return
@@ -251,122 +500,25 @@ Expected Output: {task.expected_output}.
logging.info(f"Task {task_id} failed after {self.max_retries} retries.")
def run_all_tasks(self):
if self.process == "sequential":
for task_id in self.tasks:
if self.tasks[task_id].status != "completed":
self.run_task(task_id)
"""Synchronous version of run_all_tasks method"""
process = Process(
tasks=self.tasks,
agents=self.agents,
manager_llm=self.manager_llm,
verbose=self.verbose
)
if self.process == "workflow":
for task_id in process.workflow():
self.run_task(task_id)
elif self.process == "sequential":
for task_id in process.sequential():
self.run_task(task_id)
elif self.process == "hierarchical":
logging.debug(f"Starting hierarchical task execution with {len(self.tasks)} tasks")
manager_agent = Agent(
name="Manager",
role="Project manager",
goal="Manage the entire flow of tasks and delegate them to the right agent",
backstory="Expert project manager to coordinate tasks among agents",
llm=self.manager_llm,
verbose=self.verbose,
markdown=True,
self_reflect=False
)
class ManagerInstructions(BaseModel):
task_id: int
agent_name: str
action: str
manager_task = Task(
name="manager_task",
description="Decide the order of tasks and which agent executes them",
expected_output="All tasks completed successfully",
agent=manager_agent
)
manager_task_id = self.add_task(manager_task)
logging.info(f"Created manager task with ID {manager_task_id}")
completed_count = 0
total_tasks = len(self.tasks) - 1
logging.info(f"Need to complete {total_tasks} tasks (excluding manager task)")
while completed_count < total_tasks:
tasks_summary = []
for tid, tk in self.tasks.items():
if tk.name == "manager_task":
continue
task_info = {
"task_id": tid,
"name": tk.name,
"description": tk.description,
"status": tk.status if tk.status else "not started",
"agent": tk.agent.name if tk.agent else "No agent"
}
tasks_summary.append(task_info)
logging.info(f"Task {tid} status: {task_info}")
manager_prompt = f"""
Here is the current status of all tasks except yours (manager_task):
{tasks_summary}
Provide a JSON with the structure:
{{
"task_id": <int>,
"agent_name": "<string>",
"action": "<execute or stop>"
}}
"""
try:
logging.info("Requesting manager instructions...")
manager_response = client.beta.chat.completions.parse(
model=self.manager_llm,
messages=[
{"role": "system", "content": manager_task.description},
{"role": "user", "content": manager_prompt}
],
temperature=0.7,
response_format=ManagerInstructions
)
parsed_instructions = manager_response.choices[0].message.parsed
logging.info(f"Manager instructions: {parsed_instructions}")
except Exception as e:
display_error(f"Manager parse error: {e}")
logging.error(f"Manager parse error: {str(e)}", exc_info=True)
break
selected_task_id = parsed_instructions.task_id
selected_agent_name = parsed_instructions.agent_name
action = parsed_instructions.action
logging.info(f"Manager selected task_id={selected_task_id}, agent={selected_agent_name}, action={action}")
if action.lower() == "stop":
logging.info("Manager decided to stop task execution")
break
if selected_task_id not in self.tasks:
error_msg = f"Manager selected invalid task id {selected_task_id}"
display_error(error_msg)
logging.error(error_msg)
break
original_agent = self.tasks[selected_task_id].agent.name if self.tasks[selected_task_id].agent else "None"
for a in self.agents:
if a.name == selected_agent_name:
self.tasks[selected_task_id].agent = a
logging.info(f"Changed agent for task {selected_task_id} from {original_agent} to {selected_agent_name}")
break
if self.tasks[selected_task_id].status != "completed":
logging.info(f"Starting execution of task {selected_task_id}")
self.run_task(selected_task_id)
logging.info(f"Finished execution of task {selected_task_id}, status: {self.tasks[selected_task_id].status}")
if self.tasks[selected_task_id].status == "completed":
completed_count += 1
logging.info(f"Task {selected_task_id} completed. Total completed: {completed_count}/{total_tasks}")
self.tasks[manager_task.id].status = "completed"
if self.verbose >= 1:
logging.info("All tasks completed under manager supervision.")
logging.info("Hierarchical task execution finished")
for task_id in process.hierarchical():
if isinstance(task_id, Task):
task_id = self.add_task(task_id)
self.run_task(task_id)
def get_task_status(self, task_id):
if task_id in self.tasks:
@@ -397,4 +549,20 @@ Provide a JSON with the structure:
return {
"task_status": self.get_all_tasks_status(),
"task_results": {task_id: self.get_task_result(task_id) for task_id in self.tasks}
}
}
def set_state(self, key: str, value: Any) -> None:
"""Set a state value"""
self._state[key] = value
def get_state(self, key: str, default: Any = None) -> Any:
"""Get a state value"""
return self._state.get(key, default)
def update_state(self, updates: Dict) -> None:
"""Update multiple state values"""
self._state.update(updates)
def clear_state(self) -> None:
"""Clear all state values"""
self._state.clear()
+196 -8
View File
@@ -12,6 +12,7 @@ from rich.text import Text
from rich.markdown import Markdown
from rich.logging import RichHandler
from rich.live import Live
import asyncio
LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO').upper()
@@ -25,6 +26,50 @@ logging.basicConfig(
# Global list to store error logs
error_logs = []
# Separate registries for sync and async callbacks
sync_display_callbacks = {}
async_display_callbacks = {}
# At the top of the file, add display_callbacks to __all__
__all__ = [
'error_logs',
'register_display_callback',
'sync_display_callbacks',
'async_display_callbacks',
# ... other exports
]
def register_display_callback(display_type: str, callback_fn, is_async: bool = False):
"""Register a synchronous or asynchronous callback function for a specific display type.
Args:
display_type (str): Type of display event ('interaction', 'self_reflection', etc.)
callback_fn: The callback function to register
is_async (bool): Whether the callback is asynchronous
"""
if is_async:
async_display_callbacks[display_type] = callback_fn
else:
sync_display_callbacks[display_type] = callback_fn
async def execute_callback(display_type: str, **kwargs):
"""Execute both sync and async callbacks for a given display type.
Args:
display_type (str): Type of display event
**kwargs: Arguments to pass to the callback functions
"""
# Execute synchronous callback if registered
if display_type in sync_display_callbacks:
callback = sync_display_callbacks[display_type]
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, lambda: callback(**kwargs))
# Execute asynchronous callback if registered
if display_type in async_display_callbacks:
callback = async_display_callbacks[display_type]
await callback(**kwargs)
def _clean_display_content(content: str, max_length: int = 20000) -> str:
"""Helper function to clean and truncate content for display."""
if not content or not str(content).strip():
@@ -46,21 +91,30 @@ def _clean_display_content(content: str, max_length: int = 20000) -> str:
return content.strip()
def display_interaction(message, response, markdown=True, generation_time=None, console=None):
"""Display the interaction between user and assistant."""
"""Synchronous version of display_interaction."""
if console is None:
console = Console()
if generation_time:
console.print(Text(f"Response generated in {generation_time:.1f}s", style="dim"))
# Handle multimodal content (list)
if isinstance(message, list):
# Extract just the text content from the multimodal message
text_content = next((item["text"] for item in message if item["type"] == "text"), "")
message = text_content
message = _clean_display_content(str(message))
response = _clean_display_content(str(response))
# Execute synchronous callback if registered
if 'interaction' in sync_display_callbacks:
sync_display_callbacks['interaction'](
message=message,
response=response,
markdown=markdown,
generation_time=generation_time
)
# Rest of the display logic...
if generation_time:
console.print(Text(f"Response generated in {generation_time:.1f}s", style="dim"))
if markdown:
console.print(Panel.fit(Markdown(message), title="Message", border_style="cyan"))
console.print(Panel.fit(Markdown(response), title="Response", border_style="cyan"))
@@ -74,6 +128,11 @@ def display_self_reflection(message: str, console=None):
if console is None:
console = Console()
message = _clean_display_content(str(message))
# Execute callback if registered
if 'self_reflection' in sync_display_callbacks:
sync_display_callbacks['self_reflection'](message=message)
console.print(Panel.fit(Text(message, style="bold yellow"), title="Self Reflection", border_style="magenta"))
def display_instruction(message: str, console=None):
@@ -82,6 +141,11 @@ def display_instruction(message: str, console=None):
if console is None:
console = Console()
message = _clean_display_content(str(message))
# Execute callback if registered
if 'instruction' in sync_display_callbacks:
sync_display_callbacks['instruction'](message=message)
console.print(Panel.fit(Text(message, style="bold blue"), title="Instruction", border_style="cyan"))
def display_tool_call(message: str, console=None):
@@ -90,6 +154,11 @@ def display_tool_call(message: str, console=None):
if console is None:
console = Console()
message = _clean_display_content(str(message))
# Execute callback if registered
if 'tool_call' in sync_display_callbacks:
sync_display_callbacks['tool_call'](message=message)
console.print(Panel.fit(Text(message, style="bold cyan"), title="Tool Call", border_style="green"))
def display_error(message: str, console=None):
@@ -98,19 +167,138 @@ def display_error(message: str, console=None):
if console is None:
console = Console()
message = _clean_display_content(str(message))
# Execute callback if registered
if 'error' in sync_display_callbacks:
sync_display_callbacks['error'](message=message)
console.print(Panel.fit(Text(message, style="bold red"), title="Error", border_style="red"))
# Store errors
error_logs.append(message)
def display_generating(content: str = "", start_time: Optional[float] = None):
if not content or not str(content).strip():
return Panel("", title="", border_style="green") # Return empty panel when no content
return Panel("", title="", border_style="green")
elapsed_str = ""
if start_time is not None:
elapsed = time.time() - start_time
elapsed_str = f" {elapsed:.1f}s"
content = _clean_display_content(str(content))
# Execute callback if registered
if 'generating' in sync_display_callbacks:
sync_display_callbacks['generating'](
content=content,
elapsed_time=elapsed_str.strip() if elapsed_str else None
)
return Panel(Markdown(content), title=f"Generating...{elapsed_str}", border_style="green")
# Async versions with 'a' prefix
async def adisplay_interaction(message, response, markdown=True, generation_time=None, console=None):
"""Async version of display_interaction."""
if console is None:
console = Console()
if isinstance(message, list):
text_content = next((item["text"] for item in message if item["type"] == "text"), "")
message = text_content
message = _clean_display_content(str(message))
response = _clean_display_content(str(response))
# Execute callbacks
await execute_callback(
'interaction',
message=message,
response=response,
markdown=markdown,
generation_time=generation_time
)
# Rest of the display logic...
if generation_time:
console.print(Text(f"Response generated in {generation_time:.1f}s", style="dim"))
if markdown:
console.print(Panel.fit(Markdown(message), title="Message", border_style="cyan"))
console.print(Panel.fit(Markdown(response), title="Response", border_style="cyan"))
else:
console.print(Panel.fit(Text(message, style="bold green"), title="Message", border_style="cyan"))
console.print(Panel.fit(Text(response, style="bold blue"), title="Response", border_style="cyan"))
async def adisplay_self_reflection(message: str, console=None):
"""Async version of display_self_reflection."""
if not message or not message.strip():
return
if console is None:
console = Console()
message = _clean_display_content(str(message))
if 'self_reflection' in async_display_callbacks:
await async_display_callbacks['self_reflection'](message=message)
console.print(Panel.fit(Text(message, style="bold yellow"), title="Self Reflection", border_style="magenta"))
async def adisplay_instruction(message: str, console=None):
"""Async version of display_instruction."""
if not message or not message.strip():
return
if console is None:
console = Console()
message = _clean_display_content(str(message))
if 'instruction' in async_display_callbacks:
await async_display_callbacks['instruction'](message=message)
console.print(Panel.fit(Text(message, style="bold blue"), title="Instruction", border_style="cyan"))
async def adisplay_tool_call(message: str, console=None):
"""Async version of display_tool_call."""
if not message or not message.strip():
return
if console is None:
console = Console()
message = _clean_display_content(str(message))
if 'tool_call' in async_display_callbacks:
await async_display_callbacks['tool_call'](message=message)
console.print(Panel.fit(Text(message, style="bold cyan"), title="Tool Call", border_style="green"))
async def adisplay_error(message: str, console=None):
"""Async version of display_error."""
if not message or not message.strip():
return
if console is None:
console = Console()
message = _clean_display_content(str(message))
if 'error' in async_display_callbacks:
await async_display_callbacks['error'](message=message)
console.print(Panel.fit(Text(message, style="bold red"), title="Error", border_style="red"))
error_logs.append(message)
async def adisplay_generating(content: str = "", start_time: Optional[float] = None):
"""Async version of display_generating."""
if not content or not str(content).strip():
return Panel("", title="", border_style="green")
elapsed_str = ""
if start_time is not None:
elapsed = time.time() - start_time
elapsed_str = f" {elapsed:.1f}s"
content = _clean_display_content(str(content))
if 'generating' in async_display_callbacks:
await async_display_callbacks['generating'](
content=content,
elapsed_time=elapsed_str.strip() if elapsed_str else None
)
return Panel(Markdown(content), title=f"Generating...{elapsed_str}", border_style="green")
def clean_triple_backticks(text: str) -> str:
@@ -0,0 +1,3 @@
from .process import Process
__all__ = ['Process']
+534
View File
@@ -0,0 +1,534 @@
import logging
import asyncio
from typing import Dict, Optional, List, Any, AsyncGenerator
from pydantic import BaseModel
from ..agent.agent import Agent
from ..task.task import Task
from ..main import display_error, client
class LoopItems(BaseModel):
items: List[Any]
class Process:
def __init__(self, tasks: Dict[str, Task], agents: List[Agent], manager_llm: Optional[str] = None, verbose: bool = False):
self.tasks = tasks
self.agents = agents
self.manager_llm = manager_llm
self.verbose = verbose
async def aworkflow(self) -> AsyncGenerator[str, None]:
"""Async version of workflow method"""
# Build workflow relationships first
for task in self.tasks.values():
if task.next_tasks:
for next_task_name in task.next_tasks:
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
if next_task:
next_task.previous_tasks.append(task.name)
# Find start task
start_task = None
for task_id, task in self.tasks.items():
if task.is_start:
start_task = task
break
if not start_task:
start_task = list(self.tasks.values())[0]
logging.info("No start task marked, using first task")
current_task = start_task
visited_tasks = set()
loop_data = {} # Store loop-specific data
while current_task and current_task.id not in visited_tasks:
task_id = current_task.id
logging.info(f"Executing workflow task: {current_task.name if current_task.name else task_id}")
# Add context from previous tasks to description
if current_task.previous_tasks or current_task.context:
context = "\nInput data from previous tasks:"
# Add data from previous tasks in workflow
for prev_name in current_task.previous_tasks:
prev_task = next((t for t in self.tasks.values() if t.name == prev_name), None)
if prev_task and prev_task.result:
# Handle loop data
if current_task.task_type == "loop":
# create a loop manager Agent
loop_manager = Agent(
name="Loop Manager",
role="Loop data processor",
goal="Process loop data and convert it to list format",
backstory="Expert at handling loop data and converting it to proper format",
llm=self.manager_llm,
verbose=self.verbose,
markdown=True
)
# get the loop data convert it to list using calling Agent class chat
loop_prompt = f"""
Process this data into a list format:
{prev_task.result.raw}
Return a JSON object with an 'items' array containing the items to process.
"""
if current_task.async_execution:
loop_data_str = await loop_manager.achat(
prompt=loop_prompt,
output_json=LoopItems
)
else:
loop_data_str = loop_manager.chat(
prompt=loop_prompt,
output_json=LoopItems
)
try:
# The response will already be parsed into LoopItems model
loop_data[f"loop_{current_task.name}"] = {
"items": loop_data_str.items,
"index": 0,
"remaining": len(loop_data_str.items)
}
context += f"\nCurrent loop item: {loop_data_str.items[0]}"
except Exception as e:
display_error(f"Failed to process loop data: {e}")
context += f"\n{prev_name}: {prev_task.result.raw}"
else:
context += f"\n{prev_name}: {prev_task.result.raw}"
# Add data from context tasks
if current_task.context:
for ctx_task in current_task.context:
if ctx_task.result and ctx_task.name != current_task.name:
context += f"\n{ctx_task.name}: {ctx_task.result.raw}"
# Update task description with context
current_task.description = current_task.description + context
# Execute task using existing run_task method
yield task_id
visited_tasks.add(task_id)
# Handle loop progression
if current_task.task_type == "loop":
loop_key = f"loop_{current_task.name}"
if loop_key in loop_data:
loop_info = loop_data[loop_key]
loop_info["index"] += 1
has_more = loop_info["remaining"] > 0
# Update result to trigger correct condition
if current_task.result:
result = current_task.result.raw
if has_more:
result += "\nmore"
else:
result += "\ndone"
current_task.result.raw = result
# Determine next task based on result
next_task = None
if current_task.result:
if current_task.task_type in ["decision", "loop"]:
result = current_task.result.raw.lower()
# Check conditions
for condition, tasks in current_task.condition.items():
if condition.lower() in result and tasks:
next_task_name = tasks[0]
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
# For loops, allow revisiting the same task
if next_task and next_task.id == current_task.id:
visited_tasks.discard(current_task.id)
break
if not next_task and current_task.next_tasks:
next_task_name = current_task.next_tasks[0]
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
current_task = next_task
if not current_task:
logging.info("Workflow execution completed")
break
async def asequential(self) -> AsyncGenerator[str, None]:
"""Async version of sequential method"""
for task_id in self.tasks:
if self.tasks[task_id].status != "completed":
yield task_id
async def ahierarchical(self) -> AsyncGenerator[str, None]:
"""Async version of hierarchical method"""
logging.debug(f"Starting hierarchical task execution with {len(self.tasks)} tasks")
manager_agent = Agent(
name="Manager",
role="Project manager",
goal="Manage the entire flow of tasks and delegate them to the right agent",
backstory="Expert project manager to coordinate tasks among agents",
llm=self.manager_llm,
verbose=self.verbose,
markdown=True,
self_reflect=False
)
class ManagerInstructions(BaseModel):
task_id: int
agent_name: str
action: str
manager_task = Task(
name="manager_task",
description="Decide the order of tasks and which agent executes them",
expected_output="All tasks completed successfully",
agent=manager_agent
)
manager_task_id = yield manager_task
logging.info(f"Created manager task with ID {manager_task_id}")
completed_count = 0
total_tasks = len(self.tasks) - 1
logging.info(f"Need to complete {total_tasks} tasks (excluding manager task)")
while completed_count < total_tasks:
tasks_summary = []
for tid, tk in self.tasks.items():
if tk.name == "manager_task":
continue
task_info = {
"task_id": tid,
"name": tk.name,
"description": tk.description,
"status": tk.status if tk.status else "not started",
"agent": tk.agent.name if tk.agent else "No agent"
}
tasks_summary.append(task_info)
logging.info(f"Task {tid} status: {task_info}")
manager_prompt = f"""
Here is the current status of all tasks except yours (manager_task):
{tasks_summary}
Provide a JSON with the structure:
{{
"task_id": <int>,
"agent_name": "<string>",
"action": "<execute or stop>"
}}
"""
try:
logging.info("Requesting manager instructions...")
if manager_task.async_execution:
manager_response = await client.beta.chat.completions.parse(
model=self.manager_llm,
messages=[
{"role": "system", "content": manager_task.description},
{"role": "user", "content": manager_prompt}
],
temperature=0.7,
response_format=ManagerInstructions
)
else:
manager_response = client.beta.chat.completions.parse(
model=self.manager_llm,
messages=[
{"role": "system", "content": manager_task.description},
{"role": "user", "content": manager_prompt}
],
temperature=0.7,
response_format=ManagerInstructions
)
parsed_instructions = manager_response.choices[0].message.parsed
logging.info(f"Manager instructions: {parsed_instructions}")
except Exception as e:
display_error(f"Manager parse error: {e}")
logging.error(f"Manager parse error: {str(e)}", exc_info=True)
break
selected_task_id = parsed_instructions.task_id
selected_agent_name = parsed_instructions.agent_name
action = parsed_instructions.action
logging.info(f"Manager selected task_id={selected_task_id}, agent={selected_agent_name}, action={action}")
if action.lower() == "stop":
logging.info("Manager decided to stop task execution")
break
if selected_task_id not in self.tasks:
error_msg = f"Manager selected invalid task id {selected_task_id}"
display_error(error_msg)
logging.error(error_msg)
break
original_agent = self.tasks[selected_task_id].agent.name if self.tasks[selected_task_id].agent else "None"
for a in self.agents:
if a.name == selected_agent_name:
self.tasks[selected_task_id].agent = a
logging.info(f"Changed agent for task {selected_task_id} from {original_agent} to {selected_agent_name}")
break
if self.tasks[selected_task_id].status != "completed":
logging.info(f"Starting execution of task {selected_task_id}")
yield selected_task_id
logging.info(f"Finished execution of task {selected_task_id}, status: {self.tasks[selected_task_id].status}")
if self.tasks[selected_task_id].status == "completed":
completed_count += 1
logging.info(f"Task {selected_task_id} completed. Total completed: {completed_count}/{total_tasks}")
self.tasks[manager_task.id].status = "completed"
if self.verbose >= 1:
logging.info("All tasks completed under manager supervision.")
logging.info("Hierarchical task execution finished")
def workflow(self):
"""Synchronous version of workflow method"""
# Build workflow relationships first
for task in self.tasks.values():
if task.next_tasks:
for next_task_name in task.next_tasks:
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
if next_task:
next_task.previous_tasks.append(task.name)
# Find start task
start_task = None
for task_id, task in self.tasks.items():
if task.is_start:
start_task = task
break
if not start_task:
start_task = list(self.tasks.values())[0]
logging.info("No start task marked, using first task")
current_task = start_task
visited_tasks = set()
loop_data = {} # Store loop-specific data
while current_task and current_task.id not in visited_tasks:
task_id = current_task.id
logging.info(f"Executing workflow task: {current_task.name if current_task.name else task_id}")
# Add context from previous tasks to description
if current_task.previous_tasks or current_task.context:
context = "\nInput data from previous tasks:"
# Add data from previous tasks in workflow
for prev_name in current_task.previous_tasks:
prev_task = next((t for t in self.tasks.values() if t.name == prev_name), None)
if prev_task and prev_task.result:
# Handle loop data
if current_task.task_type == "loop":
# create a loop manager Agent
loop_manager = Agent(
name="Loop Manager",
role="Loop data processor",
goal="Process loop data and convert it to list format",
backstory="Expert at handling loop data and converting it to proper format",
llm=self.manager_llm,
verbose=self.verbose,
markdown=True
)
# get the loop data convert it to list using calling Agent class chat
loop_prompt = f"""
Process this data into a list format:
{prev_task.result.raw}
Return a JSON object with an 'items' array containing the items to process.
"""
loop_data_str = loop_manager.chat(
prompt=loop_prompt,
output_json=LoopItems
)
try:
# The response will already be parsed into LoopItems model
loop_data[f"loop_{current_task.name}"] = {
"items": loop_data_str.items,
"index": 0,
"remaining": len(loop_data_str.items)
}
context += f"\nCurrent loop item: {loop_data_str.items[0]}"
except Exception as e:
display_error(f"Failed to process loop data: {e}")
context += f"\n{prev_name}: {prev_task.result.raw}"
else:
context += f"\n{prev_name}: {prev_task.result.raw}"
# Add data from context tasks
if current_task.context:
for ctx_task in current_task.context:
if ctx_task.result and ctx_task.name != current_task.name:
context += f"\n{ctx_task.name}: {ctx_task.result.raw}"
# Update task description with context
current_task.description = current_task.description + context
# Execute task using existing run_task method
yield task_id
visited_tasks.add(task_id)
# Handle loop progression
if current_task.task_type == "loop":
loop_key = f"loop_{current_task.name}"
if loop_key in loop_data:
loop_info = loop_data[loop_key]
loop_info["index"] += 1
has_more = loop_info["remaining"] > 0
# Update result to trigger correct condition
if current_task.result:
result = current_task.result.raw
if has_more:
result += "\nmore"
else:
result += "\ndone"
current_task.result.raw = result
# Determine next task based on result
next_task = None
if current_task.result:
if current_task.task_type in ["decision", "loop"]:
result = current_task.result.raw.lower()
# Check conditions
for condition, tasks in current_task.condition.items():
if condition.lower() in result and tasks:
next_task_name = tasks[0]
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
# For loops, allow revisiting the same task
if next_task and next_task.id == current_task.id:
visited_tasks.discard(current_task.id)
break
if not next_task and current_task.next_tasks:
next_task_name = current_task.next_tasks[0]
next_task = next((t for t in self.tasks.values() if t.name == next_task_name), None)
current_task = next_task
if not current_task:
logging.info("Workflow execution completed")
break
def sequential(self):
"""Synchronous version of sequential method"""
for task_id in self.tasks:
if self.tasks[task_id].status != "completed":
yield task_id
def hierarchical(self):
"""Synchronous version of hierarchical method"""
logging.debug(f"Starting hierarchical task execution with {len(self.tasks)} tasks")
manager_agent = Agent(
name="Manager",
role="Project manager",
goal="Manage the entire flow of tasks and delegate them to the right agent",
backstory="Expert project manager to coordinate tasks among agents",
llm=self.manager_llm,
verbose=self.verbose,
markdown=True,
self_reflect=False
)
class ManagerInstructions(BaseModel):
task_id: int
agent_name: str
action: str
manager_task = Task(
name="manager_task",
description="Decide the order of tasks and which agent executes them",
expected_output="All tasks completed successfully",
agent=manager_agent
)
manager_task_id = yield manager_task
logging.info(f"Created manager task with ID {manager_task_id}")
completed_count = 0
total_tasks = len(self.tasks) - 1
logging.info(f"Need to complete {total_tasks} tasks (excluding manager task)")
while completed_count < total_tasks:
tasks_summary = []
for tid, tk in self.tasks.items():
if tk.name == "manager_task":
continue
task_info = {
"task_id": tid,
"name": tk.name,
"description": tk.description,
"status": tk.status if tk.status else "not started",
"agent": tk.agent.name if tk.agent else "No agent"
}
tasks_summary.append(task_info)
logging.info(f"Task {tid} status: {task_info}")
manager_prompt = f"""
Here is the current status of all tasks except yours (manager_task):
{tasks_summary}
Provide a JSON with the structure:
{{
"task_id": <int>,
"agent_name": "<string>",
"action": "<execute or stop>"
}}
"""
try:
logging.info("Requesting manager instructions...")
manager_response = client.beta.chat.completions.parse(
model=self.manager_llm,
messages=[
{"role": "system", "content": manager_task.description},
{"role": "user", "content": manager_prompt}
],
temperature=0.7,
response_format=ManagerInstructions
)
parsed_instructions = manager_response.choices[0].message.parsed
logging.info(f"Manager instructions: {parsed_instructions}")
except Exception as e:
display_error(f"Manager parse error: {e}")
logging.error(f"Manager parse error: {str(e)}", exc_info=True)
break
selected_task_id = parsed_instructions.task_id
selected_agent_name = parsed_instructions.agent_name
action = parsed_instructions.action
logging.info(f"Manager selected task_id={selected_task_id}, agent={selected_agent_name}, action={action}")
if action.lower() == "stop":
logging.info("Manager decided to stop task execution")
break
if selected_task_id not in self.tasks:
error_msg = f"Manager selected invalid task id {selected_task_id}"
display_error(error_msg)
logging.error(error_msg)
break
original_agent = self.tasks[selected_task_id].agent.name if self.tasks[selected_task_id].agent else "None"
for a in self.agents:
if a.name == selected_agent_name:
self.tasks[selected_task_id].agent = a
logging.info(f"Changed agent for task {selected_task_id} from {original_agent} to {selected_agent_name}")
break
if self.tasks[selected_task_id].status != "completed":
logging.info(f"Starting execution of task {selected_task_id}")
yield selected_task_id
logging.info(f"Finished execution of task {selected_task_id}, status: {self.tasks[selected_task_id].status}")
if self.tasks[selected_task_id].status == "completed":
completed_count += 1
logging.info(f"Task {selected_task_id} completed. Total completed: {completed_count}/{total_tasks}")
self.tasks[manager_task.id].status = "completed"
if self.verbose >= 1:
logging.info("All tasks completed under manager supervision.")
logging.info("Hierarchical task execution finished")
+30 -6
View File
@@ -1,8 +1,10 @@
import logging
from typing import List, Optional, Dict, Any, Type
import asyncio
from typing import List, Optional, Dict, Any, Type, Callable, Union, Coroutine
from pydantic import BaseModel
from ..main import TaskOutput
from ..agent.agent import Agent
import uuid
class Task:
def __init__(
@@ -18,16 +20,22 @@ class Task:
output_file: Optional[str] = None,
output_json: Optional[Type[BaseModel]] = None,
output_pydantic: Optional[Type[BaseModel]] = None,
callback: Optional[Any] = None,
callback: Optional[Union[Callable[[TaskOutput], Any], Callable[[TaskOutput], Coroutine[Any, Any, Any]]]] = None,
status: str = "not started",
result: Optional[TaskOutput] = None,
create_directory: Optional[bool] = False,
id: Optional[int] = None,
images: Optional[List[str]] = None
images: Optional[List[str]] = None,
next_tasks: Optional[List[str]] = None,
task_type: str = "task",
condition: Optional[Dict[str, List[str]]] = None,
is_start: bool = False,
loop_state: Optional[Dict[str, Union[str, int]]] = None
):
self.id = str(uuid.uuid4()) if id is None else str(id)
self.name = name
self.description = description
self.expected_output = expected_output
self.name = name
self.agent = agent
self.tools = tools if tools else []
self.context = context if context else []
@@ -40,11 +48,27 @@ class Task:
self.status = status
self.result = result
self.create_directory = create_directory
self.id = id
self.images = images if images else []
self.next_tasks = next_tasks if next_tasks else []
self.task_type = task_type
self.condition = condition if condition else {}
self.is_start = is_start
self.loop_state = loop_state if loop_state else {}
if self.output_json and self.output_pydantic:
raise ValueError("Only one output type can be defined")
# Track previous tasks based on next_tasks relationships
self.previous_tasks = []
def __str__(self):
return f"Task(name='{self.name if self.name else 'None'}', description='{self.description}', agent='{self.agent.name if self.agent else 'None'}', status='{self.status}')"
return f"Task(name='{self.name if self.name else 'None'}', description='{self.description}', agent='{self.agent.name if self.agent else 'None'}', status='{self.status}')"
async def execute_callback(self, task_output: TaskOutput) -> None:
"""Execute the callback function, handling both sync and async callbacks"""
if self.callback:
if asyncio.iscoroutinefunction(self.callback):
await self.callback(task_output)
else:
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, self.callback, task_output)
+149
View File
@@ -0,0 +1,149 @@
# PraisonAI Tools Guide
Welcome to the PraisonAI Tools directory! This guide will help you understand how our tools work and how to create new ones, whether you're a beginner or an experienced programmer.
## What is a Tool?
A tool is a piece of code that helps our AI agents perform specific tasks. Think of tools as special abilities that we give to our agents. For example:
- An internet search tool lets agents search the web
- A stock market tool lets agents check stock prices
- A weather tool lets agents check the weather
## Creating New Tools: The Two Approaches
### 1. Function-Based Approach (Simple Tools)
Best for simple tools that do one specific thing. Like a calculator that just adds numbers.
**When to use:**
- Tool does one simple task
- Doesn't need to remember information between uses
- Doesn't need to share information with other tools
- Quick, one-time operations
**Example:**
```python
def internet_search(query: str):
# Search the internet and return results
return search_results
```
**Usage:**
```python
from praisonaiagents.tools import internet_search
results = internet_search("AI news")
```
### 2. Class-Based Approach (Complex Tools)
Best for tools that do multiple related things or need to remember information. Like a smart calculator that remembers your previous calculations and can do many different math operations.
**When to use:**
- Tool has multiple related functions
- Needs to remember or share information
- Needs to manage resources efficiently
- Has complex setup requirements
**Example:**
```python
class StockTools:
def get_stock_price(self, symbol):
# Get current stock price
return price
def get_stock_info(self, symbol):
# Get detailed stock information
return info
```
**Usage:**
```python
from praisonaiagents.tools import get_stock_price, get_stock_info
price = get_stock_price("AAPL")
info = get_stock_info("AAPL")
```
## How to Choose Your Approach
Ask yourself these questions:
1. **Is your tool doing one simple thing?**
- Yes → Use Function-Based Approach
- No → Consider Class-Based Approach
2. **Does your tool need to remember information?**
- Yes → Use Class-Based Approach
- No → Use Function-Based Approach
3. **Are your tool's operations related to each other?**
- Yes → Use Class-Based Approach
- No → Use Function-Based Approach
4. **Does your tool need to manage resources efficiently?**
- Yes → Use Class-Based Approach
- No → Use Function-Based Approach
## Real-World Examples
### Internet Search Tool (Function-Based)
- Does one thing: searches the internet
- Doesn't need to remember previous searches
- Each search is independent
- Simple input/output operation
### Stock Market Tool (Class-Based)
- Does multiple things: check prices, get company info, get historical data
- Remembers stock information to avoid repeated downloads
- Operations are related (all about stocks)
- Manages connections efficiently
## Getting Started
1. **Choose Your Approach** based on the guidelines above
2. **Create Your Tool File**:
- Name it descriptively (e.g., `weather_tools.py`)
- Place it in the `praisonaiagents/tools` directory
3. **Write Your Tool**:
- Add clear documentation
- Include type hints for better understanding
- Handle errors gracefully
4. **Test Your Tool**:
- Make sure it works as expected
- Test error cases
- Check performance
## Best Practices
1. **Documentation**:
- Explain what your tool does
- Provide examples
- List any requirements
2. **Error Handling**:
- Always handle possible errors
- Return helpful error messages
- Don't let your tool crash
3. **Performance**:
- Keep it efficient
- Don't waste resources
- Cache when helpful
4. **User-Friendly**:
- Make it easy to use
- Use clear function/method names
- Keep it simple
## Need Help?
- Check existing tools for examples
- Ask in our community
- Read the documentation
- Don't hesitate to ask questions!
Remember: The goal is to make tools that are easy to use and maintain. Choose the approach that makes the most sense for your specific tool's needs.
+178
View File
@@ -0,0 +1,178 @@
"""Tools package for PraisonAI Agents"""
from importlib import import_module
from typing import Any
# Map of function names to their module and class (if any)
TOOL_MAPPINGS = {
# Direct functions
'internet_search': ('.duckduckgo_tools', None),
'duckduckgo': ('.duckduckgo_tools', None),
# arXiv Tools
'search_arxiv': ('.arxiv_tools', None),
'get_arxiv_paper': ('.arxiv_tools', None),
'get_papers_by_author': ('.arxiv_tools', None),
'get_papers_by_category': ('.arxiv_tools', None),
'arxiv_tools': ('.arxiv_tools', None),
# Wikipedia Tools
'wiki_search': ('.wikipedia_tools', None),
'wiki_summary': ('.wikipedia_tools', None),
'wiki_page': ('.wikipedia_tools', None),
'wiki_random': ('.wikipedia_tools', None),
'wiki_language': ('.wikipedia_tools', None),
'wikipedia_tools': ('.wikipedia_tools', None),
# Newspaper Tools
'get_article': ('.newspaper_tools', None),
'get_news_sources': ('.newspaper_tools', None),
'get_articles_from_source': ('.newspaper_tools', None),
'get_trending_topics': ('.newspaper_tools', None),
'newspaper_tools': ('.newspaper_tools', None),
# Spider Tools
'scrape_page': ('.spider_tools', None),
'extract_links': ('.spider_tools', None),
'crawl': ('.spider_tools', None),
'extract_text': ('.spider_tools', None),
'spider_tools': ('.spider_tools', None),
# DuckDB Tools
'query': ('.duckdb_tools', None),
'create_table': ('.duckdb_tools', None),
'load_data': ('.duckdb_tools', None),
'export_data': ('.duckdb_tools', None),
'get_table_info': ('.duckdb_tools', None),
'analyze_data': ('.duckdb_tools', None),
'duckdb_tools': ('.duckdb_tools', None),
# Shell Tools
'execute_command': ('.shell_tools', None),
'list_processes': ('.shell_tools', None),
'kill_process': ('.shell_tools', None),
'get_system_info': ('.shell_tools', None),
'shell_tools': ('.shell_tools', None),
# Calculator Tools
'evaluate': ('.calculator_tools', None),
'solve_equation': ('.calculator_tools', None),
'convert_units': ('.calculator_tools', None),
'calculate_statistics': ('.calculator_tools', None),
'calculate_financial': ('.calculator_tools', None),
'calculator_tools': ('.calculator_tools', None),
# Class methods from YFinance
'get_stock_price': ('.yfinance_tools', 'YFinanceTools'),
'get_stock_info': ('.yfinance_tools', 'YFinanceTools'),
'get_historical_data': ('.yfinance_tools', 'YFinanceTools'),
'yfinance': ('.yfinance_tools', 'YFinanceTools'),
# File Tools
'read_file': ('.file_tools', 'FileTools'),
'write_file': ('.file_tools', 'FileTools'),
'list_files': ('.file_tools', 'FileTools'),
'get_file_info': ('.file_tools', 'FileTools'),
'copy_file': ('.file_tools', 'FileTools'),
'move_file': ('.file_tools', 'FileTools'),
'delete_file': ('.file_tools', 'FileTools'),
'file_tools': ('.file_tools', 'FileTools'),
# CSV Tools
'read_csv': ('.csv_tools', 'CSVTools'),
'write_csv': ('.csv_tools', 'CSVTools'),
'merge_csv': ('.csv_tools', 'CSVTools'),
'analyze_csv': ('.csv_tools', 'CSVTools'),
'split_csv': ('.csv_tools', 'CSVTools'),
'csv_tools': ('.csv_tools', 'CSVTools'),
# JSON Tools
'read_json': ('.json_tools', 'JSONTools'),
'write_json': ('.json_tools', 'JSONTools'),
'merge_json': ('.json_tools', 'JSONTools'),
'validate_json': ('.json_tools', 'JSONTools'),
'analyze_json': ('.json_tools', 'JSONTools'),
'transform_json': ('.json_tools', 'JSONTools'),
'json_tools': ('.json_tools', 'JSONTools'),
# Excel Tools
'read_excel': ('.excel_tools', 'ExcelTools'),
'write_excel': ('.excel_tools', 'ExcelTools'),
'merge_excel': ('.excel_tools', 'ExcelTools'),
'create_chart': ('.excel_tools', 'ExcelTools'),
'add_chart_to_sheet': ('.excel_tools', 'ExcelTools'),
'excel_tools': ('.excel_tools', 'ExcelTools'),
# XML Tools
'read_xml': ('.xml_tools', 'XMLTools'),
'write_xml': ('.xml_tools', 'XMLTools'),
'transform_xml': ('.xml_tools', 'XMLTools'),
'validate_xml': ('.xml_tools', 'XMLTools'),
'xml_to_dict': ('.xml_tools', 'XMLTools'),
'dict_to_xml': ('.xml_tools', 'XMLTools'),
'xpath_query': ('.xml_tools', 'XMLTools'),
'xml_tools': ('.xml_tools', 'XMLTools'),
# YAML Tools
'read_yaml': ('.yaml_tools', 'YAMLTools'),
'write_yaml': ('.yaml_tools', 'YAMLTools'),
'merge_yaml': ('.yaml_tools', 'YAMLTools'),
'validate_yaml': ('.yaml_tools', 'YAMLTools'),
'analyze_yaml': ('.yaml_tools', 'YAMLTools'),
'transform_yaml': ('.yaml_tools', 'YAMLTools'),
'yaml_tools': ('.yaml_tools', 'YAMLTools'),
# Python Tools
'execute_code': ('.python_tools', 'PythonTools'),
'analyze_code': ('.python_tools', 'PythonTools'),
'format_code': ('.python_tools', 'PythonTools'),
'lint_code': ('.python_tools', 'PythonTools'),
'disassemble_code': ('.python_tools', 'PythonTools'),
'python_tools': ('.python_tools', 'PythonTools'),
# Pandas Tools
'filter_data': ('.pandas_tools', 'PandasTools'),
'get_summary': ('.pandas_tools', 'PandasTools'),
'group_by': ('.pandas_tools', 'PandasTools'),
'pivot_table': ('.pandas_tools', 'PandasTools'),
'pandas_tools': ('.pandas_tools', 'PandasTools'),
}
_instances = {} # Cache for class instances
def __getattr__(name: str) -> Any:
"""Smart lazy loading of tools with class method support."""
if name not in TOOL_MAPPINGS:
raise AttributeError(f"module '{__package__}' has no attribute '{name}'")
module_path, class_name = TOOL_MAPPINGS[name]
if class_name is None:
# Direct function import
module = import_module(module_path, __package__)
if name in [
'duckduckgo', 'internet_search',
'search_arxiv', 'get_arxiv_paper', 'get_papers_by_author', 'get_papers_by_category',
'wiki_search', 'wiki_summary', 'wiki_page', 'wiki_random', 'wiki_language',
'get_article', 'get_news_sources', 'get_articles_from_source', 'get_trending_topics',
'scrape_page', 'extract_links', 'crawl', 'extract_text',
'query', 'create_table', 'load_data', 'export_data', 'get_table_info', 'analyze_data',
'execute_command', 'list_processes', 'kill_process', 'get_system_info',
'evaluate', 'solve_equation', 'convert_units', 'calculate_statistics', 'calculate_financial'
]:
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']:
return module # Returns the callable module
return getattr(module, name)
else:
# Class method import
if class_name not in _instances:
module = import_module(module_path, __package__)
class_ = getattr(module, class_name)
_instances[class_name] = class_()
# Get the method and bind it to the instance
method = getattr(_instances[class_name], name)
return method
__all__ = list(TOOL_MAPPINGS.keys())
+292
View File
@@ -0,0 +1,292 @@
"""Tools for searching and retrieving papers from arXiv.
Usage:
from praisonaiagents.tools import arxiv_tools
papers = arxiv_tools.search("quantum computing")
paper = arxiv_tools.get_paper("2401.00123")
or
from praisonaiagents.tools import search_arxiv, get_arxiv_paper
papers = search_arxiv("quantum computing")
"""
import logging
from typing import List, Dict, Union, Optional, Any
from importlib import util
import json
# Map sort criteria to arxiv.SortCriterion
SORT_CRITERIA = {
"relevance": "Relevance",
"lastupdateddate": "LastUpdatedDate",
"submitteddate": "SubmittedDate"
}
# Map sort order to arxiv.SortOrder
SORT_ORDER = {
"ascending": "Ascending",
"descending": "Descending"
}
class ArxivTools:
"""Tools for searching and retrieving papers from arXiv."""
def __init__(self):
"""Initialize ArxivTools and check for arxiv package."""
self._check_arxiv()
def _check_arxiv(self):
"""Check if arxiv package is installed."""
if util.find_spec("arxiv") is None:
raise ImportError("arxiv package is not available. Please install it using: pip install arxiv")
global arxiv
import arxiv
def search(
self,
query: str,
max_results: int = 10,
sort_by: str = "relevance",
sort_order: str = "descending",
include_fields: Optional[List[str]] = None
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
"""
Search arXiv for papers matching the query.
Args:
query: Search query (e.g., "quantum computing", "author:Einstein")
max_results: Maximum number of results to return
sort_by: Sort results by ("relevance", "lastUpdatedDate", "submittedDate")
sort_order: Sort order ("ascending" or "descending")
include_fields: List of fields to include in results. If None, includes all.
Available fields: ["title", "authors", "summary", "comment", "journal_ref",
"doi", "primary_category", "categories", "links"]
Returns:
List[Dict] or Dict: List of papers or error dict
"""
try:
import arxiv
# Configure search client
client = arxiv.Client()
# Map sort criteria
sort_by_enum = getattr(arxiv.SortCriterion, SORT_CRITERIA[sort_by.lower()])
sort_order_enum = getattr(arxiv.SortOrder, SORT_ORDER[sort_order.lower()])
# Build search query
search = arxiv.Search(
query=query,
max_results=max_results,
sort_by=sort_by_enum,
sort_order=sort_order_enum
)
# Execute search
results = []
for result in client.results(search):
# Convert to dict with selected fields
paper = self._result_to_dict(result, include_fields)
results.append(paper)
return results
except Exception as e:
error_msg = f"Error searching arXiv: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def get_paper(
self,
paper_id: str,
include_fields: Optional[List[str]] = None
) -> Union[Dict[str, Any], Dict[str, str]]:
"""
Get details of a specific paper by its arXiv ID.
Args:
paper_id: arXiv paper ID (e.g., "2401.00123")
include_fields: List of fields to include in results. If None, includes all.
Available fields: ["title", "authors", "summary", "comment", "journal_ref",
"doi", "primary_category", "categories", "links"]
Returns:
Dict: Paper details or error dict
"""
try:
import arxiv
# Configure client
client = arxiv.Client()
# Get paper by ID
search = arxiv.Search(id_list=[paper_id])
results = list(client.results(search))
if not results:
return {"error": f"Paper with ID {paper_id} not found"}
# Convert to dict with selected fields
paper = self._result_to_dict(results[0], include_fields)
return paper
except Exception as e:
error_msg = f"Error getting paper {paper_id}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def get_papers_by_author(
self,
author: str,
max_results: int = 10,
sort_by: str = "submittedDate",
sort_order: str = "descending",
include_fields: Optional[List[str]] = None
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
"""
Get papers by a specific author.
Args:
author: Author name (e.g., "Einstein")
max_results: Maximum number of results to return
sort_by: Sort results by ("relevance", "lastUpdatedDate", "submittedDate")
sort_order: Sort order ("ascending" or "descending")
include_fields: List of fields to include in results
Returns:
List[Dict] or Dict: List of papers or error dict
"""
query = f'au:"{author}"'
return self.search(query, max_results, sort_by, sort_order, include_fields)
def get_papers_by_category(
self,
category: str,
max_results: int = 10,
sort_by: str = "submittedDate",
sort_order: str = "descending",
include_fields: Optional[List[str]] = None
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
"""
Get papers from a specific category.
Args:
category: arXiv category (e.g., "cs.AI", "physics.gen-ph")
max_results: Maximum number of results to return
sort_by: Sort results by ("relevance", "lastUpdatedDate", "submittedDate")
sort_order: Sort order ("ascending" or "descending")
include_fields: List of fields to include in results
Returns:
List[Dict] or Dict: List of papers or error dict
"""
query = f'cat:{category}'
return self.search(query, max_results, sort_by, sort_order, include_fields)
def _result_to_dict(
self,
result: Any,
include_fields: Optional[List[str]] = None
) -> Dict[str, Any]:
"""Convert arxiv.Result to dictionary with selected fields."""
# Default fields to include
if include_fields is None:
include_fields = [
"title", "authors", "summary", "comment", "journal_ref",
"doi", "primary_category", "categories", "links"
]
# Build paper dict with selected fields
paper = {}
# Always include these basic fields
paper["arxiv_id"] = result.entry_id.split("/")[-1]
paper["updated"] = result.updated.isoformat() if result.updated else None
paper["published"] = result.published.isoformat() if result.published else None
# Add requested fields
if "title" in include_fields:
paper["title"] = result.title
if "authors" in include_fields:
paper["authors"] = [str(author) for author in result.authors]
if "summary" in include_fields:
paper["summary"] = result.summary
if "comment" in include_fields:
paper["comment"] = result.comment
if "journal_ref" in include_fields:
paper["journal_ref"] = result.journal_ref
if "doi" in include_fields:
paper["doi"] = result.doi
if "primary_category" in include_fields:
paper["primary_category"] = result.primary_category
if "categories" in include_fields:
paper["categories"] = result.categories
if "links" in include_fields:
paper["pdf_url"] = result.pdf_url
paper["abstract_url"] = f"https://arxiv.org/abs/{paper['arxiv_id']}"
return paper
# Create instance for direct function access
_arxiv_tools = ArxivTools()
search_arxiv = _arxiv_tools.search
get_arxiv_paper = _arxiv_tools.get_paper
get_papers_by_author = _arxiv_tools.get_papers_by_author
get_papers_by_category = _arxiv_tools.get_papers_by_category
if __name__ == "__main__":
# Example usage
print("\n==================================================")
print("ArxivTools Demonstration")
print("==================================================\n")
# 1. Search for papers
print("1. Searching for Papers")
print("------------------------------")
query = "quantum computing"
papers = search_arxiv(query, max_results=3)
print(f"Papers about {query}:")
if isinstance(papers, list):
print(json.dumps(papers, indent=2))
else:
print(papers) # Show error
print()
# 2. Get specific paper
print("2. Getting Specific Paper")
print("------------------------------")
if isinstance(papers, list) and papers:
paper_id = papers[0]["arxiv_id"]
paper = get_arxiv_paper(paper_id)
print(f"Paper {paper_id}:")
if "error" not in paper:
print(json.dumps(paper, indent=2))
else:
print(paper) # Show error
print()
# 3. Get papers by author
print("3. Getting Papers by Author")
print("------------------------------")
author = "Yoshua Bengio"
author_papers = get_papers_by_author(author, max_results=3)
print(f"Papers by {author}:")
if isinstance(author_papers, list):
print(json.dumps(author_papers, indent=2))
else:
print(author_papers) # Show error
print()
# 4. Get papers by category
print("4. Getting Papers by Category")
print("------------------------------")
category = "cs.AI"
category_papers = get_papers_by_category(category, max_results=3)
print(f"Papers in category {category}:")
if isinstance(category_papers, list):
print(json.dumps(category_papers, indent=2))
else:
print(category_papers) # Show error
print("\n==================================================")
print("Demonstration Complete")
print("==================================================")
@@ -0,0 +1,278 @@
"""Tools for performing calculations.
Usage:
from praisonaiagents.tools import calculator_tools
result = calculator_tools.evaluate("2 + 2 * 3")
or
from praisonaiagents.tools import evaluate, solve_equation, convert_units
result = evaluate("2 + 2 * 3")
"""
import logging
from typing import List, Dict, Optional, Any
from importlib import util
import math
class CalculatorTools:
"""Tools for performing calculations."""
def __init__(self):
"""Initialize CalculatorTools."""
self._check_dependencies()
def _check_dependencies(self):
"""Check if required packages are installed."""
missing = []
for package in ['sympy', 'pint']:
if util.find_spec(package) is None:
missing.append(package)
if missing:
raise ImportError(
f"Required packages not available. Please install: {', '.join(missing)}\n"
f"Run: pip install {' '.join(missing)}"
)
def evaluate(
self,
expression: str,
variables: Optional[Dict[str, float]] = None,
precision: int = 10
) -> Dict[str, Any]:
"""Evaluate a mathematical expression."""
try:
if util.find_spec('sympy') is None:
error_msg = "sympy package is not available. Please install it using: pip install sympy"
logging.error(error_msg)
return {"error": error_msg}
# Import sympy only when needed
import sympy
# Replace common mathematical functions
expression = expression.lower()
replacements = {
'sin(': 'math.sin(',
'cos(': 'math.cos(',
'tan(': 'math.tan(',
'sqrt(': 'math.sqrt(',
'log(': 'math.log(',
'exp(': 'math.exp(',
'pi': str(math.pi),
'e': str(math.e)
}
for old, new in replacements.items():
expression = expression.replace(old, new)
# Create safe namespace
safe_dict = {
'math': math,
'__builtins__': None
}
if variables:
safe_dict.update(variables)
# Evaluate expression
result = eval(expression, safe_dict)
# Round to specified precision
if isinstance(result, (int, float)):
return {"result": round(float(result), precision)}
return {"error": "Invalid expression"}
except Exception as e:
error_msg = f"Error evaluating expression: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def solve_equation(
self,
equation: str,
variable: str = 'x'
) -> Dict[str, Any]:
"""Solve a mathematical equation."""
try:
if util.find_spec('sympy') is None:
error_msg = "sympy package is not available. Please install it using: pip install sympy"
logging.error(error_msg)
return {"error": error_msg}
# Import sympy only when needed
import sympy
# Parse equation
if '=' in equation:
left, right = equation.split('=')
equation = f"({left}) - ({right})"
# Convert to SymPy expression
x = sympy.Symbol(variable)
expr = sympy.sympify(equation)
# Solve equation
solutions = sympy.solve(expr, x)
# Convert complex solutions to real if possible
real_solutions = []
for sol in solutions:
if sol.is_real:
real_solutions.append(float(sol))
return {"solutions": real_solutions} if real_solutions else {"error": "No real solutions found"}
except Exception as e:
error_msg = f"Error solving equation: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def convert_units(
self,
value: float,
from_unit: str,
to_unit: str
) -> Dict[str, Any]:
"""Convert between different units."""
try:
if util.find_spec('pint') is None:
error_msg = "pint package is not available. Please install it using: pip install pint"
logging.error(error_msg)
return {"error": error_msg}
# Import pint only when needed
import pint
# Create quantity with source unit
ureg = pint.UnitRegistry()
quantity = value * ureg(from_unit)
# Convert to target unit
result = quantity.to(to_unit)
return {"result": float(result.magnitude)}
except Exception as e:
error_msg = f"Error converting units: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def calculate_statistics(
self,
numbers: List[float]
) -> Dict[str, Any]:
"""Calculate basic statistics for a list of numbers."""
try:
if not numbers:
return {"error": "No numbers provided"}
n = len(numbers)
mean = sum(numbers) / n
# Calculate variance and std dev
squared_diff_sum = sum((x - mean) ** 2 for x in numbers)
variance = squared_diff_sum / (n - 1) if n > 1 else 0
std_dev = math.sqrt(variance)
# Calculate median
sorted_nums = sorted(numbers)
if n % 2 == 0:
median = (sorted_nums[n//2 - 1] + sorted_nums[n//2]) / 2
else:
median = sorted_nums[n//2]
return {
"mean": mean,
"median": median,
"std_dev": std_dev,
"variance": variance,
"min": min(numbers),
"max": max(numbers),
"range": max(numbers) - min(numbers),
"count": n
}
except Exception as e:
error_msg = f"Error calculating statistics: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def calculate_financial(
self,
principal: float,
rate: float,
time: float,
frequency: str = 'yearly'
) -> Dict[str, Any]:
"""Calculate financial metrics like compound interest."""
try:
# Convert rate to decimal
rate = rate / 100
# Get compounding frequency
freq_map = {
'yearly': 1,
'semi-annual': 2,
'quarterly': 4,
'monthly': 12,
'daily': 365
}
n = freq_map.get(frequency.lower(), 1)
# Calculate compound interest
amount = principal * (1 + rate/n)**(n*time)
interest = amount - principal
# Calculate simple interest
simple_interest = principal * rate * time
return {
"final_amount": amount,
"compound_interest": interest,
"simple_interest": simple_interest,
"difference": interest - simple_interest
}
except Exception as e:
error_msg = f"Error calculating financial metrics: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
# Create instance for direct function access
_calculator_tools = CalculatorTools()
evaluate = _calculator_tools.evaluate
solve_equation = _calculator_tools.solve_equation
convert_units = _calculator_tools.convert_units
calculate_statistics = _calculator_tools.calculate_statistics
calculate_financial = _calculator_tools.calculate_financial
if __name__ == "__main__":
print("\n==================================================")
print("CalculatorTools Demonstration")
print("==================================================\n")
print("1. Basic Arithmetic")
print("------------------------------")
print("2 + 2 =", evaluate("2 + 2")["result"])
print("5 * 6 =", evaluate("5 * 6")["result"])
print("10 / 3 =", evaluate("10 / 3")["result"])
print()
print("2. Complex Expressions")
print("------------------------------")
print("(2 + 3) * 4 =", evaluate("(2 + 3) * 4")["result"])
print("2^3 + 4 =", evaluate("2^3 + 4")["result"])
print("sqrt(16) + 2 =", evaluate("sqrt(16) + 2")["result"])
print()
print("3. Solving Equations")
print("------------------------------")
result = solve_equation("x^2 - 5*x + 6 = 0")
print("x^2 - 5*x + 6 = 0")
print("Solutions:", result["solutions"])
print()
print("4. Unit Conversions")
print("------------------------------")
print("100 meters to kilometers:", convert_units(100, "meters", "kilometers")["result"])
print("1 hour to minutes:", convert_units(1, "hours", "minutes")["result"])
print("32 fahrenheit to celsius:", convert_units(32, "fahrenheit", "celsius")["result"])
print()
print("==================================================")
print("Demonstration Complete")
print("==================================================")
+266
View File
@@ -0,0 +1,266 @@
"""Tools for working with CSV files.
Usage:
from praisonaiagents.tools import csv_tools
df = csv_tools.read_csv("data.csv")
or
from praisonaiagents.tools import read_csv, write_csv, merge_csv
df = read_csv("data.csv")
"""
import logging
from typing import List, Dict, Union, Optional, Any, TYPE_CHECKING
from importlib import util
import json
import csv
from pathlib import Path
if TYPE_CHECKING:
import pandas as pd
class CSVTools:
"""Tools for working with CSV files."""
def __init__(self):
"""Initialize CSVTools."""
pass
def _get_pandas(self) -> Optional['pd']:
"""Get pandas module, installing if needed"""
if util.find_spec('pandas') is None:
error_msg = "pandas package is not available. Please install it using: pip install pandas"
logging.error(error_msg)
return None
import pandas as pd
return pd
def read_csv(
self,
filepath: str,
encoding: str = 'utf-8',
delimiter: str = ',',
header: Union[int, List[int], None] = 0,
usecols: Optional[List[str]] = None,
dtype: Optional[Dict[str, str]] = None,
parse_dates: Optional[List[str]] = None,
na_values: Optional[List[str]] = None,
nrows: Optional[int] = None
) -> List[Dict[str, Any]]:
"""Read a CSV file with advanced options.
Args:
filepath: Path to CSV file
encoding: File encoding
delimiter: Column delimiter
header: Row number(s) to use as column names
usecols: Columns to read
dtype: Dict of column dtypes
parse_dates: List of columns to parse as dates
na_values: Additional strings to recognize as NA/NaN
nrows: Number of rows to read
Returns:
List of row dicts
"""
try:
pd = self._get_pandas()
if pd is None:
return {"error": "pandas package not available"}
df = pd.read_csv(
filepath,
encoding=encoding,
delimiter=delimiter,
header=header,
usecols=usecols,
dtype=dtype,
parse_dates=parse_dates,
na_values=na_values,
nrows=nrows
)
return df.to_dict('records')
except Exception as e:
error_msg = f"Error reading CSV file {filepath}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def write_csv(
self,
filepath: str,
data: List[Dict[str, Any]],
encoding: str = 'utf-8',
delimiter: str = ',',
index: bool = False,
header: bool = True,
float_format: Optional[str] = None,
date_format: Optional[str] = None,
mode: str = 'w'
) -> bool:
"""Write data to a CSV file.
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)
Returns:
bool: Success status
"""
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
except Exception as e:
error_msg = f"Error writing CSV file {filepath}: {str(e)}"
logging.error(error_msg)
return False
def merge_csv(
self,
files: List[str],
output_file: str,
how: str = 'inner',
on: Optional[Union[str, List[str]]] = None,
suffixes: Optional[tuple] = None
) -> bool:
"""Merge multiple CSV files.
Args:
files: List of CSV files to merge
output_file: Output file path
how: Merge method ('inner', 'outer', 'left', 'right')
on: Column(s) to merge on
suffixes: Suffixes for overlapping columns
Returns:
bool: Success status
"""
try:
if len(files) < 2:
error_msg = "At least two files are required for merging"
logging.error(error_msg)
return False
pd = self._get_pandas()
if pd is None:
return False
# Read first file
result = pd.read_csv(files[0])
# Merge with remaining files
for file in files[1:]:
df = pd.read_csv(file)
result = pd.merge(
result,
df,
how=how,
on=on,
suffixes=suffixes or ('_1', '_2')
)
# Write merged result
result.to_csv(output_file, index=False)
return True
except Exception as e:
error_msg = f"Error merging CSV files: {str(e)}"
logging.error(error_msg)
return False
# Create instance for direct function access
_csv_tools = CSVTools()
read_csv = _csv_tools.read_csv
write_csv = _csv_tools.write_csv
merge_csv = _csv_tools.merge_csv
if __name__ == "__main__":
print("\n==================================================")
print("CSVTools Demonstration")
print("==================================================\n")
# Create a temporary file for testing
import tempfile
import os
with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp:
temp_file = temp.name
print("1. Writing CSV File")
print("------------------------------")
data = [
{"name": "Alice", "age": 25, "city": "New York"},
{"name": "Bob", "age": 30, "city": "San Francisco"},
{"name": "Charlie", "age": 35, "city": "Chicago"}
]
result = write_csv(temp_file, data)
print(f"Data written successfully: {result}")
print()
print("2. Reading CSV File")
print("------------------------------")
read_data = read_csv(temp_file)
print("Contents of CSV file:")
for row in read_data:
print(row)
print()
print("3. Merging CSV Files")
print("------------------------------")
# Create a second file for merging
with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp2:
temp_file2 = temp2.name
data2 = [
{"name": "Alice", "salary": 75000},
{"name": "Bob", "salary": 85000},
{"name": "Charlie", "salary": 95000}
]
write_csv(temp_file2, data2)
# Merge files
with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp3:
temp_file3 = temp3.name
result = merge_csv(
[temp_file, temp_file2],
temp_file3,
how='inner',
on='name'
)
print(f"Files merged successfully: {result}")
if result:
merged_data = read_csv(temp_file3)
print("\nMerged contents:")
for row in merged_data:
print(row)
print()
# Clean up temporary files
os.unlink(temp_file)
os.unlink(temp_file2)
os.unlink(temp_file3)
print("==================================================")
print("Demonstration Complete")
print("==================================================\n")
@@ -0,0 +1,268 @@
"""Tools for working with DuckDB databases.
Usage:
from praisonaiagents.tools import duckdb_tools
df = duckdb_tools.query("SELECT * FROM my_table")
or
from praisonaiagents.tools import query_db, create_table, load_data
df = query_db("SELECT * FROM my_table")
"""
import logging
from typing import List, Dict, Any, Optional, Union, TYPE_CHECKING
from importlib import util
import json
if TYPE_CHECKING:
import duckdb
import pandas as pd
class DuckDBTools:
"""Tools for working with DuckDB databases."""
def __init__(self, database: str = ':memory:'):
"""Initialize DuckDBTools.
Args:
database: Path to database file or ':memory:' for in-memory database
"""
self.database = database
self._conn = None
def _get_duckdb(self) -> Optional['duckdb']:
"""Get duckdb module, installing if needed"""
if util.find_spec('duckdb') is None:
error_msg = "duckdb package is not available. Please install it using: pip install duckdb"
logging.error(error_msg)
return None
import duckdb
return duckdb
def _get_pandas(self) -> Optional['pd']:
"""Get pandas module, installing if needed"""
if util.find_spec('pandas') is None:
error_msg = "pandas package is not available. Please install it using: pip install pandas"
logging.error(error_msg)
return None
import pandas as pd
return pd
def _get_connection(self) -> Optional['duckdb.DuckDBPyConnection']:
"""Get or create database connection"""
if self._conn is None:
duckdb = self._get_duckdb()
if duckdb is None:
return None
try:
self._conn = duckdb.connect(self.database)
except Exception as e:
error_msg = f"Error connecting to database {self.database}: {str(e)}"
logging.error(error_msg)
return None
return self._conn
def execute_query(
self,
query: str,
params: Optional[Union[tuple, dict]] = None,
return_df: bool = True
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
"""Execute a SQL query.
Args:
query: SQL query to execute
params: Query parameters
return_df: If True, return results as DataFrame records
Returns:
Query results as list of dicts, or error dict
"""
try:
conn = self._get_connection()
if conn is None:
return {"error": "Could not connect to database"}
if params:
result = conn.execute(query, params)
else:
result = conn.execute(query)
if return_df:
pd = self._get_pandas()
if pd is None:
return {"error": "pandas package not available"}
df = result.df()
return df.to_dict('records')
else:
return [dict(row) for row in result.fetchall()]
except Exception as e:
error_msg = f"Error executing query: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def load_csv(
self,
table_name: str,
filepath: str,
schema: Optional[Dict[str, str]] = None,
if_exists: str = 'replace'
) -> bool:
"""Load a CSV file into a table.
Args:
table_name: Name of table to create
filepath: Path to CSV file
schema: Optional column definitions
if_exists: What to do if table exists ('fail', 'replace', 'append')
Returns:
bool: Success status
"""
try:
conn = self._get_connection()
if conn is None:
return False
# Check if table exists
exists = conn.execute(f"""
SELECT name FROM sqlite_master
WHERE type='table' AND name='{table_name}'
""").fetchone() is not None
if exists:
if if_exists == 'fail':
raise ValueError(f"Table {table_name} already exists")
elif if_exists == 'replace':
conn.execute(f"DROP TABLE IF EXISTS {table_name}")
elif if_exists != 'append':
raise ValueError("if_exists must be 'fail', 'replace', or 'append'")
# Create table if needed
if not exists or if_exists == 'replace':
if schema:
# Create table with schema
columns = ', '.join(f"{k} {v}" for k, v in schema.items())
conn.execute(f"CREATE TABLE {table_name} ({columns})")
else:
# Infer schema from CSV
conn.execute(f"""
CREATE TABLE {table_name} AS
SELECT * FROM read_csv_auto('{filepath}')
WHERE 1=0
""")
# Load data
conn.execute(f"""
INSERT INTO {table_name}
SELECT * FROM read_csv_auto('{filepath}')
""")
return True
except Exception as e:
error_msg = f"Error loading CSV file {filepath}: {str(e)}"
logging.error(error_msg)
return False
def export_csv(
self,
query: str,
filepath: str,
params: Optional[Union[tuple, dict]] = None
) -> bool:
"""Export query results to CSV.
Args:
query: SQL query to execute
filepath: Output file path
params: Optional query parameters
Returns:
bool: Success status
"""
try:
# Execute query and get results as DataFrame
results = self.execute_query(query, params)
if isinstance(results, dict) and 'error' in results:
return False
pd = self._get_pandas()
if pd is None:
return False
# Convert to DataFrame and save
df = pd.DataFrame(results)
df.to_csv(filepath, index=False)
return True
except Exception as e:
error_msg = f"Error exporting to CSV file {filepath}: {str(e)}"
logging.error(error_msg)
return False
def close(self):
"""Close database connection."""
if self._conn:
self._conn.close()
self._conn = None
# Create instance for direct function access
_duckdb_tools = DuckDBTools()
execute_query = _duckdb_tools.execute_query
load_csv = _duckdb_tools.load_csv
export_csv = _duckdb_tools.export_csv
if __name__ == "__main__":
print("\n==================================================")
print("DuckDBTools Demonstration")
print("==================================================\n")
# Create a temporary file for testing
import tempfile
import os
with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp:
temp_file = temp.name
# Create sample data
with open(temp_file, 'w') as f:
f.write("name,age,city\n")
f.write("Alice,25,New York\n")
f.write("Bob,30,San Francisco\n")
f.write("Charlie,35,Chicago\n")
print("1. Loading CSV File")
print("------------------------------")
result = load_csv('users', temp_file)
print(f"CSV loaded successfully: {result}")
print()
print("2. Executing Query")
print("------------------------------")
query = "SELECT * FROM users WHERE age > 25"
results = execute_query(query)
print("Query results:")
for row in results:
print(row)
print()
print("3. Exporting Query Results")
print("------------------------------")
with tempfile.NamedTemporaryFile(suffix='.csv', delete=False) as temp2:
temp_file2 = temp2.name
result = export_csv(query, temp_file2)
print(f"Results exported successfully: {result}")
if result:
print("\nExported file contents:")
with open(temp_file2) as f:
print(f.read())
# Clean up temporary files
os.unlink(temp_file)
os.unlink(temp_file2)
print("==================================================")
print("Demonstration Complete")
print("==================================================\n")
@@ -0,0 +1,52 @@
"""DuckDuckGo search functionality.
Usage:
from praisonaiagents.tools import internet_search
results = internet_search("AI news")
or
from praisonaiagents.tools import duckduckgo
results = duckduckgo("AI news")
"""
from typing import List, Dict
import logging
from importlib import util
def internet_search(query: str) -> List[Dict]:
"""Perform an internet search using DuckDuckGo."""
# Check if duckduckgo_search is installed
if util.find_spec("duckduckgo_search") is None:
error_msg = "DuckDuckGo search is not available. Please install duckduckgo_search package using: pip install duckduckgo_search"
logging.error(error_msg)
return [{"error": error_msg}]
try:
# Import only when needed
from duckduckgo_search import DDGS
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=5):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return results
except Exception as e:
error_msg = f"Error during DuckDuckGo search: {e}"
logging.error(error_msg)
return [{"error": error_msg}]
def duckduckgo(query: str) -> List[Dict]:
"""Alias for internet_search function."""
return internet_search(query)
if __name__ == "__main__":
# Example usage
results = internet_search("Python programming")
for result in results:
print(f"\nTitle: {result.get('title')}")
print(f"URL: {result.get('url')}")
print(f"Snippet: {result.get('snippet')}")
+310
View File
@@ -0,0 +1,310 @@
"""Tools for working with Excel files.
Usage:
from praisonaiagents.tools import excel_tools
df = excel_tools.read_excel("data.xlsx")
or
from praisonaiagents.tools import read_excel, write_excel, merge_excel
df = read_excel("data.xlsx")
"""
import logging
from typing import List, Dict, Union, Optional, Any, TYPE_CHECKING, Tuple
from importlib import util
import json
from pathlib import Path
import tempfile
import os
if TYPE_CHECKING:
import pandas as pd
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.chart import BarChart, LineChart, PieChart, Reference
from openpyxl.chart.label import DataLabelList
class ExcelTools:
"""Tools for working with Excel files."""
def __init__(self):
"""Initialize ExcelTools."""
pass
def _get_pandas(self) -> Optional['pd']:
"""Get pandas module, installing if needed"""
if util.find_spec('pandas') is None:
error_msg = "pandas package is not available. Please install it using: pip install pandas"
logging.error(error_msg)
return None
if util.find_spec('openpyxl') is None:
error_msg = "openpyxl package is not available. Please install it using: pip install openpyxl"
logging.error(error_msg)
return None
import pandas as pd
return pd
def read_excel(
self,
filepath: str,
sheet_name: Optional[Union[str, int, List[Union[str, int]]]] = 0,
header: Optional[int] = 0,
usecols: Optional[List[str]] = None,
skiprows: Optional[Union[int, List[int]]] = None,
na_values: Optional[List[str]] = None,
dtype: Optional[Dict[str, str]] = None
) -> Union[Dict[str, List[Dict[str, Any]]], List[Dict[str, Any]]]:
"""Read an Excel file with advanced options.
Args:
filepath: Path to Excel file
sheet_name: Sheet name(s) or index(es)
header: Row number(s) to use as column names
usecols: Columns to read
skiprows: Line numbers to skip
na_values: Additional strings to recognize as NA/NaN
dtype: Dict of column dtypes
Returns:
Dict of sheet names to data if multiple sheets, else list of row dicts
"""
try:
pd = self._get_pandas()
if pd is None:
return {"error": "Required packages not available"}
# Read Excel file
df = pd.read_excel(
filepath,
sheet_name=sheet_name,
header=header,
usecols=usecols,
skiprows=skiprows,
na_values=na_values,
dtype=dtype,
engine='openpyxl'
)
# Convert to dict format
if isinstance(df, dict):
return {
name: df[name].to_dict('records')
for name in df.keys()
}
else:
return df.to_dict('records')
except Exception as e:
error_msg = f"Error reading Excel file {filepath}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def write_excel(
self,
filepath: str,
data: Union[Dict[str, List[Dict[str, Any]]], List[Dict[str, Any]]],
sheet_name: Optional[str] = None,
index: bool = False,
header: bool = True,
mode: str = 'w'
) -> bool:
"""Write data to an Excel file.
Args:
filepath: Path to Excel file
data: Data to write (dict of sheet names to data or list of row dicts)
sheet_name: Sheet name if data is a list
index: Whether to write row indices
header: Whether to write column headers
mode: Write mode ('w' for write, 'a' for append)
Returns:
bool: Success status
"""
try:
pd = self._get_pandas()
if pd is None:
return False
# Convert data to DataFrame(s)
if isinstance(data, dict):
if mode == 'a' and os.path.exists(filepath):
book = pd.ExcelFile(filepath)
with pd.ExcelWriter(filepath, engine='openpyxl') as writer:
# Copy existing sheets
for sheet in book.sheet_names:
pd.read_excel(filepath, sheet_name=sheet).to_excel(
writer, sheet_name=sheet, index=index, header=header
)
# Add new sheets
for name, sheet_data in data.items():
df = pd.DataFrame(sheet_data)
df.to_excel(
writer, sheet_name=name, index=index, header=header
)
else:
with pd.ExcelWriter(filepath, engine='openpyxl') as writer:
for name, sheet_data in data.items():
df = pd.DataFrame(sheet_data)
df.to_excel(
writer, sheet_name=name, index=index, header=header
)
else:
if mode == 'a' and os.path.exists(filepath):
book = pd.ExcelFile(filepath)
with pd.ExcelWriter(filepath, engine='openpyxl') as writer:
# Copy existing sheets
for sheet in book.sheet_names:
pd.read_excel(filepath, sheet_name=sheet).to_excel(
writer, sheet_name=sheet, index=index, header=header
)
# Add new sheet
df = pd.DataFrame(data)
df.to_excel(
writer,
sheet_name=sheet_name or 'Sheet1',
index=index,
header=header
)
else:
df = pd.DataFrame(data)
df.to_excel(
filepath,
sheet_name=sheet_name or 'Sheet1',
index=index,
header=header,
engine='openpyxl'
)
return True
except Exception as e:
error_msg = f"Error writing Excel file {filepath}: {str(e)}"
logging.error(error_msg)
return False
def merge_excel(
self,
files: List[str],
output_file: str,
how: str = 'inner',
on: Optional[Union[str, List[str]]] = None,
suffixes: Optional[Tuple[str, str]] = None
) -> bool:
"""Merge multiple Excel files.
Args:
files: List of Excel files to merge
output_file: Output file path
how: Merge method ('inner', 'outer', 'left', 'right')
on: Column(s) to merge on
suffixes: Suffixes for overlapping columns
Returns:
bool: Success status
"""
try:
if len(files) < 2:
error_msg = "At least two files are required for merging"
logging.error(error_msg)
return False
# Read first file
result = self.read_excel(files[0])
# Merge with remaining files
for file in files[1:]:
df = self.read_excel(file)
if isinstance(df, dict):
df = next(iter(df.values()))
pd = self._get_pandas()
if pd is None:
return False
result = pd.merge(
pd.DataFrame(result),
pd.DataFrame(df),
how=how,
on=on,
suffixes=suffixes or ('_1', '_2')
).to_dict('records')
# Write merged result
return self.write_excel(output_file, result)
except Exception as e:
error_msg = f"Error merging Excel files: {str(e)}"
logging.error(error_msg)
return False
# Create instance for direct function access
_excel_tools = ExcelTools()
read_excel = _excel_tools.read_excel
write_excel = _excel_tools.write_excel
merge_excel = _excel_tools.merge_excel
if __name__ == "__main__":
print("\n==================================================")
print("ExcelTools Demonstration")
print("==================================================\n")
# Create a temporary file for testing
with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as temp:
temp_file = temp.name
print("1. Writing Excel File")
print("------------------------------")
data = [
{"name": "Alice", "age": 25, "city": "New York"},
{"name": "Bob", "age": 30, "city": "San Francisco"},
{"name": "Charlie", "age": 35, "city": "Chicago"}
]
result = write_excel(temp_file, data, "People")
print(f"Data written successfully: {result}")
print()
print("2. Reading Excel File")
print("------------------------------")
read_data = read_excel(temp_file)
print("Contents of Excel file:")
for row in read_data:
print(row)
print()
print("3. Merging Excel Files")
print("------------------------------")
# Create a second file for merging
with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as temp2:
temp_file2 = temp2.name
data2 = [
{"name": "Alice", "salary": 75000},
{"name": "Bob", "salary": 85000},
{"name": "Charlie", "salary": 95000}
]
write_excel(temp_file2, data2, "Salaries")
# Merge files
with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as temp3:
temp_file3 = temp3.name
result = merge_excel(
[temp_file, temp_file2],
temp_file3,
how='inner',
on='name'
)
print(f"Files merged successfully: {result}")
if result:
merged_data = read_excel(temp_file3)
print("\nMerged contents:")
for row in merged_data:
print(row)
print()
# Clean up temporary files
os.unlink(temp_file)
os.unlink(temp_file2)
os.unlink(temp_file3)
print("==================================================")
print("Demonstration Complete")
print("==================================================\n")
+274
View File
@@ -0,0 +1,274 @@
"""File handling tools for basic file operations.
Usage:
from praisonaiagents.tools import file_tools
content = file_tools.read_file("example.txt")
file_tools.write_file("output.txt", "Hello World")
or
from praisonaiagents.tools import read_file, write_file, list_files
content = read_file("example.txt")
"""
import os
import json
from typing import List, Dict, Union, Optional
from pathlib import Path
import shutil
import logging
class FileTools:
"""Tools for file operations including read, write, list, and information."""
@staticmethod
def read_file(filepath: str, encoding: str = 'utf-8') -> str:
"""
Read content from a file.
Args:
filepath: Path to the file
encoding: File encoding (default: utf-8)
Returns:
str: Content of the file
"""
try:
with open(filepath, 'r', encoding=encoding) as f:
return f.read()
except Exception as e:
error_msg = f"Error reading file {filepath}: {str(e)}"
logging.error(error_msg)
return error_msg
@staticmethod
def write_file(filepath: str, content: str, encoding: str = 'utf-8') -> bool:
"""
Write content to a file.
Args:
filepath: Path to the file
content: Content to write
encoding: File encoding (default: utf-8)
Returns:
bool: True if successful, False otherwise
"""
try:
# Create directory if it doesn't exist
os.makedirs(os.path.dirname(filepath), exist_ok=True)
with open(filepath, 'w', encoding=encoding) as f:
f.write(content)
return True
except Exception as e:
error_msg = f"Error writing to file {filepath}: {str(e)}"
logging.error(error_msg)
return False
@staticmethod
def list_files(directory: str, pattern: Optional[str] = None) -> List[Dict[str, Union[str, int]]]:
"""
List files in a directory with optional pattern matching.
Args:
directory: Directory path
pattern: Optional glob pattern (e.g., "*.txt")
Returns:
List[Dict]: List of file information dictionaries
"""
try:
path = Path(directory)
if pattern:
files = path.glob(pattern)
else:
files = path.iterdir()
result = []
for file in files:
if file.is_file():
stat = file.stat()
result.append({
'name': file.name,
'path': str(file),
'size': stat.st_size,
'modified': stat.st_mtime,
'created': stat.st_ctime
})
return result
except Exception as e:
error_msg = f"Error listing files in {directory}: {str(e)}"
logging.error(error_msg)
return [{'error': error_msg}]
@staticmethod
def get_file_info(filepath: str) -> Dict[str, Union[str, int]]:
"""
Get detailed information about a file.
Args:
filepath: Path to the file
Returns:
Dict: File information including size, dates, etc.
"""
try:
path = Path(filepath)
if not path.exists():
return {'error': f'File not found: {filepath}'}
stat = path.stat()
return {
'name': path.name,
'path': str(path),
'size': stat.st_size,
'modified': stat.st_mtime,
'created': stat.st_ctime,
'is_file': path.is_file(),
'is_dir': path.is_dir(),
'extension': path.suffix,
'parent': str(path.parent)
}
except Exception as e:
error_msg = f"Error getting file info for {filepath}: {str(e)}"
logging.error(error_msg)
return {'error': error_msg}
@staticmethod
def copy_file(src: str, dst: str) -> bool:
"""
Copy a file from source to destination.
Args:
src: Source file path
dst: Destination file path
Returns:
bool: True if successful, False otherwise
"""
try:
# Create destination directory if it doesn't exist
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.copy2(src, dst)
return True
except Exception as e:
error_msg = f"Error copying file from {src} to {dst}: {str(e)}"
logging.error(error_msg)
return False
@staticmethod
def move_file(src: str, dst: str) -> bool:
"""
Move a file from source to destination.
Args:
src: Source file path
dst: Destination file path
Returns:
bool: True if successful, False otherwise
"""
try:
# Create destination directory if it doesn't exist
os.makedirs(os.path.dirname(dst), exist_ok=True)
shutil.move(src, dst)
return True
except Exception as e:
error_msg = f"Error moving file from {src} to {dst}: {str(e)}"
logging.error(error_msg)
return False
@staticmethod
def delete_file(filepath: str) -> bool:
"""
Delete a file.
Args:
filepath: Path to the file
Returns:
bool: True if successful, False otherwise
"""
try:
os.remove(filepath)
return True
except Exception as e:
error_msg = f"Error deleting file {filepath}: {str(e)}"
logging.error(error_msg)
return False
# Create instance for direct function access
_file_tools = FileTools()
read_file = _file_tools.read_file
write_file = _file_tools.write_file
list_files = _file_tools.list_files
get_file_info = _file_tools.get_file_info
copy_file = _file_tools.copy_file
move_file = _file_tools.move_file
delete_file = _file_tools.delete_file
if __name__ == "__main__":
# Example usage
print("\n==================================================")
print("FileTools Demonstration")
print("==================================================\n")
# Create a test directory
test_dir = os.path.join(os.getcwd(), "test_files")
os.makedirs(test_dir, exist_ok=True)
# Create test files
test_file = os.path.join(test_dir, "test_file.txt")
test_content = "Hello, this is a test file!"
print("1. Writing to file")
print("------------------------------")
success = write_file(test_file, test_content)
print(f"Write successful: {success}\n")
print("2. Reading from file")
print("------------------------------")
content = read_file(test_file)
print(f"Content: {content}\n")
print("3. File Information")
print("------------------------------")
info = get_file_info(test_file)
print(json.dumps(info, indent=2))
print()
print("4. Listing Files")
print("------------------------------")
files = list_files(test_dir, "*.txt")
for file in files:
print(f"Found: {file['name']} ({file['size']} bytes)")
print()
print("5. Copying File")
print("------------------------------")
copy_file_path = os.path.join(test_dir, "test_file_copy.txt")
copy_success = copy_file(test_file, copy_file_path)
print(f"Copy successful: {copy_success}\n")
print("6. Moving File")
print("------------------------------")
move_file_path = os.path.join(test_dir, "test_file_moved.txt")
move_success = move_file(copy_file_path, move_file_path)
print(f"Move successful: {move_success}\n")
print("7. Deleting Files")
print("------------------------------")
delete_success = delete_file(test_file)
print(f"Delete original successful: {delete_success}")
delete_success = delete_file(move_file_path)
print(f"Delete moved file successful: {delete_success}\n")
# Clean up test directory
try:
shutil.rmtree(test_dir)
print("Test directory cleaned up successfully")
except Exception as e:
print(f"Error cleaning up test directory: {str(e)}")
print("\n==================================================")
print("Demonstration Complete")
print("==================================================")
+515
View File
@@ -0,0 +1,515 @@
"""Tools for working with JSON files.
Usage:
from praisonaiagents.tools import json_tools
data = json_tools.read_json("data.json")
or
from praisonaiagents.tools import read_json, write_json, merge_json
data = read_json("data.json")
"""
import logging
from typing import List, Dict, Union, Optional, Any, Tuple
from importlib import util
import json
from datetime import datetime
class JSONTools:
"""Tools for working with JSON files."""
def __init__(self):
"""Initialize JSONTools."""
pass
def read_json(
self,
filepath: str,
encoding: str = 'utf-8',
validate_schema: Optional[Dict[str, Any]] = None
) -> Dict[str, Any]:
"""Read a JSON file with optional schema validation.
Args:
filepath: Path to JSON file
encoding: File encoding
validate_schema: Optional JSON schema for validation
Returns:
Dict with JSON data
"""
try:
# Read JSON file
with open(filepath, 'r', encoding=encoding) as f:
data = json.load(f)
# Validate against schema if provided
if validate_schema:
if util.find_spec('jsonschema') is None:
error_msg = "jsonschema package is not available. Please install it using: pip install jsonschema"
logging.error(error_msg)
return {"error": error_msg}
import jsonschema
try:
jsonschema.validate(instance=data, schema=validate_schema)
except jsonschema.exceptions.ValidationError as e:
error_msg = f"JSON validation failed: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
return data
except Exception as e:
error_msg = f"Error reading JSON file {filepath}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def write_json(
self,
data: Union[Dict[str, Any], List[Any]],
filepath: str,
encoding: str = 'utf-8',
indent: int = 2,
sort_keys: bool = False,
ensure_ascii: bool = False
) -> bool:
"""Write data to a JSON file.
Args:
data: Data to write
filepath: Output file path
encoding: File encoding
indent: Number of spaces for indentation
sort_keys: Sort dictionary keys
ensure_ascii: Escape non-ASCII characters
Returns:
True if successful, False otherwise
"""
try:
with open(filepath, 'w', encoding=encoding) as f:
json.dump(
data,
f,
indent=indent,
sort_keys=sort_keys,
ensure_ascii=ensure_ascii
)
return True
except Exception as e:
error_msg = f"Error writing JSON file {filepath}: {str(e)}"
logging.error(error_msg)
return False
def merge_json(
self,
files: List[str],
output_file: str,
merge_arrays: bool = True,
overwrite_duplicates: bool = True
) -> bool:
"""Merge multiple JSON files.
Args:
files: List of JSON files to merge
output_file: Output file path
merge_arrays: Merge arrays instead of overwriting
overwrite_duplicates: Overwrite duplicate keys
Returns:
True if successful, False otherwise
"""
try:
if len(files) < 2:
raise ValueError("At least two files are required for merging")
# Read first file
result = self.read_json(files[0])
# Merge with remaining files
for file in files[1:]:
data = self.read_json(file)
result = self._deep_merge(
result,
data,
merge_arrays=merge_arrays,
overwrite_duplicates=overwrite_duplicates
)
# Write merged result
return self.write_json(result, output_file)
except Exception as e:
error_msg = f"Error merging JSON files: {str(e)}"
logging.error(error_msg)
return False
def _deep_merge(
self,
dict1: Dict[str, Any],
dict2: Dict[str, Any],
merge_arrays: bool = True,
overwrite_duplicates: bool = True
) -> Dict[str, Any]:
"""Deep merge two dictionaries.
Args:
dict1: First dictionary
dict2: Second dictionary
merge_arrays: Merge arrays instead of overwriting
overwrite_duplicates: Overwrite duplicate keys
Returns:
Merged dictionary
"""
result = dict1.copy()
for key, value in dict2.items():
if key in result:
if isinstance(result[key], dict) and isinstance(value, dict):
result[key] = self._deep_merge(
result[key],
value,
merge_arrays=merge_arrays,
overwrite_duplicates=overwrite_duplicates
)
elif isinstance(result[key], list) and isinstance(value, list):
if merge_arrays:
result[key].extend(value)
elif overwrite_duplicates:
result[key] = value
elif overwrite_duplicates:
result[key] = value
else:
result[key] = value
return result
def validate_json(
self,
data: Union[Dict[str, Any], str],
schema: Dict[str, Any]
) -> Tuple[bool, Optional[str]]:
"""Validate JSON data against a schema.
Args:
data: JSON data or filepath
schema: JSON schema for validation
Returns:
Tuple of (is_valid, error_message)
"""
try:
if util.find_spec('jsonschema') is None:
error_msg = "jsonschema package is not available. Please install it using: pip install jsonschema"
logging.error(error_msg)
return False, error_msg
import jsonschema
# Load data if filepath provided
if isinstance(data, str):
with open(data, 'r') as f:
data = json.load(f)
jsonschema.validate(instance=data, schema=schema)
return True, None
except jsonschema.exceptions.ValidationError as e:
error_msg = f"JSON validation failed: {str(e)}"
logging.error(error_msg)
return False, error_msg
except Exception as e:
error_msg = f"Error validating JSON: {str(e)}"
logging.error(error_msg)
return False, error_msg
def analyze_json(
self,
data: Union[Dict[str, Any], str],
max_depth: int = 10
) -> Dict[str, Any]:
"""Analyze JSON data structure.
Args:
data: JSON data or filepath
max_depth: Maximum depth to analyze
Returns:
Dict with analysis results
"""
try:
# Load data if filepath provided
if isinstance(data, str):
data = self.read_json(data)
def analyze_value(value: Any, depth: int = 0) -> Dict[str, Any]:
if depth >= max_depth:
return {'type': 'max_depth_reached'}
result = {'type': type(value).__name__}
if isinstance(value, dict):
result['size'] = len(value)
result['keys'] = list(value.keys())
if depth < max_depth - 1:
result['children'] = {
k: analyze_value(v, depth + 1)
for k, v in value.items()
}
elif isinstance(value, list):
result['length'] = len(value)
if value:
result['element_types'] = list(set(
type(x).__name__ for x in value
))
if depth < max_depth - 1:
result['sample_elements'] = [
analyze_value(x, depth + 1)
for x in value[:5]
]
elif isinstance(value, (int, float)):
result.update({
'value': value,
'is_integer': isinstance(value, int)
})
elif isinstance(value, str):
result.update({
'length': len(value),
'sample': value[:100] if len(value) > 100 else value
})
return result
return {
'analysis_time': datetime.now().isoformat(),
'structure': analyze_value(data)
}
except Exception as e:
error_msg = f"Error analyzing JSON: {str(e)}"
logging.error(error_msg)
return {}
def transform_json(
self,
data: Union[Dict[str, Any], str],
transformations: List[Dict[str, Any]]
) -> Dict[str, Any]:
"""Transform JSON data using a list of operations.
Args:
data: JSON data or filepath
transformations: List of transformation operations
Returns:
Transformed JSON data
"""
try:
# Load data if filepath provided
if isinstance(data, str):
data = self.read_json(data)
result = data.copy()
for transform in transformations:
op = transform.get('operation')
path = transform.get('path', '').split('.')
value = transform.get('value')
if op == 'set':
self._set_value(result, path, value)
elif op == 'delete':
self._delete_value(result, path)
elif op == 'rename':
old_path = path
new_path = value.split('.')
self._rename_key(result, old_path, new_path)
elif op == 'move':
old_path = path
new_path = value.split('.')
self._move_value(result, old_path, new_path)
return result
except Exception as e:
error_msg = f"Error transforming JSON: {str(e)}"
logging.error(error_msg)
return data
def _set_value(self, data: Dict[str, Any], path: List[str], value: Any):
"""Set a value at the specified path."""
current = data
for key in path[:-1]:
if key not in current:
current[key] = {}
current = current[key]
current[path[-1]] = value
def _delete_value(self, data: Dict[str, Any], path: List[str]):
"""Delete a value at the specified path."""
current = data
for key in path[:-1]:
if key not in current:
return
current = current[key]
if path[-1] in current:
del current[path[-1]]
def _rename_key(
self,
data: Dict[str, Any],
old_path: List[str],
new_path: List[str]
):
"""Rename a key at the specified path."""
value = self._get_value(data, old_path)
if value is not None:
self._delete_value(data, old_path)
self._set_value(data, new_path, value)
def _move_value(
self,
data: Dict[str, Any],
old_path: List[str],
new_path: List[str]
):
"""Move a value from one path to another."""
self._rename_key(data, old_path, new_path)
def _get_value(
self,
data: Dict[str, Any],
path: List[str]
) -> Optional[Any]:
"""Get a value at the specified path."""
current = data
for key in path:
if key not in current:
return None
current = current[key]
return current
# Create instance for direct function access
_json_tools = JSONTools()
read_json = _json_tools.read_json
write_json = _json_tools.write_json
merge_json = _json_tools.merge_json
validate_json = _json_tools.validate_json
analyze_json = _json_tools.analyze_json
transform_json = _json_tools.transform_json
if __name__ == "__main__":
# Example usage
print("\n==================================================")
print("JSONTools Demonstration")
print("==================================================\n")
# Sample data
data1 = {
'id': 1,
'name': 'Alice',
'scores': [95, 87, 92],
'details': {
'age': 25,
'city': 'New York'
}
}
data2 = {
'id': 2,
'name': 'Bob',
'scores': [88, 90, 85],
'details': {
'age': 30,
'country': 'USA'
}
}
# 1. Write JSON files
print("1. Writing JSON Files")
print("------------------------------")
success = write_json(data1, 'test1.json')
print(f"First file written: {success}")
success = write_json(data2, 'test2.json')
print(f"Second file written: {success}")
print()
# 2. Read JSON file
print("2. Reading JSON File")
print("------------------------------")
data = read_json('test1.json')
print("Contents of test1.json:")
print(json.dumps(data, indent=2))
print()
# 3. Merge JSON files
print("3. Merging JSON Files")
print("------------------------------")
success = merge_json(['test1.json', 'test2.json'], 'merged.json')
print(f"Files merged: {success}")
if success:
print("Merged contents:")
print(json.dumps(read_json('merged.json'), indent=2))
print()
# 4. Validate JSON
print("4. Validating JSON")
print("------------------------------")
schema = {
'type': 'object',
'properties': {
'id': {'type': 'integer'},
'name': {'type': 'string'},
'scores': {
'type': 'array',
'items': {'type': 'number'}
},
'details': {
'type': 'object',
'properties': {
'age': {'type': 'integer'},
'city': {'type': 'string'}
}
}
},
'required': ['id', 'name']
}
is_valid, error = validate_json(data1, schema)
print(f"Validation result: {is_valid}")
if error:
print(f"Validation error: {error}")
print()
# 5. Analyze JSON
print("5. Analyzing JSON")
print("------------------------------")
analysis = analyze_json(data1)
print("Analysis results:")
print(json.dumps(analysis, indent=2))
print()
# 6. Transform JSON
print("6. Transforming JSON")
print("------------------------------")
transformations = [
{
'operation': 'set',
'path': 'details.status',
'value': 'active'
},
{
'operation': 'rename',
'path': 'details.city',
'value': 'details.location'
}
]
transformed = transform_json(data1, transformations)
print("Transformed data:")
print(json.dumps(transformed, indent=2))
print("\n==================================================")
print("Demonstration Complete")
print("==================================================")
@@ -0,0 +1,354 @@
"""Newspaper tools for scraping and parsing news articles.
Usage:
from praisonaiagents.tools import newspaper_tools
article = newspaper_tools.get_article("https://example.com/article")
sources = newspaper_tools.get_news_sources("technology")
articles = newspaper_tools.get_articles_from_source("https://techcrunch.com")
or
from praisonaiagents.tools import get_article, get_news_sources
article = get_article("https://example.com/article")
"""
import logging
from typing import List, Dict, Union, Optional, Any
from importlib import util
import json
from urllib.parse import urlparse
# Predefined list of popular news sources
POPULAR_NEWS_SOURCES = {
'technology': [
'https://techcrunch.com',
'https://www.theverge.com',
'https://www.wired.com',
'https://www.engadget.com',
'https://arstechnica.com'
],
'business': [
'https://www.bloomberg.com',
'https://www.reuters.com',
'https://www.wsj.com',
'https://www.ft.com',
'https://www.cnbc.com'
],
'general': [
'https://www.nytimes.com',
'https://www.theguardian.com',
'https://www.washingtonpost.com',
'https://www.bbc.com',
'https://www.cnn.com'
],
'sports': [
'https://www.espn.com',
'https://sports.yahoo.com',
'https://www.cbssports.com',
'https://www.skysports.com',
'https://www.bleacherreport.com'
],
'entertainment': [
'https://variety.com',
'https://www.hollywoodreporter.com',
'https://www.ew.com',
'https://www.deadline.com',
'https://www.imdb.com/news'
],
'science': [
'https://www.scientificamerican.com',
'https://www.sciencedaily.com',
'https://www.newscientist.com',
'https://www.sciencemag.org',
'https://www.nature.com/news'
]
}
class NewspaperTools:
"""Tools for scraping and parsing news articles."""
def __init__(self):
"""Initialize NewspaperTools and check for newspaper package."""
self._check_newspaper()
def _check_newspaper(self):
"""Check if newspaper package is installed."""
if util.find_spec("newspaper") is None:
raise ImportError("newspaper3k package is not available. Please install it using: pip install newspaper3k")
global newspaper
import newspaper
def get_article(
self,
url: str,
language: str = 'en'
) -> Dict[str, Any]:
"""
Extract and parse a news article from a URL.
Args:
url: URL of the article
language: Language code (e.g., 'en' for English)
Returns:
Dict: Article information including title, text, authors, etc.
"""
try:
from newspaper import Article, Config
# Configure article download
config = Config()
config.browser_user_agent = 'Mozilla/5.0'
config.language = language
# Download and parse article
article = Article(url, config=config)
article.download()
article.parse()
# Try to extract additional information
try:
article.nlp()
except Exception as e:
logging.warning(f"NLP processing failed: {str(e)}")
# Build response
response = {
"url": url,
"title": article.title,
"text": article.text,
"authors": article.authors,
"publish_date": article.publish_date.isoformat() if article.publish_date else None,
"top_image": article.top_image,
"images": list(article.images),
"movies": list(article.movies),
"source_domain": urlparse(url).netloc,
}
# Add NLP results if available
if hasattr(article, 'keywords') and article.keywords:
response["keywords"] = article.keywords
if hasattr(article, 'summary') and article.summary:
response["summary"] = article.summary
return response
except Exception as e:
error_msg = f"Error extracting article from {url}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def get_news_sources(
self,
category: Optional[str] = None,
language: str = 'en',
country: Optional[str] = None
) -> Union[List[Dict[str, str]], Dict[str, str]]:
"""
Get a list of news sources, optionally filtered by category.
Args:
category: Category to filter by (e.g., 'technology', 'sports')
language: Language code
country: Country code
Returns:
List[Dict] or Dict: List of news sources or error dict
"""
try:
sources = []
# Get sources for the specified category or all categories
if category:
category = category.lower()
if category in POPULAR_NEWS_SOURCES:
urls = POPULAR_NEWS_SOURCES[category]
else:
urls = []
for cat_urls in POPULAR_NEWS_SOURCES.values():
urls.extend(cat_urls)
else:
urls = []
for cat_urls in POPULAR_NEWS_SOURCES.values():
urls.extend(cat_urls)
# Create source objects
for url in urls:
domain = urlparse(url).netloc
source = {
"url": url,
"domain": domain,
"name": domain.replace("www.", "").split(".")[0].title(),
"category": category if category else "general"
}
sources.append(source)
return sources
except Exception as e:
error_msg = f"Error getting news sources: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def get_articles_from_source(
self,
source_url: str,
limit: int = 10,
language: str = 'en'
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
"""
Get recent articles from a news source.
Args:
source_url: URL of the news source
limit: Maximum number of articles to return
language: Language code
Returns:
List[Dict] or Dict: List of articles or error dict
"""
try:
from newspaper import Source, Config
# Configure source scraping
config = Config()
config.browser_user_agent = 'Mozilla/5.0'
config.language = language
config.fetch_images = False # Speed up processing
# Build news source
source = Source(source_url, config=config)
source.build()
# Get articles
articles = []
for article_url in source.article_urls()[:limit]:
try:
article = self.get_article(article_url, language)
if "error" not in article:
articles.append(article)
except Exception as e:
logging.warning(f"Error processing article {article_url}: {str(e)}")
continue
if len(articles) >= limit:
break
return articles
except Exception as e:
error_msg = f"Error getting articles from {source_url}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def get_trending_topics(
self,
sources: Optional[List[str]] = None,
limit: int = 10,
language: str = 'en'
) -> Union[List[str], Dict[str, str]]:
"""
Get trending topics across news sources.
Args:
sources: List of source URLs to analyze
limit: Maximum number of trending topics to return
language: Language code
Returns:
List[str] or Dict: List of trending topics or error dict
"""
try:
from collections import Counter
# Use default sources if none provided
if not sources:
sources_data = self.get_news_sources(language=language)
if isinstance(sources_data, dict) and "error" in sources_data:
return sources_data
sources = [s["url"] for s in sources_data[:5]] # Use top 5 sources
# Collect keywords from articles
all_keywords = []
for source_url in sources:
try:
articles = self.get_articles_from_source(source_url, limit=5, language=language)
if isinstance(articles, list):
for article in articles:
if "keywords" in article:
all_keywords.extend(article["keywords"])
except Exception as e:
logging.warning(f"Error processing source {source_url}: {str(e)}")
continue
# Get most common keywords
trending = Counter(all_keywords).most_common(limit)
return [topic for topic, count in trending]
except Exception as e:
error_msg = f"Error getting trending topics: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
# Create instance for direct function access
_newspaper_tools = NewspaperTools()
get_article = _newspaper_tools.get_article
get_news_sources = _newspaper_tools.get_news_sources
get_articles_from_source = _newspaper_tools.get_articles_from_source
get_trending_topics = _newspaper_tools.get_trending_topics
if __name__ == "__main__":
# Example usage
print("\n==================================================")
print("NewspaperTools Demonstration")
print("==================================================\n")
# 1. Get news sources
print("1. Getting News Sources")
print("------------------------------")
tech_sources = get_news_sources("technology")
print("Technology news sources:")
if isinstance(tech_sources, list):
print(json.dumps(tech_sources[:3], indent=2)) # Show first 3 sources
else:
print(tech_sources) # Show error
print()
if isinstance(tech_sources, list) and tech_sources:
source_url = tech_sources[0]["url"]
# 2. Get articles from a source
print("2. Getting Articles from Source")
print("------------------------------")
articles = get_articles_from_source(source_url, limit=2)
print(f"Articles from {source_url}:")
if isinstance(articles, list):
for article in articles:
print(f"- {article['title']}")
if "summary" in article:
print(f" Summary: {article['summary'][:200]}...")
else:
print(articles) # Show error
print()
# 3. Get a single article
print("3. Getting Single Article")
print("------------------------------")
if isinstance(articles, list) and articles:
article_url = articles[0]["url"]
article = get_article(article_url)
if "error" not in article:
print(f"Article: {article['title']}")
if "summary" in article:
print(f"Summary: {article['summary'][:200]}...")
print(f"Authors: {', '.join(article['authors'])}")
print(f"Date: {article['publish_date']}")
else:
print(article) # Show error
print()
# 4. Get trending topics
print("4. Getting Trending Topics")
print("------------------------------")
topics = get_trending_topics([source_url], limit=5)
print("Trending topics:")
print(json.dumps(topics, indent=2))
print("\n==================================================")
print("Demonstration Complete")
print("==================================================")
@@ -0,0 +1,326 @@
"""Pandas tools for data manipulation and analysis.
Usage:
from praisonaiagents.tools import pandas_tools
df = pandas_tools.read_csv("data.csv")
df = pandas_tools.filter_data(df, "column > 5")
summary = pandas_tools.get_summary(df)
or
from praisonaiagents.tools import read_csv, filter_data, get_summary
df = read_csv("data.csv")
"""
import logging
from typing import List, Dict, Union, Optional, Any
from importlib import util
import json
import os
# Import pandas for type hints, but don't use it until we check it's installed
if util.find_spec("pandas") is not None:
import pandas as pd
import numpy as np
else:
# Create a placeholder for type hints
class pd:
DataFrame = None
def _convert_to_serializable(obj: Any) -> Any:
"""Convert numpy/pandas types to JSON serializable Python types."""
if isinstance(obj, (np.integer, np.floating)):
return obj.item()
elif isinstance(obj, np.ndarray):
return obj.tolist()
elif isinstance(obj, pd.Series):
return obj.to_list()
elif isinstance(obj, pd.DataFrame):
return obj.to_dict(orient='records')
return obj
class PandasTools:
"""Tools for data manipulation and analysis using pandas."""
def __init__(self):
"""Initialize PandasTools and check for pandas installation."""
self._check_pandas()
def _check_pandas(self):
"""Check if pandas is installed."""
if util.find_spec("pandas") is None:
raise ImportError("pandas is not available. Please install it using: pip install pandas")
global pd, np
import pandas as pd
import numpy as np
def read_csv(self, filepath: str, **kwargs) -> Union[pd.DataFrame, Dict[str, str]]:
"""
Read a CSV file into a pandas DataFrame.
Args:
filepath: Path to the CSV file
**kwargs: Additional arguments to pass to pd.read_csv()
Returns:
pd.DataFrame or Dict: DataFrame if successful, error dict if failed
"""
try:
return pd.read_csv(filepath, **kwargs)
except Exception as e:
error_msg = f"Error reading CSV file {filepath}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def read_excel(self, filepath: str, **kwargs) -> Union[pd.DataFrame, Dict[str, str]]:
"""
Read an Excel file into a pandas DataFrame.
Args:
filepath: Path to the Excel file
**kwargs: Additional arguments to pass to pd.read_excel()
Returns:
pd.DataFrame or Dict: DataFrame if successful, error dict if failed
"""
try:
return pd.read_excel(filepath, **kwargs)
except Exception as e:
error_msg = f"Error reading Excel file {filepath}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def write_csv(self, df: pd.DataFrame, filepath: str, **kwargs) -> bool:
"""
Write DataFrame to a CSV file.
Args:
df: DataFrame to write
filepath: Output file path
**kwargs: Additional arguments to pass to df.to_csv()
Returns:
bool: True if successful, False otherwise
"""
try:
os.makedirs(os.path.dirname(filepath), exist_ok=True)
df.to_csv(filepath, **kwargs)
return True
except Exception as e:
error_msg = f"Error writing CSV file {filepath}: {str(e)}"
logging.error(error_msg)
return False
def write_excel(self, df: pd.DataFrame, filepath: str, **kwargs) -> bool:
"""
Write DataFrame to an Excel file.
Args:
df: DataFrame to write
filepath: Output file path
**kwargs: Additional arguments to pass to df.to_excel()
Returns:
bool: True if successful, False otherwise
"""
try:
os.makedirs(os.path.dirname(filepath), exist_ok=True)
df.to_excel(filepath, **kwargs)
return True
except Exception as e:
error_msg = f"Error writing Excel file {filepath}: {str(e)}"
logging.error(error_msg)
return False
def filter_data(self, df: pd.DataFrame, query: str) -> Union[pd.DataFrame, Dict[str, str]]:
"""
Filter DataFrame using a query string.
Args:
df: Input DataFrame
query: Query string (e.g., "column > 5 and other_column == 'value'")
Returns:
pd.DataFrame or Dict: Filtered DataFrame if successful, error dict if failed
"""
try:
return df.query(query)
except Exception as e:
error_msg = f"Error filtering data with query '{query}': {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def get_summary(self, df: pd.DataFrame) -> Dict[str, Any]:
"""
Get a summary of the DataFrame including basic statistics and info.
Args:
df: Input DataFrame
Returns:
Dict: Summary statistics and information
"""
try:
numeric_summary = df.describe().to_dict()
# Convert numpy types to native Python types
for col in numeric_summary:
numeric_summary[col] = {k: _convert_to_serializable(v)
for k, v in numeric_summary[col].items()}
summary = {
"shape": list(df.shape),
"columns": list(df.columns),
"dtypes": df.dtypes.astype(str).to_dict(),
"null_counts": df.isnull().sum().to_dict(),
"numeric_summary": numeric_summary,
"memory_usage": int(df.memory_usage(deep=True).sum()),
}
return summary
except Exception as e:
error_msg = f"Error getting data summary: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def group_by(
self,
df: pd.DataFrame,
columns: Union[str, List[str]],
agg_dict: Dict[str, Union[str, List[str]]]
) -> Union[pd.DataFrame, Dict[str, str]]:
"""
Group DataFrame by columns and apply aggregation functions.
Args:
df: Input DataFrame
columns: Column(s) to group by
agg_dict: Dictionary of column:function pairs for aggregation
Returns:
pd.DataFrame or Dict: Grouped DataFrame if successful, error dict if failed
"""
try:
return df.groupby(columns).agg(agg_dict).reset_index()
except Exception as e:
error_msg = f"Error grouping data: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def pivot_table(
self,
df: pd.DataFrame,
index: Union[str, List[str]],
columns: Optional[Union[str, List[str]]] = None,
values: Optional[Union[str, List[str]]] = None,
aggfunc: str = "mean"
) -> Union[pd.DataFrame, Dict[str, str]]:
"""
Create a pivot table from DataFrame.
Args:
df: Input DataFrame
index: Column(s) to use as index
columns: Column(s) to use as columns
values: Column(s) to aggregate
aggfunc: Aggregation function to use
Returns:
pd.DataFrame or Dict: Pivot table if successful, error dict if failed
"""
try:
return pd.pivot_table(
df,
index=index,
columns=columns,
values=values,
aggfunc=aggfunc
).reset_index()
except Exception as e:
error_msg = f"Error creating pivot table: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
# Create instance for direct function access
_pandas_tools = PandasTools()
read_csv = _pandas_tools.read_csv
read_excel = _pandas_tools.read_excel
write_csv = _pandas_tools.write_csv
write_excel = _pandas_tools.write_excel
filter_data = _pandas_tools.filter_data
get_summary = _pandas_tools.get_summary
group_by = _pandas_tools.group_by
pivot_table = _pandas_tools.pivot_table
if __name__ == "__main__":
# Example usage
print("\n==================================================")
print("PandasTools Demonstration")
print("==================================================\n")
# Create a test directory
test_dir = os.path.join(os.getcwd(), "test_files")
os.makedirs(test_dir, exist_ok=True)
# Create a sample DataFrame
df = pd.DataFrame({
'name': ['John', 'Jane', 'Bob', 'Alice', 'Charlie'],
'age': [25, 30, 35, 28, 32],
'city': ['New York', 'London', 'Paris', 'Tokyo', 'London'],
'salary': [50000, 60000, 75000, 65000, 55000]
})
# 1. Write to CSV
print("1. Writing to CSV")
print("------------------------------")
csv_file = os.path.join(test_dir, "sample.csv")
success = write_csv(df, csv_file, index=False)
print(f"Write successful: {success}\n")
# 2. Read from CSV
print("2. Reading from CSV")
print("------------------------------")
df_read = read_csv(csv_file)
print("First few rows:")
print(df_read.head())
print()
# 3. Filter Data
print("3. Filtering Data")
print("------------------------------")
filtered_df = filter_data(df, "age > 30 and salary > 60000")
print("People over 30 with salary > 60000:")
print(filtered_df)
print()
# 4. Get Summary
print("4. Data Summary")
print("------------------------------")
summary = get_summary(df)
print(json.dumps(summary, indent=2))
print()
# 5. Group By
print("5. Group By")
print("------------------------------")
grouped = group_by(df, "city", {"salary": ["mean", "count"], "age": "mean"})
print("Statistics by city:")
print(grouped)
print()
# 6. Pivot Table
print("6. Pivot Table")
print("------------------------------")
pivoted = pivot_table(df, index="city", values=["salary", "age"])
print("Pivot table by city:")
print(pivoted)
print()
# Clean up test directory
try:
import shutil
shutil.rmtree(test_dir)
print("Test directory cleaned up successfully")
except Exception as e:
print(f"Error cleaning up test directory: {str(e)}")
print("\n==================================================")
print("Demonstration Complete")
print("==================================================")
@@ -0,0 +1,423 @@
"""Tools for Python code execution and analysis.
Usage:
from praisonaiagents.tools import python_tools
result = python_tools.execute_code("print('Hello, World!')")
or
from praisonaiagents.tools import execute_code, analyze_code, format_code
result = execute_code("print('Hello, World!')")
"""
import logging
from typing import Dict, List, Optional, Any
from importlib import util
import io
from contextlib import redirect_stdout, redirect_stderr
import traceback
class PythonTools:
"""Tools for Python code execution and analysis."""
def __init__(self):
"""Initialize PythonTools."""
self._check_dependencies()
def _check_dependencies(self):
"""Check if required packages are installed."""
missing = []
for package in ['black', 'pylint', 'autopep8']:
if util.find_spec(package) is None:
missing.append(package)
if missing:
raise ImportError(
f"Required packages not available. Please install: {', '.join(missing)}\n"
f"Run: pip install {' '.join(missing)}"
)
def execute_code(
self,
code: str,
globals_dict: Optional[Dict[str, Any]] = None,
locals_dict: Optional[Dict[str, Any]] = None,
timeout: int = 30,
max_output_size: int = 10000
) -> Dict[str, Any]:
"""Execute Python code safely."""
try:
# Set up execution environment
if globals_dict is None:
globals_dict = {'__builtins__': __builtins__}
if locals_dict is None:
locals_dict = {}
# Capture output
stdout_buffer = io.StringIO()
stderr_buffer = io.StringIO()
try:
# Compile code
compiled_code = compile(code, '<string>', 'exec')
# Execute with output capture
with redirect_stdout(stdout_buffer), redirect_stderr(stderr_buffer):
exec(compiled_code, globals_dict, locals_dict)
# Get last expression value if any
import ast
tree = ast.parse(code)
if tree.body and isinstance(tree.body[-1], ast.Expr):
result = eval(
compile(ast.Expression(tree.body[-1].value), '<string>', 'eval'),
globals_dict,
locals_dict
)
else:
result = None
# Get output
stdout = stdout_buffer.getvalue()
stderr = stderr_buffer.getvalue()
# Truncate output if too large
if len(stdout) > max_output_size:
stdout = stdout[:max_output_size] + "...[truncated]"
if len(stderr) > max_output_size:
stderr = stderr[:max_output_size] + "...[truncated]"
return {
'result': result,
'stdout': stdout,
'stderr': stderr,
'success': True
}
except Exception as e:
error_msg = f"Error executing code: {str(e)}"
logging.error(error_msg)
return {
'result': None,
'stdout': stdout_buffer.getvalue(),
'stderr': error_msg,
'success': False
}
except Exception as e:
error_msg = f"Error executing code: {str(e)}"
logging.error(error_msg)
return {
'result': None,
'stdout': '',
'stderr': error_msg,
'success': False
}
def analyze_code(
self,
code: str
) -> Optional[Dict[str, Any]]:
"""Analyze Python code structure and quality.
Args:
code: Python code to analyze
Returns:
Dictionary with analysis results
"""
try:
# Import ast only when needed
import ast
# Parse code
tree = ast.parse(code)
# Analyze structure
analysis = {
'imports': [],
'functions': [],
'classes': [],
'variables': [],
'complexity': {
'lines': len(code.splitlines()),
'functions': 0,
'classes': 0,
'branches': 0
}
}
# Analyze nodes
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for name in node.names:
analysis['imports'].append(name.name)
elif isinstance(node, ast.ImportFrom):
module = node.module or ''
for name in node.names:
analysis['imports'].append(f"{module}.{name.name}")
elif isinstance(node, ast.FunctionDef):
analysis['functions'].append({
'name': node.name,
'args': [
arg.arg for arg in node.args.args
],
'decorators': [
ast.unparse(d) for d in node.decorator_list
]
})
analysis['complexity']['functions'] += 1
elif isinstance(node, ast.ClassDef):
analysis['classes'].append({
'name': node.name,
'bases': [
ast.unparse(base) for base in node.bases
],
'decorators': [
ast.unparse(d) for d in node.decorator_list
]
})
analysis['complexity']['classes'] += 1
elif isinstance(node, ast.Assign):
for target in node.targets:
if isinstance(target, ast.Name):
analysis['variables'].append(target.id)
elif isinstance(node, (ast.If, ast.While, ast.For)):
analysis['complexity']['branches'] += 1
return analysis
except Exception as e:
error_msg = f"Error analyzing code: {str(e)}"
logging.error(error_msg)
return None
def format_code(
self,
code: str,
style: str = 'black',
line_length: int = 88
) -> Optional[str]:
"""Format Python code according to style guide.
Args:
code: Python code to format
style: Formatting style ('black' or 'pep8')
line_length: Maximum line length
Returns:
Formatted code
"""
try:
if util.find_spec(style) is None:
error_msg = f"{style} package is not available. Please install it using: pip install {style}"
logging.error(error_msg)
return None
if style == 'black':
import black
return black.format_str(
code,
mode=black.FileMode(
line_length=line_length
)
)
else: # pep8
import autopep8
return autopep8.fix_code(
code,
options={
'max_line_length': line_length
}
)
except Exception as e:
error_msg = f"Error formatting code: {str(e)}"
logging.error(error_msg)
return None
def lint_code(
self,
code: str
) -> Optional[Dict[str, List[Dict[str, Any]]]]:
"""Lint Python code for potential issues.
Args:
code: Python code to lint
Returns:
Dictionary with linting results
"""
try:
if util.find_spec('pylint') is None:
error_msg = "pylint package is not available. Please install it using: pip install pylint"
logging.error(error_msg)
return None
# Import pylint only when needed
from pylint.reporters import JSONReporter
from pylint.lint.run import Run
# Create temporary file for pylint
import tempfile
with tempfile.NamedTemporaryFile(
mode='w',
suffix='.py',
delete=False
) as f:
f.write(code)
temp_path = f.name
# Run pylint
reporter = JSONReporter()
Run(
[temp_path],
reporter=reporter,
exit=False
)
# Process results
results = {
'errors': [],
'warnings': [],
'conventions': []
}
for msg in reporter.messages:
item = {
'type': msg.category,
'module': msg.module,
'obj': msg.obj,
'line': msg.line,
'column': msg.column,
'path': msg.path,
'symbol': msg.symbol,
'message': msg.msg,
'message-id': msg.msg_id
}
if msg.category in ['error', 'fatal']:
results['errors'].append(item)
elif msg.category == 'warning':
results['warnings'].append(item)
else:
results['conventions'].append(item)
# Clean up
import os
os.unlink(temp_path)
return results
except Exception as e:
error_msg = f"Error linting code: {str(e)}"
logging.error(error_msg)
return {
'errors': [],
'warnings': [],
'conventions': []
}
def disassemble_code(
self,
code: str
) -> Optional[str]:
"""Disassemble Python code to bytecode.
Args:
code: Python code to disassemble
Returns:
Disassembled bytecode as string
"""
try:
# Import dis only when needed
import dis
# Compile code
compiled_code = compile(code, '<string>', 'exec')
# Capture disassembly
output = io.StringIO()
with redirect_stdout(output):
dis.dis(compiled_code)
return output.getvalue()
except Exception as e:
error_msg = f"Error disassembling code: {str(e)}"
logging.error(error_msg)
return None
# Create instance for direct function access
_python_tools = PythonTools()
execute_code = _python_tools.execute_code
analyze_code = _python_tools.analyze_code
format_code = _python_tools.format_code
lint_code = _python_tools.lint_code
disassemble_code = _python_tools.disassemble_code
if __name__ == "__main__":
print("\n==================================================")
print("PythonTools Demonstration")
print("==================================================\n")
print("1. Execute Python Code")
print("------------------------------")
code = """
def greet(name):
return f"Hello, {name}!"
result = greet("World")
print(result)
"""
print("Code to execute:")
print(code)
print("\nOutput:")
result = execute_code(code)
print(result["stdout"])
print()
print("2. Format Python Code")
print("------------------------------")
code = """
def messy_function(x,y, z):
if x>0:
return y+z
else:
return y-z
"""
print("Before formatting:")
print(code)
print("\nAfter formatting:")
result = format_code(code)
print(result)
print()
print("3. Lint Python Code")
print("------------------------------")
code = """
def bad_function():
unused_var = 42
return 'result'
"""
print("Code to lint:")
print(code)
print("\nLinting results:")
result = lint_code(code)
print(result)
print()
print("4. Execute Code with Variables")
print("------------------------------")
code = """
x = 10
y = 20
result = x + y
print(f"The sum of {x} and {y} is {result}")
"""
print("Code to execute:")
print(code)
print("\nOutput:")
result = execute_code(code)
print(result["stdout"])
print()
print("==================================================")
print("Demonstration Complete")
print("==================================================")
+278
View File
@@ -0,0 +1,278 @@
"""Tools for executing shell commands safely.
This module provides a safe interface for executing shell commands with:
- Timeout control
- Output capture
- Error handling
- Resource limits
"""
import subprocess
import shlex
import logging
import os
import time
from typing import Dict, List, Optional, Union
class ShellTools:
"""Tools for executing shell commands safely."""
def __init__(self):
"""Initialize ShellTools."""
self._check_dependencies()
def _check_dependencies(self):
"""Check if required packages are installed."""
try:
import psutil
except ImportError:
raise ImportError(
"Required package not available. Please install: psutil\n"
"Run: pip install psutil"
)
def execute_command(
self,
command: str,
cwd: Optional[str] = None,
timeout: int = 30,
shell: bool = False,
env: Optional[Dict[str, str]] = None,
max_output_size: int = 10000
) -> Dict[str, Union[str, int, bool]]:
"""Execute a shell command safely.
Args:
command: Command to execute
cwd: Working directory
timeout: Maximum execution time in seconds
shell: Whether to run command in shell
env: Environment variables
max_output_size: Maximum output size in bytes
Returns:
Dictionary with execution results
"""
try:
# Split command if not using shell
if not shell:
command = shlex.split(command)
# Set up process environment
process_env = os.environ.copy()
if env:
process_env.update(env)
# Start process
start_time = time.time()
process = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=cwd,
shell=shell,
env=process_env,
text=True
)
try:
# Wait for process with timeout
stdout, stderr = process.communicate(timeout=timeout)
# Truncate output if too large
if len(stdout) > max_output_size:
stdout = stdout[:max_output_size] + "...[truncated]"
if len(stderr) > max_output_size:
stderr = stderr[:max_output_size] + "...[truncated]"
return {
'stdout': stdout,
'stderr': stderr,
'exit_code': process.returncode,
'success': process.returncode == 0,
'execution_time': time.time() - start_time
}
except subprocess.TimeoutExpired:
# Kill process on timeout
parent = psutil.Process(process.pid)
children = parent.children(recursive=True)
for child in children:
child.kill()
parent.kill()
return {
'stdout': '',
'stderr': f'Command timed out after {timeout} seconds',
'exit_code': -1,
'success': False,
'execution_time': timeout
}
except Exception as e:
error_msg = f"Error executing command: {str(e)}"
logging.error(error_msg)
return {
'stdout': '',
'stderr': error_msg,
'exit_code': -1,
'success': False,
'execution_time': 0
}
def list_processes(self) -> List[Dict[str, Union[int, str, float]]]:
"""List running processes with their details.
Returns:
List of process information dictionaries
"""
try:
processes = []
for proc in psutil.process_iter(['pid', 'name', 'username', 'memory_percent', 'cpu_percent']):
try:
pinfo = proc.info
processes.append({
'pid': pinfo['pid'],
'name': pinfo['name'],
'username': pinfo['username'],
'memory_percent': round(pinfo['memory_percent'], 2),
'cpu_percent': round(pinfo['cpu_percent'], 2)
})
except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
return processes
except Exception as e:
error_msg = f"Error listing processes: {str(e)}"
logging.error(error_msg)
return []
def kill_process(
self,
pid: int,
force: bool = False
) -> Dict[str, Union[bool, str]]:
"""Kill a process by its PID.
Args:
pid: Process ID to kill
force: Whether to force kill (-9)
Returns:
Dictionary with operation results
"""
try:
process = psutil.Process(pid)
if force:
process.kill() # SIGKILL
else:
process.terminate() # SIGTERM
return {
'success': True,
'message': f'Process {pid} killed successfully'
}
except psutil.NoSuchProcess:
return {
'success': False,
'message': f'No process found with PID {pid}'
}
except psutil.AccessDenied:
return {
'success': False,
'message': f'Access denied to kill process {pid}'
}
except Exception as e:
error_msg = f"Error killing process: {str(e)}"
logging.error(error_msg)
return {
'success': False,
'message': error_msg
}
def get_system_info(self) -> Dict[str, Union[float, int, str, Dict]]:
"""Get system information.
Returns:
Dictionary with system information
"""
try:
cpu_percent = psutil.cpu_percent(interval=1)
memory = psutil.virtual_memory()
disk = psutil.disk_usage('/')
return {
'cpu': {
'percent': cpu_percent,
'cores': psutil.cpu_count(),
'physical_cores': psutil.cpu_count(logical=False)
},
'memory': {
'total': memory.total,
'available': memory.available,
'percent': memory.percent,
'used': memory.used,
'free': memory.free
},
'disk': {
'total': disk.total,
'used': disk.used,
'free': disk.free,
'percent': disk.percent
},
'boot_time': psutil.boot_time(),
'platform': os.uname().sysname
}
except Exception as e:
error_msg = f"Error getting system info: {str(e)}"
logging.error(error_msg)
return {}
# Create instance for direct function access
_shell_tools = ShellTools()
execute_command = _shell_tools.execute_command
list_processes = _shell_tools.list_processes
kill_process = _shell_tools.kill_process
get_system_info = _shell_tools.get_system_info
if __name__ == "__main__":
# Example usage
print("\n==================================================")
print("ShellTools Demonstration")
print("==================================================\n")
# 1. Execute command
print("1. Command Execution")
print("------------------------------")
result = execute_command("ls -la")
print(f"Success: {result['success']}")
print(f"Output:\n{result['stdout']}")
if result['stderr']:
print(f"Errors:\n{result['stderr']}")
print(f"Execution time: {result['execution_time']:.2f}s")
print()
# 2. System Information
print("2. System Information")
print("------------------------------")
info = get_system_info()
print(f"CPU Usage: {info['cpu']['percent']}%")
print(f"Memory Usage: {info['memory']['percent']}%")
print(f"Disk Usage: {info['disk']['percent']}%")
print(f"Platform: {info['platform']}")
print()
# 3. Process List
print("3. Process List (top 5 by CPU)")
print("------------------------------")
processes = sorted(
list_processes(),
key=lambda x: x['cpu_percent'],
reverse=True
)[:5]
for proc in processes:
print(f"PID: {proc['pid']}, Name: {proc['name']}, CPU: {proc['cpu_percent']}%")
print()
print("\n==================================================")
print("Demonstration Complete")
print("==================================================")
@@ -0,0 +1,431 @@
"""Tools for web scraping and crawling.
Usage:
from praisonaiagents.tools import spider_tools
content = spider_tools.scrape_page("https://example.com")
links = spider_tools.extract_links("https://example.com")
or
from praisonaiagents.tools import scrape_page, extract_links
content = scrape_page("https://example.com")
"""
import logging
from typing import List, Dict, Union, Optional, Any
from importlib import util
import json
from urllib.parse import urljoin, urlparse
import re
import os
import hashlib
import time
class SpiderTools:
"""Tools for web scraping and crawling."""
def __init__(self):
"""Initialize SpiderTools and check for required packages."""
self._session = None
def _get_session(self):
"""Get or create requests session with common headers."""
if util.find_spec('requests') is None:
error_msg = "requests package is not available. Please install it using: pip install requests"
logging.error(error_msg)
return None
if self._session is None:
import requests
self._session = requests.Session()
self._session.headers.update({
'User-Agent': 'Mozilla/5.0 (compatible; PraisonAI/1.0; +http://praisonai.com/bot)',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.5',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
})
return self._session
def scrape_page(
self,
url: str,
selector: Optional[str] = None,
extract_images: bool = False,
extract_links: bool = False,
timeout: int = 30,
verify_ssl: bool = True
) -> Union[Dict[str, Any], Dict[str, str]]:
"""
Scrape content from a webpage.
Args:
url: URL to scrape
selector: Optional CSS selector to extract specific content
extract_images: Whether to extract image URLs
extract_links: Whether to extract links
timeout: Request timeout in seconds
verify_ssl: Whether to verify SSL certificates
Returns:
Dict: Scraped content or error dict
"""
try:
session = self._get_session()
if session is None:
return {"error": "requests package not available"}
# Import BeautifulSoup only when needed
if util.find_spec('bs4') is None:
error_msg = "bs4 package is not available. Please install it using: pip install beautifulsoup4"
logging.error(error_msg)
return {"error": error_msg}
from bs4 import BeautifulSoup
# Make request
response = session.get(
url,
timeout=timeout,
verify=verify_ssl
)
response.raise_for_status()
# Parse HTML
soup = BeautifulSoup(response.text, 'lxml')
# Remove unwanted elements
for element in soup(['script', 'style']):
element.decompose()
# Initialize result
result = {
'url': url,
'status_code': response.status_code,
'encoding': response.encoding,
'headers': dict(response.headers),
}
# Extract content based on selector
if selector:
elements = soup.select(selector)
result['content'] = [elem.get_text(strip=True) for elem in elements]
result['html'] = [str(elem) for elem in elements]
else:
result['title'] = soup.title.string if soup.title else None
result['content'] = soup.get_text(separator=' ', strip=True)
result['html'] = str(soup)
# Extract metadata
meta_tags = {}
for meta in soup.find_all('meta'):
name = meta.get('name') or meta.get('property')
if name:
meta_tags[name] = meta.get('content')
result['meta_tags'] = meta_tags
# Extract images if requested
if extract_images:
images = []
for img in soup.find_all('img'):
src = img.get('src')
if src:
images.append({
'src': urljoin(url, src),
'alt': img.get('alt', ''),
'title': img.get('title', '')
})
result['images'] = images
# Extract links if requested
if extract_links:
links = []
for link in soup.find_all('a'):
href = link.get('href')
if href:
links.append({
'url': urljoin(url, href),
'text': link.get_text(strip=True),
'title': link.get('title', '')
})
result['links'] = links
return result
except Exception as e:
error_msg = f"Error scraping {url}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def extract_links(
self,
url: str,
same_domain: bool = True,
exclude_patterns: Optional[List[str]] = None,
timeout: int = 30,
verify_ssl: bool = True
) -> Union[List[Dict[str, str]], Dict[str, str]]:
"""
Extract all links from a webpage.
Args:
url: URL to extract links from
same_domain: Only return links from the same domain
exclude_patterns: List of regex patterns to exclude
timeout: Request timeout in seconds
verify_ssl: Whether to verify SSL certificates
Returns:
List[Dict] or Dict: List of links or error dict
"""
try:
# Compile exclude patterns
if exclude_patterns:
exclude_patterns = [re.compile(p) for p in exclude_patterns]
# Get base domain
base_domain = urlparse(url).netloc
# Scrape page
result = self.scrape_page(
url,
extract_links=True,
timeout=timeout,
verify_ssl=verify_ssl
)
if "error" in result:
return result
# Filter and clean links
links = []
seen_urls = set()
for link in result.get('links', []):
link_url = link['url']
# Skip if already seen
if link_url in seen_urls:
continue
# Parse URL
parsed = urlparse(link_url)
# Skip if not same domain and same_domain is True
if same_domain and parsed.netloc != base_domain:
continue
# Skip if matches exclude patterns
if exclude_patterns and any(p.search(link_url) for p in exclude_patterns):
continue
# Add to results
links.append(link)
seen_urls.add(link_url)
return links
except Exception as e:
error_msg = f"Error extracting links from {url}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def crawl(
self,
start_url: str,
max_pages: int = 10,
same_domain: bool = True,
exclude_patterns: Optional[List[str]] = None,
delay: float = 1.0,
timeout: int = 30,
verify_ssl: bool = True,
output_dir: Optional[str] = None
) -> Union[List[Dict[str, Any]], Dict[str, str]]:
"""
Crawl multiple pages starting from a URL.
Args:
start_url: Starting URL
max_pages: Maximum number of pages to crawl
same_domain: Only crawl pages from the same domain
exclude_patterns: List of regex patterns to exclude
delay: Delay between requests in seconds
timeout: Request timeout in seconds
verify_ssl: Whether to verify SSL certificates
output_dir: Directory to save crawled pages
Returns:
List[Dict] or Dict: Crawled pages or error dict
"""
try:
# Create output directory if needed
if output_dir:
os.makedirs(output_dir, exist_ok=True)
# Initialize crawl state
to_visit = {start_url}
visited = set()
results = []
while to_visit and len(visited) < max_pages:
# Get next URL
url = to_visit.pop()
# Skip if already visited
if url in visited:
continue
# Add to visited
visited.add(url)
# Delay if not first request
if len(visited) > 1:
time.sleep(delay)
# Scrape page
result = self.scrape_page(
url,
extract_links=True,
timeout=timeout,
verify_ssl=verify_ssl
)
if "error" in result:
logging.warning(f"Error crawling {url}: {result['error']}")
continue
# Save result
results.append(result)
# Save to file if requested
if output_dir:
filename = hashlib.md5(url.encode()).hexdigest() + '.json'
filepath = os.path.join(output_dir, filename)
with open(filepath, 'w', encoding='utf-8') as f:
json.dump(result, f, indent=2, ensure_ascii=False)
# Add new links to visit
for link in result.get('links', []):
link_url = link['url']
parsed = urlparse(link_url)
# Skip if not same domain and same_domain is True
if same_domain and parsed.netloc != urlparse(start_url).netloc:
continue
# Skip if matches exclude patterns
if exclude_patterns and any(
re.compile(p).search(link_url) for p in exclude_patterns
):
continue
# Add to visit if not visited
if link_url not in visited:
to_visit.add(link_url)
return results
except Exception as e:
error_msg = f"Error crawling from {start_url}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def extract_text(
self,
url: str,
selector: Optional[str] = None,
timeout: int = 30,
verify_ssl: bool = True
) -> Union[str, Dict[str, str]]:
"""
Extract clean text content from a webpage.
Args:
url: URL to extract text from
selector: Optional CSS selector to extract specific content
timeout: Request timeout in seconds
verify_ssl: Whether to verify SSL certificates
Returns:
str or Dict: Extracted text or error dict
"""
try:
result = self.scrape_page(
url,
selector=selector,
timeout=timeout,
verify_ssl=verify_ssl
)
if "error" in result:
return result
if selector:
return '\n'.join(result['content'])
return result['content']
except Exception as e:
error_msg = f"Error extracting text from {url}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
# Create instance for direct function access
_spider_tools = SpiderTools()
scrape_page = _spider_tools.scrape_page
extract_links = _spider_tools.extract_links
crawl = _spider_tools.crawl
extract_text = _spider_tools.extract_text
if __name__ == "__main__":
# Example usage
print("\n==================================================")
print("SpiderTools Demonstration")
print("==================================================\n")
# 1. Scrape a webpage
print("1. Scraping Webpage")
print("------------------------------")
url = "https://example.com"
result = scrape_page(url, extract_images=True, extract_links=True)
print(f"Content from {url}:")
if "error" not in result:
print(f"Title: {result['title']}")
print(f"Content length: {len(result['content'])} characters")
print(f"Number of images: {len(result.get('images', []))}")
print(f"Number of links: {len(result.get('links', []))}")
else:
print(result) # Show error
print()
# 2. Extract links
print("2. Extracting Links")
print("------------------------------")
links = extract_links(url)
print(f"Links from {url}:")
if isinstance(links, list):
for link in links:
print(f"- {link['url']} ({link['text']})")
else:
print(links) # Show error
print()
# 3. Extract text
print("3. Extracting Text")
print("------------------------------")
text = extract_text(url)
print(f"Text from {url}:")
if isinstance(text, str):
print(text[:500] + "..." if len(text) > 500 else text)
else:
print(text) # Show error
print()
# 4. Crawl multiple pages
print("4. Crawling Multiple Pages")
print("------------------------------")
results = crawl(url, max_pages=2, delay=1.0)
print(f"Crawl results from {url}:")
if isinstance(results, list):
print(f"Crawled {len(results)} pages")
for result in results:
print(f"- {result['url']}: {result['title']}")
else:
print(results) # Show error
print("\n==================================================")
print("Demonstration Complete")
print("==================================================")
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Test runner for all tools."""
import os
import glob
import logging
import subprocess
# Set up logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
def main():
"""Run all tool files."""
logger.info("Starting tests...")
# Get all *_tools.py files
tools_dir = os.path.dirname(os.path.abspath(__file__))
tool_files = glob.glob(os.path.join(tools_dir, "*_tools.py"))
# Run each tool file
for tool_file in sorted(tool_files):
module_name = os.path.basename(tool_file)
logger.info(f"\nRunning {module_name}...")
try:
# Run the tool file directly
result = subprocess.run(
["python3", tool_file],
capture_output=True,
text=True,
cwd=tools_dir
)
# Log output
if result.stdout:
logger.info(f"Output:\n{result.stdout}")
if result.stderr:
logger.error(f"Errors:\n{result.stderr}")
if result.returncode == 0:
logger.info(f" {module_name} completed successfully")
else:
logger.error(f" {module_name} failed with return code {result.returncode}")
except Exception as e:
logger.error(f"Error running {module_name}: {str(e)}")
continue
logger.info("\nAll tests completed!")
if __name__ == "__main__":
main()
+9
View File
@@ -0,0 +1,9 @@
"""Tools module for PraisonAI Agents"""
from .duckduckgo_tools import internet_search
class Tools:
"""Tools class for backward compatibility"""
internet_search = staticmethod(internet_search)
# Re-export the function
__all__ = ['Tools', 'internet_search']
@@ -0,0 +1,272 @@
"""Wikipedia tools for accessing and searching Wikipedia content.
Usage:
from praisonaiagents.tools import wikipedia_tools
summary = wikipedia_tools.wiki_summary("Python programming language")
page = wikipedia_tools.wiki_page("Python programming language")
results = wikipedia_tools.wiki_search("Python programming")
or
from praisonaiagents.tools import wiki_search, wiki_summary, wiki_page
summary = wiki_summary("Python programming language")
"""
import logging
from typing import List, Dict, Union, Any
from importlib import util
import json
class WikipediaTools:
"""Tools for accessing and searching Wikipedia content."""
def __init__(self):
"""Initialize WikipediaTools and check for wikipedia package."""
self._check_wikipedia()
def _check_wikipedia(self):
"""Check if wikipedia package is installed."""
if util.find_spec("wikipedia") is None:
raise ImportError("wikipedia package is not available. Please install it using: pip install wikipedia")
global wikipedia
import wikipedia
# Set default language to English
wikipedia.set_lang("en")
def wiki_search(
self,
query: str,
results: int = 10,
suggestion: bool = True
) -> Union[List[str], Dict[str, str]]:
"""
Search Wikipedia for a query.
Args:
query: Search query
results: Maximum number of results to return
suggestion: Whether to suggest similar queries
Returns:
List[str] or Dict: List of search results or error dict
"""
try:
search_results = wikipedia.search(query, results=results, suggestion=suggestion)
if isinstance(search_results, tuple):
# If suggestion is True, returns (results, suggestion)
return {
"results": search_results[0],
"suggestion": search_results[1] if search_results[1] else None
}
return {"results": search_results, "suggestion": None}
except Exception as e:
error_msg = f"Error searching Wikipedia for '{query}': {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def wiki_summary(
self,
title: str,
sentences: int = 5,
auto_suggest: bool = True
) -> Union[str, Dict[str, str]]:
"""
Get a summary of a Wikipedia page.
Args:
title: Title of the Wikipedia page
sentences: Number of sentences to return
auto_suggest: Whether to auto-suggest similar titles
Returns:
str or Dict: Page summary if successful, error dict if failed
"""
try:
return wikipedia.summary(title, sentences=sentences, auto_suggest=auto_suggest)
except wikipedia.exceptions.DisambiguationError as e:
return {
"error": "Disambiguation page",
"options": e.options[:10] # Limit to first 10 options
}
except Exception as e:
error_msg = f"Error getting summary for '{title}': {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def wiki_page(
self,
title: str,
auto_suggest: bool = True
) -> Union[Dict[str, Any], Dict[str, str]]:
"""
Get detailed information about a Wikipedia page.
Args:
title: Title of the Wikipedia page
auto_suggest: Whether to auto-suggest similar titles
Returns:
Dict: Page information if successful, error dict if failed
"""
try:
page = wikipedia.page(title, auto_suggest=auto_suggest)
return {
"title": page.title,
"url": page.url,
"content": page.content,
"summary": page.summary,
"references": page.references,
"categories": page.categories,
"links": page.links,
"images": page.images,
"sections": self.get_sections(page)
}
except wikipedia.exceptions.DisambiguationError as e:
return {
"error": "Disambiguation page",
"options": e.options[:10] # Limit to first 10 options
}
except Exception as e:
error_msg = f"Error getting page for '{title}': {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def get_sections(self, page: Any) -> List[Dict[str, Any]]:
"""
Extract sections and their content from a Wikipedia page.
Args:
page: Wikipedia page object
Returns:
List[Dict]: List of sections with their titles and content
"""
sections = []
content = page.content
section_titles = [s.strip() for s in content.split("==")[1::2]]
section_contents = [s.strip() for s in content.split("==")[2::2]]
# Add introduction section
intro = content.split("==")[0].strip()
if intro:
sections.append({
"title": "Introduction",
"level": 0,
"content": intro
})
# Process remaining sections
for title, content in zip(section_titles, section_contents):
# Count leading '=' to determine section level
level = title.count("=") // 2
clean_title = title.strip("= ")
sections.append({
"title": clean_title,
"level": level,
"content": content.strip()
})
return sections
def wiki_random(self, pages: int = 1) -> Union[List[str], Dict[str, str]]:
"""
Get random Wikipedia page titles.
Args:
pages: Number of random pages to return
Returns:
List[str] or Dict: List of random page titles or error dict
"""
try:
return wikipedia.random(pages)
except Exception as e:
error_msg = f"Error getting random pages: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def wiki_language(self, language: str) -> bool:
"""
Set the language for Wikipedia searches.
Args:
language: Language code (e.g., 'en' for English, 'es' for Spanish)
Returns:
bool: True if successful, False if failed
"""
try:
wikipedia.set_lang(language)
return True
except Exception as e:
error_msg = f"Error setting language to '{language}': {str(e)}"
logging.error(error_msg)
return False
# Create instance for direct function access
_wikipedia_tools = WikipediaTools()
wiki_search = _wikipedia_tools.wiki_search
wiki_summary = _wikipedia_tools.wiki_summary
wiki_page = _wikipedia_tools.wiki_page
wiki_random = _wikipedia_tools.wiki_random
wiki_language = _wikipedia_tools.wiki_language
if __name__ == "__main__":
# Example usage
print("\n==================================================")
print("WikipediaTools Demonstration")
print("==================================================\n")
# 1. Search for a topic
print("1. Searching Wikipedia")
print("------------------------------")
search_results = wiki_search("Python programming language")
print("Search results:")
print(json.dumps(search_results, indent=2))
print()
# 2. Get a page summary
print("2. Getting Page Summary")
print("------------------------------")
summary = wiki_summary("Python programming language", sentences=3)
print("Summary:")
print(summary)
print()
# 3. Get detailed page information
print("3. Getting Full Page Information")
print("------------------------------")
page = wiki_page("Python programming language")
if isinstance(page, dict) and "error" not in page:
print("Page sections:")
for section in page["sections"]:
print(f"- {section['title']} (Level {section['level']})")
print(f"\nNumber of references: {len(page['references'])}")
print(f"Number of categories: {len(page['categories'])}")
print(f"Number of links: {len(page['links'])}")
print(f"Number of images: {len(page['images'])}")
else:
print("Error getting page:", page.get("error"))
print()
# 4. Get random pages
print("4. Getting Random Pages")
print("------------------------------")
random_pages = wiki_random(3)
print("Random page titles:")
print(json.dumps(random_pages, indent=2))
print()
# 5. Try different language
print("5. Changing Language")
print("------------------------------")
success = wiki_language("es")
if success:
summary = wiki_summary("Python (lenguaje de programación)", sentences=1)
print("Spanish summary:")
print(summary)
print()
print("\n==================================================")
print("Demonstration Complete")
print("==================================================")
+498
View File
@@ -0,0 +1,498 @@
"""Tools for working with XML files.
Usage:
from praisonaiagents.tools import xml_tools
tree = xml_tools.read_xml("data.xml")
or
from praisonaiagents.tools import read_xml, write_xml, transform_xml
tree = read_xml("data.xml")
"""
import logging
from typing import List, Dict, Union, Optional, Any, Tuple
from importlib import util
import xml.etree.ElementTree as ET
import xml.dom.minidom as minidom
from io import StringIO
import json
class XMLTools:
"""Tools for working with XML files."""
def __init__(self):
"""Initialize XMLTools."""
pass
def read_xml(
self,
filepath: str,
encoding: str = 'utf-8',
validate_schema: Optional[str] = None,
parser: str = 'lxml'
) -> ET.Element:
"""Read an XML file with optional schema validation.
Args:
filepath: Path to XML file
encoding: File encoding
validate_schema: Optional path to XSD schema file
parser: XML parser to use ('lxml' or 'etree')
Returns:
ElementTree root element
"""
try:
if parser == 'lxml':
if util.find_spec('lxml') is None:
error_msg = "lxml package is not available. Please install it using: pip install lxml"
logging.error(error_msg)
return None
import lxml.etree as lxml_etree
tree = lxml_etree.parse(filepath)
root = tree.getroot()
else:
tree = ET.parse(filepath)
root = tree.getroot()
if validate_schema:
if util.find_spec('xmlschema') is None:
error_msg = "xmlschema package is not available. Please install it using: pip install xmlschema"
logging.error(error_msg)
return None
import xmlschema
schema = xmlschema.XMLSchema(validate_schema)
if not schema.is_valid(filepath):
error_msg = f"XML file does not validate against schema: {schema.validate(filepath)}"
logging.error(error_msg)
return None
return root
except Exception as e:
error_msg = f"Error reading XML file {filepath}: {str(e)}"
logging.error(error_msg)
return None
def write_xml(
self,
root: ET.Element,
filepath: str,
encoding: str = 'utf-8',
pretty: bool = True,
xml_declaration: bool = True
) -> bool:
"""Write XML Element tree to file.
Args:
root: XML Element tree root
filepath: Output file path
encoding: File encoding
pretty: Format output with proper indentation
xml_declaration: Include XML declaration
Returns:
True if successful, False otherwise
"""
try:
# Convert to string
if pretty:
xml_str = minidom.parseString(
ET.tostring(root, encoding='unicode')
).toprettyxml(indent=' ')
else:
xml_str = ET.tostring(
root,
encoding='unicode'
)
# Add declaration if requested
if xml_declaration:
if not xml_str.startswith('<?xml'):
xml_str = (
f'<?xml version="1.0" encoding="{encoding}"?>\n'
+ xml_str
)
# Write to file
with open(filepath, 'w', encoding=encoding) as f:
f.write(xml_str)
return True
except Exception as e:
error_msg = f"Error writing XML file {filepath}: {str(e)}"
logging.error(error_msg)
return False
def transform_xml(
self,
xml_file: str,
xslt_file: str,
output_file: str,
params: Optional[Dict[str, str]] = None
) -> bool:
"""Transform XML using XSLT stylesheet.
Args:
xml_file: Input XML file
xslt_file: XSLT stylesheet file
output_file: Output file path
params: Optional parameters for transformation
Returns:
True if successful, False otherwise
"""
try:
# Parse XML and XSLT
if util.find_spec('lxml') is None:
error_msg = "lxml package is not available. Please install it using: pip install lxml"
logging.error(error_msg)
return False
import lxml.etree as lxml_etree
xml_doc = lxml_etree.parse(xml_file)
xslt_doc = lxml_etree.parse(xslt_file)
transform = lxml_etree.XSLT(xslt_doc)
# Apply transformation
if params:
result = transform(xml_doc, **params)
else:
result = transform(xml_doc)
# Write result
result.write(
output_file,
pretty_print=True,
xml_declaration=True,
encoding='utf-8'
)
return True
except Exception as e:
error_msg = f"Error transforming XML: {str(e)}"
logging.error(error_msg)
return False
def validate_xml(
self,
xml_file: str,
schema_file: str
) -> Tuple[bool, Optional[str]]:
"""Validate XML against XSD schema.
Args:
xml_file: XML file to validate
schema_file: XSD schema file
Returns:
Tuple of (is_valid, error_message)
"""
try:
if util.find_spec('xmlschema') is None:
error_msg = "xmlschema package is not available. Please install it using: pip install xmlschema"
logging.error(error_msg)
return False, error_msg
import xmlschema
schema = xmlschema.XMLSchema(schema_file)
schema.validate(xml_file)
return True, None
except xmlschema.validators.exceptions.XMLSchemaValidationError as e:
return False, str(e)
except Exception as e:
error_msg = f"Error validating XML: {str(e)}"
logging.error(error_msg)
return False, error_msg
def xml_to_dict(
self,
root: Union[str, ET.Element],
preserve_attrs: bool = True
) -> Dict[str, Any]:
"""Convert XML to dictionary.
Args:
root: XML string or Element tree root
preserve_attrs: Keep XML attributes in result
Returns:
Dict representation of XML
"""
try:
# Parse XML if string
if isinstance(root, str):
if root.startswith('<'):
root = ET.fromstring(root)
else:
root = ET.parse(root).getroot()
result = {}
# Add attributes if present and requested
if preserve_attrs and root.attrib:
result['@attributes'] = dict(root.attrib)
# Add children
children = list(root)
if not children:
text = root.text
if text is not None and text.strip():
result = text.strip()
else:
for child in children:
child_data = self.xml_to_dict(child, preserve_attrs)
if child.tag in result:
if not isinstance(result[child.tag], list):
result[child.tag] = [result[child.tag]]
result[child.tag].append(child_data)
else:
result[child.tag] = child_data
return result
except Exception as e:
error_msg = f"Error converting XML to dict: {str(e)}"
logging.error(error_msg)
return {}
def dict_to_xml(
self,
data: Dict[str, Any],
root_tag: str = 'root'
) -> Optional[ET.Element]:
"""Convert dictionary to XML.
Args:
data: Dictionary to convert
root_tag: Tag for root element
Returns:
XML Element tree root
"""
try:
def _create_element(
parent: ET.Element,
key: str,
value: Any
):
"""Create XML element from key-value pair."""
if key == '@attributes':
for attr_key, attr_val in value.items():
parent.set(attr_key, str(attr_val))
elif isinstance(value, dict):
child = ET.SubElement(parent, key)
for k, v in value.items():
_create_element(child, k, v)
elif isinstance(value, list):
for item in value:
child = ET.SubElement(parent, key)
if isinstance(item, dict):
for k, v in item.items():
_create_element(child, k, v)
else:
child.text = str(item)
else:
child = ET.SubElement(parent, key)
child.text = str(value)
root = ET.Element(root_tag)
for key, value in data.items():
_create_element(root, key, value)
return root
except Exception as e:
error_msg = f"Error converting dict to XML: {str(e)}"
logging.error(error_msg)
return None
def xpath_query(
self,
root: Union[str, ET.Element],
query: str,
namespaces: Optional[Dict[str, str]] = None
) -> List[ET.Element]:
"""Execute XPath query on XML.
Args:
root: XML string or Element tree root
query: XPath query string
namespaces: Optional namespace mappings
Returns:
List of matching elements
"""
try:
# Parse XML if string
if isinstance(root, str):
if root.startswith('<'):
if util.find_spec('lxml') is None:
error_msg = "lxml package is not available. Please install it using: pip install lxml"
logging.error(error_msg)
return []
import lxml.etree as lxml_etree
tree = lxml_etree.fromstring(root)
else:
tree = ET.parse(root)
else:
if util.find_spec('lxml') is None:
error_msg = "lxml package is not available. Please install it using: pip install lxml"
logging.error(error_msg)
return []
import lxml.etree as lxml_etree
tree = lxml_etree.fromstring(
ET.tostring(root, encoding='unicode')
)
# Execute query
results = tree.xpath(
query,
namespaces=namespaces or {}
)
# Convert results to standard ElementTree elements
return [
ET.fromstring(lxml_etree.tostring(elem, encoding='unicode'))
for elem in results
]
except Exception as e:
error_msg = f"Error executing XPath query: {str(e)}"
logging.error(error_msg)
return []
# Create instance for direct function access
_xml_tools = XMLTools()
read_xml = _xml_tools.read_xml
write_xml = _xml_tools.write_xml
transform_xml = _xml_tools.transform_xml
validate_xml = _xml_tools.validate_xml
xml_to_dict = _xml_tools.xml_to_dict
dict_to_xml = _xml_tools.dict_to_xml
xpath_query = _xml_tools.xpath_query
if __name__ == "__main__":
print("\n==================================================")
print("XMLTools Demonstration")
print("==================================================\n")
# Create temporary files
import tempfile
import os
temp_file = tempfile.mktemp(suffix='.xml')
try:
print("1. Creating XML Document")
print("------------------------------")
xml_content = """<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="fiction">
<title>The Great Gatsby</title>
<author>F. Scott Fitzgerald</author>
<year>1925</year>
<price>10.99</price>
</book>
<book category="non-fiction">
<title>A Brief History of Time</title>
<author>Stephen Hawking</author>
<year>1988</year>
<price>15.99</price>
</book>
</bookstore>"""
with open(temp_file, 'w') as f:
f.write(xml_content)
print("Sample XML file created")
print()
print("2. Parsing XML")
print("------------------------------")
result = read_xml(temp_file)
print("XML structure:")
print(minidom.parseString(ET.tostring(result, encoding='unicode')).toprettyxml(indent=' '))
print()
print("3. Querying XML")
print("------------------------------")
xpath = "//book[@category='fiction']/title/text()"
result = xpath_query(result, xpath)
print(f"Fiction book titles (XPath: {xpath}):")
for title in result:
print(title.text)
print()
print("4. Validating XML")
print("------------------------------")
schema_file = tempfile.mktemp(suffix='.xsd')
schema_content = """<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="bookstore">
<xs:complexType>
<xs:sequence>
<xs:element name="book" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="title" type="xs:string"/>
<xs:element name="author" type="xs:string"/>
<xs:element name="year" type="xs:integer"/>
<xs:element name="price" type="xs:decimal"/>
</xs:sequence>
<xs:attribute name="category" type="xs:string"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>"""
with open(schema_file, 'w') as f:
f.write(schema_content)
result, error = validate_xml(temp_file, schema_file)
print(f"XML validation result: {result}")
if error:
print(f"Error: {error}")
print()
print("5. Transforming XML")
print("------------------------------")
xslt_content = """<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<html>
<body>
<h2>Bookstore Inventory</h2>
<table>
<tr>
<th>Title</th>
<th>Author</th>
<th>Price</th>
</tr>
<xsl:for-each select="bookstore/book">
<tr>
<td><xsl:value-of select="title"/></td>
<td><xsl:value-of select="author"/></td>
<td><xsl:value-of select="price"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>"""
xslt_file = tempfile.mktemp(suffix='.xslt')
with open(xslt_file, 'w') as f:
f.write(xslt_content)
output_file = tempfile.mktemp(suffix='.html')
result = transform_xml(temp_file, xslt_file, output_file)
print(f"XML transformation result: {result}")
if result and os.path.exists(output_file):
print("\nTransformed HTML content:")
with open(output_file, 'r') as f:
print(f.read())
finally:
# Clean up temporary files
for file in [temp_file, schema_file, xslt_file, output_file]:
if os.path.exists(file):
os.unlink(file)
print("\n==================================================")
print("Demonstration Complete")
print("==================================================")
+417
View File
@@ -0,0 +1,417 @@
"""Tools for working with YAML files.
Usage:
from praisonaiagents.tools import yaml_tools
data = yaml_tools.read_yaml("config.yaml")
or
from praisonaiagents.tools import read_yaml, write_yaml, merge_yaml
data = read_yaml("config.yaml")
"""
import logging
from typing import List, Dict, Union, Optional, Any
from importlib import util
import os
from copy import deepcopy
class YAMLTools:
"""Tools for working with YAML files."""
def __init__(self):
"""Initialize YAMLTools."""
self._check_dependencies()
def _check_dependencies(self):
"""Check if required packages are installed."""
missing = []
for package, module in [('pyyaml', 'yaml'), ('jsonschema', 'jsonschema')]:
if util.find_spec(module) is None:
missing.append(package)
if missing:
raise ImportError(
f"Required packages not available. Please install: {', '.join(missing)}\n"
f"Run: pip install {' '.join(missing)}"
)
def read_yaml(
self,
filepath: str,
safe_load: bool = True,
encoding: str = 'utf-8'
) -> Optional[Union[Dict[str, Any], List[Any]]]:
"""Read a YAML file."""
try:
if util.find_spec('yaml') is None:
error_msg = "pyyaml package is not available. Please install it using: pip install pyyaml"
logging.error(error_msg)
return None
# Import yaml only when needed
import yaml
with open(filepath, 'r', encoding=encoding) as f:
if safe_load:
return yaml.safe_load(f)
return yaml.load(f, Loader=yaml.FullLoader)
except Exception as e:
error_msg = f"Error reading YAML file {filepath}: {str(e)}"
logging.error(error_msg)
return None
def write_yaml(
self,
data: Union[Dict[str, Any], List[Any]],
filepath: str,
default_flow_style: bool = False,
encoding: str = 'utf-8',
sort_keys: bool = False,
allow_unicode: bool = True
) -> bool:
"""Write data to YAML file."""
try:
if util.find_spec('yaml') is None:
error_msg = "pyyaml package is not available. Please install it using: pip install pyyaml"
logging.error(error_msg)
return False
# Import yaml only when needed
import yaml
with open(filepath, 'w', encoding=encoding) as f:
yaml.dump(
data,
f,
default_flow_style=default_flow_style,
sort_keys=sort_keys,
allow_unicode=allow_unicode
)
return True
except Exception as e:
error_msg = f"Error writing YAML file {filepath}: {str(e)}"
logging.error(error_msg)
return False
def merge_yaml(
self,
files: List[str],
output_file: Optional[str] = None,
strategy: str = 'deep'
) -> Union[Dict[str, Any], List[Any], None]:
"""Merge multiple YAML files."""
try:
if util.find_spec('yaml') is None:
error_msg = "pyyaml package is not available. Please install it using: pip install pyyaml"
logging.error(error_msg)
return None
def deep_merge(source: dict, destination: dict) -> dict:
"""Deep merge two dictionaries."""
for key, value in source.items():
if key in destination:
if isinstance(value, dict) and isinstance(destination[key], dict):
destination[key] = deep_merge(value, destination[key])
elif isinstance(value, list) and isinstance(destination[key], list):
destination[key].extend(value)
else:
destination[key] = value
else:
destination[key] = value
return destination
result = {}
for file in files:
data = self.read_yaml(file)
if data is None:
continue
if strategy == 'deep':
if isinstance(data, dict):
result = deep_merge(data, result)
elif isinstance(data, list):
if not result:
result = []
result.extend(data)
else: # shallow
if isinstance(data, dict):
result.update(data)
elif isinstance(data, list):
if not result:
result = []
result.extend(data)
if output_file:
self.write_yaml(result, output_file)
return result
except Exception as e:
error_msg = f"Error merging YAML files: {str(e)}"
logging.error(error_msg)
return None
def validate_yaml(
self,
data: Union[Dict[str, Any], List[Any], str],
schema: Dict[str, Any]
) -> bool:
"""Validate YAML data against a schema."""
try:
if util.find_spec('jsonschema') is None:
error_msg = "jsonschema package is not available. Please install it using: pip install jsonschema"
logging.error(error_msg)
return False
# Import jsonschema only when needed
from jsonschema import validate, ValidationError
# Load data if file path
if isinstance(data, str):
data = self.read_yaml(data)
if data is None:
return False
try:
validate(instance=data, schema=schema)
return True
except ValidationError as e:
logging.error(f"YAML validation error: {str(e)}")
return False
except Exception as e:
error_msg = f"Error validating YAML: {str(e)}"
logging.error(error_msg)
return False
def analyze_yaml(
self,
data: Union[Dict[str, Any], List[Any], str]
) -> Optional[Dict[str, Any]]:
"""Analyze YAML data structure."""
try:
# Load data if file path
if isinstance(data, str):
data = self.read_yaml(data)
if data is None:
return None
def analyze_value(value: Any) -> Dict[str, Any]:
"""Analyze a single value."""
result = {
'type': type(value).__name__,
'size': len(value) if hasattr(value, '__len__') else 1
}
if isinstance(value, dict):
result['keys'] = list(value.keys())
result['nested_types'] = {
k: type(v).__name__
for k, v in value.items()
}
elif isinstance(value, list):
result['element_types'] = list(set(
type(x).__name__ for x in value
))
return result
result = {
'structure': analyze_value(data),
'stats': {
'total_keys': sum(1 for _ in self._walk_dict(data))
if isinstance(data, dict) else len(data)
}
}
return result
except Exception as e:
error_msg = f"Error analyzing YAML: {str(e)}"
logging.error(error_msg)
return None
def _walk_dict(self, d: Dict[str, Any]) -> Any:
"""Walk through nested dictionary."""
for k, v in d.items():
yield k
if isinstance(v, dict):
yield from self._walk_dict(v)
def transform_yaml(
self,
data: Union[Dict[str, Any], List[Any], str],
operations: List[Dict[str, Any]]
) -> Optional[Union[Dict[str, Any], List[Any]]]:
"""Transform YAML data using specified operations."""
try:
# Load data if file path
if isinstance(data, str):
data = self.read_yaml(data)
if data is None:
return None
result = deepcopy(data)
for op in operations:
op_type = op.get('type')
path = op.get('path', '')
value = op.get('value')
if not op_type:
continue
# Split path into parts
parts = [p for p in path.split('/') if p]
# Get reference to target location
target = result
parent = None
last_key = None
for part in parts[:-1]:
if isinstance(target, dict):
if part not in target:
target[part] = {}
parent = target
target = target[part]
last_key = part
elif isinstance(target, list):
idx = int(part)
while len(target) <= idx:
target.append({})
parent = target
target = target[idx]
last_key = idx
if parts:
last_part = parts[-1]
if isinstance(target, dict):
if op_type == 'set':
target[last_part] = value
elif op_type == 'delete':
target.pop(last_part, None)
elif op_type == 'append' and isinstance(target.get(last_part), list):
target[last_part].append(value)
elif isinstance(target, list):
idx = int(last_part)
if op_type == 'set':
while len(target) <= idx:
target.append(None)
target[idx] = value
elif op_type == 'delete' and idx < len(target):
del target[idx]
elif op_type == 'append':
target.append(value)
else:
if op_type == 'set':
result = value
elif op_type == 'append' and isinstance(result, list):
result.append(value)
return result
except Exception as e:
error_msg = f"Error transforming YAML: {str(e)}"
logging.error(error_msg)
return None
# Create instance for direct function access
_yaml_tools = YAMLTools()
read_yaml = _yaml_tools.read_yaml
write_yaml = _yaml_tools.write_yaml
merge_yaml = _yaml_tools.merge_yaml
validate_yaml = _yaml_tools.validate_yaml
analyze_yaml = _yaml_tools.analyze_yaml
transform_yaml = _yaml_tools.transform_yaml
if __name__ == "__main__":
# Example usage
print("\n==================================================")
print("YAMLTools Demonstration")
print("==================================================\n")
# Sample data
data1 = {
'server': {
'host': 'localhost',
'port': 8080,
'debug': True
},
'database': {
'url': 'postgresql://localhost:5432/db',
'pool_size': 5
}
}
data2 = {
'server': {
'workers': 4,
'timeout': 30
},
'logging': {
'level': 'INFO',
'file': 'app.log'
}
}
# 1. Write YAML files
print("1. Writing YAML Files")
print("------------------------------")
success1 = write_yaml(data1, 'config1.yaml')
success2 = write_yaml(data2, 'config2.yaml')
print(f"First file written: {success1}")
print(f"Second file written: {success2}")
print()
# 2. Read YAML file
print("2. Reading YAML File")
print("------------------------------")
config = read_yaml('config1.yaml')
print("YAML content:")
print(config)
print()
# 3. Merge YAML files
print("3. Merging YAML Files")
print("------------------------------")
merged = merge_yaml(['config1.yaml', 'config2.yaml'])
print("Merged content:")
print(merged)
print()
# 4. Analyze YAML
print("4. Analyzing YAML")
print("------------------------------")
analysis = analyze_yaml(merged)
print("Analysis results:")
print(analysis)
print()
# 5. Transform YAML
print("5. Transforming YAML")
print("------------------------------")
operations = [
{
'type': 'set',
'path': 'server/host',
'value': '0.0.0.0'
},
{
'type': 'delete',
'path': 'server/debug'
},
{
'type': 'set',
'path': 'logging/handlers',
'value': ['console', 'file']
}
]
transformed = transform_yaml(merged, operations)
print("Transformed content:")
print(transformed)
print("\n==================================================")
print("Demonstration Complete")
print("==================================================")
# Cleanup
for file in ['config1.yaml', 'config2.yaml']:
if os.path.exists(file):
os.remove(file)
@@ -0,0 +1,213 @@
"""YFinance tools for stock market data.
Usage:
from praisonaiagents.tools import get_stock_price, get_stock_info
price = get_stock_price("AAPL")
info = get_stock_info("AAPL")
or
from praisonaiagents.tools import yfinance
price = yfinance.get_stock_price("AAPL")
info = yfinance.get_stock_info("AAPL")
"""
from typing import List, Dict, Optional, Any
import logging
from importlib import util
from datetime import datetime
class YFinanceTools:
"""A comprehensive tool for financial data analysis using yfinance"""
def __init__(self):
"""Initialize YFinanceTools"""
self._tickers = {}
def _get_yfinance(self):
"""Get yfinance module, installing if needed"""
if util.find_spec('yfinance') is None:
error_msg = "yfinance package is not available. Please install it using: pip install yfinance"
logging.error(error_msg)
return None
import yfinance as yf
return yf
def _get_ticker(self, symbol: str):
"""Get or create ticker instance"""
if symbol not in self._tickers:
yf = self._get_yfinance()
if yf is None:
return None
self._tickers[symbol] = yf.Ticker(symbol)
return self._tickers[symbol]
def get_stock_price(self, symbol: str) -> Dict[str, float]:
"""
Get current stock price and related price metrics
Args:
symbol (str): Stock ticker symbol
Returns:
Dict[str, float]: Current price information
"""
try:
ticker = self._get_ticker(symbol)
if ticker is None:
return {"error": "yfinance package not available"}
info = ticker.info
return {
'symbol': symbol,
'price': info.get('regularMarketPrice', 0.0),
'open': info.get('regularMarketOpen', 0.0),
'high': info.get('regularMarketDayHigh', 0.0),
'low': info.get('regularMarketDayLow', 0.0),
'volume': info.get('regularMarketVolume', 0),
'previous_close': info.get('regularMarketPreviousClose', 0.0),
'change': info.get('regularMarketChange', 0.0),
'change_percent': info.get('regularMarketChangePercent', 0.0)
}
except Exception as e:
error_msg = f"Error getting stock price for {symbol}: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def get_stock_info(self, symbol: str) -> Dict:
"""
Get detailed information about a stock
Args:
symbol (str): Stock ticker symbol
Returns:
Dict: Stock information including company details and key metrics
"""
try:
ticker = self._get_ticker(symbol)
if ticker is None:
return {"error": "yfinance package not available"}
info = ticker.info
relevant_info = {
"longName": info.get("longName"),
"symbol": info.get("symbol"),
"sector": info.get("sector"),
"industry": info.get("industry"),
"country": info.get("country"),
"marketCap": info.get("marketCap"),
"currentPrice": info.get("currentPrice"),
"currency": info.get("currency"),
"exchange": info.get("exchange"),
"fiftyTwoWeekHigh": info.get("fiftyTwoWeekHigh"),
"fiftyTwoWeekLow": info.get("fiftyTwoWeekLow"),
"trailingPE": info.get("trailingPE"),
"forwardPE": info.get("forwardPE"),
"dividendYield": info.get("dividendYield"),
"beta": info.get("beta"),
"volume": info.get("volume"),
"averageVolume": info.get("averageVolume"),
"sharesOutstanding": info.get("sharesOutstanding"),
"website": info.get("website"),
"longBusinessSummary": info.get("longBusinessSummary")
}
return {k: v for k, v in relevant_info.items() if v is not None}
except Exception as e:
error_msg = f"Error fetching stock info: {str(e)}"
logging.error(error_msg)
return {"error": error_msg}
def get_historical_data(
self,
symbol: str,
period: str = "1y",
interval: str = "1d",
start: Optional[datetime] = None,
end: Optional[datetime] = None
) -> List[Dict[str, Any]]:
"""
Get historical price data for a stock
Args:
symbol (str): Stock ticker symbol
period (str): Data period e.g. 1d, 5d, 1mo, 3mo, 6mo, 1y, 2y, 5y, 10y, ytd, max
interval (str): Data interval e.g. 1m, 2m, 5m, 15m, 30m, 60m, 90m, 1h, 1d, 5d, 1wk, 1mo, 3mo
start (datetime, optional): Start date for data
end (datetime, optional): End date for data
Returns:
List[Dict[str, Any]]: List of historical price data points
"""
try:
ticker = self._get_ticker(symbol)
if ticker is None:
return [{"error": "yfinance package not available"}]
hist = ticker.history(period=period, interval=interval, start=start, end=end)
return hist.reset_index().to_dict('records')
except Exception as e:
error_msg = f"Error fetching historical data: {str(e)}"
logging.error(error_msg)
return [{"error": error_msg}]
if __name__ == "__main__":
# Example usage
print("\n==================================================")
print("YFinanceTools Demonstration")
print("==================================================\n")
try:
yf_tools = YFinanceTools()
# 1. Basic Stock Information
print("1. Basic Stock Information for AAPL")
print("------------------------------")
info = yf_tools.get_stock_info("AAPL")
if "error" not in info:
print(f"Company: {info.get('longName')}")
print(f"Sector: {info.get('sector')}")
print(f"Industry: {info.get('industry')}")
print(f"Country: {info.get('country')}\n")
else:
print(f"Error: {info['error']}\n")
# 2. Current Price Data
print("2. Current Price Data")
print("------------------------------")
price_data = yf_tools.get_stock_price("AAPL")
if "error" not in price_data:
print(f"Current Price: ${price_data.get('price', 'N/A')}")
print(f"Previous Close: ${price_data.get('previous_close', 'N/A')}")
print(f"Day Range: ${price_data.get('low', 'N/A')} - ${price_data.get('high', 'N/A')}\n")
else:
print(f"Error: {price_data['error']}\n")
# 3. Historical Data
print("3. Historical Data (Last 5 Days)")
print("------------------------------")
hist_data = yf_tools.get_historical_data("AAPL", period="5d")
if not any("error" in d for d in hist_data):
for day in hist_data:
print(f"Date: {day.get('Date')}, Close: ${day.get('Close', 'N/A'):.2f}")
print()
else:
print(f"{hist_data[0]['error']}\n")
# 4. Multi-Stock Example
print("5. Multi-Stock Current Prices")
print("------------------------------")
for symbol in ["AAPL", "MSFT", "GOOGL"]:
price_data = yf_tools.get_stock_price(symbol)
if "error" not in price_data:
print(f"{symbol}: ${price_data.get('price', 'N/A')}")
else:
print(f"{symbol}: Error fetching price")
print()
except Exception as e:
print(f"Error: {str(e)}")
print("==================================================")
print("Analysis Complete")
print("==================================================")
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.0.13"
version = "0.0.27"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
authors = [
{ name="Mervin Praison" }
+56
View File
@@ -0,0 +1,56 @@
from praisonaiagents import Agent, Task, PraisonAIAgents, Tools
from pydantic import BaseModel
class AnalysisReport(BaseModel):
title: str
findings: str
summary: str
# Create a researcher agent
researcher = Agent(
name="AIResearcher",
role="Technology Research Analyst",
goal="Analyze and structure information about AI developments",
backstory="Expert analyst specializing in AI technology trends",
verbose=True,
llm="gpt-4o-mini",
tools=[Tools.internet_search],
self_reflect=False
)
# Create an analyst agent
analyst = Agent(
name="DataAnalyst",
role="Data Insights Specialist",
goal="Structure and analyze research findings",
backstory="Senior data analyst with expertise in pattern recognition",
verbose=True,
llm="gpt-4o-mini",
self_reflect=False
)
# Define structured tasks
research_task = Task(
name="gather_research",
description="Research recent AI developments in 2024",
agent=researcher,
expected_output="Research findings"
)
analysis_task = Task(
name="analyze_findings",
description="Analyze research findings and create structured report. No additional text or explanation.",
agent=analyst,
output_json=AnalysisReport,
expected_output="JSON object"
)
# Initialize and run agents
agents = PraisonAIAgents(
agents=[researcher, analyst],
tasks=[research_task, analysis_task],
process="sequential",
verbose=True
)
result = agents.start()
print(result)
+147
View File
@@ -0,0 +1,147 @@
from typing import List, Dict
from pydantic import BaseModel
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.main import TaskOutput
# Pydantic models for structured output
class Chapter(BaseModel):
chapter_number: int
title: str
outline: str
class StoryOutline(BaseModel):
title: str
total_chapters: int
chapters: List[Chapter]
class ChapterContent(BaseModel):
chapter_number: int
title: str
content: str
# Create the story planner agent
planner_agent = Agent(
name="Story Planner",
role="Creative Writing Architect",
goal="Create detailed story outlines with chapter breakdowns",
backstory="Expert storyteller skilled in narrative structure and plot development",
verbose=True,
llm="gpt-4o-mini",
self_reflect=False
)
# Create the writer agent
writer_agent = Agent(
name="Story Writer",
role="Creative Writer",
goal="Write engaging and cohesive story chapters",
backstory="Experienced writer skilled in bringing stories to life with vivid details and engaging narrative",
verbose=True,
llm="gpt-4o-mini",
self_reflect=False
)
# Story title
story_title = "The Curious Cat"
# Step 1: Create outline task
outline_task = Task(
name="story_outline",
description=f"""Create a detailed story outline for a story titled '{story_title}'.
The outline should include:
1. A compelling story arc
2. Clear chapter breakdown
3. Brief description of what happens in each chapter
4. Character introductions and development points
Provide the response in this exact JSON format:
{{
"title": "Story title",
"total_chapters": number,
"chapters": [
{{
"chapter_number": 1,
"title": "Chapter title",
"outline": "Detailed chapter outline"
}}
]
}}
""",
expected_output="Complete story outline",
agent=planner_agent,
output_pydantic=StoryOutline
)
# Create initial workflow for outline
outline_workflow = PraisonAIAgents(
agents=[planner_agent],
tasks=[outline_task],
verbose=True,
process="sequential"
)
# Get the outline
outline_result = outline_workflow.start()
story_outline = outline_result["task_results"][0].pydantic
# Step 2: Create tasks for each chapter
chapter_tasks = []
chapters_content = []
# Create a task for each chapter
for chapter in story_outline.chapters:
chapter_task = Task(
name=f"write_chapter_{chapter.chapter_number}",
description=f"""Write chapter {chapter.chapter_number}: {chapter.title}
Chapter outline: {chapter.outline}
Write a complete, engaging chapter following this outline.
Return in this exact JSON format:
{{
"chapter_number": {chapter.chapter_number},
"title": "{chapter.title}",
"content": "The full chapter content..."
}}
""",
expected_output="Complete chapter content",
agent=writer_agent,
output_pydantic=ChapterContent,
context=[outline_task]
)
chapter_tasks.append(chapter_task)
# Create sequential workflow for chapters
chapter_workflow = PraisonAIAgents(
agents=[writer_agent],
tasks=chapter_tasks,
verbose=True,
process="sequential"
)
# Run the chapter workflow
chapter_results = chapter_workflow.start()
# Collect and validate all chapter contents
for task_result in chapter_results["task_results"].values():
if task_result and task_result.pydantic:
chapters_content.append(task_result.pydantic)
# Sort chapters by chapter number
chapters_content.sort(key=lambda x: x.chapter_number)
# Format final result
story = {
"title": story_outline.title,
"outline": story_outline.model_dump(),
"chapters": [chapter.model_dump() for chapter in chapters_content]
}
# Print results
print("\n=== Story Generation Results ===")
print(f"\nTitle: {story['title']}")
print("\nOutline:")
print(story['outline'])
print("\nChapters:")
for chapter in story['chapters']:
print(f"\nChapter {chapter['chapter_number']}: {chapter['title']}")
print(chapter['content'][:200] + "...") # Print first 200 chars of each chapter
+30
View File
@@ -0,0 +1,30 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import get_article, get_news_sources, get_articles_from_source, get_trending_topics
# Create Wikipedia agent
news_agent = Agent(
name="NewsAgent",
role="News Analyst",
goal="Collect and analyze news articles from various sources.",
backstory="Expert in news gathering and content analysis.",
tools=[get_article, get_news_sources, get_articles_from_source, get_trending_topics],
self_reflect=False
)
# Define research task
news_task = Task(
description="Analyze news articles about 'AI developments' from major tech news sources.",
expected_output="Summary of key AI developments with source articles.",
agent=news_agent,
name="ai_news"
)
# Run agent
agents = PraisonAIAgents(
agents=[news_agent],
tasks=[news_task],
process="sequential"
)
agents.start()
+2 -2
View File
@@ -186,7 +186,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.13"
version = "0.0.27"
source = { editable = "." }
dependencies = [
{ name = "openai" },
@@ -304,7 +304,7 @@ name = "tqdm"
version = "4.67.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "platform_system == 'Windows'" },
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a8/4b/29b4ef32e036bb34e4ab51796dd745cdba7ed47ad142a9f4a1eb8e0c744d/tqdm-4.67.1.tar.gz", hash = "sha256:f8aef9c52c08c13a65f30ea34f4e5aac3fd1a34959879d7e59e63027286627f2", size = 169737 }
wheels = [
+74
View File
@@ -0,0 +1,74 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
from typing import List, Dict
from duckduckgo_search import DDGS
# 1. Tool
def internet_search_tool(query: str) -> List[Dict]:
"""
Perform a search using DuckDuckGo.
Args:
query (str): The search query.
Returns:
list: A list of search result titles, URLs, and snippets.
"""
try:
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=10):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return results
except Exception as e:
print(f"Error during DuckDuckGo search: {e}")
return []
# 2. Agent
data_agent = Agent(
name="DataCollector",
role="Search Specialist",
goal="Perform internet searches to collect relevant information.",
backstory="Expert in finding and organising internet data.",
tools=[internet_search_tool],
self_reflect=False
)
# 3. Tasks
collect_task = Task(
description="Perform an internet search using the query: 'AI job trends in 2024'. Return results as a list of title, URL, and snippet.",
expected_output="List of search results with titles, URLs, and snippets.",
agent=data_agent,
name="collect_data",
is_start=True,
next_tasks=["validate_data"]
)
validate_task = Task(
description="""Validate the collected data. Check if:
1. At least 5 results are returned.
2. Each result contains a title and a URL.
Return validation_result as 'valid' or 'invalid' only no other text.""",
expected_output="Validation result indicating if data is valid or invalid.",
agent=data_agent,
name="validate_data",
task_type="decision",
condition={
"valid": [], # End the workflow on valid data
"invalid": ["collect_data"] # Retry data collection on invalid data
},
)
# 4. AI Agents Workflow
agents = PraisonAIAgents(
agents=[data_agent],
tasks=[collect_task, validate_task],
verbose=1,
process="workflow"
)
agents.start()
+253
View File
@@ -0,0 +1,253 @@
from praisonaiagents import Agent, Task, PraisonAIAgents
import random
from typing import List, Dict, Union
import json
from pydantic import BaseModel
# Add Pydantic models for data validation
class Person(BaseModel):
name: str
age: int
job: str
city: str
salary: int
class ProcessedPerson(Person):
salary_category: str
age_group: str
processing_status: str
class DataList(BaseModel):
items: List[Dict]
class ValidationResult(BaseModel):
validation_result: str
details: str = ""
def random_data_of_individuals() -> List[Dict]:
"""Generate random individual data"""
names = ["John", "Jane", "Mike", "Sarah", "David", "Emma"]
jobs = ["Engineer", "Doctor", "Teacher", "Artist", "Developer"]
cities = ["New York", "London", "Tokyo", "Paris", "Berlin"]
data = []
for _ in range(random.randint(3, 7)):
person = {
"name": random.choice(names),
"age": random.randint(25, 65),
"job": random.choice(jobs),
"city": random.choice(cities),
"salary": random.randint(30000, 120000)
}
data.append(person)
return data
def process_data_of_individuals(data: Union[List[Dict], Dict, str]) -> Dict:
"""Process individual data by adding categories and analysis"""
try:
print("\n[DEBUG] process_data_of_individuals input:", data)
print("[DEBUG] Current workflow state:")
print("- total_records:", workflow.get_state("total_records"))
print("- current_index:", workflow.get_state("current_index"))
print("- remaining:", workflow.get_state("remaining"))
# Get the items list from the collect_task result
collect_result = None
for task in workflow.tasks.values():
if task.name == "collect_data" and task.result:
try:
collect_data = json.loads(task.result.raw)
collect_result = collect_data.get("items", [])
print("[DEBUG] Found collect_data items:", len(collect_result))
except:
print("[DEBUG] Failed to parse collect_data result")
# Handle string input by trying to parse it as JSON
if isinstance(data, str):
if ":" in data and not data.strip().startswith("{"):
# Convert string format to dictionary
pairs = [pair.strip() for pair in data.split(",")]
data_dict = {}
for pair in pairs:
key, value = pair.split(":")
key = key.strip().lower()
value = value.strip()
if key == "age" or key == "salary":
value = int(value)
data_dict[key] = value
data = data_dict
else:
data = json.loads(data)
print("[DEBUG] Parsed data:", data)
# Handle single record
if isinstance(data, dict):
person = data
# Initialize total records if not set
total_records = workflow.get_state("total_records")
if total_records is None and collect_result:
total_records = len(collect_result)
workflow.set_state("total_records", total_records)
print(f"[DEBUG] Initialized total_records to {total_records}")
current_index = workflow.get_state("current_index", 0)
total_records = total_records or 1
remaining = total_records - (current_index + 1)
workflow.set_state("remaining", remaining)
print(f"[DEBUG] Processing record {current_index + 1}/{total_records}")
elif isinstance(data, list):
if len(data) == 0:
raise ValueError("Empty data list")
person = data[0]
workflow.set_state("total_records", len(data))
workflow.set_state("current_index", 0)
workflow.set_state("remaining", len(data) - 1)
print(f"[DEBUG] First record from list of {len(data)} items")
else:
raise ValueError("Input must be a dictionary or list with at least one record")
processed_person = person.copy()
# Add salary category
salary = person.get("salary", 0)
if salary < 50000:
processed_person["salary_category"] = "entry"
elif salary < 90000:
processed_person["salary_category"] = "mid"
else:
processed_person["salary_category"] = "senior"
# Add age group
age = person.get("age", 0)
if age < 35:
processed_person["age_group"] = "young"
elif age < 50:
processed_person["age_group"] = "mid"
else:
processed_person["age_group"] = "senior"
# Add processing status using workflow state
remaining = workflow.get_state("remaining", 0)
current_index = workflow.get_state("current_index", 0)
total_records = workflow.get_state("total_records", 1)
# Update current index for next iteration
workflow.set_state("current_index", current_index + 1)
print(f"[DEBUG] Status check - remaining: {remaining}, current_index: {current_index}, total_records: {total_records}")
if remaining <= 0 and current_index >= total_records - 1:
print("[DEBUG] Setting status to 'all records processed'")
processed_person["processing_status"] = "all records processed"
else:
print(f"[DEBUG] More records to process. Remaining: {remaining}")
processed_person["processing_status"] = f"more records to process ({remaining} remaining)"
print("[DEBUG] Final processed person:", processed_person)
return processed_person
except Exception as e:
print(f"[DEBUG] Error processing data: {str(e)}")
return {"error": str(e), "processing_status": "error occurred"}
# Create agents
data_agent = Agent(
name="DataCollector",
role="Data collection specialist",
goal="Collect and validate data about individuals",
backstory="Expert in gathering and validating demographic data",
tools=[random_data_of_individuals],
self_reflect=False
)
process_agent = Agent(
name="DataProcessor",
role="Data processor",
goal="Process and categorize individual data",
backstory="Expert in data analysis and categorization",
tools=[process_data_of_individuals],
self_reflect=False
)
# Modify tasks to use Pydantic models
collect_task = Task(
description="Collect random individual data using the random_data_of_individuals tool. Return as a JSON object with 'items' array.",
expected_output="List of individual records with basic information",
agent=data_agent,
name="collect_data",
tools=[random_data_of_individuals],
is_start=True,
next_tasks=["validate_data"],
output_json=DataList
)
validate_task = Task(
description="""Validate the collected data. Check if:
1. All required fields are present (name, age, job, city, salary)
2. Age is between 25 and 65
3. Salary is between 30000 and 120000
Return validation_result as 'valid' or 'invalid' with optional details.""",
expected_output="Validation result indicating if data is valid or invalid",
agent=data_agent,
name="validate_data",
task_type="decision",
condition={
"valid": ["process_data"],
"invalid": ["collect_data"]
},
output_json=ValidationResult
)
process_task = Task(
description="""Process one record at a time from the input data.
Current progress will be shown in Loop Status.
For the current record:
1. Use process_data_of_individuals tool to add categories
2. Return the processed record with remaining count
Current remaining: {remaining}
Current item: {current_item}
Process this record and indicate if more records remain.""",
expected_output="Processed record with categories and status",
agent=process_agent,
name="process_data",
tools=[process_data_of_individuals],
task_type="loop",
condition={
"more records to process": ["process_data"],
"all records processed": []
},
context=[collect_task],
output_json=ProcessedPerson
)
# Create PraisonAIAgents instance with workflow process
workflow = PraisonAIAgents(
agents=[data_agent, process_agent],
tasks=[collect_task, validate_task, process_task],
verbose=1,
process="workflow"
)
# Run the workflow
result = workflow.start()
# Print results
print("\nWorkflow Results:")
print("----------------")
for task_id, task in workflow.tasks.items():
print(f"\nTask: {task.name}")
print(f"Status: {task.status}")
if task.result:
print("Output:")
try:
# Try to format as pretty JSON
import json
output = json.loads(task.result.raw)
print(json.dumps(output, indent=2))
except:
# If not JSON, print raw output
print(task.result.raw[:500] + "..." if len(task.result.raw) > 500 else task.result.raw)
-1
View File
@@ -1 +0,0 @@
# Welcome to Praison AI! 🚀🤖
+21
View File
@@ -0,0 +1,21 @@
services:
ui:
build:
context: .
dockerfile: Dockerfile.ui
volumes:
- .:/app
ports:
- "8082:8082"
environment:
FLASK_ENV: development
CHAINLIT_PORT: "8082"
CHAINLIT_HOST: "0.0.0.0"
OPENAI_API_KEY: ${OPENAI_API_KEY}
CHAINLIT_AUTH_SECRET: ${CHAINLIT_AUTH_SECRET}
command: praisonai ui --host 0.0.0.0
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8082"]
interval: 10s
timeout: 5s
retries: 3
+1
View File
@@ -1,6 +1,7 @@
---
title: 'API Reference'
description: 'PraisonAI API documentation with framework-specific details'
icon: "book-open"
---
# API Reference
+1
View File
@@ -1,6 +1,7 @@
---
title: "API Reference"
description: "Complete API reference for PraisonAI, including core modules, installation options, and framework-specific features"
icon: "code"
---
## Core Modules
+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.17 gunicorn markdown\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.0.36 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;)
+79
View File
@@ -0,0 +1,79 @@
---
title: "Agent Module"
description: "Documentation for the praisonaiagents.agent.agent module"
icon: "user"
---
# Module praisonaiagents.agent.agent
## Classes
### Agent
The main class representing an AI agent with specific role, goal, and capabilities.
#### Parameters
- `name: str` - Name of the agent
- `role: str` - Role of the agent
- `goal: str` - Goal the agent aims to achieve
- `backstory: str` - Background story of the agent
- `llm: str | Any | None = 'gpt-4o'` - Language model to use
- `tools: List[Any] | None = None` - List of tools available to the agent
- `function_calling_llm: Any | None = None` - LLM for function calling
- `max_iter: int = 20` - Maximum iterations
- `max_rpm: int | None = None` - Maximum requests per minute
- `max_execution_time: int | None = None` - Maximum execution time
- `memory: bool = True` - Enable memory
- `verbose: bool = True` - Enable verbose output
- `allow_delegation: bool = False` - Allow task delegation
- `step_callback: Any | None = None` - Callback for each step
- `cache: bool = True` - Enable caching
- `system_template: str | None = None` - System prompt template
- `prompt_template: str | None = None` - Prompt template
- `response_template: str | None = None` - Response template
- `allow_code_execution: bool | None = False` - Allow code execution
- `max_retry_limit: int = 2` - Maximum retry attempts
- `respect_context_window: bool = True` - Respect context window size
- `code_execution_mode: Literal['safe', 'unsafe'] = 'safe'` - Code execution mode
- `embedder_config: Dict[str, Any] | None = None` - Embedder configuration
- `knowledge_sources: List[Any] | None = None` - Knowledge sources
- `use_system_prompt: bool | None = True` - Use system prompt
- `markdown: bool = True` - Enable markdown
- `self_reflect: bool = True` - Enable self reflection
- `max_reflect: int = 3` - Maximum reflections
- `min_reflect: int = 1` - Minimum reflections
- `reflect_llm: str | None = None` - LLM for reflection
#### Methods
- `chat(self, prompt, temperature=0.2, tools=None, output_json=None)` - Chat with the agent
- `achat(self, prompt, temperature=0.2, tools=None, output_json=None)` - Async version of chat method
- `clean_json_output(self, output: str) → str` - Clean and extract JSON from response text
- `clear_history(self)` - Clear chat history
- `execute_tool(self, function_name, arguments)` - Execute a tool dynamically based on the function name and arguments
- `_achat_completion(self, response, tools)` - Async version of _chat_completion method
#### Async Support
The Agent class provides async support through the following methods:
- `achat`: Async version of the chat method for non-blocking communication
- `_achat_completion`: Internal async method for handling chat completions
Example usage:
```python
async def main():
agent = Agent(
name="AsyncAgent",
role="Async Specialist",
goal="Handle async operations",
backstory="Expert in async processing"
)
# Use async chat
result = await agent.achat(
prompt="Your prompt here",
tools=your_tools,
output_json=your_schema
)
print(result)
asyncio.run(main())
```
+113
View File
@@ -0,0 +1,113 @@
---
title: "Agents Module"
description: "Documentation for the praisonaiagents.agents.agents module"
icon: "users"
---
# Module praisonaiagents.agents.agents
## Functions
### encode_file_to_base64(file_path: str) → str
Base64-encode a file.
### process_video(video_path: str, seconds_per_frame=2)
Split video into frames (base64-encoded).
## Classes
### LoopItems
A Pydantic model for handling loop items.
#### Class Variables
- `items: List[Any]` - List of items to loop through
- `model_config` - Model configuration
### PraisonAIAgents
The main class for managing agents and tasks execution.
#### Parameters
- `agents: List[Agent]` - List of agents
- `tasks: List[Task]` - List of tasks
- `verbose: int = 0` - Verbosity level
- `completion_checker: Optional[Callable] = None` - Custom completion checker
- `max_retries: int = 5` - Maximum retry attempts
- `process: str = "sequential"` - Process type (sequential, workflow, hierarchical)
- `manager_llm: Optional[str] = None` - Language model for manager agent
#### Methods
- `add_task(self, task)` - Add a task to the crew
- `clean_json_output(self, output: str) → str` - Clean JSON output
- `clear_state(self) → None` - Clear all state values
- `default_completion_checker(self, task, agent_output)` - Default completion checker
- `execute_task(self, task_id)` - Execute a specific task
- `get_agent_details(self, agent_name)` - Get agent details
- `get_all_tasks_status(self)` - Get status of all tasks
- `get_state(self, key: str, default: Any = None) → Any` - Get a state value
- `get_task_details(self, task_id)` - Get task details
- `get_task_result(self, task_id)` - Get task result
- `get_task_status(self, task_id)` - Get task status
- `run_all_tasks(self)` - Execute tasks based on execution mode
- `run_task(self, task_id)` - Run a specific task
- `save_output_to_file(self, task, task_output)` - Save task output to file
- `set_state(self, key: str, value: Any) → None` - Set a state value
- `start(self)` - Start the crew execution
- `update_state(self, updates: Dict) → None` - Update multiple state values
#### Async Methods
- `astart(self)` - Async version of start method
- `aexecute_task(self, task_id)` - Async version of execute_task
- `arun_task(self, task_id)` - Async version of run_task
- `arun_all_tasks(self)` - Async version of run_all_tasks
#### Async Support
The PraisonAIAgents class provides comprehensive async support through the following methods:
```python
class PraisonAIAgents:
async def astart(self):
"""Async version of start method"""
await self.arun_all_tasks()
return {
"task_status": self.get_all_tasks_status(),
"task_results": self.get_task_results()
}
async def aexecute_task(self, task_id):
"""Async version of execute_task method"""
# Async task execution implementation
async def arun_task(self, task_id):
"""Async version of run_task method"""
# Async task running with retries
async def arun_all_tasks(self):
"""Async version of run_all_tasks method"""
# Async execution of all tasks
```
Example usage:
```python
async def main():
# Initialize agents and tasks
agents = PraisonAIAgents(
agents=[async_agent1, async_agent2],
tasks=[async_task1, async_task2],
process="workflow"
)
# Start async execution
result = await agents.astart()
print(result)
# Run the async workflow
asyncio.run(main())
```
#### Async Features
- Full async/await support
- Parallel task execution
- Non-blocking operations
- Efficient resource utilization
- Mixed sync/async task handling
- Async process management
+52
View File
@@ -0,0 +1,52 @@
---
title: "API Reference"
description: "Praison AI Agents - SDK Documentation"
icon: "code"
---
<CardGroup cols={2}>
<Card title="Agent" icon="user" href="/api/praisonaiagents/agent/agent">
Documentation for individual AI agents and their capabilities
</Card>
<Card title="Agents" icon="users" href="/api/praisonaiagents/agents/agents">
Documentation for managing multiple agents and their interactions
</Card>
<Card title="Task" icon="list-check" href="/api/praisonaiagents/task/task">
Documentation for defining and managing tasks
</Card>
<Card title="Process" icon="diagram-project" href="/api/praisonaiagents/process/process">
Documentation for handling task execution flows
</Card>
</CardGroup>
# Module praisonaiagents
## Sub-modules
* praisonaiagents.agent - Agent module for defining individual AI agents
* praisonaiagents.agents - Agents module for managing multiple agents
* praisonaiagents.task - Task module for defining and managing tasks
* praisonaiagents.process - Process module for handling task execution flows
* praisonaiagents.main - Main module containing utility functions
## Functions
### clean_triple_backticks(text: str) → str
Clean triple backticks from text output.
### display_error(message: str, console=None)
Display error messages in the console.
### display_generating(content: str = '', start_time: float | None = None)
Display generation status with optional timing information.
### display_instruction(message: str, console=None)
Display instruction messages in the console.
### display_interaction(message, response, markdown=True, generation_time=None, console=None)
Display interaction between user and agent.
### display_self_reflection(message: str, console=None)
Display agent self-reflection messages.
### display_tool_call(message: str, console=None)
Display tool call messages.
@@ -0,0 +1,98 @@
---
title: "Process Module"
description: "Documentation for the praisonaiagents.process.process module"
icon: "diagram-project"
---
# Module praisonaiagents.process.process
## Classes
### Process
The main class for handling task execution processes in different modes (sequential, hierarchical, workflow).
#### Parameters
- `tasks: Dict[str, Task]` - Dictionary of tasks to be executed
- `agents: List[Agent]` - List of available agents
- `manager_llm: Optional[str] = None` - Language model for manager agent in hierarchical process
- `verbose: bool = False` - Enable verbose logging
#### Methods
- `workflow(self)` - Execute tasks in a workflow pattern with support for loops and conditions
- `sequential(self)` - Execute tasks in sequential order
- `hierarchical(self)` - Execute tasks in hierarchical order with a manager agent
#### Async Methods
- `aworkflow(self) -> AsyncGenerator[str, None]` - Async version of workflow process
- `asequential(self) -> AsyncGenerator[str, None]` - Async version of sequential process
- `ahierarchical(self) -> AsyncGenerator[str, None]` - Async version of hierarchical process
#### Async Process Support
Each process type has an async counterpart that supports non-blocking execution:
```python
class Process:
async def aworkflow(self):
"""Async workflow process execution"""
# Async workflow implementation
async def asequential(self):
"""Async sequential process execution"""
# Async sequential implementation
async def ahierarchical(self):
"""Async hierarchical process execution"""
# Async hierarchical implementation
```
Example usage:
```python
# Create process instance
process = Process(tasks=tasks, agents=agents)
# Use async workflow
async for task_id in process.aworkflow():
# Handle task execution
if tasks[task_id].async_execution:
await handle_async_task(task_id)
else:
handle_sync_task(task_id)
```
#### Async Process Features
- Non-blocking task execution
- Parallel task processing
- Mixed sync/async task support
- Efficient resource utilization
- Async manager coordination (hierarchical)
- Dynamic task scheduling (workflow)
### LoopItems
A Pydantic model for handling loop data in workflow process.
#### Parameters
- `items: List[Any]` - List of items to be processed in a loop
## Process Types
### Sequential Process
- Tasks are executed one after another in a linear fashion
- Each task is completed before moving to the next one
- Simplest form of task execution
### Hierarchical Process
- Uses a manager agent to coordinate task execution
- Manager decides task order and agent assignments
- Provides more complex task orchestration
- Requires manager_llm parameter
- Supports dynamic agent assignment
### Workflow Process
- Supports complex task relationships
- Features:
- Task dependencies
- Conditional execution
- Loops
- Context sharing between tasks
- Maintains task state and relationships
- Handles data flow between tasks
+78
View File
@@ -0,0 +1,78 @@
---
title: "Task Module"
description: "Documentation for the praisonaiagents.task.task module"
icon: "list-check"
---
# Module praisonaiagents.task.task
## Classes
### Task
Represents a task to be executed by an agent.
#### Parameters
- `description: str` - Task description
- `expected_output: str` - Expected output description
- `agent: Agent | None = None` - Agent assigned to the task
- `name: str | None = None` - Task name
- `tools: List[Any] | None = None` - Tools available for the task
- `context: List[Task] | None = None` - Context from other tasks
- `async_execution: bool | None = False` - Enable async execution
- `config: Dict[str, Any] | None = None` - Task configuration
- `output_file: str | None = None` - Output file path
- `output_json: Type[BaseModel] | None = None` - JSON output schema
- `output_pydantic: Type[BaseModel] | None = None` - Pydantic output schema
- `callback: Any | None = None` - Task callback
- `status: str = 'not started'` - Task status
- `result: TaskOutput | None = None` - Task result
- `create_directory: bool | None = False` - Create output directory
- `id: int | None = None` - Task ID
- `images: List[str] | None = None` - Task images
- `next_tasks: List[str] | None = None` - Next tasks
- `task_type: str = 'task'` - Task type
- `condition: Dict[str, List[str]] | None = None` - Task conditions
- `is_start: bool = False` - Is start task
- `loop_state: Dict[str, str | int] | None = None` - Loop state
#### Async Support
The Task class supports asynchronous execution through the following features:
- `async_execution`: Boolean flag to enable async execution
- `callback`: Supports both sync and async callback functions
- `execute_callback`: Internal async method for handling callbacks
Example usage:
```python
from typing import Coroutine
from praisonaiagents import Task, TaskOutput
# Define an async callback
async def async_callback(output: TaskOutput):
await some_async_operation()
print(f"Processed: {output.raw}")
# Create task with async execution
task = Task(
description="Async task example",
expected_output="Async result",
async_execution=True, # Enable async execution
callback=async_callback # Async callback
)
```
#### Callback Types
The Task class supports both synchronous and asynchronous callbacks:
```python
# Type definition
callback: Optional[Union[
Callable[[TaskOutput], Any], # Sync callback
Callable[[TaskOutput], Coroutine[Any, Any, Any]] # Async callback
]]
```
#### Async Task States
- `not started`: Initial state
- `in progress`: Task is being executed
- `completed`: Task finished successfully
- `failed`: Task execution failed
+1
View File
@@ -1,6 +1,7 @@
---
title: "Auto Generation Mode"
description: "Guide to PraisonAI's automatic agent generation feature, supporting both CrewAI and AutoGen frameworks for automated task execution"
icon: "robot"
---
PraisonAI supports automatic agent generation with PraisonAI Agents, CrewAI and AutoGen frameworks.
+1
View File
@@ -1,6 +1,7 @@
---
title: "PraisonAI Call"
description: "Guide to PraisonAI's voice-based interaction feature enabling AI customer service through phone calls, including setup and tool integration"
icon: "phone"
---
<iframe width="560" height="315" src="https://www.youtube.com/embed/m1cwrUG2iAk" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
+69
View File
@@ -0,0 +1,69 @@
---
title: Installation
description: Set up PraisonAI in your development environment
icon: download
---
<Note>
**Requirements**
- Python 3.10 or higher
- pip package manager
- Virtual environment (recommended)
</Note>
# Installing PraisonAI
Follow these steps to set up PraisonAI in your development environment.
<Steps>
<Step title="Create Virtual Environment (Optional)">
First, create and activate a virtual environment:
<CodeGroup>
```bash Mac/Linux
python -m venv praisonai-env
source praisonai-env/bin/activate
```
```bash Windows
python -m venv praisonai-env
.\praisonai-env\Scripts\activate
```
</CodeGroup>
</Step>
<Step title="Install PraisonAI">
Install the core package and tools:
```bash Terminal
pip install praisonaiagents
```
</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)
You can also use other LLM providers like Anthropic, Google, etc. Please refer to the [Models](/models) for more information.
</Step>
</Steps>
## Next Steps
<CardGroup cols={2}>
<Card
title="Quick Start Guide"
icon="bolt"
href="/code/quickstart"
>
Build your first AI agent
</Card>
<Card
title="API Reference"
icon="code"
href="/api/praisonaiagents/index"
>
Explore the API documentation
</Card>
</CardGroup>
+188
View File
@@ -0,0 +1,188 @@
---
title: "Introduction"
description: "Welcome to PraisonAI - The Next Generation AI Agent Framework"
icon: "book-open"
---
<Note>
PraisonAI is a powerful framework for building and deploying AI agents that can understand, reason, and execute complex tasks autonomously.
</Note>
# What is PraisonAI?
```mermaid
graph LR
%% Define the main flow
Start([▶ Start]) --> Agent1
Agent1 --> Process[⚙ Process]
Process --> Agent2
Agent2 --> Output([✓ Output])
Process -.-> Agent1
%% Styling for main elements
style Start fill:#2D3047,stroke:#7C90A0,color:#fff
style Output fill:#2D3047,stroke:#7C90A0,color:#fff
style Process fill:#5C80BC,stroke:#7C90A0,color:#fff
%% Define subgraphs for agents and their tasks
subgraph Agent1[" "]
Task1[📋 Task]
AgentIcon1[🤖 AI Agent]
Tools1[🔧 Tools]
Task1 --- AgentIcon1
AgentIcon1 --- Tools1
style Task1 fill:#2D3047,stroke:#7C90A0,color:#fff
style AgentIcon1 fill:#5C80BC,stroke:#7C90A0,color:#fff
style Tools1 fill:#2D3047,stroke:#7C90A0,color:#fff
end
subgraph Agent2[" "]
Task2[📋 Task]
AgentIcon2[🤖 AI Agent]
Tools2[🔧 Tools]
Task2 --- AgentIcon2
AgentIcon2 --- Tools2
style Task2 fill:#2D3047,stroke:#7C90A0,color:#fff
style AgentIcon2 fill:#5C80BC,stroke:#7C90A0,color:#fff
style Tools2 fill:#2D3047,stroke:#7C90A0,color:#fff
end
%% Style for agent containers
style Agent1 fill:#1B1F3B,stroke:#7C90A0,color:#fff
style Agent2 fill:#1B1F3B,stroke:#7C90A0,color:#fff
%% Global styles
classDef default fill:#2D3047,stroke:#7C90A0,color:#fff
linkStyle default stroke:#7C90A0,stroke-width:2px
```
## Core Components
<CardGroup cols={2}>
<Card title="Agents" icon="user-robot">
Autonomous AI entities that can understand context and execute tasks
</Card>
<Card title="Tasks" icon="list-check">
Units of work that agents can execute independently or collaboratively
</Card>
<Card title="Process" icon="diagram-project">
Workflow management for coordinating agent activities
</Card>
<Card title="Tools" icon="toolbox">
Extensible capabilities that agents can leverage
</Card>
</CardGroup>
## Key Features
<Steps>
<Step title="Autonomous Agents">
Create intelligent agents that can:
- Understand natural language instructions
- Make decisions based on context
- Execute complex tasks autonomously
</Step>
<Step title="Flexible Architecture">
Build with:
- Modular components
- Extensible tools
- Customizable workflows
</Step>
<Step title="Advanced Capabilities">
Leverage:
- Multi-agent collaboration
- Memory and context management
- Tool integration
</Step>
</Steps>
## Use Cases
<CardGroup cols={2}>
<Card title="Customer Service" icon="headset">
Build intelligent support agents that can handle customer inquiries and resolve issues autonomously.
</Card>
<Card title="Data Analysis" icon="chart-line">
Create agents that can process, analyze, and derive insights from complex datasets.
</Card>
<Card title="Content Creation" icon="pen-nib">
Deploy agents that can generate, edit, and optimize content across various formats.
</Card>
<Card title="Process Automation" icon="gears">
Automate complex workflows with intelligent agents that can coordinate and execute tasks.
</Card>
</CardGroup>
## Getting Started
<Warning>
Before proceeding, ensure you have Python 3.9+ installed on your system.
</Warning>
### Quick Setup
<CodeGroup>
```bash pip
pip install praisonai
```
</CodeGroup>
### Basic Example
```python
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()
```
## Next Steps
<CardGroup cols={2}>
<Card
title="Installation"
icon="download"
href="/code/installation"
>
Complete installation guide and setup instructions
</Card>
<Card
title="Quick Start"
icon="play"
href="/code/quickstart"
>
Build your first AI agent in minutes
</Card>
</CardGroup>
<Tip>
Join our community on [Discord](https://www.youtube.com/redirect?event=channel_description&redir_token=QUFFLUhqbUpiZE00YlF1MjB1LVNIeTFxTnV6MWRRc18wZ3xBQ3Jtc0ttQVZ0aFJ4RXRDanlBQU1Vd3lHU3BBcml3bzhoek1pTmlkZkFkWjlNVDBpRkxZd1dTOWhPc20xbHJOb1NibmNYeTEydklJT0pZOVJFQW93MmJOQXU0WkJNVzNsd29SZ3FBNFVwMTFGNThNSHBWVUhjSQ&q=https%3A%2F%2Fdiscord.gg%2FnNZu5gGT59) to connect with other developers and get help!
</Tip>
+195
View File
@@ -0,0 +1,195 @@
---
title: Quick Start
description: Build your first AI agent with PraisonAI
icon: bolt
---
<Steps>
<Step title="Install PraisonAI">
Install the core package:
```bash Terminal
pip install praisonaiagents
```
</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)
You can also use other LLM providers like Anthropic, Google, etc. Please refer to the [Models](/models) for more information.
</Step>
<Step title="Create Agent">
Create `app.py`:
## 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 the Program">
Execute your script:
```bash Terminal
python app.py
```
You should see:
- Agent initialization
- Agents progress
- Final results
- Generated report
</Step>
</Steps>
## Advanced Usage
### Creating Custom Tool
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
from duckduckgo_search import DDGS
from typing import List, Dict
# 1. Tool
def internet_search_tool(query: str) -> List[Dict]:
"""
Perform Internet Search
"""
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=5):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return results
# 2. Agent
data_agent = Agent(
name="DataCollector",
role="Search Specialist",
goal="Perform internet searches to collect relevant information.",
backstory="Expert in finding and organising internet data.",
tools=[internet_search_tool],
self_reflect=False
)
# 3. Tasks
collect_task = Task(
description="Perform an internet search using the query: 'AI job trends in 2024'. Return results as a list of title, URL, and snippet.",
expected_output="List of search results with titles, URLs, and snippets.",
agent=data_agent,
name="collect_data",
)
# 4. Start Agents
agents = PraisonAIAgents(
agents=[data_agent],
tasks=[collect_task],
process="sequential"
)
agents.start()
```
## Next Steps
<CardGroup cols={2}>
<Card
title="Core Concepts"
icon="book"
href="/concepts/agents"
>
Learn about agents, tasks, and processes
</Card>
<Card
title="API Reference"
icon="code"
href="/api/praisonaiagents/index"
>
Explore detailed API documentation
</Card>
</CardGroup>
+235
View File
@@ -0,0 +1,235 @@
---
title: "Agents"
description: "Understanding Agents in PraisonAI"
icon: "user"
---
<Note>
Agents are the core building blocks of PraisonAI. Each agent is an autonomous AI entity with specific roles, goals, and capabilities.
</Note>
# Understanding Agents
```mermaid
graph LR
%% Define the main flow
Start([▶ Start]) --> Agent1
Agent1 --> Process[⚙ Process]
Process --> Agent2
Agent2 --> Output([✓ Output])
Process -.-> Agent1
%% Styling for main elements
style Start fill:#2D3047,stroke:#7C90A0,color:#fff
style Output fill:#2D3047,stroke:#7C90A0,color:#fff
style Process fill:#5C80BC,stroke:#7C90A0,color:#fff
%% Define subgraphs for agents and their tasks
subgraph Agent1[" "]
Task1[📋 Task]
AgentIcon1[🤖 AI Agent]
Tools1[🔧 Tools]
Task1 --- AgentIcon1
AgentIcon1 --- Tools1
style Task1 fill:#2D3047,stroke:#7C90A0,color:#fff
style AgentIcon1 fill:#5C80BC,stroke:#7C90A0,color:#fff
style Tools1 fill:#2D3047,stroke:#7C90A0,color:#fff
end
subgraph Agent2[" "]
Task2[📋 Task]
AgentIcon2[🤖 AI Agent]
Tools2[🔧 Tools]
Task2 --- AgentIcon2
AgentIcon2 --- Tools2
style Task2 fill:#2D3047,stroke:#7C90A0,color:#fff
style AgentIcon2 fill:#5C80BC,stroke:#7C90A0,color:#fff
style Tools2 fill:#2D3047,stroke:#7C90A0,color:#fff
end
%% Style for agent containers
style Agent1 fill:#1B1F3B,stroke:#7C90A0,color:#fff
style Agent2 fill:#1B1F3B,stroke:#7C90A0,color:#fff
%% Global styles
classDef default fill:#2D3047,stroke:#7C90A0,color:#fff
linkStyle default stroke:#7C90A0,stroke-width:2px
```
## Key Components
<CardGroup cols={2}>
<Card title="Role & Goal" icon="target">
Defines the agent's purpose and objectives through role definition and specific goals
</Card>
<Card title="Capabilities" icon="toolbox">
Tools and functions available to the agent for task execution
</Card>
<Card title="Memory" icon="brain">
Context retention and learning capabilities across interactions
</Card>
<Card title="Language Model" icon="microchip">
The underlying AI model powering the agent's intelligence
</Card>
</CardGroup>
## Component Details
### Role and Goal
<Tip>
Clear role and goal definitions are crucial for optimal agent performance.
</Tip>
| Component | Description | Example |
|:----------|:------------|:--------|
| **Role** | Agent's function and expertise | Research Analyst, Code Developer |
| **Goal** | Specific objectives to achieve | Analyze market trends, Generate reports |
| **Backstory** | Contextual background | Expert with 10 years of experience |
### Capabilities
<CodeGroup>
```python Basic
agent = 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
)
```
</CodeGroup>
## Agent Types
<Steps>
<Step title="Basic Agent">
- Single-purpose focus
- Direct user interaction
- Limited tool set
</Step>
<Step title="Specialized Agent">
- Domain expertise
- Advanced capabilities
- Custom tools
</Step>
<Step title="Collaborative Agent">
- Team interaction
- Task delegation
- Shared context
</Step>
</Steps>
## Best Practices
<Warning>
Always implement proper error handling and resource management in your agent configurations.
</Warning>
### Agent Design
1. Clear role definition
2. Specific, measurable goals
3. Relevant tool selection
4. Appropriate memory configuration
### Agent Interaction
1. Clear communication protocols
2. Defined delegation rules
3. Error handling mechanisms
4. Resource management
## Advanced Features
<CardGroup cols={2}>
<Card title="Memory Management" icon="brain">
- Short-term conversation memory
- Long-term knowledge retention
- Context preservation
</Card>
<Card title="Tool Integration" icon="plug">
- Custom tool development
- External API integration
- Resource access control
</Card>
</CardGroup>
## Async Support
Agents now support asynchronous operations through the following methods:
- `achat`: Async version of the chat method
- `astart`: Async version of start method
- `aexecute_task`: Async version of execute_task method
- `arun_task`: Async version of run_task method
- `arun_all_tasks`: Async version of run_all_tasks method
### Example Usage:
```python
import asyncio
from praisonaiagents import Agent, Task, PraisonAIAgents
async def main():
# Create an async agent
async_agent = Agent(
name="AsyncAgent",
role="Async Task Specialist",
goal="Perform async operations",
backstory="Expert in async operations",
tools=[async_search_tool], # Your async tool
verbose=True
)
# Create an async task
async_task = Task(
description="Perform async operation",
expected_output="Async result",
agent=async_agent,
async_execution=True # Enable async execution
)
# Create and start agents with async support
agents = PraisonAIAgents(
agents=[async_agent],
tasks=[async_task],
verbose=True
)
# Start async execution
result = await agents.astart()
print(result)
# Run the async main function
if __name__ == "__main__":
asyncio.run(main())
```
### Key Features:
- Full async/await support
- Parallel task execution
- Async tool integration
- Async callback support
- Mixed sync/async operations
## Next Steps
<CardGroup cols={2}>
<Card
title="Create Your First Agent"
icon="code"
href="/code/quickstart"
>
Follow our quickstart guide to create your first agent
</Card>
<Card
title="API Reference"
icon="book"
href="/api/praisonaiagents/agent/agent"
>
Explore the complete Agent API documentation
</Card>
</CardGroup>
+220
View File
@@ -0,0 +1,220 @@
---
title: "Process"
description: "Understanding Process Types in PraisonAI"
icon: "diagram-project"
---
# Understanding Process Types
Process types in PraisonAI define how tasks are executed and how agents collaborate. Each process type offers different patterns for task execution and agent coordination.
## Process Types Overview
Three types of processes are implemented in PraisonAI:
<Frame>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="p-4 border rounded-lg bg-white dark:bg-gray-800">
<div className="flex items-center mb-4">
<div className="p-2 bg-blue-100 dark:bg-blue-900 rounded-lg">
<svg className="w-6 h-6 text-blue-600 dark:text-blue-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16m-7 6h7" />
</svg>
</div>
<h3 className="ml-3 text-lg font-semibold">Sequential Process</h3>
</div>
<p>Linear task execution in a predefined order</p>
</div>
<div className="p-4 border rounded-lg bg-white dark:bg-gray-800">
<div className="flex items-center mb-4">
<div className="p-2 bg-green-100 dark:bg-green-900 rounded-lg">
<svg className="w-6 h-6 text-green-600 dark:text-green-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4" />
</svg>
</div>
<h3 className="ml-3 text-lg font-semibold">Hierarchical Process</h3>
</div>
<p>Manager-coordinated task execution with dynamic assignment</p>
</div>
<div className="p-4 border rounded-lg bg-white dark:bg-gray-800">
<div className="flex items-center mb-4">
<div className="p-2 bg-purple-100 dark:bg-purple-900 rounded-lg">
<svg className="w-6 h-6 text-purple-600 dark:text-purple-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
</svg>
</div>
<h3 className="ml-3 text-lg font-semibold">Workflow Process</h3>
</div>
<p>Complex task flows with conditional execution and state management</p>
</div>
</div>
</Frame>
### Sequential Process
The simplest form of task execution where tasks are performed one after another.
<Frame>
<div className="p-4 bg-white dark:bg-gray-800 rounded-lg">
#### Characteristics
- Linear execution flow
- Predictable order
- Simple dependency management
- Direct task progression
#### Usage
```python
agents = PraisonAIAgents(
agents=[agent1, agent2],
tasks=[task1, task2, task3],
process="sequential"
)
```
</div>
</Frame>
### Hierarchical Process
Uses a manager agent to coordinate task execution and agent assignments.
<Frame>
<div className="p-4 bg-white dark:bg-gray-800 rounded-lg">
#### Characteristics
- Manager-driven coordination
- Dynamic task assignment
- Flexible execution order
- Intelligent resource allocation
#### Configuration
```python
agents = PraisonAIAgents(
agents=[manager_agent, worker_agent1, worker_agent2],
tasks=[task1, task2],
process="hierarchical",
manager_llm="gpt-4o"
)
```
</div>
</Frame>
### Workflow Process
Advanced process type supporting complex task relationships and conditional execution.
<div className="relative w-full aspect-video">
<iframe
className="absolute top-0 left-0 w-full h-full"
src="https://www.youtube.com/embed/yWTH44QPl2A"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
<br />
<Frame>
<div className="p-4 bg-white dark:bg-gray-800 rounded-lg">
#### Features
- Task dependencies
- Conditional branching
- Loop handling
- Context sharing
- State management
#### Implementation
```python
task_workflow = {
"start_task": {
"next": ["decision_task"],
"type": "task"
},
"decision_task": {
"type": "decision",
"conditions": {
"success": ["process_task"],
"failure": ["error_task"]
}
}
}
```
</div>
</Frame>
## Advanced Features
### State Management
- Task state tracking
- Context preservation
- Data flow control
### Error Handling
- Graceful failure recovery
- Alternative path execution
- Error reporting
### Monitoring
- Progress tracking
- Performance metrics
- Resource utilization
### Integration
- External system coordination
- API synchronization
- Event handling
## Async Process Execution
All process types (sequential, workflow, hierarchical) support async execution through async generators. This enables efficient handling of async tasks and parallel processing.
### Async Process Methods
- `asequential`: Async version of sequential process
- `aworkflow`: Async version of workflow process
- `ahierarchical`: Async version of hierarchical process
### Example Usage:
```python
import asyncio
from praisonaiagents import Agent, Task, PraisonAIAgents
async def main():
# Create agents and tasks
agents = [async_agent1, async_agent2]
tasks = [async_task1, async_task2]
# Initialize PraisonAIAgents with async workflow
agents = PraisonAIAgents(
agents=agents,
tasks=tasks,
process="workflow" # or "sequential" or "hierarchical"
)
# Start async execution
result = await agents.astart()
print(result)
# Run the async workflow
asyncio.run(main())
```
### Async Process Features:
- Parallel task execution in workflow
- Async task delegation in hierarchical process
- Non-blocking task execution
- Efficient resource utilization
- Mixed sync/async task support
### Process-Specific Async Behavior:
1. **Sequential Process**:
- Tasks are executed in order
- Async tasks don't block other tasks
- Maintains sequential order while allowing async execution
2. **Workflow Process**:
- Parallel execution of independent tasks
- Async branching and merging
- Dynamic task scheduling
3. **Hierarchical Process**:
- Async manager delegation
- Parallel subtask execution
- Dynamic task assignment
+317
View File
@@ -0,0 +1,317 @@
---
title: "Tasks"
description: "Understanding Tasks in PraisonAI"
icon: "list-check"
---
<Note>
Tasks are the fundamental units of work in PraisonAI, defining what needs to be done, how it should be done, and who should do it.
</Note>
# Understanding Tasks
<Frame>
<div className="p-6 bg-white dark:bg-gray-800 rounded-lg">
<div className="grid grid-cols-1 gap-6">
{/* Task Flow Steps */}
<div className="flex justify-between items-center">
{/* Task Definition */}
<div className="flex-1 p-4 border-2 border-blue-500 rounded-lg text-center">
<div className="flex items-center justify-center mb-2">
<div className="p-2 bg-blue-100 dark:bg-blue-900 rounded-full">
<svg className="w-6 h-6 text-blue-600 dark:text-blue-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2" />
</svg>
</div>
</div>
<h4 className="font-semibold">Task Definition</h4>
<p className="text-sm mt-1">Description & Requirements</p>
</div>
{/* Arrow 1 */}
<div className="flex-none px-4">
<svg className="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</div>
{/* Agent Assignment */}
<div className="flex-1 p-4 border-2 border-green-500 rounded-lg text-center">
<div className="flex items-center justify-center mb-2">
<div className="p-2 bg-green-100 dark:bg-green-900 rounded-full">
<svg className="w-6 h-6 text-green-600 dark:text-green-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
</div>
</div>
<h4 className="font-semibold">Agent Assignment</h4>
<p className="text-sm mt-1">Matching & Delegation</p>
</div>
{/* Arrow 2 */}
<div className="flex-none px-4">
<svg className="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</div>
{/* Execution */}
<div className="flex-1 p-4 border-2 border-purple-500 rounded-lg text-center">
<div className="flex items-center justify-center mb-2">
<div className="p-2 bg-purple-100 dark:bg-purple-900 rounded-full">
<svg className="w-6 h-6 text-purple-600 dark:text-purple-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 10V3L4 14h7v7l9-11h-7z" />
</svg>
</div>
</div>
<h4 className="font-semibold">Execution</h4>
<p className="text-sm mt-1">Processing & Tools</p>
</div>
{/* Arrow 3 */}
<div className="flex-none px-4">
<svg className="w-6 h-6 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
</svg>
</div>
{/* Output */}
<div className="flex-1 p-4 border-2 border-yellow-500 rounded-lg text-center">
<div className="flex items-center justify-center mb-2">
<div className="p-2 bg-yellow-100 dark:bg-yellow-900 rounded-full">
<svg className="w-6 h-6 text-yellow-600 dark:text-yellow-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
</div>
</div>
<h4 className="font-semibold">Output</h4>
<p className="text-sm mt-1">Results & Validation</p>
</div>
</div>
{/* Task States */}
<div className="grid grid-cols-4 gap-4 mt-4">
<div className="p-3 border border-gray-200 rounded-lg bg-blue-50 dark:bg-blue-900">
<div className="flex items-center justify-center">
<span className="font-semibold">Pending</span>
</div>
</div>
<div className="p-3 border border-gray-200 rounded-lg bg-yellow-50 dark:bg-yellow-900">
<div className="flex items-center justify-center">
<span className="font-semibold">In Progress</span>
</div>
</div>
<div className="p-3 border border-gray-200 rounded-lg bg-green-50 dark:bg-green-900">
<div className="flex items-center justify-center">
<span className="font-semibold">Completed</span>
</div>
</div>
<div className="p-3 border border-gray-200 rounded-lg bg-red-50 dark:bg-red-900">
<div className="flex items-center justify-center">
<span className="font-semibold">Failed</span>
</div>
</div>
</div>
</div>
</div>
</Frame>
## Core Components
<CardGroup cols={2}>
<Card title="Task Definition" icon="file-lines">
Clear description of work to be done and expected outputs
</Card>
<Card title="Agent Assignment" icon="user-check">
Matching tasks with capable agents
</Card>
<Card title="Tool Access" icon="toolbox">
Resources available for task completion
</Card>
<Card title="Output Handling" icon="file-export">
Managing and formatting task results
</Card>
</CardGroup>
## Task Configuration
<CodeGroup>
```python Basic
task = Task(
description="Research AI trends",
expected_output="Summary report",
agent=research_agent
)
```
```python Advanced
task = Task(
description="Analyze market data",
expected_output="Detailed analysis",
agent=analyst_agent,
tools=[AnalysisTool()],
output_file="analysis.md",
async_execution=True
)
```
</CodeGroup>
## Task Types
<Steps>
<Step title="Basic Task">
Simple, single-operation tasks with clear inputs and outputs
</Step>
<Step title="Decision Task">
Tasks involving choices and conditional paths
```python
decision_task = Task(
type="decision",
conditions={
"success": ["next_task"],
"failure": ["error_task"]
}
)
```
</Step>
<Step title="Loop Task">
Repetitive operations over collections
```python
loop_task = Task(
type="loop",
items=data_list,
operation="process_item"
)
```
</Step>
</Steps>
## Task Relationships
<Warning>
Properly managing task dependencies is crucial for complex workflows. Always ensure proper context sharing and error handling.
</Warning>
### Context Sharing
```python
task_a = Task(name="research")
task_b = Task(
name="analyze",
context=[task_a] # Uses task_a's output
)
```
### Task Dependencies
| Relationship | Description | Example |
|:-------------|:------------|:--------|
| **Sequential** | Tasks run one after another | Research → Analysis → Report |
| **Parallel** | Independent tasks run simultaneously | Data Collection + Processing |
| **Conditional** | Tasks depend on previous results | Success → Next Task, Failure → Retry |
## Advanced Features
<CardGroup cols={2}>
<Card title="Output Handling" icon="file-code">
- Multiple output formats
- Structured data validation
- File system integration
</Card>
<Card title="Task Control" icon="sliders">
- Execution flow control
- Error recovery
- Progress tracking
</Card>
</CardGroup>
## Best Practices
<Tip>
Design tasks to be atomic and focused. Each task should have a single, clear responsibility.
</Tip>
1. Clear task descriptions
2. Well-defined success criteria
3. Appropriate tool selection
4. Error handling consideration
5. Resource management
## Next Steps
<CardGroup cols={2}>
<Card
title="Create Tasks"
icon="plus"
href="/code/quickstart"
>
Learn how to create and configure tasks
</Card>
<Card
title="Task API"
icon="code"
href="/api/praisonaiagents/task/task"
>
Explore the complete Task API documentation
</Card>
</CardGroup>
## Async Task Execution
Tasks can be executed asynchronously by setting `async_execution=True`. This is particularly useful for tasks that involve I/O operations or when you want to run multiple tasks in parallel.
### Async Task Configuration
```python
async_task = Task(
description="Perform async operation",
expected_output="Async result",
agent=async_agent,
async_execution=True, # Enable async execution
callback=async_callback # Optional async callback
)
```
### Async Callbacks
Tasks support both synchronous and asynchronous callbacks:
```python
async def async_callback(output: TaskOutput):
await asyncio.sleep(1) # Simulate async operation
print(f"Processed output: {output.raw}")
# Task with async callback
task = Task(
description="Task with async callback",
expected_output="Result",
agent=agent,
async_execution=True,
callback=async_callback
)
```
### Mixing Sync and Async Tasks
You can mix synchronous and asynchronous tasks in the same workflow:
```python
# Sync task
sync_task = Task(
description="Synchronous operation",
expected_output="Sync result",
agent=sync_agent,
async_execution=False
)
# Async task
async_task = Task(
description="Asynchronous operation",
expected_output="Async result",
agent=async_agent,
async_execution=True
)
# Create agents with mixed tasks
agents = PraisonAIAgents(
agents=[sync_agent, async_agent],
tasks=[sync_task, async_task]
)
# Run asynchronously
result = await agents.astart()
```
+335
View File
@@ -0,0 +1,335 @@
---
title: "Tools"
description: "Understanding Tools in PraisonAI"
icon: "screwdriver-wrench"
---
# Understanding Tools
Tools in PraisonAI are functions that agents can use to interact with external systems, perform computations, or access data. They extend the capabilities of agents beyond just language processing.
## Tools Overview
<Frame>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div className="p-4 border rounded-lg bg-white dark:bg-gray-800">
<div className="flex items-center mb-4">
<div className="p-2 bg-blue-100 dark:bg-blue-900 rounded-lg">
<svg className="w-6 h-6 text-blue-600 dark:text-blue-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z" />
</svg>
</div>
<h3 className="ml-3 text-lg font-semibold">Search Tools</h3>
</div>
<p>Tools for searching and retrieving information from various sources</p>
</div>
<div className="p-4 border rounded-lg bg-white dark:bg-gray-800">
<div className="flex items-center mb-4">
<div className="p-2 bg-green-100 dark:bg-green-900 rounded-lg">
<svg className="w-6 h-6 text-green-600 dark:text-green-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7v8a2 2 0 002 2h6M8 7V5a2 2 0 012-2h4.586a1 1 0 01.707.293l4.414 4.414a1 1 0 01.293.707V15a2 2 0 01-2 2h-2M8 7H6a2 2 0 00-2 2v10a2 2 0 002 2h8a2 2 0 002-2v-2" />
</svg>
</div>
<h3 className="ml-3 text-lg font-semibold">File Tools</h3>
</div>
<p>Tools for reading, writing, and manipulating files</p>
</div>
<div className="p-4 border rounded-lg bg-white dark:bg-gray-800">
<div className="flex items-center mb-4">
<div className="p-2 bg-purple-100 dark:bg-purple-900 rounded-lg">
<svg className="w-6 h-6 text-purple-600 dark:text-purple-300" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 6l3 1m0 0l-3 9a5.002 5.002 0 006.001 0M6 7l3 9M6 7l6-2m6 2l3-1m-3 1l-3 9a5.002 5.002 0 006.001 0M18 7l3 9m-3-9l-6-2m0-2v2m0 16V5m0 16H9m3 0h3" />
</svg>
</div>
<h3 className="ml-3 text-lg font-semibold">API Tools</h3>
</div>
<p>Tools for interacting with external APIs and services</p>
</div>
</div>
</Frame>
## Creating Custom Tool
<Steps>
<Step>
Create any function that you want to use as a tool, that performs a specific task.
```python
from duckduckgo_search import DDGS
from typing import List, Dict
# Tool Implementation
def internet_search_tool(query: str) -> List[Dict]:
"""
Perform Internet Search using DuckDuckGo
Args:
query (str): The search query string
Returns:
List[Dict]: List of search results containing title, URL, and snippet
"""
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=5):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return results
```
</Step>
<Step>
Assign the tool to an agent
```python
data_agent = Agent(
name="DataCollector",
role="Search Specialist",
goal="Perform internet searches to collect relevant information.",
backstory="Expert in finding and organising internet data.",
tools=[internet_search_tool], ## Add the tool to the agent i.e the function name
)
```
</Step>
</Steps>
<Card title="That's it!">
<Check>You have created a custom tool and assigned it to an agent.</Check>
</Card>
## Implementing Tools Full Code Example
Tools are implemented as Python functions with type hints and docstrings. Here's a complete example:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
from duckduckgo_search import DDGS
from typing import List, Dict
# 1. Tool Implementation
def internet_search_tool(query: str) -> List[Dict]:
"""
Perform Internet Search using DuckDuckGo
Args:
query (str): The search query string
Returns:
List[Dict]: List of search results containing title, URL, and snippet
"""
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=5):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return results
# 2. Assign the tool to an agent
data_agent = Agent(
name="DataCollector",
role="Search Specialist",
goal="Perform internet searches to collect relevant information.",
backstory="Expert in finding and organising internet data.",
tools=[internet_search_tool],
self_reflect=False
)
# 3. Task Definition
collect_task = Task(
description="Perform an internet search using the query: 'AI job trends in 2024'. Return results as a list of title, URL, and snippet.",
expected_output="List of search results with titles, URLs, and snippets.",
agent=data_agent,
name="collect_data",
)
# 4. Start Agents
agents = PraisonAIAgents(
agents=[data_agent],
tasks=[collect_task],
process="sequential"
)
agents.start()
```
## Advanced Tool Features
### Tool Configuration
<Frame>
<div className="p-4 bg-white dark:bg-gray-800 rounded-lg">
```python
def configured_tool(
query: str,
max_results: int = 5,
timeout: int = 10
) -> List[Dict]:
"""
Example of a configurable tool
Args:
query (str): Search query
max_results (int): Maximum number of results
timeout (int): Request timeout in seconds
Returns:
List[Dict]: Search results
"""
# Tool implementation
pass
```
</div>
</Frame>
### Tool Chaining
<Frame>
<div className="p-4 bg-white dark:bg-gray-800 rounded-lg">
```python
def chain_tools(input_data: str) -> Dict:
"""
Example of chaining multiple tools
Args:
input_data (str): Input data
Returns:
Dict: Processed results
"""
# 1. Search for data
search_results = internet_search_tool(input_data)
# 2. Process results
processed_data = process_tool(search_results)
# 3. Format output
return format_tool(processed_data)
```
</div>
</Frame>
### Tool Categories
1. **Data Collection Tools**
- Web scraping
- API integration
- Database queries
2. **Processing Tools**
- Data transformation
- Text analysis
- Image processing
3. **Output Tools**
- File generation
- Report creation
- Data visualization
## Tool Integration
### Adding Tools to Agents
<Frame>
<div className="p-4 bg-white dark:bg-gray-800 rounded-lg">
```python
# Multiple tools
agent = Agent(
name="MultiTool Agent",
tools=[
internet_search_tool,
file_processing_tool,
api_integration_tool
]
)
```
</div>
</Frame>
### Tool Dependencies
<Frame>
<div className="p-4 bg-white dark:bg-gray-800 rounded-lg">
```python
# Tool with dependencies
def advanced_tool(data: Dict) -> Dict:
"""
Tool that depends on external libraries
Args:
data (Dict): Input data
Returns:
Dict: Processed data
"""
try:
import required_library
# Tool implementation
return processed_result
except ImportError:
raise Exception("Required library not installed")
```
</div>
</Frame>
## Tool Guidelines
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-4 border rounded-lg bg-white dark:bg-gray-800">
<h3 className="text-lg font-semibold mb-3">Best Practices</h3>
1. **Type Hints**
- Use Python type hints
- Define clear input/output types
- Document complex types
2. **Documentation**
- Write clear docstrings
- Explain parameters
- Provide usage examples
3. **Error Handling**
- Handle exceptions gracefully
- Return meaningful errors
- Validate inputs
</div>
<div className="p-4 border rounded-lg bg-white dark:bg-gray-800">
<h3 className="text-lg font-semibold mb-3">Tool Types</h3>
1. **Search Tools**
- Web search
- Database queries
- Document search
2. **File Tools**
- Read/write operations
- File conversion
- Data extraction
3. **API Tools**
- REST API calls
- GraphQL queries
- Service integration
</div>
</div>
## Best Practices Summary
1. **Design Principles**
- Single responsibility
- Clear interfaces
- Proper documentation
- Error handling
2. **Performance**
- Efficient processing
- Resource management
- Caching when appropriate
- Asynchronous operations
3. **Security**
- Input validation
- Rate limiting
- API key management
- Error masking
+1
View File
@@ -1,6 +1,7 @@
---
title: "Contributing"
description: "Guide for contributing to PraisonAI through GitHub, including forking, cloning, and submitting pull requests"
icon: "code-branch"
---
- Fork on GitHub: Use the "Fork" button on the repository page.
+1
View File
@@ -1,6 +1,7 @@
---
title: "Deploy"
description: "Guide for deploying PraisonAI to cloud platforms, with detailed instructions for Google Cloud deployment"
icon: "cloud"
---
## Google Cloud
+1
View File
@@ -1,6 +1,7 @@
---
title: "Agents Playbook"
description: "Examples and templates for creating PraisonAI agent playbooks, from simple to detailed configurations with multiple roles and tasks"
icon: "book"
---
# Agents Playbook
+1
View File
@@ -1,6 +1,7 @@
---
title: "Google Colab Tools"
description: "Guide for implementing and using custom tools in PraisonAI with Google Colab, including examples of internet search and data processing tools"
icon: "wrench"
---
```
+14
View File
@@ -1,8 +1,22 @@
---
title: "Google Colab Integration"
description: "Guide for using PraisonAI in Google Colab environments, with example code and configurations for agent-based tasks"
icon: "google"
---
<CardGroup cols={2}>
<Card title="Basic PraisonAI" icon="book">
<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>
</Card>
<Card title="PraisonAI with Tools" icon="screwdriver-wrench">
<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>
</Card>
</CardGroup>
```
jupyter:
accelerator: GPU
+1
View File
@@ -1,6 +1,7 @@
---
title: "Test"
description: "Instructions for running and writing tests for PraisonAI components, including unit tests and test automation"
icon: "vial"
---
# Test
+1
View File
@@ -1,6 +1,7 @@
---
title: "Integrate with Tools"
description: "Guide for integrating external tools and APIs with PraisonAI, including examples of custom tool creation and implementation"
icon: "plug"
---
# Integrate with Tools
+1
View File
@@ -1,6 +1,7 @@
---
title: "PraisonAI Package Integration"
description: "Guide for integrating the PraisonAI package into your Python projects, including YAML configuration and different execution modes"
icon: "box"
---
# Include praisonai package in your project
+494
View File
@@ -0,0 +1,494 @@
---
title: "Async AI Agents"
description: "Async AI Agents allow you to run AI tasks asynchronously, improving performance and efficiency in your applications."
icon: "clock"
---
<Note>
**Requirements**
- Python 3.10 or higher
- Basic understanding of async/await in Python
</Note>
## Quick Start
<Steps>
<Step title="Install Package">
First, install the PraisonAI Agents package:
```bash
pip install praisonaiagents
```
</Step>
<Step title="Set API Key">
Set your OpenAI API key as an environment variable:
```bash
export OPENAI_API_KEY=your_api_key_here
```
</Step>
<Step title="Create Project">
Create a new file `app.py` with the basic setup:
```python
import asyncio
from praisonaiagents import Agent, Task, PraisonAIAgents
# Create an agent
agent = Agent(
name="AsyncAgent",
role="Assistant",
goal="Help with tasks",
backstory="Expert in async operations"
)
# Create a task
task = Task(
name="hello_task",
description="Say hello and introduce yourself",
agent=agent,
async_execution=True,
expected_output="Answer to the question"
)
# Create agents manager
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
process="sequential"
)
# Main async function
async def main():
result = await agents.astart()
print(result)
# Run the program
if __name__ == "__main__":
asyncio.run(main())
```
</Step>
<Step title="Run the Program">
Run your async AI agent:
```bash
python app.py
```
</Step>
</Steps>
<div className="relative w-full aspect-video">
<iframe
className="absolute top-0 left-0 w-full h-full"
src="https://www.youtube.com/embed/VhVQfgo00LE"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## Understanding Async Execution
<Card title="What is Asynchronous Execution?" icon="question">
Async execution lets your program continue running while waiting for operations to complete. This is ideal for:
- Making multiple AI API calls
- Processing large datasets
- Handling multiple tasks simultaneously
- Maintaining responsive applications
</Card>
<CodeGroup>
```python Sync
# Synchronous execution (blocks until complete)
result = agent.chat("Hello")
```
```python Async
# Asynchronous execution (non-blocking)
result = await agent.achat("Hello")
```
</CodeGroup>
## Features
<CardGroup cols={2}>
<Card title="Async Tasks" icon="tasks">
Create tasks that run asynchronously with built-in error handling and retries.
</Card>
<Card title="Parallel Processing" icon="arrows-split-up-and-left">
Run multiple tasks in parallel using asyncio.gather().
</Card>
<Card title="Mixed Mode" icon="shuffle">
Mix sync and async tasks in the same workflow seamlessly.
</Card>
<Card title="Resource Management" icon="gear">
Built-in resource management and performance optimization.
</Card>
</CardGroup>
## Advanced Usage
### Async Tasks with Callbacks and Tools Example Code
```python
import asyncio
import time
from typing import List, Dict
from praisonaiagents import Agent, Task, PraisonAIAgents, TaskOutput
from duckduckgo_search import DDGS
from pydantic import BaseModel
# 1. Define async tool
class SearchResult(BaseModel):
query: str
results: List[Dict[str, str]]
total_results: int
async def async_search_tool(query: str) -> Dict:
"""
Asynchronous search using DuckDuckGo.
Args:
query (str): The search query.
Returns:
dict: Search results in SearchResult model format
"""
await asyncio.sleep(1) # Simulate network delay
try:
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=5):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
# Format response to match SearchResult model
return {
"query": query,
"results": results,
"total_results": len(results)
}
except Exception as e:
print(f"Error during async search: {e}")
return {
"query": query,
"results": [],
"total_results": 0
}
# 2. Define async callback
async def async_callback(output: TaskOutput):
await asyncio.sleep(1) # Simulate processing
if output.output_format == "JSON":
print(f"Processed JSON result: {output.json_dict}")
elif output.output_format == "Pydantic":
print(f"Processed Pydantic result: {output.pydantic}")
# 3. Create specialized agents
async_agent = Agent(
name="AsyncSearchAgent",
role="Asynchronous Search Specialist",
goal="Perform fast and efficient asynchronous searches with structured results",
backstory="Expert in parallel search operations and data retrieval",
tools=[async_search_tool],
self_reflect=False,
verbose=True,
markdown=True
)
summary_agent = Agent(
name="SummaryAgent",
role="Research Synthesizer",
goal="Create comprehensive summaries and identify patterns across multiple search results",
backstory="""Expert in analyzing and synthesizing information from multiple sources.
Skilled at identifying patterns, trends, and connections between different topics.
Specializes in creating clear, structured summaries that highlight key insights.""",
self_reflect=True, # Enable self-reflection for better summary quality
verbose=True,
markdown=True
)
# 4. Create async tasks
async_task = Task(
name="async_search",
description="""Search for 'Async programming' and return results in the following JSON format:
{
"query": "the search query",
"results": [
{
"title": "result title",
"url": "result url",
"snippet": "result snippet"
}
],
"total_results": number of results
}""",
expected_output="SearchResult model with query details and results",
agent=async_agent,
async_execution=True,
callback=async_callback,
output_pydantic=SearchResult
)
# 5. Example usage functions
async def run_single_task():
"""Run single async task"""
print("\nRunning Single Async Task...")
agents = PraisonAIAgents(
agents=[async_agent],
tasks=[async_task],
verbose=1,
process="sequential"
)
result = await agents.astart()
print(f"Single Task Result: {result}")
async def run_parallel_tasks():
"""Run multiple async tasks in parallel"""
print("\nRunning Parallel Async Tasks...")
# Define different search topics
search_topics = [
"Latest AI Developments 2024",
"Machine Learning Best Practices",
"Neural Networks Architecture"
]
# Create tasks for different topics
parallel_tasks = [
Task(
name=f"search_task_{i}",
description=f"""Search for '{topic}' and return results in the following JSON format:
{{
"query": "{topic}",
"results": [
{{
"title": "result title",
"url": "result url",
"snippet": "result snippet"
}}
],
"total_results": number of results
}}""",
expected_output="SearchResult model with detailed information",
agent=async_agent,
async_execution=True,
callback=async_callback,
output_pydantic=SearchResult
) for i, topic in enumerate(search_topics)
]
# Create summarization task with the specialized summary agent
summary_task = Task(
name="summary_task",
description="""As a Research Synthesizer, analyze the search results and create a comprehensive summary. Your task:
1. Analyze Results:
- Review all search results thoroughly
- Extract key findings from each topic
- Identify main themes and concepts
2. Find Connections:
- Identify relationships between topics
- Spot common patterns or contradictions
- Note emerging trends across sources
3. Create Structured Summary:
- Main findings per topic
- Cross-cutting themes
- Emerging trends
- Practical implications
- Future directions
4. Quality Checks:
- Ensure all topics are covered
- Verify accuracy of connections
- Confirm clarity of insights
- Validate practical relevance
Present the summary in a clear, structured format with sections for findings, patterns, trends, and implications.""",
expected_output="""A comprehensive research synthesis containing:
- Detailed findings from each search topic
- Cross-topic patterns and relationships
- Emerging trends and their implications
- Practical applications and future directions""",
agent=summary_agent, # Use the specialized summary agent
async_execution=False, # Run synchronously after search tasks
callback=async_callback
)
# Create a single PraisonAIAgents instance with both agents
agents = PraisonAIAgents(
agents=[async_agent, summary_agent], # Include both agents
tasks=parallel_tasks + [summary_task], # Include all tasks
verbose=1,
process="sequential" # Tasks will run in sequence, with parallel tasks running first
)
# Run all tasks
results = await agents.astart()
print(f"Tasks Results: {results}")
# Return results in a serializable format
return {
"search_results": {
"task_status": {k: v for k, v in results["task_status"].items() if k != summary_task.id},
"task_results": [str(results["task_results"][i]) if results["task_results"][i] else None
for i in range(len(parallel_tasks))]
},
"summary": str(results["task_results"][summary_task.id]) if results["task_results"].get(summary_task.id) else None,
"topics": search_topics
}
# 6. Main execution
async def main():
"""Main execution function"""
print("Starting Async AI Agents Examples...")
try:
# Run different async patterns
await run_single_task()
await run_parallel_tasks()
except Exception as e:
print(f"Error in main execution: {e}")
if __name__ == "__main__":
# Run the main function
asyncio.run(main())
```
### 1. Async Tasks with Callbacks
<CodeGroup>
```python Task
# Create an async task with callback
async_task = Task(
name="weather_task",
description="Check weather conditions",
agent=agent,
async_execution=True,
callback=async_callback
)
```
```python Callback
async def async_callback(output):
await process_result(output)
await save_to_database(output)
```
</CodeGroup>
### 2. Parallel Task Processing
<Warning>
Be mindful of rate limits and resource usage when processing tasks in parallel.
</Warning>
```python
async def process_multiple_tasks():
tasks = [
Task(
name=f"task_{i}",
description=f"Process item {i}",
async_execution=True
) for i in range(5)
]
results = await asyncio.gather(
*[agent.achat(task.description) for task in tasks]
)
return results
```
## Best Practices
<AccordionGroup>
<Accordion title="Error Handling">
```python
async def safe_async_operation():
try:
result = await agent.achat("Process this")
return result
except Exception as e:
logging.error(f"Error: {e}")
return None
```
</Accordion>
<Accordion title="Resource Management">
```python
async def efficient_processing():
semaphore = asyncio.Semaphore(5)
async with semaphore:
result = await agent.achat("Process within limits")
return result
```
</Accordion>
<Accordion title="Performance Tips">
- Use `asyncio.gather()` for parallel operations
- Implement proper error handling
- Monitor memory usage
- Use timeouts for long-running operations
</Accordion>
</AccordionGroup>
## Troubleshooting
<CardGroup cols={2}>
<Card title="ChatCompletion Error" icon="triangle-exclamation">
If you see "ChatCompletion can't be used in await expression":
- Use AsyncOpenAI() client
- Ensure proper async context
</Card>
<Card title="Event Loop Error" icon="circle-exclamation">
If you get "Event loop is closed":
- Use asyncio.run() for main entry
- Check async context
</Card>
</CardGroup>
## API Reference
### Async Methods
<ResponseField name="achat()" type="async">
Async version of chat method
</ResponseField>
<ResponseField name="aexecute_tool()" type="async">
Async tool execution method
</ResponseField>
<ResponseField name="astart()" type="async">
Async task execution starter
</ResponseField>
### Task Properties
<ResponseField name="async_execution" type="boolean">
Enable/disable async mode for tasks
</ResponseField>
<ResponseField name="callback" type="function">
Set sync or async callback function
</ResponseField>
## Next Steps
<CardGroup cols={2}>
<Card title="Examples" icon="code" href="./examples">
Check out more examples in our examples directory
</Card>
<Card title="Advanced Topics" icon="book" href="./advanced">
Learn about advanced async patterns and optimizations
</Card>
</CardGroup>
<Note>
Remember to handle errors properly, manage resources efficiently, and test thoroughly in async contexts.
</Note>
+783
View File
@@ -0,0 +1,783 @@
---
title: "AI Agents with Callbacks"
sidebarTitle: "Callbacks"
description: "Learn how to implement callbacks to monitor and log AI agent interactions, errors, and task completions."
icon: "bell"
---
<Note>
**Prerequisites**
- Python 3.10 or higher
- PraisonAI Agents package installed
- Basic understanding of Python functions
</Note>
## Quick Start
<Steps>
<Step title="Install Package">
First, install the PraisonAI Agents package:
```bash
pip install praisonaiagents
```
</Step>
<Step title="Import Required Functions">
Import the necessary components:
```python
from praisonaiagents import (
register_display_callback,
Agent,
Task,
PraisonAIAgents
)
```
</Step>
<Step title="Create a Simple Callback">
Define your first callback function:
```python
def simple_callback(message=None, response=None, **kwargs):
print(f"Received message: {message}")
print(f"Got response: {response}")
# Register as synchronous callback
register_display_callback('interaction', simple_callback, is_async=False)
# For async callbacks
async def async_simple_callback(message=None, response=None, **kwargs):
await asyncio.sleep(0) # Non-blocking pause
print(f"Received message: {message}")
print(f"Got response: {response}")
# Register as async callback
register_display_callback('interaction', async_simple_callback, is_async=True)
```
</Step>
<Step title="Use in Your Code">
Create and run an agent with callbacks enabled:
```python
# Create an agent
agent = Agent(
name="MyAgent",
role="Assistant",
goal="Help with tasks",
backstory="I am a helpful assistant",
verbose=True # Enable verbose mode to see callbacks in action
)
# Create a task
task = Task(
name="simple_task",
description="Say hello",
agent=agent,
expected_output="A greeting"
)
# Run the agent
agents = PraisonAIAgents(
agents=[agent],
tasks=[task]
)
agents.start()
```
</Step>
</Steps>
<Note>
PraisonAI Agents supports both synchronous and asynchronous callbacks. When registering a callback, specify `is_async=True` for async callbacks. Both sync and async callbacks for the same display type can be executed simultaneously.
</Note>
## Understanding Callbacks
<Card title="What are Callbacks?" icon="question">
Callbacks are functions that get called automatically when specific events occur in your AI agents:
- Interactions between user and agent
- Error messages
- Tool calls
- Self-reflection moments
- Task completion
- Generation progress
</Card>
## Available Callback Types
<CardGroup cols={2}>
<Card title="Interaction Callback" icon="comments">
Triggered when the agent interacts with users
</Card>
<Card title="Error Callback" icon="triangle-exclamation">
Called when errors occur
</Card>
<Card title="Tool Call Callback" icon="wrench">
Activated when tools are used
</Card>
<Card title="Self Reflection Callback" icon="brain">
Triggered during agent self-reflection
</Card>
<Card title="Instruction Callback" icon="list-check">
Called when instructions are processed
</Card>
<Card title="Generating Callback" icon="spinner">
Activated during content generation
</Card>
</CardGroup>
## Basic Implementation
### 1. Simple Logging Callback
<CodeGroup>
```python Basic
import logging
# Setup logging
logging.basicConfig(level=logging.INFO)
def log_callback(message=None, **kwargs):
logging.info(f"Agent message: {message}")
# Register synchronous callback
register_display_callback('interaction', log_callback, is_async=False)
# Register asynchronous callback
async def async_log_callback(message=None, **kwargs):
await asyncio.sleep(0)
logging.info(f"Agent message: {message}")
# Register as async callback
register_display_callback('interaction', async_log_callback, is_async=True)
```
```python Advanced
import logging
from datetime import datetime
# Setup logging with file output
logging.basicConfig(
filename='ai_interactions.log',
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
def detailed_callback(message=None, response=None, **kwargs):
logging.info(f"""
Time: {datetime.now()}
Message: {message}
Response: {response}
Additional Info: {kwargs}
""")
register_display_callback('interaction', detailed_callback)
```
</CodeGroup>
### 2. Multiple Callback Types
```python
# Error callback
def error_callback(message=None):
logging.error(f"Error occurred: {message}")
# Tool call callback
def tool_callback(message=None):
logging.info(f"Tool called: {message}")
# Register multiple callbacks
register_display_callback('error', error_callback)
register_display_callback('tool_call', tool_callback)
```
## Best Practices
<AccordionGroup>
<Accordion title="Error Handling">
Always include error handling in your callbacks:
```python
def safe_callback(message=None, **kwargs):
try:
# Process the callback
logging.info(message)
except Exception as e:
logging.error(f"Callback error: {e}")
```
</Accordion>
<Accordion title="Performance">
Keep callbacks lightweight:
```python
# Good - Quick logging
def good_callback(message=None):
logging.info(message)
# Bad - Heavy processing
def bad_callback(message=None):
time.sleep(1) # Delays agent execution
process_heavy_task(message)
```
</Accordion>
</AccordionGroup>
## Complete Example
Here's a full implementation showing all callback types and proper logging:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents, register_display_callback
import logging
from datetime import datetime
# Setup logging
logging.basicConfig(
filename='ai_interactions.log',
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Interaction callback
def interaction_callback(message=None, response=None, markdown=None, generation_time=None):
logging.info(f"""
=== INTERACTION ===
Time: {datetime.now()}
Generation Time: {generation_time}s
Message: {message}
Response: {response}
Markdown: {markdown}
""")
# Error callback
def error_callback(message=None):
logging.error(f"""
=== ERROR ===
Time: {datetime.now()}
Message: {message}
""")
# Tool call callback
def tool_call_callback(message=None):
logging.info(f"""
=== TOOL CALL ===
Time: {datetime.now()}
Message: {message}
""")
# Register callbacks
register_display_callback('interaction', interaction_callback)
register_display_callback('error', error_callback)
register_display_callback('tool_call', tool_call_callback)
agent = Agent(
name="CallbackAgent",
role="Assistant",
goal="Demonstrate callbacks",
backstory="I am a helpful assistant",
verbose=True
)
task = Task(
name="callback_task",
description="Show how callbacks work",
agent=agent,
expected_output="Demonstration complete"
)
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
verbose=True
)
agents.start()
```
## Advanced Examples
- All callback types
- Comprehensive logging
- Task callbacks
- Tool integration
- Multiple agents
```python
from praisonaiagents import Agent, Task, PraisonAIAgents, error_logs, register_display_callback
from duckduckgo_search import DDGS
from rich.console import Console
import json
from datetime import datetime
import logging
# Setup logging
logging.basicConfig(
filename='ai_interactions.log',
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Callback functions for different display types
def interaction_callback(message=None, response=None, markdown=None, generation_time=None):
"""Callback for display_interaction"""
logging.info(f"""
=== INTERACTION ===
Time: {datetime.now()}
Generation Time: {generation_time}s
Message: {message}
Response: {response}
Markdown: {markdown}
""")
def error_callback(message=None):
"""Callback for display_error"""
logging.error(f"""
=== ERROR ===
Time: {datetime.now()}
Message: {message}
""")
def tool_call_callback(message=None):
"""Callback for display_tool_call"""
logging.info(f"""
=== TOOL CALL ===
Time: {datetime.now()}
Message: {message}
""")
def instruction_callback(message=None):
"""Callback for display_instruction"""
logging.info(f"""
=== INSTRUCTION ===
Time: {datetime.now()}
Message: {message}
""")
def self_reflection_callback(message=None):
"""Callback for display_self_reflection"""
logging.info(f"""
=== SELF REFLECTION ===
Time: {datetime.now()}
Message: {message}
""")
def generating_callback(content=None, elapsed_time=None):
"""Callback for display_generating"""
logging.info(f"""
=== GENERATING ===
Time: {datetime.now()}
Content: {content}
Elapsed Time: {elapsed_time}
""")
# Register all callbacks
register_display_callback('interaction', interaction_callback)
register_display_callback('error', error_callback)
register_display_callback('tool_call', tool_call_callback)
register_display_callback('instruction', instruction_callback)
register_display_callback('self_reflection', self_reflection_callback)
# register_display_callback('generating', generating_callback)
def task_callback(output):
"""Callback for task completion"""
logging.info(f"""
=== TASK COMPLETED ===
Time: {datetime.now()}
Description: {output.description}
Agent: {output.agent}
Output: {output.raw[:200]}...
""")
def internet_search_tool(query) -> list:
"""
Perform a search using DuckDuckGo.
Args:
query (str): The search query.
Returns:
list: A list of search result titles and URLs.
"""
try:
results = []
ddgs = DDGS()
for result in ddgs.text(keywords=query, max_results=10):
results.append({
"title": result.get("title", ""),
"url": result.get("href", ""),
"snippet": result.get("body", "")
})
return results
except Exception as e:
print(f"Error during DuckDuckGo search: {e}")
return []
def main():
# 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,
allow_delegation=False,
tools=[internet_search_tool],
llm="gpt-4o",
markdown=True,
reflect_llm="gpt-4o",
min_reflect=2,
max_reflect=4
)
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.""",
verbose=True,
allow_delegation=True,
llm="gpt-4o",
tools=[],
markdown=True
)
# Create tasks with callbacks
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,
tools=[internet_search_tool],
callback=task_callback
)
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,
context=[task1],
callback=task_callback,
tools=[]
)
task3 = Task(
name="json_task",
description="""Create a json object with a title of "My Task" and content of "My content".""",
expected_output="""JSON output with title and content""",
agent=researcher,
callback=task_callback
)
task4 = Task(
name="save_output_task",
description="""Save the AI blog post to a file""",
expected_output="""File saved successfully""",
agent=writer,
context=[task2],
output_file='test.txt',
create_directory=True,
callback=task_callback
)
# Create and run agents manager
agents = PraisonAIAgents(
agents=[researcher, writer],
tasks=[task1, task2, task3, task4],
verbose=True,
process="sequential",
manager_llm="gpt-4o"
)
agents.start()
if __name__ == "__main__":
main()
## Async Callbacks
Async callbacks allow you to handle events asynchronously, which is particularly useful for long-running operations or when dealing with multiple agents simultaneously.
### Basic Async Callback Implementation
```python
import asyncio
from praisonaiagents import register_display_callback
async def async_interaction_callback(message=None, response=None, **kwargs):
"""Async callback for handling interactions"""
await asyncio.sleep(0) # Non-blocking pause
print(f"Async processing - Message: {message}")
print(f"Response: {response}")
# Register the async callback
register_display_callback('interaction', async_interaction_callback)
```
### Complete Async Example
```python
import asyncio
from praisonaiagents import Agent, Task, PraisonAIAgents, register_display_callback
import logging
from datetime import datetime
# Setup async logging
async def setup_async_logging():
logging.basicConfig(
filename='async_ai_interactions.log',
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
# Async callbacks
async def async_interaction_callback(message=None, response=None, markdown=None, generation_time=None):
await asyncio.sleep(0)
logging.info(f"""
=== ASYNC INTERACTION ===
Time: {datetime.now()}
Generation Time: {generation_time}s
Message: {message}
Response: {response}
""")
async def async_error_callback(message=None):
await asyncio.sleep(0)
logging.error(f"""
=== ASYNC ERROR ===
Time: {datetime.now()}
Message: {message}
""")
# Register async callbacks
register_display_callback('interaction', async_interaction_callback, is_async=True)
register_display_callback('error', async_error_callback, is_async=True)
# Create async task callback
async def async_task_callback(output):
await asyncio.sleep(0)
logging.info(f"""
=== ASYNC TASK COMPLETED ===
Time: {datetime.now()}
Description: {output.description}
Agent: {output.agent}
Output: {output.raw[:200]}...
""")
async def main():
await setup_async_logging()
# Create agent with async capabilities
agent = Agent(
name="AsyncAgent",
role="Assistant",
goal="Demonstrate async callbacks",
backstory="I am an async-capable assistant",
verbose=True
)
# Create task with async callback
task = Task(
name="async_task",
description="Demonstrate async callbacks",
agent=agent,
expected_output="Async demonstration complete",
callback=async_task_callback
)
# Create and run agents with async support
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
verbose=True
)
await agents.start_async()
if __name__ == "__main__":
asyncio.run(main())
```
### Available Async Functions
PraisonAI Agents provides several async versions of display functions, prefixed with 'a'. Here's the complete list:
<CardGroup cols={2}>
<Card title="adisplay_interaction" icon="comments">
```python
async def adisplay_interaction(
message,
response,
markdown=True,
generation_time=None,
console=None
)
```
Async version of display_interaction for showing conversations.
</Card>
<Card title="adisplay_self_reflection" icon="brain">
```python
async def adisplay_self_reflection(
message: str,
console=None
)
```
Async version for displaying agent's self-reflection moments.
</Card>
<Card title="adisplay_instruction" icon="list-check">
```python
async def adisplay_instruction(
message: str,
console=None
)
```
Async version for showing instructions.
</Card>
<Card title="adisplay_tool_call" icon="wrench">
```python
async def adisplay_tool_call(
message: str,
console=None
)
```
Async version for displaying tool calls.
</Card>
<Card title="adisplay_error" icon="triangle-exclamation">
```python
async def adisplay_error(
message: str,
console=None
)
```
Async version for error messages.
</Card>
<Card title="adisplay_generating" icon="spinner">
```python
async def adisplay_generating(
content: str = "",
start_time: Optional[float] = None
)
```
Async version for showing generation progress.
</Card>
</CardGroup>
#### Example Usage
```python
import asyncio
from praisonaiagents import (
adisplay_interaction,
adisplay_error,
adisplay_tool_call
)
async def main():
# Display an interaction
await adisplay_interaction(
message="What's the weather?",
response="Let me check that for you.",
generation_time=0.5
)
# Display a tool call
await adisplay_tool_call(
"Calling weather API for location data..."
)
# Handle an error
try:
raise Exception("API connection failed")
except Exception as e:
await adisplay_error(str(e))
if __name__ == "__main__":
asyncio.run(main())
```
### Best Practices for Async Callbacks
<AccordionGroup>
<Accordion title="Error Handling">
Implement proper error handling in async callbacks:
```python
async def safe_async_callback(message=None, **kwargs):
try:
await asyncio.sleep(0)
# Process the callback
await process_message(message)
except Exception as e:
logging.error(f"Async callback error: {e}")
```
</Accordion>
<Accordion title="Performance">
Optimize async callbacks for better performance:
```python
async def optimized_async_callback(message=None, **kwargs):
# Use asyncio.gather for concurrent operations
results = await asyncio.gather(
process_message(message),
update_status(),
log_interaction(message)
)
return results
```
</Accordion>
</AccordionGroup>
### Key Considerations
<CardGroup cols={2}>
<Card title="Concurrency" icon="arrows-split-up-and-left">
- Use asyncio.gather for parallel operations
- Avoid blocking calls in async callbacks
- Handle concurrent access to shared resources
</Card>
<Card title="Resource Management" icon="server">
- Implement proper cleanup in async contexts
- Use async context managers when appropriate
- Monitor memory usage in long-running callbacks
</Card>
</CardGroup>
<Note>
When using async callbacks, ensure all dependent operations are also async-compatible to prevent blocking the event loop.
</Note>
## Troubleshooting
<CardGroup cols={2}>
<Card title="Callback Not Firing" icon="bug">
- Verify callback registration
- Check verbose mode is enabled
- Ensure correct callback name
</Card>
<Card title="Logging Issues" icon="file-lines">
- Check file permissions
- Verify logging configuration
- Monitor log file size
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="Async Callbacks" icon="bolt" href="./async">
Learn about asynchronous callbacks
</Card>
<Card title="Advanced Patterns" icon="code" href="./advanced">
Explore advanced callback patterns
</Card>
</CardGroup>
<Note>
Remember to handle callbacks efficiently and implement proper error handling to ensure smooth agent operations.
</Note>
+287
View File
@@ -0,0 +1,287 @@
---
title: "Code Execution AI Agent"
sidebarTitle: "Code Agent"
description: "Learn how to create AI agents that can write and execute Python code safely using e2b code interpreter."
icon: "code"
---
<Note>
**Prerequisites**
- Python 3.10 or higher
- PraisonAI Agents package installed
- e2b_code_interpreter package installed
- Basic understanding of Python
</Note>
## Quick Start
Create AI agents that can write and execute Python code in a sandboxed environment!
<Steps>
<Step title="Install Packages">
First, install the required packages:
```bash
pip install praisonaiagents e2b_code_interpreter
```
</Step>
<Step title="Import Required Components">
Import the necessary components:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
from e2b_code_interpreter import Sandbox
```
</Step>
<Step title="Define Code Interpreter">
Create a code interpreter function:
```python
def code_interpreter(code: str):
print(f"\n{'='*50}\n> Running following AI-generated code:\n{code}\n{'='*50}")
exec_result = Sandbox().run_code(code)
if exec_result.error:
print("[Code Interpreter error]", exec_result.error)
return {"error": str(exec_result.error)}
else:
results = []
for result in exec_result.results:
if hasattr(result, '__iter__'):
results.extend(list(result))
else:
results.append(str(result))
logs = {"stdout": list(exec_result.logs.stdout), "stderr": list(exec_result.logs.stderr)}
return json.dumps({"results": results, "logs": logs})
```
</Step>
<Step title="Create Code Agents">
Set up your code writing and execution agents:
```python
code_agent = Agent(
name="code_agent",
backstory="Expert in writing Python scripts",
self_reflect=False
)
execution_agent = Agent(
name="execution_agent",
backstory="Expert in executing Python scripts",
self_reflect=False,
tools=[code_interpreter]
)
```
</Step>
</Steps>
## Understanding Code Agents
<Card title="What are Code Agents?" icon="question">
Code agents are specialized AI agents that can:
- Write Python code based on requirements
- Execute code safely in a sandboxed environment
- Handle code execution results and errors
- Work together in a pipeline (writer → executor)
</Card>
## Key Components
<CardGroup cols={2}>
<Card title="Code Writer Agent" icon="pencil">
Specialized in writing Python code
```python
Agent(
name="code_agent",
backstory="Expert in writing Python scripts"
)
```
</Card>
<Card title="Code Executor Agent" icon="play">
Executes code using sandbox
```python
Agent(
name="execution_agent",
tools=[code_interpreter]
)
```
</Card>
<Card title="Sandbox Environment" icon="box">
Safe code execution environment using e2b
</Card>
<Card title="Execution Results" icon="output">
Structured output with results and logs
</Card>
</CardGroup>
## Examples
### Simple Hello World Example
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
from e2b_code_interpreter import Sandbox
# Create agents
code_agent = Agent(
name="code_agent",
backstory="Expert in writing Python scripts",
self_reflect=False
)
execution_agent = Agent(
name="execution_agent",
backstory="Expert in executing Python scripts",
self_reflect=False,
tools=[code_interpreter]
)
# Define tasks
code_task = Task(
description="Write a simple Python script to print 'Hello, World!'",
expected_output="A Python script that prints 'Hello, World!'",
agent=code_agent
)
execution_task = Task(
description="Execute the Python script",
expected_output="The output of the Python script",
agent=execution_agent
)
# Initialize and run agents
agents = PraisonAIAgents(
agents=[code_agent, execution_agent],
tasks=[code_task, execution_task]
)
result = agents.start()
```
### Advanced Code Generation
<CodeGroup>
```python Data Analysis
# Create a data analysis script
code_task = Task(
description="""
Write a Python script that:
1. Creates a sample dataset
2. Performs basic statistical analysis
3. Generates a plot
""",
agent=code_agent
)
```
```python Web Scraping
# Create a web scraping script
code_task = Task(
description="""
Write a Python script that:
1. Scrapes a website
2. Extracts specific data
3. Saves to CSV
""",
agent=code_agent
)
```
</CodeGroup>
## Best Practices
<AccordionGroup>
<Accordion title="Code Safety">
Ensure safe code execution:
```python
# Always use sandbox environment
exec_result = Sandbox().run_code(code)
# Handle errors gracefully
if exec_result.error:
return {"error": str(exec_result.error)}
```
</Accordion>
<Accordion title="Task Description">
Write clear code requirements:
```python
# Good
Task(description="""
Write a Python script that:
1. Takes user input
2. Validates the input
3. Processes the data
4. Returns formatted output
""")
# Too vague
Task(description="Write some Python code")
```
</Accordion>
</AccordionGroup>
## Common Patterns
### Code Generation and Testing
```python
# Generate code
code_task = Task(
description="Write a function to calculate fibonacci numbers",
agent=code_agent
)
# Generate test cases
test_task = Task(
description="Write test cases for the fibonacci function",
agent=code_agent
)
# Execute tests
execution_task = Task(
description="Run the test cases",
agent=execution_agent
)
```
### Interactive Code Development
```python
# Write initial code
initial_code_task = Task(
description="Write initial version of the script",
agent=code_agent
)
# Test and improve
improvement_task = Task(
description="Analyze execution results and improve the code",
agent=code_agent
)
```
## Troubleshooting
<CardGroup cols={2}>
<Card title="Execution Errors" icon="triangle-exclamation">
Check sandbox execution results:
```python
if exec_result.error:
print("[Error]", exec_result.error)
```
</Card>
<Card title="Code Generation" icon="code">
Provide clear requirements and constraints in task description
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="Advanced Code Generation" icon="graduation-cap" href="./advanced-code">
Learn about complex code generation patterns
</Card>
<Card title="Custom Tools" icon="wrench" href="./custom-tools">
Create custom code execution tools
</Card>
</CardGroup>
<Note>
Code execution agents provide a powerful way to generate and run Python code safely. They're perfect for automated code generation, testing, and execution in a controlled environment.
</Note>
+279
View File
@@ -0,0 +1,279 @@
---
title: "Math AI Agent"
sidebarTitle: "Math Agent"
description: "Learn how to create AI agents that can perform complex mathematical calculations, unit conversions, and financial computations."
icon: "calculator"
---
<Note>
**Prerequisites**
- Python 3.10 or higher
- PraisonAI Agents package installed
- Basic understanding of Python
- sympy and pint packages (automatically installed when needed)
</Note>
## Quick Start
Create AI agents that can perform complex mathematical calculations, unit conversions, and financial computations!
<Steps>
<Step title="Install Package">
First, install the required package:
```bash
pip install praisonaiagents
```
</Step>
<Step title="Import Required Components">
Import the necessary components:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import (
evaluate, solve_equation, convert_units,
calculate_statistics, calculate_financial
)
```
</Step>
<Step title="Create Math Agent">
Set up your mathematical computation agent:
```python
math_agent = Agent(
name="MathAssistant",
description="Expert in mathematical calculations and analysis",
tools=[
evaluate, solve_equation, convert_units,
calculate_statistics, calculate_financial
]
)
```
</Step>
</Steps>
## Understanding Math Agents
<Card title="What are Math Agents?" icon="question">
Math agents are specialized AI agents that can:
- Evaluate mathematical expressions
- Solve equations and systems of equations
- Perform unit conversions
- Calculate statistical metrics
- Handle financial calculations
</Card>
## Key Components
<CardGroup cols={2}>
<Card title="Expression Evaluator" icon="function">
Evaluates mathematical expressions
```python
evaluate("2 + 2 * 3")
evaluate("sin(pi/2) + sqrt(16)")
```
</Card>
<Card title="Equation Solver" icon="equals">
Solves mathematical equations
```python
solve_equation("2*x + 3 = 7")
solve_equation("x^2 - 5*x + 6 = 0")
```
</Card>
<Card title="Unit Converter" icon="arrows-rotate">
Converts between different units
```python
convert_units(100, "meters", "kilometers")
```
</Card>
<Card title="Statistical Calculator" icon="chart-simple">
Calculates statistical metrics
```python
calculate_statistics([1, 2, 3, 4, 5])
```
</Card>
</CardGroup>
## Examples
### Basic Calculations Example
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import evaluate, solve_equation
# Create agent
math_agent = Agent(
name="MathAssistant",
description="Expert in mathematical calculations",
tools=[evaluate, solve_equation]
)
# Define tasks
calculation_task = Task(
description="Calculate the value of: (3 + 4) * 2 - sqrt(16)",
expected_output="The result of the mathematical expression",
agent=math_agent
)
equation_task = Task(
description="Solve the equation: x^2 - 5x + 6 = 0",
expected_output="The solutions to the quadratic equation",
agent=math_agent
)
# Initialize and run agents
agents = PraisonAIAgents(
agents=[math_agent],
tasks=[calculation_task, equation_task]
)
result = agents.start()
```
### Advanced Mathematical Operations
<CodeGroup>
```python Statistical Analysis
# Create a statistical analysis task
stats_task = Task(
description="""
Calculate statistical metrics for the dataset:
[12, 15, 18, 22, 25, 28, 32, 35, 38, 42]
""",
agent=math_agent
)
```
```python Financial Calculations
# Create a financial calculation task
finance_task = Task(
description="""
Calculate compound interest for:
- Principal: $10,000
- Rate: 5% annually
- Time: 3 years
- Compounding: Monthly
""",
agent=math_agent
)
```
</CodeGroup>
## Best Practices
<AccordionGroup>
<Accordion title="Expression Handling">
Handle expressions safely:
```python
# Good - Using variables dictionary
evaluate(
"x^2 + y",
variables={"x": 3, "y": 4}
)
# Good - Setting precision
evaluate(
"pi + e",
precision=5
)
```
</Accordion>
<Accordion title="Task Description">
Write clear mathematical requirements:
```python
# Good
Task(description="""
Calculate the following:
1. Area of a circle with radius 5
2. Convert the result to square inches
3. Round to 2 decimal places
""")
# Too vague
Task(description="Do some math")
```
</Accordion>
</AccordionGroup>
## Common Patterns
### Multi-Step Calculations
```python
# Calculate area
area_task = Task(
description="Calculate the area of a circle with radius 5",
agent=math_agent
)
# Convert units
conversion_task = Task(
description="Convert the area from square meters to square feet",
agent=math_agent
)
# Format result
formatting_task = Task(
description="Round the result to 2 decimal places",
agent=math_agent
)
```
### Statistical Analysis Pipeline
```python
# Gather data
data_task = Task(
description="Generate a dataset of 100 random numbers",
agent=math_agent
)
# Calculate statistics
stats_task = Task(
description="Calculate mean, median, mode, and standard deviation",
agent=math_agent
)
# Visualize results
viz_task = Task(
description="Create a summary of the statistical analysis",
agent=math_agent
)
```
## Troubleshooting
<CardGroup cols={2}>
<Card title="Expression Errors" icon="triangle-exclamation">
Check expression syntax and variable definitions:
```python
try:
result = evaluate("x + y", variables={"x": 1})
except Exception as e:
print(f"Error: Missing variable 'y'")
```
</Card>
<Card title="Unit Conversion" icon="ruler">
Ensure compatible unit types and valid unit names
```python
# Valid conversion
convert_units(100, "celsius", "fahrenheit")
# Invalid conversion
# convert_units(100, "meters", "celsius")
```
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="Advanced Mathematics" icon="graduation-cap" href="./advanced-math">
Learn about complex mathematical operations
</Card>
<Card title="Custom Math Tools" icon="wrench" href="./custom-tools">
Create custom mathematical computation tools
</Card>
</CardGroup>
<Note>
Math agents provide a powerful way to perform complex mathematical operations, statistical analysis, and financial calculations. They're perfect for data analysis, scientific computing, and financial modeling tasks.
</Note>
+258
View File
@@ -0,0 +1,258 @@
---
title: "Mini AI Agents"
description: "Learn how to create simple yet powerful AI agents in just a few lines of code."
icon: "robot"
---
<Note>
**Prerequisites**
- Python 3.10 or higher
- PraisonAI Agents package installed
- Basic understanding of Python
</Note>
## Quick Start
Create multiple AI agents that can work together in just a few lines of code!
<Steps>
<Step title="Install Package">
First, install the PraisonAI Agents package:
```bash
pip install praisonaiagents
```
</Step>
<Step title="Import Required Components">
Import the necessary components:
```python
from praisonaiagents import Agent, Agents
```
</Step>
<Step title="Create Your Agents">
Create specialized agents with specific roles:
```python
summarise_agent = Agent(instructions="Summarise what is Photosynthesis")
```
</Step>
<Step title="Start Your Agents">
Initialize and run your agents:
```python
agents = Agents(agents=[summarise_agent])
agents.start()
```
</Step>
</Steps>
<div className="relative w-full aspect-video">
<iframe
className="absolute top-0 left-0 w-full h-full"
src="https://www.youtube.com/embed/OkvYp5aAGSg"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## Understanding Mini AI Agents
<Card title="What are Mini AI Agents?" icon="question">
Mini AI Agents are simplified yet powerful AI agents that can:
- Work together to accomplish tasks
- Use tools like internet search
- Process and summarize information
- Execute tasks sequentially
</Card>
## Key Components
<CardGroup cols={2}>
<Card title="Agent" icon="user-robot">
Individual AI agents with specific roles and capabilities
```python
Agent(instructions="Your agent's role description")
```
</Card>
<Card title="Tools" icon="wrench">
Built-in tools that agents can use
```python
tools=[Tools.internet_search]
```
</Card>
<Card title="Agents Manager" icon="users">
Coordinates multiple agents
```python
Agents(agents=[agent1, agent2])
```
</Card>
<Card title="Sequential Flow" icon="arrow-right">
Agents work in sequence, passing results to each other
</Card>
</CardGroup>
## Available Tools
<CardGroup cols={2}>
<Card title="Internet Search" icon="search">
```python
Tools.internet_search
```
Search the internet using DuckDuckGo
<Note>
Requires: `pip install duckduckgo_search`
</Note>
</Card>
</CardGroup>
## Examples
### Basic Research and Summary
```terminal
pip install praisonaiagents duckduckgo_search
```
```python
from praisonaiagents import Agent, Agents, Tools
# Create a research agent
research_agent = Agent(
instructions="You are a research agent to search internet about AI 2024",
tools=[Tools.internet_search]
)
# Create a summarize agent
summarise_agent = Agent(
instructions="You are a summarize agent to summarise research agent's findings"
)
# Initialize and run agents
agents = Agents(agents=[research_agent, summarise_agent])
agents.start()
```
### Custom Instructions
<CodeGroup>
```python Basic
# Simple instructions
research_agent = Agent(
instructions="Research about climate change"
)
```
```python Advanced
# Detailed instructions
research_agent = Agent(
instructions="""
You are a research agent focused on gathering information about:
1. Latest AI developments in 2024
2. Major breakthroughs in machine learning
3. New AI applications in industry
Provide detailed and accurate information from reliable sources.
"""
)
```
</CodeGroup>
## Best Practices
<AccordionGroup>
<Accordion title="Agent Instructions">
Write clear and specific instructions:
```python
# Good
Agent(instructions="Research and analyze the latest AI developments in 2024")
# Too vague
Agent(instructions="Research AI")
```
</Accordion>
<Accordion title="Tool Usage">
Provide tools only to agents that need them:
```python
# Research agent needs search
research_agent = Agent(
instructions="Research AI",
tools=[Tools.internet_search]
)
# Summary agent doesn't need search
summary_agent = Agent(
instructions="Summarize findings"
)
```
</Accordion>
</AccordionGroup>
## Common Patterns
### Research and Analysis
```python
# Research agent
researcher = Agent(
instructions="Research latest developments in quantum computing",
tools=[Tools.internet_search]
)
# Analysis agent
analyst = Agent(
instructions="Analyze and explain the research findings in simple terms"
)
agents = Agents(agents=[researcher, analyst])
```
### Information Processing
```python
# Data collector
collector = Agent(
instructions="Collect information about renewable energy",
tools=[Tools.internet_search]
)
# Summarizer
summarizer = Agent(
instructions="Create a concise summary of the collected information"
)
# Report writer
writer = Agent(
instructions="Write a detailed report based on the summary"
)
agents = Agents(agents=[collector, summarizer, writer])
```
## Troubleshooting
<CardGroup cols={2}>
<Card title="Tool Not Available" icon="triangle-exclamation">
If using `Tools.internet_search`, install required package:
```bash
pip install duckduckgo_search
```
</Card>
<Card title="Agent Communication" icon="comments">
Ensure agent instructions are clear and complementary
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="Advanced Agents" icon="graduation-cap" href="./advanced">
Learn about advanced agent configurations
</Card>
<Card title="Custom Tools" icon="screwdriver-wrench" href="./tools">
Create your own agent tools
</Card>
</CardGroup>
<Note>
Mini AI Agents are designed to be simple yet powerful. They're perfect for quick prototypes and straightforward automation tasks.
</Note>
+239
View File
@@ -0,0 +1,239 @@
---
title: "Reasoning AI Agents"
sidebarTitle: "Reasoning"
description: "Reasoning enables AI agents to think logically, solve complex problems autonomously, and make human-like decisions through multi-step logical reasoning."
icon: "brain-circuit"
---
<div className="relative w-full aspect-video">
<iframe
className="absolute top-0 left-0 w-full h-full"
src="https://www.youtube.com/embed/KNDVWGN3TpM"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## Overview
Reasoning in PraisonAI agents enables:
- Autonomous problem solving
- Multi-step logical reasoning
- Human-like decision making
- Deep comprehension of tasks
- Error handling with alternative solutions
## Usage
Reasoning is controlled through reflection cycles using `min_reflect` and `max_reflect` parameters:
```python
from praisonaiagents import Agent
agent = Agent(
name="SystemOps",
role="System Administrator",
goal="Manage and monitor system operations",
backstory="You are an expert system administrator",
min_reflect=6, # Minimum reflection cycles
max_reflect=10 # Maximum reflection cycles
)
```
## Benefits
- **Deep Comprehension**: Instead of rule-based responses, agents understand tasks deeply
- **Error Recovery**: Agents can reason through errors and find alternative solutions
- **Step-by-Step Processing**: Complex tasks are broken down and processed systematically
- **Quality Control**: Multiple reflection cycles ensure high-quality outputs
- **Adaptive Problem Solving**: Agents can adjust their approach based on reasoning
## Example
Here's a complete example showing reasoning in action:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
import subprocess
import os
def run_terminal_command(command: str):
"""
Run a terminal command and return its output.
"""
try:
result = subprocess.run(command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
print(f"Command output: {result}")
return {"stdout": result.stdout, "stderr": result.stderr}
except subprocess.CalledProcessError as e:
return {"error": str(e), "stdout": e.stdout, "stderr": e.stderr}
def save_to_file(file_path: str, content: str):
"""
Save the given content to the specified file path. Create the folder/file if it doesn't exist.
"""
# Ensure the directory exists
os.makedirs(os.path.dirname(file_path), exist_ok=True)
# Write the content to the file
with open(file_path, 'w') as file:
file.write(content)
return file_path
# Create System Operations Agent
system_ops_agent = Agent(
name="SystemOps",
role="System Operations Specialist",
goal="Execute and manage complex system operations and commands",
backstory="""You are an expert system administrator with deep knowledge of Unix/Linux systems.
You excel at running complex system commands, managing processes, and handling system operations.
You always validate commands before execution and ensure they are safe to run.""",
min_reflect=6,
max_reflect=10,
tools=[run_terminal_command, save_to_file],
llm="gpt-4o-mini"
)
# Create a complex task that tests various system operations
task = Task(
name="system_analysis_task",
description="""Perform a comprehensive system analysis by executing the following operations in sequence:
1. Get system information (OS, kernel version)
2. List 5 running processes and sort them by CPU usage
3. Check disk space usage and list directories over 1GB
4. Display current system load and memory usage
5. List 5 network connections
6. Create a summary report with all findings in a text file called system_report.txt
Do it step by step. One task at a time.
Save only the Summary report in the file.
Use appropriate commands for each step and ensure proper error handling.""",
expected_output="A comprehensive system report containing all requested information saved in system_report.txt",
agent=system_ops_agent,
)
agents = PraisonAIAgents(
agents=[system_ops_agent],
tasks=[task],
process="sequential"
)
result = agents.start()
```
## e2b Tool Integration Example
```python
from praisonaiagents import Agent, Task, PraisonAIAgents, error_logs
import json
from e2b_code_interpreter import Sandbox
def code_interpret(code: str):
"""
A function to demonstrate running Python code dynamically using e2b_code_interpreter.
"""
print(f"\n{'='*50}\n> Running following AI-generated code:\n{code}\n{'='*50}")
exec_result = Sandbox().run_code(code)
if exec_result.error:
print("[Code Interpreter error]", exec_result.error)
return {"error": str(exec_result.error)}
else:
results = []
for result in exec_result.results:
if hasattr(result, '__iter__'):
results.extend(list(result))
else:
results.append(str(result))
logs = {"stdout": list(exec_result.logs.stdout), "stderr": list(exec_result.logs.stderr)}
return json.dumps({"results": results, "logs": logs})
# 1) Create Agents
web_scraper_agent = Agent(
name="WebScraper",
role="Web Scraper",
goal="Extract URLs from https://quotes.toscrape.com/",
backstory="An expert in data extraction from websites, adept at navigating and retrieving detailed information.",
tools=[code_interpret],
llm="gpt-4o"
)
url_data_extractor_agent = Agent(
name="URLDataExtractor",
role="URL Data Extractor",
goal="Crawl each URL for data extraction",
backstory="Specializes in crawling websites to gather comprehensive data, ensuring nothing is missed from each link.",
tools=[code_interpret],
llm="gpt-4o"
)
blog_writer_agent = Agent(
name="BlogWriter",
role="Creative Content Writer",
goal="Create engaging and insightful blog posts from provided data",
backstory="An experienced content creator and storyteller with a knack for weaving compelling narratives. Skilled at analyzing quotes and creating meaningful connections that resonate with readers.",
tools=[code_interpret],
llm="gpt-4o"
)
# 2) Create Tasks
task_url_extraction = Task(
name="url_extraction_task",
description="""Use code_interpret to run Python code that fetches https://quotes.toscrape.com/
and extracts 5 URLs from the page, then outputs them as a list. Only first 5 urls""",
expected_output="A list of URLs extracted from the source page.",
agent=web_scraper_agent,
output_file="urls.txt"
)
task_data_extraction = Task(
name="data_extraction_task",
description="""Take the URLs from url_extraction_task, crawl each URL and extract all pertinent
data using code_interpret to run Python code, then output raw txt, not the html.""",
expected_output="Raw data collected from each crawled URL.",
agent=url_data_extractor_agent,
context=[task_url_extraction]
)
blog_writing_task = Task(
name="blog_writing_task",
description="""Write an engaging blog post using the extracted quotes data. The blog post should:
1. Have a catchy title
2. Include relevant quotes from the data
3. Provide commentary and insights about the quotes
4. Be well-structured with introduction, body, and conclusion
5. Be approximately 500 words long""",
expected_output="A well-written blog post incorporating the extracted quotes with analysis",
agent=blog_writer_agent,
context=[task_data_extraction],
output_file="blog_post.txt"
)
# 3) Create and run Agents manager
agents_manager = PraisonAIAgents(
agents=[web_scraper_agent, url_data_extractor_agent, blog_writer_agent],
tasks=[task_url_extraction, task_data_extraction, blog_writing_task],
verbose=True,
process="hierarchical",
manager_llm="gpt-4o"
)
result = agents_manager.start()
```
## Integration with Tools
Reasoning works particularly well with tools that require complex decision-making:
- **Code Interpreter**: For dynamic code generation and execution
- **System Operations**: For multi-step system management tasks
- **Data Analysis**: For complex data processing workflows
- **Web Scraping**: For intelligent data extraction and processing
## Performance Considerations
- Higher reflection cycles mean more thorough reasoning but longer processing time
- `min_reflect` ensures minimum quality standards
- `max_reflect` prevents excessive processing
- Balance based on task complexity and quality requirements
+102
View File
@@ -0,0 +1,102 @@
---
title: "Self Reflection AI Agents"
sidebarTitle: "Self-Reflection"
description: "Self-reflection is a powerful feature in PraisonAI that enables agents to evaluate and improve their own responses before delivering them. This leads to higher quality outputs and more reliable task completion."
icon: "brain"
---
<div className="relative w-full aspect-video">
<iframe
className="absolute top-0 left-0 w-full h-full"
src="https://www.youtube.com/embed/vLXobEN2Vc8"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
## Overview
When an agent with self-reflection enabled processes a task:
1. It generates an initial response using its tools and capabilities
2. The agent then reflects on its own response, evaluating:
- Completeness
- Accuracy
- Relevance
- Quality
3. Based on this reflection, the agent may revise and improve its response
4. This cycle can repeat until the agent is satisfied with the quality
## Usage
Self-reflection is enabled by default for all agents. You can explicitly control it using the `self_reflect` parameter:
```python
from praisonaiagents import Agent
agent = Agent(
name="Researcher",
role="Senior Research Analyst",
goal="Uncover cutting-edge developments in AI",
backstory="You are an expert at analyzing tech trends",
self_reflect=True # Enable self-reflection (default)
)
```
## Benefits
- **Higher Quality Outputs**: Agents can catch and correct potential issues before delivering responses
- **More Reliable Task Completion**: Self-reflection helps ensure all aspects of a task are properly addressed
- **Conscious Decision Making**: Agents evaluate their own work, leading to more thoughtful responses
- **Improved Accuracy**: Multiple reflection cycles help refine and perfect the output
## Integration with Processes
Self-reflection works seamlessly with both sequential and hierarchical processes:
- **Sequential**: Each agent self-reflects on their individual tasks
- **Hierarchical**: Agents self-reflect before reporting to the manager agent, ensuring high-quality inputs for decision-making
## Example
Here's a complete example showing self-reflection in action:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents
researcher = Agent(
name="Researcher",
role="Senior Research Analyst",
goal="Uncover cutting-edge developments in AI",
backstory="You are an expert at analyzing tech trends",
self_reflect=True,
llm="gpt-4o"
)
task = Task(
name="research_task",
description="Analyze 2024's AI advancements",
expected_output="A detailed report on AI trends",
agent=researcher
)
agents = PraisonAIAgents(
agents=[researcher],
tasks=[task],
process="sequential"
)
result = agents.start()
```
In this example, the researcher agent will:
1. Research AI advancements
2. Generate an initial report
3. Self-reflect on the report's quality
4. Make improvements if needed
5. Deliver the final, refined output
## Performance Impact
While self-reflection adds additional processing time, the improvement in output quality often justifies the extra computation.
The number of reflection cycles need to be manually optimized to balance quality and performance.
+322
View File
@@ -0,0 +1,322 @@
---
title: "Structured AI Agents"
description: "Learn how to create AI agents that return structured, type-safe outputs using Pydantic models and JSON."
icon: "cube"
---
<Note>
**Prerequisites**
- Python 3.10 or higher
- PraisonAI Agents package installed
- Basic understanding of Python and Pydantic
</Note>
## Quick Start
Create AI agents that return structured, validated outputs in just a few lines of code!
<Steps>
<Step title="Install Package">
First, install the PraisonAI Agents package:
```bash
pip install praisonaiagents
```
</Step>
<Step title="Import Required Components">
Import the necessary components:
```python
from praisonaiagents import Agent, Task, PraisonAIAgents, Tools
from pydantic import BaseModel
```
</Step>
<Step title="Define Your Data Model">
Create a Pydantic model to define your output structure:
```python
class AnalysisReport(BaseModel):
title: str
findings: str
summary: str
```
</Step>
<Step title="Create Structured Tasks">
Create tasks with structured outputs:
```python
research_task = Task(
description="Research recent AI developments",
agent=researcher,
expected_output="Research findings"
)
analysis_task = Task(
description="Analyze research findings",
agent=analyst,
output_pydantic=AnalysisReport, # For Pydantic output
# or output_json=AnalysisReport # For JSON output
expected_output="Structured analysis report"
)
```
</Step>
</Steps>
## Understanding Structured Outputs
<Card title="What are Structured Outputs?" icon="question">
Structured outputs allow you to:
- Define exact shape of data using Pydantic models
- Get type-safe, validated responses
- Choose between Pydantic objects or JSON
- Ensure consistent output format across agent responses
</Card>
## Key Components
<CardGroup cols={2}>
<Card title="Pydantic Models" icon="cube">
Define your data structure
```python
class ResearchReport(BaseModel):
title: str
content: str
```
</Card>
<Card title="Task Configuration" icon="gear">
Configure output format
```python
Task(output_pydantic=Model)
# or
Task(output_json=Model)
```
</Card>
<Card title="Output Validation" icon="check">
Automatic validation of responses against your model
</Card>
<Card title="Format Options" icon="code">
Choose between Pydantic objects or JSON output
</Card>
</CardGroup>
## Output Types
<CardGroup cols={2}>
<Card title="Pydantic Output" icon="cube">
```python
task = Task(
output_pydantic=AnalysisReport
)
# Returns Pydantic object
```
Get type-safe Pydantic model instances
</Card>
<Card title="JSON Output" icon="brackets-curly">
```python
task = Task(
output_json=AnalysisReport
)
# Returns JSON dict
```
Get JSON dictionary matching model schema
</Card>
</CardGroup>
## Examples
### Research Analysis with Structured Output
```python
from praisonaiagents import Agent, Task, PraisonAIAgents, Tools
from pydantic import BaseModel
# Define output structure
class AnalysisReport(BaseModel):
title: str
findings: str
summary: str
# Create agents
researcher = Agent(
name="AIResearcher",
role="Technology Research Analyst",
tools=[Tools.internet_search]
)
analyst = Agent(
name="DataAnalyst",
role="Data Insights Specialist"
)
# Define structured tasks
research_task = Task(
name="gather_research",
description="Research recent AI developments in 2024",
agent=researcher,
expected_output="Research findings"
)
analysis_task = Task(
name="analyze_findings",
description="Analyze research findings and create structured report",
agent=analyst,
output_pydantic=AnalysisReport,
expected_output="Structured analysis report"
)
# Initialize and run agents
agents = PraisonAIAgents(
agents=[researcher, analyst],
tasks=[research_task, analysis_task]
)
result = agents.start()
```
### Different Output Formats
<CodeGroup>
```python Pydantic
# Get Pydantic model instance
analysis_task = Task(
description="Analyze findings",
agent=analyst,
output_pydantic=AnalysisReport
)
# Result will be AnalysisReport instance
result.pydantic.title
result.pydantic.findings
result.pydantic.summary
```
```python JSON
# Get JSON dictionary
analysis_task = Task(
description="Analyze findings",
agent=analyst,
output_json=AnalysisReport
)
# Result will be dictionary
result.json_dict['title']
result.json_dict['findings']
result.json_dict['summary']
```
</CodeGroup>
## Best Practices
<AccordionGroup>
<Accordion title="Model Design">
Design clear, focused Pydantic models:
```python
# Good
class ResearchReport(BaseModel):
title: str
findings: str
summary: str
# Too complex
class Report(BaseModel):
title: str
subtitle: Optional[str]
findings: List[Dict[str, Any]]
```
</Accordion>
<Accordion title="Output Format Selection">
Choose the right output format:
```python
# Use Pydantic when you need:
# - Type validation
# - Model methods
task = Task(output_pydantic=Model)
# Use JSON when you need:
# - Simple dictionary access
# - Serialization
task = Task(output_json=Model)
```
</Accordion>
</AccordionGroup>
## Common Patterns
### Research and Structured Analysis
```python
class ResearchReport(BaseModel):
topic: str
findings: str
sources: list[str]
class Analysis(BaseModel):
key_points: list[str]
implications: str
recommendations: str
research_task = Task(
description="Research quantum computing",
agent=researcher,
output_pydantic=ResearchReport
)
analysis_task = Task(
description="Analyze research implications",
agent=analyst,
output_pydantic=Analysis
)
```
### Data Collection and Summary
```python
class DataPoint(BaseModel):
timestamp: str
value: float
source: str
class Summary(BaseModel):
average: float
trends: str
insights: str
collection_task = Task(
description="Collect data points",
output_pydantic=DataPoint
)
summary_task = Task(
description="Summarize data",
output_pydantic=Summary
)
```
## Troubleshooting
<CardGroup cols={2}>
<Card title="Validation Errors" icon="triangle-exclamation">
Check if your model matches the expected output structure
```python
# Print model schema
print(Model.schema_json())
```
</Card>
<Card title="Output Format" icon="code">
Verify you're accessing the right output field:
- `result.pydantic` for Pydantic
- `result.json_dict` for JSON
</Card>
</CardGroup>
## Next Steps
<CardGroup cols={2}>
<Card title="Advanced Models" icon="graduation-cap" href="./advanced-models">
Learn about advanced Pydantic model configurations
</Card>
<Card title="Custom Validation" icon="check-double" href="./validation">
Add custom validation to your models
</Card>
</CardGroup>
<Note>
Structured outputs make your AI agents more reliable and their responses more predictable. They're perfect for production systems where data consistency is crucial.
</Note>
+1
View File
@@ -1,6 +1,7 @@
---
title: "Firecrawl PraisonAI Integration"
description: "Guide for integrating Firecrawl web scraping capabilities with PraisonAI, including custom tool implementation for web page content extraction"
icon: "spider"
---
# Firecrawl PraisonAI Integration
+1
View File
@@ -1,6 +1,7 @@
---
title: "AutoGen with PraisonAI"
description: "Guide for using AutoGen framework with PraisonAI, including installation, setup, and multi-agent conversation capabilities"
icon: "robot"
---
# AutoGen with PraisonAI
+1
View File
@@ -1,6 +1,7 @@
---
title: "CrewAI with PraisonAI"
description: "Guide for using CrewAI framework with PraisonAI, including installation, setup, and execution of agent-based tasks"
icon: "users"
---
# CrewAI with PraisonAI
+11
View File
@@ -1,8 +1,19 @@
---
title: "Multimodal AI Agents"
description: "Guide for creating and using multimodal AI agents in PraisonAI for processing images, videos, and other media types"
icon: "images"
---
<div className="relative w-full aspect-video">
<iframe
className="absolute top-0 left-0 w-full h-full"
src="https://www.youtube.com/embed/hjAWmUT1qqY"
title="YouTube video player"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
allowFullScreen
></iframe>
</div>
# Multimodal AI Agents
PraisonAI supports multimodal AI agents capable of processing various types of media including images and videos. This enables you to create agents that can understand and analyze visual content alongside text.
+1
View File
@@ -1,6 +1,7 @@
---
title: "PraisonAI Agents"
description: "Guide for using PraisonAI Agents framework, a lightweight package for creating and managing AI agents with advanced capabilities"
icon: "users-gear"
---
A lightweight package dedicated to creating and managing AI agents with advanced capabilities.
+298 -102
View File
@@ -1,131 +1,327 @@
---
title: "Praison AI"
description: "Home page for PraisonAI documentation, featuring installation options, user interfaces, and quick start guides for multi-agent LLM systems"
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"
icon: "house"
openGraph:
url: "/"
seo:
canonical: "/"
---
# Home
<iframe width="560" height="315" src="https://www.youtube.com/embed/Fn1lQjC0GO0" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="/logo/dark.png" />
<source media="(prefers-color-scheme: light)" srcset="/logo/light.png" />
<img alt="PraisonAI Logo" src="/logo/light.png" style={{display: "block", margin: "auto"}} />
</picture>
</p>
<p align="center">
<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads" /></a>
<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/github/v/release/MervinPraison/PraisonAI" alt="Latest Stable Version" /></a>
<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" /></a>
</p>
# Praison AI
Praison AI, leveraging both AutoGen and CrewAI or any other agent framework, represents a low-code, centralised framework designed to simplify the creation and orchestration of multi-agent systems for various LLM applications, emphasizing ease of use, customization, and human-agent interaction.
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="images/architecture-dark.png" />
<source media="(prefers-color-scheme: light)" srcset="images/architecture-light.png" />
<img alt="PraisonAI Architecture" src="images/architecture-light.png" />
</picture>
<Note>
Welcome to PraisonAI - Your comprehensive solution for building and managing multi-agent LLM systems with self-reflection capabilities.
</Note>
<div className="flex justify-start w-full max-w-xs sm:max-w-sm md:max-w-md">
<img
src="images/praisonai-logo-black.png"
alt="PraisonAI Logo"
className="w-full h-auto rounded-lg shadow-md block dark:hidden"
width="400"
height="100"
/>
<img
src="images/praisonai-logo-light.png"
alt="PraisonAI Logo"
className="w-full h-auto rounded-lg shadow-md hidden dark:block"
width="400"
height="100"
/>
</div>
<div className="flex flex-wrap gap-4 my-8">
<div className="hover:opacity-80 transition-opacity">
<img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads" />
</div>
<div className="hover:opacity-80 transition-opacity">
<img src="https://img.shields.io/github/v/release/MervinPraison/PraisonAI" alt="Latest Stable Version" />
</div>
<div className="hover:opacity-80 transition-opacity">
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" />
</div>
<div className="hover:opacity-80 transition-opacity">
<img src="https://img.shields.io/github/stars/MervinPraison/PraisonAI?style=social" alt="GitHub Stars" />
</div>
<div className="hover:opacity-80 transition-opacity">
<img src="https://img.shields.io/github/forks/MervinPraison/PraisonAI?style=social" alt="GitHub Forks" />
</div>
</div>
## Different User Interfaces:
<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.
</Tip>
| 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) |
## Key Features
## 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> |
<CardGroup cols={2}>
<Card title="AI Agents Creation" icon="robot">
Automated creation and management of AI agents with self-reflection capabilities
</Card>
<Card title="Framework Integration" icon="puzzle-piece">
Seamless integration with CrewAI and AutoGen frameworks
</Card>
<Card title="LLM Support" icon="brain">
Support for 100+ Language Learning Models
</Card>
<Card title="Code Integration" icon="code">
Chat with your entire codebase using advanced context understanding
</Card>
<Card title="Interactive UI" icon="desktop">
Rich, interactive user interfaces for better control and monitoring
</Card>
<Card title="Configuration" icon="gear">
YAML-based configuration for easy setup and customization
</Card>
<Card title="Tool Integration" icon="screwdriver-wrench">
Custom tool integration for extended functionality
</Card>
<Card title="Search Capability" icon="magnifying-glass">
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
### Basic Installation
```bash
pip install praisonai
```
<Tabs>
<Tab title="No Code">
<Steps>
<Step title="Install Package">
```bash
pip install praisonai
```
</Step>
<Step title="Set API Key">
```bash
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
```
</Step>
<Step title="Auto Mode">
```bash
praisonai --auto create a movie script about Robots in Mars
```
### Framework-specific Installation
```bash
# Install with CrewAI support
pip install "praisonai[crewai]"
This will automatically create required agents and complete the task
# Install with AutoGen support
pip install "praisonai[autogen]"
</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
# Install with both frameworks
pip install "praisonai[crewai,autogen]"
```
## Code Example
### UI and Additional Features
```bash
pip install "praisonai[ui]" # UI support
pip install "praisonai[chat]" # Chat interface
pip install "praisonai[code]" # Code interface
pip install "praisonai[realtime]" # Realtime voice interaction
pip install "praisonai[call]" # Call feature
```
<CodeGroup>
```python Basic
from praisonaiagents import Agent, Task, PraisonAIAgents
## TL;DR Multi Agents
# 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"
)
```bash
pip install praisonai
export OPENAI_API_KEY="Enter your API key"
praisonai --init create a movie script about dog in moon
praisonai
```
# Define a task
task = Task(
name="research_task",
description="Analyze 2024's AI advancements",
expected_output="A detailed report",
agent=researcher
)
## Initialise
# Run the agents
agents = PraisonAIAgents(
agents=[researcher],
tasks=[task],
verbose=False
)
```bash
export OPENAI_API_KEY="Enter your API key"
```
result = agents.start()
```
Generate your OPENAI API KEY from here: https://platform.openai.com/api-keys
```python Advanced
from praisonaiagents import Agent, Task, PraisonAIAgents
Note: You can use other providers such as Ollama, Mistral ... etc. Details are provided at the bottom.
# 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>
## Integration Options
<AccordionGroup>
<Accordion title="Ollama Integration">
```bash
export OPENAI_BASE_URL=http://localhost:11434/v1
```
</Accordion>
<Accordion title="Groq Integration">
```bash
export OPENAI_API_KEY=xxxxxxxxxxx
export OPENAI_BASE_URL=https://api.groq.com/openai/v1
```
</Accordion>
<Accordion title="Logging Configuration">
```bash
# Basic logging
export LOGLEVEL=info
```bash
praisonai --init create a movie script about dog in moon
```
This will automatically create agents.yaml file in the current directory.
# Advanced logging
export LOGLEVEL=debug
```
</Accordion>
</AccordionGroup>
### To initialse with a specific agent framework (Optional):
## Use Cases
```bash
praisonai --framework autogen --init create movie script about cat in mars
```
<CardGroup cols={2}>
<Card title="Customer Service" icon="headset">
Build intelligent support agents that can handle customer inquiries and resolve issues autonomously.
</Card>
<Card title="Data Analysis" icon="chart-line">
Create agents that can process, analyze, and derive insights from complex datasets.
</Card>
<Card title="Content Creation" icon="pen-nib">
Deploy agents that can generate, edit, and optimize content across various formats.
</Card>
<Card title="Process Automation" icon="gears">
Automate complex workflows with intelligent agents that can coordinate and execute tasks.
</Card>
</CardGroup>
## Run
## Praison AI Package Overall Features
```bash
praisonai
```
<Frame caption="PraisonAI Features Overview">
<div className="w-full max-w-4xl mx-auto">
<img
src="images/architecture-light.png"
alt="PraisonAI Features"
className="w-full h-auto rounded-lg shadow-lg block dark:hidden"
width="1200"
height="800"
/>
<img
src="images/architecture-dark.png"
alt="PraisonAI Features"
className="w-full h-auto rounded-lg shadow-lg hidden dark:block"
width="1200"
height="800"
/>
</div>
</Frame>
or
```bash
python -m praisonai
```
## Features
### Specify the agent framework (Optional):
<CardGroup cols={3}>
<Card title="Self-Reflection" icon="brain-circuit" href="/features/selfreflection">
Agents that evaluate and improve their own responses for higher accuracy
</Card>
<Card title="Reasoning" icon="gears" href="/features/reasoning">
Multi-step logical reasoning and autonomous problem solving
</Card>
<Card title="CrewAI Framework" icon="users-gear" href="/framework/crewai">
Build collaborative AI teams with CrewAI integration
</Card>
<Card title="AutoGen Framework" icon="robot" href="/framework/autogen">
Create autonomous agent networks using AutoGen
</Card>
<Card title="Multimodal Agents" icon="icons" href="/framework/multimodalagents">
Work with agents that can process text, images, and other data types
</Card>
<Card title="Train" icon="graduation-cap" href="/train">
Train and fine-tune your LLMs for specific tasks and domains. Then use it as an AI Agent.
</Card>
</CardGroup>
```bash
praisonai --framework autogen
```
## User Interfaces
### Full Automatic Mode
```bash
praisonai --auto create a movie script about Dog in Moon
```
<CardGroup cols={3}>
<Card title="Multi Agents UI" icon="users" href="/ui/ui">
Work with CrewAI or AutoGen multi-agent systems
</Card>
<Card title="Chat Interface" icon="comments" href="/ui/chat">
Chat with 100+ LLMs using a single AI Agent
</Card>
<Card title="Code Interface" icon="code" href="/ui/code">
Interact with your entire codebase
</Card>
</CardGroup>
+313 -216
View File
@@ -1,9 +1,73 @@
---
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"
icon: "house"
openGraph:
url: "/"
seo:
canonical: "/"
---
<div className="flex justify-start w-full max-w-xs sm:max-w-sm md:max-w-md">
<img
src="images/praisonai-logo-black.png"
alt="PraisonAI Logo"
className="w-full h-auto rounded-lg shadow-md block dark:hidden"
width="400"
height="100"
/>
<img
src="images/praisonai-logo-light.png"
alt="PraisonAI Logo"
className="w-full h-auto rounded-lg shadow-md hidden dark:block"
width="400"
height="100"
/>
</div>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '1rem' }}>
<div className="hover:opacity-80 transition-opacity">
<img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads" />
</div>
<div className="hover:opacity-80 transition-opacity">
<img src="https://img.shields.io/github/stars/MervinPraison/PraisonAI?style=social" alt="GitHub Stars" />
</div>
<div className="hover:opacity-80 transition-opacity">
<img src="https://img.shields.io/github/forks/MervinPraison/PraisonAI?style=social" alt="GitHub Forks" />
</div>
</div>
<a href="https://trendshift.io/repositories/9130" target="_blank">
<img src="https://trendshift.io/api/badge/repositories/9130" alt="MervinPraison/PraisonAI | Trendshift" />
</a>
## Key Features
<div className="relative w-full aspect-video overflow-hidden">
<CardGroup cols={2}>
<Card title="AI Agents Creation" icon="robot">
Automated creation and management of AI agents with self-reflection capabilities
</Card>
<Card title="Framework Integration" icon="puzzle-piece">
Seamless integration with CrewAI and AutoGen frameworks
</Card>
<Card title="LLM Support" icon="brain">
Support for 100+ Language Learning Models
</Card>
<Card title="Code Integration" icon="code">
Chat with your entire codebase using advanced context understanding
</Card>
<Card title="Interactive UI" icon="desktop">
Rich, interactive user interfaces for better control and monitoring
</Card>
<Card title="Configuration" icon="gear">
YAML-based configuration for easy setup and customization
</Card>
<Card title="Tool Integration" icon="screwdriver-wrench">
Custom tool integration for extended functionality
</Card>
<Card title="Search Capability" icon="magnifying-glass">
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"
@@ -17,228 +81,261 @@ description: "Comprehensive guide to PraisonAI - an AI Agents Framework with Sel
></iframe>
</div>
<div className="flex justify-start w-full max-w-xs sm:max-w-sm md:max-w-md">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="images/praisonai-logo-black.png" />
<source media="(prefers-color-scheme: light)" srcset="images/praisonai-logo-light.png" />
## Installation Options
<Tabs>
<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.
</Tip>
<Steps>
<Step title="Install Package">
```bash
pip install praisonai
```
</Step>
<Step title="Set API Key">
```bash
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
```
</Step>
<Step title="Auto Mode">
```bash
praisonai --auto create a movie script about Robots in Mars
```
This will automatically create required agents and complete the task
</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>
## Integration Options
<AccordionGroup>
<Accordion title="Ollama Integration">
```bash
export OPENAI_BASE_URL=http://localhost:11434/v1
```
</Accordion>
<Accordion title="Groq Integration">
```bash
export OPENAI_API_KEY=xxxxxxxxxxx
export OPENAI_BASE_URL=https://api.groq.com/openai/v1
```
</Accordion>
<Accordion title="Logging Configuration">
```bash
# Basic logging
export LOGLEVEL=info
# Advanced logging
export LOGLEVEL=debug
```
</Accordion>
</AccordionGroup>
## Use Cases
<CardGroup cols={2}>
<Card title="Customer Service" icon="headset">
Build intelligent support agents that can handle customer inquiries and resolve issues autonomously.
</Card>
<Card title="Data Analysis" icon="chart-line">
Create agents that can process, analyze, and derive insights from complex datasets.
</Card>
<Card title="Content Creation" icon="pen-nib">
Deploy agents that can generate, edit, and optimize content across various formats.
</Card>
<Card title="Process Automation" icon="gears">
Automate complex workflows with intelligent agents that can coordinate and execute tasks.
</Card>
</CardGroup>
## Praison AI Package Overall Features
<Frame caption="PraisonAI Features Overview">
<div className="w-full max-w-4xl mx-auto">
<img
src="images/praisonai-logo-light.png"
alt="PraisonAI Logo"
className="w-full h-auto"
width="400"
height="100"
/>
</picture>
</div>
<div className="flex flex-wrap gap-4 my-4">
<a href="https://github.com/MervinPraison/PraisonAI">
<img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads" />
</a>
<a href="https://github.com/MervinPraison/PraisonAI">
<img src="https://img.shields.io/github/v/release/MervinPraison/PraisonAI" alt="Latest Stable Version" />
</a>
<a href="https://github.com/MervinPraison/PraisonAI">
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" />
</a>
<a href="https://github.com/MervinPraison/PraisonAI">
<img src="https://img.shields.io/github/stars/MervinPraison/PraisonAI?style=social" alt="GitHub Stars" />
</a>
<a href="https://github.com/MervinPraison/PraisonAI">
<img src="https://img.shields.io/github/forks/MervinPraison/PraisonAI?style=social" alt="GitHub Forks" />
</a>
</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.
## Key Features
- 🤖 Automated AI Agents Creation
- 🔄 Use CrewAI or AutoGen Framework
- 💯 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
Light weight package dedicated for coding:
```bash
pip install praisonaiagents
```
```bash
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
```
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()
```
Run:
```bash
python app.py
```
## Ollama Integration
```bash
export OPENAI_BASE_URL=http://localhost:11434/v1
```
## Groq Integration
Replace xxxx with Groq API KEY:
```bash
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 className="w-full max-w-4xl mx-auto">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="images/architecture-light.png" />
<source media="(prefers-color-scheme: light)" srcset="images/architecture-dark.png" />
<img
src="images/architecture-dark.png"
alt="PraisonAI Architecture"
className="w-full h-auto"
src="images/architecture-light.png"
alt="PraisonAI Features"
className="w-full h-auto rounded-lg shadow-lg block dark:hidden"
width="1200"
height="800"
/>
</picture>
</div>
<img
src="images/architecture-dark.png"
alt="PraisonAI Features"
className="w-full h-auto rounded-lg shadow-lg hidden dark:block"
width="1200"
height="800"
/>
</div>
</Frame>
## Features
## Different User Interfaces:
<CardGroup cols={3}>
<Card title="Self-Reflection" icon="brain-circuit" href="/features/selfreflection">
Agents that evaluate and improve their own responses for higher accuracy
</Card>
<Card title="Reasoning" icon="gears" href="/features/reasoning">
Multi-step logical reasoning and autonomous problem solving
</Card>
<Card title="CrewAI Framework" icon="users-gear" href="/framework/crewai">
Build collaborative AI teams with CrewAI integration
</Card>
<Card title="AutoGen Framework" icon="robot" href="/framework/autogen">
Create autonomous agent networks using AutoGen
</Card>
<Card title="Multimodal Agents" icon="icons" href="/framework/multimodalagents">
Work with agents that can process text, images, and other data types
</Card>
<Card title="Train" icon="graduation-cap" href="/train">
Train and fine-tune your LLMs for specific tasks and domains. Then use it as an AI Agent.
</Card>
</CardGroup>
| 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) |
## User Interfaces
## Google Colab Multi Agents
<CardGroup cols={3}>
<Card title="Multi Agents UI" icon="users" href="/ui/ui">
Work with CrewAI or AutoGen multi-agent systems
</Card>
<Card title="Chat Interface" icon="comments" href="/ui/chat">
Chat with 100+ LLMs using a single AI Agent
</Card>
<Card title="Code Interface" icon="code" href="/ui/code">
Interact with your entire codebase
</Card>
</CardGroup>
| | 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> |
<Note>
Welcome to PraisonAI - Your comprehensive solution for building and managing multi-agent LLM systems with self-reflection capabilities.
</Note>
## Install
| PraisonAI | PraisonAI Code | PraisonAI Chat |
| --- | --- | --- |
| `pip install praisonai` | `pip install "praisonai[code]"` | `pip install "praisonai[chat]"` |
## 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
```
## Prerequisite:
### Export API KEY
```bash
export OPENAI_API_KEY="Enter your API key"
```
## Installation
```bash
pip install praisonai
```
## Automatically Create Agents to Perform a Task
```bash
praisonai --init create a movie script about dog in moon
```
## Run
```bash
praisonai
```
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '1rem' }}>
<div className="hover:opacity-80 transition-opacity">
<img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads" />
</div>
<div className="hover:opacity-80 transition-opacity">
<img src="https://img.shields.io/github/v/release/MervinPraison/PraisonAI" alt="Latest Stable Version" />
</div>
<div className="hover:opacity-80 transition-opacity">
<img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" />
</div>
<div className="hover:opacity-80 transition-opacity">
<img src="https://img.shields.io/github/stars/MervinPraison/PraisonAI?style=social" alt="GitHub Stars" />
</div>
<div className="hover:opacity-80 transition-opacity">
<img src="https://img.shields.io/github/forks/MervinPraison/PraisonAI?style=social" alt="GitHub Forks" />
</div>
</div>
+1
View File
@@ -1,6 +1,7 @@
---
title: "Initialise"
description: "Instructions for initializing PraisonAI with API keys and creating the initial agents configuration file"
icon: "power-off"
---
```bash
+1
View File
@@ -1,6 +1,7 @@
---
title: "Installation"
description: "Guide for installing PraisonAI and its various components including framework support and additional features"
icon: "download"
---
## Basic Installation
Loaded 100 of 182 files, more files were not shown because too many files have changed in this diff. Show more