Skip to content

Run locally

Run an application component on your workstation while using an existing development platform. This is not an offline chat demo and does not provision Azure resources. Local servers still need network access, configured models, storage, and the permissions used by their selected features.

For code changes, continue with Customize components and Tests & quality checks. For deployment, use Deploy full stack, not a component's deployment hooks.

Before you start

  • Obtain a compatible development platform and its Azure App Configuration endpoint. App Configuration holds runtime settings and references to secrets in Key Vault. Use Application settings to check effective values and labels.
  • Use Python 3.12 and Azure CLI. Sign in to the intended tenant and subscription. CLI sign-in supplies a local identity, not automatic permission to read configuration, secrets, models, Search, Blob Storage, or Cosmos DB.
  • Obtain data-plane role assignments for the services you will use. A data-plane permission authorizes access to service data, rather than resource creation. Private endpoints also require connected networking and DNS.
  • Clone only the components you need from orchestrator, ingestion, and UI. To reproduce the application, use the compatible source revisions in the platform's manifest at your platform release. Repository main can contain newer patches. For contributions, use the repository's target branch and current checks.

The credential providers try managed identity, then Azure CLI. AZURE_CLIENT_ID selects a user-assigned managed identity when applicable. Do not copy an Azure workload's credentials to a workstation. Store secrets in Key Vault and never commit local environment files or tokens.

The component infra/main.bicep files are empty resource-group entrypoints. Their azure.yaml files and deployment scripts assume an existing platform. azd deploy can build, push, and replace running images. It is not a local startup or validation command.

Prepare each repository

Run this setup separately from each component repository root. Keep separate virtual environments because the components have different dependency sets. The examples use an activated environment.

python -m venv .venv

In PowerShell:

.\.venv\Scripts\Activate.ps1

In Bash:

source .venv/bin/activate

Then install that repository's runtime requirements:

python -m pip install -r requirements.txt
az login

If PowerShell activation is restricted, use .\.venv\Scripts\python.exe in place of python rather than changing execution policy.

Set the endpoint in each server's shell. For PowerShell:

$env:APP_CONFIG_ENDPOINT = "https://<development-store>.azconfig.io"

For Bash:

export APP_CONFIG_ENDPOINT="https://<development-store>.azconfig.io"

Do not assume that setting an environment variable overrides a remote setting:

Component Local override behavior
Orchestrator Runtime environment overrides require allow_environment_variables=true. Its parser also accepts 1 and yes. The classic entrypoint loads .env, not .env.sample. The hosted entrypoint expects process environment configuration.
Ingestion Leave allow_environment_variables unset or empty to disable runtime environment overrides. Any nonempty string, including false, enables them. Use true for an intentional opt-in. Do not rely on an automatically loaded .env.
UI There is no shared allow_environment_variables switch. Specific loaders, such as hosted backend and continuity settings, support environment overrides. Ordinary provider keys still come from App Configuration. An environment-only provider does not make the application ready.

Bootstrap values such as APP_CONFIG_ENDPOINT are direct process inputs. See Application settings for provider precedence, required feature settings, and restart behavior.

Orchestrator

The orchestrator chooses an agent strategy, calls models and tools, and streams answers. In the orchestrator root, after the setup above:

python -m uvicorn main:app --app-dir src --host 127.0.0.1 --port 9000

Classic startup needs the shared strategy settings AI_FOUNDRY_PROJECT_ENDPOINT, AI_FOUNDRY_ACCOUNT_ENDPOINT, and CHAT_DEPLOYMENT_NAME. Conversation storage also needs DATABASE_ACCOUNT_NAME and DATABASE_NAME. CONVERSATIONS_DATABASE_CONTAINER defaults to conversations. Retrieval and optional integrations need their own resources and permissions. The sample environment file is illustrative, not a complete platform.

SQL and Fabric connectors use pyodbc, Python's Open Database Connectivity adapter. For SQL execution, install Microsoft ODBC Driver 18 for SQL Server and the native ODBC dependencies for your OS, as illustrated by the repository's Dockerfile. On Linux, importing pyodbc may also require the native ODBC loader even when you are not executing a SQL strategy. Installing Python requirements alone does not install a database driver. Use a least-privilege read-only database identity for generated SQL.

Call POST /orchestrator using the classic API contract. Service credentials and the user's bearer token are distinct requirements. ALLOW_ANONYMOUS does not bypass service authentication. Do not use DISABLE_AUTH=true on a shared or publicly reachable service.

For the classic mcp strategy, the client uses http://localhost:5000 outside Azure detection, even if another MCP_APP_ENDPOINT was configured. Run your local MCP test server there. Detection checks CONTAINER_APP_NAME or WEBSITE_INSTANCE_ID, not whether you ran az login. Use Tools & MCP for transport and endpoint configuration.

Hosted entrypoint

To develop the hosted protocol adapter, start this instead of the classic entrypoint, from the orchestrator root:

python -m uvicorn api.hosted_entrypoint:app --app-dir src --host 127.0.0.1 --port 8001

This selects another ASGI application, the Python web-server entrypoint. It does not register an agent with Foundry or emulate Foundry ingress and identity passthrough. Only maf_lite, maf_agent_service, single_agent_rag, and mcp are hosted-eligible. Hosted execution uses file-backed prompts and caller-supplied text history, not classic Cosmos state. mcp requires a valid platform-injected call ID and is not a bare local HTTP substitute for Toolbox. See Hosted agents and the hosted API contracts.

Ingestion

The ingestion service processes documents and can change Search indexes and Blob control data.

Starting ingestion can write and delete data

Use an isolated development environment. RUN_JOBS_ON_STARTUP defaults to true. Setting it to false skips immediate ingestion and purge runs, not scheduled runs or immediate log cleanup. Blob indexing defaults to 0 * * * *, Blob purge to 10 * * * *, and log cleanup to 0 * * * *. Blank Blob schedules receive defaults rather than disabling those jobs. Review Jobs & schedules before startup.

In the ingestion root, after reviewing the effective schedules and completing the common setup:

python -m uvicorn main:app --host 127.0.0.1 --port 8080

Configure Search, storage, embedding models, and the analysis service required by the selected pipeline. Content Understanding is the default analysis path when USE_DOCUMENT_INTELLIGENCE=false. Multimodal processing is a separate opt-in. There is no universal requirement to configure every source or SQL database for every pipeline.

GET /healthz is a liveness probe. GET /readyz checks configuration initialization and can return 503. Neither proves that indexing succeeded. Use Ingestion APIs for request envelopes and API-key requirements, not an arbitrary multipart upload.

Chat UI

The UI is a Chainlit Python application, not either service's React dashboard. In the UI root, after the common setup, install its package and start:

python -m pip install --no-deps -e .
python -m uvicorn main:app --host 127.0.0.1 --port 8000

Port 8000 avoids the ingestion server on 8080. Configure the local public origin and sign-in redirect deliberately using Sign-in & access. Features requiring a configured HTTPS origin, including protected-source recovery, still need that origin and an appropriate local HTTPS proxy. Changing the listening port alone does not reconfigure Entra or callback URLs.

CHAT_BACKEND defaults to hosted_agent. Use orchestrator explicitly for the classic backend, with its endpoint and service credentials configured. Hosted failures do not fall back to classic execution. The default hosted user_delegated authentication uses an on-behalf-of exchange, where the UI exchanges a user's API token for a downstream token. It needs the confidential-client settings and consent described in Sign-in & access. Do not switch to service_identity to work around login or consent failures.

Keep .chainlit/, public/, chainlit.config.yaml, chainlit.md, and VERSION together in a writable application root. A source-checkout main.py uses its adjacent assets. For an installed package staged elsewhere, set CHAINLIT_APP_ROOT to that root or start there. A wheel contains code, not a self-contained deployed UI. Do not write assets into site-packages. Missing VERSION is optional and a missing .chainlit/config.toml produces a warning.

A missing or disconnected App Configuration provider produces an HTTP 503 not-ready app. Uploads additionally require the optional ingestion application and an authorized conversation. See Conversations & files.

Operator dashboard frontend

The orchestrator and ingestion repositories each contain a React/TypeScript dashboard under frontend/. Vite is their local frontend server and build tool. It proxies API requests to an independently running backend.

Repository Node used by current CI Install from frontend/ Development proxy Build output
Orchestrator 20 npm install --no-audit --no-fund /api to http://localhost:9000 src/static/
Ingestion 22 npm ci /api to http://localhost:8080 static/

After the repository-specific install, run npm run dev from frontend/. Start the corresponding Python backend in another shell using the commands above. The proxy neither starts the backend nor bypasses authorization. The ingestion Dockerfile's Node 20 build stage is not the maintained local/CI Node 22 choice. Do not bypass peer-dependency checks.

npm run build writes the static bundle consumed by FastAPI at /dashboard when the administrative surface is mounted. A bundle does not enable that surface. Orchestrator requires ENABLE_DASHBOARD=true. Ingestion mounting depends on hosted/panel mode. Both can expose a development admin gate when Entra is not configured. Keep them private and follow Administration for production setup.

Verify the intended boundary

Check the selected backend, effective configuration, and logs before exercising a turn or upload. A listening port, successful probe, or local test is not Azure readiness. Use the existing offline quality checks first. Any deployment, protected-source authorization test, or ingestion run requires separate authorization and a development dataset.

© 2026 Azure AI Landing Zones