letta-nightly 0.5.0.dev20241016104103__py3-none-any.whl → 0.5.0.dev20241018104142__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.

@@ -1,6 +1,6 @@
1
1
  from typing import Literal, Optional
2
2
 
3
- from pydantic import BaseModel, ConfigDict, Field
3
+ from pydantic import BaseModel, ConfigDict, Field, root_validator
4
4
 
5
5
 
6
6
  class LLMConfig(BaseModel):
@@ -13,6 +13,7 @@ class LLMConfig(BaseModel):
13
13
  model_endpoint (str): The endpoint for the model.
14
14
  model_wrapper (str): The wrapper for the model. This is used to wrap additional text around the input/output of the model. This is useful for text-to-text completions, such as the Completions API in OpenAI.
15
15
  context_window (int): The context window size for the model.
16
+ put_inner_thoughts_in_kwargs (bool): Puts 'inner_thoughts' as a kwarg in the function call if this is set to True. This helps with function calling performance and also the generation of inner thoughts.
16
17
  """
17
18
 
18
19
  # TODO: 🤮 don't default to a vendor! bug city!
@@ -38,10 +39,32 @@ class LLMConfig(BaseModel):
38
39
  model_endpoint: Optional[str] = Field(None, description="The endpoint for the model.")
39
40
  model_wrapper: Optional[str] = Field(None, description="The wrapper for the model.")
40
41
  context_window: int = Field(..., description="The context window size for the model.")
42
+ put_inner_thoughts_in_kwargs: Optional[bool] = Field(
43
+ True,
44
+ description="Puts 'inner_thoughts' as a kwarg in the function call if this is set to True. This helps with function calling performance and also the generation of inner thoughts.",
45
+ )
41
46
 
42
47
  # FIXME hack to silence pydantic protected namespace warning
43
48
  model_config = ConfigDict(protected_namespaces=())
44
49
 
50
+ @root_validator(pre=True)
51
+ def set_default_put_inner_thoughts(cls, values):
52
+ """
53
+ Dynamically set the default for put_inner_thoughts_in_kwargs based on the model field,
54
+ falling back to True if no specific rule is defined.
55
+ """
56
+ model = values.get("model")
57
+
58
+ # Define models where we want put_inner_thoughts_in_kwargs to be False
59
+ # For now it is gpt-4
60
+ avoid_put_inner_thoughts_in_kwargs = ["gpt-4"]
61
+
62
+ # Only modify the value if it's None or not provided
63
+ if values.get("put_inner_thoughts_in_kwargs") is None:
64
+ values["put_inner_thoughts_in_kwargs"] = False if model in avoid_put_inner_thoughts_in_kwargs else True
65
+
66
+ return values
67
+
45
68
  @classmethod
46
69
  def default_config(cls, model_name: str):
47
70
  if model_name == "gpt-4":
letta/schemas/tool.py CHANGED
@@ -112,11 +112,11 @@ class Tool(BaseTool):
112
112
  Class method to create an instance of Tool from a Langchain tool (must be from langchain_community.tools).
113
113
 
114
114
  Args:
115
- langchain_tool (LangChainBaseTool): An instance of a crewAI BaseTool (BaseTool from crewai)
115
+ langchain_tool (LangChainBaseTool): An instance of a LangChain BaseTool (BaseTool from LangChain)
116
116
  additional_imports_module_attr_map (dict[str, str]): A mapping of module names to attribute name. This is used internally to import all the required classes for the langchain tool. For example, you would pass in `{"langchain_community.utilities": "WikipediaAPIWrapper"}` for `from langchain_community.tools import WikipediaQueryRun`. NOTE: You do NOT need to specify the tool import here, that is done automatically for you.
117
117
 
118
118
  Returns:
119
- Tool: A Letta Tool initialized with attributes derived from the provided crewAI BaseTool object.
119
+ Tool: A Letta Tool initialized with attributes derived from the provided LangChain BaseTool object.
120
120
  """
121
121
  description = langchain_tool.description
122
122
  source_type = "python"
@@ -174,6 +174,38 @@ class Tool(BaseTool):
174
174
  json_schema=json_schema,
175
175
  )
176
176
 
