csv-charts-ai
v1.14.1
Published
AI-powered CSV analysis, chart generation, and interactive visualization. Parse CSV, detect anomalies, suggest charts, and ask questions about your data using any LLM provider.
Maintainers
Readme
csv-charts-ai
AI-powered CSV analysis, chart generation, and interactive visualization. Built on the Vercel AI SDK and Recharts.
Works with any LLM provider — OpenAI, Anthropic, Google, Mistral, Ollama, or any OpenAI-compatible endpoint.
Installation
pnpm add csv-charts-aiThen install only the AI provider(s) you need:
# Pick one or more
pnpm add @ai-sdk/openai # OpenAI / OpenAI-compatible (Ollama, vLLM, LM Studio…)
pnpm add @ai-sdk/anthropic # Anthropic
pnpm add @ai-sdk/google # Google Generative AI
pnpm add @ai-sdk/mistral # MistralCore dependencies (ai, zod, read-excel-file) are bundled. AI provider SDKs are optional peer dependencies — you only install what you use.
Optional peer dependencies (only for React chart components): react, recharts, lucide-react.
Quick Start
import { registerProvider, fromSDK, parseCSV, analyzeData, suggestQuestions } from "csv-charts-ai";
import { createOpenAI } from "@ai-sdk/openai";
// 1. Register your provider(s) — once, at app startup
registerProvider("openai", fromSDK(createOpenAI));
// 2. Parse CSV string into structured data
const data = parseCSV(`name,age,city,salary
Alice,30,Paris,75000
Bob,25,London,62000
Charlie,35,Berlin,88000`);
// 3. Run full AI analysis (summary + anomalies + charts) in parallel
const result = await analyzeData({
model: { apiKey: "sk-...", model: "gpt-4o" },
data,
});
console.log(result.summary.keyInsights);
console.log(`Found ${result.anomalies.length} anomalies`);
console.log(`Generated ${result.charts.length} chart suggestions`);
// 4. Suggest questions the user could ask
const questions = await suggestQuestions({
model: { apiKey: "sk-...", model: "gpt-4o" },
data,
});
questions.forEach(q => console.log(`[${q.category}] ${q.question}`));Provider Setup
Register AI providers once at app startup, before calling any AI function. You only install and register the providers you need.
Using @ai-sdk/* packages
The fromSDK() helper wraps any @ai-sdk/* creator into the format the library expects:
import { registerProvider, fromSDK } from "csv-charts-ai";
import { createOpenAI } from "@ai-sdk/openai";
import { createAnthropic } from "@ai-sdk/anthropic";
import { createGoogleGenerativeAI } from "@ai-sdk/google";
import { createMistral } from "@ai-sdk/mistral";
registerProvider("openai", fromSDK(createOpenAI));
registerProvider("anthropic", fromSDK(createAnthropic));
registerProvider("google", fromSDK(createGoogleGenerativeAI));
registerProvider("mistral", fromSDK(createMistral));Custom / self-hosted providers
For full control, pass a ProviderFactory directly — a function that receives { apiKey, model, baseURL?, headers? } and returns a LanguageModel:
import { registerProvider } from "csv-charts-ai";
registerProvider("my-llm", (config) => {
return myCustomSDK.createModel(config.apiKey, config.model);
});Batch registration
import { registerProviders, fromSDK } from "csv-charts-ai";
import { createOpenAI } from "@ai-sdk/openai";
import { createAnthropic } from "@ai-sdk/anthropic";
registerProviders({
openai: fromSDK(createOpenAI),
anthropic: fromSDK(createAnthropic),
});Aliases
npm package names are resolved automatically — "@ai-sdk/openai" and "openai" map to the same slot. So createModel({ provider: "openai" }) and createAppModel({ providerNpm: "@ai-sdk/openai" }) both work after a single registration.
CSV Parsing
Parse CSV strings into the TabularData format with automatic delimiter detection and column type inference.
import { parseCSV } from "csv-charts-ai";
// Auto-detects delimiter (comma, semicolon, tab, pipe)
const data = parseCSV(csvString);
// Explicit options
const data = parseCSV(csvString, {
delimiter: ";",
hasHeader: true,
skipEmpty: true,
});
console.log(data.headers); // ["name", "age", "city"]
console.log(data.rowCount); // 100
console.log(data.columns); // [{ name: "name", type: "string", index: 0 }, ...]Type inference detects string, number, date, and boolean columns by sampling values. Handles quoted fields, escaped quotes, multi-line values, and BOM stripping (RFC 4180).
For very large files or exotic encodings, consider using PapaParse and passing the result as
TabularDatadirectly.
XLSX Parsing
Parse Excel (.xlsx) files into the same TabularData format. read-excel-file is bundled.
Browser
import { parseXLSX } from "csv-charts-ai";
const data = await parseXLSX(file); // File from <input> or drag-and-drop
console.log(data.headers, data.rowCount);Node.js / Universal
Use convertXLSXRows with any XLSX reader — it takes raw row arrays and has zero dependencies:
import readXlsxFile from "read-excel-file/node";
import { convertXLSXRows } from "csv-charts-ai";
const rows = await readXlsxFile("data.xlsx");
const data = convertXLSXRows(rows);Options: { hasHeader?: boolean, skipEmpty?: boolean } — same defaults as parseCSV.
AI Functions
All AI functions accept either a simple config object or a pre-built LanguageModel from the Vercel AI SDK. All support an optional signal (AbortSignal) for cancellation.
Chart Suggestions
import { suggestCharts } from "csv-charts-ai";
// Simple — OpenAI (provider must be registered, see Provider Setup)
const charts = await suggestCharts({
model: { apiKey: "sk-...", model: "gpt-4o" },
data,
});
// Custom endpoint — Ollama / vLLM / LM Studio (uses the "openai" provider)
const charts = await suggestCharts({
model: { apiKey: "", model: "llama3", baseURL: "http://localhost:11434/v1" },
data,
});
// Other providers — Anthropic, Google, Mistral
const charts = await suggestCharts({
model: { apiKey: "sk-ant-...", model: "claude-sonnet-4-20250514", provider: "anthropic" },
data,
});
// Advanced — any LanguageModel instance (no registration needed)
import { createAnthropic } from "@ai-sdk/anthropic";
const anthropic = createAnthropic({ apiKey: "sk-ant-..." });
const charts = await suggestCharts({
model: anthropic("claude-sonnet-4-20250514"),
data,
language: "French",
});Custom Chart from Prompt
import { suggestCustomChart } from "csv-charts-ai";
const chart = await suggestCustomChart({
model: { apiKey: "sk-...", model: "gpt-4o" },
data,
prompt: "Show me a bar chart of revenue by category",
});Repair a Broken Chart
import { repairChart } from "csv-charts-ai";
const fixed = await repairChart({
model: { apiKey: "sk-...", model: "gpt-4o" },
failedChart: brokenChart,
columns: ["name", "sales", "date"],
errorContext: "Column 'revenue' does not exist",
});Data Summary
import { summarizeData } from "csv-charts-ai";
const result = await summarizeData({
model: { apiKey: "sk-...", model: "gpt-4o" },
data,
});
console.log(result.summary); // "This dataset contains sales records..."
console.log(result.keyInsights); // ["Revenue peaks in Q4", "Product A leads..."]
console.log(result.dataQuality); // "Good completeness, 2 missing values in..."Anomaly Detection
import { detectAnomalies } from "csv-charts-ai";
const anomalies = await detectAnomalies({
model: { apiKey: "sk-...", model: "gpt-4o" },
data,
maxRows: 100, // default: 50
});
anomalies.forEach(a =>
console.log(`[${a.severity}] Row ${a.row}, ${a.column}: ${a.issue}`)
);Ask Questions About Data
import { askAboutData, streamAskAboutData } from "csv-charts-ai";
// Non-streaming
const answer = await askAboutData({
model: { apiKey: "sk-...", model: "gpt-4o" },
data,
question: "What is the average revenue by region?",
history: [{ prompt: "How many rows?", response: "There are 1000 rows." }],
});
// Streaming
await streamAskAboutData({
model: { apiKey: "sk-...", model: "gpt-4o" },
data,
question: "What trends do you see?",
onChunk: (chunk) => process.stdout.write(chunk),
onComplete: (full) => console.log("\nDone:", full.length, "chars"),
});Suggest Questions
import { suggestQuestions } from "csv-charts-ai";
const questions = await suggestQuestions({
model: { apiKey: "sk-...", model: "gpt-4o" },
data,
count: 5,
});
questions.forEach(q => console.log(`[${q.category}] ${q.question}`));
// [trend] How has revenue changed month over month?
// [comparison] Which region has the highest average order value?
// [correlation] Is there a relationship between marketing spend and sales?Full Analysis Pipeline
Runs summary, anomaly detection, and chart suggestions in parallel:
import { analyzeData } from "csv-charts-ai";
const result = await analyzeData({
model: { apiKey: "sk-...", model: "gpt-4o" },
data,
detectAnomalies: true, // default: true
suggestCharts: true, // default: true
});
// result.summary — DataSummaryResult
// result.anomalies — AnomalyResult[]
// result.charts — ChartConfig[]Cancellation (AbortSignal)
All AI functions support signal for cancellation — essential for React cleanup and timeouts:
const controller = new AbortController();
const charts = await suggestCharts({
model: { apiKey: "sk-...", model: "gpt-4o" },
data,
signal: controller.signal,
});
// Cancel from elsewhere
controller.abort();React example:
useEffect(() => {
const controller = new AbortController();
analyzeData({ model, data, signal: controller.signal })
.then(setResult)
.catch(() => {});
return () => controller.abort();
}, [data]);React Components (csv-charts-ai/charts)
Chart components are available as a separate entry point so projects that only need AI/parsing don't pull in React or Recharts.
# Optional peer dependencies — only needed if you import from csv-charts-ai/charts
pnpm add react rechartsIcons are built-in (SVG, zero dependency). To use your own icon library, see Custom Icons below.
Display Charts
import { ChartDisplay } from "csv-charts-ai/charts";
<ChartDisplay data={data} charts={charts} />With Theme
import { ChartDisplay, defaultLightTheme } from "csv-charts-ai/charts";
<ChartDisplay data={data} charts={charts} theme={defaultLightTheme} />Custom Card Wrapper
import { ChartDisplay } from "csv-charts-ai/charts";
import { repairChart } from "csv-charts-ai";
<ChartDisplay
data={data}
charts={charts}
cardWrapper={({ children, title }) => (
<div className="my-card">
<h2>{title}</h2>
{children}
</div>
)}
onRegenerate={async (chart) => {
const fixed = await repairChart({ model, failedChart: chart, columns: data.headers, errorContext: "Rendering failed" });
// update state with fixed chart
}}
/>Unstyled Mode (No Tailwind Required)
Pass unstyled to strip all built-in Tailwind classes and style components yourself:
<ChartDisplay
data={data}
charts={charts}
unstyled
className="my-charts-container"
chartClassName="my-chart-card"
titleClassName="my-chart-title"
/>You can also pass className to any component to add classes alongside the built-in ones:
<ChartDisplay data={data} charts={charts} className="my-extra-class" />Custom Icons
Chart toolbar icons are built-in as minimal SVGs (zero dependency). To use your own icon library (lucide-react, heroicons, phosphor, etc.), wrap your charts with ChartIconProvider:
import { ChartDisplay, ChartIconProvider } from "csv-charts-ai/charts";
import { RefreshCw, Download, ArrowUp, ArrowDown, RotateCcw, TrendingUp, Filter, Image } from "lucide-react";
const icons = { RefreshCw, Download, SortAsc: ArrowUp, SortDesc: ArrowDown, RotateCcw, TrendingUp, Filter, ImageIcon: Image };
<ChartIconProvider icons={icons}>
<ChartDisplay data={data} charts={charts} />
</ChartIconProvider>You only need to override the icons you want to change — the rest fall back to the built-in SVGs.
Headless Usage (No React)
The core entry point (csv-charts-ai) works without React — use it in Node.js scripts, APIs, or CLI tools:
import { registerProvider, fromSDK, parseCSV, analyzeData } from "csv-charts-ai";
import { createOpenAI } from "@ai-sdk/openai";
import { readFileSync } from "fs";
registerProvider("openai", fromSDK(createOpenAI));
const csv = readFileSync("sales.csv", "utf-8");
const data = parseCSV(csv);
const result = await analyzeData({
model: { apiKey: process.env.OPENAI_API_KEY!, model: "gpt-4o" },
data,
});
console.log(result.summary.keyInsights);Components Reference (csv-charts-ai/charts)
| Export | Description |
|--------|-------------|
| ChartDisplay | Multi-chart container with optional card wrapper and theme |
| SingleChart | Individual chart with toolbar (sort, zoom, trendline, CSV/PNG export) |
| ChartToolbar | Standalone toolbar component |
| ChartThemeProvider | React context for chart theming |
| ChartIconProvider | React context for pluggable icons (default: built-in SVGs) |
| defaultDarkTheme / defaultLightTheme | Built-in themes |
| defaultIcons | Built-in SVG icon set |
AI Functions Reference
| Export | Description |
|--------|-------------|
| suggestCharts(options) | Generate 2-4 chart suggestions from data |
| suggestCustomChart(options) | Generate a single chart from a text prompt |
| repairChart(options) | Fix a chart config that failed to render |
| summarizeData(options) | AI-generated data summary with key insights |
| detectAnomalies(options) | Find outliers, missing values, type mismatches |
| askAboutData(options) | Ask natural language questions about data |
| streamAskAboutData(options) | Streaming version of askAboutData |
| suggestQuestions(options) | Suggest interesting questions to ask about the data |
| analyzeData(options) | Full pipeline: summary + anomalies + charts in parallel |
Provider Registry Reference
| Export | Description |
|--------|-------------|
| registerProvider(name, factory) | Register a provider by name |
| registerProviders(map) | Register multiple providers at once |
| fromSDK(creator) | Wrap an @ai-sdk/* creator into a ProviderFactory |
| getProvider(name) | Get a registered provider (or undefined) |
| hasProvider(name) | Check if a provider is registered |
| clearProviders() | Remove all registered providers (for testing) |
Utilities Reference
| Export | Description |
|--------|-------------|
| parseCSV(csv, options?) | Parse CSV string into TabularData |
| parseXLSX(file, options?) | Parse XLSX file into TabularData (browser) |
| convertXLSXRows(rows, options?) | Convert raw XLSX rows into TabularData (universal) |
| computeDiff(dataA, dataB, options) | Compare two datasets (index, key, or content matching) |
| createModel(config) | Create a LanguageModel from an AIConfig |
| createAppModel(config) | Create a LanguageModel from multi-provider app config |
| resolveModel(input) | Resolve AIConfig, AppModelConfig, or LanguageModel |
| generateDataSummary(data) | Detailed human-readable data summary with sample rows |
| summarizeTabularData(data) | Compact data summary for AI prompt consumption |
| getAIErrorMessage(error) | Extract user-friendly error messages |
| processChartData(data, chart) | Process and aggregate chart data |
| processChartDataMultiSeries(data, chart) | Multi-series data processing |
| COLORS | Default 8-color palette |
Validation Schemas
| Export | Description |
|--------|-------------|
| AIConfigSchema | Zod schema for validating AI config objects |
| TabularDataSchema | Zod schema for validating TabularData input |
Chart Types
bar | line | area | scatter | pie
Multi-series supported via groupBy for bar, line, and area charts.
Aggregation Types
sum | avg | count | min | max | none
CSV Diff
Compare two datasets and detect added, removed, and changed rows:
import { computeDiff } from "csv-charts-ai";
const diff = computeDiff(dataA, dataB, { matchMode: "key", keyColumn: "id" });
console.log(diff.counts); // { same: 10, changed: 3, added: 2, removed: 1 }
diff.rows.forEach(r => {
if (r.status === "changed") {
console.log(`Row ${r.indexA}: changed columns: ${[...r.changedCols].join(", ")}`);
}
});Match modes: "index" (positional), "key" (by column value), "content" (full row match).
Provider Support
Install only the provider(s) you need and register them at startup (see Provider Setup).
| Provider | Install | Config |
|----------|---------|--------|
| OpenAI | @ai-sdk/openai | { apiKey, model } |
| Anthropic | @ai-sdk/anthropic | { provider: "anthropic", apiKey, model } |
| Google | @ai-sdk/google | { provider: "google", apiKey, model } |
| Mistral | @ai-sdk/mistral | { provider: "mistral", apiKey, model } |
| Ollama / vLLM / LM Studio | @ai-sdk/openai | { apiKey: "", model, baseURL } |
| Custom provider | — | registerProvider("name", factory) |
| Any LanguageModel | — | Pass instance directly |
License
MIT
