Skip to content

Commit

Permalink
fix: debug logs in server (letta-ai#1452) (letta-ai#1457)
Browse files Browse the repository at this point in the history
Co-authored-by: Ethan Knox <[email protected]>
  • Loading branch information
sarahwooders and norton120 authored Jun 17, 2024
1 parent 78a27ed commit 09f2e41
Show file tree
Hide file tree
Showing 15 changed files with 94 additions and 80 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -1016,3 +1016,4 @@ pgdata/
## pytest mirrors
memgpt/.pytest_cache/
memgpy/pytest.ini
**/**/pytest_cache
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# The builder image, used to build the virtual environment
FROM python:3.12-bookworm as builder
FROM python:3.12.2-bookworm as builder
ARG MEMGPT_ENVIRONMENT=PRODUCTION
ENV MEMGPT_ENVIRONMENT=${MEMGPT_ENVIRONMENT}
RUN pip install poetry==1.8.2
Expand All @@ -16,13 +16,13 @@ RUN poetry lock --no-update
RUN if [ "$MEMGPT_ENVIRONMENT" = "DEVELOPMENT" ] ; then \
poetry install --no-root -E "postgres server dev autogen" ; \
else \
poetry install --without dev --without local --no-root -E "postgres server" && \
poetry install --no-root -E "postgres server" && \
rm -rf $POETRY_CACHE_DIR ; \
fi


# The runtime image, used to just run the code provided its virtual environment
FROM python:3.12-slim-bookworm as runtime
FROM python:3.12.2-slim-bookworm as runtime
ARG MEMGPT_ENVIRONMENT=PRODUCTION
ENV MEMGPT_ENVIRONMENT=${MEMGPT_ENVIRONMENT}
ENV VIRTUAL_ENV=/app/.venv \
Expand Down
1 change: 1 addition & 0 deletions dev-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ services:
build:
context: .
dockerfile: Dockerfile
target: runtime
depends_on:
- memgpt_db
ports:
Expand Down
2 changes: 2 additions & 0 deletions development.compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ services:
- .env
environment:
- MEMGPT_SERVER_PASS=test_server_token
- WATCHFILES_FORCE_POLLING=true

volumes:
- ./memgpt:/memgpt
- ~/.memgpt/credentials:/root/.memgpt/credentials
Expand Down
4 changes: 3 additions & 1 deletion memgpt/cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from memgpt.constants import CLI_WARNING_PREFIX, MEMGPT_DIR
from memgpt.credentials import MemGPTCredentials
from memgpt.data_types import EmbeddingConfig, LLMConfig, User
from memgpt.log import logger
from memgpt.log import get_logger
from memgpt.metadata import MetadataStore
from memgpt.migrate import migrate_all_agents, migrate_all_sources
from memgpt.server.constants import WS_DEFAULT_PORT
Expand All @@ -30,6 +30,8 @@
)
from memgpt.utils import open_folder_in_explorer, printd

logger = get_logger(__name__)


def migrate(
debug: Annotated[bool, typer.Option(help="Print extra tracebacks for failed migrations")] = False,
Expand Down
4 changes: 3 additions & 1 deletion memgpt/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import memgpt.utils as utils
from memgpt.constants import DEFAULT_HUMAN, DEFAULT_PERSONA, DEFAULT_PRESET, MEMGPT_DIR
from memgpt.data_types import AgentState, EmbeddingConfig, LLMConfig
from memgpt.log import logger
from memgpt.log import get_logger

logger = get_logger(__name__)


# helper functions for writing to configs
Expand Down
11 changes: 0 additions & 11 deletions memgpt/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,6 @@
DEFAULT_HUMAN = "basic"
DEFAULT_PRESET = "memgpt_chat"

# Used to isolate MemGPT logger instance from Dependant Libraries logging
LOGGER_NAME = "MemGPT"
LOGGER_DEFAULT_LEVEL = CRITICAL
# Where to store the logs
LOGGER_DIR = os.path.join(MEMGPT_DIR, "logs")
# filename of the log
LOGGER_FILENAME = "MemGPT.log"
# Number of log files to rotate
LOGGER_FILE_BACKUP_COUNT = 3
# Max Log file size in bytes
LOGGER_MAX_FILE_SIZE = 10485760
# LOGGER_LOG_LEVEL is use to convert Text to Logging level value for logging mostly for Cli input to setting level
LOGGER_LOG_LEVELS = {"CRITICAL": CRITICAL, "ERROR": ERROR, "WARN": WARN, "WARNING": WARNING, "INFO": INFO, "DEBUG": DEBUG, "NOTSET": NOTSET}

Expand Down
98 changes: 66 additions & 32 deletions memgpt/log.py
Original file line number Diff line number Diff line change
@@ -1,43 +1,77 @@
import logging
import os
import os.path
from logging.config import dictConfig
from logging.handlers import RotatingFileHandler
from pathlib import Path
from sys import stdout
from typing import Optional

from memgpt.constants import (
LOGGER_DEFAULT_LEVEL,
LOGGER_DIR,
LOGGER_FILE_BACKUP_COUNT,
LOGGER_FILENAME,
LOGGER_MAX_FILE_SIZE,
LOGGER_NAME,
)
from memgpt.settings import settings

# Checking if log directory exists
if not os.path.exists(LOGGER_DIR):
os.makedirs(LOGGER_DIR, exist_ok=True)
selected_log_level = logging.DEBUG if settings.debug else logging.INFO

# Create logger for MemGPT
logger = logging.getLogger(LOGGER_NAME)
logger.setLevel(LOGGER_DEFAULT_LEVEL)

# create console handler and set level to debug
console_handler = logging.StreamHandler()
def _setup_logfile() -> "Path":
"""ensure the logger filepath is in place
# create rotatating file handler
file_handler = RotatingFileHandler(
os.path.join(LOGGER_DIR, LOGGER_FILENAME), maxBytes=LOGGER_MAX_FILE_SIZE, backupCount=LOGGER_FILE_BACKUP_COUNT
)
Returns: the logfile Path
"""
logfile = Path(settings.memgpt_dir / "logs" / "MemGPT.log")
logfile.parent.mkdir(parents=True, exist_ok=True)
logfile.touch(exist_ok=True)
return logfile

# create formatters
console_formatter = logging.Formatter("%(name)s - %(levelname)s - %(message)s") # not datetime
file_formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")

# add formatter to console handler
console_handler.setFormatter(console_formatter)
# TODO: production logging should be much less invasive
DEVELOPMENT_LOGGING = {
"version": 1,
"disable_existing_loggers": True,
"formatters": {
"standard": {"format": "%(asctime)s - %(name)s - %(levelname)s - %(message)s"},
"no_datetime": {
"format": "%(name)s - %(levelname)s - %(message)s",
},
},
"handlers": {
"console": {
"level": selected_log_level,
"class": "logging.StreamHandler",
"stream": stdout,
"formatter": "no_datetime",
},
"file": {
"level": "DEBUG",
"class": "logging.handlers.RotatingFileHandler",
"filename": _setup_logfile(),
"maxBytes": 1024**2 * 10,
"backupCount": 3,
"formatter": "standard",
},
},
"loggers": {
"MemGPT": {
"level": logging.DEBUG if settings.debug else logging.INFO,
"handlers": [
"console",
"file",
],
"propagate": False,
},
"uvicorn": {
"level": "INFO",
"handlers": ["console"],
"propagate": False,
},
},
}

# add formatter for file handler
file_handler.setFormatter(file_formatter)

# add ch to logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)
def get_logger(name: Optional[str] = None) -> "logging.Logger":
"""returns the project logger, scoped to a child name if provided
Args:
name: will define a child logger
"""
dictConfig(DEVELOPMENT_LOGGING)
parent_logger = logging.getLogger("MemGPT")
if name:
return parent_logger.getChild(name)
return parent_logger
2 changes: 1 addition & 1 deletion memgpt/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ def get_user_from_api_key(self, api_key: str) -> Optional[User]:
"""Get the user associated with a given API key"""
token = self.get_api_key(api_key=api_key)
if token is None:
raise ValueError(f"Token {api_key} does not exist")
raise ValueError(f"Provided token does not exist")
else:
return self.get_user(user_id=token.user_id)

Expand Down
17 changes: 7 additions & 10 deletions memgpt/server/rest_api/auth/index.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field

from memgpt.log import get_logger
from memgpt.server.rest_api.interface import QueuingInterface
from memgpt.server.server import SyncServer

logger = get_logger(__name__)
router = APIRouter()


Expand All @@ -18,6 +20,7 @@ class AuthRequest(BaseModel):


def setup_auth_router(server: SyncServer, interface: QueuingInterface, password: str) -> APIRouter:

@router.post("/auth", tags=["auth"], response_model=AuthResponse)
def authenticate_user(request: AuthRequest) -> AuthResponse:
"""
Expand All @@ -26,16 +29,10 @@ def authenticate_user(request: AuthRequest) -> AuthResponse:
Currently, this is a placeholder that simply returns a UUID placeholder
"""
interface.clear()
try:
if request.password != password:
# raise HTTPException(status_code=400, detail="Incorrect credentials")
response = server.api_key_to_user(api_key=request.password)
else:
response = server.authenticate_user()
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"{e}")
if request.password != password:
response = server.api_key_to_user(api_key=request.password)
else:
response = server.authenticate_user()
return AuthResponse(uuid=response)

