mirror of
https://github.com/crewAIInc/crewAI.git
synced 2025-12-16 20:38:29 +00:00
Compare commits
1 Commits
sec_docs
...
doc_genera
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0fc50eab5 |
@@ -10,6 +10,7 @@ from .replay_from_task import replay_task_command
|
|||||||
from .reset_memories_command import reset_memories_command
|
from .reset_memories_command import reset_memories_command
|
||||||
from .test_crew import test_crew
|
from .test_crew import test_crew
|
||||||
from .train_crew import train_crew
|
from .train_crew import train_crew
|
||||||
|
from .doc_generator import generate_documentation
|
||||||
|
|
||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
@@ -146,6 +147,18 @@ def test(n_iterations: int, model: str):
|
|||||||
click.echo(f"Testing the crew for {n_iterations} iterations with model {model}")
|
click.echo(f"Testing the crew for {n_iterations} iterations with model {model}")
|
||||||
test_crew(n_iterations, model)
|
test_crew(n_iterations, model)
|
||||||
|
|
||||||
|
@crewai.command()
|
||||||
|
@click.option('--output', '-o', default='crew_documentation.md', help='Output file for the documentation')
|
||||||
|
@click.option('--format', '-f', default='markdown', help='Output format')
|
||||||
|
def generate_docs(output, format):
|
||||||
|
"""Generate documentation for the current project setup."""
|
||||||
|
try:
|
||||||
|
click.echo(f"Generating documentation in {format} format...")
|
||||||
|
generate_documentation(output, format)
|
||||||
|
click.echo(f"Documentation generated and saved to {output}")
|
||||||
|
except ValueError as e:
|
||||||
|
click.echo(f"Error: {str(e)}", err=True)
|
||||||
|
click.echo("Please ensure you are in the root directory of your CrewAI project.")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
crewai()
|
crewai()
|
||||||
|
|||||||
204
src/crewai/cli/doc_generator.py
Normal file
204
src/crewai/cli/doc_generator.py
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
def is_project_root():
|
||||||
|
"""
|
||||||
|
Check if the current directory is the root of a CrewAI project.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if in project root, False otherwise.
|
||||||
|
"""
|
||||||
|
# Check for key indicators of a CrewAI project root
|
||||||
|
indicators = ["pyproject.toml", "poetry.lock", "src"]
|
||||||
|
return all(os.path.exists(indicator) for indicator in indicators)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_documentation(output_file, format):
|
||||||
|
"""
|
||||||
|
Generate documentation for the current CrewAI project setup.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output_file (str): The path and filename where the generated documentation
|
||||||
|
will be saved.
|
||||||
|
format (str): The desired output format for the documentation.
|
||||||
|
Supported values currently 'markdown'.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
None: The function writes the generated documentation to the specified
|
||||||
|
output file and doesn't return any value.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If not in the project root or if an unsupported output format is specified.
|
||||||
|
"""
|
||||||
|
if not is_project_root():
|
||||||
|
raise ValueError(
|
||||||
|
"Not in the root of a CrewAI project."
|
||||||
|
)
|
||||||
|
|
||||||
|
# Load the current project configuration
|
||||||
|
config = load_crew_configuration()
|
||||||
|
|
||||||
|
if config is None:
|
||||||
|
logging.error("Failed to load crew configuration. Exiting.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if format == "markdown":
|
||||||
|
content = generate_markdown(config)
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Unsupported output format: {format}")
|
||||||
|
|
||||||
|
with open(output_file, "w") as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
logging.info(f"Documentation generated and saved to {output_file}")
|
||||||
|
|
||||||
|
|
||||||
|
def find_config_dir():
|
||||||
|
"""
|
||||||
|
Find the configuration directory based on the project structure.
|
||||||
|
|
||||||
|
This function attempts to locate the configuration directory for a CrewAI project
|
||||||
|
by assuming a standard project structure. It starts from the current working
|
||||||
|
directory and constructs an expected path to the config directory.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str or None: The path to the configuration directory if found, None otherwise.
|
||||||
|
|
||||||
|
The function performs the following steps:
|
||||||
|
1. Gets the current working directory.
|
||||||
|
2. Extracts the project name from the current directory path.
|
||||||
|
3. Constructs the expected config path using the project structure convention.
|
||||||
|
4. Checks if the expected config directory exists.
|
||||||
|
5. Returns the path if found, or None if not found.
|
||||||
|
|
||||||
|
Logging:
|
||||||
|
- Logs debug information about the search process.
|
||||||
|
- Logs the starting directory, the checked path, and the result of the search.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
This function assumes a specific project structure where the config
|
||||||
|
directory is located at 'src/<project_name>/config' relative to the
|
||||||
|
project root.
|
||||||
|
"""
|
||||||
|
current_dir = os.getcwd()
|
||||||
|
logging.debug(f"Starting search from: {current_dir}")
|
||||||
|
|
||||||
|
# Split the path to get the project name
|
||||||
|
path_parts = current_dir.split(os.path.sep)
|
||||||
|
project_name = path_parts[-1]
|
||||||
|
|
||||||
|
# Construct the expected config path
|
||||||
|
expected_config_path = os.path.join(current_dir, "src", project_name, "config")
|
||||||
|
|
||||||
|
logging.debug(f"Checking for config directory: {expected_config_path}")
|
||||||
|
|
||||||
|
if os.path.isdir(expected_config_path):
|
||||||
|
logging.debug(f"Found config directory: {expected_config_path}")
|
||||||
|
return expected_config_path
|
||||||
|
|
||||||
|
logging.debug("Config directory not found in the expected location")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def load_crew_configuration():
|
||||||
|
"""
|
||||||
|
Load the crew configuration from YAML files.
|
||||||
|
|
||||||
|
This function attempts to find the configuration directory and load the agents
|
||||||
|
and tasks configurations from their respective YAML files.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict or None: A dictionary containing 'agents' and 'tasks' configurations
|
||||||
|
if successful, None if there was an error.
|
||||||
|
|
||||||
|
The function performs the following steps:
|
||||||
|
1. Finds the configuration directory using find_config_dir().
|
||||||
|
2. Constructs paths to agents.yaml and tasks.yaml files.
|
||||||
|
3. Checks if both files exist.
|
||||||
|
4. Loads and parses the YAML content of both files.
|
||||||
|
5. Returns a dictionary with the parsed configurations.
|
||||||
|
|
||||||
|
Logging:
|
||||||
|
- Logs an error if the configuration directory is not found.
|
||||||
|
- Logs an error if either agents.yaml or tasks.yaml is not found.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
This function assumes that the configuration files are named 'agents.yaml'
|
||||||
|
and 'tasks.yaml' and are located in the directory returned by find_config_dir().
|
||||||
|
"""
|
||||||
|
config_dir = find_config_dir()
|
||||||
|
if not config_dir:
|
||||||
|
logging.error(
|
||||||
|
"Configuration directory not found. Make sure you're in the root of your CrewAI project."
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
agents_file = os.path.join(config_dir, "agents.yaml")
|
||||||
|
tasks_file = os.path.join(config_dir, "tasks.yaml")
|
||||||
|
|
||||||
|
if not os.path.exists(agents_file) or not os.path.exists(tasks_file):
|
||||||
|
logging.error(f"agents.yaml or tasks.yaml not found in {config_dir}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
with open(agents_file, "r") as f:
|
||||||
|
agents_config = yaml.safe_load(f)
|
||||||
|
|
||||||
|
with open(tasks_file, "r") as f:
|
||||||
|
tasks_config = yaml.safe_load(f)
|
||||||
|
|
||||||
|
return {"agents": agents_config, "tasks": tasks_config}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_markdown(config):
|
||||||
|
"""
|
||||||
|
Generate Markdown documentation for the CrewAI project configuration.
|
||||||
|
|
||||||
|
This function takes the parsed configuration dictionary and generates
|
||||||
|
a formatted Markdown string containing documentation for the project's
|
||||||
|
agents and tasks.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
config (dict): A dictionary containing the parsed configuration
|
||||||
|
with 'agents' and 'tasks' keys.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: A formatted Markdown string containing the project documentation.
|
||||||
|
If the input config is None, it returns an error message.
|
||||||
|
|
||||||
|
The generated Markdown includes:
|
||||||
|
1. A title for the project documentation.
|
||||||
|
2. A section for Agents, listing each agent's name, role, goal, and backstory.
|
||||||
|
3. A section for Tasks, listing each task's name, description, expected output,
|
||||||
|
and assigned agent.
|
||||||
|
|
||||||
|
Each piece of information is wrapped in code blocks for better readability
|
||||||
|
in rendered Markdown.
|
||||||
|
|
||||||
|
Note:
|
||||||
|
This function assumes that the config dictionary has the correct structure
|
||||||
|
with 'agents' and 'tasks' keys, each containing nested dictionaries of
|
||||||
|
agent and task information respectively.
|
||||||
|
"""
|
||||||
|
if config is None:
|
||||||
|
return "# Error: No crew configuration available"
|
||||||
|
|
||||||
|
md = "# CrewAI Project Documentation\n\n"
|
||||||
|
|
||||||
|
md += "## Agents\n\n"
|
||||||
|
for agent_name, agent_data in config["agents"].items():
|
||||||
|
md += f"### \n```\n{agent_name}\n```\n"
|
||||||
|
md += f"Role: \n```\n{agent_data.get('role', 'Not specified')}\n```\n"
|
||||||
|
md += f"Goal: \n```\n{agent_data.get('goal', 'Not specified')}\n```\n"
|
||||||
|
md += f"Backstory: \n```\n{agent_data.get('backstory', 'Not specified')}\n```\n"
|
||||||
|
md += f""
|
||||||
|
|
||||||
|
md += "## Tasks\n\n"
|
||||||
|
for task_name, task_data in config["tasks"].items():
|
||||||
|
md += f"### {task_name}\n"
|
||||||
|
md += f"Description: \n```\n{task_data.get('description', 'Not specified')}\n```\n"
|
||||||
|
md += f"Expected Output: \n```\n{task_data.get('expected_output', 'Not specified')}\n```\n"
|
||||||
|
md += f"Assigned Agent: \n```\n{task_data.get('agent', 'Not assigned')}\n```\n"
|
||||||
|
|
||||||
|
return md
|
||||||
Reference in New Issue
Block a user