Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 80 additions & 5 deletions application/single_app/admin_settings_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -836,6 +836,41 @@
# key is what takes it out of that scan, so these five are declared rather
# than guessed at. Wording is taken from the V1 panes so both interfaces say
# the same thing.
#
# Application Insights is declared for the same reason. The scan splits
# `enable_appinsights_global_logging` into "appinsights", which matches no
# section, and "logging", which matches Debug Logging, so the switch that sends
# everything to Application Insights sat under Debug Logging. The mixed-source
# telemetry switch, which the scan had filed under Deep Research on the word
# "source", reports to the same place and is declared beside it.
"application-insights-section": [
{
"key": "enable_appinsights_global_logging",
"type": "switch",
"label": "Enable Application Insights Global Logging",
"help": (

Check warning on line 851 in application/single_app/admin_settings_fields.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.

Check warning on line 851 in application/single_app/admin_settings_fields.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.

Check warning on line 851 in application/single_app/admin_settings_fields.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains external connection or remote asset marker. Recommendation%3A Review whether changed code can send prompts, files, credentials, cookies, settings, logs, or user data to a new sink.
"Sends global logging for all agents and orchestration events to "
"Application Insights. Changing this requires an application restart "
"to take effect."
),
"default": False,
},
{
"key": "enable_mixed_source_development_telemetry",

Check warning on line 859 in application/single_app/admin_settings_fields.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
"type": "switch",
"label": "Record mixed document and spreadsheet metrics",
"help": (
"Logs aggregate counts, timings and token totals for the processing "
"behind Chat, Search, Analyze and Compare over workspace documents and "
"spreadsheets: how many sources completed, were partial, failed, were "
"skipped or were canceled, plus authorization failures and background "

Check warning on line 866 in application/single_app/admin_settings_fields.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.

Check warning on line 866 in application/single_app/admin_settings_fields.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains external connection or remote asset marker. Recommendation%3A Review whether changed code can send prompts, files, credentials, cookies, settings, logs, or user data to a new sink.
"exports. Never records prompts, content, file names, document IDs or "
"storage paths. Turn it on while checking how these requests behave; "
"each request adds several log entries."
),

Check warning on line 870 in application/single_app/admin_settings_fields.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
"default": False,

Check warning on line 871 in application/single_app/admin_settings_fields.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
},
],

Check warning on line 873 in application/single_app/admin_settings_fields.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.

Check warning on line 873 in application/single_app/admin_settings_fields.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
"health-check-section": [
{
"key": "enable_external_healthcheck",
Expand Down Expand Up @@ -2946,7 +2981,10 @@
"help": (
"Stores original files in an Azure Storage account so citations can "
"link to and preview the source document rather than only quoting "
"extracted text."
"extracted text. It also turns on spreadsheet analysis: CSV and Excel "
"files are calculated over row by row instead of searched as text, and "
"Chat, Search, Analyze and Compare can combine documents and "
"spreadsheets selected together."
),
"default": False,
},
Expand Down Expand Up @@ -2998,6 +3036,22 @@
},
],
},
{
"key": "enable_mixed_source_relevance_candidates",
"type": "switch",
"label": "Look for relevant spreadsheets when no files are selected",
"help": (
"When a chat searches workspace documents without specific files "
"selected, run a second search aimed at spreadsheet columns and sheets "
"and keep up to six spreadsheets that fit the question. A question about "
"figures can then be answered from the right file even when its text did "
"not rank among the first results. The extra search adds a little time to "
"those messages, and a matched spreadsheet that gets analyzed adds model "
"calls."
),
"default": True,
"depends_on": {"key": "enable_enhanced_citations", "equals": True},
},
{
"key": "tabular_preview_max_blob_size_mb",
"type": "number",
Expand Down Expand Up @@ -6506,12 +6560,33 @@
"administrator-editable."
),
"enable_mixed_source_chat_search": (
"Staged rollout flag for mixed-source chat and search, with no control in "
"the server-rendered admin form."
"Derived, not stored: get_settings() and update_settings() set it to "
"enable_enhanced_citations on every read and save, because mixed document and "
"spreadsheet Chat and Search needs the spreadsheet engine Enhanced Citations "
"provides. A switch here would revert on the next page load. The only override "
"is the SIMPLECHAT_DISABLE_MIXED_SOURCE environment kill switch."
),
"enable_mixed_source_conversation_continuity": (
"Staged rollout flag gated behind enable_mixed_source_chat_search, with no "
"control in the server-rendered admin form."
"Derived from enable_enhanced_citations in the same way as "
"enable_mixed_source_chat_search, which it extends with reauthorized "
"follow-up grounding."
),
"enable_cross_format_compare": (
"Derived from enable_enhanced_citations in the same way as "
"enable_mixed_source_chat_search. Comparing a document with a spreadsheet "
"needs the spreadsheet engine, and without it the request is refused rather "
"than comparing the spreadsheet as text."
),
"enable_cross_format_compare_one_to_many": (
"Derived from enable_enhanced_citations alongside enable_cross_format_compare. "
"The Comparison document limits under Document Actions already bound how many "
"Targets one request may include."
),
"enable_mixed_source_analyze_all": (
"Gates an Analyze target of every document in scope, which no chat or workflow "
"screen can request; only a hand-built API request can reach it. A switch here "
"would change nothing an administrator can see. The 0.261.266 settings upgrade "
"resets it to off once."
),
# The three tabular durable-preflight switches below are always on by design.
# normalize_tabular_parity_durable_preflight_defaults() in functions_settings.py
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 @@ -101,7 +101,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.261.265"
VERSION = "0.261.266"
IS_DEVELOPMENT = is_development_env_enabled()

# Opt-out for deployments where App Service Easy Auth is active but the platform
Expand Down
136 changes: 121 additions & 15 deletions application/single_app/functions_settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,11 +265,21 @@ def normalize_public_workspace_display_settings(settings):


# Switches that no longer exist. A stored value is removed on load so it is not echoed to
# the browser. Earlier releases already left it out of a saved run's settings fingerprint,
# so removing it does not change that fingerprint.
# the browser, and so the V2 admin page stops drawing a switch that changes nothing.
RETIRED_SETTING_KEYS = (
# Gather / Reason / Render became the only chat orchestration plan contract.
# Gather / Reason / Render became the only chat orchestration plan contract. Earlier
# releases already left it out of a saved run's settings fingerprint, so removing it
# does not change that fingerprint.
"enable_chat_orchestration_harness",
# Combined Analyze has always routed a mixed narrative and tabular selection natively
# since 0.250.071; nothing has read this switch since then. Unlike the harness toggle it
# was part of a saved run's settings fingerprint, so removing it changes that fingerprint
# once, exactly as any administrator save does.
"enable_mixed_source_analyze",
# Phase 1 shadow manifests: resolved in the background and discarded. Analyze, Compare and
# mixed-source Chat and Search all resolve their manifests for real now. Same one-time
# fingerprint change as above.
"enable_mixed_source_manifest",
)


Expand Down Expand Up @@ -561,50 +571,137 @@ def is_fact_memory_enabled(settings):
return bool((settings or {}).get('enable_fact_memory_plugin', False))


def is_mixed_source_manifest_enabled(settings):
"""Return whether Phase 1 mixed-source manifest diagnostics are enabled."""
return bool((settings or {}).get('enable_mixed_source_manifest', False))


def is_mixed_source_development_telemetry_enabled(settings):
"""Return whether aggregate-only mixed-source development telemetry is enabled."""
return bool((settings or {}).get('enable_mixed_source_development_telemetry', False))


def is_mixed_source_chat_search_enabled(settings):
"""Return whether Phase 2 mixed-source Chat and Search behavior is enabled."""
"""Return whether mixed-source Chat and Search behavior is active.

Derived from Enhanced Citations by normalize_mixed_source_derived_settings().
"""
return bool((settings or {}).get('enable_mixed_source_chat_search', False))


def is_mixed_source_conversation_continuity_enabled(settings):
"""Return whether Phase 5 reauthorized source-continuity metadata is enabled."""
"""Return whether reauthorized source-continuity metadata is active.

Derived from Enhanced Citations, and effective only with mixed-source Chat and Search.
"""
return bool(
(settings or {}).get('enable_mixed_source_chat_search', False)
and (settings or {}).get('enable_mixed_source_conversation_continuity', False)
)


def is_cross_format_compare_enabled(settings):
"""Return whether Phase 4 native mixed-source Compare behavior is enabled."""
"""Return whether native mixed narrative and tabular Compare is active.

Derived from Enhanced Citations by normalize_mixed_source_derived_settings().
"""
return bool((settings or {}).get('enable_cross_format_compare', False))


def is_cross_format_compare_one_to_many_enabled(settings):
"""Return whether the separately staged one-to-many mixed-target rollout is enabled."""
"""Return whether a mixed-format Compare may have more than one Target.

Derived from Enhanced Citations, and effective only with cross-format Compare.
"""
return bool(
(settings or {}).get('enable_cross_format_compare', False)
and (settings or {}).get('enable_cross_format_compare_one_to_many', False)
)


def is_mixed_source_relevance_candidates_enabled(settings):
"""Return whether Phase 2 relevance-mode table candidates are enabled."""
"""Return whether relevance-mode spreadsheet candidates are enabled.

An administrator choice, effective only while mixed-source Chat and Search is active.
"""
return bool(
(settings or {}).get('enable_mixed_source_chat_search', False)
and (settings or {}).get('enable_mixed_source_relevance_candidates', False)
)


# Mixed-source behaviors hand CSV and Excel files to the native tabular engine, and that engine
# only exists while Enhanced Citations is on (is_tabular_processing_enabled). With it off, those
# files are indexed row by row and answered from search chunks, while the mixed-source path would
# report them as unavailable, because a table never falls back to narrative processing. So these
# are not stored choices: they follow Enhanced Citations and are rewritten on every load and save.
MIXED_SOURCE_DERIVED_SETTING_KEYS = (
'enable_mixed_source_chat_search',
'enable_mixed_source_conversation_continuity',
'enable_cross_format_compare',
'enable_cross_format_compare_one_to_many',
)
# The only rollback path for the derived behaviors. Applied to every settings read, never stored.
MIXED_SOURCE_KILL_SWITCH_ENV = 'SIMPLECHAT_DISABLE_MIXED_SOURCE'
MIXED_SOURCE_SETTINGS_VERSION_KEY = 'mixed_source_settings_version'
MIXED_SOURCE_SETTINGS_VERSION = 1


def normalize_mixed_source_derived_settings(settings):
"""Set each derived mixed-source behavior to whether native spreadsheet processing exists.

Returns True when the settings were changed.
"""
if not isinstance(settings, dict):
return False

available = is_tabular_processing_enabled(settings)
changed = False
for key in MIXED_SOURCE_DERIVED_SETTING_KEYS:
if settings.get(key) is not available:
settings[key] = available
changed = True
return changed


def normalize_mixed_source_settings_upgrade(settings):
"""Apply the one-time mixed-source settings upgrade.

Before version 1 the V2 admin page drew these two switches in Deep Research with no
description, because nothing declared them:

- ``enable_mixed_source_relevance_candidates`` had no effect at all, because the Chat and
Search behavior it extends had no control anywhere. It now defaults on, and a stored off
was never a working choice, so it is turned on.
- ``enable_mixed_source_analyze_all`` gates an Analyze target that no chat or workflow
screen can request. It is reset to off and is no longer drawn.

The version is stored, so an administrator who turns relevance candidates off afterwards
keeps that choice. Returns True when the settings were changed.
"""
if not isinstance(settings, dict):
return False

stored_version = settings.get(MIXED_SOURCE_SETTINGS_VERSION_KEY)
if type(stored_version) is int and stored_version >= MIXED_SOURCE_SETTINGS_VERSION:
return False

settings['enable_mixed_source_relevance_candidates'] = True
settings['enable_mixed_source_analyze_all'] = False
settings[MIXED_SOURCE_SETTINGS_VERSION_KEY] = MIXED_SOURCE_SETTINGS_VERSION
return True


def _apply_mixed_source_env_kill_switch(settings_payload):
"""Force the derived mixed-source behaviors off when the emergency env kill switch is set.

They follow Enhanced Citations and have no admin switch, so this environment variable is
the only way to roll them back during an operator incident. It applies to each settings
read and is never persisted, so removing it restores the derived values immediately.
"""
if not isinstance(settings_payload, dict):
return settings_payload
if _env_flag_enabled(MIXED_SOURCE_KILL_SWITCH_ENV):
for key in MIXED_SOURCE_DERIVED_SETTING_KEYS:
settings_payload[key] = False
return settings_payload


CHAT_FILE_UPLOAD_APP_ROLE = "ChatFileUploadUser"
WORKFLOW_USER_APP_ROLE = "WorkflowUser"
DOCUMENT_INTELLIGENCE_PDF_IMAGE_EXTRACTION_MODES = {"read", "layout", "auto"}
Expand Down Expand Up @@ -1476,12 +1573,17 @@ def get_settings(use_cosmos=False, include_source=False):
'tabular_generation_systemic_failure_threshold': 0.5,
'enable_multi_agent_orchestration': False,
'enable_mixed_source_development_telemetry': False,
'enable_mixed_source_manifest': False,
# The next four follow enable_enhanced_citations on every load and save; see
# normalize_mixed_source_derived_settings(). False matches its default.
'enable_mixed_source_chat_search': False,
'enable_mixed_source_relevance_candidates': False,
'enable_mixed_source_conversation_continuity': False,
'enable_cross_format_compare': False,
'enable_cross_format_compare_one_to_many': False,
'enable_mixed_source_relevance_candidates': True,
# Backend-only Analyze target with no chat or workflow control; not drawn in admin.
'enable_mixed_source_analyze_all': False,
# One-time upgrade marker for normalize_mixed_source_settings_upgrade().
'mixed_source_settings_version': 0,

# Chat orchestration. Deliberately prefixed `chat_orchestration_*` rather than
# `orchestration_*`, because `orchestration_type` and
Expand Down Expand Up @@ -2112,6 +2214,7 @@ def get_settings(use_cosmos=False, include_source=False):
def _format_result(settings_payload, source):
if isinstance(settings_payload, dict):
settings_payload = _apply_tabular_parity_env_kill_switch(settings_payload)
settings_payload = _apply_mixed_source_env_kill_switch(settings_payload)
if include_source:
return settings_payload, source
return settings_payload
Expand Down Expand Up @@ -2165,6 +2268,8 @@ def normalize_loaded_settings(settings_item):
normalize_image_connection_api_version_settings(merged)

merged['enable_tabular_processing_plugin'] = is_tabular_processing_enabled(merged)
normalize_mixed_source_derived_settings(merged)
normalize_mixed_source_settings_upgrade(merged)

return merged

Expand Down Expand Up @@ -2379,6 +2484,7 @@ def apply_updates(settings_item):
settings_item.get('enable_multi_model_endpoints', False),
)
settings_item['enable_tabular_processing_plugin'] = is_tabular_processing_enabled(settings_item)
normalize_mixed_source_derived_settings(settings_item)
return settings_item

try:
Expand Down
31 changes: 3 additions & 28 deletions application/single_app/functions_workflow_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,6 @@
get_settings,
get_user_settings,
is_mixed_source_chat_search_enabled,
is_mixed_source_manifest_enabled,
is_cross_format_compare_enabled,
is_cross_format_compare_one_to_many_enabled,
is_tabular_processing_enabled,
Expand Down Expand Up @@ -3630,7 +3629,9 @@ def _raise_legacy_cross_format_compare_limitation(comparison_config, user_id, co
partitions = partition_source_manifest(manifest)
if partitions['narrative_sources'] and partitions['tabular_sources']:
raise ValueError(
'Mixed narrative and tabular Compare is temporarily unavailable while cross-format Compare is disabled.'
'Comparing a document with a spreadsheet requires spreadsheet processing, which is '
'available only while Enhanced Citations is enabled. Ask an administrator to enable '
'Enhanced Citations, or compare files of the same kind.'
)


Expand Down Expand Up @@ -4159,32 +4160,6 @@ def _maybe_execute_tabular_document_action(
if not user_id:
return None

if is_mixed_source_manifest_enabled(settings):
requested_source_ids, _ = _get_document_action_source_ids(action_config)
if requested_source_ids:
try:
resolve_authorized_source_manifest(
requested_source_ids,
user_id=user_id,
selection_mode='selected',
conversation_id=conversation_id,
active_group_ids=action_config.get('active_group_ids'),
active_public_workspace_ids=action_config.get('active_public_workspace_id'),
cancel_requested=cancel_requested,
request_correlation_id=request_correlation_id,
)
except MixedSourceCancellationError:
raise
except Exception:
log_event(
'[MIXED_SOURCE_MANIFEST] Workflow shadow resolution failed.',
extra={
'requested_source_count': len(requested_source_ids),
'selection_mode': 'selected',
},
level=logging.WARNING,
)

if not callable(invoke_prompt) or not is_tabular_processing_enabled(settings):
return None

Expand Down
Loading
Loading