feat: add SQL codeblock from click ui package for run_select_query when clickhouse mcp detected in tool call

This commit is contained in:
Dustin Healy 2026-01-05 15:20:41 -08:00 committed by Danny Avila
parent 762d78b7fe
commit f0a4aa9cf7
No known key found for this signature in database
GPG key ID: BF31EEB2C5CA0956
5 changed files with 1071 additions and 26 deletions

View file

@ -31,6 +31,7 @@
"dependencies": {
"@ariakit/react": "^0.4.15",
"@ariakit/react-core": "^0.4.17",
"@clickhouse/click-ui": "^0.0.247",
"@codesandbox/sandpack-react": "^2.19.10",
"@dicebear/collection": "^9.2.2",
"@dicebear/core": "^9.2.2",

View file

@ -1,12 +1,13 @@
import { useEffect } from 'react';
import { useEffect, useContext } from 'react';
import { RecoilRoot } from 'recoil';
import { DndProvider } from 'react-dnd';
import { RouterProvider } from 'react-router-dom';
import * as RadixToast from '@radix-ui/react-toast';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { ClickUIProvider } from '@clickhouse/click-ui';
import { ReactQueryDevtools } from '@tanstack/react-query-devtools';
import { Toast, ThemeProvider, ToastProvider } from '@librechat/client';
import { QueryClient, QueryClientProvider, QueryCache } from '@tanstack/react-query';
import { Toast, ThemeProvider, ToastProvider, ThemeContext } from '@librechat/client';
import { ScreenshotProvider, useApiErrorBoundary } from './hooks';
import WakeLockManager from '~/components/System/WakeLockManager';
import { getThemeFromEnv } from './utils/getThemeFromEnv';
@ -14,6 +15,13 @@ import { initializeFontSize } from '~/store/fontSize';
import { LiveAnnouncer } from '~/a11y';
import { router } from './routes';
const ClickUIWrapper = ({ children }) => {
const { theme } = useContext(ThemeContext);
const clickUITheme = theme === 'dark' ? 'dark' : 'light';
return <ClickUIProvider theme={clickUITheme}>{children}</ClickUIProvider>;
};
const App = () => {
const { setError } = useApiErrorBoundary();
@ -54,21 +62,23 @@ const App = () => {
{...(envTheme && { initialTheme: 'system', themeRGB: envTheme })}
>
{/* The ThemeProvider will automatically:
1. Apply dark/light mode classes
2. Apply custom theme colors if envTheme is provided
3. Otherwise use stored theme preferences from localStorage
4. Fall back to default theme colors if nothing is stored */}
<RadixToast.Provider>
<ToastProvider>
<DndProvider backend={HTML5Backend}>
<RouterProvider router={router} />
<WakeLockManager />
<ReactQueryDevtools initialIsOpen={false} position="top-right" />
<Toast />
<RadixToast.Viewport className="pointer-events-none fixed inset-0 z-[1000] mx-auto my-2 flex max-w-[560px] flex-col items-stretch justify-start md:pb-5" />
</DndProvider>
</ToastProvider>
</RadixToast.Provider>
1. Apply dark/light mode classes
2. Apply custom theme colors if envTheme is provided
3. Otherwise use stored theme preferences from localStorage
4. Fall back to default theme colors if nothing is stored */}
<ClickUIWrapper>
<RadixToast.Provider>
<ToastProvider>
<DndProvider backend={HTML5Backend}>
<RouterProvider router={router} />
<WakeLockManager />
<ReactQueryDevtools initialIsOpen={false} position="top-right" />
<Toast />
<RadixToast.Viewport className="pointer-events-none fixed inset-0 z-[1000] mx-auto my-2 flex max-w-[560px] flex-col items-stretch justify-start md:pb-5" />
</DndProvider>
</ToastProvider>
</RadixToast.Provider>
</ClickUIWrapper>
</ThemeProvider>
</LiveAnnouncer>
</RecoilRoot>

View file

@ -1,6 +1,7 @@
import { useMemo, useState, useEffect, useRef, useLayoutEffect } from 'react';
import { Button } from '@librechat/client';
import { TriangleAlert } from 'lucide-react';
import { CodeBlock } from '@clickhouse/click-ui';
import { actionDelimiter, actionDomainSeparator, Constants } from 'librechat-data-provider';
import type { TAttachment } from 'librechat-data-provider';
import { useLocalize, useProgress } from '~/hooks';
@ -101,6 +102,23 @@ export default function ToolCall({
const progress = useProgress(initialProgress);
const cancelled = (!isSubmitting && progress < 1) || error === true;
const isClickHouseQuery = useMemo(
() => domain?.toLowerCase().includes('clickhouse') && function_name === 'run_select_query',
[domain, function_name],
);
const parsedQuery = useMemo(() => {
if (!isClickHouseQuery || !args) {
return null;
}
try {
const parsed = JSON.parse(args);
return parsed.query && typeof parsed.query === 'string' ? parsed.query : null;
} catch {
return null;
}
}, [isClickHouseQuery, args]);
const getFinishedText = () => {
if (cancelled) {
return localize('com_ui_cancelled');
@ -181,6 +199,13 @@ export default function ToolCall({
error={cancelled}
/>
</div>
{parsedQuery && (
<div className="clickhouse-codeblock my-2">
<CodeBlock language="sql" showLineNumbers>
{parsedQuery}
</CodeBlock>
</div>
)}
<div
className="relative"
style={{

View file

@ -2918,3 +2918,11 @@ html {
.sharepoint-picker-bg {
background-color: #f5f5f5;
}
/* ClickHouse CodeBlock - Reset global hljs styles */
.clickhouse-codeblock code.hljs,
.clickhouse-codeblock code[class*='language-'],
.clickhouse-codeblock pre[class*='language-'] {
color: unset;
background: unset;
}

1019
package-lock.json generated

File diff suppressed because it is too large Load diff