diff --git a/front_end/public/favicon.ico b/front_end/public/assets/favicon.ico
similarity index 100%
rename from front_end/public/favicon.ico
rename to front_end/public/assets/favicon.ico
diff --git a/front_end/public/manifest.json b/front_end/public/assets/manifest.json
similarity index 100%
rename from front_end/public/manifest.json
rename to front_end/public/assets/manifest.json
diff --git a/front_end/public/robots.txt b/front_end/public/assets/robots.txt
similarity index 100%
rename from front_end/public/robots.txt
rename to front_end/public/assets/robots.txt
diff --git a/front_end/public/index.html b/front_end/public/index.html
index f22e966..19e3aed 100644
--- a/front_end/public/index.html
+++ b/front_end/public/index.html
@@ -4,9 +4,9 @@
LLM Gateway
-
+
-
+
diff --git a/front_end/public/styles.css b/front_end/public/styles/styles.css
similarity index 100%
rename from front_end/public/styles.css
rename to front_end/public/styles/styles.css
diff --git a/llm_gateway/_version.py b/llm_gateway/_version.py
new file mode 100644
index 0000000..81c9261
--- /dev/null
+++ b/llm_gateway/_version.py
@@ -0,0 +1,4 @@
+# file generated by setuptools_scm
+# don't change, don't track in version control
+__version__ = version = "0.1.1.dev4"
+__version_tuple__ = version_tuple = (0, 1, 1, "dev4")
diff --git a/llm_gateway/app.py b/llm_gateway/app.py
index 54b87a1..566e46c 100644
--- a/llm_gateway/app.py
+++ b/llm_gateway/app.py
@@ -15,37 +15,90 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import pathlib
+
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
+from fastapi.responses import HTMLResponse
+from fastapi.staticfiles import StaticFiles
from llm_gateway.routers import cohere_api, openai_api
-app = FastAPI()
-app.title = "LLM Proxy"
-app.description = "LLM Proxy Developed by Wealthsimple"
+def create_bare_app() -> FastAPI:
+ app = FastAPI()
+ app.title = "LLM Proxy"
+ app.description = "LLM Proxy Developed by Wealthsimple"
+ return app
+
+
+def attach_api_service(app: FastAPI) -> FastAPI:
+ api = FastAPI(openapi_prefix="/api")
+ api.include_router(openai_api.router, prefix="/openai")
+ api.include_router(cohere_api.router, prefix="/cohere")
+
+ @api.get("/healthcheck")
+ async def healthcheck():
+ """
+ Endpoint to verify that the service is up and running
+ """
+ return {"message": "llm-gateway is healthy"}
+
+ app.mount("/api", api, name="api")
+ return app
+
+
+def attach_middleware(app: FastAPI) -> FastAPI:
+ # Allow Front-end Origin in local development
+ origins = ["http://localhost:3000"]
+
+ app.add_middleware(
+ CORSMiddleware,
+ allow_origins=origins,
+ allow_credentials=True,
+ allow_methods=["*"],
+ allow_headers=["*"],
+ )
+ return app
+
+
+def mount_front_end(app: FastAPI) -> FastAPI:
+ FRONT_END_BUILD_DIRECTORY = (
+ pathlib.Path(__file__).parent.parent / "front_end" / "build"
+ )
+ app.mount(
+ "/assets",
+ StaticFiles(directory=FRONT_END_BUILD_DIRECTORY / "assets"),
+ name="assets",
+ )
+
+ app.mount(
+ "/static",
+ StaticFiles(directory=FRONT_END_BUILD_DIRECTORY / "static"),
+ name="static",
+ )
+
+ app.mount(
+ "/styles",
+ StaticFiles(directory=FRONT_END_BUILD_DIRECTORY / "styles"),
+ name="styles",
+ )
-api = FastAPI(openapi_prefix="/api")
-api.include_router(openai_api.router, prefix="/openai")
-api.include_router(cohere_api.router, prefix="/cohere")
+ @app.get("/")
+ async def home() -> HTMLResponse:
+ with open(FRONT_END_BUILD_DIRECTORY / "index.html", "r", encoding="utf-8") as f:
+ return HTMLResponse(f.read())
-app.mount("/api", api, name="api")
+ return app
-# Allow Front-end Origin in local development
-origins = ["http://localhost:3000"]
-app.add_middleware(
- CORSMiddleware,
- allow_origins=origins,
- allow_credentials=True,
- allow_methods=["*"],
- allow_headers=["*"],
-)
+def create_app() -> FastAPI:
+ app = create_bare_app()
+ app = attach_middleware(app)
+ app = mount_front_end(app)
+ app = attach_api_service(app)
+ print("Finished app initialization ")
+ return app
-@api.get("/healthcheck")
-async def healthcheck():
- """
- Endpoint to verify that the service is up and running
- """
- return {"message": "llm-gateway is healthy"}
+app = create_app()
diff --git a/llm_gateway/cli.py b/llm_gateway/cli.py
new file mode 100644
index 0000000..3c3e2f1
--- /dev/null
+++ b/llm_gateway/cli.py
@@ -0,0 +1,32 @@
+import click
+
+
+@click.group(no_args_is_help=True)
+def cli():
+ pass
+
+
+@cli.command("start")
+@click.option(
+ "--host",
+ type=str,
+ help="Bind socket to this host. Default: 127.0.0.1",
+)
+@click.option(
+ "--port",
+ type=int,
+ help="Bind socket to this port. Default: 8000",
+)
+def start(
+ host=None,
+ port=None,
+):
+ import uvicorn
+
+ host = host or "127.0.0.1"
+ port = 8000 if port is None else port
+ uvicorn.run("llm_gateway.app:app", host=host, port=port, log_level="info")
+
+
+if __name__ == "__main__":
+ cli()
diff --git a/poetry.lock b/poetry.lock
index 343cd3e..515fc96 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -366,14 +366,14 @@ files = [
[[package]]
name = "click"
-version = "8.1.3"
+version = "8.1.4"
description = "Composable command line interface toolkit"
category = "main"
optional = false
python-versions = ">=3.7"
files = [
- {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
- {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"},
+ {file = "click-8.1.4-py3-none-any.whl", hash = "sha256:2739815aaa5d2c986a88f1e9230c55e17f0caad3d958a5e13ad0797c166db9e3"},
+ {file = "click-8.1.4.tar.gz", hash = "sha256:b97d0c74955da062a7d4ef92fadb583806a585b2ea81958a81bd72726cbb8e37"},
]
[package.dependencies]
@@ -2037,4 +2037,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more
[metadata]
lock-version = "2.0"
python-versions = "3.11.*"
-content-hash = "b6cc8d020d3452611da92779246deb064100ee2e4fde1ad8f236d94458a55825"
+content-hash = "21d0c214ebcb9a7b94264a03fc407d9cff516a6ae23269876fc2af73878b12ef"
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index 947f389..0000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,33 +0,0 @@
-[tool.poetry]
-name = "llm-gateway"
-version = "0.1.0"
-description = "Service to facilitate access to large language models"
-authors = ["Data Science & Engineering @ Wealthsimple "]
-license = "Apache-2.0"
-readme = "README.md"
-
-[tool.poetry.dependencies]
-python = "3.11.*"
-fastapi = "^0.95.0"
-pydantic = "^1.10.7"
-psycopg2-binary = "~2.9.3"
-alembic = "~1.8.0"
-sqlalchemy = ">=1.3.0"
-uvicorn = {extras = ["standard"], version = "^0.21.1"}
-openai = {extras = ["datalib"], version = "^0.27.4"}
-cohere = "^4.6.1"
-
-[tool.poetry.group.dev.dependencies]
-black = "23.3.0"
-isort = "5.12.0"
-flake8 = "^6.0.0"
-pre-commit = "^3.2.2"
-pytest = "^7.3.0"
-urllib3 = "1.26.15" # https://github.com/psf/requests/issues/6437
-
-[build-system]
-requires = ["poetry-core"]
-build-backend = "poetry.core.masonry.api"
-
-[tool.isort]
-profile = "black"
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..33017e4
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,63 @@
+import os
+from os.path import exists
+
+from setuptools import find_packages, setup
+
+description = open("README.md").read() if exists("README.md") else ""
+
+setup(
+ name="llm_gateway",
+ description="",
+ long_description=description,
+ long_description_content_type="text/markdown",
+ url="https://github.com/wealthsimple/llm-gateway",
+ author="Wealthsimple Data Science & Engineering",
+ author_email="data@wealthsimple.com",
+ license="Apache License 2.0",
+ packages=find_packages(include=["llm_gateway", "front_end"]),
+ package_data={"front_end": ["front_end/**"]},
+ entry_points={
+ "console_scripts": [
+ "llm_gateway = llm_gateway.cli:cli",
+ ],
+ },
+ use_scm_version={
+ "write_to": "llm_gateway/_version.py",
+ "fallback_version": "0.0.0",
+ "local_scheme": "no-local-version",
+ },
+ setup_requires=["setuptools_scm"],
+ install_requires=[
+ "click",
+ "fastapi>=0.95.0",
+ "pydantic>=1.10.7",
+ "psycopg2-binary~=2.9.3",
+ "alembic~=1.8.0",
+ "sqlalchemy>=1.3.0",
+ "uvicorn[standard]>=0.21.1",
+ "openai[datalib]>=0.27.4",
+ "cohere>=4.6.1",
+ "click>=8.1.4",
+ ],
+ extras_require={
+ "openai": [
+ "datalib",
+ ],
+ "dev": [
+ "black==23.3.0",
+ "isort==5.12.0",
+ "flake8>=6.0.0",
+ "pre-commit>=3.2.2",
+ "pytest>=7.3.0",
+ "urllib3==1.26.15",
+ ],
+ },
+ classifiers=[
+ "Intended Audience :: Developers",
+ "Intended Audience :: Science/Research",
+ "License :: OSI Approved :: Apache Software License",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3 :: Only",
+ ],
+)