mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-07-03 06:08:15 +00:00
Fix Pydantic v2 compatibility in SecurityConfig and Fingerprint classes (#2335)
* Fix Pydantic v2 compatibility in SecurityConfig and Fingerprint classes Co-Authored-By: Joe Moura <joao@crewai.com> * Fix type-checker errors in fingerprint properties Co-Authored-By: Joe Moura <joao@crewai.com> * Enhance security validation in Fingerprint and SecurityConfig classes Co-Authored-By: Joe Moura <joao@crewai.com> --------- Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Joe Moura <joao@crewai.com>
This commit is contained in:
committed by
GitHub
parent
8be595698e
commit
9ee4e45948
@@ -482,5 +482,8 @@ class Agent(BaseAgent):
|
||||
Returns:
|
||||
Fingerprint: The agent's fingerprint
|
||||
"""
|
||||
# Ensure we always return a valid Fingerprint
|
||||
if not self.security_config.fingerprint:
|
||||
self.security_config.fingerprint = Fingerprint()
|
||||
return self.security_config.fingerprint
|
||||
|
||||
|
||||
@@ -493,6 +493,9 @@ class Crew(BaseModel):
|
||||
Returns:
|
||||
Fingerprint: The crew's fingerprint
|
||||
"""
|
||||
# Ensure we always return a valid Fingerprint
|
||||
if not self.security_config.fingerprint:
|
||||
self.security_config.fingerprint = Fingerprint()
|
||||
return self.security_config.fingerprint
|
||||
|
||||
def _setup_from_config(self):
|
||||
|
||||
@@ -9,7 +9,7 @@ import uuid
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
|
||||
class Fingerprint(BaseModel):
|
||||
@@ -30,8 +30,20 @@ class Fingerprint(BaseModel):
|
||||
created_at: datetime = Field(default_factory=datetime.now, description="When this fingerprint was created")
|
||||
metadata: Dict[str, Any] = Field(default_factory=dict, description="Additional metadata for this fingerprint")
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
model_config = ConfigDict(arbitrary_types_allowed=True)
|
||||
|
||||
@field_validator('metadata')
|
||||
@classmethod
|
||||
def validate_metadata(cls, v):
|
||||
"""Validate that metadata is a dictionary with string keys."""
|
||||
if not isinstance(v, dict):
|
||||
raise ValueError("Metadata must be a dictionary")
|
||||
|
||||
# Validate that all keys are strings
|
||||
for key, value in v.items():
|
||||
if not isinstance(key, str):
|
||||
raise ValueError(f"Metadata keys must be strings, got {type(key)}")
|
||||
return v
|
||||
|
||||
def __init__(self, **data):
|
||||
"""Initialize a Fingerprint with auto-generated uuid_str and created_at."""
|
||||
@@ -60,9 +72,16 @@ class Fingerprint(BaseModel):
|
||||
Returns:
|
||||
str: A string representation of the UUID consistently generated from the seed
|
||||
"""
|
||||
if not isinstance(seed, str):
|
||||
raise ValueError("Seed must be a string")
|
||||
|
||||
if not seed.strip():
|
||||
raise ValueError("Seed cannot be empty or whitespace")
|
||||
|
||||
# Create a deterministic UUID using v5 (SHA-1)
|
||||
# This uses the DNS namespace as a base, but we could create a custom namespace
|
||||
return str(uuid.uuid5(uuid.NAMESPACE_DNS, seed))
|
||||
# Custom namespace for CrewAI to enhance security
|
||||
CREW_AI_NAMESPACE = uuid.UUID('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
|
||||
return str(uuid.uuid5(CREW_AI_NAMESPACE, seed))
|
||||
|
||||
@classmethod
|
||||
def generate(cls, seed: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> 'Fingerprint':
|
||||
@@ -132,4 +151,4 @@ class Fingerprint(BaseModel):
|
||||
if "created_at" in data and isinstance(data["created_at"], str):
|
||||
object.__setattr__(fingerprint, 'created_at', datetime.fromisoformat(data["created_at"]))
|
||||
|
||||
return fingerprint
|
||||
return fingerprint
|
||||
|
||||
@@ -10,9 +10,9 @@ The SecurityConfig class is the primary interface for managing security settings
|
||||
in CrewAI applications.
|
||||
"""
|
||||
|
||||
from typing import Any, Dict
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
from pydantic import BaseModel, ConfigDict, Field, model_validator
|
||||
|
||||
from crewai.security.fingerprint import Fingerprint
|
||||
|
||||
@@ -31,19 +31,30 @@ class SecurityConfig(BaseModel):
|
||||
fingerprint (Fingerprint): The unique fingerprint automatically generated for the component
|
||||
"""
|
||||
|
||||
fingerprint: Fingerprint = Field(default_factory=Fingerprint, description="Unique identifier for the component")
|
||||
model_config = ConfigDict(
|
||||
arbitrary_types_allowed=True
|
||||
# Note: Cannot use frozen=True as existing tests modify the fingerprint property
|
||||
)
|
||||
|
||||
fingerprint: Fingerprint = Field(
|
||||
default_factory=Fingerprint,
|
||||
description="Unique identifier for the component"
|
||||
)
|
||||
|
||||
class Config:
|
||||
arbitrary_types_allowed = True
|
||||
|
||||
def __init__(self, fingerprint: Fingerprint = None):
|
||||
"""
|
||||
Initialize a new SecurityConfig instance.
|
||||
|
||||
Args:
|
||||
fingerprint: Fingerprint to use for the component
|
||||
"""
|
||||
self.fingerprint = fingerprint or Fingerprint()
|
||||
@model_validator(mode='before')
|
||||
@classmethod
|
||||
def validate_fingerprint(cls, values):
|
||||
"""Ensure fingerprint is properly initialized."""
|
||||
if isinstance(values, dict):
|
||||
# Handle case where fingerprint is not provided or is None
|
||||
if 'fingerprint' not in values or values['fingerprint'] is None:
|
||||
values['fingerprint'] = Fingerprint()
|
||||
# Handle case where fingerprint is a string (seed)
|
||||
elif isinstance(values['fingerprint'], str):
|
||||
if not values['fingerprint'].strip():
|
||||
raise ValueError("Fingerprint seed cannot be empty")
|
||||
values['fingerprint'] = Fingerprint.generate(seed=values['fingerprint'])
|
||||
return values
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
"""
|
||||
@@ -53,7 +64,7 @@ class SecurityConfig(BaseModel):
|
||||
Dict[str, Any]: Dictionary representation of the security config
|
||||
"""
|
||||
result = {
|
||||
"fingerprint": self.fingerprint.to_dict() if self.fingerprint else None
|
||||
"fingerprint": self.fingerprint.to_dict()
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
@@ -742,4 +742,7 @@ class Task(BaseModel):
|
||||
Returns:
|
||||
Fingerprint: The fingerprint of the task
|
||||
"""
|
||||
# Ensure we always return a valid Fingerprint
|
||||
if not self.security_config.fingerprint:
|
||||
self.security_config.fingerprint = Fingerprint()
|
||||
return self.security_config.fingerprint
|
||||
|
||||
Reference in New Issue
Block a user