From 36e85b006e70dbffe7f220971860f6876b0da2c7 Mon Sep 17 00:00:00 2001 From: jason-zl190 Date: Sat, 18 Jul 2026 14:10:25 +0800 Subject: [PATCH] docs: teach the SemanticAnnotation object form (unit, intrinsicDomain) semantic_types accepts Record, and unit / intrinsicDomain gate the currency and percent axis formatting. The skill, MCP tool schema, and docs only showed the bare-string form, so agents never find the annotation object and charts fall back to plain number formatting (verified against 0.2.2, Vega-Lite backend). - SKILL.md: fix the interface signature, adjust the Price/Percentage bullets, add an 'Annotation objects' section, extend the checklist - flint-mcp schemas.ts: describe the annotation object form - README: 70+ -> 40+ semantic types (registry ships 44) - api-reference.md: note the formatting unit / intrinsicDomain enable - sync the bundled MCP skill asset Co-Authored-By: Claude Fable 5 --- README.md | 6 ++- agent-skills/flint-chart-author/SKILL.md | 45 +++++++++++++++++-- docs/api-reference.md | 5 +++ .../assets/flint-chart-author.SKILL.md | 45 +++++++++++++++++-- packages/flint-mcp/src/tools/schemas.ts | 7 ++- 5 files changed, 97 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 889a0424..fcbec64d 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,10 @@ This repo contains two main components: ## Features -- **Semantic chart specs.** Flint captures what each field means using 70+ - semantic types such as `Rank`, `Temperature`, `Price`, or `Country`. +- **Semantic chart specs.** Flint captures what each field means using 40+ + semantic types such as `Rank`, `Temperature`, `Price`, or `Country`, plus + optional `unit` / `intrinsicDomain` annotations that enable currency and + percent axis formatting. - **Automatic layout.** Flint adapts sizing, spacing, labels, marks, and legends to the data cardinality, chart design, and canvas constraints. - **Multiple backends.** Compile one input to 30+ chart types across diff --git a/agent-skills/flint-chart-author/SKILL.md b/agent-skills/flint-chart-author/SKILL.md index c05dee2d..1f42a34f 100644 --- a/agent-skills/flint-chart-author/SKILL.md +++ b/agent-skills/flint-chart-author/SKILL.md @@ -83,7 +83,7 @@ published, use the npm package or MCP server for released workflows. interface ChartAssemblyInput { // Bound by the HOST or by you, depending on the situation (see below). data: { values: any[] } | { url: string }; - semantic_types?: Record; // field → semantic type ← you write this + semantic_types?: Record; // field → type or annotation ← you write this chart_spec: { // ← you write this chartType: string; // e.g. "Scatter Plot" encodings: Record; // channel → { field, ... } (or array) @@ -323,16 +323,52 @@ more. Pick the most specific type for each field. Full registered set: What choosing well gets you (automatically): -- `Price` / `Amount` → currency formatting, zero baseline, sequential color +- `Price` / `Amount` → zero baseline, sequential color; add `unit` for + currency axis labels (see "Annotation objects" below) - `Temperature` → diverging color scheme, no forced zero baseline - `Correlation` → fixed `[-1, 1]` diverging domain - `Rank` → reversed axis (1 on top), discrete color - `Date` / `DateTime` → temporal axis with auto-granularity formatting -- `Percentage` → percent formatting, 0–100 domain awareness +- `Percentage` → 0–100 domain awareness; add `intrinsicDomain` for percent + axis labels (see "Annotation objects" below) If you don't know, use `Quantity` for numbers, `Category` for strings, `Date`/`DateTime` for date-shaped values. Do **not** invent type names. +### Annotation objects: `unit` and `intrinsicDomain` + +Each `semantic_types` entry is either a bare type string or a +`SemanticAnnotation` object — `"Price"` is shorthand for +`{ "semanticType": "Price" }`: + +```json +"semantic_types": { + "revenue": { "semanticType": "Price", "unit": "USD" }, + "share": { "semanticType": "Percentage", "intrinsicDomain": [0, 1] }, + "rating": { "semanticType": "Score", "intrinsicDomain": [1, 5] }, + "region": "Region" +} +``` + +Without these fields Flint stays conservative: it falls back to plain +number formatting rather than guess a currency or rescale values it cannot +verify. + +| Annotation | Effect | +|---|---| +| `Price` + `unit` with a known currency code (`USD`, `EUR`, `GBP`, `JPY`, `CNY`, …) | Currency symbol on value-axis labels (`$1,200.00`) | +| `Percentage` + `intrinsicDomain: [0, 1]` over fractional data | Percent axis labels (`0.42` → `42%`) and a domain capped at 1 | +| `Percentage` + `intrinsicDomain: [0, 100]` | Domain capped at 100; whole numbers stay readable as-is | +| Bounded type + `intrinsicDomain` (e.g. `Score` `[1, 5]`) | Scale anchored to the intrinsic range instead of the data extent | +| `Temperature` (and other physical measures) + `unit` (`°C`, `kg`, `km`, …) | Unit suffix on tooltip values | + +Axis-label formatting above is fully wired in the Vega-Lite backend today; +other backends may not apply every format decision yet. + +Add `unit` / `intrinsicDomain` when the user states them or the data makes +them unambiguous (a `price_usd` column; shares that sum to 1). If neither +is known, keep the bare string — do not guess. + ## Chart-level properties (`chartProperties`) `chartProperties` is an optional per-chart tuning map. Set a property only @@ -558,7 +594,8 @@ Before returning, verify: 1. `chartType` is an exact registered name supported by the target backend. 2. Every `field` referenced in `encodings` is a real column name. -3. Every encoded field has an entry in `semantic_types` (specific type). +3. Every encoded field has an entry in `semantic_types` (specific type; + add `unit` / `intrinsicDomain` when known — see "Annotation objects"). 4. Required channels for the chart type are present (e.g. Bullet→`goal`, Candlestick→`open/high/low/close`, Pie→`size`+`color`). 5. Any `chartProperties` keys are valid for that chart type and in range. diff --git a/docs/api-reference.md b/docs/api-reference.md index 7eda96b9..917821db 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -47,6 +47,11 @@ interface SemanticAnnotation { Bare string shorthand: `"Price"` is equivalent to `{ semanticType: "Price" }`. +`unit` and `intrinsicDomain` also drive formatting: a known currency `unit` +(`USD`, `EUR`, `CNY`, …) puts the symbol on `Price` axis labels, and +`intrinsicDomain: [0, 1]` renders fractional `Percentage` values as percent +axis labels (`0.42` → `42%`) with a capped domain (Vega-Lite backend today). + --- # §2 Assemblers diff --git a/packages/flint-mcp/assets/flint-chart-author.SKILL.md b/packages/flint-mcp/assets/flint-chart-author.SKILL.md index c05dee2d..1f42a34f 100644 --- a/packages/flint-mcp/assets/flint-chart-author.SKILL.md +++ b/packages/flint-mcp/assets/flint-chart-author.SKILL.md @@ -83,7 +83,7 @@ published, use the npm package or MCP server for released workflows. interface ChartAssemblyInput { // Bound by the HOST or by you, depending on the situation (see below). data: { values: any[] } | { url: string }; - semantic_types?: Record; // field → semantic type ← you write this + semantic_types?: Record; // field → type or annotation ← you write this chart_spec: { // ← you write this chartType: string; // e.g. "Scatter Plot" encodings: Record; // channel → { field, ... } (or array) @@ -323,16 +323,52 @@ more. Pick the most specific type for each field. Full registered set: What choosing well gets you (automatically): -- `Price` / `Amount` → currency formatting, zero baseline, sequential color +- `Price` / `Amount` → zero baseline, sequential color; add `unit` for + currency axis labels (see "Annotation objects" below) - `Temperature` → diverging color scheme, no forced zero baseline - `Correlation` → fixed `[-1, 1]` diverging domain - `Rank` → reversed axis (1 on top), discrete color - `Date` / `DateTime` → temporal axis with auto-granularity formatting -- `Percentage` → percent formatting, 0–100 domain awareness +- `Percentage` → 0–100 domain awareness; add `intrinsicDomain` for percent + axis labels (see "Annotation objects" below) If you don't know, use `Quantity` for numbers, `Category` for strings, `Date`/`DateTime` for date-shaped values. Do **not** invent type names. +### Annotation objects: `unit` and `intrinsicDomain` + +Each `semantic_types` entry is either a bare type string or a +`SemanticAnnotation` object — `"Price"` is shorthand for +`{ "semanticType": "Price" }`: + +```json +"semantic_types": { + "revenue": { "semanticType": "Price", "unit": "USD" }, + "share": { "semanticType": "Percentage", "intrinsicDomain": [0, 1] }, + "rating": { "semanticType": "Score", "intrinsicDomain": [1, 5] }, + "region": "Region" +} +``` + +Without these fields Flint stays conservative: it falls back to plain +number formatting rather than guess a currency or rescale values it cannot +verify. + +| Annotation | Effect | +|---|---| +| `Price` + `unit` with a known currency code (`USD`, `EUR`, `GBP`, `JPY`, `CNY`, …) | Currency symbol on value-axis labels (`$1,200.00`) | +| `Percentage` + `intrinsicDomain: [0, 1]` over fractional data | Percent axis labels (`0.42` → `42%`) and a domain capped at 1 | +| `Percentage` + `intrinsicDomain: [0, 100]` | Domain capped at 100; whole numbers stay readable as-is | +| Bounded type + `intrinsicDomain` (e.g. `Score` `[1, 5]`) | Scale anchored to the intrinsic range instead of the data extent | +| `Temperature` (and other physical measures) + `unit` (`°C`, `kg`, `km`, …) | Unit suffix on tooltip values | + +Axis-label formatting above is fully wired in the Vega-Lite backend today; +other backends may not apply every format decision yet. + +Add `unit` / `intrinsicDomain` when the user states them or the data makes +them unambiguous (a `price_usd` column; shares that sum to 1). If neither +is known, keep the bare string — do not guess. + ## Chart-level properties (`chartProperties`) `chartProperties` is an optional per-chart tuning map. Set a property only @@ -558,7 +594,8 @@ Before returning, verify: 1. `chartType` is an exact registered name supported by the target backend. 2. Every `field` referenced in `encodings` is a real column name. -3. Every encoded field has an entry in `semantic_types` (specific type). +3. Every encoded field has an entry in `semantic_types` (specific type; + add `unit` / `intrinsicDomain` when known — see "Annotation objects"). 4. Required channels for the chart type are present (e.g. Bullet→`goal`, Candlestick→`open/high/low/close`, Pie→`size`+`color`). 5. Any `chartProperties` keys are valid for that chart type and in range. diff --git a/packages/flint-mcp/src/tools/schemas.ts b/packages/flint-mcp/src/tools/schemas.ts index de8176f1..67e439ab 100644 --- a/packages/flint-mcp/src/tools/schemas.ts +++ b/packages/flint-mcp/src/tools/schemas.ts @@ -88,7 +88,12 @@ export function buildAssemblyInputShape(disableFileReference = false) { semantic_types: z .record(z.string(), z.any()) .optional() - .describe('Field name → semantic type, e.g. { revenue: "Quantity", country: "Country" }.'), + .describe( + 'Field name → semantic type, e.g. { revenue: "Quantity", country: "Country" }. ' + + 'An entry may also be an annotation object { semanticType, unit, intrinsicDomain }: ' + + '{ revenue: { semanticType: "Price", unit: "USD" } } adds a currency symbol to axis labels; ' + + '{ share: { semanticType: "Percentage", intrinsicDomain: [0, 1] } } formats values as percentages.', + ), chart_spec: chartSpecSchema, options: z .record(z.string(), z.any())