Back to Home

Dialog management in Python via JSON config

Overview of the dialog-engine library for declarative description of multi-step dialogs in Python. The article covers the conditional visibility mechanism, contextual navigation, integration with aiogram 3 and CLI tools for config validation.

Wizards and forms in Python: declarative navigation
Advertisement 728x90

Managing Multi-Step Dialogs in Python with JSON Configurations

Building surveys, onboarding flows, and complex wizards often results in a buildup of conditional logic that's hard to maintain and scale. The dialog-engine library offers a declarative alternative: dialog structure, field visibility rules, and navigation are defined in JSON or YAML. The engine automatically computes available steps based on session context, eliminating the need for nested constructs for every scenario.

Declarative Approach to Building Wizards

Traditional multi-step form implementations require explicit state management. Developers manually track the current stage, check transition conditions, and assemble the interface. When business logic changes or new branches are added, the code quickly gets messy, turning into spaghetti of if statements and match blocks. dialog-engine flips this around: the dialog becomes a data structure rather than a sequence of instructions.

The configuration file holds an array of steps. Each item defines an ID, input type, text, and optional validation parameters. The engine doesn't interpret field types—text, choice, photo, or custom values are handled via conventions between the config and rendering layer. This allows using the same file for a Telegram bot, web interface, or console utility.

Google AdInline article slot
{
  "steps": [
    { "id": "name", "type": "text", "text": "What is your name?" },
    { "id": "plan", "type": "choice", "text": "Choose a plan",
      "choices": { "free": "Free", "pro": "Professional" } },
    { "id": "company_inn", "type": "text", "text": "Enter the company tax ID",
      "show_when": { "field": "plan", "equals": "pro" } },
    { "id": "confirm", "type": "text", "text": "Is everything correct? Sending!" }
  ]
}

Core installation requires no external dependencies. Additional modules are installed via extras as needed, keeping the base package lightweight.

pip install dialog-engine
pip install dialog-engine[validation,yaml,aiogram]

Conditional Visibility and Navigation Mechanism

The engine's key feature is context-dependent routing. Instead of hardcoding transitions, it uses a dictionary of user responses to compute indices for the next and previous visible steps. Hidden stages are automatically excluded from the progress bar and navigation logic, ensuring accurate position counts like "step 2 of 3".

Visibility conditions are set via show_when and skip_when fields. It supports basic comparison operators and existence checks, plus composite rules like any_of and all_of for complex business logic. Rules can be nested without depth limits.

Google AdInline article slot
{
  "skip_when": {
    "any_of": [
      { "field": "plan", "equals": "free" },
      { "field": "age", "lt": 18 }
    ]
  }
}

The Python API provides methods for working with indices and positions, allowing precise session state control without manual condition traversal.

ctx = {"plan": "free"}
next_idx = engine.next_index(1, ctx)
prev_idx = engine.previous_index(3, ctx)
pos = engine.effective_position(next_idx, ctx)
total = engine.effective_total(ctx)

if engine.is_last_visible(next_idx, ctx):
    # Complete dialog
    ...

For session persistence, the DialogSessionState class serializes the current index and context to JSON. This simplifies storing state in Redis or relational databases between user requests.

Integration with aiogram 3 and Callback Handling

The library core is framework-agnostic, but ready-made helpers are provided for the Telegram ecosystem. They generate inline keyboards, automatically mark selected values, and form correct callback_data. Callback handling is typed and separates logic for choices, skips, and backs.

Google AdInline article slot
from dialog_engine.integrations.aiogram import (
    build_step_keyboard,
    parse_choice_callback,
    is_named_callback,
    KeyboardCallbacks,
)

step = engine.get_step(current_index)
kb = build_step_keyboard(step, translate, show_back=True, current_value=ctx.get(step.id))
await message.answer(step.text, reply_markup=kb)

Incoming callback parsing is handled by parsers that return the step ID and selected value. This eliminates manual callback.data string parsing and reduces prefix collision risks.

@router.callback_query()
async def on_callback(callback: CallbackQuery):
    cb = KeyboardCallbacks()
    pair = parse_choice_callback(callback.data, cb)
    if pair:
        step_id, choice_key = pair
        ctx[step_id] = choice_key
        next_idx = engine.next_index(current_index, ctx)
        ...

CLI Tools and Configuration Validation

Declarative config support requires static analysis tools. The included CLI checks file structure for duplicate IDs, broken references, and syntax errors before app launch. Strict mode uses Pydantic for full type checking.

  • dialog-engine validate wizard.json — basic structure check.
  • dialog-engine validate --strict wizard.json — Pydantic validation.
  • dialog-engine mermaid wizard.json — generate transition diagram for documentation.
  • dialog-engine schema > dialog.schema.json — output JSON Schema for IDE autocomplete.
  • dialog-engine init -o my-dialog.json — create new dialog template.

Integrating validation into CI/CD pipelines ensures invalid configs don't reach production. JSON Schema provides syntax highlighting and autocomplete in code editors, lowering the entry barrier for product managers and analysts editing forms.

Key Points

  • Dialogs are described declaratively in JSON/YAML, separating business logic from app code and allowing scenario edits without redeployment.
  • Navigation and step visibility are computed dynamically from session context, avoiding nested conditional structures.
  • aiogram 3 integration provides ready keyboard generators and typed callback parsers, reducing boilerplate code.
  • CLI utilities and JSON Schema enable static config checks and smooth IDE development.
  • The library is distributed under a source-available license: use in projects is allowed, but modification and redistribution of source code are limited.

— Editorial Team

Advertisement 728x90

Read Next