solvi.charts¶
Verified charts (preview): a text with numbers → an SVG in which every number is quoted from the text; the proposers, the checker, the renderer.
Verified charts: a text with numbers → a chart in which every number is quoted from the text.
from solvi.charts import chart, RuleProposer, LLMProposer
run = chart(text, "revenue by region") # the rule-based proposer by default
run = chart(text, proposer=LLMProposer("http://127.0.0.1:8080/v1", "qwen2.5-7b-instruct"))
open("chart.svg", "w").write(run.output) # None when nothing verified
print(run.report()) # kept / dropped / changed, and why
The first specialist of solvi.specialist: a proposer writes a ChartSpec (a chart type, series of labelled values,
each value with its quote in the text, a unit, a scale, a title); the checker verifies every value — the quote is in
the text, it holds that number (thousands separators, decimals, "4.2 billion", "15%", "1 500 000 руб" are read; an
ambiguous "1.000" or "3 100" is not), with the chart's unit (percent is not percentage points, dollars are not euros,
"480 employees" is not 480 "stores") and scale — and the chart type against the data (a pie only for shares that add up
to 100% or to a stated total, a line needs two points, one unit per chart). A value that does not verify is not drawn:
the report says what was dropped and why, and the chart marks the gap. The renderer draws a deterministic, accessible
SVG from the verified values only, and the trace replays: the same checks, the same bytes.
ChartChecker ¶
Checks a ChartSpec against its source. decimal: "." or "," when the source's locale says which one is the decimal separator (else "1.000" is ambiguous and dropped).
FixedProposer ¶
Returns the spec it was given (a ChartSpec, a dict or JSON): a stand-in for a model in tests and examples.
LLMProposer ¶
LLMProposer(base_url, model, api_key=None, *, timeout=60.0, max_tokens=1500, json_mode=True, opener=None, retries=2, backoff=1.0, sleep=None)
A ChartSpec from any OpenAI-compatible POST {base_url}/chat/completions, over the shared client of
solvi.remote (retries with backoff; a wrong key, model or URL raises solvi.llm.LLMError; no answer raises
solvi.remote.NoAnswer). The API key is sent in the Authorization header only, never recorded. opener: a replacement
for urllib's urlopen (tests).
RuleProposer ¶
A rule-based proposer: every readable number of the chosen unit, labelled with the words before it in its clause.
unit: the unit to chart ("%", "USD", "employees" …); None: the unit most numbers share (with a question, the one whose sentences share most words with it). kind: "bar" / "line" / "pie"; None: pie for shares that add up to 100%, line for time labels (years, quarters, months), bars otherwise. A number whose label says "total" becomes the spec's total.
Drawing
dataclass
¶
Drawing(svg: str, width: int, height: float, texts: list = list(), fonts: set = set(), notes: list = list())
The SVG and what the layout solver placed: every text box (for the no-overlap and in-canvas checks), the fonts used and notes (a label it could not place: it is in the description).
ChartSpec ¶
Bases: BaseModel
kind: bar | line | pie. unit: "%", a currency ("USD", "$", "EUR", "₽" …), "pp", a word ("tonnes", "users") or "" for plain counts. scale: the values are in thousands / millions / billions of the unit ("" = as is). total: a total the source states for the values (checked like any value; the parts must add up to it).
Point ¶
Bases: BaseModel
One value: a category label, the number (in the chart's scale) and the quote that states it.
SourceQuote ¶
Bases: BaseModel
A passage of the source, copied character for character; start is its offset (optional: without it the
checker looks the passage up, and one that occurs several times — as whole words and numbers — must verify at every
such place, else the point is dropped: give start).
VerifiedChart ¶
Bases: BaseModel
The only input of the renderer: every number in it is in the source at [start, end).
VerifiedPoint ¶
Bases: BaseModel
A value that verified: the number (in the chart's scale), where the source states it and how it is written there.
ChartSpecialist ¶
Bases: Specialist
Text → verified chart (see the module docs). decimal: "." or "," when the text's locale says which one is the decimal separator.
draw ¶
The Drawing (the SVG with its layout: text boxes, fonts, notes) of a check's verified chart.
canon_unit ¶
A unit as written → its canonical form: "USD" / "EUR" / … for currencies, "%" for percent, "pp" for percentage points, otherwise the lower-cased word without a plural "s" ("tonnes" → "tonne"), "" for none.
spec_json_schema ¶
The JSON schema of ChartSpec (for a server that takes a json_schema response format).
render_svg ¶
A VerifiedChart → Drawing (the SVG and its layout). proposed: how many values were proposed (the footer says how many of them verified). A chart the checker never produces — no categories, no verified value, a pie whose values do not add up to more than zero or hold a negative — raises ValueError (it cannot be drawn honestly).
chart ¶
A text (and a question) → Run: .output (the SVG, or None), .report(), .issues, .trace.
ChartSpec: the typed intermediate a proposer writes — a chart type, series of labelled values, and for every value the quote in the source it was read from.
SourceQuote ¶
Bases: BaseModel
A passage of the source, copied character for character; start is its offset (optional: without it the
checker looks the passage up, and one that occurs several times — as whole words and numbers — must verify at every
such place, else the point is dropped: give start).
Point ¶
Bases: BaseModel
One value: a category label, the number (in the chart's scale) and the quote that states it.
ChartSpec ¶
Bases: BaseModel
kind: bar | line | pie. unit: "%", a currency ("USD", "$", "EUR", "₽" …), "pp", a word ("tonnes", "users") or "" for plain counts. scale: the values are in thousands / millions / billions of the unit ("" = as is). total: a total the source states for the values (checked like any value; the parts must add up to it).
VerifiedPoint ¶
Bases: BaseModel
A value that verified: the number (in the chart's scale), where the source states it and how it is written there.
VerifiedChart ¶
Bases: BaseModel
The only input of the renderer: every number in it is in the source at [start, end).
The chart checker: every number of a ChartSpec against the source, then the chart type against the data. Deterministic; what does not verify is dropped or changed with a reason, never repaired by a guess.
Reading
dataclass
¶
Reading(start: int, end: int, as_written: str, value: Decimal | None, unit: str, words: tuple, ambiguous: str = '')
A number as the source states it: its absolute value (scale applied), its unit and where it is.
SourceIndex ¶
Every number candidate of a source, read once.
ChartChecker ¶
Checks a ChartSpec against its source. decimal: "." or "," when the source's locale says which one is the decimal separator (else "1.000" is ambiguous and dropped).
canon_unit ¶
A unit as written → its canonical form: "USD" / "EUR" / … for currencies, "%" for percent, "pp" for percentage points, otherwise the lower-cased word without a plural "s" ("tonnes" → "tonne"), "" for none.
Proposers of a ChartSpec. Any callable (source, question) -> ChartSpec | dict is one; these are three:
RuleProposer — rule-based, no model: numbers of one unit and the words before them as labels (tests, simple texts); LLMProposer — any OpenAI-compatible chat-completions server (standard library HTTP), asked for the spec as JSON; FixedProposer — returns a spec given in advance (a stand-in for a model, a hand-written spec, a recorded proposal).
A proposer is never trusted: the checker verifies every number it writes against the source.
FixedProposer ¶
Returns the spec it was given (a ChartSpec, a dict or JSON): a stand-in for a model in tests and examples.
RuleProposer ¶
A rule-based proposer: every readable number of the chosen unit, labelled with the words before it in its clause.
unit: the unit to chart ("%", "USD", "employees" …); None: the unit most numbers share (with a question, the one whose sentences share most words with it). kind: "bar" / "line" / "pie"; None: pie for shares that add up to 100%, line for time labels (years, quarters, months), bars otherwise. A number whose label says "total" becomes the spec's total.
LLMProposer ¶
LLMProposer(base_url, model, api_key=None, *, timeout=60.0, max_tokens=1500, json_mode=True, opener=None, retries=2, backoff=1.0, sleep=None)
A ChartSpec from any OpenAI-compatible POST {base_url}/chat/completions, over the shared client of
solvi.remote (retries with backoff; a wrong key, model or URL raises solvi.llm.LLMError; no answer raises
solvi.remote.NoAnswer). The API key is sent in the Authorization header only, never recorded. opener: a replacement
for urllib's urlopen (tests).
spec_json_schema ¶
The JSON schema of ChartSpec (for a server that takes a json_schema response format).
The chart renderer: a VerifiedChart → SVG bytes, deterministic (no clock, no randomness, fixed number formatting), no dependencies. Accessible:
Drawing
dataclass
¶
Drawing(svg: str, width: int, height: float, texts: list = list(), fonts: set = set(), notes: list = list())
The SVG and what the layout solver placed: every text box (for the no-overlap and in-canvas checks), the fonts used and notes (a label it could not place: it is in the description).
render_svg ¶
A VerifiedChart → Drawing (the SVG and its layout). proposed: how many values were proposed (the footer says how many of them verified). A chart the checker never produces — no categories, no verified value, a pie whose values do not add up to more than zero or hold a negative — raises ValueError (it cannot be drawn honestly).