chore: remove the admin client and tests (#1923)

This commit is contained in:
Sarah Wooders 2024-10-22 15:02:28 -07:00 committed by GitHub
parent ab3431f56a
commit d84bf39c4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 34 additions and 263 deletions

View File

@ -1,33 +1,19 @@
import json import json
from letta import Admin, create_client from letta import create_client
from letta.memory import ChatMemory from letta.schemas.memory import ChatMemory
""" """
Make sure you run the Letta server before running this example. Make sure you run the Letta server before running this example.
``` ```
export MEMGPT_SERVER_PASS=your_token
letta server letta server
``` ```
""" """
def main(): def main():
# Create an admin client
admin = Admin(base_url="http://localhost:8283", token="your_token")
# Create a user + token
create_user_response = admin.create_user()
user_id = create_user_response.user_id
token = create_user_response.api_key
print(f"Created user: {user_id} with token: {token}")
# List available keys
get_keys_response = admin.get_keys(user_id=user_id)
print(f"User {user_id} has keys: {get_keys_response}")
# Connect to the server as a user # Connect to the server as a user
client = create_client(base_url="http://localhost:8283", token=token) client = create_client(base_url="http://localhost:8283")
# Create an agent # Create an agent
agent_state = client.create_agent(name="my_agent", memory=ChatMemory(human="My name is Sarah.", persona="I am a friendly AI.")) agent_state = client.create_agent(name="my_agent", memory=ChatMemory(human="My name is Sarah.", persona="I am a friendly AI."))
@ -42,10 +28,6 @@ def main():
client.delete_agent(agent_id=agent_state.id) client.delete_agent(agent_id=agent_state.id)
print(f"Deleted agent: {agent_state.name} with ID {str(agent_state.id)}") print(f"Deleted agent: {agent_state.name} with ID {str(agent_state.id)}")
# Delete user
admin.delete_user(user_id=user_id)
print(f"Deleted user: {user_id} with token: {token}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -11,6 +11,8 @@ from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build from googleapiclient.discovery import build
from googleapiclient.errors import HttpError from googleapiclient.errors import HttpError
# NOTE: THIS file it out of date for >=0.5.0
# If modifying these scopes, delete the file token.json. # If modifying these scopes, delete the file token.json.
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"] SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
TOKEN_PATH = os.path.expanduser("~/.letta/gmail_token.json") TOKEN_PATH = os.path.expanduser("~/.letta/gmail_token.json")

View File

@ -1,7 +1,6 @@
__version__ = "0.5.0" __version__ = "0.5.0"
# import clients # import clients
from letta.client.admin import Admin
from letta.client.client import LocalClient, RESTClient, create_client from letta.client.client import LocalClient, RESTClient, create_client
# imports for easier access # imports for easier access
@ -13,7 +12,13 @@ from letta.schemas.file import FileMetadata
from letta.schemas.job import Job from letta.schemas.job import Job
from letta.schemas.letta_message import LettaMessage from letta.schemas.letta_message import LettaMessage
from letta.schemas.llm_config import LLMConfig from letta.schemas.llm_config import LLMConfig
from letta.schemas.memory import ArchivalMemorySummary, Memory, RecallMemorySummary from letta.schemas.memory import (
ArchivalMemorySummary,
BasicBlockMemory,
ChatMemory,
Memory,
RecallMemorySummary,
)
from letta.schemas.message import Message from letta.schemas.message import Message
from letta.schemas.openai.chat_completion_response import UsageStatistics from letta.schemas.openai.chat_completion_response import UsageStatistics
from letta.schemas.organization import Organization from letta.schemas.organization import Organization

View File

@ -105,7 +105,7 @@ def add_tool(
"""Add or update a tool from a Python file.""" """Add or update a tool from a Python file."""
from letta.client.client import create_client from letta.client.client import create_client
client = create_client(base_url=os.getenv("MEMGPT_BASE_URL"), token=os.getenv("MEMGPT_SERVER_PASS")) client = create_client()
# 1. Parse the Python file # 1. Parse the Python file
with open(filename, "r", encoding="utf-8") as file: with open(filename, "r", encoding="utf-8") as file:
@ -145,7 +145,7 @@ def list_tools():
"""List all available tools.""" """List all available tools."""
from letta.client.client import create_client from letta.client.client import create_client
client = create_client(base_url=os.getenv("MEMGPT_BASE_URL"), token=os.getenv("MEMGPT_SERVER_PASS")) client = create_client()
tools = client.list_tools() tools = client.list_tools()
for tool in tools: for tool in tools:

View File

@ -1,171 +0,0 @@
from typing import List, Optional
import requests
from requests import HTTPError
from letta.functions.functions import parse_source_code
from letta.functions.schema_generator import generate_schema
from letta.schemas.api_key import APIKey, APIKeyCreate
from letta.schemas.organization import Organization, OrganizationCreate
from letta.schemas.user import User, UserCreate
class Admin:
"""
Admin client allows admin-level operations on the Letta server.
- Creating users
- Generating user keys
"""
def __init__(
self,
base_url: str,
token: str,
api_prefix: str = "v1",
):
self.base_url = base_url
self.api_prefix = api_prefix
self.token = token
self.headers = {"accept": "application/json", "content-type": "application/json", "authorization": f"Bearer {token}"}
def get_users(self, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[User]:
params = {}
if cursor:
params["cursor"] = str(cursor)
if limit:
params["limit"] = limit
response = requests.get(f"{self.base_url}/{self.api_prefix}/admin/users", params=params, headers=self.headers)
if response.status_code != 200:
raise HTTPError(response.json())
return [User(**user) for user in response.json()]
def create_key(self, user_id: str, key_name: Optional[str] = None) -> APIKey:
request = APIKeyCreate(user_id=user_id, name=key_name)
response = requests.post(f"{self.base_url}/{self.api_prefix}/admin/users/keys", headers=self.headers, json=request.model_dump())
if response.status_code != 200:
raise HTTPError(response.json())
return APIKey(**response.json())
def get_keys(self, user_id: str) -> List[APIKey]:
params = {"user_id": str(user_id)}
response = requests.get(f"{self.base_url}/{self.api_prefix}/admin/users/keys", params=params, headers=self.headers)
if response.status_code != 200:
raise HTTPError(response.json())
return [APIKey(**key) for key in response.json()]
def delete_key(self, api_key: str) -> APIKey:
params = {"api_key": api_key}
response = requests.delete(f"{self.base_url}/{self.api_prefix}/admin/users/keys", params=params, headers=self.headers)
if response.status_code != 200:
raise HTTPError(response.json())
return APIKey(**response.json())
def create_user(self, name: Optional[str] = None, org_id: Optional[str] = None) -> User:
request = UserCreate(name=name, org_id=org_id)
response = requests.post(f"{self.base_url}/{self.api_prefix}/admin/users", headers=self.headers, json=request.model_dump())
if response.status_code != 200:
raise HTTPError(response.json())
response_json = response.json()
return User(**response_json)
def delete_user(self, user_id: str) -> User:
params = {"user_id": str(user_id)}
response = requests.delete(f"{self.base_url}/{self.api_prefix}/admin/users", params=params, headers=self.headers)
if response.status_code != 200:
raise HTTPError(response.json())
return User(**response.json())
def create_organization(self, name: Optional[str] = None) -> Organization:
request = OrganizationCreate(name=name)
response = requests.post(f"{self.base_url}/{self.api_prefix}/admin/orgs", headers=self.headers, json=request.model_dump())
if response.status_code != 200:
raise HTTPError(response.json())
response_json = response.json()
return Organization(**response_json)
def delete_organization(self, org_id: str) -> Organization:
params = {"org_id": str(org_id)}
response = requests.delete(f"{self.base_url}/{self.api_prefix}/admin/orgs", params=params, headers=self.headers)
if response.status_code != 200:
raise HTTPError(response.json())
return Organization(**response.json())
def get_organizations(self, cursor: Optional[str] = None, limit: Optional[int] = 50) -> List[Organization]:
params = {}
if cursor:
params["cursor"] = str(cursor)
if limit:
params["limit"] = limit
response = requests.get(f"{self.base_url}/{self.api_prefix}/admin/orgs", params=params, headers=self.headers)
if response.status_code != 200:
raise HTTPError(response.json())
return [Organization(**org) for org in response.json()]
def _reset_server(self):
# DANGER: this will delete all users and keys
# clear all state associated with users
# TODO: clear out all agents, presets, etc.
users = self.get_users()
for user in users:
keys = self.get_keys(user.id)
for key in keys:
self.delete_key(key.key)
self.delete_user(user.id)
# tools
def create_tool(
self,
func,
name: Optional[str] = None,
update: Optional[bool] = True, # TODO: actually use this
tags: Optional[List[str]] = None,
):
"""Create a tool
Args:
func (callable): The function to create a tool for.
tags (Optional[List[str]], optional): Tags for the tool. Defaults to None.
update (bool, optional): Update the tool if it already exists. Defaults to True.
Returns:
Tool object
"""
# TODO: check if tool already exists
# TODO: how to load modules?
# parse source code/schema
source_code = parse_source_code(func)
json_schema = generate_schema(func, name)
source_type = "python"
json_schema["name"]
if "memory" in tags:
# special modifications to memory functions
# self.memory -> self.memory.memory, since Agent.memory.memory needs to be modified (not BaseMemory.memory)
source_code = source_code.replace("self.memory", "self.memory.memory")
# create data
data = {"source_code": source_code, "source_type": source_type, "tags": tags, "json_schema": json_schema}
CreateToolRequest(**data) # validate
# make REST request
response = requests.post(f"{self.base_url}/{self.api_prefix}/admin/tools", json=data, headers=self.headers)
if response.status_code != 200:
raise ValueError(f"Failed to create tool: {response.text}")
return ToolModel(**response.json())
def list_tools(self):
response = requests.get(f"{self.base_url}/{self.api_prefix}/admin/tools", headers=self.headers)
return ListToolsResponse(**response.json()).tools
def delete_tool(self, name: str):
response = requests.delete(f"{self.base_url}/{self.api_prefix}/admin/tools/{name}", headers=self.headers)
if response.status_code != 200:
raise ValueError(f"Failed to delete tool: {response.text}")
return response.json()
def get_tool(self, name: str):
response = requests.get(f"{self.base_url}/{self.api_prefix}/admin/tools/{name}", headers=self.headers)
if response.status_code == 404:
return None
elif response.status_code != 200:
raise ValueError(f"Failed to get tool: {response.text}")
return ToolModel(**response.json())

View File

@ -1777,6 +1777,19 @@ class LocalClient(AbstractClient):
""" """
self.server.delete_agent(user_id=self.user_id, agent_id=agent_id) self.server.delete_agent(user_id=self.user_id, agent_id=agent_id)
def get_agent_by_name(self, agent_name: str, user_id: str) -> AgentState:
"""
Get an agent by its name
Args:
agent_name (str): Name of the agent
Returns:
agent_state (AgentState): State of the agent
"""
self.interface.clear()
return self.server.get_agent(agent_name=agent_name, user_id=user_id, agent_id=None)
def get_agent(self, agent_id: str) -> AgentState: def get_agent(self, agent_id: str) -> AgentState:
""" """
Get an agent's state by its ID. Get an agent's state by its ID.

View File

@ -7,7 +7,7 @@ from typing import Union
import pytest import pytest
from dotenv import load_dotenv from dotenv import load_dotenv
from letta import Admin, create_client from letta import create_client
from letta.client.client import LocalClient, RESTClient from letta.client.client import LocalClient, RESTClient
from letta.constants import DEFAULT_PRESET from letta.constants import DEFAULT_PRESET
from letta.schemas.agent import AgentState from letta.schemas.agent import AgentState
@ -31,10 +31,6 @@ client = None
test_agent_state_post_message = None test_agent_state_post_message = None
# admin credentials
test_server_token = "test_server_token"
def run_server(): def run_server():
load_dotenv() load_dotenv()
@ -58,7 +54,6 @@ def client(request):
server_url = os.getenv("LETTA_SERVER_URL") server_url = os.getenv("LETTA_SERVER_URL")
if server_url is None: if server_url is None:
# run server in thread # run server in thread
# NOTE: must set MEMGPT_SERVER_PASS enviornment variable
server_url = "http://localhost:8283" server_url = "http://localhost:8283"
print("Starting server thread") print("Starting server thread")
thread = threading.Thread(target=run_server, daemon=True) thread = threading.Thread(target=run_server, daemon=True)
@ -66,10 +61,7 @@ def client(request):
time.sleep(5) time.sleep(5)
print("Running client tests with server:", server_url) print("Running client tests with server:", server_url)
# create user via admin client # create user via admin client
admin = Admin(server_url, test_server_token) client = create_client(base_url=server_url, token=None) # This yields control back to the test function
user = admin.create_user() # Adjust as per your client's method
api_key = admin.create_key(user.id)
client = create_client(base_url=server_url, token=api_key.key) # This yields control back to the test function
else: else:
# use local client (no server) # use local client (no server)
server_url = None server_url = None
@ -77,12 +69,7 @@ def client(request):
client.set_default_llm_config(LLMConfig.default_config("gpt-4")) client.set_default_llm_config(LLMConfig.default_config("gpt-4"))
client.set_default_embedding_config(EmbeddingConfig.default_config(provider="openai")) client.set_default_embedding_config(EmbeddingConfig.default_config(provider="openai"))
try: yield client
yield client
finally:
# cleanup user
if server_url:
admin.delete_user(user.id)
# Fixture for test agent # Fixture for test agent

View File

@ -7,7 +7,7 @@ from typing import Union
import pytest import pytest
from dotenv import load_dotenv from dotenv import load_dotenv
from letta import Admin, create_client from letta import create_client
from letta.agent import Agent from letta.agent import Agent
from letta.client.client import LocalClient, RESTClient from letta.client.client import LocalClient, RESTClient
from letta.constants import DEFAULT_PRESET from letta.constants import DEFAULT_PRESET
@ -25,10 +25,6 @@ test_agent_state_post_message = None
test_user_id = uuid.uuid4() test_user_id = uuid.uuid4()
# admin credentials
test_server_token = "test_server_token"
def run_server(): def run_server():
load_dotenv() load_dotenv()
@ -53,33 +49,21 @@ def client(request):
server_url = os.getenv("MEMGPT_SERVER_URL") server_url = os.getenv("MEMGPT_SERVER_URL")
if server_url is None: if server_url is None:
# run server in thread # run server in thread
# NOTE: must set MEMGPT_SERVER_PASS enviornment variable
server_url = "http://localhost:8283" server_url = "http://localhost:8283"
print("Starting server thread") print("Starting server thread")
thread = threading.Thread(target=run_server, daemon=True) thread = threading.Thread(target=run_server, daemon=True)
thread.start() thread.start()
time.sleep(5) time.sleep(5)
print("Running client tests with server:", server_url) print("Running client tests with server:", server_url)
# create user via admin client
admin = Admin(server_url, test_server_token)
user = admin.create_user() # Adjust as per your client's method
api_key = admin.create_key(user.id)
else: else:
# use local client (no server)
assert False, "Local client not implemented"
server_url = None server_url = None
assert False, "Local client not implemented"
assert server_url is not None assert server_url is not None
assert api_key.key is not None client = create_client(base_url=server_url) # This yields control back to the test function
client = create_client(base_url=server_url, token=api_key.key) # This yields control back to the test function
client.set_default_llm_config(LLMConfig.default_config("gpt-4o-mini")) client.set_default_llm_config(LLMConfig.default_config("gpt-4o-mini"))
client.set_default_embedding_config(EmbeddingConfig.default_config(provider="openai")) client.set_default_embedding_config(EmbeddingConfig.default_config(provider="openai"))
try: yield client
yield client
finally:
# cleanup user
if server_url:
admin.delete_user(user.id)
# Fixture for test agent # Fixture for test agent
@ -127,37 +111,6 @@ def test_create_tool(client: Union[LocalClient, RESTClient]):
response = client.user_message(agent_id=agent_state.id, message="hi") response = client.user_message(agent_id=agent_state.id, message="hi")
# TODO: add back once we fix admin client tool creation
# def test_create_agent_tool_admin(admin_client):
# if admin_client is None:
# return
#
# def print_tool(message: str):
# """
# Args:
# message (str): The message to print.
#
# Returns:
# str: The message that was printed.
#
# """
# print(message)
# return message
#
# tools = admin_client.list_tools()
# print(f"Original tools {[t.name for t in tools]}")
#
# tool = admin_client.create_tool(print_tool, tags=["extras"])
#
# tools = admin_client.list_tools()
# assert tool in tools, f"Expected {tool.name} in {[t.name for t in tools]}"
# print(f"Updated tools {[t.name for t in tools]}")
#
# # check tool id
# tool = admin_client.get_tool(tool.name)
# assert tool.user_id is None, f"Expected {tool.user_id} to be None"
def test_create_agent_tool(client): def test_create_agent_tool(client):
"""Test creation of a agent tool""" """Test creation of a agent tool"""