Files
semantica/explorer
Alex Smolya d3183d0ab3 feat(explorer): add deterministic rendering E2E example and test (#1037) (#1041)
feat(explorer): add deterministic rendering E2E example and test (#1037)

Adds a deterministic Explorer graph baseline and coverage for the full
build -> persist -> API -> frontend hydration -> canvas rendering path,
so a regression anywhere along that chain shows up in CI instead manually.

examples/explorer_deterministic_rendering_example.py builds the
canonical 4-node, 3-edge graph (Alice -WORKS_AT-> Acme, Bob -KNOWS->
Alice, Acme -LOCATED_IN-> New York) with ContextGraph.add_node()/
add_edge(), persists it with save_to_file() and reloads it with
GraphSession.from_file(), printing the setup prerequisites and the
expected node/edge/label checklist for anyone running it by hand.

tests/explorer/test_explorer_deterministic_rendering_e2e.py covers
graph construction, the serialize/deserialize round trip, GraphSession
loading, and the Explorer API's /api/graph/* responses against the
exact expected nodes, edges, and labels, plus all three auth modes
(unconfigured, API-key required, anonymous opt-in).

fix(explorer): address Qodo review findings for deterministic rendering e2e (#1037)

- configure SEMANTICA_ALLOW_ANONYMOUS=true and document
  SEMANTICA_API_KEY as the alternative in the reproduction
  instructions, so the documented commands don't 503 on a clean
  checkout
- add clean-checkout prerequisites and a visual verification
  checklist to the example
- add edge-label (WORKS_AT, KNOWS, LOCATED_IN), zoom-tier, and
  hover-interaction coverage to the frontend test
- add an explicit auth-enforcement integration test for the
  deterministic graph endpoints

fix(explorer): connect deterministic rendering E2E path

The frontend test built its own node/edge objects directly with
batchMergeNodes()/batchMergeEdges(), bypassing the real loading path
entirely -- it never went through useLoadGraph, never mounted the
canvas, and its fixture didn't even carry the same fields the backend
actually returns (e.g. no color values), so a break in API hydration,
the edge.type -> edgeType mapping, or canvas label rendering could
still pass.

Adds deterministicExplorerRendering.e2e.ts, which mounts the real
Explorer app in Chromium, serves API-shaped /api/graph/nodes and
/api/graph/edges responses through route interception, drives the
app through its actual useLoadGraph hydration path into a real Sigma
canvas, and asserts on captured canvas fillText() calls that
WORKS_AT, KNOWS, and LOCATED_IN are genuinely drawn, both after load
and after Zoom In.

fix(explorer): preserve upstream markdown dependencies
ci(explorer): isolate deterministic backend test dependencies

Wires the new Python test into ci.yml as its own focused step (it
previously only ran manually), installs Playwright's Chromium
browser before the frontend suite, and keeps the deterministic
backend test's dependency install separate from the rest of the
pipeline so it doesn't pull in unrelated optional extras during
collection.

fix(explorer): remove redundant edge label hydration

An earlier commit in this PR added an explicit `label` field to
hydrated edge attributes on the theory that it was needed for edge
labels to render. Review traced through GraphCanvas.tsx's label
resolution (`attrs.edgeType || data.label || ""`, from the earlier
#1009 fix already on main) and found that `edgeType` is set
unconditionally on every edge during hydration, so it always wins the
`||` before `data.label` is ever consulted -- the added field and its
plumbing in useLoadGraph.ts and graphStore.ts never did anything.
Removed both; reran the real Chromium E2E test against the reverted
code and confirmed all three labels still render identically, closing
out the question of whether anything else was actually broken.
2026-08-29 12:25:58 +05:00
..

Semantica Knowledge Explorer

A browser-based graph workbench for the Semantica platform. Pan and zoom live graphs, scrub the timeline, trace every decision's causal chain, resolve duplicates, and author your ontology visually. Built on React 19 + Sigma.js.


Requirements

Dependency Minimum version
Python 3.8+
Node.js 18.x or higher (20.x recommended)
npm 9.x or higher
python --version
node --version
npm --version

Two ways to run the Explorer

Install the package with the explorer extras. The pre-built frontend bundle is included in the wheel so no Node.js is required.

pip install "semantica[explorer]"

Launch the dashboard by pointing it at any graph JSON file:

semantica-explorer --graph my_graph.json

The server starts at http://127.0.0.1:8000 and opens the dashboard in your default browser automatically.

CLI flags:

Flag Default Description
--graph / -g (required) Path to a ContextGraph JSON file
--port / -p 8000 Port to bind the server to
--host 127.0.0.1 Host to bind (use 127.0.0.1 for local-only; see security note below)
--no-browser off Skip opening the browser automatically

Examples:

# Default — opens at http://127.0.0.1:8000
semantica-explorer --graph my_graph.json

# Custom port
semantica-explorer --graph my_graph.json --port 8080

# Suppress auto-open
semantica-explorer --graph my_graph.json --no-browser

# Equivalent using python -m
python -m semantica.explorer --graph my_graph.json

Security note: Since v0.6.5 the Explorer API requires an API key on protected routes. Set the SEMANTICA_API_KEY environment variable and send it as the X-API-Key header; without a configured key, protected routes fail closed with 503 rather than serving anonymously. To opt into unauthenticated access for local development only, set SEMANTICA_ALLOW_ANONYMOUS=true explicitly. (/api/health and /api/info are intentionally unauthenticated.)

The default --host 127.0.0.1 binds to localhost only, so it is not reachable from other machines on your network. If you bind to 0.0.0.0, all graph data is readable and writable by any host that can reach the port (subject to API-key auth). The CLI prints a warning when binding to a non-loopback host in anonymous mode or when SEMANTICA_API_KEY is unset.


Option B — run from source (for contributors / frontend development)

This mode runs the React dev server with hot module replacement, so frontend changes appear in the browser instantly without rebuilding.

Step 1 — Clone the repo

git clone https://github.com/semantica-agi/semantica.git
cd semantica

Step 2 — Install the Python package

pip install -e ".[explorer]"

Step 3 — Install frontend dependencies

cd explorer
npm ci

Step 4 — Start the Python backend

Open a terminal in the repo root:

semantica-explorer --graph path/to/my_graph.json --no-browser

This starts the API on http://127.0.0.1:8000. Keep this terminal open.

Step 5 — Start the frontend dev server

Open a second terminal in explorer/:

npm run dev

Vite starts on http://localhost:5173. Open that URL in your browser. All /api and /ws requests are automatically proxied to the Python backend at http://127.0.0.1:8000.


Building the production bundle

If you need to serve the UI from the Python server directly (without the Vite dev server):

cd explorer
npm ci
npm run build

This writes the compiled assets to ../semantica/static/. The Python server then serves the full dashboard at http://127.0.0.1:8000 — no separate Vite process needed.


Workspaces

Workspace What you can do
Knowledge Graph Live Sigma.js canvas · ForceAtlas2 layout · Ego Mode · semantic distance heatmap · path highlighting
Timeline Temporal event scrubber — watch the graph evolve across time
Decisions Browse causal chains behind every recorded decision with outcome badges and confidence scores
Registry Live audit log of every graph mutation (add-node, add-edge, delete, update)
Entity Resolution Review and merge duplicate entities with blocking + semantic dedup
KG Overview Aggregate stats, community breakdown, centrality heatmap
Ontology Hub SHACL Studio · visual drag-and-drop editor · cross-ontology alignments · SKOS browser
Lineage W3C PROV-O provenance visualization for any entity

Environment variables

Variable Default Description
EXPLORER_CORS_ORIGINS http://localhost:5173,http://127.0.0.1:5173 Comma-separated list of allowed CORS origins
EXPLORER_CORS_CREDENTIALS false Set to true to allow credentialed cross-origin requests (only needed behind an authenticating reverse proxy)
SEMANTICA_API_KEY (unset) API key required on protected routes since v0.6.5; send it as the X-API-Key header. When unset, protected routes fail closed with 503.
SEMANTICA_ALLOW_ANONYMOUS false Set to true to opt into unauthenticated access (local development only).

Available scripts

Run these from inside the explorer/ directory:

# Start the dev server with hot module replacement
npm run dev

# Type-check and build the production bundle into ../semantica/static/
npm run build

# Preview the production build locally
npm run preview

# Run ESLint over all source files
npm run lint

# Run the graph store multi-edge unit tests
npm run test:graph-store

# Run the graph workspace display tests
npm run test:graph-workspace

API & WebSocket proxy (dev mode only)

During development, Vite forwards requests automatically — no CORS configuration needed:

Pattern Forwarded to
/api/* http://127.0.0.1:8000/api/*
/ws/* ws://127.0.0.1:8000/ws/*

To run the backend on a different port, update server.proxy in vite.config.ts.


Project structure

explorer/
├── src/
│   ├── App.tsx                        # Root layout, tab routing, workspace wiring
│   ├── index.css                      # Global resets, fonts, keyframe animations
│   ├── store/
│   │   ├── graphStore.ts              # In-memory graph state
│   │   └── registryStore.ts           # Pub/sub audit registry
│   └── workspaces/
│       ├── GraphWorkspace/            # Sigma.js canvas + inspector + behaviors
│       ├── DecisionWorkspace/         # Causal flow diagram + decision list
│       ├── DiffMergeWorkspace/        # Graph diff and merge view
│       ├── EnrichWorkspace/           # Entity resolution + registry tabs
│       ├── ImportExportWorkspace/     # Import CSV/JSON, export graph
│       ├── LineageWorkspace/          # W3C PROV-O lineage diagram
│       ├── ManageWorkspace/           # KG Overview + Ontology Summary
│       ├── OntologyWorkspace/         # SHACL Studio, visual editor, SKOS browser
│       ├── SparqlWorkspace/           # In-browser SPARQL query editor
│       └── VocabularyWorkspace/       # SKOS vocabulary manager
├── index.html
├── vite.config.ts                     # Dev proxy → 127.0.0.1:8000, build → ../semantica/static
└── package.json

Troubleshooting

Dashboard shows a blank white page or "UI not available" message

The frontend bundle is missing from the server's static directory. Fix options:

  • If you installed via pip: pip install --upgrade "semantica[explorer]" — the wheel includes the pre-built bundle.
  • If you installed from source: run cd explorer && npm ci && npm run build from the repo root, then restart the server.
  • In dev mode: use the Vite dev server at http://localhost:5173 instead of the backend URL.

Blank graph / no data loads in the browser

  • Confirm the Python backend is running and check the terminal for errors.
  • Open browser DevTools → Network tab and look for failed /api/graph requests.
  • If the backend is on a different port, update server.proxy in vite.config.ts.

npm ci fails or reports missing lockfile

The package-lock.json must be present. Run npm install once to generate it, commit it, then use npm ci going forward.

npm run dev fails with Node version error

Vite 6 requires Node 18 or higher. Run node --version to check. If you're on Node 16, upgrade via nvm or the official Node.js installer.

Port 5173 already in use

Vite automatically tries the next available port and prints the actual URL in the terminal. Use the URL shown in the output.

WebSocket not connecting (real-time mutations not appearing)

  • Confirm the backend exposes the /ws/graph-updates WebSocket endpoint.
  • Check DevTools → Network → WS tab for the connection status and error code.
  • Ensure the backend version matches the frontend — mixing major versions can cause protocol mismatches.
  • Authentication: /ws/graph-updates enforces the same API key as the REST routes. Browsers cannot set custom headers on a WebSocket handshake, so pass the key as a query parameter instead:
    ws://127.0.0.1:8000/ws/graph-updates?api_key=<your-key>
    
    Non-browser clients (native apps, scripts) may send it as the X-API-Key header. A missing or incorrect key results in close code 4401; if SEMANTICA_API_KEY is unset and SEMANTICA_ALLOW_ANONYMOUS is not true, the connection is also rejected. Note that API keys in URLs appear in server logs — prefer the header for non-browser clients.

Tech stack

  • React 19 + TypeScript (strict mode)
  • Vite 6 with babel-plugin-react-compiler
  • Sigma.js 3 + Graphology — graph rendering and in-memory graph model
  • ForceAtlas2 — physics-based layout
  • @tanstack/react-query — async data fetching for ontology and vocab tabs
  • vis-timeline — temporal event visualization
  • @xyflow/react — lineage diagram rendering
  • Monaco Editor — in-browser SPARQL / SHACL editor
  • lucide-react — icon set

Contributing

See the root CONTRIBUTING.md and open issues on the main Semantica repository.