mirror of
https://github.com/MervinPraison/PraisonAI.git
synced 2026-11-07 04:35:58 -10:00
fix: migrate invalid AgentTeam kwargs to output=/execution= (fixes #3654)
AgentTeam.__init__ accepts neither verbose= nor **kwargs, so passing verbose= raised TypeError at construction. Verbosity is controlled via the consolidated output= param (silent/minimal/verbose) and iteration limits via execution=. Migrated all internal CLI, template generators, and examples off the removed verbose=/max_iter=/session= kwargs, and added an AST lint guard that scans examples + internal CLI for invalid AgentTeam kwargs to prevent regression. Co-authored-by: Dhivya-Bharathy <[email protected]>
This commit is contained in:
1 parent
631299846c
commit
92c2b6dd64
12 files changed
+128
-17
No files matched your search
@@ -86,7 +86,7 @@ def method_1_simple_loop():
|
||||
agents=[loop_agent],
|
||||
tasks=[loop_task],
|
||||
process="workflow",
|
||||
max_iter=10 # Maximum iterations to prevent infinite loops
|
||||
execution="balanced" # max_iter=10 preset to prevent infinite loops
|
||||
)
|
||||
|
||||
print(f"🚀 Starting loop processing of {csv_file}")
|
||||
|
||||
@@ -64,7 +64,7 @@ def main():
|
||||
agents=[url_agent],
|
||||
tasks=[url_task],
|
||||
process="workflow",
|
||||
max_iter=10 # Adjust based on how many URLs you have
|
||||
execution="balanced" # max_iter=10 preset; adjust for more URLs
|
||||
)
|
||||
|
||||
print(f"🚀 Processing URLs from {csv_file}")
|
||||
|
||||
@@ -141,7 +141,7 @@ class DynamicAgentSystem:
|
||||
agents=agents,
|
||||
tasks=tasks,
|
||||
process=process,
|
||||
verbose=inputs['depth'] == 'detailed'
|
||||
output="verbose" if inputs['depth'] == 'detailed' else "silent"
|
||||
)
|
||||
|
||||
result = praison_agents.start()
|
||||
|
||||
@@ -66,7 +66,7 @@ research_task1 = Task(
|
||||
agents_session1 = AgentTeam(
|
||||
agents=[research_agent],
|
||||
tasks=[research_task1],
|
||||
session=session1, output="verbose"
|
||||
output="verbose"
|
||||
)
|
||||
|
||||
# Execute first session
|
||||
@@ -105,7 +105,7 @@ analysis_task = Task(
|
||||
agents_session2 = AgentTeam(
|
||||
agents=[analysis_agent],
|
||||
tasks=[analysis_task],
|
||||
session=session2, output="verbose"
|
||||
output="verbose"
|
||||
)
|
||||
|
||||
print("Starting analysis session with previous context...")
|
||||
@@ -155,7 +155,7 @@ synthesis_task = Task(
|
||||
agents_recovery = AgentTeam(
|
||||
agents=[synthesis_agent],
|
||||
tasks=[synthesis_task],
|
||||
session=recovery_session, output="verbose"
|
||||
output="verbose"
|
||||
)
|
||||
|
||||
print("Starting recovery session with full context from previous sessions...")
|
||||
@@ -205,7 +205,7 @@ review_task = Task(
|
||||
agents_reviewer = AgentTeam(
|
||||
agents=[review_agent],
|
||||
tasks=[review_task],
|
||||
session=reviewer_session, output="verbose"
|
||||
output="verbose"
|
||||
)
|
||||
|
||||
print("Starting peer review session by different user...")
|
||||
|
||||
@@ -23,5 +23,5 @@ agent = Agent(
|
||||
)
|
||||
|
||||
# Run the agent
|
||||
result = AgentTeam(agents=[agent], verbose=10).start()
|
||||
result = AgentTeam(agents=[agent], output="verbose").start()
|
||||
print(result)
|
||||
@@ -0,0 +1,107 @@
|
||||
"""
|
||||
AST lint guard preventing AgentTeam API drift (invalid ``verbose=`` kwarg).
|
||||
|
||||
``AgentTeam.__init__`` does not accept ``verbose``; verbosity is controlled via
|
||||
the consolidated ``output`` parameter (e.g. ``output="silent"`` /
|
||||
``output="verbose"``). This test AST-parses example and internal-CLI source and
|
||||
fails if any ``AgentTeam(...)`` call passes a keyword not present in the real
|
||||
constructor signature, so broken copy-paste snippets can't be reintroduced.
|
||||
|
||||
The valid kwarg set is read from the ``AgentTeam.__init__`` source via AST
|
||||
(not ``inspect.signature``) because ``AgentTeam`` subclasses ``typing.Protocol``,
|
||||
which makes ``inspect`` report a generic ``(*args, **kwargs)`` signature.
|
||||
"""
|
||||
import ast
|
||||
from pathlib import Path
|
||||
|
||||
# Repo root: .../src/praisonai-agents/tests/unit/agents/<this file>
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[5]
|
||||
_AGENTS_SRC = (
|
||||
_REPO_ROOT
|
||||
/ "src"
|
||||
/ "praisonai-agents"
|
||||
/ "praisonaiagents"
|
||||
/ "agents"
|
||||
/ "agents.py"
|
||||
)
|
||||
|
||||
# Directories whose ``AgentTeam(...)`` calls must use valid kwargs only.
|
||||
_SCAN_DIRS = [
|
||||
_REPO_ROOT / "examples",
|
||||
_REPO_ROOT / "src" / "praisonai" / "praisonai",
|
||||
_REPO_ROOT / "src" / "praisonai-code" / "praisonai_code",
|
||||
]
|
||||
|
||||
|
||||
def _valid_agentteam_kwargs():
|
||||
"""Extract real ``AgentTeam.__init__`` parameter names by parsing source."""
|
||||
tree = ast.parse(_AGENTS_SRC.read_text(encoding="utf-8"))
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.ClassDef) and node.name == "AgentTeam":
|
||||
for item in node.body:
|
||||
if isinstance(item, ast.FunctionDef) and item.name == "__init__":
|
||||
args = item.args
|
||||
names = {a.arg for a in args.args}
|
||||
names.update(a.arg for a in args.kwonlyargs)
|
||||
has_var_keyword = args.kwarg is not None
|
||||
names.discard("self")
|
||||
return names, has_var_keyword
|
||||
raise AssertionError("AgentTeam.__init__ not found in agents.py")
|
||||
|
||||
|
||||
def _is_agentteam_call(node: ast.Call) -> bool:
|
||||
func = node.func
|
||||
if isinstance(func, ast.Name):
|
||||
return func.id == "AgentTeam"
|
||||
if isinstance(func, ast.Attribute):
|
||||
return func.attr == "AgentTeam"
|
||||
return False
|
||||
|
||||
|
||||
def _iter_python_files():
|
||||
for base in _SCAN_DIRS:
|
||||
if not base.exists():
|
||||
continue
|
||||
for path in base.rglob("*.py"):
|
||||
yield path
|
||||
|
||||
|
||||
def _find_bad_kwargs(path: Path, valid_kwargs):
|
||||
try:
|
||||
tree = ast.parse(path.read_text(encoding="utf-8"))
|
||||
except (SyntaxError, UnicodeDecodeError):
|
||||
return []
|
||||
bad = []
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Call) and _is_agentteam_call(node):
|
||||
for kw in node.keywords:
|
||||
if kw.arg is None:
|
||||
continue # **kwargs spread — can't statically validate
|
||||
if kw.arg not in valid_kwargs:
|
||||
bad.append((kw.arg, node.lineno))
|
||||
return bad
|
||||
|
||||
|
||||
def test_real_agentteam_init_has_no_verbose_or_var_keyword():
|
||||
valid_kwargs, has_var_keyword = _valid_agentteam_kwargs()
|
||||
assert "verbose" not in valid_kwargs
|
||||
assert "output" in valid_kwargs
|
||||
assert not has_var_keyword, (
|
||||
"AgentTeam.__init__ gained **kwargs; the lint guard below would be "
|
||||
"silently bypassed. Update this test if that change is intentional."
|
||||
)
|
||||
|
||||
|
||||
def test_no_invalid_agentteam_kwargs_in_examples_and_cli():
|
||||
valid_kwargs, _ = _valid_agentteam_kwargs()
|
||||
|
||||
failures = []
|
||||
for path in _iter_python_files():
|
||||
for arg, lineno in _find_bad_kwargs(path, valid_kwargs):
|
||||
rel = path.relative_to(_REPO_ROOT)
|
||||
failures.append(f"{rel}:{lineno} — invalid AgentTeam kwarg '{arg}'")
|
||||
|
||||
assert not failures, (
|
||||
"AgentTeam called with invalid kwargs (use output='silent'/'verbose' "
|
||||
"instead of verbose=):\n" + "\n".join(sorted(failures))
|
||||
)
|
||||
@@ -219,7 +219,7 @@ class MultiAgentHandler:
|
||||
instructions=config.get('instructions', ''),
|
||||
tools=tools if tools else None,
|
||||
llm=config.get('llm') or llm or os.environ.get('OPENAI_MODEL_NAME', 'gpt-4o-mini'),
|
||||
verbose=self.verbose
|
||||
output="verbose" if self.verbose else "silent"
|
||||
)
|
||||
agents.append(agent)
|
||||
|
||||
@@ -243,7 +243,7 @@ class MultiAgentHandler:
|
||||
agents=agents,
|
||||
tasks=tasks,
|
||||
process=process,
|
||||
verbose=self.verbose
|
||||
output="verbose" if self.verbose else "silent"
|
||||
)
|
||||
|
||||
result = praison_agents.start()
|
||||
|
||||
@@ -797,7 +797,7 @@ t_import = time.perf_counter()
|
||||
|
||||
agent = Agent(name="Helper", instructions="You are helpful", llm="{self.DEFAULT_MODEL}", output="minimal")
|
||||
task = Task(name="respond", description="{prompt}", expected_output="response", agent=agent)
|
||||
agents = AgentTeam(agents=[agent], tasks=[task], verbose=0)
|
||||
agents = AgentTeam(agents=[agent], tasks=[task], output="silent")
|
||||
t_init = time.perf_counter()
|
||||
|
||||
result = agents.start()
|
||||
@@ -845,7 +845,7 @@ agent1 = Agent(name="Analyzer", instructions="Analyze the request", llm="{self.D
|
||||
agent2 = Agent(name="Responder", instructions="Provide a response", llm="{self.DEFAULT_MODEL}", output="minimal")
|
||||
task1 = Task(name="analyze", description="Analyze: {prompt}", expected_output="analysis", agent=agent1)
|
||||
task2 = Task(name="respond", description="Respond based on analysis", expected_output="response", agent=agent2)
|
||||
agents = AgentTeam(agents=[agent1, agent2], tasks=[task1, task2], verbose=0)
|
||||
agents = AgentTeam(agents=[agent1, agent2], tasks=[task1, task2], output="silent")
|
||||
t_init = time.perf_counter()
|
||||
|
||||
result = agents.start()
|
||||
|
||||
@@ -2312,13 +2312,13 @@ class PraisonAI:
|
||||
agents=agents_list,
|
||||
tasks=tasks_list,
|
||||
process=process_type,
|
||||
verbose=1 if verbose else 0
|
||||
output="minimal" if verbose else "silent"
|
||||
)
|
||||
else:
|
||||
praison = AgentTeam(
|
||||
agents=agents_list,
|
||||
process=process_type,
|
||||
verbose=1 if verbose else 0
|
||||
output="minimal" if verbose else "silent"
|
||||
)
|
||||
praison.launch(port=port, host=host)
|
||||
|
||||
|
||||
@@ -637,11 +637,15 @@ class TemplatesHandler:
|
||||
tasks.append(task)
|
||||
|
||||
# Run
|
||||
_verbose_level = workflow_config.get("verbose", 1)
|
||||
_output_mode = "silent" if not _verbose_level else (
|
||||
"minimal" if _verbose_level == 1 else "verbose"
|
||||
)
|
||||
praison_agents = AgentTeam(
|
||||
agents=agents,
|
||||
tasks=tasks,
|
||||
process=workflow_config.get("process", "sequential"),
|
||||
verbose=workflow_config.get("verbose", 1)
|
||||
output=_output_mode
|
||||
)
|
||||
praison_agents.start()
|
||||
elif "steps" in workflow_config:
|
||||
|
||||
@@ -1210,7 +1210,7 @@ def handle_research_command(self, query: str, model: str = None, verbose: bool =
|
||||
)
|
||||
|
||||
print("[cyan]Gathering information with tools...[/cyan]")
|
||||
agents = AgentTeam(agents=[research_assistant], tasks=[gather_task], verbose=0)
|
||||
agents = AgentTeam(agents=[research_assistant], tasks=[gather_task], output="silent")
|
||||
tool_results = agents.start()
|
||||
|
||||
# Enhance query with tool results
|
||||
|
||||
@@ -567,7 +567,7 @@ if __name__ == "__main__":
|
||||
agents = AgentTeam(
|
||||
agents=[primary_agent, secondary_agent],
|
||||
tasks=[primary_task, verification_task],
|
||||
verbose=True,
|
||||
output="verbose",
|
||||
)
|
||||
|
||||
# Execute
|
||||
|
||||
Reference in new issue
Block a user