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
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.329"
VERSION = "0.261.331"
IS_DEVELOPMENT = is_development_env_enabled()

# Opt-out for deployments where App Service Easy Auth is active but the platform
Expand Down
17 changes: 17 additions & 0 deletions application/single_app/functions_appinsights.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

from azure.monitor.opentelemetry import configure_azure_monitor
import app_settings_cache
from functions_assist_diagnostics import ASSIST_DIAGNOSTIC_TEXT, safe_assist_diagnostics

# Singleton for the logger and Azure Monitor configuration
_appinsights_logger = None
Expand Down Expand Up @@ -274,6 +275,9 @@
if diagnostic_key in LOGGER_WORKFLOW_HASH_KEYS:
if re.fullmatch(r"[0-9a-f]{64}", value):
logger_extra[normalized_key] = value
elif key in ASSIST_DIAGNOSTIC_TEXT:
if value in ASSIST_DIAGNOSTIC_TEXT[key]:
logger_extra[normalized_key] = value
elif diagnostic_key in LOGGER_WORKFLOW_CODE_KEYS:
if re.fullmatch(r"[a-z][a-z0-9_]{0,79}", value):
logger_extra[normalized_key] = value
Expand Down Expand Up @@ -383,6 +387,19 @@
return bool(settings.get('enable_debug_logging', False))


def log_assist_debug(properties):
"""Send content-free Ask AI diagnostics to both sinks only while debug logging is enabled."""
if not is_debug_enabled():
return
safe = safe_assist_diagnostics(properties)
if 'assist_surface' not in safe or 'assist_event' not in safe:
return
if safe['assist_surface'] == 'workflow':
log_event('[WORKFLOW_ASSIST] Ask AI diagnostic', extra=safe)

Check warning on line 398 in application/single_app/functions_appinsights.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.
else:
log_event('[EDITOR_ASSIST] Ask AI diagnostic', extra=safe)

Check warning on line 400 in application/single_app/functions_appinsights.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.


def _get_appinsights_debug_logger() -> Optional[logging.Logger]:
"""Return a logger that can emit DEBUG traces without widening parent logger levels."""
base_logger = get_appinsights_logger()
Expand Down
147 changes: 147 additions & 0 deletions application/single_app/functions_assist_diagnostics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# functions_assist_diagnostics.py
"""Content-free diagnostics shared by the editor and workflow Ask AI cores."""

import hashlib
import math
import time


