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

# Opt-out for deployments where App Service Easy Auth is active but the platform
Expand Down
1 change: 1 addition & 0 deletions application/single_app/route_frontend_chats.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
)
from functions_model_endpoint_types import resolve_model_endpoint_request_model
from functions_agent_catalog import build_accessible_agent_catalog
from functions_model_capabilities import resolve_model_reasoning_policy

Check warning on line 22 in application/single_app/route_frontend_chats.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.
from functions_ai_notice import get_ai_notice_config, is_ai_notice_dismissed
from functions_collaboration import (
assert_user_can_participate_in_collaboration_conversation,
Expand Down
138 changes: 138 additions & 0 deletions docs/explanation/fixes/V1_CHAT_PAGE_BOOTSTRAP_FIX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# V1 Chat Page Bootstrap Fix

Fixed in version: **0.261.327**

Cosmos review follow-up implemented in version: **0.261.328** (PR #1755).

Check warning on line 5 in docs/explanation/fixes/V1_CHAT_PAGE_BOOTSTRAP_FIX.md

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.

## Issue

The classic V1 chat page could return HTTP 500 when navigating to `/chats`,
before the composer or conversation list appeared. This affected nonempty

Check warning on line 10 in docs/explanation/fixes/V1_CHAT_PAGE_BOOTSTRAP_FIX.md

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 10 in docs/explanation/fixes/V1_CHAT_PAGE_BOOTSTRAP_FIX.md

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.
legacy GPT, API Management (APIM), and multi-endpoint model catalogs.

## Root Cause

A model-catalog import cleanup removed the `functions_model_capabilities`
import from `route_frontend_chats.py`. The V1-specific
`_build_initial_chat_model_selection()` helper still called
`resolve_model_reasoning_policy()` when serializing the initial model.

Check warning on line 18 in docs/explanation/fixes/V1_CHAT_PAGE_BOOTSTRAP_FIX.md

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.

The `/chats` route invokes that helper before rendering `chats.html`, outside
the catalog-loading exception handlers. A nonempty catalog therefore raised

Check warning on line 21 in docs/explanation/fixes/V1_CHAT_PAGE_BOOTSTRAP_FIX.md

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.
`NameError: name 'resolve_model_reasoning_policy' is not defined`.

Check warning on line 22 in docs/explanation/fixes/V1_CHAT_PAGE_BOOTSTRAP_FIX.md

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.
An empty catalog returned `None` before reaching the missing dependency.

Check warning on line 23 in docs/explanation/fixes/V1_CHAT_PAGE_BOOTSTRAP_FIX.md

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.

Existing initial-selection tests checked source snippets but did not execute
the helper or authenticated route, so they did not detect the regression.

## Changes

- `application/single_app/route_frontend_chats.py` explicitly imports the
reasoning resolver again. Its module uses standard-library dependencies and
the local capability catalog; the shared model-catalog extraction remains
intact.
- `functional_tests/test_chat_v1_page_bootstrap.py` imports the real web app in
fresh normal and optimized Python processes with external I/O blocked.
It executes the real selector, catalog builder, authentication decorators,
settings sanitizer, and templates.
- `application/single_app/config.py` advances the application version from
`0.261.326` to `0.261.327`.

The fix does not change preferred model selection, legacy/APIM configured-first
ordering, deployment aliases, reasoning policies, security requirements, or
V2 routes. It does not catch the missing dependency or hide it by emptying the

Check warning on line 43 in docs/explanation/fixes/V1_CHAT_PAGE_BOOTSTRAP_FIX.md

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.
model catalog.

## Validation

Before the import was restored, the new page regressions raised the missing
resolver `NameError` through the real authenticated `/chats` route in both
normal and optimized Python.

After the fix, cold and warm bootstrap paths render HTTP 200 for legacy, APIM,
multi-endpoint, and empty catalogs. Coverage also checks selected deployments,

Check warning on line 53 in docs/explanation/fixes/V1_CHAT_PAGE_BOOTSTRAP_FIX.md

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.
canonical model names, supported/unsupported/unknown reasoning metadata,
preferred model ids and selection keys, stale preferences, catalog immutability,
and JSON serialization. Anonymous requests still redirect to login, users
without the required role receive HTTP 403, and test credentials/private
endpoint URLs do not appear in rendered HTML.

The following commands ran with an isolated Python runtime for application
integration tests; the route policy scripts also passed with the default
interpreter:

| Command | Result |
| --- | --- |
| `python -m pytest -q .\functional_tests\test_chat_v1_page_bootstrap.py .\functional_tests\test_chat_model_selector_initial_multiendpoint_bootstrap.py` | 7 passed, including the final run after the version update |
| `python -m pytest -q .\functional_tests\test_model_reasoning_capability_resolution.py .\functional_tests\test_chat_retry_import_boundaries.py` | 31 passed, including 64 passing subtests |
| `python -m pytest -q .\functional_tests\test_v2_ui_spa_route.py` | 6 passed |
| `python .\functional_tests\route_tests\test_route_blueprint_policy_inventory.py` | 15 passed |
| `python .\functional_tests\route_tests\test_route_unauthenticated_policy_contract.py` | 10 passed |
| `python .\functional_tests\route_tests\test_route_policy_test_coverage.py` | 3 passed |
| `python .\functional_tests\test_docs_app_surface_coverage.py` | 7 passed; generated inventory remains current |
| `python .\functional_tests\test_docs_site_quality.py` | 6 passed |
| `python -m pytest -q .\ui_tests\test_chat_page_multi_endpoint_notice_render.py .\ui_tests\test_staging_chat_smoke.py` | 2 skipped; an authenticated deployment test environment was not configured |
| `git -c core.whitespace=blank-at-eol,blank-at-eof,space-before-tab,cr-at-eol diff --check` | Passed |

The default local interpreter had an unrelated PyOpenSSL/cryptography
compatibility failure during Semantic Kernel imports. Integration checks used
a session-local virtual environment with the repository's Flask, Werkzeug,
and Semantic Kernel versions and a compatible TLS dependency. No shared
interpreter packages or production dependency manifests were changed.

The investigation also found an unrelated stale expectation in
`test_chat_template_json_bootstrap_safety.py`: it still requires the removed
multi-endpoint migration notice, while the notice-removal regression requires
its absence. That baseline failure is outside this fix.

## Cosmos Contract Review Follow-up

PR #1755's `cosmos-contracts` check failed with three
`Changed owners requires review` diagnostics for `_build_new_chat_conversation`,
`_resolve_chat_upload_context`, and `register_route_frontend_chats.upload_file`,
plus `Stale source-only owner review` and
`New/changed owner needs owner-runtime SDK evidence` for the upload context owner.

The gate fingerprints the entire module as well as each storage function.
Restoring the resolver import changed that module fingerprint, although all
three function bodies remained unchanged. Their base-commit boundaries matched
the committed inventory. This is a PR-caused review requirement, not a transient
runner failure or a defect in Cosmos mapping compatibility.

The inventory is refreshed and each consumer is reviewed. Both write owners
discard point-write responses; the upload context owner reads mapping fields
and returns the mapping without sending it to a strict plain-dictionary sink.
It remains `sdk-compatible`, now with fresh real-owner runtime evidence instead
of an expanded source-only baseline.

`functional_tests/test_chat_upload_cosmos_response_contract.py` uses the existing
SDK-default offline container and actual installed SDK `CosmosDict` responses.
Fresh normal and optimized web imports exercise an owned personal conversation,
cross-user denial without writes, new/missing conversation creation, retained
fields and ETags, and discarded upsert responses. No new Cosmos double,
production normalization, relaxed assertion, or CI configuration change is needed.
The follow-up advances `application/single_app/config.py` to `0.261.328`.

Follow-up validation uses the session-local interpreter with the application
pin `azure-cosmos==4.9.0`. An initial full run exposed an inherited local SDK
version mismatch (`4.16.3` versus `4.9.0`); only the virtual environment was
corrected. Dependency assertions, production pins, and CI configuration remain
unchanged.

| Command (session-local Python where applicable) | Result |
| --- | --- |
| `python -m pytest -q -p test_support.cosmos_network_guard .\functional_tests\test_cosmos_document_boundaries.py .\functional_tests\test_chat_upload_cosmos_response_contract.py .\functional_tests\test_chat_v1_page_bootstrap.py .\functional_tests\test_chat_model_selector_initial_multiendpoint_bootstrap.py --disable-warnings --tb=short` | 59 passed |
| `python .\scripts\check_cosmos_response_boundaries.py --run-contracts` | 1,051 owners, 394 doubles, zero review gaps; 683 passed and 231 subtests passed |
| `python .\functional_tests\test_docs_app_surface_coverage.py` | 7 passed; inventory remains current |
| `python .\functional_tests\test_docs_site_quality.py` | 6 passed |

## Deployment Verification

The missing import is reproduced and fixed locally. The supplied deployed
HTTP 500 screenshot did not include a server traceback, so the deployed
exception has not been independently matched to this regression.

After deploying the fixed build, verify authenticated V1 `/chats` navigation
and a complete send/response cycle with the configured model. Offline route
coverage does not establish that the deployed model service can complete a
chat request.
21 changes: 11 additions & 10 deletions functional_tests/cosmos_response_boundaries.json
Original file line number Diff line number Diff line change
Expand Up @@ -1043,7 +1043,6 @@
"application/single_app/route_enhanced_citations.py::_get_authorized_chat_artifact_message": "b2d9c0fc54c4cf9545a6c46bb93b71c0e1a69d429c8ef1c05ba3fb071582bc4b",
"application/single_app/route_enhanced_citations.py::register_enhanced_citations_routes.get_enhanced_citation_tabular": "ff7e5776314ddaf82390e3ac0817fb8270307d32da2b29a7bf4f4c7ef529d61e",
"application/single_app/route_external_public_documents.py::register_route_external_public_documents.external_get_public_documents": "7a2cab5fcb36b8d80f2733e2a3fa2df33e04fbeeea7095bec72bf185778aef02",
"application/single_app/route_frontend_chats.py::_resolve_chat_upload_context": "ffacf10e9ecdb47901fa0deb6665e82bcdf06b7cb701feb024f76ab6db06d854",
"application/single_app/route_frontend_control_center.py::get_control_center_statistics": "6fd3ab6692fe40b5e63490edcdae51eae7ebeb8067996c361cde4cac14c296e6",
"application/single_app/route_frontend_conversations.py::_authorize_frontend_personal_conversation_access": "a8650a20c098eb598306d3827dce49f4b4c1501404cbd3b200d4887b1fbde552",
"application/single_app/route_frontend_conversations.py::register_route_frontend_conversations.conversations": "a1d6c7ed62f7b68cb59058ec65e83993ef1f9756d129e439a9bccdc2377ea365",
Expand Down Expand Up @@ -24387,12 +24386,14 @@
],
"queries": [],
"batches": [],
"module_sha256": "8ebafa6d3ad84d52c1f94fefab84dd67ce8c270142c20114a1ed49b11369676f"
"module_sha256": "0641427633cbce91db00156c6934580b2a5c9578f65b4816bce1fbdeaf9eb97f"
},
"classification": "response-unused",
"contract": "_build_new_chat_conversation: point response values are discarded; query rows, if present, are separate plain SDK JSON. Existing write errors remain visible.",
"contract": "Constructs and returns its own plain conversation dictionary; the SDK upsert response is discarded. Real-owner new/missing upload context tests verify persisted SDK responses retain ETags without changing the returned domain document.",
"evidence_scope": "source-review",
"tests": []
"tests": [
"functional_tests/test_chat_upload_cosmos_response_contract.py::test_chat_upload_context_real_owner_sdk_responses"
]
},
"application/single_app/route_frontend_chats.py::_resolve_chat_upload_context": {
"boundary": {
Expand All @@ -24406,13 +24407,13 @@
],
"queries": [],
"batches": [],
"module_sha256": "8ebafa6d3ad84d52c1f94fefab84dd67ce8c270142c20114a1ed49b11369676f"
"module_sha256": "0641427633cbce91db00156c6934580b2a5c9578f65b4816bce1fbdeaf9eb97f"
},
"classification": "sdk-compatible",
"contract": "_resolve_chat_upload_context: point documents use Mapping field reads/JSON encoding/metadata projections; batch status/body lists are separate, never top-level-normalized as documents. Strict domain sinks remain with their storage owners.",
"evidence_scope": "sdk-api-and-source-review",
"contract": "Point conversation responses use mapping field access for backing detection, collaboration identity, and personal ownership; the returned upload context retains the mapping and ETag. Upload consumers read/mutate fields and upsert the mapping, without an exact-dictionary sink. Real-owner runtime checks cover SDK personal reads, cross-user denial, and new/missing context creation.",
"evidence_scope": "owner-runtime",
"tests": [
"functional_tests/test_m365_cosmos_sdk_contract.py::test_real_sdk_point_documents_are_wrapped_but_nested_json_and_query_rows_are_plain"
"functional_tests/test_chat_upload_cosmos_response_contract.py::test_chat_upload_context_real_owner_sdk_responses"
]
},
"application/single_app/route_frontend_chats.py::register_route_frontend_chats.upload_file": {
Expand Down Expand Up @@ -24478,10 +24479,10 @@
}
],
"batches": [],
"module_sha256": "8ebafa6d3ad84d52c1f94fefab84dd67ce8c270142c20114a1ed49b11369676f"
"module_sha256": "0641427633cbce91db00156c6934580b2a5c9578f65b4816bce1fbdeaf9eb97f"
},
"classification": "response-unused",
"contract": "register_route_frontend_chats.upload_file: point response values are discarded; query rows, if present, are separate plain SDK JSON. Existing write errors remain visible.",
"contract": "All six message/conversation upsert responses are discarded; upload responses project constructed message/workspace fields and conversation title/id, not the SDK write response. Query rows supply thread ids and counts separately. The unchanged body reads/mutates the SDK-compatible upload context mapping.",
"evidence_scope": "source-review",
"tests": []
},
Expand Down
87 changes: 87 additions & 0 deletions functional_tests/test_chat_upload_cosmos_response_contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# test_chat_upload_cosmos_response_contract.py
"""Real chat upload context owners with the installed SDK's point responses.

Version: 0.261.328
Implemented in: 0.261.328

Fresh normal/optimized processes import the real web application offline and
exercise existing SDK-default storage doubles, without replacing the owners.
"""

import pytest

from test_orchestration_result_imports import run_probe


PROBE = r'''
import sys

sys.path[:0] = sys.argv[1:3]
from azure.cosmos._cosmos_responses import CosmosDict
from test_support.offline_bootstrap import offline_app_imports


def check(condition, message):
if not condition:
raise AssertionError(message)


with offline_app_imports() as environment:
import app as web
import route_frontend_chats as route

container = route.cosmos_conversations_container
saved = container.upsert_item({
"id": "upload-owned", "user_id": "upload-user",
"context": [], "tags": ["retained"], "strict": False,
})
check(type(saved) is CosmosDict, "Point writes must return the installed SDK wrapper.")
context = route._resolve_chat_upload_context(
" upload-owned ", "upload-user", {"userId": "upload-user"},
)
document = context["conversation_item"]
check(type(document) is CosmosDict, "The real owner should retain its compatible SDK mapping.")
check(document["_etag"] == saved["_etag"], "Point read lost its ETag.")
check(document["tags"] == ["retained"], "Point read lost domain fields.")
check(type(document["context"]) is list, "Nested JSON should remain plain.")
check(context["conversation_id"] == "upload-owned", "Partition-routed identity changed.")
check(context["response_conversation_id"] == "upload-owned", "Personal response identity changed.")
check(context["collaboration_conversation"] is None, "Personal read invented collaboration.")

before = dict(container.items)
try:
route._resolve_chat_upload_context(
"upload-owned", "other-user", {"userId": "other-user"},
)
except PermissionError:
pass
else:
raise AssertionError("SDK mapping bypassed personal conversation ownership.")
check(container.items == before, "Forbidden upload context changed storage.")

with web.app.test_request_context():
for requested_id in ("", "missing-upload"):
created = route._resolve_chat_upload_context(
requested_id, "upload-user", {"userId": "upload-user"},
)
document = created["conversation_item"]
check(type(document) is dict, "New conversation must return the constructed domain document.")
check(document["user_id"] == "upload-user", "New conversation lost its owner.")
check(created["conversation_id"] == document["id"], "Created conversation identity changed.")
check(created["response_conversation_id"] == document["id"], "Created response identity changed.")
check(created["collaboration_conversation"] is None, "Missing context invented collaboration.")
stored = container.read_item(item=document["id"], partition_key=document["id"])
check(type(stored) is CosmosDict, "Persisted new conversation is not SDK-shaped.")
check(bool(stored["_etag"]), "New conversation write lost its ETag.")
check(stored["title"] == "New Conversation", "New conversation defaults changed.")
check("_etag" not in document, "Unused write response leaked into the constructed document.")

check(not environment.network_attempts, "Upload context attempted external I/O.")
print("PASS: chat upload SDK point response contract")
'''


@pytest.mark.parametrize("optimized", [False, True])
def test_chat_upload_context_real_owner_sdk_responses(optimized):
"""Verify mapping compatibility, ownership denial, and new/missing contexts."""
run_probe(PROBE, (), optimized)
Loading
Loading