mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-10 11:41:32 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88da2bdd7b | ||
|
|
b84e6c2460 | ||
|
|
0434b23ada |
No files matched your search
@@ -1058,6 +1058,20 @@ praisonai "Complex analysis task" --planning --planning-reasoning
|
||||
praisonai "Task" --planning --auto-approve-plan
|
||||
```
|
||||
|
||||
### Tool Approval CLI:
|
||||
```bash
|
||||
# Auto-approve ALL tool executions (use with caution!)
|
||||
praisonai "run ls command" --trust
|
||||
|
||||
# Auto-approve tools up to a risk level (prompt for higher)
|
||||
# Levels: low, medium, high, critical
|
||||
praisonai "write to file" --approve-level high # Prompts for critical tools only
|
||||
praisonai "task" --approve-level medium # Prompts for high and critical
|
||||
|
||||
# Default behavior (no flags): prompts for all dangerous tools
|
||||
praisonai "run shell command" # Will prompt for approval
|
||||
```
|
||||
|
||||
### Memory CLI:
|
||||
```bash
|
||||
# Enable memory for agent (persists across sessions)
|
||||
@@ -2399,6 +2413,7 @@ PraisonAI provides zero-dependency persistent memory for agents. For detailed ex
|
||||
- 🤝 **Agent Handoffs** - Transfer context between specialised agents
|
||||
- 🛡️ **Guardrails** - Input/output validation and safety checks
|
||||
- ✅ **Human Approval** - Require human confirmation for critical actions
|
||||
- 🔐 **Tool Approval CLI** - `--trust` (auto-approve all) and `--approve-level` (risk-based approval)
|
||||
- 💬 **Sessions Management** - Isolated conversation contexts
|
||||
- 🔄 **Stateful Agents** - Maintain state across interactions
|
||||
|
||||
|
||||
@@ -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.57" \
|
||||
"praisonai>=2.3.60" \
|
||||
"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.57" \
|
||||
"praisonai>=2.3.60" \
|
||||
"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.57" \
|
||||
"praisonai>=2.3.60" \
|
||||
"praisonai[ui]" \
|
||||
"praisonai[crewai]"
|
||||
|
||||
|
||||
@@ -1538,13 +1538,13 @@ Your Goal: {self.goal}"""
|
||||
logging.debug(f"{self.name} executing tool {function_name} with arguments: {arguments}")
|
||||
|
||||
# Check if approval is required for this tool
|
||||
from ..approval import is_approval_required, console_approval_callback, get_risk_level, mark_approved, ApprovalDecision
|
||||
from ..approval import is_approval_required, console_approval_callback, get_risk_level, mark_approved, ApprovalDecision, get_approval_callback
|
||||
if is_approval_required(function_name):
|
||||
risk_level = get_risk_level(function_name)
|
||||
logging.info(f"Tool {function_name} requires approval (risk level: {risk_level})")
|
||||
logging.debug(f"Tool {function_name} requires approval (risk level: {risk_level})")
|
||||
|
||||
# Use global approval callback or default console callback
|
||||
callback = approval_callback or console_approval_callback
|
||||
callback = get_approval_callback() or console_approval_callback
|
||||
|
||||
try:
|
||||
decision = callback(function_name, arguments, risk_level)
|
||||
|
||||
@@ -43,6 +43,15 @@ def set_approval_callback(callback_fn: Callable):
|
||||
global approval_callback
|
||||
approval_callback = callback_fn
|
||||
|
||||
def get_approval_callback() -> Optional[Callable]:
|
||||
"""Get the current approval callback function.
|
||||
|
||||
Returns the custom callback if set, otherwise None.
|
||||
This should be used instead of directly accessing approval_callback
|
||||
to ensure the latest callback is always used.
|
||||
"""
|
||||
return approval_callback
|
||||
|
||||
def mark_approved(tool_name: str):
|
||||
"""Mark a tool as approved in the current context."""
|
||||
approved = _approved_context.get(set())
|
||||
@@ -90,7 +99,7 @@ def require_approval(risk_level: RiskLevel = "high"):
|
||||
except Exception as e:
|
||||
# Fallback to sync approval if async fails
|
||||
logging.warning(f"Async approval failed, using sync fallback: {e}")
|
||||
callback = approval_callback or console_approval_callback
|
||||
callback = get_approval_callback() or console_approval_callback
|
||||
decision = callback(tool_name, kwargs, risk_level)
|
||||
|
||||
if not decision.approved:
|
||||
@@ -198,7 +207,7 @@ async def request_approval(function_name: str, arguments: Dict[str, Any]) -> App
|
||||
risk_level = TOOL_RISK_LEVELS.get(function_name, "medium")
|
||||
|
||||
# Use custom callback if set, otherwise use console callback
|
||||
callback = approval_callback or console_approval_callback
|
||||
callback = get_approval_callback() or console_approval_callback
|
||||
|
||||
try:
|
||||
# Handle async callbacks
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "praisonaiagents"
|
||||
version = "0.1.18"
|
||||
version = "0.1.20"
|
||||
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.10"
|
||||
|
||||
Generated
+1
-1
@@ -2422,7 +2422,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "praisonaiagents"
|
||||
version = "0.1.18"
|
||||
version = "0.1.20"
|
||||
source = { editable = "." }
|
||||
dependencies = [
|
||||
{ name = "aiohttp" },
|
||||
|
||||
@@ -1058,6 +1058,20 @@ praisonai "Complex analysis task" --planning --planning-reasoning
|
||||
praisonai "Task" --planning --auto-approve-plan
|
||||
```
|
||||
|
||||
### Tool Approval CLI:
|
||||
```bash
|
||||
# Auto-approve ALL tool executions (use with caution!)
|
||||
praisonai "run ls command" --trust
|
||||
|
||||
# Auto-approve tools up to a risk level (prompt for higher)
|
||||
# Levels: low, medium, high, critical
|
||||
praisonai "write to file" --approve-level high # Prompts for critical tools only
|
||||
praisonai "task" --approve-level medium # Prompts for high and critical
|
||||
|
||||
# Default behavior (no flags): prompts for all dangerous tools
|
||||
praisonai "run shell command" # Will prompt for approval
|
||||
```
|
||||
|
||||
### Memory CLI:
|
||||
```bash
|
||||
# Enable memory for agent (persists across sessions)
|
||||
@@ -2399,6 +2413,7 @@ PraisonAI provides zero-dependency persistent memory for agents. For detailed ex
|
||||
- 🤝 **Agent Handoffs** - Transfer context between specialised agents
|
||||
- 🛡️ **Guardrails** - Input/output validation and safety checks
|
||||
- ✅ **Human Approval** - Require human confirmation for critical actions
|
||||
- 🔐 **Tool Approval CLI** - `--trust` (auto-approve all) and `--approve-level` (risk-based approval)
|
||||
- 💬 **Sessions Management** - Isolated conversation contexts
|
||||
- 🔄 **Stateful Agents** - Maintain state across interactions
|
||||
|
||||
|
||||
@@ -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.57.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.57.tar.gz | shasum -a 256`.split.first
|
||||
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.60.tar.gz"
|
||||
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.3.60.tar.gz | shasum -a 256`.split.first
|
||||
license "MIT"
|
||||
|
||||
depends_on "[email protected]"
|
||||
|
||||
@@ -693,6 +693,11 @@ class PraisonAI:
|
||||
# Autonomy Mode - control AI action approval
|
||||
parser.add_argument("--autonomy", type=str, choices=["suggest", "auto_edit", "full_auto"], help="Set autonomy mode for AI actions")
|
||||
|
||||
# Tool Approval - control tool execution approval
|
||||
parser.add_argument("--trust", action="store_true", help="Auto-approve all tool executions (skip approval prompts)")
|
||||
parser.add_argument("--approve-level", type=str, choices=["low", "medium", "high", "critical"],
|
||||
help="Auto-approve tools up to this risk level (e.g., --approve-level high approves low/medium/high but prompts for critical)")
|
||||
|
||||
# Sandbox Execution - secure command execution
|
||||
parser.add_argument("--sandbox", type=str, choices=["off", "basic", "strict"], help="Enable sandboxed command execution")
|
||||
|
||||
@@ -2995,6 +3000,35 @@ Provide ONLY the commit message, no explanations."""
|
||||
|
||||
# ===== NEW CLI FEATURES INTEGRATION =====
|
||||
|
||||
# Tool Approval - Auto-approve tools based on --trust or --approve-level
|
||||
if getattr(self.args, 'trust', False) or getattr(self.args, 'approve_level', None):
|
||||
from praisonaiagents.approval import set_approval_callback, ApprovalDecision
|
||||
|
||||
if getattr(self.args, 'trust', False):
|
||||
# Auto-approve all tools
|
||||
def auto_approve_all(function_name, arguments, risk_level):
|
||||
return ApprovalDecision(approved=True, reason="Auto-approved via --trust flag")
|
||||
set_approval_callback(auto_approve_all)
|
||||
print("[bold yellow]⚠️ Trust mode enabled - all tool executions will be auto-approved[/bold yellow]")
|
||||
elif getattr(self.args, 'approve_level', None):
|
||||
# Auto-approve up to specified risk level
|
||||
max_level = self.args.approve_level
|
||||
risk_order = {"low": 1, "medium": 2, "high": 3, "critical": 4}
|
||||
max_level_value = risk_order.get(max_level, 3)
|
||||
|
||||
def level_based_approve(function_name, arguments, risk_level):
|
||||
tool_level_value = risk_order.get(risk_level, 4)
|
||||
if tool_level_value <= max_level_value:
|
||||
return ApprovalDecision(approved=True, reason=f"Auto-approved (level {risk_level} <= {max_level})")
|
||||
else:
|
||||
# Signal to pause Live display before showing approval prompt
|
||||
# This is handled by the approval_pending flag in status_info
|
||||
from praisonaiagents.approval import console_approval_callback
|
||||
return console_approval_callback(function_name, arguments, risk_level)
|
||||
|
||||
set_approval_callback(level_based_approve)
|
||||
print(f"[bold cyan]Auto-approve enabled for tools up to '{max_level}' risk level[/bold cyan]")
|
||||
|
||||
# Router - Smart model selection (must be before agent creation)
|
||||
if getattr(self.args, 'router', False):
|
||||
from .features.router import RouterHandler
|
||||
@@ -3264,7 +3298,9 @@ Now, {final_instruction.lower()}:"""
|
||||
'result': None,
|
||||
'error': None,
|
||||
'start_time': time.time(),
|
||||
'available_tools': tool_names
|
||||
'available_tools': tool_names,
|
||||
'approval_pending': False, # Flag to pause Live display during approval
|
||||
'live_instance': None # Reference to Live instance for stopping
|
||||
}
|
||||
|
||||
def tool_call_callback(message):
|
||||
@@ -3327,6 +3363,53 @@ Now, {final_instruction.lower()}:"""
|
||||
finally:
|
||||
status_info['done'] = True
|
||||
|
||||
# Set up approval callback that stops Live display before prompting
|
||||
# Only if not using --trust (which auto-approves everything)
|
||||
if not getattr(self.args, 'trust', False):
|
||||
from praisonaiagents.approval import set_approval_callback, ApprovalDecision
|
||||
from rich.prompt import Confirm
|
||||
from rich.panel import Panel
|
||||
|
||||
def cli_approval_with_live_pause(function_name, arguments, risk_level):
|
||||
"""Approval callback that stops Live display before prompting."""
|
||||
# Signal to stop Live display
|
||||
status_info['approval_pending'] = True
|
||||
|
||||
# Wait a moment for Live to stop
|
||||
time.sleep(0.2)
|
||||
|
||||
# Now show the approval prompt
|
||||
risk_colors = {"critical": "bold red", "high": "red", "medium": "yellow", "low": "blue"}
|
||||
risk_color = risk_colors.get(risk_level, "white")
|
||||
|
||||
tool_info = f"[bold]Function:[/] {function_name}\n"
|
||||
tool_info += f"[bold]Risk Level:[/] [{risk_color}]{risk_level.upper()}[/{risk_color}]\n"
|
||||
tool_info += "[bold]Arguments:[/]\n"
|
||||
for key, value in arguments.items():
|
||||
str_value = str(value)[:100] + "..." if len(str(value)) > 100 else str(value)
|
||||
tool_info += f" {key}: {str_value}\n"
|
||||
|
||||
console.print(Panel(tool_info.strip(), title="🔒 Tool Approval Required", border_style=risk_color))
|
||||
|
||||
try:
|
||||
approved = Confirm.ask(f"[{risk_color}]Execute this {risk_level} risk tool?[/{risk_color}]", default=False)
|
||||
status_info['approval_pending'] = False
|
||||
|
||||
if approved:
|
||||
console.print("[green]✅ Approved[/green]")
|
||||
return ApprovalDecision(approved=True, reason="User approved")
|
||||
else:
|
||||
console.print("[red]❌ Denied[/red]")
|
||||
return ApprovalDecision(approved=False, reason="User denied")
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
status_info['approval_pending'] = False
|
||||
console.print("\n[red]❌ Cancelled[/red]")
|
||||
return ApprovalDecision(approved=False, reason="User cancelled")
|
||||
|
||||
# Only set if not already set by --approve-level
|
||||
if not getattr(self.args, 'approve_level', None):
|
||||
set_approval_callback(cli_approval_with_live_pause)
|
||||
|
||||
# Start agent in background thread
|
||||
thread = threading.Thread(target=run_agent, daemon=True)
|
||||
thread.start()
|
||||
@@ -3334,9 +3417,26 @@ Now, {final_instruction.lower()}:"""
|
||||
# Show live status while processing
|
||||
try:
|
||||
with Live(build_status_display(), console=console, refresh_per_second=4, transient=True) as live:
|
||||
status_info['live_instance'] = live
|
||||
while not status_info['done']:
|
||||
# Check if approval is pending - stop Live to show prompt
|
||||
if status_info['approval_pending']:
|
||||
break
|
||||
live.update(build_status_display())
|
||||
time.sleep(0.1)
|
||||
|
||||
# If approval was pending, wait for it to complete then restart Live
|
||||
while status_info['approval_pending']:
|
||||
time.sleep(0.1)
|
||||
|
||||
# Continue with Live display if not done
|
||||
if not status_info['done']:
|
||||
with Live(build_status_display(), console=console, refresh_per_second=4, transient=True) as live:
|
||||
while not status_info['done']:
|
||||
if status_info['approval_pending']:
|
||||
break
|
||||
live.update(build_status_display())
|
||||
time.sleep(0.1)
|
||||
except KeyboardInterrupt:
|
||||
console.print("\n[dim]Interrupted[/dim]")
|
||||
# Unregister callback
|
||||
|
||||
@@ -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.57 gunicorn markdown\n")
|
||||
file.write("RUN pip install flask praisonai==2.3.60 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.57"
|
||||
__version__ = "2.3.60"
|
||||
@@ -12,7 +12,7 @@ dependencies = [
|
||||
"rich>=13.7",
|
||||
"markdown>=3.5",
|
||||
"pyparsing>=3.0.0",
|
||||
"praisonaiagents>=0.1.18",
|
||||
"praisonaiagents>=0.1.20",
|
||||
"python-dotenv>=0.19.0",
|
||||
"instructor>=1.3.3",
|
||||
"PyYAML>=6.0",
|
||||
|
||||
@@ -0,0 +1,221 @@
|
||||
"""
|
||||
Tests for the CLI approval system.
|
||||
|
||||
Tests:
|
||||
1. --trust flag auto-approves all tools
|
||||
2. --approve-level flag with different risk levels
|
||||
3. Approval callback integration
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add the src directory to the path
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
|
||||
|
||||
|
||||
class TestApprovalFlags:
|
||||
"""Test CLI approval flags."""
|
||||
|
||||
def test_trust_flag_exists(self):
|
||||
"""Test that --trust flag is defined in argparse."""
|
||||
import argparse
|
||||
|
||||
# Check that the args object has the trust attribute after parsing
|
||||
# Parse with --help to avoid actually running anything
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--trust", action="store_true")
|
||||
parser.add_argument("--approve-level", type=str, choices=["low", "medium", "high", "critical"])
|
||||
|
||||
# Test that the arguments can be parsed
|
||||
args = parser.parse_args(['--trust'])
|
||||
assert args.trust is True
|
||||
|
||||
args = parser.parse_args([])
|
||||
assert args.trust is False
|
||||
|
||||
def test_approve_level_flag_exists(self):
|
||||
"""Test that --approve-level flag is defined in argparse."""
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--approve-level", type=str, choices=["low", "medium", "high", "critical"])
|
||||
|
||||
# Test that the arguments can be parsed
|
||||
args = parser.parse_args(['--approve-level', 'high'])
|
||||
assert args.approve_level == 'high'
|
||||
|
||||
args = parser.parse_args(['--approve-level', 'critical'])
|
||||
assert args.approve_level == 'critical'
|
||||
|
||||
def test_trust_flag_in_praisonai_parser(self):
|
||||
"""Test that --trust flag is in PraisonAI's actual parser."""
|
||||
from praisonai.cli.main import PraisonAI
|
||||
|
||||
# Create instance - parse_args is called in __init__
|
||||
praison = PraisonAI()
|
||||
|
||||
# Call parse_args to get the args
|
||||
args = praison.parse_args()
|
||||
|
||||
# The args should have trust attribute after parse_args
|
||||
assert hasattr(args, 'trust'), "PraisonAI.args should have 'trust' attribute"
|
||||
# Cleanup
|
||||
del praison
|
||||
|
||||
def test_approve_level_flag_in_praisonai_parser(self):
|
||||
"""Test that --approve-level flag is in PraisonAI's actual parser."""
|
||||
from praisonai.cli.main import PraisonAI
|
||||
|
||||
# Create instance - parse_args is called in __init__
|
||||
praison = PraisonAI()
|
||||
|
||||
# Call parse_args to get the args
|
||||
args = praison.parse_args()
|
||||
|
||||
# The args should have approve_level attribute after parse_args
|
||||
assert hasattr(args, 'approve_level'), "PraisonAI.args should have 'approve_level' attribute"
|
||||
|
||||
|
||||
class TestApprovalCallback:
|
||||
"""Test approval callback system."""
|
||||
|
||||
def test_set_approval_callback(self):
|
||||
"""Test that we can set a custom approval callback."""
|
||||
from praisonaiagents.approval import set_approval_callback, ApprovalDecision
|
||||
|
||||
callback_called = []
|
||||
|
||||
def custom_callback(function_name, arguments, risk_level):
|
||||
callback_called.append((function_name, arguments, risk_level))
|
||||
return ApprovalDecision(approved=True, reason="Test approved")
|
||||
|
||||
set_approval_callback(custom_callback)
|
||||
|
||||
# Verify callback was set
|
||||
from praisonaiagents.approval import approval_callback
|
||||
assert approval_callback == custom_callback
|
||||
|
||||
# Reset callback
|
||||
set_approval_callback(None)
|
||||
|
||||
def test_auto_approve_callback(self):
|
||||
"""Test auto-approve callback returns approved decision."""
|
||||
from praisonaiagents.approval import ApprovalDecision
|
||||
|
||||
def auto_approve_callback(function_name, arguments, risk_level):
|
||||
return ApprovalDecision(approved=True, reason="Auto-approved")
|
||||
|
||||
decision = auto_approve_callback("execute_command", {"command": "ls"}, "critical")
|
||||
assert decision.approved is True
|
||||
assert decision.reason == "Auto-approved"
|
||||
|
||||
def test_level_based_callback(self):
|
||||
"""Test level-based approval callback."""
|
||||
from praisonaiagents.approval import ApprovalDecision
|
||||
|
||||
def level_based_callback(function_name, arguments, risk_level, max_level="high"):
|
||||
"""Approve based on risk level threshold."""
|
||||
levels = {"low": 1, "medium": 2, "high": 3, "critical": 4}
|
||||
tool_level = levels.get(risk_level, 4)
|
||||
max_allowed = levels.get(max_level, 3)
|
||||
|
||||
if tool_level <= max_allowed:
|
||||
return ApprovalDecision(approved=True, reason=f"Auto-approved (level {risk_level} <= {max_level})")
|
||||
else:
|
||||
return ApprovalDecision(approved=False, reason=f"Denied (level {risk_level} > {max_level})")
|
||||
|
||||
# Test with max_level="high"
|
||||
# Should approve low, medium, high but deny critical
|
||||
assert level_based_callback("test", {}, "low", "high").approved is True
|
||||
assert level_based_callback("test", {}, "medium", "high").approved is True
|
||||
assert level_based_callback("test", {}, "high", "high").approved is True
|
||||
assert level_based_callback("test", {}, "critical", "high").approved is False
|
||||
|
||||
# Test with max_level="critical"
|
||||
# Should approve everything
|
||||
assert level_based_callback("test", {}, "critical", "critical").approved is True
|
||||
|
||||
|
||||
class TestApprovalRequirements:
|
||||
"""Test approval requirement configuration."""
|
||||
|
||||
def test_default_dangerous_tools(self):
|
||||
"""Test that default dangerous tools are configured."""
|
||||
from praisonaiagents.approval import APPROVAL_REQUIRED_TOOLS, TOOL_RISK_LEVELS
|
||||
|
||||
# Check critical tools
|
||||
assert "execute_command" in APPROVAL_REQUIRED_TOOLS
|
||||
assert TOOL_RISK_LEVELS.get("execute_command") == "critical"
|
||||
|
||||
# Check high risk tools
|
||||
assert "write_file" in APPROVAL_REQUIRED_TOOLS
|
||||
assert TOOL_RISK_LEVELS.get("write_file") == "high"
|
||||
|
||||
def test_remove_approval_requirement(self):
|
||||
"""Test removing approval requirement."""
|
||||
from praisonaiagents.approval import (
|
||||
add_approval_requirement,
|
||||
remove_approval_requirement,
|
||||
is_approval_required,
|
||||
APPROVAL_REQUIRED_TOOLS
|
||||
)
|
||||
|
||||
# Add a test tool
|
||||
add_approval_requirement("test_tool", "medium")
|
||||
assert is_approval_required("test_tool") is True
|
||||
|
||||
# Remove it
|
||||
remove_approval_requirement("test_tool")
|
||||
assert is_approval_required("test_tool") is False
|
||||
|
||||
def test_is_approval_required(self):
|
||||
"""Test checking if approval is required."""
|
||||
from praisonaiagents.approval import is_approval_required
|
||||
|
||||
# execute_command should require approval
|
||||
assert is_approval_required("execute_command") is True
|
||||
|
||||
# read_file should not require approval
|
||||
assert is_approval_required("read_file") is False
|
||||
|
||||
# list_files should not require approval
|
||||
assert is_approval_required("list_files") is False
|
||||
|
||||
|
||||
class TestApprovalDecision:
|
||||
"""Test ApprovalDecision class."""
|
||||
|
||||
def test_approval_decision_approved(self):
|
||||
"""Test approved decision."""
|
||||
from praisonaiagents.approval import ApprovalDecision
|
||||
|
||||
decision = ApprovalDecision(approved=True, reason="User approved")
|
||||
assert decision.approved is True
|
||||
assert decision.reason == "User approved"
|
||||
assert decision.modified_args == {}
|
||||
|
||||
def test_approval_decision_denied(self):
|
||||
"""Test denied decision."""
|
||||
from praisonaiagents.approval import ApprovalDecision
|
||||
|
||||
decision = ApprovalDecision(approved=False, reason="User denied")
|
||||
assert decision.approved is False
|
||||
assert decision.reason == "User denied"
|
||||
|
||||
def test_approval_decision_with_modified_args(self):
|
||||
"""Test decision with modified arguments."""
|
||||
from praisonaiagents.approval import ApprovalDecision
|
||||
|
||||
decision = ApprovalDecision(
|
||||
approved=True,
|
||||
reason="Approved with modifications",
|
||||
modified_args={"command": "ls -la"}
|
||||
)
|
||||
assert decision.approved is True
|
||||
assert decision.modified_args == {"command": "ls -la"}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
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.18" },
|
||||
{ name = "praisonaiagents", specifier = ">=0.1.20" },
|
||||
{ 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.18"
|
||||
version = "0.1.20"
|
||||
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/2c/7c/a9d4fc48ceb7f14dff5cd494f4875e70a78962f06d3d569b0663c6282fbd/praisonaiagents-0.1.18.tar.gz", hash = "sha256:f66fae957951219d47b50b199dc190bdbc9610935e0afd293fc5ee9c1fc361ad", size = 657975, upload-time = "2025-12-19T16:41:55.53Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/33/ce/fbb183b0667eef808c766140d033cf079da46187f58782111a3e2b005360/praisonaiagents-0.1.20.tar.gz", hash = "sha256:3e7b8ffbd1e285a9e3e3412cc77ba15da45e8a38f6e49aaee86c6171a5a9fbb8", size = 658705, upload-time = "2025-12-20T03:25:39.634Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/1a/f3/cdc60237af6026ce72d9d2b92d296a5b925b5fdf01aab7d55d38e305e39f/praisonaiagents-0.1.18-py3-none-any.whl", hash = "sha256:71198b6a08d9309234b70fc58113ee95206c1709073367d9005115f03670c89c", size = 588858, upload-time = "2025-12-19T16:41:52.724Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9b/42/c87e4d7b92415fcebf08414838e25160e58184aa2ae830031029c83f139f/praisonaiagents-0.1.20-py3-none-any.whl", hash = "sha256:ca08901c9fc85e31c44beec78ac9829c3211573b11d42310976787f55f7f8202", size = 589188, upload-time = "2025-12-20T03:25:37.614Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in new issue
Block a user