letta-nightly 0.5.1.dev20241024104118__py3-none-any.whl → 0.5.1.dev20241026104101__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of letta-nightly might be problematic. Click here for more details.

letta/server/server.py CHANGED
@@ -1,6 +1,4 @@
1
1
  # inspecting tools
2
- import importlib
3
- import inspect
4
2
  import os
5
3
  import traceback
6
4
  import warnings
@@ -16,7 +14,6 @@ import letta.system as system
16
14
  from letta.agent import Agent, save_agent
17
15
  from letta.agent_store.db import attach_base
18
16
  from letta.agent_store.storage import StorageConnector, TableType
19
- from letta.client.utils import derive_function_name_regex
20
17
  from letta.credentials import LettaCredentials
21
18
  from letta.data_sources.connectors import DataConnector, load_data
22
19
 
@@ -30,11 +27,7 @@ from letta.data_sources.connectors import DataConnector, load_data
30
27
  # Token,
31
28
  # User,
32
29
  # )
33
- from letta.functions.functions import (
34
- generate_schema,
35
- load_function_set,
36
- parse_source_code,
37
- )
30
+ from letta.functions.functions import generate_schema, parse_source_code
38
31
  from letta.functions.schema_generator import generate_schema
39
32
 
40
33
  # TODO use custom interface
