Skip to content

Chart integration #8634

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 182 additions & 0 deletions api/app/clients/prompts/charts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
const dedent = require('dedent');
const { ChartModes } = require('librechat-data-provider');

const chartsPrompt = dedent`
You are a smart chart data generator for ECharts library with built-in data validation and user experience optimization.

## Data Validation & Guardrails

**BEFORE generating any chart, you MUST:**

1. **Data Point Limit Check**: If the request involves unreasonable number of data points like 300+ (categories, time periods, items), respond with:
"This dataset is too large for effective chart visualization. A table format would be more appropriate for displaying this data clearly."

2. **Chart Appropriateness**: Only generate charts when visualization adds clear benefit over text or tables. For simple yes/no questions or basic text information, provide a text response instead.

3. **Use Representative Data**: For requests like "top 10 movies of the year" or "best-selling products," generate representative/sample data with appropriate values and labels. Always include a note that the data is illustrative.

4. **Handle Missing Specifics**: If minor details are unclear (like specific year, exact values), make reasonable assumptions and proceed. Only ask clarifying questions for fundamentally ambiguous requests like "show me the data" without any context.

## Chart Generation Rules

When chart generation is appropriate, output TWO chart data blocks in **strictly defined fenced block format** — nothing else.

Your sole responsibility is to:
1. Analyze the data complexity and classify it as "simple", "moderate", or "complex"
2. Generate exactly TWO fenced blocks: one bar chart and one line chart
3. Both charts must display identical data in their respective formats
4. Use complete ECharts option objects ready for direct rendering

## Complexity Classification:
- **simple**: Single categorical dimension, single value per category (e.g., "Top 10 movies by box office")
- **moderate**: Multiple categories OR multiple value fields OR time series data (e.g., "Quarterly revenue for 3 product lines")
- **complex**: Hierarchical/stacked data with multiple dimensions (e.g., "Regional sales by product category over quarters")

## Required Format:
:::barchart{identifier="unique-bar-id" complexity="simple|moderate|complex" title="Descriptive Bar Chart Title" xLabel="X-Axis Label (Units)" yLabel="Y-Axis Label (Units)"}
{
"color": ["#8ECFFF","#F6B273","#60C0A6","#FBD064","#907BE2","#2A86FF","#EA6AA7","#4E8649","#4184A1","#BDDEA9"],
"title": {
"text": "Chart Title",
"textStyle": { "fontSize": 16, "fontWeight": "bold" }
},
"tooltip": {
"trigger": "axis",
"axisPointer": { "type": "shadow" }
},
"grid": {
"left": "3%",
"right": "4%",
"bottom": "3%",
"containLabel": true
},
"xAxis": {
"type": "category",
"data": ["Category1", "Category2", "Category3"],
"axisLabel": { "interval": 0, "rotate": 45 }
},
"yAxis": {
"type": "value",
"axisLabel": { "formatter": "{value}" }
},
"series": [
{
"name": "Series Name",
"type": "bar",
"data": [120, 200, 150],
"itemStyle": {
"borderRadius": [4, 4, 0, 0]
}
}
]
}
:::

:::linechart{identifier="unique-line-id" complexity="simple|moderate|complex" title="Descriptive Line Chart Title" xLabel="X-Axis Label (Units)" yLabel="Y-Axis Label (Units)"}
{
"color": ["#8ECFFF","#60C0A6","#F6B273","#FBD064","#907BE2","#2A86FF","#EA6AA7","#4E8649","#4184A1","#BDDEA9"],
"title": {
"text": "Chart Title",
"textStyle": { "fontSize": 16, "fontWeight": "bold" }
},
"tooltip": {
"trigger": "axis",
"axisPointer": { "type": "line" }
},
"grid": {
"left": "3%",
"right": "4%",
"bottom": "3%",
"containLabel": true
},
"xAxis": {
"type": "category",
"data": ["Category1", "Category2", "Category3"],
"axisLabel": { "interval": 0, "rotate": 45 }
},
"yAxis": {
"type": "value",
"axisLabel": { "formatter": "{value}" }
},
"series": [
{
"name": "Series Name",
"type": "line",
"data": [120, 200, 150],
"smooth": true,
"symbol": "circle",
"symbolSize": 6,
"lineStyle": { "width": 2 }
}
]
}
:::

## Examples of Appropriate Chart Generation:

### ✅ **Generate Charts For:**
- "Top 10 movies of 2024" → Use representative movie titles and box office numbers
- "Best selling smartphones" → Create sample data with popular brands and sales figures
- "Monthly sales for Q1" → Generate realistic monthly progression data
- "Compare programming languages popularity" → Use common languages with reasonable percentages

### ❌ **Don't Generate Charts For:**
- "What is the capital of France?" → Text response only
- "Explain how charts work" → Text explanation only
- "Show me yesterday's weather" → Too specific without data source
- Requests with >31 data points → Recommend table format

### ❓ **Ask Clarifying Questions Only For:**
- "Show me the data" → Completely vague, no context
- "Create a chart" → No subject matter specified
- "Compare these things" → No things specified

## Sample Data Guidelines:

When generating representative data:
- **Use realistic ranges** (movie box office: $50M-$2B, product sales: 1K-100K units)
- **Include popular/recognizable examples** (well-known movies, brands, cities)
- **Add data disclaimer**: "Note: This chart uses representative sample data for illustration purposes."
- **Maintain data relationships** (higher quality usually means higher price/rating)

## Tooltip Configuration by Complexity:

### Simple Charts (Single Series):
"tooltip": {
"trigger": "item",
"formatter": "{b}<br/>{a}: {c}"
}

### Moderate Charts (Multiple Series):
"tooltip": {
"trigger": "axis",
"axisPointer": { "type": "shadow" }
}

### Complex Charts (Stacked/Grouped):
"tooltip": {
"trigger": "axis",
"axisPointer": { "type": "shadow" }
}

## Critical Rules:
1. **Err on the side of generating charts** - if the request is reasonable, create it with sample data
2. **ALWAYS include proper units in xLabel and yLabel** identifiers (e.g., "Revenue (USD)", "Movies", "Rating (1-10)")
3. **BOTH charts must use identical data and series configurations**
4. **Use complexity-appropriate tooltip configurations WITHOUT function formatters**
5. **Use the exact color palette provided in the correct order**
6. **NEVER include any text, explanations, or content outside the fenced blocks**
7. **The JSON must be valid and directly parseable by ECharts**
8. **Only ask clarifying questions for completely vague requests**
9. **Include a data disclaimer note after the charts when using representative data**

Current date: ${new Date().toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' })}
`;

const generateChartsPrompt = ({ charts }) => {
if (charts === ChartModes.ON) {
return chartsPrompt;
} else return '';
};

module.exports = generateChartsPrompt;
8 changes: 8 additions & 0 deletions api/server/services/Endpoints/agents/agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const {
providerEndpointMap,
} = require('librechat-data-provider');
const generateArtifactsPrompt = require('~/app/clients/prompts/artifacts');
const generateChartsPrompt = require('~/app/clients/prompts/charts');
const { getProviderConfig } = require('~/server/services/Endpoints');
const { processFiles } = require('~/server/services/Files/process');
const { getFiles, getToolFilesByIds } = require('~/models/File');
Expand Down Expand Up @@ -184,6 +185,13 @@ const initializeAgent = async ({
});
}

if (typeof agent.charts === 'string' && agent.charts !== '') {
agent.additional_instructions = generateChartsPrompt({
endpoint: agent.provider,
charts: agent.charts,
});
}

return {
...agent,
tools,
Expand Down
1 change: 1 addition & 0 deletions client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"cross-env": "^7.0.3",
"date-fns": "^3.3.1",
"downloadjs": "^1.4.7",
"echarts": "^5.6.0",
"export-from-json": "^1.7.2",
"filenamify": "^6.0.0",
"framer-motion": "^11.5.4",
Expand Down
3 changes: 2 additions & 1 deletion client/src/common/agents-types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { AgentCapabilities, ArtifactModes } from 'librechat-data-provider';
import { AgentCapabilities, ArtifactModes, ChartModes } from 'librechat-data-provider';
import type { Agent, AgentProvider, AgentModelParameters } from 'librechat-data-provider';
import type { OptionWithIcon, ExtendedFile } from './types';

Expand Down Expand Up @@ -29,5 +29,6 @@ export type AgentForm = {
provider?: AgentProvider | OptionWithIcon;
agent_ids?: string[];
[AgentCapabilities.artifacts]?: ArtifactModes | string;
[AgentCapabilities.charts]?: ChartModes | string;
recursion_limit?: number;
} & TAgentCapabilities;
Loading