Skip to content

Add Dict-Style Syntax for Tool and Flow Variables #124

@loukratz-bv

Description

@loukratz-bv

Summary

Add Pydantic validators to support ergonomic dict-style syntax in YAML for defining tool inputs/outputs/parameters and flow variables, while maintaining the internal list[Variable] representation for consistency.

Motivation

Currently, QType requires verbose list syntax for defining variables in both tools and flows:

tools:
  - id: weather_api
    name: Weather API
    inputs:
      - id: location
        type: text
        optional: false
      - id: units
        type: text
        optional: true
    outputs:
      - id: temperature
        type: float
        optional: false

flows:
  - id: process_weather
    variables:
      - id: location
        type: text
      - id: result
        type: float

This is consistent across the DSL, but significantly more verbose than necessary. Users must repeat the id field as both a key and value, making the YAML harder to write and read.

Proposed Solution

Add Pydantic @field_validator(mode="before") decorators that automatically convert dict-style YAML to list[Variable] during deserialization:

tools:
  - id: weather_api
    name: Weather API
    inputs:
      location: text
      units: text?
    outputs:
      temperature: float

flows:
  - id: process_weather
    variables:
      location: text
      result: float

Benefits

  1. Reduced verbosity - No ID repetition (30-40% less YAML)
  2. Better readability - Natural YAML mapping structure
  3. Familiar pattern - Matches OpenAPI, JSON Schema, and other DSLs
  4. Backward compatible - List syntax still works
  5. No internal changes - list[Variable] representation unchanged

Design Trade-off

We previously chose consistency over convenience, requiring list syntax everywhere. This proposal reverses that decision based on:

  • Ergonomics matter - Users write YAML much more than they interact with Python internals
  • Consistency can be internal - Python models remain consistent (list[Variable]), YAML can be ergonomic
  • Industry standard - Dict syntax for parameters is ubiquitous in API specs and config DSLs

Implementation

1. Tool Classes

Add validator to Tool base class (applies to all tool types):

class Tool(StrictBaseModel, ABC):
    inputs: list[Variable] = Field(default_factory=list)
    outputs: list[Variable] = Field(default_factory=list)
    
    @field_validator("inputs", "outputs", mode="before")
    @classmethod
    def convert_dict_to_list(cls, value):
        """Convert dict-style parameters to list[Variable] for YAML ergonomics."""
        if isinstance(value, dict):
            result = []
            for name, params in value.items():
                if isinstance(params, dict):
                    result.append(Variable(id=name, **params))
                elif isinstance(params, Variable):
                    result.append(params)
                else:
                    # params is just a type string
                    result.append(Variable(id=name, type=params))
            return result
        return value

2. APITool Subclass

Override validator to also handle parameters field:

class APITool(Tool):
    parameters: list[Variable] = Field(default_factory=list)
    
    @field_validator("inputs", "outputs", "parameters", mode="before")
    @classmethod
    def convert_dict_to_list(cls, value):
        # Same implementation as base class
        ...

3. Flow and Step Classes

Add similar validators to:

  • Flow.variables
  • Step.inputs (if we want to support dict syntax for step input references - TBD)
  • Step.outputs (if we want to support dict syntax for step output references - TBD)

Note: Step inputs/outputs are typically Reference[Variable] | str not Variable, so the validator would need to be adapted or skipped.

Examples

Tool Definition

Before (current):

tools:
  - id: calculate_distance
    type: PythonFunctionTool
    name: Calculate Distance
    function_name: calculate_distance
    module_path: geo.utils
    inputs:
      - id: lat1
        type: float
        optional: false
      - id: lon1
        type: float
        optional: false
      - id: lat2
        type: float
        optional: false
      - id: lon2
        type: float
        optional: false
    outputs:
      - id: distance_km
        type: float
        optional: false

After (proposed):

tools:
  - id: calculate_distance
    type: PythonFunctionTool
    name: Calculate Distance
    function_name: calculate_distance
    module_path: geo.utils
    inputs:
      lat1: float
      lon1: float
      lat2: float
      lon2: float
    outputs:
      distance_km: float

Testing Requirements

  1. Parity tests - Verify dict syntax produces identical internal models as list syntax
  2. Round-trip tests - Load dict YAML → serialize → verify list format in output
  3. Validator coverage - Test all parameter value types:
    • Full dict with type, optional, ui
    • Type-only string shorthand
    • Already-parsed Variable objects
  4. Backward compatibility - Existing list-syntax YAML still works
  5. Update examples - Convert all examples/ and tests/document-specs/ to dict syntax

Migration Path

  1. Phase 1: Implement validators (backward compatible)
  2. Phase 2: Update documentation and examples to show dict syntax as preferred
  3. Phase 3: Add linter/formatter to auto-convert list to dict syntax (optional)
  4. Phase 4: (Future) Consider deprecating list syntax in YAML (but not in Python API)

Scope Decisions

In Scope

  • Tool inputs, outputs, parameters
  • Flow variables

Out of Scope (for now)

  • Step inputs/outputs - These are References, not Variable definitions
  • Nested variables in CustomType properties (already string-based)

Open Questions

  1. Should we support dict syntax for Flow inputs/outputs (which are Reference[Variable] | str)?
  2. Should serialization preserve user's original format (dict vs list) or normalize to one?
    • Recommendation: Serialize to list format for consistency in generated files

Related Work

  • This proposal was initially implemented in PR #XXX but reverted to prioritize consistency
  • We chose to unify Tool parameters with Flow variables using list[Variable]
  • This proposal maintains that internal consistency while improving YAML ergonomics

References

  • Original discussion: Tool refactoring to remove ToolParameter class
  • Design decision: "consistency is worth the added yaml"
  • Implementation: qtype/dsl/model.py - Tool and Flow classes

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions