AutoML / gateway_app.py
zukhriddinai's picture
Deploy public authenticated AutoML gateway
084b23f verified
Raw
History Blame Contribute Delete
66.1 kB
"""Public CPU login gateway for the private AutoML GPU Space."""
from __future__ import annotations
import os
import shutil
import tempfile
import time
import uuid
from datetime import datetime
from pathlib import Path
from typing import Any, Mapping
import gradio as gr
import pandas as pd
import auth_storage
from gateway_auth import (
AuthResult,
change_password,
handle_denied_request,
login,
request_signup,
requester_key,
)
from gateway_client import BackendResult, BackendUnavailable, GatewayBackendClient
from gateway_contract import (
PROTOCOL_VERSION,
ContractError,
check_upload_size,
inspect_package_manifest,
load_gateway_catalog,
predictions_frame,
)
from gateway_jobs import ROUTED_JOB_ADMISSION, JobRejected
from gateway_routing import (
VALID_ROUTING_MODES,
RouteDecision,
RoutingError,
declare_route,
route_existing,
route_prediction,
route_uploaded,
)
GATEWAY_CATALOG_PATH = Path(__file__).with_name("gateway_catalog.json")
AUTO_CHOICE = "🤖 Auto"
AUTO_DEVICE = "auto - GPU if available"
IMAGE_DEVICE_CHOICES = [AUTO_DEVICE, "cpu", "cuda - requires GPU hardware"]
IMAGE_TASK_CHOICES = [
"classification",
"object_detection",
"instance_segmentation",
"semantic_segmentation",
]
IMAGE_BACKEND_CHOICES = [
"Auto",
"Existing classification pipeline",
"Medical image agent",
"AutoGluon AutoMM",
"YOLO",
"Benchmark backends",
]
IMAGE_FORMAT_CHOICES = ["auto", "folder", "csv", "coco", "yolo", "semantic-mask"]
IMAGE_BACKBONE_CHOICES = [
AUTO_CHOICE,
"raw_pixels",
"mobilenet_v3_small",
"resnet18",
"resnet50",
"densenet121",
"efficientnet_b0",
]
IMAGE_CLASSIFIER_CHOICES = [
AUTO_CHOICE,
"logistic_regression",
"random_forest",
"autogluon",
]
IMAGE_SIZE_CHOICES = [AUTO_CHOICE, "32", "48", "64", "96", "128", "224"]
CANCER_PRESET_CHOICES = [
"Auto - agent decides",
"lc25000_densenet121",
"lc25000_resnet50",
"breakhis_efficientnet_b0",
"ham10000_efficientnet_b0",
"ham10000_mobilenet_v3",
"pcam_resnet18",
]
FEATURE_SELECTION_CHOICES = [
"🤖 Agent Decides",
"no_feature_selection_tool",
"t_test_feature_selection_tool",
]
TRANSFORM_CHOICES = [
"🤖 Agent Decides",
"no_transform_tool",
"pca_transform_tool",
"subspace_transform_tool",
"cluster_undersample_transform_tool",
]
ML_CHOICES = ["🤖 Agent Decides", "autogluon_tool"]
QUALITY_PRESETS = ["medium_quality", "high_quality", "best_quality", "interpretable"]
def _catalog() -> dict[str, Any]:
if GATEWAY_CATALOG_PATH.is_file():
return load_gateway_catalog(GATEWAY_CATALOG_PATH)
return {
"schema_version": 1,
"datasets": {
"No datasets configured": {"targets": [], "image": False}
},
}
def _dataset_metadata(dataset_name: str) -> dict[str, Any]:
return dict(_catalog()["datasets"].get(dataset_name) or {})
def routing_mode_from_env() -> str:
mode = os.getenv("AUTOML_ROUTING_MODE", "dual").strip().lower()
if mode not in VALID_ROUTING_MODES:
raise RuntimeError(
"AUTOML_ROUTING_MODE must be dual or single-gpu."
)
return mode
def format_route_preview(decision: RouteDecision) -> str:
label = "CPU" if decision.profile == "cpu" else "GPU"
return f"**Compute route: Private {label}** — {decision.explanation}"
def _route_preview_error(exc: Exception) -> str:
return f"**Compute route unavailable** — {exc}"
def existing_route_preview(
dataset: Any,
image_backbone: Any,
image_device: Any,
image_task: Any,
image_backend: Any,
image_benchmark_backends: Any,
cancer_preset: Any,
) -> str:
payload = {
"image_backbone": image_backbone,
"image_device": image_device,
"image_task": image_task,
"image_backend": image_backend,
"image_benchmark_backends": image_benchmark_backends,
"cancer_preset": cancer_preset,
}
try:
decision = route_existing(
payload,
dataset_is_image=bool(_dataset_metadata(str(dataset)).get("image")),
routing_mode=routing_mode_from_env(),
)
return format_route_preview(decision)
except (RoutingError, RuntimeError) as exc:
return _route_preview_error(exc)
def uploaded_route_preview(
data_type: Any,
image_backbone: Any,
image_device: Any,
uploaded_image_backend: Any,
cancer_preset: Any,
) -> str:
payload = {
"data_type": data_type,
"image_backbone": image_backbone,
"image_device": image_device,
"uploaded_image_backend": uploaded_image_backend,
"cancer_preset": cancer_preset,
}
try:
return format_route_preview(
route_uploaded(payload, routing_mode=routing_mode_from_env())
)
except (RoutingError, RuntimeError) as exc:
return _route_preview_error(exc)
def replay_route_preview(manifest: Mapping[str, Any] | None, image_device: Any) -> str:
try:
return format_route_preview(
route_prediction(
{"image_device": image_device},
dict(manifest or {}),
routing_mode=routing_mode_from_env(),
)
)
except (RoutingError, RuntimeError) as exc:
return _route_preview_error(exc)
def _normalize_token(value: Any) -> str:
return str(value or "").strip().lower().replace("-", "_").replace(" ", "_")
def normalize_image_task(value: Any) -> str:
token = _normalize_token(value)
aliases = {
"classification": "classification",
"object_detection": "object_detection",
"detection": "object_detection",
"instance_segmentation": "instance_segmentation",
"semantic_segmentation": "semantic_segmentation",
}
return aliases.get(token, "classification")
def normalize_image_backend(value: Any) -> str:
aliases = {
"auto": "auto",
"existing_classification_pipeline": "existing",
"existing": "existing",
"medical_image_agent": "medical_agent",
"medical_agent": "medical_agent",
"autogluon_automm": "automm",
"automm": "automm",
"yolo": "yolo",
"benchmark_backends": "benchmark",
"benchmark": "benchmark",
}
return aliases.get(_normalize_token(value), "auto")
def get_image_task_ui_state(image_task: Any, image_backend: Any) -> dict[str, Any]:
task = normalize_image_task(image_task)
backend = normalize_image_backend(image_backend)
effective = backend
if backend == "auto":
effective = "existing" if task == "classification" else (
"automm" if task == "semantic_segmentation" else "yolo"
)
notes = {
"classification": "Classification supports the existing classifier, Medical Image Agent, AutoMM, and benchmark mode.",
"object_detection": "Object detection uses the managed COCO dataset for an existing image dataset.",
"instance_segmentation": "Instance segmentation uses managed COCO polygon/RLE annotations.",
"semantic_segmentation": "Semantic segmentation requires a managed image/mask manifest.",
}
return {
"classification": task == "classification" and effective == "existing",
"medical": task == "classification" and effective == "medical_agent",
"detection": task == "object_detection",
"instance": task == "instance_segmentation",
"semantic": task == "semantic_segmentation",
"benchmark": backend == "benchmark",
"optional": effective in {"automm", "yolo", "benchmark"},
"note": notes[task],
}
def image_task_ui_updates(image_task: Any, image_backend: Any):
state = get_image_task_ui_state(image_task, image_backend)
return (
gr.update(visible=state["classification"]),
gr.update(visible=state["medical"]),
gr.update(visible=state["detection"]),
gr.update(visible=state["instance"]),
gr.update(visible=state["semantic"]),
gr.update(visible=state["benchmark"]),
gr.update(visible=state["optional"]),
gr.update(value=state["note"]),
)
def uploaded_image_backend_updates(image_backend: Any):
backend = normalize_image_backend(image_backend)
return gr.update(visible=backend == "existing"), gr.update(visible=backend == "medical_agent")
def update_existing_dataset_controls(dataset_name: str):
metadata = _dataset_metadata(dataset_name)
targets = list(metadata.get("targets") or [])
image = bool(metadata.get("image"))
return (
gr.update(choices=targets, value=targets[0] if targets else None),
gr.update(visible=not image, value=False if image else False),
gr.update(label="📊 Validation Size" if image else "📊 Test Size"),
gr.update(visible=not image),
gr.update(visible=image),
)
def update_uploaded_type_controls(data_type: str):
image = data_type == "Image Dataset"
return (
gr.update(
label="📁 Training Data (Image ZIP or CSV Manifest)" if image else "📁 Training Data (CSV)",
file_types=[".zip", ".csv"] if image else [".csv"],
),
gr.update(
label="📁 Test Data (Image ZIP or CSV Manifest) - Optional" if image else "📁 Test Data (CSV) - Optional",
file_types=[".zip", ".csv"] if image else [".csv"],
),
gr.update(visible=not image),
gr.update(visible=not image),
gr.update(label="📊 Validation Size" if image else "📊 Test Size"),
gr.update(visible=not image),
gr.update(visible=image),
)
def _file_path(value: Any) -> str:
if value is None:
return ""
if isinstance(value, (str, os.PathLike)):
return str(value)
for attribute in ("path", "name"):
candidate = getattr(value, attribute, None)
if candidate:
return str(candidate)
if isinstance(value, Mapping):
for key in ("path", "name"):
if value.get(key):
return str(value[key])
return ""
def update_uploaded_targets(data_type: str, train_file: Any = None):
if data_type == "Image Dataset":
return gr.update(choices=["label"], value="label")
path = _file_path(train_file)
if not path:
return gr.update(choices=[], value=None)
try:
columns = pd.read_csv(path, nrows=1).columns.tolist()
except Exception:
return gr.update(choices=[], value=None)
return gr.update(choices=columns, value=columns[0] if columns else None)
def _saved_model_choices(
session_token: str | None, request: gr.Request | None = None
) -> list[tuple[str, str]]:
username = auth_storage.current_username(request, session_token)
if not username:
return []
choices = []
for record in auth_storage.list_saved_models(username):
task = str(record.get("task_type") or "").strip()
artifact = str(record.get("artifact_type") or "model")
detail = f"{artifact}/{task}" if task else artifact
created = str(record.get("created_at") or "")[:10] or "unknown date"
target = str(record.get("target_column") or "unknown target")
label = f"{record.get('display_name') or 'AutoML model'} - {detail} - {target} - {created}"
choices.append((label, str(record["model_id"])))
return choices
def refresh_saved_model_choices(
selected: str | None = None,
session_token: str | None = None,
request: gr.Request | None = None,
):
choices = _saved_model_choices(session_token, request)
values = {value for _, value in choices}
return gr.update(
choices=choices,
value=selected if selected in values else None,
interactive=bool(choices),
label="Saved Cloud Model",
)
def _auth_controls(signed_in: bool):
return (
gr.update(interactive=signed_in),
gr.update(interactive=signed_in),
gr.update(interactive=signed_in),
gr.update(interactive=False),
)
def _neutral_replay_outputs(signed_in: bool):
return (
"Choose a saved cloud model or upload a trained model package."
if signed_in
else "Sign in before loading a model package.",
"",
"",
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False, value=AUTO_DEVICE),
gr.update(visible=False, value=16),
{},
"Choose a model to inspect its compute route."
if signed_in
else "Sign in to inspect the model compute route.",
"Status: _idle_",
"",
pd.DataFrame(),
)
def _pending_account_controls():
requests = auth_storage.list_pending_account_requests()
rows = [
[str(record.get("username") or ""), str(record.get("requested_at") or "")]
for record in requests
]
choices = [row[0] for row in rows]
return rows, choices
def _admin_ui_outputs(username: str):
if not auth_storage.is_admin(username):
return (
gr.update(visible=False),
gr.update(value=[]),
gr.update(choices=[], value=None),
"",
)
rows, choices = _pending_account_controls()
return (
gr.update(visible=True),
gr.update(value=rows),
gr.update(choices=choices, value=choices[0] if choices else None),
f"{len(choices)} pending account request(s).",
)
def _auth_outputs(
result: AuthResult,
request: gr.Request | None = None,
):
if result.ok:
return (
result.message,
result.session_token,
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=True),
f"Signed in as **{result.username}**",
refresh_saved_model_choices(
None,
result.session_token,
request,
),
"",
*_auth_controls(True),
*_neutral_replay_outputs(True),
gr.update(visible=False),
"",
"",
None,
*_admin_ui_outputs(result.username),
)
denied = result.account_status == auth_storage.ACCOUNT_STATUS_DENIED
return (
result.message,
"",
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False),
"Sign in or create an account to continue.",
refresh_saved_model_choices(None, "", request),
"",
*_auth_controls(False),
*_neutral_replay_outputs(False),
gr.update(visible=denied),
result.username if denied else "",
"",
None,
*_admin_ui_outputs(""),
)
def restore_auth_ui(
session_token: str | None = None,
request: gr.Request | None = None,
):
token = str(session_token or "").strip()
username = auth_storage.current_username(request, token)
if username:
return _auth_outputs(
AuthResult(
True,
f"Signed in as {username}.",
session_token=token,
username=username,
),
request,
)
message = (
"Your session expired. Sign in again."
if token
else "Sign in or create an account to continue."
)
return _auth_outputs(AuthResult(False, message), request)
def handle_login_ui(
username: str, password: str, request: gr.Request | None = None
):
return _auth_outputs(
login(username, password, requester_key(request)),
request,
)
def handle_signup_ui(
username: str,
password: str,
confirm_password: str,
request: gr.Request | None = None,
):
return _auth_outputs(
request_signup(
username,
password,
confirm_password,
requester_key(request),
),
request,
)
def handle_denied_request_ui(
username: str,
password: str,
choice: str,
):
result = handle_denied_request(username, password, choice)
denied = result.account_status == auth_storage.ACCOUNT_STATUS_DENIED
return (
result.message,
gr.update(visible=denied),
result.username if denied else "",
"",
None,
)
def handle_logout_ui():
return _auth_outputs(AuthResult(False, "Signed out."))
def handle_password_change_ui(
username: str,
old_password: str,
new_password: str,
confirm_password: str,
):
return change_password(
username, old_password, new_password, confirm_password
).message
def refresh_pending_account_requests(
session_token: str | None = None,
request: gr.Request | None = None,
):
admin_username = auth_storage.current_username(request, session_token)
admin = auth_storage.get_user(admin_username)
if (
not auth_storage.is_admin(admin_username)
or admin is None
or str(admin.get("account_status") or "")
!= auth_storage.ACCOUNT_STATUS_APPROVED
):
return (
gr.update(value=[]),
gr.update(choices=[], value=None),
"Only an approved administrator can view account requests.",
)
rows, choices = _pending_account_controls()
return (
gr.update(value=rows),
gr.update(choices=choices, value=choices[0] if choices else None),
f"{len(choices)} pending account request(s).",
)
def handle_admin_account_review(
session_token: str,
pending_username: str,
decision: str,
request: gr.Request | None = None,
):
admin_username = ""
try:
admin_username = auth_storage.require_authenticated_username(
request,
session_token,
)
reviewed = auth_storage.review_account_request(
admin_username,
pending_username,
decision,
)
message = (
f"Account request for {reviewed['username']} was "
f"{reviewed['account_status']} by {admin_username}."
)
except (PermissionError, ValueError) as exc:
message = f"Could not review account request: {exc}"
except Exception:
message = "Could not review the account request. Try again later."
rows, choices = (
_pending_account_controls()
if auth_storage.is_admin(admin_username)
else ([], [])
)
return (
gr.update(value=rows),
gr.update(choices=choices, value=choices[0] if choices else None),
message,
)
def handle_admin_account_approval(
session_token: str,
pending_username: str,
request: gr.Request | None = None,
):
return handle_admin_account_review(
session_token,
pending_username,
auth_storage.ACCOUNT_STATUS_APPROVED,
request,
)
def handle_admin_account_denial(
session_token: str,
pending_username: str,
request: gr.Request | None = None,
):
return handle_admin_account_review(
session_token,
pending_username,
auth_storage.ACCOUNT_STATUS_DENIED,
request,
)
def _model_info(manifest: Mapping[str, Any], artifact_type: str):
target = str(
manifest.get("target_column") or manifest.get("label_column") or "Unknown"
)
if artifact_type == "image":
task = normalize_image_task(manifest.get("task_type") or "classification")
names = {
"classification": ("Image classifier", "class label"),
"object_detection": ("Object detection model", "bounding boxes"),
"instance_segmentation": ("Instance segmentation model", "masks and bounding boxes"),
"semantic_segmentation": ("Semantic segmentation model", "segmentation masks"),
}
model_type, output = names[task]
info = f"### Loaded Model\n\n**Model type:** {model_type}\n\n**Output:** {output}\n\n**Prediction target:** `{target}`\n\n**Required input:** one image file"
cpu_decision = route_prediction(
{"image_device": "cpu"},
manifest,
routing_mode=routing_mode_from_env(),
)
default_device = (
"cpu"
if cpu_decision.profile == "cpu"
else "cuda - requires GPU hardware"
)
return (
info,
gr.update(visible=False),
gr.update(visible=True),
gr.update(visible=True, value=default_device),
gr.update(visible=True),
gr.update(interactive=True),
dict(manifest),
replay_route_preview(manifest, default_device),
)
protected = "enabled" if manifest.get("input_schema_file") else "legacy package"
info = f"### Loaded Model\n\n**Model type:** Tabular model\n\n**Prediction target:** `{target}`\n\n**Input schema protection:** {protected}\n\n**Required input:** one CSV containing one row"
return (
info,
gr.update(visible=True),
gr.update(visible=False),
gr.update(visible=False, value="cpu"),
gr.update(visible=False),
gr.update(interactive=True),
dict(manifest),
replay_route_preview(manifest, "cpu"),
)
def update_single_input_controls(
model_file: Any,
saved_model_id: str | None,
session_token: str | None,
request: gr.Request | None = None,
):
username = auth_storage.current_username(request, session_token)
if not username:
return (
"Sign in before loading a model package.",
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(interactive=False),
{},
"Sign in to inspect the model compute route.",
)
if not saved_model_id and not model_file:
return (
"Choose a saved cloud model or upload a trained model package.",
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(interactive=False),
{},
"Choose a model to inspect its compute route.",
)
try:
if saved_model_id:
record = auth_storage.get_saved_model(username, saved_model_id)
if record is None:
raise PermissionError("Saved model not found for this account.")
manifest = auth_storage.manifest_from_record(record)
artifact_type = str(
record.get("artifact_type")
or manifest.get("artifact_type")
or "tabular"
)
return _model_info(manifest, artifact_type)
package_path = _file_path(model_file)
if not package_path:
raise ValueError("Choose a saved cloud model or upload a package.")
manifest, artifact_type = inspect_package_manifest(package_path)
return _model_info(manifest, artifact_type)
except Exception as exc:
return (
f"❌ Could not inspect model package: {exc}",
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(visible=False),
gr.update(interactive=False),
{},
_route_preview_error(exc),
)
def _training_outputs(
status: str,
metrics: str = "",
predictions: pd.DataFrame | None = None,
summary: str = "",
tools: str = "",
reasoning: str = "",
package: str | None = None,
):
return status, metrics, predictions if predictions is not None else pd.DataFrame(), summary, tools, reasoning, package
def _training_error(message: str):
safe = str(message or "The request failed.")
if not safe.startswith("❌"):
safe = f"❌ Error: {safe}"
return _training_outputs(safe)
def _gateway_egress_root() -> Path:
return Path(
os.getenv("AUTOML_GATEWAY_EGRESS_DIR", "/tmp/automl_gateway_egress")
).expanduser()
def _gateway_egress_max_age() -> int:
try:
return max(
60,
int(os.getenv("AUTOML_GATEWAY_EGRESS_MAX_AGE_SECONDS", "3600")),
)
except ValueError:
return 3600
def prune_gateway_egress(
root: str | Path | None = None,
*,
max_age_seconds: int | None = None,
now: float | None = None,
) -> int:
base = Path(root) if root is not None else _gateway_egress_root()
if not base.exists():
return 0
current = float(now if now is not None else time.time())
maximum = max(60, int(max_age_seconds or _gateway_egress_max_age()))
removed = 0
for child in list(base.iterdir()):
try:
expired = current - child.stat().st_mtime > maximum
except OSError:
continue
if not expired:
continue
try:
shutil.rmtree(child) if child.is_dir() else child.unlink()
removed += 1
except OSError:
continue
return removed
def _copy_to_gateway_egress(source_path: str) -> str:
source = Path(source_path)
if not source.is_file():
raise FileNotFoundError("The backend model package was not downloaded.")
root = _gateway_egress_root()
root.mkdir(parents=True, exist_ok=True)
request_dir = root / uuid.uuid4().hex
request_dir.mkdir(mode=0o700)
destination = request_dir / (source.name or "automl_model.zip")
shutil.copy2(source, destination)
os.utime(request_dir, None)
return str(destination)
def _cleanup_files(paths: tuple[str, ...] | list[str]) -> None:
for value in paths:
try:
path = Path(value)
if path.is_file():
path.unlink()
except OSError:
continue
def _display_name(payload: Mapping[str, Any]) -> str:
source = str(payload.get("dataset") or payload.get("data_type") or "AutoML model")
target = str(payload.get("target_column") or "").strip()
stamp = datetime.now().strftime("%Y-%m-%d %H:%M")
return f"{source} ({target}) {stamp}" if target else f"{source} {stamp}"
def render_training_result(
username: str,
envelope: Mapping[str, Any],
package_path: str | None,
display_name: str,
):
if envelope.get("ok") is not True:
error = envelope.get("error") or {}
return _training_error(str(error.get("message") or "The backend job failed."))
summary = str(envelope.get("summary") or "")
download = None
if package_path:
try:
manifest, artifact_type = inspect_package_manifest(package_path)
download = _copy_to_gateway_egress(package_path)
try:
record = auth_storage.save_model_for_user(
username,
download,
display_name=display_name,
manifest=manifest,
artifact_type=artifact_type,
)
summary += f"\n\nSaved cloud model: `{record.get('display_name')}` (encrypted, id `{record.get('model_id')}`)."
except Exception:
summary += "\n\nSaved cloud model: cloud save failed; use the immediate download instead."
except Exception as exc:
summary += f"\n\nModel package rejected by the gateway: {exc}"
download = None
return _training_outputs(
str(envelope.get("status") or "Complete"),
str(envelope.get("metrics_markdown") or ""),
predictions_frame(envelope),
summary,
str(envelope.get("tools_used") or ""),
str(envelope.get("reasoning") or ""),
download,
)
def run_existing_remote(
payload: Mapping[str, Any],
session_token: str | None,
request: gr.Request | None = None,
):
"""Authenticated generator for one existing-dataset backend job."""
try:
username = auth_storage.require_authenticated_username(request, session_token)
except PermissionError:
yield _training_error("Sign in before starting an analysis.")
return
result: BackendResult | None = None
decision: RouteDecision | None = None
try:
routed_payload = dict(payload)
metadata = _dataset_metadata(str(routed_payload.get("dataset") or ""))
dataset_is_image = bool(metadata.get("image"))
routed_payload["data_type"] = (
"Image Dataset" if dataset_is_image else "Tabular CSV"
)
mode = routing_mode_from_env()
decision = route_existing(
routed_payload,
dataset_is_image=dataset_is_image,
routing_mode=mode,
)
request_payload = declare_route(routed_payload, decision, mode)
profile_label = decision.profile.upper()
yield _training_outputs(
f"Queued for the private {profile_label} backend…"
)
with ROUTED_JOB_ADMISSION.lease(
decision.profile, username, timeout=30
):
yield _training_outputs(f"{profile_label} backend is waking up…")
client = GatewayBackendClient.from_env(decision.profile)
result = client.run_existing(request_payload)
prune_gateway_egress()
yield render_training_result(
username,
result.envelope,
result.package_path,
_display_name(request_payload),
)
except BackendUnavailable as exc:
if decision is not None and decision.profile == "cpu":
yield _training_error(
"The private CPU backend is unavailable; GPU fallback was not attempted."
)
else:
yield _training_error(str(exc))
except (ContractError, RoutingError, JobRejected) as exc:
yield _training_error(str(exc))
except Exception:
yield _training_error("The gateway could not complete the request.")
finally:
if result is not None:
_cleanup_files(result.cleanup_files)
def run_uploaded_remote(
payload: Mapping[str, Any],
train_file: Any,
test_file: Any,
session_token: str | None,
request: gr.Request | None = None,
):
"""Authenticated generator for one uploaded-dataset backend job."""
try:
username = auth_storage.require_authenticated_username(request, session_token)
except PermissionError:
yield _training_error("Sign in before starting an analysis.")
return
train_path = _file_path(train_file)
test_path = _file_path(test_file)
result: BackendResult | None = None
decision: RouteDecision | None = None
try:
if not train_path:
raise ContractError("Upload a training dataset first.")
check_upload_size(train_path)
if test_path:
check_upload_size(test_path)
mode = routing_mode_from_env()
decision = route_uploaded(payload, routing_mode=mode)
request_payload = declare_route(payload, decision, mode)
profile_label = decision.profile.upper()
yield _training_outputs(
f"Queued for the private {profile_label} backend…"
)
with ROUTED_JOB_ADMISSION.lease(
decision.profile, username, timeout=30
):
yield _training_outputs(f"{profile_label} backend is waking up…")
client = GatewayBackendClient.from_env(decision.profile)
result = client.run_uploaded(
request_payload, train_path, test_path or None
)
prune_gateway_egress()
yield render_training_result(
username,
result.envelope,
result.package_path,
_display_name(request_payload),
)
except BackendUnavailable as exc:
if decision is not None and decision.profile == "cpu":
yield _training_error(
"The private CPU backend is unavailable; GPU fallback was not attempted."
)
else:
yield _training_error(str(exc))
except (ContractError, RoutingError, JobRejected) as exc:
yield _training_error(str(exc))
except Exception:
yield _training_error("The gateway could not complete the request.")
finally:
if result is not None:
_cleanup_files(result.cleanup_files)
def predict_remote(
model_file: Any,
tabular_text: str,
tabular_file: Any,
image_file: Any,
image_device: str,
image_batch_size: int,
saved_model_id: str | None,
session_token: str | None,
request: gr.Request | None = None,
):
"""Authenticate, materialize one package, and replay it privately."""
cleanup_root = ""
decision: RouteDecision | None = None
try:
username = auth_storage.require_authenticated_username(request, session_token)
if saved_model_id:
package_path, cleanup_root = auth_storage.materialize_saved_model(
username, saved_model_id
)
else:
package_path = _file_path(model_file)
if not package_path:
raise ContractError("Choose a saved model or upload a package first.")
manifest, artifact_type = inspect_package_manifest(package_path)
if not manifest.get("artifact_type"):
manifest = {**manifest, "artifact_type": artifact_type}
tabular_path = _file_path(tabular_file)
image_path = _file_path(image_file)
if artifact_type == "image" and not image_path:
raise ContractError("Upload one image for this model.")
if artifact_type != "image" and not tabular_path and not str(tabular_text or "").strip():
raise ContractError("Upload one tabular CSV row for this model.")
if tabular_path:
check_upload_size(tabular_path)
if image_path:
check_upload_size(image_path)
payload = {
"protocol_version": PROTOCOL_VERSION,
"tabular_text": str(tabular_text or ""),
"image_device": image_device or AUTO_DEVICE,
"image_batch_size": int(image_batch_size or 16),
}
mode = routing_mode_from_env()
decision = route_prediction(payload, manifest, routing_mode=mode)
request_payload = declare_route(payload, decision, mode)
with ROUTED_JOB_ADMISSION.lease(
decision.profile, username, timeout=30
):
client = GatewayBackendClient.from_env(decision.profile)
result = client.predict_package(
request_payload,
package_path,
tabular_path=tabular_path or None,
image_path=image_path or None,
)
if result.envelope.get("ok") is not True:
error = result.envelope.get("error") or {}
return f"❌ Error: {error.get('message') or 'Prediction failed.'}", "", pd.DataFrame()
return (
str(result.envelope.get("status") or "✅ Prediction complete."),
str(result.envelope.get("summary") or ""),
predictions_frame(result.envelope),
)
except BackendUnavailable as exc:
if decision is not None and decision.profile == "cpu":
return (
"❌ Error: The private CPU backend is unavailable; GPU fallback was not attempted.",
"",
pd.DataFrame(),
)
return f"❌ Error: {exc}", "", pd.DataFrame()
except (ContractError, RoutingError, JobRejected, PermissionError) as exc:
return f"❌ Error: {exc}", "", pd.DataFrame()
except Exception:
return "❌ Error: The gateway could not complete the prediction.", "", pd.DataFrame()
finally:
if cleanup_root:
shutil.rmtree(cleanup_root, ignore_errors=True)
def _existing_payload(
dataset,
target_column,
split_training_data,
test_size,
preset,
time_limit,
trials,
feature_selection_tool,
transform_tool,
ml_tool,
image_backbone,
image_classifier,
image_size,
image_device,
image_batch_size,
image_pretrained,
image_task,
image_backend,
image_dataset_format,
image_epochs,
image_imgsz,
image_benchmark_backends,
cancer_preset,
):
return {
"protocol_version": PROTOCOL_VERSION,
"dataset": dataset,
"target_column": target_column,
"split_training_data": bool(split_training_data),
"test_size": float(test_size),
"preset": preset,
"time_limit": int(time_limit),
"trials": int(trials),
"feature_selection_tool": feature_selection_tool,
"transform_tool": transform_tool,
"ml_tool": ml_tool,
"image_backbone": image_backbone,
"image_classifier": image_classifier,
"image_size": image_size,
"image_device": image_device,
"image_batch_size": int(image_batch_size),
"image_pretrained": bool(image_pretrained),
"image_task": image_task,
"image_backend": image_backend,
"image_dataset_format": image_dataset_format,
"image_data_path": "",
"image_epochs": int(image_epochs),
"image_imgsz": int(image_imgsz),
"image_benchmark_backends": image_benchmark_backends,
"cancer_preset": cancer_preset,
}
def existing_ui_handler(
dataset,
target_column,
split_training_data,
test_size,
preset,
time_limit,
trials,
feature_selection_tool,
transform_tool,
ml_tool,
image_backbone,
image_classifier,
image_size,
image_device,
image_batch_size,
image_pretrained,
image_task,
image_backend,
image_dataset_format,
image_epochs,
image_imgsz,
image_benchmark_backends,
cancer_preset,
session_token,
request: gr.Request | None = None,
):
payload = _existing_payload(
dataset,
target_column,
split_training_data,
test_size,
preset,
time_limit,
trials,
feature_selection_tool,
transform_tool,
ml_tool,
image_backbone,
image_classifier,
image_size,
image_device,
image_batch_size,
image_pretrained,
image_task,
image_backend,
image_dataset_format,
image_epochs,
image_imgsz,
image_benchmark_backends,
cancer_preset,
)
yield from run_existing_remote(payload, session_token, request)
def _uploaded_payload(
data_type,
target_column,
split_training_data,
test_size,
preset,
time_limit,
trials,
feature_selection_tool,
transform_tool,
ml_tool,
image_backbone,
image_classifier,
image_size,
image_device,
image_batch_size,
image_pretrained,
uploaded_image_backend,
cancer_preset,
):
return {
"protocol_version": PROTOCOL_VERSION,
"data_type": data_type,
"target_column": target_column,
"split_training_data": bool(split_training_data),
"test_size": float(test_size),
"preset": preset,
"time_limit": int(time_limit),
"trials": int(trials),
"feature_selection_tool": feature_selection_tool,
"transform_tool": transform_tool,
"ml_tool": ml_tool,
"image_backbone": image_backbone,
"image_classifier": image_classifier,
"image_size": image_size,
"image_device": image_device,
"image_batch_size": int(image_batch_size),
"image_pretrained": bool(image_pretrained),
"uploaded_image_backend": uploaded_image_backend,
"cancer_preset": cancer_preset,
"image_epochs": 1,
"image_imgsz": 64,
}
def uploaded_ui_handler(
data_type,
target_column,
split_training_data,
test_size,
preset,
time_limit,
trials,
feature_selection_tool,
transform_tool,
ml_tool,
image_backbone,
image_classifier,
image_size,
image_device,
image_batch_size,
image_pretrained,
uploaded_image_backend,
cancer_preset,
train_file,
test_file,
session_token,
request: gr.Request | None = None,
):
payload = _uploaded_payload(
data_type,
target_column,
split_training_data,
test_size,
preset,
time_limit,
trials,
feature_selection_tool,
transform_tool,
ml_tool,
image_backbone,
image_classifier,
image_size,
image_device,
image_batch_size,
image_pretrained,
uploaded_image_backend,
cancer_preset,
)
yield from run_uploaded_remote(
payload, train_file, test_file, session_token, request
)
def _add_results_section():
gr.Markdown("---\n## 📊 Results & Analysis")
status = gr.Markdown("Status: _idle_")
metrics = gr.Markdown("### 📊 Model Performance Metrics\n\n_Run a pipeline to see metrics._")
predictions = gr.DataFrame(label="Sample Predictions", interactive=False)
package = gr.File(label="Trained Model Package", interactive=False)
summary = gr.Textbox(label="Dataset Summary", interactive=False, max_lines=18)
tools = gr.Textbox(label="Tools Used", interactive=False)
reasoning = gr.Textbox(label="🤖 Agent Reasoning", interactive=False, max_lines=20)
return status, metrics, predictions, summary, tools, reasoning, package
def create_interface() -> gr.Blocks:
"""Build the public frontend without importing or contacting ML code."""
routing_mode_from_env()
catalog = _catalog()["datasets"]
dataset_choices = list(catalog) or ["No datasets configured"]
default_dataset = "titanic" if "titanic" in catalog else dataset_choices[0]
metadata = dict(catalog.get(default_dataset) or {})
targets = list(metadata.get("targets") or [])
default_target = targets[0] if targets else None
initial_image = bool(metadata.get("image"))
initial_state = get_image_task_ui_state("classification", "Auto")
with gr.Blocks(title="AutoML Pipeline") as demo:
session_token = gr.BrowserState(
"",
storage_key="automl-auth-session-v1",
secret=auth_storage.browser_state_secret(),
)
gr.Markdown("# 🤖 AutoML Pipeline")
gr.Markdown("Sign in or request an account, configure a workflow, and run it on the appropriate private CPU or GPU backend.")
with gr.Group(visible=True) as auth_panel:
gr.Markdown("## Account")
auth_status = gr.Markdown("Sign in or create an account to continue.")
with gr.Tabs():
with gr.TabItem("Log In"):
login_username = gr.Textbox(label="Username")
login_password = gr.Textbox(label="Password", type="password")
login_button = gr.Button("Log In")
with gr.TabItem(
"Sign Up",
visible=auth_storage.signups_allowed(),
):
signup_username = gr.Textbox(label="Username")
signup_password = gr.Textbox(label="Password", type="password")
signup_confirm_password = gr.Textbox(
label="Confirm Password",
type="password",
)
signup_button = gr.Button("Submit Account Request")
with gr.TabItem("Change Password"):
password_username = gr.Textbox(label="Username")
old_password = gr.Textbox(label="Current Password", type="password")
new_password = gr.Textbox(label="New Password", type="password")
confirm_password = gr.Textbox(label="Confirm New Password", type="password")
password_button = gr.Button("Update Password")
password_status = gr.Markdown("")
denied_username_state = gr.State("")
with gr.Group(visible=False) as denied_request_group:
gr.Markdown(
"### Denied account request\n"
"Re-enter the password used for signup, then choose whether to "
"submit the request for another review or permanently delete it."
)
denied_password = gr.Textbox(
label="Signup Password",
type="password",
)
denied_request_choice = gr.Radio(
choices=[
"Re-send request",
"Do not re-send; delete my account request",
],
label="What would you like to do?",
)
denied_request_button = gr.Button("Submit Choice")
with gr.Row(visible=False) as account_row:
account_status = gr.Markdown("Account: _signed out_")
logout_button = gr.Button("Log Out", min_width=100)
with gr.Tabs(visible=False) as main_tabs:
with gr.TabItem("📁 Use Existing Datasets"):
with gr.Row():
with gr.Column():
dataset = gr.Dropdown(dataset_choices, value=default_dataset, label="Dataset")
target = gr.Dropdown(targets, value=default_target, label="🎯 Target Column to Predict", allow_custom_value=True)
split = gr.Checkbox(False, label="🔄 Split Training Data", visible=not initial_image)
test_size = gr.Slider(0.1, 0.5, value=0.2, step=0.05, label="📊 Validation Size" if initial_image else "📊 Test Size")
preset = gr.Dropdown(QUALITY_PRESETS, value="medium_quality", label="Quality Preset")
time_limit = gr.Slider(60, 3600, value=300, step=60, label="Time Limit (seconds)")
trials = gr.Dropdown(["1", "2", "3", "4"], value="1", label="Trials")
with gr.Group(visible=not initial_image) as tabular_group:
gr.Markdown("### 🛠️ Tool Selection (Optional)")
feature = gr.Dropdown(FEATURE_SELECTION_CHOICES, value="🤖 Agent Decides", label="Feature Selection Tool")
transform = gr.Dropdown(TRANSFORM_CHOICES, value="🤖 Agent Decides", label="Data Transformation Tool")
ml_tool = gr.Dropdown(ML_CHOICES, value="🤖 Agent Decides", label="Machine Learning Tool")
with gr.Group(visible=initial_image) as image_group:
gr.Markdown("### 🖼️ Image Pipeline Controls")
image_task = gr.Dropdown(IMAGE_TASK_CHOICES, value="classification", label="Image Task")
image_backend = gr.Dropdown(IMAGE_BACKEND_CHOICES, value="Auto", label="Image Backend")
image_format = gr.Dropdown(IMAGE_FORMAT_CHOICES, value="auto", label="Dataset Format")
gr.Textbox(value="Managed existing-dataset source", label="Dataset Source", interactive=False)
image_note = gr.Markdown(initial_state["note"])
image_device = gr.Dropdown(IMAGE_DEVICE_CHOICES, value=AUTO_DEVICE, label="Image Device")
image_batch = gr.Slider(1, 128, value=64, step=1, label="Image Batch Size")
with gr.Group(visible=initial_state["classification"]) as classification_group:
image_backbone = gr.Dropdown(IMAGE_BACKBONE_CHOICES, value=AUTO_CHOICE, label="Image Backbone")
image_classifier = gr.Dropdown(IMAGE_CLASSIFIER_CHOICES, value=AUTO_CHOICE, label="Image Classifier")
image_size = gr.Dropdown(IMAGE_SIZE_CHOICES, value=AUTO_CHOICE, label="Image Size")
image_pretrained = gr.Checkbox(True, label="Use Pretrained CNN Weights")
with gr.Group(visible=initial_state["medical"]) as medical_group:
cancer_preset = gr.Dropdown(CANCER_PRESET_CHOICES, value="Auto - agent decides", label="Cancer Feature Preset")
with gr.Group(visible=initial_state["detection"]) as detection_group:
gr.Markdown("### Detection Controls\nCOCO annotations use backend defaults.")
with gr.Group(visible=initial_state["instance"]) as instance_group:
gr.Markdown("### Instance Segmentation Controls\nCOCO polygons/RLE are preserved.")
with gr.Group(visible=initial_state["semantic"]) as semantic_group:
gr.Markdown("### Semantic Segmentation Controls\nUses a managed image/mask manifest.")
with gr.Group(visible=initial_state["optional"]) as optional_group:
image_epochs = gr.Slider(1, 100, value=1, step=1, label="Backend Epochs")
image_imgsz = gr.Slider(32, 1024, value=64, step=32, label="Backend Image Size")
with gr.Group(visible=initial_state["benchmark"]) as benchmark_group:
benchmark_backends = gr.Textbox("", label="Benchmark Backends", placeholder="yolo,automm")
existing_route = gr.Markdown(
existing_route_preview(
default_dataset,
AUTO_CHOICE,
AUTO_DEVICE,
"classification",
"Auto",
"",
"Auto - agent decides",
)
)
run_button = gr.Button("🚀 Start Analysis", interactive=False)
dataset.change(
update_existing_dataset_controls,
dataset,
[target, split, test_size, tabular_group, image_group],
api_name=False,
)
for selector in (image_task, image_backend):
selector.change(
image_task_ui_updates,
[image_task, image_backend],
[classification_group, medical_group, detection_group, instance_group, semantic_group, benchmark_group, optional_group, image_note],
api_name=False,
)
existing_route_inputs = [
dataset,
image_backbone,
image_device,
image_task,
image_backend,
benchmark_backends,
cancer_preset,
]
for selector in (
dataset,
image_backbone,
image_device,
image_task,
image_backend,
benchmark_backends,
cancer_preset,
):
selector.change(
existing_route_preview,
existing_route_inputs,
existing_route,
api_name=False,
)
existing_outputs = _add_results_section()
with gr.TabItem("📤 Upload Your Data"):
with gr.Row():
with gr.Column():
upload_type = gr.Dropdown(["Tabular CSV", "Image Dataset"], value="Tabular CSV", label="Data Type")
train_upload = gr.File(label="📁 Training Data (CSV)", file_types=[".csv"])
test_upload = gr.File(label="📁 Test Data (CSV) - Optional", file_types=[".csv"])
upload_target = gr.Dropdown([], label="🎯 Target Column to Predict", allow_custom_value=True)
upload_split = gr.Checkbox(True, label="🔄 Split Training Data")
upload_test_size = gr.Slider(0.1, 0.5, value=0.2, step=0.05, label="📊 Test Size")
upload_preset = gr.Dropdown(QUALITY_PRESETS, value="medium_quality", label="Quality Preset")
upload_time = gr.Slider(60, 3600, value=300, step=60, label="Time Limit (seconds)")
upload_trials = gr.Dropdown(["1", "2", "3", "4"], value="1", label="Trials")
with gr.Group(visible=True) as upload_tabular_group:
upload_feature = gr.Dropdown(FEATURE_SELECTION_CHOICES, value="🤖 Agent Decides", label="Feature Selection Tool")
upload_transform = gr.Dropdown(TRANSFORM_CHOICES, value="🤖 Agent Decides", label="Data Transformation Tool")
upload_ml = gr.Dropdown(ML_CHOICES, value="🤖 Agent Decides", label="Machine Learning Tool")
with gr.Group(visible=False) as upload_image_group:
upload_image_backend = gr.Dropdown(["Existing classification pipeline", "Medical image agent"], value="Existing classification pipeline", label="Image Backend")
with gr.Group(visible=True) as upload_existing_group:
upload_backbone = gr.Dropdown(IMAGE_BACKBONE_CHOICES, value=AUTO_CHOICE, label="Image Backbone")
upload_classifier = gr.Dropdown(IMAGE_CLASSIFIER_CHOICES, value=AUTO_CHOICE, label="Image Classifier")
upload_size = gr.Dropdown(IMAGE_SIZE_CHOICES, value=AUTO_CHOICE, label="Image Size")
with gr.Group(visible=False) as upload_medical_group:
upload_cancer_preset = gr.Dropdown(CANCER_PRESET_CHOICES, value="Auto - agent decides", label="Cancer Feature Preset")
upload_device = gr.Dropdown(IMAGE_DEVICE_CHOICES, value=AUTO_DEVICE, label="Image Device")
upload_batch = gr.Slider(8, 128, value=64, step=8, label="Image Batch Size")
upload_pretrained = gr.Checkbox(True, label="Use Pretrained CNN Weights")
uploaded_route = gr.Markdown(
uploaded_route_preview(
"Tabular CSV",
AUTO_CHOICE,
AUTO_DEVICE,
"Existing classification pipeline",
"Auto - agent decides",
)
)
upload_run_button = gr.Button("🚀 Start Analysis on Uploaded Data", interactive=False)
upload_type.change(
update_uploaded_type_controls,
upload_type,
[train_upload, test_upload, upload_target, upload_split, upload_test_size, upload_tabular_group, upload_image_group],
api_name=False,
).then(update_uploaded_targets, [upload_type, train_upload], upload_target, api_name=False)
train_upload.change(update_uploaded_targets, [upload_type, train_upload], upload_target, api_name=False)
upload_image_backend.change(uploaded_image_backend_updates, upload_image_backend, [upload_existing_group, upload_medical_group], api_name=False)
uploaded_route_inputs = [
upload_type,
upload_backbone,
upload_device,
upload_image_backend,
upload_cancer_preset,
]
for selector in (
upload_type,
upload_backbone,
upload_device,
upload_image_backend,
upload_cancer_preset,
):
selector.change(
uploaded_route_preview,
uploaded_route_inputs,
uploaded_route,
api_name=False,
)
uploaded_outputs = _add_results_section()
with gr.TabItem("🔮 Use Trained Model") as replay_tab:
with gr.Row():
with gr.Column():
saved_models = gr.Dropdown([], label="Saved Cloud Model", interactive=False)
refresh_models = gr.Button("Refresh Saved Models", interactive=False)
model_upload = gr.File(label="Trained Model Package (.zip)", file_types=[".zip"])
model_info = gr.Markdown("Choose a saved model or upload a package.")
tabular_text = gr.Textbox("", visible=False)
with gr.Group(visible=False) as tabular_input_group:
tabular_input = gr.File(label="Single Tabular Input (CSV, one row)", file_types=[".csv"])
with gr.Group(visible=False) as image_input_group:
image_input = gr.File(
label="Single Image Input",
file_types=[".png", ".jpg", ".jpeg", ".bmp", ".tif", ".tiff"],
)
replay_device = gr.Dropdown(IMAGE_DEVICE_CHOICES, value=AUTO_DEVICE, label="Image Device", visible=False)
replay_batch = gr.Slider(1, 128, value=16, step=1, label="Image Batch Size", visible=False)
replay_manifest_state = gr.State({})
replay_route = gr.Markdown("Choose a model to inspect its compute route.")
predict_button = gr.Button("Predict", interactive=False)
with gr.Column():
predict_status = gr.Markdown("Status: _idle_")
predict_summary = gr.Markdown("")
predict_frame = gr.DataFrame(label="Prediction", interactive=False)
with gr.TabItem("👥 Account Approvals", visible=False) as admin_tab:
gr.Markdown(
"## Pending account requests\n"
"Approve a request to let that user log in, or deny it to let "
"the user choose whether to re-submit or remove their information."
)
pending_requests_df = gr.Dataframe(
headers=["Username", "Requested At (UTC)"],
datatype=["str", "str"],
value=[],
interactive=False,
label="Pending Requests",
)
pending_username = gr.Dropdown(
choices=[],
value=None,
label="Account Request",
)
with gr.Row():
approve_account_button = gr.Button("Approve", variant="primary")
deny_account_button = gr.Button("Deny", variant="stop")
refresh_accounts_button = gr.Button("Refresh")
admin_status = gr.Markdown("")
auth_outputs = [
auth_status,
session_token,
auth_panel,
account_row,
main_tabs,
account_status,
saved_models,
password_status,
run_button,
upload_run_button,
refresh_models,
predict_button,
model_info,
login_username,
login_password,
tabular_input_group,
image_input_group,
replay_device,
replay_batch,
replay_manifest_state,
replay_route,
predict_status,
predict_summary,
predict_frame,
denied_request_group,
denied_username_state,
denied_password,
denied_request_choice,
admin_tab,
pending_requests_df,
pending_username,
admin_status,
]
demo.load(
restore_auth_ui,
session_token,
auth_outputs,
api_name=False,
)
login_button.click(handle_login_ui, [login_username, login_password], auth_outputs, api_name=False)
signup_button.click(
handle_signup_ui,
[signup_username, signup_password, signup_confirm_password],
auth_outputs,
api_name=False,
)
denied_request_button.click(
handle_denied_request_ui,
[
denied_username_state,
denied_password,
denied_request_choice,
],
[
auth_status,
denied_request_group,
denied_username_state,
denied_password,
denied_request_choice,
],
api_name=False,
)
logout_button.click(handle_logout_ui, None, auth_outputs, api_name=False)
password_button.click(handle_password_change_ui, [password_username, old_password, new_password, confirm_password], password_status, api_name=False)
refresh_accounts_button.click(
refresh_pending_account_requests,
session_token,
[pending_requests_df, pending_username, admin_status],
api_name=False,
)
approve_account_button.click(
handle_admin_account_approval,
[session_token, pending_username],
[pending_requests_df, pending_username, admin_status],
api_name=False,
)
deny_account_button.click(
handle_admin_account_denial,
[session_token, pending_username],
[pending_requests_df, pending_username, admin_status],
api_name=False,
)
inspect_outputs = [model_info, tabular_input_group, image_input_group, replay_device, replay_batch, predict_button, replay_manifest_state, replay_route]
model_upload.change(update_single_input_controls, [model_upload, saved_models, session_token], inspect_outputs, api_name=False)
saved_models.change(update_single_input_controls, [model_upload, saved_models, session_token], inspect_outputs, api_name=False)
refresh_models.click(refresh_saved_model_choices, [saved_models, session_token], saved_models, api_name=False)
replay_tab.select(refresh_saved_model_choices, [saved_models, session_token], saved_models, api_name=False)
replay_device.change(
replay_route_preview,
[replay_manifest_state, replay_device],
replay_route,
api_name=False,
)
existing_inputs = [dataset, target, split, test_size, preset, time_limit, trials, feature, transform, ml_tool, image_backbone, image_classifier, image_size, image_device, image_batch, image_pretrained, image_task, image_backend, image_format, image_epochs, image_imgsz, benchmark_backends, cancer_preset, session_token]
run_button.click(existing_ui_handler, existing_inputs, list(existing_outputs), api_name=False)
uploaded_inputs = [upload_type, upload_target, upload_split, upload_test_size, upload_preset, upload_time, upload_trials, upload_feature, upload_transform, upload_ml, upload_backbone, upload_classifier, upload_size, upload_device, upload_batch, upload_pretrained, upload_image_backend, upload_cancer_preset, train_upload, test_upload, session_token]
upload_run_button.click(uploaded_ui_handler, uploaded_inputs, list(uploaded_outputs), api_name=False)
predict_button.click(predict_remote, [model_upload, tabular_text, tabular_input, image_input, replay_device, replay_batch, saved_models, session_token], [predict_status, predict_summary, predict_frame], api_name=False)
return demo
def validate_gateway_security_config() -> None:
"""Fail closed when the public deployment weakens its security boundary."""
routing_mode_from_env()
if auth_storage.env_truthy("AUTOML_AUTH_DISABLED", False):
raise RuntimeError("Public gateway does not permit AUTOML_AUTH_DISABLED=1.")
if not auth_storage.app_auth_required():
raise RuntimeError("Public gateway requires AUTOML_REQUIRE_AUTH=1.")
if not auth_storage.encryption_enabled():
raise RuntimeError(
"Public gateway requires AUTOML_MODEL_STORAGE_KEY for encrypted model storage."
)
if __name__ == "__main__":
validate_gateway_security_config()
create_interface().queue(default_concurrency_limit=4).launch(
server_name="0.0.0.0", server_port=7860, show_error=False
)
__all__ = [
"GATEWAY_CATALOG_PATH",
"create_interface",
"run_existing_remote",
"run_uploaded_remote",
"predict_remote",
"render_training_result",
"prune_gateway_egress",
"routing_mode_from_env",
"format_route_preview",
"existing_route_preview",
"uploaded_route_preview",
"replay_route_preview",
"validate_gateway_security_config",
]