feat: add upsert identities properties route (#1672)

This commit is contained in:
cthomas
2025-04-10 17:08:20 -07:00
committed by GitHub
parent f3551e5212
commit 03458a3b29
3 changed files with 62 additions and 2 deletions

View File

@@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, List, Optional
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query
from letta.orm.errors import NoResultFound, UniqueConstraintViolationError
from letta.schemas.identity import Identity, IdentityCreate, IdentityType, IdentityUpdate
from letta.schemas.identity import Identity, IdentityCreate, IdentityProperty, IdentityType, IdentityUpdate
from letta.server.rest_api.utils import get_letta_server
if TYPE_CHECKING:
@@ -125,6 +125,24 @@ def modify_identity(
raise HTTPException(status_code=500, detail=f"{e}")
@router.put("/{identity_id}/properties", tags=["identities"], operation_id="upsert_identity_properties")
def upsert_identity_properties(
identity_id: str,
properties: List[IdentityProperty] = Body(...),
server: "SyncServer" = Depends(get_letta_server),
actor_id: Optional[str] = Header(None, alias="user_id"), # Extract user_id from header, default to None if not present
):
try:
actor = server.user_manager.get_user_or_default(user_id=actor_id)
return server.identity_manager.upsert_identity_properties(identity_id=identity_id, properties=properties, actor=actor)
except HTTPException:
raise
except NoResultFound as e:
raise HTTPException(status_code=404, detail=str(e))
except Exception as e:
raise HTTPException(status_code=500, detail=f"{e}")
@router.delete("/{identity_id}", tags=["identities"], operation_id="delete_identity")
def delete_identity(
identity_id: str,

View File

@@ -8,7 +8,7 @@ from letta.orm.agent import Agent as AgentModel
from letta.orm.block import Block as BlockModel
from letta.orm.identity import Identity as IdentityModel
from letta.schemas.identity import Identity as PydanticIdentity
from letta.schemas.identity import IdentityCreate, IdentityType, IdentityUpdate
from letta.schemas.identity import IdentityCreate, IdentityProperty, IdentityType, IdentityUpdate
from letta.schemas.user import User as PydanticUser
from letta.utils import enforce_types
@@ -165,6 +165,20 @@ class IdentityManager:
existing_identity.update(session, actor=actor)
return existing_identity.to_pydantic()
@enforce_types
def upsert_identity_properties(self, identity_id: str, properties: List[IdentityProperty], actor: PydanticUser) -> PydanticIdentity:
with self.session_maker() as session:
existing_identity = IdentityModel.read(db_session=session, identifier=identity_id, actor=actor)
if existing_identity is None:
raise HTTPException(status_code=404, detail="Identity not found")
return self._update_identity(
session=session,
existing_identity=existing_identity,
identity=IdentityUpdate(properties=properties),
actor=actor,
replace=True,
)
@enforce_types
def delete_identity(self, identity_id: str, actor: PydanticUser) -> None:
with self.session_maker() as session:

View File

@@ -3488,6 +3488,34 @@ def test_get_set_blocks_for_identities(server: SyncServer, default_block, defaul
server.identity_manager.delete_identity(identity.id, actor=default_user)
def test_upsert_properties(server: SyncServer, default_user):
identity_create = IdentityCreate(
identifier_key="1234",
name="caren",
identity_type=IdentityType.user,
properties=[
IdentityProperty(key="email", value="caren@letta.com", type=IdentityPropertyType.string),
IdentityProperty(key="age", value=28, type=IdentityPropertyType.number),
],
)
identity = server.identity_manager.create_identity(identity_create, actor=default_user)
properties = [
IdentityProperty(key="email", value="caren@gmail.com", type=IdentityPropertyType.string),
IdentityProperty(key="age", value="28", type=IdentityPropertyType.string),
IdentityProperty(key="test", value=123, type=IdentityPropertyType.number),
]
updated_identity = server.identity_manager.upsert_identity_properties(
identity_id=identity.id,
properties=properties,
actor=default_user,
)
assert updated_identity.properties == properties
server.identity_manager.delete_identity(identity.id, actor=default_user)
# ======================================================================================================================
# SourceManager Tests - Sources
# ======================================================================================================================