Schema-first visualization specifications for LLMs
Project description
Vizdantic
Stop LLMs from hallucinating plotting APIs. Let them describe what to visualize instead.
Vizdantic separates the data layer (what LLMs produce) from the view layer (what your code renders).
The Problem
You want an LLM to create charts from data. You have two bad options:
- Let the LLM write plotting code → It hallucinates APIs, mixes incompatible parameters, breaks on library updates
- Hardcode every chart type → Rigid, doesn't scale, defeats the purpose of using an LLM
The actual problem: LLMs are great at understanding intent but terrible at remembering exact function signatures.
The Solution
Vizdantic gives LLMs a stable contract: describe what to visualize, not how to plot it.
# LLM outputs this (validated against schema):
{
"kind": "cartesian",
"chart": "bar",
"x": "month",
"y": "revenue"
}
# Your code renders it however you want:
fig = render(spec, df)
fig = apply_company_theme(fig)
fig.show()
You get:
- Validated LLM output (no hallucinated APIs)
- Full control over rendering (swap Plotly for Matplotlib anytime)
- Type-safe specs (a "flow" chart can't claim
chart="bar")
Is This For You?
Yes, if you:
- Build LLM apps that generate charts
- Want LLMs to pick chart types, not write plotting code
- Need to enforce brand guidelines on LLM-generated visualizations
- Want a stable interface between LLM output and rendering logic
No, if you:
- Just need a plotting library (use Plotly/Matplotlib directly)
- Don't use LLMs for visualization
- Want the LLM to control colors/fonts/styling
Quick Start
pip install vizdantic
from vizdantic import validate
from vizdantic.plugins.plotly import render
import pandas as pd
# 1. LLM produces this JSON
llm_output = {
"kind": "cartesian",
"chart": "bar",
"x": "month",
"y": "revenue"
}
# 2. Validate it
spec = validate(llm_output) # Raises ValidationError if invalid
# 3. Render it
df = pd.DataFrame({"month": ["Jan", "Feb", "Mar"], "revenue": [100, 150, 120]})
fig = render(spec, df)
fig.show()
That's it. The LLM never touches plotting code.
Codex Skill (Optional)
This repo includes a Codex skill for running Vizdantic visualizations locally.
vizdantic install-codex-skill
Or with uv:
uv run vizdantic install-codex-skill
Restart Codex after installing. The skill will appear as vizdantic-runner.
What Charts Are Supported?
Vizdantic supports 14 chart type categories covering ~90% of common use cases:
| Category | Chart Types | Example Use Case |
|---|---|---|
| Cartesian | bar, line, area, scatter | Time series, comparisons |
| Distribution | histogram, box, violin, strip | Statistical analysis |
| Parts | pie | Proportions, market share |
| Geo | choropleth, scatter_geo, mapbox | Geographic data |
| Hierarchy | treemap, sunburst, icicle | Organizational charts |
| Flow | sankey | Process flows, migrations |
| Polar | scatter_polar, line_polar, bar_polar | Directional data, wind roses |
| Ternary | scatter_ternary, line_ternary | 3-component compositions |
| 3D | scatter_3d, line_3d | Spatial data |
| Financial | funnel, funnel_area | Sales funnels, conversions |
| Parallel | parallel_coordinates, parallel_categories | Multi-dimensional data |
| Timeline | timeline | Gantt charts, schedules |
| Matrix | heatmap, imshow | Correlation matrices |
| Points | scatter | Basic scatter plots |
Each category has strongly-typed chart options. The LLM can't create invalid combinations.
How to Use With LLMs
Vizdantic works with any LLM. Two common patterns:
Option 1: Prompt-Based (Universal)
Embed the schema in your prompt:
from vizdantic import schema
prompt = f"""
You are a data visualization assistant.
Return JSON matching this schema:
{schema()}
User data columns: {df.columns.tolist()}
User request: "Show me revenue by month as a bar chart"
"""
The LLM returns JSON. You validate and render it.
Option 2: Tool/Function Calling (Structured)
For LLMs that support tools (OpenAI, Anthropic, etc.):
tool = {
"name": "create_chart",
"description": "Create a data visualization",
"input_schema": schema()
}
The LLM is now constrained to valid output only.
Styling: You're In Control
Vizdantic doesn't touch colors, fonts, or themes. That's intentional.
def company_theme(fig):
fig.update_layout(
template="plotly_dark",
colorway=["#ff0000", "#000000"],
font=dict(family="Inter")
)
return fig
fig = render(spec, df)
fig = company_theme(fig) # Apply your branding
fig.show()
The LLM picks the chart type. You control everything else.
This is why Vizdantic works in production: it never fights your design system.
Plugins
Currently supported:
- Plotly (
vizdantic.plugins.plotly)
Planned:
- Matplotlib
- Altair
- Vega-Lite
Each plugin exposes one function: render(spec, data) → figure
Want a custom plugin? Implement that function. The spec is just a Pydantic model.
Status
- Version: 0.2.2
- Stability: Experimental (breaking changes possible until 1.0)
- Python: 3.10+
- License: MIT
Feedback welcome. This is a real project solving a real problem, not a demo.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file vizdantic-0.2.2.tar.gz.
File metadata
- Download URL: vizdantic-0.2.2.tar.gz
- Upload date:
- Size: 18.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9aa3078bcb31887812a37680f5d4340943b8746e13d412e84d2429ca31a34699
|
|
| MD5 |
c0d66e808162c50b4adc2d0e87793e40
|
|
| BLAKE2b-256 |
7cb992e2ddfa41ee1a83eeac01b3c0854aefb73e36b05c8acbac3c3e7ee35ea1
|
Provenance
The following attestation bundles were made for vizdantic-0.2.2.tar.gz:
Publisher:
release.yaml on ivogarais/vizdantic
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vizdantic-0.2.2.tar.gz -
Subject digest:
9aa3078bcb31887812a37680f5d4340943b8746e13d412e84d2429ca31a34699 - Sigstore transparency entry: 873917775
- Sigstore integration time:
-
Permalink:
ivogarais/vizdantic@1395ebfe305298c5d2d70385e1374cb63725e88a -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/ivogarais
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@1395ebfe305298c5d2d70385e1374cb63725e88a -
Trigger Event:
push
-
Statement type:
File details
Details for the file vizdantic-0.2.2-py3-none-any.whl.
File metadata
- Download URL: vizdantic-0.2.2-py3-none-any.whl
- Upload date:
- Size: 18.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8b521e5795a8f35b0bfd9e53475d6f338934939a10398e8e330c28a5b461c178
|
|
| MD5 |
c172c0d61ac742ea3a905b1158115d5c
|
|
| BLAKE2b-256 |
fa4f5ede890af9a0011613123cf4bd38535d883ce9c7b399baf5e93ccc555d9a
|
Provenance
The following attestation bundles were made for vizdantic-0.2.2-py3-none-any.whl:
Publisher:
release.yaml on ivogarais/vizdantic
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
vizdantic-0.2.2-py3-none-any.whl -
Subject digest:
8b521e5795a8f35b0bfd9e53475d6f338934939a10398e8e330c28a5b461c178 - Sigstore transparency entry: 873917804
- Sigstore integration time:
-
Permalink:
ivogarais/vizdantic@1395ebfe305298c5d2d70385e1374cb63725e88a -
Branch / Tag:
refs/tags/v0.2.2 - Owner: https://github.com/ivogarais
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yaml@1395ebfe305298c5d2d70385e1374cb63725e88a -
Trigger Event:
push
-
Statement type: