mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-01 13:18:10 +00:00
fix: address review feedback - integer key coercion and in-memory cache serializers
- 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 <joao@crewai.com>
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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. "
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user