Skip to content

Improve and tests #55

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 31 commits into from
Jul 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
676a2eb
add pytest dependency
john0isaac Jun 28, 2024
296db5c
restrict workflow run and cache dependencies for faster reruns
john0isaac Jun 28, 2024
d41a6df
mypy fixes
john0isaac Jun 28, 2024
44446bb
add dataclasses for response for better typing and easier usage
john0isaac Jun 28, 2024
8c58aac
setup test client for database and without database with test coverag…
john0isaac Jun 28, 2024
a0522e6
fix tests for windows and macos
john0isaac Jun 28, 2024
06c424b
use basemodel instead of dataclass to match the other models and for …
john0isaac Jun 29, 2024
cf3bc78
fix scopes and add app fixture
john0isaac Jun 29, 2024
4d9a536
fix tests to use existing setup
john0isaac Jul 1, 2024
7573249
add mocks and use monkey patch for setting env vars
john0isaac Jul 5, 2024
9fb9e54
create database and seed data
john0isaac Jul 5, 2024
61f9b8f
add tests for items handler and similar
john0isaac Jul 5, 2024
86b7986
add search_handler tests
john0isaac Jul 5, 2024
610a418
add chat tests
john0isaac Jul 5, 2024
eac85f0
remove content length assertion to allow for flexibility in response …
john0isaac Jul 6, 2024
b36a260
add azure openai env vars and use session scoped fixutres
john0isaac Jul 6, 2024
ea722d7
Typing improvements
pamelafox Jul 11, 2024
addd3da
fix mypy module error
john0isaac Jul 12, 2024
5261ab2
typing improvements
john0isaac Jul 12, 2024
39abb0f
ignore pgvector from mypy checks
john0isaac Jul 12, 2024
121b341
follow sqlalchmey example for using columns()
john0isaac Jul 12, 2024
d0a9a02
reimplement abstract functions
john0isaac Jul 12, 2024
c5d99f5
fix typo in env var name
john0isaac Jul 12, 2024
5c17e9a
use fastapi dependency instead of global storage
john0isaac Jul 12, 2024
3a6076c
fix type and add mypy to tests
john0isaac Jul 12, 2024
da5220c
remove multiple env loading, use single azure_credentials
john0isaac Jul 12, 2024
79a8a2d
use app state to store global vars
john0isaac Jul 13, 2024
3f0286b
add pydatnic types for Item table
john0isaac Jul 13, 2024
02d0b1b
add more tests and fix azure credentials mocking
john0isaac Jul 15, 2024
17fc97f
apply feedback from pr review
john0isaac Jul 17, 2024
b2bb121
add postgres searcher tests
john0isaac Jul 17, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions .github/workflows/app-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,13 @@ on:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:

permissions:
contents: read

jobs:
test_package:
test-package:
name: Test ${{ matrix.os }} Python ${{ matrix.python_version }}
runs-on: ${{ matrix.os }}
strategy:
Expand Down Expand Up @@ -65,4 +69,8 @@ jobs:
run: |
cd ./src/frontend
npm install
npm run build
npm run build
- name: Run MyPy
run: python3 -m mypy .
- name: Run Pytest
run: python3 -m pytest
19 changes: 15 additions & 4 deletions .github/workflows/python-code-quality.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,34 @@ name: Python code quality
on:
push:
branches: [ main ]
paths:
- '**.py'

pull_request:
branches: [ main ]
paths:
- '**.py'

workflow_dispatch:

permissions:
contents: read

jobs:
build:
checks-format-and-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python 3
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements-dev.txt
python3 -m pip install --upgrade pip
python3 -m pip install ruff
- name: Lint with ruff
run: ruff check .
- name: Check formatting with ruff
run: ruff format --check .
run: ruff format . --check
28 changes: 22 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,26 @@
[tool.ruff]
line-length = 120
target-version = "py311"
target-version = "py312"
lint.select = ["E", "F", "I", "UP"]
lint.ignore = ["D203"]
lint.isort.known-first-party = ["fastapi_app"]

[tool.ruff.lint]
select = ["E", "F", "I", "UP"]
ignore = ["D203"]
[tool.mypy]
check_untyped_defs = true
python_version = 3.12
exclude = [".venv/*"]

[tool.ruff.lint.isort]
known-first-party = ["fastapi_app"]
[tool.pytest.ini_options]
addopts = "-ra --cov"
testpaths = ["tests"]
pythonpath = ['src']
filterwarnings = ["ignore::DeprecationWarning"]

[[tool.mypy.overrides]]
module = [
"pgvector.*",
]
ignore_missing_imports = true

