Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 3c3d0d1

Browse files
Merge pull request #55 from john0isaac/improve-and-tests
Improve and tests
2 parents b05f38a + b2bb121 commit 3c3d0d1

29 files changed

+2935
-264
lines changed

‎.github/workflows/app-tests.yaml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,13 @@ on:
55
branches: [ main ]
66
pull_request:
77
branches: [ main ]
8+
workflow_dispatch:
9+
10+
permissions:
11+
contents: read
812

913
jobs:
10-
test_package:
14+
test-package:
1115
name: Test ${{ matrix.os }} Python ${{ matrix.python_version }}
1216
runs-on: ${{ matrix.os }}
1317
strategy:
@@ -65,4 +69,8 @@ jobs:
6569
run: |
6670
cd ./src/frontend
6771
npm install
68-
npm run build
72+
npm run build
73+
- name: Run MyPy
74+
run: python3 -m mypy .
75+
- name: Run Pytest
76+
run: python3 -m pytest

‎.github/workflows/python-code-quality.yaml

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,34 @@ name: Python code quality
33
on:
44
push:
55
branches: [ main ]
6+
paths:
7+
- '**.py'
8+
69
pull_request:
710
branches: [ main ]
11+
paths:
12+
- '**.py'
13+
14+
workflow_dispatch:
15+
16+
permissions:
17+
contents: read
818

919
jobs:
10-
build:
20+
checks-format-and-lint:
1121
runs-on: ubuntu-latest
1222
steps:
1323
- uses: actions/checkout@v4
1424
- name: Set up Python 3
1525
uses: actions/setup-python@v5
1626
with:
1727
python-version: "3.12"
28+
cache: 'pip'
1829
- name: Install dependencies
1930
run: |
20-
python -m pip install --upgrade pip
21-
pip install -r requirements-dev.txt
31+
python3 -m pip install --upgrade pip
32+
python3 -m pip install ruff
2233
- name: Lint with ruff
2334
run: ruff check .
2435
- name: Check formatting with ruff
25-
run: ruff format --check .
36+
run: ruff format . --check

‎pyproject.toml

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,26 @@
11
[tool.ruff]
22
line-length = 120
3-
target-version = "py311"
3+
target-version = "py312"
4+
lint.select = ["E", "F", "I", "UP"]
5+
lint.ignore = ["D203"]
6+
lint.isort.known-first-party = ["fastapi_app"]
47

5-
[tool.ruff.lint]
6-
select = ["E", "F", "I", "UP"]
7-
ignore = ["D203"]
8+
[tool.mypy]
9+
check_untyped_defs = true
10+
python_version = 3.12
11+
exclude = [".venv/*"]
812

9-
[tool.ruff.lint.isort]
10-
known-first-party = ["fastapi_app"]
13+
[tool.pytest.ini_options]
14+
addopts = "-ra --cov"
15+
testpaths = ["tests"]
16+
pythonpath = ['src']
17+
filterwarnings = ["ignore::DeprecationWarning"]
18+
19+
[[tool.mypy.overrides]]
20+
module = [
21+
"pgvector.*",
22+
]
23+
ignore_missing_imports = true
24+
25+
[tool.coverage.report]
26+
show_missing = true

‎requirements-dev.txt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@
22
ruff
33
pre-commit
44
pip-tools
5-
pip-compile-cross-platform
5+
pip-compile-cross-platform
6+
pytest
7+
pytest-cov
8+
pytest-asyncio
9+
mypy

‎src/fastapi_app/__init__.py

Lines changed: 34 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,57 @@
1-
import contextlib
21
import logging
32
import os
3+
from collections.abc import AsyncIterator
4+
from contextlib import asynccontextmanager
5+
from typing import TypedDict
46

5-
import azure.identity
67
from dotenv import load_dotenv
7-
from environs import Env
88
from fastapi import FastAPI
9-
10-
from .globals import global_storage
11-
from .openai_clients import create_openai_chat_client, create_openai_embed_client
12-
from .postgres_engine import create_postgres_engine_from_env
9+
from openai import AsyncAzureOpenAI, AsyncOpenAI
10+
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker
11+
12+
from fastapi_app.dependencies import (
13+
FastAPIAppContext,
14+
common_parameters,
15+
create_async_sessionmaker,
16+
get_azure_credentials,
17+
)
18+
from fastapi_app.openai_clients import create_openai_chat_client, create_openai_embed_client
19+
from fastapi_app.postgres_engine import create_postgres_engine_from_env
1320