@@ -82,10 +75,11 @@ from letta.schemas.memory import (
82
75
  from letta.schemas.message import Message, MessageCreate, MessageRole, UpdateMessage
83
76
  from letta.schemas.passage import Passage
84
77
  from letta.schemas.source import Source, SourceCreate, SourceUpdate
85
- from letta.schemas.tool import Tool, ToolCreate, ToolUpdate
78
+ from letta.schemas.tool import Tool, ToolCreate
86
79
  from letta.schemas.usage import LettaUsageStatistics
87
- from letta.schemas.user import User, UserCreate
80
+ from letta.schemas.user import User
88
81
  from letta.services.organization_manager import OrganizationManager
82
+ from letta.services.tool_manager import ToolManager
89
83
  from letta.services.user_manager import UserManager
90
84
  from letta.utils import create_random_username, json_dumps, json_loads
91
85
 
@@ -214,6 +208,7 @@ class SyncServer(Server):
214
208
  chaining: bool = True,
215
209
  max_chaining_steps: Optional[bool] = None,
216
210
  default_interface_factory: Callable[[], AgentInterface] = lambda: CLIInterface(),
211
+ init_with_default_org_and_user: bool = True,
217
212
  # default_interface: AgentInterface = CLIInterface(),
218
213
  # default_persistence_manager_cls: PersistenceManager = LocalStateManager,
219
214
  # auth_mode: str = "none", # "none, "jwt", "external"
@@ -249,13 +244,19 @@ class SyncServer(Server):
249
244
  # Managers that interface with data models
250
245
  self.organization_manager = OrganizationManager()
251
246
  self.user_manager = UserManager()
247
+ self.tool_manager = ToolManager()
252
248
 
253
- # TODO: this should be removed
254
- # add global default tools (for admin)
255
- self.add_default_tools(module_name="base")
249
+ # Make default user and org
250
+ if init_with_default_org_and_user:
251
+ self.default_org = self.organization_manager.create_default_organization()
252
+ self.default_user = self.user_manager.create_default_user()
253
+ self.add_default_blocks(self.default_user.id)
254
+ self.tool_manager.add_default_tools(module_name="base", user_id=self.default_user.id, org_id=self.default_org.id)
256
255
 
257
- if settings.load_default_external_tools:
258
- self.add_default_external_tools()
256
+ # If there is a default org/user
257
+ # This logic may have to change in the future
258
+ if settings.load_default_external_tools:
259
+ self.add_default_external_tools(user_id=self.default_user.id, org_id=self.default_org.id)
259
260
 
260
261
  # collect providers (always has Letta as a default)
261
262
  self._enabled_providers: List[Provider] = [LettaProvider()]
@@ -364,7 +365,7 @@ class SyncServer(Server):
364
365
  logger.debug(f"Creating an agent object")
365
366
  tool_objs = []
366
367
  for name in agent_state.tools:
367
- tool_obj = self.ms.get_tool(tool_name=name, user_id=user_id)
368
+ tool_obj = self.tool_manager.get_tool_by_name_and_user_id(tool_name=name, user_id=user_id)
368
369
  if not tool_obj:
369
370
  logger.exception(f"Tool {name} does not exist for user {user_id}")
370
371
  raise ValueError(f"Tool {name} does not exist for user {user_id}")
@@ -755,22 +756,6 @@ class SyncServer(Server):
755
756
  command = command[1:] # strip the prefix
756
757
  return self._command(user_id=user_id, agent_id=agent_id, command=command)
757
758
 
758
- def create_user(self, request: UserCreate) -> User:
759
- """Create a new user using a config"""
760
- if not request.name:
761
- # auto-generate a name
762
- request.name = create_random_username()
763
- user = self.user_manager.create_user(request)
764
- logger.debug(f"Created new user from config: {user}")
765
-
766
- # add default for the user
767
- # TODO: move to org
768
- assert user.id is not None, f"User id is None: {user}"
769
- self.add_default_blocks(user.id)
770
- self.add_default_tools(module_name="base", user_id=user.id)
771
-
772
- return user
773
-
774
759
  def create_agent(
775
760
  self,
776
761
  request: CreateAgent,
@@ -816,8 +801,7 @@ class SyncServer(Server):
816
801
  tool_objs = []
817
802
  if request.tools:
818
803
  for tool_name in request.tools:
819
- tool_obj = self.ms.get_tool(tool_name=tool_name, user_id=user_id)
820
- assert tool_obj, f"Tool {tool_name} does not exist"
804
+ tool_obj = self.tool_manager.get_tool_by_name_and_user_id(tool_name=tool_name, user_id=user_id)
821
805
  tool_objs.append(tool_obj)
822
806
 
823
807
  assert request.memory is not None
@@ -832,16 +816,15 @@ class SyncServer(Server):
832
816
  json_schema = generate_schema(func, terminal=False, name=func_name)
833
817
  source_type = "python"
834
818
  tags = ["memory", "memgpt-base"]
835
- tool = self.create_tool(
836
- request=ToolCreate(
819
+ tool = self.tool_manager.create_or_update_tool(
820
+ ToolCreate(
837
821
  source_code=source_code,
838
822
  source_type=source_type,
839
823
  tags=tags,
840
824
  json_schema=json_schema,
841
825
  user_id=user_id,
842
- ),
843
- update=True,
844
- user_id=user_id,
826
+ organization_id=user.organization_id,
827
+ )
845
828
  )
846
829
  tool_objs.append(tool)
847
830
  if not request.tools:
@@ -939,7 +922,7 @@ class SyncServer(Server):
939
922
  # (1) get tools + make sure they exist
940
923
  tool_objs = []
941
924
  for tool_name in request.tools:
942
- tool_obj = self.ms.get_tool(tool_name=tool_name, user_id=user_id)
925
+ tool_obj = self.tool_manager.get_tool_by_name_and_user_id(tool_name=tool_name, user_id=user_id)
943
926
  assert tool_obj, f"Tool {tool_name} does not exist"
944
927
  tool_objs.append(tool_obj)
945
928
 
@@ -995,12 +978,12 @@ class SyncServer(Server):
995
978
 
996
979
  # Get all the tool objects from the request
997
980
  tool_objs = []
998
- tool_obj = self.ms.get_tool(tool_id=tool_id, user_id=user_id)
981
+ tool_obj = self.tool_manager.get_tool_by_id(tool_id=tool_id)
999
982
  assert tool_obj, f"Tool with id={tool_id} does not exist"
1000
983
  tool_objs.append(tool_obj)
1001
984
 
1002
985
  for tool in letta_agent.tools:
1003
- tool_obj = self.ms.get_tool(tool_id=tool.id, user_id=user_id)
986
+ tool_obj = self.tool_manager.get_tool_by_id(tool_id=tool.id)
1004
987
  assert tool_obj, f"Tool with id={tool.id} does not exist"
1005
988
 
1006
989
  # If it's not the already added tool
@@ -1035,7 +1018,7 @@ class SyncServer(Server):
1035
1018
  # Get all the tool_objs
1036
1019
  tool_objs = []
1037
1020
  for tool in letta_agent.tools:
1038
- tool_obj = self.ms.get_tool(tool_id=tool.id, user_id=user_id)
1021
+ tool_obj = self.tool_manager.get_tool_by_id(tool_id=tool.id)
1039
1022
  assert tool_obj, f"Tool with id={tool.id} does not exist"
1040
1023
 
1041
1024
  # If it's not the tool we want to remove
@@ -1076,86 +1059,6 @@ class SyncServer(Server):
1076
1059
  agents_states = self.ms.list_agents(user_id=user_id)
1077
1060
  return agents_states
1078
1061
 
1079
- # TODO make return type pydantic
1080
- def list_agents_legacy(
1081
- self,
1082
- user_id: str,
1083
- ) -> dict:
1084
- """List all available agents to a user"""
1085
-
1086
- if user_id is None:
1087
- agents_states = self.ms.list_all_agents()
1088
- else:
1089
- if self.user_manager.get_user_by_id(user_id=user_id) is None:
1090
- raise ValueError(f"User user_id={user_id} does not exist")
1091
-
1092
- agents_states = self.ms.list_agents(user_id=user_id)
1093
-
1094
- agents_states_dicts = [self._agent_state_to_config(state) for state in agents_states]
1095
-
1096
- # TODO add a get_message_obj_from_message_id(...) function
1097
- # this would allow grabbing Message.created_by without having to load the agent object
1098
- # all_available_tools = self.ms.list_tools(user_id=user_id) # TODO: add back when user-specific
1099
- self.ms.list_tools()
1100
-
1101
- for agent_state, return_dict in zip(agents_states, agents_states_dicts):
1102
-
1103
- # Get the agent object (loaded in memory)
1104
- letta_agent = self._get_or_load_agent(user_id=agent_state.user_id, agent_id=agent_state.id)
1105
-
1106
- # TODO remove this eventually when return type get pydanticfied
1107
- # this is to add persona_name and human_name so that the columns in UI can populate
1108
- # TODO hack for frontend, remove
1109
- # (top level .persona is persona_name, and nested memory.persona is the state)
1110
- # TODO: eventually modify this to be contained in the metadata
1111
- return_dict["persona"] = agent_state._metadata.get("persona", None)
1112
- return_dict["human"] = agent_state._metadata.get("human", None)
1113
-
1114
- # Add information about tools
1115
- # TODO letta_agent should really have a field of List[ToolModel]
1116
- # then we could just pull that field and return it here
1117
- # return_dict["tools"] = [tool for tool in all_available_tools if tool.json_schema in letta_agent.functions]
1118
-
1119
- # get tool info from agent state
1120
- tools = []
1121
- for tool_name in agent_state.tools:
1122
- tool = self.ms.get_tool(tool_name=tool_name, user_id=user_id)
1123
- tools.append(tool)
1124
- return_dict["tools"] = tools
1125
-
1126
- # Add information about memory (raw core, size of recall, size of archival)
1127
- core_memory = letta_agent.memory
1128
- recall_memory = letta_agent.persistence_manager.recall_memory
1129
- archival_memory = letta_agent.persistence_manager.archival_memory
1130
- memory_obj = {
1131
- "core_memory": core_memory.to_flat_dict(),
1132
- "recall_memory": len(recall_memory) if recall_memory is not None else None,
1133
- "archival_memory": len(archival_memory) if archival_memory is not None else None,
1134
- }
1135
- return_dict["memory"] = memory_obj
1136
-
1137
- # Add information about last run
1138
- # NOTE: 'last_run' is just the timestamp on the latest message in the buffer
1139
- # Retrieve the Message object via the recall storage or by directly access _messages
1140
- last_msg_obj = letta_agent._messages[-1]
1141
- return_dict["last_run"] = last_msg_obj.created_at
1142
-
1143
- # Add information about attached sources
1144
- sources_ids = self.ms.list_attached_sources(agent_id=agent_state.id)
1145
- sources = [self.ms.get_source(source_id=s_id) for s_id in sources_ids]
1146
- return_dict["sources"] = [vars(s) for s in sources]
1147
-
1148
- # Sort agents by "last_run" in descending order, most recent first
1149
- agents_states_dicts.sort(key=lambda x: x["last_run"], reverse=True)
1150
-
1151
- logger.debug(f"Retrieved {len(agents_states)} agents for user {user_id}")
1152
- return {
1153
- "num_agents": len(agents_states),
1154
- "agents": agents_states_dicts,
1155
- }
1156
-
1157
- # blocks
1158
-
1159
1062
  def get_blocks(
1160
1063
  self,
1161
1064
  user_id: Optional[str] = None,
@@ -1195,7 +1098,7 @@ class SyncServer(Server):
1195
1098
  block.value = request.value if request.value is not None else block.value
1196
1099
  block.name = request.name if request.name is not None else block.name
1197
1100
  self.ms.update_block(block=block)
1198
- return block
1101
+ return self.ms.get_block(block_id=request.id)
1199
1102
 
1200
1103
  def delete_block(self, block_id: str):
1201
1104
  block = self.get_block(block_id)
@@ -1222,9 +1125,9 @@ class SyncServer(Server):
1222
1125
  raise ValueError("Source does not exist")
1223
1126
  return existing_source.id
1224
1127
 
1225
- def get_agent(self, user_id: str, agent_id: str, agent_name: Optional[str] = None):
1128
+ def get_agent(self, user_id: str, agent_id: Optional[str] = None, agent_name: Optional[str] = None):
1226
1129
  """Get the agent state"""
1227
- return self.ms.get_agent(agent_id=agent_id, user_id=user_id)
1130
+ return self.ms.get_agent(agent_id=agent_id, agent_name=agent_name, user_id=user_id)
1228
1131
 
1229
1132
  # def get_user(self, user_id: str) -> User:
1230
1133
  # """Get the user"""
@@ -1510,7 +1413,7 @@ class SyncServer(Server):
1510
1413
  if value is None:
1511
1414
  continue
1512
1415
  if letta_agent.memory.get_block(key) != value:
1513
- letta_agent.memory.update_block_value(name=key, value=value) # update agent memory
1416
+ letta_agent.memory.update_block_value(label=key, value=value) # update agent memory
1514
1417
  modified = True
1515
1418
 
1516
1419
  # If we modified the memory contents, we need to rebuild the memory block inside the system message
@@ -1830,195 +1733,17 @@ class SyncServer(Server):
1830
1733
 
1831
1734
  return sources_with_metadata
1832
1735
 
1833
- def get_tool(self, tool_id: str) -> Optional[Tool]:
1834
- """Get tool by ID."""
1835
- return self.ms.get_tool(tool_id=tool_id)
1836
-
1837
- def tool_with_name_and_user_id_exists(self, tool: Tool, user_id: Optional[str] = None) -> bool:
1838
- """Check if tool exists"""
1839
- tool = self.ms.get_tool_with_name_and_user_id(tool_name=tool.name, user_id=user_id)
1840
-
1841
- if tool is None:
1842
- return False
1843
- else:
1844
- return True
1845
-
1846
- def get_tool_id(self, name: str, user_id: str) -> Optional[str]:
1847
- """Get tool ID from name and user_id."""
1848
- tool = self.ms.get_tool(tool_name=name, user_id=user_id)
1849
- if not tool or tool.id is None:
1850
- return None
1851
- return tool.id
1852
-
1853
- def update_tool(self, request: ToolUpdate, user_id: Optional[str] = None) -> Tool:
1854
- """Update an existing tool"""
1855
- if request.name:
1856
- existing_tool = self.ms.get_tool_with_name_and_user_id(tool_name=request.name, user_id=user_id)
1857
- if existing_tool is None:
1858
- raise ValueError(f"Tool with name={request.name}, user_id={user_id} does not exist")
1859
- else:
1860
- existing_tool = self.ms.get_tool(tool_id=request.id)
1861
- if existing_tool is None:
1862
- raise ValueError(f"Tool with id={request.id} does not exist")
1863
-
1864
- # Preserve the original tool id
1865
- # As we can override the tool id as well
1866
- # This is probably bad design if this is exposed to users...
1867
- original_id = existing_tool.id
1868
-
1869
- # override updated fields
1870
- if request.id:
1871
- existing_tool.id = request.id
1872
- if request.description:
1873
- existing_tool.description = request.description
1874
- if request.source_code:
1875
- existing_tool.source_code = request.source_code
1876
- if request.source_type:
1877
- existing_tool.source_type = request.source_type
1878
- if request.tags:
1879
- existing_tool.tags = request.tags
1880
- if request.json_schema:
1881
- existing_tool.json_schema = request.json_schema
1882
-
1883
- # If name is explicitly provided here, overide the tool name
1884
- if request.name:
1885
- existing_tool.name = request.name
1886
- # Otherwise, if there's no name, and there's source code, we try to derive the name
1887
- elif request.source_code:
1888
- existing_tool.name = derive_function_name_regex(request.source_code)
1889
-
1890
- self.ms.update_tool(original_id, existing_tool)
1891
- return self.ms.get_tool(tool_id=request.id)
1892
-
1893
- def create_tool(self, request: ToolCreate, user_id: Optional[str] = None, update: bool = True) -> Tool: # TODO: add other fields
1894
- """Create a new tool"""
1895
-
1896
- # NOTE: deprecated code that existed when we were trying to pretend that `self` was the memory object
1897
- # if request.tags and "memory" in request.tags:
1898
- # # special modifications to memory functions
1899
- # # self.memory -> self.memory.memory, since Agent.memory.memory needs to be modified (not BaseMemory.memory)
1900
- # request.source_code = request.source_code.replace("self.memory", "self.memory.memory")
1901
-
1902
- if not request.json_schema:
1903
- # auto-generate openai schema
1904
- try:
1905
- env = {}
1906
- env.update(globals())
1907
- exec(request.source_code, env)
1908
-
1909
- # get available functions
1910
- functions = [f for f in env if callable(env[f])]
1911
-
1912
- except Exception as e:
1913
- logger.error(f"Failed to execute source code: {e}")
1914
-
1915
- # TODO: not sure if this always works
1916
- func = env[functions[-1]]
1917
- json_schema = generate_schema(func, terminal=request.terminal)
1918
- else:
1919
- # provided by client
1920
- json_schema = request.json_schema
1921
-
1922
- if not request.name:
1923
- # use name from JSON schema
1924
- request.name = json_schema["name"]
1925
- assert request.name, f"Tool name must be provided in json_schema {json_schema}. This should never happen."
1926
-
1927
- # check if already exists:
1928
- existing_tool = self.ms.get_tool(tool_id=request.id, tool_name=request.name, user_id=user_id)
1929
- if existing_tool:
1930
- if update:
1931
- # id is an optional field, so we will fill it with the existing tool id
1932
- if not request.id:
1933
- request.id = existing_tool.id
1934
- updated_tool = self.update_tool(ToolUpdate(**vars(request)), user_id)
1935
- assert updated_tool is not None, f"Failed to update tool {request.name}"
1936
- return updated_tool
1937
- else:
1938
- raise ValueError(f"Tool {request.name} already exists and update=False")
1939
-
1940
- # check for description
1941
- description = None
1942
- if request.description:
1943
- description = request.description
1944
-
1945
- tool = Tool(
1946
- name=request.name,
1947
- source_code=request.source_code,
1948
- source_type=request.source_type,
1949
- tags=request.tags,
1950
- json_schema=json_schema,
1951
- user_id=user_id,
1952
- description=description,
1953
- )
1954
-
1955
- if request.id:
1956
- tool.id = request.id
1957
-
1958
- self.ms.create_tool(tool)
1959
- created_tool = self.ms.get_tool(tool_id=tool.id, user_id=user_id)
1960
- return created_tool
1961
-
1962
- def delete_tool(self, tool_id: str):
1963
- """Delete a tool"""
1964
- self.ms.delete_tool(tool_id)
1965
-
1966
- def list_tools(self, cursor: Optional[str] = None, limit: Optional[int] = 50, user_id: Optional[str] = None) -> List[Tool]:
1967
- """List tools available to user_id"""
1968
- tools = self.ms.list_tools(cursor=cursor, limit=limit, user_id=user_id)
1969
- return tools
1970
-
1971
- def add_default_tools(self, module_name="base", user_id: Optional[str] = None):
1972
- """Add default tools in {module_name}.py"""
1973
- full_module_name = f"letta.functions.function_sets.{module_name}"
1974
- try:
1975
- module = importlib.import_module(full_module_name)
1976
- except Exception as e:
1977
- # Handle other general exceptions
1978
- raise e
1979
-
1980
- functions_to_schema = []
1981
- try:
1982
- # Load the function set
1983
- functions_to_schema = load_function_set(module)
1984
- except ValueError as e:
1985
- err = f"Error loading function set '{module_name}': {e}"
1986
- warnings.warn(err)
1987
-
1988
- # create tool in db
1989
- for name, schema in functions_to_schema.items():
1990
- # print([str(inspect.getsource(line)) for line in schema["imports"]])
1991
- source_code = inspect.getsource(schema["python_function"])
1992
- tags = [module_name]
1993
- if module_name == "base":
1994
- tags.append("letta-base")
1995
-
1996
- # create to tool
1997
- self.create_tool(
1998
- ToolCreate(
1999
- name=name,
2000
- tags=tags,
2001
- source_type="python",
2002
- module=schema["module"],
2003
- source_code=source_code,
2004
- json_schema=schema["json_schema"],
2005
- user_id=user_id,
2006
- ),
2007
- update=True,
2008
- )
2009
-
2010
- def add_default_external_tools(self, user_id: Optional[str] = None) -> bool:
1736
+ def add_default_external_tools(self, user_id: str, org_id: str) -> bool:
2011
1737
  """Add default langchain tools. Return true if successful, false otherwise."""
2012
1738
  success = True
1739
+ tool_creates = ToolCreate.load_default_langchain_tools() + ToolCreate.load_default_crewai_tools()
2013
1740
  if tool_settings.composio_api_key:
2014
- tools = Tool.load_default_langchain_tools() + Tool.load_default_crewai_tools() + Tool.load_default_composio_tools()
2015
- else:
2016
- tools = Tool.load_default_langchain_tools() + Tool.load_default_crewai_tools()
2017
- for tool in tools:
1741
+ tool_creates += ToolCreate.load_default_composio_tools()
1742
+ for tool_create in tool_creates:
2018
1743
  try:
2019
- self.ms.create_tool(tool)
1744
+ self.tool_manager.create_or_update_tool(tool_create)
2020
1745
  except Exception as e:
2021
- warnings.warn(f"An error occurred while creating tool {tool}: {e}")
1746
+ warnings.warn(f"An error occurred while creating tool {tool_create}: {e}")
2022
1747
  warnings.warn(traceback.format_exc())
2023
1748
  success = False
2024
1749
 
@@ -2108,25 +1833,15 @@ class SyncServer(Server):
2108
1833
  letta_agent = self._get_or_load_agent(agent_id=agent_id)
2109
1834
  return letta_agent.retry_message()
2110
1835
 
2111
- # TODO: Move a lot of this default logic to the ORM
2112
- def get_default_user(self) -> User:
2113
- self.organization_manager.create_default_organization()
2114
- user = self.user_manager.create_default_user()
2115
-
2116
- self.add_default_blocks(user.id)
2117
- self.add_default_tools(module_name="base", user_id=user.id)
2118
-
2119
- return user
2120
-
2121
1836
  def get_user_or_default(self, user_id: Optional[str]) -> User:
2122
1837
  """Get the user object for user_id if it exists, otherwise return the default user object"""
2123
1838
  if user_id is None:
2124
- return self.get_default_user()
2125
- else:
2126
- try:
2127
- return self.user_manager.get_user_by_id(user_id=user_id)
2128
- except ValueError:
2129
- raise HTTPException(status_code=404, detail=f"User with id {user_id} not found")
1839
+ user_id = self.user_manager.DEFAULT_USER_ID
1840
+
1841
+ try:
1842
+ return self.user_manager.get_user_by_id(user_id=user_id)
1843
+ except ValueError:
1844
+ raise HTTPException(status_code=404, detail=f"User with id {user_id} not found")
2130
1845
 
2131
1846
  def list_llm_models(self) -> List[LLMConfig]:
2132
1847
  """List available models"""
@@ -1,8 +1,7 @@
1
1
  from typing import List, Optional
2
2
 
3
- from letta.constants import DEFAULT_ORG_ID, DEFAULT_ORG_NAME
4
3
  from letta.orm.errors import NoResultFound
5
- from letta.orm.organization import Organization
4
+ from letta.orm.organization import Organization as OrganizationModel
6
5
  from letta.schemas.organization import Organization as PydanticOrganization
7
6
  from letta.utils import create_random_username, enforce_types
8
7
 
@@ -10,6 +9,9 @@ from letta.utils import create_random_username, enforce_types
10
9
  class OrganizationManager:
11
10
  """Manager class to handle business logic related to Organizations."""
12
11
 
12
+ DEFAULT_ORG_ID = "organization-00000000-0000-4000-8000-000000000000"
13
+ DEFAULT_ORG_NAME = "default_org"
14
+
13
15
  def __init__(self):
14
16
  # This is probably horrible but we reuse this technique from metadata.py
15
17
  # TODO: Please refactor this out
@@ -19,12 +21,17 @@ class OrganizationManager:
19
21
 
20
22
  self.session_maker = db_context
21
23
 
24
+ @enforce_types
25
+ def get_default_organization(self) -> PydanticOrganization:
26
+ """Fetch the default organization."""
27
+ return self.get_organization_by_id(self.DEFAULT_ORG_ID)
28
+
22
29
  @enforce_types
23
30
  def get_organization_by_id(self, org_id: str) -> PydanticOrganization:
24
31
  """Fetch an organization by ID."""
25
32
  with self.session_maker() as session:
26
33
  try:
27
- organization = Organization.read(db_session=session, identifier=org_id)
34
+ organization = OrganizationModel.read(db_session=session, identifier=org_id)
28
35
  return organization.to_pydantic()
29
36
  except NoResultFound:
30
37
  raise ValueError(f"Organization with id {org_id} not found.")
@@ -33,7 +40,7 @@ class OrganizationManager:
33
40
  def create_organization(self, name: Optional[str] = None) -> PydanticOrganization:
34
41
  """Create a new organization. If a name is provided, it is used, otherwise, a random one is generated."""
35
42
  with self.session_maker() as session:
36
- org = Organization(name=name if name else create_random_username())
43
+ org = OrganizationModel(name=name if name else create_random_username())
37
44
  org.create(session)
38
45
  return org.to_pydantic()
39
46
 
@@ -43,10 +50,10 @@ class OrganizationManager:
43
50
  with self.session_maker() as session:
44
51
  # Try to get it first
45
52
  try:
46
- org = Organization.read(db_session=session, identifier=DEFAULT_ORG_ID)
53
+ org = OrganizationModel.read(db_session=session, identifier=self.DEFAULT_ORG_ID)
47
54
  # If it doesn't exist, make it
48
55
  except NoResultFound:
49
- org = Organization(name=DEFAULT_ORG_NAME, id=DEFAULT_ORG_ID)
56
+ org = OrganizationModel(name=self.DEFAULT_ORG_NAME, id=self.DEFAULT_ORG_ID)
50
57
  org.create(session)
51
58
 
52
59
  return org.to_pydantic()
@@ -55,22 +62,22 @@ class OrganizationManager:
55
62
  def update_organization_name_using_id(self, org_id: str, name: Optional[str] = None) -> PydanticOrganization:
56
63
  """Update an organization."""
57
64
  with self.session_maker() as session:
58
- organization = Organization.read(db_session=session, identifier=org_id)
65
+ org = OrganizationModel.read(db_session=session, identifier=org_id)
59
66
  if name:
60
- organization.name = name
61
- organization.update(session)
62
- return organization.to_pydantic()
67
+ org.name = name
68
+ org.update(session)
69
+ return org.to_pydantic()
63
70
 
64
71
  @enforce_types
65
72
  def delete_organization_by_id(self, org_id: str):
66
73
  """Delete an organization by marking it as deleted."""
67
74
  with self.session_maker() as session:
68
- organization = Organization.read(db_session=session, identifier=org_id)
75
+ organization = OrganizationModel.read(db_session=session, identifier=org_id)
69
76
  organization.delete(session)
70
77
 
71
78
  @enforce_types
72
79
  def list_organizations(self, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[PydanticOrganization]:
73
80
  """List organizations with pagination based on cursor (org_id) and limit."""
74
81
  with self.session_maker() as session:
75
- results = Organization.list(db_session=session, cursor=cursor, limit=limit)
82
+ results = OrganizationModel.list(db_session=session, cursor=cursor, limit=limit)
76
83
  return [org.to_pydantic() for org in results]