letta-nightly 0.4.1.dev20241013104006__py3-none-any.whl → 0.4.1.dev20241014104152__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
@@ -39,6 +39,7 @@ from letta.system import (
39
39
  get_login_event,
40
40
  package_function_response,
41
41
  package_summarize_message,
42
+ package_user_message,
42
43
  )
43
44
  from letta.utils import (
44
45
  count_tokens,
@@ -200,16 +201,7 @@ class BaseAgent(ABC):
200
201
  @abstractmethod
201
202
  def step(
202
203
  self,
203
- messages: Union[Message, List[Message], str], # TODO deprecate str inputs
204
- first_message: bool = False,
205
- first_message_retry_limit: int = FIRST_MESSAGE_ATTEMPTS,
206
- skip_verify: bool = False,
207
- return_dicts: bool = True, # if True, return dicts, if False, return Message objects
208
- recreate_message_timestamp: bool = True, # if True, when input is a Message type, recreated the 'created_at' field
209
- stream: bool = False, # TODO move to config?
210
- timestamp: Optional[datetime.datetime] = None,
211
- inner_thoughts_in_kwargs_option: OptionState = OptionState.DEFAULT,
212
- ms: Optional[MetadataStore] = None,
204
+ messages: Union[Message, List[Message]],
213
205
  ) -> AgentStepResponse:
214
206
  """
215
207
  Top-level event message handler for the agent.
@@ -730,14 +722,13 @@ class Agent(BaseAgent):
730
722
 
731
723
  def step(
732
724
  self,
733
- user_message: Union[Message, None, str], # NOTE: should be json.dump(dict)
725
+ messages: Union[Message, List[Message]],
734
726
  first_message: bool = False,
735
727
  first_message_retry_limit: int = FIRST_MESSAGE_ATTEMPTS,
736
728
  skip_verify: bool = False,
737
729
  return_dicts: bool = True,
738
- recreate_message_timestamp: bool = True, # if True, when input is a Message type, recreated the 'created_at' field
730
+ # recreate_message_timestamp: bool = True, # if True, when input is a Message type, recreated the 'created_at' field
739
731
  stream: bool = False, # TODO move to config?
740
- timestamp: Optional[datetime.datetime] = None,
741
732
  inner_thoughts_in_kwargs_option: OptionState = OptionState.DEFAULT,
742
733
  ms: Optional[MetadataStore] = None,
743
734
  ) -> AgentStepResponse:
@@ -760,50 +751,13 @@ class Agent(BaseAgent):
760
751
  self.rebuild_memory(force=True, ms=ms)
761
752
 
762
753
  # Step 1: add user message
763
- if user_message is not None:
764
- if isinstance(user_message, Message):
765
- assert user_message.text is not None
766
-
767
- # Validate JSON via save/load
768
- user_message_text = validate_json(user_message.text)
769
- cleaned_user_message_text, name = strip_name_field_from_user_message(user_message_text)
770
-
771
- if name is not None:
772
- # Update Message object
773
- user_message.text = cleaned_user_message_text
774
- user_message.name = name
754
+ if isinstance(messages, Message):
755
+ messages = [messages]
775
756
 
776
- # Recreate timestamp
777
- if recreate_message_timestamp:
778
- user_message.created_at = get_utc_time()
757
+ if not all(isinstance(m, Message) for m in messages):
758
+ raise ValueError(f"messages should be a Message or a list of Message, got {type(messages)}")
779
759
 
780
- elif isinstance(user_message, str):
781
- # Validate JSON via save/load
782
- user_message = validate_json(user_message)
783
- cleaned_user_message_text, name = strip_name_field_from_user_message(user_message)
784
-
785
- # If user_message['name'] is not None, it will be handled properly by dict_to_message
786
- # So no need to run strip_name_field_from_user_message
787
-
788
- # Create the associated Message object (in the database)
789
- user_message = Message.dict_to_message(
790
- agent_id=self.agent_state.id,
791
- user_id=self.agent_state.user_id,
792
- model=self.model,
793
- openai_message_dict={"role": "user", "content": cleaned_user_message_text, "name": name},
794
- created_at=timestamp,
795
- )
796
-
797
- else:
798
- raise ValueError(f"Bad type for user_message: {type(user_message)}")
799
-
800
- self.interface.user_message(user_message.text, msg_obj=user_message)
801
-
802
- input_message_sequence = self._messages + [user_message]
803
-
804
- # Alternatively, the requestor can send an empty user message
805
- else:
806
- input_message_sequence = self._messages
760
+ input_message_sequence = self._messages + messages
807
761
 
808
762
  if len(input_message_sequence) > 1 and input_message_sequence[-1].role != "user":
809
763
  printd(f"{CLI_WARNING_PREFIX}Attempting to run ChatCompletion without user as the last message in the queue")
@@ -846,11 +800,8 @@ class Agent(BaseAgent):
846
800
  )
847
801
 
848
802
  # Step 6: extend the message history
849
- if user_message is not None:
850
- if isinstance(user_message, Message):
851
- all_new_messages = [user_message] + all_response_messages
852
- else:
853
- raise ValueError(type(user_message))
803
+ if len(messages) > 0:
804
+ all_new_messages = messages + all_response_messages
854
805
  else:
855
806
  all_new_messages = all_response_messages
856
807
 
@@ -897,7 +848,7 @@ class Agent(BaseAgent):
897
848
  )
898
849
 
899
850
  except Exception as e:
900
- printd(f"step() failed\nuser_message = {user_message}\nerror = {e}")
851
+ printd(f"step() failed\nmessages = {messages}\nerror = {e}")
901
852
 
902
853
  # If we got a context alert, try trimming the messages length, then try again
903
854
  if is_context_overflow_error(e):
@@ -906,14 +857,14 @@ class Agent(BaseAgent):
906
857
 
907
858
  # Try step again
908
859
  return self.step(
909
- user_message,
860
+ messages=messages,
910
861
  first_message=first_message,
911
862
  first_message_retry_limit=first_message_retry_limit,
912
863
  skip_verify=skip_verify,
913
864
  return_dicts=return_dicts,
914
- recreate_message_timestamp=recreate_message_timestamp,
865
+ # recreate_message_timestamp=recreate_message_timestamp,
915
866
  stream=stream,
916
- timestamp=timestamp,
867
+ # timestamp=timestamp,
917
868
  inner_thoughts_in_kwargs_option=inner_thoughts_in_kwargs_option,
918
869
  ms=ms,
919
870
  )
@@ -922,6 +873,40 @@ class Agent(BaseAgent):
922
873
  printd(f"step() failed with an unrecognized exception: '{str(e)}'")
923
874
  raise e
924
875
 
876
+ def step_user_message(self, user_message_str: str, **kwargs) -> AgentStepResponse:
877
+ """Takes a basic user message string, turns it into a stringified JSON with extra metadata, then sends it to the agent
878
+
879
+ Example:
880
+ -> user_message_str = 'hi'
881
+ -> {'message': 'hi', 'type': 'user_message', ...}
882
+ -> json.dumps(...)
883
+ -> agent.step(messages=[Message(role='user', text=...)])
884
+ """
885
+ # Wrap with metadata, dumps to JSON
886
+ assert user_message_str and isinstance(
887
+ user_message_str, str
888
+ ), f"user_message_str should be a non-empty string, got {type(user_message_str)}"
889
+ user_message_json_str = package_user_message(user_message_str)
890
+
891
+ # Validate JSON via save/load
892
+ user_message = validate_json(user_message_json_str)
893
+ cleaned_user_message_text, name = strip_name_field_from_user_message(user_message)
894
+
895
+ # Turn into a dict
896
+ openai_message_dict = {"role": "user", "content": cleaned_user_message_text, "name": name}
897
+
898
+ # Create the associated Message object (in the database)
899
+ assert self.agent_state.user_id is not None, "User ID is not set"
900
+ user_message = Message.dict_to_message(
901
+ agent_id=self.agent_state.id,
902
+ user_id=self.agent_state.user_id,
903
+ model=self.model,
904
+ openai_message_dict=openai_message_dict,
905
+ # created_at=timestamp,
906
+ )
907
+
908
+ return self.step(messages=[user_message], **kwargs)
909
+
925
910
  def summarize_messages_inplace(self, cutoff=None, preserve_last_N_messages=True, disallow_tool_as_first=True):
926
911
  assert self.messages[0]["role"] == "system", f"self.messages[0] should be system (instead got {self.messages[0]})"
927
912
 
@@ -1340,7 +1325,8 @@ class Agent(BaseAgent):
1340
1325
 
1341
1326
  self.pop_until_user()
1342
1327
  user_message = self.pop_message(count=1)[0]
1343
- step_response = self.step(user_message=user_message.text, return_dicts=False)
1328
+ assert user_message.text is not None, "User message text is None"
1329
+ step_response = self.step_user_message(user_message_str=user_message.text, return_dicts=False)
1344
1330
  messages = step_response.messages
1345
1331
 
1346
1332
  assert messages is not None
letta/main.py CHANGED
@@ -356,19 +356,29 @@ def run_agent_loop(
356
356
  else:
357
357
  # If message did not begin with command prefix, pass inputs to Letta
358
358
  # Handle user message and append to messages
359
- user_message = system.package_user_message(user_input)
359
+ user_message = str(user_input)
360
360
 
361
361
  skip_next_user_input = False
362
362
 
363
363
  def process_agent_step(user_message, no_verify):
364
- step_response = letta_agent.step(
365
- user_message,
366
- first_message=False,
367
- skip_verify=no_verify,
368
- stream=stream,
369
- inner_thoughts_in_kwargs_option=inner_thoughts_in_kwargs,
370
- ms=ms,
371
- )
364
+ if user_message is None:
365
+ step_response = letta_agent.step(
366
+ messages=[],
367
+ first_message=False,
368
+ skip_verify=no_verify,
369
+ stream=stream,
370
+ inner_thoughts_in_kwargs_option=inner_thoughts_in_kwargs,
371
+ ms=ms,
372
+ )
373
+ else:
374
+ step_response = letta_agent.step_user_message(
375
+ user_message_str=user_message,
376
+ first_message=False,
377
+ skip_verify=no_verify,
378
+ stream=stream,
379
+ inner_thoughts_in_kwargs_option=inner_thoughts_in_kwargs,
380
+ ms=ms,
381
+ )
372
382
  new_messages = step_response.messages
373
383
  heartbeat_request = step_response.heartbeat_request
374
384
  function_failed = step_response.function_failed
letta/server/server.py CHANGED
@@ -383,9 +383,22 @@ class SyncServer(Server):
383
383
  letta_agent = self._load_agent(user_id=user_id, agent_id=agent_id)
384
384
  return letta_agent
385
385
 
386
- def _step(self, user_id: str, agent_id: str, input_message: Union[str, Message], timestamp: Optional[datetime]) -> LettaUsageStatistics:
386
+ def _step(
387
+ self,
388
+ user_id: str,
389
+ agent_id: str,
390
+ input_messages: Union[Message, List[Message]],
391
+ # timestamp: Optional[datetime],
392
+ ) -> LettaUsageStatistics:
387
393
  """Send the input message through the agent"""
388
- logger.debug(f"Got input message: {input_message}")
394
+
395
+ # Input validation
396
+ if isinstance(input_messages, Message):
397
+ input_messages = [input_messages]
398
+ if not all(isinstance(m, Message) for m in input_messages):
399
+ raise ValueError(f"messages should be a Message or a list of Message, got {type(input_messages)}")
400
+
401
+ logger.debug(f"Got input messages: {input_messages}")
389
402
  try:
390
403
 
391
404
  # Get the agent object (loaded in memory)
@@ -398,18 +411,18 @@ class SyncServer(Server):
398
411
 
399
412
  logger.debug(f"Starting agent step")
400
413
  no_verify = True
401
- next_input_message = input_message
414
+ next_input_message = input_messages
402
415
  counter = 0
403
416
  total_usage = UsageStatistics()
404
417
  step_count = 0
405
418
  while True:
406
419
  step_response = letta_agent.step(
407
- next_input_message,
420
+ messages=next_input_message,
408
421
  first_message=False,
409
422
  skip_verify=no_verify,
410
423
  return_dicts=False,
411
424
  stream=token_streaming,
412
- timestamp=timestamp,
425
+ # timestamp=timestamp,
413
426
  ms=self.ms,
414
427
  )
415
428
  step_response.messages
@@ -436,13 +449,40 @@ class SyncServer(Server):
436
449
  break
437
450
  # Chain handlers
438
451
  elif token_warning:
439
- next_input_message = system.get_token_limit_warning()
452
+ assert letta_agent.agent_state.user_id is not None
453
+ next_input_message = Message.dict_to_message(
454
+ agent_id=letta_agent.agent_state.id,
455
+ user_id=letta_agent.agent_state.user_id,
456
+ model=letta_agent.model,
457
+ openai_message_dict={
458
+ "role": "user", # TODO: change to system?
459
+ "content": system.get_token_limit_warning(),
460
+ },
461
+ )
440
462
  continue # always chain
441
463
  elif function_failed:
442
- next_input_message = system.get_heartbeat(constants.FUNC_FAILED_HEARTBEAT_MESSAGE)
464
+ assert letta_agent.agent_state.user_id is not None
465
+ next_input_message = Message.dict_to_message(
466
+ agent_id=letta_agent.agent_state.id,
467
+ user_id=letta_agent.agent_state.user_id,
468
+ model=letta_agent.model,
469
+ openai_message_dict={
470
+ "role": "user", # TODO: change to system?
471
+ "content": system.get_heartbeat(constants.FUNC_FAILED_HEARTBEAT_MESSAGE),
472
+ },
473
+ )
443
474
  continue # always chain
444
475
  elif heartbeat_request:
445
- next_input_message = system.get_heartbeat(constants.REQ_HEARTBEAT_MESSAGE)
476
+ assert letta_agent.agent_state.user_id is not None
477
+ next_input_message = Message.dict_to_message(
478
+ agent_id=letta_agent.agent_state.id,
479
+ user_id=letta_agent.agent_state.user_id,
480
+ model=letta_agent.model,
481
+ openai_message_dict={
482
+ "role": "user", # TODO: change to system?
483
+ "content": system.get_heartbeat(constants.REQ_HEARTBEAT_MESSAGE),
484
+ },
485
+ )
446
486
  continue # always chain
447
487
  # Letta no-op / yield
448
488
  else:
@@ -621,7 +661,7 @@ class SyncServer(Server):
621
661
  )
622
662
 
623
663
  # Run the agent state forward
624
- usage = self._step(user_id=user_id, agent_id=agent_id, input_message=message, timestamp=timestamp)
664
+ usage = self._step(user_id=user_id, agent_id=agent_id, input_messages=message)
625
665
  return usage
626
666
 
627
667
  def system_message(
@@ -669,7 +709,7 @@ class SyncServer(Server):
669
709
 
670
710
  if isinstance(message, Message):
671
711
  # Can't have a null text field
672
- if len(message.text) == 0 or message.text is None:
712
+ if message.text is None or len(message.text) == 0:
673
713
  raise ValueError(f"Invalid input: '{message.text}'")
674
714
  # If the input begins with a command prefix, reject
675
715
  elif message.text.startswith("/"):
@@ -683,7 +723,7 @@ class SyncServer(Server):
683
723
  message.created_at = timestamp
684
724
 
685
725
  # Run the agent state forward
686
- return self._step(user_id=user_id, agent_id=agent_id, input_message=packaged_system_message, timestamp=timestamp)
726
+ return self._step(user_id=user_id, agent_id=agent_id, input_messages=message)
687
727
 
688
728
  # @LockingServer.agent_lock_decorator
689
729
  def run_command(self, user_id: str, agent_id: str, command: str) -> LettaUsageStatistics:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: letta-nightly
3
- Version: 0.4.1.dev20241013104006
3
+ Version: 0.4.1.dev20241014104152
4
4
  Summary: Create LLM agents with long-term memory and custom tools
5
5
  License: Apache License
6
6
  Author: Letta Team
@@ -1,6 +1,6 @@
1
1
  letta/__init__.py,sha256=btKRPdyhkpIyHlCPRLwwz-SCSVcEGNBeheYA-XNesiI,996
2
2
  letta/__main__.py,sha256=6Hs2PV7EYc5Tid4g4OtcLXhqVHiNYTGzSBdoOnW2HXA,29
3
- letta/agent.py,sha256=pdsd1M_AF3uF-AgJrMmxj8uB0Rkt5AXKBxHNw9mMaP4,66559
3
+ letta/agent.py,sha256=S30yzTZjsbe3mn9Zn9eUBquL-JehUTZIStRyp7LL04s,65548
4
4
  letta/agent_store/chroma.py,sha256=upR5zGnGs6I6btulEYbiZdGG87BgKjxUJOQZ4Y-RQ_M,12492
5
5
  letta/agent_store/db.py,sha256=TAQ1rcqS8zuC1hT_BaiXn6tSz5aKHAb-YQxr4QLKgz4,22724
6
6
  letta/agent_store/lancedb.py,sha256=8RWmqVjowm5g0cc6DNRcb6f1FHGEqFnccnuekhWY39U,5101
@@ -81,7 +81,7 @@ letta/local_llm/webui/legacy_api.py,sha256=k3H3y4qp2Fs-XmP24iSIEyvq6wjWFWBzklY3-
81
81
  letta/local_llm/webui/legacy_settings.py,sha256=BLmd3TSx5StnY3ibjwaxYATPt_Lvq-o1rlcc_-Q1JcU,538
82
82
  letta/local_llm/webui/settings.py,sha256=gmLHfiOl1u4JmlAZU2d2O8YKF9lafdakyjwR_ftVPh8,552
83
83
  letta/log.py,sha256=QHquDnL7oUAvdKlAwUlCK9zXKDMUjrU9WA0bxnMsP0Y,2101
84
- letta/main.py,sha256=7uuiFv8TeOd3K92WwfYkls7l7KXJ5P0l2zgK9AfmD08,18502
84
+ letta/main.py,sha256=4zO3UDos93HXPJQtz5tO77KAlSgx5xZIgEigpEtars4,18920
85
85
  letta/memory.py,sha256=6q1x3-PY-PeXzAt6hvP-UF1ajvroPZ7XW-5nLy-JhMo,17657
86
86
  letta/metadata.py,sha256=50pEJ5BuNKT8dPv34-H_Id9an3ql45Ze7BKqn7DGQ6s,33256
87
87
  letta/openai_backcompat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -167,7 +167,7 @@ letta/server/rest_api/routers/v1/tools.py,sha256=MEhxu-zMS2ff_wwcRpMuQyWA71w_3BJ
167
167
  letta/server/rest_api/routers/v1/users.py,sha256=Y2rDvHOG1B5FLSOjutY3R22vt48IngbZ-9h8CohG5rc,3378
168
168
  letta/server/rest_api/static_files.py,sha256=NG8sN4Z5EJ8JVQdj19tkFa9iQ1kBPTab9f_CUxd_u4Q,3143
169
169
  letta/server/rest_api/utils.py,sha256=Fc2ZGKzLaBa2sEtSTVjJ8D5M0xIwsWC0CVAOIJaD3rY,2176
170
- letta/server/server.py,sha256=txNgf3AIraK-kTV4PLwzROfgfBTIul_Y74hPygFtOHw,82687
170
+ letta/server/server.py,sha256=Fs9cJLSDuh1qbyB81QD9oOUmHqChWzOkysjZvr-ghxk,84438
171
171
  letta/server/startup.sh,sha256=jeGV7B_PS0hS-tT6o6GpACrUbV9WV1NI2L9aLoUDDtc,311
172
172
  letta/server/static_files/assets/index-3ab03d5b.css,sha256=OrA9W4iKJ5h2Wlr7GwdAT4wow0CM8hVit1yOxEL49Qw,54295
173
173
  letta/server/static_files/assets/index-9a9c449b.js,sha256=qoWUq6_kuLhE9NFkNeCBptgq-oERW46r0tB3JlWe_qc,1818951
@@ -184,8 +184,8 @@ letta/settings.py,sha256=6VWC3vtTa8vqj6dqos4p_xHTMJNJS_8LRGJmqvaU1-o,3219
184
184
  letta/streaming_interface.py,sha256=_FPUWy58j50evHcpXyd7zB1wWqeCc71NCFeWh_TBvnw,15736
185
185
  letta/system.py,sha256=buKYPqG5n2x41hVmWpu6JUpyd7vTWED9Km2_M7dLrvk,6960
186
186
  letta/utils.py,sha256=neUs7mxNfndzRL5XUxerr8Lic6w7qnyyvf8FBwMnyWw,30852
187
- letta_nightly-0.4.1.dev20241013104006.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
188
- letta_nightly-0.4.1.dev20241013104006.dist-info/METADATA,sha256=L5hOlFIaGT5BnE945CxaUdQEoKsOLrysrrL_bD9MjVU,6008
189
- letta_nightly-0.4.1.dev20241013104006.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
190
- letta_nightly-0.4.1.dev20241013104006.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
191
- letta_nightly-0.4.1.dev20241013104006.dist-info/RECORD,,
187
+ letta_nightly-0.4.1.dev20241014104152.dist-info/LICENSE,sha256=mExtuZ_GYJgDEI38GWdiEYZizZS4KkVt2SF1g_GPNhI,10759
188
+ letta_nightly-0.4.1.dev20241014104152.dist-info/METADATA,sha256=55FccQj8oSaEHu0ve8g1JQU96_WBhTW07R0IVg2XOcg,6008
189
+ letta_nightly-0.4.1.dev20241014104152.dist-info/WHEEL,sha256=FMvqSimYX_P7y0a7UY-_Mc83r5zkBZsCYPm7Lr0Bsq4,88
190
+ letta_nightly-0.4.1.dev20241014104152.dist-info/entry_points.txt,sha256=2zdiyGNEZGV5oYBuS-y2nAAgjDgcC9yM_mHJBFSRt5U,40
191
+ letta_nightly-0.4.1.dev20241014104152.dist-info/RECORD,,