[tool.coverage.report]
show_missing = true
6 changes: 5 additions & 1 deletion requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
ruff
pre-commit
pip-tools
pip-compile-cross-platform
pip-compile-cross-platform
pytest
pytest-cov
pytest-asyncio
mypy
81 changes: 34 additions & 47 deletions src/fastapi_app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,70 +1,57 @@
import contextlib
import logging
import os
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from typing import TypedDict

import azure.identity
from dotenv import load_dotenv
from environs import Env
from fastapi import FastAPI

from .globals import global_storage
from .openai_clients import create_openai_chat_client, create_openai_embed_client
from .postgres_engine import create_postgres_engine_from_env
from openai import AsyncAzureOpenAI, AsyncOpenAI
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker

from fastapi_app.dependencies import (
FastAPIAppContext,
common_parameters,
create_async_sessionmaker,
get_azure_credentials,
)
from fastapi_app.openai_clients import create_openai_chat_client, create_openai_embed_client
from fastapi_app.postgres_engine import create_postgres_engine_from_env

logger = logging.getLogger("ragapp")


@contextlib.asynccontextmanager
async def lifespan(app: FastAPI):
load_dotenv(override=True)
class State(TypedDict):
sessionmaker: async_sessionmaker[AsyncSession]
context: FastAPIAppContext
chat_client: AsyncOpenAI | AsyncAzureOpenAI
embed_client: AsyncOpenAI | AsyncAzureOpenAI

azure_credential = None
try:
if client_id := os.getenv("APP_IDENTITY_ID"):
# Authenticate using a user-assigned managed identity on Azure
# See web.bicep for value of APP_IDENTITY_ID
logger.info(
"Using managed identity for client ID %s",
client_id,
)
azure_credential = azure.identity.ManagedIdentityCredential(client_id=client_id)
else:
azure_credential = azure.identity.DefaultAzureCredential()
except Exception as e:
logger.warning("Failed to authenticate to Azure: %s", e)

@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[State]:
context = await common_parameters()
azure_credential = await get_azure_credentials()
engine = await create_postgres_engine_from_env(azure_credential)
global_storage.engine = engine

openai_chat_client, openai_chat_model = await create_openai_chat_client(azure_credential)
global_storage.openai_chat_client = openai_chat_client
global_storage.openai_chat_model = openai_chat_model

openai_embed_client, openai_embed_model, openai_embed_dimensions = await create_openai_embed_client(
azure_credential
)
global_storage.openai_embed_client = openai_embed_client
global_storage.openai_embed_model = openai_embed_model
global_storage.openai_embed_dimensions = openai_embed_dimensions

yield
sessionmaker = await create_async_sessionmaker(engine)
chat_client = await create_openai_chat_client(azure_credential)
embed_client = await create_openai_embed_client(azure_credential)

yield {"sessionmaker": sessionmaker, "context": context, "chat_client": chat_client, "embed_client": embed_client}
await engine.dispose()


def create_app():
env = Env()

if not os.getenv("RUNNING_IN_PRODUCTION"):
env.read_env(".env")
logging.basicConfig(level=logging.INFO)
else:
def create_app(testing: bool = False):
if os.getenv("RUNNING_IN_PRODUCTION"):
logging.basicConfig(level=logging.WARNING)
else:
if not testing:
load_dotenv(override=True)
logging.basicConfig(level=logging.INFO)

app = FastAPI(docs_url="/docs", lifespan=lifespan)

from . import api_routes # noqa
from . import frontend_routes # noqa
from fastapi_app.routes import api_routes, frontend_routes

app.include_router(api_routes.router)
app.mount("/", frontend_routes.router)
Expand Down
28 changes: 27 additions & 1 deletion src/fastapi_app/api_models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from typing import Any

from openai.types.chat import ChatCompletionMessageParam
from pydantic import BaseModel


Expand All @@ -9,11 +10,36 @@ class Message(BaseModel):


class ChatRequest(BaseModel):
messages: list[Message]
messages: list[ChatCompletionMessageParam]
context: dict = {}


class ThoughtStep(BaseModel):
title: str
description: Any
props: dict = {}


class RAGContext(BaseModel):
data_points: dict[int, dict[str, Any]]
thoughts: list[ThoughtStep]
followup_questions: list[str] | None = None


class RetrievalResponse(BaseModel):
message: Message
context: RAGContext
session_state: Any | None = None


class ItemPublic(BaseModel):
id: int
type: str
brand: str
name: str
description: str
price: float


class ItemWithDistance(ItemPublic):
distance: float
83 changes: 0 additions & 83 deletions src/fastapi_app/api_routes.py

This file was deleted.

Loading
Loading