Skip to content
Merged
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
2 changes: 1 addition & 1 deletion .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"recommendations": [
"pkief.material-icon-theme",
"github.vscode-github-actions",
"atishay-jain.all-autocomplete",
"sadesyllas.vscode-workspace-switcher",
"editorconfig.editorconfig",
Expand Down
9 changes: 8 additions & 1 deletion src/api.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from contextlib import asynccontextmanager
from fastapi import FastAPI
from fastapi import FastAPI, responses, staticfiles
import uvicorn

import app as rag_module
Expand All @@ -13,6 +13,8 @@ async def lifespan(app: FastAPI):

app = FastAPI(lifespan=lifespan)

app.mount("/static", staticfiles.StaticFiles(directory="static"), name="static")


@app.get("/")
async def root(question: str | None = None):
Expand All @@ -24,5 +26,10 @@ async def root(question: str | None = None):
}


@app.get("/ui")
async def ui():
return responses.FileResponse("static/index.html")


if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
4 changes: 3 additions & 1 deletion src/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,7 @@ def initialize():

question = "What operating systems does nx-ng-starter support?"
answer = rag_application.run(question)
print("--- startup test ---")
print("Question:", question)
print("Answer:", answer)
print("Answer:", answer.strip())
print("--- startup test ---")
18 changes: 11 additions & 7 deletions src/start.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,27 @@ RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
NC_BOLD='\033[1m'

if docker ps | grep -q qdrant; then
echo "Qdrant is already running. Skipping docker run."
echo -e "${GREEN}[ok]${NC} Qdrant is already running. Skipping docker run."
else
echo "Starting Qdrant container..."
echo -e "${YELLOW}[i]${NC} Starting Qdrant container..."

if [ -f .env ]; then
# shellcheck source=/dev/null
source .env
else
echo "Warning: .env file not found."
echo -e "${YELLOW}[warn]${NC} .env file not found."
fi

if [ -z "$QDRANT_API_KEY" ]; then
echo "Error: QDRANT_API_KEY not found in .env and is not set globally"
echo -e "${RED}[err]${NC} QDRANT_API_KEY not found in .env and is not set globally.}"
exit 1
fi

if [ -z "$QDRANT_API_KEY_READ_ONLY" ]; then
echo "Error: QDRANT_API_KEY_READ_ONLY not found in .env and is not set globally"
echo -e "${RED}[err]${NC} QDRANT_API_KEY_READ_ONLY not found in .env and is not set globally."
exit 1
fi

Expand Down Expand Up @@ -60,6 +61,8 @@ trap cleanup EXIT

echo -e "${YELLOW}[i]${NC} Qdrant is running with data dir $DATA_DIR. Waiting for Qdrant..."

echo -e "${YELLOW}[i]${NC} Qdrant dashboard is available on ${NC_BOLD}http://localhost:6333/dashboard${NC}"

MAX_WAIT_SEC=60
SLEEP_SEC=1
ELAPSED=0
Expand All @@ -73,17 +76,18 @@ while [ "$ELAPSED" -lt "$MAX_WAIT_SEC" ]; do
sleep "$SLEEP_SEC"
ELAPSED=$((ELAPSED + SLEEP_SEC))
done

if [ "$STARTUP_STATUS" != "200" ]; then
echo -e "${RED}[err]{$NC} Qdrant startup timeout. Last HTTP status: $STARTUP_STATUS"
echo -e "${YELLOW}[i]${NC} Qdrant container logs:"
docker logs --tail 50 qdrant-rag-app || echo -e "${RED}[warn]${NC} Could not fetch qdrant-rag-app Docker logs."
exit 1
fi

echo "Qdrant status:"
echo -e "${YELLOW}[i]${NC} Qdrant status:"
docker ps | grep qdrant

echo "Qdrant version check:"
echo -e "${YELLOW}[i]${NC} Qdrant version check:"
curl -H "api-key: $QDRANT_API_KEY_READ_ONLY" http://localhost:6333 | grep version

cd "$(dirname "$0")" || exit 1
Expand Down
67 changes: 67 additions & 0 deletions src/static/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
* {
box-sizing: border-box;
}

body {
margin: 0;
font-family: Arial, sans-serif;
background: #f4f4f4;
color: #222;
}

.container {
width: min(600px, 90%);
margin: 60px auto;
padding: 24px;
background: white;
border-radius: 8px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}

h1 {
margin-top: 0;
}

label {
display: block;
margin-bottom: 8px;
font-weight: bold;
}

textarea {
width: 100%;
min-height: 120px;
padding: 12px;
resize: vertical;
border: 1px solid #ccc;
border-radius: 5px;
font: inherit;
}

button {
margin-top: 12px;
padding: 10px 18px;
border: 0;
border-radius: 5px;
background: #2563eb;
color: white;
cursor: pointer;
font-size: 16px;
}

button:disabled {
background: #999;
cursor: not-allowed;
}

.result {
margin-top: 24px;
padding: 16px;
background: #eef6ff;
border-radius: 5px;
}

.error {
color: #c00;
margin-top: 16px;
}
34 changes: 34 additions & 0 deletions src/static/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RAG User Interface</title>
<link rel="stylesheet" href="/static/index.css">
</head>

<body>
<main class="container">
<h1>Ask a Question</h1>

<form id="question-form">
<label for="question">Your question</label>

<textarea id="question" name="question" placeholder="Type your question..." required></textarea>

<button type="submit">Ask</button>
</form>

<section id="result" class="result" hidden>
<h2>Answer</h2>
<p id="answer"></p>
</section>

<p id="error" class="error"></p>
</main>

<script src="/static/index.js"></script>
</body>

</html>
49 changes: 49 additions & 0 deletions src/static/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM content loaded');

const form = document.getElementById("question-form");
const questionInput = document.getElementById("question");
const answerElement = document.getElementById("answer");
const resultElement = document.getElementById("result");
const errorElement = document.getElementById("error");
const submitButton = form.querySelector("button");

form.addEventListener("submit", async (event) => {
event.preventDefault();

const question = questionInput.value.trim();

if (!question) {
return;
}

resultElement.hidden = true;
errorElement.textContent = "";
submitButton.disabled = true;
submitButton.textContent = "Asking...";

try {
const response = await fetch(`/?question=${question}`, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});

if (!response.ok) {
throw new Error("Request failed");
}

const data = await response.json();

answerElement.textContent = data.answer;
resultElement.hidden = false;
} catch (error) {
errorElement.textContent =
"Unable to get an answer. Please try again.";
} finally {
submitButton.disabled = false;
submitButton.textContent = "Ask";
}
});
})
Loading