forked from cpacker/MemGPT
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add MemGPT "Python Client" (cpacker#713)
* First commit of memgpt client and some messy test code * rolled back unnecessary changes to abstract interface; switched client to always use Queueing Interface * Added missing interface clear() in run_command; added convenience method for checking if an agent exists, used that in create_agent * Formatting fixes * Fixed incorrect naming of get_agent_memory in rest server * Removed erroneous clear from client save method; Replaced print statements with appropriate logger calls in server * Updated readme with client usage instructions * added tests for Client * make printing to terminal togglable on queininginterface (should probably refactor this to a logger) * turn off printing to stdout via interface by default * allow importing the python client in a similar fashion to openai-python (see https://github.com/openai/openai-python) * Allowed quickstart on init of client; updated readme and test_client accordingly * oops, fixed name of openai_api_key config key * Fixed small typo * Fixed broken test by adding memgpt hosted model details to agent config * silence llamaindex 'LLM is explicitly disabled. Using MockLLM.' on server * default to openai if user's memgpt directory is empty (first time) * correct type hint * updated section on client in readme * added comment about how MemGPT config != Agent config * patch unrelated test * update wording on readme * patch another unrelated test * added python client to readme docs * Changed 'user' to 'human' in example; Defaulted AgentConfig.model to 'None'; Fixed issue in create_agent (accounting for dict config); matched test code to example * Fixed advanced example * patch test * patch --------- Co-authored-by: cpacker <packercharles@gmail.com>
- Loading branch information
Showing
13 changed files
with
387 additions
and
32 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
--- | ||
title: Python client | ||
excerpt: Developing using the MemGPT Python client | ||
category: 6580dab16cade8003f996d17 | ||
--- | ||
|
||
The fastest way to integrate MemGPT with your own Python projects is through the `MemGPT` client class: | ||
```python | ||
from memgpt import MemGPT | ||
|
||
# Create a MemGPT client object (sets up the persistent state) | ||
client = MemGPT( | ||
quickstart="openai", | ||
config={ | ||
"openai_api_key": "YOUR_API_KEY" | ||
} | ||
) | ||
|
||
# You can set many more parameters, this is just a basic example | ||
agent_id = client.create_agent( | ||
agent_config={ | ||
"persona": "sam_pov", | ||
"user": "cs_phd", | ||
} | ||
) | ||
|
||
# Now that we have an agent_name identifier, we can send it a message! | ||
# The response will have data from the MemGPT agent | ||
my_message = "Hi MemGPT! How's it going?" | ||
response = client.user_message(agent_id=agent_id, message=my_message) | ||
``` | ||
|
||
## More in-depth example of using the MemGPT Python client | ||
|
||
```python | ||
from memgpt.config import AgentConfig | ||
from memgpt import MemGPT | ||
from memgpt import constants | ||
from memgpt.cli.cli import QuickstartChoice | ||
|
||
|
||
client = MemGPT( | ||
# When auto_save is 'True' then the agent(s) will be saved after every | ||
# user message. This may have performance implications, so you | ||
# can otherwise choose when to save explicitly using client.save(). | ||
auto_save=True, | ||
|
||
# Quickstart will automatically configure MemGPT (without having to run `memgpt configure` | ||
# If you choose 'openai' then you must set the api key (env or in config) | ||
quickstart=QuickstartChoice.memgpt_hosted, | ||
|
||
# Allows you to override default config generated by quickstart or `memgpt configure` | ||
config={} | ||
) | ||
|
||
# Create an AgentConfig with default persona and human txt | ||
# In this case, assume we wrote a custom persona file "my_persona.txt", located at ~/.memgpt/personas/my_persona.txt | ||
# Same for a custom user file "my_user.txt", located at ~/.memgpt/humans/my_user.txt | ||
agent_config = AgentConfig( | ||
name="CustomAgent", | ||
persona="my_persona", | ||
human="my_user", | ||
preset="memgpt_chat", | ||
model="gpt-4", | ||
) | ||
|
||
# Create the agent according to AgentConfig we set up. If an agent with | ||
# the same name already exists it will simply return, unless you set | ||
# throw_if_exists to 'True' | ||
agent_id = client.create_agent(agent_config=agent_config) | ||
|
||
# Create a helper that sends a message and prints the assistant response only | ||
def send_message(message: str): | ||
""" | ||
sends a message and prints the assistant output only. | ||
:param message: the message to send | ||
""" | ||
response = client.user_message(agent_id=agent_id, message=message) | ||
for r in response: | ||
# Can also handle other types "function_call", "function_return", "function_message" | ||
if "assistant_message" in r: | ||
print("ASSISTANT:", r["assistant_message"]) | ||
elif "thoughts" in r: | ||
print("THOUGHTS:", r["internal_monologue"]) | ||
|
||
# Send a message and see the response | ||
send_message("Please introduce yourself and tell me about your abilities!") | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
__version__ = "0.2.10" | ||
|
||
from memgpt.client.client import Client as MemGPT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import os | ||
from typing import Dict, List, Union | ||
|
||
from memgpt.cli.cli import QuickstartChoice | ||
from memgpt.cli.cli import set_config_with_dict, quickstart as quickstart_func, str_to_quickstart_choice | ||
from memgpt.config import MemGPTConfig, AgentConfig | ||
from memgpt.persistence_manager import PersistenceManager | ||
from memgpt.server.rest_api.interface import QueuingInterface | ||
from memgpt.server.server import SyncServer | ||
|
||
|
||
class Client(object): | ||
def __init__( | ||
self, | ||
auto_save: bool = False, | ||
quickstart: Union[QuickstartChoice, str, None] = None, | ||
config: Union[Dict, MemGPTConfig] = None, # not the same thing as AgentConfig | ||
debug: bool = False, | ||
): | ||
""" | ||
Initializes a new instance of Client class. | ||
:param auto_save: indicates whether to automatically save after every message. | ||
:param quickstart: allows running quickstart on client init. | ||
:param config: optional config settings to apply after quickstart | ||
:param debug: indicates whether to display debug messages. | ||
""" | ||
self.user_id = "null" | ||
self.auto_save = auto_save | ||
|
||
# make sure everything is set up properly | ||
MemGPTConfig.create_config_dir() | ||
|
||
# If this is the first ever start, do basic initialization | ||
if not MemGPTConfig.exists() and config is None and quickstart is None: | ||
# Default to openai | ||
print("Detecting uninitialized MemGPT, defaulting to quickstart == openai") | ||
quickstart = "openai" | ||
|
||
if quickstart: | ||
# api key passed in config has priority over env var | ||
if isinstance(config, dict) and "openai_api_key" in config: | ||
openai_key = config["openai_api_key"] | ||
else: | ||
openai_key = os.environ.get("OPENAI_API_KEY", None) | ||
|
||
# throw an error if we can't resolve the key | ||
if openai_key: | ||
os.environ["OPENAI_API_KEY"] = openai_key | ||
elif quickstart == QuickstartChoice.openai or quickstart == "openai": | ||
raise ValueError("Please set OPENAI_API_KEY or pass 'openai_api_key' in config dict") | ||
|
||
if isinstance(quickstart, str): | ||
quickstart = str_to_quickstart_choice(quickstart) | ||
quickstart_func(backend=quickstart, debug=debug) | ||
|
||
if config is not None: | ||
set_config_with_dict(config) | ||
|
||
self.interface = QueuingInterface(debug=debug) | ||
self.server = SyncServer(default_interface=self.interface) | ||
|
||
def list_agents(self): | ||
self.interface.clear() | ||
return self.server.list_agents(user_id=self.user_id) | ||
|
||
def agent_exists(self, agent_id: str) -> bool: | ||
existing = self.list_agents() | ||
return agent_id in existing["agent_names"] | ||
|
||
def create_agent( | ||
self, | ||
agent_config: Union[Dict, AgentConfig], | ||
persistence_manager: Union[PersistenceManager, None] = None, | ||
throw_if_exists: bool = False, | ||
) -> str: | ||
if isinstance(agent_config, dict): | ||
agent_name = agent_config.get("name") | ||
else: | ||
agent_name = agent_config.name | ||
|
||
if not self.agent_exists(agent_id=agent_name): | ||
self.interface.clear() | ||
return self.server.create_agent(user_id=self.user_id, agent_config=agent_config, persistence_manager=persistence_manager) | ||
|
||
if throw_if_exists: | ||
raise ValueError(f"Agent {agent_config.name} already exists") | ||
|
||
return agent_config.name | ||
|
||
def get_agent_config(self, agent_id: str) -> Dict: | ||
self.interface.clear() | ||
return self.server.get_agent_config(user_id=self.user_id, agent_id=agent_id) | ||
|
||
def get_agent_memory(self, agent_id: str) -> Dict: | ||
self.interface.clear() | ||
return self.server.get_agent_memory(user_id=self.user_id, agent_id=agent_id) | ||
|
||
def update_agent_core_memory(self, agent_id: str, new_memory_contents: Dict) -> Dict: | ||
self.interface.clear() | ||
return self.server.update_agent_core_memory(user_id=self.user_id, agent_id=agent_id, new_memory_contents=new_memory_contents) | ||
|
||
def user_message(self, agent_id: str, message: str) -> List[Dict]: | ||
self.interface.clear() | ||
self.server.user_message(user_id=self.user_id, agent_id=agent_id, message=message) | ||
if self.auto_save: | ||
self.save() | ||
return self.interface.to_list() | ||
|
||
def run_command(self, agent_id: str, command: str) -> Union[str, None]: | ||
self.interface.clear() | ||
return self.server.run_command(user_id=self.user_id, agent_id=agent_id, command=command) | ||
|
||
def save(self): | ||
self.server.save_agents() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.