Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@
},
"ghcr.io/devcontainers/features/powershell:1.1.0": {},
"ghcr.io/devcontainers/features/azure-cli:1.0.8": {},
"ghcr.io/azure/azure-dev/azd:latest": {}
"ghcr.io/azure/azure-dev/azd:latest": {},
// Required for azd to package the app to ACA
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
"customizations": {
"vscode": {
Expand Down
15 changes: 15 additions & 0 deletions app/backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM mcr.microsoft.com/devcontainers/python:3.11-bullseye

RUN python -m pip install --upgrade pip

WORKDIR /demo-code

COPY requirements.txt .
RUN python -m pip install -r requirements.txt
RUN python -m pip install gunicorn==21.2.0

COPY entrypoint.sh .
RUN chmod +x entrypoint.sh

COPY . .
CMD bash -c ". entrypoint.sh"
7 changes: 5 additions & 2 deletions app/backend/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import aiohttp
import openai
from azure.identity.aio import DefaultAzureCredential
from azure.identity.aio import DefaultAzureCredential, ManagedIdentityCredential
from azure.monitor.opentelemetry import configure_azure_monitor
from azure.search.documents.aio import SearchClient
from azure.storage.blob.aio import BlobServiceClient
Expand Down Expand Up @@ -131,7 +131,10 @@ async def setup_clients():
# just use 'az login' locally, and managed identity when deployed on Azure). If you need to use keys, use separate AzureKeyCredential instances with the
# keys for each service
# If you encounter a blocking error during a DefaultAzureCredential resolution, you can exclude the problematic credential by using a parameter (ex. exclude_shared_token_cache_credential=True)
azure_credential = DefaultAzureCredential(exclude_shared_token_cache_credential = True)
if os.getenv("AZURE_IDENTITY_ID"):
azure_credential = ManagedIdentityCredential(client_id=os.getenv("AZURE_IDENTITY_ID"))
else:
azure_credential = DefaultAzureCredential(exclude_shared_token_cache_credential = True)

# Set up clients for Cognitive Search and Storage
search_client = SearchClient(
Expand Down
3 changes: 3 additions & 0 deletions app/backend/entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/bin/bash

python3 -m gunicorn main:app
29 changes: 29 additions & 0 deletions app/doc_uploader/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
**/__pycache__
**/.venv
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose*
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
**/.parcel-cache
**/dist
35 changes: 35 additions & 0 deletions app/doc_uploader/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
FROM node:20-slim AS build

WORKDIR /app
COPY . .
WORKDIR /app/client
RUN npm install && npx parcel build index.html --dist-dir ../dist
WORKDIR /app
RUN rm -rf client

# For more information, please refer to https://aka.ms/vscode-docker-python
FROM python:3.10-slim

EXPOSE 8000

# Keeps Python from generating .pyc files in the container
ENV PYTHONDONTWRITEBYTECODE=1

# Turns off buffering for easier container logging
ENV PYTHONUNBUFFERED=1

# Install pip requirements
COPY --from=build /app/requirements.txt .
RUN python -m pip install -r requirements.txt

WORKDIR /app
COPY --from=build /app /app

# Creates a non-root user with an explicit UID and adds permission to access the /app folder
# For more info, please refer to https://aka.ms/vscode-docker-python-configure-containers
RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app
USER appuser

# During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "-k", "uvicorn.workers.UvicornWorker", "main:app"]
# CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
3 changes: 3 additions & 0 deletions app/doc_uploader/build-client.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
cd client
npx parcel build index.html --dist-dir ../dist
cd ..
1 change: 1 addition & 0 deletions app/doc_uploader/client/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.parcel-cache
14 changes: 14 additions & 0 deletions app/doc_uploader/client/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<!-- index.html -->
<!DOCTYPE html>
<html>

<body>
<input type="file" id="file-input" />
<button id="upload-button">Upload</button>

<pre id="progress"></pre>
</body>

<script type="module" src="./index.js"></script>

</html>
43 changes: 43 additions & 0 deletions app/doc_uploader/client/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
const { BlockBlobClient } = require("@azure/storage-blob");

const fileInput = document.getElementById("file-input");
const uploadButton = document.getElementById("upload-button");
const progress = document.getElementById("progress");

uploadButton.addEventListener("click", async () => {
progress.innerText = "Uploading...";

const file = fileInput.files[0];
const blobName = file.name;
const { sasUrl } = await fetch(`/sas?filename=${blobName}`).then((res) => res.json());

const blobClient = new BlockBlobClient(sasUrl);
await blobClient.uploadBrowserData(file);

progress.innerText = "Uploaded! Starting job...";

const jobInfo = await fetch(`/startjob?filename=${blobName}`, { method: "POST" })
.then((res) => res.json());

console.log(JSON.stringify(jobInfo, null, 2));

const jobName = jobInfo.result?.name;
if (jobName) {
progress.innerText = `Job ${jobName} started. Waiting for output...`;
}

const logFileUrl = jobInfo.logFileUrl;
let tries = 0;
while(tries++ < 500) {
const logFileResponse = await fetch(logFileUrl);
if (logFileResponse.status === 200) {
progress.innerText = await logFileResponse.text();
window.scrollTo(0, document.body.scrollHeight);
if (progress.innerText.match(/Indexed \d+ sections, \d+ succeeded/i)) {
break;
}
}
await new Promise((resolve) => setTimeout(resolve, 2000));
}
});

Loading