"""Core schemas for the generic Memory Gateway v1 API.""" from __future__ import annotations from datetime import datetime, timezone from enum import Enum from typing import Any, Literal, Optional from uuid import uuid4 from pydantic import BaseModel, Field def utc_now() -> datetime: return datetime.now(timezone.utc) class Visibility(str, Enum): PRIVATE = "private" AGENT_ONLY = "agent-only" WORKSPACE_SHARED = "workspace-shared" GLOBAL = "global" class MemoryType(str, Enum): PROFILE = "profile" PREFERENCE = "preference" FACT = "fact" DECISION = "decision" SUMMARY = "summary" EPISODIC = "episodic" PROCEDURE = "procedure" EXPERIENCE = "experience" KNOWLEDGE = "knowledge" class SourceType(str, Enum): CONVERSATION = "conversation" TASK = "task" AGENT = "agent" OBSIDIAN = "obsidian" OPENVIKING = "openviking" EVERMEMOS = "evermemos" MANUAL = "manual" class UserRecord(BaseModel): id: str = Field(default_factory=lambda: f"user_{uuid4().hex[:12]}") display_name: str status: Literal["active", "disabled"] = "active" profile_namespace: Optional[str] = None preferences: dict[str, Any] = Field(default_factory=dict) created_at: datetime = Field(default_factory=utc_now) updated_at: datetime = Field(default_factory=utc_now) class AgentRecord(BaseModel): id: str name: str framework: str owner_user_id: Optional[str] = None created_at: datetime = Field(default_factory=utc_now) class WorkspaceRecord(BaseModel): id: str name: str owner_user_id: str member_user_ids: list[str] = Field(default_factory=list) allowed_agent_ids: list[str] = Field(default_factory=list) created_at: datetime = Field(default_factory=utc_now) class SessionRecord(BaseModel): id: str = Field(default_factory=lambda: f"sess_{uuid4().hex[:12]}") user_id: str agent_id: Optional[str] = None workspace_id: Optional[str] = None status: Literal["open", "committed", "expired"] = "open" expires_at: Optional[datetime] = None created_at: datetime = Field(default_factory=utc_now) updated_at: datetime = Field(default_factory=utc_now) class ACLRule(BaseModel): visibility: Visibility = Visibility.PRIVATE allowed_user_ids: list[str] = Field(default_factory=list) allowed_agent_ids: list[str] = Field(default_factory=list) allowed_workspace_ids: list[str] = Field(default_factory=list) class MemoryRecord(BaseModel): id: str = Field(default_factory=lambda: f"mem_{uuid4().hex[:16]}") user_id: str agent_id: Optional[str] = None workspace_id: Optional[str] = None session_id: Optional[str] = None namespace: str memory_type: MemoryType = MemoryType.FACT content: str summary: Optional[str] = None tags: list[str] = Field(default_factory=list) importance: float = Field(default=0.5, ge=0, le=1) confidence: float = Field(default=0.8, ge=0, le=1) visibility: Visibility = Visibility.PRIVATE acl: ACLRule = Field(default_factory=ACLRule) source: SourceType = SourceType.MANUAL source_ref: Optional[str] = None embedding_ref: Optional[str] = None created_at: datetime = Field(default_factory=utc_now) updated_at: datetime = Field(default_factory=utc_now) expires_at: Optional[datetime] = None archived_at: Optional[datetime] = None version: int = 1 class EpisodeRecord(BaseModel): id: str = Field(default_factory=lambda: f"epi_{uuid4().hex[:16]}") user_id: str agent_id: Optional[str] = None workspace_id: Optional[str] = None session_id: str namespace: str content: str summary: Optional[str] = None events: list[dict[str, Any]] = Field(default_factory=list) tags: list[str] = Field(default_factory=list) source: SourceType = SourceType.CONVERSATION created_at: datetime = Field(default_factory=utc_now) expires_at: Optional[datetime] = None class ProfileRecord(BaseModel): id: str = Field(default_factory=lambda: f"profile_{uuid4().hex[:12]}") user_id: str namespace: str display_name: Optional[str] = None stable_facts: list[str] = Field(default_factory=list) preferences: dict[str, Any] = Field(default_factory=dict) working_style: list[str] = Field(default_factory=list) updated_from_memory_ids: list[str] = Field(default_factory=list) version: int = 1 updated_at: datetime = Field(default_factory=utc_now) class AuditLog(BaseModel): id: str = Field(default_factory=lambda: f"audit_{uuid4().hex[:16]}") actor_user_id: Optional[str] = None actor_agent_id: Optional[str] = None action: str target_type: str target_id: Optional[str] = None namespace: Optional[str] = None decision: Literal["allow", "deny"] = "allow" reason: Optional[str] = None metadata: dict[str, Any] = Field(default_factory=dict) created_at: datetime = Field(default_factory=utc_now) class AccessContext(BaseModel): user_id: str agent_id: Optional[str] = None workspace_id: Optional[str] = None session_id: Optional[str] = None class CreateUserRequest(BaseModel): display_name: str user_id: Optional[str] = None preferences: dict[str, Any] = Field(default_factory=dict) class MemorySearchRequest(AccessContext): query: str namespaces: list[str] = Field(default_factory=list) memory_types: list[MemoryType] = Field(default_factory=list) tags: list[str] = Field(default_factory=list) limit: int = Field(default=10, ge=1, le=100) class MemoryUpsertRequest(AccessContext): namespace: Optional[str] = None memory_type: MemoryType = MemoryType.FACT content: str summary: Optional[str] = None tags: list[str] = Field(default_factory=list) importance: float = Field(default=0.5, ge=0, le=1) confidence: float = Field(default=0.8, ge=0, le=1) visibility: Visibility = Visibility.PRIVATE source: SourceType = SourceType.MANUAL expires_at: Optional[datetime] = None class MemoryPatchRequest(BaseModel): content: Optional[str] = None summary: Optional[str] = None tags: Optional[list[str]] = None importance: Optional[float] = Field(default=None, ge=0, le=1) confidence: Optional[float] = Field(default=None, ge=0, le=1) visibility: Optional[Visibility] = None expires_at: Optional[datetime] = None class EpisodeAppendRequest(AccessContext): content: str namespace: Optional[str] = None events: list[dict[str, Any]] = Field(default_factory=list) tags: list[str] = Field(default_factory=list) source: SourceType = SourceType.CONVERSATION expires_at: Optional[datetime] = None class CommitSessionRequest(AccessContext): promote: bool = True min_importance: float = Field(default=0.6, ge=0, le=1) target_namespace: Optional[str] = None class MemoryFeedbackRequest(AccessContext): feedback: Literal["useful", "not_useful", "incorrect", "duplicate", "outdated"] comment: Optional[str] = None class NamespaceInfo(BaseModel): namespace: str owner_user_id: Optional[str] = None visibility: Visibility description: str