Skip to content

Fix live metrics change view and splits plots#469

Open
Felipedino wants to merge 1 commit intodevelopfrom
fix/live-metrics-plot
Open

Fix live metrics change view and splits plots#469
Felipedino wants to merge 1 commit intodevelopfrom
fix/live-metrics-plot

Conversation

@Felipedino
Copy link
Collaborator

This pull request optimizes the LiveMetricsChart component by improving how metric data is filtered and prepared for charting, and by making the WebSocket connection more configurable. The main changes involve using useMemo for performance, and updating the WebSocket URL construction to support custom API endpoints.

Performance improvements:

  • Refactored the filtering of metrics and the computation of chart data to use useMemo, ensuring these operations are only recalculated when their dependencies change, which improves rendering efficiency. [1] [2] [3]

Configuration and maintainability:

  • Changed the WebSocket URL construction to use the REACT_APP_API_URL environment variable, allowing the frontend to connect to a custom backend endpoint instead of always using the current window location.
image

Copilot AI review requested due to automatic review settings March 4, 2026 22:28
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 updates the LiveMetricsChart React component to reduce unnecessary recalculation during renders and to allow the metrics WebSocket to connect to a configurable backend base URL (via REACT_APP_API_URL).

Changes:

  • Memoizes metric filtering and chart data preparation with useMemo to avoid recomputing on unrelated renders.
  • Refactors WebSocket URL construction to derive the WS base from REACT_APP_API_URL instead of window.location.

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

Comment on lines +75 to 78
const apiUrl = process.env.REACT_APP_API_URL || "";
const wsBase = apiUrl.replace(/^http/, "ws");
const ws = new WebSocket(`${wsBase}/v1/metrics/ws/${run.id}`);

Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

apiUrl falls back to an empty string, which makes new WebSocket(${wsBase}/v1/...) throw at runtime because the URL resolves to an http(s) URL (or just a path) instead of a ws:///wss:// URL. This means the component can break entirely when REACT_APP_API_URL is unset or set to a relative value.

Consider building the WS URL from the same base used by the REST client (e.g. api.defaults.baseURL) and, if no base is configured, derive it from window.location.origin (including the /api prefix used elsewhere) and explicitly convert the scheme to ws/wss using new URL(...) to avoid malformed URLs and double slashes.

Suggested change
const apiUrl = process.env.REACT_APP_API_URL || "";
const wsBase = apiUrl.replace(/^http/, "ws");
const ws = new WebSocket(`${wsBase}/v1/metrics/ws/${run.id}`);
const apiUrl =
process.env.REACT_APP_API_URL || `${window.location.origin}/api`;
let wsUrl;
try {
wsUrl = new URL(`/v1/metrics/ws/${run.id}`, apiUrl);
} catch (e) {
console.error("Invalid WebSocket base URL:", apiUrl, e);
return;
}
if (wsUrl.protocol === "http:") {
wsUrl.protocol = "ws:";
} else if (wsUrl.protocol === "https:") {
wsUrl.protocol = "wss:";
}
const ws = new WebSocket(wsUrl.toString());

Copilot uses AI. Check for mistakes.
Comment on lines +171 to +174
const m = data[split]?.[level] ?? {};
const a = availableMetrics[split] ?? [];
return Object.fromEntries(
Object.entries(m).filter(([name]) => a.includes(name)),
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

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

Inside the useMemo for filteredMetrics, the temporary variables m and a are quite cryptic compared to the rest of the file’s naming style. Using clearer names (e.g. metrics / allowedMetrics) would make the intent easier to follow when revisiting this logic.

Suggested change
const m = data[split]?.[level] ?? {};
const a = availableMetrics[split] ?? [];
return Object.fromEntries(
Object.entries(m).filter(([name]) => a.includes(name)),
const metrics = data[split]?.[level] ?? {};
const allowedMetrics = availableMetrics[split] ?? [];
return Object.fromEntries(
Object.entries(metrics).filter(([name]) => allowedMetrics.includes(name)),

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants