letta-nightly 0.5.1.dev20241104104148__py3-none-any.whl → 0.5.1.dev20241105104128__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/agent.py CHANGED
@@ -235,6 +235,7 @@ class Agent(BaseAgent):
235
235
  # extras
236
236
  messages_total: Optional[int] = None, # TODO remove?
237
237
  first_message_verify_mono: bool = True, # TODO move to config?
238
+ initial_message_sequence: Optional[List[Message]] = None,
238
239
  ):
239
240
  assert isinstance(agent_state.memory, Memory), f"Memory object is not of type Memory: {type(agent_state.memory)}"
240
241
  # Hold a copy of the state that was used to init the agent
@@ -294,6 +295,7 @@ class Agent(BaseAgent):
294
295
 
295
296
  else:
296
297
  printd(f"Agent.__init__ :: creating, state={agent_state.message_ids}")
298
+ assert self.agent_state.id is not None and self.agent_state.user_id is not None
297
299
 
298
300
  # Generate a sequence of initial messages to put in the buffer
299
301
  init_messages = initialize_message_sequence(
@@ -306,14 +308,40 @@ class Agent(BaseAgent):
306
308
  include_initial_boot_message=True,
307
309
  )
308
310
 
309
- # Cast the messages to actual Message objects to be synced to the DB
310
- init_messages_objs = []
311
- for msg in init_messages:
312
- init_messages_objs.append(
311
+ if initial_message_sequence is not None:
312
+ # We always need the system prompt up front
313
+ system_message_obj = Message.dict_to_message(
314
+ agent_id=self.agent_state.id,
315
+ user_id=self.agent_state.user_id,
316
+ model=self.model,
317
+ openai_message_dict=init_messages[0],
318
+ )
319
+ # Don't use anything else in the pregen sequence, instead use the provided sequence
320
+ init_messages = [system_message_obj] + initial_message_sequence
321
+
322
+ else:
323
+ # Basic "more human than human" initial message sequence
324
+ init_messages = initialize_message_sequence(
325
+ model=self.model,
326
+ system=self.system,
327
+ memory=self.memory,
328
+ archival_memory=None,
329
+ recall_memory=None,
330
+ memory_edit_timestamp=get_utc_time(),
331
+ include_initial_boot_message=True,
332
+ )
333
+ # Cast to Message objects
334
+ init_messages = [
313
335
  Message.dict_to_message(
314
336
  agent_id=self.agent_state.id, user_id=self.agent_state.user_id, model=self.model, openai_message_dict=msg
315
337
  )
316
- )
338
+ for msg in init_messages
339
+ ]
340
+
341
+ # Cast the messages to actual Message objects to be synced to the DB
342
+ init_messages_objs = []
343
+ for msg in init_messages:
344
+ init_messages_objs.append(msg)
317
345
  assert all([isinstance(msg, Message) for msg in init_messages_objs]), (init_messages_objs, init_messages)
318
346
 
319
347
  # Put the messages inside the message buffer
letta/cli/cli.py CHANGED
@@ -282,10 +282,10 @@ def run(
282
282
  system_prompt = system if system else None
283
283
 
284
284
  memory = ChatMemory(human=human_obj.value, persona=persona_obj.value, limit=core_memory_limit)
285
- metadata = {"human": human_obj.name, "persona": persona_obj.name}
285
+ metadata = {"human": human_obj.template_name, "persona": persona_obj.template_name}
286
286
 
287
- typer.secho(f"-> {ASSISTANT_MESSAGE_CLI_SYMBOL} Using persona profile: '{persona_obj.name}'", fg=typer.colors.WHITE)
288
- typer.secho(f"-> 🧑 Using human profile: '{human_obj.name}'", fg=typer.colors.WHITE)
287
+ typer.secho(f"-> {ASSISTANT_MESSAGE_CLI_SYMBOL} Using persona profile: '{persona_obj.template_name}'", fg=typer.colors.WHITE)
288
+ typer.secho(f"-> 🧑 Using human profile: '{human_obj.template_name}'", fg=typer.colors.WHITE)
289
289
 
290
290
  # add tools
291
291
  agent_state = client.create_agent(
letta/cli/cli_config.py CHANGED
@@ -59,13 +59,13 @@ def list(arg: Annotated[ListChoice, typer.Argument]):
59
59
  """List all humans"""
60
60
  table.field_names = ["Name", "Text"]
61
61
  for human in client.list_humans():
62
- table.add_row([human.name, human.value.replace("\n", "")[:100]])
62
+ table.add_row([human.template_name, human.value.replace("\n", "")[:100]])
63
63
  print(table)
64
64
  elif arg == ListChoice.personas:
65
65
  """List all personas"""
66
66
  table.field_names = ["Name", "Text"]
67
67
  for persona in client.list_personas():
68
- table.add_row([persona.name, persona.value.replace("\n", "")[:100]])
68
+ table.add_row([persona.template_name, persona.value.replace("\n", "")[:100]])
69
69
  print(table)
70
70
  elif arg == ListChoice.sources:
71
71
  """List all data sources"""
letta/client/client.py CHANGED
@@ -376,6 +376,7 @@ class RESTClient(AbstractClient):
376
376
  # metadata
377
377
  metadata: Optional[Dict] = {"human:": DEFAULT_HUMAN, "persona": DEFAULT_PERSONA},
378
378
  description: Optional[str] = None,
379
+ initial_message_sequence: Optional[List[Message]] = None,
379
380
  ) -> AgentState:
380
381
  """Create an agent
381
382
 
@@ -428,9 +429,18 @@ class RESTClient(AbstractClient):
428
429
  agent_type=agent_type,
429
430
  llm_config=llm_config if llm_config else self._default_llm_config,
430
431
  embedding_config=embedding_config if embedding_config else self._default_embedding_config,
432
+ initial_message_sequence=initial_message_sequence,
433
+ )
434
+
435
+ # Use model_dump_json() instead of model_dump()
436
+ # If we use model_dump(), the datetime objects will not be serialized correctly
437
+ # response = requests.post(f"{self.base_url}/{self.api_prefix}/agents", json=request.model_dump(), headers=self.headers)
438
+ response = requests.post(
439
+ f"{self.base_url}/{self.api_prefix}/agents",
440
+ data=request.model_dump_json(), # Use model_dump_json() instead of json=model_dump()
441
+ headers={"Content-Type": "application/json", **self.headers},
431
442
  )
432
443
 
433
- response = requests.post(f"{self.base_url}/{self.api_prefix}/agents", json=request.model_dump(), headers=self.headers)
434
444
  if response.status_code != 200:
435
445
  raise ValueError(f"Status {response.status_code} - Failed to create agent: {response.text}")
436
446
  return AgentState(**response.json())
@@ -859,8 +869,8 @@ class RESTClient(AbstractClient):
859
869
  else:
860
870
  return [Block(**block) for block in response.json()]
861
871
 
862
- def create_block(self, label: str, text: str, name: Optional[str] = None, template: bool = False) -> Block: #
863
- request = CreateBlock(label=label, value=text, template=template, name=name)
872
+ def create_block(self, label: str, text: str, template_name: Optional[str] = None, template: bool = False) -> Block: #
873
+ request = CreateBlock(label=label, value=text, template=template, template_name=template_name)
864
874
  response = requests.post(f"{self.base_url}/{self.api_prefix}/blocks", json=request.model_dump(), headers=self.headers)
865
875
  if response.status_code != 200:
866
876
  raise ValueError(f"Failed to create block: {response.text}")
@@ -872,7 +882,7 @@ class RESTClient(AbstractClient):
872
882
  return Block(**response.json())
873
883
 
874
884
  def update_block(self, block_id: str, name: Optional[str] = None, text: Optional[str] = None) -> Block:
875
- request = UpdateBlock(id=block_id, name=name, value=text)
885
+ request = UpdateBlock(id=block_id, template_name=name, value=text)
876
886
  response = requests.post(f"{self.base_url}/{self.api_prefix}/blocks/{block_id}", json=request.model_dump(), headers=self.headers)
877
887
  if response.status_code != 200:
878
888
  raise ValueError(f"Failed to update block: {response.text}")
@@ -926,7 +936,7 @@ class RESTClient(AbstractClient):
926
936
  Returns:
927
937
  human (Human): Human block
928
938
  """
929
- return self.create_block(label="human", name=name, text=text, template=True)
939
+ return self.create_block(label="human", template_name=name, text=text, template=True)
930
940
 
931
941
  def update_human(self, human_id: str, name: Optional[str] = None, text: Optional[str] = None) -> Human:
932
942
  """
@@ -939,7 +949,7 @@ class RESTClient(AbstractClient):
939
949
  Returns:
940
950
  human (Human): Updated human block
941
951
  """
942
- request = UpdateHuman(id=human_id, name=name, value=text)
952
+ request = UpdateHuman(id=human_id, template_name=name, value=text)
943
953
  response = requests.post(f"{self.base_url}/{self.api_prefix}/blocks/{human_id}", json=request.model_dump(), headers=self.headers)
944
954
  if response.status_code != 200:
945
955
  raise ValueError(f"Failed to update human: {response.text}")
@@ -966,7 +976,7 @@ class RESTClient(AbstractClient):
966
976
  Returns:
967
977
  persona (Persona): Persona block
968
978
  """
969
- return self.create_block(label="persona", name=name, text=text, template=True)
979
+ return self.create_block(label="persona", template_name=name, text=text, template=True)
970
980
 
971
981
  def update_persona(self, persona_id: str, name: Optional[str] = None, text: Optional[str] = None) -> Persona:
972
982
  """
@@ -979,7 +989,7 @@ class RESTClient(AbstractClient):
979
989
  Returns:
980
990
  persona (Persona): Updated persona block
981
991
  """
982
- request = UpdatePersona(id=persona_id, name=name, value=text)
992
+ request = UpdatePersona(id=persona_id, template_name=name, value=text)
983
993
  response = requests.post(f"{self.base_url}/{self.api_prefix}/blocks/{persona_id}", json=request.model_dump(), headers=self.headers)
984
994
  if response.status_code != 200:
985
995
  raise ValueError(f"Failed to update persona: {response.text}")
@@ -1648,6 +1658,7 @@ class LocalClient(AbstractClient):
1648
1658
  # metadata
1649
1659
  metadata: Optional[Dict] = {"human:": DEFAULT_HUMAN, "persona": DEFAULT_PERSONA},
1650
1660
  description: Optional[str] = None,
1661
+ initial_message_sequence: Optional[List[Message]] = None,
1651
1662
  ) -> AgentState:
1652
1663
  """Create an agent
1653
1664
 
@@ -1702,6 +1713,7 @@ class LocalClient(AbstractClient):
1702
1713
  agent_type=agent_type,
1703
1714
  llm_config=llm_config if llm_config else self._default_llm_config,
1704
1715
  embedding_config=embedding_config if embedding_config else self._default_embedding_config,
1716
+ initial_message_sequence=initial_message_sequence,
1705
1717
  ),
1706
1718
  actor=self.user,
1707
1719
  )
@@ -2116,7 +2128,7 @@ class LocalClient(AbstractClient):
2116
2128
  Returns:
2117
2129
  human (Human): Human block
2118
2130
  """
2119
- return self.server.create_block(CreateHuman(name=name, value=text, user_id=self.user_id), user_id=self.user_id)
2131
+ return self.server.create_block(CreateHuman(template_name=name, value=text, user_id=self.user_id), user_id=self.user_id)
2120
2132
 
2121
2133
  def create_persona(self, name: str, text: str):
2122
2134
  """
@@ -2129,7 +2141,7 @@ class LocalClient(AbstractClient):
2129
2141
  Returns:
2130
2142
  persona (Persona): Persona block
2131
2143
  """
2132
- return self.server.create_block(CreatePersona(name=name, value=text, user_id=self.user_id), user_id=self.user_id)
2144
+ return self.server.create_block(CreatePersona(template_name=name, value=text, user_id=self.user_id), user_id=self.user_id)
2133
2145
 
2134
2146
  def list_humans(self):
2135
2147
  """
@@ -2635,7 +2647,7 @@ class LocalClient(AbstractClient):
2635
2647
  """
2636
2648
  return self.server.get_blocks(label=label, template=templates_only)
2637
2649
 
2638
- def create_block(self, label: str, text: str, name: Optional[str] = None, template: bool = False) -> Block: #
2650
+ def create_block(self, label: str, text: str, template_name: Optional[str] = None, template: bool = False) -> Block: #
2639
2651
  """
2640
2652
  Create a block
2641
2653
 
@@ -2648,7 +2660,7 @@ class LocalClient(AbstractClient):
2648
2660
  block (Block): Created block
2649
2661
  """
2650
2662
  return self.server.create_block(
2651
- CreateBlock(label=label, name=name, value=text, user_id=self.user_id, template=template), user_id=self.user_id
2663
+ CreateBlock(label=label, template_name=template_name, value=text, user_id=self.user_id, template=template), user_id=self.user_id
2652
2664
  )
2653
2665
 
2654
2666
  def update_block(self, block_id: str, name: Optional[str] = None, text: Optional[str] = None) -> Block:
@@ -2663,7 +2675,7 @@ class LocalClient(AbstractClient):
2663
2675
  Returns:
2664
2676
  block (Block): Updated block
2665
2677
  """
2666
- return self.server.update_block(UpdateBlock(id=block_id, name=name, value=text))
2678
+ return self.server.update_block(UpdateBlock(id=block_id, template_name=name, value=text))
2667
2679
 
2668
2680
  def get_block(self, block_id: str) -> Block:
2669
2681
  """
@@ -139,32 +139,73 @@ def generate_schema(function, name: Optional[str] = None, description: Optional[
139
139
  return schema
140
140
 
141
141
 
142
- def generate_schema_from_args_schema(
142
+ def generate_schema_from_args_schema_v1(
143
143
  args_schema: Type[V1BaseModel], name: Optional[str] = None, description: Optional[str] = None, append_heartbeat: bool = True
144
144
  ) -> Dict[str, Any]:
145
145
  properties = {}
146
146
  required = []
147
147
  for field_name, field in args_schema.__fields__.items():
148
- if field.type_.__name__ == "str":
148
+ if field.type_ == str:
149
149
  field_type = "string"
150
- elif field.type_.__name__ == "int":
150
+ elif field.type_ == int:
151
151
  field_type = "integer"
152
- elif field.type_.__name__ == "bool":
152
+ elif field.type_ == bool:
153
153
  field_type = "boolean"
154
154
  else:
155
155
  field_type = field.type_.__name__
156
- properties[field_name] = {"type": field_type, "description": field.field_info.description}
156
+
157
+ properties[field_name] = {
158
+ "type": field_type,
159
+ "description": field.field_info.description,
160
+ }
157
161
  if field.required:
158
162
  required.append(field_name)
159
163
 
160
- # Construct the OpenAI function call JSON object
161
164
  function_call_json = {
162
165
  "name": name,
163
166
  "description": description,
164
167
  "parameters": {"type": "object", "properties": properties, "required": required},
165
168
  }
166
169
 
167
- # append heartbeat (necessary for triggering another reasoning step after this tool call)
170
+ if append_heartbeat:
171
+ function_call_json["parameters"]["properties"]["request_heartbeat"] = {
172
+ "type": "boolean",
173
+ "description": "Request an immediate heartbeat after function execution. Set to `True` if you want to send a follow-up message or run a follow-up function.",
174
+ }
175
+ function_call_json["parameters"]["required"].append("request_heartbeat")
176
+
177
+ return function_call_json
178
+
179
+
180
+ def generate_schema_from_args_schema_v2(
181
+ args_schema: Type[BaseModel], name: Optional[str] = None, description: Optional[str] = None, append_heartbeat: bool = True
182
+ ) -> Dict[str, Any]:
183
+ properties = {}
184
+ required = []
185
+ for field_name, field in args_schema.model_fields.items():
186
+ field_type_annotation = field.annotation
187
+ if field_type_annotation == str:
188
+ field_type = "string"
189
+ elif field_type_annotation == int:
190
+ field_type = "integer"
191
+ elif field_type_annotation == bool:
192
+ field_type = "boolean"
193
+ else:
194
+ field_type = field_type_annotation.__name__
195
+
196
+ properties[field_name] = {
197
+ "type": field_type,
198
+ "description": field.description,
199
+ }
200
+ if field.is_required():
201
+ required.append(field_name)
202
+
203
+ function_call_json = {
204
+ "name": name,
205
+ "description": description,
206
+ "parameters": {"type": "object", "properties": properties, "required": required},
207
+ }
208
+
168
209
  if append_heartbeat:
169
210
  function_call_json["parameters"]["properties"]["request_heartbeat"] = {
170
211
  "type": "boolean",
letta/metadata.py CHANGED
@@ -348,7 +348,7 @@ class BlockModel(Base):
348
348
  id = Column(String, primary_key=True, nullable=False)
349
349
  value = Column(String, nullable=False)
350
350
  limit = Column(BIGINT)
351
- name = Column(String)
351
+ template_name = Column(String, nullable=True, default=None)
352
352
  template = Column(Boolean, default=False) # True: listed as possible human/persona
353
353
  label = Column(String, nullable=False)
354
354
  metadata_ = Column(JSON)
@@ -357,7 +357,7 @@ class BlockModel(Base):
357
357
  Index(__tablename__ + "_idx_user", user_id),
358
358
 
359
359
  def __repr__(self) -> str:
360
- return f"<Block(id='{self.id}', name='{self.name}', template='{self.template}', label='{self.label}', user_id='{self.user_id}')>"
360
+ return f"<Block(id='{self.id}', template_name='{self.template_name}', template='{self.template_name}', label='{self.label}', user_id='{self.user_id}')>"
361
361
 
362
362
  def to_record(self) -> Block:
363
363
  if self.label == "persona":
@@ -365,7 +365,7 @@ class BlockModel(Base):
365
365
  id=self.id,
366
366
  value=self.value,
367
367
  limit=self.limit,
368
- name=self.name,
368
+ template_name=self.template_name,
369
369
  template=self.template,
370
370
  label=self.label,
371
371
  metadata_=self.metadata_,
@@ -377,7 +377,7 @@ class BlockModel(Base):
377
377
  id=self.id,
378
378
  value=self.value,
379
379
  limit=self.limit,
380
- name=self.name,
380
+ template_name=self.template_name,
381
381
  template=self.template,
382
382
  label=self.label,
383
383
  metadata_=self.metadata_,
@@ -389,7 +389,7 @@ class BlockModel(Base):
389
389
  id=self.id,
390
390
  value=self.value,
391
391
  limit=self.limit,
392
- name=self.name,
392
+ template_name=self.template_name,
393
393
  template=self.template,
394
394
  label=self.label,
395
395
  metadata_=self.metadata_,
@@ -512,7 +512,7 @@ class MetadataStore:
512
512
  # with a given name doesn't exist.
513
513
  if (
514
514
  session.query(BlockModel)
515
- .filter(BlockModel.name == block.name)
515
+ .filter(BlockModel.template_name == block.template_name)
516
516
  .filter(BlockModel.user_id == block.user_id)
517
517
  .filter(BlockModel.template == True)
518
518
  .filter(BlockModel.label == block.label)
@@ -520,7 +520,7 @@ class MetadataStore:
520
520
  > 0
521
521
  ):
522
522
 
523
- raise ValueError(f"Block with name {block.name} already exists")
523
+ raise ValueError(f"Block with name {block.template_name} already exists")
524
524
  session.add(BlockModel(**vars(block)))
525
525
  session.commit()
526
526
 
@@ -658,7 +658,7 @@ class MetadataStore:
658
658
  user_id: Optional[str],
659
659
  label: Optional[str] = None,
660
660
  template: Optional[bool] = None,
661
- name: Optional[str] = None,
661
+ template_name: Optional[str] = None,
662
662
  id: Optional[str] = None,
663
663
  ) -> Optional[List[Block]]:
664
664
  """List available blocks"""
@@ -671,8 +671,8 @@ class MetadataStore:
671
671
  if label:
672
672
  query = query.filter(BlockModel.label == label)
673
673
 
674
- if name:
675
- query = query.filter(BlockModel.name == name)
674
+ if template_name:
675
+ query = query.filter(BlockModel.template_name == template_name)
676
676
 
677
677
  if id:
678
678
  query = query.filter(BlockModel.id == id)
letta/schemas/agent.py CHANGED
@@ -1,4 +1,3 @@
1
- import uuid
2
1
  from datetime import datetime
3
2
  from enum import Enum
4
3
  from typing import Dict, List, Optional
@@ -105,7 +104,7 @@ class AgentState(BaseAgent, validate_assignment=True):
105
104
  class CreateAgent(BaseAgent):
106
105
  # all optional as server can generate defaults
107
106
  name: Optional[str] = Field(None, description="The name of the agent.")
108
- message_ids: Optional[List[uuid.UUID]] = Field(None, description="The ids of the messages in the agent's in-context memory.")
107
+ message_ids: Optional[List[str]] = Field(None, description="The ids of the messages in the agent's in-context memory.")
109
108
  memory: Optional[Memory] = Field(None, description="The in-context memory of the agent.")
110
109
  tools: Optional[List[str]] = Field(None, description="The tools used by the agent.")
111
110
  tool_rules: Optional[List[BaseToolRule]] = Field(None, description="The tool rules governing the agent.")
@@ -113,6 +112,11 @@ class CreateAgent(BaseAgent):
113
112
  agent_type: Optional[AgentType] = Field(None, description="The type of agent.")
114
113
  llm_config: Optional[LLMConfig] = Field(None, description="The LLM configuration used by the agent.")
115
114
  embedding_config: Optional[EmbeddingConfig] = Field(None, description="The embedding configuration used by the agent.")
115
+ # Note: if this is None, then we'll populate with the standard "more human than human" initial message sequence
116
+ # If the client wants to make this empty, then the client can set the arg to an empty list
117
+ initial_message_sequence: Optional[List[Message]] = Field(
118
+ None, description="The initial set of messages to put in the agent's in-context memory."
119
+ )
116
120
 
117
121
  @field_validator("name")
118
122
  @classmethod
letta/schemas/block.py CHANGED
@@ -18,7 +18,7 @@ class BaseBlock(LettaBase, validate_assignment=True):
18
18
  limit: int = Field(2000, description="Character limit of the block.")
19
19
 
20
20
  # template data (optional)
21
- name: Optional[str] = Field(None, description="Name of the block if it is a template.")
21
+ template_name: Optional[str] = Field(None, description="Name of the block if it is a template.")
22
22
  template: bool = Field(False, description="Whether the block is a template (e.g. saved human/persona options).")
23
23
 
24
24
  # context window label
@@ -21,6 +21,8 @@ class LettaBase(BaseModel):
21
21
  from_attributes=True,
22
22
  # throw errors if attributes are given that don't belong
23
23
  extra="forbid",
24
+ # handle datetime serialization consistently across all models
25
+ # json_encoders={datetime: lambda dt: (dt.replace(tzinfo=timezone.utc) if dt.tzinfo is None else dt).isoformat()},
24
26
  )
25
27
 
26
28
  # def __id_prefix__(self):
letta/schemas/tool.py CHANGED
@@ -8,7 +8,10 @@ from letta.functions.helpers import (
8
8
  generate_crewai_tool_wrapper,
9
9
  generate_langchain_tool_wrapper,
10
10
  )
11
- from letta.functions.schema_generator import generate_schema_from_args_schema
11
+ from letta.functions.schema_generator import (
12
+ generate_schema_from_args_schema_v1,
13
+ generate_schema_from_args_schema_v2,
14
+ )
12
15
  from letta.schemas.letta_base import LettaBase
13
16
  from letta.schemas.openai.chat_completions import ToolCall
14
17
 
@@ -97,7 +100,7 @@ class ToolCreate(LettaBase):
97
100
  source_type = "python"
98
101
  tags = ["composio"]
99
102
  wrapper_func_name, wrapper_function_str = generate_composio_tool_wrapper(action)
100
- json_schema = generate_schema_from_args_schema(composio_tool.args_schema, name=wrapper_func_name, description=description)
103
+ json_schema = generate_schema_from_args_schema_v2(composio_tool.args_schema, name=wrapper_func_name, description=description)
101
104
 
102
105
  return cls(
103
106
  name=wrapper_func_name,
@@ -129,7 +132,7 @@ class ToolCreate(LettaBase):
129
132
  tags = ["langchain"]
130
133
  # NOTE: langchain tools may come from different packages
131
134
  wrapper_func_name, wrapper_function_str = generate_langchain_tool_wrapper(langchain_tool, additional_imports_module_attr_map)
132
- json_schema = generate_schema_from_args_schema(langchain_tool.args_schema, name=wrapper_func_name, description=description)
135
+ json_schema = generate_schema_from_args_schema_v1(langchain_tool.args_schema, name=wrapper_func_name, description=description)
133
136
 
134
137
  return cls(
135
138
  name=wrapper_func_name,
@@ -159,7 +162,7 @@ class ToolCreate(LettaBase):
159
162
  source_type = "python"
160
163
  tags = ["crew-ai"]
161
164
  wrapper_func_name, wrapper_function_str = generate_crewai_tool_wrapper(crewai_tool, additional_imports_module_attr_map)
162
- json_schema = generate_schema_from_args_schema(crewai_tool.args_schema, name=wrapper_func_name, description=description)
165
+ json_schema = generate_schema_from_args_schema_v1(crewai_tool.args_schema, name=wrapper_func_name, description=description)
163
166
 
164
167
  return cls(
165
168
  name=wrapper_func_name,
letta/server/server.py CHANGED
@@ -857,7 +857,10 @@ class SyncServer(Server):
857
857
  agent_state=agent_state,
858
858
  tools=tool_objs,
859
859
  # gpt-3.5-turbo tends to omit inner monologue, relax this requirement for now
860
- first_message_verify_mono=True if (llm_config.model is not None and "gpt-4" in llm_config.model) else False,
860
+ first_message_verify_mono=(
861
+ True if (llm_config and llm_config.model is not None and "gpt-4" in llm_config.model) else False
862
+ ),
863
+ initial_message_sequence=request.initial_message_sequence,
861
864
  )
862
865
  elif request.agent_type == AgentType.o1_agent:
863
866
  agent = O1Agent(
@@ -865,7 +868,9 @@ class SyncServer(Server):
865
868
  agent_state=agent_state,
866
869
  tools=tool_objs,
867
870
  # gpt-3.5-turbo tends to omit inner monologue, relax this requirement for now
868
- first_message_verify_mono=True if (llm_config.model is not None and "gpt-4" in llm_config.model) else False,
871
+ first_message_verify_mono=(
872
+ True if (llm_config and llm_config.model is not None and "gpt-4" in llm_config.model) else False
873
+ ),
869
874
  )
870
875
  # rebuilding agent memory on agent create in case shared memory blocks
871
876
  # were specified in the new agent's memory config. we're doing this for two reasons:
@@ -1084,7 +1089,7 @@ class SyncServer(Server):
1084
1089
  id: Optional[str] = None,
1085
1090
  ) -> Optional[List[Block]]:
1086
1091
 
1087
- return self.ms.get_blocks(user_id=user_id, label=label, template=template, name=name, id=id)
1092
+ return self.ms.get_blocks(user_id=user_id, label=label, template=template, template_name=name, id=id)
1088
1093
 
1089
1094
  def get_block(self, block_id: str):
1090
1095
 
@@ -1096,14 +1101,18 @@ class SyncServer(Server):
1096
1101
  return blocks[0]
1097
1102
 
1098
1103
  def create_block(self, request: CreateBlock, user_id: str, update: bool = False) -> Block:
1099
- existing_blocks = self.ms.get_blocks(name=request.name, user_id=user_id, template=request.template, label=request.label)
1100
- if existing_blocks is not None:
1104
+ existing_blocks = self.ms.get_blocks(
1105
+ template_name=request.template_name, user_id=user_id, template=request.template, label=request.label
1106
+ )
1107
+
1108
+ # for templates, update existing block template if exists
1109
+ if existing_blocks is not None and request.template:
1101
1110
  existing_block = existing_blocks[0]
1102
1111
  assert len(existing_blocks) == 1
1103
1112
  if update:
1104
1113
  return self.update_block(UpdateBlock(id=existing_block.id, **vars(request)))
1105
1114
  else:
1106
- raise ValueError(f"Block with name {request.name} already exists")
1115
+ raise ValueError(f"Block with name {request.template_name} already exists")
1107
1116
  block = Block(**vars(request))
1108
1117
  self.ms.create_block(block)
1109
1118
  return block
@@ -1112,7 +1121,7 @@ class SyncServer(Server):
1112
1121
  block = self.get_block(request.id)
1113
1122
  block.limit = request.limit if request.limit is not None else block.limit
1114
1123
  block.value = request.value if request.value is not None else block.value
1115
- block.name = request.name if request.name is not None else block.name
1124
+ block.template_name = request.template_name if request.template_name is not None else block.template_name
1116
1125
  self.ms.update_block(block=block)
1117
1126
  return self.ms.get_block(block_id=request.id)
1118
1127
 
@@ -1773,12 +1782,12 @@ class SyncServer(Server):
1773
1782
  for persona_file in list_persona_files():
1774
1783
  text = open(persona_file, "r", encoding="utf-8").read()
1775
1784
  name = os.path.basename(persona_file).replace(".txt", "")
1776
- self.create_block(CreatePersona(user_id=user_id, name=name, value=text, template=True), user_id=user_id, update=True)
1785
+ self.create_block(CreatePersona(user_id=user_id, template_name=name, value=text, template=True), user_id=user_id, update=True)
1777
1786
 
1778
1787
  for human_file in list_human_files():
1779
1788
  text = open(human_file, "r", encoding="utf-8").read()
1780
1789
  name = os.path.basename(human_file).replace(".txt", "")
1781
- self.create_block(CreateHuman(user_id=user_id, name=name, value=text, template=True), user_id=user_id, update=True)
1790
+ self.create_block(CreateHuman(user_id=user_id, template_name=name, value=text, template=True), user_id=user_id, update=True)
1782
1791
 
1783
1792
  def get_agent_message(self, agent_id: str, message_id: str) -> Optional[Message]:
1784
1793
  """Get a single message from the agent's memory"""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-nightly