return router
4 changes: 2 additions & 2 deletions memgpt/server/server.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import json
import logging
import uuid
import warnings
from abc import abstractmethod
Expand Down Expand Up @@ -37,6 +36,7 @@
# TODO use custom interface
from memgpt.interface import AgentInterface # abstract
from memgpt.interface import CLIInterface # for printing to terminal
from memgpt.log import get_logger
from memgpt.metadata import MetadataStore
from memgpt.models.pydantic_models import (
DocumentModel,
Expand All @@ -47,7 +47,7 @@
ToolModel,
)

logger = logging.getLogger(__name__)
logger = get_logger(__name__)


class Server(object):
Expand Down
1 change: 1 addition & 0 deletions memgpt/server/startup.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/bin/sh
echo "Starting MEMGPT server..."
if [ "$MEMGPT_ENVIRONMENT" = "DEVELOPMENT" ] ; then
echo "Starting in development mode!"
uvicorn memgpt.server.rest_api.server:app --reload --reload-dir /memgpt --host 0.0.0.0 --port 8083
else
uvicorn memgpt.server.rest_api.server:app --host 0.0.0.0 --port 8083
Expand Down
4 changes: 4 additions & 0 deletions memgpt/settings.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
from pathlib import Path
from typing import Optional

from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="memgpt_")

memgpt_dir: Optional[Path] = Field(Path.home() / ".memgpt", env="MEMGPT_DIR")
debug: Optional[bool] = False
server_pass: Optional[str] = None
pg_db: Optional[str] = None
pg_user: Optional[str] = None
Expand Down
3 changes: 0 additions & 3 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@


def _reset_config():

# Use os.getenv with a fallback to os.environ.get
db_url = settings.memgpt_pg_uri

Expand All @@ -51,14 +50,12 @@ def _reset_config():
config.archival_storage_type = "postgres"
config.recall_storage_type = "postgres"
config.metadata_storage_type = "postgres"

config.save()
credentials.save()
print("_reset_config :: ", config.config_path)


def run_server():

load_dotenv()

_reset_config()
Expand Down
16 changes: 0 additions & 16 deletions tests/test_log.py

This file was deleted.

0 comments on commit 09f2e41

Please sign in to comment.