Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
ba363cb
add chat tutorial
nadoylemsft Feb 12, 2026
3c724df
upd chat tutorial
nadoylemsft Feb 12, 2026
4a158d3
Update FULL_WIDTH_CHAT_SUPPORT.md
paullizer Feb 13, 2026
dfe06f8
Fixed 8 files with unescaped Liquid/Jinja2 template syntax that would…
paullizer Feb 13, 2026
c2ca3a7
Update .gitignore
paullizer Mar 3, 2026
dac10fb
Updated to v0.239.001
paullizer Mar 3, 2026
25ed725
updated sidebar
paullizer Mar 3, 2026
4ab5771
fixed formatting
paullizer Mar 3, 2026
4c1ebab
latest release
paullizer Mar 3, 2026
2f5cb31
Update export-conversation.md
paullizer Mar 3, 2026
b253da5
more features
paullizer Mar 3, 2026
9332edc
Merge branch 'Development' into feature/chattutorial
nadoylemsft Mar 6, 2026
e9fc33c
Merge branch 'Development' into feature/chattutorial
paullizer Mar 25, 2026
f701c33
added tutorial to workspace and expanded chat
paullizer Mar 25, 2026
bb4b93b
added noted for tutorials
paullizer Mar 26, 2026
8f2ae3e
latest features and bug/feature submission
paullizer Mar 26, 2026
caf2e43
registration submission to receive latest release updates and calls
paullizer Mar 26, 2026
e3a7516
added images to new features
paullizer Mar 27, 2026
6b2e17d
added missing swagger
paullizer Mar 27, 2026
017f080
added swagger route check
paullizer Mar 27, 2026
a77ef5d
file no longer used
paullizer Mar 27, 2026
cd7e4e6
removed
paullizer Mar 27, 2026
d231aa3
improved docker customization for certs documentation
paullizer Mar 27, 2026
bd8d9ea
Update release_notes.md
paullizer Mar 27, 2026
26d1dba
Merge branch 'pages-documentation' into feature/chattutorial
paullizer Mar 27, 2026
9851746
developer documentation for running locally
paullizer Mar 30, 2026
7b928ed
Merge remote-tracking branch 'origin/Development' into feature/chattu…
paullizer Mar 31, 2026
4edd647
Merge branch 'Development' into feature/chattutorial
paullizer Mar 31, 2026
d01fc5d
improved exception handling for feedback and feedback logging
paullizer Mar 31, 2026
fea1a8e
Updated check_swagger_routes.py so UTF-8 read failures and SyntaxErro…
paullizer Mar 31, 2026
96aa027
Merge branch 'feature/chattutorial' of https://github.com/microsoft/s…
paullizer Mar 31, 2026
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
50 changes: 50 additions & 0 deletions .github/workflows/swagger-route-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Swagger Route Check

on:
pull_request:
branches:
- main
- Development
- staging
paths:
- 'application/single_app/**/*.py'
- 'scripts/check_swagger_routes.py'
- '.github/workflows/swagger-route-check.yml'

jobs:
swagger-route-check:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'

- name: Get changed Python files
id: changed-files
uses: tj-actions/changed-files@v46.0.1
with:
files_yaml: |
route_python:
- 'application/single_app/**/*.py'
- 'scripts/check_swagger_routes.py'

- name: Run swagger route validation
env:
CHANGED_ROUTE_FILES: ${{ steps.changed-files.outputs.route_python_all_changed_files }}
run: |
if [[ -z "$CHANGED_ROUTE_FILES" ]]; then
echo "No changed application Python files detected."
exit 0
fi

echo "Changed Python files:"
printf '%s\n' "$CHANGED_ROUTE_FILES" | tr ' ' '\n'

python scripts/check_swagger_routes.py $CHANGED_ROUTE_FILES
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ azd up
- The repo-provided `azd`, Bicep, Terraform, and Azure CLI deployers are **container-based** App Service deployments.
- For those container deployments, do **not** set an App Service Stack Settings Startup command.
- The container already starts Gunicorn through `application/single_app/Dockerfile`.
- If your environment needs private or self-signed certificate authorities for outbound TLS checks to internal services, add them during image build using [docs/how-to/docker_customization.md](docs/how-to/docker_customization.md).

## Native Python
- For **native Python App Service** deployments, deploy the `application/single_app` folder and set the App Service Startup command explicitly.
Expand Down
2 changes: 1 addition & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.240.006"
VERSION = "0.240.003"

SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production')

Expand Down
155 changes: 155 additions & 0 deletions application/single_app/functions_activity_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,23 @@
from functions_debug import debug_print
from config import cosmos_activity_logs_container


def _get_email_domain(email: str) -> str:
"""Return only the email domain for low-sensitivity audit metadata."""
normalized_email = (email or '').strip()
if '@' not in normalized_email:
return ''
return normalized_email.split('@', 1)[1].lower()


def _build_contact_metadata(name: str, email: str, organization: str) -> Dict[str, Any]:
"""Reduce contact fields to the minimum metadata needed for audit and telemetry."""
return {
'name_provided': bool((name or '').strip()),
'email_domain': _get_email_domain(email),
'organization_length': len((organization or '').strip()),
}

