mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-05 23:19:22 +00:00
Some checks failed
Refactor agent executor to delegate human interactions to a provider: add messages and ask_for_human_input properties, implement _invoke_loop and _format_feedback_message, and replace the internal iterative/training feedback logic with a call to get_provider().handle_feedback. Make LLMGuardrail kickoff coroutine-aware by detecting coroutines and running them via asyncio.run so both sync and async agents are supported. Make telemetry more robust by safely handling missing task.output (use empty string) and returning early if span is None before setting attributes. Improve serialization to detect circular references via an _ancestors set, propagate it through recursive calls, and pass exclude/max_depth/_current_depth consistently to prevent infinite recursion and produce stable serializable output.
128 lines
3.7 KiB
Python
128 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import date, datetime
|
|
import json
|
|
from typing import Any, TypeAlias
|
|
import uuid
|
|
|
|
from pydantic import BaseModel
|
|
|
|
|
|
SerializablePrimitive: TypeAlias = str | int | float | bool | None
|
|
Serializable: TypeAlias = (
|
|
SerializablePrimitive | list["Serializable"] | dict[str, "Serializable"]
|
|
)
|
|
|
|
|
|
def to_serializable(
|
|
obj: Any,
|
|
exclude: set[str] | None = None,
|
|
max_depth: int = 5,
|
|
_current_depth: int = 0,
|
|
_ancestors: set[int] | None = None,
|
|
) -> Serializable:
|
|
"""Converts a Python object into a JSON-compatible representation.
|
|
|
|
Supports primitives, datetime objects, collections, dictionaries, and
|
|
Pydantic models. Recursion depth is limited to prevent infinite nesting.
|
|
Non-convertible objects default to their string representations.
|
|
|
|
Args:
|
|
obj: Object to transform.
|
|
exclude: Set of keys to exclude from the result.
|
|
max_depth: Maximum recursion depth. Defaults to 5.
|
|
_current_depth: Current recursion depth (for internal use).
|
|
_ancestors: Set of ancestor object ids for cycle detection (for internal use).
|
|
|
|
Returns:
|
|
Serializable: A JSON-compatible structure.
|
|
"""
|
|
if _current_depth >= max_depth:
|
|
return repr(obj)
|
|
|
|
if exclude is None:
|
|
exclude = set()
|
|
|
|
if _ancestors is None:
|
|
_ancestors = set()
|
|
|
|
if isinstance(obj, (str, int, float, bool, type(None))):
|
|
return obj
|
|
if isinstance(obj, uuid.UUID):
|
|
return str(obj)
|
|
if isinstance(obj, (date, datetime)):
|
|
return obj.isoformat()
|
|
|
|
object_id = id(obj)
|
|
if object_id in _ancestors:
|
|
return f"<circular_ref:{type(obj).__name__}>"
|
|
new_ancestors = _ancestors | {object_id}
|
|
|
|
if isinstance(obj, (list, tuple, set)):
|
|
return [
|
|
to_serializable(
|
|
item,
|
|
exclude=exclude,
|
|
max_depth=max_depth,
|
|
_current_depth=_current_depth + 1,
|
|
_ancestors=new_ancestors,
|
|
)
|
|
for item in obj
|
|
]
|
|
if isinstance(obj, dict):
|
|
return {
|
|
_to_serializable_key(key): to_serializable(
|
|
obj=value,
|
|
exclude=exclude,
|
|
max_depth=max_depth,
|
|
_current_depth=_current_depth + 1,
|
|
_ancestors=new_ancestors,
|
|
)
|
|
for key, value in obj.items()
|
|
if key not in exclude
|
|
}
|
|
if isinstance(obj, BaseModel):
|
|
try:
|
|
return to_serializable(
|
|
obj=obj.model_dump(exclude=exclude),
|
|
max_depth=max_depth,
|
|
_current_depth=_current_depth + 1,
|
|
_ancestors=new_ancestors,
|
|
)
|
|
except Exception:
|
|
try:
|
|
return {
|
|
_to_serializable_key(k): to_serializable(
|
|
v,
|
|
max_depth=max_depth,
|
|
_current_depth=_current_depth + 1,
|
|
_ancestors=new_ancestors,
|
|
)
|
|
for k, v in obj.__dict__.items()
|
|
if k not in (exclude or set())
|
|
}
|
|
except Exception:
|
|
return repr(obj)
|
|
return repr(obj)
|
|
|
|
|
|
def _to_serializable_key(key: Any) -> str:
|
|
if isinstance(key, (str, int)):
|
|
return str(key)
|
|
return f"key_{id(key)}_{key!r}"
|
|
|
|
|
|
def to_string(obj: Any) -> str | None:
|
|
"""Serializes an object into a JSON string.
|
|
|
|
Args:
|
|
obj: Object to serialize.
|
|
|
|
Returns:
|
|
A JSON-formatted string or `None` if empty.
|
|
"""
|
|
serializable = to_serializable(obj)
|
|
if serializable is None:
|
|
return None
|
|
return json.dumps(serializable)
|