Skip to content

Commit

Permalink
Support for personas (#81)
Browse files Browse the repository at this point in the history
* Initial implementation of Personas API in klatchat

* Added personas endpoints and optimized middleware

* Support for generic configs management

---------

Co-authored-by: NeonKirill <[email protected]>
  • Loading branch information
NeonKirill and kirgrim authored Mar 22, 2024
1 parent 67fa984 commit 19adcf8
Show file tree
Hide file tree
Showing 25 changed files with 948 additions and 91 deletions.
101 changes: 32 additions & 69 deletions chat_server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,39 +25,25 @@
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
import logging
import importlib
import os
import random
import string
import sys
import time
import socketio

from typing import Union
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.testclient import TestClient
from starlette.requests import Request

from utils.common import get_version
from utils.logging_utils import LOG
from starlette.middleware.cors import CORSMiddleware

sys.path.append(os.path.dirname(os.path.abspath(__file__)))

from .sio import sio
from .blueprints import (
admin as admin_blueprint,
auth as auth_blueprint,
chat as chat_blueprint,
users as users_blueprint,
languages as languages_blueprint,
files_api as files_blueprint,
preferences as preferences_blueprint,
)
from utils.common import get_version
from utils.logging_utils import LOG
from chat_server.server_utils.middleware import SUPPORTED_MIDDLEWARE


def create_app(
testing_mode: bool = False, sio_server: socketio.AsyncServer = sio
testing_mode: bool = False, sio_server: socketio.AsyncServer = None
) -> Union[FastAPI, socketio.ASGIApp]:
"""
Application factory for the Klatchat Server
Expand All @@ -66,62 +52,39 @@ def create_app(
:param sio_server: socket io server instance (optional)
"""
app_version = get_version("chat_server/version.py")
LOG.name = os.environ.get("LOG_NAME", "server_err")
LOG.base_path = os.environ.get("LOG_BASE_PATH", ".")
LOG.init(
config={
"level": os.environ.get("LOG_LEVEL", "INFO"),
"path": os.environ.get("LOG_PATH", os.getcwd()),
}
)
logger = LOG.create_logger("chat_server")
logger.addHandler(logging.StreamHandler())
LOG.info(f"Starting Klatchat Server v{app_version}")
chat_app = FastAPI(title="Klatchat Server API", version=app_version)

@chat_app.middleware("http")
async def log_requests(request: Request, call_next):
"""Logs requests and gracefully handles Internal Server Errors"""
idem = "".join(random.choices(string.ascii_uppercase + string.digits, k=6))
LOG.info(f"rid={idem} start request path={request.url.path}")
start_time = time.time()
try:
response = await call_next(request)
process_time = (time.time() - start_time) * 1000
formatted_process_time = "{0:.2f}".format(process_time)
log_message = f"rid={idem} completed_in={formatted_process_time}ms status_code={response.status_code}"
LOG.info(log_message)
return response
except Exception as ex:
LOG.error(f"rid={idem} received an exception {ex}")
return None

chat_app.include_router(admin_blueprint.router)
chat_app.include_router(auth_blueprint.router)
chat_app.include_router(chat_blueprint.router)
chat_app.include_router(users_blueprint.router)
chat_app.include_router(languages_blueprint.router)
chat_app.include_router(files_blueprint.router)
chat_app.include_router(preferences_blueprint.router)

# __cors_allowed_origins = os.environ.get('COST_ALLOWED_ORIGINS', '').split(',') or ['*']
#
# LOG.info(f'CORS_ALLOWED_ORIGINS={__cors_allowed_origins}')
#
# chat_app.user_middleware.clear()
chat_app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# chat_app.middleware_stack = chat_app.build_middleware_stack()
_init_middleware(app=chat_app)
_init_blueprints(app=chat_app)

if testing_mode:
chat_app = TestClient(chat_app)

if sio_server:
chat_app = socketio.ASGIApp(socketio_server=sio_server, other_asgi_app=chat_app)

LOG.info(f"Starting Klatchat Server v{app_version}")

return chat_app


def _init_blueprints(app: FastAPI):
blueprint_module = importlib.import_module("blueprints")
for blueprint_module_name in dir(blueprint_module):
if blueprint_module_name.endswith("blueprint"):
blueprint_obj = importlib.import_module(
f"blueprints.{blueprint_module_name.split('_blueprint')[0]}"
)
app.include_router(blueprint_obj.router)


def _init_middleware(app: FastAPI):
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
for middleware_class in SUPPORTED_MIDDLEWARE:
app.add_middleware(middleware_class=middleware_class)
40 changes: 40 additions & 0 deletions chat_server/blueprints/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework
# All trademark and other rights reserved by their respective owners
# Copyright 2008-2022 Neongecko.com Inc.
# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds,
# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo
# BSD-3 License
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

# Import blueprint here so it will be included
from . import (
admin as admin_blueprint,
auth as auth_blueprint,
chat as chat_blueprint,
users as users_blueprint,
languages as languages_blueprint,
files_api as files_api_blueprint,
preferences as preferences_blueprint,
personas as personas_blueprint,
configs as configs_blueprint,
)
68 changes: 68 additions & 0 deletions chat_server/blueprints/configs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# NEON AI (TM) SOFTWARE, Software Development Kit & Application Framework
# All trademark and other rights reserved by their respective owners
# Copyright 2008-2022 Neongecko.com Inc.
# Contributors: Daniel McKnight, Guy Daniels, Elon Gasper, Richard Leeds,
# Regina Bloomstine, Casimiro Ferreira, Andrii Pernatii, Kirill Hrymailo
# BSD-3 License
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
# 3. Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from this
# software without specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

from fastapi import APIRouter, Depends
from starlette.responses import JSONResponse

from chat_server.server_utils.dependencies import CurrentUserDependency
from chat_server.server_utils.exceptions import (
ItemNotFoundException,
UserUnauthorizedException,
)
from chat_server.server_utils.http_utils import KlatAPIResponse
from chat_server.server_utils.models.configs import SetConfigModel, ConfigModel
from utils.database_utils.mongo_utils.queries.wrapper import MongoDocumentsAPI

router = APIRouter(
prefix="/configs",
responses={"404": {"description": "Unknown endpoint"}},
)


@router.get("/{config_property}")
async def get_config_data(model: ConfigModel = Depends()) -> JSONResponse:
"""Retrieves configured data by name"""
items = MongoDocumentsAPI.CONFIGS.get_by_name(
config_name=model.config_property, version=model.version
)
return JSONResponse(content=items)


@router.put("/{config_property}")
async def update_config(
current_user: CurrentUserDependency, model: SetConfigModel = Depends()
) -> JSONResponse:
"""Updates provided config by name"""
if "admin" not in current_user.roles:
raise UserUnauthorizedException
updated_data = MongoDocumentsAPI.CONFIGS.update_by_name(
config_name=model.config_property, version=model.version, data=model.data
)
if updated_data.matched_count == 0:
raise ItemNotFoundException
return KlatAPIResponse.OK
Loading

0 comments on commit 19adcf8

Please sign in to comment.