Reasoning Flows — Configurable Objects Overview

Configurable Objects Overview

Create interactive configuration interfaces for Reasoning Flows objects using HTML forms.


Purpose

In Reasoning Flows, you can create objects in PHP or Python with an optional interactive configuration interface. By adding a config.html file to your object, you enable users to configure parameters through a form instead of editing code directly.

Form values are passed directly to the startup script (index.py or index.php) at runtime, making it easy for non-technical users to adjust settings without modifying code.

Objects without a config.html file work exactly as before — opening in the standard code editor view.


Object Types

An object must use one of the following languages:

LanguageStartup FileRuntime
Pythonindex.pyPython 3.x
PHPindex.phpPHP 8.x

The startup file is required and serves as the entry point for execution. It can import or include other files within the same object.


Configuration UI (config.html)

With config.html

When an object contains a config.html file:

  • Reasoning Flows renders it as an interactive configuration form
  • User inputs are passed directly to the startup script at execution time
  • You can still access View Code Mode to see and edit the object's files

Without config.html

When config.html is absent:

  • The object opens in Code Mode by default
  • Users edit code and configuration files manually

How Form Values Reach Your Code

Each form field's name attribute becomes the key your startup script reads:

LanguageValues arrive asRead with
PythonEnvironment variablesos.environ.get('field_name')
PHPPOST parameters$_POST['field_name']

Security note: Form values are user input. Always validate them in your startup script before use — especially anything that ends up in a SQL statement. Use parameterized queries for values, and strict allowlists or pattern checks for identifiers such as table names, which cannot be parameterized.


Creating a Configuration Interface

Example: config.html

<!DOCTYPE html>
<html>
<head>
    <title>Data Export Configuration</title>
    <style>
        form { max-width: 400px; margin: 20px; }
        label { display: block; margin-top: 10px; font-weight: bold; }
        input, select { width: 100%; padding: 8px; margin-top: 5px; }
        button { margin-top: 20px; padding: 10px 20px; }
    </style>
</head>
<body>
    <h2>Data Export Settings</h2>
    <form id="config-form">
        <label for="source_table">Source Table:</label>
        <select id="source_table" name="source_table">
            <option value="sales_gold">sales_gold</option>
            <option value="customers_gold">customers_gold</option>
            <option value="inventory_gold">inventory_gold</option>
        </select>

        <label for="row_limit">Row Limit:</label>
        <input type="number" id="row_limit" name="row_limit" value="1000" min="1" max="100000">

        <label for="output_format">Output Format:</label>
        <select id="output_format" name="output_format">
            <option value="csv">CSV</option>
            <option value="json">JSON</option>
        </select>

        <label for="include_headers">Include Headers (CSV):</label>
        <select id="include_headers" name="include_headers">
            <option value="yes">Yes</option>
            <option value="no">No</option>
        </select>

        <button type="submit">Export Data</button>
    </form>
</body>
</html>

Tip: Offering the table name as a dropdown instead of a free-text field improves usability and security — users can't mistype, and your script can verify the value against the same fixed list.

Reading Config Values in Python (index.py)

import dkconnect
import os
import json

# --- Read form values (environment variables) ---
source_table = os.environ.get('source_table', 'sales_gold')
row_limit = int(os.environ.get('row_limit', 1000))
output_format = os.environ.get('output_format', 'csv')
include_headers = os.environ.get('include_headers', 'yes') == 'yes'

# --- Validate user input before use ---
# Table names cannot be parameterized in SQL, so verify against an allowlist.
ALLOWED_TABLES = {'sales_gold', 'customers_gold', 'inventory_gold'}
if source_table not in ALLOWED_TABLES:
    raise ValueError(f"Invalid source table: {source_table}")

row_limit = max(1, min(row_limit, 100000))  # clamp to a sane range

# --- Connect and query ---
conn = dkconnect.connect()
cursor = conn.cursor()

# Identifier validated above; LIMIT value parameterized.
cursor.execute(
    f"SELECT * FROM {source_table} LIMIT %s",
    (row_limit,)
)
columns = [desc[0] for desc in cursor.description]
rows = cursor.fetchall()

# --- Output based on format selection ---
if output_format == 'json':
    data = [dict(zip(columns, row)) for row in rows]
    print(json.dumps(data, indent=2, default=str))
else:  # csv (default)
    if include_headers:
        print(','.join(columns))
    for row in rows:
        print(','.join(str(val) for val in row))

