Skip to content

Latest commit

 

History

History
277 lines (221 loc) · 14.8 KB

python-development.md

File metadata and controls

277 lines (221 loc) · 14.8 KB

Python development

This guide covers the shipped UI package and contributor workflow. For platform setup, deployment, identity permissions, and supported component versions, use the central product documentation. The umbrella release manifest, not this guide, pins the compatible runtime set.

Set up a development environment

Clone this repository and run the following commands from its root. Use Python 3.12 and a virtual environment:

python -m venv .venv

Activate it in PowerShell:

.\.venv\Scripts\Activate.ps1

Or activate it in Bash:

source .venv/bin/activate

Install runtime dependencies, contributor tools, and the editable package:

python -m pip install -r requirements.txt
python -m pip install -r requirements-quality.txt
python -m pip install --no-deps -e .

requirements.txt is the runtime dependency list. The setuptools distribution installs canonical code and supported legacy modules without a second runtime dependency list. Deployment uses python -m pip install --no-deps ..

Start against a configured platform

This is not an offline chat demo. Set APP_CONFIG_ENDPOINT in the shell to the provisioned Azure App Configuration endpoint. The provider tries managed identity, then the local Azure CLI credential, and resolves Key Vault references. The identity needs access to the configuration and referenced secrets. Follow the central configuration and delegated authentication guidance before starting:

uvicorn main:app --host 0.0.0.0 --port 8080

Docker uses the same command from /app. A missing or disconnected App Configuration provider produces an HTTP 503 not-ready app. Setting individual environment variables does not make that app ready. Environment overrides exist only where a specific settings loader reads them.

CHAT_BACKEND defaults to hosted_agent when absent or blank. orchestrator is the explicit Container Apps alternative. Unknown values fail, and hosted failures do not trigger fallback. Hosted startup validates the endpoint, data-plane scope, timeout, and authentication settings. The default HOSTED_AGENT_AUTH_MODE=user_delegated also requires the three OAUTH_AZURE_AD_* confidential-client settings for OBO. Do not switch to service_identity to work around a failed user login or consent problem.

Ownership and imports

Runtime code lives under src/gpt_rag_ui/:

Area Responsibility
bootstrap.py ASGI composition and startup
api/ Routes, callback registration, Chainlit history adapter, and session bridge
services/ Chat, citations, history, continuity, conversation/download policy, and panel metadata
auth/ Identity, session, token, and transport security
clients/ Orchestrator, ingestion, hosted runtime, Conversations, Blob, and Cosmos transports
config/ App Configuration provider/cache, settings, and asset-root resolution
telemetry/ Instrumentation and boundary diagnostics
util/ Constants

Root modules such as main.py, app.py, and datalayer.py, plus connectors/, are supported compatibility adapters. Public exports forward to canonical objects. Patch canonical owners in tests, not private adapter globals. .quality/migration.json records the frozen legacy inventory. .quality/policy.json records current modules and adapters.

api.history.OrchestratorDataLayer adapts Chainlit callbacks. get_data_layer() creates a fresh adapter. services.history.HistoryService owns history/user operations and the shared user cache. The API owns ambient session selection and consume-once request metadata. Services receive explicit HistoryOperationContext.

Registration in api.callbacks, api.history, and api.oauth is idempotent. The classic history data layer is registered only for CHAT_BACKEND=orchestrator. Hosted chat retains OAuth, chat, and feedback callbacks but does not register classic orchestrator/Dapr history. Active-session context is not durable history after reload or a new chat.

Backend contracts for contributors

The UI invokes the orchestrator agent through POST /invocations?api-version=v1 on the configured hosted base URL. The request contains messages as ordered text user and assistant objects, plus metadata. The default path adds conversation_id only when the hosted runtime has issued one. It does not send a top-level conversation or previous_response_id.

The SSE response.created frame must contain a response object with conversation.id or the legacy conversation_id. If both are present they must agree. Missing, malformed, or conflicting identifiers fail explicitly. The default path resends the available Chainlit history without a local item or token cap. It adds the current ask if it is not already the final user message. A Chainlit thread ID is not a hosted conversation ID.

The default hosted bearer is obtained through OBO from the current user's access token. A missing token or failed exchange stops the hosted invocation. OBO itself can contact Entra. There is no fallback to service identity. HOSTED_AGENT_AUTH_MODE=service_identity is an explicit exception that makes the UI service the caller, not the user. Login, OBO consent, managed conversation ownership, and retrieval permission enforcement are separate boundaries. None alone proves per-user document authorization. In the orchestrator's strict retrieval mode (ALLOW_ANONYMOUS=false), missing or failed required OBO and retrieval failures remain errors, not anonymous fallbacks. Disabling ingestion does not relax that policy.

Optional continuity

HOSTED_CONTINUITY_ENABLED is false by default. When enabled, the UI owns the managed Conversations store and invokes the hosted runtime without a conversation reference. HOSTED_CONVERSATION_OWNER_BINDING defaults to delegated when enabled. That mode uses a service bearer with a server-derived x-ms-user-identity header for store calls. Explicitly selecting capability instead uses a signed owner handle and user OBO for those calls. Neither changes the hosted invocation's authentication mode.

The settings below are ignored while continuity is disabled. When enabled, nonblank environment values override provider values. Invalid required settings fail startup rather than selecting another binding mode.

Setting Default Requirements when continuity is enabled
HOSTED_CONVERSATION_OWNER_BINDING_VALIDATED false Boolean. Required as true together with the protocol version in delegated mode. Set it only after validating the UI service identity's agent-scoped Microsoft.CognitiveServices/accounts/AIServices/agents/endpoints/UserIdentityImpersonation/action grant. Not required as true in capability mode.
HOSTED_AGENT_PROTOCOL_VERSION Unset Required together with the validation flag in delegated mode. Set the deployed hosted agent's responses protocol version as three numeric components, MAJOR.MINOR.PATCH, with a value >= 2.0.0. Ignored in capability mode.
HOSTED_CONVERSATION_CAPABILITY_KEY Unset Required together with the key ID in capability mode, with at least 32 characters after trimming. Supply it through a Key Vault reference, never a literal secret. Not required in delegated mode.
HOSTED_CONVERSATION_CAPABILITY_KEY_ID Unset Nonblank key identifier required together with the signing key in capability mode. Identifies the current key so retired keys can be rejected after rotation. Not required in delegated mode.
HOSTED_CONVERSATION_CAPABILITY_TTL_SECONDS 900 Integer from 60 through 86400, inclusive. Sets the capability handle lifetime in seconds. The loader validates this range in both binding modes.
HOSTED_CONVERSATION_STORE_BASE_URL Unset Required in both modes. Absolute HTTPS URL for the Conversations store. HTTP is accepted only for localhost, 127.0.0.1, or ::1. Trailing slashes are removed. There is no hosted base URL fallback.
HOSTED_CONVERSATION_STORE_RESOURCE_SCOPE Falls back to HOSTED_AGENT_RESOURCE_SCOPE Required in both modes after fallback, with no built-in scope value. Must end in /.default and must not contain management.azure.com, ignoring case for that ARM check.

Booleans accept true/false, 1/0, yes/no, y/n, or on/off, ignoring case. A supplied validation flag must be valid in either binding mode.

The store client sends {} to POST /conversations and reads id from the response. GET /conversations/{id}/items expects a data array. POST /conversations/{id}/items sends an items array. These store operations are separate from the hosted invocation envelope above.

The shipped store client reads one page with order=asc and a request limit clamped to 1..100. It does not follow pagination, so long conversations can omit later items. HOSTED_HISTORY_MAX_ITEMS defaults to 40 and accepts 1..500, but values above 100 do not increase this one-page read. HOSTED_HISTORY_MAX_TOKENS defaults to 8000 and accepts 256..200000. drop_oldest first limits items within the returned page, then drops the oldest remaining items to meet an estimated token budget. The estimate is one token per four text characters, rounded up, with a minimum of one. The final remaining item is never dropped even if it exceeds the budget. The current ask is added afterward, so these are not hard request-token limits or a guarantee of the latest complete history.

A rejected presented owner handle never starts a replacement conversation. History-read or append failures are explicit failures. A failed append must not be presented as a durably saved turn. Locks and the bounded idempotency cache are process-local, not distributed. Delegated ownership enforcement on the deployed Conversations/items surface still needs independent live validation. Do not treat mocked store tests as proof of platform authorization.

Optional ingestion

DEPLOY_INGESTION_CONTAINER_APP is read through the App Configuration provider, not a separate environment-only flag. Missing preserves legacy upload eligibility. false hides standalone and Entra Copilot controls and rejects attachments in the handler and ingestion client. The client checks the flag before URL/Dapr resolution, payload construction, file reads, or HTTP client creation. Invalid boolean values raise a configuration error.

true retains authentication, anonymous-Copilot denial, file validation, and conversation checks. Hosted uploads require an already issued hosted conversation ID. There is no native conversation upload replacement. The flag does not change text chat, OBO, or retrieval authorization.

Installed resources

The installed artifact is code plus staged assets, not a self-contained wheel. Keep .chainlit/, public/, chainlit.config.yaml, chainlit.md, and VERSION together in a writable application root. Set CHAINLIT_APP_ROOT for an explicitly staged root, or start from that directory. A source-checkout main.py passes its adjacent asset root.

The root is resolved before importing Chainlit and is shared by VERSION, configuration synchronization, public assets, and upload paths. Do not write to site-packages or add sys.path/sys.modules compatibility workarounds. Missing VERSION is optional. Missing .chainlit/config.toml retains a warning.

Contributor validation

The complete local behavioral suite is:

python -m unittest discover -s tests -v

Focused package checks are:

python -m pip wheel --no-deps --wheel-dir .artifacts/wheel .
python -m unittest discover -s tests -p test_module_compatibility.py -v
python -m unittest discover -s tests -p test_installed_package.py -v

Installed-package acceptance builds a wheel, creates a clean non-editable environment, installs runtime requirements, runs pip check, and verifies canonical and legacy import origins outside the checkout. It copies behavioral tests, not runtime sources. This can install dependencies and is not evidence of a Linux image or live Azure integration.

Protected quality checks

Replace <protected-base-sha> below with the immutable base SHA for the actual target branch. Do not substitute the candidate head to avoid findings.

python .github/scripts/check-quality.py --check all --base-ref <protected-base-sha> --report .artifacts/quality.json
python -m unittest discover -s tests -p test_quality_policy.py -v

--check accepts all, lint, typing, architecture, exceptions, and policy. Exit 0 means passed, 1 means violations, and 2 means invalid inputs or incomplete execution. Checks do not rewrite source, policy, or baselines and do not connect to Azure. Tool pins are in requirements-quality.txt.

Exception and dynamic-import validation also requires a same-run execution receipt. In PowerShell, replace the base placeholder in both commands:

$env:QUALITY_RUN_ID = [guid]::NewGuid().ToString()
python .github\scripts\run-unittest.py --base-ref <protected-base-sha> --report .artifacts\unittest.json
python .github\scripts\check-quality.py --check all --base-ref <protected-base-sha> --test-evidence .artifacts\unittest.json --report .artifacts\quality.json

Set QUALITY_RUN_ID once per local run in other shells too. The receipt binds exact test methods, source fingerprints, base/head, and run context. Skipped, expected-failure, stale, or failed evidence cannot validate a site. Local --pattern supports focused runs. CI aggregation requires full test_*.py discovery.

Active exception records are eligible for technical validation, not blanket approval. Each needs exact-source passing evidence, an unexpired date, and a valid review stage. Proposed or retired records cannot be consumed. Registry edits alone do not block, and an independent human-review field is not an execution requirement. Protected checker, tool, workflow, schema, typing, import, and suppression rules still apply. Do not grow baselines, disable checks, or fabricate reports to make a change pass.

CI selects the protected-base evaluator and tool pins in a separate environment that does not install candidate code. The quality-gate job requires actual unit-test, quality-matrix, and ephemeral Linux container-test results plus fresh bound reports and the unittest receipt. The container helper runs without networking and does not publish or deploy an image. A local command or workflow file does not prove branch protection or required merge checks. Those settings remain separate repository controls.

For engineering-document and agent-asset validation, see the engineering framework. For embedding security, see Chainlit Copilot embedding.