1421
logger = logging.getLogger("ragapp")
1522

1623

17-
@contextlib.asynccontextmanager
18-
async def lifespan(app: FastAPI):
19-
load_dotenv(override=True)
24+
class State(TypedDict):
25+
sessionmaker: async_sessionmaker[AsyncSession]
26+
context: FastAPIAppContext
27+
chat_client: AsyncOpenAI | AsyncAzureOpenAI
28+
embed_client: AsyncOpenAI | AsyncAzureOpenAI
2029

21-
azure_credential = None
22-
try:
23-
if client_id := os.getenv("APP_IDENTITY_ID"):
24-
# Authenticate using a user-assigned managed identity on Azure
25-
# See web.bicep for value of APP_IDENTITY_ID
26-
logger.info(
27-
"Using managed identity for client ID %s",
28-
client_id,
29-
)
30-
azure_credential = azure.identity.ManagedIdentityCredential(client_id=client_id)
31-
else:
32-
azure_credential = azure.identity.DefaultAzureCredential()
33-
except Exception as e:
34-
logger.warning("Failed to authenticate to Azure: %s", e)
3530

31+
@asynccontextmanager
32+
async def lifespan(app: FastAPI) -> AsyncIterator[State]:
33+
context = await common_parameters()
34+
azure_credential = await get_azure_credentials()
3635
engine = await create_postgres_engine_from_env(azure_credential)
37-
global_storage.engine = engine
38-
39-
openai_chat_client, openai_chat_model = await create_openai_chat_client(azure_credential)
40-
global_storage.openai_chat_client = openai_chat_client
41-
global_storage.openai_chat_model = openai_chat_model
42-
43-
openai_embed_client, openai_embed_model, openai_embed_dimensions = await create_openai_embed_client(
44-
azure_credential
45-
)
46-
global_storage.openai_embed_client = openai_embed_client
47-
global_storage.openai_embed_model = openai_embed_model
48-
global_storage.openai_embed_dimensions = openai_embed_dimensions
49-
50-
yield
36+
sessionmaker = await create_async_sessionmaker(engine)
37+
chat_client = await create_openai_chat_client(azure_credential)
38+
embed_client = await create_openai_embed_client(azure_credential)
5139

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

5443

55-
def create_app():
56-
env = Env()
57-
58-
if not os.getenv("RUNNING_IN_PRODUCTION"):
59-
env.read_env(".env")
60-
logging.basicConfig(level=logging.INFO)
61-
else:
44+
def create_app(testing: bool = False):
45+
if os.getenv("RUNNING_IN_PRODUCTION"):
6246
logging.basicConfig(level=logging.WARNING)
47+
else:
48+
if not testing:
49+
load_dotenv(override=True)
50+
logging.basicConfig(level=logging.INFO)
6351

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

66-
from . import api_routes # noqa
67-
from . import frontend_routes # noqa
54+
from fastapi_app.routes import api_routes, frontend_routes
6855

6956
app.include_router(api_routes.router)
7057
app.mount("/", frontend_routes.router)

‎src/fastapi_app/api_models.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Any
22

3+
from openai.types.chat import ChatCompletionMessageParam
34
from pydantic import BaseModel
45

56

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

1011

1112
class ChatRequest(BaseModel):
12-
messages: list[Message]
13+
messages: list[ChatCompletionMessageParam]
1314
context: dict = {}
1415

1516

1617
class ThoughtStep(BaseModel):
1718
title: str
1819
description: Any
1920
props: dict = {}
21+
22+
23+
class RAGContext(BaseModel):
24+
data_points: dict[int, dict[str, Any]]
25+
thoughts: list[ThoughtStep]
26+
followup_questions: list[str] | None = None
27+
28+
29+
class RetrievalResponse(BaseModel):
30+
message: Message
31+
context: RAGContext
32+
session_state: Any | None = None
33+
34+
35+
class ItemPublic(BaseModel):
36+
id: int
37+
type: str
38+
brand: str
39+
name: str
40+
description: str
41+
price: float
42+
43+
44+
class ItemWithDistance(ItemPublic):
45+
distance: float

‎src/fastapi_app/api_routes.py

Lines changed: 0 additions & 83 deletions
This file was deleted.

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /