Skip to content

Commit de1734c

Browse files
committed
Release v3.10.24
1 parent f0e91b1 commit de1734c

File tree

12 files changed

+991
-64
lines changed

12 files changed

+991
-64
lines changed

docker/Dockerfile.chat

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
1616
# Install Python packages (using latest versions)
1717
RUN pip install --no-cache-dir \
1818
praisonai_tools \
19-
"praisonai>=3.10.23" \
19+
"praisonai>=3.10.24" \
2020
"praisonai[chat]" \
2121
"embedchain[github,youtube]"
2222

docker/Dockerfile.dev

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
2020
# Install Python packages (using latest versions)
2121
RUN pip install --no-cache-dir \
2222
praisonai_tools \
23-
"praisonai>=3.10.23" \
23+
"praisonai>=3.10.24" \
2424
"praisonai[ui]" \
2525
"praisonai[chat]" \
2626
"praisonai[realtime]" \

docker/Dockerfile.ui

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
1616
# Install Python packages (using latest versions)
1717
RUN pip install --no-cache-dir \
1818
praisonai_tools \
19-
"praisonai>=3.10.23" \
19+
"praisonai>=3.10.24" \
2020
"praisonai[ui]" \
2121
"praisonai[crewai]"
2222

src/praisonai/praisonai.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ class Praisonai < Formula
33

44
desc "AI tools for various AI applications"
55
homepage "https://github.com/MervinPraison/PraisonAI"
6-
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.10.23.tar.gz"
7-
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.10.23.tar.gz | shasum -a 256`.split.first
6+
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.10.24.tar.gz"
7+
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.10.24.tar.gz | shasum -a 256`.split.first
88
license "MIT"
99

1010
depends_on "python@3.11"

src/praisonai/praisonai/agents_generator.py

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -848,22 +848,56 @@ def _run_crewai(self, config, topic, tools_dict):
848848
def _run_praisonai(self, config, topic, tools_dict):
849849
"""
850850
Run agents using the PraisonAI framework.
851+
852+
Tool resolution order:
853+
1. Local tools.py (backward compat, custom tools)
854+
2. YAML tools: field resolved via ToolResolver
855+
3. Built-in tools from praisonaiagents.tools
851856
"""
852857
agents = {}
853858
tasks = []
854859
tasks_dict = {}
855860

856-
# Load tools once at the beginning
861+
# Import tool resolver (lazy import to avoid circular deps)
862+
from praisonai.tool_resolver import ToolResolver
863+
tool_resolver = ToolResolver()
864+
865+
# Load tools from local tools.py (backward compat)
857866
tools_list = self.load_tools_from_tools_py()
858-
self.logger.debug(f"Loaded tools: {tools_list}")
867+
self.logger.debug(f"Loaded tools from tools.py: {tools_list}")
859868

860869
# Create agents from config
861870
for role, details in config['roles'].items():
862871
role_filled = safe_format(details['role'], topic=topic)
863872
goal_filled = safe_format(details['goal'], topic=topic)
864873
backstory_filled = safe_format(details['backstory'], topic=topic)
865874

866-
# Pass all loaded tools to the agent
875+
# Resolve tools for this agent from YAML tools: field
876+
yaml_tool_names = details.get('tools', [])
877+
agent_tools = list(tools_list) # Start with local tools.py tools
878+
879+
if yaml_tool_names:
880+
# Resolve each tool name from YAML
881+
for tool_name in yaml_tool_names:
882+
if not tool_name or not isinstance(tool_name, str):
883+
continue
884+
tool_name = tool_name.strip()
885+
886+
# Check if already in tools_list (from tools.py)
887+
already_loaded = any(
888+
getattr(t, '__name__', None) == tool_name or
889+
getattr(t, 'name', None) == tool_name
890+
for t in agent_tools
891+
)
892+
893+
if not already_loaded:
894+
resolved_tool = tool_resolver.resolve(tool_name)
895+
if resolved_tool is not None:
896+
agent_tools.append(resolved_tool)
897+
self.logger.debug(f"Resolved tool '{tool_name}' for agent {role}")
898+
else:
899+
self.logger.warning(f"Tool '{tool_name}' not found for agent {role}")
900+
867901
# Get LLM from config or environment
868902
llm_config = details.get('llm', {})
869903
llm_model = llm_config.get("model") if isinstance(llm_config, dict) else llm_config
@@ -875,7 +909,7 @@ def _run_praisonai(self, config, topic, tools_dict):
875909
goal=goal_filled,
876910
backstory=backstory_filled,
877911
instructions=details.get('instructions'),
878-
tools=tools_list, # Pass the entire tools list to the agent
912+
tools=agent_tools, # Pass resolved tools to the agent
879913
allow_delegation=details.get('allow_delegation', False),
880914
llm=llm_model,
881915
reflection=details.get('reflection', False),
@@ -911,7 +945,7 @@ def _run_praisonai(self, config, topic, tools_dict):
911945
description=description_filled,
912946
expected_output=expected_output_filled,
913947
agent=agent,
914-
tools=tools_list, # Pass the same tools list to the task
948+
tools=agent_tools, # Pass resolved tools to the task
915949
async_execution=task_details.get('async_execution', False),
916950
context=[],
917951
config=task_details.get('config', {}),
Lines changed: 166 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,204 @@
11
"""
22
Tools command group for PraisonAI CLI.
33
4-
Provides tool management commands.
4+
Provides tool management commands including:
5+
- List available tools from all sources
6+
- Validate YAML tool references
7+
- Show tool information
58
"""
69

