letta-nightly 0.4.1.dev20241008104105__py3-none-any.whl → 0.4.1.dev20241010104112__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.

Files changed (43) hide show
  1. letta/agent.py +18 -2
  2. letta/agent_store/db.py +23 -7
  3. letta/cli/cli.py +2 -1
  4. letta/cli/cli_config.py +1 -1098
  5. letta/client/client.py +8 -1
  6. letta/client/utils.py +7 -2
  7. letta/credentials.py +2 -2
  8. letta/embeddings.py +3 -0
  9. letta/functions/schema_generator.py +1 -1
  10. letta/interface.py +6 -2
  11. letta/llm_api/anthropic.py +3 -24
  12. letta/llm_api/azure_openai.py +47 -98
  13. letta/llm_api/azure_openai_constants.py +10 -0
  14. letta/llm_api/google_ai.py +38 -63
  15. letta/llm_api/helpers.py +64 -2
  16. letta/llm_api/llm_api_tools.py +6 -15
  17. letta/llm_api/openai.py +6 -49
  18. letta/local_llm/constants.py +3 -0
  19. letta/main.py +1 -1
  20. letta/metadata.py +2 -0
  21. letta/providers.py +165 -31
  22. letta/schemas/agent.py +14 -0
  23. letta/schemas/llm_config.py +0 -3
  24. letta/schemas/openai/chat_completion_response.py +3 -0
  25. letta/schemas/tool.py +3 -3
  26. letta/server/rest_api/routers/openai/assistants/threads.py +5 -5
  27. letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +2 -2
  28. letta/server/rest_api/routers/v1/agents.py +11 -11
  29. letta/server/rest_api/routers/v1/blocks.py +2 -2
  30. letta/server/rest_api/routers/v1/jobs.py +2 -2
  31. letta/server/rest_api/routers/v1/sources.py +12 -12
  32. letta/server/rest_api/routers/v1/tools.py +6 -6
  33. letta/server/server.py +26 -7
  34. letta/settings.py +3 -112
  35. letta/streaming_interface.py +8 -4
  36. {letta_nightly-0.4.1.dev20241008104105.dist-info → letta_nightly-0.4.1.dev20241010104112.dist-info}/METADATA +1 -1
  37. {letta_nightly-0.4.1.dev20241008104105.dist-info → letta_nightly-0.4.1.dev20241010104112.dist-info}/RECORD +40 -42
  38. letta/configs/anthropic.json +0 -13
  39. letta/configs/letta_hosted.json +0 -11
  40. letta/configs/openai.json +0 -12
  41. {letta_nightly-0.4.1.dev20241008104105.dist-info → letta_nightly-0.4.1.dev20241010104112.dist-info}/LICENSE +0 -0
  42. {letta_nightly-0.4.1.dev20241008104105.dist-info → letta_nightly-0.4.1.dev20241010104112.dist-info}/WHEEL +0 -0
  43. {letta_nightly-0.4.1.dev20241008104105.dist-info → letta_nightly-0.4.1.dev20241010104112.dist-info}/entry_points.txt +0 -0
letta/agent.py CHANGED
@@ -239,6 +239,7 @@ class Agent(BaseAgent):
239
239
  assert isinstance(self.agent_state.memory, Memory), f"Memory object is not of type Memory: {type(self.agent_state.memory)}"
240
240
 
241
241
  # link tools
242
+ self.tools = tools
242
243
  self.link_tools(tools)
243
244
 
244
245
  # gpt-4, gpt-3.5-turbo, ...
@@ -338,6 +339,9 @@ class Agent(BaseAgent):
338
339
  for tool_name in self.agent_state.tools:
339
340
  assert tool_name in [tool.name for tool in tools], f"Tool name {tool_name} not included in agent tool list"
340
341
 
342
+ # Update tools
343
+ self.tools = tools
344
+
341
345
  # Store the functions schemas (this is passed as an argument to ChatCompletion)
342
346
  self.functions = []
343
347
  self.functions_python = {}
@@ -482,7 +486,7 @@ class Agent(BaseAgent):
482
486
  inner_thoughts_in_kwargs_option=inner_thoughts_in_kwargs_option,
483
487
  )
484
488
 
485
- if len(response.choices) == 0:
489
+ if len(response.choices) == 0 or response.choices[0] is None:
486
490
  raise Exception(f"API call didn't return a message: {response}")
487
491
 
488
492
  # special case for 'length'
@@ -552,9 +556,12 @@ class Agent(BaseAgent):
552
556
  ) # extend conversation with assistant's reply
553
557
  printd(f"Function call message: {messages[-1]}")
554
558
 
559
+ nonnull_content = False
555
560
  if response_message.content:
556
561
  # The content if then internal monologue, not chat
557
562
  self.interface.internal_monologue(response_message.content, msg_obj=messages[-1])
563
+ # Flag to avoid printing a duplicate if inner thoughts get popped from the function call
564
+ nonnull_content = True
558
565
 
559
566
  # Step 3: call the function
560
567
  # Note: the JSON response may not always be valid; be sure to handle errors
@@ -615,12 +622,17 @@ class Agent(BaseAgent):
615
622
  if "inner_thoughts" in function_args:
