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
from letta import Admin, create_client
from letta.memory import ChatMemory
from letta import create_client
from letta.schemas.memory import ChatMemory
"""
Make sure you run the Letta server before running this example.
```
export MEMGPT_SERVER_PASS=your_token
letta server
```
"""
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
client = create_client(base_url="http://localhost:8283", token=token)
client = create_client(base_url="http://localhost:8283")
# Create an agent
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)
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__":
main()

View File

@ -11,6 +11,8 @@ from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
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.
SCOPES = ["https://www.googleapis.com/auth/gmail.readonly"]
TOKEN_PATH = os.path.expanduser("~/.letta/gmail_token.json")

View File

@ -1,7 +1,6 @@
__version__ = "0.5.0"
# import clients
from letta.client.admin import Admin
from letta.client.client import LocalClient, RESTClient, create_client
# imports for easier access
@ -13,7 +12,13 @@ from letta.schemas.file import FileMetadata
from letta.schemas.job import Job
from letta.schemas.letta_message import LettaMessage
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.openai.chat_completion_response import UsageStatistics
from letta.schemas.organization import Organization

View File

@ -105,7 +105,7 @@ def add_tool(
"""Add or update a tool from a Python file."""
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
with open(filename, "r", encoding="utf-8") as file:
@ -145,7 +145,7 @@ def list_tools():
"""List all available tools."""
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()
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)
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:
"""
Get an agent's state by its ID.

View File

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

View File

@ -7,7 +7,7 @@ from typing import Union
import pytest
from dotenv import load_dotenv
from letta import Admin, create_client
from letta import create_client
from letta.agent import Agent
from letta.client.client import LocalClient, RESTClient
from letta.constants import DEFAULT_PRESET
@ -25,10 +25,6 @@ test_agent_state_post_message = None
test_user_id = uuid.uuid4()
# admin credentials
test_server_token = "test_server_token"
def run_server():
load_dotenv()
@ -53,33 +49,21 @@ def client(request):
server_url = os.getenv("MEMGPT_SERVER_URL")
if server_url is None:
# run server in thread
# NOTE: must set MEMGPT_SERVER_PASS enviornment variable
server_url = "http://localhost:8283"
print("Starting server thread")
thread = threading.Thread(target=run_server, daemon=True)
thread.start()
time.sleep(5)
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:
# use local client (no server)
assert False, "Local client not implemented"
server_url = None
assert False, "Local client not implemented"
assert server_url is not None
assert api_key.key is not None
client = create_client(base_url=server_url, token=api_key.key) # This yields control back to the test function
client = create_client(base_url=server_url) # This yields control back to the test function
client.set_default_llm_config(LLMConfig.default_config("gpt-4o-mini"))
client.set_default_embedding_config(EmbeddingConfig.default_config(provider="openai"))
try:
yield client
finally:
# cleanup user
if server_url:
admin.delete_user(user.id)
yield client
# 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")
# 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):
"""Test creation of a agent tool"""