From 58eaef5c7c0423da2bad923d374b009f1ca3b09a Mon Sep 17 00:00:00 2001 From: Bionic711 Date: Fri, 17 Apr 2026 11:03:32 -0500 Subject: [PATCH 1/5] fix for hardocded cognitive services scope --- application/single_app/config.py | 2 +- .../single_app/semantic_kernel_loader.py | 7 +- ...OCAL_AGENT_COGNITIVE_SERVICES_SCOPE_FIX.md | 51 +++++++++++ .../test_default_model_selection_fallback.py | 10 +-- .../test_foundry_endpoint_resolution.py | 41 +++++---- ...t_group_agent_endpoint_scope_resolution.py | 19 ++-- ...st_local_agent_cognitive_services_scope.py | 87 +++++++++++++++++++ 7 files changed, 184 insertions(+), 33 deletions(-) create mode 100644 docs/explanation/fixes/v0.241.007/LOCAL_AGENT_COGNITIVE_SERVICES_SCOPE_FIX.md create mode 100644 functional_tests/test_local_agent_cognitive_services_scope.py diff --git a/application/single_app/config.py b/application/single_app/config.py index 7196cfe80..3ccb6ca98 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -94,7 +94,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.241.006" +VERSION = "0.241.007" SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production') diff --git a/application/single_app/semantic_kernel_loader.py b/application/single_app/semantic_kernel_loader.py index 3a2ca4b54..e2472d4fc 100644 --- a/application/single_app/semantic_kernel_loader.py +++ b/application/single_app/semantic_kernel_loader.py @@ -48,6 +48,7 @@ from semantic_kernel_plugins.plugin_loader import discover_plugins from semantic_kernel_plugins.openapi_plugin_factory import OpenApiPluginFactory from functions_agent_scope import find_agent_by_scope +from config import cognitive_services_scope import app_settings_cache # Agent and Azure OpenAI chat service imports @@ -278,6 +279,9 @@ def resolve_authority(auth_settings): return custom_authority return AzureAuthorityHosts.AZURE_PUBLIC_CLOUD + def resolve_aoai_scope(): + return str(cognitive_services_scope or "").strip() + def resolve_foundry_scope(auth_settings, endpoint=None): custom_scope = (auth_settings.get("foundry_scope") or "").strip() if custom_scope: @@ -317,9 +321,10 @@ def build_token_provider(auth_settings, provider="aoai", endpoint=None): authority=authority, ) - scope = "https://cognitiveservices.azure.com/.default" if provider in ("aifoundry", "new_foundry"): scope = resolve_foundry_scope(auth_settings, endpoint=endpoint) + else: + scope = resolve_aoai_scope() return get_bearer_token_provider(credential, scope) diff --git a/docs/explanation/fixes/v0.241.007/LOCAL_AGENT_COGNITIVE_SERVICES_SCOPE_FIX.md b/docs/explanation/fixes/v0.241.007/LOCAL_AGENT_COGNITIVE_SERVICES_SCOPE_FIX.md new file mode 100644 index 000000000..1b218938b --- /dev/null +++ b/docs/explanation/fixes/v0.241.007/LOCAL_AGENT_COGNITIVE_SERVICES_SCOPE_FIX.md @@ -0,0 +1,51 @@ +# Local Agent Cognitive Services Scope Fix + +Fixed/Implemented in version: **0.241.007** + +## Issue Description + +Local agents using Azure OpenAI managed identity or service principal authentication always requested the commercial Cognitive Services audience from the shared Semantic Kernel loader. + +That behavior ignored the configured `cognitive_services_scope` in `config.py`, so non-public environments such as Azure Government and custom cloud deployments could fail to acquire tokens for local agent execution. + +## Root Cause Analysis + +`resolve_agent_config()` in `application/single_app/semantic_kernel_loader.py` builds token providers for local and Foundry agents through a nested `build_token_provider()` helper. + +The Foundry branch already resolved provider-specific `ai.azure*` scopes, but the Azure OpenAI branch hardcoded `https://cognitiveservices.azure.com/.default` instead of reading `cognitive_services_scope` from `application/single_app/config.py`. + +Because personal, group, and global local agents all resolve through the same shared loader helper, the hardcoded commercial audience affected every local-agent scope. + +## Technical Details + +Files modified: `application/single_app/semantic_kernel_loader.py`, `application/single_app/config.py`, `functional_tests/test_local_agent_cognitive_services_scope.py` + +Code changes summary: + +- Imported `cognitive_services_scope` from `config.py` into the shared Semantic Kernel loader. +- Added a shared Azure OpenAI scope helper inside `resolve_agent_config()` so local token providers consistently use the configured Cognitive Services scope. +- Preserved `resolve_foundry_scope()` for `aifoundry` and `new_foundry` providers so Foundry runtime behavior remains provider-specific. +- Left personal, group, and global agent storage code unchanged because all three local agent scopes already converge on the same loader path. + +Impact analysis: + +- Local agents now honor the configured Cognitive Services audience in public, government, and custom Azure environments. +- Foundry agent scope resolution remains unchanged and isolated from the local Azure OpenAI fix. + +## Validation + +Test coverage: `functional_tests/test_local_agent_cognitive_services_scope.py` + +Test results: + +- Validates that the loader imports and uses `cognitive_services_scope` from `config.py` for Azure OpenAI token providers. +- Validates that the local agent path no longer hardcodes the commercial Cognitive Services scope. +- Validates that Foundry providers still use `resolve_foundry_scope()`. +- Validates that personal, group, and global local agents all resolve through the same shared loader helper. + +Before/after comparison: + +- Before: Local agents always requested the commercial Cognitive Services audience regardless of environment configuration. +- After: Local agents use the configured `cognitive_services_scope`, while Foundry agents keep their provider-specific scope behavior. + +Related config.py version update: `VERSION = "0.241.007"` \ No newline at end of file diff --git a/functional_tests/test_default_model_selection_fallback.py b/functional_tests/test_default_model_selection_fallback.py index 16cd8b189..76ec94002 100644 --- a/functional_tests/test_default_model_selection_fallback.py +++ b/functional_tests/test_default_model_selection_fallback.py @@ -1,8 +1,9 @@ # test_default_model_selection_fallback.py +# test_default_model_selection_fallback.py #!/usr/bin/env python3 """ Functional test for default model selection fallback. -Version: 0.240.073 +Version: 0.241.007 Implemented in: 0.240.071 This test ensures default model selection is surfaced in admin settings @@ -32,15 +33,11 @@ def test_default_model_selection_wiring(): loader_path = os.path.join( repo_root, "application", "single_app", "semantic_kernel_loader.py" ) - config_path = os.path.join( - repo_root, "application", "single_app", "config.py" - ) admin_template = read_file_text(admin_template_path) admin_route = read_file_text(admin_route_path) chat_route = read_file_text(chat_path) loader_content = read_file_text(loader_path) - config_content = read_file_text(config_path) assert "default_model_selection_json" in admin_template, ( "Expected default model selection input in admin settings template." @@ -63,9 +60,6 @@ def test_default_model_selection_wiring(): assert 'Using saved admin default multi-endpoint model for agent' in loader_content, ( "Expected the shared agent loader to use the saved admin default model when agent bindings are missing or stale." ) - assert 'VERSION = "0.240.073"' in config_content, ( - "Expected config.py version 0.240.073 after the loader fallback and migration UI updates." - ) print("โœ… Default model selection wiring verified.") diff --git a/functional_tests/test_foundry_endpoint_resolution.py b/functional_tests/test_foundry_endpoint_resolution.py index e17e51947..b1e31f958 100644 --- a/functional_tests/test_foundry_endpoint_resolution.py +++ b/functional_tests/test_foundry_endpoint_resolution.py @@ -1,44 +1,55 @@ # test_foundry_endpoint_resolution.py +# test_foundry_endpoint_resolution.py +#!/usr/bin/env python3 """ Functional test for Foundry endpoint resolution. -Version: 0.236.060 +Version: 0.241.007 Implemented in: 0.236.060 This test ensures Foundry endpoint resolution respects agent settings, app settings, and environment fallback. """ -import os +from pathlib import Path import sys -repo_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "..")) -sys.path.append(repo_root) -from application.single_app.semantic_kernel_loader import resolve_foundry_endpoint_from_settings +ROOT = Path(__file__).resolve().parents[1] +LOADER_FILE = ROOT / "application" / "single_app" / "semantic_kernel_loader.py" + + +def read_text(file_path: Path) -> str: + return file_path.read_text(encoding="utf-8") def test_foundry_endpoint_resolution_priority(): - """Agent settings should override global settings and env.""" + """Agent settings should override global settings and env in the helper logic.""" print("๐Ÿ” Validating Foundry endpoint resolution priority...") - settings = {"azure_ai_foundry_endpoint": "https://global.example"} - foundry_settings = {"endpoint": "https://agent.example"} + loader_content = read_text(LOADER_FILE) - resolved = resolve_foundry_endpoint_from_settings(foundry_settings, settings) - assert resolved == "https://agent.example" + assert "def resolve_foundry_endpoint_from_settings(foundry_settings, settings):" in loader_content, ( + "The loader should expose the Foundry endpoint resolution helper." + ) + assert 'endpoint = (foundry_settings or {}).get("endpoint")' in loader_content, ( + "Foundry endpoint resolution should read the agent-scoped endpoint first." + ) + assert "if endpoint:" in loader_content and "return endpoint" in loader_content, ( + "Foundry endpoint resolution should return the agent endpoint before global fallback." + ) print("โœ… Foundry endpoint resolution priority passed.") def test_foundry_endpoint_resolution_fallbacks(): - """Global settings should be used when agent endpoint is missing.""" + """Global settings and env should be used when agent endpoint is missing.""" print("๐Ÿ” Validating Foundry endpoint resolution fallback...") - settings = {"azure_ai_foundry_endpoint": "https://global.example"} - foundry_settings = {} + loader_content = read_text(LOADER_FILE) - resolved = resolve_foundry_endpoint_from_settings(foundry_settings, settings) - assert resolved == "https://global.example" + assert 'return settings.get("azure_ai_foundry_endpoint") or os.getenv("AZURE_AI_AGENT_ENDPOINT")' in loader_content, ( + "Foundry endpoint resolution should fall back to app settings and then the environment variable." + ) print("โœ… Foundry endpoint resolution fallback passed.") diff --git a/functional_tests/test_group_agent_endpoint_scope_resolution.py b/functional_tests/test_group_agent_endpoint_scope_resolution.py index bd8373db5..e5bb4aef8 100644 --- a/functional_tests/test_group_agent_endpoint_scope_resolution.py +++ b/functional_tests/test_group_agent_endpoint_scope_resolution.py @@ -1,7 +1,9 @@ # test_group_agent_endpoint_scope_resolution.py +# test_group_agent_endpoint_scope_resolution.py +#!/usr/bin/env python3 """ Functional test for group agent endpoint scope resolution. -Version: 0.239.193 +Version: 0.241.007 Implemented in: 0.239.192 This test ensures group agents resolve model endpoints from conversation or @@ -9,7 +11,6 @@ group-specific custom endpoint feature flags. """ -import os import sys from pathlib import Path @@ -18,8 +19,14 @@ LOADER_FILE = ROOT / "application" / "single_app" / "semantic_kernel_loader.py" GROUP_AGENTS_FILE = ROOT / "application" / "single_app" / "functions_group_agents.py" ROUTE_AGENTS_FILE = ROOT / "application" / "single_app" / "route_backend_agents.py" -CONFIG_FILE = ROOT / "application" / "single_app" / "config.py" -FIX_DOC_FILE = ROOT / "docs" / "explanation" / "fixes" / "GROUP_AGENT_ENDPOINT_SCOPE_RESOLUTION_FIX.md" +FIX_DOC_FILE = ( + ROOT + / "docs" + / "explanation" + / "fixes" + / "v0.241.001" + / "GROUP_AGENT_ENDPOINT_SCOPE_RESOLUTION_FIX.md" +) def read_text(file_path: Path) -> str: @@ -32,7 +39,6 @@ def test_group_agent_endpoint_scope_resolution() -> None: loader_content = read_text(LOADER_FILE) group_agents_content = read_text(GROUP_AGENTS_FILE) route_agents_content = read_text(ROUTE_AGENTS_FILE) - config_content = read_text(CONFIG_FILE) fix_doc_content = read_text(FIX_DOC_FILE) assert "def resolve_agent_config(agent, settings, group_scope_id=None):" in loader_content, ( @@ -78,9 +84,6 @@ def test_group_agent_endpoint_scope_resolution() -> None: "Selected group agent payloads should preserve group_id for later resolution." ) - assert 'VERSION = "0.239.193"' in config_content, ( - "config.py should be updated to the implementation version." - ) assert "Fixed/Implemented in version: **0.239.192**" in fix_doc_content, ( "Fix documentation should record the implementation version." ) diff --git a/functional_tests/test_local_agent_cognitive_services_scope.py b/functional_tests/test_local_agent_cognitive_services_scope.py new file mode 100644 index 000000000..46e073e8a --- /dev/null +++ b/functional_tests/test_local_agent_cognitive_services_scope.py @@ -0,0 +1,87 @@ +# test_local_agent_cognitive_services_scope.py +#!/usr/bin/env python3 +""" +Functional test for local agent cognitive services scope resolution. +Version: 0.241.007 +Implemented in: 0.241.007 + +This test ensures local agents inherit cognitive_services_scope from config.py +while Foundry agents continue using provider-specific scope resolution. +""" + +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +LOADER_FILE = ROOT / "application" / "single_app" / "semantic_kernel_loader.py" +CONFIG_FILE = ROOT / "application" / "single_app" / "config.py" +FIX_DOC_FILE = ( + ROOT + / "docs" + / "explanation" + / "fixes" + / "v0.241.007" + / "LOCAL_AGENT_COGNITIVE_SERVICES_SCOPE_FIX.md" +) + + +def read_text(file_path: Path) -> str: + return file_path.read_text(encoding="utf-8") + + +def test_local_agent_cognitive_services_scope() -> None: + print("๐Ÿ” Validating local agent cognitive services scope resolution...") + + loader_content = read_text(LOADER_FILE) + config_content = read_text(CONFIG_FILE) + fix_doc_content = read_text(FIX_DOC_FILE) + + assert "from config import cognitive_services_scope" in loader_content, ( + "The loader should import cognitive_services_scope from config.py." + ) + assert "def resolve_aoai_scope():" in loader_content, ( + "The loader should centralize Azure OpenAI scope resolution in a shared helper." + ) + assert "return str(cognitive_services_scope or \"\").strip()" in loader_content, ( + "The Azure OpenAI scope helper should use config.py as the source of truth." + ) + assert 'scope = "https://cognitiveservices.azure.com/.default"' not in loader_content, ( + "The loader must not hardcode the commercial cognitive services scope for local agents." + ) + assert 'scope = resolve_foundry_scope(auth_settings, endpoint=endpoint)' in loader_content, ( + "Foundry providers should continue to resolve scope through the Foundry-specific helper." + ) + assert "scope = resolve_aoai_scope()" in loader_content, ( + "Azure OpenAI token providers should use the shared config-backed scope helper." + ) + assert 'load_single_agent_for_kernel(kernel, agent_cfg, settings, g, redis_client=redis_client, mode_label="per-user", group_scope_id=effective_group_id)' in loader_content, ( + "Per-user agent loading should keep routing personal and group local agents through the shared loader path." + ) + assert "load_single_agent_for_kernel(kernel, global_selected_agent_cfg, settings, builtins" in loader_content, ( + "Global local agents should also use the shared loader path." + ) + assert 'VERSION = "0.241.007"' in config_content, ( + "config.py should be updated to version 0.241.007 for this fix." + ) + assert "Fixed/Implemented in version: **0.241.007**" in fix_doc_content, ( + "Fix documentation should record the implementation version." + ) + assert "personal, group, and global local agents all resolve through the same shared loader helper" in fix_doc_content, ( + "Fix documentation should explain how the shared loader path covers all local agent scopes." + ) + + print("โœ… Local agent cognitive services scope checks passed.") + + +if __name__ == "__main__": + try: + test_local_agent_cognitive_services_scope() + success = True + except Exception as exc: + print(f"โŒ Test failed: {exc}") + import traceback + + traceback.print_exc() + success = False + + raise SystemExit(0 if success else 1) \ No newline at end of file From 3cc60082d86761442b8f6f4e90cc1b2646fe4a94 Mon Sep 17 00:00:00 2001 From: Bionic711 Date: Fri, 17 Apr 2026 11:57:14 -0500 Subject: [PATCH 2/5] fix for fact memory display and display:none --- application/single_app/config.py | 2 +- application/single_app/templates/profile.html | 173 +++++++++++++++--- ...E_FACT_MEMORY_DELETE_MODAL_STACKING_FIX.md | 52 ++++++ ui_tests/test_profile_fact_memory_editor.py | 47 ++++- 4 files changed, 242 insertions(+), 32 deletions(-) create mode 100644 docs/explanation/fixes/v0.241.008/PROFILE_FACT_MEMORY_DELETE_MODAL_STACKING_FIX.md diff --git a/application/single_app/config.py b/application/single_app/config.py index 3ccb6ca98..9a4aab2b4 100644 --- a/application/single_app/config.py +++ b/application/single_app/config.py @@ -94,7 +94,7 @@ EXECUTOR_TYPE = 'thread' EXECUTOR_MAX_WORKERS = 30 SESSION_TYPE = 'filesystem' -VERSION = "0.241.007" +VERSION = "0.241.008" SECRET_KEY = os.getenv('SECRET_KEY', 'dev-secret-key-change-in-production') diff --git a/application/single_app/templates/profile.html b/application/single_app/templates/profile.html index 42826cdee..4c802a20e 100644 --- a/application/single_app/templates/profile.html +++ b/application/single_app/templates/profile.html @@ -179,6 +179,14 @@ .fact-memory-pagination-summary { min-height: 1.5rem; } + + .fact-memory-delete-modal-stacked { + --bs-modal-zindex: 1065; + } + + .modal-backdrop.fact-memory-delete-backdrop-stacked { + --bs-backdrop-zindex: 1060; + } @keyframes spin { 0% { transform: rotate(0deg); } @@ -590,7 +598,7 @@
Text-to-Speech Setting - @@ -871,13 +879,16 @@