diff --git a/docs/concepts/llms.mdx b/docs/concepts/llms.mdx index e17098f6a..2aada5fac 100644 --- a/docs/concepts/llms.mdx +++ b/docs/concepts/llms.mdx @@ -59,7 +59,7 @@ There are three ways to configure LLMs in CrewAI. Choose the method that best fi goal: Conduct comprehensive research and analysis backstory: A dedicated research professional with years of experience verbose: true - llm: openai/gpt-4o-mini # your model here + llm: openai/gpt-4o-mini # your model here # (see provider configuration examples below for more) ``` @@ -111,7 +111,7 @@ There are three ways to configure LLMs in CrewAI. Choose the method that best fi ## Provider Configuration Examples -CrewAI supports a multitude of LLM providers, each offering unique features, authentication methods, and model capabilities. +CrewAI supports a multitude of LLM providers, each offering unique features, authentication methods, and model capabilities. In this section, you'll find detailed examples that help you select, configure, and optimize the LLM that best fits your project's needs. @@ -121,7 +121,7 @@ In this section, you'll find detailed examples that help you select, configure, ```toml Code # Required OPENAI_API_KEY=sk-... - + # Optional OPENAI_API_BASE= OPENAI_ORGANIZATION= @@ -226,7 +226,7 @@ In this section, you'll find detailed examples that help you select, configure, AZURE_API_KEY= AZURE_API_BASE= AZURE_API_VERSION= - + # Optional AZURE_AD_TOKEN= AZURE_API_TYPE= @@ -289,7 +289,7 @@ In this section, you'll find detailed examples that help you select, configure, | Mistral 8x7B Instruct | Up to 32k tokens | An MOE LLM that follows instructions, completes requests, and generates creative text. | - + ```toml Code AWS_ACCESS_KEY_ID= @@ -474,7 +474,7 @@ In this section, you'll find detailed examples that help you select, configure, WATSONX_URL= WATSONX_APIKEY= WATSONX_PROJECT_ID= - + # Optional WATSONX_TOKEN= WATSONX_DEPLOYMENT_SPACE_ID= @@ -491,7 +491,7 @@ In this section, you'll find detailed examples that help you select, configure, 1. Install Ollama: [ollama.ai](https://ollama.ai/) - 2. Run a model: `ollama run llama2` + 2. Run a model: `ollama run llama3` 3. Configure: ```python Code @@ -600,7 +600,7 @@ In this section, you'll find detailed examples that help you select, configure, ```toml Code OPENROUTER_API_KEY= ``` - + Example usage in your CrewAI project: ```python Code llm = LLM( @@ -723,7 +723,7 @@ Learn how to get the most out of your LLM configuration: - Small tasks (up to 4K tokens): Standard models - Medium tasks (between 4K-32K): Enhanced models - Large tasks (over 32K): Large context models - + ```python # Configure model with appropriate settings llm = LLM( @@ -760,11 +760,11 @@ Learn how to get the most out of your LLM configuration: Most authentication issues can be resolved by checking API key format and environment variable names. - + ```bash # OpenAI OPENAI_API_KEY=sk-... - + # Anthropic ANTHROPIC_API_KEY=sk-ant-... ``` @@ -773,11 +773,11 @@ Learn how to get the most out of your LLM configuration: Always include the provider prefix in model names - + ```python # Correct llm = LLM(model="openai/gpt-4") - + # Incorrect llm = LLM(model="gpt-4") ``` @@ -786,5 +786,10 @@ Learn how to get the most out of your LLM configuration: Use larger context models for extensive tasks + + ```python + # Large context model + llm = LLM(model="openai/gpt-4o") # 128K tokens + ``` diff --git a/docs/quickstart.mdx b/docs/quickstart.mdx index df57f756f..1edccee0e 100644 --- a/docs/quickstart.mdx +++ b/docs/quickstart.mdx @@ -300,7 +300,7 @@ email_summarizer: ``` - Note how we use the same name for the agent in the `tasks.yaml` (`email_summarizer_task`) file as the method name in the `crew.py` (`email_summarizer_task`) file. + Note how we use the same name for the task in the `tasks.yaml` (`email_summarizer_task`) file as the method name in the `crew.py` (`email_summarizer_task`) file. ```yaml tasks.yaml diff --git a/pyproject.toml b/pyproject.toml index 2e319e8d0..6e895be32 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,9 +17,9 @@ dependencies = [ "pdfplumber>=0.11.4", "regex>=2024.9.11", # Telemetry and Monitoring - "opentelemetry-api>=1.22.0", - "opentelemetry-sdk>=1.22.0", - "opentelemetry-exporter-otlp-proto-http>=1.22.0", + "opentelemetry-api>=1.30.0", + "opentelemetry-sdk>=1.30.0", + "opentelemetry-exporter-otlp-proto-http>=1.30.0", # Data Handling "chromadb>=0.5.23", "openpyxl>=3.1.5", diff --git a/src/crewai/agents/parser.py b/src/crewai/agents/parser.py index 05c5bc003..88a869c16 100644 --- a/src/crewai/agents/parser.py +++ b/src/crewai/agents/parser.py @@ -136,7 +136,7 @@ class CrewAgentParser: def _clean_action(self, text: str) -> str: """Clean action string by removing non-essential formatting characters.""" - return re.sub(r"^\s*\*+\s*|\s*\*+\s*$", "", text).strip() + return text.strip().strip("*").strip() def _safe_repair_json(self, tool_input: str) -> str: UNABLE_TO_REPAIR_JSON_RESULTS = ['""', "{}"] diff --git a/src/crewai/cli/git.py b/src/crewai/cli/git.py index 94c3648b0..58836e733 100644 --- a/src/crewai/cli/git.py +++ b/src/crewai/cli/git.py @@ -1,4 +1,5 @@ import subprocess +from functools import lru_cache class Repository: @@ -35,6 +36,7 @@ class Repository: encoding="utf-8", ).strip() + @lru_cache(maxsize=None) def is_git_repo(self) -> bool: """Check if the current directory is a git repository.""" try: diff --git a/src/crewai/llm.py b/src/crewai/llm.py index fb8367dfe..68ddbacc7 100644 --- a/src/crewai/llm.py +++ b/src/crewai/llm.py @@ -114,6 +114,60 @@ LLM_CONTEXT_WINDOW_SIZES = { "Llama-3.2-11B-Vision-Instruct": 16384, "Meta-Llama-3.2-3B-Instruct": 4096, "Meta-Llama-3.2-1B-Instruct": 16384, + # bedrock + "us.amazon.nova-pro-v1:0": 300000, + "us.amazon.nova-micro-v1:0": 128000, + "us.amazon.nova-lite-v1:0": 300000, + "us.anthropic.claude-3-5-sonnet-20240620-v1:0": 200000, + "us.anthropic.claude-3-5-haiku-20241022-v1:0": 200000, + "us.anthropic.claude-3-5-sonnet-20241022-v2:0": 200000, + "us.anthropic.claude-3-7-sonnet-20250219-v1:0": 200000, + "us.anthropic.claude-3-sonnet-20240229-v1:0": 200000, + "us.anthropic.claude-3-opus-20240229-v1:0": 200000, + "us.anthropic.claude-3-haiku-20240307-v1:0": 200000, + "us.meta.llama3-2-11b-instruct-v1:0": 128000, + "us.meta.llama3-2-3b-instruct-v1:0": 131000, + "us.meta.llama3-2-90b-instruct-v1:0": 128000, + "us.meta.llama3-2-1b-instruct-v1:0": 131000, + "us.meta.llama3-1-8b-instruct-v1:0": 128000, + "us.meta.llama3-1-70b-instruct-v1:0": 128000, + "us.meta.llama3-3-70b-instruct-v1:0": 128000, + "us.meta.llama3-1-405b-instruct-v1:0": 128000, + "eu.anthropic.claude-3-5-sonnet-20240620-v1:0": 200000, + "eu.anthropic.claude-3-sonnet-20240229-v1:0": 200000, + "eu.anthropic.claude-3-haiku-20240307-v1:0": 200000, + "eu.meta.llama3-2-3b-instruct-v1:0": 131000, + "eu.meta.llama3-2-1b-instruct-v1:0": 131000, + "apac.anthropic.claude-3-5-sonnet-20240620-v1:0": 200000, + "apac.anthropic.claude-3-5-sonnet-20241022-v2:0": 200000, + "apac.anthropic.claude-3-sonnet-20240229-v1:0": 200000, + "apac.anthropic.claude-3-haiku-20240307-v1:0": 200000, + "amazon.nova-pro-v1:0": 300000, + "amazon.nova-micro-v1:0": 128000, + "amazon.nova-lite-v1:0": 300000, + "anthropic.claude-3-5-sonnet-20240620-v1:0": 200000, + "anthropic.claude-3-5-haiku-20241022-v1:0": 200000, + "anthropic.claude-3-5-sonnet-20241022-v2:0": 200000, + "anthropic.claude-3-7-sonnet-20250219-v1:0": 200000, + "anthropic.claude-3-sonnet-20240229-v1:0": 200000, + "anthropic.claude-3-opus-20240229-v1:0": 200000, + "anthropic.claude-3-haiku-20240307-v1:0": 200000, + "anthropic.claude-v2:1": 200000, + "anthropic.claude-v2": 100000, + "anthropic.claude-instant-v1": 100000, + "meta.llama3-1-405b-instruct-v1:0": 128000, + "meta.llama3-1-70b-instruct-v1:0": 128000, + "meta.llama3-1-8b-instruct-v1:0": 128000, + "meta.llama3-70b-instruct-v1:0": 8000, + "meta.llama3-8b-instruct-v1:0": 8000, + "amazon.titan-text-lite-v1": 4000, + "amazon.titan-text-express-v1": 8000, + "cohere.command-text-v14": 4000, + "ai21.j2-mid-v1": 8191, + "ai21.j2-ultra-v1": 8191, + "ai21.jamba-instruct-v1:0": 256000, + "mistral.mistral-7b-instruct-v0:2": 32000, + "mistral.mixtral-8x7b-instruct-v0:1": 32000, # mistral "mistral-tiny": 32768, "mistral-small-latest": 32768, diff --git a/src/crewai/memory/storage/mem0_storage.py b/src/crewai/memory/storage/mem0_storage.py index be889afff..0319c6a8a 100644 --- a/src/crewai/memory/storage/mem0_storage.py +++ b/src/crewai/memory/storage/mem0_storage.py @@ -1,7 +1,7 @@ import os from typing import Any, Dict, List -from mem0 import MemoryClient +from mem0 import Memory, MemoryClient from crewai.memory.storage.interface import Storage @@ -32,13 +32,16 @@ class Mem0Storage(Storage): mem0_org_id = config.get("org_id") mem0_project_id = config.get("project_id") - # Initialize MemoryClient with available parameters - if mem0_org_id and mem0_project_id: - self.memory = MemoryClient( - api_key=mem0_api_key, org_id=mem0_org_id, project_id=mem0_project_id - ) + # Initialize MemoryClient or Memory based on the presence of the mem0_api_key + if mem0_api_key: + if mem0_org_id and mem0_project_id: + self.memory = MemoryClient( + api_key=mem0_api_key, org_id=mem0_org_id, project_id=mem0_project_id + ) + else: + self.memory = MemoryClient(api_key=mem0_api_key) else: - self.memory = MemoryClient(api_key=mem0_api_key) + self.memory = Memory() # Fallback to Memory if no Mem0 API key is provided def _sanitize_role(self, role: str) -> str: """ diff --git a/src/crewai/telemetry/telemetry.py b/src/crewai/telemetry/telemetry.py index 984a4938d..559ca8d4f 100644 --- a/src/crewai/telemetry/telemetry.py +++ b/src/crewai/telemetry/telemetry.py @@ -281,8 +281,16 @@ class Telemetry: return self._safe_telemetry_operation(operation) def task_ended(self, span: Span, task: Task, crew: Crew): - """Records task execution in a crew.""" + """Records the completion of a task execution in a crew. + Args: + span (Span): The OpenTelemetry span tracking the task execution + task (Task): The task that was completed + crew (Crew): The crew context in which the task was executed + + Note: + If share_crew is enabled, this will also record the task output + """ def operation(): if crew.share_crew: self._add_attribute( @@ -297,8 +305,13 @@ class Telemetry: self._safe_telemetry_operation(operation) def tool_repeated_usage(self, llm: Any, tool_name: str, attempts: int): - """Records the repeated usage 'error' of a tool by an agent.""" + """Records when a tool is used repeatedly, which might indicate an issue. + Args: + llm (Any): The language model being used + tool_name (str): Name of the tool being repeatedly used + attempts (int): Number of attempts made with this tool + """ def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Tool Repeated Usage") @@ -317,8 +330,13 @@ class Telemetry: self._safe_telemetry_operation(operation) def tool_usage(self, llm: Any, tool_name: str, attempts: int): - """Records the usage of a tool by an agent.""" + """Records the usage of a tool by an agent. + Args: + llm (Any): The language model being used + tool_name (str): Name of the tool being used + attempts (int): Number of attempts made with this tool + """ def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Tool Usage") @@ -337,8 +355,11 @@ class Telemetry: self._safe_telemetry_operation(operation) def tool_usage_error(self, llm: Any): - """Records the usage of a tool by an agent.""" + """Records when a tool usage results in an error. + Args: + llm (Any): The language model being used when the error occurred + """ def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Tool Usage Error") @@ -357,6 +378,14 @@ class Telemetry: def individual_test_result_span( self, crew: Crew, quality: float, exec_time: int, model_name: str ): + """Records individual test results for a crew execution. + + Args: + crew (Crew): The crew being tested + quality (float): Quality score of the execution + exec_time (int): Execution time in seconds + model_name (str): Name of the model used + """ def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Crew Individual Test Result") @@ -383,6 +412,14 @@ class Telemetry: inputs: dict[str, Any] | None, model_name: str, ): + """Records the execution of a test suite for a crew. + + Args: + crew (Crew): The crew being tested + iterations (int): Number of test iterations + inputs (dict[str, Any] | None): Input parameters for the test + model_name (str): Name of the model used in testing + """ def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Crew Test Execution") @@ -408,6 +445,7 @@ class Telemetry: self._safe_telemetry_operation(operation) def deploy_signup_error_span(self): + """Records when an error occurs during the deployment signup process.""" def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Deploy Signup Error") @@ -417,6 +455,11 @@ class Telemetry: self._safe_telemetry_operation(operation) def start_deployment_span(self, uuid: Optional[str] = None): + """Records the start of a deployment process. + + Args: + uuid (Optional[str]): Unique identifier for the deployment + """ def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Start Deployment") @@ -428,6 +471,7 @@ class Telemetry: self._safe_telemetry_operation(operation) def create_crew_deployment_span(self): + """Records the creation of a new crew deployment.""" def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Create Crew Deployment") @@ -437,6 +481,12 @@ class Telemetry: self._safe_telemetry_operation(operation) def get_crew_logs_span(self, uuid: Optional[str], log_type: str = "deployment"): + """Records the retrieval of crew logs. + + Args: + uuid (Optional[str]): Unique identifier for the crew + log_type (str, optional): Type of logs being retrieved. Defaults to "deployment". + """ def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Get Crew Logs") @@ -449,6 +499,11 @@ class Telemetry: self._safe_telemetry_operation(operation) def remove_crew_span(self, uuid: Optional[str] = None): + """Records the removal of a crew. + + Args: + uuid (Optional[str]): Unique identifier for the crew being removed + """ def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Remove Crew") @@ -574,6 +629,11 @@ class Telemetry: self._safe_telemetry_operation(operation) def flow_creation_span(self, flow_name: str): + """Records the creation of a new flow. + + Args: + flow_name (str): Name of the flow being created + """ def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Flow Creation") @@ -584,6 +644,12 @@ class Telemetry: self._safe_telemetry_operation(operation) def flow_plotting_span(self, flow_name: str, node_names: list[str]): + """Records flow visualization/plotting activity. + + Args: + flow_name (str): Name of the flow being plotted + node_names (list[str]): List of node names in the flow + """ def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Flow Plotting") @@ -595,6 +661,12 @@ class Telemetry: self._safe_telemetry_operation(operation) def flow_execution_span(self, flow_name: str, node_names: list[str]): + """Records the execution of a flow. + + Args: + flow_name (str): Name of the flow being executed + node_names (list[str]): List of nodes being executed in the flow + """ def operation(): tracer = trace.get_tracer("crewai.telemetry") span = tracer.start_span("Flow Execution") diff --git a/src/crewai/tools/tool_usage.py b/src/crewai/tools/tool_usage.py index 25e4b126a..9c924027d 100644 --- a/src/crewai/tools/tool_usage.py +++ b/src/crewai/tools/tool_usage.py @@ -455,7 +455,7 @@ class ToolUsage: # Attempt 4: Repair JSON try: - repaired_input = repair_json(tool_input) + repaired_input = repair_json(tool_input, skip_json_loads=True) self._printer.print( content=f"Repaired JSON: {repaired_input}", color="blue" ) diff --git a/src/crewai/utilities/planning_handler.py b/src/crewai/utilities/planning_handler.py index 6ce74f236..1bd14a0c8 100644 --- a/src/crewai/utilities/planning_handler.py +++ b/src/crewai/utilities/planning_handler.py @@ -96,6 +96,10 @@ class CrewPlanner: tasks_summary = [] for idx, task in enumerate(self.tasks): knowledge_list = self._get_agent_knowledge(task) + agent_tools = ( + f"[{', '.join(str(tool) for tool in task.agent.tools)}]" if task.agent and task.agent.tools else '"agent has no tools"', + f',\n "agent_knowledge": "[\\"{knowledge_list[0]}\\"]"' if knowledge_list and str(knowledge_list) != "None" else "" + ) task_summary = f""" Task Number {idx + 1} - {task.description} "task_description": {task.description} @@ -103,10 +107,7 @@ class CrewPlanner: "agent": {task.agent.role if task.agent else "None"} "agent_goal": {task.agent.goal if task.agent else "None"} "task_tools": {task.tools} - "agent_tools": %s%s""" % ( - f"[{', '.join(str(tool) for tool in task.agent.tools)}]" if task.agent and task.agent.tools else '"agent has no tools"', - f',\n "agent_knowledge": "[\\"{knowledge_list[0]}\\"]"' if knowledge_list and str(knowledge_list) != "None" else "" - ) + "agent_tools": {"".join(agent_tools)}""" tasks_summary.append(task_summary) return " ".join(tasks_summary)