{"owner":"fossasia","repo":"visdom","hasSpec":true,"specFile":"openapi.yaml","branch":"HEAD","format":"yaml","version":"3.x (YAML)","title":"visdom","description":"","endpoints":[],"spec":"# SPDX-License-Identifier: Apache-2.0\n#\n# https://www.apache.org/licenses/LICENSE-2.0\nopenapi: 3.1.0\ninfo:\n  title: Visdom API\n  description: >\n    Visdom is a flexible tool for creating, organizing, and sharing\n    visualizations of live, rich data. It supports Plotly-based charts,\n    images, text, audio, video, embeddings, and more. This specification\n    documents the HTTP REST endpoints exposed by the Visdom Tornado server.\n\n\n    All POST endpoints accept JSON request bodies (`application/json`).\n    When authentication is enabled, most endpoints require a valid session\n    cookie (set via the login endpoint at `POST /`). Unauthenticated\n    requests currently receive a `400` status code.\n\n\n    The server also supports WebSocket connections at `/socket` (read-only\n    subscriber) and `/vis_socket` (write-capable visualization source), as\n    well as HTTP polling fallbacks at `/socket_wrap` and `/vis_socket_wrap`.\n  version: 0.2.4\n  license:\n    name: Apache-2.0\n    url: https://www.apache.org/licenses/LICENSE-2.0\n  contact:\n    name: Visdom Authors\n    url: https://github.com/fossasia/visdom\n\nservers:\n  - url: \"{protocol}://{host}:{port}{base_url}\"\n    description: Visdom server\n    variables:\n      protocol:\n        default: http\n        enum: [http, https]\n      host:\n        default: \"0.0.0.0\"\n        description: Server hostname or IP address\n      port:\n        default: \"8097\"\n        description: Server port\n      base_url:\n        default: \"\"\n        description: Optional base URL prefix (e.g., \"/visdom\")\n\ntags:\n  - name: Visualization\n    description: Create and update visualization windows (panes)\n  - name: Window Management\n    description: Query, close, and retrieve window data\n  - name: Environment\n    description: Manage environments (create, delete, fork, list, save, compare)\n  - name: Experiments\n    description: Track experiment metadata (hyper-parameters, metrics, tags)\n  - name: Authentication\n    description: Login and session management\n  - name: Socket Polling\n    description: HTTP polling fallbacks for WebSocket connections\n  - name: System\n    description: Health check and diagnostics\n\nsecurity:\n  - cookieAuth: []\n  - {}\n\npaths:\n  /events:\n    post:\n      operationId: createWindow\n      tags: [Visualization]\n      summary: Create a new visualization window\n      description: >\n        Creates a new visualization pane in the specified environment. The\n        window type is determined by `data[].type`. If `win` is omitted, a\n        new window ID is generated automatically. If `win` is provided and\n        already exists, the existing window is replaced.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/CreateWindowRequest\"\n      responses:\n        \"200\":\n          description: Window created successfully. Returns the window ID as plain text.\n          content:\n            text/plain:\n              schema:\n                type: string\n                description: The ID of the created window.\n                examples:\n                  - \"window_38fa9db8e4f5b2\"\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n\n  /update:\n    post:\n      operationId: updateWindow\n      tags: [Visualization]\n      summary: Update an existing visualization window\n      description: >\n        Updates the data, options, or layout of an existing window. Supports\n        appending data to scatter/line plots, updating heatmaps (append/prepend\n        rows or columns), appending text, managing image history, updating\n        embeddings selections, adding/removing traces, and modifying plot\n        options or layout properties. If `append` is true and the window does\n        not exist, a new window is created.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/UpdateWindowRequest\"\n      responses:\n        \"200\":\n          description: >\n            Window updated successfully. Returns the window ID, or an error\n            message string if the window does not exist or the update is not\n            supported for the window type.\n          content:\n            text/plain:\n              schema:\n                type: string\n              examples:\n                success:\n                  value: \"window_38fa9db8e4f5b2\"\n                not_found:\n                  value: \"win does not exist\"\n                unsupported:\n                  value: \"win is not scatter, heatmap, custom, image_history, embeddings, or text; was bar\"\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n\n  /close:\n    post:\n      operationId: closeWindow\n      tags: [Window Management]\n      summary: Close one or all windows in an environment\n      description: >\n        Closes (deletes) a specific window or all windows in the given\n        environment. The close event is broadcast to all subscribed clients.\n        If `win` is omitted or null, all windows in the environment are\n        closed.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                win:\n                  type:\n                    - string\n                    - \"null\"\n                  description: >\n                    Window ID to close. If null or omitted, all windows in\n                    the environment are closed.\n                eid:\n                  type:\n                    - string\n                    - \"null\"\n                  description: >\n                    Environment ID. Defaults to `\"main\"` if omitted.\n      responses:\n        \"200\":\n          description: >\n            Window(s) closed. A close broadcast is sent to all subscribed\n            clients. No body is returned.\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n\n  /win_exists:\n    post:\n      operationId: checkWindowExists\n      tags: [Window Management]\n      summary: Check if a window exists in an environment\n      description: >\n        Returns `\"true\"` or `\"false\"` (as a plain text string) indicating\n        whether the specified window exists in the given environment.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              required: [win]\n              properties:\n                win:\n                  type: string\n                  description: Window ID to check.\n                eid:\n                  type:\n                    - string\n                    - \"null\"\n                  description: >\n                    Environment ID. Defaults to `\"main\"` if omitted.\n      responses:\n        \"200\":\n          description: Returns `\"true\"` or `\"false\"` as plain text.\n          content:\n            text/plain:\n              schema:\n                type: string\n                enum: [\"true\", \"false\"]\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n\n  /win_data:\n    post:\n      operationId: getOrSetWindowData\n      tags: [Window Management]\n      summary: Get or set raw window/environment data\n      description: >\n        When `data` is absent, retrieves the raw JSON data for a specific\n        window (if `win` is provided) or all windows in an environment (if\n        `win` is null). When `data` is present, sets (overwrites) the\n        window or environment data.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              oneOf:\n                - $ref: \"#/components/schemas/GetWindowDataRequest\"\n                - $ref: \"#/components/schemas/SetWindowDataRequest\"\n      responses:\n        \"200\":\n          description: >\n            For GET operations: returns JSON data of the window or all\n            windows. For SET operations: no body, but broadcasts an\n            environment update.\n          content:\n            application/json:\n              schema:\n                oneOf:\n                  - $ref: \"#/components/schemas/WindowObject\"\n                  - type: object\n                    additionalProperties:\n                      $ref: \"#/components/schemas/WindowObject\"\n                    description: Map of window IDs to window objects (when win is null).\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n        \"500\":\n          description: >\n            Server error. Occurs if the requested window does not exist\n            (unhandled assertion error).\n\n  /save:\n    post:\n      operationId: saveEnvironments\n      tags: [Environment]\n      summary: Persist environments to disk\n      description: >\n        Saves the specified environments to JSON files on disk. Environment\n        IDs that do not exist in the current server state are silently\n        dropped. Returns the list of environment IDs that were actually\n        saved.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              required: [data]\n              properties:\n                data:\n                  type: array\n                  items:\n                    type: string\n                  description: List of environment IDs to save.\n                  examples:\n                    - [\"main\", \"experiment_1\"]\n      responses:\n        \"200\":\n          description: Returns JSON array of successfully saved environment IDs.\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  type: string\n                examples:\n                  - [\"main\", \"experiment_1\"]\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n\n  /delete_env:\n    post:\n      operationId: deleteEnvironment\n      tags: [Environment]\n      summary: Delete an environment\n      description: >\n        Permanently deletes an environment and its associated JSON file on\n        disk. The `\"main\"` environment cannot be deleted (the request is\n        silently ignored). After deletion, an `env_update` broadcast is\n        sent to all subscribed clients. Handles both standard and\n        hash-named environment files.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                eid:\n                  type: string\n                  description: >\n                    Environment ID to delete. Forward/backward slashes are\n                    replaced with underscores.\n      responses:\n        \"200\":\n          description: >\n            Environment deleted (or silently ignored if `\"main\"`). An\n            `env_update` broadcast is sent. No body returned.\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n\n  /env_state:\n    post:\n      operationId: listEnvironments\n      tags: [Environment]\n      summary: List all available environments\n      description: >\n        Returns a JSON array of all environment IDs currently loaded in\n        the server state. The request body is ignored but must be valid\n        JSON (an empty object `{}` is sufficient).\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              description: Request body is ignored. Send an empty object.\n      responses:\n        \"200\":\n          description: JSON array of environment IDs.\n          content:\n            application/json:\n              schema:\n                type: array\n                items:\n                  type: string\n                examples:\n                  - [\"main\", \"experiment_1\", \"experiment_2\"]\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n\n  /fork_env:\n    post:\n      operationId: forkEnvironment\n      tags: [Environment]\n      summary: Clone an environment with a new ID\n      description: >\n        Deep-copies the source environment (all windows and metadata) into\n        a new environment with the specified ID. The forked environment is\n        immediately saved to disk and an `env_update` broadcast is sent.\n        Fails with an assertion error if the source environment does not\n        exist.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              required: [prev_eid, eid]\n              properties:\n                prev_eid:\n                  type: string\n                  description: Source environment ID to clone from.\n                eid:\n                  type: string\n                  description: New environment ID for the clone.\n      responses:\n        \"200\":\n          description: Returns the new environment ID as plain text.\n          content:\n            text/plain:\n              schema:\n                type: string\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n        \"500\":\n          description: Server error. Occurs if the source environment does not exist (unhandled assertion error).\n\n  /experiments/log:\n    post:\n      operationId: logExperiment\n      tags: [Experiments]\n      summary: Record experiment metadata for an environment\n      description: >\n        Attaches experiment metadata (hyper-parameters, metric observations,\n        and tags) to an environment, stored under the environment's\n        `experiment` key and persisted through the server's data store. The\n        `action` field selects the operation:\n\n\n        - `log` (default): create or update the experiment. Repeated calls\n        merge new `params`/`tags` and overwrite `name`/`description`.\n\n\n        - `metrics`: append one or more `{name: value}` observations at an\n        optional `step`, creating the experiment if it does not exist yet.\n\n\n        - `finish`: mark the experiment terminal (`finished` or `failed`). An\n        experiment that is already terminal cannot be finished again.\n\n\n        Once an experiment is terminal, every action is rejected with `409`, so\n        neither a finished run's recorded data nor its final status can change\n        after the fact.\n        Every action writes, so the endpoint is rejected when the server is\n        running in readonly mode.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                eid:\n                  type: string\n                  description: Target environment ID. Defaults to `main`.\n                action:\n                  type: string\n                  enum: [log, metrics, finish]\n                  default: log\n                  description: Operation to perform.\n                name:\n                  type: string\n                  description: Display name (action `log`). Defaults to the eid.\n                description:\n                  type: string\n                  description: Free-form description (action `log`).\n                params:\n                  type: object\n                  additionalProperties: true\n                  description: 'Hyper-parameters as `{name: value}` (action `log`).'\n                tags:\n                  type: object\n                  additionalProperties: true\n                  description: 'Free-form tags as `{name: value}` (action `log`).'\n                metrics:\n                  type: object\n                  additionalProperties:\n                    type: number\n                  description: >\n                    Metric observations as `{name: value}` (action `metrics`).\n                    Must be a non-empty object.\n                step:\n                  type: integer\n                  description: Optional training step for the metrics (action `metrics`).\n                status:\n                  type: string\n                  enum: [finished, failed]\n                  default: finished\n                  description: Terminal status (action `finish`).\n      responses:\n        \"200\":\n          description: The stored experiment as JSON.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/Experiment\"\n        \"400\":\n          description: >\n            Invalid request — unknown `action`, non-object `params`/`tags`/`metrics`,\n            empty `metrics`, or a non-terminal `finish` status. Also returned when\n            authentication is required but not provided.\n        \"403\":\n          description: >\n            Experiment logging is disabled because the server is in readonly\n            mode.\n        \"404\":\n          description: A `finish` was requested for an env that has no experiment.\n        \"409\":\n          description: >\n            A write was attempted on a terminal (finished/failed) experiment —\n            a `log`/`metrics` call, or a second `finish`.\n\n  /experiments/search:\n    post:\n      operationId: searchExperiments\n      tags: [Experiments]\n      summary: Search experiments across all environments\n      description: >\n        Returns the experiments matching `query`, sorted and paged. The query is\n        a small readable syntax of comparisons (`<`, `<=`, `>`, `>=`, `=`, `!=`,\n        `contains`) combined with `AND`/`OR` and parentheses, for example\n        `lr < 0.01 AND (acc > 0.9 OR status = finished)`. Omitting `query`\n        matches every experiment.\n\n\n        Comparison names are the experiment's built-in fields (`name`,\n        `status`, `created_at`, ...), its params, its metrics and its tags. A\n        name may be given bare (`acc`) or namespaced when it is ambiguous\n        (`metric.acc`, `param.lr`, `tag.owner`); metrics compare on their latest\n        logged value. A name no experiment has simply matches nothing.\n\n\n        Queries are parsed into a predicate and evaluated in Python — never\n        eval'd and never turned into SQL — so a hostile query is rejected as a\n        parse error rather than executed.\n\n\n        Experiments are read back through the server's data store, so a server\n        running with no persistence path configured has nothing to search.\n      requestBody:\n        required: false\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                query:\n                  type: string\n                  nullable: true\n                  description: >\n                    Filter expression. Omitted, null or blank matches every\n                    experiment.\n                sort_by:\n                  type: string\n                  nullable: true\n                  default: created_at\n                  description: >\n                    Field to sort by — any name a query can compare on.\n                    Experiments lacking the field sort last in both directions.\n                descending:\n                  type: boolean\n                  default: true\n                  description: Sort direction; newest/highest first by default.\n                limit:\n                  type: integer\n                  nullable: true\n                  minimum: 0\n                  default: 100\n                  description: >\n                    Maximum number of experiments in the reply. `0` returns just\n                    the count; `null` returns all matches.\n                offset:\n                  type: integer\n                  minimum: 0\n                  default: 0\n                  description: Number of matches to skip before the returned page.\n      responses:\n        \"200\":\n          description: The matching page of experiments.\n          content:\n            application/json:\n              schema:\n                type: object\n                required: [experiments, total, limit, offset, query]\n                properties:\n                  experiments:\n                    type: array\n                    description: One page of matching experiments.\n                    items:\n                      $ref: \"#/components/schemas/Experiment\"\n                  total:\n                    type: integer\n                    description: >\n                      Total matches for the query, ignoring `limit`/`offset`.\n                  limit:\n                    type: integer\n                    nullable: true\n                    description: The limit applied to this reply.\n                  offset:\n                    type: integer\n                    description: The offset applied to this reply.\n                  query:\n                    type: string\n                    description: The query used, `\"\"` if none was given.\n        \"400\":\n          description: >\n            Invalid request — a body that is not a JSON object, malformed\n            `query` syntax, a non-string `query`/`sort_by`, or a `limit`/`offset`\n            that is not a non-negative integer. Also returned when\n            authentication is required but not provided.\n\n  /experiments/compare:\n    post:\n      operationId: compareExperiments\n      tags: [Experiments]\n      summary: Compare experiments field by field\n      description: >\n        Lines several experiments up beside each other and reports, per section\n        (params, metrics and tags), which fields they agree on and which they do\n        not — the short list of knobs that actually changed between runs.\n\n\n        The runs are named by `env_ids` and compared in the order given; every id\n        must have an experiment, otherwise `404`.\n\n\n        Finding the runs is `/experiments/search`'s job: it answers \"which runs\n        match?\", this answers \"how do these runs differ?\". To compare a query's\n        matches, search first and pass the resulting ids on.\n\n\n        Metrics are a time series, so a comparison uses each metric's latest\n        observation — the same value a search compares on.\n\n\n        Experiments are read back through the server's data store, so a server\n        running with no persistence path configured has nothing to compare.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              required: [env_ids]\n              properties:\n                env_ids:\n                  type: array\n                  minItems: 1\n                  description: >\n                    Environments to compare, in the order given. Duplicate ids\n                    collapse to one.\n                  items:\n                    type: string\n      responses:\n        \"200\":\n          description: The comparison of the selected experiments.\n          content:\n            application/json:\n              schema:\n                type: object\n                required: [env_ids, experiments, params, metrics, tags]\n                properties:\n                  env_ids:\n                    type: array\n                    description: The runs compared, in the order compared.\n                    items:\n                      type: string\n                  experiments:\n                    type: array\n                    description: The compared experiments, in full.\n                    items:\n                      $ref: \"#/components/schemas/Experiment\"\n                  params:\n                    $ref: \"#/components/schemas/ExperimentComparisonSection\"\n                  metrics:\n                    $ref: \"#/components/schemas/ExperimentComparisonSection\"\n                  tags:\n                    $ref: \"#/components/schemas/ExperimentComparisonSection\"\n        \"400\":\n          description: >\n            Invalid request — the body is not a JSON object, or `env_ids` is\n            missing, empty, not a list, or holds something other than strings.\n            Also returned when authentication is required but not provided.\n        \"404\":\n          description: One or more of the given `env_ids` has no experiment.\n\n  /experiments/suggest:\n    post:\n      operationId: suggestExperiment\n      tags: [Experiments]\n      summary: Suggest parameters for the next run (reserved)\n      description: >\n        Reserved endpoint for hyper-parameter suggestion. Choosing the next set\n        of parameters to try is a search-strategy problem (Optuna-backed) that\n        lands in a later release, so this is currently a stub: it accepts the\n        request and replies `501 Not Implemented` with a JSON body carrying a\n        `suggestion: null` placeholder, rather than a made-up suggestion. The\n        route, the `suggest_experiment` client method and this documentation are\n        in place so the strategy can be wired in later without changing the\n        surface.\n      requestBody:\n        required: false\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                eid:\n                  type: string\n                  description: Target environment ID. Defaults to `main`.\n                params:\n                  type: object\n                  additionalProperties: true\n                  description: >\n                    The search space to suggest over, as `{name: spec}`. Ignored\n                    by the stub; reserved for the eventual strategy.\n      responses:\n        \"501\":\n          description: >\n            Not implemented — the suggestion strategy is reserved for a later\n            release. The body is a JSON stub.\n          content:\n            application/json:\n              schema:\n                type: object\n                required: [status, detail, suggestion]\n                properties:\n                  status:\n                    type: string\n                    example: not_implemented\n                  detail:\n                    type: string\n                  suggestion:\n                    nullable: true\n                    description: Always `null` while the endpoint is a stub.\n        \"400\":\n          description: >\n            Invalid request — the body is present but is not valid JSON, or is\n            not a JSON object. Also returned when authentication is required\n            but not provided.\n\n  /upload_env:\n    post:\n      operationId: uploadEnvironment\n      tags: [Environment]\n      summary: Upload a Visdom JSON file as a new environment\n      description: >\n        Accepts a Visdom environment-export JSON file via multipart form upload\n        and loads it as a new environment named `uploaded_<name>_<id>`. The\n        uploaded JSON must be an object containing `jsons` and `reload` keys.\n        The new environment is saved to disk (when a persistence path is\n        configured) and an `env_update` broadcast is sent. Maximum file size is\n        100 MB. Rejected when the server is running in readonly mode.\n      requestBody:\n        required: true\n        content:\n          multipart/form-data:\n            schema:\n              type: object\n              required: [file]\n              properties:\n                file:\n                  type: string\n                  format: binary\n                  description: >\n                    A Visdom environment-export JSON file (object with `jsons`\n                    and `reload` keys).\n      responses:\n        \"200\":\n          description: Environment created from the uploaded file.\n          content:\n            application/json:\n              schema:\n                type: object\n                required: [success, eid, message]\n                properties:\n                  success:\n                    type: boolean\n                  eid:\n                    type: string\n                    description: ID of the newly created environment.\n                  message:\n                    type: string\n              examples:\n                uploaded:\n                  value:\n                    success: true\n                    eid: uploaded_myrun_1a2b3c4d\n                    message: \"Dashboard loaded successfully as 'uploaded_myrun_1a2b3c4d'\"\n        \"400\":\n          description: >\n            No file uploaded, invalid JSON, not a valid Visdom JSON, or\n            authentication required but not provided (when login is enabled).\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/UploadErrorResponse\"\n        \"403\":\n          description: Uploads are disabled because the server is in readonly mode.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/UploadErrorResponse\"\n        \"413\":\n          description: Uploaded file exceeds the 100 MB size limit.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/UploadErrorResponse\"\n\n  /env/{eid}:\n    get:\n      operationId: renderEnvironmentPage\n      tags: [Environment]\n      summary: Render the environment UI page\n      description: >\n        Returns the Visdom HTML page for the specified environment. This is\n        the main web UI entry point for a specific environment.\n      parameters:\n        - name: eid\n          in: path\n          required: true\n          schema:\n            type: string\n          description: Environment ID.\n      responses:\n        \"200\":\n          description: HTML page for the environment.\n          content:\n            text/html:\n              schema:\n                type: string\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n    post:\n      operationId: loadEnvironment\n      tags: [Environment]\n      summary: Load environment data to a client socket or create a new environment\n      description: >\n        If `sid` is provided and matches a subscribed socket, the\n        environment's windows and layout are sent to that specific socket.\n        If `eid` is provided and does not exist, a new empty environment\n        is created and an `env_update` broadcast is sent.\n      parameters:\n        - name: eid\n          in: path\n          required: true\n          schema:\n            type: string\n          description: Environment ID (from URL path).\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              properties:\n                sid:\n                  type: string\n                  description: >\n                    Socket ID of the subscriber to load the environment to.\n                eid:\n                  type: string\n                  description: >\n                    Environment ID to create if it does not exist.\n      responses:\n        \"200\":\n          description: >\n            Environment loaded to the specified socket, or new environment\n            created. No body returned.\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n\n  /compare/{eids}:\n    get:\n      operationId: renderComparePage\n      tags: [Environment]\n      summary: Render the comparison view for multiple environments\n      description: >\n        Returns the Visdom HTML page configured for comparing multiple\n        environments side by side. Windows with matching titles across\n        environments are combined. Plots show traces from each environment\n        with prefixed legend names. Images are displayed in a comparison\n        grid.\n      parameters:\n        - name: eids\n          in: path\n          required: true\n          schema:\n            type: string\n          description: >\n            Environment IDs separated by `+` (e.g., `env1+env2+env3`).\n      responses:\n        \"200\":\n          description: HTML page for environment comparison.\n          content:\n            text/html:\n              schema:\n                type: string\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n    post:\n      operationId: loadComparisonData\n      tags: [Environment]\n      summary: Load comparison data for multiple environments to a client socket\n      description: >\n        Sends comparison window data to the specified subscriber socket.\n        Only windows of type `\"plot\"` or `\"image\"` with matching, non-empty\n        titles across at least two environments are included. Plot traces\n        are prefixed with the environment index. Images are shown side by\n        side with captioned labels.\n      parameters:\n        - name: eids\n          in: path\n          required: true\n          schema:\n            type: string\n          description: >\n            Environment IDs separated by `+` (e.g., `env1+env2+env3`).\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              required: [sid]\n              properties:\n                sid:\n                  type: string\n                  description: Socket ID of the subscriber to load comparison data to.\n      responses:\n        \"200\":\n          description: >\n            Comparison data sent to the specified socket. No body returned.\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n\n  /user/{path}:\n    get:\n      operationId: getUserSettings\n      tags: [System]\n      summary: Retrieve user settings files\n      description: >\n        Currently only supports `style.css` as the path parameter. Returns\n        the user's custom CSS stylesheet.\n      security: []\n      parameters:\n        - name: path\n          in: path\n          required: true\n          schema:\n            type: string\n            enum: [\"style.css\"]\n          description: Settings file path. Currently only `\"style.css\"` is supported.\n      responses:\n        \"200\":\n          description: User CSS content.\n          content:\n            text/css:\n              schema:\n                type: string\n\n  /health:\n    get:\n      operationId: healthCheck\n      tags: [System]\n      summary: Health check endpoint\n      description: >\n        Returns a simple JSON object indicating the server is running.\n        This endpoint does not require authentication.\n      security: []\n      responses:\n        \"200\":\n          description: Server is healthy.\n          content:\n            application/json:\n              schema:\n                type: object\n                required: [status]\n                properties:\n                  status:\n                    type: string\n                    enum: [\"ok\"]\n              examples:\n                healthy:\n                  value:\n                    status: ok\n        \"400\":\n          description: Bad request.\n\n  /error/{text}:\n    get:\n      operationId: triggerError\n      tags: [System]\n      summary: Test endpoint that raises an error\n      description: >\n        Raises an exception with the provided text message. Used for\n        testing error handling. This endpoint does not require\n        authentication.\n      security: []\n      parameters:\n        - name: text\n          in: path\n          required: true\n          schema:\n            type: string\n          description: Error message text. Defaults to `\"test error\"` if empty.\n      responses:\n        \"500\":\n          description: Server error with the specified message (endpoint always raises).\n\n  /:\n    get:\n      operationId: renderMainPage\n      tags: [Authentication]\n      summary: Render the main Visdom page or login page\n      description: >\n        If authentication is disabled or the user is authenticated, renders\n        the main Visdom UI (`index.html`). If authentication is enabled\n        and the user is not authenticated, renders the login page\n        (`login.html`).\n      security: []\n      responses:\n        \"200\":\n          description: HTML page (index.html or login.html).\n          content:\n            text/html:\n              schema:\n                type: string\n        \"400\":\n          description: Bad request.\n    post:\n      operationId: login\n      tags: [Authentication]\n      summary: Authenticate and create a session\n      description: >\n        Validates the username and password against the server's configured\n        credentials. The password is SHA-256 hashed on the server side\n        (the client should also pre-hash once before sending). On success,\n        sets a secure cookie (`user_password`). On failure, returns 400.\n      security: []\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              type: object\n              required: [username, password]\n              properties:\n                username:\n                  type: string\n                  description: Username for authentication.\n                password:\n                  type: string\n                  description: >\n                    Password string (typically pre-hashed once on the\n                    client side with SHA-256, then hashed again on the\n                    server).\n      responses:\n        \"200\":\n          description: >\n            Authentication successful. A `user_password` secure cookie is\n            set. No body returned.\n          headers:\n            Set-Cookie:\n              schema:\n                type: string\n              description: Secure session cookie.\n        \"400\":\n          description: Invalid credentials.\n\n  /socket:\n    get:\n      operationId: subscribeSocket\n      tags: [Socket Polling]\n      summary: WebSocket endpoint for subscribing to visualization updates\n      description: >\n        WebSocket connection for read-only subscribers. Clients connect via\n        the WebSocket protocol (HTTP Upgrade). On connection, the server\n        sends a `register` message with the assigned socket ID, readonly\n        status, and current environment list. The server then streams\n        window updates, close events, environment updates, and layout\n        changes. Clients can send commands: `close`, `save`, `save_all`,\n        `delete_env`, `save_layouts`, `forward_to_vis`,\n        `layout_item_update`, `pop_embeddings_pane`.\n\n\n        **Note:** This is a WebSocket endpoint. OpenAPI does not natively\n        support WebSocket, so this is documented as a GET for reference.\n      responses:\n        \"200\":\n          description: Not applicable. This endpoint upgrades to WebSocket (101).\n        \"101\":\n          description: >\n            Switching Protocols. WebSocket connection established. Server\n            sends a JSON register message with `command`, `data` (socket\n            ID), `readonly`, and `envList` fields. If authentication is\n            enabled and the user is not authenticated, the connection is\n            accepted then immediately closed.\n\n  /vis_socket:\n    get:\n      operationId: visSourceSocket\n      tags: [Socket Polling]\n      summary: WebSocket endpoint for visualization data sources\n      description: >\n        WebSocket connection for visualization sources with write access.\n        Clients connect via the WebSocket protocol (HTTP Upgrade). On\n        connection, the server sends an `alive` message. Sources can\n        push visualization data and send commands: `echo`, `close`,\n        `save`, `save_all`, `delete_env`, `save_layouts`,\n        `forward_to_vis`, `layout_item_update`, `pop_embeddings_pane`.\n\n\n        **Note:** This is a WebSocket endpoint. OpenAPI does not natively\n        support WebSocket, so this is documented as a GET for reference.\n      responses:\n        \"200\":\n          description: Not applicable. This endpoint upgrades to WebSocket (101).\n        \"101\":\n          description: >\n            Switching Protocols. WebSocket connection established. Server\n            sends a JSON alive message with `{\"command\": \"alive\", \"data\": \"vis_alive\"}`.\n            If authentication is enabled and the user is not authenticated,\n            the connection is accepted then immediately closed.\n\n  /socket_wrap:\n    get:\n      operationId: createSocketWrapper\n      tags: [Socket Polling]\n      summary: Create a new polling-based subscriber socket\n      description: >\n        Creates a new subscriber socket wrapper for HTTP polling. Returns\n        the socket ID that must be used in subsequent POST requests to\n        send/receive messages. This is a fallback for environments that\n        do not support WebSocket connections.\n      responses:\n        \"200\":\n          description: New socket wrapper created.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/SocketWrapCreateResponse\"\n        \"400\":\n          description: Authentication required but not provided (when login is enabled).\n    post:\n      operationId: pollSubscriberSocket\n      tags: [Socket Polling]\n      summary: Send or query messages on a polling-based subscriber socket\n      description: >\n        Interact with a polling-based subscriber socket. Use\n        `message_type: \"query\"` to retrieve pending messages, or\n        `message_type: \"send\"` to send a message (command) to the server.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/SocketWrapRequest\"\n      responses:\n        \"200\":\n          description: Operation result.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/SocketWrapResponse\"\n\n  /vis_socket_wrap:\n    post:\n      operationId: pollVisSourceSocket\n      tags: [Socket Polling]\n      summary: Send or query messages on a polling-based visualization source socket\n      description: >\n        Interact with a polling-based visualization source socket (write\n        access). Use `message_type: \"query\"` to retrieve pending messages,\n        or `message_type: \"send\"` to send a message. If `sid` is null or\n        omitted, a new visualization source socket is created and its ID\n        is returned.\n      requestBody:\n        required: true\n        content:\n          application/json:\n            schema:\n              $ref: \"#/components/schemas/SocketWrapRequest\"\n      responses:\n        \"200\":\n          description: Operation result.\n          content:\n            application/json:\n              schema:\n                $ref: \"#/components/schemas/SocketWrapResponse\"\n\ncomponents:\n  securitySchemes:\n    cookieAuth:\n      type: apiKey\n      in: cookie\n      name: user_password\n      description: >\n        Session cookie set by the `POST /` login endpoint. Required when\n        the server is started with `--enable_login`.\n\n  schemas:\n\n    ExperimentComparisonSection:\n      type: object\n      description: >\n        One section (params, metrics or tags) of an experiment comparison,\n        diffed across the compared runs.\n      required: [fields, shared, differing, values, groups]\n      properties:\n        fields:\n          type: array\n          description: Every field name any compared run has, sorted.\n          items:\n            type: string\n        shared:\n          type: object\n          additionalProperties: true\n          description: >\n            The fields every compared run carries with the same value, as\n            `{name: value}`.\n        differing:\n          type: array\n          description: >\n            The remaining fields — those whose value varies between runs, or\n            that some run is missing.\n          items:\n            type: string\n        values:\n          type: object\n          additionalProperties:\n            type: object\n            additionalProperties: true\n          description: >\n            Per-field, per-run values as `{field: {env_id: value}}`. A run that\n            never logged the field is omitted from that field's map.\n        groups:\n          type: object\n          description: >\n            Per field, the runs clustered by the value they used — the finer\n            question \"which runs agree?\", where `shared`/`differing` answer only\n            \"do they all?\". With three runs on two learning rates, the two that\n            match share a group. A field appears in `shared` exactly when its\n            groups are a single cluster holding every compared run, so the two\n            never disagree. Groups are ordered by first appearance, and a run\n            that never logged the field is in no group.\n          additionalProperties:\n            type: array\n            items:\n              type: object\n              required: [value, env_ids]\n              properties:\n                value:\n                  description: The value these runs share.\n                env_ids:\n                  type: array\n                  description: The runs that used it, in compared order.\n                  items:\n                    type: string\n\n    Experiment:\n      type: object\n      description: Experiment metadata attached to an environment.\n      properties:\n        env_id:\n          type: string\n          description: Environment the experiment belongs to.\n        name:\n          type: string\n          description: Display name. Defaults to the env_id.\n        description:\n          type: string\n        status:\n          type: string\n          enum: [running, finished, failed]\n          description: Lifecycle state. New experiments start `running`.\n        created_at:\n          type: number\n          description: Unix timestamp when the experiment was created.\n        finished_at:\n          type: [number, \"null\"]\n          description: Unix timestamp when finished, or `null` while running.\n        params:\n          type: array\n          description: Hyper-parameters, keyed by name.\n          items:\n            type: object\n            properties:\n              key:\n                type: string\n              value: {}\n              dtype:\n                type: string\n                enum: [bool, int, float, str]\n                description: Inferred type, so a stored value can be cast back.\n        metrics:\n          type: array\n          description: Metric observations, appended over time.\n          items:\n            type: object\n            properties:\n              key:\n                type: string\n              value:\n                type: number\n              step:\n                type: [integer, \"null\"]\n              timestamp:\n                type: number\n        tags:\n          type: array\n          description: Free-form key/value labels.\n          items:\n            type: object\n            properties:\n              key:\n                type: string\n              value:\n                type: string\n\n    UploadErrorResponse:\n      type: object\n      description: Error response returned by the /upload_env endpoint.\n      properties:\n        success:\n          type: boolean\n          description: Always `false` for error responses.\n        error:\n          type: string\n          description: Human-readable description of what went wrong.\n\n    CreateWindowRequest:\n      type: object\n      required: [data]\n      description: >\n        Request body for creating a new visualization window. The `data`\n        array defines the content and type of visualization. The server\n        determines the window type from `data[0].type`.\n      properties:\n        data:\n          type: array\n          minItems: 1\n          items:\n            $ref: \"#/components/schemas/WindowData\"\n          description: >\n            Array of data objects. For plot types (scatter, line, bar, etc.),\n            each element is a Plotly trace object. For visdom-native types\n            (image, text, properties, network, embeddings), a single element\n            with a `content` field is used.\n        layout:\n          $ref: \"#/components/schemas/PlotlyLayout\"\n        opts:\n          $ref: \"#/components/schemas/WindowOptions\"\n        win:\n          type:\n            - string\n            - \"null\"\n          description: >\n            Window ID. If null or omitted, a new unique ID is\n            auto-generated.\n        eid:\n          type:\n            - string\n            - \"null\"\n          description: >\n            Environment ID. Defaults to `\"main\"` if omitted.\n\n    UpdateWindowRequest:\n      type: object\n      required: [win]\n      description: Request body for updating an existing visualization window.\n      properties:\n        win:\n          type: string\n          description: Window ID to update.\n        eid:\n          type:\n            - string\n            - \"null\"\n          description: >\n            Environment ID. Defaults to `\"main\"` if omitted.\n        data:\n          oneOf:\n            - type: array\n              items:\n                $ref: \"#/components/schemas/WindowData\"\n              description: >\n                Array of data objects for plot updates.\n            - type: object\n              description: >\n                Object data for embeddings updates.\n              properties:\n                update_type:\n                  type: string\n                  enum: [EntitySelected, RegionSelected]\n                  description: Type of embeddings update.\n                selected:\n                  type:\n                    - integer\n                    - \"null\"\n                  description: >\n                    Selected entity index (for EntitySelected) or null.\n                points:\n                  type: array\n                  items:\n                    type: array\n                    items:\n                      type: number\n                  description: >\n                    Array of [x, y] points (for RegionSelected).\n        name:\n          type:\n            - string\n            - \"null\"\n          description: >\n            Trace name to update (for scatter/line plots). When specified\n            with `delete: true`, deletes that trace.\n        append:\n          type: boolean\n          description: >\n            If true, append data to existing traces instead of replacing.\n            If the window does not exist and `append` is true, a new\n            window is created.\n        delete:\n          type: boolean\n          description: >\n            If true, delete the trace specified by `name`.\n        updateDir:\n          type: string\n          enum: [replace, appendRow, prependRow, appendColumn, prependColumn]\n          description: >\n            Direction for heatmap updates. `replace` overwrites the entire\n            heatmap. Other values append or prepend rows/columns.\n        opts:\n          $ref: \"#/components/schemas/WindowOptions\"\n        layout:\n          $ref: \"#/components/schemas/PlotlyLayout\"\n\n    GetWindowDataRequest:\n      type: object\n      description: Request to retrieve window or environment data.\n      properties:\n        eid:\n          type:\n            - string\n            - \"null\"\n          description: >\n            Environment ID. Defaults to `\"main\"` if omitted.\n        win:\n          type:\n            - string\n            - \"null\"\n          description: >\n            Window ID. If null, returns all windows in the environment.\n\n    SetWindowDataRequest:\n      type: object\n      required: [data]\n      description: Request to overwrite window or environment data.\n      properties:\n        eid:\n          type:\n            - string\n            - \"null\"\n          description: >\n            Environment ID. Defaults to `\"main\"` if omitted.\n        win:\n          type:\n            - string\n            - \"null\"\n          description: >\n            Window ID. If null, replaces all windows in the environment.\n        data:\n          type: string\n          description: >\n            JSON-encoded string of the window object or the complete\n            environment jsons map (when win is null).\n\n    SocketWrapRequest:\n      type: object\n      description: Request body for socket polling operations.\n      properties:\n        message_type:\n          type: string\n          enum: [query, send]\n          description: >\n            `\"query\"` to retrieve pending messages; `\"send\"` to dispatch\n            a message.\n        sid:\n          type:\n            - string\n            - \"null\"\n          description: >\n            Socket ID from a previous create call. If null on\n            `/vis_socket_wrap`, creates a new visualization source socket.\n        message:\n          type: string\n          description: >\n            JSON-encoded message to send (required when\n            `message_type` is `\"send\"`).\n\n    SocketWrapCreateResponse:\n      type: object\n      properties:\n        success:\n          type: boolean\n          const: true\n        sid:\n          type: string\n          description: Socket ID for subsequent polling requests.\n      required: [success, sid]\n\n    SocketWrapResponse:\n      type: object\n      description: >\n        Response from a socket polling operation. The exact shape depends\n        on the operation.\n      properties:\n        success:\n          type: boolean\n        messages:\n          type: array\n          items:\n            type: string\n          description: >\n            Pending messages (present when `message_type` was `\"query\"`\n            and `success` is true).\n        reason:\n          type: string\n          enum: [closed, no msg, invalid]\n          description: >\n            Failure reason (present when `success` is false).\n        sid:\n          type: string\n          description: >\n            Socket ID (present when a new socket was created).\n      required: [success]\n\n    WindowData:\n      type: object\n      description: >\n        A single data element in a visualization request. For Plotly-based\n        plot types, this follows the Plotly trace schema. For visdom-native\n        types (image, text, properties, network, embeddings, image_history),\n        this contains a `content` field with type-specific data.\n      properties:\n        type:\n          type: string\n          description: >\n            The visualization type. Plotly types: `scatter`, `scatter3d`,\n            `scattergl`, `bar`, `histogram`, `box`, `heatmap`, `contour`,\n            `surface`. Visdom-native types: `image`, `image_history`,\n            `text`, `properties`, `network`, `embeddings`, `custom`.\n          enum:\n            - scatter\n            - scatter3d\n            - scattergl\n            - bar\n            - histogram\n            - box\n            - heatmap\n            - contour\n            - surface\n            - image\n            - image_history\n            - text\n            - properties\n            - network\n            - embeddings\n            - custom\n        content:\n          description: >\n            Content for visdom-native types. Shape depends on `type`:\n            - `image`: `{ \"src\": \"data:image/png;base64,...\", \"caption\": \"...\" }`\n            - `text`: HTML string\n            - `properties`: array of property objects\n            - `network`: `{ \"nodes\": [...], \"edges\": [...] }`\n            - `embeddings`: `{ \"data\": [[x,y], ...], \"selected\": null, \"has_previous\": false }`\n            - `image_history`: same as `image` content\n          oneOf:\n            - $ref: \"#/components/schemas/ImageContent\"\n            - type: string\n              description: HTML content (for text type).\n            - type: array\n              items:\n                $ref: \"#/components/schemas/PropertyItem\"\n              description: Properties array (for properties type).\n            - $ref: \"#/components/schemas/NetworkContent\"\n            - $ref: \"#/components/schemas/EmbeddingsContent\"\n        x:\n          type: array\n          items:\n            type:\n              - number\n              - \"null\"\n          description: X-axis values (Plotly trace).\n        y:\n          type: array\n          items:\n            type:\n              - number\n              - \"null\"\n          description: Y-axis values (Plotly trace).\n        z:\n          oneOf:\n            - type: array\n              items:\n                type: number\n              description: Z-axis values for 3D scatter.\n            - type: array\n              items:\n                type: array\n                items:\n                  type: number\n              description: 2D array of z values for heatmap/surface/contour.\n          description: Z values (3D scatter, heatmap, surface, contour).\n        name:\n          type: string\n          description: Trace name (shown in legend).\n        mode:\n          type: string\n          description: >\n            Plotly trace mode (e.g., `\"markers\"`, `\"lines\"`,\n            `\"lines+markers\"`, `\"markers+text\"`).\n        text:\n          type:\n            - array\n            - \"null\"\n          items:\n            type: string\n          description: Text labels for each data point.\n        textposition:\n          type: string\n          description: Position of text labels relative to markers.\n        fill:\n          type: string\n          description: Fill area mode (e.g., `\"tonexty\"`).\n        line:\n          type: object\n          properties:\n            dash:\n              type:\n                - string\n                - \"null\"\n              description: Dash style (e.g., `\"solid\"`, `\"dash\"`, `\"dot\"`).\n            color:\n              type:\n                - string\n                - \"null\"\n              description: Line color.\n        marker:\n          $ref: \"#/components/schemas/MarkerProperties\"\n\n    MarkerProperties:\n      type: object\n      description: Plotly marker styling properties.\n      properties:\n        size:\n          type: number\n          description: Marker size in pixels.\n        symbol:\n          type: string\n          description: >\n            Marker symbol (e.g., `\"dot\"`, `\"cross\"`, `\"diamond\"`).\n        color:\n          oneOf:\n            - type: string\n            - type: array\n              items:\n                type: number\n            - type: \"null\"\n          description: Marker color (single color string or array of values).\n        line:\n          type: object\n          properties:\n            color:\n              type: string\n              description: Marker border color.\n            width:\n              type: number\n              description: Marker border width.\n\n    ImageContent:\n      type: object\n      description: Image data for image and image_history window types.\n      required: [src]\n      properties:\n        src:\n          type: string\n          description: >\n            Base64-encoded image data URI\n            (e.g., `\"data:image/png;base64,iVBOR...\"`).\n        caption:\n          type:\n            - string\n            - \"null\"\n          description: Optional caption displayed below the image.\n\n    PropertyItem:\n      type: object\n      description: A single property in a properties pane.\n      required: [type, name, value]\n      properties:\n        type:\n          type: string\n          enum: [text, number, button, checkbox, select]\n          description: >\n            Property input type. Determines how the value is rendered.\n        name:\n          type: string\n          description: Display label for the property.\n        value:\n          oneOf:\n            - type: string\n            - type: number\n            - type: boolean\n          description: >\n            Current value. Type depends on `type` field:\n            text -> string, number -> string (decimal),\n            button -> string (label), checkbox -> boolean,\n            select -> integer (zero-based index).\n        values:\n          type: array\n          items:\n            type: string\n          description: >\n            List of possible values (only for `select` type).\n\n    NetworkContent:\n      type: object\n      description: Network graph data.\n      properties:\n        nodes:\n          type: array\n          items:\n            type: object\n          description: Array of node objects.\n        edges:\n          type: array\n          items:\n            type: object\n          description: Array of edge objects.\n\n    EmbeddingsContent:\n      type: object\n      description: Embeddings visualization data (t-SNE 2D points).\n      properties:\n        data:\n          type: array\n          items:\n            type: array\n            items:\n              type: number\n            minItems: 2\n            maxItems: 2\n          description: Array of [x, y] coordinates for each entity.\n        selected:\n          type:\n            - integer\n            - \"null\"\n          description: Index of the currently selected entity, or null.\n        has_previous:\n          type: boolean\n          description: Whether a previous zoom level can be popped.\n\n    PlotlyLayout:\n      type: object\n      description: >\n        Plotly layout configuration. Supports all standard Plotly layout\n        properties. Common properties are listed here.\n      properties:\n        title:\n          type: string\n          description: Plot title.\n        xaxis:\n          $ref: \"#/components/schemas/PlotlyAxis\"\n        yaxis:\n          $ref: \"#/components/schemas/PlotlyAxis\"\n        scene:\n          type: object\n          description: 3D scene configuration (used by 3D plots).\n          properties:\n            xaxis:\n              $ref: \"#/components/schemas/PlotlyAxis\"\n            yaxis:\n              $ref: \"#/components/schemas/PlotlyAxis\"\n            zaxis:\n              $ref: \"#/components/schemas/PlotlyAxis\"\n        showlegend:\n          type: boolean\n          description: Whether to show the legend.\n        legend:\n          type: object\n          description: Legend configuration object.\n        margin:\n          type: object\n          properties:\n            l:\n              type: integer\n            r:\n              type: integer\n            t:\n              type: integer\n            b:\n              type: integer\n      additionalProperties: true\n\n    PlotlyAxis:\n      type: object\n      description: Plotly axis configuration.\n      properties:\n        title:\n          type: string\n          description: Axis title.\n        type:\n          type: string\n          enum: [\"-\", linear, log, date, category]\n          description: Axis type.\n        autorange:\n          type: boolean\n        range:\n          type: array\n          items:\n            type: number\n          minItems: 2\n          maxItems: 2\n          description: Fixed axis range [min, max].\n        showgrid:\n          type: boolean\n        zeroline:\n          type: boolean\n        showline:\n          type: boolean\n        tickvals:\n          type: array\n          items: {}\n          description: Custom tick positions.\n        ticktext:\n          type: array\n          items:\n            type: string\n          description: Custom tick labels.\n      additionalProperties: true\n\n    WindowOptions:\n      type: object\n      description: >\n        Common window options that apply across visualization types. These\n        are stored as top-level properties of the window object.\n      properties:\n        title:\n          type: string\n          description: Window title displayed in the pane header.\n        width:\n          type:\n            - integer\n            - \"null\"\n          description: Window width in pixels.\n        height:\n          type:\n            - integer\n            - \"null\"\n          description: Window height in pixels.\n        inflate:\n          type: boolean\n          default: true\n          description: Whether the window content inflates to fill the pane.\n        show_slider:\n          type: boolean\n          default: true\n          description: Whether to show the image slider (image_history only).\n        caption:\n          type:\n            - string\n            - \"null\"\n          description: Caption text (for image, audio, and video panes).\n        jpgquality:\n          type: integer\n          description: >\n            JPEG quality (0-100). If set, images are sent as JPEG\n            instead of PNG.\n        store_history:\n          type: boolean\n          description: >\n            If true, images are stored as image_history type with a\n            slider for browsing history.\n        legend:\n          type: array\n          items:\n            type: string\n          description: Legend names for plot traces.\n        markersymbol:\n          type: string\n          default: dot\n          description: Marker symbol for scatter plots.\n        markersize:\n          type: number\n          default: 10\n          description: Marker size for scatter plots.\n        markercolor:\n          description: >\n            Marker colors as numpy array. Shape depends on number of\n            traces and labels.\n        markerborderwidth:\n          type: number\n          default: 0.5\n          description: Marker border line width.\n        dash:\n          description: >\n            Dash patterns for each trace (e.g., `\"solid\"`, `\"dash\"`,\n            `\"dot\"`).\n        linecolor:\n          description: Line colors for each trace.\n        textlabels:\n          type:\n            - array\n            - \"null\"\n          items:\n            type: string\n          description: Text labels for each point in scatter plots.\n        mode:\n          type: string\n          description: >\n            Plotly trace mode override (e.g., `\"markers\"`, `\"lines\"`,\n            `\"lines+markers\"`).\n        webgl:\n          type: boolean\n          description: >\n            If true, use `scattergl` (WebGL) instead of `scatter`\n            (SVG) for better performance with large datasets.\n        fillarea:\n          type: boolean\n          description: If true, fill the area under lines.\n        columnnames:\n          type: array\n          items:\n            type: string\n          description: Column names for heatmap X axis.\n        rownames:\n          type: array\n          items:\n            type: string\n          description: Row names for heatmap Y axis.\n        colormap:\n          type: string\n          description: >\n            Plotly colorscale name for heatmaps (e.g., `\"Viridis\"`,\n            `\"Electric\"`).\n        directed:\n          type: boolean\n          default: false\n          description: Whether the network graph is directed.\n        showEdgeLabels:\n          type: string\n          enum: [hover, all]\n          default: hover\n          description: When to show edge labels.\n        showVertexLabels:\n          type: string\n          enum: [hover, all]\n          default: hover\n          description: When to show vertex labels.\n        sample_frequency:\n          type: integer\n          default: 44100\n          description: Audio sample frequency in Hz.\n        videowidth:\n          type: integer\n          description: Video player width.\n        videoheight:\n          type: integer\n          description: Video player height.\n        fps:\n          type: integer\n          default: 25\n          description: Frames per second for tensor-based video.\n        traceopts:\n          type: object\n          description: >\n            Per-trace Plotly options keyed by trace name under a\n            `\"plotly\"` sub-object.\n          properties:\n            plotly:\n              type: object\n              additionalProperties:\n                type: object\n      additionalProperties: true\n\n    WindowObject:\n      type: object\n      description: >\n        The complete window data structure as stored on the server and sent\n        to clients via WebSocket broadcasts.\n      required: [command, id, type, content, contentID]\n      properties:\n        command:\n          type: string\n          const: window\n          description: Always `\"window\"` for window objects.\n        version:\n          type: integer\n          default: 1\n          description: >\n            Window version counter. Incremented on each update.\n        id:\n          type: string\n          description: Unique window identifier.\n        title:\n          type: string\n          description: Window title.\n        inflate:\n          type: boolean\n          description: Whether content inflates to fill the pane.\n        width:\n          type:\n            - integer\n            - \"null\"\n          description: Window width in pixels.\n        height:\n          type:\n            - integer\n            - \"null\"\n          description: Window height in pixels.\n        contentID:\n          type: string\n          description: >\n            Random string that changes on every update, used by clients\n            to detect changes.\n        type:\n          type: string\n          enum: [plot, image, image_history, image_compare, text, properties, network, embeddings]\n          description: Window type.\n        content:\n          description: >\n            Window content. Shape depends on `type`:\n            - `plot`: `{ \"data\": [...traces], \"layout\": {...} }`\n            - `image`: `{ \"src\": \"data:...\", \"caption\": \"...\" }`\n            - `image_history`: array of image content objects\n            - `image_compare`: array of image content objects\n            - `text`: HTML string\n            - `properties`: array of property items\n            - `network`: `{ \"nodes\": [...], \"edges\": [...] }`\n            - `embeddings`: `{ \"data\": [[x,y],...], \"selected\": null, \"has_previous\": false }`\n        i:\n          type: integer\n          description: Position index in the environment layout.\n        selected:\n          type: integer\n          description: Currently selected image index (image_history only).\n        show_slider:\n          type: boolean\n          description: Whether to show the slider (image_history only).\n        directed:\n          type: boolean\n          description: Whether the graph is directed (network only).\n        showEdgeLabels:\n          type: string\n          enum: [hover, all]\n          description: Edge label visibility (network only).\n        showVertexLabels:\n          type: string\n          enum: [hover, all]\n          description: Vertex label visibility (network only).\n        old_content:\n          type: array\n          description: >\n            Stack of previous embeddings data for zoom-out\n            (embeddings only).\n        has_compare:\n          type: boolean\n          description: >\n            Whether this window is part of an environment comparison\n            (set by `/compare`).\n"}