mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-02 13:48:09 +00:00
- 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.
209 lines
7.4 KiB
Python
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
|