Pipeline GUI Schema¶
SMF describes its seven high-level Pipelines as plain Python data, so that a GUI can render a configuration form without importing any frontend package. The schema layer, available since 0.5.4, gives you for every Pipeline:
- a registry entry: name, description, how to call
run, and which result attributes to show; - one entry per field of its
*Configdataclass: type, default, suggested widget, options, numeric range, form section, dependency, and flags for what to hide or collapse; - YAML-safe export and import of a config, a lightweight config-only validator, and a code generator.
The GUI itself should live outside the modeling package. SMF is the source of truth for what can be configured; it does not depend on Streamlit or any other frontend.
Display text
The human-readable strings are English: display_name, description, use_case, and audience of the registry
entries, label, description, and group of the fields, and the messages from validate_pipeline_config. Field
names, option values, types, and defaults are language-neutral. If your GUI needs another language, key your own
translations on the registry key and the field name.
Curated text is incomplete. A field without a curated label gets its title-cased name as label (16 of the 260
fields in 0.8.2, for example Score Cols), and for most fields (201 of 260) description just repeats label. Do not
rely on description as help text: the Top-Level Pipelines page describes the parameters of
each Pipeline.
All blocks on this page run top to bottom in one Python session. The YAML helpers need PyYAML (pip install pyyaml), which
SMF does not install.
Public API¶
from Modeling_Tool import (
FieldMeta,
PipelineRegistryEntry,
PIPELINE_REGISTRY,
get_pipeline_registry,
get_pipeline_registry_schema,
get_config_field_meta,
extract_pipeline_schema,
extract_config_schema,
extract_schema,
config_to_dict,
config_from_dict,
config_to_yaml,
config_from_yaml,
validate_pipeline_config,
generate_pipeline_code,
)
The same names are also exported from Modeling_Tool.Pipeline.
| Name | Signature | Returns |
|---|---|---|
PIPELINE_REGISTRY |
dict of registry key to PipelineRegistryEntry |
|
get_pipeline_registry_schema |
() |
dict of key to registry item, free of class objects |
get_pipeline_registry |
(include_classes=True) |
The same items; by default each also holds the pipeline_class, config_class, and result_class objects |
extract_pipeline_schema |
(pipeline_key=None) |
One Pipeline's schema; with None, {"pipelines": {key: schema}} for all |
extract_config_schema |
(config_class_or_key) |
One Pipeline's schema (what extract_pipeline_schema(key) returns) |
extract_schema |
(config_class_or_key) |
Only the fields list of that schema |
get_config_field_meta |
(config_class_or_key) |
dict of field name to FieldMeta |
config_to_dict |
(config, *, include_non_serializable=False, exclude_none=False) |
Plain dict |
config_from_dict |
(config_class_or_key, payload, *, strict=True) |
A Config instance |
config_to_yaml |
(config, *, pipeline_key=None, include_non_serializable=False) |
YAML text |
config_from_yaml |
(config_class_or_key, yaml_text, *, strict=True) |
A Config instance |
validate_pipeline_config |
(pipeline_key, values) |
list[str] of messages |
generate_pipeline_code |
(pipeline_key, values) |
Python source as text |
Wherever a function takes a Pipeline reference, you can pass the registry key ("credit_model"), the name of the Pipeline,
Config, or Result class as text ("CreditModelPipelineConfig"), one of those classes, or a Config instance. An unknown
reference raises KeyError.
Registered Pipelines¶
get_pipeline_registry_schema() returns a JSON/YAML-friendly registry:
registry = get_pipeline_registry_schema()
for key, item in registry.items():
print(key, item["pipeline_class_name"], item["run_method"], item["run_requires_data"])
| Key | Pipeline | Config | run_requires_data |
|---|---|---|---|
credit_model |
CreditModelPipeline |
CreditModelPipelineConfig |
True |
feature_validation |
FeatureValidationPipeline |
FeatureValidationPipelineConfig |
True |
reject_inference |
RejectInferencePipeline |
RejectInferencePipelineConfig |
True |
score_comparison |
ScoreComparisonPipeline |
ScoreComparisonPipelineConfig |
True |
score_consistency_uat |
ScoreConsistencyUATPipeline |
ScoreConsistencyUATPipelineConfig |
False |
sample_analysis |
SampleAnalysisPipeline |
SampleAnalysisPipelineConfig |
True |
mock_sample |
MockSamplePipeline |
MockSamplePipelineConfig |
False |
Each registry item has these keys:
| Key | Meaning |
|---|---|
key |
The registry key |
display_name, description, use_case |
Card text; audience is a list of reader roles |
pipeline_class_name, config_class_name, result_class_name |
Class names |
module_path, import_path |
Where the classes are defined, and the package that exports them (Modeling_Tool.Pipeline); generated code imports from import_path |
run_requires_data |
False when run() takes no DataFrame |
run_method |
The call to show on the card. For score_consistency_uat it lists both forms, run() and run(offline_data=..., online_data=...), joined by the word or |
result_attrs |
Headline attributes of the result object. The result dataclasses have more fields than these |
run_requires_data=False means the generated code should not pass data=your_dataframe. ScoreConsistencyUATPipeline.run
still accepts offline_data and online_data when you call it from code.
Extract One Schema¶
schema = extract_pipeline_schema("feature_validation")
print(schema["config_class_name"])
print(schema["run_method"])
for field in schema["fields"][:5]:
print(field["name"], field["widget"], field["default"])
A schema holds the registry item keys plus fields, a list in the order of the Config dataclass. Group the editable
fields into form sections like this:
sections = {}
for field in schema["fields"]:
if field["gui_editable"]:
sections.setdefault(field["group"], []).append(field["name"])
for group, names in sections.items():
print(group, len(names), names[:3])
Each field item has these keys:
| Key | Meaning |
|---|---|
name |
Config dataclass field name |
type |
The type hint as text (see the note below the table). Choose a control from widget, not from this text |
default |
The default as JSON/YAML-safe data (tuples and ranges become lists), or its repr() text when it cannot be converted |
has_default |
Whether the dataclass declares a default. It is True for every field in 0.8.2, so every Config can be built with no arguments |
label, description |
Display text |
widget |
text, number, select, multiselect, toggle, slider, textarea, json, or hidden |
options |
Allowed values for select and multiselect, for example ["equal_freq", "monotone"] for woe_engine; otherwise None |
min_val, max_val, step |
Numeric control hints, mostly for sliders; otherwise None |
required |
The form should ask for this field. It comes from a fixed list per Pipeline (credit_model: target_col) and does not mean "has no default" |
group |
Name of the suggested form section: Basic settings, Data input, Sample split, Output and reports, WOE/Binning, Model training, Evaluation settings, Analysis settings, or CSV batching. Sections are inferred from the field name, so a few assignments are rough: main_model_score_col of score_consistency_uat lands in the model-training section |
depends_on |
Display condition {field_name: value}, or None. Only four fields have one: warm_start_score_col ({"warm_start_enabled": True}) and the three monotone_refine_*_params fields of feature_validation |
since_version |
Reserved: None for every field in 0.8.2 |
yaml_serializable |
False for the code-only fields listed under Dict Export |
gui_editable |
False for the same fields: do not render them |
advanced |
Hint to collapse the field. It is inferred from the name: names containing model and the lr_, warm_start, backward, optuna, and explain families, every *_params dictionary, and CSV batching. It can flag a field that is also required (main_model_score_col), and you should not hide that one |
expert_only |
Hint for fields most users should leave alone: extra_eval_datasets, batch_corr_pair_chunk_size, pairwise_cross_agg_dict |
placeholder |
Example input text for a few fields such as feature_cols; otherwise None |
is_dict_subkey, parent_field |
Set only on nested entries (below) |
nested_fields |
Sub-form hints for dictionary fields (below) |
type is the type hint as Python prints it, with the typing. prefix removed: plain classes appear as <class 'bool'>,
other hints as str | None, list[str], or Literal['skip', 'raise'].
Nested fields¶
nested_fields describes the keys of these dictionary fields: split_config, feature_selection, woe_params,
monotone_woe_params, corr_params, psi_params, ivks_params, distribution_params, ri_method_params, and
submodel_pairs. A nested entry has the same keys as a field item, except name, type, default, and has_default: the
dictionary key is stored in label (for submodel_pairs, the pattern offline_col = online_col), parent_field names the
owning field, and is_dict_subkey is True.
credit = extract_pipeline_schema("credit_model")
split_config = next(f for f in credit["fields"] if f["name"] == "split_config")
print(split_config["default"])
for nested in split_config["nested_fields"]:
print(nested["parent_field"], nested["label"], nested["widget"], nested["min_val"], nested["max_val"])
The nested hints are a starting point, not a full list of the accepted keys. For example, those of monotone_woe_params do
not include eps, missing_woe, special_values, the sv_* options, or unseen_special_policy of MonotoneWOEBinner,
and corr_params.base_metric offers lift although CorrelationFilter supports only iv and ks (lift raises
KeyError). Offer a JSON text area as a fallback.
Extract All Schemas¶
all_schemas = extract_pipeline_schema()
credit_schema = all_schemas["pipelines"]["credit_model"]
print(list(all_schemas["pipelines"]))
print(len(credit_schema["fields"]))
extract_schema("credit_model") is a compatibility helper that returns only the fields list.
Field metadata objects¶
get_config_field_meta returns the same information as FieldMeta objects keyed by field name, without the name, type,
default, and has_default keys. FieldMeta.to_dict() converts one back to a dictionary.
meta = get_config_field_meta("credit_model")
print(type(meta["target_col"]).__name__, meta["target_col"].widget, meta["target_col"].required)
print(sorted(meta["target_col"].to_dict()))
YAML Export¶
from Modeling_Tool.Pipeline import MockSamplePipelineConfig
cfg = MockSamplePipelineConfig(
n_samples=80000,
applied_sample=1,
)
yaml_text = config_to_yaml(cfg)
print(yaml_text)
The output (smf_version is the installed SMF version):
pipeline: mock_sample
pipeline_class: MockSamplePipeline
config_class: MockSamplePipelineConfig
smf_version: 0.8.2
config:
n_samples: 80000
applied_sample: 1
approve_rate: 0.25
num_online_scores: 5
y_flag_candidates:
- 15
- 30
- 45
num_features: 20
min_num_feature_business_type: 5
random_state: 42
observation_timestamp: null
application_months: 18
write_csv: false
output_path: output/mock_sample/mock_sample.csv
config_to_yaml writes every serializable field, defaults included, so the file is a complete snapshot of the config.
Keep include_non_serializable at its default for YAML: the option writes raw objects that yaml.safe_dump cannot always
represent. config_to_yaml needs PyYAML at call time and raises ImportError without it.
YAML Import¶
When the first argument is None, SMF resolves the Config class from the pipeline or config_class entry of the YAML.
You can also pass a key:
By default (strict=True), a key that is not a field of the Config raises KeyError. With strict=False, unknown keys are
dropped. The round trip is exact except that tuples and ranges are written as lists, so range(3000, 3020), the default of
SampleAnalysisPipelineConfig.random_seeds, comes back as a list.
Dict Export¶
from Modeling_Tool import config_to_dict
payload = config_to_dict(cfg)
print(payload["n_samples"], payload["applied_sample"])
By default, fields that cannot be serialized are skipped. This keeps GUI and YAML payloads safe. A field is skipped when the
schema marks it yaml_serializable=False, or when its value cannot be converted. The code-only fields are:
| Pipeline | Code-only fields |
|---|---|
credit_model |
screening_artifact, feature_validation_result, extra_eval_datasets |
feature_validation |
psi_reference_data |
reject_inference |
oot_data, ri_approved_data, ri_approved_func |
score_comparison |
gains_add_func |
score_consistency_uat |
sqlrunner, offline_data, online_data |
These fields remain usable in Python code, but a generic GUI form should not edit them. Two options change the result:
exclude_none=True drops fields whose value is None, and include_non_serializable=True keeps every field, with the
repr() text for values that cannot be converted.
config_from_dict(config_class_or_key, payload, *, strict=True) is the inverse. It accepts the plain dictionary or the whole
YAML payload (the fields are read from its config entry), and strict behaves as for YAML.
from Modeling_Tool import config_from_dict
same_cfg = config_from_dict("mock_sample", payload)
print(same_cfg == cfg)
Config Validation¶
from Modeling_Tool import validate_pipeline_config
messages = validate_pipeline_config(
"credit_model",
{
"target_col": "badflag",
"warm_start_enabled": True,
"warm_start_score_col": None,
},
)
for msg in messages:
print(msg)
This prints one message, warm_start_score_col is required when warm_start_enabled is on. The
validator returns a list of strings, errors first, then warnings, each prefixed with WARNING:. An empty list means it
found nothing. values can be a dictionary or a Config instance. A key you leave out counts as unset: a required key such as
target_col is reported as empty, and the others fall back to the Config default. Unknown keys are not reported (use
config_from_dict for that).
The validator is intentionally lightweight. It catches common form mistakes before code generation, while each Pipeline's
run() method remains the authoritative runtime validation. The checks are:
| Pipeline | Error | Warning |
|---|---|---|
credit_model |
target_col empty; warm_start_enabled without warm_start_score_col; optuna_n_trials below 1; lr_search_params keys other than objective, primary_set, gap_ref_sets, metric, refit, verbose |
optuna_n_trials below 5 |
feature_validation |
feature_batch_size not positive; enable_batch without feature_batch_size or feature_batches; batch_corr_mode="block_pairwise" with corr_params["method"]="kendall" |
enable_batch=False while batch settings are present |
reject_inference |
approved_col or target_col empty; no ri_methods; train_prescore=False without score_col; both ri_approved_frac and ri_approved_n set |
|
score_comparison |
target_col empty; invalid group_specs; a cross_metrics entry that is not a [column, aggregation] pair; pairwise_cross_agg_dict that is not a dictionary |
Neither score_cols nor base_score (the Pipeline then detects scores itself) |
score_consistency_uat |
main_model_score_col empty; numeric_coercion_mode not safe, aggressive, or off; comparison_block_size not positive |
|
sample_analysis |
target_cols or time_col empty; materialize_split=True without id_col |
|
mock_sample |
n_samples below 1; applied_sample not 0 or 1; min_num_feature_business_type above min(num_features, 10) |
n_samples below 1000 |
Code Generation¶
from Modeling_Tool import generate_pipeline_code
code = generate_pipeline_code(
"score_comparison",
{
"target_col": "badflag",
"score_cols": ["score_old", "score_new"],
"base_score": "score_old",
},
)
print(code)
The generated code:
from Modeling_Tool.Pipeline import ScoreComparisonPipeline, ScoreComparisonPipelineConfig
cfg = ScoreComparisonPipelineConfig(
target_col='badflag',
score_cols=['score_old', 'score_new'],
base_score='score_old',
)
pipeline = ScoreComparisonPipeline(cfg)
result = pipeline.run(data=your_dataframe)
For MockSamplePipeline and ScoreConsistencyUATPipeline, the generated code ends with result = pipeline.run(). For the
other five Pipelines it ends with result = pipeline.run(data=your_dataframe). Values are written with repr(), so they
must be Python literals, and field names are not checked: a misspelled name only fails when you run the generated code.
GUI Design Guidance¶
- Render the Pipeline cards from
get_pipeline_registry_schema(). - Render each form from
extract_pipeline_schema(key)["fields"], in section order bygroup. Collapseadvancedfields unless they are alsorequired, and ask forrequiredones. - Hide fields with
gui_editable=Falseunless the GUI has an explicit code-only advanced mode. - Do not put non-serializable fields in YAML exports.
- Render dictionary fields with
nested_fieldsas sub-forms when possible; otherwise, and for keys the hints do not list, use a JSON text area. - Run
validate_pipeline_configon every change, and treat its result as a form hint: the Pipeline'srun()does the real validation. - Show messages and labels through your own translation table, keyed on the registry
keyand the fieldname. - Treat the
FeatureScreeningArtifacthand-off as a Python-code workflow, not a YAML field. - Keep Streamlit or web UI dependencies outside the SMF main package.