3
- Version: 0.5.1.dev20241104104148
3
+ Version: 0.5.1.dev20241105104128
4
4
  Summary: Create LLM agents with long-term memory and custom tools
5
5
  License: Apache License
6
6
  Author: Letta Team
@@ -52,7 +52,7 @@ Requires-Dist: pg8000 (>=1.30.3,<2.0.0) ; extra == "postgres"
52
52
  Requires-Dist: pgvector (>=0.2.3,<0.3.0) ; extra == "postgres"
53
53
  Requires-Dist: pre-commit (>=3.5.0,<4.0.0) ; extra == "dev"
54
54
  Requires-Dist: prettytable (>=3.9.0,<4.0.0)
55
- Requires-Dist: psycopg2 (>=2.9.10,<3.0.0)
55
+ Requires-Dist: psycopg2 (>=2.9.10,<3.0.0) ; extra == "postgres"
56
56
  Requires-Dist: psycopg2-binary (>=2.9.10,<3.0.0) ; extra == "postgres"
57
57
  Requires-Dist: pyautogen (==0.2.22) ; extra == "autogen"
58
58
  Requires-Dist: pydantic (>=2.7.4,<3.0.0)
@@ -1,6 +1,6 @@
1
1
  letta/__init__.py,sha256=zlZXKr0iQIsC7FTkN9fjTvAgQGPXyr0nnkMJ7_OE1D0,1014
2
2
  letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
- letta/agent.py,sha256=uMU0_5XEitgYyfN5R53EzBnjajSCxzM5OTuEtOTmHFg,74850
3
+ letta/agent.py,sha256=sXRJHwgrsbOQrZwdrej0i8qAFj64xFu7fPXkpz4zj5c,76191
4
4
  letta/agent_store/chroma.py,sha256=upR5zGnGs6I6btulEYbiZdGG87BgKjxUJOQZ4Y-RQ_M,12492
5
5
  letta/agent_store/db.py,sha256=mTY3YWUs5n17479GMeTmxN8DAyzbBweVcypyNsK_slg,23435
6
6
  letta/agent_store/lancedb.py,sha256=i63d4VZwj9UIOTNs5f0JZ_r5yZD-jKWz4FAH4RMpXOE,5104
@@ -9,11 +9,11 @@ letta/agent_store/qdrant.py,sha256=6_33V-FEDpT9LG5zmr6-3y9slw1YFLswxpahiyMkvHA,7
9
9
  letta/agent_store/storage.py,sha256=4gKvMRYBGm9cwyaDOzljxDKgqr4MxGXcC4yGhAdKcAA,6693
10
10
  letta/benchmark/benchmark.py,sha256=ebvnwfp3yezaXOQyGXkYCDYpsmre-b9hvNtnyx4xkG0,3701
11
11
  letta/benchmark/constants.py,sha256=aXc5gdpMGJT327VuxsT5FngbCK2J41PQYeICBO7g_RE,536
12
- letta/cli/cli.py,sha256=i6wFBaX8-WwZ6nl_T0FFptfozlnYEBM7RcShwBl-qfw,16106
13
- letta/cli/cli_config.py,sha256=ynsezKawQ0l95rymlv-AJ3QjbqiyaXZyuzsDfBYwMwg,8537
12
+ letta/cli/cli.py,sha256=JP5-nw38f2BjCOU4pU4LsDBQmTJNNhJcJuviw-9a2Yw,16142
13
+ letta/cli/cli_config.py,sha256=D-CpSZI1cDvdSQr3-zhGuDrJnZo1Ko7bi_wuxcBxxqo,8555
14
14
  letta/cli/cli_load.py,sha256=x4L8s15GwIW13xrhKYFWHo_y-IVGtoPDHWWKcHDRP10,4587