177
+ @classmethod
178
+ def load_default_langchain_tools(cls) -> List["Tool"]:
179
+ # For now, we only support wikipedia tool
180
+ from langchain_community.tools import WikipediaQueryRun
181
+ from langchain_community.utilities import WikipediaAPIWrapper
182
+
183
+ wikipedia_tool = Tool.from_langchain(
184
+ WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper()), {"langchain_community.utilities": "WikipediaAPIWrapper"}
185
+ )
186
+
187
+ return [wikipedia_tool]
188
+
189
+ @classmethod
190
+ def load_default_crewai_tools(cls) -> List["Tool"]:
191
+ # For now, we only support scrape website tool
192
+ from crewai_tools import ScrapeWebsiteTool
193
+
194
+ web_scrape_tool = Tool.from_crewai(ScrapeWebsiteTool())
195
+
196
+ return [web_scrape_tool]
197
+
198
+ @classmethod
199
+ def load_default_composio_tools(cls) -> List["Tool"]:
200
+ from composio_langchain import Action
201
+
202
+ calculator = Tool.get_composio_tool(action=Action.MATHEMATICAL_CALCULATOR)
203
+ serp_news = Tool.get_composio_tool(action=Action.SERPAPI_NEWS_SEARCH)
204
+ serp_google_search = Tool.get_composio_tool(action=Action.SERPAPI_SEARCH)
205
+ serp_google_maps = Tool.get_composio_tool(action=Action.SERPAPI_GOOGLE_MAPS_SEARCH)
206
+
207
+ return [calculator, serp_news, serp_google_search, serp_google_maps]
208
+
177
209
 
178
210
  class ToolCreate(BaseTool):
179
211
  id: Optional[str] = Field(None, description="The unique identifier of the tool. If this is not provided, it will be autogenerated.")
letta/server/server.py CHANGED
@@ -43,12 +43,12 @@ from letta.interface import CLIInterface # for printing to terminal
43
43
  from letta.log import get_logger
44
44
  from letta.memory import get_memory_functions
45
45
  from letta.metadata import Base, MetadataStore
46
+ from letta.o1_agent import O1Agent
46
47
  from letta.prompts import gpt_system
47
48
  from letta.providers import (
48
49
  AnthropicProvider,
49
50
  AzureProvider,
50
51
  GoogleAIProvider,
51
- GroqProvider,
52
52
  LettaProvider,
53
53
  OllamaProvider,
54
54
  OpenAIProvider,
@@ -73,12 +73,7 @@ from letta.schemas.file import FileMetadata
73
73
  from letta.schemas.job import Job
74
74
  from letta.schemas.letta_message import LettaMessage
75
75
  from letta.schemas.llm_config import LLMConfig
76
- from letta.schemas.memory import (
77
- ArchivalMemorySummary,
78
- ContextWindowOverview,
79
- Memory,
80
- RecallMemorySummary,
81
- )
76
+ from letta.schemas.memory import ArchivalMemorySummary, Memory, RecallMemorySummary
82
77
  from letta.schemas.message import Message, MessageCreate, MessageRole, UpdateMessage
83
78
  from letta.schemas.organization import Organization, OrganizationCreate
84
79
  from letta.schemas.passage import Passage
@@ -249,6 +244,9 @@ class SyncServer(Server):
249
244
  # add global default tools (for admin)
250
245
  self.add_default_tools(module_name="base")
251
246
 
247
+ if settings.load_default_external_tools:
248
+ self.add_default_external_tools()
249
+
252
250
  # collect providers (always has Letta as a default)
253
251
  self._enabled_providers: List[Provider] = [LettaProvider()]
254
252
  if model_settings.openai_api_key:
@@ -303,12 +301,6 @@ class SyncServer(Server):
303
301
  base_url=model_settings.vllm_api_base,
304
302
  )
305
303
  )
306
- if model_settings.groq_api_key:
307
- self._enabled_providers.append(
308
- GroqProvider(
309
- api_key=model_settings.groq_api_key,
310
- )
311
- )
312
304
 
313
305
  def save_agents(self):
314
306
  """Saves all the agents that are in the in-memory object store"""
@@ -373,8 +365,10 @@ class SyncServer(Server):
373
365
 
374
366
  if agent_state.agent_type == AgentType.memgpt_agent:
375
367
  letta_agent = Agent(agent_state=agent_state, interface=interface, tools=tool_objs)
368
+ elif agent_state.agent_type == AgentType.o1_agent:
369
+ letta_agent = O1Agent(agent_state=agent_state, interface=interface, tools=tool_objs)
376
370
  else:
377
- raise NotImplementedError("Only base agents are supported as of right now!")
371
+ raise NotImplementedError("Not a supported agent type")
378
372
 
379
373
  # Add the agent to the in-memory store and return its reference
380
374
  logger.debug(f"Adding agent to the agent cache: user_id={user_id}, agent_id={agent_id}")
@@ -806,10 +800,18 @@ class SyncServer(Server):
806
800
  if request.name is None:
807
801
  request.name = create_random_username()
808
802
 
803
+ if request.agent_type is None:
804
+ request.agent_type = AgentType.memgpt_agent
805
+
809
806
  # system debug
810
807
  if request.system is None:
811
808
  # TODO: don't hardcode