10+
from typing import Optional
11+
712
import typer
13+
from rich.console import Console
14+
from rich.table import Table
815

9-
app = typer.Typer(help="Tool management")
16+
app = typer.Typer(help="Tool management and discovery")
17+
console = Console()
1018

1119

1220
@app.command("list")
1321
def tools_list(
22+
source: Optional[str] = typer.Option(
23+
None, "--source", "-s",
24+
help="Filter by source: builtin, local, external"
25+
),
1426
verbose: bool = typer.Option(False, "--verbose", "-v", help="Show detailed info"),
1527
):
16-
"""List available tools."""
17-
from praisonai.cli.main import PraisonAI
18-
import sys
28+
"""List all available tools that can be used in YAML files.
29+
30+
Shows tools from:
31+
- Built-in tools (praisonaiagents.tools)
32+
- Local tools.py (if present)
33+
- External tools (praisonai-tools package)
34+
"""
35+
from praisonai.tool_resolver import ToolResolver
36+
37+
resolver = ToolResolver()
38+
available = resolver.list_available()
39+
40+
if not available:
41+
console.print("[yellow]No tools available.[/yellow]")
42+
return
43+
44+
# Categorize tools
45+
builtin_tools = {}
46+
local_tools = {}
47+
external_tools = {}
48+
49+
for name, desc in available.items():
50+
if "Local tool" in desc:
51+
local_tools[name] = desc
52+
elif "praisonai-tools" in desc:
53+
external_tools[name] = desc
54+
else:
55+
builtin_tools[name] = desc
1956

20-
argv = ['tools', 'list']
57+
# Filter by source if specified
58+
if source == "builtin":
59+
available = builtin_tools
60+
elif source == "local":
61+
available = local_tools
62+
elif source == "external":
63+
available = external_tools
64+
65+
# Create table
66+
table = Table(title="Available Tools", show_header=True, header_style="bold cyan")
67+
table.add_column("Tool Name", style="green")
68+
table.add_column("Source", style="blue")
2169
if verbose:
22-
argv.append('--verbose')
70+
table.add_column("Description", style="dim")
71+
72+
# Add rows
73+
for name in sorted(available.keys()):
74+
desc = available[name]
75+
if "Local tool" in desc:
76+
src = "local"
77+
elif "praisonai-tools" in desc:
78+
src = "external"
79+
else:
80+
src = "builtin"
81+
82+
if verbose:
83+
table.add_row(name, src, desc[:60] + "..." if len(desc) > 60 else desc)
84+
else:
85+
table.add_row(name, src)
86+
87+
console.print(table)
88+
console.print(f"\n[dim]Total: {len(available)} tools[/dim]")
89+
90+
if not source:
91+
console.print(f"[dim] Built-in: {len(builtin_tools)} | Local: {len(local_tools)} | External: {len(external_tools)}[/dim]")
92+
93+
94+
@app.command("validate")
95+
def tools_validate(
96+
yaml_file: str = typer.Argument("agents.yaml", help="YAML file to validate"),
97+
):
98+
"""Validate that all tools in a YAML file can be resolved.
99+
100+
Checks that every tool name in the YAML can be found in:
101+
- Local tools.py
102+
- Built-in tools (praisonaiagents.tools)
103+
- External tools (praisonai-tools)
104+
"""
105+
import yaml
106+
from pathlib import Path
107+
from praisonai.tool_resolver import ToolResolver
23108

24-
original_argv = sys.argv
25-
sys.argv = ['praisonai'] + argv
109+
yaml_path = Path(yaml_file)
110+
if not yaml_path.exists():
111+
console.print(f"[red]Error: File not found: {yaml_file}[/red]")
112+
raise typer.Exit(1)
26113

