mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-09 17:31:52 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3aa1b825e4 | ||
|
|
b4ee30d837 | ||
|
|
825648aead | ||
|
|
1532c31e72 | ||
|
|
b313fefd86 | ||
|
|
435e1afb55 | ||
|
|
4cb3c0565a | ||
|
|
a7634b66dc |
No files matched your search
@@ -27,6 +27,9 @@ jobs:
|
||||
comprehensive-test:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
env:
|
||||
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
|
||||
OPENAI_MODEL_NAME: gpt-4o-mini
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: ["3.11"]
|
||||
@@ -55,11 +58,13 @@ jobs:
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
uv pip install --system "praisonaiagents[knowledge]"
|
||||
|
||||
- name: Set environment variables
|
||||
- name: Verify environment variables
|
||||
run: |
|
||||
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY || 'sk-test-key-for-github-actions-testing-only-not-real' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_API_BASE=${{ secrets.OPENAI_API_BASE || 'https://api.openai.com/v1' }}" >> $GITHUB_ENV
|
||||
echo "OPENAI_MODEL_NAME=${{ secrets.OPENAI_MODEL_NAME || 'gpt-5-nano' }}" >> $GITHUB_ENV
|
||||
if [ -n "$OPENAI_API_KEY" ]; then
|
||||
echo "✅ OPENAI_API_KEY is set (length: ${#OPENAI_API_KEY})"
|
||||
else
|
||||
echo "⚠️ OPENAI_API_KEY is NOT set"
|
||||
fi
|
||||
|
||||
- name: Run Comprehensive Test Suite
|
||||
run: |
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
cd src/praisonai
|
||||
uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
uv pip install --system duckduckgo_search
|
||||
uv pip install --system pytest pytest-asyncio pytest-cov
|
||||
uv pip install --system pytest pytest-asyncio pytest-cov pytest-timeout
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
uv pip install --system "praisonaiagents[knowledge]"
|
||||
|
||||
@@ -262,7 +262,7 @@ jobs:
|
||||
export OPENAI_API_KEY="$OPENAI_API_KEY"
|
||||
export OPENAI_API_BASE="$OPENAI_API_BASE"
|
||||
export OPENAI_MODEL_NAME="$OPENAI_MODEL_NAME"
|
||||
cd src/praisonai && python -m pytest tests/test.py -v --tb=short --disable-warnings
|
||||
cd src/praisonai && python -m pytest tests/test.py -v --tb=short --disable-warnings -m "not real" --timeout=60
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
if: matrix.python-version == '3.11'
|
||||
|
||||
@@ -27,7 +27,7 @@ jobs:
|
||||
cd src/praisonai
|
||||
uv pip install --system ."[ui,gradio,api,agentops,google,openai,anthropic,cohere,chat,code,realtime,call,crewai,autogen]"
|
||||
uv pip install --system duckduckgo_search
|
||||
uv pip install --system pytest pytest-asyncio pytest-cov
|
||||
uv pip install --system pytest pytest-asyncio pytest-cov pytest-timeout
|
||||
# Install knowledge dependencies from praisonai-agents
|
||||
uv pip install --system "praisonaiagents[knowledge]"
|
||||
|
||||
@@ -489,7 +489,7 @@ jobs:
|
||||
|
||||
- name: Run Legacy Example Tests
|
||||
run: |
|
||||
cd src/praisonai && python -m pytest tests/test.py -v --tb=short --disable-warnings
|
||||
cd src/praisonai && python -m pytest tests/test.py -v --tb=short --disable-warnings -m "not real" --timeout=60
|
||||
continue-on-error: true
|
||||
|
||||
- name: Upload coverage reports to Codecov
|
||||
|
||||
@@ -718,6 +718,47 @@ agent = Agent(tools=["my_tool"]) # Works automatically!
|
||||
```
|
||||
|
||||
|
||||
## Prompt Expansion
|
||||
|
||||
Expand short prompts into detailed, actionable prompts:
|
||||
|
||||
### CLI Usage
|
||||
|
||||
```bash
|
||||
# Expand a short prompt into detailed prompt
|
||||
praisonai "write a movie script in 3 lines" --expand-prompt
|
||||
|
||||
# With verbose output
|
||||
praisonai "blog about AI" --expand-prompt -v
|
||||
|
||||
# With tools for context gathering
|
||||
praisonai "latest AI trends" --expand-prompt --expand-tools tools.py
|
||||
|
||||
# Combine with query rewrite
|
||||
praisonai "AI news" --query-rewrite --expand-prompt
|
||||
```
|
||||
|
||||
### Programmatic Usage
|
||||
|
||||
```python
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy
|
||||
|
||||
# Basic usage
|
||||
agent = PromptExpanderAgent()
|
||||
result = agent.expand("write a movie script in 3 lines")
|
||||
print(result.expanded_prompt)
|
||||
|
||||
# With specific strategy
|
||||
result = agent.expand("blog about AI", strategy=ExpandStrategy.DETAILED)
|
||||
|
||||
# Available strategies: BASIC, DETAILED, STRUCTURED, CREATIVE, AUTO
|
||||
```
|
||||
|
||||
**Key Difference:**
|
||||
- `--query-rewrite`: Optimizes queries for **search/retrieval** (RAG)
|
||||
- `--expand-prompt`: Expands prompts for **detailed task execution**
|
||||
|
||||
|
||||
## Development:
|
||||
|
||||
Below is used for development only.
|
||||
@@ -735,6 +776,19 @@ 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.
|
||||
|
||||
+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.2.98" \
|
||||
"praisonai>=2.3.0" \
|
||||
"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.2.98" \
|
||||
"praisonai>=2.3.0" \
|
||||
"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.2.98" \
|
||||
"praisonai>=2.3.0" \
|
||||
"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.2.98" \
|
||||
"praisonai>=2.3.0" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -31,6 +31,11 @@ from .agent.query_rewriter_agent import (
|
||||
RewriteStrategy,
|
||||
RewriteResult
|
||||
)
|
||||
from .agent.prompt_expander_agent import (
|
||||
PromptExpanderAgent,
|
||||
ExpandStrategy,
|
||||
ExpandResult
|
||||
)
|
||||
from .agents.agents import PraisonAIAgents
|
||||
from .task.task import Task
|
||||
from .tools.tools import Tools
|
||||
@@ -228,7 +233,10 @@ __all__ = [
|
||||
'Provider',
|
||||
'QueryRewriterAgent',
|
||||
'RewriteStrategy',
|
||||
'RewriteResult'
|
||||
'RewriteResult',
|
||||
'PromptExpanderAgent',
|
||||
'ExpandStrategy',
|
||||
'ExpandResult'
|
||||
]
|
||||
|
||||
# Add MCP to __all__ if available
|
||||
|
||||
@@ -20,6 +20,11 @@ from .query_rewriter_agent import (
|
||||
RewriteStrategy,
|
||||
RewriteResult
|
||||
)
|
||||
from .prompt_expander_agent import (
|
||||
PromptExpanderAgent,
|
||||
ExpandStrategy,
|
||||
ExpandResult
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
'Agent',
|
||||
@@ -43,5 +48,8 @@ __all__ = [
|
||||
'Provider',
|
||||
'QueryRewriterAgent',
|
||||
'RewriteStrategy',
|
||||
'RewriteResult'
|
||||
'RewriteResult',
|
||||
'PromptExpanderAgent',
|
||||
'ExpandStrategy',
|
||||
'ExpandResult'
|
||||
]
|
||||
@@ -0,0 +1,348 @@
|
||||
"""
|
||||
Prompt Expander Agent Module
|
||||
|
||||
This module provides the PromptExpanderAgent class for expanding short prompts
|
||||
into detailed, comprehensive prompts for better task execution.
|
||||
|
||||
Unlike QueryRewriterAgent (which optimizes queries for search/retrieval),
|
||||
PromptExpanderAgent focuses on enriching prompts for task execution.
|
||||
|
||||
Supported Expansion Strategies:
|
||||
- **BASIC**: Simple expansion with clarity improvements
|
||||
- **DETAILED**: Rich expansion with context, constraints, and examples
|
||||
- **STRUCTURED**: Expansion with clear structure (task, format, requirements)
|
||||
- **CREATIVE**: Expansion with creative flair and vivid language
|
||||
- **AUTO**: Automatically selects the best strategy based on prompt analysis
|
||||
|
||||
Example:
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy
|
||||
|
||||
agent = PromptExpanderAgent(verbose=True)
|
||||
|
||||
# Basic expansion
|
||||
result = agent.expand("write a movie script in 3 lines")
|
||||
print(result.expanded_prompt)
|
||||
|
||||
# Detailed expansion
|
||||
result = agent.expand("blog about AI", strategy=ExpandStrategy.DETAILED)
|
||||
print(result.expanded_prompt)
|
||||
|
||||
# With tools for context gathering
|
||||
from praisonaiagents.tools import internet_search
|
||||
agent = PromptExpanderAgent(tools=[internet_search], verbose=True)
|
||||
result = agent.expand("latest AI developments")
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import List, Optional, Any, Dict
|
||||
from dataclasses import dataclass, field
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class ExpandStrategy(Enum):
|
||||
"""Enumeration of available prompt expansion strategies."""
|
||||
BASIC = "basic"
|
||||
DETAILED = "detailed"
|
||||
STRUCTURED = "structured"
|
||||
CREATIVE = "creative"
|
||||
AUTO = "auto"
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExpandResult:
|
||||
"""Result of a prompt expansion operation."""
|
||||
original_prompt: str
|
||||
expanded_prompt: str
|
||||
strategy_used: ExpandStrategy
|
||||
metadata: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def __repr__(self):
|
||||
return f"ExpandResult(strategy={self.strategy_used.value}, original_len={len(self.original_prompt)}, expanded_len={len(self.expanded_prompt)})"
|
||||
|
||||
|
||||
class PromptExpanderAgent:
|
||||
"""
|
||||
Agent for expanding short prompts into detailed, comprehensive prompts.
|
||||
|
||||
This agent transforms brief task descriptions into rich, detailed prompts
|
||||
that provide better context and guidance for task execution.
|
||||
|
||||
Key difference from QueryRewriterAgent:
|
||||
- QueryRewriterAgent: Optimizes queries for search/retrieval (RAG)
|
||||
- PromptExpanderAgent: Expands prompts for detailed task execution
|
||||
|
||||
Attributes:
|
||||
name: Name of the agent
|
||||
model: LLM model to use for expansion
|
||||
verbose: Whether to print detailed logs
|
||||
temperature: Temperature for LLM generation (higher = more creative)
|
||||
tools: Optional tools for context gathering
|
||||
|
||||
Example:
|
||||
agent = PromptExpanderAgent(model="gpt-4o-mini", verbose=True)
|
||||
result = agent.expand("write a poem", strategy=ExpandStrategy.CREATIVE)
|
||||
print(result.expanded_prompt)
|
||||
"""
|
||||
|
||||
# Default prompts for each strategy
|
||||
PROMPTS = {
|
||||
"basic": """You are an expert at improving task prompts.
|
||||
|
||||
Given the following task prompt, expand it to be clearer and more actionable while preserving the original intent.
|
||||
|
||||
Guidelines:
|
||||
- Keep it as a TASK/COMMAND (do NOT convert to a question)
|
||||
- Add clarity without changing the core request
|
||||
- Fix any ambiguity
|
||||
- Keep it concise but complete
|
||||
|
||||
Original prompt: {prompt}
|
||||
|
||||
Expanded prompt:""",
|
||||
|
||||
"detailed": """You are an expert at creating comprehensive task prompts.
|
||||
|
||||
Given the following brief task prompt, expand it into a detailed, actionable prompt that provides rich context and clear guidance.
|
||||
|
||||
Guidelines:
|
||||
- Keep it as a TASK/COMMAND (do NOT convert to a question)
|
||||
- Add specific details about what to include
|
||||
- Specify quality expectations
|
||||
- Add relevant constraints or requirements
|
||||
- Include format guidance if applicable
|
||||
- Mention style or tone if relevant
|
||||
|
||||
Original prompt: {prompt}
|
||||
|
||||
Detailed expanded prompt:""",
|
||||
|
||||
"structured": """You are an expert at creating well-structured task prompts.
|
||||
|
||||
Given the following task prompt, expand it into a structured format with clear sections.
|
||||
|
||||
Guidelines:
|
||||
- Keep it as a TASK/COMMAND (do NOT convert to a question)
|
||||
- Use a clear structure:
|
||||
* Task: What to do
|
||||
* Format: Expected output format
|
||||
* Requirements: Specific requirements
|
||||
* Style: Tone or style guidance
|
||||
* Constraints: Any limitations
|
||||
- Be specific and actionable
|
||||
|
||||
Original prompt: {prompt}
|
||||
|
||||
Structured expanded prompt:""",
|
||||
|
||||
"creative": """You are an expert at crafting inspiring, creative task prompts.
|
||||
|
||||
Given the following task prompt, expand it with vivid language and creative direction while keeping it actionable.
|
||||
|
||||
Guidelines:
|
||||
- Keep it as a TASK/COMMAND (do NOT convert to a question)
|
||||
- Use evocative, inspiring language
|
||||
- Paint a picture of the desired outcome
|
||||
- Add creative direction and artistic guidance
|
||||
- Make it exciting and engaging
|
||||
- Preserve the core task intent
|
||||
|
||||
Original prompt: {prompt}
|
||||
|
||||
Creative expanded prompt:""",
|
||||
|
||||
"auto": """You are an expert at analyzing and expanding task prompts.
|
||||
|
||||
Analyze the following prompt and expand it appropriately based on its nature.
|
||||
|
||||
For short/vague prompts: Add significant detail and context
|
||||
For task prompts: Preserve the action-oriented nature
|
||||
For creative tasks: Add artistic direction
|
||||
For technical tasks: Add precision and requirements
|
||||
|
||||
IMPORTANT: Keep it as a TASK/COMMAND. Never convert to a question.
|
||||
|
||||
Original prompt: {prompt}
|
||||
{context}
|
||||
|
||||
Expanded prompt:"""
|
||||
}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "PromptExpanderAgent",
|
||||
model: str = "gpt-4o-mini",
|
||||
instructions: Optional[str] = None,
|
||||
verbose: bool = False,
|
||||
temperature: float = 0.7, # Higher for creativity
|
||||
max_tokens: int = 1000,
|
||||
tools: Optional[List[Any]] = None
|
||||
):
|
||||
"""
|
||||
Initialize the PromptExpanderAgent.
|
||||
|
||||
Args:
|
||||
name: Name of the agent
|
||||
model: LLM model to use (default: gpt-4o-mini)
|
||||
instructions: Custom instructions for the agent
|
||||
verbose: Whether to print detailed logs
|
||||
temperature: Temperature for LLM generation (default: 0.7 for creativity)
|
||||
max_tokens: Maximum tokens for LLM response
|
||||
tools: Optional list of tools for context gathering
|
||||
"""
|
||||
self.name = name
|
||||
self.model = model
|
||||
self.instructions = instructions
|
||||
self.verbose = verbose
|
||||
self.temperature = temperature
|
||||
self.max_tokens = max_tokens
|
||||
self.tools = tools or []
|
||||
self._agent = None # Lazy initialized
|
||||
|
||||
# Setup logging
|
||||
self.logger = logging.getLogger(__name__)
|
||||
if self.verbose:
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
|
||||
@property
|
||||
def agent(self):
|
||||
"""Lazy initialization of internal Agent."""
|
||||
if self._agent is None:
|
||||
from .agent import Agent
|
||||
self._agent = Agent(
|
||||
name=self.name,
|
||||
role="Prompt Expansion Specialist",
|
||||
goal="Expand brief prompts into detailed, actionable prompts for better task execution. Use available tools when needed to gather context.",
|
||||
backstory="You are an expert at understanding user intent and transforming brief requests into comprehensive, detailed prompts.",
|
||||
tools=self.tools,
|
||||
llm=self.model,
|
||||
verbose=self.verbose,
|
||||
markdown=False
|
||||
)
|
||||
return self._agent
|
||||
|
||||
def _call_agent(self, prompt: str) -> str:
|
||||
"""Call the internal Agent with the given prompt."""
|
||||
try:
|
||||
response = self.agent.chat(prompt)
|
||||
return response if response else ""
|
||||
except Exception as e:
|
||||
self.logger.error(f"Agent call failed: {e}")
|
||||
raise
|
||||
|
||||
def _is_short_prompt(self, prompt: str, threshold: int = 10) -> bool:
|
||||
"""Check if prompt is considered short."""
|
||||
return len(prompt.split()) < threshold
|
||||
|
||||
def _is_task_prompt(self, prompt: str) -> bool:
|
||||
"""Check if prompt is a task/command (vs a question)."""
|
||||
task_verbs = [
|
||||
"write", "create", "generate", "build", "make", "design",
|
||||
"develop", "implement", "compose", "draft", "produce",
|
||||
"craft", "construct", "prepare", "draw", "paint", "code"
|
||||
]
|
||||
prompt_lower = prompt.lower().strip()
|
||||
return any(prompt_lower.startswith(verb) for verb in task_verbs)
|
||||
|
||||
def _is_creative_task(self, prompt: str) -> bool:
|
||||
"""Check if prompt is a creative task."""
|
||||
creative_indicators = [
|
||||
"poem", "story", "script", "song", "art", "creative",
|
||||
"fiction", "novel", "essay", "blog", "article", "write"
|
||||
]
|
||||
prompt_lower = prompt.lower()
|
||||
return any(ind in prompt_lower for ind in creative_indicators)
|
||||
|
||||
def _detect_strategy(self, prompt: str) -> ExpandStrategy:
|
||||
"""Auto-detect the best expansion strategy for a prompt."""
|
||||
# Very short prompts need detailed expansion
|
||||
if self._is_short_prompt(prompt, threshold=5):
|
||||
return ExpandStrategy.DETAILED
|
||||
|
||||
# Creative tasks benefit from creative expansion
|
||||
if self._is_creative_task(prompt):
|
||||
return ExpandStrategy.CREATIVE
|
||||
|
||||
# Task prompts get structured expansion
|
||||
if self._is_task_prompt(prompt):
|
||||
return ExpandStrategy.STRUCTURED
|
||||
|
||||
# Default to basic
|
||||
return ExpandStrategy.BASIC
|
||||
|
||||
def expand(
|
||||
self,
|
||||
prompt: str,
|
||||
strategy: ExpandStrategy = ExpandStrategy.AUTO,
|
||||
context: Optional[str] = None
|
||||
) -> ExpandResult:
|
||||
"""
|
||||
Expand a prompt using the specified strategy.
|
||||
|
||||
All operations go through an internal Agent which automatically handles
|
||||
tool calling when tools are provided. The agent decides when to use tools
|
||||
based on the prompt context.
|
||||
|
||||
Args:
|
||||
prompt: The original user prompt
|
||||
strategy: Expansion strategy to use
|
||||
context: Additional context to help with expansion
|
||||
|
||||
Returns:
|
||||
ExpandResult containing the expanded prompt
|
||||
"""
|
||||
if self.verbose:
|
||||
self.logger.debug(f"Expanding prompt: '{prompt}' with strategy: {strategy.value}")
|
||||
if self.tools:
|
||||
print(f"[cyan]Agent has {len(self.tools)} tools available (will use if needed)...[/cyan]")
|
||||
|
||||
# Auto-detect strategy if needed
|
||||
if strategy == ExpandStrategy.AUTO:
|
||||
strategy = self._detect_strategy(prompt)
|
||||
if self.verbose:
|
||||
self.logger.debug(f"Auto-detected strategy: {strategy.value}")
|
||||
|
||||
# Build the expansion prompt
|
||||
if strategy == ExpandStrategy.AUTO:
|
||||
# Fallback if auto-detect returns AUTO
|
||||
strategy = ExpandStrategy.DETAILED
|
||||
|
||||
expansion_prompt = self.PROMPTS[strategy.value].format(
|
||||
prompt=prompt,
|
||||
context=context or ""
|
||||
)
|
||||
|
||||
# Call the agent
|
||||
expanded = self._call_agent(expansion_prompt).strip()
|
||||
|
||||
# If expansion failed, return original
|
||||
if not expanded:
|
||||
expanded = prompt
|
||||
|
||||
return ExpandResult(
|
||||
original_prompt=prompt,
|
||||
expanded_prompt=expanded,
|
||||
strategy_used=strategy,
|
||||
metadata={
|
||||
"original_length": len(prompt),
|
||||
"expanded_length": len(expanded),
|
||||
"has_context": bool(context),
|
||||
"has_tools": bool(self.tools)
|
||||
}
|
||||
)
|
||||
|
||||
# Convenience methods for direct strategy access
|
||||
def expand_basic(self, prompt: str, context: Optional[str] = None) -> ExpandResult:
|
||||
"""Convenience method for basic expansion."""
|
||||
return self.expand(prompt, strategy=ExpandStrategy.BASIC, context=context)
|
||||
|
||||
def expand_detailed(self, prompt: str, context: Optional[str] = None) -> ExpandResult:
|
||||
"""Convenience method for detailed expansion."""
|
||||
return self.expand(prompt, strategy=ExpandStrategy.DETAILED, context=context)
|
||||
|
||||
def expand_structured(self, prompt: str, context: Optional[str] = None) -> ExpandResult:
|
||||
"""Convenience method for structured expansion."""
|
||||
return self.expand(prompt, strategy=ExpandStrategy.STRUCTURED, context=context)
|
||||
|
||||
def expand_creative(self, prompt: str, context: Optional[str] = None) -> ExpandResult:
|
||||
"""Convenience method for creative expansion."""
|
||||
return self.expand(prompt, strategy=ExpandStrategy.CREATIVE, context=context)
|
||||
@@ -110,20 +110,23 @@ class QueryRewriterAgent:
|
||||
|
||||
# Default prompts for each strategy
|
||||
PROMPTS = {
|
||||
"basic": """You are an expert at rewriting search queries for better retrieval.
|
||||
"basic": """You are an expert at improving prompts and queries.
|
||||
|
||||
Given the following query, rewrite it to be clearer, more specific, and better suited for searching a knowledge base.
|
||||
Given the following input, improve it while preserving its original intent and type.
|
||||
|
||||
Guidelines:
|
||||
- If it's a TASK (e.g., "write X", "create Y", "generate Z"), keep it as a task - do NOT convert to a question
|
||||
- If it's a QUESTION (e.g., "what is X?", "how does Y work?"), keep it as a question
|
||||
- Expand abbreviations and acronyms
|
||||
- Fix typos and grammatical errors
|
||||
- Add relevant context if the query is too short
|
||||
- Transform keyword searches into natural questions
|
||||
- Keep the original intent intact
|
||||
- Add relevant context if too short
|
||||
- Keep the original intent and action intact
|
||||
|
||||
Query: {query}
|
||||
IMPORTANT: If the input is a command/task to DO something, the output must also be a command/task to DO that thing.
|
||||
|
||||
Rewritten query:""",
|
||||
Input: {query}
|
||||
|
||||
Improved version:""",
|
||||
|
||||
"hyde": """You are an expert at generating hypothetical documents.
|
||||
|
||||
@@ -198,21 +201,23 @@ Follow-up query: {query}
|
||||
|
||||
Standalone question:""",
|
||||
|
||||
"auto": """You are an expert at analyzing and rewriting queries.
|
||||
"auto": """You are an expert at analyzing and improving prompts and queries.
|
||||
|
||||
Analyze the following query and determine the best rewriting strategy, then apply it.
|
||||
Analyze the following input and improve it while preserving its original intent and type.
|
||||
|
||||
Query types to consider:
|
||||
1. Short/keyword queries → Expand into full questions
|
||||
2. Ambiguous queries → Clarify with context
|
||||
3. Complex multi-part queries → Break into sub-queries
|
||||
4. Follow-up queries → Resolve references from context
|
||||
5. Well-formed queries → Minor optimization only
|
||||
Input types to consider:
|
||||
1. TASK/COMMAND (write, create, generate, build) → Keep as a task, make it clearer
|
||||
2. QUESTION (what, how, why, when) → Keep as a question, make it more specific
|
||||
3. Short/keyword inputs → Expand with context but preserve type
|
||||
4. Complex multi-part inputs → Clarify structure
|
||||
5. Well-formed inputs → Minor optimization only
|
||||
|
||||
Query: {query}
|
||||
IMPORTANT: Never convert a task into a question. "Write X" should remain "Write X", not "How to write X?"
|
||||
|
||||
Input: {query}
|
||||
{context}
|
||||
|
||||
Analysis and rewritten query:"""
|
||||
Improved version:"""
|
||||
}
|
||||
|
||||
def __init__(
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.0.169"
|
||||
version = "0.0.170"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
requires-python = ">=3.10"
|
||||
authors = [
|
||||
|
||||
@@ -0,0 +1,316 @@
|
||||
"""
|
||||
Tests for PromptExpanderAgent
|
||||
|
||||
Run with: python -m pytest tests/test_prompt_expander_agent.py -v
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import patch, MagicMock
|
||||
|
||||
|
||||
class TestPromptExpanderImports:
|
||||
"""Test that all imports work correctly."""
|
||||
|
||||
def test_import_from_package(self):
|
||||
"""Test importing from main package."""
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy, ExpandResult
|
||||
assert PromptExpanderAgent is not None
|
||||
assert ExpandStrategy is not None
|
||||
assert ExpandResult is not None
|
||||
|
||||
def test_import_from_agent_module(self):
|
||||
"""Test importing from agent module."""
|
||||
from praisonaiagents.agent import PromptExpanderAgent, ExpandStrategy, ExpandResult
|
||||
assert PromptExpanderAgent is not None
|
||||
assert ExpandStrategy is not None
|
||||
assert ExpandResult is not None
|
||||
|
||||
|
||||
class TestExpandStrategy:
|
||||
"""Test ExpandStrategy enum."""
|
||||
|
||||
def test_strategy_values(self):
|
||||
"""Test all strategy values exist."""
|
||||
from praisonaiagents import ExpandStrategy
|
||||
|
||||
assert ExpandStrategy.BASIC.value == "basic"
|
||||
assert ExpandStrategy.DETAILED.value == "detailed"
|
||||
assert ExpandStrategy.STRUCTURED.value == "structured"
|
||||
assert ExpandStrategy.CREATIVE.value == "creative"
|
||||
assert ExpandStrategy.AUTO.value == "auto"
|
||||
|
||||
|
||||
class TestExpandResult:
|
||||
"""Test ExpandResult dataclass."""
|
||||
|
||||
def test_result_creation(self):
|
||||
"""Test creating an ExpandResult."""
|
||||
from praisonaiagents import ExpandResult, ExpandStrategy
|
||||
|
||||
result = ExpandResult(
|
||||
original_prompt="write a movie script",
|
||||
expanded_prompt="Write a compelling movie script...",
|
||||
strategy_used=ExpandStrategy.BASIC
|
||||
)
|
||||
|
||||
assert result.original_prompt == "write a movie script"
|
||||
assert result.expanded_prompt == "Write a compelling movie script..."
|
||||
assert result.strategy_used == ExpandStrategy.BASIC
|
||||
|
||||
def test_result_with_metadata(self):
|
||||
"""Test ExpandResult with metadata."""
|
||||
from praisonaiagents import ExpandResult, ExpandStrategy
|
||||
|
||||
result = ExpandResult(
|
||||
original_prompt="test",
|
||||
expanded_prompt="expanded test",
|
||||
strategy_used=ExpandStrategy.DETAILED,
|
||||
metadata={"word_count": 50, "has_context": True}
|
||||
)
|
||||
|
||||
assert result.metadata["word_count"] == 50
|
||||
assert result.metadata["has_context"] is True
|
||||
|
||||
|
||||
class TestPromptExpanderAgentInit:
|
||||
"""Test PromptExpanderAgent initialization."""
|
||||
|
||||
def test_default_initialization(self):
|
||||
"""Test agent initializes with defaults."""
|
||||
from praisonaiagents import PromptExpanderAgent
|
||||
|
||||
agent = PromptExpanderAgent()
|
||||
|
||||
assert agent.name == "PromptExpanderAgent"
|
||||
assert agent.model == "gpt-4o-mini"
|
||||
assert not agent.verbose
|
||||
assert agent.temperature == 0.7 # Higher for creativity
|
||||
|
||||
def test_custom_initialization(self):
|
||||
"""Test agent initializes with custom values."""
|
||||
from praisonaiagents import PromptExpanderAgent
|
||||
|
||||
agent = PromptExpanderAgent(
|
||||
name="CustomExpander",
|
||||
model="gpt-4o",
|
||||
verbose=True,
|
||||
temperature=0.5
|
||||
)
|
||||
|
||||
assert agent.name == "CustomExpander"
|
||||
assert agent.model == "gpt-4o"
|
||||
assert agent.verbose
|
||||
assert agent.temperature == 0.5
|
||||
|
||||
def test_initialization_with_tools(self):
|
||||
"""Test agent initializes with tools."""
|
||||
from praisonaiagents import PromptExpanderAgent
|
||||
|
||||
def mock_tool(query: str) -> str:
|
||||
return "mock result"
|
||||
|
||||
agent = PromptExpanderAgent(tools=[mock_tool])
|
||||
|
||||
assert len(agent.tools) == 1
|
||||
|
||||
|
||||
class TestPromptExpansion:
|
||||
"""Test prompt expansion functionality."""
|
||||
|
||||
def test_expand_preserves_task_intent(self):
|
||||
"""Test that expansion preserves task intent (not converting to question)."""
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandResult
|
||||
|
||||
agent = PromptExpanderAgent()
|
||||
|
||||
# Mock the internal agent call
|
||||
with patch.object(agent, '_call_agent') as mock_call:
|
||||
mock_call.return_value = "Write a compelling three-line movie script that captures a dramatic moment with vivid imagery and emotional depth."
|
||||
|
||||
result = agent.expand("write a movie script in 3 lines")
|
||||
|
||||
assert result.original_prompt == "write a movie script in 3 lines"
|
||||
assert "Write" in result.expanded_prompt # Should start with action verb
|
||||
assert "?" not in result.expanded_prompt # Should NOT be a question
|
||||
|
||||
def test_expand_adds_detail(self):
|
||||
"""Test that expansion adds meaningful detail."""
|
||||
from praisonaiagents import PromptExpanderAgent
|
||||
|
||||
agent = PromptExpanderAgent()
|
||||
|
||||
with patch.object(agent, '_call_agent') as mock_call:
|
||||
mock_call.return_value = "Create a detailed blog post about artificial intelligence trends in 2025, covering key developments in large language models, multimodal AI, and autonomous agents. Include specific examples and expert predictions."
|
||||
|
||||
result = agent.expand("write about AI")
|
||||
|
||||
# Expanded should be longer than original
|
||||
assert len(result.expanded_prompt) > len(result.original_prompt)
|
||||
|
||||
|
||||
class TestExpandStrategies:
|
||||
"""Test different expansion strategies."""
|
||||
|
||||
def test_basic_strategy(self):
|
||||
"""Test BASIC expansion strategy."""
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy
|
||||
|
||||
agent = PromptExpanderAgent()
|
||||
|
||||
with patch.object(agent, '_call_agent') as mock_call:
|
||||
mock_call.return_value = "Write a movie script in three lines."
|
||||
|
||||
result = agent.expand("movie script 3 lines", strategy=ExpandStrategy.BASIC)
|
||||
|
||||
assert result.strategy_used == ExpandStrategy.BASIC
|
||||
mock_call.assert_called_once()
|
||||
|
||||
def test_detailed_strategy(self):
|
||||
"""Test DETAILED expansion strategy."""
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy
|
||||
|
||||
agent = PromptExpanderAgent()
|
||||
|
||||
with patch.object(agent, '_call_agent') as mock_call:
|
||||
mock_call.return_value = """Write a compelling three-line movie script that:
|
||||
- Opens with a hook that immediately grabs attention
|
||||
- Builds tension or emotion in the middle line
|
||||
- Delivers a powerful, memorable conclusion
|
||||
Include vivid imagery, strong verbs, and emotional resonance."""
|
||||
|
||||
result = agent.expand("movie script 3 lines", strategy=ExpandStrategy.DETAILED)
|
||||
|
||||
assert result.strategy_used == ExpandStrategy.DETAILED
|
||||
|
||||
def test_structured_strategy(self):
|
||||
"""Test STRUCTURED expansion strategy."""
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy
|
||||
|
||||
agent = PromptExpanderAgent()
|
||||
|
||||
with patch.object(agent, '_call_agent') as mock_call:
|
||||
mock_call.return_value = """Task: Write a three-line movie script
|
||||
Format: Three lines, each serving a narrative purpose
|
||||
Requirements:
|
||||
- Line 1: Setup/Hook
|
||||
- Line 2: Conflict/Development
|
||||
- Line 3: Resolution/Twist
|
||||
Style: Cinematic, impactful"""
|
||||
|
||||
result = agent.expand("movie script 3 lines", strategy=ExpandStrategy.STRUCTURED)
|
||||
|
||||
assert result.strategy_used == ExpandStrategy.STRUCTURED
|
||||
|
||||
def test_creative_strategy(self):
|
||||
"""Test CREATIVE expansion strategy."""
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy
|
||||
|
||||
agent = PromptExpanderAgent()
|
||||
|
||||
with patch.object(agent, '_call_agent') as mock_call:
|
||||
mock_call.return_value = "Craft an unforgettable three-line movie script that transports the reader into a vivid world. Let each line breathe with cinematic tension - a hook that grabs, a twist that surprises, and a finale that lingers in the mind long after reading."
|
||||
|
||||
result = agent.expand("movie script 3 lines", strategy=ExpandStrategy.CREATIVE)
|
||||
|
||||
assert result.strategy_used == ExpandStrategy.CREATIVE
|
||||
|
||||
def test_auto_strategy_detection(self):
|
||||
"""Test AUTO strategy detects appropriate expansion type."""
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy
|
||||
|
||||
agent = PromptExpanderAgent()
|
||||
|
||||
# Short prompts should get DETAILED expansion
|
||||
strategy = agent._detect_strategy("AI")
|
||||
assert strategy in [ExpandStrategy.BASIC, ExpandStrategy.DETAILED]
|
||||
|
||||
# Task prompts should preserve task nature
|
||||
strategy = agent._detect_strategy("write a poem about love")
|
||||
assert strategy is not None
|
||||
|
||||
|
||||
class TestToolsIntegration:
|
||||
"""Test tools integration with PromptExpanderAgent."""
|
||||
|
||||
def test_expand_with_tools(self):
|
||||
"""Test expansion with tools for context gathering."""
|
||||
from praisonaiagents import PromptExpanderAgent
|
||||
|
||||
def search_tool(query: str) -> str:
|
||||
return "Latest AI trends: LLMs, multimodal, agents"
|
||||
|
||||
agent = PromptExpanderAgent(tools=[search_tool])
|
||||
|
||||
assert len(agent.tools) == 1
|
||||
|
||||
def test_tools_passed_to_internal_agent(self):
|
||||
"""Test that tools are passed to the internal agent."""
|
||||
from praisonaiagents import PromptExpanderAgent
|
||||
|
||||
def mock_tool(query: str) -> str:
|
||||
return "result"
|
||||
|
||||
agent = PromptExpanderAgent(tools=[mock_tool])
|
||||
|
||||
# Access the lazy-initialized agent
|
||||
internal_agent = agent.agent
|
||||
|
||||
# The internal agent should have the tools
|
||||
assert internal_agent.tools is not None
|
||||
|
||||
|
||||
class TestConvenienceMethods:
|
||||
"""Test convenience methods."""
|
||||
|
||||
def test_convenience_methods_exist(self):
|
||||
"""Test all convenience methods exist."""
|
||||
from praisonaiagents import PromptExpanderAgent
|
||||
|
||||
agent = PromptExpanderAgent()
|
||||
|
||||
assert hasattr(agent, 'expand')
|
||||
assert hasattr(agent, 'expand_basic')
|
||||
assert hasattr(agent, 'expand_detailed')
|
||||
assert hasattr(agent, 'expand_structured')
|
||||
assert hasattr(agent, 'expand_creative')
|
||||
|
||||
|
||||
class TestEdgeCases:
|
||||
"""Test edge cases."""
|
||||
|
||||
def test_empty_prompt(self):
|
||||
"""Test handling of empty prompt."""
|
||||
from praisonaiagents import PromptExpanderAgent
|
||||
|
||||
agent = PromptExpanderAgent()
|
||||
|
||||
with patch.object(agent, '_call_agent') as mock_call:
|
||||
mock_call.return_value = ""
|
||||
|
||||
result = agent.expand("")
|
||||
|
||||
# Should return original if expansion fails
|
||||
assert result.expanded_prompt == "" or result.original_prompt == ""
|
||||
|
||||
def test_already_detailed_prompt(self):
|
||||
"""Test that already detailed prompts are minimally modified."""
|
||||
from praisonaiagents import PromptExpanderAgent
|
||||
|
||||
agent = PromptExpanderAgent()
|
||||
|
||||
detailed_prompt = """Write a comprehensive blog post about machine learning,
|
||||
covering supervised learning, unsupervised learning, and reinforcement learning.
|
||||
Include code examples in Python and real-world applications."""
|
||||
|
||||
with patch.object(agent, '_call_agent') as mock_call:
|
||||
# For detailed prompts, minimal expansion expected
|
||||
mock_call.return_value = detailed_prompt + " Ensure clarity and technical accuracy."
|
||||
|
||||
result = agent.expand(detailed_prompt)
|
||||
|
||||
# Should not drastically change already detailed prompts
|
||||
assert result.original_prompt == detailed_prompt
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
+826
-21
@@ -1,34 +1,839 @@
|
||||
# PraisonAI Package
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="docs/logo/dark.png" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="docs/logo/light.png" />
|
||||
<img alt="PraisonAI Logo" src="docs/logo/light.png" />
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
## Development
|
||||
<p align="center">
|
||||
<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://static.pepy.tech/badge/PraisonAI" alt="Total Downloads" /></a>
|
||||
<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/github/v/release/MervinPraison/PraisonAI" alt="Latest Stable Version" /></a>
|
||||
<a href="https://github.com/MervinPraison/PraisonAI"><img src="https://img.shields.io/badge/License-MIT-yellow.svg" alt="License" /></a>
|
||||
</p>
|
||||
|
||||
<div align="center">
|
||||
|
||||
# Praison AI
|
||||
|
||||
<a href="https://trendshift.io/repositories/9130" target="_blank"><img src="https://trendshift.io/api/badge/repositories/9130" alt="MervinPraison%2FPraisonAI | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a>
|
||||
|
||||
</div>
|
||||
|
||||
PraisonAI is a production-ready Multi-AI Agents framework with self-reflection, designed to create AI Agents to automate and solve problems ranging from simple tasks to complex challenges. By integrating PraisonAI Agents, AG2 (Formerly AutoGen), and CrewAI into a low-code solution, it streamlines the building and management of multi-agent LLM systems, emphasising simplicity, customisation, and effective human-agent collaboration.
|
||||
|
||||
<div align="center">
|
||||
<a href="https://docs.praison.ai">
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/📚_Documentation-Visit_docs.praison.ai-blue?style=for-the-badge&logo=bookstack&logoColor=white" alt="Documentation" />
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Key Features
|
||||
|
||||
- 🤖 Automated AI Agents Creation
|
||||
- 🔄 Self Reflection AI Agents
|
||||
- 🧠 Reasoning AI Agents
|
||||
- 👁️ Multi Modal AI Agents
|
||||
- 🤝 Multi Agent Collaboration
|
||||
- 🎭 AI Agent Workflow
|
||||
- 📚 Add Custom Knowledge
|
||||
- 🧠 Agents with Short and Long Term Memory
|
||||
- 📄 Chat with PDF Agents
|
||||
- 💻 Code Interpreter Agents
|
||||
- 📚 RAG Agents
|
||||
- 🤔 Async & Parallel Processing
|
||||
- 🔄 Auto Agents
|
||||
- 🔢 Math Agents
|
||||
- 🎯 Structured Output Agents
|
||||
- 🔗 LangChain Integrated Agents
|
||||
- 📞 Callback Agents
|
||||
- 🤏 Mini AI Agents
|
||||
- 🛠️ 100+ Custom Tools
|
||||
- 📄 YAML Configuration
|
||||
- 💯 100+ LLM Support
|
||||
- 🔬 **Deep Research Agents** (OpenAI & Gemini)
|
||||
- 🔄 **Query Rewriter Agent** (HyDE, Step-back, Multi-query)
|
||||
|
||||
## Using Python Code
|
||||
|
||||
Light weight package dedicated for coding:
|
||||
```bash
|
||||
# Install
|
||||
uv sync
|
||||
|
||||
# Build
|
||||
uv build
|
||||
|
||||
# Publish
|
||||
uv publish
|
||||
pip install praisonaiagents
|
||||
```
|
||||
|
||||
## Version Bump
|
||||
```bash
|
||||
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
### 1. Single Agent
|
||||
|
||||
Create app.py file and add the code below:
|
||||
```python
|
||||
from praisonaiagents import Agent
|
||||
agent = Agent(instructions="Your are a helpful AI assistant")
|
||||
agent.start("Write a movie script about a robot in Mars")
|
||||
```
|
||||
|
||||
Run:
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
### 2. Multi Agents
|
||||
|
||||
Create app.py file and add the code below:
|
||||
```python
|
||||
from praisonaiagents import Agent, PraisonAIAgents
|
||||
|
||||
research_agent = Agent(instructions="Research about AI")
|
||||
summarise_agent = Agent(instructions="Summarise research agent's findings")
|
||||
agents = PraisonAIAgents(agents=[research_agent, summarise_agent])
|
||||
agents.start()
|
||||
```
|
||||
|
||||
Run:
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
### 3. Deep Research Agent
|
||||
|
||||
Automated research with real-time streaming, web search, and citations using OpenAI or Gemini Deep Research APIs.
|
||||
|
||||
```python
|
||||
from praisonaiagents import DeepResearchAgent
|
||||
|
||||
# OpenAI Deep Research
|
||||
agent = DeepResearchAgent(
|
||||
model="o4-mini-deep-research", # or "o3-deep-research"
|
||||
verbose=True
|
||||
)
|
||||
|
||||
result = agent.research("What are the latest AI trends in 2025?")
|
||||
print(result.report)
|
||||
print(f"Citations: {len(result.citations)}")
|
||||
```
|
||||
|
||||
```python
|
||||
# Gemini Deep Research
|
||||
from praisonaiagents import DeepResearchAgent
|
||||
|
||||
agent = DeepResearchAgent(
|
||||
model="deep-research-pro", # Auto-detected as Gemini
|
||||
verbose=True
|
||||
)
|
||||
|
||||
result = agent.research("Research quantum computing advances")
|
||||
print(result.report)
|
||||
```
|
||||
|
||||
**Features:**
|
||||
- 🔍 Multi-provider support (OpenAI, Gemini, LiteLLM)
|
||||
- 📡 Real-time streaming with reasoning summaries
|
||||
- 📚 Structured citations with URLs
|
||||
- 🛠️ Built-in tools: web search, code interpreter, MCP, file search
|
||||
- 🔄 Automatic provider detection from model name
|
||||
|
||||
### 4. Query Rewriter Agent
|
||||
|
||||
Transform user queries to improve RAG retrieval quality using multiple strategies.
|
||||
|
||||
```python
|
||||
from praisonaiagents import QueryRewriterAgent, RewriteStrategy
|
||||
|
||||
agent = QueryRewriterAgent(model="gpt-4o-mini")
|
||||
|
||||
# Basic - expands abbreviations, adds context
|
||||
result = agent.rewrite("AI trends")
|
||||
print(result.primary_query) # "What are the current trends in Artificial Intelligence?"
|
||||
|
||||
# HyDE - generates hypothetical document for semantic matching
|
||||
result = agent.rewrite("What is quantum computing?", strategy=RewriteStrategy.HYDE)
|
||||
|
||||
# Step-back - generates broader context question
|
||||
result = agent.rewrite("GPT-4 vs Claude 3?", strategy=RewriteStrategy.STEP_BACK)
|
||||
|
||||
# Sub-queries - decomposes complex questions
|
||||
result = agent.rewrite("RAG setup and best embedding models?", strategy=RewriteStrategy.SUB_QUERIES)
|
||||
|
||||
# Contextual - resolves references using chat history
|
||||
result = agent.rewrite("What about cost?", chat_history=[...])
|
||||
```
|
||||
|
||||
**Strategies:**
|
||||
- **BASIC**: Expand abbreviations, fix typos, add context
|
||||
- **HYDE**: Generate hypothetical document for semantic matching
|
||||
- **STEP_BACK**: Generate higher-level concept questions
|
||||
- **SUB_QUERIES**: Decompose multi-part questions
|
||||
- **MULTI_QUERY**: Generate multiple paraphrased versions
|
||||
- **CONTEXTUAL**: Resolve references using conversation history
|
||||
- **AUTO**: Automatically detect best strategy
|
||||
|
||||
## Using No Code
|
||||
|
||||
### Auto Mode:
|
||||
```bash
|
||||
pip install praisonai
|
||||
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
|
||||
praisonai --auto create a movie script about Robots in Mars
|
||||
```
|
||||
|
||||
### Query Rewriting (works with any command):
|
||||
```bash
|
||||
# Rewrite query for better results (uses QueryRewriterAgent)
|
||||
praisonai "AI trends" --query-rewrite
|
||||
|
||||
# Rewrite with search tools (agent decides when to search)
|
||||
praisonai "latest developments" --query-rewrite --rewrite-tools "internet_search"
|
||||
|
||||
# Works with any prompt
|
||||
praisonai "explain quantum computing" --query-rewrite -v
|
||||
```
|
||||
|
||||
### Deep Research CLI:
|
||||
```bash
|
||||
# Default: OpenAI (o4-mini-deep-research)
|
||||
praisonai research "What are the latest AI trends in 2025?"
|
||||
|
||||
# Use Gemini
|
||||
praisonai research --model deep-research-pro "Your research query"
|
||||
|
||||
# Rewrite query before research
|
||||
praisonai research --query-rewrite "AI trends"
|
||||
|
||||
# Rewrite with search tools
|
||||
praisonai research --query-rewrite --rewrite-tools "internet_search" "AI trends"
|
||||
|
||||
# Use custom tools from file (gathers context before deep research)
|
||||
praisonai research --tools tools.py "Your research query"
|
||||
praisonai research -t my_tools.py "Your research query"
|
||||
|
||||
# Use built-in tools by name (comma-separated)
|
||||
praisonai research --tools "internet_search,wiki_search" "Your query"
|
||||
praisonai research -t "yfinance,calculator_tools" "Stock analysis query"
|
||||
|
||||
# Save output to file (output/research/{query}.md)
|
||||
praisonai research --save "Your research query"
|
||||
praisonai research -s "Your research query"
|
||||
|
||||
# Combine options
|
||||
praisonai research --query-rewrite --tools tools.py --save "Your research query"
|
||||
|
||||
# Verbose mode (show debug logs)
|
||||
praisonai research -v "Your research query"
|
||||
```
|
||||
|
||||
## Using JavaScript Code
|
||||
|
||||
```bash
|
||||
# From project root
|
||||
python src/praisonai/scripts/bump_version.py 2.2.96
|
||||
npm install praisonai
|
||||
export OPENAI_API_KEY=xxxxxxxxxxxxxxxxxxxxxx
|
||||
```
|
||||
|
||||
```javascript
|
||||
const { Agent } = require('praisonai');
|
||||
const agent = new Agent({ instructions: 'You are a helpful AI assistant' });
|
||||
agent.start('Write a movie script about a robot in Mars');
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Star History
|
||||
|
||||
[](https://docs.praison.ai)
|
||||
|
||||
## AI Agents Flow
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
%% Define the main flow
|
||||
Start([▶ Start]) --> Agent1
|
||||
Agent1 --> Process[⚙ Process]
|
||||
Process --> Agent2
|
||||
Agent2 --> Output([✓ Output])
|
||||
Process -.-> Agent1
|
||||
|
||||
%% Define subgraphs for agents and their tasks
|
||||
subgraph Agent1[ ]
|
||||
Task1[📋 Task]
|
||||
AgentIcon1[🤖 AI Agent]
|
||||
Tools1[🔧 Tools]
|
||||
|
||||
Task1 --- AgentIcon1
|
||||
AgentIcon1 --- Tools1
|
||||
end
|
||||
|
||||
subgraph Agent2[ ]
|
||||
Task2[📋 Task]
|
||||
AgentIcon2[🤖 AI Agent]
|
||||
Tools2[🔧 Tools]
|
||||
|
||||
Task2 --- AgentIcon2
|
||||
AgentIcon2 --- Tools2
|
||||
end
|
||||
|
||||
classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
|
||||
classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
|
||||
classDef tools fill:#2E8B57,stroke:#7C90A0,color:#fff
|
||||
classDef transparent fill:none,stroke:none
|
||||
|
||||
class Start,Output,Task1,Task2 input
|
||||
class Process,AgentIcon1,AgentIcon2 process
|
||||
class Tools1,Tools2 tools
|
||||
class Agent1,Agent2 transparent
|
||||
```
|
||||
|
||||
## AI Agents with Tools
|
||||
|
||||
Create AI agents that can use tools to interact with external systems and perform actions.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Tools
|
||||
direction TB
|
||||
T3[Internet Search]
|
||||
T1[Code Execution]
|
||||
T2[Formatting]
|
||||
end
|
||||
|
||||
Input[Input] ---> Agents
|
||||
subgraph Agents
|
||||
direction LR
|
||||
A1[Agent 1]
|
||||
A2[Agent 2]
|
||||
A3[Agent 3]
|
||||
end
|
||||
Agents ---> Output[Output]
|
||||
|
||||
T3 --> A1
|
||||
T1 --> A2
|
||||
T2 --> A3
|
||||
|
||||
style Tools fill:#189AB4,color:#fff
|
||||
style Agents fill:#8B0000,color:#fff
|
||||
style Input fill:#8B0000,color:#fff
|
||||
style Output fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
## AI Agents with Memory
|
||||
|
||||
Create AI agents with memory capabilities for maintaining context and information across tasks.
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Memory
|
||||
direction TB
|
||||
STM[Short Term]
|
||||
LTM[Long Term]
|
||||
end
|
||||
|
||||
subgraph Store
|
||||
direction TB
|
||||
DB[(Vector DB)]
|
||||
end
|
||||
|
||||
Input[Input] ---> Agents
|
||||
subgraph Agents
|
||||
direction LR
|
||||
A1[Agent 1]
|
||||
A2[Agent 2]
|
||||
A3[Agent 3]
|
||||
end
|
||||
Agents ---> Output[Output]
|
||||
|
||||
Memory <--> Store
|
||||
Store <--> A1
|
||||
Store <--> A2
|
||||
Store <--> A3
|
||||
|
||||
style Memory fill:#189AB4,color:#fff
|
||||
style Store fill:#2E8B57,color:#fff
|
||||
style Agents fill:#8B0000,color:#fff
|
||||
style Input fill:#8B0000,color:#fff
|
||||
style Output fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
## AI Agents with Different Processes
|
||||
|
||||
### Sequential Process
|
||||
|
||||
The simplest form of task execution where tasks are performed one after another.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Input[Input] --> A1
|
||||
subgraph Agents
|
||||
direction LR
|
||||
A1[Agent 1] --> A2[Agent 2] --> A3[Agent 3]
|
||||
end
|
||||
A3 --> Output[Output]
|
||||
|
||||
classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
|
||||
classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
|
||||
classDef transparent fill:none,stroke:none
|
||||
|
||||
class Input,Output input
|
||||
class A1,A2,A3 process
|
||||
class Agents transparent
|
||||
```
|
||||
|
||||
### Hierarchical Process
|
||||
|
||||
Uses a manager agent to coordinate task execution and agent assignments.
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Input[Input] --> Manager
|
||||
|
||||
subgraph Agents
|
||||
Manager[Manager Agent]
|
||||
|
||||
subgraph Workers
|
||||
direction LR
|
||||
W1[Worker 1]
|
||||
W2[Worker 2]
|
||||
W3[Worker 3]
|
||||
end
|
||||
|
||||
Manager --> W1
|
||||
Manager --> W2
|
||||
Manager --> W3
|
||||
end
|
||||
|
||||
W1 --> Manager
|
||||
W2 --> Manager
|
||||
W3 --> Manager
|
||||
Manager --> Output[Output]
|
||||
|
||||
classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
|
||||
classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
|
||||
classDef transparent fill:none,stroke:none
|
||||
|
||||
class Input,Output input
|
||||
class Manager,W1,W2,W3 process
|
||||
class Agents,Workers transparent
|
||||
```
|
||||
|
||||
### Workflow Process
|
||||
|
||||
Advanced process type supporting complex task relationships and conditional execution.
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
Input[Input] --> Start
|
||||
|
||||
subgraph Workflow
|
||||
direction LR
|
||||
Start[Start] --> C1{Condition}
|
||||
C1 --> |Yes| A1[Agent 1]
|
||||
C1 --> |No| A2[Agent 2]
|
||||
A1 --> Join
|
||||
A2 --> Join
|
||||
Join --> A3[Agent 3]
|
||||
end
|
||||
|
||||
A3 --> Output[Output]
|
||||
|
||||
classDef input fill:#8B0000,stroke:#7C90A0,color:#fff
|
||||
classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
|
||||
classDef decision fill:#2E8B57,stroke:#7C90A0,color:#fff
|
||||
classDef transparent fill:none,stroke:none
|
||||
|
||||
class Input,Output input
|
||||
class Start,A1,A2,A3,Join process
|
||||
class C1 decision
|
||||
class Workflow transparent
|
||||
```
|
||||
|
||||
#### Agentic Routing Workflow
|
||||
|
||||
Create AI agents that can dynamically route tasks to specialized LLM instances.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[In] --> Router[LLM Call Router]
|
||||
Router --> LLM1[LLM Call 1]
|
||||
Router --> LLM2[LLM Call 2]
|
||||
Router --> LLM3[LLM Call 3]
|
||||
LLM1 --> Out[Out]
|
||||
LLM2 --> Out
|
||||
LLM3 --> Out
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Router fill:#2E8B57,color:#fff
|
||||
style LLM1 fill:#2E8B57,color:#fff
|
||||
style LLM2 fill:#2E8B57,color:#fff
|
||||
style LLM3 fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
#### Agentic Orchestrator Worker
|
||||
|
||||
Create AI agents that orchestrate and distribute tasks among specialized workers.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[In] --> Router[LLM Call Router]
|
||||
Router --> LLM1[LLM Call 1]
|
||||
Router --> LLM2[LLM Call 2]
|
||||
Router --> LLM3[LLM Call 3]
|
||||
LLM1 --> Synthesizer[Synthesizer]
|
||||
LLM2 --> Synthesizer
|
||||
LLM3 --> Synthesizer
|
||||
Synthesizer --> Out[Out]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Router fill:#2E8B57,color:#fff
|
||||
style LLM1 fill:#2E8B57,color:#fff
|
||||
style LLM2 fill:#2E8B57,color:#fff
|
||||
style LLM3 fill:#2E8B57,color:#fff
|
||||
style Synthesizer fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
#### Agentic Autonomous Workflow
|
||||
|
||||
Create AI agents that can autonomously monitor, act, and adapt based on environment feedback.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
Human[Human] <--> LLM[LLM Call]
|
||||
LLM -->|ACTION| Environment[Environment]
|
||||
Environment -->|FEEDBACK| LLM
|
||||
LLM --> Stop[Stop]
|
||||
|
||||
style Human fill:#8B0000,color:#fff
|
||||
style LLM fill:#2E8B57,color:#fff
|
||||
style Environment fill:#8B0000,color:#fff
|
||||
style Stop fill:#333,color:#fff
|
||||
```
|
||||
|
||||
#### Agentic Parallelization
|
||||
|
||||
Create AI agents that can execute tasks in parallel for improved performance.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[In] --> LLM2[LLM Call 2]
|
||||
In --> LLM1[LLM Call 1]
|
||||
In --> LLM3[LLM Call 3]
|
||||
LLM1 --> Aggregator[Aggregator]
|
||||
LLM2 --> Aggregator
|
||||
LLM3 --> Aggregator
|
||||
Aggregator --> Out[Out]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style LLM1 fill:#2E8B57,color:#fff
|
||||
style LLM2 fill:#2E8B57,color:#fff
|
||||
style LLM3 fill:#2E8B57,color:#fff
|
||||
style Aggregator fill:#fff,color:#000
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
#### Agentic Prompt Chaining
|
||||
|
||||
Create AI agents with sequential prompt chaining for complex workflows.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[In] --> LLM1[LLM Call 1] --> Gate{Gate}
|
||||
Gate -->|Pass| LLM2[LLM Call 2] -->|Output 2| LLM3[LLM Call 3] --> Out[Out]
|
||||
Gate -->|Fail| Exit[Exit]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style LLM1 fill:#2E8B57,color:#fff
|
||||
style LLM2 fill:#2E8B57,color:#fff
|
||||
style LLM3 fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
style Exit fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
#### Agentic Evaluator Optimizer
|
||||
|
||||
Create AI agents that can generate and optimize solutions through iterative feedback.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[In] --> Generator[LLM Call Generator]
|
||||
Generator -->|SOLUTION| Evaluator[LLM Call Evaluator] -->|ACCEPTED| Out[Out]
|
||||
Evaluator -->|REJECTED + FEEDBACK| Generator
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style Generator fill:#2E8B57,color:#fff
|
||||
style Evaluator fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
#### Repetitive Agents
|
||||
|
||||
Create AI agents that can efficiently handle repetitive tasks through automated loops.
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
In[Input] --> LoopAgent[("Looping Agent")]
|
||||
LoopAgent --> Task[Task]
|
||||
Task --> |Next iteration| LoopAgent
|
||||
Task --> |Done| Out[Output]
|
||||
|
||||
style In fill:#8B0000,color:#fff
|
||||
style LoopAgent fill:#2E8B57,color:#fff,shape:circle
|
||||
style Task fill:#2E8B57,color:#fff
|
||||
style Out fill:#8B0000,color:#fff
|
||||
```
|
||||
|
||||
## Adding Models
|
||||
|
||||
<div align="center">
|
||||
<a href="https://docs.praison.ai/models">
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/%F0%9F%93%9A_Models-Visit_docs.praison.ai-blue?style=for-the-badge&logo=bookstack&logoColor=white" alt="Models" />
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
## Ollama Integration
|
||||
```bash
|
||||
export OPENAI_BASE_URL=http://localhost:11434/v1
|
||||
```
|
||||
|
||||
## 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
|
||||
|
||||
## Agents Playbook
|
||||
|
||||
### Simple Playbook Example
|
||||
|
||||
Create `agents.yaml` file and add the code below:
|
||||
|
||||
```yaml
|
||||
framework: praisonai
|
||||
topic: Artificial Intelligence
|
||||
roles:
|
||||
screenwriter:
|
||||
backstory: "Skilled in crafting scripts with engaging dialogue about {topic}."
|
||||
goal: Create scripts from concepts.
|
||||
role: Screenwriter
|
||||
tasks:
|
||||
scriptwriting_task:
|
||||
description: "Develop scripts with compelling characters and dialogue about {topic}."
|
||||
expected_output: "Complete script ready for production."
|
||||
```
|
||||
|
||||
*To run the playbook:*
|
||||
```bash
|
||||
praisonai agents.yaml
|
||||
```
|
||||
|
||||
## Use 100+ Models
|
||||
|
||||
- https://docs.praison.ai/models/
|
||||
<div align="center">
|
||||
<a href="https://docs.praison.ai">
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/📚_Documentation-Visit_docs.praison.ai-blue?style=for-the-badge&logo=bookstack&logoColor=white" alt="Documentation" />
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
|
||||
## Custom Tools
|
||||
|
||||
### Using `@tool` Decorator
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent, tool
|
||||
|
||||
@tool
|
||||
def search(query: str) -> str:
|
||||
"""Search the web for information."""
|
||||
return f"Results for: {query}"
|
||||
|
||||
@tool
|
||||
def calculate(expression: str) -> float:
|
||||
"""Evaluate a math expression."""
|
||||
return eval(expression)
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a helpful assistant",
|
||||
tools=[search, calculate]
|
||||
)
|
||||
agent.start("Search for AI news and calculate 15*4")
|
||||
```
|
||||
|
||||
### Using `BaseTool` Class
|
||||
|
||||
```python
|
||||
from praisonaiagents import Agent, BaseTool
|
||||
|
||||
class WeatherTool(BaseTool):
|
||||
name = "weather"
|
||||
description = "Get current weather for a location"
|
||||
|
||||
def run(self, location: str) -> str:
|
||||
return f"Weather in {location}: 72°F, Sunny"
|
||||
|
||||
agent = Agent(
|
||||
instructions="You are a weather assistant",
|
||||
tools=[WeatherTool()]
|
||||
)
|
||||
agent.start("What's the weather in Paris?")
|
||||
```
|
||||
|
||||
### Creating a Tool Package (pip installable)
|
||||
|
||||
```toml
|
||||
# pyproject.toml
|
||||
[project]
|
||||
name = "my-praisonai-tools"
|
||||
version = "1.0.0"
|
||||
dependencies = ["praisonaiagents"]
|
||||
|
||||
[project.entry-points."praisonaiagents.tools"]
|
||||
my_tool = "my_package:MyTool"
|
||||
```
|
||||
|
||||
```python
|
||||
# my_package/__init__.py
|
||||
from praisonaiagents import BaseTool
|
||||
|
||||
class MyTool(BaseTool):
|
||||
name = "my_tool"
|
||||
description = "My custom tool"
|
||||
|
||||
def run(self, param: str) -> str:
|
||||
return f"Result: {param}"
|
||||
```
|
||||
|
||||
After `pip install`, tools are auto-discovered:
|
||||
```python
|
||||
agent = Agent(tools=["my_tool"]) # Works automatically!
|
||||
```
|
||||
|
||||
|
||||
## Prompt Expansion
|
||||
|
||||
Expand short prompts into detailed, actionable prompts:
|
||||
|
||||
### CLI Usage
|
||||
|
||||
```bash
|
||||
# Expand a short prompt into detailed prompt
|
||||
praisonai "write a movie script in 3 lines" --expand-prompt
|
||||
|
||||
# With verbose output
|
||||
praisonai "blog about AI" --expand-prompt -v
|
||||
|
||||
# With tools for context gathering
|
||||
praisonai "latest AI trends" --expand-prompt --expand-tools tools.py
|
||||
|
||||
# Combine with query rewrite
|
||||
praisonai "AI news" --query-rewrite --expand-prompt
|
||||
```
|
||||
|
||||
### Programmatic Usage
|
||||
|
||||
```python
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy
|
||||
|
||||
# Basic usage
|
||||
agent = PromptExpanderAgent()
|
||||
result = agent.expand("write a movie script in 3 lines")
|
||||
print(result.expanded_prompt)
|
||||
|
||||
# With specific strategy
|
||||
result = agent.expand("blog about AI", strategy=ExpandStrategy.DETAILED)
|
||||
|
||||
# Available strategies: BASIC, DETAILED, STRUCTURED, CREATIVE, AUTO
|
||||
```
|
||||
|
||||
**Key Difference:**
|
||||
- `--query-rewrite`: Optimizes queries for **search/retrieval** (RAG)
|
||||
- `--expand-prompt`: Expands prompts for **detailed task execution**
|
||||
|
||||
|
||||
## 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_version.py 2.2.96 --agents 0.0.167
|
||||
```
|
||||
|
||||
## Release
|
||||
|
||||
```bash
|
||||
# From project root (runs uv lock, build, git tag, gh release)
|
||||
python src/praisonai/scripts/release.py
|
||||
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.
|
||||
|
||||
## Other Features
|
||||
|
||||
- 🔄 Use CrewAI or AG2 (Formerly AutoGen) Framework
|
||||
- 💻 Chat with ENTIRE Codebase
|
||||
- 🎨 Interactive UIs
|
||||
- 📄 YAML-based Configuration
|
||||
- 🛠️ Custom Tool Integration
|
||||
- 🔍 Internet Search Capability (using Crawl4AI and Tavily)
|
||||
- 🖼️ Vision Language Model (VLM) Support
|
||||
- 🎙️ Real-time Voice Interaction
|
||||
|
||||
## Video Tutorials
|
||||
|
||||
| Topic | Video |
|
||||
|-------|--------|
|
||||
| AI Agents with Self Reflection | [](https://www.youtube.com/watch?v=vLXobEN2Vc8) |
|
||||
| Reasoning Data Generating Agent | [](https://www.youtube.com/watch?v=fUT332Y2zA8) |
|
||||
| AI Agents with Reasoning | [](https://www.youtube.com/watch?v=KNDVWGN3TpM) |
|
||||
| Multimodal AI Agents | [](https://www.youtube.com/watch?v=hjAWmUT1qqY) |
|
||||
| AI Agents Workflow | [](https://www.youtube.com/watch?v=yWTH44QPl2A) |
|
||||
| Async AI Agents | [](https://www.youtube.com/watch?v=VhVQfgo00LE) |
|
||||
| Mini AI Agents | [](https://www.youtube.com/watch?v=OkvYp5aAGSg) |
|
||||
| AI Agents with Memory | [](https://www.youtube.com/watch?v=1hVfVxvPnnQ) |
|
||||
| Repetitive Agents | [](https://www.youtube.com/watch?v=dAYGxsjDOPg) |
|
||||
| Introduction | [](https://www.youtube.com/watch?v=Fn1lQjC0GO0) |
|
||||
| Tools Overview | [](https://www.youtube.com/watch?v=XaQRgRpV7jo) |
|
||||
| Custom Tools | [](https://www.youtube.com/watch?v=JSU2Rndh06c) |
|
||||
| Firecrawl Integration | [](https://www.youtube.com/watch?v=UoqUDcLcOYo) |
|
||||
| User Interface | [](https://www.youtube.com/watch?v=tg-ZjNl3OCg) |
|
||||
| Crawl4AI Integration | [](https://www.youtube.com/watch?v=KAvuVUh0XU8) |
|
||||
| Chat Interface | [](https://www.youtube.com/watch?v=sw3uDqn2h1Y) |
|
||||
| Code Interface | [](https://www.youtube.com/watch?v=_5jQayO-MQY) |
|
||||
| Mem0 Integration | [](https://www.youtube.com/watch?v=KIGSgRxf1cY) |
|
||||
| Training | [](https://www.youtube.com/watch?v=aLawE8kwCrI) |
|
||||
| Realtime Voice Interface | [](https://www.youtube.com/watch?v=frRHfevTCSw) |
|
||||
| Call Interface | [](https://www.youtube.com/watch?v=m1cwrUG2iAk) |
|
||||
| Reasoning Extract Agents | [](https://www.youtube.com/watch?v=2PPamsADjJA) |
|
||||
|
||||
@@ -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.2.98.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.98.tar.gz | shasum -a 256`.split.first
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.0.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.0.tar.gz | shasum -a 256`.split.first
|
||||
license "MIT"
|
||||
|
||||
depends_on "[email protected]"
|
||||
|
||||
@@ -579,6 +579,8 @@ class PraisonAI:
|
||||
parser.add_argument("--research", action="store_true", help="Run deep research on a topic")
|
||||
parser.add_argument("--query-rewrite", action="store_true", help="Rewrite query for better results (works with any command)")
|
||||
parser.add_argument("--rewrite-tools", type=str, help="Tools for query rewriter (e.g., 'internet_search' or path to tools.py)")
|
||||
parser.add_argument("--expand-prompt", action="store_true", help="Expand short prompt into detailed prompt (works with any command)")
|
||||
parser.add_argument("--expand-tools", type=str, help="Tools for prompt expander (e.g., 'internet_search' or path to tools.py)")
|
||||
parser.add_argument("--tools", "-t", type=str, help="Path to tools.py file for research agent")
|
||||
parser.add_argument("--save", "-s", action="store_true", help="Save research output to file (output/research/)")
|
||||
parser.add_argument("--verbose", "-v", action="store_true", help="Enable verbose output for research")
|
||||
@@ -839,12 +841,106 @@ class PraisonAI:
|
||||
verbose = getattr(self.args, 'verbose', False)
|
||||
return self._rewrite_query(query, rewrite_tools, verbose)
|
||||
|
||||
def _expand_prompt(self, prompt: str, expand_tools: str = None, verbose: bool = False) -> str:
|
||||
"""
|
||||
Expand prompt using PromptExpanderAgent.
|
||||
|
||||
Args:
|
||||
prompt: The prompt to expand
|
||||
expand_tools: Tool names (comma-separated) or path to tools.py
|
||||
verbose: Enable verbose output
|
||||
|
||||
Returns:
|
||||
Expanded prompt or original if expansion fails
|
||||
"""
|
||||
try:
|
||||
from praisonaiagents import PromptExpanderAgent, ExpandStrategy
|
||||
from rich import print
|
||||
|
||||
print("[bold cyan]Expanding prompt for detailed execution...[/bold cyan]")
|
||||
|
||||
# Load expand tools if specified
|
||||
expand_tools_list = []
|
||||
if expand_tools:
|
||||
if os.path.isfile(expand_tools):
|
||||
# Load from file
|
||||
try:
|
||||
import inspect
|
||||
import importlib.util
|
||||
spec = importlib.util.spec_from_file_location("expand_tools_module", expand_tools)
|
||||
if spec and spec.loader:
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
for name, obj in inspect.getmembers(module):
|
||||
if inspect.isfunction(obj) and not name.startswith('_'):
|
||||
expand_tools_list.append(obj)
|
||||
if expand_tools_list:
|
||||
print(f"[cyan]Loaded {len(expand_tools_list)} tools for prompt expander[/cyan]")
|
||||
except Exception as e:
|
||||
print(f"[yellow]Warning: Failed to load expand tools: {e}[/yellow]")
|
||||
else:
|
||||
# Treat as comma-separated tool names
|
||||
try:
|
||||
from praisonaiagents.tools import TOOL_MAPPINGS
|
||||
import praisonaiagents.tools as tools_module
|
||||
|
||||
tool_names = [t.strip() for t in expand_tools.split(',')]
|
||||
for tool_name in tool_names:
|
||||
if tool_name in TOOL_MAPPINGS:
|
||||
try:
|
||||
tool = getattr(tools_module, tool_name)
|
||||
expand_tools_list.append(tool)
|
||||
except Exception as e:
|
||||
print(f"[yellow]Warning: Failed to load expand tool '{tool_name}': {e}[/yellow]")
|
||||
else:
|
||||
print(f"[yellow]Warning: Unknown expand tool '{tool_name}'[/yellow]")
|
||||
if expand_tools_list:
|
||||
print(f"[cyan]Using expand tools: {', '.join(tool_names)}[/cyan]")
|
||||
except ImportError:
|
||||
print("[yellow]Warning: Could not import tools module[/yellow]")
|
||||
|
||||
expander = PromptExpanderAgent(
|
||||
model="gpt-4o-mini",
|
||||
verbose=verbose,
|
||||
tools=expand_tools_list if expand_tools_list else None
|
||||
)
|
||||
result = expander.expand(prompt, strategy=ExpandStrategy.AUTO)
|
||||
expanded = result.expanded_prompt
|
||||
|
||||
print(f"[cyan]Original:[/cyan] {prompt}")
|
||||
print(f"[cyan]Expanded:[/cyan] {expanded}")
|
||||
|
||||
return expanded
|
||||
|
||||
except ImportError:
|
||||
from rich import print
|
||||
print("[yellow]Warning: PromptExpanderAgent not available, using original prompt[/yellow]")
|
||||
return prompt
|
||||
except Exception as e:
|
||||
from rich import print
|
||||
print(f"[yellow]Warning: Prompt expansion failed ({e}), using original prompt[/yellow]")
|
||||
return prompt
|
||||
|
||||
def _expand_prompt_if_enabled(self, prompt: str) -> str:
|
||||
"""
|
||||
Expand prompt using PromptExpanderAgent if --expand-prompt is enabled.
|
||||
Returns the expanded prompt or original if expansion is disabled/fails.
|
||||
"""
|
||||
if not hasattr(self, 'args') or not getattr(self.args, 'expand_prompt', False):
|
||||
return prompt
|
||||
|
||||
expand_tools = getattr(self.args, 'expand_tools', None)
|
||||
verbose = getattr(self.args, 'verbose', False)
|
||||
return self._expand_prompt(prompt, expand_tools, verbose)
|
||||
|
||||
def handle_direct_prompt(self, prompt):
|
||||
"""
|
||||
Handle direct prompt by creating a single agent and running it.
|
||||
"""
|
||||
# Apply query rewriting if enabled
|
||||
prompt = self._rewrite_query_if_enabled(prompt)
|
||||
# Apply prompt expansion if enabled
|
||||
prompt = self._expand_prompt_if_enabled(prompt)
|
||||
|
||||
if PRAISONAI_AVAILABLE:
|
||||
agent_config = {
|
||||
|
||||
@@ -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.2.98 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.3.0 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.2.98"
|
||||
__version__ = "2.3.0"
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"rich>=13.7",
|
||||
"markdown>=3.5",
|
||||
"pyparsing>=3.0.0",
|
||||
"praisonaiagents>=0.0.169",
|
||||
"praisonaiagents>=0.0.170",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Combined bump version and release script for PraisonAI package.
|
||||
|
||||
Usage:
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99
|
||||
|
||||
Or with optional praisonaiagents version:
|
||||
python src/praisonai/scripts/bump_and_release.py 2.2.99 --agents 0.0.169
|
||||
|
||||
This script:
|
||||
1. Bumps version in all required files
|
||||
2. Copies root README.md to package dir
|
||||
3. Runs uv lock & uv build
|
||||
4. Commits changes
|
||||
5. Creates git tag
|
||||
6. Pushes to GitHub
|
||||
7. Creates GitHub release (latest)
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def get_project_root() -> Path:
|
||||
"""Get the project root directory (praisonai-package)."""
|
||||
return Path(__file__).parent.parent.parent.parent
|
||||
|
||||
|
||||
def get_praisonai_dir() -> Path:
|
||||
"""Get the praisonai package directory."""
|
||||
return get_project_root() / "src/praisonai"
|
||||
|
||||
|
||||
def run(cmd: list[str], cwd: Path | None = None, check: bool = True) -> subprocess.CompletedProcess:
|
||||
"""Run a command and print it."""
|
||||
print(f"$ {' '.join(cmd)}")
|
||||
result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True)
|
||||
if result.stdout:
|
||||
print(result.stdout)
|
||||
if result.stderr:
|
||||
print(result.stderr, file=sys.stderr)
|
||||
if check and result.returncode != 0:
|
||||
print(f"❌ Command failed with exit code {result.returncode}")
|
||||
sys.exit(1)
|
||||
return result
|
||||
|
||||
|
||||
def update_file(filepath: Path, patterns: list[tuple[str, str]], root: Path) -> bool:
|
||||
"""Update a file with the given regex patterns and replacements."""
|
||||
if not filepath.exists():
|
||||
print(f" ⚠️ File not found: {filepath}")
|
||||
return False
|
||||
|
||||
content = filepath.read_text()
|
||||
original = content
|
||||
|
||||
for pattern, replacement in patterns:
|
||||
content = re.sub(pattern, replacement, content)
|
||||
|
||||
if content != original:
|
||||
filepath.write_text(content)
|
||||
print(f" ✅ Updated: {filepath.relative_to(root)}")
|
||||
return True
|
||||
else:
|
||||
print(f" ⏭️ No changes: {filepath.relative_to(root)}")
|
||||
return False
|
||||
|
||||
|
||||
def bump_version(new_version: str, agents_version: str | None = None):
|
||||
"""Bump version in all required files."""
|
||||
root = get_project_root()
|
||||
praisonai_dir = get_praisonai_dir()
|
||||
|
||||
print(f"\n🚀 Bumping PraisonAI version to {new_version}\n")
|
||||
|
||||
# 1. Update version.py (single source of truth)
|
||||
print("📦 Python Package:")
|
||||
update_file(
|
||||
praisonai_dir / "praisonai/version.py",
|
||||
[(r'__version__ = "[^"]+"', f'__version__ = "{new_version}"')],
|
||||
root
|
||||
)
|
||||
|
||||
# 2. Update deploy.py (Dockerfile template)
|
||||
print("\n🐳 Deploy Script:")
|
||||
update_file(
|
||||
praisonai_dir / "praisonai/deploy.py",
|
||||
[(r'praisonai==[0-9.]+', f'praisonai=={new_version}')],
|
||||
root
|
||||
)
|
||||
|
||||
# 3. Update Dockerfiles
|
||||
print("\n🐳 Dockerfiles:")
|
||||
dockerfiles = [
|
||||
"docker/Dockerfile",
|
||||
"docker/Dockerfile.chat",
|
||||
"docker/Dockerfile.dev",
|
||||
"docker/Dockerfile.ui",
|
||||
]
|
||||
for dockerfile in dockerfiles:
|
||||
update_file(
|
||||
root / dockerfile,
|
||||
[(r'"praisonai>=[0-9.]+"', f'"praisonai>={new_version}"')],
|
||||
root
|
||||
)
|
||||
|
||||
# 4. Update Homebrew formula
|
||||
print("\n🍺 Homebrew Formula:")
|
||||
update_file(
|
||||
praisonai_dir / "praisonai.rb",
|
||||
[(r'v[0-9]+\.[0-9]+\.[0-9]+', f'v{new_version}')],
|
||||
root
|
||||
)
|
||||
|
||||
# 5. Update praisonaiagents dependency if specified
|
||||
if agents_version:
|
||||
print(f"\n📦 Updating praisonaiagents dependency to {agents_version}:")
|
||||
update_file(
|
||||
praisonai_dir / "pyproject.toml",
|
||||
[(r'praisonaiagents>=[0-9.]+', f'praisonaiagents>={agents_version}')],
|
||||
root
|
||||
)
|
||||
|
||||
print("\n✨ Version bump complete!")
|
||||
|
||||
|
||||
def release(version: str):
|
||||
"""Run the release process."""
|
||||
root = get_project_root()
|
||||
praisonai_dir = get_praisonai_dir()
|
||||
tag = f"v{version}"
|
||||
|
||||
print(f"\n🚀 Releasing PraisonAI {tag}\n")
|
||||
|
||||
# 1. Copy root README.md to package dir for PyPI
|
||||
print("📄 Copying README.md...")
|
||||
root_readme = root / "README.md"
|
||||
pkg_readme = praisonai_dir / "README.md"
|
||||
if root_readme.exists():
|
||||
shutil.copy(root_readme, pkg_readme)
|
||||
print(f" ✅ Copied {root_readme} -> {pkg_readme}")
|
||||
|
||||
# 2. uv lock
|
||||
print("\n📦 Running uv lock...")
|
||||
run(["uv", "lock"], cwd=praisonai_dir)
|
||||
|
||||
# 3. uv build
|
||||
print("\n🔨 Running uv build...")
|
||||
run(["uv", "build"], cwd=praisonai_dir)
|
||||
|
||||
# 4. Git add and commit
|
||||
print("\n📝 Committing changes...")
|
||||
run(["git", "add", "-A"], cwd=root)
|
||||
run(["git", "commit", "-m", f"Release {tag}"], cwd=root, check=False)
|
||||
|
||||
# 5. Create git tag
|
||||
print(f"\n🏷️ Creating tag {tag}...")
|
||||
run(["git", "tag", "-f", tag], cwd=root)
|
||||
|
||||
# 6. Push to GitHub
|
||||
print("\n⬆️ Pushing to GitHub...")
|
||||
run(["git", "push"], cwd=root)
|
||||
run(["git", "push", "--tags", "-f"], cwd=root)
|
||||
|
||||
# 7. Create GitHub release
|
||||
print(f"\n🎉 Creating GitHub release {tag}...")
|
||||
run([
|
||||
"gh", "release", "create", tag,
|
||||
"--title", f"PraisonAI {tag}",
|
||||
"--notes", f"Release {tag}",
|
||||
"--latest"
|
||||
], cwd=root)
|
||||
|
||||
print(f"\n✅ Released PraisonAI {tag}")
|
||||
print("\nNext step:")
|
||||
print(" cd src/praisonai && uv publish")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Bump version and release PraisonAI package"
|
||||
)
|
||||
parser.add_argument(
|
||||
"version",
|
||||
help="New version number (e.g., 2.2.99)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--agents", "-a",
|
||||
help="Optional: Update praisonaiagents dependency version (e.g., 0.0.169)",
|
||||
default=None
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Validate version format
|
||||
if not re.match(r'^\d+\.\d+\.\d+$', args.version):
|
||||
print(f"❌ Invalid version format: {args.version}")
|
||||
print(" Expected format: X.Y.Z (e.g., 2.2.99)")
|
||||
sys.exit(1)
|
||||
|
||||
if args.agents and not re.match(r'^\d+\.\d+\.\d+$', args.agents):
|
||||
print(f"❌ Invalid agents version format: {args.agents}")
|
||||
print(" Expected format: X.Y.Z (e.g., 0.0.169)")
|
||||
sys.exit(1)
|
||||
|
||||
# Run bump version
|
||||
bump_version(args.version, args.agents)
|
||||
|
||||
# Run release
|
||||
release(args.version)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -62,11 +62,20 @@ def main():
|
||||
|
||||
print(f"\n🚀 Releasing PraisonAI {tag}\n")
|
||||
|
||||
# 1. uv lock
|
||||
print("📦 Running uv lock...")
|
||||
# 1. Copy root README.md to package dir for PyPI
|
||||
print("📄 Copying README.md...")
|
||||
root_readme = root / "README.md"
|
||||
pkg_readme = praisonai_dir / "README.md"
|
||||
if root_readme.exists():
|
||||
import shutil
|
||||
shutil.copy(root_readme, pkg_readme)
|
||||
print(f" ✅ Copied {root_readme} -> {pkg_readme}")
|
||||
|
||||
# 2. uv lock
|
||||
print("\n📦 Running uv lock...")
|
||||
run(["uv", "lock"], cwd=praisonai_dir)
|
||||
|
||||
# 2. uv build
|
||||
# 3. uv build
|
||||
print("\n🔨 Running uv build...")
|
||||
run(["uv", "build"], cwd=praisonai_dir)
|
||||
|
||||
|
||||
@@ -72,10 +72,10 @@ roles:
|
||||
except Exception as e:
|
||||
pytest.fail(f"CrewAI real test failed: {e}")
|
||||
|
||||
@pytest.mark.skipif(not os.getenv("OPENAI_API_KEY"), reason="OPENAI_API_KEY not set")
|
||||
def test_crewai_environment_check(self):
|
||||
"""Verify CrewAI environment is properly configured"""
|
||||
# Check API key is available
|
||||
assert os.getenv("OPENAI_API_KEY"), "OPENAI_API_KEY required for real tests"
|
||||
# Check API key is available (already checked by skipif)
|
||||
|
||||
# Check CrewAI can be imported
|
||||
try:
|
||||
|
||||
@@ -59,8 +59,11 @@ class TestPraisonAICommandLine(unittest.TestCase):
|
||||
# In CI, it should be set by the workflow
|
||||
print("Warning: OPENAI_API_KEY not found in CLI test environment. API calls might fail if not mocked.")
|
||||
|
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True, env=env)
|
||||
return result.stdout + result.stderr
|
||||
try:
|
||||
result = subprocess.run(command, shell=True, capture_output=True, text=True, env=env, timeout=120)
|
||||
return result.stdout + result.stderr
|
||||
except subprocess.TimeoutExpired:
|
||||
return "TIMEOUT: Command exceeded 120 seconds"
|
||||
|
||||
@pytest.mark.real
|
||||
def test_praisonai_command(self):
|
||||
|
||||
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.0.169" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.0.170" },
|
||||
{ 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.0.169"
|
||||
version = "0.0.170"
|
||||
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/4e/0c/94820a01306d53ce99f2b8d0f424566e74fe883aaca0e1a5c81f8364dddf/praisonaiagents-0.0.169.tar.gz", hash = "sha256:0774dd1fd8a5066e4ab2d612153d07bf1ac275105c10396730a9b542d4060ff3", size = 319791, upload-time = "2025-12-15T10:09:24.429Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/20/35/a45a1f616bc0a661b9816b7cd7afdace803bacdc91cbacc6080960e7d1fb/praisonaiagents-0.0.170.tar.gz", hash = "sha256:b0114255b82184657af074f55316238c1ddcb7105aab6b23817d0315830d2082", size = 324448, upload-time = "2025-12-15T11:12:25.135Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d6/f4/9342ee5340a2fdf1e3b9b8c1d10d9f2962c4fc577925c41b81db7b9beea1/praisonaiagents-0.0.169-py3-none-any.whl", hash = "sha256:ea491a2097224544f7ebb19b19daaabfc814c22bf92fb2adeb8e7d3f33d1233c", size = 333017, upload-time = "2025-12-15T10:09:23.016Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b1/cc/6a6c8da467f400ab51201bd0008948326483ccae89d370a32b46e66f87ae/praisonaiagents-0.0.170-py3-none-any.whl", hash = "sha256:5930c1a9a4d1b6ae5f03e471351701b5c0af21a8312fdb2c9b756a89e88cdd4d", size = 337210, upload-time = "2025-12-15T11:12:23.518Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in new issue
Block a user