From c1ae3da1cd99885d81341d3a417cd44e6fb5a29b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 15:26:38 +0000 Subject: [PATCH] fix: address review feedback - integer key coercion and in-memory cache serializers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Convert train_iteration to string keys in crew_agent_executor.py, experimental/agent_executor.py, and training_handler.py for JSON compatibility - Revert file_store.py and agent_card.py back to PickleSerializer since they use in-memory caches only (no untrusted deserialization risk) - Add explanatory comments for why PickleSerializer is safe for in-memory caches Co-Authored-By: João --- lib/crewai/src/crewai/a2a/utils/agent_card.py | 7 +++++-- lib/crewai/src/crewai/agents/crew_agent_executor.py | 11 +++++++---- lib/crewai/src/crewai/experimental/agent_executor.py | 11 +++++++---- lib/crewai/src/crewai/utilities/file_store.py | 7 +++++-- lib/crewai/src/crewai/utilities/training_handler.py | 2 +- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/lib/crewai/src/crewai/a2a/utils/agent_card.py b/lib/crewai/src/crewai/a2a/utils/agent_card.py index 9f2a0397c..304959030 100644 --- a/lib/crewai/src/crewai/a2a/utils/agent_card.py +++ b/lib/crewai/src/crewai/a2a/utils/agent_card.py @@ -14,7 +14,7 @@ from typing import TYPE_CHECKING from a2a.client.errors import A2AClientHTTPError from a2a.types import AgentCapabilities, AgentCard, AgentSkill from aiocache import cached # type: ignore[import-untyped] -from aiocache.serializers import JsonSerializer # type: ignore[import-untyped] +from aiocache.serializers import PickleSerializer # type: ignore[import-untyped] import httpx from crewai.a2a.auth.client_schemes import APIKeyAuth, HTTPDigestAuth @@ -220,7 +220,10 @@ def _fetch_agent_card_cached( return asyncio.run(coro) -@cached(ttl=300, serializer=JsonSerializer()) # type: ignore[untyped-decorator] +# PickleSerializer is safe here: this is an in-memory cache only. +# Data never leaves the process, so there is no untrusted deserialization risk. +# JsonSerializer would break AgentCard (Pydantic model) serialization. +@cached(ttl=300, serializer=PickleSerializer()) # type: ignore[untyped-decorator] async def _afetch_agent_card_cached( endpoint: str, auth_hash: str, diff --git a/lib/crewai/src/crewai/agents/crew_agent_executor.py b/lib/crewai/src/crewai/agents/crew_agent_executor.py index ac1cccbeb..61549b7ca 100644 --- a/lib/crewai/src/crewai/agents/crew_agent_executor.py +++ b/lib/crewai/src/crewai/agents/crew_agent_executor.py @@ -8,8 +8,8 @@ from __future__ import annotations import asyncio from collections.abc import Callable -import contextvars from concurrent.futures import ThreadPoolExecutor, as_completed +import contextvars import inspect import logging from typing import TYPE_CHECKING, Any, Literal, cast @@ -1599,16 +1599,19 @@ class CrewAgentExecutor(CrewAgentExecutorMixin): # Initialize or retrieve agent's training data agent_training_data = training_data.get(agent_id, {}) + # Use string key for JSON compatibility (JSON converts int keys to strings) + train_key = str(train_iteration) + if human_feedback is not None: # Save initial output and human feedback - agent_training_data[train_iteration] = { + agent_training_data[train_key] = { "initial_output": result.output, "human_feedback": human_feedback, } else: # Save improved output - if train_iteration in agent_training_data: - agent_training_data[train_iteration]["improved_output"] = result.output + if train_key in agent_training_data: + agent_training_data[train_key]["improved_output"] = result.output else: if self.agent.verbose: self._printer.print( diff --git a/lib/crewai/src/crewai/experimental/agent_executor.py b/lib/crewai/src/crewai/experimental/agent_executor.py index b0662f6c6..b1649facb 100644 --- a/lib/crewai/src/crewai/experimental/agent_executor.py +++ b/lib/crewai/src/crewai/experimental/agent_executor.py @@ -1,9 +1,9 @@ from __future__ import annotations import asyncio -import contextvars from collections.abc import Callable, Coroutine from concurrent.futures import ThreadPoolExecutor, as_completed +import contextvars from datetime import datetime import inspect import json @@ -1492,16 +1492,19 @@ class AgentExecutor(Flow[AgentReActState], CrewAgentExecutorMixin): # Initialize or retrieve agent's training data agent_training_data = training_data.get(agent_id, {}) + # Use string key for JSON compatibility (JSON converts int keys to strings) + train_key = str(train_iteration) + if human_feedback is not None: # Save initial output and human feedback - agent_training_data[train_iteration] = { + agent_training_data[train_key] = { "initial_output": result.output, "human_feedback": human_feedback, } else: # Save improved output - if train_iteration in agent_training_data: - agent_training_data[train_iteration]["improved_output"] = result.output + if train_key in agent_training_data: + agent_training_data[train_key]["improved_output"] = result.output else: train_error = Text() train_error.append("❌ ", style="red bold") diff --git a/lib/crewai/src/crewai/utilities/file_store.py b/lib/crewai/src/crewai/utilities/file_store.py index dcc1b7fab..6847bfe1d 100644 --- a/lib/crewai/src/crewai/utilities/file_store.py +++ b/lib/crewai/src/crewai/utilities/file_store.py @@ -20,9 +20,12 @@ _file_store: Cache | None = None try: from aiocache import Cache - from aiocache.serializers import JsonSerializer + from aiocache.serializers import PickleSerializer - _file_store = Cache(Cache.MEMORY, serializer=JsonSerializer()) + # PickleSerializer is safe here: this is an in-memory cache only. + # Data never leaves the process, so there is no untrusted deserialization risk. + # JsonSerializer would break FileInput objects (Pydantic models with IO streams). + _file_store = Cache(Cache.MEMORY, serializer=PickleSerializer()) except ImportError: logger.debug( "aiocache is not installed. File store features will be disabled. " diff --git a/lib/crewai/src/crewai/utilities/training_handler.py b/lib/crewai/src/crewai/utilities/training_handler.py index 98d781e11..c24790c1e 100644 --- a/lib/crewai/src/crewai/utilities/training_handler.py +++ b/lib/crewai/src/crewai/utilities/training_handler.py @@ -27,7 +27,7 @@ class CrewTrainingHandler(PickleHandler): data = self.load() if agent_id not in data: data[agent_id] = {} - data[agent_id][train_iteration] = new_data + data[agent_id][str(train_iteration)] = new_data self.save(data) def clear(self) -> None: