Skip to content

Commit e8ed7d9

Browse files
authored
Changes to add a new react frontend app (#100)
* Initial set of changes to setup react app for getting started template (#95) * Initial set of changes to setup react app for getting started template * Changes to use separate index html file for the react app * Reverting index.html file changes * Changes to add a new react frontend app
1 parent d28bde8 commit e8ed7d9

15 files changed

+2023
-2
lines changed

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,11 @@ ENV/
99
env.bak/
1010
venv.bak/
1111
.azure
12+
13+
# Node.js dependencies
14+
node_modules/
15+
.pnpm-store/
16+
.pnpm-debug.log
17+
18+
# React build output
19+
src/api/static/react/

src/.dockerignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.git*
22
.venv/
33
**/*.pyc
4+
frontend/node_modules

src/api/main.py

+5
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,11 @@ def create_app():
121121
directory = os.path.join(os.path.dirname(__file__), "static")
122122
app = fastapi.FastAPI(lifespan=lifespan)
123123
app.mount("/static", StaticFiles(directory=directory), name="static")
124+
125+
# Mount React static files
126+
# Uncomment the following lines if you have a React frontend
127+
# react_directory = os.path.join(os.path.dirname(__file__), "static/react")
128+
# app.mount("/static/react", StaticFiles(directory=react_directory), name="react")
124129

125130
from . import routes # Import routes
126131
app.include_router(routes.router)

src/api/routes.py

+34-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,28 @@ def get_agent(request: Request) -> Agent:
4949
return request.app.state.agent
5050

5151

52+
def get_react_bundle_path() -> str:
53+
"""Get the path to the latest React bundle from the Vite manifest file."""
54+
manifest_path = os.path.join(os.path.dirname(__file__), "static", "react", ".vite", "manifest.json")
55+
try:
56+
with open(manifest_path, 'r') as f:
57+
manifest = json.load(f)
58+
# Get the main entry point bundle
59+
if "src/main.jsx" in manifest:
60+
return f"/static/react/{manifest['src/main.jsx']['file']}"
61+
# Fallback to any entry point if the expected one isn't found
62+
for key, value in manifest.items():
63+
if value.get("isEntry", False):
64+
return f"/static/react/{value['file']}"
65+
# If no entries are found, return a default path
66+
logger.warning("No entries found in Vite manifest, using fallback path")
67+
return "/static/react/assets/main.js"
68+
except (FileNotFoundError, json.JSONDecodeError, KeyError) as e:
69+
logger.error(f"Error reading Vite manifest: {e}")
70+
# Return a default path if the manifest can't be read
71+
return "/static/react/assets/main.js"
72+
73+
5274
def serialize_sse_event(data: Dict) -> str:
5375
return f"data: {json.dumps(data)}\n\n"
5476

@@ -132,7 +154,18 @@ async def on_run_step(self, step: RunStep) -> Optional[str]:
132154

133155
@router.get("/", response_class=HTMLResponse)
134156
async def index(request: Request):
135-
return templates.TemplateResponse("index.html", {"request": request})
157+
# Check if the useReactApp query parameter is present and set to 'true'
158+
use_react_app = request.query_params.get('useReactApp', '').lower() == 'true'
159+
160+
# Use different template files based on whether React is enabled
161+
template_name = "index_react.html" if use_react_app else "index.html"
162+
163+
return templates.TemplateResponse(
164+
template_name,
165+
{
166+
"request": request,
167+
}
168+
)
136169

137170

138171
async def get_result(thread_id: str, agent_id: str, ai_client : AIProjectClient) -> AsyncGenerator[str, None]:

src/api/templates/index.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -101,4 +101,4 @@ <h5 class="mb-0">Document Viewer</h5>
101101
<script src="https://cdn.jsdelivr.net/npm/markdown-it/dist/markdown-it.min.js"></script>
102102
<script type="module" src="/static/main.js"></script>
103103
</body>
104-
</html>
104+
</html>

src/api/templates/index_react.html

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<!doctype html>
2+
<html lang="en" class="h-100">
3+
<head>
4+
<meta charset="utf-8">
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<meta name="description" content="">
7+
<title>Azure AI Agents Demo (React Version)</title>
8+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css"
9+
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ" crossorigin="anonymous">
10+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.3/font/bootstrap-icons.css"
11+
integrity="sha256-4RctOgogjPAdwGbwq+rxfwAmSpZhWaafcZR9btzUk18=" crossorigin="anonymous">
12+
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootswatch@5.2.3/dist/cosmo/bootstrap.min.css"
13+
integrity="sha256-axRDISYf7Hht1KhcMnfDV2nq7hD/8Q9Rxa0YlW/o3NU=" crossorigin="anonymous">
14+
<link href="/static/styles.css" rel="stylesheet" type="text/css">
15+
<style>
16+
.message-content {
17+
text-align: left;
18+
}
19+
</style>
20+
</head>
21+
<body>
22+
<div class="container-fluid h-100 d-flex flex-row">
23+
<div class="row flex-grow-1 h-100">
24+
<!-- React App Mount Point -->
25+
<div id="react-root" class="col-full d-flex flex-column h-100"></div>
26+
27+
<!-- Document Viewer Section -->
28+
<div class="col-6 d-flex flex-column h-100 p-3 bg-light border-left" id="document-viewer-section" style="display: none !important;">
29+
<div class="d-flex justify-content-between align-items-center mb-2">
30+
<h5 class="mb-0">Document Viewer</h5>
31+
<button id="close-button" class="btn bi-x"></button>
32+
</div>
33+
<iframe id="document-viewer" class="flex-grow-1 border-0 rounded" style="background-color: white;"></iframe>
34+
</div>
35+
</div>
36+
</div>
37+
38+
<!-- Load React app with fixed filename -->
39+
<script type="module" src="/static/react/assets/main-react-app.js"></script>
40+
</body>
41+
</html>

src/frontend/package.json

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"name": "frontend",
3+
"version": "1.0.0",
4+
"description": "React frontend for Azure AI Agents Demo",
5+
"main": "index.js",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "vite build",
9+
"preview": "vite preview"
10+
},
11+
"keywords": [],
12+
"author": "",
13+
"license": "ISC",
14+
"packageManager": "pnpm@10.4.1",
15+
"dependencies": {
16+
"@vitejs/plugin-react": "4.4.1",
17+
"react": "19.1.0",
18+
"react-dom": "19.1.0",
19+
"react-markdown": "10.1.0",
20+
"vite": "6.3.3"
21+
}
22+
}

0 commit comments

Comments
 (0)