15
15
  letta/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- letta/client/client.py,sha256=H3BfhxEAW852mFsC6i5HXm50IkfU2I1dlS7KL1DNZvw,95824
16
+ letta/client/client.py,sha256=LQA4wm8BW5yoTrIcrgEN6dFKmNYnlao5MD6XgUoRmsQ,96632
17
17
  letta/client/streaming.py,sha256=Hh5pjlyrdCuO2V75ZCxSSOCPd3BmHdKFGaIUJC6fBp0,4775
18
18
  letta/client/utils.py,sha256=OJlAKWrldc4I6M1WpcTWNtPJ4wfxlzlZqWLfCozkFtI,2872
19
19
  letta/config.py,sha256=eK-ip06ELHNYriInkgfidDvJxQ2tD1u49I-VLXB87nE,18929
@@ -28,7 +28,7 @@ letta/functions/function_sets/base.py,sha256=N4QmOjL6gDEyOg67ocF6zVKM-NquTo-yXG_
28
28
  letta/functions/function_sets/extras.py,sha256=Jik3UiDqYTm4Lam1XPTvuVjvgUHwIAhopsnbmVhGMBg,4732
29
29
  letta/functions/functions.py,sha256=n-g5qx_-5BBMEYpa-lrzeHIUbuXImPO4ABrFZTPZKMQ,4102
30
30
  letta/functions/helpers.py,sha256=JU3e5xkkTVx4EevBmtyCRnidf0ncAeASvH2ZT_aBvPc,9905
31
- letta/functions/schema_generator.py,sha256=M6cLDnoJOARd5OkAQUeGxjpLUQtMu4WEztcIkZFFh-I,7098
31
+ letta/functions/schema_generator.py,sha256=CoDZQfXsOKBp5VOv-024efcR833wyrchQbQIN7mL11A,8407
32
32
  letta/helpers/__init__.py,sha256=p0luQ1Oe3Skc6sH4O58aHHA3Qbkyjifpuq0DZ1GAY0U,59
33
33
  letta/helpers/tool_rule_solver.py,sha256=rcl8LBLbK5Hpxgf0gNMSV2uqWuVTuJNFJdCDsR5Eyd8,4758
34
34
  letta/humans/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -85,7 +85,7 @@ letta/local_llm/webui/settings.py,sha256=gmLHfiOl1u4JmlAZU2d2O8YKF9lafdakyjwR_ft
85
85
  letta/log.py,sha256=QHquDnL7oUAvdKlAwUlCK9zXKDMUjrU9WA0bxnMsP0Y,2101
86
86
  letta/main.py,sha256=yHgM1lltQZvbE8k0QDQMmVyJiWEj07ZTOYIBHDxE_DQ,18709
87
87
  letta/memory.py,sha256=6q1x3-PY-PeXzAt6hvP-UF1ajvroPZ7XW-5nLy-JhMo,17657
88
- letta/metadata.py,sha256=B4B8f5WSqmUwIGU5MN6fyBdHwryxrr9y0cZWr_eq9pE,29168
88
+ letta/metadata.py,sha256=f3cF7j6O6n8eUFI72xjPnqWrdchld3VPpjTPC9HzUV0,29346
89
89
  letta/o1_agent.py,sha256=LqATgTpkc02-nCH_F87EOvgxLjdjT9F07kdzj3zSdQg,3118
90
90
  letta/openai_backcompat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
91
91
  letta/openai_backcompat/openai_object.py,sha256=Y1ZS1sATP60qxJiOsjOP3NbwSzuzvkNAvb3DeuhM5Uk,13490
@@ -124,15 +124,15 @@ letta/prompts/system/memgpt_modified_chat.txt,sha256=HOaPVurEftD8KsuwsclDgE2afIf
124
124
  letta/prompts/system/memgpt_modified_o1.txt,sha256=AxxYVjYLZwpZ6yfifh1SuPtwlJGWTcTVzw53QbkN-Ao,5492
125
125
  letta/providers.py,sha256=nWvTvVsZH1EE2aHAwihJvCIDJpgfeWAOUE_YK1x5Zj4,19635
126
126
  letta/pytest.ini,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
- letta/schemas/agent.py,sha256=erAIm5wSzb7h9eWiYPMyuiH9J45dQXa5bz2kxde82j0,7210
127
+ letta/schemas/agent.py,sha256=UszwPl2pZKmply1IHc6n1Vas2TwuoTLNTjuNNqXzs2g,7569
128
128
  letta/schemas/api_key.py,sha256=u07yzzMn-hBAHZIIKbWY16KsgiFjSNR8lAghpMUo3_4,682
129
- letta/schemas/block.py,sha256=2_GftSIoTie6qHqUcZTfvN5bZT-p3goGcVx3TAuNOPQ,3936
129
+ letta/schemas/block.py,sha256=cbHN_xVbTFlkx5_04kiRnMdj0eN0YtsDeUsFeKuOKTg,3945
130
130
  letta/schemas/embedding_config.py,sha256=1kD6NpiXeH4roVumxqDAKk7xt8SpXGWNhZs_XXUSlEU,2855
131
131
  letta/schemas/enums.py,sha256=WfRYpLh_pD-VyhEnp3Y6pPfx063zq2o4jky6PulqO8w,629
132
132
  letta/schemas/file.py,sha256=Ns0V2Jp6_lmiCmLCnXsOAh_UNqTW-fstcLKjYI35VOA,1357
133
133
  letta/schemas/health.py,sha256=zT6mYovvD17iJRuu2rcaQQzbEEYrkwvAE9TB7iU824c,139
