Classes
Composable SDK classes for widget flow, reports, log detection, credits, and usage.
Each SDK class wraps a group of DuckViz API endpoints behind a Promise-returning and SSE-streaming interface. All classes share the same constructor options.
DuckvizWidgetFlow
Generate AI chart recommendations from a table schema.
import { DuckvizWidgetFlow } from "@duckviz/sdk";
const widgetFlow = new DuckvizWidgetFlow({ token });
const result = await widgetFlow.invoke({
tables: [
{
tableName: "t_sales",
schema: [
{ column_name: "date", column_type: "DATE" },
{ column_name: "category", column_type: "VARCHAR" },
{ column_name: "amount", column_type: "DOUBLE" },
],
},
],
});
console.log(result.widgets); // RecommendedWidget[]SSE streaming is supported via stream():
for await (const event of widgetFlow.stream(input)) {
switch (event.event) {
case "domain":
console.log("Domain:", event.data.domain);
break;
case "widget":
console.log("Widget:", event.data.title);
break;
case "done":
console.log("Complete");
break;
}
}DuckvizReports
Generate AI-written report sections.
import { DuckvizReports } from "@duckviz/sdk";
const reports = new DuckvizReports({ token });
const result = await reports.invoke({
reportTitle: "Q4 Sales Report",
systemPromptHint: "Professional executive tone",
widgets: [
{ title: "Revenue by Category", type: "bar", description: "...", duckdbQuery: "..." },
],
sections: [
{ key: "summary", title: "Executive Summary", promptHint: "High-level overview", position: "pre" },
{ key: "analysis", title: "Analysis", promptHint: "Detailed analysis", position: "post" },
],
});Parallel SSE streaming (3 concurrent max) via reports.stream(input). Each section emits section-start / section-complete / section-error events.
DuckvizLogFormatDetector
Detect log file format from sample lines.
import { DuckvizLogFormatDetector } from "@duckviz/sdk";
const detector = new DuckvizLogFormatDetector({ token });
const result = await detector.invoke({
lines: [
"2024-01-15 10:30:45 INFO [main] Application started",
"2024-01-15 10:30:46 WARN [db] Connection pool low",
],
});
console.log(result.logType); // "custom"
console.log(result.formatName); // "Custom Application Log"DuckvizCredits
Check credit balance for the token's user.
import { DuckvizCredits } from "@duckviz/sdk";
const credits = new DuckvizCredits({ token });
const { balance, plan } = await credits.balance();DuckvizUsage
Query usage history — one event per AI call with endpoint, credit cost, and running balance.
import { DuckvizUsage } from "@duckviz/sdk";
const usage = new DuckvizUsage({ token });
const { events } = await usage.list({ limit: 50 });
// events: Array<{ id, ts, endpoint, credits, balanceAfter }>Accepts from and to (ISO dates) to filter a window.
Dashboard config validation
validateDashboardConfig(input: unknown): DashboardConfig parses and type-narrows an unknown blob (e.g. JSON from a request body or your DB) into a DashboardConfig. It's fail-fast — the first invalid field throws DashboardConfigError with the path that failed; subsequent fields are not checked.
import {
validateDashboardConfig,
DashboardConfigError,
type DashboardConfig,
} from "@duckviz/sdk";
export async function POST(req: Request) {
const body: unknown = await req.json();
let config: DashboardConfig;
try {
config = validateDashboardConfig(body);
} catch (e) {
if (e instanceof DashboardConfigError) {
// e.message is "DashboardConfig.<path>: <reason>"
// e.path is the dotted path, e.g. "widgets[2].dataKey"
return Response.json(
{ error: "Invalid dashboard config", path: e.path, reason: e.message },
{ status: 400 },
);
}
throw e; // unexpected — re-throw
}
// From here, `config` is a fully-typed DashboardConfig.
await saveToDb(config);
return Response.json({ ok: true });
}What gets validated
widgets— must be an array; each widget must have a non-empty stringid,type,title, anddataKey. Optional fields (description,config,layout) are loosely checked.grid— if present,colsandrowHeightmust be numbers;marginmust be a 2-tuple of numbers.name,description,dataset— optional strings.theme— shape-checked, but token values are passed through without parsing.
It does not validate that dataKey is real SQL or that type matches a known chart string — that's a runtime concern handled by @duckviz/dashboard's validateChartAliases after the query runs.
When to use it
- Before persisting a config you received over the wire (form post, API, file upload)
- Before forwarding a config to
<Dashboard config={...}>from untrusted input - In tests, to make sure your factory functions emit valid configs
For trusted internal configs (e.g. from a typed DashboardConfig constant in your codebase), validation is redundant — TypeScript already covers it.