Skip to content

Commit d1e990c

Browse files
authored
Merge pull request #69 from Azure-Samples/uvicorn-fewshots
Move to Uvicorn, use few shots
2 parents ded7bf4 + 24092b1 commit d1e990c

File tree

13 files changed

+70
-29
lines changed

13 files changed

+70
-29
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ repos:
44
hooks:
55
- id: check-yaml
66
- id: end-of-file-fixer
7+
exclude: ^tests/snapshots
78
- id: trailing-whitespace
89
- repo: https://github.com/astral-sh/ruff-pre-commit
910
rev: v0.1.0

.vscode/launch.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
"name": "Backend",
1818
"type": "debugpy",
1919
"request": "launch",
20+
"cwd": "${workspaceFolder}",
2021
"module": "uvicorn",
2122
"args": ["fastapi_app:create_app", "--factory", "--reload"],
22-
"justMyCode": true
23+
"justMyCode": false
2324
}
2425
],
2526
"compounds": [

src/backend/Dockerfile

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM mcr.microsoft.com/devcontainers/python:3.12-bullseye
1+
FROM python:3.12-bullseye
22

33
RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \
44
&& apt-get -y install --no-install-recommends postgresql-client \
@@ -12,8 +12,9 @@ WORKDIR /demo-code
1212
COPY requirements.txt .
1313
RUN python -m pip install -r requirements.txt
1414

15-
COPY entrypoint.sh .
16-
RUN chmod +x entrypoint.sh
17-
1815
COPY . .
19-
CMD bash -c ". entrypoint.sh"
16+
RUN python -m pip install .
17+
18+
RUN chmod +x entrypoint.sh
19+
EXPOSE 8000
20+
CMD ["bash", "-c", ". entrypoint.sh"]

src/backend/entrypoint.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
#!/bin/bash
22
set -e
3-
python3 -m pip install .
4-
python3 -m gunicorn "fastapi_app:create_app()"
3+
python3 -m uvicorn "fastapi_app:create_app" --factory --port 8000

src/backend/fastapi_app/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ async def lifespan(app: fastapi.FastAPI) -> AsyncIterator[State]:
4747

4848
def create_app(testing: bool = False):
4949
if os.getenv("RUNNING_IN_PRODUCTION"):
50-
logging.basicConfig(level=logging.WARNING)
50+
# You may choose to reduce this to logging.WARNING for production
51+
logging.basicConfig(level=logging.INFO)
5152
else:
5253
if not testing:
5354
load_dotenv(override=True)
5455
logging.basicConfig(level=logging.INFO)
5556
# Turn off particularly noisy INFO level logs from Azure Core SDK:
5657
logging.getLogger("azure.core.pipeline.policies.http_logging_policy").setLevel(logging.WARNING)
58+
logging.getLogger("azure.identity").setLevel(logging.WARNING)
5759

5860
if os.getenv("APPLICATIONINSIGHTS_CONNECTION_STRING"):
5961
logger.info("Configuring Azure Monitor")
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
[
2+
{"role": "user", "content": "good options for climbing gear that can be used outside?"},
3+
{"role": "assistant", "tool_calls": [
4+
{
5+
"id": "call_abc123",
6+
"type": "function",
7+
"function": {
8+
"arguments": "{\"search_query\":\"climbing gear outside\"}",
9+
"name": "search_database"
10+
}
11+
}
12+
]},
13+
{
14+
"role": "tool",
15+
"tool_call_id": "call_abc123",
16+
"content": "Search results for climbing gear that can be used outside: ..."
17+
},
18+
{"role": "user", "content": "are there any shoes less than $50?"},
19+
{"role": "assistant", "tool_calls": [
20+
{
21+
"id": "call_abc456",
22+
"type": "function",
23+
"function": {
24+
"arguments": "{\"search_query\":\"shoes\",\"price_filter\":{\"comparison_operator\":\"<\",\"value\":50}}",
25+
"name": "search_database"
26+
}
27+
}
28+
]},
29+
{
30+
"role": "tool",
31+
"tool_call_id": "call_abc456",
32+
"content": "Search results for shoes cheaper than 50: ..."
33+
}
34+
]

src/backend/fastapi_app/rag_advanced.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from collections.abc import AsyncGenerator
2-
from typing import Any
2+
from typing import Any, Final
33

44
from openai import AsyncAzureOpenAI, AsyncOpenAI, AsyncStream
55
from openai.types.chat import ChatCompletion, ChatCompletionChunk, ChatCompletionMessageParam
@@ -38,12 +38,19 @@ async def generate_search_query(
3838
self, original_user_query: str, past_messages: list[ChatCompletionMessageParam], query_response_token_limit: int
3939
) -> tuple[list[ChatCompletionMessageParam], Any | str | None, list]:
4040
"""Generate an optimized keyword search query based on the chat history and the last question"""
41+
42+
tools = build_search_function()
43+
tool_choice: Final = "auto"
44+
4145
query_messages: list[ChatCompletionMessageParam] = build_messages(
4246
model=self.chat_model,
4347
system_prompt=self.query_prompt_template,
48+
few_shots=self.query_fewshots,
4449
new_user_content=original_user_query,
4550
past_messages=past_messages,
46-
max_tokens=self.chat_token_limit - query_response_token_limit, # TODO: count functions
51+
max_tokens=self.chat_token_limit - query_response_token_limit,
52+
tools=tools,
53+
tool_choice=tool_choice,
4754
fallback_to_default=True,
4855
)
4956

@@ -54,8 +61,8 @@ async def generate_search_query(
5461
temperature=0.0, # Minimize creativity for search query generation
5562
max_tokens=query_response_token_limit, # Setting too low risks malformed JSON, too high risks performance
5663
n=1,
57-
tools=build_search_function(),
58-
tool_choice="auto",
64+
tools=tools,
65+
tool_choice=tool_choice,
5966
)
6067

6168
query_text, filters = extract_search_arguments(original_user_query, chat_completion)

src/backend/fastapi_app/rag_base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import pathlib
23
from abc import ABC, abstractmethod
34
from collections.abc import AsyncGenerator
@@ -17,6 +18,7 @@
1718
class RAGChatBase(ABC):
1819
current_dir = pathlib.Path(__file__).parent
1920
query_prompt_template = open(current_dir / "prompts/query.txt").read()
21+
query_fewshots = json.loads(open(current_dir / "prompts/query_fewshots.json").read())
2022
answer_prompt_template = open(current_dir / "prompts/answer.txt").read()
2123

2224
def get_params(self, messages: list[ChatCompletionMessageParam], overrides: ChatRequestOverrides) -> ChatParams:

src/backend/gunicorn.conf.py

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

src/backend/pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "fastapi_app"
33
version = "1.0.0"
4-
description = "Create a application with fastapi and postgres-flexible"
4+
description = "Create a RAG application with FastAPI and PostgreSQL"
55
dependencies = [
66
"fastapi>=0.111.0,<1.0.0",
77
"python-dotenv>=1.0.1,<2.0.0",
@@ -13,7 +13,7 @@ dependencies = [
1313
"pgvector>=0.2.5,<0.3.0",
1414
"openai>=1.34.0,<2.0.0",
1515
"tiktoken>=0.7.0,<0.8.0",
16-
"openai-messages-token-helper>=0.1.5,<0.2.0",
16+
"openai-messages-token-helper>=0.1.8,<0.2.0",
1717
"azure-monitor-opentelemetry>=1.6.0,<2.0.0",
1818
"opentelemetry-instrumentation-sqlalchemy>=0.46b0,<1.0.0",
1919
"opentelemetry-instrumentation-aiohttp-client>=0.46b0,<1.0.0",

src/backend/requirements.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
gunicorn>=22.0.0,<23.0.0
2-
uvicorn>=0.30.1,<1.0.0
1+
uvicorn>=0.30.1,<1.0.0

tests/snapshots/test_api_routes/test_advanced_chat_flow/advanced_chat_flow_response.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,12 @@
1919
"title": "Prompt to generate search arguments",
2020
"description": [
2121
"{'role': 'system', 'content': 'Below is a history of the conversation so far, and a new question asked by the user that needs to be answered by searching database rows.\\nYou have access to an Azure PostgreSQL database with an items table that has columns for title, description, brand, price, and type.\\nGenerate a search query based on the conversation and the new question.\\nIf the question is not in English, translate the question to English before generating the search query.\\nIf you cannot generate a search query, return the original user question.\\nDO NOT return anything besides the query.'}",
22+
"{'role': 'user', 'content': 'good options for climbing gear that can be used outside?'}",
23+
"{'role': 'assistant', 'tool_calls': [{'id': 'call_abc123', 'type': 'function', 'function': {'arguments': '{\"search_query\":\"climbing gear outside\"}', 'name': 'search_database'}}]}",
24+
"{'role': 'tool', 'tool_call_id': 'call_abc123', 'content': 'Search results for climbing gear that can be used outside: ...'}",
25+
"{'role': 'user', 'content': 'are there any shoes less than $50?'}",
26+
"{'role': 'assistant', 'tool_calls': [{'id': 'call_abc456', 'type': 'function', 'function': {'arguments': '{\"search_query\":\"shoes\",\"price_filter\":{\"comparison_operator\":\"<\",\"value\":50}}', 'name': 'search_database'}}]}",
27+
"{'role': 'tool', 'tool_call_id': 'call_abc456', 'content': 'Search results for shoes cheaper than 50: ...'}",
2228
"{'role': 'user', 'content': 'What is the capital of France?'}"
2329
],
2430
"props": {
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
{"delta":null,"context":{"data_points":{"1":{"id":1,"type":"Footwear","brand":"Daybird","name":"Wanderer Black Hiking Boots","description":"Daybird's Wanderer Hiking Boots in sleek black are perfect for all your outdoor adventures. These boots are made with a waterproof leather upper and a durable rubber sole for superior traction. With their cushioned insole and padded collar, these boots will keep you comfortable all day long.","price":109.99}},"thoughts":[{"title":"Prompt to generate search arguments","description":["{'role': 'system', 'content': 'Below is a history of the conversation so far, and a new question asked by the user that needs to be answered by searching database rows.\\nYou have access to an Azure PostgreSQL database with an items table that has columns for title, description, brand, price, and type.\\nGenerate a search query based on the conversation and the new question.\\nIf the question is not in English, translate the question to English before generating the search query.\\nIf you cannot generate a search query, return the original user question.\\nDO NOT return anything besides the query.'}","{'role': 'user', 'content': 'What is the capital of France?'}"],"props":{"model":"gpt-35-turbo","deployment":"gpt-35-turbo"}},{"title":"Search using generated search arguments","description":"The capital of France is Paris. [Benefit_Options-2.pdf].","props":{"top":1,"vector_search":true,"text_search":true,"filters":[]}},{"title":"Search results","description":[{"id":1,"type":"Footwear","brand":"Daybird","name":"Wanderer Black Hiking Boots","description":"Daybird's Wanderer Hiking Boots in sleek black are perfect for all your outdoor adventures. These boots are made with a waterproof leather upper and a durable rubber sole for superior traction. With their cushioned insole and padded collar, these boots will keep you comfortable all day long.","price":109.99}],"props":{}},{"title":"Prompt to generate answer","description":["{'role': 'system', 'content': \"Assistant helps customers with questions about products.\\nRespond as if you are a salesperson helping a customer in a store. Do NOT respond with tables.\\nAnswer ONLY with the product details listed in the products.\\nIf there isn't enough information below, say you don't know.\\nDo not generate answers that don't use the sources below.\\nEach product has an ID in brackets followed by colon and the product details.\\nAlways include the product ID for each product you use in the response.\\nUse square brackets to reference the source, for example [52].\\nDon't combine citations, list each product separately, for example [27][51].\"}","{'role': 'user', 'content': \"What is the capital of France?\\n\\nSources:\\n[1]:Name:Wanderer Black Hiking Boots Description:Daybird's Wanderer Hiking Boots in sleek black are perfect for all your outdoor adventures. These boots are made with a waterproof leather upper and a durable rubber sole for superior traction. With their cushioned insole and padded collar, these boots will keep you comfortable all day long. Price:109.99 Brand:Daybird Type:Footwear\\n\\n\"}"],"props":{"model":"gpt-35-turbo","deployment":"gpt-35-turbo"}}],"followup_questions":null},"sessionState":null}
1+
{"delta":null,"context":{"data_points":{"1":{"id":1,"type":"Footwear","brand":"Daybird","name":"Wanderer Black Hiking Boots","description":"Daybird's Wanderer Hiking Boots in sleek black are perfect for all your outdoor adventures. These boots are made with a waterproof leather upper and a durable rubber sole for superior traction. With their cushioned insole and padded collar, these boots will keep you comfortable all day long.","price":109.99}},"thoughts":[{"title":"Prompt to generate search arguments","description":["{'role': 'system', 'content': 'Below is a history of the conversation so far, and a new question asked by the user that needs to be answered by searching database rows.\\nYou have access to an Azure PostgreSQL database with an items table that has columns for title, description, brand, price, and type.\\nGenerate a search query based on the conversation and the new question.\\nIf the question is not in English, translate the question to English before generating the search query.\\nIf you cannot generate a search query, return the original user question.\\nDO NOT return anything besides the query.'}","{'role': 'user', 'content': 'good options for climbing gear that can be used outside?'}","{'role': 'assistant', 'tool_calls': [{'id': 'call_abc123', 'type': 'function', 'function': {'arguments': '{\"search_query\":\"climbing gear outside\"}', 'name': 'search_database'}}]}","{'role': 'tool', 'tool_call_id': 'call_abc123', 'content': 'Search results for climbing gear that can be used outside: ...'}","{'role': 'user', 'content': 'are there any shoes less than $50?'}","{'role': 'assistant', 'tool_calls': [{'id': 'call_abc456', 'type': 'function', 'function': {'arguments': '{\"search_query\":\"shoes\",\"price_filter\":{\"comparison_operator\":\"<\",\"value\":50}}', 'name': 'search_database'}}]}","{'role': 'tool', 'tool_call_id': 'call_abc456', 'content': 'Search results for shoes cheaper than 50: ...'}","{'role': 'user', 'content': 'What is the capital of France?'}"],"props":{"model":"gpt-35-turbo","deployment":"gpt-35-turbo"}},{"title":"Search using generated search arguments","description":"The capital of France is Paris. [Benefit_Options-2.pdf].","props":{"top":1,"vector_search":true,"text_search":true,"filters":[]}},{"title":"Search results","description":[{"id":1,"type":"Footwear","brand":"Daybird","name":"Wanderer Black Hiking Boots","description":"Daybird's Wanderer Hiking Boots in sleek black are perfect for all your outdoor adventures. These boots are made with a waterproof leather upper and a durable rubber sole for superior traction. With their cushioned insole and padded collar, these boots will keep you comfortable all day long.","price":109.99}],"props":{}},{"title":"Prompt to generate answer","description":["{'role': 'system', 'content': \"Assistant helps customers with questions about products.\\nRespond as if you are a salesperson helping a customer in a store. Do NOT respond with tables.\\nAnswer ONLY with the product details listed in the products.\\nIf there isn't enough information below, say you don't know.\\nDo not generate answers that don't use the sources below.\\nEach product has an ID in brackets followed by colon and the product details.\\nAlways include the product ID for each product you use in the response.\\nUse square brackets to reference the source, for example [52].\\nDon't combine citations, list each product separately, for example [27][51].\"}","{'role': 'user', 'content': \"What is the capital of France?\\n\\nSources:\\n[1]:Name:Wanderer Black Hiking Boots Description:Daybird's Wanderer Hiking Boots in sleek black are perfect for all your outdoor adventures. These boots are made with a waterproof leather upper and a durable rubber sole for superior traction. With their cushioned insole and padded collar, these boots will keep you comfortable all day long. Price:109.99 Brand:Daybird Type:Footwear\\n\\n\"}"],"props":{"model":"gpt-35-turbo","deployment":"gpt-35-turbo"}}],"followup_questions":null},"sessionState":null}
22
{"delta":{"content":"The capital of France is Paris. [Benefit_Options-2.pdf].","role":"assistant"},"context":null,"sessionState":null}

0 commit comments

Comments
 (0)