134
134
  letta/schemas/job.py,sha256=605TWjUdNy5Rgoc7en_DX3Giz-I7sVTXiSRZqxL__d8,1543
135
- letta/schemas/letta_base.py,sha256=4QXFgyjCHqIagi8B6_4nmqb9eoJ52Y6aCxBxQpGX48M,2832
135
+ letta/schemas/letta_base.py,sha256=VP6h6mV7u2AVSrcgzTIoptORv6BqmQXkkMzb7-NlcWs,3026
136
136
  letta/schemas/letta_message.py,sha256=RuVVtwFbi85yP3dXQxowofQ6cI2cO_CdGtgpHGQzgHc,6563
137
137
  letta/schemas/letta_request.py,sha256=_oiDshc_AoFWIfXRk2VX5-AxO5vDlyN-9r-gnyLj_30,1890
138
138
  letta/schemas/letta_response.py,sha256=ClI0LKyhMVI0N5CSnTAbcHaw7simkyrUTKYX0fr0qzw,1610
@@ -147,7 +147,7 @@ letta/schemas/openai/openai.py,sha256=Hilo5BiLAGabzxCwnwfzK5QrWqwYD8epaEKFa4Pwnd
147
147
  letta/schemas/organization.py,sha256=DxWkApcw6ES1k_TKtF8Vw3pcPDHx1sWwPSmvhgw2fJo,679
148
148
  letta/schemas/passage.py,sha256=eYQMxD_XjHAi72jmqcGBU4wM4VZtSU0XK8uhQxxN3Ug,3563
149
149
  letta/schemas/source.py,sha256=hB4Ai6Nj8dFdbxv5_Qaf4uN_cmdGmnzgc-4QnHXcV3o,2562
150
- letta/schemas/tool.py,sha256=UW2S4N00PJpN8TzDZMsQah5pOTkJ4hcfDUqH2CRhTZ0,9333
150
+ letta/schemas/tool.py,sha256=f_sluD9aZ-9tgJWLFllvJ1Jeo5sFvHOv1qzU714YHEk,9395
151
151
  letta/schemas/tool_rule.py,sha256=dHEwVOZ40lMEVCrry7wlZM0IJo5SJrZqXKYpXe40bjY,778
152
152
  letta/schemas/usage.py,sha256=lvn1ooHwLEdv6gwQpw5PBUbcwn_gwdT6HA-fCiix6sY,817
153
153
  letta/schemas/user.py,sha256=nRuH0yVVmAFDq6ubqaKkupUmohMpikla0bQUaKVGooo,1543
@@ -179,7 +179,7 @@ letta/server/rest_api/routers/v1/tools.py,sha256=AYJP1dGqFLVZkWaypPi7TP7r53Vfz2E
179
179
  letta/server/rest_api/routers/v1/users.py,sha256=bxQ-YdevjDqmgNDfbSPAG_4KEVvPNBHD_-Lp1MdeMec,3374
180
180
  letta/server/rest_api/static_files.py,sha256=NG8sN4Z5EJ8JVQdj19tkFa9iQ1kBPTab9f_CUxd_u4Q,3143
181
181
  letta/server/rest_api/utils.py,sha256=GdHYCzXtbM5VCAYDPR0z5gnNZpRhwPld2BGZV7xT6cU,2924
182
- letta/server/server.py,sha256=LxRrLkjosuxpAcL7mhKVY4dpZwH1QpNgWIo1tAJDkrs,78511
182
+ letta/server/server.py,sha256=Mr6ERBRzG__LVZTNPab3HZlZs2oKJPmBnreKLJKgOsY,78916
183
183
  letta/server/startup.sh,sha256=Atyf4hL5XLpC4aA3esEyOeNWfemlgXlgpeKB59fDFvw,334
184
184
  letta/server/static_files/assets/index-3ab03d5b.css,sha256=OrA9W4iKJ5h2Wlr7GwdAT4wow0CM8hVit1yOxEL49Qw,54295
185
185
  letta/server/static_files/assets/index-9fa459a2.js,sha256=j2oMcDJO9dWJaH5e-tsflbVpWK20gLWpZKJk4-Kuy6A,1815592
@@ -201,8 +201,8 @@ letta/streaming_interface.py,sha256=_FPUWy58j50evHcpXyd7zB1wWqeCc71NCFeWh_TBvnw,
201
201
  letta/streaming_utils.py,sha256=329fsvj1ZN0r0LpQtmMPZ2vSxkDBIUUwvGHZFkjm2I8,11745
202
202
  letta/system.py,sha256=buKYPqG5n2x41hVmWpu6JUpyd7vTWED9Km2_M7dLrvk,6960
203
203
  letta/utils.py,sha256=SXLEYhyp3gHyIjrxNIKNZZ5ittKo3KOj6zxgC_Trex0,31012
204
- letta_nightly-0.5.1.dev20241104104148.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
205
- letta_nightly-0.5.1.dev20241104104148.dist-info/METADATA,sha256=VksBbdW6vMWGlxrthoZ1SO72H0_jEj9alL1jkCVYhf8,10773
206
- letta_nightly-0.5.1.dev20241104104148.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
207
- letta_nightly-0.5.1.dev20241104104148.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
208
- letta_nightly-0.5.1.dev20241104104148.dist-info/RECORD,,
204
+ letta_nightly-0.5.1.dev20241105104128.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
205
+ letta_nightly-0.5.1.dev20241105104128.dist-info/METADATA,sha256=uExxjv_fkEhqkbYDYrYJqcEOCCaI6MuFTzhr1x0J0z0,10795
206
+ letta_nightly-0.5.1.dev20241105104128.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
207
+ letta_nightly-0.5.1.dev20241105104128.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
208
+ letta_nightly-0.5.1.dev20241105104128.dist-info/RECORD,,