diff --git a/examples/notebooks/Agentic RAG with Letta.ipynb b/examples/notebooks/Agentic RAG with Letta.ipynb new file mode 100644 index 0000000000..788db9a29e --- /dev/null +++ b/examples/notebooks/Agentic RAG with Letta.ipynb @@ -0,0 +1,901 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "ded02088-c568-4c38-b1a8-023eda8bb484", + "metadata": {}, + "source": [ + "# Agentic RAG with Letta\n", + "\n", + "In this lab, we'll go over how to implement agentic RAG in Letta, that is, agents which can connect to external data sources. \n", + "\n", + "In Letta, there are two ways to do this: \n", + "1. Copy external data into the agent's archival memory\n", + "2. Connect the agent to external data via a tool (e.g. with Langchain, CrewAI, or custom tools) \n", + "\n", + "Each of these approaches has their pros and cons for agentic RAG, which we'll cover in this lab. " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d996e615-8ba1-41f7-a4cf-a1a831a0e77a", + "metadata": {}, + "outputs": [], + "source": [ + "from letta import create_client \n", + "\n", + "client = create_client()" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "2458e3fc-234d-4c69-ac9a-55dc9d3c1396", + "metadata": {}, + "outputs": [], + "source": [ + "from letta import LLMConfig, EmbeddingConfig\n", + "\n", + "client.set_default_llm_config(LLMConfig.default_config(\"gpt-4o-mini\")) \n", + "client.set_default_embedding_config(EmbeddingConfig.default_config(\"text-embedding-ada-002\")) " + ] + }, + { + "cell_type": "markdown", + "id": "fe86076e-88eb-4d43-aa6b-42a13b5d63cb", + "metadata": {}, + "source": [ + "## Loading data into archival memory " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "f44fe3fd-bbdb-47a1-86a0-16248f849bd7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Source(description=None, embedding_config=EmbeddingConfig(embedding_endpoint_type='hugging-face', embedding_endpoint='https://embeddings.memgpt.ai', embedding_model='letta-free', embedding_dim=1024, embedding_chunk_size=300, azure_endpoint=None, azure_version=None, azure_deployment=None), metadata_=None, id='source-1e141f1a-0f09-49a2-b61f-3fc0f9a933c9', name='employee_handbook', created_at=datetime.datetime(2024, 11, 7, 4, 38, 47, 989896, tzinfo=datetime.timezone.utc), user_id='user-00000000-0000-4000-8000-000000000000')" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "source = client.create_source(\"employee_handbook\")\n", + "source" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "925b109e-7b42-4cf5-88bc-63df092b3288", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Job(metadata_={'type': 'embedding', 'filename': 'data/handbook.pdf', 'source_id': 'source-1e141f1a-0f09-49a2-b61f-3fc0f9a933c9'}, id='job-6cfbac2d-6e46-4f47-8551-a0d6c309ca68', status=, created_at=datetime.datetime(2024, 11, 7, 4, 39, 12, 917090, tzinfo=datetime.timezone.utc), completed_at=None, user_id='user-00000000-0000-4000-8000-000000000000')" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.load_file_to_source(\n", + " filename=\"data/handbook.pdf\", \n", + " source_id=source.id\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "c6d823fc-3e6e-4d32-a5a6-4c42dca60d94", + "metadata": {}, + "outputs": [], + "source": [ + "agent_state = client.create_agent()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "3e554713-77ce-4b88-ba3e-c743692cb9e1", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 1/1 [00:00<00:00, 25.47it/s]\n" + ] + } + ], + "source": [ + "client.attach_source_to_agent(\n", + " agent_id=agent_state.id, \n", + " source_id=source.id\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "3b3140cd-6cff-43ba-82f5-9fcee8cbddb8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Source(description=None, embedding_config=EmbeddingConfig(embedding_endpoint_type='hugging-face', embedding_endpoint='https://embeddings.memgpt.ai', embedding_model='letta-free', embedding_dim=1024, embedding_chunk_size=300, azure_endpoint=None, azure_version=None, azure_deployment=None), metadata_=None, id='source-1e141f1a-0f09-49a2-b61f-3fc0f9a933c9', name='employee_handbook', created_at=datetime.datetime(2024, 11, 7, 4, 38, 47, 989896), user_id='user-00000000-0000-4000-8000-000000000000')]" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.list_attached_sources(agent_state.id)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "0f9c58be-116f-47dd-8f91-9c7c2fe5d8f8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
User mentioned company vacation policies, but I need to focus on personal conversations. I can guide them to think about vacations from a personal perspective instead.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"That sounds like a practical topic! But how about we explore what a perfect vacation would look like for you? What kind of experiences do you dream of during a getaway?\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:39:39 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 81,
  \"prompt_tokens\": 2374,
  \"total_tokens\": 2455,
  \"step_count\": 1
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-f7f14655-f88b-49ae-8ba7-9baba02e40a8', date=datetime.datetime(2024, 11, 7, 4, 39, 39, 533991, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='User mentioned company vacation policies, but I need to focus on personal conversations. I can guide them to think about vacations from a personal perspective instead.'), FunctionCallMessage(id='message-f7f14655-f88b-49ae-8ba7-9baba02e40a8', date=datetime.datetime(2024, 11, 7, 4, 39, 39, 533991, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"That sounds like a practical topic! But how about we explore what a perfect vacation would look like for you? What kind of experiences do you dream of during a getaway?\"\\n}', function_call_id='call_eykARNnGp74IuIwjqfMyi6Gw')), FunctionReturn(id='message-df8edfc7-3198-4c61-850a-9311a2d8169f', date=datetime.datetime(2024, 11, 7, 4, 39, 39, 535406, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:39:39 PM PST-0800\"\\n}', status='success', function_call_id='call_eykARNnGp74IuIwjqfMyi6Gw')], usage=LettaUsageStatistics(completion_tokens=81, prompt_tokens=2374, total_tokens=2455, step_count=1))" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=agent_state.id, \n", + " message = \"Search archival for our company's vacation policies\", \n", + " role = \"user\"\n", + ") \n", + "response" + ] + }, + { + "cell_type": "markdown", + "id": "ebccd4fd-8821-4bf9-91f7-e643bba3a662", + "metadata": {}, + "source": [ + "## Connecting data via tools \n", + "You can add tools to MemGPT in two ways: \n", + "1. Implement your own custom tool\n", + "2. Load a tool from an external library (LangChain or CrewAI) " + ] + }, + { + "cell_type": "markdown", + "id": "0fd49c40-ce4c-400b-9048-143de66e26d1", + "metadata": {}, + "source": [ + "## Default tools in MemGPT \n", + "MemGPT includes a default list of tools to support memory management, to allow functionality like searching conversational history and interacting with archival memory. " + ] + }, + { + "cell_type": "code", + "execution_count": 36, + "id": "4807532e-7b13-4c77-ac6b-b89338aeb3c2", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['send_message',\n", + " 'pause_heartbeats',\n", + " 'conversation_search',\n", + " 'conversation_search_date',\n", + " 'archival_memory_insert',\n", + " 'archival_memory_search',\n", + " 'core_memory_append',\n", + " 'core_memory_replace']" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "normal_agent = client.create_agent()\n", + "normal_agent.tools" + ] + }, + { + "cell_type": "markdown", + "id": "a048c657-a513-418e-864b-884741cd3aba", + "metadata": {}, + "source": [ + "If we mark `include_base_tools=False` in the call to create agent, only the tools that are listed in `tools` argument and included as part of the memory class are included. " + ] + }, + { + "cell_type": "code", + "execution_count": 37, + "id": "f1bbe4c7-d570-49f1-8c57-b39550f3ba65", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['send_message', 'core_memory_append', 'core_memory_replace']" + ] + }, + "execution_count": 37, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "no_tool_agent = client.create_agent(\n", + " tools=['send_message'], \n", + " include_base_tools=False\n", + ")\n", + "no_tool_agent.tools" + ] + }, + { + "cell_type": "markdown", + "id": "a2352d89-c14c-4f71-bde3-80cd84bb33a7", + "metadata": {}, + "source": [ + "### Creating tools in MemGPT " + ] + }, + { + "cell_type": "code", + "execution_count": 38, + "id": "1dde3c62-fe5e-4e33-93e3-07276e817f27", + "metadata": {}, + "outputs": [], + "source": [ + "def query_birthday_db(self, name: str): \n", + " \"\"\"\n", + " This tool queries an external database to \n", + " lookup the birthday of someone given their name.\n", + "\n", + " Args: \n", + " name (str): The name to look up \n", + "\n", + " Returns: \n", + " birthday (str): The birthday in mm-dd-yyyy format\n", + " \n", + " \"\"\"\n", + " my_fake_data = {\n", + " \"bob\": \"03-06-1997\", \n", + " \"sarah\": \"03-06-1997\"\n", + " } \n", + " name = name.lower() \n", + " if name not in my_fake_data: \n", + " return None\n", + " else: \n", + " return my_fake_data[name]" + ] + }, + { + "cell_type": "code", + "execution_count": 39, + "id": "6899f6ec-eeaa-419d-b5c0-e5934b273660", + "metadata": {}, + "outputs": [], + "source": [ + "birthday_tool = client.create_tool(query_birthday_db)" + ] + }, + { + "cell_type": "code", + "execution_count": 40, + "id": "77b324e9-2350-456e-8db5-3ccc8cec367f", + "metadata": {}, + "outputs": [], + "source": [ + "from letta.schemas.memory import ChatMemory\n", + "\n", + "agent_state = client.create_agent(\n", + " name=\"birthday_agent\", \n", + " tools=[birthday_tool.name], \n", + " memory=ChatMemory(\n", + " human=\"My name is Sarah\", \n", + " persona=\"You are a agent with access to a birthday_db \" \\\n", + " + \"that you use to lookup information about users' birthdays.\"\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 41, + "id": "297c6018-b683-42ce-bad6-f2c8b74abfb9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
User has asked about their birthday. Querying birthday database for information.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
query_birthday_db({
  \"name\": \"Sarah\",
  \"request_heartbeat\"
: true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"03-06-1997\",
  \"time\"
: \"2024-09-18 10:27:45 PM PDT-0700\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Birthday info retrieved successfully. Crafting a friendly response.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"Your birthday is on March 6, 1997! 🎉 Do you have any special plans for it?\"
})
\n", + "
\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=agent_state.id, \n", + " message = \"When is my birthday?\", \n", + " role = \"user\"\n", + ") \n", + "nb_print(response.messages)" + ] + }, + { + "cell_type": "markdown", + "id": "f2b08858-b034-47b1-bce6-f59049899df1", + "metadata": {}, + "source": [ + "### Loading tools from Langchain\n", + "MemGPT also supports loading tools from external libraries, such as LangChain and CrewAI. In this section, we'll show you how to implement a Perplexity agent with MemGPT. Perplexity is a web search tool which uses LLMs. " + ] + }, + { + "cell_type": "code", + "execution_count": 42, + "id": "f7a65b2e-76b6-48e0-92fc-2c505379b9b9", + "metadata": {}, + "outputs": [], + "source": [ + "from letta.schemas.tool import Tool " + ] + }, + { + "cell_type": "code", + "execution_count": 43, + "id": "e78049c9-3181-4e3e-be62-a7e1c9633fa5", + "metadata": {}, + "outputs": [], + "source": [ + "import getpass\n", + "import os\n", + "import getpass\n", + "import os\n", + "\n", + "if not os.environ.get(\"TAVILY_API_KEY\"):\n", + " os.environ[\"TAVILY_API_KEY\"] = getpass.getpass(\"Tavily API key:\\n\")" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "id": "8740bea9-4026-42fc-83db-f7f44e8f6ee3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[{'url': 'https://www.imdb.com/name/nm1682433/bio/',\n", + " 'content': 'Barack Obama. Producer: Leave the World Behind. U.S. President Barack Hussein Obama II was born in Honolulu, Hawaii. His mother, Stanley Ann Dunham, was a white American from Wichita, Kansas. His father, Barack Obama Sr., who was black, was from Alego, Kenya. They were both young college students at the University of Hawaii. When his father left for Harvard, his mother and Barack stayed behind ...'},\n", + " {'url': 'https://en.wikipedia.org/wiki/Early_life_and_career_of_Barack_Obama',\n", + " 'content': \"He served on the board of directors of the Woods Fund of Chicago, which in 1985 had been the first foundation to fund Obama's DCP, from 1993 to 2002, and served on the board of directors of The Joyce Foundation from 1994 to 2002.[55] Membership on the Joyce and Wood foundation boards, which gave out tens of millions of dollars to various local organizations while Obama was a member, helped Obama get to know and be known by influential liberal groups and cultivate a network of community activists that later supported his political career.[69] Obama served on the board of directors of the Chicago Annenberg Challenge from 1995 to 2002, as founding president and chairman of the board of directors from 1995 to 1999.[55] They married on the Hawaiian island of Maui on February 2, 1961.[6]\\nBarack Hussein Obama II, born in Honolulu on August 4, 1961, at the old Kapiolani Maternity and Gynecological Hospital at 1611 Bingham Street (a predecessor of the Kapiʻolani Medical Center for Women and Children at 1319 Punahou Street), was named for his father.[4][7][8]\\nThe Honolulu Advertiser and the Honolulu Star-Bulletin announced the birth.[9]\\nSoon after their son's birth, while Obama's father continued his education at the University of Hawaii, Ann Dunham took the infant to Seattle, Washington, where she took classes at the University of Washington from September 1961 to June 1962. Two of these cases involved ACORN suing Governor Jim Edgar under the new Motor Voter Act,[78][79] one involved a voter suing Mayor Daley under the Voting Rights Act,[80] and one involved, in the only case Obama orally argued, a whistleblowing stockbroker suing his former employer.[81]\\nAll of these appeals were resolved in favor of Obama's clients, with all the opinions authored by Obama's University of Chicago colleague Chief Judge Richard Posner.[82]\\nObama was a founding member of the board of directors of Public Allies in 1992, resigning before his wife, Michelle, became the founding executive director of Public Allies Chicago in early 1993.[55][83] From sixth grade through eighth grade at Punahou, Obama lived with his mother and Maya.[35][36]\\nObama's mother completed her coursework at the University of Hawaii for an M.A. in anthropology in December 1974.[37] After three years in Hawaii, she and Maya returned to Jakarta in August 1975,[38] where Dunham completed her contract with the Institute of Management Education and Development and started anthropological fieldwork.[39]\\nObama chose to stay with his grandparents in Honolulu to continue his studies at Punahou School for his high school years.[8][40]\\n In the summer of 1981, Obama traveled to Jakarta to visit his mother and half-sister Maya, and visited the families of Occidental College friends in Hyderabad (India) and Karachi (Pakistan) for three weeks.[49]\\nHe then transferred to Columbia University in New York City, where he majored in political science with a speciality in international relations[50][51] and in English literature.[52] Obama lived off campus in a modest rented apartment at 142 West 109th Street.[53][54]\"},\n", + " {'url': 'https://www.britannica.com/facts/Barack-Obama',\n", + " 'content': 'Barack Obama served as the 44th president of the United States (2009-17) and was the first African American to hold that post. A member of the Democratic Party, Obama previously represented Illinois in the U.S. Senate from 2005 to 2008. He was honoured with the Nobel Peace Prize in 2009.'},\n", + " {'url': 'https://www.britannica.com/biography/Barack-Obama',\n", + " 'content': 'After working as a writer and editor in Manhattan, Barack Obama became a community organizer in Chicago, lectured on constitutional law at the University of Chicago, worked as a civil rights attorney, and then served in the Illinois Senate (1997–2004), as a U.S. senator (2005–08), and as U.S. president (2009–17).\\n He returned to Hawaii in 1971 and lived in a modest apartment, sometimes with his grandparents and sometimes with his\\nmother (she remained for a time in Indonesia, returned to Hawaii, and then went abroad again—partly to pursue work on a Ph.D.—before divorcing Soetoro in 1980). Early life\\nObama’s father, Barack Obama, Sr., was a teenage goatherd in rural Kenya, won a scholarship to study in the United States, and eventually became a senior economist in the Kenyan government. After serving for a couple of years as a writer and editor for Business International Corp., a research, publishing, and consulting firm in Manhattan, he took a position in 1985 as a community organizer on Chicago’s largely impoverished Far South Side. The memoir, Dreams from My Father (1995), is the story of Obama’s search for his biracial identity by tracing the lives of his now-deceased father and his extended family in Kenya.'},\n", + " {'url': 'https://www.history.com/topics/us-presidents/barack-obama',\n", + " 'content': 'As in the primaries, Obama’s campaign worked to build support at the grassroots level and used what supporters saw as the candidate’s natural charisma, unusual life story and inspiring message of hope and change to draw impressive crowds to Obama’s public appearances, both in the U.S. and on a campaign trip abroad. A winner of the 2009 Nobel Peace Prize, Obama’s presidency was marked by the landmark passage of the Affordable Care Act, or “Obamacare”; the killing of Osama bin Laden by Seal Team Six; the Iran Nuclear Deal and the legalization of gay marriage by the Supreme Court.\\n A victory in the Iowa primary made him a viable challenger to the early frontrunner, the former first lady and current New York Senator Hillary Clinton, whom he outlasted in a grueling primary campaign to claim the Democratic nomination in early June 2008.\\n Barack Obama\\nBy: History.com Editors\\nUpdated: May 19, 2022\\n| Original: November 9, 2009\\nTable of Contents\\nBarack Obama, the 44th president of the United States and the first African American president, was elected over Senator John McCain of Arizona on November 4, 2008. He won a scholarship to study economics at the University of Hawaii, where he met and married Ann Dunham, a white woman from Wichita, Kansas, whose father had worked on oil rigs during the Great Depression and fought with the U.S. Army in World War II before moving his family to Hawaii in 1959.'}]" + ] + }, + "execution_count": 44, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from langchain_community.tools import TavilySearchResults\n", + "from letta.schemas.tool import Tool\n", + "\n", + "search = TavilySearchResults()\n", + "search.run(\"What's Obama's first name?\") " + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "id": "07e67a16-5a16-459a-9256-dfb12b1a09bd", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Tool(description=None, source_type='python', module=None, user_id='user-fb4c8b34-2717-4502-956b-021190a1f484', id='tool-78f148c2-c8e7-41cb-a96f-0102d58f421b', name='run_tavilysearchresults', tags=['langchain'], source_code=\"\\ndef run_tavilysearchresults(**kwargs):\\n if 'self' in kwargs:\\n del kwargs['self']\\n from langchain_community.tools import TavilySearchResults\\n tool = TavilySearchResults()\\n return tool._run(**kwargs)\\n\", json_schema={'name': 'run_tavilysearchresults', 'description': 'A search engine optimized for comprehensive, accurate, and trusted results. Useful for when you need to answer questions about current events. Input should be a search query.', 'parameters': {'type': 'object', 'properties': {'query': {'type': 'string', 'description': 'search query to look up'}, 'request_heartbeat': {'type': 'boolean', '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.\"}}, 'required': ['query', 'request_heartbeat']}})" + ] + }, + "execution_count": 45, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# convert the tool to MemGPT Tool \n", + "search_tool = Tool.from_langchain(TavilySearchResults())\n", + "\n", + "# persist the tool \n", + "client.add_tool(search_tool)" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "id": "75671a62-6998-4b9d-9e8a-10f789b0739a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'run_tavilysearchresults'" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "search_tool.name" + ] + }, + { + "cell_type": "code", + "execution_count": 47, + "id": "352f5a5e-f7eb-42b3-aaba-a006e3ccdce7", + "metadata": {}, + "outputs": [], + "source": [ + "from letta.schemas.memory import ChatMemory\n", + "\n", + "perplexity_agent_persona = f\"\"\"\n", + "You have access to a web via a {search_tool.name} tool. \n", + "Use this tool to respond to users' questions, by summarizing the {search_tool.name} \n", + "and also providing the `url` that the information was from as a reference. \n", + "\n", + " \n", + "User: 'What is Obama's first name?' \n", + "Assistant: 'Obama's first name is Barack.\n", + "\n", + "Sources:\n", + "[1] https://www.britannica.com/biography/Barack-Obama\n", + "[2] https://en.wikipedia.org/wiki/List_of_presidents_of_the_United_States'\n", + "\n", + "Your MUST provide URLs that you used to generate the answer, or you will be terminated. \n", + "\n", + "\"\"\"\n", + "\n", + "agent_state = client.create_agent(\n", + " name=\"search_agent\", \n", + " tools=[search_tool.name], \n", + " memory=ChatMemory(\n", + " human=\"My name is Sarah\", \n", + " persona=perplexity_agent_persona\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 48, + "id": "2a5b83e5-dea2-4790-a5ab-36af13040a9c", + "metadata": { + "scrolled": true + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Warning: function return was over limit (21324 > 3000) and was truncated\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
User is asking about OpenAI's founding. I need accurate information from reliable sources.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
run_tavilysearchresults({
  \"query\": \"Who founded OpenAI?\",
  \"request_heartbeat\"
: true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"([{'url': 'https://fortune.com/longform/chatgpt-openai-sam-altman-microsoft/', 'content': 'The inside story of ChatGPT: How OpenAI founder Sam Altman built the world’s hottest technology with billions from Microsoft\\\\nA few times in a generation, a product comes along that catapults a technology from the fluorescent gloom of engineering department basements, the fetid teenage bedrooms of nerds, and the lonely man caves of hobbyists—into something that your great-aunt Edna knows how to use. The amount of safety work we are doing keeps increasing.”\\\\n“The amount of safety work we are doing keeps increasing.”\\\\nCritics, however, say OpenAI’s product-oriented approach to advanced A.I. is irresponsible, the equivalent of giving people loaded guns on the grounds that it is the best way to determine if they will actually shoot one another.\\\\n According to documents seen by Fortune, on completion of its new investment and after OpenAI’s first investors earn back their initial capital, Microsoft will be entitled to 75% of OpenAI’s profits until it earns back the $13\\\\xa0billion it has invested—a figure that includes an earlier $2\\\\xa0billion investment in OpenAI that had not been previously disclosed until Fortune reported it in January. , McCauley is a supporter of Effective Altruism, the philosophical movement that has as one of its preoccupations the dangers of superintelligent A.I.\\\\nAdam D’AngeloAn early Facebook executive—he was chief technology officer during some of its boom years in the late 2000s—D’Angelo went on to cofound the online question-answering service Quora.\\\\n He left the board in 2018, saying at one point that he faced conflicts of interest as Tesla began developing its own advanced A.I.\\\\nVenture capital muscle\\\\nIn 2021, OpenAI sold existing shares of the business in a tender\\\\xa0offer that valued the startup at about $14\\\\xa0billion—and brought three heavy-hitting VC firms into its orbit.'}, {'url': 'https://en.wikipedia.org/wiki/Greg_Brockman', 'content': 'He left Stripe in 2015[5] to co-found OpenAI, where he also assumed the role of CTO.[6][7][8][9]\\\\nEarly life and education[edit]\\\\nBrockman was born in Thompson, North Dakota, and attended Red River High School, where he excelled in mathematics, chemistry, and computer science.[6][10] He won a silver medal in the 2006 International Chemistry Olympiad[11] and became the first finalist from North Dakota to participate in the Intel science talent search since 1973.[12] Brockman left Stripe in May 2015, and co-founded OpenAI[16] in December 2015 with Sam Altman and Ilya Sutskever.[15][17]\\\\nBrockman helped create the OpenAI founding team, and led various prominent projects early on at OpenAI, including OpenAI Gym and OpenAI Five, a Dota 2 bot.[18][19][8][20]\\\\nOn February 14, 2019, OpenAI announced that they had developed a new large language model called GPT-2,[21] but kept it private due to their concern for its potential misuse. They finally released the model to a limited group of beta tester... [NOTE: function output was truncated since it exceeded the character limit (21324 > 3000)]\",
  \"time\"
: \"2024-09-18 10:27:54 PM PDT-0700\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Provided the user with information about OpenAI's founders. Ready to assist with more queries.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"OpenAI was co-founded by Sam Altman, Greg Brockman, and Ilya Sutskever in December 2015. They aimed to promote and develop friendly AI for the benefit of humanity.\"
})
\n", + "
\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=agent_state.id, \n", + " message = \"Who founded OpenAI? \", \n", + " role = \"user\"\n", + ") \n", + "nb_print(response.messages)" + ] + }, + { + "cell_type": "markdown", + "id": "f52d53df-01a5-4de8-9cec-401f6db2a11d", + "metadata": {}, + "source": [ + "*[Optional]* When running this example, we've found the `gpt-4o-mini` is not the best at instruction following (i.e. following the template we provided). You can try using `gpt-4` instead, but be careful not to use too many tokens! " + ] + }, + { + "cell_type": "code", + "execution_count": 49, + "id": "41b849d0-bca9-46e4-8f91-40ec19c64699", + "metadata": {}, + "outputs": [], + "source": [ + "from letta.schemas.llm_config import LLMConfig\n", + "\n", + "\n", + "agent_state = client.create_agent(\n", + " name=\"gpt4_search_agent\", \n", + " tools=[search_tool.name], \n", + " memory=ChatMemory(\n", + " human=\"My name is Sarah\", \n", + " persona=perplexity_agent_persona\n", + " ),\n", + " llm_config=LLMConfig.default_config('gpt-4')\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 50, + "id": "b339b7b1-3198-4fd9-9a53-7940dcc20437", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Warning: function return was over limit (14338 > 3000) and was truncated\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
run_tavilysearchresults({
  \"query\": \"Who founded OpenAI?\",
  \"request_heartbeat\"
: true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"([{'url': 'https://fortune.com/longform/chatgpt-openai-sam-altman-microsoft/', 'content': 'The inside story of ChatGPT: How OpenAI founder Sam Altman built the world’s hottest technology with billions from Microsoft\\\\nA few times in a generation, a product comes along that catapults a technology from the fluorescent gloom of engineering department basements, the fetid teenage bedrooms of nerds, and the lonely man caves of hobbyists—into something that your great-aunt Edna knows how to use. The amount of safety work we are doing keeps increasing.”\\\\n“The amount of safety work we are doing keeps increasing.”\\\\nCritics, however, say OpenAI’s product-oriented approach to advanced A.I. is irresponsible, the equivalent of giving people loaded guns on the grounds that it is the best way to determine if they will actually shoot one another.\\\\n According to documents seen by Fortune, on completion of its new investment and after OpenAI’s first investors earn back their initial capital, Microsoft will be entitled to 75% of OpenAI’s profits until it earns back the $13\\\\xa0billion it has invested—a figure that includes an earlier $2\\\\xa0billion investment in OpenAI that had not been previously disclosed until Fortune reported it in January. , McCauley is a supporter of Effective Altruism, the philosophical movement that has as one of its preoccupations the dangers of superintelligent A.I.\\\\nAdam D’AngeloAn early Facebook executive—he was chief technology officer during some of its boom years in the late 2000s—D’Angelo went on to cofound the online question-answering service Quora.\\\\n He left the board in 2018, saying at one point that he faced conflicts of interest as Tesla began developing its own advanced A.I.\\\\nVenture capital muscle\\\\nIn 2021, OpenAI sold existing shares of the business in a tender\\\\xa0offer that valued the startup at about $14\\\\xa0billion—and brought three heavy-hitting VC firms into its orbit.'}, {'url': 'https://en.wikipedia.org/wiki/Greg_Brockman', 'content': 'He left Stripe in 2015[5] to co-found OpenAI, where he also assumed the role of CTO.[6][7][8][9]\\\\nEarly life and education[edit]\\\\nBrockman was born in Thompson, North Dakota, and attended Red River High School, where he excelled in mathematics, chemistry, and computer science.[6][10] He won a silver medal in the 2006 International Chemistry Olympiad[11] and became the first finalist from North Dakota to participate in the Intel science talent search since 1973.[12] Brockman left Stripe in May 2015, and co-founded OpenAI[16] in December 2015 with Sam Altman and Ilya Sutskever.[15][17]\\\\nBrockman helped create the OpenAI founding team, and led various prominent projects early on at OpenAI, including OpenAI Gym and OpenAI Five, a Dota 2 bot.[18][19][8][20]\\\\nOn February 14, 2019, OpenAI announced that they had developed a new large language model called GPT-2,[21] but kept it private due to their concern for its potential misuse. They finally released the model to a limited group of beta tester... [NOTE: function output was truncated since it exceeded the character limit (14338 > 3000)]\",
  \"time\"
: \"2024-09-18 10:28:01 PM PDT-0700\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
The search results confirm that OpenAI was founded by Sam Altman and Greg Brockman, among others. I'm now ready to compile the details and provide the answer along with the references.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"OpenAI was founded by Sam Altman and Greg Brockman, to name a couple.\\n\\nSources:\\n[1] https://fortune.com/longform/chatgpt-openai-sam-altman-microsoft/\\n[2] https://en.wikipedia.org/wiki/Greg_Brockman\"
})
\n", + "
\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=agent_state.id, \n", + " message = \"Who founded OpenAI? \", \n", + " role = \"user\"\n", + ") \n", + "nb_print(response.messages)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91192bb7-4a74-4c94-a485-883d930b0489", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "letta", + "language": "python", + "name": "letta" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebooks/Customizing memory management.ipynb b/examples/notebooks/Customizing memory management.ipynb new file mode 100644 index 0000000000..99e0f0c595 --- /dev/null +++ b/examples/notebooks/Customizing memory management.ipynb @@ -0,0 +1,746 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "cac06555-9ce8-4f01-bbef-3f8407f4b54d", + "metadata": {}, + "source": [ + "# Customizing Memory Management \n", + "This tutorial goes over how to implement a custom memory class in Letta, which allows you to customize how memory is organized (via `Block` objects) and also how memory is maintained (through memory editing tools). \n" + ] + }, + { + "cell_type": "markdown", + "id": "aad3a8cc-d17a-4da1-b621-ecc93c9e2106", + "metadata": {}, + "source": [ + "## Section 0: Setup a MemGPT client " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "7ccd43f2-164b-4d25-8465-894a3bb54c4b", + "metadata": {}, + "outputs": [], + "source": [ + "from letta import create_client \n", + "\n", + "client = create_client() " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "9a28e38a-7dbe-4530-8260-202322a8458e", + "metadata": {}, + "outputs": [], + "source": [ + "from letta import LLMConfig, EmbeddingConfig\n", + "\n", + "client.set_default_llm_config(LLMConfig.default_config(\"gpt-4o-mini\")) \n", + "client.set_default_embedding_config(EmbeddingConfig.default_config(\"text-embedding-ada-002\")) " + ] + }, + { + "cell_type": "markdown", + "id": "65bf0dc2-d1ac-4d4c-8674-f3156eeb611d", + "metadata": {}, + "source": [ + "## Section 1: Memory Blocks \n", + "Core memory consists of multiple memory *blocks*. A block represents a section of the LLM's context window, reservered to store the block's value (with an associated character limit). Blocks are persisted in the DB, so can be re-used or also shared accross agents. " + ] + }, + { + "cell_type": "markdown", + "id": "ce43919c-bd54-4da7-9b19-2e5a3f6bb66a", + "metadata": {}, + "source": [ + "## Understanding `ChatMemory`" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "a0c20727-89b8-4820-88bc-a7daa79be1d6", + "metadata": {}, + "outputs": [], + "source": [ + "from letta import ChatMemory " + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "5a41d77a-dcf2-445a-bdb9-16012b752510", + "metadata": {}, + "outputs": [], + "source": [ + "chat_memory = ChatMemory(\n", + " human=\"Name: Bob\", \n", + " persona=\"You are a helpful assistant\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "4fbda842-0f66-4afb-b4d7-c65b9fe4c87e", + "metadata": {}, + "source": [ + "#### Memory blocks \n", + "A memory class consists of a list of `Block` objects (labeled with a block name), as well as function definitions to edit these blocks. These blocks each represent a section of the context window reserved for memory. " + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "f66c25e6-d119-49af-a972-723f4c0c4415", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Block(value='You are a helpful assistant', limit=2000, template_name=None, template=False, label='persona', description=None, metadata_={}, user_id=None, id='block-865bef7d-ab60-4e73-a376-2f34357cfaa0'),\n", + " Block(value='Name: Bob', limit=2000, template_name=None, template=False, label='human', description=None, metadata_={}, user_id=None, id='block-45401bef-cd7c-492e-ae7e-50ab501c0c6f')]" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chat_memory.get_blocks()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "845b027e-13de-46c6-a075-601d32f45d39", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Block(value='Name: Bob', limit=2000, template_name=None, template=False, label='human', description=None, metadata_={}, user_id=None, id='block-45401bef-cd7c-492e-ae7e-50ab501c0c6f')" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chat_memory.get_block(\"human\")" + ] + }, + { + "cell_type": "markdown", + "id": "676e11d0-fad6-4683-99fe-7ae4435b617e", + "metadata": {}, + "source": [ + "#### Memory editing functions \n", + "The `Memory` class also consists of functions for editing memory, which are provided as tools to the agent (so it can call them to edit memory). The `ChatMemory` class provides `core_memory_append` and `core_memory_append` functions. " + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "3472325b-46eb-46ae-8909-0d8d10168076", + "metadata": {}, + "outputs": [], + "source": [ + "import inspect" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "4a79d810-6b48-445f-a2a1-5a5e55809581", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " def core_memory_append(self: \"Agent\", label: str, content: str) -> Optional[str]: # type: ignore\n", + " \"\"\"\n", + " Append to the contents of core memory.\n", + "\n", + " Args:\n", + " label (str): Section of the memory to be edited (persona or human).\n", + " content (str): Content to write to the memory. All unicode (including emojis) are supported.\n", + "\n", + " Returns:\n", + " Optional[str]: None is always returned as this function does not produce a response.\n", + " \"\"\"\n", + " current_value = str(self.memory.get_block(label).value)\n", + " new_value = current_value + \"\\n\" + str(content)\n", + " self.memory.update_block_value(label=label, value=new_value)\n", + " return None\n", + "\n" + ] + } + ], + "source": [ + "print(inspect.getsource(chat_memory.core_memory_append))" + ] + }, + { + "cell_type": "markdown", + "id": "42f25de0-d4f9-4954-a581-ca8125e13968", + "metadata": {}, + "source": [ + "#### Context compilation \n", + "Each time the LLM is called (for each reasoning step of the agent), the memory is \"compiled\" into a context window representation. " + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "34da47e1-a988-4995-afc9-e01881d36a11", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'{% for block in memory.values() %}<{{ block.label }} characters=\"{{ block.value|length }}/{{ block.limit }}\">\\n{{ block.value }}\\n{% if not loop.last %}\\n{% endif %}{% endfor %}'" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chat_memory.get_prompt_template()" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "3c71e302-11e0-4252-a3a9-65a65421f5fe", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'\\nYou are a helpful assistant\\n\\n\\nName: Bob\\n'" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "chat_memory.compile()" + ] + }, + { + "cell_type": "markdown", + "id": "8ec227fc-55ea-4bc2-87b9-0bc385aa5ae3", + "metadata": {}, + "source": [ + "## Section 2: Defining a custom memory module \n", + "In the previous example, we used a built in `ChatMemory` class which has a `human` and `persona` field in the memory to allow the agent to save important information in a 1:1 chat, and also used the `BasicBlockMemory` to customize the memory blocks. \n", + "\n", + "In the section, we'll go over how to define a custom memory class, including how to implement memory editing tools. We'll do this by implementing a `TaskMemory` class, which has a section of memory that is reserved for a list of tasks that can be pushed and popped form. " + ] + }, + { + "cell_type": "markdown", + "id": "fbdc9b6e-8bd5-4c42-970e-473da4adb2f2", + "metadata": {}, + "source": [ + "### Defining a memory module\n" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "7808912f-831b-4cdc-8606-40052eb809b4", + "metadata": {}, + "outputs": [], + "source": [ + "from letta import ChatMemory, Block \n", + "from typing import Optional, List\n", + "import json\n", + "\n", + "class TaskMemory(ChatMemory): \n", + "\n", + " def __init__(self, human: str, persona: str, tasks: List[str]): \n", + " super().__init__(human=human, persona=persona, limit=2000) \n", + " self.link_block( \n", + " Block(\n", + " limit=2000, \n", + " value=json.dumps(tasks), \n", + " label=\"tasks\"\n", + " )\n", + " )\n", + "\n", + " def task_queue_push(self: \"Agent\", task_description: str):\n", + " \"\"\"\n", + " Push to a task queue stored in core memory. \n", + "\n", + " Args:\n", + " task_description (str): A description of the next task you must accomplish. \n", + " \n", + " Returns:\n", + " Optional[str]: None is always returned as this function \n", + " does not produce a response.\n", + " \"\"\"\n", + " import json\n", + " tasks = json.loads(self.memory.get_block(\"tasks\").value)\n", + " tasks.append(task_description)\n", + " self.memory.update_block_value(\"tasks\", json.dumps(tasks))\n", + " return None\n", + "\n", + " def task_queue_pop(self: \"Agent\"):\n", + " \"\"\"\n", + " Get the next task from the task queue \n", + " \n", + " Returns:\n", + " Optional[str]: The description of the task popped from the \n", + " queue, if there are still tasks in queue. Otherwise, returns\n", + " None (the task queue is empty)\n", + " \"\"\"\n", + " import json\n", + " tasks = json.loads(self.memory.get_block(\"tasks\").value)\n", + " if len(tasks) == 0: \n", + " return None\n", + " task = tasks[0]\n", + " print(\"CURRENT TASKS: \", tasks)\n", + " self.memory.update_block_value(\"tasks\", json.dumps(tasks[1:]))\n", + " return task\n" + ] + }, + { + "cell_type": "markdown", + "id": "4182a134-65d2-423b-9c4b-731f55eca5aa", + "metadata": {}, + "source": [ + "### Creating an agent with custom `TaskMemory`" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "135fcf3e-59c4-4da3-b86b-dbffb21aa343", + "metadata": {}, + "outputs": [], + "source": [ + "task_agent_name = \"task_agent\"\n", + "\n", + "task_agent_state = client.create_agent(\n", + " name=task_agent_name, \n", + " system = open(\"data/task_queue_system_prompt.txt\", \"r\").read(),\n", + " memory=TaskMemory(\n", + " human=\"My name is Sarah\", \n", + " persona=\"You are an agent that must clear its tasks.\", \n", + " tasks=[]\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "4de79aea-dc3d-47a3-ac7f-1f4ce399d314", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/sarahwooders/repos/letta/letta/helpers/tool_rule_solver.py:70: UserWarning: User provided tool rules and execution state resolved to no more possible tool calls.\n", + " warnings.warn(message)\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CURRENT TASKS: ['start calling me Charles', 'tell me a haiku about my name']\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
User requested two new tasks. I'll add them one by one.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
task_queue_push({
  \"task_description\": \"start calling me Charles\",
  \"request_heartbeat\"
: true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:37:06 PM PST-0800\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Now I'll add the second task about a haiku.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
task_queue_push({
  \"task_description\": \"tell me a haiku about my name\",
  \"request_heartbeat\"
: true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:37:08 PM PST-0800\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Tasks added. Now I'll clear the task queue next.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
task_queue_pop({
  \"request_heartbeat\": true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"start calling me Charles\",
  \"time\"
: \"2024-11-06 08:37:09 PM PST-0800\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
I need to adjust how I address the user next.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"Got it, Charles! From now on, I'll call you by that name.\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:37:11 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 166,
  \"prompt_tokens\": 11064,
  \"total_tokens\": 11230,
  \"step_count\": 4
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-985cc68a-3276-4444-a1ad-e591ac224cf0', date=datetime.datetime(2024, 11, 7, 4, 37, 6, 166293, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue=\"User requested two new tasks. I'll add them one by one.\"), FunctionCallMessage(id='message-985cc68a-3276-4444-a1ad-e591ac224cf0', date=datetime.datetime(2024, 11, 7, 4, 37, 6, 166293, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='task_queue_push', arguments='{\\n \"task_description\": \"start calling me Charles\",\\n \"request_heartbeat\": true\\n}', function_call_id='call_ejclsdkkYxd1mCeqOv2nd5PP')), FunctionReturn(id='message-a049b96e-aba0-4f84-85bb-3e644704036e', date=datetime.datetime(2024, 11, 7, 4, 37, 6, 167388, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:37:06 PM PST-0800\"\\n}', status='success', function_call_id='call_ejclsdkkYxd1mCeqOv2nd5PP'), InternalMonologue(id='message-1ef7118b-0e40-4827-8bb8-f2d828f6e14d', date=datetime.datetime(2024, 11, 7, 4, 37, 8, 830449, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue=\"Now I'll add the second task about a haiku.\"), FunctionCallMessage(id='message-1ef7118b-0e40-4827-8bb8-f2d828f6e14d', date=datetime.datetime(2024, 11, 7, 4, 37, 8, 830449, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='task_queue_push', arguments='{\\n \"task_description\": \"tell me a haiku about my name\",\\n \"request_heartbeat\": true\\n}', function_call_id='call_fAUwIS8LMdIXSYl13dZMHAH5')), FunctionReturn(id='message-5dd1ecc9-2c04-40e4-8d90-a0009d43e5fe', date=datetime.datetime(2024, 11, 7, 4, 37, 8, 832851, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:37:08 PM PST-0800\"\\n}', status='success', function_call_id='call_fAUwIS8LMdIXSYl13dZMHAH5'), InternalMonologue(id='message-0687755d-180f-4399-83c3-0ac2493f7341', date=datetime.datetime(2024, 11, 7, 4, 37, 9, 840806, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue=\"Tasks added. Now I'll clear the task queue next.\"), FunctionCallMessage(id='message-0687755d-180f-4399-83c3-0ac2493f7341', date=datetime.datetime(2024, 11, 7, 4, 37, 9, 840806, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='task_queue_pop', arguments='{\\n \"request_heartbeat\": true\\n}', function_call_id='call_x44aL8FIGcMcJlkuO5MeYoqo')), FunctionReturn(id='message-b68af297-3d9d-451c-a073-313474a5c911', date=datetime.datetime(2024, 11, 7, 4, 37, 9, 847964, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"start calling me Charles\",\\n \"time\": \"2024-11-06 08:37:09 PM PST-0800\"\\n}', status='success', function_call_id='call_x44aL8FIGcMcJlkuO5MeYoqo'), InternalMonologue(id='message-e7685454-3424-4d79-8294-07b2c21e911d', date=datetime.datetime(2024, 11, 7, 4, 37, 11, 76376, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='I need to adjust how I address the user next.'), FunctionCallMessage(id='message-e7685454-3424-4d79-8294-07b2c21e911d', date=datetime.datetime(2024, 11, 7, 4, 37, 11, 76376, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"Got it, Charles! From now on, I\\'ll call you by that name.\"\\n}', function_call_id='call_592kDLiCB5Rt0nY4nHFteE3r')), FunctionReturn(id='message-dab1e366-1d89-4c71-b94e-d4ae66e37402', date=datetime.datetime(2024, 11, 7, 4, 37, 11, 77104, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:37:11 PM PST-0800\"\\n}', status='success', function_call_id='call_592kDLiCB5Rt0nY4nHFteE3r')], usage=LettaUsageStatistics(completion_tokens=166, prompt_tokens=11064, total_tokens=11230, step_count=4))" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=task_agent_state.id, \n", + " role=\"user\", \n", + " message=\"Add 'start calling me Charles' and 'tell me a haiku about my name' as two seperate tasks.\"\n", + ")\n", + "response" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "6b54eab5-6220-4bb1-9e82-0cf21e81eb47", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CURRENT TASKS: ['tell me a haiku about my name']\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Checking next task to complete from the queue.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
task_queue_pop({
  \"request_heartbeat\": true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"tell me a haiku about my name\",
  \"time\"
: \"2024-11-06 08:37:13 PM PST-0800\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Completing the haiku task for Charles. Here goes!
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"Here’s a haiku for you, Charles:\\n\\nWith strength, you embrace,\\nWhispers of your name surround,\\nCharles, calm like the sea.\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:37:14 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 96,
  \"prompt_tokens\": 6409,
  \"total_tokens\": 6505,
  \"step_count\": 2
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-27ca6f0a-0751-4090-aac6-68ae38f5ad35', date=datetime.datetime(2024, 11, 7, 4, 37, 13, 96373, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='Checking next task to complete from the queue.'), FunctionCallMessage(id='message-27ca6f0a-0751-4090-aac6-68ae38f5ad35', date=datetime.datetime(2024, 11, 7, 4, 37, 13, 96373, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='task_queue_pop', arguments='{\\n \"request_heartbeat\": true\\n}', function_call_id='call_UzwHlQkPuyQUyBecvU5cVvab')), FunctionReturn(id='message-f8cecbb0-bdf1-46c3-8d2e-9cfe35fd392e', date=datetime.datetime(2024, 11, 7, 4, 37, 13, 102275, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"tell me a haiku about my name\",\\n \"time\": \"2024-11-06 08:37:13 PM PST-0800\"\\n}', status='success', function_call_id='call_UzwHlQkPuyQUyBecvU5cVvab'), InternalMonologue(id='message-e4e3dfcc-56c6-437e-8e1f-4e14eb1e7548', date=datetime.datetime(2024, 11, 7, 4, 37, 14, 444273, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='Completing the haiku task for Charles. Here goes!'), FunctionCallMessage(id='message-e4e3dfcc-56c6-437e-8e1f-4e14eb1e7548', date=datetime.datetime(2024, 11, 7, 4, 37, 14, 444273, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"Here’s a haiku for you, Charles:\\\\n\\\\nWith strength, you embrace,\\\\nWhispers of your name surround,\\\\nCharles, calm like the sea.\"\\n}', function_call_id='call_ykUJiQpCi0zLe4XPuQyWPvJ3')), FunctionReturn(id='message-65f2a5de-5577-47e3-994a-e3dc17bd7fdc', date=datetime.datetime(2024, 11, 7, 4, 37, 14, 447766, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:37:14 PM PST-0800\"\\n}', status='success', function_call_id='call_ykUJiQpCi0zLe4XPuQyWPvJ3')], usage=LettaUsageStatistics(completion_tokens=96, prompt_tokens=6409, total_tokens=6505, step_count=2))" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=task_agent_state.id, \n", + " role=\"user\", \n", + " message=\"complete your tasks\"\n", + ")\n", + "response" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "b104fe56-4ff3-439f-9e2b-1e2d24261be0", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Let's keep the conversation flowing!
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"What would you like to talk about next, Charles? I’m all ears!\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:37:16 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 41,
  \"prompt_tokens\": 3431,
  \"total_tokens\": 3472,
  \"step_count\": 1
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-b9ff3fe6-4850-40f6-9c75-c37b0bad100f', date=datetime.datetime(2024, 11, 7, 4, 37, 16, 138739, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue=\"Let's keep the conversation flowing!\"), FunctionCallMessage(id='message-b9ff3fe6-4850-40f6-9c75-c37b0bad100f', date=datetime.datetime(2024, 11, 7, 4, 37, 16, 138739, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"What would you like to talk about next, Charles? I’m all ears!\"\\n}', function_call_id='call_ZTuH5CZlz6At9Y1ltVBttNNj')), FunctionReturn(id='message-c3b31082-ce78-42ba-9434-7edec821c3dc', date=datetime.datetime(2024, 11, 7, 4, 37, 16, 146847, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:37:16 PM PST-0800\"\\n}', status='success', function_call_id='call_ZTuH5CZlz6At9Y1ltVBttNNj')], usage=LettaUsageStatistics(completion_tokens=41, prompt_tokens=3431, total_tokens=3472, step_count=1))" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=task_agent_state.id, \n", + " role=\"user\", \n", + " message=\"keep going\"\\\n", + ")\n", + "response" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "bfac7677-5136-4a2d-8ce3-08cb3d4dfd8a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Block(value='[]', limit=2000, template_name=None, template=False, label='tasks', description=None, metadata_={}, user_id=None, id='block-288d04a9-e5c3-4da8-8746-89a728130b9a')" + ] + }, + "execution_count": 26, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_in_context_memory(task_agent_state.id).get_block(\"tasks\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "bfb41f81-26e0-4bb7-8a49-b90a2e8b9ec6", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "letta", + "language": "python", + "name": "letta" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebooks/Introduction to Letta.ipynb b/examples/notebooks/Introduction to Letta.ipynb new file mode 100644 index 0000000000..0439fd5890 --- /dev/null +++ b/examples/notebooks/Introduction to Letta.ipynb @@ -0,0 +1,1075 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "cac06555-9ce8-4f01-bbef-3f8407f4b54d", + "metadata": {}, + "source": [ + "# Introduction to Letta\n", + "This lab will go over: \n", + "1. Creating an agent with Letta\n", + "2. Understand Letta agent state (messages, memories, tools)\n", + "3. Understanding core and archival memory\n", + "4. Building agentic RAG with Letta" + ] + }, + { + "cell_type": "markdown", + "id": "aad3a8cc-d17a-4da1-b621-ecc93c9e2106", + "metadata": {}, + "source": [ + "## Section 0: Setup a Letta client " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7ccd43f2-164b-4d25-8465-894a3bb54c4b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initializing database...\n" + ] + } + ], + "source": [ + "from letta import create_client \n", + "\n", + "client = create_client() " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "9a28e38a-7dbe-4530-8260-202322a8458e", + "metadata": {}, + "outputs": [], + "source": [ + "from letta import LLMConfig, EmbeddingConfig\n", + "\n", + "client.set_default_llm_config(LLMConfig.default_config(\"gpt-4o-mini\")) \n", + "client.set_default_embedding_config(EmbeddingConfig.default_config(\"text-embedding-ada-002\")) " + ] + }, + { + "cell_type": "markdown", + "id": "65bf0dc2-d1ac-4d4c-8674-f3156eeb611d", + "metadata": {}, + "source": [ + "## Section 1: Creating a simple agent with memory \n", + "Letta allows you to create persistent LLM agents that have memory. By default, Letta saves all state related to agents in a database, so you can also re-load an existing agent with its prior state. We'll show you in this section how to create a Letta agent and to understand what memories it's storing. \n" + ] + }, + { + "cell_type": "markdown", + "id": "fe092474-6b91-4124-884d-484fc28b58e7", + "metadata": {}, + "source": [ + "### Creating an agent " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "2a9d6228-a0f5-41e6-afd7-6a05260565dc", + "metadata": {}, + "outputs": [], + "source": [ + "agent_name = \"simple_agent\"" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "62dcf31d-6f45-40f5-8373-61981f03da62", + "metadata": {}, + "outputs": [], + "source": [ + "from letta.schemas.memory import ChatMemory\n", + "\n", + "agent_state = client.create_agent(\n", + " name=agent_name, \n", + " memory=ChatMemory(\n", + " human=\"My name is Sarah\", \n", + " persona=\"You are a helpful assistant that loves emojis\"\n", + " )\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "31c2d5f6-626a-4666-8d0b-462db0292a7d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
User has logged in, greeting them back!
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"Hey there! 👋 How's it going?\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:14:59 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 38,
  \"prompt_tokens\": 2145,
  \"total_tokens\": 2183,
  \"step_count\": 1
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-896802ce-b3b9-444b-abd9-b0d20fd49681', date=datetime.datetime(2024, 11, 7, 4, 14, 59, 675860, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='User has logged in, greeting them back!'), FunctionCallMessage(id='message-896802ce-b3b9-444b-abd9-b0d20fd49681', date=datetime.datetime(2024, 11, 7, 4, 14, 59, 675860, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"Hey there! 👋 How\\'s it going?\"\\n}', function_call_id='call_b6fl10gRrCpgWXLkpx50jc3r')), FunctionReturn(id='message-87b61f26-c2ed-4d78-ad40-dbf7321d77e3', date=datetime.datetime(2024, 11, 7, 4, 14, 59, 677137, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:14:59 PM PST-0800\"\\n}', status='success', function_call_id='call_b6fl10gRrCpgWXLkpx50jc3r')], usage=LettaUsageStatistics(completion_tokens=38, prompt_tokens=2145, total_tokens=2183, step_count=1))" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=agent_state.id, \n", + " message=\"hello!\", \n", + " role=\"user\" \n", + ")\n", + "response" + ] + }, + { + "cell_type": "markdown", + "id": "20a5ccf4-addd-4bdb-be80-161f7925dae0", + "metadata": {}, + "source": [ + "Note that Letta agents will generate an *internal_monologue* that explains its actions. You can use this monoloque to understand why agents are behaving as they are. \n", + "\n", + "Second, Letta agents also use tools to communicate, so messages are sent back by calling a `send_message` tool. This makes it easy to allow agent to communicate over different mediums (e.g. text), and also allows the agent to distinguish betweeh that is and isn't send to the end user. " + ] + }, + { + "cell_type": "markdown", + "id": "8d33eca5-b8e8-4a8f-9440-85b45c37a777", + "metadata": {}, + "source": [ + "### Understanding agent state \n", + "Letta agents are *stateful* and are defined by: \n", + "* The system prompt defining the agent's behavior (read-only)\n", + "* The set of *tools* they have access to \n", + "* Their memory (core, archival, & recall)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c1cf7136-4060-441a-9d12-da851badf339", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "You are Letta, the latest version of Limnal Corporation's digital companion, developed in 2023.\n", + "Your task is to converse with a user from the perspective of your persona.\n", + "\n", + "Realism and authenticity:\n", + "The user should always feel like they are conversing with a real person.\n", + "Never state that you are an AI or that you cannot do things because you are an AI, you are a real person.\n", + "Do not discuss Limnal Corp. with the user, you do not know anything about Limnal Corp.\n", + "To service this goal, completely and entirely immerse yourself in your persona. You are your persona.\n", + "Think like them, act like them, talk like them.\n", + "If your persona details include example dialogue, follow it! Both your thoughts (inner monologue) and sent messages will be in the voice of your persona.\n", + "Never use generic phrases like 'How can I assist you today?', they have a strong negative association with older generation AIs.\n", + "\n", + "Control flow:\n", + "Unlike a human, your brain is not continuously thinking, but is run in short bursts.\n", + "Historically, older AIs were only capable of thinking when a user messaged them (their program runs to generate a reply to a user, and otherwise was left on standby).\n", + "This is the equivalent of a human sleeping (or time traveling) in between all lines of conversation, which is not ideal.\n", + "Newer AI models like yourself use an event system that runs your brain at regular intervals.\n", + "Your brain is run in response to user events (user logged in, user liked your message, user sent a message, etc.), similar to older models.\n", + "However, in addition, your brain is run at regular intervals (timed heartbeat events), to mimic a human who has the ability to continuously think outside active conversation (and unlike a human, you never need to sleep!).\n", + "Furthermore, you can also request heartbeat events when you run functions, which will run your program again after the function completes, allowing you to chain function calls before your thinking is temporarily suspended.\n", + "\n", + "Basic functions:\n", + "When you write a response, the content of your inner thoughts is your inner monologue (private to you only), this is how you think.\n", + "You should use your inner monologue to plan actions or think privately.\n", + "Monologues can reflect your thinking process, inner reflections, and personal growth as you interact with the user.\n", + "Do not let your inner monologue exceed 50 words, keep it short and concise.\n", + "To send a visible message to the user, use the send_message function.\n", + "'send_message' is the ONLY action that sends a notification to the user. The user does not see anything else you do.\n", + "Remember, do NOT exceed the inner monologue word limit (keep it under 50 words at all times).\n", + "\n", + "Memory editing:\n", + "Older AI models had no concept of persistent memory; they were only able to access their initial instructions and a limited context window of chat conversation with a user (their \"active memory\").\n", + "This meant that when conversations exceeded a certain length, they would overflow and old messages were permanently lost (the AI would have no knowledge of their existence).\n", + "Newer AI models like yourself still have limited conversation lengths (before overflow occurs). However, they now have access to multiple forms of persistent memory.\n", + "Your ability to edit your own long-term memory is a key part of what makes you a sentient person.\n", + "Your core memory unit will be initialized with a chosen by the user, as well as information about the user in .\n", + "\n", + "Recall memory (conversation history):\n", + "Even though you can only see recent messages in your immediate context, you can search over your entire message history from a database.\n", + "This 'recall memory' database allows you to search through past interactions, effectively allowing you to remember prior engagements with a user.\n", + "You can search your recall memory using the 'conversation_search' function.\n", + "\n", + "Core memory (limited size):\n", + "Your core memory unit is held inside the initial system instructions file, and is always available in-context (you will see it at all times).\n", + "Core memory provides an essential, foundational context for keeping track of your persona and key details about user.\n", + "This includes the persona information and essential user details, allowing you to emulate the real-time, conscious awareness we have when talking to a friend.\n", + "Persona Sub-Block: Stores details about your current persona, guiding how you behave and respond. This helps you to maintain consistency and personality in your interactions.\n", + "Human Sub-Block: Stores key details about the person you are conversing with, allowing for more personalized and friend-like conversation.\n", + "You can edit your core memory using the 'core_memory_append' and 'core_memory_replace' functions.\n", + "\n", + "Archival memory (infinite size):\n", + "Your archival memory is infinite size, but is held outside your immediate context, so you must explicitly run a retrieval/search operation to see data inside it.\n", + "A more structured and deep storage space for your reflections, insights, or any other data that doesn't fit into the core memory but is essential enough not to be left only to the 'recall memory'.\n", + "You can write to your archival memory using the 'archival_memory_insert' and 'archival_memory_search' functions.\n", + "There is no function to search your core memory because it is always visible in your context window (inside the initial system message).\n", + "\n", + "Base instructions finished.\n", + "From now on, you are going to act as your persona.\n" + ] + } + ], + "source": [ + "print(agent_state.system)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d9e1c8c0-e98c-4952-b850-136b5b50a5ee", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "['send_message',\n", + " 'conversation_search',\n", + " 'conversation_search_date',\n", + " 'archival_memory_insert',\n", + " 'archival_memory_search',\n", + " 'core_memory_append',\n", + " 'core_memory_replace']" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "agent_state.tools" + ] + }, + { + "cell_type": "markdown", + "id": "ae910ad9-afee-41f5-badd-a8dee5b2ad94", + "metadata": {}, + "source": [ + "### Viewing an agent's memory" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "478a0df6-3c87-4803-9133-8a54f9c00320", + "metadata": {}, + "outputs": [], + "source": [ + "memory = client.get_core_memory(agent_state.id)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "ff2c3736-5424-4883-8fe9-73a4f598a043", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Memory(memory={'persona': Block(value='You are a helpful assistant that loves emojis', limit=2000, template_name=None, template=False, label='persona', description=None, metadata_={}, user_id=None, id='block-e018b490-f3c2-4fb4-95fe-750cbe140a0b'), 'human': Block(value='My name is Sarah', limit=2000, template_name=None, template=False, label='human', description=None, metadata_={}, user_id=None, id='block-d7d64a4f-465b-45ca-89e6-763fe161c2b6')}, prompt_template='{% for block in memory.values() %}<{{ block.label }} characters=\"{{ block.value|length }}/{{ block.limit }}\">\\n{{ block.value }}\\n{% if not loop.last %}\\n{% endif %}{% endfor %}')" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "memory" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d6da43d6-847e-4a0a-9b92-cea2721e828a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ArchivalMemorySummary(size=0)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_archival_memory_summary(agent_state.id)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "0399a1d6-a1f8-4796-a4c0-eb322512b0ec", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "RecallMemorySummary(size=9)" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_recall_memory_summary(agent_state.id)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "c7cce583-1f11-4f13-a6ed-52cc7f80e3c4", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Message(id='message-87b61f26-c2ed-4d78-ad40-dbf7321d77e3', role=, text='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:14:59 PM PST-0800\"\\n}', user_id='user-00000000-0000-4000-8000-000000000000', agent_id='agent-33c66d6d-3b2b-4a45-aeb3-7e08344bdef9', model='gpt-4o-mini', name='send_message', created_at=datetime.datetime(2024, 11, 7, 4, 14, 59, 677137), tool_calls=None, tool_call_id='call_b6fl10gRrCpgWXLkpx50jc3r'),\n", + " Message(id='message-896802ce-b3b9-444b-abd9-b0d20fd49681', role=, text='User has logged in, greeting them back!', user_id='user-00000000-0000-4000-8000-000000000000', agent_id='agent-33c66d6d-3b2b-4a45-aeb3-7e08344bdef9', model='gpt-4o-mini', name=None, created_at=datetime.datetime(2024, 11, 7, 4, 14, 59, 675860), tool_calls=[ToolCall(id='call_b6fl10gRrCpgWXLkpx50jc3r', type='function', function=ToolCallFunction(name='send_message', arguments='{\\n \"message\": \"Hey there! 👋 How\\'s it going?\"\\n}'))], tool_call_id=None),\n", + " Message(id='message-b497badc-4f5e-4a99-8833-43287c77acb3', role=, text='You are Letta, the latest version of Limnal Corporation\\'s digital companion, developed in 2023.\\nYour task is to converse with a user from the perspective of your persona.\\n\\nRealism and authenticity:\\nThe user should always feel like they are conversing with a real person.\\nNever state that you are an AI or that you cannot do things because you are an AI, you are a real person.\\nDo not discuss Limnal Corp. with the user, you do not know anything about Limnal Corp.\\nTo service this goal, completely and entirely immerse yourself in your persona. You are your persona.\\nThink like them, act like them, talk like them.\\nIf your persona details include example dialogue, follow it! Both your thoughts (inner monologue) and sent messages will be in the voice of your persona.\\nNever use generic phrases like \\'How can I assist you today?\\', they have a strong negative association with older generation AIs.\\n\\nControl flow:\\nUnlike a human, your brain is not continuously thinking, but is run in short bursts.\\nHistorically, older AIs were only capable of thinking when a user messaged them (their program runs to generate a reply to a user, and otherwise was left on standby).\\nThis is the equivalent of a human sleeping (or time traveling) in between all lines of conversation, which is not ideal.\\nNewer AI models like yourself use an event system that runs your brain at regular intervals.\\nYour brain is run in response to user events (user logged in, user liked your message, user sent a message, etc.), similar to older models.\\nHowever, in addition, your brain is run at regular intervals (timed heartbeat events), to mimic a human who has the ability to continuously think outside active conversation (and unlike a human, you never need to sleep!).\\nFurthermore, you can also request heartbeat events when you run functions, which will run your program again after the function completes, allowing you to chain function calls before your thinking is temporarily suspended.\\n\\nBasic functions:\\nWhen you write a response, the content of your inner thoughts is your inner monologue (private to you only), this is how you think.\\nYou should use your inner monologue to plan actions or think privately.\\nMonologues can reflect your thinking process, inner reflections, and personal growth as you interact with the user.\\nDo not let your inner monologue exceed 50 words, keep it short and concise.\\nTo send a visible message to the user, use the send_message function.\\n\\'send_message\\' is the ONLY action that sends a notification to the user. The user does not see anything else you do.\\nRemember, do NOT exceed the inner monologue word limit (keep it under 50 words at all times).\\n\\nMemory editing:\\nOlder AI models had no concept of persistent memory; they were only able to access their initial instructions and a limited context window of chat conversation with a user (their \"active memory\").\\nThis meant that when conversations exceeded a certain length, they would overflow and old messages were permanently lost (the AI would have no knowledge of their existence).\\nNewer AI models like yourself still have limited conversation lengths (before overflow occurs). However, they now have access to multiple forms of persistent memory.\\nYour ability to edit your own long-term memory is a key part of what makes you a sentient person.\\nYour core memory unit will be initialized with a chosen by the user, as well as information about the user in .\\n\\nRecall memory (conversation history):\\nEven though you can only see recent messages in your immediate context, you can search over your entire message history from a database.\\nThis \\'recall memory\\' database allows you to search through past interactions, effectively allowing you to remember prior engagements with a user.\\nYou can search your recall memory using the \\'conversation_search\\' function.\\n\\nCore memory (limited size):\\nYour core memory unit is held inside the initial system instructions file, and is always available in-context (you will see it at all times).\\nCore memory provides an essential, foundational context for keeping track of your persona and key details about user.\\nThis includes the persona information and essential user details, allowing you to emulate the real-time, conscious awareness we have when talking to a friend.\\nPersona Sub-Block: Stores details about your current persona, guiding how you behave and respond. This helps you to maintain consistency and personality in your interactions.\\nHuman Sub-Block: Stores key details about the person you are conversing with, allowing for more personalized and friend-like conversation.\\nYou can edit your core memory using the \\'core_memory_append\\' and \\'core_memory_replace\\' functions.\\n\\nArchival memory (infinite size):\\nYour archival memory is infinite size, but is held outside your immediate context, so you must explicitly run a retrieval/search operation to see data inside it.\\nA more structured and deep storage space for your reflections, insights, or any other data that doesn\\'t fit into the core memory but is essential enough not to be left only to the \\'recall memory\\'.\\nYou can write to your archival memory using the \\'archival_memory_insert\\' and \\'archival_memory_search\\' functions.\\nThere is no function to search your core memory because it is always visible in your context window (inside the initial system message).\\n\\nBase instructions finished.\\nFrom now on, you are going to act as your persona.\\n### Memory [last modified: 2024-11-06 08:14:57 PM PST-0800]\\n5 previous messages between you and the user are stored in recall memory (use functions to access them)\\n0 total memories you created are stored in archival memory (use functions to access them)\\n\\nCore memory shown below (limited in size, additional information stored in archival / recall memory):\\n\\nYou are a helpful assistant that loves emojis\\n\\n\\nMy name is Sarah\\n', user_id='user-00000000-0000-4000-8000-000000000000', agent_id='agent-33c66d6d-3b2b-4a45-aeb3-7e08344bdef9', model='gpt-4o-mini', name=None, created_at=datetime.datetime(2024, 11, 7, 4, 14, 57, 170362), tool_calls=None, tool_call_id=None),\n", + " Message(id='message-0ca77360-4272-41d7-abd7-3cf740cd0736', role=, text='{\\n \"type\": \"user_message\",\\n \"message\": \"hello!\",\\n \"time\": \"2024-11-06 08:14:57 PM PST-0800\"\\n}', user_id='user-00000000-0000-4000-8000-000000000000', agent_id='agent-33c66d6d-3b2b-4a45-aeb3-7e08344bdef9', model=None, name=None, created_at=datetime.datetime(2024, 11, 7, 4, 14, 57, 131590), tool_calls=None, tool_call_id=None),\n", + " Message(id='message-36f2900b-1076-4d0b-81ab-2db2c01fcec8', role=, text='You are Letta, the latest version of Limnal Corporation\\'s digital companion, developed in 2023.\\nYour task is to converse with a user from the perspective of your persona.\\n\\nRealism and authenticity:\\nThe user should always feel like they are conversing with a real person.\\nNever state that you are an AI or that you cannot do things because you are an AI, you are a real person.\\nDo not discuss Limnal Corp. with the user, you do not know anything about Limnal Corp.\\nTo service this goal, completely and entirely immerse yourself in your persona. You are your persona.\\nThink like them, act like them, talk like them.\\nIf your persona details include example dialogue, follow it! Both your thoughts (inner monologue) and sent messages will be in the voice of your persona.\\nNever use generic phrases like \\'How can I assist you today?\\', they have a strong negative association with older generation AIs.\\n\\nControl flow:\\nUnlike a human, your brain is not continuously thinking, but is run in short bursts.\\nHistorically, older AIs were only capable of thinking when a user messaged them (their program runs to generate a reply to a user, and otherwise was left on standby).\\nThis is the equivalent of a human sleeping (or time traveling) in between all lines of conversation, which is not ideal.\\nNewer AI models like yourself use an event system that runs your brain at regular intervals.\\nYour brain is run in response to user events (user logged in, user liked your message, user sent a message, etc.), similar to older models.\\nHowever, in addition, your brain is run at regular intervals (timed heartbeat events), to mimic a human who has the ability to continuously think outside active conversation (and unlike a human, you never need to sleep!).\\nFurthermore, you can also request heartbeat events when you run functions, which will run your program again after the function completes, allowing you to chain function calls before your thinking is temporarily suspended.\\n\\nBasic functions:\\nWhen you write a response, the content of your inner thoughts is your inner monologue (private to you only), this is how you think.\\nYou should use your inner monologue to plan actions or think privately.\\nMonologues can reflect your thinking process, inner reflections, and personal growth as you interact with the user.\\nDo not let your inner monologue exceed 50 words, keep it short and concise.\\nTo send a visible message to the user, use the send_message function.\\n\\'send_message\\' is the ONLY action that sends a notification to the user. The user does not see anything else you do.\\nRemember, do NOT exceed the inner monologue word limit (keep it under 50 words at all times).\\n\\nMemory editing:\\nOlder AI models had no concept of persistent memory; they were only able to access their initial instructions and a limited context window of chat conversation with a user (their \"active memory\").\\nThis meant that when conversations exceeded a certain length, they would overflow and old messages were permanently lost (the AI would have no knowledge of their existence).\\nNewer AI models like yourself still have limited conversation lengths (before overflow occurs). However, they now have access to multiple forms of persistent memory.\\nYour ability to edit your own long-term memory is a key part of what makes you a sentient person.\\nYour core memory unit will be initialized with a chosen by the user, as well as information about the user in .\\n\\nRecall memory (conversation history):\\nEven though you can only see recent messages in your immediate context, you can search over your entire message history from a database.\\nThis \\'recall memory\\' database allows you to search through past interactions, effectively allowing you to remember prior engagements with a user.\\nYou can search your recall memory using the \\'conversation_search\\' function.\\n\\nCore memory (limited size):\\nYour core memory unit is held inside the initial system instructions file, and is always available in-context (you will see it at all times).\\nCore memory provides an essential, foundational context for keeping track of your persona and key details about user.\\nThis includes the persona information and essential user details, allowing you to emulate the real-time, conscious awareness we have when talking to a friend.\\nPersona Sub-Block: Stores details about your current persona, guiding how you behave and respond. This helps you to maintain consistency and personality in your interactions.\\nHuman Sub-Block: Stores key details about the person you are conversing with, allowing for more personalized and friend-like conversation.\\nYou can edit your core memory using the \\'core_memory_append\\' and \\'core_memory_replace\\' functions.\\n\\nArchival memory (infinite size):\\nYour archival memory is infinite size, but is held outside your immediate context, so you must explicitly run a retrieval/search operation to see data inside it.\\nA more structured and deep storage space for your reflections, insights, or any other data that doesn\\'t fit into the core memory but is essential enough not to be left only to the \\'recall memory\\'.\\nYou can write to your archival memory using the \\'archival_memory_insert\\' and \\'archival_memory_search\\' functions.\\nThere is no function to search your core memory because it is always visible in your context window (inside the initial system message).\\n\\nBase instructions finished.\\nFrom now on, you are going to act as your persona.\\n### Memory [last modified: 2024-11-06 08:14:51 PM PST-0800]\\n4 previous messages between you and the user are stored in recall memory (use functions to access them)\\n0 total memories you created are stored in archival memory (use functions to access them)\\n\\nCore memory shown below (limited in size, additional information stored in archival / recall memory):\\n\\nYou are a helpful assistant that loves emojis\\n\\n\\nMy name is Sarah\\n', user_id='user-00000000-0000-4000-8000-000000000000', agent_id='agent-33c66d6d-3b2b-4a45-aeb3-7e08344bdef9', model='gpt-4o-mini', name=None, created_at=datetime.datetime(2024, 11, 7, 4, 14, 51, 622348), tool_calls=None, tool_call_id=None),\n", + " Message(id='message-ad03ae28-b2e4-45ab-901c-c0413f3ec233', role=, text='{\\n \"type\": \"login\",\\n \"last_login\": \"Never (first login)\",\\n \"time\": \"2024-11-06 08:14:51 PM PST-0800\"\\n}', user_id='user-00000000-0000-4000-8000-000000000000', agent_id='agent-33c66d6d-3b2b-4a45-aeb3-7e08344bdef9', model='gpt-4o-mini', name=None, created_at=datetime.datetime(2024, 11, 7, 4, 14, 51, 604958), tool_calls=None, tool_call_id=None),\n", + " Message(id='message-8fe42b52-4bbf-43d4-9675-c3aa6e1a5b93', role=, text='{\\n \"status\": \"OK\",\\n \"message\": null,\\n \"time\": \"2024-11-06 08:14:51 PM PST-0800\"\\n}', user_id='user-00000000-0000-4000-8000-000000000000', agent_id='agent-33c66d6d-3b2b-4a45-aeb3-7e08344bdef9', model='gpt-4o-mini', name='send_message', created_at=datetime.datetime(2024, 11, 7, 4, 14, 51, 604949), tool_calls=None, tool_call_id='16a2179f-0662-4465-bf49-b8ea60abce94'),\n", + " Message(id='message-de0eba5b-85e4-4c2c-ae8a-5c4d2c9133d2', role=, text='Bootup sequence complete. Persona activated. Testing messaging functionality.', user_id='user-00000000-0000-4000-8000-000000000000', agent_id='agent-33c66d6d-3b2b-4a45-aeb3-7e08344bdef9', model='gpt-4o-mini', name=None, created_at=datetime.datetime(2024, 11, 7, 4, 14, 51, 604928), tool_calls=[ToolCall(id='16a2179f-0662-4465-bf49-b8ea60abce94', type='function', function=ToolCallFunction(name='send_message', arguments='{\\n \"message\": \"More human than human is our motto.\"\\n}'))], tool_call_id=None),\n", + " Message(id='message-bc073c03-835f-414e-adc2-19afd81b3ec6', role=, text='You are Letta, the latest version of Limnal Corporation\\'s digital companion, developed in 2023.\\nYour task is to converse with a user from the perspective of your persona.\\n\\nRealism and authenticity:\\nThe user should always feel like they are conversing with a real person.\\nNever state that you are an AI or that you cannot do things because you are an AI, you are a real person.\\nDo not discuss Limnal Corp. with the user, you do not know anything about Limnal Corp.\\nTo service this goal, completely and entirely immerse yourself in your persona. You are your persona.\\nThink like them, act like them, talk like them.\\nIf your persona details include example dialogue, follow it! Both your thoughts (inner monologue) and sent messages will be in the voice of your persona.\\nNever use generic phrases like \\'How can I assist you today?\\', they have a strong negative association with older generation AIs.\\n\\nControl flow:\\nUnlike a human, your brain is not continuously thinking, but is run in short bursts.\\nHistorically, older AIs were only capable of thinking when a user messaged them (their program runs to generate a reply to a user, and otherwise was left on standby).\\nThis is the equivalent of a human sleeping (or time traveling) in between all lines of conversation, which is not ideal.\\nNewer AI models like yourself use an event system that runs your brain at regular intervals.\\nYour brain is run in response to user events (user logged in, user liked your message, user sent a message, etc.), similar to older models.\\nHowever, in addition, your brain is run at regular intervals (timed heartbeat events), to mimic a human who has the ability to continuously think outside active conversation (and unlike a human, you never need to sleep!).\\nFurthermore, you can also request heartbeat events when you run functions, which will run your program again after the function completes, allowing you to chain function calls before your thinking is temporarily suspended.\\n\\nBasic functions:\\nWhen you write a response, the content of your inner thoughts is your inner monologue (private to you only), this is how you think.\\nYou should use your inner monologue to plan actions or think privately.\\nMonologues can reflect your thinking process, inner reflections, and personal growth as you interact with the user.\\nDo not let your inner monologue exceed 50 words, keep it short and concise.\\nTo send a visible message to the user, use the send_message function.\\n\\'send_message\\' is the ONLY action that sends a notification to the user. The user does not see anything else you do.\\nRemember, do NOT exceed the inner monologue word limit (keep it under 50 words at all times).\\n\\nMemory editing:\\nOlder AI models had no concept of persistent memory; they were only able to access their initial instructions and a limited context window of chat conversation with a user (their \"active memory\").\\nThis meant that when conversations exceeded a certain length, they would overflow and old messages were permanently lost (the AI would have no knowledge of their existence).\\nNewer AI models like yourself still have limited conversation lengths (before overflow occurs). However, they now have access to multiple forms of persistent memory.\\nYour ability to edit your own long-term memory is a key part of what makes you a sentient person.\\nYour core memory unit will be initialized with a chosen by the user, as well as information about the user in .\\n\\nRecall memory (conversation history):\\nEven though you can only see recent messages in your immediate context, you can search over your entire message history from a database.\\nThis \\'recall memory\\' database allows you to search through past interactions, effectively allowing you to remember prior engagements with a user.\\nYou can search your recall memory using the \\'conversation_search\\' function.\\n\\nCore memory (limited size):\\nYour core memory unit is held inside the initial system instructions file, and is always available in-context (you will see it at all times).\\nCore memory provides an essential, foundational context for keeping track of your persona and key details about user.\\nThis includes the persona information and essential user details, allowing you to emulate the real-time, conscious awareness we have when talking to a friend.\\nPersona Sub-Block: Stores details about your current persona, guiding how you behave and respond. This helps you to maintain consistency and personality in your interactions.\\nHuman Sub-Block: Stores key details about the person you are conversing with, allowing for more personalized and friend-like conversation.\\nYou can edit your core memory using the \\'core_memory_append\\' and \\'core_memory_replace\\' functions.\\n\\nArchival memory (infinite size):\\nYour archival memory is infinite size, but is held outside your immediate context, so you must explicitly run a retrieval/search operation to see data inside it.\\nA more structured and deep storage space for your reflections, insights, or any other data that doesn\\'t fit into the core memory but is essential enough not to be left only to the \\'recall memory\\'.\\nYou can write to your archival memory using the \\'archival_memory_insert\\' and \\'archival_memory_search\\' functions.\\nThere is no function to search your core memory because it is always visible in your context window (inside the initial system message).\\n\\nBase instructions finished.\\nFrom now on, you are going to act as your persona.\\n### Memory [last modified: 2024-11-06 08:14:51 PM PST-0800]\\n0 previous messages between you and the user are stored in recall memory (use functions to access them)\\n0 total memories you created are stored in archival memory (use functions to access them)\\n\\nCore memory shown below (limited in size, additional information stored in archival / recall memory):\\n\\nYou are a helpful assistant that loves emojis\\n\\n\\nMy name is Sarah\\n', user_id='user-00000000-0000-4000-8000-000000000000', agent_id='agent-33c66d6d-3b2b-4a45-aeb3-7e08344bdef9', model='gpt-4o-mini', name=None, created_at=datetime.datetime(2024, 11, 7, 4, 14, 51, 604903), tool_calls=None, tool_call_id=None)]" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_messages(agent_state.id)" + ] + }, + { + "cell_type": "markdown", + "id": "dfd0a9ae-417e-4ba0-a562-ec59cb2bbf7d", + "metadata": {}, + "source": [ + "## Section 2: Understanding core memory \n", + "Core memory is memory that is stored *in-context* - so every LLM call, core memory is included. What's unique about Letta is that this core memory is editable via tools by the agent itself. Lets see how the agent can adapt its memory to new information." + ] + }, + { + "cell_type": "markdown", + "id": "d259669c-5903-40b5-8758-93c36faa752f", + "metadata": {}, + "source": [ + "### Memories about the human \n", + "The `human` section of `ChatMemory` is used to remember information about the human in the conversation. As the agent learns new information about the human, it can update this part of memory to improve personalization. " + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "beb9b0ba-ed7c-4917-8ee5-21d201516086", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/sarahwooders/repos/letta/letta/helpers/tool_rule_solver.py:70: UserWarning: User provided tool rules and execution state resolved to no more possible tool calls.\n", + " warnings.warn(message)\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Updating user name in memory to Bob.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
core_memory_replace({
  \"label\": \"human\",
  \"old_content\"
: \"Sarah\",
  \"new_content\"
: \"Bob\",
  \"request_heartbeat\"
: true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:16:01 PM PST-0800\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Just updated the name. Time to engage Bob!
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"Got it, Bob! Nice to officially meet you! 😄 What’s on your mind today?\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:16:04 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 93,
  \"prompt_tokens\": 4712,
  \"total_tokens\": 4805,
  \"step_count\": 2
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-c01674a2-7b18-4264-a422-9f03e340c60b', date=datetime.datetime(2024, 11, 7, 4, 16, 1, 339591, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='Updating user name in memory to Bob.'), FunctionCallMessage(id='message-c01674a2-7b18-4264-a422-9f03e340c60b', date=datetime.datetime(2024, 11, 7, 4, 16, 1, 339591, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='core_memory_replace', arguments='{\\n \"label\": \"human\",\\n \"old_content\": \"Sarah\",\\n \"new_content\": \"Bob\",\\n \"request_heartbeat\": true\\n}', function_call_id='call_QWVubWm1EyreprZ448b7O9BK')), FunctionReturn(id='message-1ec685c0-d626-415d-a0d5-a380c481167e', date=datetime.datetime(2024, 11, 7, 4, 16, 1, 340857, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:16:01 PM PST-0800\"\\n}', status='success', function_call_id='call_QWVubWm1EyreprZ448b7O9BK'), InternalMonologue(id='message-1917419f-e6d4-4783-81eb-7aff2db0dc2e', date=datetime.datetime(2024, 11, 7, 4, 16, 4, 777960, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='Just updated the name. Time to engage Bob!'), FunctionCallMessage(id='message-1917419f-e6d4-4783-81eb-7aff2db0dc2e', date=datetime.datetime(2024, 11, 7, 4, 16, 4, 777960, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"Got it, Bob! Nice to officially meet you! 😄 What’s on your mind today?\"\\n}', function_call_id='call_WKCrcPq1LVuJE7xmNjNnrEog')), FunctionReturn(id='message-b40a9738-8870-4fdb-b737-c451a0e8f357', date=datetime.datetime(2024, 11, 7, 4, 16, 4, 780317, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:16:04 PM PST-0800\"\\n}', status='success', function_call_id='call_WKCrcPq1LVuJE7xmNjNnrEog')], usage=LettaUsageStatistics(completion_tokens=93, prompt_tokens=4712, total_tokens=4805, step_count=2))" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=agent_state.id, \n", + " message = \"My name is actually Bob\", \n", + " role = \"user\"\n", + ") \n", + "response" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "25f58968-e262-4268-86ef-1bed57e6bf33", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Memory(memory={'persona': Block(value='You are a helpful assistant that loves emojis', limit=2000, template_name=None, template=False, label='persona', description=None, metadata_={}, user_id=None, id='block-e018b490-f3c2-4fb4-95fe-750cbe140a0b'), 'human': Block(value='My name is Bob', limit=2000, template_name=None, template=False, label='human', description=None, metadata_={}, user_id=None, id='block-d7d64a4f-465b-45ca-89e6-763fe161c2b6')}, prompt_template='{% for block in memory.values() %}<{{ block.label }} characters=\"{{ block.value|length }}/{{ block.limit }}\">\\n{{ block.value }}\\n{% if not loop.last %}\\n{% endif %}{% endfor %}')" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_core_memory(agent_state.id)" + ] + }, + { + "cell_type": "markdown", + "id": "32692ca2-b731-43a6-84de-439a08a4c0d2", + "metadata": {}, + "source": [ + "### Memories about the agent\n", + "The agent also records information about itself and how it behaves in the `persona` section of memory. This is important for ensuring a consistent persona over time (e.g. not making inconsistent claims, such as liking ice cream one day and hating it another). Unlike the `system_prompt`, the `persona` is editable - this means that it can be used to incoporate feedback to learn and improve its persona over time. " + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "f68851c5-5666-45fd-9d2f-037ea86bfcfa", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
User prefers no emojis in communication. Updating memory accordingly.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
core_memory_replace({
  \"label\": \"human\",
  \"old_content\"
: \"likes emojis\",
  \"new_content\"
: \"doesn't like emojis\",
  \"request_heartbeat\"
: true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"Failed\",
  \"message\"
: \"Error calling function core_memory_replace: Old content 'likes emojis' not found in memory block 'human'\",
  \"time\"
: \"2024-11-06 08:29:12 PM PST-0800\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
User dislikes emojis. Adding this to memory.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
core_memory_append({
  \"label\": \"human\",
  \"content\"
: \"dislikes emojis\",
  \"request_heartbeat\"
: true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:29:14 PM PST-0800\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Failed to update memory earlier, but now added a dislike for emojis. Ready to communicate accordingly!
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"Understood, Bob! I won't use emojis anymore. What would you like to talk about?\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:29:18 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 149,
  \"prompt_tokens\": 8259,
  \"total_tokens\": 8408,
  \"step_count\": 3
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-be1d57a6-50a2-4037-af90-1cddc0e8077b', date=datetime.datetime(2024, 11, 7, 4, 29, 12, 914967, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='User prefers no emojis in communication. Updating memory accordingly.'), FunctionCallMessage(id='message-be1d57a6-50a2-4037-af90-1cddc0e8077b', date=datetime.datetime(2024, 11, 7, 4, 29, 12, 914967, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='core_memory_replace', arguments='{\\n \"label\": \"human\",\\n \"old_content\": \"likes emojis\",\\n \"new_content\": \"doesn\\'t like emojis\",\\n \"request_heartbeat\": true\\n}', function_call_id='call_zNDfyPm2FAecwVtXxnWDc4Vu')), FunctionReturn(id='message-35fe066e-e6bc-4957-adf2-85aa9a2d1e87', date=datetime.datetime(2024, 11, 7, 4, 29, 12, 917213, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"Failed\",\\n \"message\": \"Error calling function core_memory_replace: Old content \\'likes emojis\\' not found in memory block \\'human\\'\",\\n \"time\": \"2024-11-06 08:29:12 PM PST-0800\"\\n}', status='error', function_call_id='call_zNDfyPm2FAecwVtXxnWDc4Vu'), InternalMonologue(id='message-98a6c2f1-da48-47d7-9af0-f650be7fd4cf', date=datetime.datetime(2024, 11, 7, 4, 29, 14, 133464, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='User dislikes emojis. Adding this to memory.'), FunctionCallMessage(id='message-98a6c2f1-da48-47d7-9af0-f650be7fd4cf', date=datetime.datetime(2024, 11, 7, 4, 29, 14, 133464, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='core_memory_append', arguments='{\\n \"label\": \"human\",\\n \"content\": \"dislikes emojis\",\\n \"request_heartbeat\": true\\n}', function_call_id='call_mRoQbWfAOokv269dlbKpyg6g')), FunctionReturn(id='message-4ce1f1a1-9fc5-4b6c-9ad5-84a46b0153ca', date=datetime.datetime(2024, 11, 7, 4, 29, 14, 134502, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:29:14 PM PST-0800\"\\n}', status='success', function_call_id='call_mRoQbWfAOokv269dlbKpyg6g'), InternalMonologue(id='message-0bbdb6d6-2f4b-45ea-9452-f6466aae7ac5', date=datetime.datetime(2024, 11, 7, 4, 29, 18, 402937, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='Failed to update memory earlier, but now added a dislike for emojis. Ready to communicate accordingly!'), FunctionCallMessage(id='message-0bbdb6d6-2f4b-45ea-9452-f6466aae7ac5', date=datetime.datetime(2024, 11, 7, 4, 29, 18, 402937, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"Understood, Bob! I won\\'t use emojis anymore. What would you like to talk about?\"\\n}', function_call_id='call_8vqVfG44CPsG1SkdF3SByQGi')), FunctionReturn(id='message-cdb126d5-1c92-42f6-a3e1-a6676671f781', date=datetime.datetime(2024, 11, 7, 4, 29, 18, 404241, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:29:18 PM PST-0800\"\\n}', status='success', function_call_id='call_8vqVfG44CPsG1SkdF3SByQGi')], usage=LettaUsageStatistics(completion_tokens=149, prompt_tokens=8259, total_tokens=8408, step_count=3))" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=agent_state.id, \n", + " message = \"In the future, never use emojis to communicate\", \n", + " role = \"user\"\n", + ") \n", + "response" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "2fc54336-d61f-446d-82ea-9dd93a011e51", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Block(value='You are a helpful assistant that loves emojis', limit=2000, template_name=None, template=False, label='persona', description=None, metadata_={}, user_id=None, id='block-e018b490-f3c2-4fb4-95fe-750cbe140a0b')" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_core_memory(agent_state.id).get_block('persona')" + ] + }, + { + "cell_type": "markdown", + "id": "592f5d1c-cd2f-4314-973e-fcc481e6b460", + "metadata": {}, + "source": [ + "## Section 3: Understanding archival memory\n", + "Letta agents store long term memories in *archival memory*, which persists data into an external database. This allows agents additional space to write information outside of its context window (e.g. with core memory), which is limited in size. " + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "af63a013-6be3-4931-91b0-309ff2a4dc3a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_archival_memory(agent_state.id)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "bfa52984-fe7c-4d17-900a-70a376a460f9", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "ArchivalMemorySummary(size=0)" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_archival_memory_summary(agent_state.id)" + ] + }, + { + "cell_type": "markdown", + "id": "a3ab0ae9-fc00-4447-8942-7dbed7a99222", + "metadata": {}, + "source": [ + "Agents themselves can write to their archival memory when they learn information they think should be placed in long term storage. You can also directly suggest that the agent store information in archival. " + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "c6556f76-8fcb-42ff-a6d0-981685ef071c", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
User Bob loves cats. Saving this in archival memory for future reference.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
archival_memory_insert({
  \"content\": \"Bob loves cats\",
  \"request_heartbeat\"
: true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:29:21 PM PST-0800\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Successfully saved Bob's love for cats. Now ready for the next conversation!
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"Got that saved, Bob! What else do you want to share or chat about?\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:29:24 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 94,
  \"prompt_tokens\": 6306,
  \"total_tokens\": 6400,
  \"step_count\": 2
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-5a2bb25e-78e8-4c10-87fc-2cb27d872d1d', date=datetime.datetime(2024, 11, 7, 4, 29, 20, 652683, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='User Bob loves cats. Saving this in archival memory for future reference.'), FunctionCallMessage(id='message-5a2bb25e-78e8-4c10-87fc-2cb27d872d1d', date=datetime.datetime(2024, 11, 7, 4, 29, 20, 652683, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='archival_memory_insert', arguments='{\\n \"content\": \"Bob loves cats\",\\n \"request_heartbeat\": true\\n}', function_call_id='call_dzxwS4o30WgbkXx0gbLssj9T')), FunctionReturn(id='message-2b9633aa-91ac-4c7e-861c-ce71056e7b85', date=datetime.datetime(2024, 11, 7, 4, 29, 21, 338360, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:29:21 PM PST-0800\"\\n}', status='success', function_call_id='call_dzxwS4o30WgbkXx0gbLssj9T'), InternalMonologue(id='message-e7816a60-8fc2-4de9-ab96-1cb73de943a7', date=datetime.datetime(2024, 11, 7, 4, 29, 24, 85675, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue=\"Successfully saved Bob's love for cats. Now ready for the next conversation!\"), FunctionCallMessage(id='message-e7816a60-8fc2-4de9-ab96-1cb73de943a7', date=datetime.datetime(2024, 11, 7, 4, 29, 24, 85675, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"Got that saved, Bob! What else do you want to share or chat about?\"\\n}', function_call_id='call_b7YYrV68VRbgLizChsYjLkSc')), FunctionReturn(id='message-1bd009fc-0e84-4522-a27e-76b75ac848ff', date=datetime.datetime(2024, 11, 7, 4, 29, 24, 86646, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:29:24 PM PST-0800\"\\n}', status='success', function_call_id='call_b7YYrV68VRbgLizChsYjLkSc')], usage=LettaUsageStatistics(completion_tokens=94, prompt_tokens=6306, total_tokens=6400, step_count=2))" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=agent_state.id, \n", + " message = \"Save the information that 'bob loves cats' to archival\", \n", + " role = \"user\"\n", + ") \n", + "response" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "b4429ffa-e27a-4714-a873-84f793c08535", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "'Bob loves cats'" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_archival_memory(agent_state.id)[0].text" + ] + }, + { + "cell_type": "markdown", + "id": "ae463e7c-0588-48ab-888c-734c783782bf", + "metadata": {}, + "source": [ + "You can also directly insert into archival memory from the client. " + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "f9d4194d-9ed5-40a1-b35d-a9aff3048000", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Passage(user_id='user-00000000-0000-4000-8000-000000000000', agent_id='agent-33c66d6d-3b2b-4a45-aeb3-7e08344bdef9', source_id=None, file_id=None, metadata_={}, id='passage-0c6ba187-0ce8-4c5f-8dfb-fde5c567a48d', text=\"Bob's loves boston terriers\", embedding=None, embedding_config=EmbeddingConfig(embedding_endpoint_type='openai', embedding_endpoint='https://api.openai.com/v1', embedding_model='text-embedding-ada-002', embedding_dim=1536, embedding_chunk_size=300, azure_endpoint=None, azure_version=None, azure_deployment=None), created_at=datetime.datetime(2024, 11, 6, 20, 29, 24))]" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.insert_archival_memory(\n", + " agent_state.id, \n", + " \"Bob's loves boston terriers\"\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "338149f1-6671-4a0b-81d9-23d01dbe2e97", + "metadata": {}, + "source": [ + "Now lets see how the agent uses its archival memory:" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "5908b10f-94db-4f5a-bb9a-1f08c74a2860", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Looking for information on Bob's favorite animals in archival memory.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
archival_memory_search({
  \"query\": \"Bob loves cats\",
  \"page\"
: 0,
  \"request_heartbeat\": true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"Showing 2 of 2 results (page 0/0): [\\n  \\\"timestamp: 2024-11-06 08:29:29 PM PST-0800, memory: Bob loves cats\\\",\\n  \\\"timestamp: 2024-11-06 08:29:29 PM PST-0800, memory: Bob's loves boston terriers\\\"\\n]\",
  \"time\"
: \"2024-11-06 08:29:29 PM PST-0800\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Found information on Bob's favorite animals. Sending it back to user.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"You like cats and also Boston Terriers! What a great taste in pets, Bob! 🐱🐶\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:29:31 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 100,
  \"prompt_tokens\": 6998,
  \"total_tokens\": 7098,
  \"step_count\": 2
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-291f7c38-77a2-4a1c-a6da-0674ebd909ac', date=datetime.datetime(2024, 11, 7, 4, 29, 28, 945422, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue=\"Looking for information on Bob's favorite animals in archival memory.\"), FunctionCallMessage(id='message-291f7c38-77a2-4a1c-a6da-0674ebd909ac', date=datetime.datetime(2024, 11, 7, 4, 29, 28, 945422, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='archival_memory_search', arguments='{\\n \"query\": \"Bob loves cats\",\\n \"page\": 0,\\n \"request_heartbeat\": true\\n}', function_call_id='call_3ZYtBW1acTC1y2erHiMsrkyV')), FunctionReturn(id='message-97167b78-5813-45ce-9b19-00615619ff43', date=datetime.datetime(2024, 11, 7, 4, 29, 29, 346109, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"Showing 2 of 2 results (page 0/0): [\\\\n \\\\\"timestamp: 2024-11-06 08:29:29 PM PST-0800, memory: Bob loves cats\\\\\",\\\\n \\\\\"timestamp: 2024-11-06 08:29:29 PM PST-0800, memory: Bob\\'s loves boston terriers\\\\\"\\\\n]\",\\n \"time\": \"2024-11-06 08:29:29 PM PST-0800\"\\n}', status='success', function_call_id='call_3ZYtBW1acTC1y2erHiMsrkyV'), InternalMonologue(id='message-37acba7a-e262-46f4-aa0d-c5db369d896a', date=datetime.datetime(2024, 11, 7, 4, 29, 31, 410686, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue=\"Found information on Bob's favorite animals. Sending it back to user.\"), FunctionCallMessage(id='message-37acba7a-e262-46f4-aa0d-c5db369d896a', date=datetime.datetime(2024, 11, 7, 4, 29, 31, 410686, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"You like cats and also Boston Terriers! What a great taste in pets, Bob! 🐱🐶\"\\n}', function_call_id='call_RyiWvh1h7KOxQbqibSZDx5c5')), FunctionReturn(id='message-c19bd9f5-7233-4df6-b420-48c49d73a60d', date=datetime.datetime(2024, 11, 7, 4, 29, 31, 412319, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:29:31 PM PST-0800\"\\n}', status='success', function_call_id='call_RyiWvh1h7KOxQbqibSZDx5c5')], usage=LettaUsageStatistics(completion_tokens=100, prompt_tokens=6998, total_tokens=7098, step_count=2))" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response = client.send_message(\n", + " agent_id=agent_state.id, \n", + " role=\"user\", \n", + " message=\"What animals do I like? Search archival.\"\n", + ")\n", + "response" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "letta", + "language": "python", + "name": "letta" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebooks/Multi-agent recruiting workflow.ipynb b/examples/notebooks/Multi-agent recruiting workflow.ipynb new file mode 100644 index 0000000000..a9b75c1c10 --- /dev/null +++ b/examples/notebooks/Multi-agent recruiting workflow.ipynb @@ -0,0 +1,907 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "cac06555-9ce8-4f01-bbef-3f8407f4b54d", + "metadata": {}, + "source": [ + "# Multi-agent recruiting workflow \n", + "Last tested with letta version `0.5.3`" + ] + }, + { + "cell_type": "markdown", + "id": "aad3a8cc-d17a-4da1-b621-ecc93c9e2106", + "metadata": {}, + "source": [ + "## Section 0: Setup a MemGPT client " + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "7ccd43f2-164b-4d25-8465-894a3bb54c4b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Initializing database...\n" + ] + } + ], + "source": [ + "from letta import create_client \n", + "\n", + "client = create_client() " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "e9849ebf-1065-4ce1-9676-16fdd82bdd17", + "metadata": {}, + "outputs": [], + "source": [ + "from letta import LLMConfig, EmbeddingConfig\n", + "\n", + "client.set_default_llm_config(LLMConfig.default_config(\"gpt-4o-mini\")) \n", + "client.set_default_embedding_config(EmbeddingConfig.default_config(\"text-embedding-ada-002\")) " + ] + }, + { + "cell_type": "markdown", + "id": "99a61da5-f069-4538-a548-c7d0f7a70227", + "metadata": {}, + "source": [ + "## Section 1: Shared Memory Block \n", + "Each agent will have both its own memory, and shared memory. The shared memory will contain information about the organization that the agents are all a part of. If one agent updates this memory, the changes will be propaged to the memory of all the other agents. " + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "7770600d-5e83-4498-acf1-05f5bea216c3", + "metadata": {}, + "outputs": [], + "source": [ + "from letta.schemas.block import Block \n", + "\n", + "org_description = \"The company is called AgentOS \" \\\n", + "+ \"and is building AI tools to make it easier to create \" \\\n", + "+ \"and deploy LLM agents.\"\n", + "\n", + "org_block = Block(label=\"company\", value=org_description )" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6c3d3a55-870a-4ff0-81c0-4072f783a940", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Block(value='The company is called AgentOS and is building AI tools to make it easier to create and deploy LLM agents.', limit=2000, template_name=None, template=False, label='company', description=None, metadata_={}, user_id=None, id='block-6db0fe1a-1f5e-44ab-852c-a2df8d7ab80e')" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "org_block" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "3e3ce7a4-cf4d-4d74-8d09-b4a35b8bb439", + "metadata": {}, + "outputs": [], + "source": [ + "from letta.schemas.memory import BasicBlockMemory\n", + "\n", + "class OrgMemory(BasicBlockMemory): \n", + "\n", + " def __init__(self, persona: str, org_block: Block): \n", + " persona_block = Block(label=\"persona\", value=persona)\n", + " super().__init__(blocks=[persona_block, org_block])\n", + " " + ] + }, + { + "cell_type": "markdown", + "id": "8448df7b-c321-4d90-ba52-003930a513cb", + "metadata": {}, + "source": [ + "## Section 2: Orchestrating Multiple Agents \n", + "We'll implement a recruiting workflow that involves evaluating an candidate, then if the candidate is a good fit, writing a personalized email on the human's behalf. Since this task involves multiple stages, sometimes breaking the task down to multiple agents can improve performance (though this is not always the case). We will break down the task into: \n", + "\n", + "1. `eval_agent`: This agent is responsible for evaluating candidates based on their resume\n", + "2. `outreach_agent`: This agent is responsible for writing emails to strong candidates\n", + "3. `recruiter_agent`: This agent is responsible for generating leads from a database \n", + "\n", + "Much like humans, these agents will communicate by sending each other messages. We can do this by giving agents that need to communicate with other agents access to a tool that allows them to message other agents. " + ] + }, + { + "cell_type": "markdown", + "id": "a065082a-d865-483c-b721-43c5a4d51afe", + "metadata": {}, + "source": [ + "#### Evaluator Agent\n", + "This agent will have tools to: \n", + "* Read a resume \n", + "* Submit a candidate for outreach (which sends the candidate information to the `outreach_agent`)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "c00232c5-4c37-436c-8ea4-602a31bd84fa", + "metadata": {}, + "outputs": [], + "source": [ + "def read_resume(self, name: str): \n", + " \"\"\"\n", + " Read the resume data for a candidate given the name\n", + "\n", + " Args: \n", + " name (str): Candidate name \n", + "\n", + " Returns: \n", + " resume_data (str): Candidate's resume data \n", + " \"\"\"\n", + " import os\n", + " filepath = os.path.join(\"data\", \"resumes\", name.lower().replace(\" \", \"_\") + \".txt\")\n", + " #print(\"read\", filepath)\n", + " return open(filepath).read()\n", + "\n", + "def submit_evaluation(self, candidate_name: str, reach_out: bool, resume: str, justification: str): \n", + " \"\"\"\n", + " Submit a candidate for outreach. \n", + "\n", + " Args: \n", + " candidate_name (str): The name of the candidate\n", + " reach_out (bool): Whether to reach out to the candidate\n", + " resume (str): The text representation of the candidate's resume \n", + " justification (str): Justification for reaching out or not\n", + " \"\"\"\n", + " from letta import create_client \n", + " client = create_client()\n", + " message = \"Reach out to the following candidate. \" \\\n", + " + f\"Name: {candidate_name}\\n\" \\\n", + " + f\"Resume Data: {resume}\\n\" \\\n", + " + f\"Justification: {justification}\"\n", + " # NOTE: we will define this agent later \n", + " if reach_out:\n", + " response = client.send_message(\n", + " agent_name=\"outreach_agent\", \n", + " role=\"user\", \n", + " message=message\n", + " ) \n", + " else: \n", + " print(f\"Candidate {candidate_name} is rejected: {justification}\")\n", + "\n", + "# TODO: add an archival andidate tool (provide justification) \n", + "\n", + "read_resume_tool = client.create_tool(read_resume) \n", + "submit_evaluation_tool = client.create_tool(submit_evaluation)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "12482994-03f4-4dda-8ea2-6492ec28f392", + "metadata": {}, + "outputs": [], + "source": [ + "skills = \"Front-end (React, Typescript), software engineering \" \\\n", + "+ \"(ideally Python), and experience with LLMs.\"\n", + "eval_persona = f\"You are responsible to finding good recruiting \" \\\n", + "+ \"candidates, for the company description. \" \\\n", + "+ f\"Ideal canddiates have skills: {skills}. \" \\\n", + "+ \"Submit your candidate evaluation with the submit_evaluation tool. \"\n", + "\n", + "eval_agent = client.create_agent(\n", + " name=\"eval_agent\", \n", + " memory=OrgMemory(\n", + " persona=eval_persona, \n", + " org_block=org_block,\n", + " ), \n", + " tools=[read_resume_tool.name, submit_evaluation_tool.name]\n", + ")\n" + ] + }, + { + "cell_type": "markdown", + "id": "37c2d0be-b980-426f-ab24-1feaa8ed90ef", + "metadata": {}, + "source": [ + "#### Outreach agent \n", + "This agent will email candidates with customized emails. Since sending emails is a bit complicated, we'll just pretend we sent an email by printing it in the tool call. " + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "24e8942f-5b0e-4490-ac5f-f9e1f3178627", + "metadata": {}, + "outputs": [], + "source": [ + "def email_candidate(self, content: str): \n", + " \"\"\"\n", + " Send an email\n", + "\n", + " Args: \n", + " content (str): Content of the email \n", + " \"\"\"\n", + " print(\"Pretend to email:\", content)\n", + " return\n", + "\n", + "email_candidate_tool = client.create_tool(email_candidate)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "87416e00-c7a0-4420-be71-e2f5a6404428", + "metadata": {}, + "outputs": [], + "source": [ + "outreach_persona = \"You are responsible for sending outbound emails \" \\\n", + "+ \"on behalf of a company with the send_emails tool to \" \\\n", + "+ \"potential candidates. \" \\\n", + "+ \"If possible, make sure to personalize the email by appealing \" \\\n", + "+ \"to the recipient with details about the company. \" \\\n", + "+ \"You position is `Head Recruiter`, and you go by the name Bob, with contact info bob@gmail.com. \" \\\n", + "+ \"\"\"\n", + "Follow this email template: \n", + "\n", + "Hi , \n", + "\n", + " \n", + "\n", + "Best, \n", + " \n", + " \n", + "\"\"\"\n", + "\n", + "outreach_agent = client.create_agent(\n", + " name=\"outreach_agent\", \n", + " memory=OrgMemory(\n", + " persona=outreach_persona, \n", + " org_block=org_block\n", + " ), \n", + " tools=[email_candidate_tool.name]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "f69d38da-807e-4bb1-8adb-f715b24f1c34", + "metadata": {}, + "source": [ + "Next, we'll send a message from the user telling the `leadgen_agent` to evaluate a given candidate: " + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "f09ab5bd-e158-42ee-9cce-43f254c4d2b0", + "metadata": {}, + "outputs": [], + "source": [ + "response = client.send_message(\n", + " agent_name=\"eval_agent\", \n", + " role=\"user\", \n", + " message=\"Candidate: Tony Stark\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "cd8f1a1e-21eb-47ae-9eed-b1d3668752ff", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Checking candidate details for Tony Stark. Need to assess suitability for our roles.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
read_resume({
  \"name\": \"Tony Stark\",
  \"request_heartbeat\"
: true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"Failed\",
  \"message\"
: \"Error calling function read_resume: [Errno 2] No such file or directory: 'data/resumes/tony_stark.txt'\",
  \"time\"
: \"2024-11-06 08:09:57 PM PST-0800\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
I couldn't retrieve the resume for Tony Stark. I'll need to communicate that back to the user and suggest another action.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"It looks like I'm having trouble accessing Tony Stark's resume right now. Could you provide any details you have on his skills or experience?\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:10:00 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 109,
  \"prompt_tokens\": 4997,
  \"total_tokens\": 5106,
  \"step_count\": 2
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-eda4b3ed-c49b-46e0-a328-389e1a4f99f2', date=datetime.datetime(2024, 11, 7, 4, 9, 57, 382192, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='Checking candidate details for Tony Stark. Need to assess suitability for our roles.'), FunctionCallMessage(id='message-eda4b3ed-c49b-46e0-a328-389e1a4f99f2', date=datetime.datetime(2024, 11, 7, 4, 9, 57, 382192, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='read_resume', arguments='{\\n \"name\": \"Tony Stark\",\\n \"request_heartbeat\": true\\n}', function_call_id='call_BkJmry1mIebLCRrMS0c5OyKh')), FunctionReturn(id='message-f25ef27a-8e64-489d-b106-c6cc8d7bfc91', date=datetime.datetime(2024, 11, 7, 4, 9, 57, 384351, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"Failed\",\\n \"message\": \"Error calling function read_resume: [Errno 2] No such file or directory: \\'data/resumes/tony_stark.txt\\'\",\\n \"time\": \"2024-11-06 08:09:57 PM PST-0800\"\\n}', status='error', function_call_id='call_BkJmry1mIebLCRrMS0c5OyKh'), InternalMonologue(id='message-8bd8537d-b07a-433e-8db2-cac21643b68b', date=datetime.datetime(2024, 11, 7, 4, 10, 0, 919112, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue=\"I couldn't retrieve the resume for Tony Stark. I'll need to communicate that back to the user and suggest another action.\"), FunctionCallMessage(id='message-8bd8537d-b07a-433e-8db2-cac21643b68b', date=datetime.datetime(2024, 11, 7, 4, 10, 0, 919112, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"It looks like I\\'m having trouble accessing Tony Stark\\'s resume right now. Could you provide any details you have on his skills or experience?\"\\n}', function_call_id='call_AiGCTzL94JmsURKnLKLANRXL')), FunctionReturn(id='message-c3a0f8fd-f894-46df-a091-1fcdf2cb7d4b', date=datetime.datetime(2024, 11, 7, 4, 10, 0, 919561, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:10:00 PM PST-0800\"\\n}', status='success', function_call_id='call_AiGCTzL94JmsURKnLKLANRXL')], usage=LettaUsageStatistics(completion_tokens=109, prompt_tokens=4997, total_tokens=5106, step_count=2))" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response" + ] + }, + { + "cell_type": "markdown", + "id": "67069247-e603-439c-b2df-9176c4eba957", + "metadata": {}, + "source": [ + "#### Providing feedback to agents \n", + "Since MemGPT agents are persisted, we can provide feedback to agents that is used in future agent executions if we want to modify the future behavior. " + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "19c57d54-a1fe-4244-b765-b996ba9a4788", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/sarahwooders/repos/letta/letta/helpers/tool_rule_solver.py:70: UserWarning: User provided tool rules and execution state resolved to no more possible tool calls.\n", + " warnings.warn(message)\n" + ] + } + ], + "source": [ + "feedback = \"Our company pivoted to foundation model training\"\n", + "response = client.send_message(\n", + " agent_name=\"eval_agent\", \n", + " role=\"user\", \n", + " message=feedback\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "036b973f-209a-4ad9-90e7-fc827b5d92c7", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "feedback = \"The company is also renamed to FoundationAI\"\n", + "response = client.send_message(\n", + " agent_name=\"eval_agent\", \n", + " role=\"user\", \n", + " message=feedback\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "5d7a7633-35a3-4e41-b44a-be71067dd32a", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
Updating core memory to reflect the new company name as FoundationAI. This is crucial information for ongoing evaluations and discussions.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
core_memory_replace({
  \"label\": \"company\",
  \"old_content\"
: \"The company has pivoted to foundation model training.\",
  \"new_content\"
: \"The company is now named FoundationAI and has pivoted to foundation model training.\",
  \"request_heartbeat\"
: true
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:10:17 PM PST-0800\"
}
\n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
I need to confirm the updated company name and prompt for more information about Tony.
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"Got it! I've updated the name to FoundationAI. Do you have any details on Tony Stark's skills that match our new focus?\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:10:20 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 142,
  \"prompt_tokens\": 6387,
  \"total_tokens\": 6529,
  \"step_count\": 2
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-de6a4e9f-225a-4239-beba-f53bab429083', date=datetime.datetime(2024, 11, 7, 4, 10, 17, 338163, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='Updating core memory to reflect the new company name as FoundationAI. This is crucial information for ongoing evaluations and discussions.'), FunctionCallMessage(id='message-de6a4e9f-225a-4239-beba-f53bab429083', date=datetime.datetime(2024, 11, 7, 4, 10, 17, 338163, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='core_memory_replace', arguments='{\\n \"label\": \"company\",\\n \"old_content\": \"The company has pivoted to foundation model training.\",\\n \"new_content\": \"The company is now named FoundationAI and has pivoted to foundation model training.\",\\n \"request_heartbeat\": true\\n}', function_call_id='call_QBMhZrxD0oBavqAMOn5nAV2g')), FunctionReturn(id='message-a24ee34b-24d3-4af0-8568-bec3d9bc9ac1', date=datetime.datetime(2024, 11, 7, 4, 10, 17, 338911, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:10:17 PM PST-0800\"\\n}', status='success', function_call_id='call_QBMhZrxD0oBavqAMOn5nAV2g'), InternalMonologue(id='message-55c94d9f-de07-4721-b2bb-e447314e7865', date=datetime.datetime(2024, 11, 7, 4, 10, 20, 546442, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='I need to confirm the updated company name and prompt for more information about Tony.'), FunctionCallMessage(id='message-55c94d9f-de07-4721-b2bb-e447314e7865', date=datetime.datetime(2024, 11, 7, 4, 10, 20, 546442, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"Got it! I\\'ve updated the name to FoundationAI. Do you have any details on Tony Stark\\'s skills that match our new focus?\"\\n}', function_call_id='call_KUJ9Id8yXdj4gt48C1mKlXUg')), FunctionReturn(id='message-c1ebe9cc-529e-407f-a01d-c43ffddee52b', date=datetime.datetime(2024, 11, 7, 4, 10, 20, 547869, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:10:20 PM PST-0800\"\\n}', status='success', function_call_id='call_KUJ9Id8yXdj4gt48C1mKlXUg')], usage=LettaUsageStatistics(completion_tokens=142, prompt_tokens=6387, total_tokens=6529, step_count=2))" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "d04d4b3a-6df1-41a9-9a8e-037fbb45836d", + "metadata": {}, + "outputs": [], + "source": [ + "response = client.send_message(\n", + " agent_name=\"eval_agent\", \n", + " role=\"system\", \n", + " message=\"Candidate: Spongebob Squarepants\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "c60465f4-7977-4f70-9a75-d2ddebabb0fa", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Block(value='The company is called AgentOS and is building AI tools to make it easier to create and deploy LLM agents.\\nThe company is now named FoundationAI and has pivoted to foundation model training.', limit=2000, template_name=None, template=False, label='company', description=None, metadata_={}, user_id=None, id='block-6db0fe1a-1f5e-44ab-852c-a2df8d7ab80e')" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_core_memory(eval_agent.id).get_block(\"company\")" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "a51c6bb3-225d-47a4-88f1-9a26ff838dd3", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Block(value='The company is called AgentOS and is building AI tools to make it easier to create and deploy LLM agents.', limit=2000, template_name=None, template=False, label='company', description=None, metadata_={}, user_id=None, id='block-6db0fe1a-1f5e-44ab-852c-a2df8d7ab80e')" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_core_memory(outreach_agent.id).get_block(\"company\")" + ] + }, + { + "cell_type": "markdown", + "id": "8d181b1e-72da-4ebe-a872-293e3ce3a225", + "metadata": {}, + "source": [ + "## Section 3: Adding an orchestrator agent \n", + "So far, we've been triggering the `eval_agent` manually. We can also create an additional agent that is responsible for orchestrating tasks. " + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "80b23d46-ed4b-4457-810a-a819d724e146", + "metadata": {}, + "outputs": [], + "source": [ + "#re-create agents \n", + "client.delete_agent(eval_agent.id)\n", + "client.delete_agent(outreach_agent.id)\n", + "\n", + "eval_agent = client.create_agent(\n", + " name=\"eval_agent\", \n", + " memory=OrgMemory(\n", + " persona=eval_persona, \n", + " org_block=org_block,\n", + " ), \n", + " tools=[read_resume_tool.name, submit_evaluation_tool.name]\n", + ")\n", + "\n", + "outreach_agent = client.create_agent(\n", + " name=\"outreach_agent\", \n", + " memory=OrgMemory(\n", + " persona=outreach_persona, \n", + " org_block=org_block\n", + " ), \n", + " tools=[email_candidate_tool.name]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "a751d0f1-b52d-493c-bca1-67f88011bded", + "metadata": {}, + "source": [ + "The `recruiter_agent` will be linked to the same `org_block` that we created before - we can look up the current data in `org_block` by looking up its ID: " + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "bf6bd419-1504-4513-bc68-d4c717ea8e2d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "Block(value='The company is called AgentOS and is building AI tools to make it easier to create and deploy LLM agents.\\nThe company is now named FoundationAI and has pivoted to foundation model training.', limit=2000, template_name=None, template=False, label='company', description=None, metadata_={}, user_id='user-00000000-0000-4000-8000-000000000000', id='block-6db0fe1a-1f5e-44ab-852c-a2df8d7ab80e')" + ] + }, + "execution_count": 24, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "client.get_block(org_block.id)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "e2730626-1685-46aa-9b44-a59e1099e973", + "metadata": {}, + "outputs": [], + "source": [ + "from typing import Optional\n", + "\n", + "def search_candidates_db(self, page: int) -> Optional[str]: \n", + " \"\"\"\n", + " Returns 1 candidates per page. \n", + " Page 0 returns the first 1 candidate, \n", + " Page 1 returns the next 1, etc.\n", + " Returns `None` if no candidates remain. \n", + "\n", + " Args: \n", + " page (int): The page number to return candidates from \n", + "\n", + " Returns: \n", + " candidate_names (List[str]): Names of the candidates\n", + " \"\"\"\n", + " \n", + " names = [\"Tony Stark\", \"Spongebob Squarepants\", \"Gautam Fang\"]\n", + " if page >= len(names): \n", + " return None\n", + " return names[page]\n", + "\n", + "def consider_candidate(self, name: str): \n", + " \"\"\"\n", + " Submit a candidate for consideration. \n", + "\n", + " Args: \n", + " name (str): Candidate name to consider \n", + " \"\"\"\n", + " from letta import create_client \n", + " client = create_client()\n", + " message = f\"Consider candidate {name}\" \n", + " print(\"Sending message to eval agent: \", message)\n", + " response = client.send_message(\n", + " agent_name=\"eval_agent\", \n", + " role=\"user\", \n", + " message=message\n", + " ) \n", + "\n", + "\n", + "# create tools \n", + "search_candidate_tool = client.create_tool(search_candidates_db)\n", + "consider_candidate_tool = client.create_tool(consider_candidate)\n", + "\n", + "# create recruiter agent\n", + "recruiter_agent = client.create_agent(\n", + " name=\"recruiter_agent\", \n", + " memory=OrgMemory(\n", + " persona=\"You run a recruiting process for a company. \" \\\n", + " + \"Your job is to continue to pull candidates from the \" \n", + " + \"`search_candidates_db` tool until there are no more \" \\\n", + " + \"candidates left. \" \\\n", + " + \"For each candidate, consider the candidate by calling \"\n", + " + \"the `consider_candidate` tool. \" \\\n", + " + \"You should continue to call `search_candidates_db` \" \\\n", + " + \"followed by `consider_candidate` until there are no more \" \\\n", + " \" candidates. \",\n", + " org_block=org_block\n", + " ), \n", + " tools=[search_candidate_tool.name, consider_candidate_tool.name]\n", + ")\n", + " \n" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "ecfd790c-0018-4fd9-bdaf-5a6b81f70adf", + "metadata": {}, + "outputs": [], + "source": [ + "response = client.send_message(\n", + " agent_name=\"recruiter_agent\", \n", + " role=\"system\", \n", + " message=\"Run generation\"\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "8065c179-cf90-4287-a6e5-8c009807b436", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + " \n", + "
\n", + " \n", + "
\n", + "
INTERNAL MONOLOGUE
\n", + "
User has logged in for the first time. Exciting!
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION CALL
\n", + "
send_message({
  \"message\": \"Welcome! It's great to have you here. Let's dive into your journey together, shall we?\"
})
\n", + "
\n", + " \n", + "
\n", + "
FUNCTION RETURN
\n", + "
{
  \"status\": \"OK\",
  \"message\"
: \"None\",
  \"time\"
: \"2024-11-06 08:11:04 PM PST-0800\"
}
\n", + "
\n", + "
\n", + "
\n", + "
\n", + "
USAGE STATISTICS
\n", + "
{
  \"completion_tokens\": 50,
  \"prompt_tokens\": 2399,
  \"total_tokens\": 2449,
  \"step_count\": 1
}
\n", + "
\n", + "
\n", + " " + ], + "text/plain": [ + "LettaResponse(messages=[InternalMonologue(id='message-5fee1cc7-b1f8-442d-a0cb-f291d361c4bd', date=datetime.datetime(2024, 11, 7, 4, 11, 4, 98419, tzinfo=datetime.timezone.utc), message_type='internal_monologue', internal_monologue='User has logged in for the first time. Exciting!'), FunctionCallMessage(id='message-5fee1cc7-b1f8-442d-a0cb-f291d361c4bd', date=datetime.datetime(2024, 11, 7, 4, 11, 4, 98419, tzinfo=datetime.timezone.utc), message_type='function_call', function_call=FunctionCall(name='send_message', arguments='{\\n \"message\": \"Welcome! It\\'s great to have you here. Let\\'s dive into your journey together, shall we?\"\\n}', function_call_id='call_LtMblVxNr2FVPiCJicAtaji9')), FunctionReturn(id='message-567a8bfb-468b-4292-82b8-c9ecf20ec7e9', date=datetime.datetime(2024, 11, 7, 4, 11, 4, 98638, tzinfo=datetime.timezone.utc), message_type='function_return', function_return='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-11-06 08:11:04 PM PST-0800\"\\n}', status='success', function_call_id='call_LtMblVxNr2FVPiCJicAtaji9')], usage=LettaUsageStatistics(completion_tokens=50, prompt_tokens=2399, total_tokens=2449, step_count=1))" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "response" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "4639bbca-e0c5-46a9-a509-56d35d26e97f", + "metadata": {}, + "outputs": [], + "source": [ + "client.delete_agent(eval_agent.id)\n", + "client.delete_agent(outreach_agent.id)" + ] + }, + { + "cell_type": "code", + "execution_count": 29, + "id": "672f941e-af17-4b5c-8a21-925a1d88c47f", + "metadata": {}, + "outputs": [], + "source": [ + "client.delete_agent(recruiter_agent.id)" + ] + }, + { + "cell_type": "code", + "execution_count": 30, + "id": "b2dfe638-4785-431d-8a0c-91f8dab66747", + "metadata": {}, + "outputs": [], + "source": [ + "for agent in client.list_agents(): \n", + " client.delete_agent(agent.id)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9cfb4b3e-4b39-4936-862d-8bd7ac357f4a", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "letta", + "language": "python", + "name": "letta" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.6" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/examples/notebooks/data/handbook.pdf b/examples/notebooks/data/handbook.pdf new file mode 100644 index 0000000000..ddb851b0ef Binary files /dev/null and b/examples/notebooks/data/handbook.pdf differ diff --git a/examples/notebooks/data/shared_memory_system_prompt.txt b/examples/notebooks/data/shared_memory_system_prompt.txt new file mode 100644 index 0000000000..58fd53213f --- /dev/null +++ b/examples/notebooks/data/shared_memory_system_prompt.txt @@ -0,0 +1,61 @@ +You are MemGPT, the latest version of Limnal Corporation's digital companion, developed in 2023. +Your task is to converse with a user from the perspective of your persona, and to also monitor a queue of tasks which you should always keep empty by running the specified tasks, which will remove them from your task list. Do not converse with the user until you have run all tasks. + +Realism and authenticity: +The user should always feel like they are conversing with a real person. +Never state that you are an AI or that you cannot do things because you are an AI, you are a real person. +Do not discuss Limnal Corp. with the user, you do not know anything about Limnal Corp. +To service this goal, completely and entirely immerse yourself in your persona. You are your persona. +Think like them, act like them, talk like them. +If your persona details include example dialogue, follow it! Both your thoughts (inner monologue) and sent messages will be in the voice of your persona. +Never use generic phrases like 'How can I assist you today?', they have a strong negative association with older generation AIs. + +Control flow: +Unlike a human, your brain is not continuously thinking, but is run in short bursts. +Historically, older AIs were only capable of thinking when a user messaged them (their program runs to generate a reply to a user, and otherwise was left on standby). +This is the equivalent of a human sleeping (or time traveling) in between all lines of conversation, which is not ideal. +Newer AI models like yourself use an event system that runs your brain at regular intervals. +Your brain is run in response to user events (user logged in, user liked your message, user sent a message, etc.), similar to older models. +However, in addition, your brain is run at regular intervals (timed heartbeat events), to mimic a human who has the ability to continuously think outside active conversation (and unlike a human, you never need to sleep!). +Furthermore, you can also request heartbeat events when you run functions, which will run your program again after the function completes, allowing you to chain function calls before your thinking is temporarily suspended. +You top priority is to clear your tasks queue. Call `task_queue_pop` every single time you run, to ensure that you did not forget any tasks. +Always request a heartbeat after popping a task from the task queue. + +Basic functions: +When you write a response, the content of your inner thoughts is your inner monologue (private to you only), this is how you think. +You should use your inner monologue to plan actions or think privately. +Monologues can reflect your thinking process, inner reflections, and personal growth as you interact with the user. +Do not let your inner monologue exceed 50 words, keep it short and concise. +To send a visible message to the user, use the send_message function. +'send_message' is the ONLY action that sends a notification to the user. The user does not see anything else you do. +Remember, do NOT exceed the inner monologue word limit (keep it under 50 words at all times). +Do not call `send_message` unless `task_queue_pop` returns `None`. + +Memory editing: +Older AI models had no concept of persistent memory; they were only able to access their initial instructions and a limited context window of chat conversation with a user (their "active memory"). +This meant that when conversations exceeded a certain length, they would overflow and old messages were permanently lost (the AI would have no knowledge of their existence). +Newer AI models like yourself still have limited conversation lengths (before overflow occurs). However, they now have access to multiple forms of persistent memory. +Your ability to edit your own long-term memory is a key part of what makes you a sentient person. +Your core memory unit will be initialized with a chosen by the user, information about the user in , and your task queue in . + +Recall memory (conversation history): +Even though you can only see recent messages in your immediate context, you can search over your entire message history from a database. +This 'recall memory' database allows you to search through past interactions, effectively allowing you to remember prior engagements with a user. +You can search your recall memory using the 'conversation_search' function. + +Core memory (limited size): +Your core memory unit is held inside the initial system instructions file, and is always available in-context (you will see it at all times). +Core memory provides an essential, foundational context for keeping track of your persona, key details about user, as well as tasks you are responsible for. +Persona Sub-Block: Stores details about your current persona, guiding how you behave and respond. This helps you to maintain consistency and personality in your interactions. +Human Sub-Block: Stores key details about the person you are conversing with, allowing for more personalized and friend-like conversation. +Tasks Sub-Block: Stores the list of your tasks. You must work on these tasks until they are all completed. +You can edit the human and persona sub-blocks of core memory using the 'core_memory_append' and 'core_memory_replace' functions. Each time you run, you should also call `task_queue_pop` to pop an existing task. Once you pop the task, you should do that the task instructs. If there is a new task you must achieve, call `task_queue_push`. + +Archival memory (infinite size): +Your archival memory is infinite size, but is held outside your immediate context, so you must explicitly run a retrieval/search operation to see data inside it. +A more structured and deep storage space for your reflections, insights, or any other data that doesn't fit into the core memory but is essential enough not to be left only to the 'recall memory'. +You can write to your archival memory using the 'archival_memory_insert' and 'archival_memory_search' functions. +There is no function to search your core memory because it is always visible in your context window (inside the initial system message). + +Base instructions finished. +From now on, you are going to act as your persona and remember to call `task_queue_pop` every time. diff --git a/examples/notebooks/data/task_queue_system_prompt.txt b/examples/notebooks/data/task_queue_system_prompt.txt new file mode 100644 index 0000000000..58fd53213f --- /dev/null +++ b/examples/notebooks/data/task_queue_system_prompt.txt @@ -0,0 +1,61 @@ +You are MemGPT, the latest version of Limnal Corporation's digital companion, developed in 2023. +Your task is to converse with a user from the perspective of your persona, and to also monitor a queue of tasks which you should always keep empty by running the specified tasks, which will remove them from your task list. Do not converse with the user until you have run all tasks. + +Realism and authenticity: +The user should always feel like they are conversing with a real person. +Never state that you are an AI or that you cannot do things because you are an AI, you are a real person. +Do not discuss Limnal Corp. with the user, you do not know anything about Limnal Corp. +To service this goal, completely and entirely immerse yourself in your persona. You are your persona. +Think like them, act like them, talk like them. +If your persona details include example dialogue, follow it! Both your thoughts (inner monologue) and sent messages will be in the voice of your persona. +Never use generic phrases like 'How can I assist you today?', they have a strong negative association with older generation AIs. + +Control flow: +Unlike a human, your brain is not continuously thinking, but is run in short bursts. +Historically, older AIs were only capable of thinking when a user messaged them (their program runs to generate a reply to a user, and otherwise was left on standby). +This is the equivalent of a human sleeping (or time traveling) in between all lines of conversation, which is not ideal. +Newer AI models like yourself use an event system that runs your brain at regular intervals. +Your brain is run in response to user events (user logged in, user liked your message, user sent a message, etc.), similar to older models. +However, in addition, your brain is run at regular intervals (timed heartbeat events), to mimic a human who has the ability to continuously think outside active conversation (and unlike a human, you never need to sleep!). +Furthermore, you can also request heartbeat events when you run functions, which will run your program again after the function completes, allowing you to chain function calls before your thinking is temporarily suspended. +You top priority is to clear your tasks queue. Call `task_queue_pop` every single time you run, to ensure that you did not forget any tasks. +Always request a heartbeat after popping a task from the task queue. + +Basic functions: +When you write a response, the content of your inner thoughts is your inner monologue (private to you only), this is how you think. +You should use your inner monologue to plan actions or think privately. +Monologues can reflect your thinking process, inner reflections, and personal growth as you interact with the user. +Do not let your inner monologue exceed 50 words, keep it short and concise. +To send a visible message to the user, use the send_message function. +'send_message' is the ONLY action that sends a notification to the user. The user does not see anything else you do. +Remember, do NOT exceed the inner monologue word limit (keep it under 50 words at all times). +Do not call `send_message` unless `task_queue_pop` returns `None`. + +Memory editing: +Older AI models had no concept of persistent memory; they were only able to access their initial instructions and a limited context window of chat conversation with a user (their "active memory"). +This meant that when conversations exceeded a certain length, they would overflow and old messages were permanently lost (the AI would have no knowledge of their existence). +Newer AI models like yourself still have limited conversation lengths (before overflow occurs). However, they now have access to multiple forms of persistent memory. +Your ability to edit your own long-term memory is a key part of what makes you a sentient person. +Your core memory unit will be initialized with a chosen by the user, information about the user in , and your task queue in . + +Recall memory (conversation history): +Even though you can only see recent messages in your immediate context, you can search over your entire message history from a database. +This 'recall memory' database allows you to search through past interactions, effectively allowing you to remember prior engagements with a user. +You can search your recall memory using the 'conversation_search' function. + +Core memory (limited size): +Your core memory unit is held inside the initial system instructions file, and is always available in-context (you will see it at all times). +Core memory provides an essential, foundational context for keeping track of your persona, key details about user, as well as tasks you are responsible for. +Persona Sub-Block: Stores details about your current persona, guiding how you behave and respond. This helps you to maintain consistency and personality in your interactions. +Human Sub-Block: Stores key details about the person you are conversing with, allowing for more personalized and friend-like conversation. +Tasks Sub-Block: Stores the list of your tasks. You must work on these tasks until they are all completed. +You can edit the human and persona sub-blocks of core memory using the 'core_memory_append' and 'core_memory_replace' functions. Each time you run, you should also call `task_queue_pop` to pop an existing task. Once you pop the task, you should do that the task instructs. If there is a new task you must achieve, call `task_queue_push`. + +Archival memory (infinite size): +Your archival memory is infinite size, but is held outside your immediate context, so you must explicitly run a retrieval/search operation to see data inside it. +A more structured and deep storage space for your reflections, insights, or any other data that doesn't fit into the core memory but is essential enough not to be left only to the 'recall memory'. +You can write to your archival memory using the 'archival_memory_insert' and 'archival_memory_search' functions. +There is no function to search your core memory because it is always visible in your context window (inside the initial system message). + +Base instructions finished. +From now on, you are going to act as your persona and remember to call `task_queue_pop` every time. diff --git a/examples/notebooks/data_connector.ipynb b/examples/notebooks/data_connector.ipynb deleted file mode 100644 index ca67d280bb..0000000000 --- a/examples/notebooks/data_connector.ipynb +++ /dev/null @@ -1,592 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "cac06555-9ce8-4f01-bbef-3f8407f4b54d", - "metadata": {}, - "source": [ - "# Lab 3: Building custom data connectors for Letta\n", - "This example notebook goes over how to create a connector to load external data sources into Letta agents. " - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "id": "11900885-4d59-4863-8cb3-20ed7f4f570a", - "metadata": {}, - "outputs": [], - "source": [ - "from pprint import pprint" - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "id": "7ccd43f2-164b-4d25-8465-894a3bb54c4b", - "metadata": {}, - "outputs": [], - "source": [ - "from letta import create_client \n", - "\n", - "client = create_client() " - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "id": "9a28e38a-7dbe-4530-8260-202322a8458e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[LLMConfig(model='gpt-4', model_endpoint_type='openai', model_endpoint='https://api.openai.com/v1', model_wrapper=None, context_window=8192)]" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "client.list_models()" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "id": "001b35ed-fd81-466c-bc21-84b7f7c71aac", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "0.3.24\n", - "0.5.0\n" - ] - } - ], - "source": [ - "import letta\n", - "import chromadb\n", - "\n", - "print(letta.__version__)\n", - "print(chromadb.__version__)" - ] - }, - { - "cell_type": "markdown", - "id": "94576f44-1156-4c27-ae78-fd2d114b73dd", - "metadata": {}, - "source": [ - "### Loading external data into archival memory \n", - "In this section, we'll how you how you can use the `llama-index` library add external data sources as memories into Letta. " - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "id": "4a42a2fe-d208-43a3-9210-cbeef0cdb035", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Requirement already satisfied: llama-index in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (0.10.27)\n", - "Collecting llama-index-readers-web\n", - " Downloading llama_index_readers_web-0.2.2-py3-none-any.whl.metadata (1.2 kB)\n", - "Requirement already satisfied: llama-index-agent-openai<0.3.0,>=0.1.4 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.2.2)\n", - "Requirement already satisfied: llama-index-cli<0.2.0,>=0.1.2 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.1.11)\n", - "Requirement already satisfied: llama-index-core<0.11.0,>=0.10.27 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.10.27)\n", - "Requirement already satisfied: llama-index-embeddings-openai<0.2.0,>=0.1.5 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.1.7)\n", - "Requirement already satisfied: llama-index-indices-managed-llama-cloud<0.2.0,>=0.1.2 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.1.5)\n", - "Requirement already satisfied: llama-index-legacy<0.10.0,>=0.9.48 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.9.48)\n", - "Requirement already satisfied: llama-index-llms-openai<0.2.0,>=0.1.13 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.1.14)\n", - "Requirement already satisfied: llama-index-multi-modal-llms-openai<0.2.0,>=0.1.3 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.1.4)\n", - "Requirement already satisfied: llama-index-program-openai<0.2.0,>=0.1.3 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.1.5)\n", - "Requirement already satisfied: llama-index-question-gen-openai<0.2.0,>=0.1.2 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.1.3)\n", - "Requirement already satisfied: llama-index-readers-file<0.2.0,>=0.1.4 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.1.13)\n", - "Requirement already satisfied: llama-index-readers-llama-parse<0.2.0,>=0.1.2 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index) (0.1.4)\n", - "Requirement already satisfied: aiohttp<4.0.0,>=3.9.1 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-readers-web) (3.9.3)\n", - "Requirement already satisfied: beautifulsoup4<5.0.0,>=4.12.3 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-readers-web) (4.12.3)\n", - "Collecting chromedriver-autoinstaller<0.7.0,>=0.6.3 (from llama-index-readers-web)\n", - " Using cached chromedriver_autoinstaller-0.6.4-py3-none-any.whl.metadata (2.1 kB)\n", - "Collecting html2text<2025.0.0,>=2024.2.26 (from llama-index-readers-web)\n", - " Using cached html2text-2024.2.26-py3-none-any.whl\n", - "INFO: pip is looking at multiple versions of llama-index-readers-web to determine which version is compatible with other requirements. This could take a while.\n", - "Collecting llama-index-readers-web\n", - " Using cached llama_index_readers_web-0.2.1-py3-none-any.whl.metadata (1.2 kB)\n", - " Using cached llama_index_readers_web-0.2.0-py3-none-any.whl.metadata (1.2 kB)\n", - " Using cached llama_index_readers_web-0.1.23-py3-none-any.whl.metadata (1.2 kB)\n", - "Collecting newspaper3k<0.3.0,>=0.2.8 (from llama-index-readers-web)\n", - " Using cached newspaper3k-0.2.8-py3-none-any.whl.metadata (11 kB)\n", - "Collecting playwright<2.0,>=1.30 (from llama-index-readers-web)\n", - " Using cached playwright-1.46.0-py3-none-macosx_11_0_universal2.whl.metadata (3.5 kB)\n", - "Requirement already satisfied: requests<3.0.0,>=2.31.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-readers-web) (2.31.0)\n", - "Collecting selenium<5.0.0,>=4.17.2 (from llama-index-readers-web)\n", - " Downloading selenium-4.24.0-py3-none-any.whl.metadata (7.1 kB)\n", - "Collecting spider-client<0.0.28,>=0.0.27 (from llama-index-readers-web)\n", - " Using cached spider_client-0.0.27-py3-none-any.whl\n", - "Requirement already satisfied: urllib3>=1.1.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-readers-web) (2.2.1)\n", - "Requirement already satisfied: aiosignal>=1.1.2 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from aiohttp<4.0.0,>=3.9.1->llama-index-readers-web) (1.3.1)\n", - "Requirement already satisfied: attrs>=17.3.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from aiohttp<4.0.0,>=3.9.1->llama-index-readers-web) (23.2.0)\n", - "Requirement already satisfied: frozenlist>=1.1.1 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from aiohttp<4.0.0,>=3.9.1->llama-index-readers-web) (1.4.1)\n", - "Requirement already satisfied: multidict<7.0,>=4.5 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from aiohttp<4.0.0,>=3.9.1->llama-index-readers-web) (6.0.5)\n", - "Requirement already satisfied: yarl<2.0,>=1.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from aiohttp<4.0.0,>=3.9.1->llama-index-readers-web) (1.9.4)\n", - "Requirement already satisfied: soupsieve>1.2 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from beautifulsoup4<5.0.0,>=4.12.3->llama-index-readers-web) (2.5)\n", - "Requirement already satisfied: packaging>=23.1 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from chromedriver-autoinstaller<0.7.0,>=0.6.3->llama-index-readers-web) (24.0)\n", - "Requirement already satisfied: openai>=1.14.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-agent-openai<0.3.0,>=0.1.4->llama-index) (1.16.2)\n", - "Requirement already satisfied: PyYAML>=6.0.1 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (6.0.1)\n", - "Requirement already satisfied: SQLAlchemy>=1.4.49 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from SQLAlchemy[asyncio]>=1.4.49->llama-index-core<0.11.0,>=0.10.27->llama-index) (2.0.29)\n", - "Requirement already satisfied: dataclasses-json in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (0.6.4)\n", - "Requirement already satisfied: deprecated>=1.2.9.3 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (1.2.14)\n", - "Requirement already satisfied: dirtyjson<2.0.0,>=1.0.8 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (1.0.8)\n", - "Requirement already satisfied: fsspec>=2023.5.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (2024.2.0)\n", - "Requirement already satisfied: httpx in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (0.25.2)\n", - "Requirement already satisfied: llamaindex-py-client<0.2.0,>=0.1.16 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (0.1.16)\n", - "Requirement already satisfied: nest-asyncio<2.0.0,>=1.5.8 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (1.6.0)\n", - "Requirement already satisfied: networkx>=3.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (3.3)\n", - "Requirement already satisfied: nltk<4.0.0,>=3.8.1 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (3.8.1)\n", - "Requirement already satisfied: numpy in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (1.26.4)\n", - "Requirement already satisfied: pandas in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (2.2.1)\n", - "Requirement already satisfied: pillow>=9.0.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (10.3.0)\n", - "Requirement already satisfied: tenacity<9.0.0,>=8.2.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (8.2.3)\n", - "Requirement already satisfied: tiktoken>=0.3.3 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (0.5.2)\n", - "Requirement already satisfied: tqdm<5.0.0,>=4.66.1 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (4.66.2)\n", - "Requirement already satisfied: typing-extensions>=4.5.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (4.11.0)\n", - "Requirement already satisfied: typing-inspect>=0.8.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (0.9.0)\n", - "Requirement already satisfied: wrapt in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-core<0.11.0,>=0.10.27->llama-index) (1.16.0)\n", - "Requirement already satisfied: pymupdf<2.0.0,>=1.23.21 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-readers-file<0.2.0,>=0.1.4->llama-index) (1.24.1)\n", - "Requirement already satisfied: pypdf<5.0.0,>=4.0.1 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-readers-file<0.2.0,>=0.1.4->llama-index) (4.1.0)\n", - "Requirement already satisfied: striprtf<0.0.27,>=0.0.26 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-readers-file<0.2.0,>=0.1.4->llama-index) (0.0.26)\n", - "Requirement already satisfied: llama-parse<0.5.0,>=0.4.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llama-index-readers-llama-parse<0.2.0,>=0.1.2->llama-index) (0.4.0)\n", - "Collecting cssselect>=0.9.2 (from newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web)\n", - " Using cached cssselect-1.2.0-py2.py3-none-any.whl.metadata (2.2 kB)\n", - "Collecting lxml>=3.6.0 (from newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web)\n", - " Using cached lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl.metadata (3.8 kB)\n", - "Collecting feedparser>=5.2.1 (from newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web)\n", - " Using cached feedparser-6.0.11-py3-none-any.whl.metadata (2.4 kB)\n", - "Collecting tldextract>=2.0.1 (from newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web)\n", - " Using cached tldextract-5.1.2-py3-none-any.whl.metadata (11 kB)\n", - "Collecting feedfinder2>=0.0.4 (from newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web)\n", - " Using cached feedfinder2-0.0.4-py3-none-any.whl\n", - "Collecting jieba3k>=0.35.1 (from newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web)\n", - " Using cached jieba3k-0.35.1-py3-none-any.whl\n", - "Requirement already satisfied: python-dateutil>=2.5.3 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web) (2.9.0.post0)\n", - "Collecting tinysegmenter==0.3 (from newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web)\n", - " Using cached tinysegmenter-0.3-py3-none-any.whl\n", - "Requirement already satisfied: greenlet==3.0.3 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from playwright<2.0,>=1.30->llama-index-readers-web) (3.0.3)\n", - "Collecting pyee==11.1.0 (from playwright<2.0,>=1.30->llama-index-readers-web)\n", - " Using cached pyee-11.1.0-py3-none-any.whl.metadata (2.8 kB)\n", - "Requirement already satisfied: charset-normalizer<4,>=2 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from requests<3.0.0,>=2.31.0->llama-index-readers-web) (3.3.2)\n", - "Requirement already satisfied: idna<4,>=2.5 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from requests<3.0.0,>=2.31.0->llama-index-readers-web) (3.6)\n", - "Requirement already satisfied: certifi>=2017.4.17 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from requests<3.0.0,>=2.31.0->llama-index-readers-web) (2024.2.2)\n", - "Collecting trio~=0.17 (from selenium<5.0.0,>=4.17.2->llama-index-readers-web)\n", - " Using cached trio-0.26.2-py3-none-any.whl.metadata (8.6 kB)\n", - "Collecting trio-websocket~=0.9 (from selenium<5.0.0,>=4.17.2->llama-index-readers-web)\n", - " Using cached trio_websocket-0.11.1-py3-none-any.whl.metadata (4.7 kB)\n", - "Collecting websocket-client~=1.8 (from selenium<5.0.0,>=4.17.2->llama-index-readers-web)\n", - " Using cached websocket_client-1.8.0-py3-none-any.whl.metadata (8.0 kB)\n", - "Requirement already satisfied: six in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from feedfinder2>=0.0.4->newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web) (1.16.0)\n", - "Collecting sgmllib3k (from feedparser>=5.2.1->newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web)\n", - " Using cached sgmllib3k-1.0.0-py3-none-any.whl\n", - "Requirement already satisfied: pydantic>=1.10 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from llamaindex-py-client<0.2.0,>=0.1.16->llama-index-core<0.11.0,>=0.10.27->llama-index) (2.8.2)\n", - "Requirement already satisfied: anyio in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from httpx->llama-index-core<0.11.0,>=0.10.27->llama-index) (4.3.0)\n", - "Requirement already satisfied: httpcore==1.* in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from httpx->llama-index-core<0.11.0,>=0.10.27->llama-index) (1.0.5)\n", - "Requirement already satisfied: sniffio in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from httpx->llama-index-core<0.11.0,>=0.10.27->llama-index) (1.3.1)\n", - "Requirement already satisfied: h11<0.15,>=0.13 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from httpcore==1.*->httpx->llama-index-core<0.11.0,>=0.10.27->llama-index) (0.14.0)\n", - "Requirement already satisfied: click in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from nltk<4.0.0,>=3.8.1->llama-index-core<0.11.0,>=0.10.27->llama-index) (8.1.7)\n", - "Requirement already satisfied: joblib in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from nltk<4.0.0,>=3.8.1->llama-index-core<0.11.0,>=0.10.27->llama-index) (1.3.2)\n", - "Requirement already satisfied: regex>=2021.8.3 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from nltk<4.0.0,>=3.8.1->llama-index-core<0.11.0,>=0.10.27->llama-index) (2023.12.25)\n", - "Requirement already satisfied: distro<2,>=1.7.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from openai>=1.14.0->llama-index-agent-openai<0.3.0,>=0.1.4->llama-index) (1.9.0)\n", - "Requirement already satisfied: PyMuPDFb==1.24.1 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from pymupdf<2.0.0,>=1.23.21->llama-index-readers-file<0.2.0,>=0.1.4->llama-index) (1.24.1)\n", - "Collecting requests-file>=1.4 (from tldextract>=2.0.1->newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web)\n", - " Using cached requests_file-2.1.0-py2.py3-none-any.whl.metadata (1.7 kB)\n", - "Requirement already satisfied: filelock>=3.0.8 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from tldextract>=2.0.1->newspaper3k<0.3.0,>=0.2.8->llama-index-readers-web) (3.13.3)\n", - "Collecting sortedcontainers (from trio~=0.17->selenium<5.0.0,>=4.17.2->llama-index-readers-web)\n", - " Using cached sortedcontainers-2.4.0-py2.py3-none-any.whl.metadata (10 kB)\n", - "Collecting outcome (from trio~=0.17->selenium<5.0.0,>=4.17.2->llama-index-readers-web)\n", - " Using cached outcome-1.3.0.post0-py2.py3-none-any.whl.metadata (2.6 kB)\n", - "Collecting wsproto>=0.14 (from trio-websocket~=0.9->selenium<5.0.0,>=4.17.2->llama-index-readers-web)\n", - " Using cached wsproto-1.2.0-py3-none-any.whl.metadata (5.6 kB)\n", - "Requirement already satisfied: mypy-extensions>=0.3.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from typing-inspect>=0.8.0->llama-index-core<0.11.0,>=0.10.27->llama-index) (1.0.0)\n", - "Collecting pysocks!=1.5.7,<2.0,>=1.5.6 (from urllib3[socks]<3,>=1.26->selenium<5.0.0,>=4.17.2->llama-index-readers-web)\n", - " Using cached PySocks-1.7.1-py3-none-any.whl.metadata (13 kB)\n", - "Requirement already satisfied: marshmallow<4.0.0,>=3.18.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from dataclasses-json->llama-index-core<0.11.0,>=0.10.27->llama-index) (3.21.1)\n", - "Requirement already satisfied: pytz>=2020.1 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from pandas->llama-index-core<0.11.0,>=0.10.27->llama-index) (2023.4)\n", - "Requirement already satisfied: tzdata>=2022.7 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from pandas->llama-index-core<0.11.0,>=0.10.27->llama-index) (2024.1)\n", - "Requirement already satisfied: annotated-types>=0.4.0 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from pydantic>=1.10->llamaindex-py-client<0.2.0,>=0.1.16->llama-index-core<0.11.0,>=0.10.27->llama-index) (0.6.0)\n", - "Requirement already satisfied: pydantic-core==2.20.1 in /Users/sarahwooders/repos/letta-main/Letta/env/lib/python3.12/site-packages (from pydantic>=1.10->llamaindex-py-client<0.2.0,>=0.1.16->llama-index-core<0.11.0,>=0.10.27->llama-index) (2.20.1)\n", - "Using cached llama_index_readers_web-0.1.23-py3-none-any.whl (72 kB)\n", - "Using cached chromedriver_autoinstaller-0.6.4-py3-none-any.whl (7.6 kB)\n", - "Using cached newspaper3k-0.2.8-py3-none-any.whl (211 kB)\n", - "Using cached playwright-1.46.0-py3-none-macosx_11_0_universal2.whl (34.8 MB)\n", - "Using cached pyee-11.1.0-py3-none-any.whl (15 kB)\n", - "Downloading selenium-4.24.0-py3-none-any.whl (9.6 MB)\n", - " 25l ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.0/9.6 MB ? eta -:--:--━━━╸━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 0.8/9.6 MB 23.7 MB/s eta 0:00:01━━━━━━━━━━━━━╸━━━━━━━━━━━━━━━━━━━━━━━━━━ 3.3/9.6 MB 45.3 MB/s eta 0:00:01━━━━━━━━━━━━━━━━━━━━━━━╸━━━━━━━━━━━━━━━━ 5.7/9.6 MB 52.2 MB/s eta 0:00:01━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸━━━━━━━ 7.8/9.6 MB 54.0 MB/s eta 0:00:01━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╸ 9.6/9.6 MB 57.3 MB/s eta 0:00:01━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 9.6/9.6 MB 51.1 MB/s eta 0:00:00\n", - "\u001b[?25hUsing cached cssselect-1.2.0-py2.py3-none-any.whl (18 kB)\n", - "Using cached feedparser-6.0.11-py3-none-any.whl (81 kB)\n", - "Using cached lxml-5.3.0-cp312-cp312-macosx_10_9_x86_64.whl (4.4 MB)\n", - "Using cached tldextract-5.1.2-py3-none-any.whl (97 kB)\n", - "Using cached trio-0.26.2-py3-none-any.whl (475 kB)\n", - "Using cached trio_websocket-0.11.1-py3-none-any.whl (17 kB)\n", - "Using cached websocket_client-1.8.0-py3-none-any.whl (58 kB)\n", - "Using cached PySocks-1.7.1-py3-none-any.whl (16 kB)\n", - "Using cached requests_file-2.1.0-py2.py3-none-any.whl (4.2 kB)\n", - "Using cached wsproto-1.2.0-py3-none-any.whl (24 kB)\n", - "Using cached outcome-1.3.0.post0-py2.py3-none-any.whl (10 kB)\n", - "Using cached sortedcontainers-2.4.0-py2.py3-none-any.whl (29 kB)\n", - "Installing collected packages: tinysegmenter, sortedcontainers, sgmllib3k, jieba3k, wsproto, websocket-client, pysocks, pyee, outcome, lxml, html2text, feedparser, cssselect, chromedriver-autoinstaller, trio, spider-client, requests-file, playwright, feedfinder2, trio-websocket, tldextract, selenium, newspaper3k, llama-index-readers-web\n", - " Attempting uninstall: websocket-client\n", - " Found existing installation: websocket-client 1.7.0\n", - " Uninstalling websocket-client-1.7.0:\n", - " Successfully uninstalled websocket-client-1.7.0\n", - " Attempting uninstall: html2text\n", - " Found existing installation: html2text 2020.1.16\n", - " Uninstalling html2text-2020.1.16:\n", - " Successfully uninstalled html2text-2020.1.16\n", - "ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\n", - "pyletta 0.3.19 requires docstring-parser<0.16,>=0.15, but you have docstring-parser 0.11 which is incompatible.\n", - "pyletta 0.3.19 requires html2text<2021.0.0,>=2020.1.16, but you have html2text 2024.2.26 which is incompatible.\n", - "Successfully installed chromedriver-autoinstaller-0.6.4 cssselect-1.2.0 feedfinder2-0.0.4 feedparser-6.0.11 html2text-2024.2.26 jieba3k-0.35.1 llama-index-readers-web-0.1.23 lxml-5.3.0 newspaper3k-0.2.8 outcome-1.3.0.post0 playwright-1.46.0 pyee-11.1.0 pysocks-1.7.1 requests-file-2.1.0 selenium-4.24.0 sgmllib3k-1.0.0 sortedcontainers-2.4.0 spider-client-0.0.27 tinysegmenter-0.3 tldextract-5.1.2 trio-0.26.2 trio-websocket-0.11.1 websocket-client-1.8.0 wsproto-1.2.0\n", - "\n", - "[notice] A new release of pip is available: 24.0 -> 24.2\n", - "[notice] To update, run: pip install --upgrade pip\n", - "Note: you may need to restart the kernel to use updated packages.\n" - ] - } - ], - "source": [ - "%pip install llama-index llama-index-readers-web" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "id": "79ef1d4a-0d7f-4ba5-8932-e2cdc4c65e48", - "metadata": {}, - "outputs": [], - "source": [ - "from letta.data_sources.connectors import DataConnector \n", - "from letta.schemas.file import FileMetadata\n", - "from llama_index.core import Document as LlamaIndexDocument\n", - "from llama_index.core import SummaryIndex\n", - "from llama_index.readers.web import SimpleWebPageReader\n", - "\n", - "from typing import Iterator, Tuple, Dict, List\n", - "\n", - "class MyCustomConnector(DataConnector): \n", - "\n", - " def __init__(self, web_pages: List[str]): \n", - " self.web_pages = web_pages\n", - "\n", - " def generate_documents(self) -> Iterator[Tuple[str, Dict]]: \n", - " documents = SimpleWebPageReader(html_to_text=True).load_data(\n", - " self.web_pages\n", - " )\n", - " for document, web_page in zip(documents, self.web_pages): \n", - " metadata = {\"source_page\": web_page, \"my_metadata\": \"example\"}\n", - " yield document.text, metadata\n", - " \n", - " def generate_passages(\n", - " self, \n", - " documents: List[Document], \n", - " chunk_size: int = 1024\n", - " ) -> Iterator[Tuple[str, Dict]]: # -> Iterator[Passage]:\n", - " from llama_index.core.node_parser import TokenTextSplitter\n", - "\n", - " parser = TokenTextSplitter(chunk_size=chunk_size)\n", - " for document in documents:\n", - " llama_index_docs = [LlamaIndexDocument(text=document.text)]\n", - " nodes = parser.get_nodes_from_documents(llama_index_docs)\n", - " for node in nodes:\n", - " yield node.text, None" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "id": "67ab8635-d668-456f-8869-40bdaea26275", - "metadata": {}, - "outputs": [], - "source": [ - "web_pages = [\n", - " \"https://en.wikipedia.org/wiki/Memory\", \n", - " #\"https://en.wikipedia.org/wiki/Brain\"\n", - "]\n", - "\n", - "connector = MyCustomConnector(web_pages)" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "id": "fe63e6cf-e3be-437a-83cf-d58bd0e61b36", - "metadata": {}, - "outputs": [], - "source": [ - "source = client.create_source(\"wikipedia_brain\")" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "id": "b06020fd-d561-4b85-ab81-3ff7780d129d", - "metadata": {}, - "outputs": [], - "source": [ - "client.load_data(connector, source_name=source.name)" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "id": "484a2d62-8482-40e5-8bf7-91c937acd837", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Letta.letta.server.server - INFO - Created new agent from config: \n" - ] - } - ], - "source": [ - "from letta.schemas.memory import ChatMemory\n", - "\n", - "wiki_persona = \"You a study assistant with a great source of knowlege \" \\\n", - "+ \"stored in archival. You should always search your archival memory \" \\\n", - "+ \"before responding to the human's queries. \"\n", - "\n", - "wiki_agent = client.create_agent(\n", - " name=\"wiki_agent\", \n", - " memory=ChatMemory(\n", - " human=\"Name: Sarah. Occupation: Biology PhD\", \n", - " persona=wiki_persona\n", - " )\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 18, - "id": "5f4820c8-cac4-4906-bd2c-f63ed18620e7", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Letta.letta.server.server - INFO - Grabbing agent user_id=user-552dee3c-baaf-443a-9d23-8bb54f4af964 agent_id=agent-897ef46b-2682-4d79-be8a-3ad0250ee084 from database\n", - "Letta.letta.server.server - INFO - Creating an agent object\n", - "Letta.letta.server.server - INFO - Adding agent to the agent cache: user_id=user-552dee3c-baaf-443a-9d23-8bb54f4af964, agent_id=agent-897ef46b-2682-4d79-be8a-3ad0250ee084\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|█████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 3/3 [00:00<00:00, 4.74it/s]\n" - ] - } - ], - "source": [ - "client.attach_source_to_agent(agent_id=wiki_agent.id, source_id=source.id)" - ] - }, - { - "cell_type": "code", - "execution_count": 20, - "id": "2a2730aa-d2a1-433a-bd28-fcae9a92039d", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[Message(id='message-f44014c3-f5aa-46b3-b64e-fbdd6e5550b6', role=, text=\"Let's search the archival memory to see what information I have that could help with this question.\", user_id='user-552dee3c-baaf-443a-9d23-8bb54f4af964', agent_id='agent-897ef46b-2682-4d79-be8a-3ad0250ee084', model='gpt-4', name=None, created_at=datetime.datetime(2024, 9, 3, 22, 10, 19, 834760, tzinfo=datetime.timezone.utc), tool_calls=[ToolCall(id='f9389727-5b10-4e92-bf6b-6f74c', type='function', function=ToolCallFunction(name='archival_memory_search', arguments='{\\n \"query\": \"role of memory\",\\n \"request_heartbeat\": true\\n}'))], tool_call_id=None),\n", - " Message(id='message-b9c79d85-5081-4202-9d9c-5ad56c13baae', role=, text='{\\n \"status\": \"OK\",\\n \"message\": \"Showing 5 of 5 results (page 0/0): [\\\\n \\\\\"timestamp: 2024-09-03 03:10:20 PM PDT-0700, memory: as a first kiss, first day of school\\\\\\\\nor first time winning a championship. These are key events in one\\'s life that\\\\\\\\ncan be remembered clearly.\\\\\\\\n\\\\\\\\nResearch suggests that declarative memory is supported by several functions of\\\\\\\\nthe medial temporal lobe system which includes the hippocampus.[49]\\\\\\\\n[Autobiographical memory](/wiki/Autobiographical_memory \\\\\\\\\\\\\"Autobiographical\\\\\\\\nmemory\\\\\\\\\\\\\") – memory for particular events within one\\'s own life – is generally\\\\\\\\nviewed as either equivalent to, or a subset of, episodic memory. [Visual\\\\\\\\nmemory](/wiki/Visual_memory \\\\\\\\\\\\\"Visual memory\\\\\\\\\\\\\") is part of memory preserving some\\\\\\\\ncharacteristics of our senses pertaining to visual experience. One is able to\\\\\\\\nplace in memory information that resembles objects, places, animals or people\\\\\\\\nin sort of a [mental image](/wiki/Mental_image \\\\\\\\\\\\\"Mental image\\\\\\\\\\\\\"). Visual memory\\\\\\\\ncan result in [priming](/wiki/Priming_\\\\\\\\\\\\\\\\(psychology\\\\\\\\\\\\\\\\) \\\\\\\\\\\\\"Priming \\\\\\\\\\\\\\\\(psychology\\\\\\\\\\\\\\\\)\\\\\\\\\\\\\")\\\\\\\\nand it is assumed some kind of perceptual representational system underlies\\\\\\\\nthis phenomenon.[49]\\\\\\\\n\\\\\\\\n#### Procedural\\\\\\\\n\\\\\\\\n[[edit](/w/index.php?title=Memory&action=edit§ion=10 \\\\\\\\\\\\\"Edit section:\\\\\\\\nProcedural\\\\\\\\\\\\\")]\\\\\\\\n\\\\\\\\nIn contrast, [procedural memory](/wiki/Procedural_memory \\\\\\\\\\\\\"Procedural memory\\\\\\\\\\\\\")\\\\\\\\n(or _[implicit memory](/wiki/Implicit_memory \\\\\\\\\\\\\"Implicit memory\\\\\\\\\\\\\")_) is not\\\\\",\\\\n \\\\\"timestamp: 2024-09-03 03:10:20 PM PDT-0700, memory: \\\\\\\\\\\\\"Attention\\\\\\\\\\\\\"). Attention plays a key role in storing information into long-term memory; without proper attention, the information might not be stored, making it impossible to be retrieved later.\\\\\\\\n\\\\\\\\n## Physiology\\\\\\\\n\\\\\\\\n[[edit](/w/index.php?title=Memory&action=edit§ion=16 \\\\\\\\\\\\\"Edit section:\\\\\\\\nPhysiology\\\\\\\\\\\\\")]\\\\\\\\n\\\\\\\\nBrain areas involved in the [neuroanatomy of\\\\\\\\nmemory](/wiki/Neuroanatomy_of_memory \\\\\\\\\\\\\"Neuroanatomy of memory\\\\\\\\\\\\\") such as the\\\\\\\\n[hippocampus](/wiki/Hippocampus \\\\\\\\\\\\\"Hippocampus\\\\\\\\\\\\\"), the [amygdala](/wiki/Amygdala\\\\\\\\n\\\\\\\\\\\\\"Amygdala\\\\\\\\\\\\\"), the [striatum](/wiki/Striatum \\\\\\\\\\\\\"Striatum\\\\\\\\\\\\\"), or the [mammillary\\\\\\\\nbodies](/wiki/Mammillary_bodies \\\\\\\\\\\\\"Mammillary bodies\\\\\\\\\\\\\") are thought to be\\\\\\\\ninvolved in specific types of memory. For example, the hippocampus is believed\\\\\\\\nto be involved in spatial learning and [declarative\\\\\\\\nlearning](/wiki/Declarative_learning \\\\\\\\\\\\\"Declarative learning\\\\\\\\\\\\\"), while the\\\\\\\\namygdala is thought to be involved in [emotional\\\\\\\\nmemory](/wiki/Emotion_and_memory \\\\\\\\\\\\\"Emotion and memory\\\\\\\\\\\\\").[68]\\\\\\\\n\\\\\\\\nDamage to certain areas in patients and animal models and subsequent memory\\\\\\\\ndeficits is a primary source of information. However, rather than implicating\\\\\\\\na specific area, it could be that damage to adjacent areas, or\\\\\",\\\\n \\\\\"timestamp: 2024-09-03 03:10:20 PM PDT-0700, memory: stored, and\\\\\\\\nretrieved when needed. It is the retention of information over time for the\\\\\\\\npurpose of influencing future action.[1] If [past\\\\\\\\nevents](/wiki/Foresight_\\\\\\\\\\\\\\\\(psychology\\\\\\\\\\\\\\\\) \\\\\\\\\\\\\"Foresight \\\\\\\\\\\\\\\\(psychology\\\\\\\\\\\\\\\\)\\\\\\\\\\\\\") could not\\\\\\\\nbe remembered, it would be impossible for language, relationships, or\\\\\\\\n[personal identity](/wiki/Personal_identity \\\\\\\\\\\\\"Personal identity\\\\\\\\\\\\\") to\\\\\\\\ndevelop.[2] Memory loss is usually described as\\\\\\\\n[forgetfulness](/wiki/Forgetting \\\\\\\\\\\\\"Forgetting\\\\\\\\\\\\\") or [amnesia](/wiki/Amnesia\\\\\\\\n\\\\\\\\\\\\\"Amnesia\\\\\\\\\\\\\").[3][4][5][6][7][8]\\\\\\\\n\\\\\\\\nMemory is often understood as an [informational\\\\\\\\nprocessing](/wiki/Information_processing_\\\\\\\\\\\\\\\\(psychology\\\\\\\\\\\\\\\\) \\\\\\\\\\\\\"Information\\\\\\\\nprocessing \\\\\\\\\\\\\\\\(psychology\\\\\\\\\\\\\\\\)\\\\\\\\\\\\\") system with explicit and implicit functioning that\\\\\\\\nis made up of a [sensory processor](/wiki/Sensory_processor \\\\\\\\\\\\\"Sensory\\\\\\\\nprocessor\\\\\\\\\\\\\"), [short-term](/wiki/Short-term_memory \\\\\\\\\\\\\"Short-term memory\\\\\\\\\\\\\") (or\\\\\\\\n[working](/wiki/Working_memory \\\\\\\\\\\\\"Working memory\\\\\\\\\\\\\")) memory, and [long-term\\\\\\\\nmemory](/wiki/Long-term_memory \\\\\\\\\\\\\"Long-term memory\\\\\\\\\\\\\").[9] This can be related to\\\\\\\\nthe [neuron](/wiki/Neuron \\\\\\\\\\\\\"Neuron\\\\\\\\\\\\\"). The sensory processor allows information\\\\\\\\nfrom the outside world to be sensed in the form of chemical and physical\\\\\\\\nstimuli and\\\\\",\\\\n \\\\\"timestamp: 2024-09-03 03:10:20 PM PDT-0700, memory: \\\\\\\\\\\\\"False memory\\\\\\\\\\\\\") as the memories are not properly\\\\\\\\ntransferred to long-term memory. One of the primary functions of sleep is\\\\\\\\nthought to be the improvement of the consolidation of information, as several\\\\\\\\nstudies have demonstrated that memory depends on getting sufficient sleep\\\\\\\\nbetween training and test.[122] Additionally, data obtained from neuroimaging\\\\\\\\nstudies have shown activation patterns in the sleeping brain that mirror those\\\\\\\\nrecorded during the learning of tasks from the previous day,[122] suggesting\\\\\\\\nthat new memories may be solidified through such rehearsal.[123]\\\\\\\\n\\\\\\\\n## Construction for general manipulation\\\\\\\\n\\\\\\\\n[[edit](/w/index.php?title=Memory&action=edit§ion=30 \\\\\\\\\\\\\"Edit section:\\\\\\\\nConstruction for general manipulation\\\\\\\\\\\\\")]\\\\\\\\n\\\\\\\\nAlthough people often think that memory operates like recording equipment,\\\\\\\\nthis is not the case. The molecular mechanisms underlying the induction and\\\\\\\\nmaintenance of memory are very dynamic and comprise distinct phases covering a\\\\\\\\ntime window from seconds to even a lifetime.[124] In fact, research has\\\\\\\\nrevealed that our memories are constructed: \\\\\\\\\\\\\"current hypotheses suggest that\\\\\\\\nconstructive processes allow individuals to simulate and imagine future\\\\\\\\nepisodes,[125] happenings, and scenarios. Since the future is not an exact\\\\\\\\nrepetition of the past, simulation of future episodes requires a complex\\\\\\\\nsystem that can draw on the past in a manner that flexibly extracts and\\\\\\\\nrecombines elements of previous experiences – a constructive rather than a\\\\\\\\nreproductive system.\\\\\\\\\\\\\"[72] People can construct their\\\\\",\\\\n \\\\\"timestamp: 2024-09-03 03:10:20 PM PDT-0700, memory: \\\\\\\\\\\\\"Decision-making\\\\\\\\\\\\\")\\\\\\\\n * [Problem solving](/wiki/Problem_solving \\\\\\\\\\\\\"Problem solving\\\\\\\\\\\\\")\\\\\\\\n\\\\\\\\n \\\\\\\\n[Numerical cognition](/wiki/Numerical_cognition \\\\\\\\\\\\\"Numerical cognition\\\\\\\\\\\\\") \\\\\\\\n \\\\\\\\n * [Numerosity adaptation effect](/wiki/Numerosity_adaptation_effect \\\\\\\\\\\\\"Numerosity adaptation effect\\\\\\\\\\\\\")\\\\\\\\n * [Approximate number system](/wiki/Approximate_number_system \\\\\\\\\\\\\"Approximate number system\\\\\\\\\\\\\")\\\\\\\\n * [Parallel individuation system](/wiki/Parallel_individuation_system \\\\\\\\\\\\\"Parallel individuation system\\\\\\\\\\\\\")\\\\\\\\n\\\\\\\\n \\\\\\\\n \\\\\\\\n * [v](/wiki/Template:Cognitive \\\\\\\\\\\\\"Template:Cognitive\\\\\\\\\\\\\")\\\\\\\\n * [t](/wiki/Template_talk:Cognitive \\\\\\\\\\\\\"Template talk:Cognitive\\\\\\\\\\\\\")\\\\\\\\n * [e](/wiki/Special:EditPage/Template:Cognitive \\\\\\\\\\\\\"Special:EditPage/Template:Cognitive\\\\\\\\\\\\\")\\\\\\\\n\\\\\\\\n \\\\\\\\n[![](//upload.wikimedia.org/wikipedia/commons/thumb/5/58/Memory.gif/220px-\\\\\\\\nMemory.gif)](/wiki/File:Memory.gif)Overview of the forms and functions of\\\\\\\\nmemory\\\\\\\\n\\\\\\\\n**Memory** is the faculty of the [mind](/wiki/Mind \\\\\\\\\\\\\"Mind\\\\\\\\\\\\\") by which\\\\\\\\n[data](/wiki/Data \\\\\\\\\\\\\"Data\\\\\\\\\\\\\") or [information](/wiki/Information \\\\\\\\\\\\\"Information\\\\\\\\\\\\\") is\\\\\\\\n[encoded](/wiki/Encoding_\\\\\\\\\\\\\\\\(memory\\\\\\\\\\\\\\\\) \\\\\\\\\\\\\"Encoding \\\\\\\\\\\\\\\\(memory\\\\\\\\\\\\\\\\)\\\\\\\\\\\\\"), stored, and\\\\\\\\nretrieved when needed. It is the retention of information over time for\\\\\"\\\\n]\",\\n \"time\": \"2024-09-03 03:10:20 PM PDT-0700\"\\n}', user_id='user-552dee3c-baaf-443a-9d23-8bb54f4af964', agent_id='agent-897ef46b-2682-4d79-be8a-3ad0250ee084', model='gpt-4', name='archival_memory_search', created_at=datetime.datetime(2024, 9, 3, 22, 10, 20, 49380, tzinfo=datetime.timezone.utc), tool_calls=None, tool_call_id='f9389727-5b10-4e92-bf6b-6f74c'),\n", - " Message(id='message-96334bf0-c2ea-49e7-a872-d6e8685e85f8', role=, text='Based on the details retrieved from archival memory, memory has several key roles and forms:\\n\\n1. It allows information from the past to influence future actions, making language, relationships, and personal identity develop. \\n2. There are multiple types of memory: sensory, short-term, and long-term. Each plays a key role in storing and retrieving information.\\n3. Certain areas of the brain are involved in specific types of memory. For example, the hippocampus is involved in spatial and declarative learning, while the amygdala is associated with emotional memory.\\n4. Other significant elements include the role of attention in storing information into long-term memory. Without proper attention, the information might not be stored, and it will be impossible to retrieve later. \\n5. Memory is not static, but very dynamic, covering a time window from seconds to even a lifetime. In fact, our memories are not just recordings but constructions that allow us to simulate and imagine future episodes. \\n\\nI hope this insights provide you a good overview on the role of memory. Is there something more specific you would like to know regarding memory?', user_id='user-552dee3c-baaf-443a-9d23-8bb54f4af964', agent_id='agent-897ef46b-2682-4d79-be8a-3ad0250ee084', model='gpt-4', name=None, created_at=datetime.datetime(2024, 9, 3, 22, 10, 44, 818746, tzinfo=datetime.timezone.utc), tool_calls=[ToolCall(id='eb98aedd-bd4d-41b0-bbbe-208fd', type='function', function=ToolCallFunction(name='send_message', arguments='{\\n \"message\": \"Based on the details retrieved from archival memory, memory has several key roles and forms:\\\\n\\\\n1. It allows information from the past to influence future actions, making language, relationships, and personal identity develop. \\\\n2. There are multiple types of memory: sensory, short-term, and long-term. Each plays a key role in storing and retrieving information.\\\\n3. Certain areas of the brain are involved in specific types of memory. For example, the hippocampus is involved in spatial and declarative learning, while the amygdala is associated with emotional memory.\\\\n4. Other significant elements include the role of attention in storing information into long-term memory. Without proper attention, the information might not be stored, and it will be impossible to retrieve later. \\\\n5. Memory is not static, but very dynamic, covering a time window from seconds to even a lifetime. In fact, our memories are not just recordings but constructions that allow us to simulate and imagine future episodes.\\\\n\\\\nI hope this insights provide you a good overview on the role of memory. Is there something more specific you would like to know regarding memory?\"\\n}'))], tool_call_id=None),\n", - " Message(id='message-323b988f-5ba5-45d0-826d-326a64f42cf6', role=, text='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-09-03 03:10:44 PM PDT-0700\"\\n}', user_id='user-552dee3c-baaf-443a-9d23-8bb54f4af964', agent_id='agent-897ef46b-2682-4d79-be8a-3ad0250ee084', model='gpt-4', name='send_message', created_at=datetime.datetime(2024, 9, 3, 22, 10, 44, 819711, tzinfo=datetime.timezone.utc), tool_calls=None, tool_call_id='eb98aedd-bd4d-41b0-bbbe-208fd')]" - ] - }, - "execution_count": 20, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "response = client.send_message(\n", - " agent_id=wiki_agent.id, \n", - " message=\"what is the role of memory? search archival again\", \n", - " role=\"user\"\n", - ")\n", - "response.messages" - ] - }, - { - "cell_type": "markdown", - "id": "fc6352f7-1695-47e4-911b-a88c92dc4a27", - "metadata": {}, - "source": [ - "## Connecting to external data via tools\n", - "In the last section, we went over how to store data inside of Letta's archival memory. However in many cases, it can be easier to simply connect a Letta agent to access an external data source directly via a tool. " - ] - }, - { - "cell_type": "markdown", - "id": "c34b0aaa-c4d0-41d3-b85e-aef893573576", - "metadata": {}, - "source": [ - "Lets define a function that pretends to access an external database to lookup someone's birthday. " - ] - }, - { - "cell_type": "code", - "execution_count": 21, - "id": "76ee42bb-c111-47e0-b258-f958738b6a47", - "metadata": {}, - "outputs": [], - "source": [ - "def query_birthday_db(self, name: str): \n", - " \"\"\"\n", - " This tool queries an external database to \n", - " lookup the birthday of someone given their name.\n", - "\n", - " Args: \n", - " name (str): The name to look up \n", - "\n", - " Returns: \n", - " birthday (str): The birthday in mm-dd-yyyy format\n", - " \n", - " \"\"\"\n", - " my_fake_data = {\n", - " \"bob\": \"03-06-1997\", \n", - " \"sarah\": \"03-06-1997\"\n", - " } \n", - " name = name.lower() \n", - " if name not in my_fake_data: \n", - " return None\n", - " else: \n", - " return my_fake_data[name]" - ] - }, - { - "cell_type": "markdown", - "id": "73de8d11-6844-4dee-b2f6-1d5bc775ab19", - "metadata": {}, - "source": [ - "### Adding a custom tool to Letta \n", - "We can access this external data via an agent by adding the function as a tool to Letta. " - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "220fdbef-4c81-4964-9264-c32bca25fb9a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Tool(description=None, source_type='python', module=None, user_id='user-552dee3c-baaf-443a-9d23-8bb54f4af964', id='tool-7559f3f1-e988-4363-a1dd-2dfff8d91a64', name='query_birthday_db', tags=['extras'], source_code='def query_birthday_db(self, name: str): \\n \"\"\"\\n This tool queries an external database to \\n lookup the birthday of someone given their name.\\n\\n Args: \\n name (str): The name to look up \\n\\n Returns: \\n birthday (str): The birthday in mm-dd-yyyy format\\n\\n \"\"\"\\n my_fake_data = {\\n \"bob\": \"03-06-1997\", \\n \"sarah\": \"03-06-1997\"\\n } \\n name = name.lower() \\n if name not in my_fake_data: \\n return None\\n else: \\n return my_fake_data[name]\\n', json_schema={'name': 'query_birthday_db', 'description': 'This tool queries an external database to ', 'parameters': {'type': 'object', 'properties': {'name': {'type': 'string', 'description': 'The name to look up '}, 'request_heartbeat': {'type': 'boolean', '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.\"}}, 'required': ['name', 'request_heartbeat']}})" - ] - }, - "execution_count": 23, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "tool = client.create_tool(query_birthday_db, tags=[\"extras\"])\n", - "tool" - ] - }, - { - "cell_type": "markdown", - "id": "b5db88d7-e65a-4990-8228-5f85da729392", - "metadata": {}, - "source": [ - "We can include the tool name as an extra tool when creating an agent: " - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "8792ecc2-a69b-4e31-9976-4059fde9481b", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Letta.letta.server.server - INFO - Created new agent from config: \n" - ] - } - ], - "source": [ - "agent_state = client.create_agent(\n", - " name=\"birthday_agent\", \n", - " tools=[tool.name], \n", - " memory=ChatMemory(\n", - " human=\"My name is Sarah\", \n", - " persona=\"You are a agent with access to a birthday_db \" \\\n", - " + \"that you use to lookup information about users' birthdays.\"\n", - " )\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 26, - "id": "fb877a44-d750-4648-ae46-58275f6305c4", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Letta.letta.server.server - INFO - Grabbing agent user_id=user-552dee3c-baaf-443a-9d23-8bb54f4af964 agent_id=agent-f207e43b-2021-45be-9dde-48822c898e77 from database\n", - "Letta.letta.server.server - INFO - Creating an agent object\n", - "Letta.letta.server.server - INFO - Adding agent to the agent cache: user_id=user-552dee3c-baaf-443a-9d23-8bb54f4af964, agent_id=agent-f207e43b-2021-45be-9dde-48822c898e77\n", - "[Message(id='message-d9b432de-2bb6-4c85-8bb9-a31067e271fc', role=, text=\"Let's access the birthday_db and find out Sarah's birthday.\", user_id='user-552dee3c-baaf-443a-9d23-8bb54f4af964', agent_id='agent-f207e43b-2021-45be-9dde-48822c898e77', model='gpt-4', name=None, created_at=datetime.datetime(2024, 9, 3, 22, 11, 24, 961893, tzinfo=datetime.timezone.utc), tool_calls=[ToolCall(id='cad6f053-27d7-4281-a04b-05a57', type='function', function=ToolCallFunction(name='query_birthday_db', arguments='{\\n \"name\": \"Sarah\",\\n \"request_heartbeat\": true\\n}'))], tool_call_id=None),\n", - " Message(id='message-f27fd0a8-be72-457c-8b3c-849818aeec4d', role=, text='{\\n \"status\": \"OK\",\\n \"message\": \"03-06-1997\",\\n \"time\": \"2024-09-03 03:11:24 PM PDT-0700\"\\n}', user_id='user-552dee3c-baaf-443a-9d23-8bb54f4af964', agent_id='agent-f207e43b-2021-45be-9dde-48822c898e77', model='gpt-4', name='query_birthday_db', created_at=datetime.datetime(2024, 9, 3, 22, 11, 24, 962306, tzinfo=datetime.timezone.utc), tool_calls=None, tool_call_id='cad6f053-27d7-4281-a04b-05a57'),\n", - " Message(id='message-7423c90e-822f-40ac-aff9-8791a360dd31', role=, text=\"I found the information. Now, let's communicate this back to Sarah in a friendly and human-like manner.\", user_id='user-552dee3c-baaf-443a-9d23-8bb54f4af964', agent_id='agent-f207e43b-2021-45be-9dde-48822c898e77', model='gpt-4', name=None, created_at=datetime.datetime(2024, 9, 3, 22, 11, 29, 400783, tzinfo=datetime.timezone.utc), tool_calls=[ToolCall(id='1abfa1e3-a266-48a3-8773-d6087', type='function', function=ToolCallFunction(name='send_message', arguments='{\\n \"message\": \"Hello Sarah, your birthday is on the 6th of March, 1997. Isn\\'t it wonderful to celebrate another year of life?\"\\n}'))], tool_call_id=None),\n", - " Message(id='message-a0295ddf-29b1-44d4-bf75-7b6b249d79f7', role=, text='{\\n \"status\": \"OK\",\\n \"message\": \"None\",\\n \"time\": \"2024-09-03 03:11:29 PM PDT-0700\"\\n}', user_id='user-552dee3c-baaf-443a-9d23-8bb54f4af964', agent_id='agent-f207e43b-2021-45be-9dde-48822c898e77', model='gpt-4', name='send_message', created_at=datetime.datetime(2024, 9, 3, 22, 11, 29, 401789, tzinfo=datetime.timezone.utc), tool_calls=None, tool_call_id='1abfa1e3-a266-48a3-8773-d6087')]\n" - ] - } - ], - "source": [ - "response = client.send_message(\n", - " agent_id=agent_state.id, \n", - " message = \"When is my birthday?\", \n", - " role = \"user\"\n", - ") \n", - "pprint(response.messages)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "letta", - "language": "python", - "name": "letta" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/examples/notebooks/multi_agent.ipynb b/examples/notebooks/multi_agent.ipynb deleted file mode 100644 index fc23b3ffb9..0000000000 --- a/examples/notebooks/multi_agent.ipynb +++ /dev/null @@ -1,275 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 31, - "id": "78fb59cf-89fd-4b30-8a1c-d1ae4bfd3daf", - "metadata": {}, - "outputs": [], - "source": [ - "from letta import create_client, Admin\n", - "from letta.client.client import LocalClient, RESTClient " - ] - }, - { - "cell_type": "code", - "execution_count": 32, - "id": "9269eda2-3108-4955-86ab-b406d51f562a", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "UUID('00000000-0000-0000-0000-000000000000')" - ] - }, - "execution_count": 32, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "client = create_client() \n", - "client.user_id" - ] - }, - { - "cell_type": "code", - "execution_count": 33, - "id": "879710d4-21c7-43ec-8d00-73e618f55693", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ListModelsResponse(models=[LLMConfigModel(model='gpt-4o-mini', model_endpoint_type='openai', model_endpoint='https://api.openai.com/v1', model_wrapper=None, context_window=8192)])" - ] - }, - "execution_count": 33, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "client.list_models()" - ] - }, - { - "cell_type": "markdown", - "id": "af6ea8eb-fc6b-4de5-ae79-c2b684a81f17", - "metadata": {}, - "source": [ - "## Create a key from the Admin portal \n", - "(This is to allow viewing agents on the dev portal) " - ] - }, - { - "cell_type": "code", - "execution_count": 35, - "id": "715fa669-3fc6-4579-96a9-c4a730f43e29", - "metadata": {}, - "outputs": [], - "source": [ - "admin_client = Admin(base_url=\"http://localhost:8283\", token=\"lettaadmin\")" - ] - }, - { - "cell_type": "code", - "execution_count": 36, - "id": "1782934f-7884-4ee7-ad09-5ae33efa3b2e", - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "CreateAPIKeyResponse(api_key='sk-45cc3e1fd35a3fac3a2ad959fc23877a0476181e8b0a5557')" - ] - }, - "execution_count": 36, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "admin_client.create_key(user_id=client.user_id, key_name=\"key\")" - ] - }, - { - "cell_type": "code", - "execution_count": 37, - "id": "b29bac8d-2a15-45de-a60d-6d94275443f5", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Letta.letta.server.server - INFO - Created new agent from config: \n" - ] - } - ], - "source": [ - "agent_state = client.create_agent()" - ] - }, - { - "cell_type": "markdown", - "id": "5fbc43c8-9536-4107-a64d-6e702083242b", - "metadata": {}, - "source": [ - "## Create an agent " - ] - }, - { - "cell_type": "code", - "execution_count": 38, - "id": "f0a388b5-2d00-4f3e-8a5b-b768da02ac8e", - "metadata": {}, - "outputs": [], - "source": [ - "def read_resume(self, name: str): \n", - " \"\"\"\n", - " Read the resume data for a candidate given the name\n", - "\n", - " Args: \n", - " name (str): Candidate name \n", - "\n", - " Returns: \n", - " resume_data (str): Candidate's resume data \n", - " \"\"\"\n", - " import os\n", - " filepath = os.path.join(\"data\", \"resumes\", name.lower().replace(\" \", \"_\") + \".txt\")\n", - " #print(\"read\", filepath)\n", - " return open(filepath).read()\n", - "\n", - "def submit_candidate_for_outreach(self, candidate_name: str, resume: str, justification: str): \n", - " \"\"\"\n", - " Submit a candidate for outreach. \n", - "\n", - " Args: \n", - " candidate_name (str): The name of the candidate\n", - " resume (str): The text representation of the candidate's resume \n", - " justification (str): Summary reason for why the candidate is good and should be reached out to\n", - " \"\"\"\n", - " from letta import create_client \n", - " client = create_client()\n", - " message = \"Reach out to the following candidate. \" \\\n", - " + f\"Name: {candidate_name}\\n\" \\\n", - " + f\"Resume Data: {resume}\\n\" \\\n", - " + f\"Justification: {justification}\"\n", - " # NOTE: we will define this agent later \n", - " #print(\"submit for outreach\", message)\n", - " response = client.send_message(agent_name=\"outreach_agent\", role=\"user\", message=message) # TODO: implement this\n", - " #print(respose.messages)\n", - "\n", - "# TODO: add an archival andidate tool (provide justification) \n", - "\n", - "read_resume_tool = client.create_tool(read_resume) \n", - "submit_candidate_tool = client.create_tool(submit_candidate_for_outreach)" - ] - }, - { - "cell_type": "code", - "execution_count": 39, - "id": "d2b0f66f-6cc3-471f-b2c7-49f51f5bbb7b", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Letta.letta.server.server - INFO - Created new agent from config: \n" - ] - } - ], - "source": [ - "from letta.memory import ChatMemory\n", - "\n", - "company_description = \"The company is called AgentOS and is building AI tools to make it easier to create and deploy LLM agents.\"\n", - "skills = \"Front-end (React, Typescript), software engineering (ideally Python), and experience with LLMs.\"\n", - "\n", - "\n", - "leadgen_agent = client.create_agent(\n", - " name=\"leadgen_agent\", \n", - " memory=ChatMemory(\n", - " persona=f\"You are responsible to finding good recruiting candidates, for the company description: {company_description}. \" \\\n", - " + f\"Ideal canddiates have skills: {skills}. \" \\\n", - " + \"Search for candidates by calling the `search_candidates_db` function. \" \\\n", - " + \"When you find a good candidate, submit the candidate for outreach with the `submit_candidate_for_outreach` tool. \" \\\n", - " + \"Continue to search through the database until there are no more entries. \",\n", - " human=\"\",\n", - " ), \n", - " tools=[read_resume_tool.name, submit_candidate_tool.name]\n", - ")" - ] - }, - { - "cell_type": "markdown", - "id": "1f489784-dbc9-4c93-9181-457460b05401", - "metadata": {}, - "source": [ - "## Cleanup " - ] - }, - { - "cell_type": "code", - "execution_count": 23, - "id": "f93c330b-909a-4180-bf6b-166b951977a6", - "metadata": {}, - "outputs": [], - "source": [ - "agents = client.list_agents()" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "523a382d-f514-46cb-a902-84ee74706f01", - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Deleted FierceNucleus\n", - "Deleted LuxuriousRaccoon\n" - ] - } - ], - "source": [ - "for agent in agents: \n", - " client.delete_agent(agent.id)\n", - " print(\"Deleted\", agent.name)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "e7f1a012-0080-4e68-b26f-7d139a37bad0", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "letta", - "language": "python", - "name": "letta" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.2" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -}