From 1c10747262da197e09733159f39c764521d268eb Mon Sep 17 00:00:00 2001 From: FBISiri Date: Mon, 1 Jun 2026 17:09:47 +0800 Subject: [PATCH] docs: modernize type annotations to Python 3.10+ built-in generics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since crewAI requires Python >=3.10 (pyproject.toml), code examples in documentation should use modern built-in generic types instead of importing from typing: - List[X] → list[X] - Dict[K, V] → dict[K, V] - Tuple[X] → tuple[X] - Optional[X] → X | None - Union[X, Y] → X | Y This removes unnecessary typing imports and aligns documentation with PEP 585 (built-in generics) and PEP 604 (union type operator). Updated 9 documentation files across quickstart, guides, learn, and observability sections. --- .../guides/concepts/evaluating-use-cases.mdx | 2 -- docs/en/guides/crews/first-crew.mdx | 6 ++---- docs/en/guides/flows/first-flow.mdx | 13 ++++++------ docs/en/guides/flows/mastering-flow-state.mdx | 16 ++++++-------- docs/en/learn/bring-your-own-agent.mdx | 21 +++++++------------ docs/en/learn/conditional-tasks.mdx | 4 ++-- docs/en/learn/custom-llm.mdx | 21 +++++++++---------- docs/en/observability/braintrust.mdx | 3 +-- docs/en/quickstart.mdx | 15 +++---------- 9 files changed, 37 insertions(+), 64 deletions(-) diff --git a/docs/en/guides/concepts/evaluating-use-cases.mdx b/docs/en/guides/concepts/evaluating-use-cases.mdx index f7895deec6..96dfb86f8b 100644 --- a/docs/en/guides/concepts/evaluating-use-cases.mdx +++ b/docs/en/guides/concepts/evaluating-use-cases.mdx @@ -174,7 +174,6 @@ Flows are ideal when: # Example: Customer Support Flow with structured processing from crewai.flow.flow import Flow, listen, router, start from pydantic import BaseModel -from typing import List, Dict # Define structured state class SupportTicketState(BaseModel): @@ -262,7 +261,6 @@ The most sophisticated applications often benefit from combining Crews and Flows from crewai.flow.flow import Flow, listen, start from crewai import Agent, Crew, Process, Task from pydantic import BaseModel -from typing import List, Dict class ContentState(BaseModel): topic: str = "" diff --git a/docs/en/guides/crews/first-crew.mdx b/docs/en/guides/crews/first-crew.mdx index 22495f34da..2c524450a8 100644 --- a/docs/en/guides/crews/first-crew.mdx +++ b/docs/en/guides/crews/first-crew.mdx @@ -58,7 +58,6 @@ This will generate a project with the basic structure needed for your crew. The CrewAI Framework Overview - ## Step 2: Explore the Project Structure Let's take a moment to understand the project structure created by the CLI. CrewAI follows best practices for Python projects, making it easy to maintain and extend your code as your crews become more complex. @@ -187,14 +186,13 @@ from crewai import Agent, Crew, Process, Task from crewai.project import CrewBase, agent, crew, task from crewai_tools import SerperDevTool from crewai.agents.agent_builder.base_agent import BaseAgent -from typing import List @CrewBase class ResearchCrew(): """Research crew for comprehensive topic analysis and reporting""" - agents: List[BaseAgent] - tasks: List[Task] + agents: list[BaseAgent] + tasks: list[Task] @agent def researcher(self) -> Agent: diff --git a/docs/en/guides/flows/first-flow.mdx b/docs/en/guides/flows/first-flow.mdx index a5b8c347ce..f6a335bd33 100644 --- a/docs/en/guides/flows/first-flow.mdx +++ b/docs/en/guides/flows/first-flow.mdx @@ -206,14 +206,13 @@ These task definitions provide detailed instructions to our agents, ensuring the from crewai import Agent, Crew, Process, Task from crewai.project import CrewBase, agent, crew, task from crewai.agents.agent_builder.base_agent import BaseAgent -from typing import List @CrewBase class ContentCrew(): """Content writing crew""" - agents: List[BaseAgent] - tasks: List[Task] + agents: list[BaseAgent] + tasks: list[Task] @agent def content_writer(self) -> Agent: @@ -271,7 +270,7 @@ Let's create our flow in the `main.py` file: #!/usr/bin/env python import json import os -from typing import List, Dict + from pydantic import BaseModel, Field from crewai import LLM from crewai.flow.flow import Flow, listen, start @@ -286,7 +285,7 @@ class GuideOutline(BaseModel): title: str = Field(description="Title of the guide") introduction: str = Field(description="Introduction to the topic") target_audience: str = Field(description="Description of the target audience") - sections: List[Section] = Field(description="List of sections in the guide") + sections: list[Section] = Field(description="List of sections in the guide") conclusion: str = Field(description="Conclusion or summary of the guide") # Define our flow state @@ -294,7 +293,7 @@ class GuideCreatorState(BaseModel): topic: str = "" audience_level: str = "" guide_outline: GuideOutline = None - sections_content: Dict[str, str] = {} + sections_content: dict[str, str] = {} class GuideCreatorFlow(Flow[GuideCreatorState]): """Flow for creating a comprehensive guide on any topic""" @@ -590,7 +589,7 @@ class GuideCreatorState(BaseModel): topic: str = "" audience_level: str = "" guide_outline: GuideOutline = None - sections_content: Dict[str, str] = {} + sections_content: dict[str, str] = {} ``` This provides a type-safe way to track and transform data throughout your flow. diff --git a/docs/en/guides/flows/mastering-flow-state.mdx b/docs/en/guides/flows/mastering-flow-state.mdx index 68a8212464..6deb530008 100644 --- a/docs/en/guides/flows/mastering-flow-state.mdx +++ b/docs/en/guides/flows/mastering-flow-state.mdx @@ -149,7 +149,6 @@ Here's how to implement structured state management: ```python from crewai.flow.flow import Flow, listen, start from pydantic import BaseModel, Field -from typing import List, Dict, Optional # Define your state model class UserPreferences(BaseModel): @@ -159,7 +158,7 @@ class UserPreferences(BaseModel): class AppState(BaseModel): user_name: str = "" preferences: UserPreferences = UserPreferences() - items: List[str] = [] + items: list[str] = [] processed: bool = False completion_percentage: float = 0.0 @@ -388,7 +387,6 @@ Behavior notes: - `restore_from_state_id=None` (default) is byte-identical to a kickoff without the parameter. - Pinning `inputs["id"]` while forking means the new run shares a persistence key with another flow — usually you want only `restore_from_state_id`. - ## Advanced State Patterns ### Conditional starts and resumable execution @@ -469,7 +467,6 @@ For complex state transformations, you can create dedicated methods: ```python from crewai.flow.flow import Flow, listen, start from pydantic import BaseModel -from typing import List, Dict class UserData(BaseModel): name: str @@ -477,7 +474,7 @@ class UserData(BaseModel): login_count: int = 0 class ComplexState(BaseModel): - users: Dict[str, UserData] = {} + users: dict[str, UserData] = {} active_user_count: int = 0 class TransformationFlow(Flow[ComplexState]): @@ -641,8 +638,8 @@ class BloatedState(BaseModel): # Better: Focused state class FocusedState(BaseModel): user_id: str - preferences: Dict[str, str] - completion_status: Dict[str, bool] + preferences: dict[str, str] + completion_status: dict[str, bool] ``` ### 2. Use Structured State for Complex Flows @@ -663,7 +660,7 @@ class UserRegistrationState(BaseModel): email: str verification_status: bool = False registration_date: datetime = Field(default_factory=datetime.now) - last_login: Optional[datetime] = None + last_login: datetime | None = None class RegistrationFlow(Flow[UserRegistrationState]): # Methods with strongly-typed state access @@ -748,10 +745,9 @@ self.state.items.append(new_item) # Mutable operation # Consider creating new state: from pydantic import BaseModel -from typing import List class ItemState(BaseModel): - items: List[str] = [] + items: list[str] = [] class ImmutableFlow(Flow[ItemState]): @start() diff --git a/docs/en/learn/bring-your-own-agent.mdx b/docs/en/learn/bring-your-own-agent.mdx index fb7b22dc21..c9d74a62c7 100644 --- a/docs/en/learn/bring-your-own-agent.mdx +++ b/docs/en/learn/bring-your-own-agent.mdx @@ -7,7 +7,6 @@ mode: "wide" Interoperability is a core concept in CrewAI. This guide will show you how to bring your own agents that work within a Crew. - ## Adapter Guide for Bringing your own agents (Langgraph Agents, OpenAI Agents, etc...) We require 3 adapters to turn any agent from different frameworks to work within crew. @@ -15,7 +14,6 @@ We require 3 adapters to turn any agent from different frameworks to work within 2. BaseToolAdapter 3. BaseConverter - ## BaseAgentAdapter This abstract class defines the common interface and functionality that all agent adapters must implement. It extends BaseAgent to maintain compatibility @@ -35,7 +33,7 @@ Here's how you implement your custom adapter: ```python from crewai.agents.agent_adapters.base_agent_adapter import BaseAgentAdapter from crewai.tools import BaseTool - from typing import List, Optional, Any, Dict + from typing import Any class MyCustomAgentAdapter(BaseAgentAdapter): # ... implementation details ... @@ -45,7 +43,7 @@ Here's how you implement your custom adapter: The constructor should call the parent class constructor `super().__init__(**kwargs)` and perform any initialization specific to your external agent. You can use the optional `agent_config` dictionary passed during CrewAI's `Agent` initialization to configure your adapter and the underlying agent. ```python - def __init__(self, agent_config: Optional[Dict[str, Any]] = None, **kwargs: Any): + def __init__(self, agent_config: dict[str, Any] | None = None, **kwargs: Any): super().__init__(agent_config=agent_config, **kwargs) # Initialize your external agent here, possibly using agent_config # Example: self.external_agent = initialize_my_agent(agent_config) @@ -56,7 +54,7 @@ Here's how you implement your custom adapter: This abstract method is crucial. It receives a list of CrewAI `BaseTool` instances. Your implementation must convert or adapt these tools into the format expected by your external agent framework. This might involve wrapping them, extracting specific attributes, or registering them with the external agent instance. ```python - def configure_tools(self, tools: Optional[List[BaseTool]] = None) -> None: + def configure_tools(self, tools: list[BaseTool] | None = None) -> None: if tools: adapted_tools = [] for tool in tools: @@ -96,7 +94,7 @@ Here's how you implement your custom tool adapter: ```python from crewai.agents.agent_adapters.base_tool_adapter import BaseToolAdapter from crewai.tools import BaseTool - from typing import List, Any + from typing import Any class MyCustomToolAdapter(BaseToolAdapter): # ... implementation details ... @@ -106,7 +104,7 @@ Here's how you implement your custom tool adapter: This is the core abstract method you must implement. It receives a list of CrewAI `BaseTool` instances provided to the agent. Your task is to iterate through this list, adapt each `BaseTool` into the format expected by your external framework, and store the converted tools in the `self.converted_tools` list (which is initialized in the base class constructor). ```python - def configure_tools(self, tools: List[BaseTool]) -> None: + def configure_tools(self, tools: list[BaseTool]) -> None: """Configure and convert CrewAI tools for the specific implementation.""" self.converted_tools = [] # Reset in case it's called multiple times for tool in tools: @@ -175,7 +173,7 @@ Here's how you implement your custom tool adapter: ```python # Inside MyCustomAgentAdapter.configure_tools - def configure_tools(self, tools: Optional[List[BaseTool]] = None) -> None: + def configure_tools(self, tools: list[BaseTool] | None = None) -> None: if tools: tool_adapter = MyCustomToolAdapter() # Instantiate your tool adapter tool_adapter.configure_tools(tools) # Convert the tools @@ -328,7 +326,6 @@ We provide out of the box adapters for the following frameworks: ```python import json import os -from typing import List from crewai_tools import SerperDevTool from src.crewai import Agent, Crew, Task @@ -368,11 +365,9 @@ reporter_agent = LangGraphAgentAdapter( verbose=True, ) - class Code(BaseModel): code: str - task = Task( description="Give an answer to the coding question: {task}", expected_output="A thorough answer to the coding question: {task}", @@ -385,11 +380,9 @@ task2 = Task( agent=link_finder_agent, ) - class Report(BaseModel): code: str - links: List[str] - + links: list[str] task3 = Task( description="Report the results of the tasks.", diff --git a/docs/en/learn/conditional-tasks.mdx b/docs/en/learn/conditional-tasks.mdx index 5c7914fae2..a95ff2c1a7 100644 --- a/docs/en/learn/conditional-tasks.mdx +++ b/docs/en/learn/conditional-tasks.mdx @@ -13,7 +13,7 @@ This powerful feature enables crews to make decisions and execute tasks selectiv ## Example Usage ```python Code -from typing import List + from pydantic import BaseModel from crewai import Agent, Crew from crewai.tasks.conditional_task import ConditionalTask @@ -50,7 +50,7 @@ summary_generator_agent = Agent( ) class EventOutput(BaseModel): - events: List[str] + events: list[str] task1 = Task( description="Fetch data about events in San Francisco using Serper tool", diff --git a/docs/en/learn/custom-llm.mdx b/docs/en/learn/custom-llm.mdx index 0eee2387a9..71f27ed0f6 100644 --- a/docs/en/learn/custom-llm.mdx +++ b/docs/en/learn/custom-llm.mdx @@ -15,11 +15,11 @@ Here's a minimal custom LLM implementation: ```python from crewai import BaseLLM -from typing import Any, Dict, List, Optional, Union +from typing import Any import requests class CustomLLM(BaseLLM): - def __init__(self, model: str, api_key: str, endpoint: str, temperature: Optional[float] = None): + def __init__(self, model: str, api_key: str, endpoint: str, temperature: float | None = None): # IMPORTANT: Call super().__init__() with required parameters super().__init__(model=model, temperature=temperature) @@ -28,11 +28,11 @@ class CustomLLM(BaseLLM): def call( self, - messages: Union[str, List[Dict[str, str]]], - tools: Optional[List[dict]] = None, - callbacks: Optional[List[Any]] = None, - available_functions: Optional[Dict[str, Any]] = None, - ) -> Union[str, Any]: + messages: str | list[dict[str, str]], + tools: list[dict] | None = None, + callbacks: list[Any] | None = None, + available_functions: dict[str, Any] | None = None, + ) -> str | Any: """Call the LLM with the given messages.""" # Convert string to message format if needed if isinstance(messages, str): @@ -113,7 +113,7 @@ result = crew.kickoff() **Critical**: You must call `super().__init__(model, temperature)` with the required parameters: ```python -def __init__(self, model: str, api_key: str, temperature: Optional[float] = None): +def __init__(self, model: str, api_key: str, temperature: float | None = None): # REQUIRED: Call parent constructor with model and temperature super().__init__(model=model, temperature=temperature) @@ -176,10 +176,9 @@ def call(self, messages, tools=None, callbacks=None, available_functions=None): ```python from crewai import BaseLLM -from typing import Optional class CustomAuthLLM(BaseLLM): - def __init__(self, model: str, auth_token: str, endpoint: str, temperature: Optional[float] = None): + def __init__(self, model: str, auth_token: str, endpoint: str, temperature: float | None = None): super().__init__(model=model, temperature=temperature) self.auth_token = auth_token self.endpoint = endpoint @@ -293,7 +292,7 @@ def __init__(self, api_key: str): super().__init__() # ✅ Correct -def __init__(self, model: str, api_key: str, temperature: Optional[float] = None): +def __init__(self, model: str, api_key: str, temperature: float | None = None): super().__init__(model=model, temperature=temperature) ``` diff --git a/docs/en/observability/braintrust.mdx b/docs/en/observability/braintrust.mdx index 21ab52c852..239d4586df 100644 --- a/docs/en/observability/braintrust.mdx +++ b/docs/en/observability/braintrust.mdx @@ -47,7 +47,7 @@ Initialize the Braintrust OpenTelemetry instrumentation to start capturing trace ```python import os -from typing import Any, Dict +from typing import Any from braintrust.otel import BraintrustSpanProcessor from crewai import Agent, Crew, Task @@ -70,7 +70,6 @@ def setup_tracing() -> None: CrewAIInstrumentor().instrument(tracer_provider=provider) OpenAIInstrumentor().instrument(tracer_provider=provider) - setup_tracing() ``` diff --git a/docs/en/quickstart.mdx b/docs/en/quickstart.mdx index 3b1b76ddfe..cd42670258 100644 --- a/docs/en/quickstart.mdx +++ b/docs/en/quickstart.mdx @@ -78,20 +78,19 @@ If you have not installed CrewAI yet, follow the [installation guide](/en/instal ```python content_crew.py # src/latest_ai_flow/crews/content_crew/content_crew.py - from typing import List + from crewai import Agent, Crew, Process, Task from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.project import CrewBase, agent, crew, task from crewai_tools import SerperDevTool - @CrewBase class ResearchCrew: """Single-agent research crew used inside the Flow.""" - agents: List[BaseAgent] - tasks: List[Task] + agents: list[BaseAgent] + tasks: list[Task] agents_config = "config/agents.yaml" tasks_config = "config/tasks.yaml" @@ -133,12 +132,10 @@ If you have not installed CrewAI yet, follow the [installation guide](/en/instal from latest_ai_flow.crews.content_crew.content_crew import ResearchCrew - class ResearchFlowState(BaseModel): topic: str = "" report: str = "" - class LatestAiFlow(Flow[ResearchFlowState]): @start() def prepare_topic(self, crewai_trigger_payload: dict | None = None): @@ -158,15 +155,12 @@ If you have not installed CrewAI yet, follow the [installation guide](/en/instal def summarize(self): print("Report path: output/report.md") - def kickoff(): LatestAiFlow().kickoff() - def plot(): LatestAiFlow().plot() - if __name__ == "__main__": kickoff() ``` @@ -194,9 +188,6 @@ If you have not installed CrewAI yet, follow the [installation guide](/en/instal `crewai run` executes the Flow entrypoint defined in your project (same command as for crews; project type is `"flow"` in `pyproject.toml`). - - - You should see logs from the Flow and the crew. Open **`output/report.md`** for the generated report (excerpt):