fix: Improvements Jan 22 (#734)
Co-authored-by: Charles Packer <packercharles@gmail.com> Co-authored-by: dboyliao <qmalliao@gmail.com> Co-authored-by: Shubham Naik <shub@memgpt.ai> Co-authored-by: Shubham Naik <shubham.naik10@gmail.com> Co-authored-by: Caren Thomas <caren@letta.com> Co-authored-by: Sarah Wooders <sarahwooders@gmail.com> Co-authored-by: Nuno Rocha <nunuroxa@gmail.com> Co-authored-by: Theo Conrads <theo.conrads@ella-lab.io> Co-authored-by: Jyotirmaya Mahanta <jyotirmaya.mahanta@gmail.com> Co-authored-by: Stephan Fitzpatrick <knowsuchagency@gmail.com> Co-authored-by: Stephan Fitzpatrick <stephan@knowsuchagency.com> Co-authored-by: mlong93 <35275280+mlong93@users.noreply.github.com> Co-authored-by: Mindy Long <mindy@letta.com> Co-authored-by: Krishnakumar R (KK) <65895020+kk-src@users.noreply.github.com>
This commit is contained in:
1
.github/workflows/docker-image.yml
vendored
1
.github/workflows/docker-image.yml
vendored
@@ -38,4 +38,3 @@ jobs:
|
||||
letta/letta:latest
|
||||
memgpt/letta:${{ env.CURRENT_VERSION }}
|
||||
memgpt/letta:latest
|
||||
|
||||
|
||||
19
.github/workflows/letta-code-sync.yml
vendored
Normal file
19
.github/workflows/letta-code-sync.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: Sync Code
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
notify:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ !contains(github.event.head_commit.message, '[sync-skip]') }}
|
||||
steps:
|
||||
- name: Trigger repository_dispatch
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Authorization: token ${{ secrets.SYNC_PAT }}" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
https://api.github.com/repos/letta-ai/letta-cloud/dispatches \
|
||||
-d '{"event_type":"oss-update"}'
|
||||
@@ -19,18 +19,46 @@ Now, let's bring your new playground to your local machine.
|
||||
git clone https://github.com/your-username/letta.git
|
||||
```
|
||||
|
||||
### 🧩 Install Dependencies
|
||||
### 🧩 Install dependencies & configure environment
|
||||
|
||||
#### Install poetry and dependencies
|
||||
|
||||
First, install Poetry using [the official instructions here](https://python-poetry.org/docs/#installation).
|
||||
|
||||
Once Poetry is installed, navigate to the Letta directory and install the Letta project with Poetry:
|
||||
Once Poetry is installed, navigate to the letta directory and install the Letta project with Poetry:
|
||||
```shell
|
||||
cd Letta
|
||||
cd letta
|
||||
poetry shell
|
||||
poetry install --all-extras
|
||||
```
|
||||
#### Setup PostgreSQL environment (optional)
|
||||
|
||||
If you are planning to develop letta connected to PostgreSQL database, you need to take the following actions.
|
||||
If you are not planning to use PostgreSQL database, you can skip to the step which talks about [running letta](#running-letta-with-poetry).
|
||||
|
||||
Assuming you have a running PostgreSQL instance, first you need to create the user, database and ensure the pgvector
|
||||
extension is ready. Here are sample steps for a case where user and database name is letta and assumes no password is set:
|
||||
|
||||
```shell
|
||||
createuser letta
|
||||
createdb letta --owner=letta
|
||||
psql -d letta -c 'CREATE EXTENSION IF NOT EXISTS vector'
|
||||
```
|
||||
Setup the environment variable to tell letta code to contact PostgreSQL database:
|
||||
```shell
|
||||
export LETTA_PG_URI="postgresql://${POSTGRES_USER:-letta}:${POSTGRES_PASSWORD:-letta}@localhost:5432/${POSTGRES_DB:-letta}"
|
||||
```
|
||||
|
||||
After this you need to prep the database with initial content. You can use alembic upgrade to populate the initial
|
||||
contents from template test data. Please ensure to activate poetry environment using `poetry shell`.
|
||||
```shell
|
||||
alembic upgrade head
|
||||
```
|
||||
|
||||
#### Running letta with poetry
|
||||
|
||||
Now when you want to use `letta`, make sure you first activate the `poetry` environment using poetry shell:
|
||||
|
||||
```shell
|
||||
$ poetry shell
|
||||
(pyletta-py3.12) $ letta run
|
||||
|
||||
12
README.md
12
README.md
@@ -9,7 +9,7 @@
|
||||
<div align="center">
|
||||
<h1>Letta (previously MemGPT)</h1>
|
||||
|
||||
**☄️ New release: Letta Agent Development Environment (_read more [here](#-access-the-letta-ade-agent-development-environment)_) ☄️**
|
||||
**☄️ New release: Letta Agent Development Environment (_read more [here](#-access-the-ade-agent-development-environment)_) ☄️**
|
||||
|
||||
<p align="center">
|
||||
<picture>
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<h3>
|
||||
|
||||
[Homepage](https://letta.com) // [Documentation](https://docs.letta.com) // [ADE](https://app.letta.com) // [Letta Cloud](https://forms.letta.com/early-access)
|
||||
[Homepage](https://letta.com) // [Documentation](https://docs.letta.com) // [ADE](https://docs.letta.com/agent-development-environment) // [Letta Cloud](https://forms.letta.com/early-access)
|
||||
|
||||
</h3>
|
||||
|
||||
@@ -80,12 +80,12 @@ docker run \
|
||||
|
||||
Once the Letta server is running, you can access it via port `8283` (e.g. sending REST API requests to `http://localhost:8283/v1`). You can also connect your server to the Letta ADE to access and manage your agents in a web interface.
|
||||
|
||||
### 👾 Access the [Letta ADE (Agent Development Environment)](https://app.letta.com)
|
||||
### 👾 Access the ADE (Agent Development Environment)
|
||||
|
||||
> [!NOTE]
|
||||
> The Letta ADE is a graphical user interface for creating, deploying, interacting and observing with your Letta agents.
|
||||
>
|
||||
> For example, if you're running a Letta server to power an end-user application (such as a customer support chatbot), you can use the ADE to test, debug, and observe the agents in your server. You can also use the ADE as a general chat interface to interact with your Letta agents.
|
||||
> For a guided tour of the ADE, watch our [ADE walkthrough on YouTube](https://www.youtube.com/watch?v=OzSCFR0Lp5s), or read our [blog post](https://www.letta.com/blog/introducing-the-agent-development-environment) and [developer docs](https://docs.letta.com/agent-development-environment).
|
||||
|
||||
The Letta ADE is a graphical user interface for creating, deploying, interacting and observing with your Letta agents. For example, if you're running a Letta server to power an end-user application (such as a customer support chatbot), you can use the ADE to test, debug, and observe the agents in your server. You can also use the ADE as a general chat interface to interact with your Letta agents.
|
||||
|
||||
<p align="center">
|
||||
<picture>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__version__ = "0.6.9"
|
||||
__version__ = "0.6.13"
|
||||
|
||||
|
||||
# import clients
|
||||
|
||||
@@ -442,7 +442,8 @@ class RESTClient(AbstractClient):
|
||||
def __init__(
|
||||
self,
|
||||
base_url: str,
|
||||
token: str,
|
||||
token: Optional[str] = None,
|
||||
password: Optional[str] = None,
|
||||
api_prefix: str = "v1",
|
||||
debug: bool = False,
|
||||
default_llm_config: Optional[LLMConfig] = None,
|
||||
@@ -458,11 +459,18 @@ class RESTClient(AbstractClient):
|
||||
default_llm_config (Optional[LLMConfig]): The default LLM configuration.
|
||||
default_embedding_config (Optional[EmbeddingConfig]): The default embedding configuration.
|
||||
headers (Optional[Dict]): The additional headers for the REST API.
|
||||
token (Optional[str]): The token for the REST API when using managed letta service.
|
||||
password (Optional[str]): The password for the REST API when using self hosted letta service.
|
||||
"""
|
||||
super().__init__(debug=debug)
|
||||
self.base_url = base_url
|
||||
self.api_prefix = api_prefix
|
||||
self.headers = {"accept": "application/json", "authorization": f"Bearer {token}"}
|
||||
if token:
|
||||
self.headers = {"accept": "application/json", "Authorization": f"Bearer {token}"}
|
||||
elif password:
|
||||
self.headers = {"accept": "application/json", "X-BARE-PASSWORD": f"password {password}"}
|
||||
else:
|
||||
self.headers = {"accept": "application/json"}
|
||||
if headers:
|
||||
self.headers.update(headers)
|
||||
self._default_llm_config = default_llm_config
|
||||
|
||||
30
letta/orm/job_usage_statistics.py
Normal file
30
letta/orm/job_usage_statistics.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from typing import TYPE_CHECKING, Optional
|
||||
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from letta.orm.sqlalchemy_base import SqlalchemyBase
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from letta.orm.job import Job
|
||||
|
||||
|
||||
class JobUsageStatistics(SqlalchemyBase):
|
||||
"""Tracks usage statistics for jobs, with future support for per-step tracking."""
|
||||
|
||||
__tablename__ = "job_usage_statistics"
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, doc="Unique identifier for the usage statistics entry")
|
||||
job_id: Mapped[str] = mapped_column(
|
||||
ForeignKey("jobs.id", ondelete="CASCADE"), nullable=False, doc="ID of the job these statistics belong to"
|
||||
)
|
||||
step_id: Mapped[Optional[str]] = mapped_column(
|
||||
nullable=True, doc="ID of the specific step within the job (for future per-step tracking)"
|
||||
)
|
||||
completion_tokens: Mapped[int] = mapped_column(default=0, doc="Number of tokens generated by the agent")
|
||||
prompt_tokens: Mapped[int] = mapped_column(default=0, doc="Number of tokens in the prompt")
|
||||
total_tokens: Mapped[int] = mapped_column(default=0, doc="Total number of tokens processed by the agent")
|
||||
step_count: Mapped[int] = mapped_column(default=0, doc="Number of steps taken by the agent")
|
||||
|
||||
# Relationship back to the job
|
||||
job: Mapped["Job"] = relationship("Job", back_populates="usage_statistics")
|
||||
@@ -560,6 +560,9 @@ async def process_message_background(
|
||||
)
|
||||
server.job_manager.update_job_by_id(job_id=job_id, job_update=job_update, actor=actor)
|
||||
|
||||
# Add job usage statistics
|
||||
server.job_manager.add_job_usage(job_id=job_id, usage=result.usage, actor=actor)
|
||||
|
||||
except Exception as e:
|
||||
# Update job status to failed
|
||||
job_update = JobUpdate(
|
||||
|
||||
22
poetry.lock
generated
22
poetry.lock
generated
@@ -4507,18 +4507,15 @@ cli = ["click (>=5.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "python-multipart"
|
||||
version = "0.0.9"
|
||||
version = "0.0.19"
|
||||
description = "A streaming multipart parser for Python"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "python_multipart-0.0.9-py3-none-any.whl", hash = "sha256:97ca7b8ea7b05f977dc3849c3ba99d51689822fab725c3703af7c866a0c2b215"},
|
||||
{file = "python_multipart-0.0.9.tar.gz", hash = "sha256:03f54688c663f1b7977105f021043b0793151e4cb1c1a9d4a11fc13d622c4026"},
|
||||
{file = "python_multipart-0.0.19-py3-none-any.whl", hash = "sha256:f8d5b0b9c618575bf9df01c684ded1d94a338839bdd8223838afacfb4bb2082d"},
|
||||
{file = "python_multipart-0.0.19.tar.gz", hash = "sha256:905502ef39050557b7a6af411f454bc19526529ca46ae6831508438890ce12cc"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["atomicwrites (==1.4.1)", "attrs (==23.2.0)", "coverage (==7.4.1)", "hatch", "invoke (==2.2.0)", "more-itertools (==10.2.0)", "pbr (==6.0.0)", "pluggy (==1.4.0)", "py (==1.11.0)", "pytest (==8.0.0)", "pytest-cov (==4.1.0)", "pytest-timeout (==2.2.0)", "pyyaml (==6.0.1)", "ruff (==0.2.1)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2023.4"
|
||||
@@ -5172,19 +5169,18 @@ tornado = ["tornado (>=6)"]
|
||||
|
||||
[[package]]
|
||||
name = "setuptools"
|
||||
version = "68.2.2"
|
||||
version = "70.3.0"
|
||||
description = "Easily download, build, install, upgrade, and uninstall Python packages"
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "setuptools-68.2.2-py3-none-any.whl", hash = "sha256:b454a35605876da60632df1a60f736524eb73cc47bbc9f3f1ef1b644de74fd2a"},
|
||||
{file = "setuptools-68.2.2.tar.gz", hash = "sha256:4ac1475276d2f1c48684874089fefcd83bd7162ddaafb81fac866ba0db282a87"},
|
||||
{file = "setuptools-70.3.0-py3-none-any.whl", hash = "sha256:fe384da74336c398e0d956d1cae0669bc02eed936cdb1d49b57de1990dc11ffc"},
|
||||
{file = "setuptools-70.3.0.tar.gz", hash = "sha256:f171bab1dfbc86b132997f26a119f6056a57950d058587841a0082e8830f9dc5"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-ruff", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
testing-integration = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "packaging (>=23.1)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
|
||||
doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
|
||||
test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "importlib-metadata", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "jaraco.test", "mypy (==1.10.0)", "packaging (>=23.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-home (>=0.5)", "pytest-mypy", "pytest-perf", "pytest-ruff (>=0.3.2)", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
|
||||
|
||||
[[package]]
|
||||
name = "shellingham"
|
||||
@@ -6293,4 +6289,4 @@ tests = ["wikipedia"]
|
||||
[metadata]
|
||||
lock-version = "2.0"
|
||||
python-versions = "<3.14,>=3.10"
|
||||
content-hash = "2f552617ff233fe8b07bdec4dc1679935df30030046984962b69ebe625717815"
|
||||
content-hash = "bfb2713daba35ef8c78ee1b568c35afe3f1d0c247ea58a58a079e1fb4d984c10"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
[tool.poetry]
|
||||
name = "letta"
|
||||
version = "0.6.9"
|
||||
|
||||
version = "0.6.13"
|
||||
packages = [
|
||||
{include = "letta"},
|
||||
]
|
||||
@@ -21,7 +22,7 @@ questionary = "^2.0.1"
|
||||
pytz = "^2023.3.post1"
|
||||
tqdm = "^4.66.1"
|
||||
black = {extras = ["jupyter"], version = "^24.2.0"}
|
||||
setuptools = "^68.2.2"
|
||||
setuptools = "^70"
|
||||
datasets = { version = "^2.14.6", optional = true}
|
||||
prettytable = "^3.9.0"
|
||||
pgvector = { version = "^0.2.3", optional = true }
|
||||
@@ -47,7 +48,7 @@ qdrant-client = {version="^1.9.1", optional = true}
|
||||
python-box = "^7.1.1"
|
||||
sqlmodel = "^0.0.16"
|
||||
autoflake = {version = "^2.3.0", optional = true}
|
||||
python-multipart = "^0.0.9"
|
||||
python-multipart = "^0.0.19"
|
||||
sqlalchemy-utils = "^0.41.2"
|
||||
pytest-order = {version = "^1.2.0", optional = true}
|
||||
pytest-asyncio = {version = "^0.23.2", optional = true}
|
||||
@@ -56,7 +57,7 @@ httpx-sse = "^0.4.0"
|
||||
isort = { version = "^5.13.2", optional = true }
|
||||
docker = {version = "^7.1.0", optional = true}
|
||||
nltk = "^3.8.1"
|
||||
jinja2 = "^3.1.4"
|
||||
jinja2 = "^3.1.5"
|
||||
locust = {version = "^2.31.5", optional = true}
|
||||
wikipedia = {version = "^1.4.0", optional = true}
|
||||
composio-langchain = "^0.6.15"
|
||||
@@ -79,6 +80,7 @@ e2b-code-interpreter = {version = "^1.0.3", optional = true}
|
||||
anthropic = "^0.43.0"
|
||||
letta_client = "^0.1.16"
|
||||
|
||||
|
||||
[tool.poetry.extras]
|
||||
postgres = ["pgvector", "pg8000", "psycopg2-binary", "psycopg2"]
|
||||
dev = ["pytest", "pytest-asyncio", "pexpect", "black", "pre-commit", "datasets", "pyright", "pytest-order", "autoflake", "isort", "locust"]
|
||||
|
||||
@@ -195,6 +195,14 @@ def composio_gmail_get_profile_tool(test_user):
|
||||
yield tool
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def composio_gmail_get_profile_tool(test_user):
|
||||
tool_manager = ToolManager()
|
||||
tool_create = ToolCreate.from_composio(action_name="GMAIL_GET_PROFILE")
|
||||
tool = tool_manager.create_or_update_tool(pydantic_tool=Tool(**tool_create.model_dump()), actor=test_user)
|
||||
yield tool
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def clear_core_memory_tool(test_user):
|
||||
def clear_memory(agent_state: "AgentState"):
|
||||
@@ -418,6 +426,14 @@ def test_local_sandbox_e2e_composio_star_github_without_setting_db_env_vars(
|
||||
assert result.func_return["details"] == "Action executed successfully"
|
||||
|
||||
|
||||
@pytest.mark.local_sandbox
|
||||
def test_local_sandbox_e2e_composio_star_github_without_setting_db_env_vars(
|
||||
mock_e2b_api_key_none, check_composio_key_set, composio_github_star_tool, test_user
|
||||
):
|
||||
result = ToolExecutionSandbox(composio_github_star_tool.name, {"owner": "letta-ai", "repo": "letta"}, user=test_user).run()
|
||||
assert result.func_return["details"] == "Action executed successfully"
|
||||
|
||||
|
||||
@pytest.mark.local_sandbox
|
||||
def test_local_sandbox_external_codebase(mock_e2b_api_key_none, custom_test_sandbox_config, external_codebase_tool, test_user):
|
||||
# Set the args
|
||||
|
||||
@@ -203,4 +203,5 @@ def test_composio_tool_schema_generation(openai_model: str, structured_output: b
|
||||
print(f"Successfully called OpenAI using schema {schema} generated from {action_name}\n\n")
|
||||
except:
|
||||
print(f"Failed to call OpenAI using schema {schema} generated from {action_name}\n\n")
|
||||
|
||||
raise
|
||||
|
||||
Reference in New Issue
Block a user