cursor.close()
conn.close()

Reading Config Values in PHP (index.php)

<?php
require_once('datakubes-connect.php');

// --- Read form values (POST parameters) ---
$source_table = $_POST['source_table'] ?? 'sales_gold';
$row_limit = (int)($_POST['row_limit'] ?? 1000);
$output_format = $_POST['output_format'] ?? 'csv';
$include_headers = ($_POST['include_headers'] ?? 'yes') === 'yes';

// --- Validate user input before use ---
// Table names cannot be parameterized in SQL, so verify against an allowlist.
$allowed_tables = ['sales_gold', 'customers_gold', 'inventory_gold'];
if (!in_array($source_table, $allowed_tables, true)) {
    http_response_code(400);
    die('Invalid source table.');
}

$row_limit = max(1, min($row_limit, 100000)); // clamp to a sane range

// --- Connect and query ---
$dk_conn = dk_connect();

// Identifier validated above; LIMIT value parameterized.
$stmt = $dk_conn->prepare("SELECT * FROM $source_table LIMIT ?");
$stmt->bind_param('i', $row_limit);
$stmt->execute();
$result = $stmt->get_result();

// Get column names
$columns = [];
foreach ($result->fetch_fields() as $field) {
    $columns[] = $field->name;
}

// --- Output based on format selection ---
if ($output_format === 'json') {
    header('Content-Type: application/json');
    $data = [];
    while ($row = $result->fetch_assoc()) {
        $data[] = $row;
    }
    echo json_encode($data, JSON_PRETTY_PRINT);
} else { // csv (default)
    header('Content-Type: text/csv');
    if ($include_headers) {
        echo implode(',', $columns) . "\n";
    }
    while ($row = $result->fetch_row()) {
        echo implode(',', $row) . "\n";
    }
}

$stmt->close();
$dk_conn->close();

Common Use Cases

Use CaseDescription
Data Transformation PipelinesLet users select data sources, filters, or transformation rules before processing
API Integration ConfigurationsCollect authentication keys, endpoints, and query parameters via form inputs
AI & Machine Learning ParametersAdjust model settings, thresholds, or prompts before execution
Report GenerationAllow users to specify date ranges, departments, or output formats
Automation & Task RunnersSchedule or parameterize recurring backend tasks without code changes
Environment-Specific SettingsSwitch between dev, staging, and production values dynamically

Who Uses Configurable Objects

This feature has two distinct roles by design — the person who builds the object and the people who run it:

RoleSideHow they use it
Platform / data engineersMakerWrap recurring ETL, exports, and pipelines in a form so parameter changes stop being tickets
ML engineersMakerExpose thresholds, prompts, and model parameters for experimentation without code access
AnalystsConsumerRun extracts and reports with their own filters, tables, and output formats
Operations / business staffConsumerTrigger parameterized recurring tasks without touching code
Team leads / security ownersBeneficiaryGrant run-with-parameters capability without granting code-write access

Note: Every form field is a contract. Once consumers depend on a parameter, renaming or removing it is a breaking change — design forms with the same care as an API.


Comparison

With config.htmlWithout config.html
UI rendered for configurationShows file/code browser
Inputs sent directly to main fileEdit code/config manually
User-friendly for non-developersRequires code editing
Always starts at index.*Always starts at index.*

Benefits

Flexibility
Choose PHP or Python based on your workflow and team expertise.

Ease of Use
Non-technical users can configure objects via forms instead of editing code.

Predictable Execution
Always starts from a single entry file (index.py or index.php).

Modularity
Startup scripts can import helper files, libraries, and shared modules.

Backward Compatibility
Objects without config.html work exactly as before.


Best Practices

Keep forms simple. Focus on the parameters users actually need to change.

Provide sensible defaults. Pre-populate form fields with common values.

Validate inputs. Treat every form value as untrusted. Parameterize SQL values; allowlist identifiers like table or column names (see the examples above). Clamp numeric inputs to sane ranges.

Prefer constrained inputs. Dropdowns, number fields with min/max, and fixed option sets prevent invalid input at the source — and make server-side validation trivial.

Use clear labels. Make form fields self-explanatory for non-technical users.

Add help text. Include placeholders or descriptions to explain expected values.

Handle missing values gracefully. Always provide fallback defaults in your code.

Test both modes. Verify your object works correctly with and without the configuration form.


Related Documentation