+```
+If you followed the steps above to install the pre-commit hooks, then you can just wait for those hooks to run `ruff` for you.
diff --git a/README.md b/README.md
index 5b25c775..e70b01af 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
# RAG on PostgreSQL
-[](placeholder)
-[](placeholder)
+[](https://codespaces.new/Azure-Samples/rag-postgres-openai-python)
+[](https://vscode.dev/redirect?url=vscode://ms-vscode-remote.remote-containers/cloneInVolume?url=https://github.com/azure-samples/rag-postgres-openai-python)
This project creates a web-based chat application with an API backend that can use OpenAI chat models to answer questions about the items in a PostgreSQL database table. The frontend is built with React and FluentUI, while the backend is written with Python and FastAPI.
@@ -42,7 +42,7 @@ You can run this template virtually by using GitHub Codespaces. The button will
3. Sign in to your Azure account:
```shell
- azd auth login --use-device-code
+ azd auth login
```
4. Provision the resources and deploy the code:
diff --git a/infra/main.bicep b/infra/main.bicep
index 2fbba05f..28e572af 100644
--- a/infra/main.bicep
+++ b/infra/main.bicep
@@ -246,7 +246,6 @@ module web 'web.bicep' = {
}
}
-
resource openAiResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' existing =
if (!empty(openAiResourceGroupName)) {
name: !empty(openAiResourceGroupName) ? openAiResourceGroupName : resourceGroup.name
@@ -293,15 +292,16 @@ module openAi 'core/ai/cognitiveservices.bicep' = {
}
// USER ROLES
-module openAiRoleUser 'core/security/role.bicep' = if (empty(runningOnGh)) {
- scope: openAiResourceGroup
- name: 'openai-role-user'
- params: {
- principalId: principalId
- roleDefinitionId: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'
- principalType: 'User'
+module openAiRoleUser 'core/security/role.bicep' =
+ if (empty(runningOnGh)) {
+ scope: openAiResourceGroup
+ name: 'openai-role-user'
+ params: {
+ principalId: principalId
+ roleDefinitionId: '5e0bd9bd-7b93-4f28-af87-19fc36ad61bd'
+ principalType: 'User'
+ }
}
-}
// Backend roles
module openAiRoleBackend 'core/security/role.bicep' = {
@@ -314,7 +314,6 @@ module openAiRoleBackend 'core/security/role.bicep' = {
}
}
-
output AZURE_LOCATION string = location
output APPLICATIONINSIGHTS_NAME string = monitoring.outputs.applicationInsightsName
diff --git a/infra/web.bicep b/infra/web.bicep
index f3766933..6a6c4145 100644
--- a/infra/web.bicep
+++ b/infra/web.bicep
@@ -9,13 +9,11 @@ param identityName string
param serviceName string = 'web'
param environmentVariables array = []
-
resource webIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = {
name: identityName
location: location
}
-
module app 'core/host/container-app-upsert.bicep' = {
name: '${serviceName}-container-app-module'
params: {
@@ -26,7 +24,8 @@ module app 'core/host/container-app-upsert.bicep' = {
exists: exists
containerAppsEnvironmentName: containerAppsEnvironmentName
containerRegistryName: containerRegistryName
- env: union(environmentVariables,
+ env: union(
+ environmentVariables,
[
{
name: 'APP_IDENTITY_ID'
diff --git a/pyproject.toml b/pyproject.toml
index 89e677bc..d6d7928b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,13 +1,10 @@
[tool.ruff]
line-length = 120
target-version = "py311"
+
+[tool.ruff.lint]
select = ["E", "F", "I", "UP"]
ignore = ["D203"]
-show-source = true
[tool.ruff.lint.isort]
known-first-party = ["fastapi_app"]
-
-[tool.black]
-line-length = 120
-target-version = ["py311"]
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 87fda3e7..0924b724 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,5 +1,4 @@
-r src/requirements.txt
ruff
-black
pre-commit
-pip-tools
\ No newline at end of file
+pip-tools
diff --git a/src/fastapi_app/api_routes.py b/src/fastapi_app/api_routes.py
index 0153250e..c3f7f106 100644
--- a/src/fastapi_app/api_routes.py
+++ b/src/fastapi_app/api_routes.py
@@ -11,7 +11,6 @@
@router.post("/chat")
async def chat_handler(chat_request: ChatRequest):
-
messages = [message.model_dump() for message in chat_request.messages]
overrides = chat_request.context.get("overrides", {})
diff --git a/src/fastapi_app/openai_clients.py b/src/fastapi_app/openai_clients.py
index 26c4a3f6..c3d4fd2d 100644
--- a/src/fastapi_app/openai_clients.py
+++ b/src/fastapi_app/openai_clients.py
@@ -38,7 +38,6 @@ async def create_openai_chat_client(azure_credential):
async def create_openai_embed_client(azure_credential):
-
OPENAI_EMBED_HOST = os.getenv("OPENAI_EMBED_HOST")
if OPENAI_EMBED_HOST == "azure":
token_provider = azure.identity.aio.get_bearer_token_provider(
diff --git a/src/fastapi_app/postgres_engine.py b/src/fastapi_app/postgres_engine.py
index fd3a2959..08a2f7a4 100644
--- a/src/fastapi_app/postgres_engine.py
+++ b/src/fastapi_app/postgres_engine.py
@@ -8,7 +8,6 @@
async def create_postgres_engine(*, host, username, database, password, sslmode, azure_credential) -> AsyncEngine:
-
if host.endswith(".database.azure.com"):
logger.info("Authenticating to Azure Database for PostgreSQL using Azure Identity...")
if azure_credential is None:
diff --git a/src/fastapi_app/postgres_searcher.py b/src/fastapi_app/postgres_searcher.py
index 0d9cfab0..1765a23b 100644
--- a/src/fastapi_app/postgres_searcher.py
+++ b/src/fastapi_app/postgres_searcher.py
@@ -6,7 +6,6 @@
class PostgresSearcher:
-
def __init__(self, engine):
self.async_session_maker = async_sessionmaker(engine, expire_on_commit=False)
@@ -30,7 +29,6 @@ async def search(
query_top: int = 5,
filters: list[dict] | None = None,
):
-
filter_clause_where, filter_clause_and = self.build_filter_clause(filters)
vector_query = f"""
diff --git a/src/fastapi_app/query_rewriter.py b/src/fastapi_app/query_rewriter.py
index a02157ee..99a86245 100644
--- a/src/fastapi_app/query_rewriter.py
+++ b/src/fastapi_app/query_rewriter.py
@@ -26,7 +26,7 @@ def build_search_function() -> list[ChatCompletionToolParam]:
"properties": {
"comparison_operator": {
"type": "string",
- "description": "Operator to compare the column value, either '>', '<', '>=', '<=', '=='",
+ "description": "Operator to compare the column value, either '>', '<', '>=', '<=', '=='", # noqa
},
"value": {
"type": "number",
diff --git a/src/fastapi_app/rag_advanced.py b/src/fastapi_app/rag_advanced.py
index d6bdc4dc..81a1fd51 100644
--- a/src/fastapi_app/rag_advanced.py
+++ b/src/fastapi_app/rag_advanced.py
@@ -17,7 +17,6 @@
class AdvancedRAGChat:
-
def __init__(
self,
*,
@@ -46,7 +45,6 @@ def __init__(
async def run(
self, messages: list[dict], overrides: dict[str, Any] = {}
) -> dict[str, Any] | AsyncGenerator[dict[str, Any], None]:
-
text_search = overrides.get("retrieval_mode") in ["text", "hybrid", None]
vector_search = overrides.get("retrieval_mode") in ["vectors", "hybrid", None]
top = overrides.get("top", 3)
@@ -70,7 +68,7 @@ async def run(
# Azure OpenAI takes the deployment name as the model name
model=self.chat_deployment if self.chat_deployment else self.chat_model,
temperature=0.0, # Minimize creativity for search query generation
- max_tokens=query_response_token_limit, # Setting too low risks malformed JSON, setting too high may affect performance
+ max_tokens=query_response_token_limit, # Setting too low risks malformed JSON, too high risks performance
n=1,
tools=build_search_function(),
tool_choice="auto",
diff --git a/src/fastapi_app/rag_simple.py b/src/fastapi_app/rag_simple.py
index 00dc97bd..fc0864a3 100644
--- a/src/fastapi_app/rag_simple.py
+++ b/src/fastapi_app/rag_simple.py
@@ -13,7 +13,6 @@
class SimpleRAGChat:
-
def __init__(
self,
*,
@@ -41,7 +40,6 @@ def __init__(
async def run(
self, messages: list[dict], overrides: dict[str, Any] = {}
) -> dict[str, Any] | AsyncGenerator[dict[str, Any], None]:
-
text_search = overrides.get("retrieval_mode") in ["text", "hybrid", None]
vector_search = overrides.get("retrieval_mode") in ["vectors", "hybrid", None]
top = overrides.get("top", 3)
diff --git a/src/fastapi_app/setup_postgres_azurerole.py b/src/fastapi_app/setup_postgres_azurerole.py
index 9406932a..a73e3826 100644
--- a/src/fastapi_app/setup_postgres_azurerole.py
+++ b/src/fastapi_app/setup_postgres_azurerole.py
@@ -11,7 +11,6 @@
async def assign_role_for_webapp(engine, app_identity_name):
-
async with engine.begin() as conn:
identities = await conn.execute(
text(f"select * from pgaadauth_list_principals(false) WHERE rolname = '{app_identity_name}'")
@@ -39,7 +38,6 @@ async def assign_role_for_webapp(engine, app_identity_name):
async def main():
-
parser = argparse.ArgumentParser(description="Create database schema")
parser.add_argument("--host", type=str, help="Postgres host")
parser.add_argument("--username", type=str, help="Postgres username")
@@ -64,7 +62,6 @@ async def main():
if __name__ == "__main__":
-
logging.basicConfig(level=logging.WARNING)
logger.setLevel(logging.INFO)
load_dotenv(override=True)
diff --git a/src/fastapi_app/setup_postgres_database.py b/src/fastapi_app/setup_postgres_database.py
index 54df1d1e..f39abf54 100644
--- a/src/fastapi_app/setup_postgres_database.py
+++ b/src/fastapi_app/setup_postgres_database.py
@@ -22,7 +22,6 @@ async def create_db_schema(engine):
async def main():
-
parser = argparse.ArgumentParser(description="Create database schema")
parser.add_argument("--host", type=str, help="Postgres host")
parser.add_argument("--username", type=str, help="Postgres username")
@@ -45,7 +44,6 @@ async def main():
if __name__ == "__main__":
-
logging.basicConfig(level=logging.WARNING)
logger.setLevel(logging.INFO)
load_dotenv(override=True)
diff --git a/src/fastapi_app/setup_postgres_seeddata.py b/src/fastapi_app/setup_postgres_seeddata.py
index 4ee31dfb..e8719c90 100644
--- a/src/fastapi_app/setup_postgres_seeddata.py
+++ b/src/fastapi_app/setup_postgres_seeddata.py
@@ -9,19 +9,21 @@
from sqlalchemy import select, text
from sqlalchemy.ext.asyncio import async_sessionmaker
-from fastapi_app.postgres_engine import create_postgres_engine_from_args, create_postgres_engine_from_env
+from fastapi_app.postgres_engine import (
+ create_postgres_engine_from_args,
+ create_postgres_engine_from_env,
+)
from fastapi_app.postgres_models import Item
logger = logging.getLogger("ragapp")
async def seed_data(engine):
-
# Check if Item table exists
async with engine.begin() as conn:
result = await conn.execute(
text(
- "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'items')"
+ "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'items')" # noqa
)
)
if not result.scalar():
@@ -29,7 +31,6 @@ async def seed_data(engine):
return
async with async_sessionmaker(engine, expire_on_commit=False)() as session:
-
# Insert the items from the JSON file into the database
current_dir = os.path.dirname(os.path.realpath(__file__))
with open(os.path.join(current_dir, "seed_data.json")) as f:
@@ -57,7 +58,6 @@ async def seed_data(engine):
async def main():
-
parser = argparse.ArgumentParser(description="Create database schema")
parser.add_argument("--host", type=str, help="Postgres host")
parser.add_argument("--username", type=str, help="Postgres username")
@@ -78,7 +78,6 @@ async def main():
if __name__ == "__main__":
-
logging.basicConfig(level=logging.WARNING)
logger.setLevel(logging.INFO)
load_dotenv(override=True)
diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json
index 3e2cf158..7a250730 100644
--- a/src/frontend/package-lock.json
+++ b/src/frontend/package-lock.json
@@ -31,7 +31,7 @@
"@vitejs/plugin-react": "^4.1.1",
"prettier": "^3.0.3",
"typescript": "^5.2.2",
- "vite": "^4.5.2"
+ "vite": "^4.5.3"
},
"engines": {
"node": ">=14.0.0"
@@ -3522,9 +3522,9 @@
}
},
"node_modules/vite": {
- "version": "4.5.2",
- "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
- "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz",
+ "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
"dev": true,
"dependencies": {
"esbuild": "^0.18.10",
@@ -5802,9 +5802,9 @@
"requires": {}
},
"vite": {
- "version": "4.5.2",
- "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.2.tgz",
- "integrity": "sha512-tBCZBNSBbHQkaGyhGCDUGqeo2ph8Fstyp6FMSvTtsXeZSPpSMGlviAOav2hxVTqFcx8Hj/twtWKsMJXNY0xI8w==",
+ "version": "4.5.3",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.3.tgz",
+ "integrity": "sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==",
"dev": true,
"requires": {
"esbuild": "^0.18.10",
diff --git a/src/frontend/package.json b/src/frontend/package.json
index 632d80a0..d6ae12e9 100644
--- a/src/frontend/package.json
+++ b/src/frontend/package.json
@@ -35,6 +35,6 @@
"prettier": "^3.0.3",
"typescript": "^5.2.2",
"@types/react-syntax-highlighter": "^15.5.7",
- "vite": "^4.5.2"
+ "vite": "^4.5.3"
}
}
diff --git a/src/gunicorn.conf.py b/src/gunicorn.conf.py
index c6bc3bac..03df0f74 100644
--- a/src/gunicorn.conf.py
+++ b/src/gunicorn.conf.py
@@ -8,4 +8,4 @@
worker_class = "uvicorn.workers.UvicornWorker"
-timeout = 600
\ No newline at end of file
+timeout = 600
diff --git a/src/pyproject.toml b/src/pyproject.toml
index 41aa0051..a4bb0de1 100644
--- a/src/pyproject.toml
+++ b/src/pyproject.toml
@@ -15,10 +15,9 @@ dependencies = [
"pgvector",
"openai",
"tiktoken",
- "openai-messages-token-helper",
- "rich"
+ "openai-messages-token-helper"
]
[build-system]
requires = ["flit_core<4"]
-build-backend = "flit_core.buildapi"
\ No newline at end of file
+build-backend = "flit_core.buildapi"
diff --git a/src/requirements.txt b/src/requirements.txt
index c4998aed..4ae104d4 100644
--- a/src/requirements.txt
+++ b/src/requirements.txt
@@ -36,25 +36,35 @@ cffi==1.16.0
charset-normalizer==3.3.2
# via requests
click==8.1.7
- # via uvicorn
-cryptography==42.0.6
+ # via
+ # typer
+ # uvicorn
+cryptography==42.0.7
# via
# azure-identity
# msal
# pyjwt
distro==1.9.0
# via openai
+dnspython==2.6.1
+ # via email-validator
+email-validator==2.1.1
+ # via fastapi
environs==11.0.0
# via fastapi_app (pyproject.toml)
-fastapi==0.110.3
- # via fastapi_app (pyproject.toml)
+fastapi==0.111.0
+ # via
+ # fastapi-cli
+ # fastapi_app (pyproject.toml)
+fastapi-cli==0.0.3
+ # via fastapi
frozenlist==1.4.1
# via
# aiohttp
# aiosignal
greenlet==3.0.3
# via sqlalchemy
-gunicorn==20.1.0
+gunicorn==22.0.0
# via fastapi_app (pyproject.toml)
h11==0.14.0
# via
@@ -65,15 +75,22 @@ httpcore==1.0.5
httptools==0.6.1
# via uvicorn
httpx==0.27.0
- # via openai
+ # via
+ # fastapi
+ # openai
idna==3.7
# via
# anyio
+ # email-validator
# httpx
# requests
# yarl
+jinja2==3.1.4
+ # via fastapi
markdown-it-py==3.0.0
# via rich
+markupsafe==2.1.5
+ # via jinja2
marshmallow==3.21.2
# via environs
mdurl==0.1.2
@@ -90,14 +107,17 @@ multidict==6.0.5
# yarl
numpy==1.26.4
# via pgvector
-openai==1.25.2
+openai==1.30.1
# via
# fastapi_app (pyproject.toml)
# openai-messages-token-helper
-openai-messages-token-helper==0.1.3
+openai-messages-token-helper==0.1.4
# via fastapi_app (pyproject.toml)
+orjson==3.10.3
+ # via fastapi
packaging==24.0
# via
+ # gunicorn
# marshmallow
# msal-extensions
pgvector==0.2.5
@@ -125,9 +145,11 @@ python-dotenv==1.0.1
# environs
# fastapi_app (pyproject.toml)
# uvicorn
+python-multipart==0.0.9
+ # via fastapi
pyyaml==6.0.1
# via uvicorn
-regex==2024.4.28
+regex==2024.5.10
# via tiktoken
requests==2.31.0
# via
@@ -135,7 +157,9 @@ requests==2.31.0
# msal
# tiktoken
rich==13.7.1
- # via fastapi_app (pyproject.toml)
+ # via typer
+shellingham==1.5.4
+ # via typer
six==1.16.0
# via azure-core
sniffio==1.3.1
@@ -147,12 +171,14 @@ sqlalchemy[asyncio]==2.0.30
# via fastapi_app (pyproject.toml)
starlette==0.37.2
# via fastapi
-tiktoken==0.6.0
+tiktoken==0.7.0
# via
# fastapi_app (pyproject.toml)
# openai-messages-token-helper
tqdm==4.66.4
# via openai
+typer==0.12.3
+ # via fastapi-cli
typing-extensions==4.11.0
# via
# azure-core
@@ -161,10 +187,16 @@ typing-extensions==4.11.0
# pydantic
# pydantic-core
# sqlalchemy
+ # typer
+ujson==5.10.0
+ # via fastapi
urllib3==2.2.1
# via requests
-uvicorn[standard]==0.23.2
- # via fastapi_app (pyproject.toml)
+uvicorn[standard]==0.29.0
+ # via
+ # fastapi
+ # fastapi-cli
+ # fastapi_app (pyproject.toml)
uvloop==0.19.0
# via uvicorn
watchfiles==0.21.0
@@ -173,6 +205,3 @@ websockets==12.0
# via uvicorn
yarl==1.9.4
# via aiohttp
-
-# The following packages are considered to be unsafe in a requirements file:
-# setuptools
pFad - Phonifier reborn
Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies:
Alternative Proxy
pFad Proxy
pFad v3 Proxy
pFad v4 Proxy