ASSIST_DIAGNOSTIC_LIMIT = 12
ASSIST_DIAGNOSTIC_TEXT = {
'assist_surface': frozenset({'agent', 'action', 'workflow'}),

Check warning on line 11 in application/single_app/functions_assist_diagnostics.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.
'assist_scope': frozenset({'personal', 'group', 'global'}),
'assist_event': frozenset({
'model_started', 'model_finished', 'provider_finished', 'provider_failed', 'json_mode_fallback',
'validation_rejected', 'correction_started', 'request_finished',
}),
'assist_stage': frozenset({
'request', 'limit', 'base', 'projection', 'references', 'options', 'handles', 'excerpts',
'envelope', 'model', 'evaluate', 'warnings', 'response', 'parse', 'shape', 'length',
'operations', 'schema', 'apply', 'violation', 'validation',
}),
'assist_outcome': frozenset({'changed', 'explained', 'question', 'failed'}),
'assist_code': frozenset({
'invalid_request', 'instruction_invalid', 'conversation_invalid', 'draft_invalid', 'focus_invalid',

Check warning on line 24 in application/single_app/functions_assist_diagnostics.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.
'time_zone_invalid', 'tags_unsupported', 'reference_limit', 'reference_unavailable',
'group_workflow_unsupported', 'workflow_read_only', 'assistant_input_too_large', 'request_too_large',
'workflow_not_found', 'workflow_definition_conflict', 'workflow_deleted', 'assistant_busy',
'assistant_rate_limited', 'assistant_output_invalid', 'assistant_refused', 'assistant_unavailable',
'assistant_timeout', 'assistant_limit_unavailable', 'reference_check_failed', 'assistant_failed',
'agent_assistant_disabled', 'action_assistant_disabled', 'scope_forbidden', 'scope_not_found',

Check warning on line 30 in application/single_app/functions_assist_diagnostics.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.

Check warning on line 30 in application/single_app/functions_assist_diagnostics.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.
}),
'assist_finish_reason': frozenset({'stop', 'length', 'content_filter', 'tool_calls', 'function_call', 'missing', 'other'}),

Check warning on line 32 in application/single_app/functions_assist_diagnostics.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.
'assist_validation_code': frozenset({
'invalid_workflow', 'invalid_workflow_definition', 'invalid_workflow_settings', 'invalid_workflow_alerts',
'blueprint_invalid', 'unsupported_field', 'too_many_tasks', 'trigger_invalid', 'cadence_below_minimum',
'quota_exceeded', 'agent_unavailable', 'reference_unknown', 'reference_unauthorized',

Check warning on line 36 in application/single_app/functions_assist_diagnostics.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.
'file_sync_source_unavailable', 'workflow_conflict', 'workflows_unavailable', 'scope_unavailable',
'merge_unavailable', 'merge_inputs_required', 'merge_trigger_required', 'merge_runner_invalid',
'merge_options_invalid', 'merge_format_invalid',
}),
'assist_field_kind': frozenset({'text', 'textarea', 'number', 'boolean', 'select', 'choices', 'lines', 'web_sources'}),
'assist_rule': frozenset({
'empty_reply', 'invalid_json', 'invalid_json_value', 'reply_not_object',

Check warning on line 43 in application/single_app/functions_assist_diagnostics.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains dynamic execution, persistence, or system access marker. Recommendation%3A Do not execute changed lifecycle scripts or installers while this finding is unresolved.
'unexpected_reply_field', 'reply_required', 'operations_not_array', 'too_many_operations',
'operation_shape', 'output_truncated', 'create_unavailable', 'create_shape',
'new_handle_invalid', 'too_many_new_items', 'new_type_invalid', 'new_values_not_object',
'new_field_unknown', 'new_required_field', 'field_unknown', 'field_read_only',
'field_repeated', 'text_invalid', 'text_multiline', 'text_too_long', 'required_value',
'number_invalid', 'number_below_minimum', 'number_above_maximum', 'boolean_invalid',
'select_invalid', 'choices_not_array', 'choice_handle_invalid', 'too_many_items',
'lines_not_array', 'line_invalid', 'line_too_long', 'web_sources_not_array',
'web_source_shape', 'web_source_url', 'web_source_mode', 'field_kind_invalid',
'created_item_unassigned', 'operation_invalid', 'handle_invalid', 'schedule_invalid',
'protected_workflow', 'projection_invalid', 'save_validation', 'candidate_invalid',
'task_limit', 'task_required', 'task_self_reference', 'new_task_key_repeated',
'document_scope_unknown', 'reference_not_bound', 'alert_scope_invalid',
'schema_required', 'schema_additional_properties', 'schema_not', 'schema_enum',
'schema_const', 'schema_type', 'schema_pattern', 'schema_min_length', 'schema_max_length',
'schema_min_items', 'schema_max_items', 'schema_unique_items', 'schema_minimum',
'schema_maximum', 'schema_one_of', 'schema_invalid',
}),
'assist_operation': frozenset({
'set', 'create_item', 'set_name', 'set_description', 'set_trigger_manual',
'set_schedule_interval', 'set_schedule_calendar', 'set_alert_mode', 'add_alert_rule',
'update_alert_rule', 'remove_alert_rule', 'set_workflow_runner', 'set_task_runner',
'add_task', 'remove_task', 'move_task', 'set_task_name', 'set_task_instructions',

Check warning on line 66 in application/single_app/functions_assist_diagnostics.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.
'set_task_inputs', 'bind_reference', 'set_task_references', 'unbind_reference',
'set_task_document_target', 'clear_task_document_target',
}),
'assist_field': frozenset({
'/display_name', '/displayName', '/description', '/instructions', '/actions', '/model',
'/type', '/agent_type', '/reasoning_effort', '/max_completion_tokens',
'/endpoint', '/deployment', '/api_version', '/auth/type',
'/knowledge/enabled', '/knowledge/sources', '/knowledge/documents', '/knowledge/tags',
'/knowledge/web_sources', '/additionalFields/database_type', '/additionalFields/server',
'/additionalFields/database', '/additionalFields/port', '/additionalFields/driver',
'/additionalFields/read_only', '/additionalFields/max_rows', '/additionalFields/timeout',
}),
}
ASSIST_DIAGNOSTIC_NUMBERS = frozenset({
'attempt', 'provider_attempt', 'operation_index', 'reason_index', 'reason_count', 'elapsed_ms', 'remaining_ms',
'provider_duration_ms', 'provider_status', 'output_length', 'operation_count', 'model_calls',
'correction_count', 'status', 'change_count', 'new_item_count',
})
ASSIST_DIAGNOSTIC_FLAGS = frozenset({'output_present', 'json_mode', 'field_known'})


def safe_assist_diagnostics(properties):
"""Keep only closed categories, finite measurements, flags, and a submission hash."""
safe = {}
for key, value in properties.items():
if key in ASSIST_DIAGNOSTIC_TEXT:
if isinstance(value, str) and value in ASSIST_DIAGNOSTIC_TEXT[key]:
safe[key] = value
elif key in ASSIST_DIAGNOSTIC_NUMBERS:
if isinstance(value, (int, float)) and not isinstance(value, bool) and (
isinstance(value, int) or math.isfinite(value)
):
safe[key] = max(0, min(value, 1_000_000_000))
elif key in ASSIST_DIAGNOSTIC_FLAGS:
if isinstance(value, bool):
safe[key] = value
elif key == 'turn_id_hash':
if isinstance(value, str) and len(value) == 64 and all(char in '0123456789abcdef' for char in value):
safe[key] = value
return safe


class AssistDiagnostics:
"""An injected, best-effort observer; no settings, Azure, or request content dependencies."""

def __init__(self, emit=None):
self._emit = emit
self._context = {}
self._clock = time.monotonic
self._started = self._clock()
self.attempt = 0

def bind(self, surface, scope, submission_id=None, *, clock=time.monotonic):
self._clock = clock
self._started = clock()
self.attempt = 0
self._context = safe_assist_diagnostics({'assist_surface': surface, 'assist_scope': scope})
if isinstance(submission_id, str) and submission_id:
self._context['turn_id_hash'] = hashlib.sha256(submission_id.encode('utf-8')).hexdigest()

def emit(self, event, **properties):
if self._emit is None or 'assist_surface' not in self._context:
return
safe = safe_assist_diagnostics({
**properties, **self._context, 'assist_event': event, 'attempt': self.attempt,
'elapsed_ms': int((self._clock() - self._started) * 1000),
})
try:
self._emit(safe)
except Exception:
# An observer must not replace the proposal, rejection, or limiter cleanup.
pass


def assist_finish_reason(value):
"""A provider finish reason is diagnostic data, not an arbitrary log string."""
if value is None:
return 'missing'
if isinstance(value, str) and value in ASSIST_DIAGNOSTIC_TEXT['assist_finish_reason']:
return value
return 'other'
Loading
Loading