Working with Pydapter Protocols¶
Protocol + Mixin Pattern¶
Each protocol provides:
- Protocol: Interface for type checking
- Mixin: Implementation with behavior
@runtime_checkable
class Identifiable(Protocol):
id: UUID
class IdentifiableMixin:
def __hash__(self) -> int:
return hash(self.id)
Available Protocols¶
Identifiable¶
- Purpose: UUID-based identity management
- Fields:
id: UUID
- Methods:
__hash__()
, UUID serialization - Usage: Base for all tracked entities
Temporal¶
- Purpose: Timestamp management
- Fields:
created_at: datetime
,updated_at: datetime
- Methods:
update_timestamp()
, ISO datetime serialization - Usage: Audit trails, versioning
Embeddable¶
- Purpose: Vector embeddings for ML/AI
- Fields:
content: str | None
,embedding: list[float]
- Methods: Content processing, embedding validation
- Usage: RAG systems, semantic search
Event¶
- Purpose: Comprehensive event tracking
- Inherits: Combines Identifiable, Temporal, Embeddable
- Usage: Event sourcing, audit logs
Composition Patterns¶
Basic Composition¶
class User(BaseModel, IdentifiableMixin, TemporalMixin):
id: UUID
created_at: datetime
updated_at: datetime
name: str
email: str
Inheritance Order Matters¶
# ✓ Correct: BaseModel first, dependency order for mixins
class Document(BaseModel, IdentifiableMixin, TemporalMixin, EmbeddableMixin):
# Protocol fields first
id: UUID
created_at: datetime
updated_at: datetime
content: str | None = None
embedding: list[float] = Field(default_factory=list)
# Domain fields
title: str
Custom Protocol Creation¶
@runtime_checkable
class Versionable(Protocol):
version: int
version_history: list[int]
class VersionableMixin:
def increment_version(self) -> None:
if hasattr(self, 'version'):
self.version_history.append(self.version)
self.version += 1
Type Checking¶
Static Type Checking¶
def process_identifiable_items(items: list[Identifiable]) -> list[UUID]:
return [item.id for item in items]
Runtime Type Checking¶
def safe_get_id(obj: object) -> UUID | None:
if isinstance(obj, Identifiable):
return obj.id
return None
Integration with Fields¶
from pydapter.fields import ID_FROZEN, DATETIME
class AdvancedModel(BaseModel, IdentifiableMixin, TemporalMixin):
id: UUID = ID_FROZEN.field_info
created_at: datetime = DATETIME.field_info
updated_at: datetime = DATETIME.field_info
Key Tips for LLM Developers¶
1. Protocol Contract Compliance¶
- Always implement all required protocol fields
- Use proper type annotations
- Test protocol compliance with
isinstance()
2. Mixin Order¶
BaseModel
first- Protocol mixins in dependency order
- Custom mixins last
3. Automatic Serialization¶
IdentifiableMixin
: UUID → stringTemporalMixin
: datetime → ISO string- Use
model_dump_json()
for proper serialization
4. Common Patterns¶
# Standard composition for entities
class Entity(BaseModel, IdentifiableMixin, TemporalMixin):
pass
# Standard composition for ML content
class MLContent(BaseModel, IdentifiableMixin, EmbeddableMixin):
pass
# Standard composition for events
class EventRecord(BaseModel, Event): # Event includes all protocols
pass
5. Testing Protocol Implementation¶
def test_protocol_compliance(model_instance):
assert isinstance(model_instance, Identifiable)
assert hasattr(model_instance, 'id')
assert callable(getattr(model_instance, '__hash__'))
This protocol system enables consistent, type-safe behavior composition across your models while maintaining clean separation of concerns.