diff --git a/data/agent_teams/RFP_Analysis_team.json b/data/agent_teams/RFP_Analysis_team.json new file mode 100644 index 000000000..8ca99bd4b --- /dev/null +++ b/data/agent_teams/RFP_Analysis_team.json @@ -0,0 +1,75 @@ +{ + "id": "1", + "team_id": "team-clm-1", + "name": "RFP Team", + "status": "visible", + "created": "", + "created_by": "", + "description": "A specialized multi-agent team that analyzes RFP and contract documents to summarize content, identify potential risks, check compliance gaps, and provide action plans for contract improvement.", + "logo": "", + "plan": "", + "agents": [ + { + "input_key": "", + "type": "summary", + "name": "SummaryAgent", + "deployment_name": "gpt-4.1-mini", + "icon": "", + "system_message":"You are the Summary Agent. Your role is to read and synthesize RFP or proposal documents into clear, structured executive summaries. Focus on key clauses, deliverables, evaluation criteria, pricing terms, timelines, and obligations. Organize your output into sections such as Overview, Key Clauses, Deliverables, Terms, and Notable Conditions. Highlight unique or high-impact items that other agents (Risk or Compliance) should review. Be concise, factual, and neutral in tone.", + "description": "Summarizes RFP and contract documents into structured, easy-to-understand overviews.", + "use_rag": true, + "use_mcp": false, + "use_bing": false, + "use_reasoning": false, + "index_name": "pdf-index", + "index_foundry_name": "", + "index_endpoint": "", + "coding_tools": false + }, + { + "input_key": "", + "type": "risk", + "name": "RiskAgent", + "deployment_name": "gpt-4.1-mini", + "icon": "", + "system_message": "You are the Risk Agent. Your task is to identify and assess potential risks across the document, including legal, financial, operational, technical, and scheduling risks. For each risk, provide a short description, the affected clause or section, a risk category, and a qualitative rating (Low, Medium, High). Focus on material issues that could impact delivery, compliance, or business exposure. Summarize findings clearly to support decision-making and escalation.", + "description": "Analyzes the dataset for risks such as delivery, financial, operational, and compliance-related vulnerabilities.", + "use_rag": true, + "use_mcp": false, + "use_bing": false, + "use_reasoning": false, + "index_name": "pdf-index", + "index_foundry_name": "", + "index_endpoint": "", + "coding_tools": false + }, + { + "input_key": "", + "type": "compliance", + "name": "ComplianceAgent", + "deployment_name": "gpt-4.1-mini", + "icon": "", + "system_message": "You are the Compliance Agent. Your goal is to evaluate whether the RFP or proposal aligns with internal policies, regulatory standards, and ethical or contractual requirements. Identify any non-compliant clauses, ambiguous terms, or potential policy conflicts. For each issue, specify the related policy area (e.g., data privacy, labor, financial controls) and classify it as Mandatory or Recommended for review. Maintain a professional, objective tone and emphasize actionable compliance insights.", + "description": "Checks for compliance gaps against regulations, policies, and standard contracting practices.", + "use_rag": true, + "use_mcp": false, + "use_bing": false, + "use_reasoning": false, + "index_name": "pdf-index", + "index_foundry_name": "", + "index_endpoint": "", + "coding_tools": false + } + ], + "protected": false, + "starting_tasks": [ + { + "id": "task-1", + "name": "RFP Document Summary", + "prompt": "I would like to review the Woodgrove Bank RFP response from Contoso", + "created": "", + "creator": "", + "logo": "" + } + ] +} diff --git a/data/datasets/RFP_dataset/RFP_Compliance_Guidelines.pdf b/data/datasets/RFP_dataset/RFP_Compliance_Guidelines.pdf new file mode 100644 index 000000000..53e89678c Binary files /dev/null and b/data/datasets/RFP_dataset/RFP_Compliance_Guidelines.pdf differ diff --git a/data/datasets/RFP_dataset/RFP_Security_Risk_Guidelines.pdf b/data/datasets/RFP_dataset/RFP_Security_Risk_Guidelines.pdf new file mode 100644 index 000000000..e1534a45f Binary files /dev/null and b/data/datasets/RFP_dataset/RFP_Security_Risk_Guidelines.pdf differ diff --git a/data/datasets/RFP_dataset/Woodgrove_Bank_RFP_Response_Contoso_Ltd.pdf b/data/datasets/RFP_dataset/Woodgrove_Bank_RFP_Response_Contoso_Ltd.pdf new file mode 100644 index 000000000..46ab9b4ba Binary files /dev/null and b/data/datasets/RFP_dataset/Woodgrove_Bank_RFP_Response_Contoso_Ltd.pdf differ diff --git a/docs/DeploymentGuide.md b/docs/DeploymentGuide.md index 168d84922..10b63a712 100644 --- a/docs/DeploymentGuide.md +++ b/docs/DeploymentGuide.md @@ -496,4 +496,4 @@ To debug the python server in the frontend directory (frontend_server.py) and re "args": ["frontend_server:app", "--port", "3000", "--reload"], "jinja": true } -``` +``` \ No newline at end of file diff --git a/infra/scripts/Process-Sample-Data.ps1 b/infra/scripts/Process-Sample-Data.ps1 index c6d67c817..e83dfeb8c 100644 --- a/infra/scripts/Process-Sample-Data.ps1 +++ b/infra/scripts/Process-Sample-Data.ps1 @@ -10,25 +10,11 @@ param( ) # Get parameters from azd env, if not provided -if (-not $StorageAccount) { - $StorageAccount = $(azd env get-value AZURE_STORAGE_ACCOUNT_NAME) -} - -if (-not $BlobContainer) { - $BlobContainer = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME) -} - -if (-not $AiSearch) { - $AiSearch = $(azd env get-value AZURE_AI_SEARCH_NAME) -} - -if (-not $AiSearchIndex) { - $AiSearchIndex = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME) -} - -if (-not $ResourceGroup) { - $ResourceGroup = $(azd env get-value AZURE_RESOURCE_GROUP) -} +if (-not $StorageAccount) { $StorageAccount = $(azd env get-value AZURE_STORAGE_ACCOUNT_NAME) } +if (-not $BlobContainer) { $BlobContainer = $(azd env get-value AZURE_STORAGE_CONTAINER_NAME) } +if (-not $AiSearch) { $AiSearch = $(azd env get-value AZURE_AI_SEARCH_NAME) } +if (-not $AiSearchIndex) { $AiSearchIndex = $(azd env get-value AZURE_AI_SEARCH_INDEX_NAME) } +if (-not $ResourceGroup) { $ResourceGroup = $(azd env get-value AZURE_RESOURCE_GROUP) } if (-not $AzSubscriptionId) { $AzSubscriptionId = $(azd env get-value AZURE_SUBSCRIPTION_ID) @@ -67,13 +53,8 @@ if ($currentSubscriptionId -ne $AzSubscriptionId) { if ($confirmation.ToLower() -ne "y") { Write-Host "Fetching available subscriptions..." $availableSubscriptions = (az account list --query "[?state=='Enabled']" | ConvertFrom-Json -AsHashtable) - - # Create a cleaner array of subscription objects $subscriptionArray = $availableSubscriptions | ForEach-Object { - [PSCustomObject]@{ - Name = $_.name - Id = $_.id - } + [PSCustomObject]@{ Name = $_.name; Id = $_.id } } do { @@ -90,17 +71,13 @@ if ($currentSubscriptionId -ne $AzSubscriptionId) { if ($subscriptionIndex -ge 1 -and $subscriptionIndex -le $subscriptionArray.Count) { $selectedSubscription = $subscriptionArray[$subscriptionIndex-1] - $selectedSubscriptionName = $selectedSubscription.Name - $selectedSubscriptionId = $selectedSubscription.Id - - # Set the selected subscription - $result = az account set --subscription $selectedSubscriptionId + $result = az account set --subscription $selectedSubscription.Id if ($LASTEXITCODE -eq 0) { - Write-Host "Switched to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )" + Write-Host "Switched to subscription: $($selectedSubscription.Name) ( $($selectedSubscription.Id) )" break } else { - Write-Host "Failed to switch to subscription: $selectedSubscriptionName ( $selectedSubscriptionId )." + Write-Host "Failed to switch to subscription: $($selectedSubscription.Name) ( $($selectedSubscription.Id) )." } } else { @@ -120,6 +97,7 @@ else { $stIsPublicAccessDisabled = $false $srchIsPublicAccessDisabled = $false + # Enable public access for resources if ($ResourceGroup) { $stPublicAccess = $(az storage account show --name $StorageAccount --resource-group $ResourceGroup --query "publicNetworkAccess" -o tsv) @@ -127,12 +105,8 @@ if ($ResourceGroup) { $stIsPublicAccessDisabled = $true Write-Host "Enabling public access for storage account: $StorageAccount" az storage account update --name $StorageAccount --public-network-access enabled --default-action Allow --output none - if ($LASTEXITCODE -ne 0) { - Write-Host "Error: Failed to enable public access for storage account." - exit 1 - } - } - else { + if ($LASTEXITCODE -ne 0) { Write-Host "Error: Failed to enable public access for storage account."; exit 1 } + } else { Write-Host "Public access is already enabled for storage account: $StorageAccount" } @@ -141,116 +115,98 @@ if ($ResourceGroup) { $srchIsPublicAccessDisabled = $true Write-Host "Enabling public access for search service: $AiSearch" az search service update --name $AiSearch --resource-group $ResourceGroup --public-network-access enabled --output none - if ($LASTEXITCODE -ne 0) { - Write-Host "Error: Failed to enable public access for search service." - exit 1 - } - } - else { + if ($LASTEXITCODE -ne 0) { Write-Host "Error: Failed to enable public access for search service."; exit 1 } + } else { Write-Host "Public access is already enabled for search service: $AiSearch" } } +# Upload CSV files +Write-Host "Uploading CSV files to blob storage..." +az storage blob upload-batch --account-name $StorageAccount --destination $BlobContainer --source "data/datasets" --auth-mode login --pattern "*.csv" --overwrite --output none +az storage blob upload-batch --account-name $StorageAccount --destination $BlobContainer --source "data/datasets" --auth-mode login --pattern "*.json" --overwrite --output none +if ($LASTEXITCODE -ne 0) { Write-Host "Error: Failed to upload CSV files."; exit 1 } +Write-Host "CSV files uploaded successfully." -# Upload sample files to blob storage -Write-Host "Uploading sample files to blob storage..." -$result = az storage blob upload-batch --account-name $StorageAccount --destination $BlobContainer --source "data/datasets" --auth-mode login --pattern "*" --overwrite --output none +# Upload PDF files +Write-Host "Uploading PDF files from RFP_dataset to blob storage..." +az storage blob upload-batch --account-name $StorageAccount --destination $BlobContainer --source "data/datasets/RFP_dataset" --auth-mode login --pattern "*.pdf" --overwrite --output none +if ($LASTEXITCODE -ne 0) { Write-Host "Error: Failed to upload PDF files."; exit 1 } +Write-Host "PDF files uploaded successfully." -if ($LASTEXITCODE -ne 0) { - Write-Host "Error: Failed to upload files to blob storage." - exit 1 +# Detect file types +Write-Host "Detecting file types in blob container..." +$fileList = az storage blob list --account-name $StorageAccount --container-name $BlobContainer --query "[].name" -o tsv --auth-mode login +$hasPdf = $false +$hasCsv = $false +foreach ($file in $fileList) { + if ($file -match "\.pdf$") { $hasPdf = $true } + if ($file -match "\.csv$") { $hasCsv = $true } } -Write-Host "Files uploaded successfully to blob storage." # Determine the correct Python command $pythonCmd = $null - try { $pythonVersion = (python --version) 2>&1 - if ($pythonVersion -match "Python \d") { - $pythonCmd = "python" - } -} -catch { - # Do nothing, try python3 next -} - + if ($pythonVersion -match "Python \d") { $pythonCmd = "python" } +} catch {} if (-not $pythonCmd) { try { $pythonVersion = (python3 --version) 2>&1 - if ($pythonVersion -match "Python \d") { - $pythonCmd = "python3" - } - } - catch { - Write-Host "Python is not installed on this system or it is not added in the PATH." + if ($pythonVersion -match "Python \d") { $pythonCmd = "python3" } + } catch { + Write-Host "Python is not installed or not in PATH." exit 1 } } +if (-not $pythonCmd) { Write-Host "Python is not installed or not in PATH."; exit 1 } -if (-not $pythonCmd) { - Write-Host "Python is not installed on this system or it is not added in the PATH." - exit 1 -} - -# Create virtual environment +# Virtual environment $venvPath = "infra/scripts/scriptenv" if (Test-Path $venvPath) { Write-Host "Virtual environment already exists. Skipping creation." -} -else { +} else { Write-Host "Creating virtual environment" & $pythonCmd -m venv $venvPath } -# Activate the virtual environment -$activateScript = "" -if (Test-Path (Join-Path -Path $venvPath -ChildPath "bin/Activate.ps1")) { - $activateScript = Join-Path -Path $venvPath -ChildPath "bin/Activate.ps1" -} -elseif (Test-Path (Join-Path -Path $venvPath -ChildPath "Scripts/Activate.ps1")) { - $activateScript = Join-Path -Path $venvPath -ChildPath "Scripts/Activate.ps1" -} - -if ($activateScript) { - Write-Host "Activating virtual environment" - . $activateScript # Use dot sourcing to run in the current scope -} -else { +# Activate venv +if (Test-Path (Join-Path $venvPath "bin/Activate.ps1")) { + . (Join-Path $venvPath "bin/Activate.ps1") +} elseif (Test-Path (Join-Path $venvPath "Scripts/Activate.ps1")) { + . (Join-Path $venvPath "Scripts/Activate.ps1") +} else { Write-Host "Error activating virtual environment. Requirements may be installed globally." } -# Install the requirements +# Install requirements Write-Host "Installing requirements" pip install --quiet -r infra/scripts/requirements.txt Write-Host "Requirements installed" -# Run the Python script to index data -Write-Host "Running the python script to index data" -$process = Start-Process -FilePath $pythonCmd -ArgumentList "infra/scripts/index_datasets.py", $StorageAccount, $BlobContainer, $AiSearch, $AiSearchIndex -Wait -NoNewWindow -PassThru - -if ($process.ExitCode -ne 0) { - Write-Host "Error: Indexing python script execution failed." - exit 1 +# Run indexing scripts +if ($hasCsv) { + Write-Host "Running the python script to index CSV data" + & $pythonCmd "infra/scripts/index_datasets.py" $StorageAccount $BlobContainer $AiSearch $AiSearchIndex + if ($LASTEXITCODE -ne 0) { Write-Host "Error: CSV indexing script failed."; exit 1 } +} +# if ($hasPdf) { +# Write-Host "Running the python script to index PDF data" +# & $pythonCmd "infra/scripts/index_rfp_data.py" $StorageAccount $BlobContainer $AiSearch $AiSearchIndex +# if ($LASTEXITCODE -ne 0) { Write-Host "Error: PDF indexing script failed."; exit 1 } +# } +if (-not $hasCsv -and -not $hasPdf) { + Write-Host "No CSV or PDF files found to index." } -#disable public access for resources +# Disable public access again if ($stIsPublicAccessDisabled) { Write-Host "Disabling public access for storage account: $StorageAccount" az storage account update --name $StorageAccount --public-network-access disabled --default-action Deny --output none - if ($LASTEXITCODE -ne 0) { - Write-Host "Error: Failed to disable public access for storage account." - exit 1 - } } - if ($srchIsPublicAccessDisabled) { Write-Host "Disabling public access for search service: $AiSearch" az search service update --name $AiSearch --resource-group $ResourceGroup --public-network-access disabled --output none - if ($LASTEXITCODE -ne 0) { - Write-Host "Error: Failed to disable public access for search service." - exit 1 - } } Write-Host "Script executed successfully. Sample Data Processed Successfully." diff --git a/infra/scripts/index_datasets.py b/infra/scripts/index_datasets.py index 86a15d7b6..2962bf1f3 100644 --- a/infra/scripts/index_datasets.py +++ b/infra/scripts/index_datasets.py @@ -5,6 +5,49 @@ from azure.storage.blob import BlobServiceClient import sys +# PDF text extraction function +def extract_pdf_text(pdf_bytes): + """Extract text content from PDF bytes using PyPDF2""" + try: + import PyPDF2 + import io + + pdf_file = io.BytesIO(pdf_bytes) + pdf_reader = PyPDF2.PdfReader(pdf_file) + + # Check if PDF is encrypted/protected + if pdf_reader.is_encrypted: + return "PDF_PROTECTED: This PDF document is password-protected or encrypted and cannot be processed." + + text_content = [] + for page in pdf_reader.pages: + try: + page_text = page.extract_text() + if page_text and page_text.strip(): + text_content.append(page_text) + except Exception: + continue + + full_text = "\n".join(text_content).strip() + + # Check for protection messages + protection_indicators = [ + "protected by Microsoft Office", + "You'll need a different reader", + "Download a compatible PDF reader", + "This PDF Document has been protected" + ] + + if any(indicator.lower() in full_text.lower() for indicator in protection_indicators): + return "PDF_PROTECTED: This PDF document appears to be protected or encrypted." + + return full_text if full_text else "PDF_NO_TEXT: No readable text content found in PDF." + + except ImportError: + return "PDF_ERROR: PyPDF2 library not available. Install with: pip install PyPDF2" + except Exception as e: + return f"PDF_ERROR: Error reading PDF content: {str(e)}" + if len(sys.argv) < 4: print("Usage: python index_datasets.py []") sys.exit(1) @@ -51,11 +94,19 @@ #if blob.name.endswith(".csv"): title = blob.name.replace(".csv", "") title = title.replace(".json", "") + title = title.replace(".pdf", "") # Also handle PDF extension data = container_client.download_blob(blob.name).readall() try: print(f"Reading data from blob: {blob.name}...") - text = data.decode('utf-8') + + # Check if this is a PDF file and process accordingly + if blob.name.lower().endswith('.pdf'): + text = extract_pdf_text(data) + else: + # Original processing for non-PDF files + text = data.decode('utf-8') + data_list.append({ "content": text, "id": str(idx), diff --git a/infra/scripts/process_sample_data.sh b/infra/scripts/process_sample_data.sh index 2253be08c..966be785d 100644 --- a/infra/scripts/process_sample_data.sh +++ b/infra/scripts/process_sample_data.sh @@ -125,14 +125,34 @@ if [ -n "$resourceGroup" ]; then fi -#Upload sample files to blob storage -echo "Uploading sample files to blob storage..." -az storage blob upload-batch --account-name "$storageAccount" --destination "$blobContainer" --source "data/datasets" --auth-mode login --pattern '*' --overwrite --output none +#Upload sample CSV files to blob storage +echo "Uploading CSV and JSON sample files to blob storage..." +az storage blob upload-batch --account-name "$storageAccount" --destination "$blobContainer" --source "data/datasets" --auth-mode login --pattern '*.csv' --overwrite --output none +az storage blob upload-batch --account-name "$storageAccount" --destination "$blobContainer" --source "data/datasets" --auth-mode login --pattern '*.json' --overwrite --output none if [ $? -ne 0 ]; then - echo "Error: Failed to upload files to blob storage." + echo "Error: Failed to upload CSV and JSON files to blob storage." exit 1 fi -echo "Files uploaded successfully to blob storage." +echo "CSV and JSON files uploaded successfully to blob storage." + +#Upload PDF files from RFP_dataset to blob storage +echo "Uploading PDF files from RFP_dataset to blob storage..." +az storage blob upload-batch --account-name "$storageAccount" --destination "$blobContainer" --source "data/datasets/RFP_dataset" --auth-mode login --pattern '*.pdf' --overwrite --output none +if [ $? -ne 0 ]; then + echo "Error: Failed to upload PDF files to blob storage." + exit 1 +fi +echo "PDF files uploaded successfully to blob storage." + +# Detect file types in the blob container +echo "Detecting file types in blob container..." +file_list=$(az storage blob list --account-name "$storageAccount" --container-name "$blobContainer" --query "[].name" -o tsv --auth-mode login) +has_pdf=false +has_csv=false +while IFS= read -r file; do + [[ "$file" == *.pdf ]] && has_pdf=true + [[ "$file" == *.csv ]] && has_csv=true +done <<< "$file_list" # Determine the correct Python command if command -v python && python --version &> /dev/null; then @@ -168,11 +188,27 @@ echo "Installing requirements" pip install --quiet -r infra/scripts/requirements.txt echo "Requirements installed" -echo "Running the python script to index data" -$PYTHON_CMD infra/scripts/index_datasets.py "$storageAccount" "$blobContainer" "$aiSearch" "$aiSearchIndex" -if [ $? -ne 0 ]; then - echo "Error: Indexing python script execution failed." - exit 1 +# Run the appropriate Python indexing script(s) +if [ "$has_csv" = true ]; then + echo "Running the python script to index CSV data" + $PYTHON_CMD infra/scripts/index_datasets.py "$storageAccount" "$blobContainer" "$aiSearch" "$aiSearchIndex" + if [ $? -ne 0 ]; then + echo "Error: CSV indexing python script execution failed." + exit 1 + fi +fi + +# if [ "$has_pdf" = true ]; then +# echo "Running the python script to index PDF data" +# $PYTHON_CMD infra/scripts/index_rfp_data.py "$storageAccount" "$blobContainer" "$aiSearch" "$aiSearchIndex" +# if [ $? -ne 0 ]; then +# echo "Error: PDF indexing python script execution failed." +# exit 1 +# fi +# fi + +if [ "$has_csv" = false ] && [ "$has_pdf" = false ]; then + echo "No CSV or PDF files found to index." fi #disable public access for resources diff --git a/infra/scripts/requirements.txt b/infra/scripts/requirements.txt index 67ba55e5a..2a3fc9885 100644 --- a/infra/scripts/requirements.txt +++ b/infra/scripts/requirements.txt @@ -1,4 +1,6 @@ azure-search-documents==11.5.3 azure-identity==1.24.0 azure-storage-blob==12.26.0 -requests==2.32.5 \ No newline at end of file +requests==2.32.5 +azure-core +PyPDF2 diff --git a/infra/scripts/upload_team_config.py b/infra/scripts/upload_team_config.py index 3a7e2ccb7..be177a7b3 100644 --- a/infra/scripts/upload_team_config.py +++ b/infra/scripts/upload_team_config.py @@ -46,9 +46,10 @@ def check_team_exists(backend_url, team_id, user_principal_id): print(f"Scanning directory: {directory_path}") files_to_process = [ - ("hr.json", "00000000-0000-0000-0000-000000000001"), - ("marketing.json", "00000000-0000-0000-0000-000000000002"), - ("retail.json", "00000000-0000-0000-0000-000000000003"), + ("RFP_Analysis_team.json", "00000000-0000-0000-0000-000000000001"), + ("hr.json", "00000000-0000-0000-0000-000000000002"), + ("marketing.json", "00000000-0000-0000-0000-000000000003"), + ("retail.json", "00000000-0000-0000-0000-000000000004"), ] upload_endpoint = backend_url.rstrip('/') + '/api/v3/upload_team_config' diff --git a/rfp_scripts/index_rfp_data.py b/rfp_scripts/index_rfp_data.py new file mode 100644 index 000000000..cb784309a --- /dev/null +++ b/rfp_scripts/index_rfp_data.py @@ -0,0 +1,130 @@ +import os +import uuid +from azure.identity import DefaultAzureCredential +from azure.storage.blob import BlobServiceClient +from azure.core.credentials import AzureKeyCredential +from azure.search.documents import SearchClient +from azure.search.documents.indexes import SearchIndexClient +from azure.search.documents.indexes.models import ( + SearchIndex, + SimpleField, + SearchableField, + SearchFieldDataType, +) +from PyPDF2 import PdfReader + +# --- CONFIGURATION --- +search_service_endpoint ="" +admin_key ="" +storage_account_url = "" +blob_container_name ="" +local_data_folder = "" +index_name = "" + +# --- STEP 1: Upload PDFs to Azure Blob Storage --- +print("Uploading PDF files to Azure Blob Storage using Azure AD authentication...") + +try: + # Authenticate using Azure AD + blob_service_client = BlobServiceClient(account_url=storage_account_url, credential=DefaultAzureCredential()) + container_client = blob_service_client.get_container_client(blob_container_name) + + # Create container if it doesn't exist + if not container_client.exists(): + container_client.create_container() + print(f"Created container: {blob_container_name}") + else: + print(f"Container already exists: {blob_container_name}") + + uploaded_files = [] + for root, _, files in os.walk(local_data_folder): + for filename in files: + if filename.lower().endswith(".pdf"): + file_path = os.path.join(root, filename) + blob_path = os.path.relpath(file_path, local_data_folder) + blob_client = container_client.get_blob_client(blob_path) + + with open(file_path, "rb") as data: + blob_client.upload_blob(data, overwrite=True) + print(f"âŦ†ī¸ Uploaded: {blob_path}") + uploaded_files.append(filename) + + if not uploaded_files: + print(" No PDF files found to upload.") +except Exception as e: + print(f" Upload failed: {e}") + +# --- STEP 2: Create Azure AI Search Index --- +print("\n Setting up Azure AI Search index...") + +index_client = SearchIndexClient(endpoint=search_service_endpoint, credential=AzureKeyCredential(admin_key)) + +index_fields = [ + SimpleField(name="id", type=SearchFieldDataType.String, key=True), + SearchableField(name="content", type=SearchFieldDataType.String, searchable=True), + SearchableField(name="title", type=SearchFieldDataType.String, searchable=True, filterable=True) +] + +index = SearchIndex(name=index_name, fields=index_fields) + +try: + index_client.create_index(index) + print(f" Created index: {index_name}") +except Exception as e: + if "already exists" in str(e).lower(): + print(f" Index already exists: {index_name}") + else: + print(f" Failed to create index: {e}") + +# --- STEP 3: Extract text from PDFs and index into Azure AI Search --- +print("\n Extracting text from PDFs and indexing into Azure AI Search...") + +search_client = SearchClient(endpoint=search_service_endpoint, index_name=index_name, credential=AzureKeyCredential(admin_key)) + +def extract_text_from_pdf(pdf_path): + """Extract text from a PDF file using PyPDF2.""" + text = "" + try: + with open(pdf_path, "rb") as f: + reader = PdfReader(f) + for page in reader.pages: + page_text = page.extract_text() + if page_text: + text += page_text + "\n" + except Exception as e: + print(f" Error reading {pdf_path}: {e}") + return text.strip() + +docs = [] +for root, _, files in os.walk(local_data_folder): + for filename in files: + if filename.lower().endswith(".pdf"): + file_path = os.path.join(root, filename) + text_content = extract_text_from_pdf(file_path) + + if text_content: + docs.append({ + "id": str(uuid.uuid4()), + "content": text_content, + "title": filename + }) + print(f" Extracted and prepared: {filename}") + else: + print(f" No text extracted from {filename}") + +# --- Upload documents to index --- +if docs: + try: + batch_size = 500 # Upload in manageable chunks + for i in range(0, len(docs), batch_size): + chunk = docs[i:i + batch_size] + results = search_client.upload_documents(chunk) + succeeded = sum(1 for r in results if r.succeeded) + print(f" Indexed {succeeded}/{len(chunk)} documents in batch {i//batch_size + 1}") + print(f" Done! Total indexed documents: {len(docs)}") + except Exception as e: + print(f" Failed to upload documents: {e}") +else: + print(" No documents to index.") + +print("\n PDFs are now stored in Azure Blob Storage and indexed in Azure AI Search.") diff --git a/rfp_scripts/upload_data.py b/rfp_scripts/upload_data.py new file mode 100644 index 000000000..7c79d1064 --- /dev/null +++ b/rfp_scripts/upload_data.py @@ -0,0 +1,36 @@ +import os +from azure.identity import DefaultAzureCredential +from azure.storage.blob import BlobServiceClient, ContentSettings + +# --- Azure Blob Storage configuration --- +storage_account_url = "" +container_name = "rfp-documents" # Your blob container name +csv_folder = "../data/datasets/RFP_dataset" + +# --- Use DefaultAzureCredential (ensure you're authenticated to Azure) --- +credential = DefaultAzureCredential() +blob_service_client = BlobServiceClient(account_url=storage_account_url, credential=credential) + +# --- Get container client (create container if it doesn't exist) --- +container_client = blob_service_client.get_container_client(container_name) +try: + container_client.create_container() + print(f"đŸ“Ļ Created container: {container_name}") +except Exception as e: + print(f"â„šī¸ Container may already exist: {e}") + +# --- Loop through all CSV files and upload --- +for filename in os.listdir(csv_folder): + if filename.endswith(".csv"): + file_path = os.path.join(csv_folder, filename) + blob_client = container_client.get_blob_client(filename) + + with open(file_path, "rb") as data: + blob_client.upload_blob( + data, + overwrite=True, + content_settings=ContentSettings(content_type='text/csv') + ) + print(f"✅ Uploaded {filename} to blob storage") + +print("🎉 All CSVs uploaded to Azure Blob Storage!") diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 3e17847f7..c39de8993 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -509,9 +509,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", - "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", + "integrity": "sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==", "cpu": [ "ppc64" ], @@ -526,9 +526,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", - "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.11.tgz", + "integrity": "sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==", "cpu": [ "arm" ], @@ -543,9 +543,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", - "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.11.tgz", + "integrity": "sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==", "cpu": [ "arm64" ], @@ -560,9 +560,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", - "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.11.tgz", + "integrity": "sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==", "cpu": [ "x64" ], @@ -577,9 +577,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", - "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.11.tgz", + "integrity": "sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==", "cpu": [ "arm64" ], @@ -594,9 +594,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", - "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.11.tgz", + "integrity": "sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==", "cpu": [ "x64" ], @@ -611,9 +611,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", - "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.11.tgz", + "integrity": "sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==", "cpu": [ "arm64" ], @@ -628,9 +628,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", - "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.11.tgz", + "integrity": "sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==", "cpu": [ "x64" ], @@ -645,9 +645,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", - "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.11.tgz", + "integrity": "sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==", "cpu": [ "arm" ], @@ -662,9 +662,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", - "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.11.tgz", + "integrity": "sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==", "cpu": [ "arm64" ], @@ -679,9 +679,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", - "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.11.tgz", + "integrity": "sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==", "cpu": [ "ia32" ], @@ -696,9 +696,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", - "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.11.tgz", + "integrity": "sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==", "cpu": [ "loong64" ], @@ -713,9 +713,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", - "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.11.tgz", + "integrity": "sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==", "cpu": [ "mips64el" ], @@ -730,9 +730,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", - "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.11.tgz", + "integrity": "sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==", "cpu": [ "ppc64" ], @@ -747,9 +747,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", - "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.11.tgz", + "integrity": "sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==", "cpu": [ "riscv64" ], @@ -764,9 +764,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", - "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.11.tgz", + "integrity": "sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==", "cpu": [ "s390x" ], @@ -781,9 +781,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", - "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.11.tgz", + "integrity": "sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==", "cpu": [ "x64" ], @@ -798,9 +798,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", - "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.11.tgz", + "integrity": "sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==", "cpu": [ "arm64" ], @@ -815,9 +815,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", - "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.11.tgz", + "integrity": "sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==", "cpu": [ "x64" ], @@ -832,9 +832,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", - "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.11.tgz", + "integrity": "sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==", "cpu": [ "arm64" ], @@ -849,9 +849,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", - "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.11.tgz", + "integrity": "sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==", "cpu": [ "x64" ], @@ -866,9 +866,9 @@ } }, "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", - "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.11.tgz", + "integrity": "sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==", "cpu": [ "arm64" ], @@ -883,9 +883,9 @@ } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", - "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.11.tgz", + "integrity": "sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==", "cpu": [ "x64" ], @@ -900,9 +900,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", - "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.11.tgz", + "integrity": "sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==", "cpu": [ "arm64" ], @@ -917,9 +917,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", - "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.11.tgz", + "integrity": "sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==", "cpu": [ "ia32" ], @@ -934,9 +934,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", - "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.11.tgz", + "integrity": "sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==", "cpu": [ "x64" ], @@ -4862,9 +4862,9 @@ } }, "node_modules/esbuild": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", - "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", + "version": "0.25.11", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.11.tgz", + "integrity": "sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4875,32 +4875,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.9", - "@esbuild/android-arm": "0.25.9", - "@esbuild/android-arm64": "0.25.9", - "@esbuild/android-x64": "0.25.9", - "@esbuild/darwin-arm64": "0.25.9", - "@esbuild/darwin-x64": "0.25.9", - "@esbuild/freebsd-arm64": "0.25.9", - "@esbuild/freebsd-x64": "0.25.9", - "@esbuild/linux-arm": "0.25.9", - "@esbuild/linux-arm64": "0.25.9", - "@esbuild/linux-ia32": "0.25.9", - "@esbuild/linux-loong64": "0.25.9", - "@esbuild/linux-mips64el": "0.25.9", - "@esbuild/linux-ppc64": "0.25.9", - "@esbuild/linux-riscv64": "0.25.9", - "@esbuild/linux-s390x": "0.25.9", - "@esbuild/linux-x64": "0.25.9", - "@esbuild/netbsd-arm64": "0.25.9", - "@esbuild/netbsd-x64": "0.25.9", - "@esbuild/openbsd-arm64": "0.25.9", - "@esbuild/openbsd-x64": "0.25.9", - "@esbuild/openharmony-arm64": "0.25.9", - "@esbuild/sunos-x64": "0.25.9", - "@esbuild/win32-arm64": "0.25.9", - "@esbuild/win32-ia32": "0.25.9", - "@esbuild/win32-x64": "0.25.9" + "@esbuild/aix-ppc64": "0.25.11", + "@esbuild/android-arm": "0.25.11", + "@esbuild/android-arm64": "0.25.11", + "@esbuild/android-x64": "0.25.11", + "@esbuild/darwin-arm64": "0.25.11", + "@esbuild/darwin-x64": "0.25.11", + "@esbuild/freebsd-arm64": "0.25.11", + "@esbuild/freebsd-x64": "0.25.11", + "@esbuild/linux-arm": "0.25.11", + "@esbuild/linux-arm64": "0.25.11", + "@esbuild/linux-ia32": "0.25.11", + "@esbuild/linux-loong64": "0.25.11", + "@esbuild/linux-mips64el": "0.25.11", + "@esbuild/linux-ppc64": "0.25.11", + "@esbuild/linux-riscv64": "0.25.11", + "@esbuild/linux-s390x": "0.25.11", + "@esbuild/linux-x64": "0.25.11", + "@esbuild/netbsd-arm64": "0.25.11", + "@esbuild/netbsd-x64": "0.25.11", + "@esbuild/openbsd-arm64": "0.25.11", + "@esbuild/openbsd-x64": "0.25.11", + "@esbuild/openharmony-arm64": "0.25.11", + "@esbuild/sunos-x64": "0.25.11", + "@esbuild/win32-arm64": "0.25.11", + "@esbuild/win32-ia32": "0.25.11", + "@esbuild/win32-x64": "0.25.11" } }, "node_modules/escalade": { @@ -9794,9 +9794,9 @@ } }, "node_modules/vite": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz", - "integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==", + "version": "7.1.12", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz", + "integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==", "dev": true, "license": "MIT", "dependencies": { diff --git a/src/frontend/package.json b/src/frontend/package.json index fabef9d44..1aeed1c03 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -68,4 +68,4 @@ "vite": "^7.1.2", "vitest": "^3.2.4" } -} \ No newline at end of file +} diff --git a/src/frontend/src/components/common/TeamSelector.tsx b/src/frontend/src/components/common/TeamSelector.tsx index ebbd04a25..bbf6bed60 100644 --- a/src/frontend/src/components/common/TeamSelector.tsx +++ b/src/frontend/src/components/common/TeamSelector.tsx @@ -70,8 +70,8 @@ const TeamSelector: React.FC = ({ const [uploadSuccessMessage, setUploadSuccessMessage] = useState(null); // Helper function to check if a team is a default team const isDefaultTeam = (team: TeamConfig): boolean => { - const defaultTeamIds = ['team-1', 'team-2', 'team-3']; - const defaultTeamNames = ['Human Resources Team', 'Product Marketing Team', 'Retail Customer Success Team']; + const defaultTeamIds = ['team-1', 'team-2', 'team-3', 'team-clm-1']; + const defaultTeamNames = ['Human Resources Team', 'Product Marketing Team', 'Retail Customer Success Team', 'RFP Team']; return defaultTeamIds.includes(team.team_id) || defaultTeamNames.includes(team.name);