Skip to content

feat live metrics and hyperparameter plots to models#432

Merged
cristian-tamblay merged 19 commits intodevelopfrom
feat/models-live-metrics
Feb 26, 2026
Merged

feat live metrics and hyperparameter plots to models#432
cristian-tamblay merged 19 commits intodevelopfrom
feat/models-live-metrics

Conversation

@Felipedino
Copy link
Collaborator

This pull request introduces several improvements and new features to the model management and prediction flow in the frontend. The most significant changes are the addition of dialogs for editing model runs and confirming parameter updates, enhancements to user experience by persisting UI state, and refactoring the prediction creation wizard for clarity and flexibility.

Dialogs and Model Run Editing:

Added a new EditRunDialog component that provides a step-by-step wizard for editing existing model run parameters, including model configuration and optimizer setup. It validates input, prevents duplicate names, and supports dynamic optimizer configuration.
Added an EditConfirmationDialog component to warn users about the consequences of updating model run parameters, including data deletion and retraining, with a clear summary of affected data.
UI State Persistence:

Updated both ExplainersCard and PredictionCard components to persist their expanded/collapsed state in localStorage, ensuring consistent UI experience across page reloads. [1] [2]
Prediction Creation Flow Refactor:

Refactored the PredictionCreationDialog wizard to remove the mode selection step and allow the default input mode to be set via a prop, simplifying the user flow and making it more flexible for different use cases. [1] [2] [3] [4] [5] [6] [7]
UI/UX Improvements:

Improved layout and typography in ExplainersCard for better readability and alignment, especially when displaying explainer names and captions.
Miscellaneous:

Added import statements for new UI components and utilities in relevant files to support the new features. [1] [2] [3]

- Implemented EditRunDialog to allow users to edit existing model run parameters, including model name, parameters, optimizer settings, and goal metric.
- Integrated validation for model name uniqueness and required fields.
- Added functionality to handle optimizer selection and parameter configuration.
- Updated PredictionCard to persist expanded state in local storage.
- Modified PredictionCreationDialog to remove mode selection and allow default mode configuration.
- Enhanced RunCard with improved layout and action buttons for training and deleting runs.
- Refactored RunOperations to include tabs for Explainability and Predictions, with separate sections for global and local explainers.
- Updated ModelsContent to manage edit run functionality and confirmation dialog for run updates.
Copilot AI review requested due to automatic review settings January 27, 2026 02:01
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the models “run” experience to consolidate run results (metrics, explainers, predictions, hyperparameters) into a tabbed view on each run card, adds live metrics and hyperparameter plots directly in the models page, and introduces editing capabilities for run parameters and optimizer configuration (partially wired). It also adjusts the models page training flow to account for existing operations and removes some branding elements from the three-section layout header/footer.

Changes:

  • Replace the old RunOperations accordion with a new RunResults tabbed panel on each RunCard, including live metrics, explainers management, prediction creation (dataset/manual), and hyperparameter plots, plus persistence of expanded/selected tab state.
  • Extend the models page training flow to compute and pass per-run operation counts so retraining can warn about and clean up explainers/predictions, and add inline run-editing UI in RunCard (with new dialogs for future edit-confirm flows).
  • Improve UX persistence and layout by storing explainer/prediction card expansion in localStorage, simplifying the prediction creation wizard to be mode-driven via a defaultMode prop, and removing logo/title elements from Footer and BarHeader.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
DashAI/front/src/pages/results/components/ResultsDetailsLayout.jsx Removes unused Parameters/Metrics tabs in favor of hyperparameter plots and info, matching the updated tabs configuration.
DashAI/front/src/pages/models/ModelsContent.jsx Extends handleTrainRun to accept an optional operations count, integrates getRunOperationsCount/deleteRunOperations, wires retrain confirmation, and passes onRefreshRuns/updated onTrain into SessionVisualization.
DashAI/front/src/components/threeSectionLayout/Footer.jsx Simplifies the footer to an empty flex container, removing the logo and divider while leaving the structural box in place.
DashAI/front/src/components/threeSectionLayout/BarHeader.jsx Simplifies the header bar to an empty container, removing the “Dash” title text and associated styling.
DashAI/front/src/components/models/SessionVisualization.jsx Refactors run cards to use the new RunResults component instead of RunOperations, and passes onRefreshRuns down so run operations can refresh the runs list.
DashAI/front/src/components/models/RunResults.jsx New component that consolidates per-run live metrics, explainers, predictions, and hyperparameter plots into tabs, with persisted visibility/tab state and dialogs to create explainers and predictions.
DashAI/front/src/components/models/RunOperations.jsx Legacy accordion-based operations view removed in favor of the new tabbed RunResults layout.
DashAI/front/src/components/models/RunCard.jsx Adds inline edit mode for run parameters/optimizer, integrates operations count and retrain warnings into the train button, swaps in RunResults for per-run outputs, and introduces various UI/validation updates.
DashAI/front/src/components/models/PredictionCreationDialog.jsx Refactors the prediction wizard to support a defaultMode prop and adjusts step logic, intending a 2-step (configure, confirm) flow instead of the prior 3-step mode-selection flow.
DashAI/front/src/components/models/PredictionCard.jsx Persists each prediction card’s expanded/collapsed state to localStorage so expansion survives reloads.
DashAI/front/src/components/models/LiveMetricsChart.jsx New live metrics chart for runs on the models page, mirroring the results page implementation with WebSocket updates and per-split/level metric selection.
DashAI/front/src/components/models/HyperparameterPlots.jsx New hyperparameter plots component that fetches and renders historical, slice, contour, and importance plots based on how many parameters are optimizable for the run.
DashAI/front/src/components/models/EditRunDialog.jsx New (currently unused) dialog that walks through editing run parameters and optimizer configuration via forms, preparing data for a confirmation flow.
DashAI/front/src/components/models/EditConfirmationDialog.jsx New confirmation dialog component describing the consequences of updating run parameters (metrics reset, retraining, operations possibly invalid), intended to be used before applying edits.
DashAI/front/src/components/explainers/ExplanainersCard.jsx Persists each explainer card’s expanded/collapsed state to localStorage and improves layout by combining explainer display name and instance name into a single, wrapped typography block.
Comments suppressed due to low confidence (1)

