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

Co-authored-by: Ethan Knox <ethan.m.knox@gmail.com>
This commit is contained in:
Sarah Wooders
2024-06-16 19:21:53 -07:00
committed by GitHub
parent 24f21284a1
commit b1fbece818
15 changed files with 94 additions and 80 deletions

1
.gitignore vendored
View File

@@ -1016,3 +1016,4 @@ pgdata/
## pytest mirrors
memgpt/.pytest_cache/
memgpy/pytest.ini
**/**/pytest_cache

View File

@@ -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
@@ -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 \

View File

@@ -22,6 +22,7 @@ services:
build:
context: .
dockerfile: Dockerfile
target: runtime
depends_on:
- memgpt_db
ports:

View File

@@ -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

View File

@@ -19,7 +19,7 @@ from memgpt.config import MemGPTConfig
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
@@ -30,6 +30,8 @@ from memgpt.streaming_interface import (
)
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,

View File

@@ -9,7 +9,9 @@ import memgpt
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

View File

@@ -21,17 +21,6 @@ DEFAULT_PERSONA = "sam_pov"
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}

View File

@@ -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

View File

@@ -418,7 +418,7 @@ class MetadataStore:
"""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)

View File

@@ -3,9 +3,11 @@ from uuid import UUID
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()
@@ -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:
"""
@@ -26,16 +29,10 @@ def setup_auth_router(server: SyncServer, interface: QueuingInterface, password:
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

View File

@@ -1,5 +1,4 @@
import json
import logging
import uuid
import warnings
from abc import abstractmethod
@@ -37,6 +36,7 @@ from memgpt.data_types import (
# 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,
@@ -47,7 +47,7 @@ from memgpt.models.pydantic_models import (
ToolModel,
)
logger = logging.getLogger(__name__)
logger = get_logger(__name__)
class Server(object):

View File

@@ -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

View File

@@ -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

View File

@@ -29,7 +29,6 @@ test_server_token = "test_server_token"
def _reset_config():
# Use os.getenv with a fallback to os.environ.get
db_url = settings.memgpt_pg_uri
@@ -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()

View File

@@ -1,16 +0,0 @@
import logging
from memgpt.constants import LOGGER_LOG_LEVELS
from memgpt.log import logger
def test_log_debug():
# test setting logging level
assert logging.DEBUG == LOGGER_LOG_LEVELS["DEBUG"]
logger.setLevel(LOGGER_LOG_LEVELS["DEBUG"])
assert logger.isEnabledFor(logging.DEBUG)
# Assert that the message was logged
assert logger.hasHandlers()
logger.debug("This is a Debug message")
assert 1 == 1