Quick Apps (2.0) is a composer for building DIAL applications from reusable tools and integrations. It lets you declaratively compose new DIAL applications by wiring DIAL-native tools, REST APIs, and MCP servers with any LLM registered in DIAL Core acting as the orchestrator. Publishing a Quick App produces a DIAL application record managed by Core.
- Compose applications by wiring tools and an LLM orchestrator via JSON-schema–validated manifests.
- Advanced flow controls: fallbacks, parallel tool execution, loops and retries.
- Native DIAL tools plus external REST and MCP integrations.
- Any LLM available in DIAL Core (Azure OpenAI, Anthropic, Vertex AI, etc.).
Some features are released as Preview before becoming Stable:
| Stage | Meaning |
|---|---|
| Preview | Available for use but may change in breaking ways without a major version bump. Feedback is welcome. |
| Stable | Covered by semantic versioning — breaking changes require a major version bump. |
Features in Preview are marked with a [Preview] tag in documentation.
- Configuration Reference - Full configuration model, environment variables, and examples
- Agent Skills - How to create and manage reusable agent skills
- Config-Driven Hooks
[Preview]- Declarative synthetic tool call injection at orchestrator seams - Technical Documentation - Internal architecture and design documents
- Local development and run instructions, utilities and tests remain in this repository. For detailed setup commands ( venv, poetry, make targets) see the project docs and Makefile.
- To run Quick Apps as a standalone service you need to provide DIAL Core endpoint and relevant environment variables.
All configuration-specific details (configuration model, environment variables, orchestrator, contexts, tool_sets, tool fallback, attachments, authorization types, parameter/display configurations and examples) were moved to a dedicated file:
- Configuration — full configuration reference and examples.
Hooks let you pre-populate the agent's message history with synthetic tool call results — without writing Python code. Each hook fires at a named orchestrator seam and injects a (ASSISTANT/tool_calls, TOOL) message pair.
Enable with ENABLE_PREVIEW_FEATURES=true, then add a hooks array to the app manifest:
{
"hooks": [
{
"kind": "tool_call",
"event": "on_request_start",
"toolset_name": "memory_server",
"tool_name": "get_memories",
"arguments": { "user_id": "123" },
"frequency": "always"
}
]
}Key fields:
| Field | Description |
|---|---|
kind |
Hook type. Only "tool_call" is supported today. |
event |
Orchestrator seam. Only "on_request_start" is wired today. |
toolset_name |
Toolset prefix for REST API / MCP tools. Omit for DIAL Deployment and Internal tools. |
tool_name |
Tool name within the toolset, or the exact function name when toolset_name is omitted. |
arguments |
Arguments forwarded to the tool call. |
frequency |
"always" — inject on every request. "append_if_changed" (default) — inject only when the result differs from the last injection. |
See Config-Driven Hooks design doc for the full reference.
Incoming request headers whose names start with X- (case-insensitive) are automatically forwarded to all outbound
calls made during that chat completion. No configuration is required.
- Orchestrator (Azure OpenAI): forwarded headers are sent on each chat completion request.
- MCP tools: forwarded headers are merged into the HTTP/SSE headers used when connecting to MCP servers.
- DIAL deployment tools: forwarded headers are sent as
extra_headerswhen calling DIAL chat completions. - REST API tools: forwarded headers are merged into the outgoing HTTP request headers.
Use this for tracing (e.g. X-Request-Id, X-Correlation-Id), multi-tenancy (X-Tenant-Id), or any custom header
your gateways or downstream services expect.
Controls which tool-execution stages are surfaced in the DIAL UI for each app. Set features.stage_display.level in the app manifest:
| Value | Behavior |
|---|---|
none |
No stages shown at all, not even for errors |
error |
Show stages only for failed tool calls |
info |
Show stages for regular tool calls and errors (default) |
debug |
Show stages for all tool calls, including internal/system ones |
{
"features": {
"stage_display": {
"level": "debug"
}
}
}| Variable | Default | Required | Description |
|---|---|---|---|
| DIAL Core | |||
DIAL_URL |
— | Yes | URL of the DIAL Core API |
DIAL_API_VERSION |
2025-01-01-preview |
No | API version for DIAL Core API |
APP_SCHEMA_ID |
https://mydial.epam.com/custom_application_schemas/quickapps2 |
No | Full application type schema $id emitted in the generated app schema. When unset, the built-in default is used. |
| Logging | |||
DIAL_SDK_LOG_FORMAT |
text |
No | Console log output format: text (human-readable) or json (escape-safe, one record per line). See docs/logging.md. |
DIAL_SDK_TEXT_LOG_FORMAT |
see docs/logging.md | No | Custom %-style format string for text output. Unset (default) keeps the built-in format with the conditional OTEL trace block. |
DIAL_SDK_JSON_LOG_FORMAT |
see docs/logging.md | No | Custom template for json output — a JSON document whose string leaves are %-style format strings, values escaped via json.dumps. |
LOG_LEVEL |
INFO |
No | Root logger level (all loggers except quickapp) |
QUICKAPP_LOG_LEVEL |
INFO |
No | Log level for quickapp loggers |
LOG_PAYLOADS |
false |
No | Emit payload content (message bodies, tool-call arguments, tool/LLM response bodies) at DEBUG. When false, no payload content is logged at any level and the payload-capable third-party loggers (openai/httpx/httpcore) are capped at INFO. Local development only — see Payload Logging. |
LOG_PAYLOADS_MAX_LENGTH |
2000 |
No | Per-field character cap applied to each payload value when LOG_PAYLOADS=true; longer values are truncated. Inert when LOG_PAYLOADS=false. |
| Agent | |||
DEFAULT_AGENT_MAX_ITERATIONS |
15 |
No | Maximum number of orchestrator iterations (-1 for infinite) |
DEFAULT_ORCHESTRATOR_DEPLOYMENT_ID |
— | No | Default DIAL deployment id used as the orchestrator model when a QuickApp manifest omits orchestrator.deployment. Also surfaces as the JSON-schema default for that field so DIAL Core can pre-fill new manifests. Apps can override per-app. |
SHOW_USAGE_STATISTICS |
false |
No | Include usage statistics in chat completion stream |
SHOW_EXECUTION_TIME_STAGE |
false |
No | Show execution time stage in the UI |
| Python Interpreter | |||
PY_INTERPRETER_LOCAL_RUN |
false |
No | Run PyInterpreter locally instead of via DIAL Core API |
PY_INTERPRETER_URL |
(falls back to DIAL_URL) | No | URL of the PyInterpreter service |
PY_INTERPRETER_API_KEY |
— | No | API key for local-run PyInterpreter |
PY_INTERPRETER_DEFAULT_SESSION_ID |
— | No | Default session ID for the PyInterpreter |
PY_INTERPRETER_CLIENT_MAX_RETRIES |
3 |
No | Max retries for PyInterpreter client requests |
| Tool Timeouts | |||
DEFAULT_TOOL_TIMEOUT_SECONDS |
300.0 |
No | Deployment-wide default timeout (seconds, 0 < x ≤ 3600) applied to every tool call (deployment, REST API, MCP, Python interpreter). Apps can override per-app via tool_defaults.timeout_seconds. |
DEFAULT_FILE_LOADING_SIZE_LIMIT |
10485760 |
No | Deployment-wide default maximum size (in bytes) for files the agent downloads. Apps can override per-app via features.file_loading.size_limit. |
| Stage Display | |||
DEFAULT_STAGE_DISPLAY_LEVEL |
— | No | Deployment-wide override for stage visibility threshold (none, error, info, debug; case-insensitive). When set, wins over every app's features.stage_display.level. Unset (default) defers to the per-app config, which defaults to info. |
| DIAL Files — Tool-Response Offload | |||
TOOL_CALL_RESULT_OFFLOAD__ENABLED_BY_DEFAULT |
true |
No | Default value of the per-app enabled flag (features.dial_files.tool_call_result_offload.enabled). Apps override per-app; enabled: false disables offload for that app. |
TOOL_CALL_RESULT_OFFLOAD__SIZE_THRESHOLD |
40000 |
No | Default byte threshold above which a tool-call response is offloaded to a DIAL file. Apps override per-app via features.dial_files.tool_call_result_offload.size_threshold. |
TOOL_CALL_RESULT_OFFLOAD__EXCLUDED_TOOLS |
[] |
No | Default JSON list of additional tool names exempt from offloading. The read-back tools (internal_file_read_lines, internal_file_search) are always excluded regardless of this value, so a large read-back slice is never re-offloaded. Apps add more per-app via features.dial_files.tool_call_result_offload.excluded_tools. |
| External URL Egress | |||
EXTERNAL_URL_FETCH_ENABLED |
false |
No | Admin cap on fetching external (non-DIAL) URLs. When false (default), no app may fetch external URLs regardless of its manifest; the deployment-handoff branch (deployments with features.url_attachments) is unaffected. Apps can opt out per-app via features.external_url_fetch.enabled=false even when the admin allows. |
EXTERNAL_URL_FETCH_HOST_ALLOWLIST |
— | No | Comma-separated allowlist of host patterns for external URL fetches. Unset (default) means no admin-level host restriction. Patterns: exact host (example.com) or *.example.com for any subdomain. Re-checked on every redirect hop. Per-app features.external_url_fetch.host_allowlist narrows further (intersection) but never expands. |
EXTERNAL_URL_FETCH_MAX_REDIRECTS |
5 |
No | Maximum HTTP redirects on external URL fetches. Each hop is SSRF-checked. Hard ceiling 10. |
EXTERNAL_URL_FETCH_CONNECT_TIMEOUT_SECONDS |
5.0 |
No | TCP connect timeout (seconds) for external URL fetches. Read/write/pool timeouts use the resolved tool timeout. |
| Feature Gating | |||
ENABLE_PREVIEW_FEATURES |
false |
No | Enable preview features across the deployment (schema visibility + runtime activation) |
| Templates | |||
PREDEFINED_EXTRA_PATHS |
— | No | JSON list of directories layered on top of built-in predefined content (later entries override earlier ones) |
CONFIG_PROMPT_MAPPING |
(built-in mapping) | No | JSON mapping of predefined system prompts to DIAL Core deployments |
| Observability | |||
OTEL_SERVICE_NAME |
quickapps |
No | Service name stamped on all exported telemetry (traces, metrics, logs) |
OTEL_TRACES_EXPORTER |
— | No | Set to otlp to enable tracing and export spans over OTLP/gRPC. Instruments the FastAPI server and outgoing HTTP clients (httpx, requests, aiohttp, urllib) and stamps trace context onto log records — see docs/logging.md. |
OTEL_METRICS_EXPORTER |
— | No | Comma-separated metric exporters: otlp (push over OTLP/gRPC) and/or prometheus (serve a scrape endpoint). Enables FastAPI and system/process metrics. |
OTEL_LOGS_EXPORTER |
— | No | Set to otlp to export log records (INFO and above) over OTLP/gRPC alongside console output — see docs/logging.md. |
OTEL_EXPORTER_OTLP_ENDPOINT |
http://localhost:4317 |
No | OTLP/gRPC collector endpoint shared by trace, metric, and log export. One of the standard OpenTelemetry SDK variables, which the underlying exporters honor as usual (per-signal endpoints, headers, timeouts, resource attributes, …). |
OTEL_EXPORTER_PROMETHEUS_PORT |
9464 |
No | Port of the Prometheus scrape endpoint (effective only with prometheus in OTEL_METRICS_EXPORTER) |
| Scripts & Tests | |||
REMOTE_DIAL_URL |
— | No | URL of the remote DIAL Core, used only by generate_dial_config script and e2e/integration tests |
REMOTE_DIAL_API_KEY |
— | No | API key of the remote DIAL Core, used only by generate_dial_config script and e2e/integration tests |
Caution
These variables still work but will be removed in a future major version.
| Variable | Replacement | Description |
|---|---|---|
PREDEFINED_BASE_PATH |
PREDEFINED_EXTRA_PATHS |
If set alone, treated as a single extra layer on top of the built-in content |
PY_INTERPRETER_CLIENT_TIMEOUT |
DEFAULT_TOOL_TIMEOUT_SECONDS or tool_defaults.timeout_seconds |
When set, still controls the PyInterpreter client timeout (seconds, default 60.0), but the unified tool-timeout settings are preferred. |
LOG_FORMAT |
DIAL_SDK_TEXT_LOG_FORMAT or DIAL_SDK_LOG_FORMAT=json |
When set, still controls the text output format (and wins over the replacements); a warning is emitted at startup. See docs/logging.md. |
LOG_DATE_FORMAT |
— | Still honored alongside LOG_FORMAT; going forward the timestamp format is fixed to %Y-%m-%d %H:%M:%S (the previous default). |
OTEL_PYTHON_LOG_CORRELATION |
— (automatic) | Deprecated by aidial-sdk; a warning is emitted at startup. Trace fields are stamped onto log records whenever tracing is enabled, so the switch is redundant — and setting it installs OTel's legacy root-logger format, which double-logs SDK records and bypasses this service's console formatting. See docs/logging.md. |
Notes:
- Variables listed above are a superset used across development and deployment modes. Some variables (e.g.
REMOTE_DIAL_*) are only used when running the full local stack via docker-compose or during testing. - Telemetry is opt-in: when none of
OTEL_TRACES_EXPORTER/OTEL_METRICS_EXPORTER/OTEL_LOGS_EXPORTERis set, OpenTelemetry is not initialized at all. - For a standalone Quick Apps deployment the essential variable is only
DIAL_URL - For PyInterpreter tool setup see: DIAL Core, PyInterpreter.
Moved to docs/logging.md, which covers the text and JSON output modes, format customization, OTEL trace correlation, and OTLP log export.
By policy, logs carry structure — roles, counts, sizes, names, ids, statuses, durations, HTTP codes, header names, and URLs stripped to scheme/host/path — and never content: message bodies, tool-call argument values, tool/LLM response bodies, attachment content, header values, or URL query strings. This holds at every level, DEBUG included, so raising verbosity during an incident never brings conversation content into the logs.
LOG_PAYLOADS=true is the single, explicit exception: it re-enables the payload-bearing DEBUG records (message
context, tool-call arguments, raw responses), each field truncated to LOG_PAYLOADS_MAX_LENGTH, and lifts the
INFO cap on the wire-level third-party loggers (openai, httpx, httpcore). Every payload record is prefixed
with a [payload] marker so these lines can be found — or excluded — with a single filter. Forwarded header values are
never logged, even with the switch on. The switch is additive to the level — content appears only when
QUICKAPP_LOG_LEVEL=DEBUG and LOG_PAYLOADS=true.
Caution
LOG_PAYLOADS is intended for local development only. It writes conversation content and wire-level
third-party payloads to the log pipeline (including any OTLP export). Do not enable it in shared or
production environments.
-
Install Make
- macOS: usually preinstalled.
- Windows: see https://gnuwin32.sourceforge.net/packages/make.htm or use Chocolatey.
- Ensure
makeis in PATH (which make).
-
Install Python 3.13
- macOS (Homebrew):
brew install python@3.13 - Official downloads: https://www.python.org/downloads/
- Ensure
python3.13(orpython3) is in PATH (python3.13 --version).
- macOS (Homebrew):
-
Recommended way - system-wide, independent of any particular python venv:
- MacOS - recommended way to install poetry is to use pipx
- Windows - recommended way to install poetry is to use official installer
- Make sure that
poetryis in the PATH and works properly (runpoetry --version). - Alternative - venv-specific (using
pip): make sure the correct python venv is activatedmake install_poetry
-
Clone the repository
-
Create and activate virtual environment
make init_venv source .venv/bin/activate -
Install dev dependencies
make install_dev
-
Create
.envfile in the root of the project. Copy.env.templatefile data to the.envand fill the values. The full information about ENV variables can be found in Configuration.cp .env.template .env
-
Generate DIAL configuration files:
make generate_dial_config
This command will generate two files in
docker_compose_files/core/configuration/generated/:models.json- contains the models configuration for DIAL.
-
Option A — Full local stack (docker-compose)
-
Use this if you want to bring up DIAL Core, chat UI, redis, themes and adapters locally for end-to-end development and testing.
-
This docker-compose setup launches multiple services and uses internal hostnames (for example core, redis, themes).
-
Setup expects the Quick Apps service to run on your host machine at
host.docker.internal:5000python3 ./src/quickapp/app.py
-
Then start the local stack:
docker compose up -d
-
Optional — DIAL Admin (UI + backend API, embedded H2 database):
docker compose --profile admin up -d
Or uncomment
COMPOSE_PROFILES=adminin.envand rundocker compose up -das usual.Startup order:
admin-export-init→redis/core(healthy) →admin-backend(healthy) →admin-frontend.- Admin UI: http://localhost:3020 (unauthenticated — only
DIAL_ADMIN_API_URL+NEXTAUTH_SECRET; do not setNEXTAUTH_URLor anyAUTH_*vars) - Admin API: http://localhost:8092
- Backend uses H2 with dev encryption keys; Core access via
dial_api_key. Not production-equivalent. - Admin → Core sync:
admin-export-initcreatesdocker_compose_files/core/admin-export/out.json(gitignored) before Core starts. Admin exports merged config there (~15s after changes) and calls Core reload (ENABLE_CONFIG_RELOAD). Core loads static JSON fromdocker_compose_files/core/configuration/plusout.json(last), so Admin UI edits override the static files. Allow ~20s after a save for export + reload. - Populate Admin H2 initially via the Admin UI import flow, or keep an existing
admin-backend-datavolume. Core static config is not auto-imported into Admin on startup.
- Admin UI: http://localhost:3020 (unauthenticated — only
-
Notes:
- If you want to run Quick Apps in Docker instead of on the host, update
application-schemas.json and change the Quick
Apps host from
host.docker.internal:5000toquick-apps:5000. - When running via docker-compose the compose files set service hostnames (for example DIAL URL inside containers is http://core:8080). Those container-internal hostnames are not valid from your host machine — use the exposed ports (for example http://localhost:8090) when calling services from the host.
- Some environment variables in the repo (e.g. adapter or chat-specific variables) are only relevant for the full stack docker-compose setup and may be ignored when you deploy Quick Apps standalone.
- If you want to run Quick Apps in Docker instead of on the host, update
application-schemas.json and change the Quick
Apps host from
-
-
Option B — Quick Apps standalone (connect to an existing DIAL Core)
- Use this when you already have a DIAL Core instance available (local, staging, or cloud). You do NOT need to run core, chat, redis, themes, or adapter containers to deploy Quick Apps.
- Steps:
- Create and fill your .env (or set environment variables) with the fields required by Quick Apps. At minimum
ensure:
- DIAL_URL points to your DIAL Core API endpoint (example: https://core.example.com or http://core:8080 depending on your environment).
- If required by your DIAL Core instance, set DIAL_API_KEY or other auth variables.
- PREDEFINED_EXTRA_PATHS if you need to add or override predefined templates/toolsets.
- Run the Quick Apps 2 backend. One of:
python3 ./src/quickapp/app.pydocker build -t quickapp:latest -f Dockerfile . && docker run --rm -it quickapp:latest
- Register the application in DIAL Core
- Add the Quick App schema generated by
make dump_app_schemato the Core configuration so Core knows the Quick App application schema. For Helm create/update a ConfigMap or mount the generated file under the path referenced by PREDEFINED_EXTRA_PATHS so Core can load it. - Ensure the chat UI has the Quick Apps feature enabled. Verify the chat service (or your ai-dial-chat
deployment) includes
quick-appsin its ENABLED_FEATURES flags so the UI will surface Quick Apps.
- Add the Quick App schema generated by
- Create and fill your .env (or set environment variables) with the fields required by Quick Apps. At minimum
ensure:
- Notes:
- Quick Apps will act as a client of DIAL Core; it must be able to reach the DIAL Core API (DIAL_URL) and, if required, present credentials (DIAL_API_KEY).
- Registering on the DIAL Core side can be done by adding the application JSON to Core configuration or via your Core management UI — ensure the application record points to the Quick Apps service as appropriate.
-
Format the code:
make format # Format all source files + regenerate app schema make format FILES="src/quickapp/core/agent/orchestrator.py" # Format specific files (skips schema dump)
-
Run linters:
make lint # Run all linters (always checks all source files) -
Run individual tools (accept
FILES="..."to target specific files):make black # Run black formatter make isort # Run isort formatter make flake8 # Run flake8 linter make mypy # Run type checking
-
Run tests:
make test # Run all unit tests make test ARGS="-k test_name -x" # Run specific tests / fail fast make test_cov # Run unit tests with coverage report
-
Run arbitrary Python scripts:
make run_python SCRIPT=src/scripts/dump_app_schema.py
-
To automatically apply black and isort on commit, enable PreCommit:
make install_pre_commit_hooks
This command will set up the git hook scripts.
Refer to Testing Guide for detailed instructions on setting up and running tests.
For more information about DIAL and its components, visit the DIAL documentation. Join the DIAL community on Discord for support and collaboration.