27114
try:
28-
praison = PraisonAI()
29-
praison.main()
30-
except SystemExit:
31-
pass
32-
finally:
33-
sys.argv = original_argv
115+
with open(yaml_path) as f:
116+
config = yaml.safe_load(f)
117+
except Exception as e:
118+
console.print(f"[red]Error parsing YAML: {e}[/red]")
119+
raise typer.Exit(1)
120+
121+
resolver = ToolResolver()
122+
missing = resolver.validate_yaml_tools(config)
123+
124+
if not missing:
125+
console.print(f"[green]✓ All tools in {yaml_file} are valid![/green]")
126+
127+
# Show which tools were found
128+
roles = config.get('roles', config.get('agents', {}))
129+
all_tools = set()
130+
for role_config in roles.values():
131+
if isinstance(role_config, dict):
132+
all_tools.update(role_config.get('tools', []))
133+
134+
if all_tools:
135+
console.print(f"[dim]Tools found: {', '.join(sorted(all_tools))}[/dim]")
136+
else:
137+
console.print(f"[red]✗ Missing tools in {yaml_file}:[/red]")
138+
for tool in missing:
139+
console.print(f" [red]• {tool}[/red]")
140+
141+
console.print("\n[yellow]Hint: Run 'praisonai tools list' to see available tools.[/yellow]")
142+
raise typer.Exit(1)
34143

35144

36145
@app.command("info")
37146
def tools_info(
38147
name: str = typer.Argument(..., help="Tool name"),
39148
):
40-
"""Show tool information."""
41-
from praisonai.cli.main import PraisonAI
42-
import sys
149+
"""Show detailed information about a tool."""
150+
from praisonai.tool_resolver import ToolResolver
43151

44-
argv = ['tools', 'info', name]
152+
resolver = ToolResolver()
153+
tool = resolver.resolve(name)
45154

46-
original_argv = sys.argv
47-
sys.argv = ['praisonai'] + argv
155+
if tool is None:
156+
console.print(f"[red]Tool '{name}' not found.[/red]")
157+
console.print("[yellow]Hint: Run 'praisonai tools list' to see available tools.[/yellow]")
158+
raise typer.Exit(1)
48159

160+
console.print(f"\n[bold green]{name}[/bold green]")
161+
console.print("-" * 40)
162+
163+
# Get docstring
164+
doc = getattr(tool, '__doc__', None)
165+
if doc:
166+
console.print(f"[dim]{doc}[/dim]")
167+
168+
# Get signature if possible
169+
import inspect
49170
try:
50-
praison = PraisonAI()
51-
praison.main()
52-
except SystemExit:
171+
sig = inspect.signature(tool)
172+
console.print(f"\n[cyan]Signature:[/cyan] {name}{sig}")
173+
except (ValueError, TypeError):
53174
pass
54-
finally:
55-
sys.argv = original_argv
175+
176+
# Show source
177+
available = resolver.list_available()
178+
if name in available:
179+
desc = available[name]
180+
if "Local tool" in desc:
181+
console.print("\n[blue]Source:[/blue] Local tools.py")
182+
elif "praisonai-tools" in desc:
183+
console.print("\n[blue]Source:[/blue] praisonai-tools package")
184+
else:
185+
console.print("\n[blue]Source:[/blue] praisonaiagents.tools (built-in)")
56186

57187

58188
@app.command("test")
59189
def tools_test(
60190
name: str = typer.Argument(..., help="Tool name to test"),
61191
):
62-
"""Test a tool."""
63-
from praisonai.cli.main import PraisonAI
64-
import sys
192+
"""Test a tool with a simple invocation."""
193+
from praisonai.tool_resolver import ToolResolver
65194

66-
argv = ['tools', 'test', name]
195+
resolver = ToolResolver()
196+
tool = resolver.resolve(name)
67197

68-
original_argv = sys.argv
69-
sys.argv = ['praisonai'] + argv
198+
if tool is None:
199+
console.print(f"[red]Tool '{name}' not found.[/red]")
200+
raise typer.Exit(1)
70201

71-
try:
72-
praison = PraisonAI()
73-
praison.main()
74-
except SystemExit:
75-
pass
76-
finally:
77-
sys.argv = original_argv
202+
console.print(f"[green]✓ Tool '{name}' resolved successfully![/green]")
203+
console.print(f"[dim]Type: {type(tool).__name__}[/dim]")
204+
console.print(f"[dim]Callable: {callable(tool)}[/dim]")

src/praisonai/praisonai/deploy.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def create_dockerfile(self):
5757
file.write("FROM python:3.11-slim\n")
5858
file.write("WORKDIR /app\n")
5959
file.write("COPY . .\n")
60-
file.write("RUN pip install flask praisonai==3.10.23 gunicorn markdown\n")
60+
file.write("RUN pip install flask praisonai==3.10.24 gunicorn markdown\n")
6161
file.write("EXPOSE 8080\n")
6262
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
6363

0 commit comments

Comments
 (0)