812
- request.system = gpt_system.get_system_text("memgpt_chat")
809
+ if request.agent_type == AgentType.memgpt_agent:
810
+ request.system = gpt_system.get_system_text("memgpt_chat")
811
+ elif request.agent_type == AgentType.o1_agent:
812
+ request.system = gpt_system.get_system_text("memgpt_modified_o1")
813
+ else:
814
+ raise ValueError(f"Invalid agent type: {request.agent_type}")
813
815
 
814
816
  logger.debug(f"Attempting to find user: {user_id}")
815
817
  user = self.ms.get_user(user_id=user_id)
@@ -869,13 +871,22 @@ class SyncServer(Server):
869
871
  description=request.description,
870
872
  metadata_=request.metadata_,
871
873
  )
872
- agent = Agent(
873
- interface=interface,
874
- agent_state=agent_state,
875
- tools=tool_objs,
876
- # gpt-3.5-turbo tends to omit inner monologue, relax this requirement for now
877
- first_message_verify_mono=True if (llm_config.model is not None and "gpt-4" in llm_config.model) else False,
878
- )
874
+ if request.agent_type == AgentType.memgpt_agent:
875
+ agent = Agent(
876
+ interface=interface,
877
+ agent_state=agent_state,
878
+ tools=tool_objs,
879
+ # gpt-3.5-turbo tends to omit inner monologue, relax this requirement for now
880
+ first_message_verify_mono=True if (llm_config.model is not None and "gpt-4" in llm_config.model) else False,
881
+ )
882
+ elif request.agent_type == AgentType.o1_agent:
883
+ agent = O1Agent(
884
+ interface=interface,
885
+ agent_state=agent_state,
886
+ tools=tool_objs,
887
+ # gpt-3.5-turbo tends to omit inner monologue, relax this requirement for now
888
+ first_message_verify_mono=True if (llm_config.model is not None and "gpt-4" in llm_config.model) else False,
889
+ )
879
890
  # rebuilding agent memory on agent create in case shared memory blocks
880
891
  # were specified in the new agent's memory config. we're doing this for two reasons:
881
892
  # 1. if only the ID of the shared memory block was specified, we can fetch its most recent value
@@ -1453,7 +1464,6 @@ class SyncServer(Server):
1453
1464
  # Get the agent object (loaded in memory)
1454
1465
  letta_agent = self._get_or_load_agent(agent_id=agent_id)
1455
1466
  assert isinstance(letta_agent.memory, Memory)
1456
- assert isinstance(letta_agent.agent_state.memory, Memory)
1457
1467
  return letta_agent.agent_state.model_copy(deep=True)
1458
1468
 
1459
1469
  def get_server_config(self, include_defaults: bool = False) -> dict:
@@ -1969,11 +1979,13 @@ class SyncServer(Server):
1969
1979
  # Handle other general exceptions
1970
1980
  raise e
1971
1981
 
1982
+ functions_to_schema = []
1972
1983
  try:
1973
1984
  # Load the function set
1974
1985
  functions_to_schema = load_function_set(module)
1975
1986
  except ValueError as e:
1976
1987
  err = f"Error loading function set '{module_name}': {e}"
1988
+ warnings.warn(err)
1977
1989
 
1978
1990
  # create tool in db
1979
1991
  for name, schema in functions_to_schema.items():
@@ -1997,6 +2009,20 @@ class SyncServer(Server):
1997
2009
  update=True,
1998
2010
  )
1999
2011
 
2012
+ def add_default_external_tools(self, user_id: Optional[str] = None) -> bool:
2013
+ """Add default langchain tools. Return true if successful, false otherwise."""
2014
+ success = True
2015
+ tools = Tool.load_default_langchain_tools() + Tool.load_default_crewai_tools() + Tool.load_default_composio_tools()
2016
+ for tool in tools:
2017
+ try:
2018
+ self.ms.create_tool(tool)
2019
+ except Exception as e:
2020
+ warnings.warn(f"An error occurred while creating tool {tool}: {e}")
2021
+ warnings.warn(traceback.format_exc())
2022
+ success = False
2023
+
2024
+ return success
2025
+
2000
2026
  def add_default_blocks(self, user_id: str):
2001
2027
  from letta.utils import list_human_files, list_persona_files
2002
2028
 
@@ -2140,13 +2166,3 @@ class SyncServer(Server):
2140
2166
 
2141
2167
  def add_embedding_model(self, request: EmbeddingConfig) -> EmbeddingConfig:
2142
2168
  """Add a new embedding model"""
2143
-
2144
- def get_agent_context_window(
2145
- self,
2146
- user_id: str,
2147
- agent_id: str,
2148
- ) -> ContextWindowOverview:
2149
-
2150
- # Get the current message
2151
- letta_agent = self._get_or_load_agent(agent_id=agent_id)
2152
- return letta_agent.get_context_window()