Co-authored-by: Ethan Knox <ethan.m.knox@gmail.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1016,3 +1016,4 @@ pgdata/
|
|||||||
## pytest mirrors
|
## pytest mirrors
|
||||||
memgpt/.pytest_cache/
|
memgpt/.pytest_cache/
|
||||||
memgpy/pytest.ini
|
memgpy/pytest.ini
|
||||||
|
**/**/pytest_cache
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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):
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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
|
|
||||||
Reference in New Issue
Block a user