Skip to content

Commit be67f70

Browse files
committed
tested deployment. Make it support one file upload only. Update readme
1 parent 08a58e7 commit be67f70

File tree

8 files changed

+62
-79
lines changed

8 files changed

+62
-79
lines changed

.gitignore

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
__pycache__/
22

33
# Environments
4-
config.ini
54
.env
65
.venv
76
env/
87
venv/
98
ENV/
109
env.bak/
11-
venv.bak/
10+
venv.bak/
11+
.azure

README.md

+17-25
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Sample Application using Agents from Azure AI Projects and File Search tool (Python)
22

3-
This sample includes a simple Python [Quart](https://quart.palletsprojects.com/en/latest/) app that streams responses from OpenAI Assistant to an HTML/JS frontend using Server-Sent Events (SSEs). The application is configured to upload two documents under the `files` folder for use with the OpenAI Assistant's File Search tool.
3+
This sample includes a simple Python [Quart](https://quart.palletsprojects.com/en/latest/) app that streams responses from Azure AI Agents to an HTML/JS frontend using Server-Sent Events (SSEs). The application is configured to upload two documents under the `files` folder for use with the Azure AI Agents' File Search tool.
44

55
The sample is designed for use with [Docker containers](https://www.docker.com/), both for local development and Azure deployment. For Azure deployment to [Azure Container Apps](https://learn.microsoft.com/azure/container-apps/overview), please use this [template](https://github.com/Azure-Samples/openai-chat-app-quickstart) and replace the `src` folder content with this application.
66

@@ -43,16 +43,17 @@ sequenceDiagram
4343

4444
As a web application, it is designed to serve multiple users on multiple browsers. This application uses cookies to ensure that the same thread is reused for conversations across multiple tabs in the same browser. If the browser is restarted, the old thread will continue to serve the user. However, if the application has a new agent after a server restart or a thread is deleted, a new thread will be created without requiring a browser refresh or signaling to the users.
4545

46-
To achieve this, when users submit a message to the web server, the web server will create an agent, thread, and stream back a reply. The response contains `agent_id` and `thread_id` in cookies. As a result, each subsequent message sent to the web server will also contain these IDs. As long as the same agent is being used in the system and the thread can be retrieved in cookie, the same thread will be used to serve the users.
46+
To achieve this, when users submit a message to the web server, the web server will create an agent, thread, and stream back a reply. The response contains `agent_id` and `thread_id` in cookies. As a result, each subsequent message sent to the web server will also contain these IDs. As long as the same agent is being used in the system and the thread can be retrieved in the cookie, the same thread will be used to serve the users.
4747

48-
## Local development
48+
## Local Development
4949

50-
1. Run `pip install -r requirements.txt`
50+
1. Run `pip install -r requirements.txt`.
5151

5252
2. Make sure that the `.env` file exists.
5353

54-
3. Store Azure AI Projects connection string in `.env` file as `PROJECT_CONNECTION_STRING`.
55-
4. Run `az login`
54+
3. Store the Azure AI Projects connection string in the `.env` file as `PROJECT_CONNECTION_STRING`.
55+
56+
4. Run `az login`.
5657

5758
5. Start the services with this command:
5859

@@ -62,28 +63,19 @@ To achieve this, when users submit a message to the web server, the web server w
6263

6364
6. Click 'http://localhost:50505' in the browser to run the application.
6465

65-
## Local development with Docker
66-
67-
This sample includes a `docker-compose.yaml` for local development which creates a volume for the app code. That allows you to make changes to the code and see them instantly.
68-
69-
1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop/). If you opened this inside Github Codespaces or a Dev Container in VS Code, installation is not needed. ⚠️ If you're on an Apple M1/M2, you won't be able to run `docker` commands inside a Dev Container; either use Codespaces or do not open the Dev Container.
7066

71-
2. Make sure that the `.env` file exists.
72-
73-
3. Store Azure AI Projects connection string in `.env` file as `PROJECT_CONNECTION_STRING`.
67+
## Example Run
7468

75-
4. Start the services with this command:
69+
![File-Search-screenshot](assets/FileSearchAgent.png)
7670

77-
```shell
78-
docker-compose up --build
79-
```
80-
81-
5. Click 'http://localhost:50505' in the browser to run the application.
82-
83-
## Example run
71+
## Deployment to Azure
8472

85-
![File-Search-screenshot](assets/FileSearchAssistant.png)
73+
Follow these steps for deployment:
74+
1. Install [Docker Desktop](https://www.docker.com/products/docker-desktop/). If you opened this inside GitHub Codespaces or a Dev Container in VS Code, installation is not needed. ⚠️ If you're on an Apple M1/M2, you won't be able to run `docker` commands inside a Dev Container; either use Codespaces or do not open the Dev Container.
75+
2. Integrate this app using [template](https://github.com/Azure-Samples/openai-chat-app-quickstart) and follow the Azure Container App deployment steps there.
76+
3. Add an entry of environment variable, `PROJECT_CONNECTION_STRING` in `infra\aca.bicep`.
77+
4. When you visit the Azure Container App, you will see the deployment has a permission error in the `Console Log`.
8678

87-
## Deployment to Azure
79+
![Deployment-Error](assets/DeploymentError.png)
8880

89-
As mentioned earlier, please integrate this app using [template](https://github.com/Azure-Samples/openai-chat-app-quickstart) and following the Azure Container App deployment steps there.
81+
To resolve this permission issue, you need to assign `Contributor` and `Cognitive Services OpenAI User` roles for this `object id` in the `Resource Group` of the `Azure AI Projects`.

assets/DeploymentError.png

189 KB
Loading
File renamed without changes.

src/pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[project]
22
name = "quartapp"
33
version = "1.0.0"
4-
description = "Create a simple chat app using Quart and OpenAI"
4+
description = "Create a simple chat app using Quart and Azure AI Agents"
55
dependencies = [
66
"quart",
77
"werkzeug",

src/quartapp/chat.py

+38-47
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
# Licensed under the MIT license. See LICENSE.md file in the project root for full license information.
33

44
from typing import Any
5-
import azure.identity.aio
65
from quart import Blueprint, jsonify, request, Response, render_template, current_app
76

87
import asyncio
@@ -22,67 +21,62 @@
2221
AgentStreamEvent
2322
)
2423

25-
from src.quartapp.config_helper import ConfigHelper
26-
27-
config = ConfigHelper()
28-
2924
bp = Blueprint("chat", __name__, template_folder="templates", static_folder="static")
3025

3126

3227
@bp.before_app_serving
33-
async def configure_assistant_client():
28+
async def start_server():
3429

35-
3630
ai_client = AIProjectClient.from_connection_string(
37-
credential=DefaultAzureCredential(
38-
exclude_shared_token_cache_credential=True),
31+
credential=DefaultAzureCredential(exclude_shared_token_cache_credential=True),
3932
conn_str=os.environ["PROJECT_CONNECTION_STRING"],
4033
)
4134

42-
agent_id = config.get("Agent", "AGENT_ID")
35+
# TODO: add more files are not supported for citation at the moment
36+
files = ["product_info_1.md"]
37+
file_ids = []
38+
for file in files:
39+
file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'files', file))
40+
print(f"Uploading file {file_path}")
41+
file = await ai_client.agents.upload_file_and_poll(file_path=file_path, purpose=FilePurpose.AGENTS)
42+
file_ids.append(file.id)
4343

44-
agent = None
45-
46-
if agent_id:
47-
try:
48-
agent = await ai_client.agents.get_agent(agent_id)
49-
print(f"Agent already exists, agent ID: {agent.id}")
50-
except Exception as e:
51-
print(f"Agent not found: {e}")
52-
53-
if agent is None:
54-
files = ["product_info_1.md", "product_info_2.md"]
55-
file_ids = []
56-
for file in files:
57-
file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'files', file))
58-
print(f"Uploading file {file_path}")
59-
file = await ai_client.agents.upload_file_and_poll(file_path=file_path, purpose=FilePurpose.AGENTS)
60-
file_ids.append(file.id)
61-
62-
vector_store = await ai_client.agents.create_vector_store(file_ids=file_ids, name="sample_store")
44+
vector_store = await ai_client.agents.create_vector_store(file_ids=file_ids, name="sample_store")
6345

64-
file_search_tool = FileSearchTool(vector_store_ids=[vector_store.id])
65-
66-
tool_set = AsyncToolSet()
67-
tool_set.add(file_search_tool)
68-
69-
agent = await ai_client.agents.create_agent(
70-
model="gpt-4-1106-preview", name="my-assistant", instructions="You are helpful assistant", tools = tool_set.definitions, tool_resources=tool_set.resources
71-
)
46+
file_search_tool = FileSearchTool(vector_store_ids=[vector_store.id])
47+
48+
tool_set = AsyncToolSet()
49+
tool_set.add(file_search_tool)
50+
51+
print(f"ToolResource: {tool_set.resources}")
7252

73-
print(f"Created agent, agent ID: {agent.id}")
74-
75-
config.set('Agent', 'AGENT_ID', agent.id)
76-
config.save()
77-
53+
agent = await ai_client.agents.create_agent(
54+
model="gpt-4o-mini", name="my-assistant", instructions="You are helpful assistant", tools = tool_set.definitions, tool_resources=tool_set.resources
55+
)
7856

57+
print(f"Created agent, agent ID: {agent.id}")
58+
7959
bp.ai_client = ai_client
8060
bp.agent = agent
61+
bp.vector_store = vector_store
62+
bp.file_ids = file_ids
8163

8264

8365
@bp.after_app_serving
84-
async def shutdown_assistant_client():
66+
async def stop_server():
67+
for file_id in bp.file_ids:
68+
await bp.ai_client.agents.delete_file(file_id)
69+
print(f"Deleted file {file_id}")
70+
71+
await bp.ai_client.agents.delete_vector_store(bp.vector_store.id)
72+
print(f"Deleted vector store {bp.vector_store.id}")
73+
74+
await bp.ai_client.agents.delete_agent(bp.agent.id)
75+
76+
print(f"Deleted agent {bp.agent.id}")
77+
8578
await bp.ai_client.close()
79+
print("Closed AIProjectClient")
8680

8781
@bp.get("/")
8882
async def index():
@@ -160,10 +154,7 @@ async def chat():
160154

161155
@bp.route('/fetch-document', methods=['GET'])
162156
async def fetch_document():
163-
filename = request.args.get('filename')
164-
current_app.logger.info(f"Fetching document: {filename}")
165-
if not filename:
166-
return jsonify({"error": "Filename is required"}), 400
157+
filename = "product_info_1.md"
167158

168159
# Get the file path from the mapping
169160
file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'files', filename))

src/quartapp/static/ChatUI.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,9 @@ class ChatUI {
1515

1616
preprocessContent(content) {
1717
// Regular expression to find citations like 【n:m†filename.md】
18-
const citationRegex = /(\d+):(\d+)([^\s]+\.md)/g;
19-
return content.replace(citationRegex, (match, p1, p2, p3) => {
20-
return `<a href="#" class="file-citation" data-file-name="${p3}">[${p1}:${p2}] ${p3}</a>`;
18+
const citationRegex = /\u3010(\d+):(\d+)\u2020([^\s]+)\u3011/g;
19+
return content.replace(citationRegex, (match, _, __, filename) => {
20+
return `<a href="#" class="file-citation" data-file-name="${filename}">${match}</a>`;
2121
});
2222
}
2323

src/quartapp/templates/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<meta charset="utf-8">
55
<meta name="viewport" content="width=device-width, initial-scale=1">
66
<meta name="description" content="">
7-
<title>OpenAI ChatGPT Demo</title>
7+
<title>Azure AI Agents Demo</title>
88
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css"
99
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
1010
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css"

0 commit comments

Comments
 (0)