Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/en/concepts/llms.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -1212,7 +1212,7 @@ Learn how to get the most out of your LLM configuration:
```python
import httpx
from crewai import LLM
from crewai.llms.hooks import BaseInterceptor
from crewai.llm.hooks import BaseInterceptor

class CustomInterceptor(BaseInterceptor[httpx.Request, httpx.Response]):
"""Custom interceptor to modify requests and responses."""
Expand Down
4 changes: 2 additions & 2 deletions lib/crewai/src/crewai/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
from crewai.crews.crew_output import CrewOutput
from crewai.flow.flow import Flow
from crewai.knowledge.knowledge import Knowledge
from crewai.llm import LLM
from crewai.llms.base_llm import BaseLLM
from crewai.llm.base_llm import BaseLLM
from crewai.llm.core import LLM
from crewai.process import Process
from crewai.task import Task
from crewai.tasks.llm_guardrail import LLMGuardrail
Expand Down
37 changes: 21 additions & 16 deletions lib/crewai/src/crewai/agent/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
from crewai.knowledge.utils.knowledge_utils import extract_knowledge_context
from crewai.lite_agent import LiteAgent
from crewai.llms.base_llm import BaseLLM
from crewai.llm.base_llm import BaseLLM
from crewai.mcp import (
MCPClient,
MCPServerConfig,
Expand Down Expand Up @@ -633,7 +633,7 @@ def create_agent_executor(
)

self.agent_executor = CrewAgentExecutor(
llm=self.llm,
llm=self.llm, # type: ignore[arg-type]
task=task, # type: ignore[arg-type]
agent=self,
crew=self.crew,
Expand Down Expand Up @@ -810,6 +810,7 @@ def _get_native_mcp_tools(
from crewai.tools.base_tool import BaseTool
from crewai.tools.mcp_native_tool import MCPNativeTool

transport: StdioTransport | HTTPTransport | SSETransport
if isinstance(mcp_config, MCPServerStdio):
transport = StdioTransport(
command=mcp_config.command,
Expand Down Expand Up @@ -903,10 +904,12 @@ async def _setup_client_and_list_tools() -> list[dict[str, Any]]:
server_name=server_name,
run_context=None,
)
if mcp_config.tool_filter(context, tool):
# Try new signature first
if mcp_config.tool_filter(context, tool): # type: ignore[arg-type,call-arg]
filtered_tools.append(tool)
except (TypeError, AttributeError):
if mcp_config.tool_filter(tool):
# Fallback to old signature
if mcp_config.tool_filter(tool): # type: ignore[arg-type,call-arg]
filtered_tools.append(tool)
else:
# Not callable - include tool
Expand Down Expand Up @@ -981,7 +984,9 @@ def _extract_server_name(server_url: str) -> str:
path = parsed.path.replace("/", "_").strip("_")
return f"{domain}_{path}" if path else domain

def _get_mcp_tool_schemas(self, server_params: dict) -> dict[str, dict]:
def _get_mcp_tool_schemas(
self, server_params: dict[str, Any]
) -> dict[str, dict[str, Any]]:
"""Get tool schemas from MCP server for wrapper creation with caching."""
server_url = server_params["url"]

Expand All @@ -995,7 +1000,7 @@ def _get_mcp_tool_schemas(self, server_params: dict) -> dict[str, dict]:
self._logger.log(
"debug", f"Using cached MCP tool schemas for {server_url}"
)
return cached_data
return cast(dict[str, dict[str, Any]], cached_data)

try:
schemas = asyncio.run(self._get_mcp_tool_schemas_async(server_params))
Expand All @@ -1013,15 +1018,15 @@ def _get_mcp_tool_schemas(self, server_params: dict) -> dict[str, dict]:

async def _get_mcp_tool_schemas_async(
self, server_params: dict[str, Any]
) -> dict[str, dict]:
) -> dict[str, dict[str, Any]]:
"""Async implementation of MCP tool schema retrieval with timeouts and retries."""
server_url = server_params["url"]
return await self._retry_mcp_discovery(
self._discover_mcp_tools_with_timeout, server_url
)

async def _retry_mcp_discovery(
self, operation_func, server_url: str
self, operation_func: Any, server_url: str
) -> dict[str, dict[str, Any]]:
"""Retry MCP discovery operation with exponential backoff, avoiding try-except in loop."""
last_error = None
Expand Down Expand Up @@ -1052,7 +1057,7 @@ async def _retry_mcp_discovery(

@staticmethod
async def _attempt_mcp_discovery(
operation_func, server_url: str
operation_func: Any, server_url: str
) -> tuple[dict[str, dict[str, Any]] | None, str, bool]:
"""Attempt single MCP discovery operation and return (result, error_message, should_retry)."""
try:
Expand Down Expand Up @@ -1156,13 +1161,13 @@ def _json_schema_to_pydantic(
Field(..., description=field_description),
)
else:
field_definitions[field_name] = (
field_definitions[field_name] = ( # type: ignore[assignment]
field_type | None,
Field(default=None, description=field_description),
)

model_name = f"{tool_name.replace('-', '_').replace(' ', '_')}Schema"
return create_model(model_name, **field_definitions)
return create_model(model_name, **field_definitions) # type: ignore[no-any-return,call-overload]

def _json_type_to_python(self, field_schema: dict[str, Any]) -> type:
"""Convert JSON Schema type to Python type.
Expand All @@ -1182,16 +1187,16 @@ def _json_type_to_python(self, field_schema: dict[str, Any]) -> type:
if "const" in option:
types.append(str)
else:
types.append(self._json_type_to_python(option))
types.append(self._json_type_to_python(option)) # type: ignore[arg-type]
unique_types = list(set(types))
if len(unique_types) > 1:
result = unique_types[0]
for t in unique_types[1:]:
result = result | t
result = result | t # type: ignore[assignment]
return result
return unique_types[0]

type_mapping = {
type_mapping: dict[str, type] = {
"string": str,
"number": float,
"integer": int,
Expand All @@ -1200,10 +1205,10 @@ def _json_type_to_python(self, field_schema: dict[str, Any]) -> type:
"object": dict,
}

return type_mapping.get(json_type, Any)
return type_mapping.get(json_type or "", Any)

@staticmethod
def _fetch_amp_mcp_servers(mcp_name: str) -> list[dict]:
def _fetch_amp_mcp_servers(mcp_name: str) -> list[dict[str, Any]]:
"""Fetch MCP server configurations from CrewAI AMP API."""
# TODO: Implement AMP API call to "integrations/mcps" endpoint
# Should return list of server configs with URLs
Expand Down
6 changes: 3 additions & 3 deletions lib/crewai/src/crewai/agents/agent_builder/base_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta):
default=False,
description="Enable agent to delegate and ask questions among each other.",
)
tools: list[BaseTool] | None = Field(
tools: list[BaseTool] = Field(
default_factory=list, description="Tools at agents' disposal"
)
max_iter: int = Field(
Expand All @@ -161,7 +161,7 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta):
description="An instance of the ToolsHandler class.",
)
tools_results: list[dict[str, Any]] = Field(
default=[], description="Results of the tools used by the agent."
default_factory=list, description="Results of the tools used by the agent."
)
max_tokens: int | None = Field(
default=None, description="Maximum number of tokens for the agent's execution."
Expand Down Expand Up @@ -265,7 +265,7 @@ def validate_mcps(
if not mcps:
return mcps

validated_mcps = []
validated_mcps: list[str | MCPServerConfig] = []
for mcp in mcps:
if isinstance(mcp, str):
if mcp.startswith(("https://", "crewai-amp:")):
Expand Down
2 changes: 1 addition & 1 deletion lib/crewai/src/crewai/agents/crew_agent_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
from crewai.agent import Agent
from crewai.agents.tools_handler import ToolsHandler
from crewai.crew import Crew
from crewai.llms.base_llm import BaseLLM
from crewai.llm.base_llm import BaseLLM
from crewai.task import Task
from crewai.tools.base_tool import BaseTool
from crewai.tools.structured_tool import CrewStructuredTool
Expand Down
3 changes: 2 additions & 1 deletion lib/crewai/src/crewai/cli/crew_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
from crewai.cli.utils import read_toml
from crewai.cli.version import get_crewai_version
from crewai.crew import Crew
from crewai.llm import LLM, BaseLLM
from crewai.llm import LLM
from crewai.llm.base_llm import BaseLLM
from crewai.types.crew_chat import ChatInputField, ChatInputs
from crewai.utilities.llm_utils import create_llm
from crewai.utilities.printer import Printer
Expand Down
4 changes: 2 additions & 2 deletions lib/crewai/src/crewai/crew.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@
from crewai.flow.flow_trackable import FlowTrackable
from crewai.knowledge.knowledge import Knowledge
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
from crewai.llm import LLM
from crewai.llms.base_llm import BaseLLM
from crewai.llm.base_llm import BaseLLM
from crewai.llm.core import LLM
from crewai.memory.entity.entity_memory import EntityMemory
from crewai.memory.external.external_memory import ExternalMemory
from crewai.memory.long_term.long_term_memory import LongTermMemory
Expand Down
2 changes: 1 addition & 1 deletion lib/crewai/src/crewai/events/event_listener.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
ToolUsageStartedEvent,
)
from crewai.events.utils.console_formatter import ConsoleFormatter
from crewai.llm import LLM
from crewai.llm.core import LLM
from crewai.task import Task
from crewai.telemetry.telemetry import Telemetry
from crewai.utilities import Logger
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from crewai.agent import Agent
from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.llm import BaseLLM
from crewai.llm.base_llm import BaseLLM
from crewai.task import Task
from crewai.utilities.llm_utils import create_llm

Expand Down
9 changes: 5 additions & 4 deletions lib/crewai/src/crewai/lite_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
from crewai.events.types.logging_events import AgentLogsExecutionEvent
from crewai.flow.flow_trackable import FlowTrackable
from crewai.lite_agent_output import LiteAgentOutput
from crewai.llm import LLM
from crewai.llms.base_llm import BaseLLM
from crewai.llm.base_llm import BaseLLM
from crewai.llm.core import LLM
from crewai.tools.base_tool import BaseTool
from crewai.tools.structured_tool import CrewStructuredTool
from crewai.utilities.agent_utils import (
Expand Down Expand Up @@ -504,7 +504,7 @@ def _invoke_loop(self) -> AgentFinish:
AgentFinish: The final result of the agent execution.
"""
# Execute the agent loop
formatted_answer = None
formatted_answer: AgentAction | AgentFinish | None = None
while not isinstance(formatted_answer, AgentFinish):
try:
if has_reached_max_iterations(self._iterations, self.max_iterations):
Expand Down Expand Up @@ -553,7 +553,8 @@ def _invoke_loop(self) -> AgentFinish:
show_logs=self._show_logs,
)

self._append_message(formatted_answer.text, role="assistant")
if formatted_answer is not None:
self._append_message(formatted_answer.text, role="assistant")
except OutputParserError as e: # noqa: PERF203
self._printer.print(
content="Failed to parse LLM output. Retrying...",
Expand Down
4 changes: 4 additions & 0 deletions lib/crewai/src/crewai/llm/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from crewai.llm.core import LLM


__all__ = ["LLM"]
Loading
Loading