Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: debug logs in server (#1452) #1457

Merged
merged 4 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

Loading