"""Pure UI state and presentation helpers for the Guided Research Workbench. The module is intentionally independent from Gradio, gateway routing, and training code. It consumes only the stable UI contracts so state transitions and rendering can be tested without waking a backend. """ from __future__ import annotations import html import json import uuid from dataclasses import dataclass, field, replace from typing import Any, Mapping, Sequence from execution_metadata import normalize_execution_metadata from ui_contracts import ( CandidateVerification, CandidatePipeline, DecisionSource, DecisionTrace, ExperimentReflection, FeatureExtractionReport, MockResearchPlanProvider, ResearchPlan, ) WORKBENCH_STEPS = ( "data", "objective", "research", "pipeline", "compute", "review", "run", ) PIPELINE_NODE_IDS = ( "input", "preprocessing", "feature_extractor", "postprocessing", "model", "evaluation", ) PIPELINE_NODE_PRESENTATION = { "input": ("IN", "Input"), "preprocessing": ("PR", "Preprocessing"), "feature_extractor": ("FX", "Feature extractor"), "postprocessing": ("PO", "Post-processing"), "model": ("ML", "Model"), "evaluation": ("EV", "Evaluation"), } # Six stages share one row of the pipeline track, so the visible stage chip # needs labels that survive a ~130px card. The full names above still carry the # accessible name for each card. PIPELINE_NODE_SHORT_LABELS = { "input": "Input", "preprocessing": "Prepare", "feature_extractor": "Features", "postprocessing": "Transform", "model": "Model", "evaluation": "Evaluate", } SOURCE_LABELS = { DecisionSource.USER: "User", DecisionSource.PRESET: "Preset", DecisionSource.DETERMINISTIC_RULE: "Deterministic Rule", DecisionSource.BENCHMARK: "Benchmark", DecisionSource.RESEARCH_AGENT: "Research Agent", DecisionSource.LLM: "LLM", DecisionSource.FALLBACK: "Fallback", } _DATA_SOURCES = {"managed", "upload"} _DATA_MODALITIES = {"tabular", "image"} _AUTOMATIC_VALUES = {"", "auto", "automatic selection"} def _json_copy(value: Any) -> Any: return json.loads(json.dumps(value)) def _clean_text(value: Any, *, limit: int = 2_000) -> str: text = " ".join(str(value or "").split()) return text[:limit] def _positive_int(value: Any, field_name: str) -> int: if isinstance(value, bool): raise ValueError(f"{field_name} must be an integer") try: result = int(value) except (TypeError, ValueError) as exc: raise ValueError(f"{field_name} must be an integer") from exc if result < 1: raise ValueError(f"{field_name} must be positive") return result def _constraints(value: Any) -> tuple[str, ...]: if value is None: return () if isinstance(value, str): value = [value] if not isinstance(value, (list, tuple)): raise ValueError("constraints must be an array of strings") cleaned = tuple(_clean_text(item, limit=500) for item in value) return tuple(item for item in cleaned if item) def candidate_verification_map( plan: ResearchPlan, ) -> dict[str, CandidateVerification]: """Return the canonical verifier result for each candidate, if present.""" if plan.verification is None: return {} return { item.candidate_id: item for item in plan.verification.candidate_results } def candidate_is_executable(plan: ResearchPlan, candidate_id: str) -> bool: """Treat verifier rejection as a public UI block, never as execution authority.""" verification = candidate_verification_map(plan).get(_clean_text(candidate_id)) return verification is None or ( verification.compatible and verification.status in {"approved", "revised"} ) @dataclass(frozen=True) class RunVariant: """Serializable snapshot for one member of a two-run plan.""" variant_id: str data_source: str data_modality: str candidate_id: str dataset: str target: str trials: int preset: str time_limit: int image_backend: str research_plan: Mapping[str, Any] feature_report: Mapping[str, Any] approved_research: Mapping[str, Any] def to_dict(self) -> dict[str, Any]: return { "variant_id": self.variant_id, "data_source": self.data_source, "data_modality": self.data_modality, "candidate_id": self.candidate_id, "dataset": self.dataset, "target": self.target, "trials": self.trials, "preset": self.preset, "time_limit": self.time_limit, "image_backend": self.image_backend, "research_plan": _json_copy(dict(self.research_plan)), "feature_report": _json_copy(dict(self.feature_report)), "approved_research": _json_copy(dict(self.approved_research)), } @dataclass(frozen=True) class PipelineNodeView: """Presentation-ready projection of one pipeline stage.""" node_id: str label: str component: str source: DecisionSource parameters: tuple[tuple[str, str], ...] input_shape: tuple[int, ...] output_shape: tuple[int, ...] compatibility: str rationale: str alternatives: tuple[str, ...] trace: DecisionTrace feature_report: FeatureExtractionReport | None = None @dataclass(frozen=True) class WorkbenchState: """Immutable, JSON-serializable wizard state.""" schema_version: int = 1 step: str = "data" data_source: str = "managed" data_modality: str = "tabular" dataset_name: str = "" upload_type: str = "Tabular CSV" target: str = "" task_type: str = "classification" review_metric: str = "accuracy" user_objective: str = "" constraints: tuple[str, ...] = () research_plan: ResearchPlan | None = None selected_candidate_id: str = "" approved_candidate_id: str = "" approved_pipeline_config: Mapping[str, Any] = field(default_factory=dict) approved_evidence_ids: tuple[str, ...] = () approval_trace: DecisionTrace | None = None approval_error: str = "" selected_node_id: str = "input" preset: str = "medium_quality" time_limit: int = 300 trials: int = 1 image_backend: str = "Auto" feature_tool: str = "Automatic selection" transform_tool: str = "Automatic selection" ml_tool: str = "Automatic selection" variants: tuple[RunVariant, ...] = () provider_error: str = "" def __post_init__(self) -> None: if self.schema_version != 1: raise ValueError( f"unsupported workbench schema_version {self.schema_version!r}" ) if self.step not in WORKBENCH_STEPS: raise ValueError(f"unknown workbench step {self.step!r}") if self.data_source not in _DATA_SOURCES: raise ValueError(f"unknown data source {self.data_source!r}") if self.data_modality not in _DATA_MODALITIES: raise ValueError(f"unknown data modality {self.data_modality!r}") if self.selected_node_id not in PIPELINE_NODE_IDS: raise ValueError(f"unknown pipeline node {self.selected_node_id!r}") object.__setattr__(self, "constraints", _constraints(self.constraints)) if not isinstance(self.approved_pipeline_config, Mapping): raise ValueError("approved_pipeline_config must be a mapping") object.__setattr__( self, "approved_pipeline_config", _json_copy(dict(self.approved_pipeline_config)), ) object.__setattr__( self, "approved_evidence_ids", tuple(_clean_text(item, limit=200) for item in self.approved_evidence_ids), ) if self.approval_trace is not None and not isinstance( self.approval_trace, DecisionTrace ): raise ValueError("approval_trace must be a DecisionTrace or null") object.__setattr__(self, "time_limit", _positive_int(self.time_limit, "time_limit")) object.__setattr__(self, "trials", _positive_int(self.trials, "trials")) object.__setattr__(self, "variants", tuple(self.variants)) @property def current_candidate(self) -> CandidatePipeline: if self.research_plan is None: raise ValueError("Generate a research plan before selecting a candidate.") for candidate in self.research_plan.candidates: if candidate.candidate_id == self.selected_candidate_id: return candidate raise ValueError("The selected candidate is not present in the research plan.") def to_dict(self) -> dict[str, Any]: return { "schema_version": self.schema_version, "step": self.step, "data_source": self.data_source, "data_modality": self.data_modality, "dataset_name": self.dataset_name, "upload_type": self.upload_type, "target": self.target, "task_type": self.task_type, "review_metric": self.review_metric, "user_objective": self.user_objective, "constraints": list(self.constraints), "research_plan": ( self.research_plan.to_dict() if self.research_plan else None ), "selected_candidate_id": self.selected_candidate_id, "approved_candidate_id": self.approved_candidate_id, "approved_pipeline_config": _json_copy( dict(self.approved_pipeline_config) ), "approved_evidence_ids": list(self.approved_evidence_ids), "approval_trace": ( self.approval_trace.to_dict() if self.approval_trace else None ), "approval_error": self.approval_error, "selected_node_id": self.selected_node_id, "preset": self.preset, "time_limit": self.time_limit, "trials": self.trials, "image_backend": self.image_backend, "feature_tool": self.feature_tool, "transform_tool": self.transform_tool, "ml_tool": self.ml_tool, "variants": [variant.to_dict() for variant in self.variants], "provider_error": self.provider_error, } def _variant_from_mapping(value: Mapping[str, Any]) -> RunVariant: if not isinstance(value, Mapping): raise ValueError("run variant must be a mapping") data_source = _clean_text(value.get("data_source") or "managed").lower() if data_source not in _DATA_SOURCES: raise ValueError("run variant data_source is invalid") research_plan = value.get("research_plan") or {} feature_report = value.get("feature_report") or {} approved_research = value.get("approved_research") or {} if not isinstance(research_plan, Mapping) or not isinstance( feature_report, Mapping ) or not isinstance(approved_research, Mapping): raise ValueError("run variant snapshots must be mappings") return RunVariant( variant_id=_clean_text(value.get("variant_id")), data_source=data_source, data_modality=_clean_text( value.get("data_modality") or "tabular" ).lower(), candidate_id=_clean_text(value.get("candidate_id")), dataset=_clean_text(value.get("dataset")), target=_clean_text(value.get("target")), trials=_positive_int(value.get("trials", 1), "trials"), preset=_clean_text(value.get("preset") or "medium_quality"), time_limit=_positive_int(value.get("time_limit", 300), "time_limit"), image_backend=_clean_text(value.get("image_backend") or "Auto"), research_plan=_json_copy(dict(research_plan)), feature_report=_json_copy(dict(feature_report)), approved_research=_json_copy(dict(approved_research)), ) def state_from_mapping(value: Mapping[str, Any] | WorkbenchState) -> WorkbenchState: """Rebuild state from the plain mapping kept by ``gr.State``.""" if isinstance(value, WorkbenchState): return value if not isinstance(value, Mapping): raise ValueError("workbench state must be a mapping") version = value.get("schema_version", 1) if version != 1: raise ValueError(f"unsupported workbench schema_version {version!r}") raw_plan = value.get("research_plan") plan = ( None if raw_plan in (None, {}) else ResearchPlan.from_mapping(raw_plan) ) raw_variants = value.get("variants") or () if not isinstance(raw_variants, (list, tuple)): raise ValueError("variants must be an array") raw_approval_trace = value.get("approval_trace") approval_trace = ( None if raw_approval_trace in (None, {}) else DecisionTrace.from_mapping(raw_approval_trace) ) approved_config = value.get("approved_pipeline_config") or {} if not isinstance(approved_config, Mapping): raise ValueError("approved_pipeline_config must be a mapping") raw_evidence_ids = value.get("approved_evidence_ids") or () if not isinstance(raw_evidence_ids, (list, tuple)) or not all( isinstance(item, str) for item in raw_evidence_ids ): raise ValueError("approved_evidence_ids must be an array of strings") return WorkbenchState( step=_clean_text(value.get("step") or "data").lower(), data_source=_clean_text(value.get("data_source") or "managed").lower(), data_modality=_clean_text( value.get("data_modality") or "tabular" ).lower(), dataset_name=_clean_text(value.get("dataset_name")), upload_type=_clean_text(value.get("upload_type") or "Tabular CSV"), target=_clean_text(value.get("target")), task_type=_clean_text(value.get("task_type") or "classification"), review_metric=_clean_text(value.get("review_metric") or "accuracy"), user_objective=_clean_text(value.get("user_objective"), limit=1_000), constraints=_constraints(value.get("constraints")), research_plan=plan, selected_candidate_id=_clean_text(value.get("selected_candidate_id")), approved_candidate_id=_clean_text(value.get("approved_candidate_id")), approved_pipeline_config=_json_copy(dict(approved_config)), approved_evidence_ids=tuple(raw_evidence_ids), approval_trace=approval_trace, approval_error=_clean_text(value.get("approval_error")), selected_node_id=_clean_text( value.get("selected_node_id") or "input" ).lower(), preset=_clean_text(value.get("preset") or "medium_quality"), time_limit=_positive_int(value.get("time_limit", 300), "time_limit"), trials=_positive_int(value.get("trials", 1), "trials"), image_backend=_clean_text(value.get("image_backend") or "Auto"), feature_tool=_clean_text( value.get("feature_tool") or "Automatic selection" ), transform_tool=_clean_text( value.get("transform_tool") or "Automatic selection" ), ml_tool=_clean_text(value.get("ml_tool") or "Automatic selection"), variants=tuple(_variant_from_mapping(item) for item in raw_variants), provider_error=_clean_text(value.get("provider_error")), ) def new_workbench_state( *, data_source: str = "managed", data_modality: str = "tabular", dataset_name: str = "", upload_type: str = "Tabular CSV", target: str = "", task_type: str = "classification", user_objective: str = "", ) -> WorkbenchState: return WorkbenchState( data_source=_clean_text(data_source).lower() or "managed", data_modality=_clean_text(data_modality).lower() or "tabular", dataset_name=_clean_text(dataset_name), upload_type=_clean_text(upload_type) or "Tabular CSV", target=_clean_text(target), task_type=_clean_text(task_type) or "classification", user_objective=_clean_text(user_objective, limit=1_000), ) def update_context( state: Mapping[str, Any] | WorkbenchState, **changes: Any ) -> WorkbenchState: current = state_from_mapping(state) allowed = { "data_source", "data_modality", "dataset_name", "upload_type", "target", "task_type", "review_metric", "user_objective", "constraints", "preset", "time_limit", "trials", "image_backend", "feature_tool", "transform_tool", "ml_tool", } unknown = set(changes) - allowed if unknown: raise ValueError(f"unknown workbench field {sorted(unknown)[0]!r}") normalized = dict(changes) for name in ( "data_source", "data_modality", "dataset_name", "upload_type", "target", "task_type", "review_metric", "user_objective", "preset", "image_backend", "feature_tool", "transform_tool", "ml_tool", ): if name in normalized: normalized[name] = _clean_text(normalized[name]) if "data_source" in normalized: normalized["data_source"] = normalized["data_source"].lower() if "data_modality" in normalized: normalized["data_modality"] = normalized["data_modality"].lower() if "constraints" in normalized: normalized["constraints"] = _constraints(normalized["constraints"]) if "time_limit" in normalized: normalized["time_limit"] = _positive_int( normalized["time_limit"], "time_limit" ) if "trials" in normalized: normalized["trials"] = _positive_int(normalized["trials"], "trials") research_fields = { "data_source", "data_modality", "dataset_name", "upload_type", "target", "task_type", "review_metric", "user_objective", "constraints", "time_limit", } if any( name in normalized and getattr(current, name) != normalized[name] for name in research_fields ): normalized.update( research_plan=None, selected_candidate_id="", approved_candidate_id="", approved_pipeline_config={}, approved_evidence_ids=(), approval_trace=None, approval_error="", variants=(), provider_error="", ) approval_fields = { "image_backend", "feature_tool", "transform_tool", "ml_tool", } if any( name in normalized and getattr(current, name) != normalized[name] for name in approval_fields ): normalized.update( approved_candidate_id="", approved_pipeline_config={}, approved_evidence_ids=(), approval_trace=None, approval_error="", ) return replace(current, **normalized) def set_step( state: Mapping[str, Any] | WorkbenchState, step: str ) -> WorkbenchState: current = state_from_mapping(state) normalized = _clean_text(step).lower() if normalized not in WORKBENCH_STEPS: raise ValueError(f"unknown workbench step {normalized!r}") return replace(current, step=normalized) def _fallback_plan(context: Mapping[str, Any]) -> ResearchPlan: target = _clean_text(context.get("target_column") or "target") task = _clean_text(context.get("task_type") or "classification") return ResearchPlan.from_mapping( { "schema_version": 1, "plan_id": "fallback-research-plan-v1", "task_summary": f"Model {target} as {task} with automatic components.", "candidates": [ { "candidate_id": "fallback-automatic-pipeline", "name": "Conservative automatic pipeline", "task_type": task, "stages": [ {"stage": "preprocess", "component": "automatic"}, {"stage": "model", "component": "automatic"}, ], "rationale": "Preserves safe existing automatic defaults.", "expected_tradeoffs": [ "No agent comparison is available for this fallback." ], "expected_metrics": { "relative_quality": { "kind": "expected", "estimate": "unavailable", "basis": "fallback", } }, "measured_metrics": {}, "warnings": ["Research planning was unavailable."], } ], "recommendation": { "candidate_id": "fallback-automatic-pipeline", "rationale": "Use existing automatic defaults.", }, "warnings": [ "This fallback contains no measured results or live agent evidence." ], "decision_provenance": [ { "schema_version": 1, "trace_id": "fallback-plan-selection", "decision_type": "recommend_candidate", "decision_source": "fallback", "selected": "fallback-automatic-pipeline", "options": ["fallback-automatic-pipeline"], "rationale": "The research provider was unavailable.", } ], "provider_provenance": { "provider": "fallback", "network_used": False, "llm_used": False, }, } ) def _plan_with_constraint_recommendation( plan: ResearchPlan, constraints: Sequence[str] ) -> ResearchPlan: combined = " ".join(constraints).lower() if not any(word in combined for word in ("interpret", "runtime", "fast")): return plan preferred = next( ( candidate for candidate in plan.candidates if "interpret" in ( f"{candidate.candidate_id} {candidate.name}" ).lower() ), None, ) if preferred is None: preferred = next( ( candidate for candidate in plan.candidates if any( word in f"{candidate.candidate_id} {candidate.name}".lower() for word in ("efficient", "inspectable") ) ), None, ) if preferred is None: return plan data = plan.to_dict() data["recommendation"] = { "candidate_id": preferred.candidate_id, "rationale": ( "Selected by a deterministic refinement rule for the stated constraint." ), } data["decision_provenance"] = [ *data.get("decision_provenance", []), { "schema_version": 1, "trace_id": "mock-refinement-rule", "decision_type": "refine_recommendation", "decision_source": "deterministic_rule", "selected": preferred.candidate_id, "options": [item.candidate_id for item in plan.candidates], "rationale": "The refinement prioritizes interpretability or runtime.", }, ] return ResearchPlan.from_mapping(data) def generate_research_plan( state: Mapping[str, Any] | WorkbenchState, provider: Any | None = None, ) -> WorkbenchState: current = state_from_mapping(state) selected_provider = provider or MockResearchPlanProvider() context = { "dataset_name": current.dataset_name or current.upload_type, "modality": current.data_modality, "task_type": current.task_type, "target_column": current.target, "constraints": list(current.constraints), } try: plan = selected_provider.create_plan(context) if not isinstance(plan, ResearchPlan): plan = ResearchPlan.from_mapping(plan) plan = _plan_with_constraint_recommendation(plan, current.constraints) provider_error = "" except Exception: plan = _fallback_plan(context) provider_error = ( "Research planning was unavailable. " "A conservative fallback plan is shown." ) recommended = _clean_text(plan.recommendation.get("candidate_id")) executable_ids = [ item.candidate_id for item in plan.candidates if candidate_is_executable(plan, item.candidate_id) ] if recommended not in executable_ids: recommended = executable_ids[0] if executable_ids else "" return replace( current, research_plan=plan, selected_candidate_id=recommended, approved_candidate_id="", approved_pipeline_config={}, approved_evidence_ids=(), approval_trace=None, approval_error="", provider_error=provider_error, ) def apply_research_plan( state: Mapping[str, Any] | WorkbenchState, plan_value: Mapping[str, Any] | ResearchPlan, *, provider_error: str = "", ) -> WorkbenchState: """Apply one validated real plan while invalidating every prior approval.""" current = state_from_mapping(state) plan = ( plan_value if isinstance(plan_value, ResearchPlan) else ResearchPlan.from_mapping(plan_value) ) if not 1 <= len(plan.candidates) <= 3: raise ValueError("research plan must contain one to three candidates") recommended = _clean_text(plan.recommendation.get("candidate_id")) candidate_ids = {item.candidate_id for item in plan.candidates} executable_ids = [ item.candidate_id for item in plan.candidates if candidate_is_executable(plan, item.candidate_id) ] if recommended not in candidate_ids or recommended not in executable_ids: recommended = executable_ids[0] if executable_ids else "" return replace( current, research_plan=plan, approved_candidate_id="", approved_pipeline_config={}, approved_evidence_ids=(), approval_trace=None, approval_error="", variants=(), provider_error=_clean_text(provider_error, limit=500), selected_candidate_id="", ) def apply_research_error( state: Mapping[str, Any] | WorkbenchState, message: str ) -> WorkbenchState: """Fail closed when the real research provider is unavailable or invalid.""" current = state_from_mapping(state) return replace( current, research_plan=None, selected_candidate_id="", approved_candidate_id="", approved_pipeline_config={}, approved_evidence_ids=(), approval_trace=None, approval_error="", variants=(), provider_error=_clean_text(message, limit=500), ) def apply_candidate_approval( state: Mapping[str, Any] | WorkbenchState, approval_value: Mapping[str, Any], ) -> WorkbenchState: """Apply a private-backend registry approval to the selected candidate.""" current = state_from_mapping(state) if current.research_plan is None or not current.selected_candidate_id: raise ValueError("select a research candidate before approval") if not isinstance(approval_value, Mapping): raise ValueError("candidate approval must be a mapping") approved_id = _clean_text(approval_value.get("approved_candidate_id")) plan_id = _clean_text(approval_value.get("plan_id")) if approved_id != current.selected_candidate_id: raise ValueError("approval does not match the selected candidate") if plan_id != current.research_plan.plan_id: raise ValueError("approval does not match the active research plan") if not candidate_is_executable(current.research_plan, approved_id): raise ValueError("candidate verification does not permit execution") config = approval_value.get("pipeline_config") evidence_ids = approval_value.get("evidence_ids") trace_value = approval_value.get("decision_trace") if not isinstance(config, Mapping): raise ValueError("approved pipeline config must be a mapping") if not isinstance(evidence_ids, (list, tuple)) or not all( isinstance(item, str) for item in evidence_ids ): raise ValueError("approved evidence IDs must be an array of strings") if not isinstance(trace_value, Mapping): raise ValueError("approval decision trace is required") trace = DecisionTrace.from_mapping(trace_value) if trace.decision_source is not DecisionSource.RESEARCH_AGENT: raise ValueError("approval trace must come from the research agent") if trace.selected != approved_id: raise ValueError("approval trace selected value does not match") config_copy = _json_copy(dict(config)) if config_copy.get("candidate_id") not in (None, approved_id): raise ValueError("approved pipeline config references another candidate") if config_copy.get("data_modality") not in (None, current.data_modality): raise ValueError("approved pipeline config has an incompatible modality") candidate = current.current_candidate known_evidence = { str(item.get("evidence_id")) for item in current.research_plan.evidence if isinstance(item, Mapping) and item.get("evidence_id") } if not set(evidence_ids) <= known_evidence: raise ValueError("approval references unknown research evidence") changes: dict[str, Any] = { "approved_candidate_id": approved_id, "approved_pipeline_config": config_copy, "approved_evidence_ids": tuple(evidence_ids), "approval_trace": trace, "approval_error": "", } control_fields = { "feature_selection_tool": "feature_tool", "transform_tool": "transform_tool", "ml_tool": "ml_tool", "image_backend": "image_backend", } for config_name, state_name in control_fields.items(): value = config_copy.get(config_name) if isinstance(value, str) and value.strip(): changes[state_name] = _clean_text(value) if not set(candidate.evidence_ids) >= set(evidence_ids): raise ValueError("approval contains evidence not used by the candidate") return replace(current, **changes) def apply_approval_error( state: Mapping[str, Any] | WorkbenchState, message: str ) -> WorkbenchState: current = state_from_mapping(state) return replace( current, approved_candidate_id="", approved_pipeline_config={}, approved_evidence_ids=(), approval_trace=None, approval_error=_clean_text(message, limit=500), ) def approved_research_metadata( state: Mapping[str, Any] | WorkbenchState, ) -> dict[str, Any]: """Return the optional canonical metadata carried into an approved run.""" current = state_from_mapping(state) if ( current.research_plan is None or current.approved_candidate_id != current.selected_candidate_id or not current.approved_pipeline_config or current.approval_trace is None ): return {} return { "schema_version": 1, "research_plan": current.research_plan.to_dict(), "approved_candidate_id": current.approved_candidate_id, "pipeline_config": _json_copy(dict(current.approved_pipeline_config)), "evidence_ids": list(current.approved_evidence_ids), "approval_trace": current.approval_trace.to_dict(), } def select_candidate( state: Mapping[str, Any] | WorkbenchState, candidate_id: str ) -> WorkbenchState: current = state_from_mapping(state) selected = _clean_text(candidate_id) if current.research_plan is None or selected not in { candidate.candidate_id for candidate in current.research_plan.candidates }: raise ValueError(f"unknown candidate_id {selected!r}") if not candidate_is_executable(current.research_plan, selected): raise ValueError("candidate verification does not permit execution") preset = current.preset if "interpretable" in selected.lower(): preset = "interpretable" elif "balanced" in selected.lower(): preset = "medium_quality" return replace( current, selected_candidate_id=selected, preset=preset, approved_candidate_id="", approved_pipeline_config={}, approved_evidence_ids=(), approval_trace=None, approval_error="", ) def apply_next_experiment_draft( state: Mapping[str, Any] | WorkbenchState, reflection: Mapping[str, Any] | ExperimentReflection, suggestion_id: str, ) -> WorkbenchState: """Create a fresh unapproved objective draft without execution authority.""" current = state_from_mapping(state) canonical = ( reflection if isinstance(reflection, ExperimentReflection) else ExperimentReflection.from_mapping(reflection) ) selected_id = _clean_text(suggestion_id) suggestion = next( (item for item in canonical.suggestions if item.suggestion_id == selected_id), None, ) if suggestion is None: raise ValueError(f"unknown next experiment suggestion {selected_id!r}") constraints: tuple[str, ...] = () if suggestion.executable and not suggestion.suggestion_only: components = tuple( _clean_text(stage.get("component"), limit=200) for stage in suggestion.stages if isinstance(stage, Mapping) and stage.get("component") ) constraints = tuple( dict.fromkeys( ( *(f"Trusted component: {component}" for component in components), *suggestion.expected_tradeoffs, ) ) ) drafted = update_context( current, user_objective=suggestion.title, constraints=constraints, ) # Clear explicitly even if a package repeats the current objective text. drafted = replace( drafted, research_plan=None, selected_candidate_id="", approved_candidate_id="", approved_pipeline_config={}, approved_evidence_ids=(), approval_trace=None, approval_error="", variants=(), provider_error="", ) return set_step(drafted, "objective") def apply_refinement( state: Mapping[str, Any] | WorkbenchState, refinement: str ) -> WorkbenchState: current = state_from_mapping(state) text = _clean_text(refinement, limit=500) if not text: return current refined = replace(current, constraints=(*current.constraints, text)) return generate_research_plan(refined) def select_node( state: Mapping[str, Any] | WorkbenchState, node_id: str ) -> WorkbenchState: current = state_from_mapping(state) selected = _clean_text(node_id).lower() if selected not in PIPELINE_NODE_IDS: raise ValueError(f"unknown pipeline node {selected!r}") return replace(current, selected_node_id=selected) def validate_state( state: Mapping[str, Any] | WorkbenchState, ) -> tuple[str, ...]: current = state_from_mapping(state) issues: list[str] = [] if current.data_source == "managed" and not current.dataset_name: issues.append("Data: choose a managed dataset.") if current.data_source == "upload" and not current.upload_type: issues.append("Data: choose an upload type.") if not current.target: issues.append("Objective: choose an outcome to predict.") if current.research_plan is None: issues.append("Research: generate a research plan.") elif not current.selected_candidate_id: issues.append("Research: select a candidate pipeline.") else: candidate_ids = { candidate.candidate_id for candidate in current.research_plan.candidates } if current.selected_candidate_id not in candidate_ids: issues.append("Research: the selected candidate is no longer available.") elif not candidate_is_executable( current.research_plan, current.selected_candidate_id ): issues.append("Research: verifier rejected the selected candidate.") elif ( current.approved_candidate_id != current.selected_candidate_id or not current.approved_pipeline_config or current.approval_trace is None ): issues.append("Research: approve the selected candidate before Run.") if not 60 <= current.time_limit <= 3_600: issues.append("Compute: time budget must be between 60 and 3600 seconds.") if not 1 <= current.trials <= 4: issues.append("Compute: candidate runs must be between 1 and 4.") return tuple(issues) def _planned_feature_report(state: WorkbenchState) -> FeatureExtractionReport: method = state.feature_tool if method.lower() in _AUTOMATIC_VALUES: method = "Automatic feature extraction" return FeatureExtractionReport.from_mapping( { "schema_version": 1, "report_id": "planned-feature-report", "status": "planned", "modality": state.data_modality, "method": method, "input_shape": [], "output_shape": [], "features_per_sample": None, "parameters": {"selection": state.feature_tool}, "warnings": [ "Execution shapes are unavailable until the backend reports them." ], "provenance": { "phase": "planned", "source": ( "user" if state.feature_tool.lower() not in _AUTOMATIC_VALUES else "deterministic_rule" ), }, } ) def _candidate_stage_component( candidate: CandidatePipeline | None, stage_names: set[str], fallback: str ) -> str: if candidate is None: return fallback for stage in candidate.stages: stage_name = _clean_text(stage.get("stage")).lower() if stage_name in stage_names: component = _clean_text(stage.get("component")) if component: return component.replace("_", " ").title() return fallback def _is_user_selection(value: str) -> bool: return _clean_text(value).lower() not in _AUTOMATIC_VALUES def _node_trace( node_id: str, component: str, source: DecisionSource, rationale: str, alternatives: Sequence[str], parameters: Sequence[tuple[str, str]], ) -> DecisionTrace: return DecisionTrace.from_mapping( { "schema_version": 1, "trace_id": f"planned-{node_id}", "decision_type": f"select_{node_id}", "stage": node_id, "decision_source": source.value, "selected": component, "options": list(alternatives), "rationale": rationale, "parameters": dict(parameters), "provenance": {"phase": "planned"}, } ) def pipeline_nodes( state: Mapping[str, Any] | WorkbenchState, ) -> tuple[PipelineNodeView, ...]: current = state_from_mapping(state) try: candidate = current.current_candidate except ValueError: candidate = None planned_report = _planned_feature_report(current) has_plan = candidate is not None input_component = ( current.dataset_name if current.data_source == "managed" else current.upload_type ) or "Data selection pending" preprocessing = _candidate_stage_component( candidate, {"preprocess", "preprocessing"}, "Automatic preprocessing", ) feature_component = planned_report.method postprocessing = ( current.transform_tool if _is_user_selection(current.transform_tool) else "Automatic post-processing" ) model = _candidate_stage_component( candidate, {"model", "training"}, current.ml_tool if _is_user_selection(current.ml_tool) else "Model selection pending", ) evaluation = f"{current.review_metric.title()} review" definitions = ( ( "input", "Input", input_component, DecisionSource.USER, (("source", current.data_source),), "The user selected the data source for this run.", ("Managed dataset", "Upload"), "compatible" if input_component != "Data selection pending" else "blocked", ), ( "preprocessing", "Preprocessing", preprocessing, DecisionSource.DETERMINISTIC_RULE, (("task", current.task_type),), "A deterministic compatibility rule supplies preprocessing defaults.", ("Automatic preprocessing", "Backend-compatible preprocessing"), "compatible" if has_plan else "review", ), ( "feature_extractor", "Feature Extractor", feature_component, ( DecisionSource.USER if _is_user_selection(current.feature_tool) else DecisionSource.DETERMINISTIC_RULE ), (("selection", current.feature_tool),), ( "The user selected this feature method." if _is_user_selection(current.feature_tool) else "A deterministic rule defers feature selection to the backend." ), ( "Automatic feature extraction", "No feature selection", "T-test feature selection", ), "compatible" if has_plan else "review", ), ( "postprocessing", "Post-processing", postprocessing, ( DecisionSource.USER if _is_user_selection(current.transform_tool) else DecisionSource.PRESET ), (("selection", current.transform_tool),), ( "The user selected this post-processing method." if _is_user_selection(current.transform_tool) else "The active preset supplies post-processing defaults." ), ( "Automatic post-processing", "No transformation", "PCA transformation", ), "compatible" if has_plan else "review", ), ( "model", "Model", model, ( DecisionSource.USER if _is_user_selection(current.ml_tool) else DecisionSource.PRESET ), (("quality_profile", current.preset),), ( "The user selected the training engine." if _is_user_selection(current.ml_tool) else "The selected candidate and quality preset define the model route." ), tuple( item.name for item in current.research_plan.candidates ) if current.research_plan else ("Generate a research plan",), "compatible" if has_plan else "blocked", ), ( "evaluation", "Evaluation", evaluation, DecisionSource.USER, (("review_priority", current.review_metric),), "The user selected which reported metric to emphasize during review.", ("Accuracy", "F1", "ROC AUC", "Backend-reported primary metric"), "compatible" if current.target else "blocked", ), ) result: list[PipelineNodeView] = [] for ( node_id, label, component, source, parameters, rationale, alternatives, compatibility, ) in definitions: trace = _node_trace( node_id, component, source, rationale, alternatives, parameters, ) result.append( PipelineNodeView( node_id=node_id, label=label, component=component, source=source, parameters=tuple(parameters), input_shape=(), output_shape=(), compatibility=compatibility, rationale=rationale, alternatives=tuple(alternatives), trace=trace, feature_report=( planned_report if node_id == "feature_extractor" else None ), ) ) return tuple(result) def _variant_signature(variant: RunVariant) -> tuple[Any, ...]: return ( variant.data_source, variant.candidate_id, variant.dataset, variant.target, variant.trials, variant.preset, variant.time_limit, variant.image_backend, ) def add_variant( state: Mapping[str, Any] | WorkbenchState, ) -> WorkbenchState: current = state_from_mapping(state) if current.data_source != "managed": raise ValueError( "Parallel run plans currently support managed datasets only." ) issues = validate_state(current) if issues: raise ValueError(" ".join(issues)) if len(current.variants) >= 2: raise ValueError("A run plan supports at most two variants.") feature_report = _planned_feature_report(current) variant = RunVariant( variant_id=uuid.uuid4().hex, data_source=current.data_source, data_modality=current.data_modality, candidate_id=current.selected_candidate_id, dataset=current.dataset_name, target=current.target, trials=current.trials, preset=current.preset, time_limit=current.time_limit, image_backend=current.image_backend, research_plan=current.research_plan.to_dict(), feature_report=feature_report.to_dict(), approved_research=approved_research_metadata(current), ) if _variant_signature(variant) in { _variant_signature(item) for item in current.variants }: raise ValueError("This configuration is already in the run plan.") return replace(current, variants=(*current.variants, variant)) def source_badge_label(source: DecisionSource | str) -> str: try: normalized = DecisionSource(source) except (TypeError, ValueError) as exc: raise ValueError(f"unknown decision source {source!r}") from exc return SOURCE_LABELS[normalized] def _escape(value: Any) -> str: return html.escape(_clean_text(value), quote=True) def _list_html(values: Sequence[Any], *, empty: str = "None recorded") -> str: cleaned = [_clean_text(value) for value in values if _clean_text(value)] if not cleaned: return f'

