Merge branch 'main' into fix/fingerprint-config-not-args

This commit is contained in:
Lorenze Jay
2026-04-02 09:45:43 -07:00
committed by GitHub
23 changed files with 392 additions and 96 deletions

View File

@@ -4,6 +4,28 @@ description: "تحديثات المنتج والتحسينات وإصلاحات
icon: "clock" icon: "clock"
mode: "wide" mode: "wide"
--- ---
<Update label="2 أبريل 2026">
## v1.13.0a7
[عرض الإصدار على GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a7)
## ما الذي تغير
### الميزات
- إضافة امتداد A2UI مع دعم v0.8/v0.9، والمخططات، والوثائق
### إصلاحات الأخطاء
- إصلاح بادئات الرؤية متعددة الأنماط عن طريق إضافة GPT-5 وسلسلة o
### الوثائق
- تحديث سجل التغييرات والإصدار لـ v1.13.0a6
## المساهمون
@alex-clawd, @greysonlalonde, @joaomdmoura
</Update>
<Update label="1 أبريل 2026"> <Update label="1 أبريل 2026">
## v1.13.0a6 ## v1.13.0a6

View File

@@ -4,6 +4,28 @@ description: "Product updates, improvements, and bug fixes for CrewAI"
icon: "clock" icon: "clock"
mode: "wide" mode: "wide"
--- ---
<Update label="Apr 02, 2026">
## v1.13.0a7
[View release on GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a7)
## What's Changed
### Features
- Add A2UI extension with v0.8/v0.9 support, schemas, and docs
### Bug Fixes
- Fix multimodal vision prefixes by adding GPT-5 and o-series
### Documentation
- Update changelog and version for v1.13.0a6
## Contributors
@alex-clawd, @greysonlalonde, @joaomdmoura
</Update>
<Update label="Apr 01, 2026"> <Update label="Apr 01, 2026">
## v1.13.0a6 ## v1.13.0a6

View File

@@ -4,6 +4,28 @@ description: "CrewAI의 제품 업데이트, 개선 사항 및 버그 수정"
icon: "clock" icon: "clock"
mode: "wide" mode: "wide"
--- ---
<Update label="2026년 4월 2일">
## v1.13.0a7
[GitHub 릴리스 보기](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a7)
## 변경 사항
### 기능
- v0.8/v0.9 지원, 스키마 및 문서가 포함된 A2UI 확장 추가
### 버그 수정
- GPT-5 및 o-series를 추가하여 다중 모드 비전 접두사 수정
### 문서
- v1.13.0a6에 대한 변경 로그 및 버전 업데이트
## 기여자
@alex-clawd, @greysonlalonde, @joaomdmoura
</Update>
<Update label="2026년 4월 1일"> <Update label="2026년 4월 1일">
## v1.13.0a6 ## v1.13.0a6

View File

@@ -4,6 +4,28 @@ description: "Atualizações de produto, melhorias e correções do CrewAI"
icon: "clock" icon: "clock"
mode: "wide" mode: "wide"
--- ---
<Update label="02 abr 2026">
## v1.13.0a7
[Ver release no GitHub](https://github.com/crewAIInc/crewAI/releases/tag/1.13.0a7)
## O que Mudou
### Funcionalidades
- Adicionar a extensão A2UI com suporte a v0.8/v0.9, esquemas e documentação
### Correções de Bugs
- Corrigir prefixos de visão multimodal adicionando GPT-5 e o-series
### Documentação
- Atualizar changelog e versão para v1.13.0a6
## Contribuidores
@alex-clawd, @greysonlalonde, @joaomdmoura
</Update>
<Update label="01 abr 2026"> <Update label="01 abr 2026">
## v1.13.0a6 ## v1.13.0a6

View File

@@ -152,4 +152,4 @@ __all__ = [
"wrap_file_source", "wrap_file_source",
] ]
__version__ = "1.13.0a6" __version__ = "1.13.0a7"

View File

@@ -11,7 +11,7 @@ dependencies = [
"pytube~=15.0.0", "pytube~=15.0.0",
"requests~=2.32.5", "requests~=2.32.5",
"docker~=7.1.0", "docker~=7.1.0",
"crewai==1.13.0a6", "crewai==1.13.0a7",
"tiktoken~=0.8.0", "tiktoken~=0.8.0",
"beautifulsoup4~=4.13.4", "beautifulsoup4~=4.13.4",
"python-docx~=1.2.0", "python-docx~=1.2.0",

View File

@@ -309,4 +309,4 @@ __all__ = [
"ZapierActionTools", "ZapierActionTools",
] ]
__version__ = "1.13.0a6" __version__ = "1.13.0a7"

View File

