mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-01 13:18:10 +00:00
feat(types): use cloudpickle for callable serialization
This commit is contained in:
@@ -43,6 +43,7 @@ dependencies = [
|
||||
"uv~=0.9.13",
|
||||
"aiosqlite~=0.21.0",
|
||||
"lancedb>=0.29.2",
|
||||
"cloudpickle~=3.1.2",
|
||||
]
|
||||
|
||||
[project.urls]
|
||||
|
||||
@@ -1,45 +1,35 @@
|
||||
"""Serializable callable type for Pydantic models."""
|
||||
"""Serializable callable type for Pydantic models.
|
||||
|
||||
All callables (named functions, lambdas, closures, methods) are serialized
|
||||
via ``cloudpickle`` + base64. On deserialization the base64 payload is
|
||||
decoded and unpickled back into a live callable.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import base64
|
||||
from collections.abc import Callable
|
||||
import importlib
|
||||
from typing import Annotated, Any
|
||||
|
||||
import cloudpickle # type: ignore[import-untyped]
|
||||
from pydantic import BeforeValidator, PlainSerializer, WithJsonSchema
|
||||
|
||||
|
||||
def _deserialize_callable(v: str | Callable[..., Any]) -> Callable[..., Any]:
|
||||
"""Deserialize a dotted import path to a callable, or pass through if already callable."""
|
||||
"""Deserialize a base64-encoded cloudpickle payload, or pass through if already callable."""
|
||||
if isinstance(v, str):
|
||||
module_path, _, name = v.rpartition(".")
|
||||
if not module_path:
|
||||
raise ValueError(f"Invalid callable path: {v!r} (expected 'module.name')")
|
||||
module = importlib.import_module(module_path)
|
||||
obj: Callable[..., Any] = getattr(module, name)
|
||||
obj = cloudpickle.loads(base64.b85decode(v))
|
||||
if not callable(obj):
|
||||
raise ValueError(f"{v!r} resolved to {type(obj).__name__}, not a callable")
|
||||
return obj
|
||||
raise ValueError(
|
||||
f"Deserialized object is {type(obj).__name__}, not a callable"
|
||||
)
|
||||
return obj # type: ignore[no-any-return]
|
||||
return v
|
||||
|
||||
|
||||
def _serialize_callable(v: Callable[..., Any]) -> str:
|
||||
"""Serialize a callable to its dotted import path."""
|
||||
module = getattr(v, "__module__", None)
|
||||
qualname = getattr(v, "__qualname__", None)
|
||||
name = getattr(v, "__name__", None)
|
||||
|
||||
if not module or not name:
|
||||
raise ValueError(
|
||||
f"Cannot serialize {v!r}: missing __module__ or __name__. "
|
||||
"Only top-level named functions are serializable."
|
||||
)
|
||||
if qualname and "<" in qualname:
|
||||
raise ValueError(
|
||||
f"Cannot serialize {v!r}: lambdas and nested functions are not serializable. "
|
||||
"Use a top-level named function instead."
|
||||
)
|
||||
return f"{module}.{qualname or name}"
|
||||
"""Serialize any callable to a base64-encoded cloudpickle payload."""
|
||||
return base64.b85encode(cloudpickle.dumps(v)).decode("ascii")
|
||||
|
||||
|
||||
SerializableCallable = Annotated[
|
||||
|
||||
11
uv.lock
generated
11
uv.lock
generated
@@ -911,6 +911,15 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188, upload-time = "2024-12-21T18:38:41.666Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cloudpickle"
|
||||
version = "3.1.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/27/fb/576f067976d320f5f0114a8d9fa1215425441bb35627b1993e5afd8111e5/cloudpickle-3.1.2.tar.gz", hash = "sha256:7fda9eb655c9c230dab534f1983763de5835249750e85fbcef43aaa30a9a2414", size = 22330, upload-time = "2025-11-03T09:25:26.604Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/88/39/799be3f2f0f38cc727ee3b4f1445fe6d5e4133064ec2e4115069418a5bb6/cloudpickle-3.1.2-py3-none-any.whl", hash = "sha256:9acb47f6afd73f60dc1df93bb801b472f05ff42fa6c84167d25cb206be1fbf4a", size = 22228, upload-time = "2025-11-03T09:25:25.534Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "colorama"
|
||||
version = "0.4.6"
|
||||
@@ -1097,6 +1106,7 @@ dependencies = [
|
||||
{ name = "appdirs" },
|
||||
{ name = "chromadb" },
|
||||
{ name = "click" },
|
||||
{ name = "cloudpickle" },
|
||||
{ name = "httpx" },
|
||||
{ name = "instructor" },
|
||||
{ name = "json-repair" },
|
||||
@@ -1193,6 +1203,7 @@ requires-dist = [
|
||||
{ name = "boto3", marker = "extra == 'bedrock'", specifier = "~=1.40.45" },
|
||||
{ name = "chromadb", specifier = "~=1.1.0" },
|
||||
{ name = "click", specifier = "~=8.1.7" },
|
||||
{ name = "cloudpickle", specifier = "~=3.1.2" },
|
||||
{ name = "crewai-files", marker = "extra == 'file-processing'", editable = "lib/crewai-files" },
|
||||
{ name = "crewai-tools", marker = "extra == 'tools'", editable = "lib/crewai-tools" },
|
||||
{ name = "docling", marker = "extra == 'docling'", specifier = "~=2.75.0" },
|
||||
|
||||
Reference in New Issue
Block a user