Files
crewAI/lib/crewai/tests/new_agent/test_definition_parser.py
Joao Moura fe7f730546 feat: add interactive agent creation and TUI for multi-agent interaction
- Introduced a new `create_agent` command for interactive agent definition.
- Added `agent_tui.py` for a conversational TUI supporting multi-agent interactions.
- Updated CLI to support agent creation and training workflows.
- Enhanced `.gitignore` to exclude demo files and configuration artifacts.
- Implemented a benchmark runner for testing agent performance against defined cases.

This commit lays the groundwork for a more interactive and user-friendly experience in managing agents within the CrewAI framework.
2026-05-13 12:28:25 -07:00

209 lines
7.4 KiB
Python

"""Tests for the agent definition parser and JSON Schema."""
from __future__ import annotations
import json
import tempfile
from pathlib import Path
from unittest.mock import patch
import pytest
from crewai.new_agent.definition_parser import (
load_agent_from_definition,
parse_agent_definition,
strip_jsonc_comments,
)
class TestStripJsoncComments:
def test_no_comments(self):
text = '{"key": "value"}'
assert json.loads(strip_jsonc_comments(text)) == {"key": "value"}
def test_single_line_comments(self):
text = '{\n // This is a comment\n "key": "value"\n}'
result = json.loads(strip_jsonc_comments(text))
assert result == {"key": "value"}
def test_multi_line_comments(self):
text = '{\n /* This is\n a multi-line comment */\n "key": "value"\n}'
result = json.loads(strip_jsonc_comments(text))
assert result == {"key": "value"}
def test_url_in_value_not_stripped(self):
text = '{"url": "https://example.com"}'
result = json.loads(strip_jsonc_comments(text))
assert result["url"] == "https://example.com"
class TestParseAgentDefinition:
def test_parse_dict(self):
defn = {"role": "R", "goal": "g"}
result = parse_agent_definition(defn)
assert result == defn
def test_parse_json_string(self):
raw = '{"role": "R", "goal": "g"}'
result = parse_agent_definition(raw)
assert result["role"] == "R"
def test_parse_json_file(self):
with tempfile.NamedTemporaryFile(suffix=".json", mode="w", delete=False) as f:
json.dump({"role": "Writer", "goal": "Write articles"}, f)
f.flush()
result = parse_agent_definition(f.name)
assert result["role"] == "Writer"
def test_parse_jsonc_file(self):
with tempfile.NamedTemporaryFile(suffix=".jsonc", mode="w", delete=False) as f:
f.write('{\n // Agent definition\n "role": "Writer",\n "goal": "Write"\n}')
f.flush()
result = parse_agent_definition(f.name)
assert result["role"] == "Writer"
class TestLoadAgentFromDefinition:
def test_basic_definition(self):
defn = {
"role": "Senior Researcher",
"goal": "Find information",
"backstory": "Expert researcher.",
}
agent = load_agent_from_definition(defn)
assert agent.role == "Senior Researcher"
assert agent.goal == "Find information"
assert agent.backstory == "Expert researcher."
def test_minimal_definition(self):
agent = load_agent_from_definition({"role": "R", "goal": "g"})
assert agent.role == "R"
assert agent.goal == "g"
def test_settings_mapping(self):
defn = {
"role": "R",
"goal": "g",
"settings": {
"memory": False,
"reasoning": False,
"planning": False,
"narration_guard": True,
"max_history_messages": 50,
},
}
agent = load_agent_from_definition(defn)
assert agent.settings.memory_enabled is False
assert agent.settings.reasoning_enabled is False
assert agent.settings.planning_enabled is False
assert agent.settings.narration_guard is True
assert agent.settings.max_history_messages == 50
def test_verbose_and_max_iter(self):
defn = {"role": "R", "goal": "g", "verbose": True, "max_iter": 10}
agent = load_agent_from_definition(defn)
assert agent.verbose is True
assert agent.max_iter == 10
def test_llm_setting(self):
defn = {"role": "R", "goal": "g", "llm": "openai/gpt-4o"}
agent = load_agent_from_definition(defn)
assert agent.llm == "openai/gpt-4o"
def test_guardrail_llm(self):
defn = {
"role": "R",
"goal": "g",
"guardrail": {"type": "llm", "instructions": "Be safe"},
}
agent = load_agent_from_definition(defn)
assert agent.guardrail is not None
from crewai.tasks.llm_guardrail import LLMGuardrail
assert isinstance(agent.guardrail, LLMGuardrail)
assert agent.guardrail.description == "Be safe"
def test_from_json_file(self):
defn = {"role": "FileAgent", "goal": "Test file loading", "backstory": "From JSON"}
with tempfile.NamedTemporaryFile(suffix=".json", mode="w", delete=False) as f:
json.dump(defn, f)
f.flush()
agent = load_agent_from_definition(f.name)
assert agent.role == "FileAgent"
assert agent.backstory == "From JSON"
def test_coworker_amp_handle(self):
defn = {
"role": "Manager",
"goal": "Manage",
"coworkers": [{"amp": "content-writer"}],
}
agent = load_agent_from_definition(defn)
# AMP handles are passed as strings for resolution
assert "content-writer" in agent.coworkers
def test_coworker_ref_with_agents_dir(self):
with tempfile.TemporaryDirectory() as tmpdir:
agents_dir = Path(tmpdir)
writer_defn = {"role": "Writer", "goal": "Write"}
(agents_dir / "writer.json").write_text(json.dumps(writer_defn))
defn = {
"role": "Manager",
"goal": "Manage",
"coworkers": [{"ref": "writer"}],
}
agent = load_agent_from_definition(defn, agents_dir=agents_dir)
assert len(agent.coworkers) == 1
def test_circular_coworker_ref_no_crash(self):
"""Two agents referencing each other as coworkers should not crash."""
with tempfile.TemporaryDirectory() as tmpdir:
agents_dir = Path(tmpdir)
a_defn = {
"name": "agent_a",
"role": "A",
"goal": "Do A",
"coworkers": [{"ref": "agent_b"}],
}
b_defn = {
"name": "agent_b",
"role": "B",
"goal": "Do B",
"coworkers": [{"ref": "agent_a"}],
}
(agents_dir / "agent_a.json").write_text(json.dumps(a_defn))
(agents_dir / "agent_b.json").write_text(json.dumps(b_defn))
agent = load_agent_from_definition(
agents_dir / "agent_a.json", agents_dir=agents_dir
)
assert agent is not None
assert agent.role == "A"
# B should be loaded as a coworker, but B's ref to A is skipped
assert len(agent.coworkers) == 1
class TestJsonSchema:
def test_schema_is_valid_json(self):
schema_path = Path(__file__).parent.parent.parent / "src" / "crewai" / "new_agent" / "agent_schema.json"
with open(schema_path) as f:
schema = json.load(f)
assert schema["$schema"] == "https://json-schema.org/draft/2020-12/schema"
assert "role" in schema["required"]
assert "goal" in schema["required"]
def test_schema_has_key_properties(self):
schema_path = Path(__file__).parent.parent.parent / "src" / "crewai" / "new_agent" / "agent_schema.json"
with open(schema_path) as f:
schema = json.load(f)
props = schema["properties"]
assert "role" in props
assert "goal" in props
assert "backstory" in props
assert "llm" in props
assert "tools" in props
assert "coworkers" in props
assert "settings" in props
assert "guardrail" in props