Spaces:
Sleeping
Sleeping
Deploy public authenticated AutoML gateway
Browse files- gateway_app.py +87 -122
- guided_workbench.py +17 -4
- studio_design.py +322 -0
- ui_design.py +7 -0
gateway_app.py
CHANGED
|
@@ -38,6 +38,7 @@ from guided_workbench import (
|
|
| 38 |
approval_status_html,
|
| 39 |
approved_research_metadata,
|
| 40 |
candidate_cards_html,
|
|
|
|
| 41 |
candidate_is_executable,
|
| 42 |
experiment_reflection_outputs,
|
| 43 |
new_workbench_state,
|
|
@@ -54,15 +55,12 @@ from guided_workbench import (
|
|
| 54 |
update_context,
|
| 55 |
validate_state,
|
| 56 |
)
|
|
|
|
| 57 |
from ui_design import (
|
| 58 |
-
APP_CSS,
|
| 59 |
AUTOMATIC_SELECTION,
|
| 60 |
-
INFERENCE_GUIDE_HTML,
|
| 61 |
QUALITY_PRESET_CHOICES,
|
| 62 |
RESEARCH_NOTICE_HTML,
|
| 63 |
-
RESPONSIVE_HEAD_HTML,
|
| 64 |
STUDIO_APP_HEADER_HTML,
|
| 65 |
-
professional_theme,
|
| 66 |
)
|
| 67 |
from gateway_auth import (
|
| 68 |
AuthResult,
|
|
@@ -2830,7 +2828,7 @@ def _sync_workbench_ui_state(
|
|
| 2830 |
def _workbench_candidate_update(state: WorkbenchState):
|
| 2831 |
candidates = (
|
| 2832 |
[
|
| 2833 |
-
(candidate
|
| 2834 |
for candidate in state.research_plan.candidates
|
| 2835 |
if candidate_is_executable(
|
| 2836 |
state.research_plan, candidate.candidate_id
|
|
@@ -3356,15 +3354,6 @@ def clear_workbench_variants_ui(state_data):
|
|
| 3356 |
|
| 3357 |
def _add_results_section(label: str = "Latest run"):
|
| 3358 |
with gr.Group(elem_classes=["results-shell"]):
|
| 3359 |
-
gr.HTML(
|
| 3360 |
-
f"""
|
| 3361 |
-
<div class="results-heading">
|
| 3362 |
-
<p class="section-kicker">Measured run record</p>
|
| 3363 |
-
<h2>{html.escape(label)}</h2>
|
| 3364 |
-
<p>Measured outputs remain separate from pre-run estimates.</p>
|
| 3365 |
-
</div>
|
| 3366 |
-
"""
|
| 3367 |
-
)
|
| 3368 |
status = gr.Markdown(
|
| 3369 |
"Status: _idle_",
|
| 3370 |
visible=False,
|
|
@@ -3606,27 +3595,13 @@ def create_interface() -> gr.Blocks:
|
|
| 3606 |
|
| 3607 |
with gr.Tabs(visible=False, elem_id="workspace-tabs") as main_tabs:
|
| 3608 |
with gr.TabItem("Build"):
|
| 3609 |
-
gr.HTML(
|
| 3610 |
-
"""
|
| 3611 |
-
<section class="guided-workbench-heading studio-build-intro">
|
| 3612 |
-
<div>
|
| 3613 |
-
<p class="section-kicker">Guided research workbench</p>
|
| 3614 |
-
<h2>Design a defensible training run</h2>
|
| 3615 |
-
</div>
|
| 3616 |
-
<p>
|
| 3617 |
-
Move from data to execution while preserving the evidence,
|
| 3618 |
-
assumptions, and decisions behind every component.
|
| 3619 |
-
</p>
|
| 3620 |
-
</section>
|
| 3621 |
-
"""
|
| 3622 |
-
)
|
| 3623 |
with gr.Row(
|
| 3624 |
equal_height=False,
|
| 3625 |
elem_classes=["guided-instrument-bench", "studio-workbench"],
|
| 3626 |
):
|
| 3627 |
with gr.Column(
|
| 3628 |
-
scale=
|
| 3629 |
-
min_width=
|
| 3630 |
elem_classes=["guided-step-rail", "studio-workflow"],
|
| 3631 |
):
|
| 3632 |
gr.HTML(
|
|
@@ -3654,67 +3629,9 @@ def create_interface() -> gr.Blocks:
|
|
| 3654 |
|
| 3655 |
with gr.Column(
|
| 3656 |
scale=7,
|
| 3657 |
-
min_width=
|
| 3658 |
elem_classes=["guided-work-canvas", "studio-primary"],
|
| 3659 |
):
|
| 3660 |
-
gr.HTML(
|
| 3661 |
-
"""
|
| 3662 |
-
<div class="guided-canvas-heading">
|
| 3663 |
-
<div>
|
| 3664 |
-
<h2>Explore the recommended pipeline</h2>
|
| 3665 |
-
<p>Every component includes its source, compatibility, and supporting evidence.</p>
|
| 3666 |
-
</div>
|
| 3667 |
-
<span class="guided-quality-badge">All stages visible</span>
|
| 3668 |
-
</div>
|
| 3669 |
-
<div class="guided-pipeline-meta">
|
| 3670 |
-
<strong>Candidate pipeline</strong>
|
| 3671 |
-
<span>Select a stage to inspect its evidence</span>
|
| 3672 |
-
</div>
|
| 3673 |
-
"""
|
| 3674 |
-
)
|
| 3675 |
-
pipeline_card_outputs = []
|
| 3676 |
-
pipeline_buttons = []
|
| 3677 |
-
with gr.Row(
|
| 3678 |
-
elem_classes=["guided-pipeline-diagram", "studio-pipeline"],
|
| 3679 |
-
equal_height=True,
|
| 3680 |
-
):
|
| 3681 |
-
for node_id, node_label, card_html in zip(
|
| 3682 |
-
PIPELINE_NODE_IDS,
|
| 3683 |
-
(
|
| 3684 |
-
"Input",
|
| 3685 |
-
"Preprocessing",
|
| 3686 |
-
"Feature Extractor",
|
| 3687 |
-
"Post-processing",
|
| 3688 |
-
"Model",
|
| 3689 |
-
"Evaluation",
|
| 3690 |
-
),
|
| 3691 |
-
initial_node_cards,
|
| 3692 |
-
):
|
| 3693 |
-
with gr.Group(
|
| 3694 |
-
elem_classes=[
|
| 3695 |
-
"guided-pipeline-card-shell",
|
| 3696 |
-
f"guided-node-{node_id}",
|
| 3697 |
-
]
|
| 3698 |
-
):
|
| 3699 |
-
pipeline_card_outputs.append(
|
| 3700 |
-
gr.HTML(
|
| 3701 |
-
card_html,
|
| 3702 |
-
container=False,
|
| 3703 |
-
padding=False,
|
| 3704 |
-
elem_classes=[
|
| 3705 |
-
"guided-pipeline-card-output"
|
| 3706 |
-
],
|
| 3707 |
-
)
|
| 3708 |
-
)
|
| 3709 |
-
pipeline_buttons.append(
|
| 3710 |
-
gr.Button(
|
| 3711 |
-
f"Inspect {node_label} node",
|
| 3712 |
-
elem_classes=[
|
| 3713 |
-
"guided-pipeline-node-hitarea"
|
| 3714 |
-
],
|
| 3715 |
-
)
|
| 3716 |
-
)
|
| 3717 |
-
|
| 3718 |
with gr.Group(
|
| 3719 |
visible=True,
|
| 3720 |
elem_classes=["guided-step-panel"],
|
|
@@ -3873,6 +3790,49 @@ def create_interface() -> gr.Blocks:
|
|
| 3873 |
'<p>Open any node to review its selection source, '
|
| 3874 |
'alternatives, compatibility, and report contract.</p></div>'
|
| 3875 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3876 |
node_inspector = gr.HTML(
|
| 3877 |
node_inspector_html(initial_workbench),
|
| 3878 |
elem_classes=["guided-node-inspector-output"],
|
|
@@ -4193,25 +4153,24 @@ def create_interface() -> gr.Blocks:
|
|
| 4193 |
'prepared. The current selection can run once.</div>'
|
| 4194 |
)
|
| 4195 |
|
| 4196 |
-
|
| 4197 |
-
|
| 4198 |
-
|
| 4199 |
-
|
| 4200 |
-
|
| 4201 |
-
|
| 4202 |
-
|
| 4203 |
-
elem_classes=["guided-dossier-output"],
|
| 4204 |
-
)
|
| 4205 |
-
with gr.Group(elem_classes=["guided-refinement-box"]):
|
| 4206 |
-
refinement = gr.Textbox(
|
| 4207 |
-
label="Refine the recommendation",
|
| 4208 |
-
placeholder="For example, prefer interpretability",
|
| 4209 |
-
lines=2,
|
| 4210 |
-
)
|
| 4211 |
-
refine_button = gr.Button(
|
| 4212 |
-
"Apply refinement",
|
| 4213 |
-
elem_classes=["guided-secondary-action"],
|
| 4214 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4215 |
|
| 4216 |
with gr.Group(elem_classes=["guided-run-bar", "studio-runbar"]):
|
| 4217 |
with gr.Row(equal_height=True):
|
|
@@ -4253,8 +4212,8 @@ def create_interface() -> gr.Blocks:
|
|
| 4253 |
variant_two_outputs = _add_results_section("Variant 2")
|
| 4254 |
|
| 4255 |
with gr.TabItem("Models") as replay_tab:
|
| 4256 |
-
with gr.Row(equal_height=False):
|
| 4257 |
-
with gr.Column(scale=2, elem_classes=["
|
| 4258 |
gr.HTML(
|
| 4259 |
"""
|
| 4260 |
<div class="configuration-heading">
|
|
@@ -4269,18 +4228,19 @@ def create_interface() -> gr.Blocks:
|
|
| 4269 |
interactive=False,
|
| 4270 |
)
|
| 4271 |
saved_model_selection = gr.State("")
|
| 4272 |
-
|
| 4273 |
-
|
| 4274 |
-
|
| 4275 |
-
|
| 4276 |
-
|
|
|
|
|
|
|
|
|
|
| 4277 |
saved_export_status = gr.Markdown("")
|
| 4278 |
-
saved_export_file = gr.DownloadButton(
|
| 4279 |
-
"Download saved model", interactive=False,
|
| 4280 |
-
)
|
| 4281 |
model_upload = gr.File(
|
| 4282 |
label="Trained Model Package (.zip)",
|
| 4283 |
file_types=[".zip"],
|
|
|
|
| 4284 |
)
|
| 4285 |
model_info = gr.Markdown(
|
| 4286 |
"Choose a saved model or upload a package."
|
|
@@ -4330,6 +4290,11 @@ def create_interface() -> gr.Blocks:
|
|
| 4330 |
scale=3,
|
| 4331 |
elem_classes=["inference-output"],
|
| 4332 |
):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4333 |
predict_status = gr.Markdown(
|
| 4334 |
"Ready for a model.",
|
| 4335 |
elem_classes=["run-status"],
|
|
@@ -4371,10 +4336,10 @@ def create_interface() -> gr.Blocks:
|
|
| 4371 |
)
|
| 4372 |
predict_frame = gr.DataFrame(
|
| 4373 |
label="Prediction",
|
|
|
|
|
|
|
| 4374 |
interactive=False,
|
| 4375 |
)
|
| 4376 |
-
with gr.Column(scale=1):
|
| 4377 |
-
gr.HTML(INFERENCE_GUIDE_HTML)
|
| 4378 |
|
| 4379 |
with gr.TabItem("Account"):
|
| 4380 |
with gr.Group(elem_classes=["panel-card"]):
|
|
@@ -5162,9 +5127,9 @@ if __name__ == "__main__":
|
|
| 5162 |
server_name="0.0.0.0",
|
| 5163 |
server_port=7860,
|
| 5164 |
show_error=False,
|
| 5165 |
-
theme=
|
| 5166 |
-
css=
|
| 5167 |
-
head=
|
| 5168 |
)
|
| 5169 |
|
| 5170 |
|
|
|
|
| 38 |
approval_status_html,
|
| 39 |
approved_research_metadata,
|
| 40 |
candidate_cards_html,
|
| 41 |
+
candidate_display_name,
|
| 42 |
candidate_is_executable,
|
| 43 |
experiment_reflection_outputs,
|
| 44 |
new_workbench_state,
|
|
|
|
| 55 |
update_context,
|
| 56 |
validate_state,
|
| 57 |
)
|
| 58 |
+
from studio_design import STUDIO_CSS, STUDIO_HEAD_HTML, studio_theme
|
| 59 |
from ui_design import (
|
|
|
|
| 60 |
AUTOMATIC_SELECTION,
|
|
|
|
| 61 |
QUALITY_PRESET_CHOICES,
|
| 62 |
RESEARCH_NOTICE_HTML,
|
|
|
|
| 63 |
STUDIO_APP_HEADER_HTML,
|
|
|
|
| 64 |
)
|
| 65 |
from gateway_auth import (
|
| 66 |
AuthResult,
|
|
|
|
| 2828 |
def _workbench_candidate_update(state: WorkbenchState):
|
| 2829 |
candidates = (
|
| 2830 |
[
|
| 2831 |
+
(candidate_display_name(candidate), candidate.candidate_id)
|
| 2832 |
for candidate in state.research_plan.candidates
|
| 2833 |
if candidate_is_executable(
|
| 2834 |
state.research_plan, candidate.candidate_id
|
|
|
|
| 3354 |
|
| 3355 |
def _add_results_section(label: str = "Latest run"):
|
| 3356 |
with gr.Group(elem_classes=["results-shell"]):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3357 |
status = gr.Markdown(
|
| 3358 |
"Status: _idle_",
|
| 3359 |
visible=False,
|
|
|
|
| 3595 |
|
| 3596 |
with gr.Tabs(visible=False, elem_id="workspace-tabs") as main_tabs:
|
| 3597 |
with gr.TabItem("Build"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3598 |
with gr.Row(
|
| 3599 |
equal_height=False,
|
| 3600 |
elem_classes=["guided-instrument-bench", "studio-workbench"],
|
| 3601 |
):
|
| 3602 |
with gr.Column(
|
| 3603 |
+
scale=1,
|
| 3604 |
+
min_width=180,
|
| 3605 |
elem_classes=["guided-step-rail", "studio-workflow"],
|
| 3606 |
):
|
| 3607 |
gr.HTML(
|
|
|
|
| 3629 |
|
| 3630 |
with gr.Column(
|
| 3631 |
scale=7,
|
| 3632 |
+
min_width=320,
|
| 3633 |
elem_classes=["guided-work-canvas", "studio-primary"],
|
| 3634 |
):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3635 |
with gr.Group(
|
| 3636 |
visible=True,
|
| 3637 |
elem_classes=["guided-step-panel"],
|
|
|
|
| 3790 |
'<p>Open any node to review its selection source, '
|
| 3791 |
'alternatives, compatibility, and report contract.</p></div>'
|
| 3792 |
)
|
| 3793 |
+
pipeline_card_outputs = []
|
| 3794 |
+
pipeline_buttons = []
|
| 3795 |
+
with gr.Row(
|
| 3796 |
+
elem_classes=["guided-pipeline-diagram", "studio-pipeline"],
|
| 3797 |
+
equal_height=True,
|
| 3798 |
+
):
|
| 3799 |
+
for node_id, node_label, card_html in zip(
|
| 3800 |
+
PIPELINE_NODE_IDS,
|
| 3801 |
+
(
|
| 3802 |
+
"Input",
|
| 3803 |
+
"Preprocessing",
|
| 3804 |
+
"Feature Extractor",
|
| 3805 |
+
"Post-processing",
|
| 3806 |
+
"Model",
|
| 3807 |
+
"Evaluation",
|
| 3808 |
+
),
|
| 3809 |
+
initial_node_cards,
|
| 3810 |
+
):
|
| 3811 |
+
with gr.Group(
|
| 3812 |
+
elem_classes=[
|
| 3813 |
+
"guided-pipeline-card-shell",
|
| 3814 |
+
f"guided-node-{node_id}",
|
| 3815 |
+
]
|
| 3816 |
+
):
|
| 3817 |
+
pipeline_card_outputs.append(
|
| 3818 |
+
gr.HTML(
|
| 3819 |
+
card_html,
|
| 3820 |
+
container=False,
|
| 3821 |
+
padding=False,
|
| 3822 |
+
elem_classes=[
|
| 3823 |
+
"guided-pipeline-card-output"
|
| 3824 |
+
],
|
| 3825 |
+
)
|
| 3826 |
+
)
|
| 3827 |
+
pipeline_buttons.append(
|
| 3828 |
+
gr.Button(
|
| 3829 |
+
f"Inspect {node_label} node",
|
| 3830 |
+
elem_classes=[
|
| 3831 |
+
"guided-pipeline-node-hitarea"
|
| 3832 |
+
],
|
| 3833 |
+
)
|
| 3834 |
+
)
|
| 3835 |
+
|
| 3836 |
node_inspector = gr.HTML(
|
| 3837 |
node_inspector_html(initial_workbench),
|
| 3838 |
elem_classes=["guided-node-inspector-output"],
|
|
|
|
| 4153 |
'prepared. The current selection can run once.</div>'
|
| 4154 |
)
|
| 4155 |
|
| 4156 |
+
with gr.Accordion(
|
| 4157 |
+
"Research assistant", open=False,
|
| 4158 |
+
elem_classes=["studio-assistant"],
|
| 4159 |
+
):
|
| 4160 |
+
dossier = gr.HTML(
|
| 4161 |
+
research_dossier_html(initial_workbench),
|
| 4162 |
+
elem_classes=["guided-dossier-output"],
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4163 |
)
|
| 4164 |
+
with gr.Group(elem_classes=["guided-refinement-box"]):
|
| 4165 |
+
refinement = gr.Textbox(
|
| 4166 |
+
label="Refine the recommendation",
|
| 4167 |
+
placeholder="For example, prefer interpretability",
|
| 4168 |
+
lines=2,
|
| 4169 |
+
)
|
| 4170 |
+
refine_button = gr.Button(
|
| 4171 |
+
"Apply refinement",
|
| 4172 |
+
elem_classes=["guided-secondary-action"],
|
| 4173 |
+
)
|
| 4174 |
|
| 4175 |
with gr.Group(elem_classes=["guided-run-bar", "studio-runbar"]):
|
| 4176 |
with gr.Row(equal_height=True):
|
|
|
|
| 4212 |
variant_two_outputs = _add_results_section("Variant 2")
|
| 4213 |
|
| 4214 |
with gr.TabItem("Models") as replay_tab:
|
| 4215 |
+
with gr.Row(equal_height=False, elem_classes=["studio-models"]):
|
| 4216 |
+
with gr.Column(scale=2, min_width=300, elem_classes=["studio-model-controls"]):
|
| 4217 |
gr.HTML(
|
| 4218 |
"""
|
| 4219 |
<div class="configuration-heading">
|
|
|
|
| 4228 |
interactive=False,
|
| 4229 |
)
|
| 4230 |
saved_model_selection = gr.State("")
|
| 4231 |
+
with gr.Row(elem_classes=["studio-model-actions"]):
|
| 4232 |
+
refresh_models = gr.Button(
|
| 4233 |
+
"Refresh models", interactive=False,
|
| 4234 |
+
)
|
| 4235 |
+
export_saved_model = gr.Button("Prepare download")
|
| 4236 |
+
saved_export_file = gr.DownloadButton(
|
| 4237 |
+
"Download package", interactive=False,
|
| 4238 |
+
)
|
| 4239 |
saved_export_status = gr.Markdown("")
|
|
|
|
|
|
|
|
|
|
| 4240 |
model_upload = gr.File(
|
| 4241 |
label="Trained Model Package (.zip)",
|
| 4242 |
file_types=[".zip"],
|
| 4243 |
+
height=150,
|
| 4244 |
)
|
| 4245 |
model_info = gr.Markdown(
|
| 4246 |
"Choose a saved model or upload a package."
|
|
|
|
| 4290 |
scale=3,
|
| 4291 |
elem_classes=["inference-output"],
|
| 4292 |
):
|
| 4293 |
+
gr.HTML(
|
| 4294 |
+
'<div class="configuration-heading">'
|
| 4295 |
+
'<h2>Prediction results</h2>'
|
| 4296 |
+
'<p>Visual previews and the complete prediction table.</p></div>'
|
| 4297 |
+
)
|
| 4298 |
predict_status = gr.Markdown(
|
| 4299 |
"Ready for a model.",
|
| 4300 |
elem_classes=["run-status"],
|
|
|
|
| 4336 |
)
|
| 4337 |
predict_frame = gr.DataFrame(
|
| 4338 |
label="Prediction",
|
| 4339 |
+
headers=["Prediction"],
|
| 4340 |
+
value=[],
|
| 4341 |
interactive=False,
|
| 4342 |
)
|
|
|
|
|
|
|
| 4343 |
|
| 4344 |
with gr.TabItem("Account"):
|
| 4345 |
with gr.Group(elem_classes=["panel-card"]):
|
|
|
|
| 5127 |
server_name="0.0.0.0",
|
| 5128 |
server_port=7860,
|
| 5129 |
show_error=False,
|
| 5130 |
+
theme=studio_theme(),
|
| 5131 |
+
css=STUDIO_CSS,
|
| 5132 |
+
head=STUDIO_HEAD_HTML,
|
| 5133 |
)
|
| 5134 |
|
| 5135 |
|
guided_workbench.py
CHANGED
|
@@ -1407,6 +1407,16 @@ def _expected_metrics_html(candidate: CandidatePipeline) -> str:
|
|
| 1407 |
return "".join(cards)
|
| 1408 |
|
| 1409 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1410 |
def candidate_cards_html(
|
| 1411 |
state: Mapping[str, Any] | WorkbenchState,
|
| 1412 |
) -> str:
|
|
@@ -1470,11 +1480,14 @@ def candidate_cards_html(
|
|
| 1470 |
f'" data-candidate-id="{_escape(candidate.candidate_id)}">'
|
| 1471 |
f'<div class="guided-candidate-index">Candidate {index}</div>'
|
| 1472 |
f"{recommended_badge}{verification_badge}"
|
| 1473 |
-
f'<h3>{_escape(candidate
|
| 1474 |
'<p class="guided-candidate-rationale">'
|
| 1475 |
f'{_escape(candidate.rationale or "No rationale provided")}</p>'
|
| 1476 |
'<div class="guided-candidate-section"><h4>Estimated metric values · unmeasured</h4>'
|
| 1477 |
f'<div class="guided-estimate-grid">{_expected_metrics_html(candidate)}</div></div>'
|
|
|
|
|
|
|
|
|
|
| 1478 |
'<div class="guided-candidate-columns">'
|
| 1479 |
'<div><h4>Assumptions</h4>'
|
| 1480 |
f'{_list_html(candidate.assumptions)}</div>'
|
|
@@ -1485,7 +1498,7 @@ def candidate_cards_html(
|
|
| 1485 |
f'{_list_html(candidate.evidence_ids)}</div>'
|
| 1486 |
'<div class="guided-candidate-section"><h4>Warnings</h4>'
|
| 1487 |
f'{_list_html(candidate.warnings)}</div>'
|
| 1488 |
-
f"{verification_details}"
|
| 1489 |
'<p class="guided-selection-state">'
|
| 1490 |
f'{_escape("Selected · " + execution_status if selected else execution_status)}</p>'
|
| 1491 |
"</article>"
|
|
@@ -1932,7 +1945,7 @@ def review_html(state: Mapping[str, Any] | WorkbenchState) -> str:
|
|
| 1932 |
f'{_escape(current.dataset_name or current.upload_type)}</strong></div>'
|
| 1933 |
f'<div><small>Objective</small><strong>{_escape(current.target)}</strong></div>'
|
| 1934 |
'<div><small>Candidate</small><strong>'
|
| 1935 |
-
f'{_escape(current.current_candidate
|
| 1936 |
f'<div><small>Quality profile</small><strong>{_escape(current.preset)}</strong></div>'
|
| 1937 |
f'<div><small>Time budget</small><strong>{current.time_limit} seconds</strong></div>'
|
| 1938 |
f'<div><small>Candidate trials</small><strong>{current.trials}</strong></div>'
|
|
@@ -1952,7 +1965,7 @@ def run_plan_summary_html(
|
|
| 1952 |
variant_label = "variant" if run_count == 1 else "variants"
|
| 1953 |
candidate = "Selection pending"
|
| 1954 |
try:
|
| 1955 |
-
candidate = current.current_candidate
|
| 1956 |
except ValueError:
|
| 1957 |
pass
|
| 1958 |
status = "Ready" if not issues else f"{len(issues)} item(s) need attention"
|
|
|
|
| 1407 |
return "".join(cards)
|
| 1408 |
|
| 1409 |
|
| 1410 |
+
def candidate_display_name(candidate: CandidatePipeline) -> str:
|
| 1411 |
+
"""Readable labels for known deterministic defaults; identity stays canonical."""
|
| 1412 |
+
names = {
|
| 1413 |
+
"no_feature_selection_tool + no_transform_tool + autogluon_tool": "AutoGluon baseline",
|
| 1414 |
+
"t_test_feature_selection_tool + no_transform_tool + autogluon_tool": "AutoGluon with feature selection",
|
| 1415 |
+
"no_feature_selection_tool + pca_transform_tool + autogluon_tool": "AutoGluon with PCA",
|
| 1416 |
+
}
|
| 1417 |
+
return names.get(candidate.name, candidate.name or candidate.candidate_id)
|
| 1418 |
+
|
| 1419 |
+
|
| 1420 |
def candidate_cards_html(
|
| 1421 |
state: Mapping[str, Any] | WorkbenchState,
|
| 1422 |
) -> str:
|
|
|
|
| 1480 |
f'" data-candidate-id="{_escape(candidate.candidate_id)}">'
|
| 1481 |
f'<div class="guided-candidate-index">Candidate {index}</div>'
|
| 1482 |
f"{recommended_badge}{verification_badge}"
|
| 1483 |
+
f'<h3>{_escape(candidate_display_name(candidate))}</h3>'
|
| 1484 |
'<p class="guided-candidate-rationale">'
|
| 1485 |
f'{_escape(candidate.rationale or "No rationale provided")}</p>'
|
| 1486 |
'<div class="guided-candidate-section"><h4>Estimated metric values · unmeasured</h4>'
|
| 1487 |
f'<div class="guided-estimate-grid">{_expected_metrics_html(candidate)}</div></div>'
|
| 1488 |
+
'<details class="guided-candidate-details"><summary>Evidence and assessment</summary>'
|
| 1489 |
+
'<h4>Registered pipeline</h4>'
|
| 1490 |
+
f'<p>{_escape(candidate.name or candidate.candidate_id)}</p>'
|
| 1491 |
'<div class="guided-candidate-columns">'
|
| 1492 |
'<div><h4>Assumptions</h4>'
|
| 1493 |
f'{_list_html(candidate.assumptions)}</div>'
|
|
|
|
| 1498 |
f'{_list_html(candidate.evidence_ids)}</div>'
|
| 1499 |
'<div class="guided-candidate-section"><h4>Warnings</h4>'
|
| 1500 |
f'{_list_html(candidate.warnings)}</div>'
|
| 1501 |
+
f"{verification_details}</details>"
|
| 1502 |
'<p class="guided-selection-state">'
|
| 1503 |
f'{_escape("Selected · " + execution_status if selected else execution_status)}</p>'
|
| 1504 |
"</article>"
|
|
|
|
| 1945 |
f'{_escape(current.dataset_name or current.upload_type)}</strong></div>'
|
| 1946 |
f'<div><small>Objective</small><strong>{_escape(current.target)}</strong></div>'
|
| 1947 |
'<div><small>Candidate</small><strong>'
|
| 1948 |
+
f'{_escape(candidate_display_name(current.current_candidate))}</strong></div>'
|
| 1949 |
f'<div><small>Quality profile</small><strong>{_escape(current.preset)}</strong></div>'
|
| 1950 |
f'<div><small>Time budget</small><strong>{current.time_limit} seconds</strong></div>'
|
| 1951 |
f'<div><small>Candidate trials</small><strong>{current.trials}</strong></div>'
|
|
|
|
| 1965 |
variant_label = "variant" if run_count == 1 else "variants"
|
| 1966 |
candidate = "Selection pending"
|
| 1967 |
try:
|
| 1968 |
+
candidate = candidate_display_name(current.current_candidate)
|
| 1969 |
except ValueError:
|
| 1970 |
pass
|
| 1971 |
status = "Ready" if not issues else f"{len(issues)} item(s) need attention"
|
studio_design.py
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Public Studio visual system, independent of the legacy/private application CSS."""
|
| 2 |
+
|
| 3 |
+
import gradio as gr
|
| 4 |
+
|
| 5 |
+
from ui_design import STUDIO_INTERACTION_HEAD_HTML
|
| 6 |
+
|
| 7 |
+
def studio_theme() -> gr.Theme:
|
| 8 |
+
"""System fonts and restrained defaults, without remote font requests."""
|
| 9 |
+
return gr.themes.Base(
|
| 10 |
+
primary_hue=gr.themes.colors.blue,
|
| 11 |
+
neutral_hue=gr.themes.colors.slate,
|
| 12 |
+
font=["system-ui", "sans-serif"],
|
| 13 |
+
font_mono=["ui-monospace", "monospace"],
|
| 14 |
+
).set(
|
| 15 |
+
body_background_fill="#f7f8fa",
|
| 16 |
+
body_text_color="#18212f",
|
| 17 |
+
block_label_text_color="#18212f",
|
| 18 |
+
input_border_color="#e1e5eb",
|
| 19 |
+
button_primary_background_fill="#315fd6",
|
| 20 |
+
button_primary_background_fill_hover="#264dac",
|
| 21 |
+
button_primary_text_color="#ffffff",
|
| 22 |
+
shadow_drop="none",
|
| 23 |
+
)
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
# Document-level rules belong in head: Gradio scopes launch CSS to its app root.
|
| 27 |
+
STUDIO_HEAD_HTML = STUDIO_INTERACTION_HEAD_HTML + """
|
| 28 |
+
<style>
|
| 29 |
+
html { color-scheme: light; background: #f7f8fa; }
|
| 30 |
+
body { margin: 0; background: #f7f8fa; }
|
| 31 |
+
gradio-app { display: block; min-height: 100vh; }
|
| 32 |
+
/* Gradio scopes media selectors beneath .contain; the root is outside it. */
|
| 33 |
+
@media (max-width: 1100px) { html .gradio-container { padding: 0 24px 24px !important; } }
|
| 34 |
+
@media (max-width: 800px) { html .gradio-container { padding: 0 20px 24px !important; } }
|
| 35 |
+
@media (max-width: 480px) { html .gradio-container { padding: 0 16px 20px !important; } }
|
| 36 |
+
</style>
|
| 37 |
+
"""
|
| 38 |
+
|
| 39 |
+
STUDIO_CSS = r"""
|
| 40 |
+
.gradio-container {
|
| 41 |
+
--studio-canvas: #f7f8fa;
|
| 42 |
+
--studio-surface: #fff;
|
| 43 |
+
--studio-text: #18212f;
|
| 44 |
+
--studio-muted: #5c6778;
|
| 45 |
+
--studio-line: #e1e5eb;
|
| 46 |
+
--studio-action: #315fd6;
|
| 47 |
+
--body-background-fill: var(--studio-canvas);
|
| 48 |
+
--body-text-color: var(--studio-text);
|
| 49 |
+
--body-text-color-subdued: var(--studio-muted);
|
| 50 |
+
--block-background-fill: var(--studio-surface);
|
| 51 |
+
--block-border-color: var(--studio-line);
|
| 52 |
+
--block-radius: 8px;
|
| 53 |
+
--input-background-fill: var(--studio-surface);
|
| 54 |
+
--input-border-color: var(--studio-line);
|
| 55 |
+
--input-radius: 6px;
|
| 56 |
+
--button-primary-background-fill: var(--studio-action);
|
| 57 |
+
--button-primary-background-fill-hover: #264dac;
|
| 58 |
+
--button-primary-text-color: #fff;
|
| 59 |
+
--button-primary-border-color: var(--studio-action);
|
| 60 |
+
--button-secondary-background-fill: #fff;
|
| 61 |
+
--button-secondary-background-fill-hover: #f0f3f8;
|
| 62 |
+
--button-secondary-border-color: var(--studio-line);
|
| 63 |
+
--button-secondary-text-color: var(--studio-text);
|
| 64 |
+
width: 100% !important;
|
| 65 |
+
max-width: 1440px !important;
|
| 66 |
+
margin: 0 auto;
|
| 67 |
+
padding: 0 40px 32px !important;
|
| 68 |
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
| 69 |
+
font-size: 15px;
|
| 70 |
+
line-height: 1.5;
|
| 71 |
+
color: var(--studio-text);
|
| 72 |
+
background: var(--studio-canvas);
|
| 73 |
+
}
|
| 74 |
+
footer { display: none !important; }
|
| 75 |
+
.gradio-container .app { gap: 0; }
|
| 76 |
+
.gradio-container .main.app { width: 100%; max-width: none; padding: 0; }
|
| 77 |
+
.gradio-container .html-container { padding: 0 !important; }
|
| 78 |
+
.gradio-container h1, .gradio-container h2, .gradio-container h3,
|
| 79 |
+
.gradio-container h4 { color: var(--studio-text); font-family: inherit; letter-spacing: -.025em; }
|
| 80 |
+
h1 { font-size: 22px; line-height: 1.2; font-weight: 650; }
|
| 81 |
+
h2 { font-size: 28px; line-height: 1.25; font-weight: 650; }
|
| 82 |
+
h3 { font-size: 22px; line-height: 1.3; font-weight: 650; }
|
| 83 |
+
h4 { font-size: 14px; line-height: 1.5; font-weight: 650; }
|
| 84 |
+
p { margin: 0; }
|
| 85 |
+
p, li, dd { overflow-wrap: anywhere; }
|
| 86 |
+
a { color: var(--studio-action); }
|
| 87 |
+
button, input, textarea, select { font: inherit; }
|
| 88 |
+
button { border-radius: 6px !important; box-shadow: none !important; }
|
| 89 |
+
button:focus-visible, input:focus-visible, textarea:focus-visible,
|
| 90 |
+
summary:focus-visible { outline: 2px solid var(--studio-action) !important; outline-offset: 3px; }
|
| 91 |
+
button:disabled { cursor: not-allowed; opacity: .48; }
|
| 92 |
+
.gradio-container .form { border: 0; box-shadow: none; background: transparent; gap: 16px; }
|
| 93 |
+
.gradio-container .block { box-shadow: none; }
|
| 94 |
+
/* Gradio 6 duplicates Group classes on its inner wrapper. Decorate once. */
|
| 95 |
+
.gr-group > .gr-group { flex: 0 0 auto !important; min-width: 0 !important; padding: 0 !important; margin: 0 !important; border: 0 !important; border-radius: 0 !important; background: transparent !important; max-width: none; }
|
| 96 |
+
.gr-group > .styler { --layout-gap: 16px !important; --form-gap-width: 16px !important; --block-radius: 6px !important; --button-border-width: 1px !important; --button-large-radius: 6px !important; --button-small-radius: 6px !important; background: transparent !important; }
|
| 97 |
+
/* A hidden Gradio Group can retain nested visible children. Its step heading
|
| 98 |
+
is the authoritative visibility signal set by the existing navigation event. */
|
| 99 |
+
.studio-primary > .guided-step-panel:not(:has(.guided-step-heading)) { display: none !important; }
|
| 100 |
+
.account-card:not(:has(h2)) { display: none !important; }
|
| 101 |
+
.gradio-container .block:has(> .html-container) { padding: 0 !important; }
|
| 102 |
+
.gradio-container .block .wrap > label { border-radius: 6px; }
|
| 103 |
+
.gradio-container .block .wrap:has(> .wrap-inner) { border: 1px solid var(--studio-line); border-radius: 6px; min-height: 42px; }
|
| 104 |
+
.gradio-container textarea { border: 1px solid var(--studio-line) !important; border-radius: 6px; background: #fff; padding: 12px; }
|
| 105 |
+
.gradio-container .html-container > .prose > :first-child { margin-top: 0; }
|
| 106 |
+
.gradio-container .html-container > .prose > :last-child { margin-bottom: 0; }
|
| 107 |
+
.guided-rail-note { border-top: 1px solid var(--studio-line); padding-top: 16px; font-size: 12px; color: var(--studio-muted); }
|
| 108 |
+
.guided-rail-note strong { display: block; color: var(--studio-text); font-size: 12px; margin-bottom: 6px; }
|
| 109 |
+
.guided-rail-note span { display: block; }
|
| 110 |
+
|
| 111 |
+
.gradio-container .gap { gap: 16px; }
|
| 112 |
+
.gradio-container .row { gap: 24px; }
|
| 113 |
+
.gradio-container .prose { font-size: 15px; }
|
| 114 |
+
.gradio-container .prose h3 { font-size: 20px; }
|
| 115 |
+
.gradio-container .prose p { color: var(--studio-muted); }
|
| 116 |
+
.gradio-container label > span, .gradio-container .block > .label-wrap { color: var(--studio-text); }
|
| 117 |
+
.studio-product-header { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding: 24px 0 20px; }
|
| 118 |
+
.studio-product-identity { display: flex; align-items: center; gap: 12px; }
|
| 119 |
+
.studio-product-identity h1 { margin: 0; font-size: 20px; }
|
| 120 |
+
.studio-product-identity p { color: var(--studio-muted); font-size: 12px; margin-top: 3px; }
|
| 121 |
+
.studio-product-mark { display: grid; place-items: center; width: 36px; height: 36px; border: 1px solid #ccd6eb; border-radius: 9px; font-size: 12px; font-weight: 750; color: var(--studio-action); background: #edf2fc; }
|
| 122 |
+
.studio-product-profile { display: flex; align-items: center; gap: 10px; color: var(--studio-muted); font-size: 12px; }
|
| 123 |
+
.studio-profile-badge { display: none; }
|
| 124 |
+
.research-notice { display: flex; gap: 12px; border-top: 1px solid var(--studio-line); padding: 10px 0; color: var(--studio-muted); font-size: 11px; }
|
| 125 |
+
.notice-title { flex: 0 0 auto; font-weight: 650; }
|
| 126 |
+
#workspace-tabs { border: 0; padding: 0; background: transparent; }
|
| 127 |
+
#workspace-tabs > .tab-wrapper { border-bottom: 1px solid var(--studio-line); padding: 0; margin-bottom: 28px; }
|
| 128 |
+
#workspace-tabs > .tab-wrapper > .tab-container { gap: 28px; }
|
| 129 |
+
#workspace-tabs > .tab-wrapper button[role="tab"] { border-radius: 0 !important; border: 0; border-bottom: 2px solid transparent; padding: 14px 2px; background: transparent; font-size: 14px; color: var(--studio-muted); }
|
| 130 |
+
#workspace-tabs > .tab-wrapper button[role="tab"][aria-selected="true"] { border-bottom-color: var(--studio-action); color: var(--studio-text); font-weight: 650; }
|
| 131 |
+
#workspace-tabs > .tabitem { border: 0; padding: 0; background: transparent; }
|
| 132 |
+
.studio-workbench { align-items: flex-start; gap: 40px !important; }
|
| 133 |
+
.studio-workflow { flex: 0 0 180px !important; min-width: 0 !important; gap: 20px !important; padding: 4px 0; }
|
| 134 |
+
.guided-rail-label, .section-kicker { font-size: 11px; font-weight: 650; color: var(--studio-muted); letter-spacing: .08em; text-transform: uppercase; }
|
| 135 |
+
.guided-stepper { padding: 0 !important; background: transparent !important; border: 0 !important; }
|
| 136 |
+
.guided-stepper .wrap { flex-direction: column; gap: 4px; counter-reset: step; }
|
| 137 |
+
.guided-stepper label { display: flex; align-items: center; gap: 12px; border: 0 !important; border-radius: 6px !important; background: transparent !important; padding: 10px 12px !important; margin: 0 !important; min-height: 42px; color: var(--studio-muted); font-size: 14px; counter-increment: step; }
|
| 138 |
+
.guided-stepper label::before { content: counter(step, decimal-leading-zero); font-size: 11px; color: #7b8595; font-variant-numeric: tabular-nums; }
|
| 139 |
+
.guided-stepper label:has(input:checked) { background: #eaf0fc !important; color: var(--studio-action); font-weight: 650; }
|
| 140 |
+
.guided-stepper label:has(input:checked)::before { color: var(--studio-action); }
|
| 141 |
+
.guided-stepper input { position: absolute; opacity: 0; width: 1px; }
|
| 142 |
+
.guided-stepper label:has(input:focus-visible) { outline: 2px solid var(--studio-action); outline-offset: 2px; }
|
| 143 |
+
.studio-primary { flex: 1 1 0 !important; min-width: 0 !important; }
|
| 144 |
+
.guided-step-panel { border: 1px solid var(--studio-line) !important; border-radius: 12px !important; background: var(--studio-surface) !important; padding: 28px !important; }
|
| 145 |
+
.guided-step-panel > .styler { background: transparent !important; }
|
| 146 |
+
.guided-step-heading { margin-bottom: 12px; }
|
| 147 |
+
.guided-step-heading > span { display: block; color: var(--studio-muted); font-size: 11px; font-weight: 650; letter-spacing: .07em; text-transform: uppercase; margin-bottom: 8px; }
|
| 148 |
+
.guided-step-heading h3 { font-size: 28px; margin: 0 0 10px; }
|
| 149 |
+
.guided-step-heading p { color: var(--studio-muted); max-width: 72ch; font-size: 14px; }
|
| 150 |
+
.guided-research-status, .guided-approval-status, .guided-inline-error, .guided-inline-warning { font-size: 13px; line-height: 1.6; overflow-wrap: anywhere; }
|
| 151 |
+
.guided-approval-status strong, .guided-approval-status span { display: block; }
|
| 152 |
+
.guided-approval-status span { color: var(--studio-muted); margin-top: 4px; }
|
| 153 |
+
.guided-inline-error { color: #ad3434; }
|
| 154 |
+
.guided-inline-warning { color: #8a5a14; }
|
| 155 |
+
.studio-assistant { border: 1px solid var(--studio-line) !important; border-radius: 8px !important; background: transparent !important; }
|
| 156 |
+
.studio-assistant > .label-wrap { padding: 14px 16px; }
|
| 157 |
+
.guided-dossier-header h2 { font-size: 22px; }
|
| 158 |
+
.guided-dossier-block, .guided-inspector-contract, .guided-candidate-section { margin-top: 20px; }
|
| 159 |
+
.guided-dossier-block h4, .guided-inspector-contract h4, .guided-candidate-section h4 { margin: 0 0 8px; }
|
| 160 |
+
.guided-dossier-block p, .guided-dossier-block li { font-size: 14px; color: var(--studio-muted); }
|
| 161 |
+
.guided-refinement-box { background: transparent !important; border: 0 !important; margin-top: 20px; }
|
| 162 |
+
.guided-run-bar { margin-top: 28px; border: 0 !important; border-top: 1px solid var(--studio-line) !important; border-radius: 0 !important; padding: 20px 0 !important; background: transparent !important; }
|
| 163 |
+
.guided-run-bar > .styler { background: transparent !important; }
|
| 164 |
+
.guided-run-bar .row { align-items: center; }
|
| 165 |
+
.guided-run-summary-output { flex: 3 1 540px !important; }
|
| 166 |
+
.guided-run-bar button { flex: 1 1 160px !important; min-width: 160px !important; }
|
| 167 |
+
.guided-run-summary { display: flex; align-items: flex-start; gap: 24px; font-size: 12px; }
|
| 168 |
+
.guided-summary-status { min-width: 120px; }
|
| 169 |
+
.guided-summary-status > span, .guided-summary-facts small { display: block; color: var(--studio-muted); font-size: 11px; margin-bottom: 5px; }
|
| 170 |
+
.guided-summary-status strong { font-size: 13px; font-weight: 600; }
|
| 171 |
+
.guided-summary-facts { display: flex; flex-wrap: wrap; gap: 12px 24px; }
|
| 172 |
+
.guided-summary-facts > span:first-child { max-width: 240px; }
|
| 173 |
+
.guided-empty-state { padding: 24px 0; color: var(--studio-muted); }
|
| 174 |
+
.guided-empty-state h3 { font-size: 16px; margin: 0 0 8px; }
|
| 175 |
+
.guided-empty-copy { color: var(--studio-muted); font-size: 13px; }
|
| 176 |
+
.guided-candidate-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)); gap: 16px; }
|
| 177 |
+
.guided-plan-context { grid-column: 1 / -1; font-size: 13px; color: var(--studio-muted); }
|
| 178 |
+
.guided-plan-context h4 { margin: 0; }
|
| 179 |
+
.guided-plan-context ul { margin: 8px 0; padding-left: 20px; }
|
| 180 |
+
.guided-candidate-card { padding: 20px; border: 1px solid var(--studio-line); border-radius: 10px; min-width: 0; background: #fff; }
|
| 181 |
+
.guided-candidate-card.recommended { border-color: #9cb3ed; }
|
| 182 |
+
.guided-candidate-card.selected { outline: 2px solid var(--studio-action); outline-offset: 2px; }
|
| 183 |
+
.guided-candidate-index { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--studio-muted); margin-bottom: 12px; }
|
| 184 |
+
.guided-candidate-card h3 { font-size: 20px; margin: 12px 0; }
|
| 185 |
+
.guided-candidate-card p, .guided-candidate-card li { font-size: 13px; line-height: 1.6; }
|
| 186 |
+
.guided-candidate-card ul { padding-left: 18px; }
|
| 187 |
+
.guided-candidate-rationale { color: var(--studio-muted); }
|
| 188 |
+
.guided-recommended-badge, .guided-verification-badge, .guided-source-badge, .guided-node-source, .guided-compatibility { display: inline-block; border: 1px solid var(--studio-line); border-radius: 4px; padding: 2px 6px; font-size: 10px; font-weight: 600; margin: 0 4px 4px 0; }
|
| 189 |
+
.guided-recommended-badge { color: var(--studio-action); background: #edf2fc; border-color: #d4dff8; }
|
| 190 |
+
.guided-verification-badge.passed, .guided-compatibility.compatible { color: #267350; }
|
| 191 |
+
.guided-verification-badge.rejected, .guided-compatibility.incompatible { color: #ad3434; }
|
| 192 |
+
.guided-estimate-grid { display: grid; grid-template-columns: repeat(2,minmax(0,1fr)); gap: 8px; }
|
| 193 |
+
.guided-estimate-grid > div { padding: 8px; background: #f7f8fa; border-radius: 6px; font-size: 12px; }
|
| 194 |
+
.guided-estimate-grid strong, .guided-estimate-grid small { display: block; }
|
| 195 |
+
.guided-candidate-details { margin-top: 20px; border-top: 1px solid var(--studio-line); padding-top: 12px; }
|
| 196 |
+
.guided-candidate-details summary { font-size: 13px; font-weight: 600; cursor: pointer; }
|
| 197 |
+
.guided-selection-state { border-top: 1px solid var(--studio-line); margin-top: 16px; padding-top: 12px; color: var(--studio-muted); }
|
| 198 |
+
.studio-pipeline { gap: 8px !important; }
|
| 199 |
+
.guided-pipeline-card-shell { flex: 1 1 120px !important; min-width: 120px !important; border: 1px solid var(--studio-line) !important; border-radius: 8px !important; background: #fafbfc !important; }
|
| 200 |
+
.guided-pipeline-card-shell > .styler { background: transparent !important; }
|
| 201 |
+
.guided-pipeline-card-content { padding: 12px; min-height: 156px; }
|
| 202 |
+
.guided-node-topline { display: flex; gap: 6px; font-size: 11px; color: var(--studio-muted); }
|
| 203 |
+
.guided-node-icon { display: none; }
|
| 204 |
+
.guided-pipeline-card-content h4 { margin: 12px 0 6px; font-size: 13px; overflow-wrap: anywhere; }
|
| 205 |
+
.guided-node-parameter, .guided-node-shape { font-size: 11px; color: var(--studio-muted); margin: 4px 0; }
|
| 206 |
+
.guided-node-shape { display: none; }
|
| 207 |
+
.guided-node-selected { font-size: 10px; color: var(--studio-action); }
|
| 208 |
+
.guided-node-footer { display: flex !important; flex-wrap: wrap; gap: 4px; margin-top: 10px; }
|
| 209 |
+
.guided-node-compatibility { font-size: 10px; }
|
| 210 |
+
.guided-pipeline-node-hitarea { min-width: 0 !important; font-size: 11px !important; padding: 8px 4px !important; border: 0 !important; border-top: 1px solid var(--studio-line) !important; background: #fff !important; border-radius: 0 !important; }
|
| 211 |
+
.guided-node-inspector { margin-top: 12px; }
|
| 212 |
+
.guided-inspector-heading { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; padding-bottom: 16px; border-bottom: 1px solid var(--studio-line); }
|
| 213 |
+
.guided-inspector-heading h3 { margin: 4px 0; font-size: 22px; }
|
| 214 |
+
.guided-inspector-heading span { color: var(--studio-muted); font-size: 12px; }
|
| 215 |
+
.guided-mapping-row, .guided-detail-row { display: grid; grid-template-columns: minmax(120px, 1fr) 3fr; gap: 16px; padding: 8px 0; border-bottom: 1px solid #eef0f3; font-size: 13px; }
|
| 216 |
+
.guided-workbench-heading { margin: 0 0 24px; display: flex; justify-content: space-between; align-items: flex-end; gap: 24px; }
|
| 217 |
+
.guided-workbench-heading h2 { margin: 6px 0 0; }
|
| 218 |
+
.guided-workbench-heading > p { color: var(--studio-muted); font-size: 13px; max-width: 50ch; }
|
| 219 |
+
.results-shell { border: 0 !important; background: transparent !important; }
|
| 220 |
+
.guided-result-tabs > .tabitem { border: 1px solid var(--studio-line); border-radius: 8px; padding: 24px; background: #fff; }
|
| 221 |
+
.guided-result-tabs .tab-container { flex-wrap: wrap; }
|
| 222 |
+
.guided-snapshot-heading { margin-bottom: 20px; }
|
| 223 |
+
.guided-snapshot-heading span { font-size: 11px; color: var(--studio-muted); text-transform: uppercase; letter-spacing: .06em; }
|
| 224 |
+
.guided-snapshot-heading h3 { margin: 6px 0; }
|
| 225 |
+
.guided-snapshot { max-width: 100%; }
|
| 226 |
+
.guided-snapshot pre, .guided-snapshot code { white-space: pre-wrap; overflow-wrap: anywhere; }
|
| 227 |
+
.guided-reflection-metric-grid, .metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px,1fr)); gap: 16px; margin: 16px 0; }
|
| 228 |
+
.guided-reflection-metric, .metric-card { padding: 16px; border: 1px solid var(--studio-line); border-radius: 8px; }
|
| 229 |
+
.candidate-comparison { overflow-x: auto; }
|
| 230 |
+
.candidate-comparison table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
| 231 |
+
.candidate-comparison table th, .candidate-comparison table td { border: 0 !important; border-bottom: 1px solid var(--studio-line) !important; padding: 12px !important; text-align: left; }
|
| 232 |
+
.candidate-comparison table th { color: var(--studio-muted); background: #f7f8fa; font-weight: 500; }
|
| 233 |
+
.studio-model-actions { gap: 8px !important; }
|
| 234 |
+
.studio-model-actions button { min-width: 100px !important; font-size: 12px !important; padding: 10px 8px !important; }
|
| 235 |
+
.studio-models { gap: 32px !important; }
|
| 236 |
+
.studio-model-controls { padding-right: 24px; border-right: 1px solid var(--studio-line); }
|
| 237 |
+
.configuration-heading { margin-bottom: 12px; }
|
| 238 |
+
.configuration-heading h2 { font-size: 28px; margin: 0 0 12px; }
|
| 239 |
+
.configuration-heading p { font-size: 14px; color: var(--studio-muted); }
|
| 240 |
+
.inference-output { min-width: 0; }
|
| 241 |
+
.visual-prediction-gallery { background: #fff; border: 1px solid var(--studio-line); border-radius: 8px; }
|
| 242 |
+
.panel-card, .account-card { border: 1px solid var(--studio-line) !important; border-radius: 12px !important; padding: 28px !important; background: #fff !important; }
|
| 243 |
+
.account-card { max-width: 600px; margin: 40px auto; }
|
| 244 |
+
.account-card h2 { margin: 0 0 12px; }
|
| 245 |
+
.account-card p { color: var(--studio-muted); }
|
| 246 |
+
.run-context-card { padding: 0; }
|
| 247 |
+
.run-status-strip { display: flex; align-items: center; gap: 8px; font-size: 13px; padding-bottom: 20px; }
|
| 248 |
+
.run-dot { width: 7px; height: 7px; border-radius: 50%; background: #8a94a3; }
|
| 249 |
+
.state-complete .run-dot { background: #267350; }
|
| 250 |
+
.state-failed .run-dot { background: #ad3434; }
|
| 251 |
+
.run-alias-row { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; border-bottom: 1px solid var(--studio-line); padding-bottom: 16px; }
|
| 252 |
+
.run-context-label { font-size: 12px; color: var(--studio-muted); }
|
| 253 |
+
.run-alias { font-size: 14px; }
|
| 254 |
+
.run-id-copy { padding: 4px 8px; font-size: 11px; border: 1px solid var(--studio-line); background: #fff; }
|
| 255 |
+
.run-context-list, .run-technical-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 20px; margin: 20px 0; }
|
| 256 |
+
.run-context-item { display: flex; flex-direction: column; gap: 4px; font-size: 14px; }
|
| 257 |
+
.run-context-item strong { font-weight: 500; }
|
| 258 |
+
.run-technical summary, .reported-metrics-detail summary { font-size: 13px; color: var(--studio-muted); cursor: pointer; }
|
| 259 |
+
.workbench-metric-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 16px; margin-bottom: 24px; }
|
| 260 |
+
.workbench-metric-card { border-bottom: 1px solid var(--studio-line); padding: 16px 0; }
|
| 261 |
+
.metric-label { display: block; color: var(--studio-muted); font-size: 12px; margin-bottom: 8px; }
|
| 262 |
+
.metric-value { display: block; font-size: 28px; font-weight: 600; letter-spacing: -.03em; font-variant-numeric: tabular-nums; }
|
| 263 |
+
.metric-value.metric-model { font-size: 18px; }
|
| 264 |
+
.guided-review-grid, .guided-inspector-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 24px; margin: 24px 0; }
|
| 265 |
+
.guided-review-grid small, .guided-review-grid strong { display: block; }
|
| 266 |
+
.guided-review-grid small { color: var(--studio-muted); font-size: 12px; margin-bottom: 8px; }
|
| 267 |
+
.guided-review-grid strong { font-size: 14px; font-weight: 500; overflow-wrap: anywhere; }
|
| 268 |
+
.guided-provider-badge, .guided-evidence-provenance { display: inline-block; font-size: 11px; color: var(--studio-muted); margin: 4px 0; }
|
| 269 |
+
.guided-dossier-heading { margin-bottom: 20px; }
|
| 270 |
+
.guided-dossier-heading h3 { margin: 8px 0; }
|
| 271 |
+
.guided-research-insights, .guided-evidence-grid, .guided-next-experiment-grid { display: grid; gap: 16px; margin: 20px 0; }
|
| 272 |
+
.guided-research-insight, .guided-evidence-card, .guided-next-experiment-card, .guided-trace-entry { padding: 16px 0; border-top: 1px solid var(--studio-line); }
|
| 273 |
+
.guided-evidence-heading h4 { margin: 0 0 8px; }
|
| 274 |
+
.guided-insight-label { color: var(--studio-muted); font-size: 11px; text-transform: uppercase; letter-spacing: .05em; }
|
| 275 |
+
.guided-research-icon { display: none; }
|
| 276 |
+
.guided-dossier-empty h3 { font-size: 18px; margin: 0 0 6px; }
|
| 277 |
+
.guided-empty-insight { padding: 20px 0; }
|
| 278 |
+
.guided-provider-note, .guided-agent-intro { color: var(--studio-muted); font-size: 13px; }
|
| 279 |
+
.guided-estimate > span { display: block; font-size: 11px; color: var(--studio-muted); }
|
| 280 |
+
.guided-estimate strong { font-size: 16px; margin: 6px 0; }
|
| 281 |
+
.guided-trace-list { padding: 0; list-style: none; }
|
| 282 |
+
@media (max-width: 1100px) {
|
| 283 |
+
.studio-workbench { gap: 24px !important; }
|
| 284 |
+
.studio-workflow { flex-basis: 150px !important; }
|
| 285 |
+
.guided-step-panel { padding: 24px !important; }
|
| 286 |
+
.guided-summary-facts { gap: 12px 16px; }
|
| 287 |
+
}
|
| 288 |
+
@media (max-width: 800px) {
|
| 289 |
+
.studio-workbench { flex-direction: column; }
|
| 290 |
+
.studio-workflow, .studio-primary { width: 100%; flex: 1 1 auto !important; }
|
| 291 |
+
.studio-workflow { gap: 8px !important; padding: 0; }
|
| 292 |
+
.guided-rail-label { display: none; }
|
| 293 |
+
.guided-stepper .wrap { flex-direction: row; flex-wrap: wrap; gap: 4px; }
|
| 294 |
+
.guided-stepper label { flex: 0 0 auto; padding: 8px 10px !important; min-height: 40px; gap: 6px; font-size: 12px; }
|
| 295 |
+
.guided-stepper label::before { font-size: 10px; }
|
| 296 |
+
.studio-model-controls { padding-right: 0; border-right: 0; }
|
| 297 |
+
.guided-workbench-heading { display: block; }
|
| 298 |
+
.guided-workbench-heading > p { margin-top: 12px; }
|
| 299 |
+
.guided-run-summary { flex-direction: column; gap: 12px; }
|
| 300 |
+
.research-notice { font-size: 10px; }
|
| 301 |
+
}
|
| 302 |
+
@media (max-width: 480px) {
|
| 303 |
+
.guided-stepper .wrap { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
|
| 304 |
+
.guided-stepper label { padding: 8px 4px !important; font-size: 11px; justify-content: center; }
|
| 305 |
+
.guided-stepper label::before { display: none; }
|
| 306 |
+
.studio-product-header { padding: 20px 0 16px; }
|
| 307 |
+
.studio-product-profile { display: none; }
|
| 308 |
+
.research-notice { display: block; }
|
| 309 |
+
.notice-title { margin-bottom: 4px; }
|
| 310 |
+
#workspace-tabs > .tab-wrapper { margin-bottom: 20px; }
|
| 311 |
+
#workspace-tabs > .tab-wrapper > .tab-container { gap: 24px; }
|
| 312 |
+
.guided-step-panel, .panel-card { padding: 20px !important; }
|
| 313 |
+
.guided-step-heading h3, .configuration-heading h2 { font-size: 24px; }
|
| 314 |
+
.guided-run-bar .row { gap: 12px; }
|
| 315 |
+
.guided-result-tabs > .tabitem { padding: 16px; }
|
| 316 |
+
.guided-mapping-row, .guided-detail-row { grid-template-columns: 1fr; gap: 4px; }
|
| 317 |
+
.guided-inspector-heading { flex-direction: column; }
|
| 318 |
+
}
|
| 319 |
+
@media (prefers-reduced-motion: reduce) {
|
| 320 |
+
*, *::before, *::after { scroll-behavior: auto !important; transition: none !important; }
|
| 321 |
+
}
|
| 322 |
+
"""
|
ui_design.py
CHANGED
|
@@ -2,6 +2,8 @@
|
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
|
|
|
|
|
|
| 5 |
import gradio as gr
|
| 6 |
|
| 7 |
|
|
@@ -6025,6 +6027,11 @@ html:not(.automl-embedded) .gradio-container:not(:has(#workspace-tabs)) {
|
|
| 6025 |
</script>
|
| 6026 |
"""
|
| 6027 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6028 |
|
| 6029 |
def professional_theme() -> gr.Theme:
|
| 6030 |
"""Return a restrained, high-contrast theme built from system fonts."""
|
|
|
|
| 2 |
|
| 3 |
from __future__ import annotations
|
| 4 |
|
| 5 |
+
import re
|
| 6 |
+
|
| 7 |
import gradio as gr
|
| 8 |
|
| 9 |
|
|
|
|
| 6027 |
</script>
|
| 6028 |
"""
|
| 6029 |
|
| 6030 |
+
# Reuse the proven interaction fixes without importing the legacy shell layout.
|
| 6031 |
+
STUDIO_INTERACTION_HEAD_HTML = "\n".join(
|
| 6032 |
+
re.findall(r"<script>.*?</script>", RESPONSIVE_HEAD_HTML, flags=re.DOTALL)
|
| 6033 |
+
)
|
| 6034 |
+
|
| 6035 |
|
| 6036 |
def professional_theme() -> gr.Theme:
|
| 6037 |
"""Return a restrained, high-contrast theme built from system fonts."""
|