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 ## pytest mirrors
memgpt/.pytest_cache/ memgpt/.pytest_cache/
memgpy/pytest.ini memgpy/pytest.ini
**/**/pytest_cache

View File

@@ -1,5 +1,5 @@
# The builder image, used to build the virtual environment # 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 ARG MEMGPT_ENVIRONMENT=PRODUCTION
ENV MEMGPT_ENVIRONMENT=${MEMGPT_ENVIRONMENT} ENV MEMGPT_ENVIRONMENT=${MEMGPT_ENVIRONMENT}
RUN pip install poetry==1.8.2 RUN pip install poetry==1.8.2
@@ -16,13 +16,13 @@ RUN poetry lock --no-update
RUN if [ "$MEMGPT_ENVIRONMENT" = "DEVELOPMENT" ] ; then \ RUN if [ "$MEMGPT_ENVIRONMENT" = "DEVELOPMENT" ] ; then \
poetry install --no-root -E "postgres server dev autogen" ; \ poetry install --no-root -E "postgres server dev autogen" ; \
else \ else \
poetry install --without dev --without local --no-root -E "postgres server" && \ poetry install --no-root -E "postgres server" && \
rm -rf $POETRY_CACHE_DIR ; \ rm -rf $POETRY_CACHE_DIR ; \
fi fi
# The runtime image, used to just run the code provided its virtual environment # 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 ARG MEMGPT_ENVIRONMENT=PRODUCTION
ENV MEMGPT_ENVIRONMENT=${MEMGPT_ENVIRONMENT} ENV MEMGPT_ENVIRONMENT=${MEMGPT_ENVIRONMENT}
ENV VIRTUAL_ENV=/app/.venv \ ENV VIRTUAL_ENV=/app/.venv \

View File

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

View File