def log_chat_activity(
user_id: str,
conversation_id: str,
Expand Down Expand Up @@ -118,6 +135,144 @@ def log_user_activity(
debug_print(f"Error logging user activity for user {user_id}: {str(e)}")


def log_admin_feedback_email_submission(
user_id: str,
admin_email: str,
feedback_type: str,
reporter_name: str,
reporter_email: str,
organization: str,
details: str,
recipient_email: str = 'simplechat@microsoft.com'
) -> None:
"""
Log an admin-initiated feedback email draft event to the activity log.

This records that an admin prepared a bug report or feature request email
from Admin Settings.
"""

feedback_metadata = {
'feedback_type': feedback_type,
'details_length': len(details or ''),
**_build_contact_metadata(reporter_name, reporter_email, organization),
}

try:
timestamp = datetime.utcnow().isoformat()
activity_record = {
'id': str(uuid.uuid4()),
'partitionKey': user_id,
'user_id': user_id,
'timestamp': timestamp,
'activity_type': 'admin_feedback_email_submission',
'submission_channel': 'mailto',
'recipient_email': recipient_email,
'feedback_submission': feedback_metadata,
}

cosmos_activity_logs_container.create_item(body=activity_record)

log_event(
message='[Admin Feedback] Mailto draft prepared',
extra={
'user_id': user_id,
'activity_type': 'admin_feedback_email_submission',
'submission_channel': 'mailto',
'recipient_email': recipient_email,
**feedback_metadata,
},
level=logging.INFO
)
debug_print(f"[Admin Feedback] Logged feedback email submission for user {user_id}")

except Exception:
log_event(
message='[Admin Feedback] Failed to record feedback mailto draft',
extra={
'user_id': user_id,
'feedback_type': feedback_type,
'activity_type': 'admin_feedback_email_submission',
'recipient_email': recipient_email,
'details_length': len(details or ''),
**_build_contact_metadata(reporter_name, reporter_email, organization),
},
level=logging.ERROR,
exceptionTraceback=True
)
debug_print(f"[Admin Feedback] Failed to log feedback email submission for user {user_id}")


def log_admin_release_notifications_registration(
user_id: str,
admin_email: str,
registrant_name: str,
registrant_email: str,
organization: str,
registered_at: str,
updated_at: str,
recipient_email: str = 'simplechat@microsoft.com',
source: str = 'admin_settings'
) -> None:
"""
Log an admin release notifications registration email draft event.

This records that an admin prepared a registration email for release
and community call notifications from Admin Settings.
"""

registration_metadata = {
'registered': True,
'registered_at': registered_at,
'updated_at': updated_at,
**_build_contact_metadata(registrant_name, registrant_email, organization),
}

try:
activity_record = {
'id': str(uuid.uuid4()),
'partitionKey': user_id,
'user_id': user_id,
'timestamp': datetime.utcnow().isoformat(),
'activity_type': 'admin_release_notifications_registration',
'registration_channel': 'mailto',
'recipient_email': recipient_email,
'source': source,
'release_notifications_registration': registration_metadata,
}

cosmos_activity_logs_container.create_item(body=activity_record)

log_event(
message='[Admin Release Notifications] Mailto registration prepared',
extra={
'user_id': user_id,
'activity_type': 'admin_release_notifications_registration',
'registration_channel': 'mailto',
'recipient_email': recipient_email,
'source': source,
**registration_metadata,
},
level=logging.INFO
)
debug_print(f"[Admin Release Notifications] Logged registration for user {user_id}")

except Exception:
log_event(
message='[Admin Release Notifications] Failed to record registration mailto draft',
extra={
'user_id': user_id,
'activity_type': 'admin_release_notifications_registration',
'recipient_email': recipient_email,
'source': source,
**registration_metadata,
},
level=logging.ERROR,
exceptionTraceback=True
)
debug_print(f"[Admin Release Notifications] Failed to log registration for user {user_id}")


def log_web_search_consent_acceptance(
user_id: str,
admin_email: str,
Expand Down
18 changes: 14 additions & 4 deletions application/single_app/functions_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import app_settings_cache
import inspect


def is_tabular_processing_enabled(settings):
"""Tabular processing is available whenever enhanced citations is enabled."""
return bool((settings or {}).get('enable_enhanced_citations', False))
DEFAULT_VIDEO_INDEXER_ARM_API_VERSION = os.getenv(
'VIDEO_INDEXER_ARM_API_VERSION',
'2024-01-01' if AZURE_ENVIRONMENT == 'usgovernment' else '2025-04-01'
Expand Down Expand Up @@ -76,6 +80,12 @@ def get_settings(use_cosmos=False):
'favicon_version': 1,
'enable_dark_mode_default': False,
'enable_left_nav_default': True,
'release_notifications_registered': False,
'release_notifications_name': '',
'release_notifications_email': '',
'release_notifications_org': '',
'release_notifications_registered_at': '',
'release_notifications_updated_at': '',

# GPT Settings
'enable_gpt_apim': False,
Expand Down Expand Up @@ -422,6 +432,8 @@ def get_settings(use_cosmos=False):
merged = deep_merge_dicts(default_settings, settings_item)
migration_updated = apply_custom_endpoint_setting_migration(merged)

merged['enable_tabular_processing_plugin'] = is_tabular_processing_enabled(merged)

# If merging added anything new, upsert back to Cosmos so future reads remain up to date
if merged != settings_item or migration_updated:
cosmos_settings_container.upsert_item(merged)
Expand Down Expand Up @@ -450,9 +462,7 @@ def update_settings(new_settings):
existing_multi_endpoint_enabled,
settings_item.get('enable_multi_model_endpoints', False),
)
# Dependency enforcement: tabular processing requires enhanced citations
if not settings_item.get('enable_enhanced_citations', False):
settings_item['enable_tabular_processing_plugin'] = False
settings_item['enable_tabular_processing_plugin'] = is_tabular_processing_enabled(settings_item)
cosmos_settings_container.upsert_item(settings_item)
cache_updater = getattr(app_settings_cache, "update_settings_cache", None)
if callable(cache_updater):
Expand Down Expand Up @@ -888,7 +898,7 @@ def get_user_settings(user_id):
"user_id": user_id,
"previous_type": previous_type,
})

if 'personal_model_endpoints' not in doc['settings']:
doc['settings']['personal_model_endpoints'] = []

Expand Down
12 changes: 6 additions & 6 deletions application/single_app/route_backend_chats.py
Original file line number Diff line number Diff line change
Expand Up @@ -4192,15 +4192,15 @@ def result_requires_message_reload(result: Any) -> bool:
}), status_code

workspace_tabular_files = set()
if hybrid_search_enabled and settings.get('enable_tabular_processing_plugin', False) and settings.get('enable_enhanced_citations', False):
if hybrid_search_enabled and is_tabular_processing_enabled(settings):
workspace_tabular_files = collect_workspace_tabular_filenames(
combined_documents=combined_documents,
selected_document_ids=selected_document_ids,
selected_document_id=selected_document_id,
document_scope=document_scope,
)

if hybrid_search_enabled and workspace_tabular_files and settings.get('enable_tabular_processing_plugin', False) and settings.get('enable_enhanced_citations', False):
if hybrid_search_enabled and workspace_tabular_files and is_tabular_processing_enabled(settings):
tabular_source_hint = determine_tabular_source_hint(
document_scope,
active_group_id=active_group_id,
Expand Down Expand Up @@ -4563,7 +4563,7 @@ def result_requires_message_reload(result: Any) -> bool:
# Ignored roles: 'safety', 'blocked', 'system' (if they are only for augmentation/summary)

# --- Mini SK analysis for tabular files uploaded directly to chat ---
if chat_tabular_files and settings.get('enable_tabular_processing_plugin', False) and settings.get('enable_enhanced_citations', False):
if chat_tabular_files and is_tabular_processing_enabled(settings):
chat_tabular_filenames_str = ", ".join(chat_tabular_files)
chat_tabular_execution_mode = get_tabular_execution_mode(user_message)
log_event(
Expand Down Expand Up @@ -6620,15 +6620,15 @@ def publish_live_plugin_thought(thought_payload):
hybrid_citations_list.sort(key=lambda x: x.get('page_number', 0), reverse=True)

workspace_tabular_files = set()
if hybrid_search_enabled and settings.get('enable_tabular_processing_plugin', False) and settings.get('enable_enhanced_citations', False):
if hybrid_search_enabled and is_tabular_processing_enabled(settings):
workspace_tabular_files = collect_workspace_tabular_filenames(
combined_documents=combined_documents,
selected_document_ids=selected_document_ids,
selected_document_id=selected_document_id,
document_scope=document_scope,
)

if hybrid_search_enabled and workspace_tabular_files and settings.get('enable_tabular_processing_plugin', False) and settings.get('enable_enhanced_citations', False):
if hybrid_search_enabled and workspace_tabular_files and is_tabular_processing_enabled(settings):
tabular_source_hint = determine_tabular_source_hint(
document_scope,
active_group_id=active_group_id,
Expand Down Expand Up @@ -6835,7 +6835,7 @@ def publish_live_plugin_thought(thought_payload):
})

# --- Mini SK analysis for tabular files uploaded directly to chat ---
if chat_tabular_files and settings.get('enable_tabular_processing_plugin', False) and settings.get('enable_enhanced_citations', False):
if chat_tabular_files and is_tabular_processing_enabled(settings):
chat_tabular_filenames_str = ", ".join(chat_tabular_files)
chat_tabular_execution_mode = get_tabular_execution_mode(user_message)
log_event(
Expand Down
1 change: 1 addition & 0 deletions application/single_app/route_backend_control_center.py
Original file line number Diff line number Diff line change
Expand Up @@ -6076,6 +6076,7 @@ def api_admin_deny_request(approval_id):

# New standalone approvals API endpoints (accessible to all users with permissions)
@app.route('/api/approvals', methods=['GET'])
@swagger_route(security=get_auth_security())
@login_required
def api_get_approvals():
"""
Expand Down
Loading
Loading