pypi visdom 0.3.1
Release 0.3.1

2 hours ago

This release covers the 229 pull requests merged into dev since v0.3.0 (September 2026). It adds a hyper-parameter dashboard and an experiment search layer, moves the server and a new client onto asyncio, adds autologging for Keras, PyTorch Lightning, XGBoost and scikit-learn, an Optuna dashboard, experiment tagging, and the first pieces of multi-workspace support so a client can talk to a hosted server such as visdom.dev.

Before You Upgrade

Most of this release is additive. Four changes can affect an existing setup.

1. Environments are now saved on a timer

The server writes changed environments to disk every 30 seconds by default, and writes a busy one early after 50 updates. Before, the only automatic save was at exit. -save_interval and -save_threshold control this. What to do: nothing, unless you relied on environments only being written at exit. Set both to 0 to get the old behaviour. (#1704)

2. The send argument is gone from the client

The legacy send=False request-building path was removed. Any call that passes send will fail. (#1666)

3. html_table argument order changed

table and html_table now take the same arguments in the same order. A positional call written as html_table(headers, data) is now read as html_table(data, headers). What to do: pass headers= and data= by name. (#1698, #1676, #1677)

4. Experiment search and compare are bounded

/experiments/search caps a page at 1000 results and offset + limit at 10000. /experiments/compare accepts at most 1000 environment ids. A bad offset or env_ids now returns 400 instead of a smaller answer. The HParams pane batches its requests, so large panes still work. What to do: page through large result sets. (#1660)

Also worth knowing: the plotly camera icon was removed from the plot toolbar, because export lives in the top bar (#1662).

Highlights

  • Hyper-parameter dashboard. One pane to compare runs by hyper-parameter and metric, with a sortable run table, scatter matrix, parallel coordinates, a Compare view, a Metrics view, live updates and export, backed by an injection-safe experiment query language and search and compare endpoints.
  • Asyncio server and AsyncVisdom client. The server runs under asyncio and does disk work on a storage worker instead of the event loop. A new asyncio client lets plotting stop blocking the caller.
  • Autologging. New VisdomKerasLogger, VisdomLightningLogger and VisdomXGBLogger, scikit-learn support for neural-net training history, and opt-in experiment tracking from the loggers.
  • Optuna dashboard. An Optuna callback plus study, intermediate-value, Pareto-front and contour views.
  • Experiment tagging across the server, the Python client and the UI.
  • Works with visdom.dev. Visdom() and AsyncVisdom() accept api_key and workspace (#1993), and the server can keep each workspace's environments apart.
  • Faster server and client. Thirteen performance changes cut repeated work when building, encoding and broadcasting plots, and when searching experiments.
  • Pane exports to PDF, LaTeX, and images with DPI and EXIF metadata, and a dedicated table API.
  • A Dockerfile for the server (#1994).

Features

Hyper-parameter dashboard and experiment search

The new HParams pane compares runs side by side. Under it sits a query parser that turns a filter string into a safe search, a vis.search_experiments client method, field-by-field vis.compare_experiments, and a vis.hparams() call that opens the pane. The pane has a sortable, filterable run table, a scatter matrix, parallel coordinates, a Compare view for selected runs, a shared filter sidebar, export, and live updates while a run is still logging. Panes now persist when created and have an update endpoint.

Related Pull Requests: #1498, #1602, #1604, #1605, #1609, #1613, #1616, #1620, #1635, #1641, #1645, #1647, #1656, #1660, #1663

Asyncio server and client

The server now runs under asyncio with a graceful stop. Environment reads and writes, the hot window and event routes, socket commands, the HParams routes and the login page no longer touch the disk on the event loop. AsyncVisdom is a new client that does not block the caller's loop, supports websocket and polling for incoming messages, skips the window-exists check, and sends an append as a single request. Type stubs and README and website docs are included.

Related Pull Requests: #1703, #1711, #1713, #1717, #1721, #1780, #1784, #1794, #1800, #1808, #1816, #1823, #1835, #1839, #1845

Autologging and experiment tracking

  • Add VisdomXGBLogger with autolog for xgboost training (#1621)
  • Added support for nn module training history in sklearn autologger (#1642)
  • Add VisdomKerasLogger to plot tensorflow Keras metrics (#1708)
  • Add opt-in experiment tracking to VisdomLogger via params (#1719)
  • Add opt-in experiment tracking to VisdomKerasLogger (#1759)
  • Add VisdomLightningLogger to plot pytorch lightning metric (#1777)
  • Plot and track from rank zero only in VisdomLogger under distributed training (#1962)
  • Do not cache a failed window id in the keras and pytorch loggers (#1820)
  • Add VisdomSystemMonitor to plot a script's CPU and RAM usage while it runs (#2049)
  • Restore an experiment from its JSON record (#2038)

Unit tests and README notes for each logger: #1722, #1734, #1737, #1772, #1809, #1743

Contributed by @Jayantparashar10 and @rajnisht7.

Optuna

  • Add Optuna experiment callback (#1746)
  • Add Optuna study dashboard (#1753)
  • Visualize Optuna intermediate values and pruned trials (#1757)
  • Add multi-objective Pareto front visualization for Optuna studies (#1767)
  • Keep short Optuna trials visible in the Optuna timeline (#1776)
  • Fix Optuna dashboard trial selection across callbacks (#1778)
  • Add Optuna contour visualization (#1782)

Contributed by @tonypzy.

Experiment tagging

  • Add environment tag store domain (#1709)
  • Add experiment tagging backend API and transport (#1712)
  • Add experiment tagging Python SDK (#1716)
  • Add experiment tagging frontend UI and filtering (#1728)
  • Add environment filtering and improve deletion behavior (#1791)

Contributed by @tonypzy.

Multi-workspace support and visdom.dev

Visdom() and AsyncVisdom() take an optional api_key and workspace. When a key is given, every request, the socket handshake and the asyncio client carry it as X-API-KEY with the workspace as X-Visdom-Workspace. Without a key nothing changes. On the server side, each workspace keeps its own environments, layouts and files, requests and sockets are routed to the workspace they belong to, the top bar shows the workspace, and internal endpoints for activity and metrics are served on their own TLS port to signed requests only. Endpoints to remove a workspace, or its environments past a retention window, and to close a workspace's sockets are included. A plain server without these options behaves as before.

  • Send an API key and workspace from both clients (#1993)
  • Add a Dockerfile for the server (#1994)
  • Keep each workspace's environments and files apart (#2005)
  • Route each request and socket to the workspace it belongs to (#2006)
  • Warn when the server refuses a request (#2013)
  • Report activity, work done and active minutes per workspace (#2014)
  • Show the workspace in the top bar and link back to the console (#2015)
  • Serve the internal endpoints on their own port, over TLS, to signed requests only (#2016)
  • Close a workspace's sockets when it is withdrawn or moves to another server (#2017)
  • Add an endpoint that removes a workspace's environments past a retention window (#2018)
  • Add an endpoint that removes a whole workspace (#2019)
  • Keep sending a plot when a browser tab closes mid-broadcast (#2054)

Contributed by @vedansh-5.

Performance

  • Add a benchmark harness for the server and client hot paths (#1855)
  • Drop the stringify length comparison from the update broadcast (#1875)
  • Make NanSafeEncoder sanitize once per encode instead of twice (#1885)
  • Build the append patch directly instead of diffing a deepcopy (#1888)
  • Stop walking every plotted point to find a layout title (#1955)
  • Encode each broadcast once instead of once per recipient (#1961)
  • Stop importing torch and scikit-learn just to import visdom (#1970)
  • Read and append experiment metrics without rebuilding the series (#1985)
  • Cache environment metadata so a search stops re-parsing every env file (#2010)
  • Stop validating and grouping plotted points one at a time (#2022)
  • Copy environment data as JSON instead of element by element (#2042)
  • Stop widening image batches to float64 and let callers set the PNG level (#2048)
  • Redraw a plot pane only when its figure changed (#2056)

Contributed by @Manik-Khajuria-5.

Export, tables and plots

  • Remove plotly camera icon(export PNG) (#1662)
  • Add pdf export for panes (#1665)
  • Embed DPI metadata in the images (#1674)
  • Embed EXIF resolution tags in JPEG exports (#1690)
  • Export pane latex (#1696)
  • Add multiple export option in ImagePane (#1710)
  • Add latex export in table (#1727)
  • Increase the threshold and diff for image_save_jpeg (#1887)
  • Failing latex plot ci test (#1726)

Table API:

  • Rename current table api to html_table (#1676)
  • Add dedicated table API (#1677)
  • Add test, demo, update readme and export(html) for table api (#1686)
  • Unify table and html_table and add readme (#1698)
  • Add enable/disable edit option in TablePane (#1701)
  • Accept 1-D numpy rows in table() and html_table() (#1849)

New plot options: opts.aspectmode for 3D axis scaling (#1786), an annotation marker (#1670), and the annotate pencil moved into the plotly toolbar (#1700).

Contributed by @rajnisht7 and @thisisanubhav.

Interface

  • Upgrade React to 19 and migrate to createRoot API (#1536)
  • Bump plotly from 2.11.1 to 3.7.0 (#1702)
  • Remove bootstrap and replace bootstrap icons with lucid-react (#1720)
  • Remove jquery from login.html (#1723)
  • Remove reduntant CDN links (#1725)
  • Memoize unmemoized pane components to stop unwanted re-renders (#1741)
  • Pane resize jitters (removing unnecessary re-renders during drag) (#1760)
  • Pane relayout order (#1678)
  • Add a shared color palette and restyle the pane frame (#1833)
  • Restyle top bar into a zoned layout with unified controls (#1857)
  • Turn the environment delete selector into a dropdown (#1863)
  • Preserve selections across filter changes in EnvModal Select All (#1826)
  • Plot annotation marker (#1670)
  • Move annotate pencil button into plotly modebar (#1700)
  • Measure grid width on mount and repack panes on width change (#2020)
  • Add favicon in static html files (#1769)
  • Add visdom header and remove un-required text (#1664)

Contributed by @rajnisht7, @Jayantparashar10, @Manik-Khajuria-5, @tonypzy, @Pcmhacker-piro and @norbusan.

Fixes

Plots and data handling

  • Fix matplot pt-unit stripping inflating whole-number dims 100x (#1655)
  • Use plotlyRef instead of contentID string for Plotly.react target (#1714)
  • Pr_curve computes wrong AUC when precision/recall have tied recall values (#1744)
  • Pr_curve baseline always showed 1.0 instead of true class prevalence (#1765)
  • Tickstep axis opts values (#1685)
  • Video channel key error (#1697)
  • Crash when opts contain a numpy scalar (#1742)
  • Histogram crash in range nan/inf (#1750)
  • Fix unconditional np.squeeze() crashing bar/histogram/sunburst on size-1 inputs (#1788)
  • Fix unconditional np.squeeze() crashing pie/boxplot/stem/_surface/histogram2d on size-1 inputs (#1821)
  • Guard np.squeeze on Y in parallel_coordinates for size-1 inputs (#1854)
  • Update_window_opts crashes on non-plot panes (#1856)
  • Image and scatter silently reusing the caller opts dict (#1876)
  • Decode SVG files instead of stringifying bytes (#1870)
  • Clip video pixels before converting to bytes (#1902)
  • Handle empty input with automatic histogram bins (#1904)
  • Preserve labelled bar matrix axes (#1911)
  • Bound tracking filename slugs by UTF-8 bytes (#1868)
  • Fix vis.sunburst: root,shape,value validation, opts naming, and readme (#1729)
  • Add opts.aspectmode to control 3D plot axis scaling (#1786)

Contributed by @Jayantparashar10, @YashAnand69, @piyush182004, @munazzaghazali7-hash, @Maya-Mohamed, @rishi919-rgb and @rajnisht7.

Server, handlers and security

  • Remove the separate persistence flow from update_comment in the socket handler (#1693)
  • Fix & test : polling connect, constant-time login, readonly HTTP writes, and error-page disclosure (#1679)
  • HTTP window and environment lifecycle, and fix two request-handling bugs (#1669)
  • Cover the /update path, and fix the 500s and unbounded plot history it exposed (#1671)
  • Cover the socket handlers and fix the five crashes that surfaced (#1673)
  • Test hygiene: track stray suites, cover shared utils, atomic env writes (#1672)
  • Env with trailing whitespace treated as two different env (#1733)
  • List_envs() dropping envs named hash_<64-hex> (#1735)
  • Bump pane version on every update so patches are not discarded (#1775)
  • Bump the pane version for every type, not just plots (#1802)
  • Guard contributing plot data in compare_envs (#1796)
  • Compare wide integers instead of 500ing /experiments/search (#1797)
  • Keep numbers ahead of other types when ranking experiments descending (#1818)
  • Guard CloseHandler and DataHandler against missing environments (#1827)
  • Validate prev_eid and eid in ForkEnvHandler (#1841)
  • Validate request payload in SaveHandler to prevent HTTP 500 (#1852)
  • Fix broad exception swallowing in BaseHandler (#1860)
  • Validate sid and request body in CompareHandler (#1878)
  • Handle uninitialized session in CompareHandler and compare view (#1900)
  • Remove duplicate hparams paths from OpenAPI schema (#1866)
  • Introduce ServerState to decouple handlers from Application (#1384)
  • Add experiment tracking (saved in json) (#1752)
  • Confusing crash when comparing runs with a bad env_ids list (#1758)
  • Save changed environments automatically instead of only at exit (#1704)
  • Remove legacy send mode and stale index.html code (#1666)

Contributed by @rajnisht7, @Manik-Khajuria-5, @rishi919-rgb, @adity1raut, @Shlok1729, @tonypzy, @tarone-saloni, @thisisanubhav, @piyush182004, @ArnavBallinCode, @YashAnand69 and @vedansh-5.

Testing, CI and packaging

A pytest suite now covers the server handlers, sockets, comparison, the CLI, client payloads and content panes, with coverage reported in CI. End-to-end tests moved to Playwright and the last Cypress test was removed.

  • Add shared test fixtures and split py/tests into unit and integration (#1668)
  • Cover environment comparison, environment transfer and the server CLI with tests (#1682)
  • Cover the client's metric and layout helpers, and apply a lone tick step (#1683)
  • Cover client plot and media payloads, fix images() tile padding (#1687)
  • Cover the client content panes, mark the suite, and report coverage in CI (#1692)
  • Convert the last unittest classes onto pytest fixtures (#1694)
  • Migrate Cypress image.js E2E tests to Playwright (#1561)
  • Switch E2E tests to Playwright (#1691)
  • Remove retired Cypress test (#1705)
  • Implement condition based wait in playwright(basic and modal) (#1730)
  • Extend playwright test for UploadDashboard.spec.js (#1732)
  • Run newly added pytests in CI (#1658)
  • Builds the js bundle for each PR (#1822)
  • Skip the keras and lightning tests when they are not installed (#1990)
  • Fix flake8 F824 unused global declaration (#1873)

CI and packaging:

  • Add workflow to check quality of PR (#1286)
  • Modify permission for pr-quality workflow (#1749)
  • Skip pr quality workflow for dependabot PRs (#1754)
  • Extend pr quality workflow check to files (#1755)
  • Ask for a screencast on pull requests that need one (#1943)
  • Add code ql workflow (#1763)
  • Refractor .gitignore (#1756)
  • Allow the PyPI workflow to publish an existing tag (#1830)
  • Fix the PyPI packaging step (#1832)
  • Update PyPI author and project links (#1838)

Also merged without a pull request number: the CodeQL analysis workflow (Mario Behling) and a thank-you comment workflow for merged PRs (Hong Phuc Dang).

Contributed by @rajnisht7, @Manik-Khajuria-5, @vedansh-5, @tonypzy, @Debajeet-1411, @Jayantparashar10, @fiftyeightcoins and @sharmavikas18.

Dependencies

42 dependency updates from dependabot (npm packages and GitHub Actions): #1583, #1667, #1684, #1688, #1689, #1699, #1707, #1724, #1731, #1748, #1761, #1773, #1783, #1785, #1790, #1792, #1793, #1798, #1799, #1810, #1812, #1813, #1814, #1815, #1837, #1847, #1850, #1858, #1874, #1879, #1880, #1891, #1896, #1942, #1965, #1966, #1967, #1968, #1969, #1982, #1991, #1992

New Contributors

Welcome to the 12 people who made their first contribution in this release: @adity1raut, @fiftyeightcoins, @Maya-Mohamed, @munazzaghazali7-hash, @Pcmhacker-piro, @piyush182004, @rishi919-rgb, @sharmavikas18, @Shlok1729, @tarone-saloni, @thisisanubhav and @YashAnand69.

Contributors

Thanks to everyone who worked on this release:

Full Changelog: v0.3.0...v0.3.1

Don't miss a new visdom release

NewReleases is sending notifications on new releases.