@@ -54,7 +54,7 @@ Repository = "https://github.com/crewAIInc/crewAI"
[project.optional-dependencies] [project.optional-dependencies]
tools = [ tools = [
"crewai-tools==1.13.0a6", "crewai-tools==1.13.0a7",
] ]
embeddings = [ embeddings = [
"tiktoken~=0.8.0" "tiktoken~=0.8.0"

View File

@@ -10,6 +10,7 @@ from crewai.agent.core import Agent
from crewai.agent.planning_config import PlanningConfig from crewai.agent.planning_config import PlanningConfig
from crewai.crew import Crew from crewai.crew import Crew
from crewai.crews.crew_output import CrewOutput from crewai.crews.crew_output import CrewOutput
from crewai.execution_context import ExecutionContext
from crewai.flow.flow import Flow from crewai.flow.flow import Flow
from crewai.knowledge.knowledge import Knowledge from crewai.knowledge.knowledge import Knowledge
from crewai.llm import LLM from crewai.llm import LLM
@@ -44,7 +45,7 @@ def _suppress_pydantic_deprecation_warnings() -> None:
_suppress_pydantic_deprecation_warnings() _suppress_pydantic_deprecation_warnings()
__version__ = "1.13.0a6" __version__ = "1.13.0a7"
_telemetry_submitted = False _telemetry_submitted = False
@@ -96,6 +97,10 @@ def __getattr__(name: str) -> Any:
try: try:
from crewai.agents.agent_builder.base_agent import BaseAgent as _BaseAgent
from crewai.agents.agent_builder.base_agent_executor_mixin import (
CrewAgentExecutorMixin as _CrewAgentExecutorMixin,
)
from crewai.agents.tools_handler import ToolsHandler as _ToolsHandler from crewai.agents.tools_handler import ToolsHandler as _ToolsHandler
from crewai.experimental.agent_executor import AgentExecutor as _AgentExecutor from crewai.experimental.agent_executor import AgentExecutor as _AgentExecutor
from crewai.hooks.llm_hooks import LLMCallHookContext as _LLMCallHookContext from crewai.hooks.llm_hooks import LLMCallHookContext as _LLMCallHookContext
@@ -105,25 +110,66 @@ try:
SystemPromptResult as _SystemPromptResult, SystemPromptResult as _SystemPromptResult,
) )
_AgentExecutor.model_rebuild( _base_namespace: dict[str, type] = {
force=True, "Agent": Agent,
_types_namespace={ "Crew": Crew,
"Agent": Agent, "BaseLLM": BaseLLM,
"ToolsHandler": _ToolsHandler, "Task": Task,
"Crew": Crew, "CrewAgentExecutorMixin": _CrewAgentExecutorMixin,
"BaseLLM": BaseLLM, }
"Task": Task,
"StandardPromptResult": _StandardPromptResult, try:
"SystemPromptResult": _SystemPromptResult, from crewai.a2a.config import (
"LLMCallHookContext": _LLMCallHookContext, A2AClientConfig as _A2AClientConfig,
"ToolResult": _ToolResult, A2AConfig as _A2AConfig,
}, A2AServerConfig as _A2AServerConfig,
) )
_base_namespace.update(
{
"A2AConfig": _A2AConfig,
"A2AClientConfig": _A2AClientConfig,
"A2AServerConfig": _A2AServerConfig,
}
)
except ImportError:
pass
import sys
_full_namespace = {
**_base_namespace,
"ToolsHandler": _ToolsHandler,
"StandardPromptResult": _StandardPromptResult,
"SystemPromptResult": _SystemPromptResult,
"LLMCallHookContext": _LLMCallHookContext,
"ToolResult": _ToolResult,
}
_resolve_namespace = {
**_full_namespace,
**sys.modules[_BaseAgent.__module__].__dict__,
}
for _mod_name in (
_BaseAgent.__module__,
Agent.__module__,
_AgentExecutor.__module__,
):
sys.modules[_mod_name].__dict__.update(_resolve_namespace)
_BaseAgent.model_rebuild(force=True, _types_namespace=_full_namespace)
_AgentExecutor.model_rebuild(force=True, _types_namespace=_full_namespace)
try:
Agent.model_rebuild(force=True, _types_namespace=_full_namespace)
except PydanticUserError:
pass
except (ImportError, PydanticUserError): except (ImportError, PydanticUserError):
import logging as _logging import logging as _logging
_logging.getLogger(__name__).warning( _logging.getLogger(__name__).warning(
"AgentExecutor.model_rebuild() failed; forward refs may be unresolved.", "model_rebuild() failed; forward refs may be unresolved.",
exc_info=True, exc_info=True,
) )
@@ -133,6 +179,7 @@ __all__ = [
"BaseLLM", "BaseLLM",
"Crew", "Crew",
"CrewOutput", "CrewOutput",
"ExecutionContext",
"Flow", "Flow",
"Knowledge", "Knowledge",
"LLMGuardrail", "LLMGuardrail",

View File

@@ -25,6 +25,7 @@ from pydantic import (
BaseModel, BaseModel,
ConfigDict, ConfigDict,
Field, Field,
InstanceOf,
PrivateAttr, PrivateAttr,
model_validator, model_validator,
) )
@@ -267,6 +268,9 @@ class Agent(BaseAgent):
Can be a single A2AConfig/A2AClientConfig/A2AServerConfig, or a list of any number of A2AConfig/A2AClientConfig with a single A2AServerConfig. Can be a single A2AConfig/A2AClientConfig/A2AServerConfig, or a list of any number of A2AConfig/A2AClientConfig with a single A2AServerConfig.
""", """,
) )
agent_executor: InstanceOf[CrewAgentExecutor] | InstanceOf[AgentExecutor] | None = (
Field(default=None, description="An instance of the CrewAgentExecutor class.")
)
executor_class: type[CrewAgentExecutor] | type[AgentExecutor] = Field( executor_class: type[CrewAgentExecutor] | type[AgentExecutor] = Field(
default=CrewAgentExecutor, default=CrewAgentExecutor,
description="Class to use for the agent executor. Defaults to CrewAgentExecutor, can optionally use AgentExecutor.", description="Class to use for the agent executor. Defaults to CrewAgentExecutor, can optionally use AgentExecutor.",
@@ -690,7 +694,9 @@ class Agent(BaseAgent):
task_prompt, task_prompt,
knowledge_config, knowledge_config,
self.knowledge.query if self.knowledge else lambda *a, **k: None, self.knowledge.query if self.knowledge else lambda *a, **k: None,
self.crew.query_knowledge if self.crew else lambda *a, **k: None, self.crew.query_knowledge
if self.crew and not isinstance(self.crew, str)
else lambda *a, **k: None,
) )
task_prompt = self._finalize_task_prompt(task_prompt, tools, task) task_prompt = self._finalize_task_prompt(task_prompt, tools, task)
@@ -777,14 +783,18 @@ class Agent(BaseAgent):
if not self.agent_executor: if not self.agent_executor:
raise RuntimeError("Agent executor is not initialized.") raise RuntimeError("Agent executor is not initialized.")
return self.agent_executor.invoke( result = cast(
{ dict[str, Any],
"input": task_prompt, self.agent_executor.invoke(
"tool_names": self.agent_executor.tools_names, {
"tools": self.agent_executor.tools_description, "input": task_prompt,
"ask_for_human_input": task.human_input, "tool_names": self.agent_executor.tools_names,
} "tools": self.agent_executor.tools_description,
)["output"] "ask_for_human_input": task.human_input,
}
),
)
return result["output"]
async def aexecute_task( async def aexecute_task(
self, self,
@@ -955,19 +965,23 @@ class Agent(BaseAgent):
if self.agent_executor is not None: if self.agent_executor is not None:
self._update_executor_parameters( self._update_executor_parameters(
task=task, task=task,
tools=parsed_tools, # type: ignore[arg-type] tools=parsed_tools,
raw_tools=raw_tools, raw_tools=raw_tools,
prompt=prompt, prompt=prompt,
stop_words=stop_words, stop_words=stop_words,
rpm_limit_fn=rpm_limit_fn, rpm_limit_fn=rpm_limit_fn,
) )
else: else:
if not isinstance(self.llm, BaseLLM):
raise RuntimeError(
"LLM must be resolved before creating agent executor."
)
self.agent_executor = self.executor_class( self.agent_executor = self.executor_class(
llm=cast(BaseLLM, self.llm), llm=self.llm,
task=task, # type: ignore[arg-type] task=task, # type: ignore[arg-type]
i18n=self.i18n, i18n=self.i18n,
agent=self, agent=self,
crew=self.crew, crew=self.crew, # type: ignore[arg-type]
tools=parsed_tools, tools=parsed_tools,
prompt=prompt, prompt=prompt,
original_tools=raw_tools, original_tools=raw_tools,
@@ -991,7 +1005,7 @@ class Agent(BaseAgent):
def _update_executor_parameters( def _update_executor_parameters(
self, self,
task: Task | None, task: Task | None,
tools: list[BaseTool], tools: list[CrewStructuredTool],
raw_tools: list[BaseTool], raw_tools: list[BaseTool],
prompt: SystemPromptResult | StandardPromptResult, prompt: SystemPromptResult | StandardPromptResult,
stop_words: list[str], stop_words: list[str],
@@ -1007,11 +1021,17 @@ class Agent(BaseAgent):
stop_words: Stop words list. stop_words: Stop words list.
rpm_limit_fn: RPM limit callback function. rpm_limit_fn: RPM limit callback function.
""" """
if self.agent_executor is None:
raise RuntimeError("Agent executor is not initialized.")
self.agent_executor.task = task self.agent_executor.task = task
self.agent_executor.tools = tools self.agent_executor.tools = tools
self.agent_executor.original_tools = raw_tools self.agent_executor.original_tools = raw_tools
self.agent_executor.prompt = prompt self.agent_executor.prompt = prompt
self.agent_executor.stop_words = stop_words if isinstance(self.agent_executor, AgentExecutor):
self.agent_executor.stop_words = stop_words
else:
self.agent_executor.stop = stop_words
self.agent_executor.tools_names = get_tool_names(tools) self.agent_executor.tools_names = get_tool_names(tools)
self.agent_executor.tools_description = render_text_description_and_args(tools) self.agent_executor.tools_description = render_text_description_and_args(tools)
self.agent_executor.response_model = ( self.agent_executor.response_model = (
@@ -1787,21 +1807,3 @@ class Agent(BaseAgent):
LiteAgentOutput: The result of the agent execution. LiteAgentOutput: The result of the agent execution.
""" """
return await self.kickoff_async(messages, response_format, input_files) return await self.kickoff_async(messages, response_format, input_files)
try:
from crewai.a2a.config import (
A2AClientConfig as _A2AClientConfig,
A2AConfig as _A2AConfig,
A2AServerConfig as _A2AServerConfig,
)
Agent.model_rebuild(
_types_namespace={
"A2AConfig": _A2AConfig,
"A2AClientConfig": _A2AClientConfig,
"A2AServerConfig": _A2AServerConfig,
}
)
except ImportError:
pass

View File

@@ -137,7 +137,8 @@ def handle_knowledge_retrieval(
Returns: Returns:
The task prompt potentially augmented with knowledge context. The task prompt potentially augmented with knowledge context.
""" """
if not (agent.knowledge or (agent.crew and agent.crew.knowledge)): _crew = agent.crew if not isinstance(agent.crew, str) else None
if not (agent.knowledge or (_crew and _crew.knowledge)):
return task_prompt return task_prompt
crewai_event_bus.emit( crewai_event_bus.emit(
@@ -244,7 +245,7 @@ def apply_training_data(agent: Agent, task_prompt: str) -> str:
Returns: Returns:
The task prompt with training data applied. The task prompt with training data applied.
""" """
if agent.crew and agent.crew._train: if agent.crew and not isinstance(agent.crew, str) and agent.crew._train:
return agent._training_handler(task_prompt=task_prompt) return agent._training_handler(task_prompt=task_prompt)
return agent._use_trained_data(task_prompt=task_prompt) return agent._use_trained_data(task_prompt=task_prompt)
@@ -355,7 +356,8 @@ async def ahandle_knowledge_retrieval(
Returns: Returns:
The task prompt potentially augmented with knowledge context. The task prompt potentially augmented with knowledge context.
""" """
if not (agent.knowledge or (agent.crew and agent.crew.knowledge)): _crew = agent.crew if not isinstance(agent.crew, str) else None
if not (agent.knowledge or (_crew and _crew.knowledge)):
return task_prompt return task_prompt
crewai_event_bus.emit( crewai_event_bus.emit(
@@ -381,15 +383,16 @@ async def ahandle_knowledge_retrieval(
if agent.agent_knowledge_context: if agent.agent_knowledge_context:
task_prompt += agent.agent_knowledge_context task_prompt += agent.agent_knowledge_context
knowledge_snippets = await agent.crew.aquery_knowledge( if _crew:
[agent.knowledge_search_query], **knowledge_config knowledge_snippets = await _crew.aquery_knowledge(
) [agent.knowledge_search_query], **knowledge_config
if knowledge_snippets:
agent.crew_knowledge_context = extract_knowledge_context(
knowledge_snippets
) )
if agent.crew_knowledge_context: if knowledge_snippets:
task_prompt += agent.crew_knowledge_context agent.crew_knowledge_context = extract_knowledge_context(
knowledge_snippets
)
if agent.crew_knowledge_context:
task_prompt += agent.crew_knowledge_context
crewai_event_bus.emit( crewai_event_bus.emit(
agent, agent,

View File

@@ -188,14 +188,14 @@ class OpenAIAgentAdapter(BaseAgentAdapter):
self._openai_agent = OpenAIAgent( self._openai_agent = OpenAIAgent(
name=self.role, name=self.role,
instructions=instructions, instructions=instructions,
model=self.llm, model=str(self.llm),
**self._agent_config or {}, **self._agent_config or {},
) )
if all_tools: if all_tools:
self.configure_tools(all_tools) self.configure_tools(all_tools)
self.agent_executor = Runner self.agent_executor = Runner # type: ignore[assignment]
def configure_tools(self, tools: list[BaseTool] | None = None) -> None: def configure_tools(self, tools: list[BaseTool] | None = None) -> None:
"""Configure tools for the OpenAI Assistant. """Configure tools for the OpenAI Assistant.

View File

@@ -5,21 +5,25 @@ from copy import copy as shallow_copy
from hashlib import md5 from hashlib import md5
from pathlib import Path from pathlib import Path
import re import re
from typing import Any, Final, Literal from typing import TYPE_CHECKING, Annotated, Any, Final, Literal
import uuid import uuid
from pydantic import ( from pydantic import (
UUID4, UUID4,
BaseModel, BaseModel,
BeforeValidator,
Field, Field,
InstanceOf,
PrivateAttr, PrivateAttr,
field_validator, field_validator,
model_validator, model_validator,
) )
from pydantic.functional_serializers import PlainSerializer
from pydantic_core import PydanticCustomError from pydantic_core import PydanticCustomError
from typing_extensions import Self from typing_extensions import Self
from crewai.agent.internal.meta import AgentMeta from crewai.agent.internal.meta import AgentMeta
from crewai.agents.agent_builder.base_agent_executor_mixin import CrewAgentExecutorMixin
from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess from crewai.agents.agent_builder.utilities.base_token_process import TokenProcess
from crewai.agents.cache.cache_handler import CacheHandler from crewai.agents.cache.cache_handler import CacheHandler
from crewai.agents.tools_handler import ToolsHandler from crewai.agents.tools_handler import ToolsHandler
@@ -27,6 +31,7 @@ from crewai.knowledge.knowledge import Knowledge
from crewai.knowledge.knowledge_config import KnowledgeConfig from crewai.knowledge.knowledge_config import KnowledgeConfig
from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource from crewai.knowledge.source.base_knowledge_source import BaseKnowledgeSource
from crewai.knowledge.storage.base_knowledge_storage import BaseKnowledgeStorage from crewai.knowledge.storage.base_knowledge_storage import BaseKnowledgeStorage
from crewai.llms.base_llm import BaseLLM
from crewai.mcp.config import MCPServerConfig from crewai.mcp.config import MCPServerConfig
from crewai.memory.memory_scope import MemoryScope, MemorySlice from crewai.memory.memory_scope import MemoryScope, MemorySlice
from crewai.memory.unified_memory import Memory from crewai.memory.unified_memory import Memory
@@ -42,6 +47,20 @@ from crewai.utilities.rpm_controller import RPMController
from crewai.utilities.string_utils import interpolate_only from crewai.utilities.string_utils import interpolate_only
if TYPE_CHECKING:
from crewai.crew import Crew
def _validate_crew_ref(value: Any) -> Any:
return value
def _serialize_crew_ref(value: Any) -> str | None:
if value is None:
return None
return str(value.id) if hasattr(value, "id") else str(value)
_SLUG_RE: Final[re.Pattern[str]] = re.compile( _SLUG_RE: Final[re.Pattern[str]] = re.compile(
r"^(?:crewai-amp:)?[a-zA-Z0-9][a-zA-Z0-9_-]*(?:#[\w-]+)?$" r"^(?:crewai-amp:)?[a-zA-Z0-9][a-zA-Z0-9_-]*(?:#[\w-]+)?$"
) )
@@ -122,7 +141,7 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta):
__hash__ = object.__hash__ __hash__ = object.__hash__
_logger: Logger = PrivateAttr(default_factory=lambda: Logger(verbose=False)) _logger: Logger = PrivateAttr(default_factory=lambda: Logger(verbose=False))
_rpm_controller: RPMController | None = PrivateAttr(default=None) _rpm_controller: RPMController | None = PrivateAttr(default=None)
_request_within_rpm_limit: Any = PrivateAttr(default=None) _request_within_rpm_limit: SerializableCallable | None = PrivateAttr(default=None)
_original_role: str | None = PrivateAttr(default=None) _original_role: str | None = PrivateAttr(default=None)
_original_goal: str | None = PrivateAttr(default=None) _original_goal: str | None = PrivateAttr(default=None)
_original_backstory: str | None = PrivateAttr(default=None) _original_backstory: str | None = PrivateAttr(default=None)
@@ -154,13 +173,19 @@ class BaseAgent(BaseModel, ABC, metaclass=AgentMeta):
max_iter: int = Field( max_iter: int = Field(
default=25, description="Maximum iterations for an agent to execute a task" default=25, description="Maximum iterations for an agent to execute a task"
) )
agent_executor: Any = Field( agent_executor: InstanceOf[CrewAgentExecutorMixin] | None = Field(
default=None, description="An instance of the CrewAgentExecutor class." default=None, description="An instance of the CrewAgentExecutor class."
) )
llm: Any = Field( llm: str | BaseLLM | None = Field(
default=None, description="Language model that will run the agent." default=None, description="Language model that will run the agent."
) )
crew: Any = Field(default=None, description="Crew to which the agent belongs.") crew: Annotated[
Crew | str | None,
BeforeValidator(_validate_crew_ref),
PlainSerializer(
_serialize_crew_ref, return_type=str | None, when_used="always"
),
] = Field(default=None, description="Crew to which the agent belongs.")
i18n: I18N = Field( i18n: I18N = Field(
default_factory=get_i18n, description="Internationalization settings." default_factory=get_i18n, description="Internationalization settings."
) )

View File

@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
authors = [{ name = "Your Name", email = "you@example.com" }] authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<3.14" requires-python = ">=3.10,<3.14"
dependencies = [ dependencies = [
"crewai[tools]==1.13.0a6" "crewai[tools]==1.13.0a7"
] ]
[project.scripts] [project.scripts]

View File

@@ -5,7 +5,7 @@ description = "{{name}} using crewAI"
authors = [{ name = "Your Name", email = "you@example.com" }] authors = [{ name = "Your Name", email = "you@example.com" }]
requires-python = ">=3.10,<3.14" requires-python = ">=3.10,<3.14"
dependencies = [ dependencies = [
"crewai[tools]==1.13.0a6" "crewai[tools]==1.13.0a7"
] ]
[project.scripts] [project.scripts]

View File

@@ -5,7 +5,7 @@ description = "Power up your crews with {{folder_name}}"
readme = "README.md" readme = "README.md"
requires-python = ">=3.10,<3.14" requires-python = ">=3.10,<3.14"
dependencies = [ dependencies = [
"crewai[tools]==1.13.0a6" "crewai[tools]==1.13.0a7"
] ]
[tool.crewai] [tool.crewai]

View File

@@ -266,7 +266,7 @@ class Crew(FlowTrackable, BaseModel):
default=False, default=False,
description="Plan the crew execution and add the plan to the crew.", description="Plan the crew execution and add the plan to the crew.",
) )
planning_llm: str | BaseLLM | Any | None = Field( planning_llm: str | BaseLLM | None = Field(
default=None, default=None,
description=( description=(
"Language model that will run the AgentPlanner if planning is True." "Language model that will run the AgentPlanner if planning is True."
@@ -287,7 +287,7 @@ class Crew(FlowTrackable, BaseModel):
"knowledge object." "knowledge object."
), ),
) )
chat_llm: str | BaseLLM | Any | None = Field( chat_llm: str | BaseLLM | None = Field(
default=None, default=None,
description="LLM used to handle chatting with the crew.", description="LLM used to handle chatting with the crew.",
) )
@@ -1311,7 +1311,7 @@ class Crew(FlowTrackable, BaseModel):
and hasattr(agent, "multimodal") and hasattr(agent, "multimodal")
and getattr(agent, "multimodal", False) and getattr(agent, "multimodal", False)
): ):
if not (agent.llm and agent.llm.supports_multimodal()): if not (isinstance(agent.llm, BaseLLM) and agent.llm.supports_multimodal()):
tools = self._add_multimodal_tools(agent, tools) tools = self._add_multimodal_tools(agent, tools)
if agent and (hasattr(agent, "apps") and getattr(agent, "apps", None)): if agent and (hasattr(agent, "apps") and getattr(agent, "apps", None)):
@@ -1328,7 +1328,11 @@ class Crew(FlowTrackable, BaseModel):
files = get_all_files(self.id, task.id) files = get_all_files(self.id, task.id)
if files: if files:
supported_types: list[str] = [] supported_types: list[str] = []
if agent and agent.llm and agent.llm.supports_multimodal(): if (
agent
and isinstance(agent.llm, BaseLLM)
and agent.llm.supports_multimodal()
):
provider = ( provider = (
getattr(agent.llm, "provider", None) getattr(agent.llm, "provider", None)
or getattr(agent.llm, "model", None) or getattr(agent.llm, "model", None)
@@ -1781,17 +1785,10 @@ class Crew(FlowTrackable, BaseModel):
token_sum = self.manager_agent._token_process.get_summary() token_sum = self.manager_agent._token_process.get_summary()
total_usage_metrics.add_usage_metrics(token_sum) total_usage_metrics.add_usage_metrics(token_sum)
if ( if self.manager_agent:
self.manager_agent
and hasattr(self.manager_agent, "llm")
and hasattr(self.manager_agent.llm, "get_token_usage_summary")
):
if isinstance(self.manager_agent.llm, BaseLLM): if isinstance(self.manager_agent.llm, BaseLLM):
llm_usage = self.manager_agent.llm.get_token_usage_summary() llm_usage = self.manager_agent.llm.get_token_usage_summary()
else: total_usage_metrics.add_usage_metrics(llm_usage)
llm_usage = self.manager_agent.llm._token_process.get_summary()
total_usage_metrics.add_usage_metrics(llm_usage)
self.usage_metrics = total_usage_metrics self.usage_metrics = total_usage_metrics
return total_usage_metrics return total_usage_metrics

View File

@@ -11,6 +11,7 @@ from opentelemetry import baggage
from crewai.agents.agent_builder.base_agent import BaseAgent from crewai.agents.agent_builder.base_agent import BaseAgent
from crewai.crews.crew_output import CrewOutput from crewai.crews.crew_output import CrewOutput
from crewai.llms.base_llm import BaseLLM
from crewai.rag.embeddings.types import EmbedderConfig from crewai.rag.embeddings.types import EmbedderConfig
from crewai.skills.loader import activate_skill, discover_skills from crewai.skills.loader import activate_skill, discover_skills
from crewai.skills.models import INSTRUCTIONS, Skill as SkillModel from crewai.skills.models import INSTRUCTIONS, Skill as SkillModel
@@ -50,7 +51,7 @@ def enable_agent_streaming(agents: Iterable[BaseAgent]) -> None:
agents: Iterable of agents to enable streaming on. agents: Iterable of agents to enable streaming on.
""" """
for agent in agents: for agent in agents:
if agent.llm is not None: if isinstance(agent.llm, BaseLLM):
agent.llm.stream = True agent.llm.stream = True

View File

@@ -25,13 +25,25 @@ def _get_or_create_counter() -> Iterator[int]:
return counter return counter
_last_emitted: contextvars.ContextVar[int] = contextvars.ContextVar(
"_last_emitted", default=0
)
def get_next_emission_sequence() -> int: def get_next_emission_sequence() -> int:
"""Get the next emission sequence number. """Get the next emission sequence number.
Returns: Returns:
The next sequence number. The next sequence number.
""" """
return next(_get_or_create_counter()) seq = next(_get_or_create_counter())
_last_emitted.set(seq)
return seq
def get_emission_sequence() -> int:
"""Get the current emission sequence value without incrementing."""
return _last_emitted.get()
def reset_emission_counter() -> None: def reset_emission_counter() -> None:
@@ -41,6 +53,14 @@ def reset_emission_counter() -> None:
""" """
counter: Iterator[int] = itertools.count(start=1) counter: Iterator[int] = itertools.count(start=1)
_emission_counter.set(counter) _emission_counter.set(counter)
_last_emitted.set(0)
def set_emission_counter(start: int) -> None:
"""Set the emission counter to resume from a given value."""
counter: Iterator[int] = itertools.count(start=start + 1)
_emission_counter.set(counter)
_last_emitted.set(start)
class BaseEvent(BaseModel): class BaseEvent(BaseModel):

View File

@@ -0,0 +1,80 @@
"""Checkpointable execution context for the crewAI runtime.
Captures the ContextVar state needed to resume execution from a checkpoint.
Used by the RootModel (step 5) to include execution context in snapshots.
"""
from __future__ import annotations
from typing import Any
from pydantic import BaseModel, Field
from crewai.context import (
_current_task_id,
_platform_integration_token,
)
from crewai.events.base_events import (
get_emission_sequence,
set_emission_counter,
)
from crewai.events.event_context import (
_event_id_stack,
_last_event_id,
_triggering_event_id,
)
from crewai.flow.flow_context import (
current_flow_id,
current_flow_method_name,
current_flow_request_id,
)
class ExecutionContext(BaseModel):
"""Snapshot of ContextVar state required for checkpoint/resume."""
current_task_id: str | None = Field(default=None)
flow_request_id: str | None = Field(default=None)
flow_id: str | None = Field(default=None)
flow_method_name: str = Field(default="unknown")
event_id_stack: tuple[tuple[str, str], ...] = Field(default=())
last_event_id: str | None = Field(default=None)
triggering_event_id: str | None = Field(default=None)
emission_sequence: int = Field(default=0)
feedback_callback_info: dict[str, Any] | None = Field(default=None)
platform_token: str | None = Field(default=None)
def capture_execution_context(
feedback_callback_info: dict[str, Any] | None = None,
) -> ExecutionContext:
"""Read all checkpoint-required ContextVars into an ExecutionContext."""
return ExecutionContext(
current_task_id=_current_task_id.get(),
flow_request_id=current_flow_request_id.get(),
flow_id=current_flow_id.get(),
flow_method_name=current_flow_method_name.get(),
event_id_stack=_event_id_stack.get(),
last_event_id=_last_event_id.get(),
triggering_event_id=_triggering_event_id.get(),
emission_sequence=get_emission_sequence(),
feedback_callback_info=feedback_callback_info,
platform_token=_platform_integration_token.get(),
)
def apply_execution_context(ctx: ExecutionContext) -> None:
"""Write an ExecutionContext back into the ContextVars."""
_current_task_id.set(ctx.current_task_id)
current_flow_request_id.set(ctx.flow_request_id)
current_flow_id.set(ctx.flow_id)
current_flow_method_name.set(ctx.flow_method_name)
_event_id_stack.set(ctx.event_id_stack)
_last_event_id.set(ctx.last_event_id)
_triggering_event_id.set(ctx.triggering_event_id)
set_emission_counter(ctx.emission_sequence)
_platform_integration_token.set(ctx.platform_token)

View File

@@ -41,6 +41,7 @@ from crewai.events.types.task_events import (
TaskFailedEvent, TaskFailedEvent,
TaskStartedEvent, TaskStartedEvent,
) )
from crewai.llms.base_llm import BaseLLM
from crewai.security import Fingerprint, SecurityConfig from crewai.security import Fingerprint, SecurityConfig
from crewai.tasks.output_format import OutputFormat from crewai.tasks.output_format import OutputFormat
from crewai.tasks.task_output import TaskOutput from crewai.tasks.task_output import TaskOutput
@@ -316,6 +317,10 @@ class Task(BaseModel):
if self.agent is None: if self.agent is None:
raise ValueError("Agent is required to use LLMGuardrail") raise ValueError("Agent is required to use LLMGuardrail")
if not isinstance(self.agent.llm, BaseLLM):
raise ValueError(
"Agent must have a BaseLLM instance to use LLMGuardrail"
)
self._guardrail = cast( self._guardrail = cast(
GuardrailCallable, GuardrailCallable,
LLMGuardrail(description=self.guardrail, llm=self.agent.llm), LLMGuardrail(description=self.guardrail, llm=self.agent.llm),
@@ -339,6 +344,10 @@ class Task(BaseModel):
) )
from crewai.tasks.llm_guardrail import LLMGuardrail from crewai.tasks.llm_guardrail import LLMGuardrail
if not isinstance(self.agent.llm, BaseLLM):
raise ValueError(
"Agent must have a BaseLLM instance to use LLMGuardrail"
)
guardrails.append( guardrails.append(
cast( cast(
GuardrailCallable, GuardrailCallable,
@@ -359,6 +368,10 @@ class Task(BaseModel):
) )
from crewai.tasks.llm_guardrail import LLMGuardrail from crewai.tasks.llm_guardrail import LLMGuardrail
if not isinstance(self.agent.llm, BaseLLM):
raise ValueError(
"Agent must have a BaseLLM instance to use LLMGuardrail"
)
guardrails.append( guardrails.append(
cast( cast(
GuardrailCallable, GuardrailCallable,
@@ -646,7 +659,12 @@ class Task(BaseModel):
await cb_result await cb_result
crew = self.agent.crew # type: ignore[union-attr] crew = self.agent.crew # type: ignore[union-attr]
if crew and crew.task_callback and crew.task_callback != self.callback: if (
crew
and not isinstance(crew, str)
and crew.task_callback
and crew.task_callback != self.callback
):
cb_result = crew.task_callback(self.output) cb_result = crew.task_callback(self.output)
if inspect.isawaitable(cb_result): if inspect.isawaitable(cb_result):
await cb_result await cb_result
@@ -761,7 +779,12 @@ class Task(BaseModel):
asyncio.run(cb_result) asyncio.run(cb_result)
crew = self.agent.crew # type: ignore[union-attr] crew = self.agent.crew # type: ignore[union-attr]
if crew and crew.task_callback and crew.task_callback != self.callback: if (
crew
and not isinstance(crew, str)
and crew.task_callback
and crew.task_callback != self.callback
):
cb_result = crew.task_callback(self.output) cb_result = crew.task_callback(self.output)
if inspect.iscoroutine(cb_result): if inspect.iscoroutine(cb_result):
asyncio.run(cb_result) asyncio.run(cb_result)
@@ -812,11 +835,14 @@ class Task(BaseModel):
if trigger_payload is not None: if trigger_payload is not None:
description += f"\n\nTrigger Payload: {trigger_payload}" description += f"\n\nTrigger Payload: {trigger_payload}"
if self.agent and self.agent.crew: if self.agent and self.agent.crew and not isinstance(self.agent.crew, str):
files = get_all_files(self.agent.crew.id, self.id) files = get_all_files(self.agent.crew.id, self.id)
if files: if files:
supported_types: list[str] = [] supported_types: list[str] = []
if self.agent.llm and self.agent.llm.supports_multimodal(): if (
isinstance(self.agent.llm, BaseLLM)
and self.agent.llm.supports_multimodal()
):
provider: str = str( provider: str = str(
getattr(self.agent.llm, "provider", None) getattr(self.agent.llm, "provider", None)
or getattr(self.agent.llm, "model", "openai") or getattr(self.agent.llm, "model", "openai")

View File

@@ -41,6 +41,7 @@ from crewai.events.types.system_events import (
SigTStpEvent, SigTStpEvent,
SigTermEvent, SigTermEvent,
) )
from crewai.llms.base_llm import BaseLLM
from crewai.telemetry.constants import ( from crewai.telemetry.constants import (
CREWAI_TELEMETRY_BASE_URL, CREWAI_TELEMETRY_BASE_URL,
CREWAI_TELEMETRY_SERVICE_NAME, CREWAI_TELEMETRY_SERVICE_NAME,
@@ -323,7 +324,9 @@ class Telemetry:
if getattr(agent, "function_calling_llm", None) if getattr(agent, "function_calling_llm", None)
else "" else ""
), ),
"llm": agent.llm.model, "llm": agent.llm.model
if isinstance(agent.llm, BaseLLM)
else str(agent.llm),
"delegation_enabled?": agent.allow_delegation, "delegation_enabled?": agent.allow_delegation,
"allow_code_execution?": getattr( "allow_code_execution?": getattr(
agent, "allow_code_execution", False agent, "allow_code_execution", False
@@ -427,7 +430,9 @@ class Telemetry:
if getattr(agent, "function_calling_llm", None) if getattr(agent, "function_calling_llm", None)
else "" else ""
), ),
"llm": agent.llm.model, "llm": agent.llm.model
if isinstance(agent.llm, BaseLLM)
else str(agent.llm),
"delegation_enabled?": agent.allow_delegation, "delegation_enabled?": agent.allow_delegation,
"allow_code_execution?": getattr( "allow_code_execution?": getattr(
agent, "allow_code_execution", False agent, "allow_code_execution", False
@@ -840,7 +845,9 @@ class Telemetry:
"max_iter": agent.max_iter, "max_iter": agent.max_iter,
"max_rpm": agent.max_rpm, "max_rpm": agent.max_rpm,
"i18n": agent.i18n.prompt_file, "i18n": agent.i18n.prompt_file,
"llm": agent.llm.model, "llm": agent.llm.model
if isinstance(agent.llm, BaseLLM)
else str(agent.llm),
"delegation_enabled?": agent.allow_delegation, "delegation_enabled?": agent.allow_delegation,
"tools_names": [ "tools_names": [
sanitize_tool_name(tool.name) sanitize_tool_name(tool.name)

View File

@@ -1,3 +1,3 @@
"""CrewAI development tools.""" """CrewAI development tools."""
__version__ = "1.13.0a6" __version__ = "1.13.0a7"