-
-
Notifications
You must be signed in to change notification settings - Fork 5.4k
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
base: main
Are you sure you want to change the base?
Chart integration #8634
Conversation
It would be great to have support for graphics. It seems that your solutions works like this:
I works probably very well for a small amount of data but it is not scalable. With another LLM frontend 2 years ago I have tried cubejs, and it was working like this:
So the llm did not "see" the data, but it was an advantage because:
Would it be possible the same kind of solution with echarts? ie: have a query in the datasource ( which could be generated by an MCP by the way ) |
Possible solution: Provide a way to specify a url or mcp resource instead of the echarts json. The url could then be fetched to get the echarts json itself. Then the LLM would just need to correctly copy-paste the url / resource identifier. Could possible be used for other artifacts as well. If the llm wants to "see" the data behind the graph itself, it could still do so by fetching the resource or url. I still like the ability to provide the echarts json directly though since it might be useful even without integrating a specialized mcp server (just by using server instructions for echarts). |
@owengo Thank you for taking the time to thoroughly review my PR and providing such detailed feedback. I really appreciate you sharing your experience with CubeJS and the query-based approach - it's clear you've thought deeply about the scalability challenges of chart rendering systems. You raise valid points about performance and scalability. However, I'd like to clarify a few aspects of our current implementation and the challenges we're trying to solve: Our system needs to handle truly arbitrary data sources and query types that can't be predefined. While CubeJS works excellently for known schemas with predefined cubes, our use case involves dynamic query generation (Snowflake, MongoDB, custom APIs, GraphQL endpoints, etc.), unknown data structures that change per request and can't be defined upfront, and varied data formats (nested objects, flat arrays, mixed types) that need intelligent interpretation. The approach we've implemented actually addresses many of your performance concerns. Our MCP tool already optimizes data flow by converting NLP → SQL → aggregated data, returning chart-ready datasets (not raw records). We return summarized data like ["Q1", "Q2", "Q3"] and , not thousands of individual records. The LLM handles the "last mile" of intelligent formatting of any data structure into proper ECharts options. I'm definitely open to exploring a hybrid solution that combines the best of both approaches. We could implement a mode-based system where known, high-volume data sources use query mode with direct backend connections, while arbitrary/unknown data uses our current embedded approach. We could also add an optional backend integration layer that detects if incoming data matches known schemas and routes accordingly, with fallback to our current approach for arbitrary data. I'll admit I'm not deeply familiar with CubeJS, but from what I've researched, it seems to require predefined data models that need to be configured upfront, known database schemas with established relationships, and standardized query patterns within the OLAP paradigm. While this works great for traditional BI scenarios, it can't handle the truly dynamic, arbitrary data visualization that our system aims to provide. Our current approach can create charts for literally any data structure that an MCP tool can return. Would you be interested in collaborating on defining what a hybrid approach might look like? I think there's definitely value in keeping the current flexible approach as the default for arbitrary data, adding query-based optimization for specific high-volume use cases, and creating a configuration layer that lets users choose the appropriate mode. I'd love to hear your thoughts on this direction and whether you have specific backend integration patterns in mind that would work well with ECharts. Thanks again for the thoughtful feedback - it's exactly this kind of architectural discussion that makes features better! |
@fstadt @owengo The URL/resource approach shifts "arbitrary data handling" from the LLM (where it excels) to my rendering logic (where it becomes much more complex). When fetching from a URL, the data format can still be anything - nested objects, flat arrays, unknown schemas, etc. The core value of my approach is using the LLM as an intelligent data transformation layer. It handles pattern recognition and makes smart visualization decisions that would be exponentially harder to code programmatically. The performance concerns assume large raw datasets, but our MCP tools return pre-aggregated, chart-ready data. The LLM does "last mile" intelligent formatting, not heavy data processing. I'm open to a hybrid approach for high-volume known schemas, but for "create charts from any data source," the LLM-based approach provides flexibility that's hard to replicate otherwise. Thoughts on preserving this intelligent data handling while addressing scalability concerns? |
api/app/clients/prompts/charts.js
Outdated
@@ -0,0 +1,182 @@ | |||
const dedent = require('dedent'); | |||
const { ChartModes, EModelEndpoint } = require('librechat-data-provider'); |
Check warning
Code scanning / ESLint
Disallow unused variables Warning
api/app/clients/prompts/charts.js
Outdated
Current date: ${new Date().toLocaleString('en-IN', { timeZone: 'Asia/Kolkata' })} | ||
`; | ||
|
||
const generateChartsPrompt = ({ charts, endpoint }) => { |
Check warning
Code scanning / ESLint
Disallow unused variables Warning
<BarChartIcon className="h-4 w-4" /> | ||
Bar Chart | ||
</button> |
Check failure
Code scanning / ESLint
disallow literal string Error
onClick={() => onToggle('bar')}
aria-label="Switch to bar chart view"
className={cn(
'flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium transition-all duration-200',
activeChart === 'bar'
? 'bg-white text-gray-900 shadow-sm dark:bg-indigo-600 dark:text-white dark:shadow-indigo-500/25'
: 'text-gray-600 hover:text-gray-900 dark:text-slate-300 dark:hover:bg-slate-700/50 dark:hover:text-white',
)}
>
Bar Chart
<ActivityLogIcon className="h-4 w-4" /> | ||
Line Chart | ||
</button> |
Check failure
Code scanning / ESLint
disallow literal string Error
onClick={() => onToggle('line')}
aria-label="Switch to line chart view"
className={cn(
'flex items-center gap-2 rounded-md px-3 py-1.5 text-sm font-medium transition-all duration-200',
activeChart === 'line'
? 'bg-white text-gray-900 shadow-sm dark:bg-indigo-600 dark:text-white dark:shadow-indigo-500/25'
: 'text-gray-600 hover:text-gray-900 dark:text-slate-300 dark:hover:bg-slate-700/50 dark:hover:text-white',
)}
>
Line Chart
|
||
const ChartWithCustomLegend = ({ | ||
data, | ||
complexity, |
Check warning
Code scanning / ESLint
Disallow unused variables Warning
return ( | ||
<div className="flex items-center justify-center rounded-lg border border-red-200 bg-red-50 p-8 dark:border-red-800 dark:bg-red-900/20"> | ||
<div className="text-center"> | ||
<p className="font-medium text-red-600 dark:text-red-400">Chart rendering failed</p> |
Check failure
Code scanning / ESLint
disallow literal string Error
<p className="mt-1 text-sm text-red-600 dark:text-red-400"> | ||
Unable to display this chart | ||
</p> |
Check failure
Code scanning / ESLint
disallow literal string Error
Unable to display this chart
clipRule="evenodd" | ||
/> | ||
</svg> | ||
<span className="font-medium">Failed to render charts for the requested data</span> |
Check failure
Code scanning / ESLint
disallow literal string Error
<p className="mt-2 text-sm text-red-600 dark:text-red-400"> | ||
The chart data could not be processed. Please try rephrasing your request or check the data | ||
format. | ||
</p> |
Check failure
Code scanning / ESLint
disallow literal string Error
The chart data could not be processed. Please try rephrasing your request or check the data
format.
…LibreChat into chart-integration
@danny-avila is there plan to get this into main? We were thinking to use it. |
Summary
This PR adds a new Chart Toggle option to the agent configuration UI, enabling inline rendering of charts in chat conversations. When this toggle is enabled, the LLM responds with a strict Echarts options tructure for chart data, which the frontend parses and renders using ECharts.
The main motivation is to allow users to visualize analytical or structured data directly within conversations—similar to the artifact feature but specifically for charts. This unlocks real-time visualization use cases such as AI usage metrics, cost breakdowns, or monitoring reports.
An example application of this feature is an internal MCP tool we've developed, which generates AWS billing data formatted as ECharts JSON. When combined with this chart toggle, the data is rendered inline as a cost dashboard, effectively acting as a conversational mini-Cost Explorer.
There are opportunities for design improvement and extended parsing flexibility in future iterations.
Change Type
New feature (non-breaking change which adds functionality)
Testing
Manual testing was performed to validate the following:
Toggling the chart feature on/off in agent settings correctly alters rendering behavior.
Responses from the LLM in the :::chart{} format are parsed and rendered using ECharts in the chat window.
Existing functionality like artifacts, markdown, and code rendering remain unaffected.
Checklist
I have added two video files showing the working of this functionality
Screencast.from.2025-07-24.04-48-28.mp4
Screencast.from.2025-07-24.04-50-42.mp4