From 584b2cefa3743dad11893134ea88831d226bf070 Mon Sep 17 00:00:00 2001 From: Stream Date: Wed, 20 Aug 2025 17:03:15 +0800 Subject: [PATCH] feat: add pydantic models for memory --- api/core/memory/entities.py | 99 +++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 api/core/memory/entities.py diff --git a/api/core/memory/entities.py b/api/core/memory/entities.py new file mode 100644 index 0000000000..84e6d915b9 --- /dev/null +++ b/api/core/memory/entities.py @@ -0,0 +1,99 @@ +from enum import Enum +from typing import Optional, Dict, Any +from uuid import uuid4 + +from pydantic import BaseModel, Field + + +class MemoryScope(str, Enum): + """Memory scope determined by node_id field""" + APP = "app" # node_id is None + NODE = "node" # node_id is not None + + +class MemoryTerm(str, Enum): + """Memory term determined by conversation_id field""" + SESSION = "session" # conversation_id is not None + PERSISTENT = "persistent" # conversation_id is None + + +class MemoryStrategy(str, Enum): + ON_TURNS = "on_turns" + + +class MemoryScheduleMode(str, Enum): + SYNC = "sync" + ASYNC = "async" + + +class MemoryBlockSpec(BaseModel): + """Memory block specification for workflow configuration""" + id: str = Field( + default_factory=lambda: str(uuid4()), + description="Unique identifier for the memory block", + ) + name: str = Field(description="Display name of the memory block") + description: str = Field(default="", description="Description of the memory block") + template: str = Field(description="Initial template content for the memory") + instruction: str = Field(description="Instructions for updating the memory") + scope: MemoryScope = Field(description="Scope of the memory (app or node level)") + term: MemoryTerm = Field(description="Term of the memory (session or persistent)") + strategy: MemoryStrategy = Field(description="Update strategy for the memory") + update_turns: int = Field(gt=0, description="Number of turns between updates") + preserved_turns: int = Field(gt=0, description="Number of conversation turns to preserve") + schedule_mode: MemoryScheduleMode = Field(description="Synchronous or asynchronous update mode") + model: Optional[Dict[str, Any]] = Field(default=None, description="Model configuration for memory updates") + end_user_visible: bool = Field(default=False, description="Whether memory is visible to end users") + end_user_editable: bool = Field(default=False, description="Whether memory is editable by end users") + + +class MemoryBlock(BaseModel): + """Runtime memory block instance + + Design Rules: + - app_id = None: Global memory (future feature, not implemented yet) + - app_id = str: App-specific memory + - conversation_id = None: Persistent memory (cross-conversation) + - conversation_id = str: Session memory (conversation-specific) + - node_id = None: App-level scope + - node_id = str: Node-level scope + + These rules implicitly determine scope and term without redundant storage. + """ + id: str + memory_id: str + name: str + value: str + scope: MemoryScope # Derived from node_id: None=APP, str=NODE + term: MemoryTerm # Derived from conversation_id: None=PERSISTENT, str=SESSION + app_id: Optional[str] = None # None=global(future), str=app-specific + conversation_id: Optional[str] = None # None=persistent, str=session + node_id: Optional[str] = None # None=app-scope, str=node-scope + created_at: Optional[str] = None + updated_at: Optional[str] = None + + @property + def is_global(self) -> bool: + """Check if this is global memory (future feature)""" + return self.app_id is None + + @property + def is_persistent(self) -> bool: + """Check if this is persistent memory (cross-conversation)""" + return self.conversation_id is None + + @property + def is_app_scope(self) -> bool: + """Check if this is app-level scope""" + return self.node_id is None + + @property + def is_node_scope(self) -> bool: + """Check if this is node-level scope""" + return self.node_id is not None + + +class ChatflowConversationMetadata(BaseModel): + """Metadata for chatflow conversation with visible message count""" + type: str = "mutable_visible_window" + visible_count: int = Field(gt=0, description="Number of visible messages to keep")