""" Fingerprint Module This module provides functionality for generating and validating unique identifiers for CrewAI agents. These identifiers are used for tracking, auditing, and security. """ import uuid from datetime import datetime from typing import Any, Dict, Optional from pydantic import BaseModel, Field class Fingerprint(BaseModel): """ A class for generating and managing unique identifiers for agents. Each agent has dual identifiers: - Human-readable ID: For debugging and reference (derived from role if not specified) - Fingerprint UUID: Unique runtime identifier for tracking and auditing Attributes: uuid_str (str): String representation of the UUID for this fingerprint, auto-generated created_at (datetime): When this fingerprint was created, auto-generated metadata (Dict[str, Any]): Additional metadata associated with this fingerprint """ uuid_str: str = Field(default_factory=lambda: str(uuid.uuid4()), description="String representation of the UUID") 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 def __init__(self, **data): """Initialize a Fingerprint with auto-generated uuid_str and created_at.""" # Remove uuid_str and created_at from data to ensure they're auto-generated if 'uuid_str' in data: data.pop('uuid_str') if 'created_at' in data: data.pop('created_at') # Call the parent constructor with the modified data super().__init__(**data) @property def uuid(self) -> uuid.UUID: """Get the UUID object for this fingerprint.""" return uuid.UUID(self.uuid_str) @classmethod def _generate_uuid(cls, seed: str) -> str: """ Generate a deterministic UUID based on a seed string. Args: seed (str): The seed string to use for UUID generation Returns: str: A string representation of the UUID consistently generated from the seed """ # 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)) @classmethod def generate(cls, seed: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None) -> 'Fingerprint': """ Static factory method to create a new Fingerprint. Args: seed (Optional[str]): A string to use as seed for the UUID generation. If None, a random UUID is generated. metadata (Optional[Dict[str, Any]]): Additional metadata to store with the fingerprint. Returns: Fingerprint: A new Fingerprint instance """ fingerprint = cls(metadata=metadata or {}) if seed: # For seed-based generation, we need to manually set the uuid_str after creation object.__setattr__(fingerprint, 'uuid_str', cls._generate_uuid(seed)) return fingerprint def __str__(self) -> str: """String representation of the fingerprint (the UUID).""" return self.uuid_str def __eq__(self, other) -> bool: """Compare fingerprints by their UUID.""" if isinstance(other, Fingerprint): return self.uuid_str == other.uuid_str return False def __hash__(self) -> int: """Hash of the fingerprint (based on UUID).""" return hash(self.uuid_str) def to_dict(self) -> Dict[str, Any]: """ Convert the fingerprint to a dictionary representation. Returns: Dict[str, Any]: Dictionary representation of the fingerprint """ return { "uuid_str": self.uuid_str, "created_at": self.created_at.isoformat(), "metadata": self.metadata } @classmethod def from_dict(cls, data: Dict[str, Any]) -> 'Fingerprint': """ Create a Fingerprint from a dictionary representation. Args: data (Dict[str, Any]): Dictionary representation of a fingerprint Returns: Fingerprint: A new Fingerprint instance """ if not data: return cls() fingerprint = cls(metadata=data.get("metadata", {})) # For consistency with existing stored fingerprints, we need to manually set these if "uuid_str" in data: object.__setattr__(fingerprint, 'uuid_str', data["uuid_str"]) if "created_at" in data and isinstance(data["created_at"], str): object.__setattr__(fingerprint, 'created_at', datetime.fromisoformat(data["created_at"])) return fingerprint