From 4d3540507e81c59fad2445f1a809da26857739f3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Mar 2026 05:34:35 +0000 Subject: [PATCH 1/6] fix: align env var names with GitHub secrets and update gpt-4 default to gpt-4o Agent-Logs-Url: https://github.com/TriResolve-AI/grounded-knowledge-assistant/sessions/6069e4cf-bd84-44e3-bc55-61e227ad5e7c Co-authored-by: portiajefferson <67895258+portiajefferson@users.noreply.github.com> --- .env.example | 43 +++++++++++++++++++++++++++++++ backend/config/azureConfig.js | 4 +-- backend/services/auditService.js | 2 +- backend/services/blobService.js | 4 +-- backend/services/openaiService.js | 2 +- services/auditService.js | 4 +-- 6 files changed, 51 insertions(+), 8 deletions(-) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..68dbd1b --- /dev/null +++ b/.env.example @@ -0,0 +1,43 @@ +# ─── Server ────────────────────────────────────────────────────────────────── +PORT=3000 + +# Set to "true" to enable /audit-log and /audit-log/schema endpoints +AUDIT_LOG_ENABLED=false + +# ─── Azure OpenAI ───────────────────────────────────────────────────────────── +# Your Azure OpenAI resource endpoint (e.g. https://.openai.azure.com) +AZURE_OPENAI_ENDPOINT=https://.openai.azure.com +# API key from your Azure OpenAI resource > Keys and Endpoint +AZURE_OPENAI_KEY= +# Chat/completions deployment name (must match the deployment name in Azure AI Studio, not the base model name) +AZURE_OPENAI_DEPLOYMENT=gpt-4o +# Embeddings deployment name +AZURE_OPENAI_EMBEDDING_DEPLOYMENT=text-embedding-ada-002 +# Azure OpenAI REST API version +AZURE_OPENAI_API_VERSION=2024-12-01 + +# ─── Azure AI Search ────────────────────────────────────────────────────────── +# Endpoint for your Azure AI Search service (e.g. https://.search.windows.net) +AZURE_SEARCH_ENDPOINT=https://.search.windows.net +# Admin or query API key from your Search service > Keys +AZURE_SEARCH_KEY= +# Name of the search index (must match between runtime and ingest script) +AZURE_SEARCH_INDEX=cg-knowledge-index +# Azure AI Search REST API version (used by root searchService.js) +AZURE_SEARCH_API_VERSION=2023-11-01 + +# ─── Azure Blob Storage ─────────────────────────────────────────────────────── +# Full connection string from Storage Account > Access Keys +AZURE_BLOB_CONNECTION_STRING=DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net +# Container name (used by both document storage and audit log service) +# Document upload/ingest uses this container for raw documents; audit service appends to audit_log_live.jsonl here +AZURE_BLOB_CONTAINER_NAME= + +# ─── Azure Document Intelligence ───────────────────────────────────────────── +# Required for PDF text extraction during document ingestion +AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT=https://.cognitiveservices.azure.com +AZURE_DOCUMENT_INTELLIGENCE_API_KEY= + +# ─── Audit log blob name (optional) ────────────────────────────────────────── +# Defaults to "audit_log_live.jsonl" — matches the blob already in the storage container +# AUDIT_LOG_BLOB=audit_log_live.jsonl diff --git a/backend/config/azureConfig.js b/backend/config/azureConfig.js index c31a71e..63c485e 100644 --- a/backend/config/azureConfig.js +++ b/backend/config/azureConfig.js @@ -8,7 +8,7 @@ const { DocumentAnalysisClient } = require("@azure/ai-form-recognizer"); // ─── Blob ───────────────────────────────────────────────────────────────────── const blobServiceClient = BlobServiceClient.fromConnectionString( - process.env.AZURE_STORAGE_CONNECTION_STRING + process.env.AZURE_BLOB_CONNECTION_STRING ); const containerClient = blobServiceClient.getContainerClient( process.env.AZURE_BLOB_CONTAINER_NAME || "raw-documents" @@ -28,7 +28,7 @@ const searchClient = new SearchClient( // ─── OpenAI ─────────────────────────────────────────────────────────────────── const openaiClient = new OpenAIClient( process.env.AZURE_OPENAI_ENDPOINT, - new OAICredential(process.env.AZURE_OPENAI_API_KEY) + new OAICredential(process.env.AZURE_OPENAI_KEY) ); // ─── Document Intelligence ──────────────────────────────────────────────────── diff --git a/backend/services/auditService.js b/backend/services/auditService.js index 10bbb4b..addf278 100644 --- a/backend/services/auditService.js +++ b/backend/services/auditService.js @@ -38,7 +38,7 @@ const LOCAL_AUDIT_SCHEMA = { let cachedSchema = null; function isLocalFallbackEnabled() { - return !process.env.AZURE_STORAGE_CONNECTION_STRING; + return !process.env.AZURE_BLOB_CONNECTION_STRING; } async function ensureLocalDataDir() { diff --git a/backend/services/blobService.js b/backend/services/blobService.js index 93e4baa..efd80d4 100644 --- a/backend/services/blobService.js +++ b/backend/services/blobService.js @@ -1,11 +1,11 @@ const { BlobServiceClient } = require("@azure/storage-blob"); -const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING; +const connectionString = process.env.AZURE_BLOB_CONNECTION_STRING; const containerName = process.env.AZURE_BLOB_CONTAINER_NAME || "audit"; function getBlobServiceClient() { if (!connectionString) { - throw new Error("AZURE_STORAGE_CONNECTION_STRING is required"); + throw new Error("AZURE_BLOB_CONNECTION_STRING is required"); } return BlobServiceClient.fromConnectionString(connectionString); } diff --git a/backend/services/openaiService.js b/backend/services/openaiService.js index 10196a3..46f366d 100644 --- a/backend/services/openaiService.js +++ b/backend/services/openaiService.js @@ -5,7 +5,7 @@ class OpenAIService { constructor() { this.client = openaiClient; this.embeddingDeployment = process.env.AZURE_OPENAI_EMBEDDING_DEPLOYMENT || "text-embedding-ada-002"; - this.completionDeployment = process.env.AZURE_OPENAI_DEPLOYMENT || "gpt-4"; + this.completionDeployment = process.env.AZURE_OPENAI_DEPLOYMENT || "gpt-4o"; } /** diff --git a/services/auditService.js b/services/auditService.js index 964b36a..56f2320 100644 --- a/services/auditService.js +++ b/services/auditService.js @@ -3,7 +3,7 @@ const fs = require("fs/promises"); const path = require("path"); const { BlobServiceClient } = require("@azure/storage-blob"); -const CONNECTION_STRING = process.env.AZURE_STORAGE_CONNECTION_STRING; +const CONNECTION_STRING = process.env.AZURE_BLOB_CONNECTION_STRING; const CONTAINER_NAME = process.env.AZURE_BLOB_CONTAINER_NAME || "audit"; const AUDIT_LOG_BLOB = process.env.AUDIT_LOG_BLOB || "audit_log_live.jsonl"; const AUDIT_SCHEMA_BLOB = "audit_log_schema.json"; @@ -66,7 +66,7 @@ async function appendLocalAuditLine(line) { function getContainerClient() { if (!CONNECTION_STRING) { - throw new Error("AZURE_STORAGE_CONNECTION_STRING is required"); + throw new Error("AZURE_BLOB_CONNECTION_STRING is required"); } const blobServiceClient = BlobServiceClient.fromConnectionString(CONNECTION_STRING); From c8bbeb3e6f6ba28020fbdfd9239298e040857ce9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 28 Mar 2026 05:44:40 +0000 Subject: [PATCH 2/6] fix: backend server.js bugs, frontend env-driven API URL, explicit search index default Agent-Logs-Url: https://github.com/TriResolve-AI/grounded-knowledge-assistant/sessions/b7b214fd-4472-4d49-925b-d146c2f4b18c Co-authored-by: portiajefferson <67895258+portiajefferson@users.noreply.github.com> --- .env.example | 7 ++++++- backend/config/azureConfig.js | 2 +- backend/server.js | 9 +++------ frontend/.env.example | 3 +++ frontend/src/App.jsx | 2 +- 5 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 frontend/.env.example diff --git a/.env.example b/.env.example index 68dbd1b..521c20e 100644 --- a/.env.example +++ b/.env.example @@ -40,4 +40,9 @@ AZURE_DOCUMENT_INTELLIGENCE_API_KEY= # ─── Audit log blob name (optional) ────────────────────────────────────────── # Defaults to "audit_log_live.jsonl" — matches the blob already in the storage container -# AUDIT_LOG_BLOB=audit_log_live.jsonl +AUDIT_LOG_BLOB=audit_log_live.jsonl + +# ─── Frontend ───────────────────────────────────────────────────────────────── +# Backend API base URL consumed by Vite (frontend/src/App.jsx via import.meta.env.VITE_API_URL) +# Set this in frontend/.env for local dev, or in your deployment environment +VITE_API_URL=http://localhost:3000 diff --git a/backend/config/azureConfig.js b/backend/config/azureConfig.js index 63c485e..f2a91f3 100644 --- a/backend/config/azureConfig.js +++ b/backend/config/azureConfig.js @@ -21,7 +21,7 @@ const searchIndexClient = new SearchIndexClient( ); const searchClient = new SearchClient( process.env.AZURE_SEARCH_ENDPOINT, - process.env.AZURE_SEARCH_INDEX || "documents", + process.env.AZURE_SEARCH_INDEX || "cg-knowledge-index", new AzureKeyCredential(process.env.AZURE_SEARCH_KEY) ); diff --git a/backend/server.js b/backend/server.js index 6872f07..1737cc3 100644 --- a/backend/server.js +++ b/backend/server.js @@ -4,6 +4,7 @@ const cors = require("cors"); const queryRoutes = require("./routes/query"); const auditRoutes = require("./routes/audit"); +const documentRoutes = require("./routes/documents"); const app = express(); @@ -51,10 +52,6 @@ app.get('/', (req, res) => { }); }); -// Mount routes -app.use(queryRoutes); -app.use(auditRoutes); - // Error handling middleware app.use((err, req, res, next) => { console.error(err.stack); @@ -68,8 +65,8 @@ app.use((err, req, res, next) => { const PORT = process.env.PORT || 3000; // Start server -app.listen(port, () => { - console.log(`\n🚀 Grounded Knowledge Assistant API running on port ${port}`); +app.listen(PORT, () => { + console.log(`\n🚀 Grounded Knowledge Assistant API running on port ${PORT}`); console.log(`\n📋 Available endpoints:`); console.log(` POST /query - Ask a question`); console.log(` GET /audit - View audit logs`); diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 0000000..f999503 --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1,3 @@ +# Backend API base URL — used by Vite via import.meta.env.VITE_API_URL +# Copy this file to frontend/.env and update the value for your environment +VITE_API_URL=http://localhost:3000 diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index e515bf6..200b47f 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -8,7 +8,7 @@ function App() { const [loading, setLoading] = useState(false) const [error, setError] = useState('') - const API_BASE = 'http://localhost:3000' + const API_BASE = import.meta.env.VITE_API_URL || 'http://localhost:3000' const handleSearch = async () => { if (!query.trim()) return From 610f08ea6479d24d19fc99d90452b4d4d8739361 Mon Sep 17 00:00:00 2001 From: Portia Jefferson <67895258+portiajefferson@users.noreply.github.com> Date: Mon, 30 Mar 2026 00:01:27 -0400 Subject: [PATCH 3/6] Update backend/config/azureConfig.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- backend/config/azureConfig.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/backend/config/azureConfig.js b/backend/config/azureConfig.js index 46e88d8..c019377 100644 --- a/backend/config/azureConfig.js +++ b/backend/config/azureConfig.js @@ -22,16 +22,22 @@ const containerClient = blobServiceClient.getContainerClient( ); // ─── Search ─────────────────────────────────────────────────────────────────── -const searchIndexClient = new SearchIndexClient( - process.env.AZURE_SEARCH_ENDPOINT, - new AzureKeyCredential(process.env.AZURE_SEARCH_KEY) -); -const searchClient = new SearchClient( - process.env.AZURE_SEARCH_ENDPOINT, - process.env.AZURE_SEARCH_INDEX || "cg-knowledge-index", - new AzureKeyCredential(process.env.AZURE_SEARCH_KEY) -); +let searchIndexClient = null; +let searchClient = null; +if (process.env.AZURE_SEARCH_ENDPOINT && process.env.AZURE_SEARCH_KEY) { + searchIndexClient = new SearchIndexClient( + process.env.AZURE_SEARCH_ENDPOINT, + new AzureKeyCredential(process.env.AZURE_SEARCH_KEY) + ); + searchClient = new SearchClient( + process.env.AZURE_SEARCH_ENDPOINT, + process.env.AZURE_SEARCH_INDEX || "cg-knowledge-index", + new AzureKeyCredential(process.env.AZURE_SEARCH_KEY) + ); +} else { + warnMissing("Azure Cognitive Search", ["AZURE_SEARCH_ENDPOINT", "AZURE_SEARCH_KEY"]); +} // ─── OpenAI ─────────────────────────────────────────────────────────────────── const openaiClient = new OpenAIClient( process.env.AZURE_OPENAI_ENDPOINT, From 287397d7a7717d86ed2e9d99596dec23be58e0e3 Mon Sep 17 00:00:00 2001 From: Portia Jefferson <67895258+portiajefferson@users.noreply.github.com> Date: Mon, 30 Mar 2026 00:01:52 -0400 Subject: [PATCH 4/6] Update backend/config/azureConfig.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- backend/config/azureConfig.js | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/backend/config/azureConfig.js b/backend/config/azureConfig.js index c019377..8fc5b7f 100644 --- a/backend/config/azureConfig.js +++ b/backend/config/azureConfig.js @@ -39,11 +39,26 @@ if (process.env.AZURE_SEARCH_ENDPOINT && process.env.AZURE_SEARCH_KEY) { warnMissing("Azure Cognitive Search", ["AZURE_SEARCH_ENDPOINT", "AZURE_SEARCH_KEY"]); } // ─── OpenAI ─────────────────────────────────────────────────────────────────── -const openaiClient = new OpenAIClient( - process.env.AZURE_OPENAI_ENDPOINT, - new OAICredential(process.env.AZURE_OPENAI_KEY) -); +let openaiClient = null; +const openaiApiKey = process.env.AZURE_OPENAI_KEY || process.env.AZURE_OPENAI_API_KEY; +if (process.env.AZURE_OPENAI_ENDPOINT && openaiApiKey) { + openaiClient = new OpenAIClient( + process.env.AZURE_OPENAI_ENDPOINT, + new OAICredential(openaiApiKey) + ); +} else { + const missingVars = []; + if (!process.env.AZURE_OPENAI_ENDPOINT) { + missingVars.push("AZURE_OPENAI_ENDPOINT"); + } + if (!process.env.AZURE_OPENAI_KEY && !process.env.AZURE_OPENAI_API_KEY) { + missingVars.push("AZURE_OPENAI_KEY or AZURE_OPENAI_API_KEY"); + } + if (missingVars.length > 0) { + warnMissing("Azure OpenAI", missingVars); + } +} // ─── Document Intelligence ──────────────────────────────────────────────────── let docIntelligenceClient = null; if (process.env.AZURE_DOCUMENT_INTELLIGENCE_ENDPOINT && process.env.AZURE_DOCUMENT_INTELLIGENCE_API_KEY) { From ebf5d77682818103c71ebdf8abef3bfc1f4f50df Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 30 Mar 2026 04:03:47 +0000 Subject: [PATCH 5/6] Support both AZURE_BLOB_CONNECTION_STRING and AZURE_STORAGE_CONNECTION_STRING as fallback across all blob services Agent-Logs-Url: https://github.com/TriResolve-AI/grounded-knowledge-assistant/sessions/52f2b27c-f935-484f-957d-1141eef7547f Co-authored-by: portiajefferson <67895258+portiajefferson@users.noreply.github.com> --- backend/config/azureConfig.js | 21 +++++++++++++++------ backend/routes/documents.js | 2 +- backend/services/blobService.js | 6 ++++-- services/auditService.js | 6 ++++-- 4 files changed, 24 insertions(+), 11 deletions(-) diff --git a/backend/config/azureConfig.js b/backend/config/azureConfig.js index 8fc5b7f..933f713 100644 --- a/backend/config/azureConfig.js +++ b/backend/config/azureConfig.js @@ -14,12 +14,21 @@ function warnMissing(service, vars) { } // ─── Blob ───────────────────────────────────────────────────────────────────── -const blobServiceClient = BlobServiceClient.fromConnectionString( - process.env.AZURE_BLOB_CONNECTION_STRING -); -const containerClient = blobServiceClient.getContainerClient( - process.env.AZURE_BLOB_CONTAINER_NAME || "raw-documents" -); +const blobConnString = + process.env.AZURE_BLOB_CONNECTION_STRING || + process.env.AZURE_STORAGE_CONNECTION_STRING; + +let blobServiceClient = null; +let containerClient = null; + +if (blobConnString) { + blobServiceClient = BlobServiceClient.fromConnectionString(blobConnString); + containerClient = blobServiceClient.getContainerClient( + process.env.AZURE_BLOB_CONTAINER_NAME || "raw-documents" + ); +} else { + warnMissing("Azure Blob Storage", ["AZURE_BLOB_CONNECTION_STRING", "AZURE_STORAGE_CONNECTION_STRING"]); +} // ─── Search ─────────────────────────────────────────────────────────────────── let searchIndexClient = null; diff --git a/backend/routes/documents.js b/backend/routes/documents.js index 9ac9483..10a38a1 100644 --- a/backend/routes/documents.js +++ b/backend/routes/documents.js @@ -49,7 +49,7 @@ router.post('/upload', upload.single('file'), async (req, res) => { if (!blobServiceClient) { return res.status(503).json({ success: false, - error: "Blob Storage service is not configured. Set AZURE_STORAGE_CONNECTION_STRING in your environment." + error: "Blob Storage service is not configured. Set AZURE_BLOB_CONNECTION_STRING (or AZURE_STORAGE_CONNECTION_STRING) in your environment." }); } diff --git a/backend/services/blobService.js b/backend/services/blobService.js index efd80d4..60496d1 100644 --- a/backend/services/blobService.js +++ b/backend/services/blobService.js @@ -1,11 +1,13 @@ const { BlobServiceClient } = require("@azure/storage-blob"); -const connectionString = process.env.AZURE_BLOB_CONNECTION_STRING; +const connectionString = + process.env.AZURE_BLOB_CONNECTION_STRING || + process.env.AZURE_STORAGE_CONNECTION_STRING; const containerName = process.env.AZURE_BLOB_CONTAINER_NAME || "audit"; function getBlobServiceClient() { if (!connectionString) { - throw new Error("AZURE_BLOB_CONNECTION_STRING is required"); + throw new Error("AZURE_BLOB_CONNECTION_STRING (or AZURE_STORAGE_CONNECTION_STRING) is required"); } return BlobServiceClient.fromConnectionString(connectionString); } diff --git a/services/auditService.js b/services/auditService.js index 56f2320..9638b14 100644 --- a/services/auditService.js +++ b/services/auditService.js @@ -3,7 +3,9 @@ const fs = require("fs/promises"); const path = require("path"); const { BlobServiceClient } = require("@azure/storage-blob"); -const CONNECTION_STRING = process.env.AZURE_BLOB_CONNECTION_STRING; +const CONNECTION_STRING = + process.env.AZURE_BLOB_CONNECTION_STRING || + process.env.AZURE_STORAGE_CONNECTION_STRING; const CONTAINER_NAME = process.env.AZURE_BLOB_CONTAINER_NAME || "audit"; const AUDIT_LOG_BLOB = process.env.AUDIT_LOG_BLOB || "audit_log_live.jsonl"; const AUDIT_SCHEMA_BLOB = "audit_log_schema.json"; @@ -66,7 +68,7 @@ async function appendLocalAuditLine(line) { function getContainerClient() { if (!CONNECTION_STRING) { - throw new Error("AZURE_BLOB_CONNECTION_STRING is required"); + throw new Error("AZURE_BLOB_CONNECTION_STRING (or AZURE_STORAGE_CONNECTION_STRING) is required"); } const blobServiceClient = BlobServiceClient.fromConnectionString(CONNECTION_STRING); From 98ee1d72d94a6ff222455af2d63fa0bc29745ba2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 30 Mar 2026 04:19:34 +0000 Subject: [PATCH 6/6] Restore "documents" as hardcoded search index fallback; cg-knowledge-index documented in .env.example Agent-Logs-Url: https://github.com/TriResolve-AI/grounded-knowledge-assistant/sessions/b3869fd0-36a2-4d35-9330-34bbdf742f99 Co-authored-by: portiajefferson <67895258+portiajefferson@users.noreply.github.com> --- backend/config/azureConfig.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/config/azureConfig.js b/backend/config/azureConfig.js index 933f713..2ee735f 100644 --- a/backend/config/azureConfig.js +++ b/backend/config/azureConfig.js @@ -41,7 +41,7 @@ if (process.env.AZURE_SEARCH_ENDPOINT && process.env.AZURE_SEARCH_KEY) { ); searchClient = new SearchClient( process.env.AZURE_SEARCH_ENDPOINT, - process.env.AZURE_SEARCH_INDEX || "cg-knowledge-index", + process.env.AZURE_SEARCH_INDEX || "documents", new AzureKeyCredential(process.env.AZURE_SEARCH_KEY) ); } else {