616
623
  response_message.content = function_args.pop("inner_thoughts")
617
624
  # The content if then internal monologue, not chat
618
- if response_message.content:
625
+ if response_message.content and not nonnull_content:
619
626
  self.interface.internal_monologue(response_message.content, msg_obj=messages[-1])
620
627
 
621
628
  # (Still parsing function args)
622
629
  # Handle requests for immediate heartbeat
623
630
  heartbeat_request = function_args.pop("request_heartbeat", None)
631
+
632
+ # Edge case: heartbeat_request is returned as a stringified boolean, we will attempt to parse:
633
+ if isinstance(heartbeat_request, str) and heartbeat_request.lower().strip() == "true":
634
+ heartbeat_request = True
635
+
624
636
  if not isinstance(heartbeat_request, bool) or heartbeat_request is None:
625
637
  printd(
626
638
  f"{CLI_WARNING_PREFIX}'request_heartbeat' arg parsed was not a bool or None, type={type(heartbeat_request)}, value={heartbeat_request}"
@@ -1353,6 +1365,10 @@ def save_agent(agent: Agent, ms: MetadataStore):
1353
1365
  else:
1354
1366
  ms.create_agent(agent_state)
1355
1367
 
1368
+ for tool in agent.tools:
1369
+ if ms.get_tool(tool_name=tool.name, user_id=tool.user_id) is None:
1370
+ ms.create_tool(tool)
1371
+
1356
1372
  agent.agent_state = ms.get_agent(agent_id=agent_id)
1357
1373
  assert isinstance(agent.agent_state.memory, Memory), f"Memory is not a Memory object: {type(agent_state.memory)}"
1358
1374
 
letta/agent_store/db.py CHANGED
@@ -398,8 +398,6 @@ class PostgresStorageConnector(SQLStorageConnector):
398
398
  return records
399
399
 
400
400
  def insert_many(self, records, exists_ok=True, show_progress=False):
401
- pass
402
-
403
401
  # TODO: this is terrible, should eventually be done the same way for all types (migrate to SQLModel)
404
402
  if len(records) == 0:
405
403
  return
@@ -506,18 +504,36 @@ class SQLLiteStorageConnector(SQLStorageConnector):
506
504
  # sqlite3.register_converter("UUID", lambda b: uuid.UUID(bytes_le=b))
507
505
 
508
506
  def insert_many(self, records, exists_ok=True, show_progress=False):
509
- pass
510
-
511
507
  # TODO: this is terrible, should eventually be done the same way for all types (migrate to SQLModel)
512
508
  if len(records) == 0:
513
509
  return
510
+
511
+ added_ids = [] # avoid adding duplicates
512
+ # NOTE: this has not great performance due to the excessive commits
514
513
  with self.session_maker() as session:
515
514
  iterable = tqdm(records) if show_progress else records
516
515
  for record in iterable:
517
516
  # db_record = self.db_model(**vars(record))
518
- db_record = self.db_model(**record.dict())
519
- session.add(db_record)
520
- session.commit()
517
+
518
+ if record.id in added_ids:
519
+ continue
520
+
521
+ existing_record = session.query(self.db_model).filter_by(id=record.id).first()
522
+ if existing_record:
523
+ if exists_ok:
524
+ fields = record.model_dump()
525
+ fields.pop("id")
526
+ session.query(self.db_model).filter(self.db_model.id == record.id).update(fields)
527
+ session.commit()
528
+ else:
529
+ raise ValueError(f"Record with id {record.id} already exists.")
530
+
531
+ else:
532
+ db_record = self.db_model(**record.dict())
533
+ session.add(db_record)
534
+ session.commit()
535
+
536
+ added_ids.append(record.id)
521
537
 
522
538
  def insert(self, record, exists_ok=True):
523
539
  self.insert_many([record], exists_ok=exists_ok)
letta/cli/cli.py CHANGED
@@ -11,6 +11,7 @@ from letta import create_client
11
11
  from letta.agent import Agent, save_agent
12
12
  from letta.config import LettaConfig
13
13
  from letta.constants import CLI_WARNING_PREFIX, LETTA_DIR
14
+ from letta.local_llm.constants import ASSISTANT_MESSAGE_CLI_SYMBOL
14
15
  from letta.log import get_logger
15
16
  from letta.metadata import MetadataStore
16
17
  from letta.schemas.enums import OptionState
@@ -276,7 +277,7 @@ def run(
276
277
  memory = ChatMemory(human=human_obj.value, persona=persona_obj.value, limit=core_memory_limit)
277
278
  metadata = {"human": human_obj.name, "persona": persona_obj.name}
278
279
 
279
- typer.secho(f"-> 🤖 Using persona profile: '{persona_obj.name}'", fg=typer.colors.WHITE)
280
+ typer.secho(f"-> {ASSISTANT_MESSAGE_CLI_SYMBOL} Using persona profile: '{persona_obj.name}'", fg=typer.colors.WHITE)
280
281
  typer.secho(f"-> 🧑 Using human profile: '{human_obj.name}'", fg=typer.colors.WHITE)
281
282
 
282
283
  # add tools