-
Notifications
You must be signed in to change notification settings - Fork 0
DL-5: Implement ProposedChange CRUD operations for MongoDB #67
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
5fbf041
4645d5d
7691675
f4b3278
b58b2a2
741a6b0
a82a107
8894427
2f13a91
a7dbfbf
73435b7
b264687
6ff8297
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,171 @@ | ||||||||||||||||||
| """ | ||||||||||||||||||
| Pydantic schemas for ProposedChange operations (MongoDB). | ||||||||||||||||||
|
|
||||||||||||||||||
| LAYER: 1 (data-layer) | ||||||||||||||||||
| IMPORTS FROM: External libraries (pydantic, uuid, datetime) and base schemas | ||||||||||||||||||
| CALLED BY: mongodb_tools.py | ||||||||||||||||||
|
|
||||||||||||||||||
| These schemas define the data contracts for ProposedChange CRUD operations. | ||||||||||||||||||
| ProposedChanges are staging documents for canonical changes that CanonKeeper | ||||||||||||||||||
| evaluates at scene end. | ||||||||||||||||||
|
|
||||||||||||||||||
| USE CASE: DL-5 | ||||||||||||||||||
| """ | ||||||||||||||||||
|
|
||||||||||||||||||
| from datetime import datetime | ||||||||||||||||||
| from typing import Optional, List, Dict, Any | ||||||||||||||||||
| from uuid import UUID | ||||||||||||||||||
|
|
||||||||||||||||||
| from pydantic import BaseModel, Field, field_validator | ||||||||||||||||||
|
|
||||||||||||||||||
| from monitor_data.schemas.base import ProposalStatus, ProposalType, Authority | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # EVIDENCE SCHEMAS | ||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| class Evidence(BaseModel): | ||||||||||||||||||
| """Evidence supporting a proposed change.""" | ||||||||||||||||||
|
|
||||||||||||||||||
| type: str = Field( | ||||||||||||||||||
| description="Evidence type: turn, snippet, source, rule", | ||||||||||||||||||
| pattern="^(turn|snippet|source|rule)$", | ||||||||||||||||||
| ) | ||||||||||||||||||
| ref_id: UUID = Field(description="Reference to the evidence source") | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # DECISION METADATA SCHEMAS | ||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| class DecisionMetadata(BaseModel): | ||||||||||||||||||
| """Metadata about CanonKeeper's decision on a proposal.""" | ||||||||||||||||||
|
|
||||||||||||||||||
| decided_by: str = Field( | ||||||||||||||||||
| description="Agent that made the decision (e.g., CanonKeeper)" | ||||||||||||||||||
| ) | ||||||||||||||||||
| decided_at: datetime = Field(description="When the decision was made") | ||||||||||||||||||
| reason: str = Field( | ||||||||||||||||||
| description="Rationale for accepting or rejecting the proposal", | ||||||||||||||||||
| max_length=2000, | ||||||||||||||||||
| ) | ||||||||||||||||||
| canonical_ref: Optional[UUID] = Field( | ||||||||||||||||||
| None, | ||||||||||||||||||
| description="UUID of the created canonical entity in Neo4j (if accepted)", | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
| # PROPOSED CHANGE SCHEMAS | ||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| class ProposedChangeCreate(BaseModel): | ||||||||||||||||||
| """Request to create a ProposedChange.""" | ||||||||||||||||||
|
|
||||||||||||||||||
| scene_id: Optional[UUID] = Field( | ||||||||||||||||||
| None, description="Scene ID (required for scene-based proposals)" | ||||||||||||||||||
| ) | ||||||||||||||||||
| story_id: Optional[UUID] = Field( | ||||||||||||||||||
| None, description="Story ID (for story-level proposals)" | ||||||||||||||||||
| ) | ||||||||||||||||||
| turn_id: Optional[UUID] = Field( | ||||||||||||||||||
| None, description="Turn ID that proposed this (if from a turn)" | ||||||||||||||||||
| ) | ||||||||||||||||||
| change_type: ProposalType = Field(description="Type of proposed change") | ||||||||||||||||||
| content: Dict[str, Any] = Field( | ||||||||||||||||||
| description="Flexible JSON payload for the proposed change" | ||||||||||||||||||
| ) | ||||||||||||||||||
| evidence: List[Evidence] = Field( | ||||||||||||||||||
| default_factory=list, description="Supporting evidence for this proposal" | ||||||||||||||||||
| ) | ||||||||||||||||||
| confidence: float = Field( | ||||||||||||||||||
| default=1.0, | ||||||||||||||||||
| ge=0.0, | ||||||||||||||||||
| le=1.0, | ||||||||||||||||||
| description="Confidence level for this proposal (0.0-1.0)", | ||||||||||||||||||
| ) | ||||||||||||||||||
| authority: Authority = Field( | ||||||||||||||||||
| default=Authority.SYSTEM, description="Who asserted this change" | ||||||||||||||||||
| ) | ||||||||||||||||||
| proposer: str = Field( | ||||||||||||||||||
| default="Unknown", description="Agent or user who created this proposal" | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| @field_validator("scene_id", "story_id") | ||||||||||||||||||
| @classmethod | ||||||||||||||||||
| def validate_scene_or_story(cls, v: Optional[UUID], info) -> Optional[UUID]: | ||||||||||||||||||
| """Validate that at least one of scene_id or story_id is provided.""" | ||||||||||||||||||
| # If this is scene_id being validated and it's None, check if story_id exists | ||||||||||||||||||
| if info.field_name == "scene_id" and v is None: | ||||||||||||||||||
| # We can't check story_id here as it might not be set yet | ||||||||||||||||||
| pass | ||||||||||||||||||
| return v | ||||||||||||||||||
|
|
||||||||||||||||||
| def model_post_init(self, __context): | ||||||||||||||||||
| """Post-initialization validation to ensure scene_id or story_id is provided.""" | ||||||||||||||||||
| if self.scene_id is None and self.story_id is None: | ||||||||||||||||||
| raise ValueError("Either scene_id or story_id must be provided") | ||||||||||||||||||
|
Comment on lines
+109
to
+111
|
||||||||||||||||||
| """Post-initialization validation to ensure scene_id or story_id is provided.""" | |
| if self.scene_id is None and self.story_id is None: | |
| raise ValueError("Either scene_id or story_id must be provided") | |
| """Post-initialization validation to ensure exactly one of scene_id or story_id is provided.""" | |
| if self.scene_id is None and self.story_id is None: | |
| raise ValueError("Either scene_id or story_id must be provided") | |
| if self.scene_id is not None and self.story_id is not None: | |
| raise ValueError("Only one of scene_id or story_id can be provided, not both") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The field_validator for scene_id and story_id doesn't perform any actual validation. The validator body is empty (just a pass statement), making it ineffective. The actual validation is performed in model_post_init, so this validator can be removed entirely as it serves no purpose.