Files
letta-server/letta/schemas/block.py
Shubham Naik 56e2800892 feat: add hidden property to group and blocks [PRO-1145] (#4415)
* feat: add hidden property to group and blocks

* feat: add hidden property to group and blocks

* chore: bup

* chore: add hidden property

* chore: next

---------

Co-authored-by: Shubham Naik <shub@memgpt.ai>
2025-09-04 10:53:16 -07:00

188 lines
7.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from datetime import datetime
from typing import Optional
from pydantic import ConfigDict, Field, model_validator
from typing_extensions import Self
from letta.constants import CORE_MEMORY_BLOCK_CHAR_LIMIT, DEFAULT_HUMAN_BLOCK_DESCRIPTION, DEFAULT_PERSONA_BLOCK_DESCRIPTION
from letta.schemas.letta_base import LettaBase
# block of the LLM context
class BaseBlock(LettaBase, validate_assignment=True):
"""Base block of the LLM context"""
__id_prefix__ = "block"
# data value
value: str = Field(..., description="Value of the block.")
limit: int = Field(CORE_MEMORY_BLOCK_CHAR_LIMIT, description="Character limit of the block.")
project_id: Optional[str] = Field(None, description="The associated project id.")
# template data (optional)
template_name: Optional[str] = Field(None, description="Name of the block if it is a template.", alias="name")
is_template: bool = Field(False, description="Whether the block is a template (e.g. saved human/persona options).")
template_id: Optional[str] = Field(None, description="The id of the template.", alias="name")
base_template_id: Optional[str] = Field(None, description="The base template id of the block.")
deployment_id: Optional[str] = Field(None, description="The id of the deployment.")
entity_id: Optional[str] = Field(None, description="The id of the entity within the template.")
preserve_on_migration: Optional[bool] = Field(False, description="Preserve the block on template migration.")
# context window label
label: Optional[str] = Field(None, description="Label of the block (e.g. 'human', 'persona') in the context window.")
# permissions of the agent
read_only: bool = Field(False, description="Whether the agent has read-only access to the block.")
# metadata
description: Optional[str] = Field(None, description="Description of the block.")
metadata: Optional[dict] = Field({}, description="Metadata of the block.")
hidden: Optional[bool] = Field(
None,
description="If set to True, the block will be hidden.",
)
# def __len__(self):
# return len(self.value)
model_config = ConfigDict(extra="ignore") # Ignores extra fields
@model_validator(mode="after")
def verify_char_limit(self) -> Self:
# self.limit can be None from
if self.limit is not None and self.value and len(self.value) > self.limit:
error_msg = f"Edit failed: Exceeds {self.limit} character limit (requested {len(self.value)}) - {str(self)}."
raise ValueError(error_msg)
return self
def __setattr__(self, name, value):
"""Run validation if self.value is updated"""
super().__setattr__(name, value)
if name == "value":
# run validation
self.__class__.model_validate(self.model_dump(exclude_unset=True))
class Block(BaseBlock):
"""
A Block represents a reserved section of the LLM's context window which is editable. `Block` objects contained in the `Memory` object, which is able to edit the Block values.
Parameters:
label (str): The label of the block (e.g. 'human', 'persona'). This defines a category for the block.
value (str): The value of the block. This is the string that is represented in the context window.
limit (int): The character limit of the block.
is_template (bool): Whether the block is a template (e.g. saved human/persona options). Non-template blocks are not stored in the database and are ephemeral, while templated blocks are stored in the database.
label (str): The label of the block (e.g. 'human', 'persona'). This defines a category for the block.
template_name (str): The name of the block template (if it is a template).
description (str): Description of the block.
metadata (Dict): Metadata of the block.
user_id (str): The unique identifier of the user associated with the block.
"""
id: str = BaseBlock.generate_id_field()
# default orm fields
created_by_id: Optional[str] = Field(None, description="The id of the user that made this Block.")
last_updated_by_id: Optional[str] = Field(None, description="The id of the user that last updated this Block.")
class FileBlock(Block):
file_id: str = Field(..., description="Unique identifier of the file.")
source_id: str = Field(..., description="Unique identifier of the source.")
is_open: bool = Field(..., description="True if the agent currently has the file open.")
last_accessed_at: Optional[datetime] = Field(
default_factory=datetime.utcnow,
description="UTC timestamp of the agents most recent access to this file. Any operations from the open, close, or search tools will update this field.",
)
class Human(Block):
"""Human block of the LLM context"""
label: str = "human"
description: Optional[str] = Field(DEFAULT_HUMAN_BLOCK_DESCRIPTION, description="Description of the block.")
class Persona(Block):
"""Persona block of the LLM context"""
label: str = "persona"
description: Optional[str] = Field(DEFAULT_PERSONA_BLOCK_DESCRIPTION, description="Description of the block.")
DEFAULT_BLOCKS = [Human(value=""), Persona(value="")]
class BlockUpdate(BaseBlock):
"""Update a block"""
limit: Optional[int] = Field(None, description="Character limit of the block.")
value: Optional[str] = Field(None, description="Value of the block.")
project_id: Optional[str] = Field(None, description="The associated project id.")
model_config = ConfigDict(extra="ignore") # Ignores extra fields
class CreateBlock(BaseBlock):
"""Create a block"""
label: str = Field(..., description="Label of the block.")
limit: int = Field(CORE_MEMORY_BLOCK_CHAR_LIMIT, description="Character limit of the block.")
value: str = Field(..., description="Value of the block.")
project_id: Optional[str] = Field(None, description="The associated project id.")
# block templates
is_template: bool = False
template_name: Optional[str] = Field(None, description="Name of the block if it is a template.", alias="name")
@model_validator(mode="before")
@classmethod
def ensure_value_is_string(cls, data):
"""Convert None value to empty string"""
if data and isinstance(data, dict) and data.get("value") is None:
data["value"] = ""
return data
class CreateHuman(CreateBlock):
"""Create a human block"""
label: str = "human"
class CreatePersona(CreateBlock):
"""Create a persona block"""
label: str = "persona"
class CreateBlockTemplate(CreateBlock):
"""Create a block template"""
is_template: bool = True
class CreateHumanBlockTemplate(CreateHuman):
"""Create a human block template"""
is_template: bool = True
label: str = "human"
class CreatePersonaBlockTemplate(CreatePersona):
"""Create a persona block template"""
is_template: bool = True
label: str = "persona"
class InternalTemplateBlockCreate(CreateBlock):
"""Used for Letta Cloud"""
base_template_id: str = Field(..., description="The id of the base template.")
template_id: str = Field(..., description="The id of the template.")
deployment_id: str = Field(..., description="The id of the deployment.")
entity_id: str = Field(..., description="The id of the entity within the template.")