mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-10 03:31:28 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81ad7db679 | ||
|
|
0482c3ebb0 | ||
|
|
3107458016 | ||
|
|
44c8ceebae | ||
|
|
20c0b02528 | ||
|
|
cfc886b591 | ||
|
|
77ac4b7a09 | ||
|
|
5897cedc45 | ||
|
|
a4d1079813 | ||
|
|
4eed985c4d | ||
|
|
0658491072 | ||
|
|
9f82368b4c |
No files matched your search
@@ -1,45 +1,54 @@
|
||||
name: Daily Publish Docker
|
||||
name: Publish Docker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
tag:
|
||||
description: 'Docker image tag (default: latest)'
|
||||
required: false
|
||||
default: 'latest'
|
||||
|
||||
jobs:
|
||||
check-and-build:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Check last commit time
|
||||
id: check_commit
|
||||
run: |
|
||||
last_commit_time=$(git log -1 --format=%ct)
|
||||
current_time=$(date +%s)
|
||||
time_diff=$((current_time - last_commit_time))
|
||||
if [ $time_diff -ge 120 ] && [ $time_diff -lt 240 ]; then
|
||||
echo "should_run=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "should_run=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
if: steps.check_commit.outputs.should_run == 'true'
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Log in to the GitHub Container Registry
|
||||
if: steps.check_commit.outputs.should_run == 'true'
|
||||
run: echo ${{ secrets.GH_TOKEN }} | docker login ghcr.io -u USERNAME --password-stdin
|
||||
- name: Log in to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract metadata for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ghcr.io/mervinpraison/praisonai
|
||||
tags: |
|
||||
type=raw,value=latest,enable=${{ github.event_name == 'release' }}
|
||||
type=semver,pattern={{version}},enable=${{ github.event_name == 'release' }}
|
||||
type=semver,pattern={{major}}.{{minor}},enable=${{ github.event_name == 'release' }}
|
||||
type=raw,value=${{ github.event.inputs.tag || 'latest' }},enable=${{ github.event_name == 'workflow_dispatch' }}
|
||||
|
||||
- name: Build and push Docker image
|
||||
if: steps.check_commit.outputs.should_run == 'true'
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
file: ./docker/Dockerfile
|
||||
push: true
|
||||
tags: ghcr.io/mervinpraison/praisonai:latest
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
@@ -30,7 +30,113 @@ PraisonAI is a production-ready Multi-AI Agents framework with self-reflection,
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Key Features
|
||||
---
|
||||
|
||||
## 📑 Table of Contents
|
||||
|
||||
- [🚀 Quick Start](#-quick-start)
|
||||
- [📦 Installation](#-installation)
|
||||
- [Python SDK](#python-sdk)
|
||||
- [JavaScript SDK](#javascript-sdk)
|
||||
- [💻 Usage](#-usage)
|
||||
- [Python Code Examples](#python-code-examples)
|
||||
- [CLI / No-Code Interface](#cli--no-code-interface)
|
||||
- [JavaScript Code Examples](#javascript-code-examples)
|
||||
- [✨ Key Features](#-key-features)
|
||||
- [🌐 Supported Providers](#-supported-providers)
|
||||
- [🛠️ Custom Tools](#️-custom-tools)
|
||||
- [🔧 Configuration & Integration](#-configuration--integration)
|
||||
- [Ollama Integration](#ollama-integration)
|
||||
- [Groq Integration](#groq-integration)
|
||||
- [100+ Models Support](#100-models-support)
|
||||
- [📋 Workflows](#-workflows)
|
||||
- [Workflow Patterns](#workflow-patterns)
|
||||
- [YAML Configuration](#yaml-configuration)
|
||||
- [Agents Playbook](#agents-playbook)
|
||||
- [🧠 Memory & Context](#-memory--context)
|
||||
- [🔬 Advanced Features](#-advanced-features)
|
||||
- [Research & Intelligence](#research--intelligence)
|
||||
- [Planning & Workflows](#planning--workflows)
|
||||
- [Specialised Agents](#specialised-agents)
|
||||
- [MCP Protocol](#mcp-protocol)
|
||||
- [Safety & Control](#safety--control)
|
||||
- [Developer Tools](#developer-tools)
|
||||
- [📊 Process Types & Patterns](#-process-types--patterns)
|
||||
- [Sequential Process](#sequential-process)
|
||||
- [Hierarchical Process](#hierarchical-process)
|
||||
- [Workflow Process](#workflow-process)
|
||||
- [Agentic Patterns](#agentic-patterns)
|
||||
- [🎓 Video Tutorials](#-video-tutorials)
|
||||
- [⭐ Star History](#-star-history)
|
||||
- [👥 Contributing](#-contributing)
|
||||
- [🔧 Development](#-development)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
Get started with PraisonAI in under 1 minute:
|
||||
|
||||
```bash
|
||||
# Install
|
||||
pip install praisonaiagents
|
||||
|
||||
# Set API key
|
||||
export OPENAI_API_KEY=your_key_here
|
||||
|
||||
# Create a simple agent
|
||||
python -c "from praisonaiagents import Agent; Agent(instructions='You are a helpful AI assistant').start('Write a haiku about AI')"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
### Python SDK
|
||||
|
||||
Lightweight package dedicated for coding:
|
||||
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
|
||||
For the full framework with CLI support:
|
||||
|
||||
```bash
|
||||
pip install praisonai
|
||||
```
|
||||
|
||||
### JavaScript SDK
|
||||
|
||||
```bash
|
||||
npm install praisonai
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💻 Usage
|
||||
|
||||
### Python Code Examples
|
||||
|
||||
- [📘 Complete Python Examples](#using-python-code)
|
||||
- [🔧 Custom Tools](#️-custom-tools)
|
||||
- [🧠 Memory Configuration](#-memory--context)
|
||||
|
||||
### CLI / No-Code Interface
|
||||
|
||||
- [🎯 Auto Mode](#auto-mode)
|
||||
- [🔬 Deep Research](#deep-research-cli)
|
||||
- [📋 Planning Mode](#planning-mode-cli)
|
||||
- [💾 Memory Management](#memory-cli)
|
||||
- [🔄 Workflow Execution](#workflow-cli)
|
||||
|
||||
### JavaScript Code Examples
|
||||
|
||||
- [💻 JavaScript Usage](#using-javascript-code)
|
||||
|
||||
---
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
| Feature | Code | Docs |
|
||||
|---------|:----:|:----:|
|
||||
@@ -114,7 +220,11 @@ PraisonAI is a production-ready Multi-AI Agents framework with self-reflection,
|
||||
| 💾 Auto-Save Sessions | [Example](#session-management-python) | [📖](https://docs.praison.ai/cli/session) |
|
||||
| 📜 History in Context | [Example](#session-management-python) | [📖](https://docs.praison.ai/cli/session) |
|
||||
|
||||
## Supported Providers
|
||||
---
|
||||
|
||||
## 🌐 Supported Providers
|
||||
|
||||
PraisonAI supports 100+ LLM providers through seamless integration:
|
||||
|
||||
| Provider | Example |
|
||||
|----------|:-------:|
|
||||
@@ -143,14 +253,14 @@ PraisonAI is a production-ready Multi-AI Agents framework with self-reflection,
|
||||
| Moonshot | [Example](examples/python/providers/moonshot/moonshot_example.py) |
|
||||
| vLLM | [Example](examples/python/providers/vllm/vllm_example.py) |
|
||||
|
||||
## Using Python Code
|
||||
---
|
||||
|
||||
## 📘 Using Python Code
|
||||
|
||||
Light weight package dedicated for coding:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
@@ -605,6 +715,77 @@ result = manager.execute_yaml(
|
||||
)
|
||||
```
|
||||
|
||||
### Complete workflow.yaml Reference
|
||||
|
||||
```yaml
|
||||
# workflow.yaml - Full feature reference
|
||||
name: Complete Workflow
|
||||
description: Demonstrates all workflow.yaml features
|
||||
framework: praisonai # praisonai, crewai, autogen
|
||||
process: workflow # sequential, hierarchical, workflow
|
||||
|
||||
workflow:
|
||||
planning: true
|
||||
planning_llm: gpt-4o
|
||||
reasoning: true
|
||||
verbose: true
|
||||
memory_config:
|
||||
provider: chroma
|
||||
persist: true
|
||||
|
||||
variables:
|
||||
topic: AI trends
|
||||
items: [ML, NLP, Vision]
|
||||
|
||||
agents:
|
||||
researcher:
|
||||
name: Researcher
|
||||
role: Research Analyst
|
||||
goal: Research topics thoroughly
|
||||
instructions: "Provide detailed research findings"
|
||||
backstory: "Expert researcher with 10 years experience" # alias for instructions
|
||||
llm: gpt-4o-mini
|
||||
function_calling_llm: gpt-4o # For tool calls
|
||||
max_rpm: 10 # Rate limiting
|
||||
max_execution_time: 300 # Timeout in seconds
|
||||
reflect_llm: gpt-4o # For self-reflection
|
||||
min_reflect: 1
|
||||
max_reflect: 3
|
||||
system_template: "You are a helpful assistant"
|
||||
tools:
|
||||
- tavily_search
|
||||
|
||||
writer:
|
||||
name: Writer
|
||||
role: Content Writer
|
||||
goal: Write clear content
|
||||
instructions: "Write engaging content"
|
||||
|
||||
steps:
|
||||
- name: research_step
|
||||
agent: researcher
|
||||
action: "Research {{topic}}"
|
||||
expected_output: "Comprehensive research report"
|
||||
output_file: "output/research.md"
|
||||
create_directory: true
|
||||
|
||||
- name: writing_step
|
||||
agent: writer
|
||||
action: "Write article based on research"
|
||||
context: # Task dependencies
|
||||
- research_step
|
||||
output_json: # Structured output
|
||||
type: object
|
||||
properties:
|
||||
title: { type: string }
|
||||
content: { type: string }
|
||||
|
||||
callbacks:
|
||||
on_workflow_start: log_start
|
||||
on_step_complete: log_step
|
||||
on_workflow_complete: log_complete
|
||||
```
|
||||
|
||||
### 9. Hooks
|
||||
|
||||
Configure in `.praison/hooks.json`:
|
||||
@@ -621,9 +802,50 @@ hooks.register("pre_write_code", lambda ctx: print(f"Writing {ctx['file']}"))
|
||||
result = hooks.execute("pre_write_code", {"file": "main.py"})
|
||||
```
|
||||
|
||||
### 10. Extended agents.yaml with Workflow Patterns
|
||||
### 10. Field Names Reference (A-I-G-S)
|
||||
|
||||
You can now use advanced workflow patterns directly in agents.yaml by setting `process: workflow`:
|
||||
PraisonAI accepts both old (agents.yaml) and new (workflow.yaml) field names. Use the **canonical names** for new projects:
|
||||
|
||||
| Canonical (Recommended) | Alias (Also Works) | Purpose |
|
||||
|-------------------------|-------------------|---------|
|
||||
| `agents` | `roles` | Define agent personas |
|
||||
| `instructions` | `backstory` | Agent behavior/persona |
|
||||
| `action` | `description` | What the step does |
|
||||
| `steps` | `tasks` (nested) | Define work items |
|
||||
| `name` | `topic` | Workflow identifier |
|
||||
|
||||
**A-I-G-S Mnemonic** - Easy to remember:
|
||||
- **A**gents - Who does the work
|
||||
- **I**nstructions - How they behave
|
||||
- **G**oal - What they achieve
|
||||
- **S**teps - What they do
|
||||
|
||||
```yaml
|
||||
# Quick Reference - Canonical Format
|
||||
name: My Workflow # Workflow name (not 'topic')
|
||||
agents: # Define agents (not 'roles')
|
||||
my_agent:
|
||||
role: Job Title # Agent's role
|
||||
goal: What to achieve # Agent's goal
|
||||
instructions: How to act # Agent's behavior (not 'backstory')
|
||||
|
||||
steps: # Define steps (not 'tasks')
|
||||
- agent: my_agent
|
||||
action: What to do # Step action (not 'description')
|
||||
```
|
||||
|
||||
> **Note:** The parser accepts both old and new names. Run `praisonai workflow validate <file.yaml>` to see suggestions for canonical names.
|
||||
|
||||
### 11. Extended agents.yaml with Workflow Patterns
|
||||
|
||||
**Feature Parity:** Both `agents.yaml` and `workflow.yaml` now support the same features:
|
||||
- All workflow patterns (route, parallel, loop, repeat)
|
||||
- All agent fields (function_calling_llm, max_rpm, max_execution_time, reflect_llm, templates)
|
||||
- All step fields (expected_output, context, output_json, create_directory, callback)
|
||||
- Framework support (praisonai, crewai, autogen)
|
||||
- Process types (sequential, hierarchical, workflow)
|
||||
|
||||
You can use advanced workflow patterns directly in agents.yaml by setting `process: workflow`:
|
||||
|
||||
```yaml
|
||||
# agents.yaml with workflow patterns
|
||||
@@ -639,15 +861,15 @@ workflow:
|
||||
variables:
|
||||
topic: AI trends
|
||||
|
||||
roles:
|
||||
agents: # Canonical: use 'agents' instead of 'roles'
|
||||
classifier:
|
||||
role: Request Classifier
|
||||
backstory: "Classify requests into categories"
|
||||
instructions: "Classify requests into categories" # Canonical: use 'instructions' instead of 'backstory'
|
||||
goal: Classify requests
|
||||
|
||||
researcher:
|
||||
role: Research Analyst
|
||||
backstory: "Expert researcher"
|
||||
instructions: "Expert researcher" # Canonical: use 'instructions' instead of 'backstory'
|
||||
goal: Research topics
|
||||
tools:
|
||||
- tavily_search
|
||||
@@ -690,9 +912,13 @@ Run with the same simple command:
|
||||
praisonai agents.yaml
|
||||
```
|
||||
|
||||
## Using No Code
|
||||
---
|
||||
|
||||
### Auto Mode:
|
||||
## 🎯 CLI / No-Code Interface
|
||||
|
||||
PraisonAI provides a powerful CLI for no-code automation and quick prototyping.
|
||||
|
||||
### Auto Mode
|
||||
```bash
|
||||
pip install praisonai
|
||||
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
|
||||
@@ -839,10 +1065,43 @@ praisonai "GPT-5" --workflow "Research:Search for info,Write:Write blog" --tools
|
||||
praisonai workflow help
|
||||
```
|
||||
|
||||
#### YAML Workflow Files:
|
||||
```bash
|
||||
# Run a YAML workflow file
|
||||
praisonai workflow run research.yaml
|
||||
|
||||
# Run with variables
|
||||
praisonai workflow run research.yaml --var topic="AI trends"
|
||||
|
||||
# Validate a YAML workflow
|
||||
praisonai workflow validate research.yaml
|
||||
|
||||
# Create from template (simple, routing, parallel, loop, evaluator-optimizer)
|
||||
praisonai workflow template routing --output my_workflow.yaml
|
||||
```
|
||||
|
||||
#### Auto-Generate Workflows:
|
||||
```bash
|
||||
# Auto-generate a sequential workflow from topic
|
||||
praisonai workflow auto "Research AI trends"
|
||||
|
||||
# Generate parallel workflow (multiple agents work concurrently)
|
||||
praisonai workflow auto "Research AI trends" --pattern parallel
|
||||
|
||||
# Generate routing workflow (classifier routes to specialists)
|
||||
praisonai workflow auto "Build a chatbot" --pattern routing
|
||||
|
||||
# Specify output file
|
||||
praisonai workflow auto "Research AI" --pattern sequential --output my_workflow.yaml
|
||||
```
|
||||
|
||||
**Workflow CLI Options:**
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--workflow-var key=value` | Set workflow variable (can be repeated) |
|
||||
| `--var key=value` | Set variable for YAML workflows |
|
||||
| `--pattern <pattern>` | Pattern for auto-generation (sequential, parallel, routing, loop) |
|
||||
| `--output <file>` | Output file for auto-generation |
|
||||
| `--llm <model>` | LLM model (e.g., openai/gpt-4o-mini) |
|
||||
| `--tools <tools>` | Tools (comma-separated, e.g., tavily) |
|
||||
| `--planning` | Enable planning mode |
|
||||
@@ -1328,7 +1587,9 @@ server.run() # stdio for Claude Desktop
|
||||
| 📥 Web Fetch - URL content retrieval | [📖](https://docs.praison.ai/cli/web-fetch) |
|
||||
| 💾 Prompt Caching - Cost reduction | [📖](https://docs.praison.ai/cli/prompt-caching) |
|
||||
|
||||
## Using JavaScript Code
|
||||
---
|
||||
|
||||
## 💻 Using JavaScript Code
|
||||
|
||||
```bash
|
||||
npm install praisonai
|
||||
@@ -1343,11 +1604,17 @@ agent.start('Write a movie script about a robot in Mars');
|
||||
|
||||

|
||||
|
||||
## Star History
|
||||
---
|
||||
|
||||
## ⭐ Star History
|
||||
|
||||
[](https://docs.praison.ai)
|
||||
|
||||
## AI Agents Flow
|
||||
---
|
||||
|
||||
## 📊 Process Types & Patterns
|
||||
|
||||
### AI Agents Flow
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
@@ -1686,31 +1953,40 @@ flowchart LR
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
## Adding Models
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<a href="https://docs.praison.ai/models">
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/%F0%9F%93%9A_Models-Visit_docs.praison.ai-blue?style=for-the-badge&logo=bookstack&logoColor=white" alt="Models" />
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
## 🔧 Configuration & Integration
|
||||
|
||||
### Ollama Integration
|
||||
|
||||
## Ollama Integration
|
||||
```bash
|
||||
export OPENAI_BASE_URL=http://localhost:11434/v1
|
||||
```
|
||||
|
||||
## Groq Integration
|
||||
### Groq Integration
|
||||
|
||||
Replace xxxx with Groq API KEY:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=xxxxxxxxxxx
|
||||
export OPENAI_BASE_URL=https://api.groq.com/openai/v1
|
||||
```
|
||||
|
||||
## No Code Options
|
||||
### 100+ Models Support
|
||||
|
||||
## Agents Playbook
|
||||
PraisonAI supports 100+ LLM models from various providers. Visit our [models documentation](https://docs.praison.ai/models/) for the complete list.
|
||||
|
||||
<div align="center">
|
||||
<a href="https://docs.praison.ai/models">
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/📚_Models_Documentation-Visit_docs.praison.ai-blue?style=for-the-badge&logo=bookstack&logoColor=white" alt="Models Documentation" />
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📋 Agents Playbook
|
||||
|
||||
### Simple Playbook Example
|
||||
|
||||
@@ -1719,9 +1995,9 @@ Create `agents.yaml` file and add the code below:
|
||||
```yaml
|
||||
framework: praisonai
|
||||
topic: Artificial Intelligence
|
||||
roles:
|
||||
agents: # Canonical: use 'agents' instead of 'roles'
|
||||
screenwriter:
|
||||
backstory: "Skilled in crafting scripts with engaging dialogue about {topic}."
|
||||
instructions: "Skilled in crafting scripts with engaging dialogue about {topic}." # Canonical: use 'instructions' instead of 'backstory'
|
||||
goal: Create scripts from concepts.
|
||||
role: Screenwriter
|
||||
tasks:
|
||||
@@ -1735,18 +2011,11 @@ roles:
|
||||
praisonai agents.yaml
|
||||
```
|
||||
|
||||
## Use 100+ Models
|
||||
---
|
||||
|
||||
- https://docs.praison.ai/models/
|
||||
<div align="center">
|
||||
<a href="https://docs.praison.ai">
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/📚_Documentation-Visit_docs.praison.ai-blue?style=for-the-badge&logo=bookstack&logoColor=white" alt="Documentation" />
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
## 🛠️ Custom Tools / Create Plugins
|
||||
|
||||
## Custom Tools
|
||||
PraisonAI supports multiple ways to create and integrate custom tools (plugins) into your agents.
|
||||
|
||||
### Using `@tool` Decorator
|
||||
|
||||
@@ -1819,276 +2088,96 @@ After `pip install`, tools are auto-discovered:
|
||||
agent = Agent(tools=["my_tool"]) # Works automatically!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Prompt Expansion
|
||||
## 🧠 Memory & Context
|
||||
|
||||
Expand short prompts into detailed, actionable prompts:
|
||||
PraisonAI provides zero-dependency persistent memory for agents. For detailed examples, see [section 6. Agent Memory](#6-agent-memory-zero-dependencies) in the Python Code Examples.
|
||||
|
||||
### CLI Usage
|
||||
---
|
||||
|
||||
```bash
|
||||
# Expand a short prompt into detailed prompt
|
||||
praisonai "write a movie script in 3 lines" --expand-prompt
|
||||
## 🔬 Advanced Features
|
||||
|
||||
# With verbose output
|
||||
praisonai "blog about AI" --expand-prompt -v
|
||||
### Research & Intelligence
|
||||
|
||||
# With tools for context gathering
|
||||
praisonai "latest AI trends" --expand-prompt --expand-tools tools.py
|
||||
- 🔬 **Deep Research Agents** - OpenAI & Gemini support for automated research
|
||||
- 🔄 **Query Rewriter Agent** - HyDE, Step-back, Multi-query strategies for RAG optimization
|
||||
- 🌐 **Native Web Search** - Real-time search via OpenAI, Gemini, Anthropic, xAI, Perplexity
|
||||
- 📥 **Web Fetch** - Retrieve full content from URLs (Anthropic)
|
||||
- 📝 **Prompt Expander Agent** - Expand short prompts into detailed instructions
|
||||
|
||||
# Combine with query rewrite
|
||||
praisonai "AI news" --query-rewrite --expand-prompt
|
||||
```
|
||||
### Memory & Caching
|
||||
|
||||
### Programmatic Usage
|
||||
- 💾 **Prompt Caching** - Reduce costs & latency (OpenAI, Anthropic, Bedrock, Deepseek)
|
||||
- 🧠 **Claude Memory Tool** - Persistent cross-conversation memory (Anthropic Beta)
|
||||
- 💾 **File-Based Memory** - Zero-dependency persistent memory for all agents
|
||||
- 🔍 **Built-in Search Tools** - Tavily, You.com, Exa for web search, news, content extraction
|
||||
|
||||
```python
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy
|
||||
### Planning & Workflows
|
||||
|
||||
# Basic usage
|
||||
agent = PromptExpanderAgent()
|
||||
result = agent.expand("write a movie script in 3 lines")
|
||||
print(result.expanded_prompt)
|
||||
- 📋 **Planning Mode** - Plan before execution for agents & multi-agent systems
|
||||
- 🔧 **Planning Tools** - Research with tools during planning phase
|
||||
- 🧠 **Planning Reasoning** - Chain-of-thought planning for complex tasks
|
||||
- ⛓️ **Prompt Chaining** - Sequential prompt workflows with conditional gates
|
||||
- 🔍 **Evaluator Optimiser** - Generate and optimize through iterative feedback
|
||||
- 👷 **Orchestrator Workers** - Distribute tasks among specialised workers
|
||||
- ⚡ **Parallelisation** - Execute tasks in parallel for improved performance
|
||||
- 🔁 **Repetitive Agents** - Handle repetitive tasks through automated loops
|
||||
- 🤖 **Autonomous Workflow** - Monitor, act, adapt based on environment feedback
|
||||
|
||||
# With specific strategy
|
||||
result = agent.expand("blog about AI", strategy=ExpandStrategy.DETAILED)
|
||||
### Specialised Agents
|
||||
|
||||
# Available strategies: BASIC, DETAILED, STRUCTURED, CREATIVE, AUTO
|
||||
```
|
||||
- 🖼️ **Image Generation Agent** - Create images from text descriptions
|
||||
- 📷 **Image to Text Agent** - Extract text and descriptions from images
|
||||
- 🎬 **Video Agent** - Analyse and process video content
|
||||
- 📊 **Data Analyst Agent** - Analyse data and generate insights
|
||||
- 💰 **Finance Agent** - Financial analysis and recommendations
|
||||
- 🛒 **Shopping Agent** - Price comparison and shopping assistance
|
||||
- ⭐ **Recommendation Agent** - Personalised recommendations
|
||||
- 📖 **Wikipedia Agent** - Search and extract Wikipedia information
|
||||
- 💻 **Programming Agent** - Code development and analysis
|
||||
- 📝 **Markdown Agent** - Generate and format Markdown content
|
||||
- 🔀 **Router Agent** - Dynamic task routing with cost optimisation
|
||||
|
||||
**Key Difference:**
|
||||
- `--query-rewrite`: Optimizes queries for **search/retrieval** (RAG)
|
||||
- `--expand-prompt`: Expands prompts for **detailed task execution**
|
||||
### MCP Protocol
|
||||
|
||||
- 🔌 **MCP Transports** - stdio, Streamable HTTP, WebSocket, SSE (Protocol 2025-11-25)
|
||||
- 🌐 **WebSocket MCP** - Real-time bidirectional connections with auto-reconnect
|
||||
- 🔐 **MCP Security** - Origin validation, DNS rebinding prevention, secure sessions
|
||||
- 🔄 **MCP Resumability** - SSE stream recovery via Last-Event-ID
|
||||
|
||||
## Web Search, Web Fetch & Prompt Caching
|
||||
### Safety & Control
|
||||
|
||||
### CLI Usage
|
||||
- 🤝 **Agent Handoffs** - Transfer context between specialised agents
|
||||
- 🛡️ **Guardrails** - Input/output validation and safety checks
|
||||
- ✅ **Human Approval** - Require human confirmation for critical actions
|
||||
- 💬 **Sessions Management** - Isolated conversation contexts
|
||||
- 🔄 **Stateful Agents** - Maintain state across interactions
|
||||
|
||||
```bash
|
||||
# Web Search - Get real-time information
|
||||
praisonai "What are the latest AI news today?" --web-search --llm openai/gpt-4o-search-preview
|
||||
### Developer Tools
|
||||
|
||||
# Web Fetch - Retrieve and analyze URL content (Anthropic only)
|
||||
praisonai "Summarize https://docs.praison.ai" --web-fetch --llm anthropic/claude-sonnet-4-20250514
|
||||
- ⚡ **Fast Context** - Rapid parallel code search (10-20x faster)
|
||||
- 📜 **Rules & Instructions** - Auto-discover CLAUDE.md, AGENTS.md, GEMINI.md
|
||||
- 🪝 **Hooks** - Pre/post operation hooks for custom logic
|
||||
- 📈 **Telemetry** - Track agent performance and usage
|
||||
- 📹 **Camera Integration** - Capture and analyse camera input
|
||||
|
||||
# Prompt Caching - Reduce costs for repeated prompts
|
||||
praisonai "Analyze this document..." --prompt-caching --llm anthropic/claude-sonnet-4-20250514
|
||||
```
|
||||
### Other Features
|
||||
|
||||
### Programmatic Usage
|
||||
- 🔄 **CrewAI & AG2 Integration** - Use CrewAI or AG2 (Formerly AutoGen) Framework
|
||||
- 💻 **Codebase Chat** - Chat with entire codebase
|
||||
- 🎨 **Interactive UIs** - Multiple interactive interfaces
|
||||
- 📄 **YAML Configuration** - YAML-based agent and workflow configuration
|
||||
- 🛠️ **Custom Tools** - Easy custom tool integration
|
||||
- 🔍 **Internet Search** - Multiple providers (Tavily, You.com, Exa, DuckDuckGo, Crawl4AI)
|
||||
- 🖼️ **VLM Support** - Vision Language Model support
|
||||
- 🎙️ **Voice Interaction** - Real-time voice interaction
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent
|
||||
---
|
||||
|
||||
# Web Search
|
||||
agent = Agent(
|
||||
instructions="You are a research assistant",
|
||||
llm="openai/gpt-4o-search-preview",
|
||||
web_search=True
|
||||
)
|
||||
## 🎓 Video Tutorials
|
||||
|
||||
# Web Fetch (Anthropic only)
|
||||
agent = Agent(
|
||||
instructions="You are a content analyzer",
|
||||
llm="anthropic/claude-sonnet-4-20250514",
|
||||
web_fetch=True
|
||||
)
|
||||
|
||||
# Prompt Caching
|
||||
agent = Agent(
|
||||
instructions="You are an AI assistant..." * 50, # Long system prompt
|
||||
llm="anthropic/claude-sonnet-4-20250514",
|
||||
prompt_caching=True
|
||||
)
|
||||
```
|
||||
|
||||
**Supported Providers:**
|
||||
| Feature | Providers |
|
||||
|---------|-----------|
|
||||
| Web Search | OpenAI, Gemini, Anthropic, xAI, Perplexity |
|
||||
| Web Fetch | Anthropic |
|
||||
| Prompt Caching | OpenAI (auto), Anthropic, Bedrock, Deepseek |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## MCP (Model Context Protocol)
|
||||
|
||||
PraisonAI supports MCP Protocol Revision 2025-11-25 with multiple transports.
|
||||
|
||||
### MCP Client (Consume MCP Servers)
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent, MCP
|
||||
|
||||
# stdio - Local NPX/Python servers
|
||||
agent = Agent(tools=MCP("npx @modelcontextprotocol/server-memory"))
|
||||
|
||||
# Streamable HTTP - Production servers
|
||||
agent = Agent(tools=MCP("https://api.example.com/mcp"))
|
||||
|
||||
# WebSocket - Real-time bidirectional
|
||||
agent = Agent(tools=MCP("wss://api.example.com/mcp", auth_token="token"))
|
||||
|
||||
# SSE (Legacy) - Backward compatibility
|
||||
agent = Agent(tools=MCP("http://localhost:8080/sse"))
|
||||
|
||||
# With environment variables
|
||||
agent = Agent(
|
||||
tools=MCP(
|
||||
command="npx",
|
||||
args=["-y", "@modelcontextprotocol/server-brave-search"],
|
||||
env={"BRAVE_API_KEY": "your-key"}
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
### MCP Server (Expose Tools as MCP Server)
|
||||
|
||||
Expose your Python functions as MCP tools for Claude Desktop, Cursor, and other MCP clients:
|
||||
|
||||
```python
|
||||
from praisonaiagents.mcp import ToolsMCPServer
|
||||
|
||||
def search_web(query: str, max_results: int = 5) -> dict:
|
||||
"""Search the web for information."""
|
||||
return {"results": [f"Result for {query}"]}
|
||||
|
||||
def calculate(expression: str) -> dict:
|
||||
"""Evaluate a mathematical expression."""
|
||||
return {"result": eval(expression)}
|
||||
|
||||
# Create and run MCP server
|
||||
server = ToolsMCPServer(name="my-tools")
|
||||
server.register_tools([search_web, calculate])
|
||||
server.run() # stdio for Claude Desktop
|
||||
# server.run_sse(host="0.0.0.0", port=8080) # SSE for web clients
|
||||
```
|
||||
|
||||
### MCP Features
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| **Session Management** | Automatic `Mcp-Session-Id` handling |
|
||||
| **Protocol Versioning** | `Mcp-Protocol-Version` header |
|
||||
| **Resumability** | SSE stream recovery via `Last-Event-ID` |
|
||||
| **Security** | Origin validation, DNS rebinding prevention |
|
||||
| **WebSocket** | Auto-reconnect with exponential backoff |
|
||||
|
||||
|
||||
## Development:
|
||||
|
||||
Below is used for development only.
|
||||
|
||||
### Using uv
|
||||
```bash
|
||||
# Install uv if you haven't already
|
||||
pip install uv
|
||||
|
||||
# Install from requirements
|
||||
uv pip install -r pyproject.toml
|
||||
|
||||
# Install with extras
|
||||
uv pip install -r pyproject.toml --extra code
|
||||
uv pip install -r pyproject.toml --extra "crewai,autogen"
|
||||
```
|
||||
|
||||
### Bump and Release
|
||||
|
||||
```bash
|
||||
# From project root - bumps version and releases in one command
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99
|
||||
|
||||
# With praisonaiagents dependency
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99 --agents 0.0.169
|
||||
|
||||
# Then publish
|
||||
cd src/praisonai && uv publish
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
- Fork on GitHub: Use the "Fork" button on the repository page.
|
||||
- Clone your fork: `git clone https://github.com/yourusername/praisonAI.git`
|
||||
- Create a branch: `git checkout -b new-feature`
|
||||
- Make changes and commit: `git commit -am "Add some feature"`
|
||||
- Push to your fork: `git push origin new-feature`
|
||||
- Submit a pull request via GitHub's web interface.
|
||||
- Await feedback from project maintainers.
|
||||
|
||||
## Advanced Features
|
||||
|
||||
**Research & Intelligence:**
|
||||
- 🔬 **Deep Research Agents** (OpenAI & Gemini)
|
||||
- 🔄 **Query Rewriter Agent** (HyDE, Step-back, Multi-query)
|
||||
- 🌐 **Native Web Search** (OpenAI, Gemini, Anthropic, xAI, Perplexity)
|
||||
- 📥 **Web Fetch** (Retrieve full content from URLs - Anthropic)
|
||||
- 📝 **Prompt Expander Agent** (Expand short prompts into detailed instructions)
|
||||
|
||||
**Memory & Caching:**
|
||||
- 💾 **Prompt Caching** (Reduce costs & latency - OpenAI, Anthropic, Bedrock, Deepseek)
|
||||
- 🧠 **Claude Memory Tool** (Persistent cross-conversation memory - Anthropic Beta)
|
||||
- 💾 **File-Based Memory** (Zero-dependency persistent memory for all agents)
|
||||
- 🔍 **Built-in Search Tools** (Tavily, You.com, Exa - web search, news, content extraction)
|
||||
|
||||
**Planning & Workflows:**
|
||||
- 📋 **Planning Mode** (Plan before execution - Agent & Multi-Agent)
|
||||
- 🔧 **Planning Tools** (Research with tools during planning)
|
||||
- 🧠 **Planning Reasoning** (Chain-of-thought planning)
|
||||
- ⛓️ **Prompt Chaining** (Sequential prompt workflows with gates)
|
||||
- 🔍 **Evaluator Optimiser** (Generate and optimize through iterative feedback)
|
||||
- 👷 **Orchestrator Workers** (Distribute tasks among specialized workers)
|
||||
- ⚡ **Parallelisation** (Execute tasks in parallel for improved performance)
|
||||
- 🔁 **Repetitive Agents** (Handle repetitive tasks through automated loops)
|
||||
- 🤖 **Autonomous Workflow** (Monitor, act, adapt based on environment feedback)
|
||||
|
||||
**Agent Types:**
|
||||
- 🖼️ **Image Generation Agent** (Create images from text descriptions)
|
||||
- 📷 **Image to Text Agent** (Extract text and descriptions from images)
|
||||
- 🎬 **Video Agent** (Analyze and process video content)
|
||||
- 📊 **Data Analyst Agent** (Analyze data and generate insights)
|
||||
- 💰 **Finance Agent** (Financial analysis and recommendations)
|
||||
- 🛒 **Shopping Agent** (Price comparison and shopping assistance)
|
||||
- ⭐ **Recommendation Agent** (Personalized recommendations)
|
||||
- 📖 **Wikipedia Agent** (Search and extract Wikipedia information)
|
||||
- 💻 **Programming Agent** (Code development and analysis)
|
||||
- 📝 **Markdown Agent** (Generate and format Markdown content)
|
||||
- 🔀 **Router Agent** (Dynamic task routing with cost optimization)
|
||||
|
||||
**MCP Protocol:**
|
||||
- 🔌 **MCP Transports** (stdio, Streamable HTTP, WebSocket, SSE - Protocol 2025-11-25)
|
||||
- 🌐 **WebSocket MCP** (Real-time bidirectional connections with auto-reconnect)
|
||||
- 🔐 **MCP Security** (Origin validation, DNS rebinding prevention, secure sessions)
|
||||
- 🔄 **MCP Resumability** (SSE stream recovery via Last-Event-ID)
|
||||
|
||||
**Safety & Control:**
|
||||
- 🤝 **Agent Handoffs** (Transfer context between specialized agents)
|
||||
- 🛡️ **Guardrails** (Input/output validation and safety checks)
|
||||
- ✅ **Human Approval** (Require human confirmation for critical actions)
|
||||
- 💬 **Sessions Management** (Isolated conversation contexts)
|
||||
- 🔄 **Stateful Agents** (Maintain state across interactions)
|
||||
|
||||
**Developer Tools:**
|
||||
- ⚡ **Fast Context** (Rapid parallel code search - 10-20x faster than traditional methods)
|
||||
- 📜 **Rules & Instructions** (Auto-discover CLAUDE.md, AGENTS.md, GEMINI.md)
|
||||
- 🪝 **Hooks** (Pre/post operation hooks for custom logic)
|
||||
- 📈 **Telemetry** (Track agent performance and usage)
|
||||
- 📹 **Camera Integration** (Capture and analyze camera input)
|
||||
|
||||
## Other Features
|
||||
|
||||
- 🔄 Use CrewAI or AG2 (Formerly AutoGen) Framework
|
||||
- 💻 Chat with ENTIRE Codebase
|
||||
- 🎨 Interactive UIs
|
||||
- 📄 YAML-based Configuration
|
||||
- 🛠️ Custom Tool Integration
|
||||
- 🔍 Internet Search Capability (Tavily, You.com, Exa, DuckDuckGo, Crawl4AI)
|
||||
- 🖼️ Vision Language Model (VLM) Support
|
||||
- 🎙️ Real-time Voice Interaction
|
||||
|
||||
## Video Tutorials
|
||||
Learn PraisonAI through our comprehensive video series:
|
||||
|
||||
| Topic | Video |
|
||||
|-------|--------|
|
||||
@@ -2115,3 +2204,58 @@ cd src/praisonai && uv publish
|
||||
| Call Interface | [](https://www.youtube.com/watch?v=m1cwrUG2iAk) |
|
||||
| Reasoning Extract Agents | [](https://www.youtube.com/watch?v=2PPamsADjJA) |
|
||||
|
||||
---
|
||||
|
||||
## 👥 Contributing
|
||||
|
||||
We welcome contributions from the community! Here's how you can contribute:
|
||||
|
||||
1. **Fork on GitHub** - Use the "Fork" button on the [repository page](https://github.com/MervinPraison/PraisonAI)
|
||||
2. **Clone your fork** - `git clone https://github.com/yourusername/praisonAI.git`
|
||||
3. **Create a branch** - `git checkout -b new-feature`
|
||||
4. **Make changes and commit** - `git commit -am "Add some feature"`
|
||||
5. **Push to your fork** - `git push origin new-feature`
|
||||
6. **Submit a pull request** - Via GitHub's web interface
|
||||
7. **Await feedback** - From project maintainers
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Development
|
||||
|
||||
### Using uv
|
||||
|
||||
```bash
|
||||
# Install uv if you haven't already
|
||||
pip install uv
|
||||
|
||||
# Install from requirements
|
||||
uv pip install -r pyproject.toml
|
||||
|
||||
# Install with extras
|
||||
uv pip install -r pyproject.toml --extra code
|
||||
uv pip install -r pyproject.toml --extra "crewai,autogen"
|
||||
```
|
||||
|
||||
### Bump and Release
|
||||
|
||||
```bash
|
||||
# From project root - bumps version and releases in one command
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99
|
||||
|
||||
# With praisonaiagents dependency
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99 --agents 0.0.169
|
||||
|
||||
# Then publish
|
||||
cd src/praisonai && uv publish
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<p><strong>Made with ❤️ by the PraisonAI Team</strong></p>
|
||||
<p>
|
||||
<a href="https://docs.praison.ai">Documentation</a> •
|
||||
<a href="https://github.com/MervinPraison/PraisonAI">GitHub</a> •
|
||||
<a href="https://github.com/MervinPraison/PraisonAI/issues">Issues</a>
|
||||
</p>
|
||||
</div>
|
||||
+1
-1
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
flask \
|
||||
"praisonai>=2.3.26" \
|
||||
"praisonai>=2.3.28" \
|
||||
"praisonai[api]" \
|
||||
gunicorn \
|
||||
markdown
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.3.26" \
|
||||
"praisonai>=2.3.28" \
|
||||
"praisonai[chat]" \
|
||||
"embedchain[github,youtube]"
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.3.26" \
|
||||
"praisonai>=2.3.28" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[chat]" \
|
||||
"praisonai[realtime]" \
|
||||
|
||||
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
|
||||
# Install Python packages (using latest versions)
|
||||
RUN pip install --no-cache-dir \
|
||||
praisonai_tools \
|
||||
"praisonai>=2.3.26" \
|
||||
"praisonai>=2.3.28" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
framework: crewai
|
||||
topic: "Technology Research and Content Creation using AWS Bedrock"
|
||||
|
||||
roles:
|
||||
agents: # Canonical: use 'agents' instead of 'roles'
|
||||
research_specialist:
|
||||
role: "Research Specialist"
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are an experienced technology researcher with deep expertise in analyzing
|
||||
emerging technologies and market trends. You excel at gathering comprehensive
|
||||
information from various sources and synthesizing it into actionable insights.
|
||||
@@ -35,7 +35,7 @@ roles:
|
||||
|
||||
content_creator:
|
||||
role: "Technical Content Creator"
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are a skilled technical writer and content creator who specializes in
|
||||
translating complex technology concepts into engaging, accessible content
|
||||
for various audiences including developers, business leaders, and end users.
|
||||
@@ -64,7 +64,7 @@ roles:
|
||||
|
||||
quality_reviewer:
|
||||
role: "Content Quality Reviewer"
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are a meticulous editor and quality assurance specialist with extensive
|
||||
experience in technical content review. You ensure accuracy, clarity, and
|
||||
consistency while maintaining the intended tone and message.
|
||||
|
||||
@@ -8,10 +8,10 @@ variables:
|
||||
industry: "Electric Vehicles"
|
||||
research_focus: "comprehensive market analysis"
|
||||
|
||||
roles:
|
||||
agents: # Canonical: use 'agents' instead of 'roles'
|
||||
market_overview_specialist:
|
||||
role: "Market Overview Specialist"
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are a seasoned market research analyst with 10+ years of experience
|
||||
in analyzing market dynamics, trends, and growth patterns across various
|
||||
industries. You excel at synthesizing complex market data into clear,
|
||||
@@ -64,7 +64,7 @@ roles:
|
||||
|
||||
competitive_intelligence_analyst:
|
||||
role: "Competitive Intelligence Analyst"
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are a competitive intelligence expert specializing in analyzing
|
||||
competitive landscapes, market positioning, and strategic intelligence.
|
||||
You have deep expertise in competitor analysis, market share assessment,
|
||||
@@ -119,7 +119,7 @@ roles:
|
||||
|
||||
financial_performance_analyst:
|
||||
role: "Financial Performance Analyst"
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are a financial analyst with expertise in corporate finance,
|
||||
financial statement analysis, and industry benchmarking. You specialize
|
||||
in analyzing financial performance metrics, trends, and comparative
|
||||
@@ -173,7 +173,7 @@ roles:
|
||||
|
||||
growth_opportunities_researcher:
|
||||
role: "Growth Opportunities Researcher"
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are a strategic business analyst specializing in identifying growth
|
||||
opportunities, market expansion strategies, and emerging business models.
|
||||
You excel at spotting trends, gaps, and untapped potential in markets
|
||||
@@ -228,7 +228,7 @@ roles:
|
||||
|
||||
risk_assessment_specialist:
|
||||
role: "Risk Assessment Specialist"
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are a risk management expert with extensive experience in identifying,
|
||||
analyzing, and assessing business risks across various industries. You
|
||||
specialize in market risks, competitive threats, regulatory challenges,
|
||||
@@ -287,7 +287,7 @@ roles:
|
||||
|
||||
research_report_synthesizer:
|
||||
role: "Research Report Synthesizer"
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are an expert business report writer and analyst with extensive
|
||||
experience in synthesizing complex research findings into clear,
|
||||
actionable business intelligence reports. You excel at creating
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
framework: praisonai
|
||||
topic: crawl and extract all data from each url from this page https://news.ycombinator.com/
|
||||
roles:
|
||||
agents: # Canonical: use 'agents' instead of 'roles'
|
||||
web_scraper:
|
||||
backstory: An expert in data extraction from websites, adept at navigating and
|
||||
instructions: # Canonical An expert in data extraction from websites, adept at navigating and
|
||||
retrieving detailed information.
|
||||
goal: Extract URLs from the source page
|
||||
role: Web Scraper
|
||||
@@ -15,7 +15,7 @@ roles:
|
||||
tools:
|
||||
- 'code_interpret'
|
||||
url_data_extractor:
|
||||
backstory: Specializes in crawling websites to gather comprehensive data, ensuring
|
||||
instructions: # Canonical Specializes in crawling websites to gather comprehensive data, ensuring
|
||||
nothing is missed from each link.
|
||||
goal: Crawl each URL for data extraction
|
||||
role: URL Data Extractor
|
||||
@@ -29,7 +29,7 @@ roles:
|
||||
tools:
|
||||
- 'code_interpret'
|
||||
data_organizer:
|
||||
backstory: Known for keen attention to detail, capable of structuring vast amounts
|
||||
instructions: # Canonical Known for keen attention to detail, capable of structuring vast amounts
|
||||
of information into usable formats efficiently.
|
||||
goal: Organize extracted data into a structured format
|
||||
role: Data Organizer
|
||||
|
||||
@@ -31,17 +31,17 @@ variables:
|
||||
- Natural Language Processing
|
||||
|
||||
# Agent definitions using familiar 'roles' format
|
||||
roles:
|
||||
agents: # Canonical: use 'agents' instead of 'roles'
|
||||
classifier:
|
||||
role: Request Classifier
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are an expert at classifying requests into categories.
|
||||
Respond with ONLY one word: 'technical', 'creative', or 'research'.
|
||||
goal: Classify incoming requests accurately
|
||||
|
||||
researcher:
|
||||
role: Research Analyst
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are a skilled research analyst with expertise in technology trends.
|
||||
You provide concise, factual research findings with citations.
|
||||
goal: Research topics thoroughly and provide insights
|
||||
@@ -52,28 +52,28 @@ roles:
|
||||
|
||||
tech_expert:
|
||||
role: Technical Expert
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are a technical expert with deep knowledge in AI and ML.
|
||||
You provide detailed technical explanations.
|
||||
goal: Handle technical questions with expertise
|
||||
|
||||
creative_writer:
|
||||
role: Creative Writer
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You are a creative writer who can make complex topics engaging.
|
||||
You write in a clear, accessible style.
|
||||
goal: Create engaging content from research
|
||||
|
||||
market_analyst:
|
||||
role: Market Analyst
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You analyze market trends and competitive landscapes.
|
||||
You provide actionable business insights.
|
||||
goal: Analyze market trends and opportunities
|
||||
|
||||
aggregator:
|
||||
role: Synthesizer
|
||||
backstory: |
|
||||
instructions: # Canonical |
|
||||
You combine findings from multiple sources into coherent reports.
|
||||
You identify patterns and key insights.
|
||||
goal: Synthesize all findings into comprehensive reports
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../README.md
|
||||
@@ -798,11 +798,16 @@ Create a brief execution plan (2-3 sentences) describing how to best accomplish
|
||||
elif hasattr(step, 'chat'):
|
||||
# It's an Agent - wrap with agent reference and preserve tools
|
||||
agent_tools = getattr(step, 'tools', None)
|
||||
# Check for _yaml_action from YAML parser (canonical: action, alias: description)
|
||||
yaml_action = getattr(step, '_yaml_action', None)
|
||||
# Use yaml_action if set, otherwise fall back to previous_output/input
|
||||
default_action = "{{previous_output}}" if index > 0 else "{{input}}"
|
||||
action = yaml_action if yaml_action else default_action
|
||||
return WorkflowStep(
|
||||
name=getattr(step, 'name', f'agent_{index+1}'),
|
||||
agent=step,
|
||||
tools=agent_tools,
|
||||
action="{{previous_output}}" if index > 0 else "{{input}}"
|
||||
action=action
|
||||
)
|
||||
elif callable(step):
|
||||
return WorkflowStep(
|
||||
|
||||
@@ -102,7 +102,105 @@ class YAMLWorkflowParser:
|
||||
Workflow object ready for execution
|
||||
"""
|
||||
data = yaml.safe_load(yaml_content)
|
||||
return self._parse_workflow_data(data)
|
||||
# Normalize to canonical format (accept both old and new field names)
|
||||
normalized_data = self._normalize_yaml_config(data)
|
||||
return self._parse_workflow_data(normalized_data)
|
||||
|
||||
def _normalize_yaml_config(self, data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
Normalize YAML config to canonical format.
|
||||
|
||||
Accepts both old (agents.yaml) and new (workflow.yaml) field names.
|
||||
This implements "Accept liberally, suggest canonically" principle.
|
||||
|
||||
Canonical names (preferred):
|
||||
- agents (not roles)
|
||||
- instructions (not backstory)
|
||||
- action (not description)
|
||||
- steps (not tasks)
|
||||
- name (not topic)
|
||||
|
||||
Args:
|
||||
data: Raw parsed YAML data
|
||||
|
||||
Returns:
|
||||
Normalized data with canonical field names
|
||||
"""
|
||||
if not data:
|
||||
return data
|
||||
|
||||
normalized = data.copy()
|
||||
|
||||
# 1. Normalize workflow name: topic -> name
|
||||
if 'topic' in normalized and 'name' not in normalized:
|
||||
normalized['name'] = normalized.get('topic', 'Unnamed Workflow')
|
||||
|
||||
# 2. Normalize container: roles -> agents
|
||||
# Note: _convert_roles_to_agents handles backstory -> instructions
|
||||
if 'roles' in normalized and 'agents' not in normalized:
|
||||
normalized['agents'] = self._convert_roles_to_agents(normalized['roles'])
|
||||
# Extract steps from tasks nested in roles if no steps defined
|
||||
if 'steps' not in normalized:
|
||||
normalized['steps'] = self._extract_steps_from_roles(normalized['roles'])
|
||||
|
||||
# 3. Normalize agent fields: backstory -> instructions (for agents section)
|
||||
if 'agents' in normalized:
|
||||
for agent_id, agent_config in normalized['agents'].items():
|
||||
if isinstance(agent_config, dict):
|
||||
# backstory -> instructions
|
||||
if 'backstory' in agent_config and 'instructions' not in agent_config:
|
||||
agent_config['instructions'] = agent_config['backstory']
|
||||
|
||||
# 4. Normalize step fields: description -> action
|
||||
if 'steps' in normalized:
|
||||
for step in normalized['steps']:
|
||||
if isinstance(step, dict):
|
||||
# description -> action
|
||||
if 'description' in step and 'action' not in step:
|
||||
step['action'] = step['description']
|
||||
|
||||
# Handle parallel steps
|
||||
if 'parallel' in step:
|
||||
for parallel_step in step['parallel']:
|
||||
if isinstance(parallel_step, dict):
|
||||
if 'description' in parallel_step and 'action' not in parallel_step:
|
||||
parallel_step['action'] = parallel_step['description']
|
||||
|
||||
return normalized
|
||||
|
||||
def _extract_steps_from_roles(self, roles: Dict[str, Dict]) -> List[Dict]:
|
||||
"""
|
||||
Extract steps from tasks nested within roles (agents.yaml format).
|
||||
|
||||
Args:
|
||||
roles: The roles section from agents.yaml
|
||||
|
||||
Returns:
|
||||
List of step definitions
|
||||
"""
|
||||
steps = []
|
||||
for role_id, role_config in roles.items():
|
||||
if 'tasks' in role_config:
|
||||
for task_id, task_config in role_config['tasks'].items():
|
||||
step = {
|
||||
'name': task_id,
|
||||
'agent': role_id,
|
||||
}
|
||||
# description -> action
|
||||
if 'description' in task_config:
|
||||
step['action'] = task_config['description']
|
||||
elif 'action' in task_config:
|
||||
step['action'] = task_config['action']
|
||||
|
||||
# Copy other task fields
|
||||
for field in ['expected_output', 'context', 'output_file',
|
||||
'output_json', 'create_directory', 'callback',
|
||||
'async_execution', 'guardrail', 'max_retries']:
|
||||
if field in task_config:
|
||||
step[field] = task_config[field]
|
||||
|
||||
steps.append(step)
|
||||
return steps
|
||||
|
||||
def _parse_workflow_data(self, data: Dict[str, Any]) -> Workflow:
|
||||
"""
|
||||
|
||||
@@ -4,8 +4,9 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.1.4"
|
||||
version = "0.1.7"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
authors = [
|
||||
{ name="Mervin Praison" }
|
||||
|
||||
@@ -493,17 +493,22 @@ class TestAgentIntegration:
|
||||
|
||||
def test_agent_store_memory(self):
|
||||
"""Test Agent store_memory method."""
|
||||
import uuid
|
||||
from praisonaiagents import Agent
|
||||
|
||||
# Use unique user_id to avoid stale data from previous test runs
|
||||
unique_user_id = f"test_user_{uuid.uuid4().hex[:8]}"
|
||||
|
||||
agent = Agent(
|
||||
name="Test Agent",
|
||||
memory=True
|
||||
memory=True,
|
||||
user_id=unique_user_id
|
||||
)
|
||||
|
||||
agent.store_memory("User prefers dark mode", memory_type="short_term")
|
||||
|
||||
items = agent._memory_instance.get_short_term()
|
||||
assert len(items) == 1
|
||||
assert len(items) == 1, f"Expected 1 item, got {len(items)} items"
|
||||
assert items[0].content == "User prefers dark mode"
|
||||
|
||||
def test_agent_get_memory_context(self):
|
||||
@@ -658,10 +663,12 @@ class TestMultiAgentMemorySharing:
|
||||
|
||||
def test_agents_share_memory_with_same_user_id(self):
|
||||
"""Test that agents with same user_id share memory."""
|
||||
import uuid
|
||||
from praisonaiagents import Agent
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmpdir:
|
||||
user_id = "shared_user"
|
||||
# Use unique user_id to avoid stale data from previous test runs
|
||||
user_id = f"shared_user_{uuid.uuid4().hex[:8]}"
|
||||
base_path = f"{tmpdir}/memory"
|
||||
|
||||
# Agent 1 stores memories
|
||||
@@ -676,9 +683,9 @@ class TestMultiAgentMemorySharing:
|
||||
agent1._memory_instance.add_entity("Alice", "person", {"role": "developer"})
|
||||
|
||||
# Verify Agent1 stored memories
|
||||
assert len(agent1._memory_instance.get_short_term()) == 1
|
||||
assert len(agent1._memory_instance.get_long_term()) == 1
|
||||
assert len(agent1._memory_instance.get_all_entities()) == 1
|
||||
assert len(agent1._memory_instance.get_short_term()) == 1, f"Expected 1 short_term, got {len(agent1._memory_instance.get_short_term())}"
|
||||
assert len(agent1._memory_instance.get_long_term()) == 1, f"Expected 1 long_term, got {len(agent1._memory_instance.get_long_term())}"
|
||||
assert len(agent1._memory_instance.get_all_entities()) == 1, f"Expected 1 entity, got {len(agent1._memory_instance.get_all_entities())}"
|
||||
|
||||
# Agent 2 with same user_id should load memories
|
||||
agent2 = Agent(
|
||||
|
||||
@@ -6,6 +6,8 @@ The agent should:
|
||||
3. Return the final result (200)
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import os
|
||||
from praisonaiagents import Agent
|
||||
|
||||
def get_stock_price(company_name: str) -> str:
|
||||
@@ -35,41 +37,60 @@ def multiply(a: int, b: int) -> int:
|
||||
print(f"[Tool Called] multiply({a}, {b})")
|
||||
return a * b
|
||||
|
||||
# Test with Gemini
|
||||
print("=" * 60)
|
||||
print("Testing with Gemini model")
|
||||
print("=" * 60)
|
||||
|
||||
agent_gemini = Agent(
|
||||
instructions="You are a helpful assistant. You can use the tools provided to you to help the user. When asked to multiply a stock price, first get the stock price, then multiply it.",
|
||||
llm="gemini/gemini-2.5-pro",
|
||||
tools=[get_stock_price, multiply],
|
||||
verbose=True
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("OPENAI_API_KEY"),
|
||||
reason="OPENAI_API_KEY not set - skipping integration test"
|
||||
)
|
||||
def test_sequential_tool_calling_gpt():
|
||||
"""Test sequential tool calling with GPT model"""
|
||||
print("=" * 60)
|
||||
print("Testing with GPT model")
|
||||
print("=" * 60)
|
||||
|
||||
result_gemini = agent_gemini.start("what is the stock price of Google? multiply the Google stock price with 2")
|
||||
print(f"\nFinal Result (Gemini): {result_gemini}")
|
||||
agent_gpt = Agent(
|
||||
instructions="You are a helpful assistant. When asked to multiply a stock price, first get the stock price, then multiply it.",
|
||||
llm="gpt-4o-mini",
|
||||
tools=[get_stock_price, multiply],
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Test with GPT-4
|
||||
print("\n" + "=" * 60)
|
||||
print("Testing with GPT-4 model")
|
||||
print("=" * 60)
|
||||
result = agent_gpt.start("what is the stock price of Google? multiply the Google stock price with 2")
|
||||
print(f"\nFinal Result (GPT): {result}")
|
||||
|
||||
# Verify result
|
||||
assert result, "GPT returned empty result"
|
||||
print(f"GPT result contains '200': {'200' in str(result)}")
|
||||
|
||||
agent_gpt4 = Agent(
|
||||
instructions="You are a helpful assistant. You can use the tools provided to you to help the user. When asked to multiply a stock price, first get the stock price, then multiply it.",
|
||||
llm="gpt-5-nano",
|
||||
tools=[get_stock_price, multiply],
|
||||
verbose=True
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("GOOGLE_API_KEY"),
|
||||
reason="GOOGLE_API_KEY not set - skipping integration test"
|
||||
)
|
||||
def test_sequential_tool_calling_gemini():
|
||||
"""Test sequential tool calling with Gemini model"""
|
||||
print("=" * 60)
|
||||
print("Testing with Gemini model")
|
||||
print("=" * 60)
|
||||
|
||||
result_gpt4 = agent_gpt4.start("what is the stock price of Google? multiply the Google stock price with 2")
|
||||
print(f"\nFinal Result (GPT-4): {result_gpt4}")
|
||||
agent_gemini = Agent(
|
||||
instructions="You are a helpful assistant. When asked to multiply a stock price, first get the stock price, then multiply it.",
|
||||
llm="gemini/gemini-2.0-flash",
|
||||
tools=[get_stock_price, multiply],
|
||||
verbose=True
|
||||
)
|
||||
|
||||
# Verify results
|
||||
print("\n" + "=" * 60)
|
||||
print("Test Results Summary")
|
||||
print("=" * 60)
|
||||
print(f"Gemini result contains '200': {'200' in str(result_gemini) if result_gemini else False}")
|
||||
print(f"GPT-4 result contains '200': {'200' in str(result_gpt4) if result_gpt4 else False}")
|
||||
print(f"Gemini returned empty: {not result_gemini or result_gemini == ''}")
|
||||
print(f"GPT-4 returned empty: {not result_gpt4 or result_gpt4 == ''}")
|
||||
result = agent_gemini.start("what is the stock price of Google? multiply the Google stock price with 2")
|
||||
print(f"\nFinal Result (Gemini): {result}")
|
||||
|
||||
# Verify result
|
||||
assert result, "Gemini returned empty result"
|
||||
print(f"Gemini result contains '200': {'200' in str(result)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Run tests manually
|
||||
if os.environ.get("OPENAI_API_KEY"):
|
||||
test_sequential_tool_calling_gpt()
|
||||
if os.environ.get("GOOGLE_API_KEY"):
|
||||
test_sequential_tool_calling_gemini()
|
||||
@@ -2,7 +2,23 @@
|
||||
"""Test LLM class directly to verify self-reflection fix"""
|
||||
|
||||
from praisonaiagents.llm import LLM
|
||||
from praisonaiagents.tools import calculator
|
||||
|
||||
# Define calculator tool locally to avoid import issues
|
||||
def calculator(expression: str) -> str:
|
||||
"""
|
||||
Evaluate a mathematical expression.
|
||||
|
||||
Args:
|
||||
expression: A mathematical expression to evaluate (e.g., "2 + 2", "15 * 23")
|
||||
|
||||
Returns:
|
||||
The result of the calculation as a string
|
||||
"""
|
||||
try:
|
||||
result = eval(expression)
|
||||
return str(result)
|
||||
except Exception as e:
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
def test_llm_direct():
|
||||
"""Test LLM class directly with self-reflection and tools"""
|
||||
|
||||
@@ -5,7 +5,9 @@ Test script to verify Ollama empty response fix for both sync and async methods.
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents, TaskOutput
|
||||
import os
|
||||
import pytest
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from typing import Dict, Any
|
||||
|
||||
# Enable debug logging
|
||||
@@ -25,8 +27,8 @@ def search_tool(query: str) -> Dict[str, Any]:
|
||||
logging.debug(f"[TEST] Search tool returning: {results}")
|
||||
return results
|
||||
|
||||
# Test with both sync and async workflows
|
||||
async def test_model_async(model_name: str):
|
||||
# Helper async function to test a model
|
||||
async def _test_model_async_helper(model_name: str):
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Testing ASYNC with model: {model_name}")
|
||||
print('='*60)
|
||||
@@ -78,7 +80,7 @@ async def test_model_async(model_name: str):
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
def test_model_sync(model_name: str):
|
||||
def _test_model_sync_helper(model_name: str):
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Testing SYNC with model: {model_name}")
|
||||
print('='*60)
|
||||
@@ -130,18 +132,60 @@ def test_model_sync(model_name: str):
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("OPENAI_API_KEY"),
|
||||
reason="OPENAI_API_KEY not set - skipping integration test"
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
async def test_ollama_async_openai():
|
||||
"""Test async with OpenAI model as baseline."""
|
||||
result = await _test_model_async_helper("gpt-4o-mini")
|
||||
assert result, "OpenAI async model test failed"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("OPENAI_API_KEY"),
|
||||
reason="OPENAI_API_KEY not set - skipping integration test"
|
||||
)
|
||||
def test_ollama_sync_openai():
|
||||
"""Test sync with OpenAI model as baseline."""
|
||||
result = _test_model_sync_helper("gpt-4o-mini")
|
||||
assert result, "OpenAI sync model test failed"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("OLLAMA_HOST", "").strip(),
|
||||
reason="OLLAMA_HOST not set - skipping Ollama integration test"
|
||||
)
|
||||
@pytest.mark.asyncio
|
||||
async def test_ollama_async_ollama():
|
||||
"""Test async with Ollama model."""
|
||||
result = await _test_model_async_helper("ollama/llama3.2")
|
||||
assert result, "Ollama async model test failed"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("OLLAMA_HOST", "").strip(),
|
||||
reason="OLLAMA_HOST not set - skipping Ollama integration test"
|
||||
)
|
||||
def test_ollama_sync_ollama():
|
||||
"""Test sync with Ollama model."""
|
||||
result = _test_model_sync_helper("ollama/llama3.2")
|
||||
assert result, "Ollama sync model test failed"
|
||||
|
||||
|
||||
async def main():
|
||||
print("Testing Ollama empty response fix for both sync and async...")
|
||||
|
||||
# Test sync methods
|
||||
print("\n1. Testing SYNC methods:")
|
||||
openai_sync_success = test_model_sync("openai/gpt-5-nano")
|
||||
ollama_sync_success = test_model_sync("ollama/llama3.2")
|
||||
openai_sync_success = _test_model_sync_helper("gpt-4o-mini")
|
||||
ollama_sync_success = _test_model_sync_helper("ollama/llama3.2")
|
||||
|
||||
# Test async methods
|
||||
print("\n2. Testing ASYNC methods:")
|
||||
openai_async_success = await test_model_async("openai/gpt-5-nano")
|
||||
ollama_async_success = await test_model_async("ollama/llama3.2")
|
||||
openai_async_success = await _test_model_async_helper("gpt-4o-mini")
|
||||
ollama_async_success = await _test_model_async_helper("ollama/llama3.2")
|
||||
|
||||
# Summary
|
||||
print(f"\n{'='*60}")
|
||||
|
||||
@@ -4,7 +4,9 @@ Test script to verify Ollama empty response fix.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents, TaskOutput
|
||||
import os
|
||||
import pytest
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from typing import Dict, Any
|
||||
|
||||
# Enable debug logging
|
||||
@@ -24,8 +26,8 @@ def search_tool(query: str) -> Dict[str, Any]:
|
||||
logging.debug(f"[TEST] Search tool returning: {results}")
|
||||
return results
|
||||
|
||||
# Test with both OpenAI and Ollama models
|
||||
def test_model(model_name: str):
|
||||
# Helper function to test a model
|
||||
def _test_model_helper(model_name: str):
|
||||
print(f"\n{'='*60}")
|
||||
print(f"Testing with model: {model_name}")
|
||||
print('='*60)
|
||||
@@ -77,16 +79,36 @@ def test_model(model_name: str):
|
||||
traceback.print_exc()
|
||||
return False
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("OPENAI_API_KEY"),
|
||||
reason="OPENAI_API_KEY not set - skipping integration test"
|
||||
)
|
||||
def test_ollama_fix_openai():
|
||||
"""Test with OpenAI model as baseline."""
|
||||
result = _test_model_helper("gpt-4o-mini")
|
||||
assert result, "OpenAI model test failed"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not os.environ.get("OLLAMA_HOST", "").strip(),
|
||||
reason="OLLAMA_HOST not set - skipping Ollama integration test"
|
||||
)
|
||||
def test_ollama_fix_ollama():
|
||||
"""Test with Ollama model."""
|
||||
result = _test_model_helper("ollama/llama3.2")
|
||||
assert result, "Ollama model test failed"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Testing Ollama empty response fix...")
|
||||
|
||||
# Test with OpenAI first (as baseline)
|
||||
print("\n1. Testing with OpenAI (baseline):")
|
||||
openai_success = test_model("openai/gpt-5-nano")
|
||||
openai_success = _test_model_helper("gpt-4o-mini")
|
||||
|
||||
# Test with Ollama
|
||||
print("\n2. Testing with Ollama:")
|
||||
ollama_success = test_model("ollama/llama3.2")
|
||||
ollama_success = _test_model_helper("ollama/llama3.2")
|
||||
|
||||
# Summary
|
||||
print(f"\n{'='*60}")
|
||||
|
||||
@@ -53,11 +53,14 @@ def test_ollama_argument_validation():
|
||||
print("\n2. Testing mixed arguments (the main issue):")
|
||||
mixed_args = {"a": "get_stock_price", "company_name": "Google", "b": "2"}
|
||||
filtered_args = llm._validate_and_filter_ollama_arguments("multiply", mixed_args, tools)
|
||||
expected_filtered = {"a": "get_stock_price", "b": "2"} # Should remove 'company_name'
|
||||
# Note: The method may coerce "2" to int 2 based on type hints - both are acceptable
|
||||
expected_filtered_str = {"a": "get_stock_price", "b": "2"} # String version
|
||||
expected_filtered_int = {"a": "get_stock_price", "b": 2} # Int version (coerced)
|
||||
print(f"Original: {mixed_args}")
|
||||
print(f"Filtered: {filtered_args}")
|
||||
print(f"Expected: {expected_filtered}")
|
||||
assert filtered_args == expected_filtered, f"Expected {expected_filtered}, got {filtered_args}"
|
||||
print(f"Expected: {expected_filtered_str} or {expected_filtered_int}")
|
||||
assert filtered_args == expected_filtered_str or filtered_args == expected_filtered_int, \
|
||||
f"Expected {expected_filtered_str} or {expected_filtered_int}, got {filtered_args}"
|
||||
print("✅ Mixed arguments filtering test passed")
|
||||
|
||||
# Test case 3: All invalid arguments
|
||||
|
||||
@@ -704,7 +704,8 @@ class TestPlanningAgent:
|
||||
planner = PlanningAgent()
|
||||
|
||||
mock_llm = MagicMock()
|
||||
mock_llm.achat = AsyncMock(return_value=json.dumps({
|
||||
# Mock get_response_async which is the method actually called
|
||||
mock_llm.get_response_async = AsyncMock(return_value=json.dumps({
|
||||
"name": "Test Plan",
|
||||
"description": "A test plan",
|
||||
"steps": [
|
||||
@@ -733,7 +734,8 @@ class TestPlanningAgent:
|
||||
planner = PlanningAgent()
|
||||
|
||||
mock_llm = MagicMock()
|
||||
mock_llm.achat = AsyncMock(return_value=json.dumps({
|
||||
# Mock get_response_async which is the method actually called
|
||||
mock_llm.get_response_async = AsyncMock(return_value=json.dumps({
|
||||
"name": "Refined Plan",
|
||||
"description": "Updated plan",
|
||||
"steps": [
|
||||
@@ -765,7 +767,8 @@ class TestPlanningAgent:
|
||||
planner = PlanningAgent()
|
||||
|
||||
mock_llm = MagicMock()
|
||||
mock_llm.achat = AsyncMock(return_value="Analysis: Python project with FastAPI")
|
||||
# Mock get_response_async which is the method actually called
|
||||
mock_llm.get_response_async = AsyncMock(return_value="Analysis: Python project with FastAPI")
|
||||
|
||||
planner._llm = mock_llm
|
||||
|
||||
|
||||
@@ -2,7 +2,29 @@
|
||||
"""Comprehensive test script to verify self-reflection works with tools after the fix"""
|
||||
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import duckduckgo_search
|
||||
|
||||
# Define duckduckgo_search tool locally to avoid import issues
|
||||
def duckduckgo_search(query: str) -> str:
|
||||
"""
|
||||
Search the web using DuckDuckGo.
|
||||
|
||||
Args:
|
||||
query: The search query string
|
||||
|
||||
Returns:
|
||||
Search results as a string
|
||||
"""
|
||||
try:
|
||||
from duckduckgo_search import DDGS
|
||||
with DDGS() as ddgs:
|
||||
results = list(ddgs.text(query, max_results=3))
|
||||
if results:
|
||||
return "\n".join([f"- {r.get('title', '')}: {r.get('body', '')}" for r in results])
|
||||
return "No results found"
|
||||
except ImportError:
|
||||
return f"Mock search results for: {query}"
|
||||
except Exception as e:
|
||||
return f"Search error: {str(e)}"
|
||||
|
||||
def test_self_reflection_with_tools():
|
||||
"""Test self-reflection with tools - should work after the fix"""
|
||||
|
||||
@@ -2,7 +2,23 @@
|
||||
"""Simple test to verify the self-reflection fix works"""
|
||||
|
||||
from praisonaiagents import Agent
|
||||
from praisonaiagents.tools import calculator
|
||||
|
||||
# Define calculator tool locally to avoid import issues
|
||||
def calculator(expression: str) -> str:
|
||||
"""
|
||||
Evaluate a mathematical expression.
|
||||
|
||||
Args:
|
||||
expression: A mathematical expression to evaluate (e.g., "2 + 2", "25 * 17")
|
||||
|
||||
Returns:
|
||||
The result of the calculation as a string
|
||||
"""
|
||||
try:
|
||||
result = eval(expression)
|
||||
return str(result)
|
||||
except Exception as e:
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
def test_self_reflection_fix():
|
||||
"""Test that self-reflection works with tools after the fix"""
|
||||
|
||||
@@ -2,7 +2,23 @@
|
||||
"""Test to verify the self-reflection fix works with tools"""
|
||||
|
||||
from praisonaiagents import Agent, Task, PraisonAIAgents
|
||||
from praisonaiagents.tools import calculator
|
||||
|
||||
# Define calculator tool locally to avoid import issues
|
||||
def calculator(expression: str) -> str:
|
||||
"""
|
||||
Evaluate a mathematical expression.
|
||||
|
||||
Args:
|
||||
expression: A mathematical expression to evaluate (e.g., "123 * 456")
|
||||
|
||||
Returns:
|
||||
The result of the calculation as a string
|
||||
"""
|
||||
try:
|
||||
result = eval(expression)
|
||||
return str(result)
|
||||
except Exception as e:
|
||||
return f"Error: {str(e)}"
|
||||
|
||||
def test_self_reflection_fix_verification():
|
||||
"""Test that self-reflection now works with tools"""
|
||||
|
||||
@@ -319,7 +319,14 @@ class TestExecuteWithAgents:
|
||||
result = manager.execute("test_workflow")
|
||||
|
||||
assert not result["success"]
|
||||
assert "No executor available" in result.get("error", "")
|
||||
# Error can be at top level or in results array
|
||||
error_found = "No executor available" in result.get("error", "")
|
||||
if not error_found and "results" in result:
|
||||
error_found = any(
|
||||
"No executor available" in r.get("error", "")
|
||||
for r in result.get("results", [])
|
||||
)
|
||||
assert error_found, f"Expected 'No executor available' error, got: {result}"
|
||||
|
||||
|
||||
class TestWorkflowLevelDefaults:
|
||||
|
||||
@@ -1339,3 +1339,235 @@ steps:
|
||||
assert researcher is not None
|
||||
# backstory should be used as instructions
|
||||
assert researcher.backstory == "You are an experienced researcher with 10 years of experience."
|
||||
|
||||
|
||||
class TestFieldNameNormalization:
|
||||
"""Tests for field name normalization (Accept liberally, suggest canonically)."""
|
||||
|
||||
def test_roles_normalized_to_agents(self):
|
||||
"""Test that 'roles' is normalized to 'agents'."""
|
||||
from praisonaiagents.workflows import YAMLWorkflowParser
|
||||
|
||||
yaml_content = """
|
||||
name: Roles Test
|
||||
roles:
|
||||
researcher:
|
||||
role: Research Analyst
|
||||
goal: Research topics
|
||||
backstory: "Expert researcher"
|
||||
|
||||
steps:
|
||||
- agent: researcher
|
||||
action: "Research AI"
|
||||
"""
|
||||
parser = YAMLWorkflowParser()
|
||||
workflow = parser.parse_string(yaml_content)
|
||||
|
||||
assert workflow is not None
|
||||
assert 'researcher' in parser._agents
|
||||
|
||||
def test_topic_normalized_to_name(self):
|
||||
"""Test that 'topic' is normalized to 'name'."""
|
||||
from praisonaiagents.workflows import YAMLWorkflowParser
|
||||
|
||||
yaml_content = """
|
||||
topic: My Topic Workflow
|
||||
agents:
|
||||
researcher:
|
||||
name: Researcher
|
||||
role: Research Analyst
|
||||
goal: Research topics
|
||||
instructions: "Provide research"
|
||||
|
||||
steps:
|
||||
- agent: researcher
|
||||
action: "Research"
|
||||
"""
|
||||
parser = YAMLWorkflowParser()
|
||||
workflow = parser.parse_string(yaml_content)
|
||||
|
||||
assert workflow is not None
|
||||
assert workflow.name == "My Topic Workflow"
|
||||
|
||||
def test_backstory_normalized_to_instructions(self):
|
||||
"""Test that 'backstory' is normalized to 'instructions'."""
|
||||
from praisonaiagents.workflows import YAMLWorkflowParser
|
||||
|
||||
yaml_content = """
|
||||
name: Backstory Test
|
||||
agents:
|
||||
researcher:
|
||||
name: Researcher
|
||||
role: Research Analyst
|
||||
goal: Research topics
|
||||
backstory: "Expert researcher with 10 years experience"
|
||||
|
||||
steps:
|
||||
- agent: researcher
|
||||
action: "Research AI"
|
||||
"""
|
||||
parser = YAMLWorkflowParser()
|
||||
workflow = parser.parse_string(yaml_content)
|
||||
|
||||
assert workflow is not None
|
||||
researcher = parser._agents.get('researcher')
|
||||
assert researcher is not None
|
||||
|
||||
def test_description_normalized_to_action(self):
|
||||
"""Test that 'description' is normalized to 'action'."""
|
||||
from praisonaiagents.workflows import YAMLWorkflowParser
|
||||
|
||||
yaml_content = """
|
||||
name: Description Test
|
||||
agents:
|
||||
researcher:
|
||||
name: Researcher
|
||||
role: Research Analyst
|
||||
goal: Research topics
|
||||
instructions: "Provide research"
|
||||
|
||||
steps:
|
||||
- agent: researcher
|
||||
description: "Research AI trends"
|
||||
"""
|
||||
parser = YAMLWorkflowParser()
|
||||
workflow = parser.parse_string(yaml_content)
|
||||
|
||||
assert workflow is not None
|
||||
# The step should have been parsed with action from description
|
||||
researcher = parser._agents.get('researcher')
|
||||
assert researcher is not None
|
||||
assert hasattr(researcher, '_yaml_action')
|
||||
assert researcher._yaml_action == "Research AI trends"
|
||||
|
||||
def test_full_agents_yaml_format(self):
|
||||
"""Test that full agents.yaml format with roles, backstory, tasks is normalized."""
|
||||
from praisonaiagents.workflows import YAMLWorkflowParser
|
||||
|
||||
yaml_content = """
|
||||
topic: AI Research
|
||||
framework: praisonai
|
||||
process: workflow
|
||||
|
||||
workflow:
|
||||
planning: true
|
||||
verbose: true
|
||||
|
||||
roles:
|
||||
researcher:
|
||||
role: Research Analyst
|
||||
backstory: "Expert researcher"
|
||||
goal: Research topics
|
||||
|
||||
writer:
|
||||
role: Content Writer
|
||||
backstory: "Expert writer"
|
||||
goal: Write content
|
||||
|
||||
steps:
|
||||
- agent: researcher
|
||||
description: "Research AI trends"
|
||||
- agent: writer
|
||||
description: "Write summary"
|
||||
"""
|
||||
parser = YAMLWorkflowParser()
|
||||
workflow = parser.parse_string(yaml_content)
|
||||
|
||||
assert workflow is not None
|
||||
assert workflow.name == "AI Research"
|
||||
assert 'researcher' in parser._agents
|
||||
assert 'writer' in parser._agents
|
||||
|
||||
def test_parallel_steps_description_normalized(self):
|
||||
"""Test that description in parallel steps is normalized to action."""
|
||||
from praisonaiagents.workflows import YAMLWorkflowParser
|
||||
|
||||
yaml_content = """
|
||||
name: Parallel Test
|
||||
agents:
|
||||
researcher1:
|
||||
name: Researcher1
|
||||
role: Market Analyst
|
||||
goal: Research market
|
||||
instructions: "Provide market insights"
|
||||
researcher2:
|
||||
name: Researcher2
|
||||
role: Tech Analyst
|
||||
goal: Research tech
|
||||
instructions: "Provide tech insights"
|
||||
|
||||
steps:
|
||||
- name: parallel_research
|
||||
parallel:
|
||||
- agent: researcher1
|
||||
description: "Research market trends"
|
||||
- agent: researcher2
|
||||
description: "Research tech trends"
|
||||
"""
|
||||
parser = YAMLWorkflowParser()
|
||||
workflow = parser.parse_string(yaml_content)
|
||||
|
||||
assert workflow is not None
|
||||
# Both agents should have their actions set
|
||||
researcher1 = parser._agents.get('researcher1')
|
||||
researcher2 = parser._agents.get('researcher2')
|
||||
assert researcher1 is not None
|
||||
assert researcher2 is not None
|
||||
|
||||
def test_canonical_names_work(self):
|
||||
"""Test that canonical names (agents, instructions, action, steps, name) work."""
|
||||
from praisonaiagents.workflows import YAMLWorkflowParser
|
||||
|
||||
yaml_content = """
|
||||
name: Canonical Test
|
||||
agents:
|
||||
researcher:
|
||||
name: Researcher
|
||||
role: Research Analyst
|
||||
goal: Research topics
|
||||
instructions: "Provide detailed research"
|
||||
|
||||
steps:
|
||||
- agent: researcher
|
||||
action: "Research AI trends"
|
||||
"""
|
||||
parser = YAMLWorkflowParser()
|
||||
workflow = parser.parse_string(yaml_content)
|
||||
|
||||
assert workflow is not None
|
||||
assert workflow.name == "Canonical Test"
|
||||
researcher = parser._agents.get('researcher')
|
||||
assert researcher is not None
|
||||
assert researcher._yaml_action == "Research AI trends"
|
||||
|
||||
def test_mixed_old_and_new_names(self):
|
||||
"""Test that mixing old and new names works."""
|
||||
from praisonaiagents.workflows import YAMLWorkflowParser
|
||||
|
||||
yaml_content = """
|
||||
name: Mixed Test
|
||||
agents:
|
||||
researcher:
|
||||
name: Researcher
|
||||
role: Research Analyst
|
||||
goal: Research topics
|
||||
backstory: "Expert researcher" # Old name
|
||||
|
||||
writer:
|
||||
name: Writer
|
||||
role: Content Writer
|
||||
goal: Write content
|
||||
instructions: "Write clearly" # New name
|
||||
|
||||
steps:
|
||||
- agent: researcher
|
||||
action: "Research AI" # New name
|
||||
- agent: writer
|
||||
description: "Write summary" # Old name
|
||||
"""
|
||||
parser = YAMLWorkflowParser()
|
||||
workflow = parser.parse_string(yaml_content)
|
||||
|
||||
assert workflow is not None
|
||||
assert 'researcher' in parser._agents
|
||||
assert 'writer' in parser._agents
|
||||
Generated
+1
-1
@@ -2422,7 +2422,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.1.4"
|
||||
version = "0.1.7"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
||||
+431
-287
@@ -30,7 +30,113 @@ PraisonAI is a production-ready Multi-AI Agents framework with self-reflection,
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Key Features
|
||||
---
|
||||
|
||||
## 📑 Table of Contents
|
||||
|
||||
- [🚀 Quick Start](#-quick-start)
|
||||
- [📦 Installation](#-installation)
|
||||
- [Python SDK](#python-sdk)
|
||||
- [JavaScript SDK](#javascript-sdk)
|
||||
- [💻 Usage](#-usage)
|
||||
- [Python Code Examples](#python-code-examples)
|
||||
- [CLI / No-Code Interface](#cli--no-code-interface)
|
||||
- [JavaScript Code Examples](#javascript-code-examples)
|
||||
- [✨ Key Features](#-key-features)
|
||||
- [🌐 Supported Providers](#-supported-providers)
|
||||
- [🛠️ Custom Tools](#️-custom-tools)
|
||||
- [🔧 Configuration & Integration](#-configuration--integration)
|
||||
- [Ollama Integration](#ollama-integration)
|
||||
- [Groq Integration](#groq-integration)
|
||||
- [100+ Models Support](#100-models-support)
|
||||
- [📋 Workflows](#-workflows)
|
||||
- [Workflow Patterns](#workflow-patterns)
|
||||
- [YAML Configuration](#yaml-configuration)
|
||||
- [Agents Playbook](#agents-playbook)
|
||||
- [🧠 Memory & Context](#-memory--context)
|
||||
- [🔬 Advanced Features](#-advanced-features)
|
||||
- [Research & Intelligence](#research--intelligence)
|
||||
- [Planning & Workflows](#planning--workflows)
|
||||
- [Specialised Agents](#specialised-agents)
|
||||
- [MCP Protocol](#mcp-protocol)
|
||||
- [Safety & Control](#safety--control)
|
||||
- [Developer Tools](#developer-tools)
|
||||
- [📊 Process Types & Patterns](#-process-types--patterns)
|
||||
- [Sequential Process](#sequential-process)
|
||||
- [Hierarchical Process](#hierarchical-process)
|
||||
- [Workflow Process](#workflow-process)
|
||||
- [Agentic Patterns](#agentic-patterns)
|
||||
- [🎓 Video Tutorials](#-video-tutorials)
|
||||
- [⭐ Star History](#-star-history)
|
||||
- [👥 Contributing](#-contributing)
|
||||
- [🔧 Development](#-development)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
Get started with PraisonAI in under 1 minute:
|
||||
|
||||
```bash
|
||||
# Install
|
||||
pip install praisonaiagents
|
||||
|
||||
# Set API key
|
||||
export OPENAI_API_KEY=your_key_here
|
||||
|
||||
# Create a simple agent
|
||||
python -c "from praisonaiagents import Agent; Agent(instructions='You are a helpful AI assistant').start('Write a haiku about AI')"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
### Python SDK
|
||||
|
||||
Lightweight package dedicated for coding:
|
||||
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
|
||||
For the full framework with CLI support:
|
||||
|
||||
```bash
|
||||
pip install praisonai
|
||||
```
|
||||
|
||||
### JavaScript SDK
|
||||
|
||||
```bash
|
||||
npm install praisonai
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💻 Usage
|
||||
|
||||
### Python Code Examples
|
||||
|
||||
- [📘 Complete Python Examples](#using-python-code)
|
||||
- [🔧 Custom Tools](#️-custom-tools)
|
||||
- [🧠 Memory Configuration](#-memory--context)
|
||||
|
||||
### CLI / No-Code Interface
|
||||
|
||||
- [🎯 Auto Mode](#auto-mode)
|
||||
- [🔬 Deep Research](#deep-research-cli)
|
||||
- [📋 Planning Mode](#planning-mode-cli)
|
||||
- [💾 Memory Management](#memory-cli)
|
||||
- [🔄 Workflow Execution](#workflow-cli)
|
||||
|
||||
### JavaScript Code Examples
|
||||
|
||||
- [💻 JavaScript Usage](#using-javascript-code)
|
||||
|
||||
---
|
||||
|
||||
## ✨ Key Features
|
||||
|
||||
| Feature | Code | Docs |
|
||||
|---------|:----:|:----:|
|
||||
@@ -114,7 +220,11 @@ PraisonAI is a production-ready Multi-AI Agents framework with self-reflection,
|
||||
| 💾 Auto-Save Sessions | [Example](#session-management-python) | [📖](https://docs.praison.ai/cli/session) |
|
||||
| 📜 History in Context | [Example](#session-management-python) | [📖](https://docs.praison.ai/cli/session) |
|
||||
|
||||
## Supported Providers
|
||||
---
|
||||
|
||||
## 🌐 Supported Providers
|
||||
|
||||
PraisonAI supports 100+ LLM providers through seamless integration:
|
||||
|
||||
| Provider | Example |
|
||||
|----------|:-------:|
|
||||
@@ -143,14 +253,14 @@ PraisonAI is a production-ready Multi-AI Agents framework with self-reflection,
|
||||
| Moonshot | [Example](examples/python/providers/moonshot/moonshot_example.py) |
|
||||
| vLLM | [Example](examples/python/providers/vllm/vllm_example.py) |
|
||||
|
||||
## Using Python Code
|
||||
---
|
||||
|
||||
## 📘 Using Python Code
|
||||
|
||||
Light weight package dedicated for coding:
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
```
|
||||
|
||||
```bash
|
||||
pip install praisonaiagents
|
||||
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
@@ -605,6 +715,77 @@ result = manager.execute_yaml(
|
||||
)
|
||||
```
|
||||
|
||||
### Complete workflow.yaml Reference
|
||||
|
||||
```yaml
|
||||
# workflow.yaml - Full feature reference
|
||||
name: Complete Workflow
|
||||
description: Demonstrates all workflow.yaml features
|
||||
framework: praisonai # praisonai, crewai, autogen
|
||||
process: workflow # sequential, hierarchical, workflow
|
||||
|
||||
workflow:
|
||||
planning: true
|
||||
planning_llm: gpt-4o
|
||||
reasoning: true
|
||||
verbose: true
|
||||
memory_config:
|
||||
provider: chroma
|
||||
persist: true
|
||||
|
||||
variables:
|
||||
topic: AI trends
|
||||
items: [ML, NLP, Vision]
|
||||
|
||||
agents:
|
||||
researcher:
|
||||
name: Researcher
|
||||
role: Research Analyst
|
||||
goal: Research topics thoroughly
|
||||
instructions: "Provide detailed research findings"
|
||||
backstory: "Expert researcher with 10 years experience" # alias for instructions
|
||||
llm: gpt-4o-mini
|
||||
function_calling_llm: gpt-4o # For tool calls
|
||||
max_rpm: 10 # Rate limiting
|
||||
max_execution_time: 300 # Timeout in seconds
|
||||
reflect_llm: gpt-4o # For self-reflection
|
||||
min_reflect: 1
|
||||
max_reflect: 3
|
||||
system_template: "You are a helpful assistant"
|
||||
tools:
|
||||
- tavily_search
|
||||
|
||||
writer:
|
||||
name: Writer
|
||||
role: Content Writer
|
||||
goal: Write clear content
|
||||
instructions: "Write engaging content"
|
||||
|
||||
steps:
|
||||
- name: research_step
|
||||
agent: researcher
|
||||
action: "Research {{topic}}"
|
||||
expected_output: "Comprehensive research report"
|
||||
output_file: "output/research.md"
|
||||
create_directory: true
|
||||
|
||||
- name: writing_step
|
||||
agent: writer
|
||||
action: "Write article based on research"
|
||||
context: # Task dependencies
|
||||
- research_step
|
||||
output_json: # Structured output
|
||||
type: object
|
||||
properties:
|
||||
title: { type: string }
|
||||
content: { type: string }
|
||||
|
||||
callbacks:
|
||||
on_workflow_start: log_start
|
||||
on_step_complete: log_step
|
||||
on_workflow_complete: log_complete
|
||||
```
|
||||
|
||||
### 9. Hooks
|
||||
|
||||
Configure in `.praison/hooks.json`:
|
||||
@@ -621,9 +802,50 @@ hooks.register("pre_write_code", lambda ctx: print(f"Writing {ctx['file']}"))
|
||||
result = hooks.execute("pre_write_code", {"file": "main.py"})
|
||||
```
|
||||
|
||||
### 10. Extended agents.yaml with Workflow Patterns
|
||||
### 10. Field Names Reference (A-I-G-S)
|
||||
|
||||
You can now use advanced workflow patterns directly in agents.yaml by setting `process: workflow`:
|
||||
PraisonAI accepts both old (agents.yaml) and new (workflow.yaml) field names. Use the **canonical names** for new projects:
|
||||
|
||||
| Canonical (Recommended) | Alias (Also Works) | Purpose |
|
||||
|-------------------------|-------------------|---------|
|
||||
| `agents` | `roles` | Define agent personas |
|
||||
| `instructions` | `backstory` | Agent behavior/persona |
|
||||
| `action` | `description` | What the step does |
|
||||
| `steps` | `tasks` (nested) | Define work items |
|
||||
| `name` | `topic` | Workflow identifier |
|
||||
|
||||
**A-I-G-S Mnemonic** - Easy to remember:
|
||||
- **A**gents - Who does the work
|
||||
- **I**nstructions - How they behave
|
||||
- **G**oal - What they achieve
|
||||
- **S**teps - What they do
|
||||
|
||||
```yaml
|
||||
# Quick Reference - Canonical Format
|
||||
name: My Workflow # Workflow name (not 'topic')
|
||||
agents: # Define agents (not 'roles')
|
||||
my_agent:
|
||||
role: Job Title # Agent's role
|
||||
goal: What to achieve # Agent's goal
|
||||
instructions: How to act # Agent's behavior (not 'backstory')
|
||||
|
||||
steps: # Define steps (not 'tasks')
|
||||
- agent: my_agent
|
||||
action: What to do # Step action (not 'description')
|
||||
```
|
||||
|
||||
> **Note:** The parser accepts both old and new names. Run `praisonai workflow validate <file.yaml>` to see suggestions for canonical names.
|
||||
|
||||
### 11. Extended agents.yaml with Workflow Patterns
|
||||
|
||||
**Feature Parity:** Both `agents.yaml` and `workflow.yaml` now support the same features:
|
||||
- All workflow patterns (route, parallel, loop, repeat)
|
||||
- All agent fields (function_calling_llm, max_rpm, max_execution_time, reflect_llm, templates)
|
||||
- All step fields (expected_output, context, output_json, create_directory, callback)
|
||||
- Framework support (praisonai, crewai, autogen)
|
||||
- Process types (sequential, hierarchical, workflow)
|
||||
|
||||
You can use advanced workflow patterns directly in agents.yaml by setting `process: workflow`:
|
||||
|
||||
```yaml
|
||||
# agents.yaml with workflow patterns
|
||||
@@ -639,15 +861,15 @@ workflow:
|
||||
variables:
|
||||
topic: AI trends
|
||||
|
||||
roles:
|
||||
agents: # Canonical: use 'agents' instead of 'roles'
|
||||
classifier:
|
||||
role: Request Classifier
|
||||
backstory: "Classify requests into categories"
|
||||
instructions: "Classify requests into categories" # Canonical: use 'instructions' instead of 'backstory'
|
||||
goal: Classify requests
|
||||
|
||||
researcher:
|
||||
role: Research Analyst
|
||||
backstory: "Expert researcher"
|
||||
instructions: "Expert researcher" # Canonical: use 'instructions' instead of 'backstory'
|
||||
goal: Research topics
|
||||
tools:
|
||||
- tavily_search
|
||||
@@ -690,9 +912,13 @@ Run with the same simple command:
|
||||
praisonai agents.yaml
|
||||
```
|
||||
|
||||
## Using No Code
|
||||
---
|
||||
|
||||
### Auto Mode:
|
||||
## 🎯 CLI / No-Code Interface
|
||||
|
||||
PraisonAI provides a powerful CLI for no-code automation and quick prototyping.
|
||||
|
||||
### Auto Mode
|
||||
```bash
|
||||
pip install praisonai
|
||||
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
|
||||
@@ -839,10 +1065,43 @@ praisonai "GPT-5" --workflow "Research:Search for info,Write:Write blog" --tools
|
||||
praisonai workflow help
|
||||
```
|
||||
|
||||
#### YAML Workflow Files:
|
||||
```bash
|
||||
# Run a YAML workflow file
|
||||
praisonai workflow run research.yaml
|
||||
|
||||
# Run with variables
|
||||
praisonai workflow run research.yaml --var topic="AI trends"
|
||||
|
||||
# Validate a YAML workflow
|
||||
praisonai workflow validate research.yaml
|
||||
|
||||
# Create from template (simple, routing, parallel, loop, evaluator-optimizer)
|
||||
praisonai workflow template routing --output my_workflow.yaml
|
||||
```
|
||||
|
||||
#### Auto-Generate Workflows:
|
||||
```bash
|
||||
# Auto-generate a sequential workflow from topic
|
||||
praisonai workflow auto "Research AI trends"
|
||||
|
||||
# Generate parallel workflow (multiple agents work concurrently)
|
||||
praisonai workflow auto "Research AI trends" --pattern parallel
|
||||
|
||||
# Generate routing workflow (classifier routes to specialists)
|
||||
praisonai workflow auto "Build a chatbot" --pattern routing
|
||||
|
||||
# Specify output file
|
||||
praisonai workflow auto "Research AI" --pattern sequential --output my_workflow.yaml
|
||||
```
|
||||
|
||||
**Workflow CLI Options:**
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--workflow-var key=value` | Set workflow variable (can be repeated) |
|
||||
| `--var key=value` | Set variable for YAML workflows |
|
||||
| `--pattern <pattern>` | Pattern for auto-generation (sequential, parallel, routing, loop) |
|
||||
| `--output <file>` | Output file for auto-generation |
|
||||
| `--llm <model>` | LLM model (e.g., openai/gpt-4o-mini) |
|
||||
| `--tools <tools>` | Tools (comma-separated, e.g., tavily) |
|
||||
| `--planning` | Enable planning mode |
|
||||
@@ -1328,7 +1587,9 @@ server.run() # stdio for Claude Desktop
|
||||
| 📥 Web Fetch - URL content retrieval | [📖](https://docs.praison.ai/cli/web-fetch) |
|
||||
| 💾 Prompt Caching - Cost reduction | [📖](https://docs.praison.ai/cli/prompt-caching) |
|
||||
|
||||
## Using JavaScript Code
|
||||
---
|
||||
|
||||
## 💻 Using JavaScript Code
|
||||
|
||||
```bash
|
||||
npm install praisonai
|
||||
@@ -1343,11 +1604,17 @@ agent.start('Write a movie script about a robot in Mars');
|
||||
|
||||

|
||||
|
||||
## Star History
|
||||
---
|
||||
|
||||
## ⭐ Star History
|
||||
|
||||
[](https://docs.praison.ai)
|
||||
|
||||
## AI Agents Flow
|
||||
---
|
||||
|
||||
## 📊 Process Types & Patterns
|
||||
|
||||
### AI Agents Flow
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
@@ -1686,31 +1953,40 @@ flowchart LR
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
## Adding Models
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<a href="https://docs.praison.ai/models">
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/%F0%9F%93%9A_Models-Visit_docs.praison.ai-blue?style=for-the-badge&logo=bookstack&logoColor=white" alt="Models" />
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
## 🔧 Configuration & Integration
|
||||
|
||||
### Ollama Integration
|
||||
|
||||
## Ollama Integration
|
||||
```bash
|
||||
export OPENAI_BASE_URL=http://localhost:11434/v1
|
||||
```
|
||||
|
||||
## Groq Integration
|
||||
### Groq Integration
|
||||
|
||||
Replace xxxx with Groq API KEY:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY=xxxxxxxxxxx
|
||||
export OPENAI_BASE_URL=https://api.groq.com/openai/v1
|
||||
```
|
||||
|
||||
## No Code Options
|
||||
### 100+ Models Support
|
||||
|
||||
## Agents Playbook
|
||||
PraisonAI supports 100+ LLM models from various providers. Visit our [models documentation](https://docs.praison.ai/models/) for the complete list.
|
||||
|
||||
<div align="center">
|
||||
<a href="https://docs.praison.ai/models">
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/📚_Models_Documentation-Visit_docs.praison.ai-blue?style=for-the-badge&logo=bookstack&logoColor=white" alt="Models Documentation" />
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
## 📋 Agents Playbook
|
||||
|
||||
### Simple Playbook Example
|
||||
|
||||
@@ -1719,9 +1995,9 @@ Create `agents.yaml` file and add the code below:
|
||||
```yaml
|
||||
framework: praisonai
|
||||
topic: Artificial Intelligence
|
||||
roles:
|
||||
agents: # Canonical: use 'agents' instead of 'roles'
|
||||
screenwriter:
|
||||
backstory: "Skilled in crafting scripts with engaging dialogue about {topic}."
|
||||
instructions: "Skilled in crafting scripts with engaging dialogue about {topic}." # Canonical: use 'instructions' instead of 'backstory'
|
||||
goal: Create scripts from concepts.
|
||||
role: Screenwriter
|
||||
tasks:
|
||||
@@ -1735,18 +2011,11 @@ roles:
|
||||
praisonai agents.yaml
|
||||
```
|
||||
|
||||
## Use 100+ Models
|
||||
---
|
||||
|
||||
- https://docs.praison.ai/models/
|
||||
<div align="center">
|
||||
<a href="https://docs.praison.ai">
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/📚_Documentation-Visit_docs.praison.ai-blue?style=for-the-badge&logo=bookstack&logoColor=white" alt="Documentation" />
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
## 🛠️ Custom Tools / Create Plugins
|
||||
|
||||
## Custom Tools
|
||||
PraisonAI supports multiple ways to create and integrate custom tools (plugins) into your agents.
|
||||
|
||||
### Using `@tool` Decorator
|
||||
|
||||
@@ -1819,276 +2088,96 @@ After `pip install`, tools are auto-discovered:
|
||||
agent = Agent(tools=["my_tool"]) # Works automatically!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Prompt Expansion
|
||||
## 🧠 Memory & Context
|
||||
|
||||
Expand short prompts into detailed, actionable prompts:
|
||||
PraisonAI provides zero-dependency persistent memory for agents. For detailed examples, see [section 6. Agent Memory](#6-agent-memory-zero-dependencies) in the Python Code Examples.
|
||||
|
||||
### CLI Usage
|
||||
---
|
||||
|
||||
```bash
|
||||
# Expand a short prompt into detailed prompt
|
||||
praisonai "write a movie script in 3 lines" --expand-prompt
|
||||
## 🔬 Advanced Features
|
||||
|
||||
# With verbose output
|
||||
praisonai "blog about AI" --expand-prompt -v
|
||||
### Research & Intelligence
|
||||
|
||||
# With tools for context gathering
|
||||
praisonai "latest AI trends" --expand-prompt --expand-tools tools.py
|
||||
- 🔬 **Deep Research Agents** - OpenAI & Gemini support for automated research
|
||||
- 🔄 **Query Rewriter Agent** - HyDE, Step-back, Multi-query strategies for RAG optimization
|
||||
- 🌐 **Native Web Search** - Real-time search via OpenAI, Gemini, Anthropic, xAI, Perplexity
|
||||
- 📥 **Web Fetch** - Retrieve full content from URLs (Anthropic)
|
||||
- 📝 **Prompt Expander Agent** - Expand short prompts into detailed instructions
|
||||
|
||||
# Combine with query rewrite
|
||||
praisonai "AI news" --query-rewrite --expand-prompt
|
||||
```
|
||||
### Memory & Caching
|
||||
|
||||
### Programmatic Usage
|
||||
- 💾 **Prompt Caching** - Reduce costs & latency (OpenAI, Anthropic, Bedrock, Deepseek)
|
||||
- 🧠 **Claude Memory Tool** - Persistent cross-conversation memory (Anthropic Beta)
|
||||
- 💾 **File-Based Memory** - Zero-dependency persistent memory for all agents
|
||||
- 🔍 **Built-in Search Tools** - Tavily, You.com, Exa for web search, news, content extraction
|
||||
|
||||
```python
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy
|
||||
### Planning & Workflows
|
||||
|
||||
# Basic usage
|
||||
agent = PromptExpanderAgent()
|
||||
result = agent.expand("write a movie script in 3 lines")
|
||||
print(result.expanded_prompt)
|
||||
- 📋 **Planning Mode** - Plan before execution for agents & multi-agent systems
|
||||
- 🔧 **Planning Tools** - Research with tools during planning phase
|
||||
- 🧠 **Planning Reasoning** - Chain-of-thought planning for complex tasks
|
||||
- ⛓️ **Prompt Chaining** - Sequential prompt workflows with conditional gates
|
||||
- 🔍 **Evaluator Optimiser** - Generate and optimize through iterative feedback
|
||||
- 👷 **Orchestrator Workers** - Distribute tasks among specialised workers
|
||||
- ⚡ **Parallelisation** - Execute tasks in parallel for improved performance
|
||||
- 🔁 **Repetitive Agents** - Handle repetitive tasks through automated loops
|
||||
- 🤖 **Autonomous Workflow** - Monitor, act, adapt based on environment feedback
|
||||
|
||||
# With specific strategy
|
||||
result = agent.expand("blog about AI", strategy=ExpandStrategy.DETAILED)
|
||||
### Specialised Agents
|
||||
|
||||
# Available strategies: BASIC, DETAILED, STRUCTURED, CREATIVE, AUTO
|
||||
```
|
||||
- 🖼️ **Image Generation Agent** - Create images from text descriptions
|
||||
- 📷 **Image to Text Agent** - Extract text and descriptions from images
|
||||
- 🎬 **Video Agent** - Analyse and process video content
|
||||
- 📊 **Data Analyst Agent** - Analyse data and generate insights
|
||||
- 💰 **Finance Agent** - Financial analysis and recommendations
|
||||
- 🛒 **Shopping Agent** - Price comparison and shopping assistance
|
||||
- ⭐ **Recommendation Agent** - Personalised recommendations
|
||||
- 📖 **Wikipedia Agent** - Search and extract Wikipedia information
|
||||
- 💻 **Programming Agent** - Code development and analysis
|
||||
- 📝 **Markdown Agent** - Generate and format Markdown content
|
||||
- 🔀 **Router Agent** - Dynamic task routing with cost optimisation
|
||||
|
||||
**Key Difference:**
|
||||
- `--query-rewrite`: Optimizes queries for **search/retrieval** (RAG)
|
||||
- `--expand-prompt`: Expands prompts for **detailed task execution**
|
||||
### MCP Protocol
|
||||
|
||||
- 🔌 **MCP Transports** - stdio, Streamable HTTP, WebSocket, SSE (Protocol 2025-11-25)
|
||||
- 🌐 **WebSocket MCP** - Real-time bidirectional connections with auto-reconnect
|
||||
- 🔐 **MCP Security** - Origin validation, DNS rebinding prevention, secure sessions
|
||||
- 🔄 **MCP Resumability** - SSE stream recovery via Last-Event-ID
|
||||
|
||||
## Web Search, Web Fetch & Prompt Caching
|
||||
### Safety & Control
|
||||
|
||||
### CLI Usage
|
||||
- 🤝 **Agent Handoffs** - Transfer context between specialised agents
|
||||
- 🛡️ **Guardrails** - Input/output validation and safety checks
|
||||
- ✅ **Human Approval** - Require human confirmation for critical actions
|
||||
- 💬 **Sessions Management** - Isolated conversation contexts
|
||||
- 🔄 **Stateful Agents** - Maintain state across interactions
|
||||
|
||||
```bash
|
||||
# Web Search - Get real-time information
|
||||
praisonai "What are the latest AI news today?" --web-search --llm openai/gpt-4o-search-preview
|
||||
### Developer Tools
|
||||
|
||||
# Web Fetch - Retrieve and analyze URL content (Anthropic only)
|
||||
praisonai "Summarize https://docs.praison.ai" --web-fetch --llm anthropic/claude-sonnet-4-20250514
|
||||
- ⚡ **Fast Context** - Rapid parallel code search (10-20x faster)
|
||||
- 📜 **Rules & Instructions** - Auto-discover CLAUDE.md, AGENTS.md, GEMINI.md
|
||||
- 🪝 **Hooks** - Pre/post operation hooks for custom logic
|
||||
- 📈 **Telemetry** - Track agent performance and usage
|
||||
- 📹 **Camera Integration** - Capture and analyse camera input
|
||||
|
||||
# Prompt Caching - Reduce costs for repeated prompts
|
||||
praisonai "Analyze this document..." --prompt-caching --llm anthropic/claude-sonnet-4-20250514
|
||||
```
|
||||
### Other Features
|
||||
|
||||
### Programmatic Usage
|
||||
- 🔄 **CrewAI & AG2 Integration** - Use CrewAI or AG2 (Formerly AutoGen) Framework
|
||||
- 💻 **Codebase Chat** - Chat with entire codebase
|
||||
- 🎨 **Interactive UIs** - Multiple interactive interfaces
|
||||
- 📄 **YAML Configuration** - YAML-based agent and workflow configuration
|
||||
- 🛠️ **Custom Tools** - Easy custom tool integration
|
||||
- 🔍 **Internet Search** - Multiple providers (Tavily, You.com, Exa, DuckDuckGo, Crawl4AI)
|
||||
- 🖼️ **VLM Support** - Vision Language Model support
|
||||
- 🎙️ **Voice Interaction** - Real-time voice interaction
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent
|
||||
---
|
||||
|
||||
# Web Search
|
||||
agent = Agent(
|
||||
instructions="You are a research assistant",
|
||||
llm="openai/gpt-4o-search-preview",
|
||||
web_search=True
|
||||
)
|
||||
## 🎓 Video Tutorials
|
||||
|
||||
# Web Fetch (Anthropic only)
|
||||
agent = Agent(
|
||||
instructions="You are a content analyzer",
|
||||
llm="anthropic/claude-sonnet-4-20250514",
|
||||
web_fetch=True
|
||||
)
|
||||
|
||||
# Prompt Caching
|
||||
agent = Agent(
|
||||
instructions="You are an AI assistant..." * 50, # Long system prompt
|
||||
llm="anthropic/claude-sonnet-4-20250514",
|
||||
prompt_caching=True
|
||||
)
|
||||
```
|
||||
|
||||
**Supported Providers:**
|
||||
| Feature | Providers |
|
||||
|---------|-----------|
|
||||
| Web Search | OpenAI, Gemini, Anthropic, xAI, Perplexity |
|
||||
| Web Fetch | Anthropic |
|
||||
| Prompt Caching | OpenAI (auto), Anthropic, Bedrock, Deepseek |
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## MCP (Model Context Protocol)
|
||||
|
||||
PraisonAI supports MCP Protocol Revision 2025-11-25 with multiple transports.
|
||||
|
||||
### MCP Client (Consume MCP Servers)
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent, MCP
|
||||
|
||||
# stdio - Local NPX/Python servers
|
||||
agent = Agent(tools=MCP("npx @modelcontextprotocol/server-memory"))
|
||||
|
||||
# Streamable HTTP - Production servers
|
||||
agent = Agent(tools=MCP("https://api.example.com/mcp"))
|
||||
|
||||
# WebSocket - Real-time bidirectional
|
||||
agent = Agent(tools=MCP("wss://api.example.com/mcp", auth_token="token"))
|
||||
|
||||
# SSE (Legacy) - Backward compatibility
|
||||
agent = Agent(tools=MCP("http://localhost:8080/sse"))
|
||||
|
||||
# With environment variables
|
||||
agent = Agent(
|
||||
tools=MCP(
|
||||
command="npx",
|
||||
args=["-y", "@modelcontextprotocol/server-brave-search"],
|
||||
env={"BRAVE_API_KEY": "your-key"}
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
### MCP Server (Expose Tools as MCP Server)
|
||||
|
||||
Expose your Python functions as MCP tools for Claude Desktop, Cursor, and other MCP clients:
|
||||
|
||||
```python
|
||||
from praisonaiagents.mcp import ToolsMCPServer
|
||||
|
||||
def search_web(query: str, max_results: int = 5) -> dict:
|
||||
"""Search the web for information."""
|
||||
return {"results": [f"Result for {query}"]}
|
||||
|
||||
def calculate(expression: str) -> dict:
|
||||
"""Evaluate a mathematical expression."""
|
||||
return {"result": eval(expression)}
|
||||
|
||||
# Create and run MCP server
|
||||
server = ToolsMCPServer(name="my-tools")
|
||||
server.register_tools([search_web, calculate])
|
||||
server.run() # stdio for Claude Desktop
|
||||
# server.run_sse(host="0.0.0.0", port=8080) # SSE for web clients
|
||||
```
|
||||
|
||||
### MCP Features
|
||||
|
||||
| Feature | Description |
|
||||
|---------|-------------|
|
||||
| **Session Management** | Automatic `Mcp-Session-Id` handling |
|
||||
| **Protocol Versioning** | `Mcp-Protocol-Version` header |
|
||||
| **Resumability** | SSE stream recovery via `Last-Event-ID` |
|
||||
| **Security** | Origin validation, DNS rebinding prevention |
|
||||
| **WebSocket** | Auto-reconnect with exponential backoff |
|
||||
|
||||
|
||||
## Development:
|
||||
|
||||
Below is used for development only.
|
||||
|
||||
### Using uv
|
||||
```bash
|
||||
# Install uv if you haven't already
|
||||
pip install uv
|
||||
|
||||
# Install from requirements
|
||||
uv pip install -r pyproject.toml
|
||||
|
||||
# Install with extras
|
||||
uv pip install -r pyproject.toml --extra code
|
||||
uv pip install -r pyproject.toml --extra "crewai,autogen"
|
||||
```
|
||||
|
||||
### Bump and Release
|
||||
|
||||
```bash
|
||||
# From project root - bumps version and releases in one command
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99
|
||||
|
||||
# With praisonaiagents dependency
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99 --agents 0.0.169
|
||||
|
||||
# Then publish
|
||||
cd src/praisonai && uv publish
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
- Fork on GitHub: Use the "Fork" button on the repository page.
|
||||
- Clone your fork: `git clone https://github.com/yourusername/praisonAI.git`
|
||||
- Create a branch: `git checkout -b new-feature`
|
||||
- Make changes and commit: `git commit -am "Add some feature"`
|
||||
- Push to your fork: `git push origin new-feature`
|
||||
- Submit a pull request via GitHub's web interface.
|
||||
- Await feedback from project maintainers.
|
||||
|
||||
## Advanced Features
|
||||
|
||||
**Research & Intelligence:**
|
||||
- 🔬 **Deep Research Agents** (OpenAI & Gemini)
|
||||
- 🔄 **Query Rewriter Agent** (HyDE, Step-back, Multi-query)
|
||||
- 🌐 **Native Web Search** (OpenAI, Gemini, Anthropic, xAI, Perplexity)
|
||||
- 📥 **Web Fetch** (Retrieve full content from URLs - Anthropic)
|
||||
- 📝 **Prompt Expander Agent** (Expand short prompts into detailed instructions)
|
||||
|
||||
**Memory & Caching:**
|
||||
- 💾 **Prompt Caching** (Reduce costs & latency - OpenAI, Anthropic, Bedrock, Deepseek)
|
||||
- 🧠 **Claude Memory Tool** (Persistent cross-conversation memory - Anthropic Beta)
|
||||
- 💾 **File-Based Memory** (Zero-dependency persistent memory for all agents)
|
||||
- 🔍 **Built-in Search Tools** (Tavily, You.com, Exa - web search, news, content extraction)
|
||||
|
||||
**Planning & Workflows:**
|
||||
- 📋 **Planning Mode** (Plan before execution - Agent & Multi-Agent)
|
||||
- 🔧 **Planning Tools** (Research with tools during planning)
|
||||
- 🧠 **Planning Reasoning** (Chain-of-thought planning)
|
||||
- ⛓️ **Prompt Chaining** (Sequential prompt workflows with gates)
|
||||
- 🔍 **Evaluator Optimiser** (Generate and optimize through iterative feedback)
|
||||
- 👷 **Orchestrator Workers** (Distribute tasks among specialized workers)
|
||||
- ⚡ **Parallelisation** (Execute tasks in parallel for improved performance)
|
||||
- 🔁 **Repetitive Agents** (Handle repetitive tasks through automated loops)
|
||||
- 🤖 **Autonomous Workflow** (Monitor, act, adapt based on environment feedback)
|
||||
|
||||
**Agent Types:**
|
||||
- 🖼️ **Image Generation Agent** (Create images from text descriptions)
|
||||
- 📷 **Image to Text Agent** (Extract text and descriptions from images)
|
||||
- 🎬 **Video Agent** (Analyze and process video content)
|
||||
- 📊 **Data Analyst Agent** (Analyze data and generate insights)
|
||||
- 💰 **Finance Agent** (Financial analysis and recommendations)
|
||||
- 🛒 **Shopping Agent** (Price comparison and shopping assistance)
|
||||
- ⭐ **Recommendation Agent** (Personalized recommendations)
|
||||
- 📖 **Wikipedia Agent** (Search and extract Wikipedia information)
|
||||
- 💻 **Programming Agent** (Code development and analysis)
|
||||
- 📝 **Markdown Agent** (Generate and format Markdown content)
|
||||
- 🔀 **Router Agent** (Dynamic task routing with cost optimization)
|
||||
|
||||
**MCP Protocol:**
|
||||
- 🔌 **MCP Transports** (stdio, Streamable HTTP, WebSocket, SSE - Protocol 2025-11-25)
|
||||
- 🌐 **WebSocket MCP** (Real-time bidirectional connections with auto-reconnect)
|
||||
- 🔐 **MCP Security** (Origin validation, DNS rebinding prevention, secure sessions)
|
||||
- 🔄 **MCP Resumability** (SSE stream recovery via Last-Event-ID)
|
||||
|
||||
**Safety & Control:**
|
||||
- 🤝 **Agent Handoffs** (Transfer context between specialized agents)
|
||||
- 🛡️ **Guardrails** (Input/output validation and safety checks)
|
||||
- ✅ **Human Approval** (Require human confirmation for critical actions)
|
||||
- 💬 **Sessions Management** (Isolated conversation contexts)
|
||||
- 🔄 **Stateful Agents** (Maintain state across interactions)
|
||||
|
||||
**Developer Tools:**
|
||||
- ⚡ **Fast Context** (Rapid parallel code search - 10-20x faster than traditional methods)
|
||||
- 📜 **Rules & Instructions** (Auto-discover CLAUDE.md, AGENTS.md, GEMINI.md)
|
||||
- 🪝 **Hooks** (Pre/post operation hooks for custom logic)
|
||||
- 📈 **Telemetry** (Track agent performance and usage)
|
||||
- 📹 **Camera Integration** (Capture and analyze camera input)
|
||||
|
||||
## Other Features
|
||||
|
||||
- 🔄 Use CrewAI or AG2 (Formerly AutoGen) Framework
|
||||
- 💻 Chat with ENTIRE Codebase
|
||||
- 🎨 Interactive UIs
|
||||
- 📄 YAML-based Configuration
|
||||
- 🛠️ Custom Tool Integration
|
||||
- 🔍 Internet Search Capability (Tavily, You.com, Exa, DuckDuckGo, Crawl4AI)
|
||||
- 🖼️ Vision Language Model (VLM) Support
|
||||
- 🎙️ Real-time Voice Interaction
|
||||
|
||||
## Video Tutorials
|
||||
Learn PraisonAI through our comprehensive video series:
|
||||
|
||||
| Topic | Video |
|
||||
|-------|--------|
|
||||
@@ -2115,3 +2204,58 @@ cd src/praisonai && uv publish
|
||||
| Call Interface | [](https://www.youtube.com/watch?v=m1cwrUG2iAk) |
|
||||
| Reasoning Extract Agents | [](https://www.youtube.com/watch?v=2PPamsADjJA) |
|
||||
|
||||
---
|
||||
|
||||
## 👥 Contributing
|
||||
|
||||
We welcome contributions from the community! Here's how you can contribute:
|
||||
|
||||
1. **Fork on GitHub** - Use the "Fork" button on the [repository page](https://github.com/MervinPraison/PraisonAI)
|
||||
2. **Clone your fork** - `git clone https://github.com/yourusername/praisonAI.git`
|
||||
3. **Create a branch** - `git checkout -b new-feature`
|
||||
4. **Make changes and commit** - `git commit -am "Add some feature"`
|
||||
5. **Push to your fork** - `git push origin new-feature`
|
||||
6. **Submit a pull request** - Via GitHub's web interface
|
||||
7. **Await feedback** - From project maintainers
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Development
|
||||
|
||||
### Using uv
|
||||
|
||||
```bash
|
||||
# Install uv if you haven't already
|
||||
pip install uv
|
||||
|
||||
# Install from requirements
|
||||
uv pip install -r pyproject.toml
|
||||
|
||||
# Install with extras
|
||||
uv pip install -r pyproject.toml --extra code
|
||||
uv pip install -r pyproject.toml --extra "crewai,autogen"
|
||||
```
|
||||
|
||||
### Bump and Release
|
||||
|
||||
```bash
|
||||
# From project root - bumps version and releases in one command
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99
|
||||
|
||||
# With praisonaiagents dependency
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99 --agents 0.0.169
|
||||
|
||||
# Then publish
|
||||
cd src/praisonai && uv publish
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<p><strong>Made with ❤️ by the PraisonAI Team</strong></p>
|
||||
<p>
|
||||
<a href="https://docs.praison.ai">Documentation</a> •
|
||||
<a href="https://github.com/MervinPraison/PraisonAI">GitHub</a> •
|
||||
<a href="https://github.com/MervinPraison/PraisonAI/issues">Issues</a>
|
||||
</p>
|
||||
</div>
|
||||
@@ -3,8 +3,8 @@ class Praisonai < Formula
|
||||
|
||||
desc "AI tools for various AI applications"
|
||||
homepage "https://github.com/MervinPraison/PraisonAI"
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.26.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.26.tar.gz | shasum -a 256`.split.first
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.28.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.28.tar.gz | shasum -a 256`.split.first
|
||||
license "MIT"
|
||||
|
||||
depends_on "[email protected]"
|
||||
|
||||
@@ -367,6 +367,14 @@ Example:
|
||||
self.print_status(f"Validating: {file_path}", "info")
|
||||
|
||||
try:
|
||||
# Load raw YAML to check for non-canonical names
|
||||
import yaml
|
||||
with open(file_path, 'r') as f:
|
||||
raw_data = yaml.safe_load(f)
|
||||
|
||||
# Check for non-canonical names and suggest canonical ones
|
||||
suggestions = self._get_canonical_suggestions(raw_data)
|
||||
|
||||
workflow = parser.parse_file(file_path)
|
||||
|
||||
# Print validation results
|
||||
@@ -386,12 +394,82 @@ Example:
|
||||
|
||||
rprint(table)
|
||||
self.print_status("✓ Workflow is valid!", "success")
|
||||
|
||||
# Show suggestions for canonical names
|
||||
if suggestions:
|
||||
rprint()
|
||||
rprint("[yellow]💡 Suggestions for canonical field names:[/yellow]")
|
||||
for suggestion in suggestions:
|
||||
rprint(f" [dim]•[/dim] {suggestion}")
|
||||
rprint()
|
||||
rprint("[dim]Note: Both old and new names work, but canonical names are recommended.[/dim]")
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
self.print_status(f"✗ Validation failed: {e}", "error")
|
||||
return False
|
||||
|
||||
def _get_canonical_suggestions(self, data: Dict) -> List[str]:
|
||||
"""
|
||||
Check for non-canonical field names and return suggestions.
|
||||
|
||||
Canonical names (A-I-G-S mnemonic):
|
||||
- Agents (not roles)
|
||||
- Instructions (not backstory)
|
||||
- Goal (same)
|
||||
- Steps (not tasks)
|
||||
|
||||
Also:
|
||||
- name (not topic)
|
||||
- action (not description)
|
||||
|
||||
Args:
|
||||
data: Raw YAML data
|
||||
|
||||
Returns:
|
||||
List of suggestion strings
|
||||
"""
|
||||
suggestions = []
|
||||
|
||||
if not data:
|
||||
return suggestions
|
||||
|
||||
# Check top-level keys
|
||||
if 'roles' in data:
|
||||
suggestions.append("Use 'agents' instead of 'roles'")
|
||||
|
||||
if 'topic' in data and 'name' not in data:
|
||||
suggestions.append("Use 'name' instead of 'topic'")
|
||||
|
||||
# Check agent fields
|
||||
agents_data = data.get('agents', data.get('roles', {}))
|
||||
for agent_id, agent_config in agents_data.items():
|
||||
if isinstance(agent_config, dict):
|
||||
if 'backstory' in agent_config:
|
||||
suggestions.append(f"Agent '{agent_id}': Use 'instructions' instead of 'backstory'")
|
||||
|
||||
# Check nested tasks
|
||||
if 'tasks' in agent_config:
|
||||
suggestions.append(f"Agent '{agent_id}': Use 'steps' at top level instead of nested 'tasks'")
|
||||
|
||||
# Check step fields
|
||||
steps_data = data.get('steps', [])
|
||||
for i, step in enumerate(steps_data):
|
||||
if isinstance(step, dict):
|
||||
if 'description' in step and 'action' not in step:
|
||||
step_name = step.get('name', f'step {i+1}')
|
||||
suggestions.append(f"Step '{step_name}': Use 'action' instead of 'description'")
|
||||
|
||||
# Check parallel steps
|
||||
if 'parallel' in step:
|
||||
for j, parallel_step in enumerate(step['parallel']):
|
||||
if isinstance(parallel_step, dict):
|
||||
if 'description' in parallel_step and 'action' not in parallel_step:
|
||||
suggestions.append(f"Parallel step {j+1}: Use 'action' instead of 'description'")
|
||||
|
||||
return suggestions
|
||||
|
||||
def action_list(self, args: List[str], **kwargs) -> List[str]:
|
||||
"""
|
||||
List available workflows in .praison/workflows/ directory.
|
||||
@@ -567,8 +645,8 @@ Example:
|
||||
|
||||
return result_path
|
||||
|
||||
except ImportError as e:
|
||||
self.print_status(f"Auto-generation requires instructor: pip install instructor", "error")
|
||||
except ImportError:
|
||||
self.print_status("Auto-generation requires instructor: pip install instructor", "error")
|
||||
return None
|
||||
except Exception as e:
|
||||
self.print_status(f"Generation failed: {e}", "error")
|
||||
|
||||
@@ -1632,6 +1632,10 @@ class PraisonAI:
|
||||
if arg == '--output' and i + 1 < len(action_args):
|
||||
output_file = action_args[i + 1]
|
||||
self._create_workflow_from_template(template_name, output_file)
|
||||
|
||||
elif action == 'auto':
|
||||
# Auto-generate workflow from topic
|
||||
self._auto_generate_workflow(action_args)
|
||||
|
||||
elif action == 'help' or action == '--help':
|
||||
print("[bold]Workflow Commands:[/bold]")
|
||||
@@ -1642,6 +1646,7 @@ class PraisonAI:
|
||||
print(" praisonai workflow create <name> - Create a new workflow")
|
||||
print(" praisonai workflow validate <file.yaml> - Validate a YAML workflow")
|
||||
print(" praisonai workflow template <name> - Create from template")
|
||||
print(' praisonai workflow auto "topic" - Auto-generate workflow')
|
||||
print("\n[bold]Templates:[/bold]")
|
||||
print(" simple, routing, parallel, loop, evaluator-optimizer")
|
||||
print("\n[bold]Options (uses global flags):[/bold]")
|
||||
@@ -1763,6 +1768,7 @@ class PraisonAI:
|
||||
from rich import print
|
||||
from rich.table import Table
|
||||
from rich.console import Console
|
||||
import yaml
|
||||
|
||||
console = Console()
|
||||
|
||||
@@ -1772,6 +1778,13 @@ class PraisonAI:
|
||||
|
||||
print(f"[cyan]Validating: {yaml_file}[/cyan]")
|
||||
|
||||
# Load raw YAML to check for non-canonical names
|
||||
with open(yaml_file, 'r') as f:
|
||||
raw_data = yaml.safe_load(f)
|
||||
|
||||
# Check for non-canonical names and suggest canonical ones
|
||||
suggestions = self._get_canonical_suggestions(raw_data)
|
||||
|
||||
parser = YAMLWorkflowParser()
|
||||
workflow = parser.parse_file(yaml_file)
|
||||
|
||||
@@ -1790,8 +1803,77 @@ class PraisonAI:
|
||||
console.print(table)
|
||||
print("[green]✓ Workflow is valid![/green]")
|
||||
|
||||
# Show suggestions for canonical names
|
||||
if suggestions:
|
||||
print()
|
||||
print("[yellow]💡 Suggestions for canonical field names:[/yellow]")
|
||||
for suggestion in suggestions:
|
||||
print(f" [dim]•[/dim] {suggestion}")
|
||||
print()
|
||||
print("[dim]Note: Both old and new names work, but canonical names are recommended.[/dim]")
|
||||
|
||||
except Exception as e:
|
||||
print(f"[red]✗ Validation failed: {e}[/red]")
|
||||
|
||||
def _get_canonical_suggestions(self, data: dict) -> list:
|
||||
"""
|
||||
Check for non-canonical field names and return suggestions.
|
||||
|
||||
Canonical names (A-I-G-S mnemonic):
|
||||
- Agents (not roles)
|
||||
- Instructions (not backstory)
|
||||
- Goal (same)
|
||||
- Steps (not tasks)
|
||||
|
||||
Also:
|
||||
- name (not topic)
|
||||
- action (not description)
|
||||
|
||||
Args:
|
||||
data: Raw YAML data
|
||||
|
||||
Returns:
|
||||
List of suggestion strings
|
||||
"""
|
||||
suggestions = []
|
||||
|
||||
if not data:
|
||||
return suggestions
|
||||
|
||||
# Check top-level keys
|
||||
if 'roles' in data:
|
||||
suggestions.append("Use 'agents' instead of 'roles'")
|
||||
|
||||
if 'topic' in data and 'name' not in data:
|
||||
suggestions.append("Use 'name' instead of 'topic'")
|
||||
|
||||
# Check agent fields
|
||||
agents_data = data.get('agents', data.get('roles', {}))
|
||||
for agent_id, agent_config in agents_data.items():
|
||||
if isinstance(agent_config, dict):
|
||||
if 'backstory' in agent_config:
|
||||
suggestions.append(f"Agent '{agent_id}': Use 'instructions' instead of 'backstory'")
|
||||
|
||||
# Check nested tasks
|
||||
if 'tasks' in agent_config:
|
||||
suggestions.append(f"Agent '{agent_id}': Use 'steps' at top level instead of nested 'tasks'")
|
||||
|
||||
# Check step fields
|
||||
steps_data = data.get('steps', [])
|
||||
for i, step in enumerate(steps_data):
|
||||
if isinstance(step, dict):
|
||||
if 'description' in step and 'action' not in step:
|
||||
step_name = step.get('name', f'step {i+1}')
|
||||
suggestions.append(f"Step '{step_name}': Use 'action' instead of 'description'")
|
||||
|
||||
# Check parallel steps
|
||||
if 'parallel' in step:
|
||||
for j, parallel_step in enumerate(step['parallel']):
|
||||
if isinstance(parallel_step, dict):
|
||||
if 'description' in parallel_step and 'action' not in parallel_step:
|
||||
suggestions.append(f"Parallel step {j+1}: Use 'action' instead of 'description'")
|
||||
|
||||
return suggestions
|
||||
|
||||
def _create_workflow_from_template(self, template_name: str = None, output_file: str = None):
|
||||
"""
|
||||
@@ -1839,6 +1921,76 @@ class PraisonAI:
|
||||
print(f"[green]✓ Created workflow: {output_file}[/green]")
|
||||
print(f"[cyan]Run with: praisonai workflow run {output_file}[/cyan]")
|
||||
|
||||
def _auto_generate_workflow(self, action_args: list):
|
||||
"""
|
||||
Auto-generate a workflow from a topic description.
|
||||
|
||||
Args:
|
||||
action_args: ["topic description", --pattern, <pattern>, --output, <file.yaml>]
|
||||
"""
|
||||
from rich import print
|
||||
|
||||
# Parse arguments
|
||||
topic = None
|
||||
pattern = "sequential"
|
||||
output_file = None
|
||||
|
||||
i = 0
|
||||
while i < len(action_args):
|
||||
if action_args[i] == "--pattern" and i + 1 < len(action_args):
|
||||
pattern = action_args[i + 1]
|
||||
i += 2
|
||||
elif action_args[i] == "--output" and i + 1 < len(action_args):
|
||||
output_file = action_args[i + 1]
|
||||
i += 2
|
||||
elif not action_args[i].startswith("--") and topic is None:
|
||||
topic = action_args[i]
|
||||
i += 1
|
||||
else:
|
||||
i += 1
|
||||
|
||||
if not topic:
|
||||
print('[red]Usage: praisonai workflow auto "topic" --pattern <pattern>[/red]')
|
||||
print("[cyan]Patterns: sequential, routing, parallel[/cyan]")
|
||||
return
|
||||
|
||||
# Validate pattern
|
||||
valid_patterns = ["sequential", "routing", "parallel", "loop"]
|
||||
if pattern not in valid_patterns:
|
||||
print(f"[red]Unknown pattern: {pattern}[/red]")
|
||||
print(f"[cyan]Valid patterns: {', '.join(valid_patterns)}[/cyan]")
|
||||
return
|
||||
|
||||
# Default output file
|
||||
if not output_file:
|
||||
safe_name = "".join(c if c.isalnum() else "_" for c in topic[:30]).lower()
|
||||
output_file = f"{safe_name}_workflow.yaml"
|
||||
|
||||
# Check if file exists
|
||||
if os.path.exists(output_file):
|
||||
print(f"[red]ERROR: File already exists: {output_file}[/red]")
|
||||
return
|
||||
|
||||
print(f"[cyan]Generating {pattern} workflow for: {topic}[/cyan]")
|
||||
|
||||
try:
|
||||
from praisonai.auto import WorkflowAutoGenerator
|
||||
|
||||
generator = WorkflowAutoGenerator(
|
||||
topic=topic,
|
||||
workflow_file=output_file
|
||||
)
|
||||
|
||||
result_path = generator.generate(pattern=pattern)
|
||||
|
||||
print(f"[green]✓ Created workflow: {result_path}[/green]")
|
||||
print(f"[cyan]Run with: praisonai workflow run {output_file}[/cyan]")
|
||||
|
||||
except ImportError:
|
||||
print("[red]Auto-generation requires instructor: pip install instructor[/red]")
|
||||
except Exception as e:
|
||||
print(f"[red]Generation failed: {e}[/red]")
|
||||
|
||||
def handle_hooks_command(self, action: str):
|
||||
"""
|
||||
Handle hooks subcommand actions.
|
||||
|
||||
@@ -57,7 +57,7 @@ class CloudDeployer:
|
||||
file.write("FROM python:3.11-slim\n")
|
||||
file.write("WORKDIR /app\n")
|
||||
file.write("COPY . .\n")
|
||||
file.write("RUN pip install flask praisonai==2.3.26 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.3.28 gunicorn markdown\n")
|
||||
file.write("EXPOSE 8080\n")
|
||||
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
__version__ = "2.3.26"
|
||||
__version__ = "2.3.28"
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"rich>=13.7",
|
||||
"markdown>=3.5",
|
||||
"pyparsing>=3.0.0",
|
||||
"praisonaiagents>=0.1.4",
|
||||
"praisonaiagents>=0.1.7",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
|
||||
Generated
+4
-4
@@ -4197,7 +4197,7 @@ requires-dist = [
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.22" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'autogen-v4'", specifier = ">=0.0.22" },
|
||||
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.22" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.1.4" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.1.7" },
|
||||
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = "==0.2.29" },
|
||||
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
|
||||
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
|
||||
@@ -4252,7 +4252,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.1.4"
|
||||
version = "0.1.7"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
@@ -4261,9 +4261,9 @@ dependencies = [
|
||||
{ name = "pydantic" },
|
||||
{ name = "rich" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/ab/9d/d286e0ba5421cb8a37ea86f4714a7fbcb39fe1f38e9a202cf0cf6381ae8c/praisonaiagents-0.1.4.tar.gz", hash = "sha256:d00a326ac65701bcc66c44aba7d1616ef1317a2491034a242ad4290f0269e190", size = 560571, upload-time = "2025-12-17T11:43:41.829Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/07/19/d59400d701c3433c1b0c08024f34d9e17d63bb6257c0f56e39c9715f66a8/praisonaiagents-0.1.7.tar.gz", hash = "sha256:42ac442ff80bdbcca0f25987d8ac722e4ce9480a650acecde1c6fc7fa124acdf", size = 615505, upload-time = "2025-12-17T13:15:11.644Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/f3/90/b6429a58f75aeec4da0034d7594863b3fd6e4bf916dbcbeb53a8597f0f4a/praisonaiagents-0.1.4-py3-none-any.whl", hash = "sha256:c7ad9b64e3d3a24b9560d695bc7447f286928e52f0979e84d02484662cb8a49d", size = 524190, upload-time = "2025-12-17T11:43:40.119Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/cb/cdd00ec34abb60925133ed8643432f93f45f4c679ccd3f599d01a26fc354/praisonaiagents-0.1.7-py3-none-any.whl", hash = "sha256:b07ecc2b088f9bcbd4dab352685f35142b422d002581aaa8b992a8f7033d6d13", size = 545981, upload-time = "2025-12-17T13:15:09.434Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in new issue
Block a user