{_escape(empty)}

' return "" def _mapping_rows(value: Mapping[str, Any]) -> str: if not value: return '

Unavailable

' rows = [] for key, item in value.items(): normalized_key = str(key).lower() if item is None: rendered = "Not reported" elif isinstance(item, (list, tuple)) and not item: rendered = ( ("Not reported" if value.get("status") == "completed" else "Available after training") if "shape" in normalized_key else "None recorded" ) elif isinstance(item, dict) and not item: rendered = "Not reported" elif item == "": rendered = "Not reported" elif isinstance(item, Mapping): rendered = "; ".join( f"{str(nested_key).replace('_', ' ').title()}: " f"{_clean_text(str(nested_value)) if nested_value is not None else 'Not reported'}" for nested_key, nested_value in item.items() ) elif isinstance(item, (list, tuple)): separator = " × " if "shape" in normalized_key else ", " rendered = separator.join(_clean_text(entry) for entry in item) else: rendered = str(item) rows.append( '
' f'{_escape(str(key).replace("_", " ").title())}' f'{_escape(rendered)}' "
" ) return "".join(rows) def _runtime_contracts( value: Mapping[str, Any] | None, ) -> tuple[FeatureExtractionReport | None, tuple[DecisionTrace, ...]]: """Validate optional terminal execution metadata for presentation.""" if not isinstance(value, Mapping): return None, () try: normalized = normalize_execution_metadata(value) except (TypeError, ValueError): return None, () report = None raw_report = normalized.get("feature_extraction_report") if isinstance(raw_report, Mapping): try: report = FeatureExtractionReport.from_mapping(raw_report) except (TypeError, ValueError): report = None traces: list[DecisionTrace] = [] raw_traces = normalized.get("decision_traces") or () for raw_trace in raw_traces: if not isinstance(raw_trace, Mapping): continue try: traces.append(DecisionTrace.from_mapping(raw_trace)) except (TypeError, ValueError): continue return report, tuple(traces) def _expected_metrics_html(candidate: CandidatePipeline) -> str: if not candidate.expected_metrics: return '

No estimates available

' cards = [] for name, value in candidate.expected_metrics.items(): estimate = value.get("estimate", "Unavailable") if isinstance(value, Mapping) else value basis = value.get("basis", "") if isinstance(value, Mapping) else "" cards.append( '
' 'Estimate · unmeasured' f'{_escape(str(name).replace("_", " ").title())}' f'{_escape(str(estimate) if estimate is not None else "Unavailable")}' f'{_escape(basis or "Planning estimate; not measured.")}' "
" ) return "".join(cards) def candidate_display_name(candidate: CandidatePipeline) -> str: """Readable labels for known deterministic defaults; identity stays canonical.""" names = { "no_feature_selection_tool + no_transform_tool + autogluon_tool": "AutoGluon baseline", "t_test_feature_selection_tool + no_transform_tool + autogluon_tool": "AutoGluon with feature selection", "no_feature_selection_tool + pca_transform_tool + autogluon_tool": "AutoGluon with PCA", } return names.get(candidate.name, candidate.name or candidate.candidate_id) def candidate_cards_html( state: Mapping[str, Any] | WorkbenchState, ) -> str: current = state_from_mapping(state) if current.research_plan is None: return ( '
' "

Research plan not generated

" "

Complete Data and Objective, then generate a plan.

" "
" ) constraints = ( '

Research constraints

' f'{_list_html(current.constraints, empty="No additional constraints")}
' ) cards = [] verifications = candidate_verification_map(current.research_plan) recommended_id = _clean_text( current.research_plan.recommendation.get("candidate_id") ) for index, candidate in enumerate(current.research_plan.candidates, start=1): selected = candidate.candidate_id == current.selected_candidate_id recommended = candidate.candidate_id == recommended_id verification = verifications.get(candidate.candidate_id) executable = candidate_is_executable( current.research_plan, candidate.candidate_id ) recommended_badge = ( 'Recommended' if recommended else "" ) verification_badge = "" verification_details = "" if verification is not None: verification_badge = ( 'Verifier · ' f'{_escape(verification.status.title())}' ) verification_details = ( '
' '

Verifier assessment

' f'

{_escape(verification.revised_rationale or verification.rationale)}

' '
' '

Weaknesses

' f'{_list_html(verification.weaknesses)}
' '

Verifier warnings

' f'{_list_html(verification.warnings)}
' ) execution_status = ( "Available after explicit approval" if executable else "Not executable · verifier rejected or marked incompatible" ) cards.append( f'
' f'
Candidate {index}
' f"{recommended_badge}{verification_badge}" f'

{_escape(candidate_display_name(candidate))}

' '

' f'{_escape(candidate.rationale or "No rationale provided")}

' '

Estimated metric values · unmeasured

' f'
{_expected_metrics_html(candidate)}
' '
Evidence and assessment' '

Registered pipeline

' f'

{_escape(candidate.name or candidate.candidate_id)}

' '
' '

Assumptions

' f'{_list_html(candidate.assumptions)}
' '

Expected tradeoffs

' f'{_list_html(candidate.expected_tradeoffs)}
' "
" '

Evidence references

' f'{_list_html(candidate.evidence_ids)}
' '

Warnings

' f'{_list_html(candidate.warnings)}
' f"{verification_details}
" '

' f'{_escape("Selected · " + execution_status if selected else execution_status)}

' "
" ) return ( '
' f"{constraints}{''.join(cards)}
" ) def approval_status_html( state: Mapping[str, Any] | WorkbenchState, ) -> str: current = state_from_mapping(state) if current.approval_error: return ( '
' f'{_escape(current.approval_error)}
' ) if ( current.approved_candidate_id and current.approved_candidate_id == current.selected_candidate_id and current.approval_trace is not None ): return ( '
' 'ApprovedRegistry validated · ' f'{_escape(source_badge_label(current.approval_trace.decision_source))}' "
" ) if current.selected_candidate_id: return ( '
' 'Approval required' 'Review and explicitly approve this candidate before Run.' "
" ) return ( '
' 'Select a candidate' 'Run remains disabled until registry validation and approval.' "
" ) def _evidence_card_html(item: Mapping[str, Any]) -> str: evidence_type = _clean_text(item.get("evidence_type") or "catalog") source = _clean_text(item.get("source") or "unknown") title = _clean_text( item.get("title") or item.get("name") or item.get("external_id") or item.get("evidence_id") ) detail_fields = ( "authors", "date", "external_id", "source_url", "source_reference", "query", "pipeline_stages", "modality", "task", "size", "target", "classes", "license", "authentication_required", "download_required", "architecture", "framework", "parameter_count", "memory_bytes", "compatibility", "executable", "suggestion_only", "status", ) details = { field: item[field] for field in detail_fields if field in item and item[field] not in (None, "", [], {}) } return ( '
' '
' f'{_escape(evidence_type.replace("_", " ").title())}' f'{_escape(source.replace("_", " ").title())}
' f'

{_escape(title)}

' f'

{_escape(item.get("summary") or "No summary provided")}

' '
Evidence ID' f'{_escape(item.get("evidence_id"))}
' f'{_mapping_rows(details)}
' ) def research_dossier_html( state: Mapping[str, Any] | WorkbenchState, ) -> str: current = state_from_mapping(state) if current.research_plan is None: provider_error = ( f'
{_escape(current.provider_error)}
' if current.provider_error else "" ) return ( '
' '
' '' '

Research assistant

' 'Private research backend
' '

Compare viable pipelines for your ' 'dataset, objective, and compute budget.

' '
' 'Research plan

Ready when your objective is set

' '

Generate a plan to review recommendations, tradeoffs, assumptions, ' 'and supporting evidence.

' f"{provider_error}" '

Research is generated only when ' "the private backend feature is enabled.

" ) plan = current.research_plan recommendation = plan.recommendation decision_provenance = tuple( " · ".join( part for part in ( source_badge_label(trace.decision_source), _clean_text(trace.stage or trace.decision_type or "Plan decision"), _clean_text(trace.rationale), ( "Alternatives: " + ", ".join(_clean_text(item) for item in trace.options) if trace.options else "" ), ( "Evidence: " + ", ".join(trace.evidence_ids) if trace.evidence_ids else "" ), ( "Warnings: " + ", ".join(trace.warnings) if trace.warnings else "" ), ) if part ) for trace in plan.decision_provenance ) provider = _clean_text( plan.provider_provenance.get("provider") or "research_agent" ) planner_source = ( DecisionSource.LLM if plan.provider_provenance.get("llm_used") else DecisionSource.FALLBACK if plan.provider_provenance.get("fallback_used") else DecisionSource.RESEARCH_AGENT ) planner_model = _clean_text( plan.provider_provenance.get("llm_model") or "Not configured" ) verification = plan.verification verifier_source = ( verification.source if verification is not None else DecisionSource.FALLBACK ) verifier_model = _clean_text( ( verification.provider_provenance.get("model") if verification is not None else None ) or plan.provider_provenance.get("verification_model") or "Not configured" ) planner_status_html = ( '
' '
{_escape(source_badge_label(planner_source))}' f'

{_escape(source_badge_label(planner_source))} planner

' f'

Model: {_escape(planner_model)}

' 'Planning only · no execution authority
' '
{_escape(source_badge_label(verifier_source))}' f'

{_escape(source_badge_label(verifier_source))} verifier

' f'

Status: {_escape(verification.status.title() if verification else "Unavailable")}

' f'

Model: {_escape(verifier_model)}

' 'Deterministic validation remains final
' ) evidence_cards = "".join( _evidence_card_html(item) for item in plan.evidence ) provider_error = ( f'
{_escape(current.provider_error)}
' if current.provider_error else "" ) try: selected_candidate = current.current_candidate except ValueError: selected_candidate = None tradeoffs = ( selected_candidate.expected_tradeoffs if selected_candidate and selected_candidate.expected_tradeoffs else plan.expected_tradeoffs ) assumptions = ( selected_candidate.assumptions if selected_candidate and selected_candidate.assumptions else plan.assumptions ) warnings = tuple(dict.fromkeys( (*plan.warnings, *(selected_candidate.warnings if selected_candidate else ())) )) insight_blocks = [ _research_insight_html( "recommendation", "Recommendation", recommendation.get("candidate_id") or "Unavailable", recommendation.get("rationale") or "No rationale provided", ) ] if tradeoffs: insight_blocks.append( _research_insight_html( "tradeoff", "Tradeoff", "Expected tradeoff", tradeoffs[0] ) ) if assumptions: insight_blocks.append( _research_insight_html( "assumption", "Assumption", "Confirm before running", assumptions[0] ) ) for warning in warnings: insight_blocks.append( _research_insight_html( "warning", "Warning", "Review before running", warning ) ) return ( '
' '
Research assistant' '

Recommendation and evidence

' '' f'{_escape(provider.title())}' 'Planning only · no automatic execution
' f"{provider_error}{planner_status_html}" '

Dataset profile

' f'

{_escape(plan.extensions.get("dataset_profile_source") or "Profile source not recorded")}

' f'{_mapping_rows(plan.extensions.get("dataset_profile") or {})}
' f'
{"".join(insight_blocks)}
' '

Verifier summary

' f'

{_escape(verification.summary if verification else "No verifier result is available.")}

' f'{_list_html(verification.warnings if verification else ())}
' '

Evidence

' f'
{evidence_cards}
' '

Decision provenance

' f'{_list_html(decision_provenance)}
' '

External search: ' + ( "enabled; partial source failures are shown above." if plan.provider_provenance.get("external_search_enabled") else "disabled; deterministic planning and internal evidence remain available." ) + "

" "
" ) def _research_insight_html( kind: str, label: str, heading: str, body: str, ) -> str: return ( '
' f'{_escape(label)}' f'

{_escape(heading)}

' f'

{_escape(body)}

' "
" ) def _shape_summary(input_shape: Sequence[int], output_shape: Sequence[int]) -> str: if not input_shape and not output_shape: return "Unavailable before execution" before = " x ".join(str(item) for item in input_shape) or "Unavailable" after = " x ".join(str(item) for item in output_shape) or "Unavailable" return f"{before} to {after}" def _parameter_summary(parameters: Sequence[tuple[str, str]]) -> str: if not parameters: return "Backend defaults" return ", ".join( f"{key.replace('_', ' ').title()}: {value}" for key, value in parameters ) def pipeline_node_labels( state: Mapping[str, Any] | WorkbenchState, ) -> tuple[str, ...]: return tuple( "\n".join( ( node.label, node.component or "Selection pending", f"Source: {source_badge_label(node.source)}", f"Parameters: {_parameter_summary(node.parameters)}", f"Shape: {_shape_summary(node.input_shape, node.output_shape)}", f"Compatibility: {node.compatibility.title()}", ) ) for node in pipeline_nodes(state) ) _COMPATIBILITY_ICONS = { "compatible": "✓", "review": "!", "blocked": "×", } def pipeline_node_cards_html( state: Mapping[str, Any] | WorkbenchState, ) -> tuple[str, ...]: """Render the compact, escaped visual contract for every pipeline node.""" current = state_from_mapping(state) cards = [] for index, node in enumerate(pipeline_nodes(current), start=1): selected = node.node_id == current.selected_node_id selected_class = " is-selected" if selected else "" compatibility = node.compatibility source = source_badge_label(node.source) component = node.component or "Selection pending" parameters = _parameter_summary(node.parameters) shape = _shape_summary(node.input_shape, node.output_shape) compatibility_icon = _COMPATIBILITY_ICONS[compatibility] node_icon, stage_name = PIPELINE_NODE_PRESENTATION[node.node_id] stage_chip = PIPELINE_NODE_SHORT_LABELS[node.node_id] current_attribute = ' aria-current="true"' if selected else "" selected_label = ( 'Inspecting' if selected else "" ) cards.append( f'
' '
' f'' f'' f"{_escape(stage_chip)}" "
" f"{selected_label}" f'

{_escape(component)}

' f'

' f"{_escape(parameters)}

" f'

' f"{_escape(shape)}

" '
" ) return tuple(cards) def node_inspector_html( state: Mapping[str, Any] | WorkbenchState, ) -> str: current = state_from_mapping(state) node = next( item for item in pipeline_nodes(current) if item.node_id == current.selected_node_id ) trace = node.trace.to_dict() report = node.feature_report.to_dict() if node.feature_report else None trace_rows = { key: value for key, value in trace.items() if key not in {"extensions", "schema_version"} } report_html = ( '

FeatureExtractionReport

' f'{_mapping_rows(report)}
' if report is not None else ( '

FeatureExtractionReport

' '

Not applicable to this node

' ) ) return ( '
' '
' f'{_escape(node.label)}

{_escape(node.component)}

' f'' f'{_escape(node.compatibility.title())}
' '
' '

Selection reason

' f'

{_escape(node.rationale)}

' '

Alternatives considered

' f'{_list_html(node.alternatives)}
' "
" '

DecisionTrace

' '
Decision source' f'{_escape(source_badge_label(node.source))}
' f"{_mapping_rows(trace_rows)}" "
" f"{report_html}" "
" ) def review_html(state: Mapping[str, Any] | WorkbenchState) -> str: current = state_from_mapping(state) issues = validate_state(current) if issues: return ( '
' "Needs attention

The run plan is not ready

" f'{_list_html(issues)}
' ) warnings = current.research_plan.warnings if current.research_plan else () return ( '
' "Ready to run

Review complete

" '
' '
Data' f'{_escape(current.dataset_name or current.upload_type)}
' f'
Objective{_escape(current.target)}
' '
Candidate' f'{_escape(candidate_display_name(current.current_candidate))}
' f'
Quality profile{_escape(current.preset)}
' f'
Time budget{current.time_limit} seconds
' f'
Candidate trials{current.trials}
' f'
Run variants{max(1, len(current.variants))}
' "
" '

Warnings to review

' f'{_list_html(warnings)}
' ) def run_plan_summary_html( state: Mapping[str, Any] | WorkbenchState, ) -> str: current = state_from_mapping(state) issues = validate_state(current) run_count = max(1, len(current.variants)) variant_label = "variant" if run_count == 1 else "variants" candidate = "Selection pending" try: candidate = candidate_display_name(current.current_candidate) except ValueError: pass status = "Ready" if not issues else f"{len(issues)} item(s) need attention" return ( '
' '
Run plan' f'{_escape(status)}
' '
' f'Candidate{_escape(candidate)}' f'Quality{_escape(current.preset)}' f'Budget{current.time_limit} seconds' f'Trials{current.trials}' f'Variants{run_count} {variant_label}' "
" ) def _research_plan_snapshot(state: WorkbenchState) -> str: if state.research_plan is None: return '

Research plan unavailable

' plan = state.research_plan evidence = tuple( _clean_text(item.get("summary") or item.get("evidence_id")) for item in plan.evidence ) return ( '
' '
Pre-run record' '

Research plan

' '

Recommendation

' f'{_escape(plan.recommendation.get("candidate_id") or "Unavailable")}' f'

{_escape(plan.recommendation.get("rationale") or "No rationale provided")}

' '

Evidence

' f'{_list_html(evidence)}
' '

Assumptions

' f'{_list_html(plan.assumptions)}
' '

Warnings

' f'{_list_html(plan.warnings)}
' '

Candidate estimates

' f'{candidate_cards_html(state)}
' ) def _pipeline_trace_snapshot( state: WorkbenchState, traces: Sequence[DecisionTrace] = (), ) -> str: if traces: entries = [] for trace in traces: selected = ( trace.selected if trace.selected is not None else "Not reported" ) trace_rows = { "Source": source_badge_label(trace.decision_source), "Stage": trace.stage or trace.decision_type or "Not reported", "Selected": selected, "Alternatives": list(trace.options), "Rationale": trace.rationale or "Not reported", "Parameters": dict(trace.parameters), "Evidence references": list(trace.evidence_ids), "Warnings": list(trace.warnings), } entries.append( '
' f'{_escape(trace.stage or trace.decision_type or "Decision")}' f'

{_escape(selected)}

' f"{_mapping_rows(trace_rows)}" "
" ) return ( '
' '
Execution provenance' '

Pipeline trace

' f'
{"".join(entries)}
' '

Research assumptions

' f'{_list_html(state.research_plan.assumptions if state.research_plan else ())}' '
' ) entries = [] for node in pipeline_nodes(state): entries.append( '
' f'{_escape(node.label)}

{_escape(node.component)}

' f'

Decision source: {_escape(source_badge_label(node.source))}

' f'

{_escape(node.rationale)}

' ) return ( '
' '
Planned provenance' '

Pipeline trace

' f'
{"".join(entries)}
' ) def _feature_report_snapshot( state: WorkbenchState, report: FeatureExtractionReport | None = None, ) -> str: if report is not None: warning_html = ( '

Warnings

' f"{_list_html(report.warnings)}
" if report.warnings else "" ) return ( '
' '
Execution report' '

Feature extraction

' f'{_mapping_rows(report.to_dict())}{warning_html}
' ) report = _planned_feature_report(state) return ( '
' '
Planned report' '

Feature extraction

' '

Execution shapes are unavailable ' "unless the backend reports them.

" f'{_mapping_rows(report.to_dict())}
' ) def _reflection_metric_cards( metrics: Mapping[str, Any], *, measured: bool ) -> str: if not metrics: return '

No metrics recorded

' cards = [] for name, raw in metrics.items(): value = raw.get("value") if isinstance(raw, Mapping) else raw estimate = ( raw.get("estimate") if not measured and isinstance(raw, Mapping) else None ) basis = raw.get("basis") if isinstance(raw, Mapping) else "" if measured and value is None: continue displayed = value if measured else ( estimate if estimate is not None else "Requires validation" ) kind = "Measured" if measured else "Estimate · unmeasured" cards.append( '
' f'{_escape(kind)}' f'{_escape(str(name).replace("_", " ").title())}' f'{_escape(str(displayed))}' f'{_escape(basis)}
' ) return "".join(cards) or '

No metrics recorded

' def experiment_reflection_outputs( execution_metadata: Mapping[str, Any] | None, ) -> tuple[str, dict[str, Any], tuple[tuple[str, str], ...]]: """Project canonical reflection metadata without granting execution authority.""" empty = ( '
' '

No experiment reflection

' '

A completed governed run may provide a bounded reflection here.

' "
" ) if not isinstance(execution_metadata, Mapping) or not execution_metadata: return empty, {}, () try: normalized = normalize_execution_metadata(execution_metadata) raw_reflection = normalized.get("experiment_reflection") if not isinstance(raw_reflection, Mapping): return empty, {}, () reflection = ExperimentReflection.from_mapping(raw_reflection) except (TypeError, ValueError): return empty, {}, () approved = normalized.get("approved_research") recommendation_id = "Unavailable" recommendation_rationale = "No original recommendation was recorded." approved_id = "Unavailable" pipeline_config: Mapping[str, Any] = {} if isinstance(approved, Mapping): raw_plan = approved.get("research_plan") if isinstance(raw_plan, Mapping): try: plan = ResearchPlan.from_mapping(raw_plan) recommendation_id = _clean_text( plan.recommendation.get("candidate_id") or "Unavailable" ) recommendation_rationale = _clean_text( plan.recommendation.get("rationale") or "No original recommendation rationale was recorded." ) except (TypeError, ValueError): pass approved_id = _clean_text( approved.get("approved_candidate_id") or "Unavailable" ) if isinstance(approved.get("pipeline_config"), Mapping): pipeline_config = approved["pipeline_config"] actual_decisions = [] for raw_trace in normalized.get("decision_traces") or (): if not isinstance(raw_trace, Mapping): continue try: trace = DecisionTrace.from_mapping(raw_trace) except (TypeError, ValueError): continue if trace.stage in {"research_verification", "candidate_approval", "experiment_reflection"}: continue selected = trace.selected if trace.selected is not None else "Not reported" actual_decisions.append( f"{trace.stage or trace.decision_type}: {selected} " f"({source_badge_label(trace.decision_source)})" ) suggestion_cards = [] choices: list[tuple[str, str]] = [] for suggestion in reflection.suggestions: choices.append((suggestion.title, suggestion.suggestion_id)) status = ( "Executable · fresh approval required" if suggestion.executable and not suggestion.suggestion_only else "Suggestion only · cannot execute" ) suggestion_cards.append( '
' f'{_escape(status)}

{_escape(suggestion.title)}

' f'

{_escape(suggestion.rationale)}

' '
Expected tradeoffs
' f'{_list_html(suggestion.expected_tradeoffs)}
' '
Expected metric estimates
' '
' f'{_reflection_metric_cards(suggestion.expected_metrics, measured=False)}' "
" '
Evidence references
' f'{_list_html(suggestion.evidence_ids)}
' '
Warnings
' f'{_list_html(suggestion.warnings)}
' ) provider_model = _clean_text( reflection.provider_provenance.get("model") or "Not configured" ) reflection_source = source_badge_label(reflection.source) html_output = ( '
' '
Post-run interpretation' '

Experiment reflection

' '
' '

Original research recommendation

' f'{_escape(recommendation_id)}' f'

{_escape(recommendation_rationale)}

' '

Actual executed pipeline

' f'Approved candidate: {_escape(approved_id)}' f'{_mapping_rows(pipeline_config)}' f'{_list_html(actual_decisions, empty="No actual execution decisions recorded")}
' '

Measured results

' '
' f'{_reflection_metric_cards(reflection.measured_metrics, measured=True)}' "
" '

Experiment reflection

' '{_escape(reflection_source)}' f'

Expectations: {_escape(reflection.expectations_met.replace("_", " ").title())}

' f'

{_escape(reflection.summary)}

{_escape(reflection.interpretation)}

' '
Observed limitations
' f'{_list_html(reflection.limitations)}' '
Evidence references
' f'{_list_html(reflection.evidence_ids)}' '
Warnings
' f'{_list_html(reflection.warnings)}' f'Model: {_escape(provider_model)} · no automatic execution' "
" '

Suggested next experiments

' f'
{"".join(suggestion_cards)}
' '

Using a suggestion creates an unapproved ' "draft only. Fresh research, verification, validation, and explicit approval " "remain required.

" ) return html_output, reflection.to_dict(), tuple(choices) def snapshot_outputs( state: Mapping[str, Any] | WorkbenchState, execution_metadata: Mapping[str, Any] | None = None, ) -> tuple[str, str, str]: current = state_from_mapping(state) report, traces = _runtime_contracts(execution_metadata) return ( _research_plan_snapshot(current), _pipeline_trace_snapshot(current, traces), _feature_report_snapshot(current, report), ) __all__ = [ "PIPELINE_NODE_IDS", "SOURCE_LABELS", "WORKBENCH_STEPS", "PipelineNodeView", "RunVariant", "WorkbenchState", "add_variant", "apply_next_experiment_draft", "apply_refinement", "candidate_is_executable", "candidate_verification_map", "candidate_cards_html", "experiment_reflection_outputs", "generate_research_plan", "new_workbench_state", "node_inspector_html", "pipeline_node_cards_html", "pipeline_node_labels", "pipeline_nodes", "research_dossier_html", "review_html", "run_plan_summary_html", "select_candidate", "select_node", "set_step", "snapshot_outputs", "source_badge_label", "state_from_mapping", "update_context", "validate_state", ]