@@ -14,6 +14,8 @@ services:
- .env - .env
environment: environment:
- MEMGPT_SERVER_PASS=test_server_token - MEMGPT_SERVER_PASS=test_server_token
- WATCHFILES_FORCE_POLLING=true
volumes: volumes:
- ./memgpt:/memgpt - ./memgpt:/memgpt
- ~/.memgpt/credentials:/root/.memgpt/credentials - ~/.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.constants import CLI_WARNING_PREFIX, MEMGPT_DIR
from memgpt.credentials import MemGPTCredentials from memgpt.credentials import MemGPTCredentials
from memgpt.data_types import EmbeddingConfig, LLMConfig, User 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.metadata import MetadataStore
from memgpt.migrate import migrate_all_agents, migrate_all_sources from memgpt.migrate import migrate_all_agents, migrate_all_sources
from memgpt.server.constants import WS_DEFAULT_PORT 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 from memgpt.utils import open_folder_in_explorer, printd
logger = get_logger(__name__)
def migrate( def migrate(
debug: Annotated[bool, typer.Option(help="Print extra tracebacks for failed migrations")] = False, 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 import memgpt.utils as utils
from memgpt.constants import DEFAULT_HUMAN, DEFAULT_PERSONA, DEFAULT_PRESET, MEMGPT_DIR from memgpt.constants import DEFAULT_HUMAN, DEFAULT_PERSONA, DEFAULT_PRESET, MEMGPT_DIR
from memgpt.data_types import AgentState, EmbeddingConfig, LLMConfig 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 # helper functions for writing to configs

View File

@@ -21,17 +21,6 @@ DEFAULT_PERSONA = "sam_pov"
DEFAULT_HUMAN = "basic" DEFAULT_HUMAN = "basic"
DEFAULT_PRESET = "memgpt_chat" 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_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} 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 logging
import os from logging.config import dictConfig
import os.path
from logging.handlers import RotatingFileHandler from logging.handlers import RotatingFileHandler
from pathlib import Path
from sys import stdout
from typing import Optional
from memgpt.constants import ( from memgpt.settings import settings
LOGGER_DEFAULT_LEVEL,
LOGGER_DIR,
LOGGER_FILE_BACKUP_COUNT,
LOGGER_FILENAME,
LOGGER_MAX_FILE_SIZE,
LOGGER_NAME,
)
# Checking if log directory exists selected_log_level = logging.DEBUG if settings.debug else logging.INFO
if not os.path.exists(LOGGER_DIR):
os.makedirs(LOGGER_DIR, exist_ok=True)
# Create logger for MemGPT
logger = logging.getLogger(LOGGER_NAME)
logger.setLevel(LOGGER_DEFAULT_LEVEL)
# create console handler and set level to debug def _setup_logfile() -> "Path":
console_handler = logging.StreamHandler() """ensure the logger filepath is in place
# create rotatating file handler Returns: the logfile Path
file_handler = RotatingFileHandler( """
os.path.join(LOGGER_DIR, LOGGER_FILENAME), maxBytes=LOGGER_MAX_FILE_SIZE, backupCount=LOGGER_FILE_BACKUP_COUNT 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 # TODO: production logging should be much less invasive
console_handler.setFormatter(console_formatter) 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 def get_logger(name: Optional[str] = None) -> "logging.Logger":
logger.addHandler(console_handler) """returns the project logger, scoped to a child name if provided
logger.addHandler(file_handler) 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""" """Get the user associated with a given API key"""
token = self.get_api_key(api_key=api_key) token = self.get_api_key(api_key=api_key)
if token is None: if token is None:
raise ValueError(f"Token {api_key} does not exist") raise ValueError(f"Provided token does not exist")
else: else:
return self.get_user(user_id=token.user_id) 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 fastapi import APIRouter, HTTPException
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from memgpt.log import get_logger
from memgpt.server.rest_api.interface import QueuingInterface from memgpt.server.rest_api.interface import QueuingInterface
from memgpt.server.server import SyncServer from memgpt.server.server import SyncServer
logger = get_logger(__name__)
router = APIRouter() router = APIRouter()
@@ -18,6 +20,7 @@ class AuthRequest(BaseModel):
def setup_auth_router(server: SyncServer, interface: QueuingInterface, password: str) -> APIRouter: def setup_auth_router(server: SyncServer, interface: QueuingInterface, password: str) -> APIRouter:
@router.post("/auth", tags=["auth"], response_model=AuthResponse) @router.post("/auth", tags=["auth"], response_model=AuthResponse)
def authenticate_user(request: AuthRequest) -> 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 Currently, this is a placeholder that simply returns a UUID placeholder
""" """
interface.clear() interface.clear()
try: if request.password != password:
if request.password != password: response = server.api_key_to_user(api_key=request.password)
# raise HTTPException(status_code=400, detail="Incorrect credentials") else:
response = server.api_key_to_user(api_key=request.password) response = server.authenticate_user()
else:
response = server.authenticate_user()
except HTTPException:
raise
except Exception as e:
raise HTTPException(status_code=500, detail=f"{e}")
return AuthResponse(uuid=response) return AuthResponse(uuid=response)
return router return router

View File

@@ -1,5 +1,4 @@
import json import json
import logging
import uuid import uuid
import warnings import warnings
from abc import abstractmethod from abc import abstractmethod
@@ -37,6 +36,7 @@ from memgpt.data_types import (
# TODO use custom interface # TODO use custom interface
from memgpt.interface import AgentInterface # abstract from memgpt.interface import AgentInterface # abstract
from memgpt.interface import CLIInterface # for printing to terminal from memgpt.interface import CLIInterface # for printing to terminal
from memgpt.log import get_logger
from memgpt.metadata import MetadataStore from memgpt.metadata import MetadataStore
from memgpt.models.pydantic_models import ( from memgpt.models.pydantic_models import (
DocumentModel, DocumentModel,
@@ -47,7 +47,7 @@ from memgpt.models.pydantic_models import (
ToolModel, ToolModel,
) )
logger = logging.getLogger(__name__) logger = get_logger(__name__)
class Server(object): class Server(object):

View File

@@ -1,6 +1,7 @@
#!/bin/sh #!/bin/sh
echo "Starting MEMGPT server..." echo "Starting MEMGPT server..."
if [ "$MEMGPT_ENVIRONMENT" = "DEVELOPMENT" ] ; then 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 uvicorn memgpt.server.rest_api.server:app --reload --reload-dir /memgpt --host 0.0.0.0 --port 8083
else else
uvicorn memgpt.server.rest_api.server:app --host 0.0.0.0 --port 8083 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 typing import Optional
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings): class Settings(BaseSettings):
model_config = SettingsConfigDict(env_prefix="memgpt_") 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 server_pass: Optional[str] = None
pg_db: Optional[str] = None pg_db: Optional[str] = None
pg_user: Optional[str] = None pg_user: Optional[str] = None

View File

@@ -29,7 +29,6 @@ test_server_token = "test_server_token"
def _reset_config(): def _reset_config():
# Use os.getenv with a fallback to os.environ.get # Use os.getenv with a fallback to os.environ.get
db_url = settings.memgpt_pg_uri db_url = settings.memgpt_pg_uri
@@ -51,14 +50,12 @@ def _reset_config():
config.archival_storage_type = "postgres" config.archival_storage_type = "postgres"
config.recall_storage_type = "postgres" config.recall_storage_type = "postgres"
config.metadata_storage_type = "postgres" config.metadata_storage_type = "postgres"
config.save() config.save()
credentials.save() credentials.save()
print("_reset_config :: ", config.config_path) print("_reset_config :: ", config.config_path)
def run_server(): def run_server():
load_dotenv() load_dotenv()
_reset_config() _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