From 343ad02c883bef419f5fe72d9a1dfe27429bfeea Mon Sep 17 00:00:00 2001 From: Greyson LaLonde Date: Fri, 23 Jan 2026 09:21:22 -0500 Subject: [PATCH] feat(files): use FileInput type for all input_files parameters --- lib/crewai/src/crewai/crew.py | 17 ++++++++----- lib/crewai/src/crewai/crews/utils.py | 4 ++- lib/crewai/src/crewai/flow/flow.py | 8 +++--- lib/crewai/src/crewai/lite_agent.py | 13 +++++++--- lib/crewai/src/crewai/task.py | 37 +++++++++++++--------------- 5 files changed, 45 insertions(+), 34 deletions(-) diff --git a/lib/crewai/src/crewai/crew.py b/lib/crewai/src/crewai/crew.py index e7de9c0d3..226cc28e7 100644 --- a/lib/crewai/src/crewai/crew.py +++ b/lib/crewai/src/crewai/crew.py @@ -8,6 +8,7 @@ from hashlib import md5 import json import re from typing import ( + TYPE_CHECKING, Any, cast, ) @@ -31,6 +32,10 @@ from rich.console import Console from rich.panel import Panel from typing_extensions import Self + +if TYPE_CHECKING: + from crewai_files import FileInput + from crewai.agent import Agent from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.agents.cache.cache_handler import CacheHandler @@ -679,7 +684,7 @@ class Crew(FlowTrackable, BaseModel): def kickoff( self, inputs: dict[str, Any] | None = None, - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> CrewOutput | CrewStreamingOutput: """Execute the crew's workflow. @@ -751,7 +756,7 @@ class Crew(FlowTrackable, BaseModel): def kickoff_for_each( self, inputs: list[dict[str, Any]], - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> list[CrewOutput | CrewStreamingOutput]: """Executes the Crew's workflow for each input and aggregates results. @@ -787,7 +792,7 @@ class Crew(FlowTrackable, BaseModel): async def kickoff_async( self, inputs: dict[str, Any] | None = None, - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> CrewOutput | CrewStreamingOutput: """Asynchronous kickoff method to start the crew execution. @@ -834,7 +839,7 @@ class Crew(FlowTrackable, BaseModel): async def kickoff_for_each_async( self, inputs: list[dict[str, Any]], - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> list[CrewOutput | CrewStreamingOutput] | CrewStreamingOutput: """Executes the Crew's workflow for each input asynchronously. @@ -860,7 +865,7 @@ class Crew(FlowTrackable, BaseModel): async def akickoff( self, inputs: dict[str, Any] | None = None, - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> CrewOutput | CrewStreamingOutput: """Native async kickoff method using async task execution throughout. @@ -936,7 +941,7 @@ class Crew(FlowTrackable, BaseModel): async def akickoff_for_each( self, inputs: list[dict[str, Any]], - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> list[CrewOutput | CrewStreamingOutput] | CrewStreamingOutput: """Native async execution of the Crew's workflow for each input. diff --git a/lib/crewai/src/crewai/crews/utils.py b/lib/crewai/src/crewai/crews/utils.py index e534d9dca..2ac8266cc 100644 --- a/lib/crewai/src/crewai/crews/utils.py +++ b/lib/crewai/src/crewai/crews/utils.py @@ -35,6 +35,8 @@ except ImportError: if TYPE_CHECKING: + from crewai_files import FileInput + from crewai.crew import Crew @@ -225,7 +227,7 @@ def _extract_files_from_inputs(inputs: dict[str, Any]) -> dict[str, Any]: def prepare_kickoff( crew: Crew, inputs: dict[str, Any] | None, - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> dict[str, Any] | None: """Prepare crew for kickoff execution. diff --git a/lib/crewai/src/crewai/flow/flow.py b/lib/crewai/src/crewai/flow/flow.py index b9b7c5b54..b92d10d2d 100644 --- a/lib/crewai/src/crewai/flow/flow.py +++ b/lib/crewai/src/crewai/flow/flow.py @@ -83,6 +83,8 @@ from crewai.flow.utils import ( if TYPE_CHECKING: + from crewai_files import FileInput + from crewai.flow.async_feedback.types import PendingFeedbackContext from crewai.flow.human_feedback import HumanFeedbackResult from crewai.llms.base_llm import BaseLLM @@ -1414,7 +1416,7 @@ class Flow(Generic[T], metaclass=FlowMeta): def kickoff( self, inputs: dict[str, Any] | None = None, - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> Any | FlowStreamingOutput: """Start the flow execution in a synchronous context. @@ -1475,7 +1477,7 @@ class Flow(Generic[T], metaclass=FlowMeta): async def kickoff_async( self, inputs: dict[str, Any] | None = None, - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> Any | FlowStreamingOutput: """Start the flow execution asynchronously. @@ -1720,7 +1722,7 @@ class Flow(Generic[T], metaclass=FlowMeta): async def akickoff( self, inputs: dict[str, Any] | None = None, - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> Any | FlowStreamingOutput: """Native async method to start the flow execution. Alias for kickoff_async. diff --git a/lib/crewai/src/crewai/lite_agent.py b/lib/crewai/src/crewai/lite_agent.py index 0cff9b6fe..c3c6d2cc7 100644 --- a/lib/crewai/src/crewai/lite_agent.py +++ b/lib/crewai/src/crewai/lite_agent.py @@ -3,6 +3,7 @@ from collections.abc import Callable import inspect import json from typing import ( + TYPE_CHECKING, Any, Literal, cast, @@ -23,6 +24,10 @@ from pydantic import ( ) from typing_extensions import Self + +if TYPE_CHECKING: + from crewai_files import FileInput + from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess from crewai.agents.cache.cache_handler import CacheHandler @@ -296,7 +301,7 @@ class LiteAgent(FlowTrackable, BaseModel): self, messages: str | list[LLMMessage], response_format: type[BaseModel] | None = None, - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> LiteAgentOutput: """Execute the agent with the given messages. @@ -470,7 +475,7 @@ class LiteAgent(FlowTrackable, BaseModel): self, messages: str | list[LLMMessage], response_format: type[BaseModel] | None = None, - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> LiteAgentOutput: """Execute the agent asynchronously with the given messages. @@ -492,7 +497,7 @@ class LiteAgent(FlowTrackable, BaseModel): self, messages: str | list[LLMMessage], response_format: type[BaseModel] | None = None, - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> LiteAgentOutput: """Async version of kickoff. Alias for kickoff_async. @@ -548,7 +553,7 @@ class LiteAgent(FlowTrackable, BaseModel): self, messages: str | list[LLMMessage], response_format: type[BaseModel] | None = None, - input_files: dict[str, Any] | None = None, + input_files: dict[str, FileInput] | None = None, ) -> list[LLMMessage]: """Format messages for the LLM. diff --git a/lib/crewai/src/crewai/task.py b/lib/crewai/src/crewai/task.py index 2a4667264..87ce1ed43 100644 --- a/lib/crewai/src/crewai/task.py +++ b/lib/crewai/src/crewai/task.py @@ -10,6 +10,7 @@ import logging from pathlib import Path import threading from typing import ( + TYPE_CHECKING, Any, ClassVar, cast, @@ -30,6 +31,10 @@ from pydantic import ( from pydantic_core import PydanticCustomError from typing_extensions import Self + +if TYPE_CHECKING: + from crewai_files import FileInput + from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.events.event_bus import crewai_event_bus from crewai.events.types.task_events import ( @@ -52,10 +57,7 @@ from crewai.utilities.file_store import ( try: - from crewai_files import ( - FilePath, - normalize_input_files, - ) + from crewai_files import FilePath HAS_CREWAI_FILES = True except ImportError: @@ -158,9 +160,9 @@ class Task(BaseModel): default_factory=list, description="Tools the agent is limited to use for this task.", ) - input_files: list[Any] = Field( - default_factory=list, - description="List of input files for this task. Accepts paths, bytes, or File objects.", + input_files: dict[str, FileInput] = Field( + default_factory=dict, + description="Named input files for this task. Keys are reference names, values are paths or File objects.", ) security_config: SecurityConfig = Field( default_factory=SecurityConfig, @@ -379,7 +381,7 @@ class Task(BaseModel): @field_validator("input_files", mode="before") @classmethod - def _normalize_input_files(cls, v: list[Any]) -> list[Any]: + def _normalize_input_files(cls, v: dict[str, Any]) -> dict[str, Any]: """Convert string paths to FilePath objects.""" if not v: return v @@ -387,12 +389,12 @@ class Task(BaseModel): if not HAS_CREWAI_FILES: return v - result = [] - for item in v: - if isinstance(item, str): - result.append(FilePath(path=Path(item))) + result = {} + for key, value in v.items(): + if isinstance(value, str): + result[key] = FilePath(path=Path(value)) else: - result.append(item) + result[key] = value return result @field_validator("output_file") @@ -1038,16 +1040,11 @@ Follow these guidelines: return def _store_input_files(self) -> None: - """Store task input files in the file store. - - Converts input_files list to a named dict and stores under task ID. - """ + """Store task input files in the file store.""" if not HAS_CREWAI_FILES or not self.input_files: return - files_dict = normalize_input_files(self.input_files) - if files_dict: - store_task_files(self.id, files_dict) + store_task_files(self.id, self.input_files) def __repr__(self) -> str: return f"Task(description={self.description}, expected_output={self.expected_output})"