diff --git a/documents/LocalDevelopmentSetup.md b/documents/LocalDevelopmentSetup.md index 970937768..e2f2480c1 100644 --- a/documents/LocalDevelopmentSetup.md +++ b/documents/LocalDevelopmentSetup.md @@ -293,6 +293,46 @@ Create `.vscode/settings.json` and copy the following JSON: --- +### Running with Automated Script + +For convenience, you can use the provided startup scripts that handle environment setup and start both backend and frontend services automatically. This is the quickest way to get up and running locally. + +> **Note**: You must complete **Step 1 (Prerequisites)** and **Step 2 (Development Tools Setup)** before using the automated scripts. + +#### Windows (Command Prompt or PowerShell): + +```cmd +cd src +.\start.cmd +``` + +#### macOS/Linux/WSL: + +```bash +cd src +chmod +x start.sh +./start.sh +``` + +### What the Scripts Do + +The startup scripts automatically handle: +- Environment variable configuration +- Azure authentication +- Azure RBAC role assignments (Cosmos DB, SQL Server, AI Foundry, AI Search) +- Python virtual environment setup +- Backend dependency installation +- Frontend dependency installation +- Starting both backend and frontend servers + +> **Note**: The script includes a 30-second wait for the backend to initialize before starting the frontend. If you see connection errors initially, wait a moment and reload the page. + +--- + +## Running Backend and Frontend Manually + +If you prefer more control over the setup process, follow the steps below to configure and run each service individually. + ## Step 3: Azure Authentication Setup Before configuring services, authenticate with Azure: @@ -385,13 +425,17 @@ az role assignment create \ ``` #### Cosmos DB Access - ```bash +# Get your principal ID +PRINCIPAL_ID=$(az ad signed-in-user show --query id -o tsv) + # Assign Cosmos DB Built-in Data Contributor role -az role assignment create \ - --role "Cosmos DB Built-in Data Contributor" \ - --assignee $PRINCIPAL_ID \ - --scope "/subscriptions//resourceGroups//providers/Microsoft.DocumentDB/databaseAccounts/" +az cosmosdb sql role assignment create \ + --resource-group \ + --account-name \ + --role-definition-name "Cosmos DB Built-in Data Contributor" \ + --principal-id $PRINCIPAL_ID \ + --scope /subscriptions//resourceGroups//providers/Microsoft.DocumentDB/databaseAccounts/ ``` #### Azure Storage Access diff --git a/infra/main.bicep b/infra/main.bicep index f0b6b8f3f..f627e0eaf 100644 --- a/infra/main.bicep +++ b/infra/main.bicep @@ -780,6 +780,7 @@ module searchServiceUpdate 'br/public:avm/res/search/search-service:0.12.0' = { params: { // Required parameters name: aiSearchName + location: location enableTelemetry: enableTelemetry diagnosticSettings: enableMonitoring ? [ { diff --git a/infra/main.json b/infra/main.json index 608634937..139ecad94 100644 --- a/infra/main.json +++ b/infra/main.json @@ -6,7 +6,7 @@ "_generator": { "name": "bicep", "version": "0.41.2.15936", - "templateHash": "9287430903779325833" + "templateHash": "949305639975172329" } }, "parameters": { @@ -28296,9 +28296,9 @@ }, "dependsOn": [ "aiFoundryAiServices", - "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)]", - "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)]", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').cognitiveServices)]", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').openAI)]", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').aiServices)]", "virtualNetwork" ] }, @@ -31613,6 +31613,9 @@ "name": { "value": "[variables('aiSearchName')]" }, + "location": { + "value": "[parameters('location')]" + }, "enableTelemetry": { "value": "[parameters('enableTelemetry')]" }, @@ -41904,9 +41907,9 @@ } }, "dependsOn": [ - "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageDfs)]", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageFile)]", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageQueue)]", + "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageDfs)]", "[format('avmPrivateDnsZones[{0}]', variables('dnsZoneIndex').storageBlob)]", "userAssignedIdentity", "virtualNetwork" diff --git a/infra/main_custom.bicep b/infra/main_custom.bicep index 4f0a9dcc2..20087baa0 100644 --- a/infra/main_custom.bicep +++ b/infra/main_custom.bicep @@ -781,6 +781,7 @@ module searchServiceUpdate 'br/public:avm/res/search/search-service:0.12.0' = { params: { // Required parameters name: aiSearchName + location: location enableTelemetry: enableTelemetry diagnosticSettings: enableMonitoring ? [ { @@ -856,6 +857,9 @@ module searchServiceUpdate 'br/public:avm/res/search/search-service:0.12.0' = { ] : [] } + dependsOn: [ + searchService + ] } // ========== Search Service to AI Services Role Assignment ========== // diff --git a/infra/scripts/agent_scripts/requirements.txt b/infra/scripts/agent_scripts/requirements.txt index 5c3d5e927..b1179006b 100644 --- a/infra/scripts/agent_scripts/requirements.txt +++ b/infra/scripts/agent_scripts/requirements.txt @@ -1,3 +1,3 @@ -aiohttp==3.13.3 +aiohttp==3.13.4 azure-identity==1.25.2 azure-ai-projects==2.0.0b3 diff --git a/infra/scripts/index_scripts/requirements.txt b/infra/scripts/index_scripts/requirements.txt index f905453ef..c96054146 100644 --- a/infra/scripts/index_scripts/requirements.txt +++ b/infra/scripts/index_scripts/requirements.txt @@ -5,7 +5,7 @@ azure-ai-agents==1.2.0b5 azure-ai-inference==1.0.0b9 agent-framework-core==1.0.0rc2 agent-framework-azure-ai==1.0.0rc2 -pypdf==6.6.2 +pypdf==6.10.0 tiktoken==0.12.0 azure-identity==1.25.2 azure-ai-textanalytics==5.3.0 diff --git a/infra/scripts/run_create_agents_scripts.sh b/infra/scripts/run_create_agents_scripts.sh index 42aa2bdb2..6f7914d4c 100644 --- a/infra/scripts/run_create_agents_scripts.sh +++ b/infra/scripts/run_create_agents_scripts.sh @@ -330,6 +330,8 @@ titleAgentName="" while IFS='=' read -r key value; do # Skip empty lines or lines without '=' [ -z "$key" ] && continue + # Strip trailing carriage return if present (Windows line endings) + value="${value%$'\r'}" case "$key" in conversationAgentName) conversationAgentName="$value" diff --git a/src/App/package-lock.json b/src/App/package-lock.json index 4eab2f1d3..b3b6d6198 100644 --- a/src/App/package-lock.json +++ b/src/App/package-lock.json @@ -16083,9 +16083,9 @@ } }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, "node_modules/lodash-es": { diff --git a/src/App/package.json b/src/App/package.json index b14b93aa3..c159b492d 100644 --- a/src/App/package.json +++ b/src/App/package.json @@ -70,6 +70,7 @@ "d3-color": "$d3-color", "nth-check": "$nth-check", "flatted": "^3.4.2", + "lodash": "4.18.1", "node-forge": "^1.4.0", "react-scripts": { "picomatch": "^4.0.4" diff --git a/src/api/requirements.txt b/src/api/requirements.txt index 07f563955..ab91b5d31 100644 --- a/src/api/requirements.txt +++ b/src/api/requirements.txt @@ -7,9 +7,9 @@ pydantic[email]==2.11.10 # Azure SDK Core azure-core==1.38.0 -requests==2.32.5 -types-requests==2.32.4.20260107 -aiohttp==3.13.3 +requests==2.33.0 +types-requests==2.33.0.20260408 +aiohttp==3.13.4 # Azure Services azure-identity==1.25.2 @@ -35,6 +35,6 @@ opentelemetry-instrumentation==0.60b0 azure-monitor-opentelemetry==1.8.3 # Development tools -pytest==9.0.2 +pytest==9.0.3 pytest-cov==7.0.0 pytest-asyncio==1.3.0 diff --git a/src/start.cmd b/src/start.cmd index d53248e22..386a736ac 100644 --- a/src/start.cmd +++ b/src/start.cmd @@ -107,7 +107,7 @@ if exist "%API_ENV_FILE%" ( echo 2. Manually create %API_ENV_FILE% with required environment variables echo 3. Copy an existing .env file to %API_ENV_FILE% echo. - echo For more information, see: documents/LocalDebuggingSetup.md + echo For more information, see: documents/LocalDevelopmentSetup.md exit /b 1 ) @@ -131,7 +131,7 @@ set APP_ENV_FILE=%ROOT_DIR%\src\App\.env ( echo REACT_APP_API_BASE_URL=http://127.0.0.1:8000 ) > "%APP_ENV_FILE%" -echo Updated src/App/.env with APP_API_BASE_URL +echo Updated src/App/.env with REACT_APP_API_BASE_URL REM Add or update APP_ENV="dev" in API .env file echo Checking for existing APP_ENV in src/api/.env... @@ -284,6 +284,18 @@ if errorlevel 1 ( ) cd %ROOT_DIR% +REM Close any processes using ports 8000 and 3000 +echo Checking for processes using ports 8000 and 3000... +REM Kill any existing processes on ports 8000 and 3000 before starting +for %%P in (8000 3000) do ( + for /f "tokens=5" %%A in ('netstat -ano ^| findstr "LISTENING" ^| findstr ":%%P "') do ( + if "%%A" neq "0" ( + echo Port %%P is already in use by PID %%A. Stopping it... + taskkill /F /PID %%A /T >nul 2>&1 + ) + ) +) + REM Start backend and frontend echo Starting backend server... cd %ROOT_DIR% @@ -297,10 +309,27 @@ timeout /t 30 /nobreak >nul echo Starting frontend server... cd %ROOT_DIR%\src\App -call npm start -echo Both servers have been started. -echo Backend running at http://127.0.0.1:8000 -echo Frontend running at http://localhost:3000 +REM Show server information before starting +echo. +echo ======================================== +echo Both servers are now running: +echo Backend: http://127.0.0.1:8000 +echo Frontend: http://localhost:3000 +echo ======================================== +echo Press Ctrl+C to stop all servers +echo. + +REM Start npm with PowerShell wrapper for automatic cleanup on Ctrl+C (single line for reliability) +powershell -NoProfile -ExecutionPolicy Bypass -Command "try { npm start } finally { Write-Host ''; Write-Host 'Stopping all processes...'; Start-Sleep -Milliseconds 500; @(8000, 3000) | ForEach-Object { $port = $_; Get-NetTCPConnection -LocalPort $port -State Listen -ErrorAction SilentlyContinue | ForEach-Object { $pid_ = $_.OwningProcess; if ($pid_ -and $pid_ -ne 0) { taskkill /F /PID $pid_ /T 2>$null } } }; Write-Host 'Cleanup complete.'; Write-Host ''; Write-Host 'All servers stopped.' -ForegroundColor Yellow }" + +REM Fallback cleanup in case PowerShell finally block was interrupted +for %%P in (8000 3000) do ( + for /f "tokens=5" %%A in ('netstat -ano ^| findstr "LISTENING" ^| findstr ":%%P "') do ( + if "%%A" neq "0" ( + taskkill /F /PID %%A /T >nul 2>&1 + ) + ) +) endlocal \ No newline at end of file diff --git a/src/start.sh b/src/start.sh index b737955db..6b7e19c22 100644 --- a/src/start.sh +++ b/src/start.sh @@ -45,7 +45,7 @@ check_local_env() { echo " 2. Manually create $API_ENV_FILE with required environment variables" echo " 3. Copy an existing .env file to $API_ENV_FILE" echo "" - echo "For more information, see: documents/LocalDebuggingSetup.md" + echo "For more information, see: documents/LocalDevelopmentSetup.md" exit 1 fi } @@ -97,7 +97,12 @@ setup_environment() { echo "Checking for existing APP_ENV in src/api/.env..." if grep -q "^APP_ENV=" "$API_ENV_FILE" 2>/dev/null; then echo "APP_ENV already exists, updating to \"dev\"..." - sed -i 's/^APP_ENV=.*/APP_ENV="dev"/' "$API_ENV_FILE" + # macOS/BSD sed requires -i with extension, use portable approach + if [[ "$OSTYPE" == "darwin"* ]]; then + sed -i '' 's/^APP_ENV=.*/APP_ENV="dev"/' "$API_ENV_FILE" + else + sed -i 's/^APP_ENV=.*/APP_ENV="dev"/' "$API_ENV_FILE" + fi else echo "APP_ENV not found, adding APP_ENV=\"dev\"..." echo 'APP_ENV="dev"' >> "$API_ENV_FILE" @@ -286,6 +291,15 @@ setup_environment() { npm install --force || { echo "Failed to restore frontend npm packages"; exit 1; } cd "$ROOT_DIR" + # Kill any existing processes on ports 8000 and 3000 before starting + for port in 8000 3000; do + pid=$(lsof -ti :"$port" 2>/dev/null) + if [ -n "$pid" ]; then + echo "Port $port is already in use by PID $pid. Stopping it..." + kill -9 $pid + fi + done + # Start backend and frontend echo "Starting backend server..." cd "$ROOT_DIR" @@ -305,11 +319,43 @@ setup_environment() { echo "Starting frontend server..." cd "$ROOT_DIR/src/App" + + # Show server information before starting + echo "" + echo "========================================" + echo "Both servers are now running:" + echo " Backend: http://127.0.0.1:8000" + echo " Frontend: http://localhost:3000" + echo "========================================" + echo "Press Ctrl+C to stop all servers" + echo "" + + # Setup cleanup function to kill both backend and frontend on Ctrl+C + cleanup() { + echo "" + echo "Cleaning up processes..." + sleep 0.5 + + for port in 8000 3000; do + pid=$(lsof -ti :"$port" 2>/dev/null) + if [ -n "$pid" ]; then + echo "Stopping process on port $port (PID $pid)..." + kill -9 $pid 2>/dev/null + fi + done + + echo "All servers stopped." + exit 0 + } + + # Trap Ctrl+C and other termination signals + trap cleanup INT TERM + + # Start npm (this will block until Ctrl+C) npm start - - echo "Both servers have been started." - echo "Backend running at http://127.0.0.1:8000" - echo "Frontend running at http://localhost:3000" + + # Cleanup after npm exits normally + cleanup } # Check if .azure folder exists first