Skip to content

Commit

Permalink
fix: Improvements Jan 22 (#734)
Browse files Browse the repository at this point in the history
Co-authored-by: Charles Packer <[email protected]>
Co-authored-by: dboyliao <[email protected]>
Co-authored-by: Shubham Naik <[email protected]>
Co-authored-by: Shubham Naik <[email protected]>
Co-authored-by: Caren Thomas <[email protected]>
Co-authored-by: Sarah Wooders <[email protected]>
Co-authored-by: Nuno Rocha <[email protected]>
Co-authored-by: Theo Conrads <[email protected]>
Co-authored-by: Jyotirmaya Mahanta <[email protected]>
Co-authored-by: Stephan Fitzpatrick <[email protected]>
Co-authored-by: Stephan Fitzpatrick <[email protected]>
Co-authored-by: mlong93 <[email protected]>
Co-authored-by: Mindy Long <[email protected]>
Co-authored-by: Krishnakumar R (KK) <[email protected]>
  • Loading branch information
15 people authored Jan 23, 2025
1 parent cc8f93c commit 02a579f
Show file tree
Hide file tree
Showing 12 changed files with 134 additions and 32 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/docker-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Login to Docker Hub
uses: docker/login-action@v3
Expand All @@ -17,7 +17,7 @@ jobs:
password: ${{ secrets.DOCKERHUB_TOKEN }}

- uses: actions/checkout@v3

- name: Set up QEMU
uses: docker/setup-qemu-action@v3

Expand All @@ -38,4 +38,3 @@ jobs:
letta/letta:latest
memgpt/letta:${{ env.CURRENT_VERSION }}
memgpt/letta:latest
19 changes: 19 additions & 0 deletions .github/workflows/letta-code-sync.yml
Original file line number Diff line number Diff line change
@@ -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"}'
34 changes: 31 additions & 3 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand All @@ -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>

Expand Down Expand Up @@ -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>
Expand Down
2 changes: 1 addition & 1 deletion letta/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = "0.6.9"
__version__ = "0.6.13"


# import clients
Expand Down
12 changes: 10 additions & 2 deletions letta/client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -458,11 +459,18 @@ def __init__(
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
Expand Down
30 changes: 30 additions & 0 deletions letta/orm/job_usage_statistics.py
Original file line number Diff line number Diff line change
@@ -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")
3 changes: 3 additions & 0 deletions letta/server/rest_api/routers/v1/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
22 changes: 9 additions & 13 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 6 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[tool.poetry]
name = "letta"
version = "0.6.9"

version = "0.6.13"
packages = [
{include = "letta"},
]
Expand All @@ -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 }
Expand All @@ -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}
Expand All @@ -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"
Expand All @@ -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"]
Expand Down
16 changes: 16 additions & 0 deletions tests/integration_test_tool_execution_sandbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"):
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/test_tool_schema_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 02a579f

Please sign in to comment.