DashAI/front/src/components/models/PredictionCreationDialog.jsx:265

  • PredictionCreationDialog currently defines the steps array twice (once at the module level and again inside the component) and still includes a "select mode" step in the inner steps even though ModeSelector has been removed from the imports. On top of that, renderStepContent now has two case 1 branches (one for configuring input and one for confirmation), which is invalid and will either fail linting/compilation or make the confirmation step unreachable. Please consolidate to a single steps definition matching the new 2-step flow (configure input, confirm), remove the obsolete select-mode logic and ModeSelector usage, and ensure the switch cases align (e.g. case 0 for configuration, case 1 for confirmation).
  const steps = [
    t("prediction:label.selectMode"),
    t("prediction:label.configureInput"),
    t("prediction:label.confirm"),
  ];

  useEffect(() => {
    if (!open) {
      setActiveStep(0);
      setPredictionMode(defaultMode);
      setDatasets([]);
      setSelectedDataset(null);
      setManualRows([]);
      setIsLoading(false);
    }
  }, [open, defaultMode]);

  useEffect(() => {
    const fetchData = async () => {
      if (!run || !open) return;

      setLoadingExperiment(true);

      try {
        const availableDatasets = await filterDatasets({ run_id: run.id });
        const availableDatasetsWithInfo = await Promise.all(
          availableDatasets.map(async (dataset) => {
            const datasetInfo = await getDatasetInfo(dataset.id);
            return { ...dataset, ...datasetInfo };
          }),
        );
        setDatasets(availableDatasetsWithInfo);

        if (run.model_session_id || session?.id) {
          const sessionData = await getModelSessionById(
            run.model_session_id || session.id,
          );
          setModelSession(sessionData);

          const datasetTypes = await getDatasetTypes(sessionData.dataset_id);
          setTypes(datasetTypes);

          const datasetSample = await getDatasetSample(sessionData.dataset_id);
          setSample(datasetSample);
        }
      } catch (error) {
        console.error("Error fetching data:", error);
        enqueueSnackbar(t("prediction:error.loadingPredictionData"), {
          variant: "error",
        });
      } finally {
        setLoadingExperiment(false);
      }
    };

    fetchData();
  }, [run, session, open, enqueueSnackbar]);

  const canProceed = () => {
    if (activeStep === 0) return true;
    if (activeStep === 1) {
      if (predictionMode === "dataset") {
        return selectedDataset !== null;
      }
      return manualRows && manualRows.length > 0;
    }
    return true;
  };

  const handleNext = () => {
    if (activeStep < steps.length - 1) {
      setActiveStep((prev) => prev + 1);
    } else {
      handleSubmit();
    }
  };

  const handleBack = () => {
    setActiveStep((prev) => prev - 1);
  };

  const handleSubmit = async () => {
    setIsLoading(true);

    try {
      const prediction = await createPrediction(
        run.id,
        predictionMode === "dataset" ? selectedDataset.id : null,
      );

      const jobResponse = await enqueuePredictionJob(
        prediction.id,
        predictionMode === "manual" ? manualRows : null,
      );

      if (!jobResponse || !jobResponse.id) {
        throw new Error("Failed to enqueue prediction job");
      }

      enqueueSnackbar(t("prediction:message.predictionJobSubmitted"), {
        variant: "success",
      });

      startJobPolling(
        jobResponse.id,
        async () => {
          const updatedPredictions = await getPredictions(run.id);
          const updatedPrediction = updatedPredictions.find(
            (p) => p.id === prediction.id,
          );

          enqueueSnackbar(t("prediction:message.predictionCompleted"), {
            variant: "success",
          });

          if (onPredictionCreated) {
            onPredictionCreated(updatedPrediction || prediction);
          }
        },
        (result) => {
          // On failure
          console.error("Prediction job failed:", result);
          enqueueSnackbar(
            t("prediction:error.predictionFailed", {
              error: result.error || t("common:unknownError"),
            }),
            { variant: "error" },
          );

          if (onPredictionCreated) {
            onPredictionCreated();
          }
        },
      );

      if (onPredictionCreated) {
        onPredictionCreated(prediction);
      }

      onClose();
    } catch (error) {
      console.error("Error creating prediction:", error);
      enqueueSnackbar(t("prediction:error.creatingPrediction"), {
        variant: "error",
      });
    } finally {
      setIsLoading(false);
    }
  };

  const renderStepContent = (step) => {
    switch (step) {
      case 0:
        return (
          <Box sx={{ py: 2 }}>
            <Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}>
              {t("prediction:label.selectPredictionMode")}
            </Typography>
            <ModeSelector
              predictionMode={predictionMode}
              setPredictionMode={setPredictionMode}
            />
          </Box>
        );

      case 1:
        return (
          <Box sx={{ py: 2 }}>
            {predictionMode === "dataset" ? (
              <>
                <Typography
                  variant="body2"
                  color="text.secondary"
                  sx={{ mb: 2 }}
                >
                  {t("prediction:label.selectDataset")}
                </Typography>
                <DatasetSelector
                  experiment={modelSession}
                  datasets={datasets}
                  selectedDataset={selectedDataset}
                  setSelectedDataset={setSelectedDataset}
                />
              </>
            ) : (
              <>
                <ManualInput
                  experiment={modelSession}
                  loading={loadingExperiment}
                  types={types}
                  sample={sample}
                  manualInputData={manualRows}
                  setManualInputData={setManualRows}
                />
              </>
            )}
          </Box>
        );

      case 1:
        return (
          <Box sx={{ py: 2 }}>
            <Typography variant="h6" gutterBottom>
              {t("prediction:label.confirmPrediction")}
            </Typography>

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Felipedino
Copy link
Collaborator Author

aaaapq no pasa los precommit

@cristian-tamblay cristian-tamblay added the conflict This PR has merge conflicts label Feb 24, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 30 out of 30 changed files in this pull request and generated 16 comments.

Comments suppressed due to low confidence (1)

DashAI/front/src/api/run.ts:75

  • updateRunParameters changed its positional arguments by inserting name as the 2nd parameter, which will silently break existing call sites still using the old signature (e.g., callers that pass parameters as arg #2 will now send it as run_name). Consider switching this API to a single options object (or add a backward-compatible argument parser/overload) and update existing callers accordingly so PATCH payload fields map correctly.
export const updateRunParameters = async (
  runId: string,
  name?: string,
  parameters?: object,
  optimizer?: string,
  optimizer_parameters?: object,
  goal_metric?: string,
): Promise<IRun> => {
  const response = await api.patch<IRun>(`/v1/run/${runId}`, {
    run_name: name,
    parameters,
    optimizer,
    optimizer_parameters,
    goal_metric,
  });

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@cristian-tamblay cristian-tamblay merged commit 28e0a64 into develop Feb 26, 2026
19 checks passed
@cristian-tamblay cristian-tamblay deleted the feat/models-live-metrics branch February 26, 2026 20:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflict This PR has merge conflicts

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants