mirror of
https://github.com/csunny/DB-GPT.git
synced 2025-07-30 23:28:35 +00:00
refactor: upgrade gpt-vis (#1883)
This commit is contained in:
parent
131bc7b89b
commit
1cb7e35295
@ -2,7 +2,7 @@ import { ChatContext } from '@/app/chat-context';
|
||||
import { IChatDialogueMessageSchema } from '@/types/chat';
|
||||
import classNames from 'classnames';
|
||||
import { memo, useContext } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { GPTVis } from '@antv/gpt-vis';
|
||||
import markdownComponents from './chat-content/config';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
|
||||
@ -21,15 +21,21 @@ function AgentContent({ content }: Props) {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('relative w-full p-2 md:p-4 rounded-xl break-words', {
|
||||
'bg-white dark:bg-[#232734]': isView,
|
||||
'lg:w-full xl:w-full pl-0': ['chat_with_db_execute', 'chat_dashboard'].includes(scene),
|
||||
})}
|
||||
className={classNames(
|
||||
"relative w-full p-2 md:p-4 rounded-xl break-words",
|
||||
{
|
||||
"bg-white dark:bg-[#232734]": isView,
|
||||
"lg:w-full xl:w-full pl-0": [
|
||||
"chat_with_db_execute",
|
||||
"chat_dashboard",
|
||||
].includes(scene),
|
||||
}
|
||||
)}
|
||||
>
|
||||
{isView ? (
|
||||
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>
|
||||
<GPTVis components={markdownComponents} rehypePlugins={[rehypeRaw]}>
|
||||
{formatMarkdownVal(content.context)}
|
||||
</ReactMarkdown>
|
||||
</GPTVis>
|
||||
) : (
|
||||
<div className="">{content.context}</div>
|
||||
)}
|
||||
|
@ -6,7 +6,7 @@ import { githubLightTheme } from '@uiw/react-json-view/githubLight';
|
||||
import { Alert, Spin } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import React, { useContext, useMemo } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { GPTVis } from '@antv/gpt-vis';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
@ -41,30 +41,36 @@ const VisResponse: React.FC<{ data: VisResponseProps }> = ({ data }) => {
|
||||
return (
|
||||
<div className="flex flex-1 flex-col">
|
||||
<Alert
|
||||
className={classNames('mb-4', {
|
||||
'bg-[#fafafa] border-[transparent]': !type,
|
||||
className={classNames("mb-4", {
|
||||
"bg-[#fafafa] border-[transparent]": !type,
|
||||
})}
|
||||
message={data.name}
|
||||
type={type}
|
||||
{...(type && { showIcon: true })}
|
||||
{...(type === 'warning' && { icon: <Spin indicator={<LoadingOutlined spin />} /> })}
|
||||
{...(type === "warning" && {
|
||||
icon: <Spin indicator={<LoadingOutlined spin />} />,
|
||||
})}
|
||||
/>
|
||||
{data.result && (
|
||||
<JsonView
|
||||
style={{ ...theme, width: '100%', padding: 10 }}
|
||||
style={{ ...theme, width: "100%", padding: 10 }}
|
||||
className={classNames({
|
||||
'bg-[#fafafa]': mode === 'light',
|
||||
"bg-[#fafafa]": mode === "light",
|
||||
})}
|
||||
value={JSON.parse(data.result || '{}')}
|
||||
value={JSON.parse(data.result || "{}")}
|
||||
enableClipboard={false}
|
||||
displayDataTypes={false}
|
||||
objectSortKeys={false}
|
||||
/>
|
||||
)}
|
||||
{data.err_msg && (
|
||||
<ReactMarkdown components={markdownComponents} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
|
||||
<GPTVis
|
||||
components={markdownComponents}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
>
|
||||
{data.err_msg}
|
||||
</ReactMarkdown>
|
||||
</GPTVis>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,7 +1,6 @@
|
||||
import ModelIcon from '@/new-components/chat/content/ModelIcon';
|
||||
import { LinkOutlined, SwapRightOutlined } from '@ant-design/icons';
|
||||
import { Popover, Space } from 'antd';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { SwapRightOutlined } from '@ant-design/icons';
|
||||
import { GPTVis } from '@antv/gpt-vis';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import markdownComponents from './config';
|
||||
@ -24,7 +23,11 @@ function AgentMessages({ data }: Props) {
|
||||
{data.map((item, index) => (
|
||||
<div key={index} className="rounded">
|
||||
<div className="flex items-center mb-3 text-sm">
|
||||
{item.model ? <ModelIcon model={item.model} /> : <div className="rounded-full w-6 h-6 bg-gray-100" />}
|
||||
{item.model ? (
|
||||
<ModelIcon model={item.model} />
|
||||
) : (
|
||||
<div className="rounded-full w-6 h-6 bg-gray-100" />
|
||||
)}
|
||||
<div className="ml-2 opacity-70">
|
||||
{item.sender}
|
||||
<SwapRightOutlined className="mx-2 text-base" />
|
||||
@ -32,11 +35,17 @@ function AgentMessages({ data }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
<div className="whitespace-normal text-sm mb-3">
|
||||
<ReactMarkdown components={markdownComponents} remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
|
||||
<GPTVis
|
||||
components={markdownComponents}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
>
|
||||
{item.markdown}
|
||||
</ReactMarkdown>
|
||||
</GPTVis>
|
||||
</div>
|
||||
{item.resource && item.resource !== 'null' && <ReferencesContent references={item.resource} />}
|
||||
{item.resource && item.resource !== "null" && (
|
||||
<ReferencesContent references={item.resource} />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { CaretRightOutlined, CheckOutlined, ClockCircleOutlined } from '@ant-design/icons';
|
||||
import { Collapse } from 'antd';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { GPTVis } from '@antv/gpt-vis';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
@ -32,7 +32,7 @@ function AgentPlans({ data }: Props) {
|
||||
<span>
|
||||
{item.name} - {item.agent}
|
||||
</span>
|
||||
{item.status === 'complete' ? (
|
||||
{item.status === "complete" ? (
|
||||
<CheckOutlined className="!text-green-500 ml-2" />
|
||||
) : (
|
||||
<ClockCircleOutlined className="!text-gray-500 ml-2" />
|
||||
@ -40,9 +40,13 @@ function AgentPlans({ data }: Props) {
|
||||
</div>
|
||||
),
|
||||
children: (
|
||||
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]} remarkPlugins={[remarkGfm]}>
|
||||
<GPTVis
|
||||
components={markdownComponents}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
>
|
||||
{item.markdown}
|
||||
</ReactMarkdown>
|
||||
</GPTVis>
|
||||
),
|
||||
};
|
||||
})}
|
||||
|
@ -1,146 +1,241 @@
|
||||
import { AutoChart, BackEndChartType, getChartType } from '@/components/chart';
|
||||
import { LinkOutlined, ReadOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
import { Datum } from '@antv/ava';
|
||||
import { Image, Table, Tabs, TabsProps, Tag } from 'antd';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { LinkOutlined, ReadOutlined, SyncOutlined } from "@ant-design/icons";
|
||||
import { GPTVis, withDefaultChartCode } from "@antv/gpt-vis";
|
||||
import { Table, Image, Tag, Tabs, TabsProps } from "antd";
|
||||
import { AutoChart, BackEndChartType, getChartType } from "@/components/chart";
|
||||
import { Datum } from "@antv/ava";
|
||||
import rehypeRaw from "rehype-raw";
|
||||
import remarkGfm from "remark-gfm";
|
||||
import AgentMessages from "./agent-messages";
|
||||
import AgentPlans from "./agent-plans";
|
||||
import { CodePreview } from "./code-preview";
|
||||
import ReferencesContent from "./ReferencesContent";
|
||||
import VisChart from "./vis-chart";
|
||||
import VisCode from "./vis-code";
|
||||
import VisConvertError from "./vis-convert-error";
|
||||
import VisDashboard from "./vis-dashboard";
|
||||
import VisPlugin from "./vis-plugin";
|
||||
import VisAppLink from "./VisAppLink";
|
||||
import VisChatLink from "./VisChatLink";
|
||||
import VisResponse from "./VisResponse";
|
||||
import { formatSql } from "@/utils";
|
||||
|
||||
import AgentMessages from './agent-messages';
|
||||
import AgentPlans from './agent-plans';
|
||||
import { CodePreview } from './code-preview';
|
||||
import ReferencesContent from './ReferencesContent';
|
||||
import VisChart from './vis-chart';
|
||||
import VisCode from './vis-code';
|
||||
import VisConvertError from './vis-convert-error';
|
||||
import VisDashboard from './vis-dashboard';
|
||||
import VisPlugin from './vis-plugin';
|
||||
import VisAppLink from './VisAppLink';
|
||||
import VisChatLink from './VisChatLink';
|
||||
import VisResponse from './VisResponse';
|
||||
import { formatSql } from '@/utils';
|
||||
type MarkdownComponent = Parameters<typeof GPTVis>["0"]["components"];
|
||||
|
||||
type MarkdownComponent = Parameters<typeof ReactMarkdown>['0']['components'];
|
||||
|
||||
const customeTags: (keyof JSX.IntrinsicElements)[] = ['custom-view', 'chart-view', 'references', 'summary'];
|
||||
const customeTags: (keyof JSX.IntrinsicElements)[] = [
|
||||
"custom-view",
|
||||
"chart-view",
|
||||
"references",
|
||||
"summary",
|
||||
];
|
||||
|
||||
function matchCustomeTagValues(context: string) {
|
||||
const matchValues = customeTags.reduce<string[]>((acc, tagName) => {
|
||||
const tagReg = new RegExp(`<${tagName}[^>]*\/?>`, 'gi');
|
||||
const tagReg = new RegExp(`<${tagName}[^>]*\/?>`, "gi");
|
||||
context = context.replace(tagReg, (matchVal) => {
|
||||
acc.push(matchVal);
|
||||
return '';
|
||||
return "";
|
||||
});
|
||||
return acc;
|
||||
}, []);
|
||||
return { context, matchValues };
|
||||
}
|
||||
|
||||
const codeComponents = {
|
||||
/**
|
||||
* @description
|
||||
* Custom code block rendering, which can be used to render custom components in the code block.
|
||||
* Is it defined in gpt-vis, and the default rendering contains `vis-chart`.
|
||||
*/
|
||||
code: withDefaultChartCode({
|
||||
languageRenderers: {
|
||||
"agent-plans": ({
|
||||
node,
|
||||
className,
|
||||
children,
|
||||
style,
|
||||
}) => {
|
||||
const content = String(children);
|
||||
/**
|
||||
* @description
|
||||
* In some cases, tags are nested within code syntax,
|
||||
* so it is necessary to extract the tags present in the code block and render them separately.
|
||||
*/
|
||||
const lang = className?.replace("language-", "") || "javascript";
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<
|
||||
typeof AgentPlans
|
||||
>[0]["data"];
|
||||
return <AgentPlans data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
},
|
||||
"agent-messages": ({
|
||||
node,
|
||||
className,
|
||||
children,
|
||||
style,
|
||||
}) => {
|
||||
const content = String(children);
|
||||
const lang = className?.replace("language-", "") || "javascript";
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<
|
||||
typeof AgentMessages
|
||||
>[0]["data"];
|
||||
return <AgentMessages data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
},
|
||||
"vis-convert-error": ({
|
||||
node,
|
||||
className,
|
||||
children,
|
||||
style,
|
||||
}) => {
|
||||
const content = String(children);
|
||||
const lang = className?.replace("language-", "") || "javascript";
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<
|
||||
typeof VisConvertError
|
||||
>[0]["data"];
|
||||
return <VisConvertError data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
},
|
||||
"vis-dashboard": ({
|
||||
node,
|
||||
className,
|
||||
children,
|
||||
style,
|
||||
}) => {
|
||||
const content = String(children);
|
||||
const lang = className?.replace("language-", "") || "javascript";
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<
|
||||
typeof VisDashboard
|
||||
>[0]["data"];
|
||||
return <VisDashboard data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
},
|
||||
"vis-chart": ({ node, className, children, style }) => {
|
||||
const content = String(children);
|
||||
const lang = className?.replace("language-", "") || "javascript";
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<
|
||||
typeof VisChart
|
||||
>[0]["data"];
|
||||
return <VisChart data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
},
|
||||
"vis-plugin": ({
|
||||
node,
|
||||
className,
|
||||
children,
|
||||
style,
|
||||
}) => {
|
||||
const content = String(children);
|
||||
const lang = className?.replace("language-", "") || "javascript";
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<
|
||||
typeof VisPlugin
|
||||
>[0]["data"];
|
||||
return <VisPlugin data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
},
|
||||
"vis-code": ({ node, className, children, style, ...props }) => {
|
||||
const content = String(children);
|
||||
const lang = className?.replace("language-", "") || "javascript";
|
||||
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<
|
||||
typeof VisCode
|
||||
>[0]["data"];
|
||||
return <VisCode data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
},
|
||||
"vis-app-link": ({
|
||||
node,
|
||||
className,
|
||||
children,
|
||||
style,
|
||||
...props
|
||||
}) => {
|
||||
const content = String(children);
|
||||
const lang = className?.replace("language-", "") || "javascript";
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<
|
||||
typeof VisAppLink
|
||||
>[0]["data"];
|
||||
return <VisAppLink data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
},
|
||||
"vis-api-response": ({
|
||||
node,
|
||||
className,
|
||||
children,
|
||||
style,
|
||||
...props
|
||||
}) => {
|
||||
const content = String(children);
|
||||
const lang = className?.replace("language-", "") || "javascript";
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<
|
||||
typeof VisResponse
|
||||
>[0]["data"];
|
||||
return <VisResponse data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
},
|
||||
},
|
||||
defaultRenderer({ node, className, children, style, ...props }) {
|
||||
|
||||
const content = String(children);
|
||||
const lang = className?.replace("language-", "") || "";
|
||||
const { context, matchValues } = matchCustomeTagValues(content);
|
||||
|
||||
|
||||
console.log(111, { node, className, children, style, ...props }, lang);
|
||||
return (
|
||||
<>
|
||||
{lang ? (
|
||||
<CodePreview code={context} language={lang || "javascript"} />
|
||||
) : (
|
||||
<code
|
||||
{...props}
|
||||
style={style}
|
||||
className="p-1 mx-1 rounded bg-theme-light dark:bg-theme-dark text-sm"
|
||||
>
|
||||
{children}
|
||||
</code>
|
||||
)}
|
||||
<GPTVis
|
||||
components={markdownComponents}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
>
|
||||
{matchValues.join("\n")}
|
||||
</GPTVis>
|
||||
</>
|
||||
);
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
const basicComponents: MarkdownComponent = {
|
||||
code({ inline, node, className, children, style, ...props }) {
|
||||
const content = String(children);
|
||||
/**
|
||||
* @description
|
||||
* In some cases, tags are nested within code syntax,
|
||||
* so it is necessary to extract the tags present in the code block and render them separately.
|
||||
*/
|
||||
const { context, matchValues } = matchCustomeTagValues(content);
|
||||
const lang = className?.replace('language-', '') || 'javascript';
|
||||
|
||||
if (lang === 'agent-plans') {
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<typeof AgentPlans>[0]['data'];
|
||||
return <AgentPlans data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
}
|
||||
|
||||
if (lang === 'agent-messages') {
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<typeof AgentMessages>[0]['data'];
|
||||
return <AgentMessages data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
}
|
||||
|
||||
if (lang === 'vis-convert-error') {
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<typeof VisConvertError>[0]['data'];
|
||||
return <VisConvertError data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
}
|
||||
|
||||
if (lang === 'vis-dashboard') {
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<typeof VisDashboard>[0]['data'];
|
||||
return <VisDashboard data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
}
|
||||
|
||||
if (lang === 'vis-chart') {
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<typeof VisChart>[0]['data'];
|
||||
return <VisChart data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
}
|
||||
|
||||
if (lang === 'vis-plugin') {
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<typeof VisPlugin>[0]['data'];
|
||||
return <VisPlugin data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
}
|
||||
|
||||
if (lang === 'vis-code') {
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<typeof VisCode>[0]['data'];
|
||||
return <VisCode data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
}
|
||||
|
||||
if (lang === 'vis-app-link') {
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<typeof VisAppLink>[0]['data'];
|
||||
return <VisAppLink data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
}
|
||||
if (lang === 'vis-api-response') {
|
||||
try {
|
||||
const data = JSON.parse(content) as Parameters<typeof VisResponse>[0]['data'];
|
||||
return <VisResponse data={data} />;
|
||||
} catch (e) {
|
||||
return <CodePreview language={lang} code={content} />;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<>
|
||||
{!inline ? (
|
||||
<CodePreview code={context} language={lang} />
|
||||
) : (
|
||||
<code {...props} style={style} className="p-1 mx-1 rounded bg-theme-light dark:bg-theme-dark text-sm">
|
||||
{children}
|
||||
</code>
|
||||
)}
|
||||
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]} remarkPlugins={[remarkGfm]}>
|
||||
{matchValues.join('\n')}
|
||||
</ReactMarkdown>
|
||||
</>
|
||||
);
|
||||
},
|
||||
...codeComponents,
|
||||
ul({ children }) {
|
||||
return <ul className="py-1">{children}</ul>;
|
||||
},
|
||||
@ -148,22 +243,46 @@ const basicComponents: MarkdownComponent = {
|
||||
return <ol className="py-1">{children}</ol>;
|
||||
},
|
||||
li({ children, ordered }) {
|
||||
return <li className={`text-sm leading-7 ml-5 pl-2 text-gray-600 dark:text-gray-300 ${ordered ? 'list-decimal' : 'list-disc'}`}>{children}</li>;
|
||||
return (
|
||||
<li
|
||||
className={`text-sm leading-7 ml-5 pl-2 text-gray-600 dark:text-gray-300 ${
|
||||
ordered ? "list-decimal" : "list-disc"
|
||||
}`}
|
||||
>
|
||||
{children}
|
||||
</li>
|
||||
);
|
||||
},
|
||||
table({ children }) {
|
||||
return <table className="my-2 rounded-tl-md rounded-tr-md bg-white dark:bg-gray-800 text-sm rounded-lg overflow-hidden">{children}</table>;
|
||||
return (
|
||||
<table className="my-2 rounded-tl-md rounded-tr-md bg-white dark:bg-gray-800 text-sm rounded-lg overflow-hidden">
|
||||
{children}
|
||||
</table>
|
||||
);
|
||||
},
|
||||
thead({ children }) {
|
||||
return <thead className="bg-[#fafafa] dark:bg-black font-semibold">{children}</thead>;
|
||||
return (
|
||||
<thead className="bg-[#fafafa] dark:bg-black font-semibold">
|
||||
{children}
|
||||
</thead>
|
||||
);
|
||||
},
|
||||
th({ children }) {
|
||||
return <th className="!text-left p-4">{children}</th>;
|
||||
},
|
||||
td({ children }) {
|
||||
return <td className="p-4 border-t border-[#f0f0f0] dark:border-gray-700">{children}</td>;
|
||||
return (
|
||||
<td className="p-4 border-t border-[#f0f0f0] dark:border-gray-700">
|
||||
{children}
|
||||
</td>
|
||||
);
|
||||
},
|
||||
h1({ children }) {
|
||||
return <h3 className="text-2xl font-bold my-4 border-b border-slate-300 pb-4">{children}</h3>;
|
||||
return (
|
||||
<h3 className="text-2xl font-bold my-4 border-b border-slate-300 pb-4">
|
||||
{children}
|
||||
</h3>
|
||||
);
|
||||
},
|
||||
h2({ children }) {
|
||||
return <h3 className="text-xl font-bold my-3">{children}</h3>;
|
||||
@ -209,8 +328,8 @@ const basicComponents: MarkdownComponent = {
|
||||
);
|
||||
},
|
||||
button({ children, className, ...restProps }) {
|
||||
if (className === 'chat-link') {
|
||||
const msg = (restProps as any)?.['data-msg'];
|
||||
if (className === "chat-link") {
|
||||
const msg = (restProps as any)?.["data-msg"];
|
||||
return <VisChatLink msg={msg}>{children}</VisChatLink>;
|
||||
}
|
||||
return (
|
||||
@ -223,32 +342,32 @@ const basicComponents: MarkdownComponent = {
|
||||
|
||||
const returnSqlVal = (val: string) => {
|
||||
const punctuationMap: any = {
|
||||
',': ',',
|
||||
'。': '.',
|
||||
'?': '?',
|
||||
'!': '!',
|
||||
':': ':',
|
||||
';': ';',
|
||||
'“': '"',
|
||||
'”': '"',
|
||||
'‘': "'",
|
||||
'’': "'",
|
||||
'(': '(',
|
||||
')': ')',
|
||||
'【': '[',
|
||||
'】': ']',
|
||||
'《': '<',
|
||||
'》': '>',
|
||||
'—': '-',
|
||||
'、': ',',
|
||||
'…': '...',
|
||||
",": ",",
|
||||
"。": ".",
|
||||
"?": "?",
|
||||
"!": "!",
|
||||
":": ":",
|
||||
";": ";",
|
||||
"“": '"',
|
||||
"”": '"',
|
||||
"‘": "'",
|
||||
"’": "'",
|
||||
"(": "(",
|
||||
")": ")",
|
||||
"【": "[",
|
||||
"】": "]",
|
||||
"《": "<",
|
||||
"》": ">",
|
||||
"—": "-",
|
||||
"、": ",",
|
||||
"…": "...",
|
||||
};
|
||||
const regex = new RegExp(Object.keys(punctuationMap).join('|'), 'g');
|
||||
const regex = new RegExp(Object.keys(punctuationMap).join("|"), "g");
|
||||
return val.replace(regex, (match) => punctuationMap[match]);
|
||||
};
|
||||
|
||||
const extraComponents: MarkdownComponent = {
|
||||
'chart-view': function ({ content, children }) {
|
||||
"chart-view": function ({ content, children }) {
|
||||
let data: {
|
||||
data: Datum[];
|
||||
type: BackEndChartType;
|
||||
@ -259,11 +378,12 @@ const extraComponents: MarkdownComponent = {
|
||||
} catch (e) {
|
||||
console.log(e, content);
|
||||
data = {
|
||||
type: 'response_table',
|
||||
sql: '',
|
||||
type: "response_table",
|
||||
sql: "",
|
||||
data: [],
|
||||
};
|
||||
}
|
||||
console.log(111, data);
|
||||
|
||||
const columns = data?.data?.[0]
|
||||
? Object.keys(data?.data?.[0])?.map((item) => {
|
||||
@ -276,25 +396,46 @@ const extraComponents: MarkdownComponent = {
|
||||
: [];
|
||||
|
||||
const ChartItem = {
|
||||
key: 'chart',
|
||||
label: 'Chart',
|
||||
children: <AutoChart data={data?.data} chartType={getChartType(data?.type)} />,
|
||||
key: "chart",
|
||||
label: "Chart",
|
||||
children: (
|
||||
<AutoChart data={data?.data} chartType={getChartType(data?.type)} />
|
||||
),
|
||||
};
|
||||
const SqlItem = {
|
||||
key: 'sql',
|
||||
label: 'SQL',
|
||||
children: <CodePreview code={formatSql(returnSqlVal(data?.sql), 'mysql') as string} language={'sql'} />,
|
||||
key: "sql",
|
||||
label: "SQL",
|
||||
children: (
|
||||
<CodePreview
|
||||
code={formatSql(returnSqlVal(data?.sql), "mysql") as string}
|
||||
language={"sql"}
|
||||
/>
|
||||
),
|
||||
};
|
||||
const DataItem = {
|
||||
key: 'data',
|
||||
label: 'Data',
|
||||
children: <Table dataSource={data?.data} columns={columns} scroll={{x:true}} virtual={true} />,
|
||||
key: "data",
|
||||
label: "Data",
|
||||
children: (
|
||||
<Table
|
||||
dataSource={data?.data}
|
||||
columns={columns}
|
||||
scroll={{ x: true }}
|
||||
virtual={true}
|
||||
/>
|
||||
),
|
||||
};
|
||||
const TabItems: TabsProps['items'] = data?.type === 'response_table' ? [DataItem, SqlItem] : [ChartItem, SqlItem, DataItem];
|
||||
const TabItems: TabsProps["items"] =
|
||||
data?.type === "response_table"
|
||||
? [DataItem, SqlItem]
|
||||
: [ChartItem, SqlItem, DataItem];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Tabs defaultActiveKey={data?.type === 'response_table' ? 'data' : 'chart'} items={TabItems} size="small" />
|
||||
<Tabs
|
||||
defaultActiveKey={data?.type === "response_table" ? "data" : "chart"}
|
||||
items={TabItems}
|
||||
size="small"
|
||||
/>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { PropsWithChildren, ReactNode, memo, useContext, useMemo } from 'react';
|
||||
import { CheckOutlined, ClockCircleOutlined, CloseOutlined, CodeOutlined, LoadingOutlined, RobotOutlined, UserOutlined } from '@ant-design/icons';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { GPTVis } from '@antv/gpt-vis';
|
||||
import { IChatDialogueMessageSchema } from '@/types/chat';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import classNames from 'classnames';
|
||||
@ -22,7 +22,7 @@ interface Props {
|
||||
onLinkClick?: () => void;
|
||||
}
|
||||
|
||||
type MarkdownComponent = Parameters<typeof ReactMarkdown>['0']['components'];
|
||||
type MarkdownComponent = Parameters<typeof GPTVis>["0"]["components"];
|
||||
|
||||
type DBGPTView = {
|
||||
name: string;
|
||||
@ -112,15 +112,23 @@ function ChatContent({ children, content, isChartChat, onLinkClick }: PropsWithC
|
||||
const { bgClass, icon } = pluginViewStatusMapper[status] ?? {};
|
||||
return (
|
||||
<div className="bg-white dark:bg-[#212121] rounded-lg overflow-hidden my-2 flex flex-col lg:max-w-[80%]">
|
||||
<div className={classNames('flex px-4 md:px-6 py-2 items-center text-white text-sm', bgClass)}>
|
||||
<div
|
||||
className={classNames(
|
||||
"flex px-4 md:px-6 py-2 items-center text-white text-sm",
|
||||
bgClass
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
{icon}
|
||||
</div>
|
||||
{result ? (
|
||||
<div className="px-4 md:px-6 py-4 text-sm">
|
||||
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>
|
||||
{result ?? ''}
|
||||
</ReactMarkdown>
|
||||
<GPTVis
|
||||
components={markdownComponents}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
>
|
||||
{result ?? ""}
|
||||
</GPTVis>
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-4 md:px-6 py-4 text-sm">{err_msg}</div>
|
||||
@ -136,32 +144,48 @@ function ChatContent({ children, content, isChartChat, onLinkClick }: PropsWithC
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames('relative flex flex-wrap w-full p-2 md:p-4 rounded-xl break-words', {
|
||||
'bg-white dark:bg-[#232734]': isRobot,
|
||||
'lg:w-full xl:w-full pl-0': ['chat_with_db_execute', 'chat_dashboard'].includes(scene),
|
||||
})}
|
||||
className={classNames(
|
||||
"relative flex flex-wrap w-full p-2 md:p-4 rounded-xl break-words",
|
||||
{
|
||||
"bg-white dark:bg-[#232734]": isRobot,
|
||||
"lg:w-full xl:w-full pl-0": [
|
||||
"chat_with_db_execute",
|
||||
"chat_dashboard",
|
||||
].includes(scene),
|
||||
}
|
||||
)}
|
||||
>
|
||||
<div className="mr-2 flex flex-shrink-0 items-center justify-center h-7 w-7 rounded-full text-lg sm:mr-4">
|
||||
{isRobot ? renderModelIcon(model_name) || <RobotOutlined /> : <UserOutlined />}
|
||||
{isRobot ? (
|
||||
renderModelIcon(model_name) || <RobotOutlined />
|
||||
) : (
|
||||
<UserOutlined />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex-1 overflow-hidden items-center text-md leading-8 pb-2">
|
||||
{/* User Input */}
|
||||
{!isRobot && typeof context === 'string' && context}
|
||||
{!isRobot && typeof context === "string" && context}
|
||||
{/* Render Report */}
|
||||
{isRobot && isChartChat && typeof context === 'object' && (
|
||||
{isRobot && isChartChat && typeof context === "object" && (
|
||||
<div>
|
||||
{`[${context.template_name}]: `}
|
||||
<span className="text-theme-primary cursor-pointer" onClick={onLinkClick}>
|
||||
<span
|
||||
className="text-theme-primary cursor-pointer"
|
||||
onClick={onLinkClick}
|
||||
>
|
||||
<CodeOutlined className="mr-1" />
|
||||
{context.template_introduce || 'More Details'}
|
||||
{context.template_introduce || "More Details"}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{/* Markdown */}
|
||||
{isRobot && typeof context === 'string' && (
|
||||
<ReactMarkdown components={{ ...markdownComponents, ...extraMarkdownComponents }} rehypePlugins={[rehypeRaw]}>
|
||||
{isRobot && typeof context === "string" && (
|
||||
<GPTVis
|
||||
components={{ ...markdownComponents, ...extraMarkdownComponents }}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
>
|
||||
{formatMarkdownVal(value)}
|
||||
</ReactMarkdown>
|
||||
</GPTVis>
|
||||
)}
|
||||
{!!relations?.length && (
|
||||
<div className="flex flex-wrap mt-2">
|
||||
|
@ -1,4 +1,4 @@
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { GPTVis } from '@antv/gpt-vis';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import markdownComponents from './config';
|
||||
import { CodePreview } from './code-preview';
|
||||
@ -29,9 +29,12 @@ function VisCode({ data }: Props) {
|
||||
{data.code.map((item, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className={classNames('px-4 py-2 text-[#121417] dark:text-white cursor-pointer', {
|
||||
'bg-white dark:bg-theme-dark-container': index === show,
|
||||
})}
|
||||
className={classNames(
|
||||
"px-4 py-2 text-[#121417] dark:text-white cursor-pointer",
|
||||
{
|
||||
"bg-white dark:bg-theme-dark-container": index === show,
|
||||
}
|
||||
)}
|
||||
onClick={() => {
|
||||
setShow(index);
|
||||
}}
|
||||
@ -53,13 +56,18 @@ function VisCode({ data }: Props) {
|
||||
<div>
|
||||
<div className="flex">
|
||||
<div className="bg-white dark:bg-theme-dark-container px-4 py-2 text-[#121417] dark:text-white">
|
||||
{t('Terminal')} {data.exit_success ? <CheckOutlined className="text-green-600" /> : <CloseOutlined className="text-red-600" />}
|
||||
{t("Terminal")}{" "}
|
||||
{data.exit_success ? (
|
||||
<CheckOutlined className="text-green-600" />
|
||||
) : (
|
||||
<CloseOutlined className="text-red-600" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4 max-h-72 overflow-y-auto whitespace-normal bg-white dark:dark:bg-theme-dark">
|
||||
<ReactMarkdown components={markdownComponents} remarkPlugins={[remarkGfm]}>
|
||||
<GPTVis components={markdownComponents} remarkPlugins={[remarkGfm]}>
|
||||
{data.log}
|
||||
</ReactMarkdown>
|
||||
</GPTVis>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { CheckOutlined, ClockCircleOutlined, CloseOutlined, LoadingOutlined } from '@ant-design/icons';
|
||||
import classNames from 'classnames';
|
||||
import { ReactNode } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { GPTVis } from '@antv/gpt-vis';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
@ -47,15 +47,24 @@ function VisPlugin({ data }: Props) {
|
||||
|
||||
return (
|
||||
<div className="bg-theme-light dark:bg-theme-dark-container rounded overflow-hidden my-2 flex flex-col">
|
||||
<div className={classNames('flex px-4 md:px-6 py-2 items-center text-white text-sm', bgClass)}>
|
||||
<div
|
||||
className={classNames(
|
||||
"flex px-4 md:px-6 py-2 items-center text-white text-sm",
|
||||
bgClass
|
||||
)}
|
||||
>
|
||||
{data.name}
|
||||
{icon}
|
||||
</div>
|
||||
{data.result ? (
|
||||
<div className="px-4 md:px-6 py-4 text-sm whitespace-normal">
|
||||
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]} remarkPlugins={[remarkGfm]}>
|
||||
{data.result ?? ''}
|
||||
</ReactMarkdown>
|
||||
<GPTVis
|
||||
components={markdownComponents}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
>
|
||||
{data.result ?? ""}
|
||||
</GPTVis>
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-4 md:px-6 py-4 text-sm">{data.err_msg}</div>
|
||||
|
@ -7,7 +7,7 @@ import Image from 'next/image';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import React, { memo, useMemo } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { GPTVis } from '@antv/gpt-vis';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
@ -42,7 +42,7 @@ type DBGPTView = {
|
||||
err_msg?: string;
|
||||
};
|
||||
|
||||
type MarkdownComponent = Parameters<typeof ReactMarkdown>['0']['components'];
|
||||
type MarkdownComponent = Parameters<typeof GPTVis>["0"]["components"];
|
||||
|
||||
const pluginViewStatusMapper: Record<DBGPTView['status'], { bgClass: string; icon: React.ReactNode }> = {
|
||||
todo: {
|
||||
@ -143,15 +143,24 @@ const ChatContent: React.FC<{
|
||||
const { bgClass, icon } = pluginViewStatusMapper[status] ?? {};
|
||||
return (
|
||||
<div className="bg-white dark:bg-[#212121] rounded-lg overflow-hidden my-2 flex flex-col lg:max-w-[80%]">
|
||||
<div className={classNames('flex px-4 md:px-6 py-2 items-center text-white text-sm', bgClass)}>
|
||||
<div
|
||||
className={classNames(
|
||||
"flex px-4 md:px-6 py-2 items-center text-white text-sm",
|
||||
bgClass
|
||||
)}
|
||||
>
|
||||
{name}
|
||||
{icon}
|
||||
</div>
|
||||
{result ? (
|
||||
<div className="px-4 md:px-6 py-4 text-sm">
|
||||
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]} remarkPlugins={[remarkGfm]}>
|
||||
{result ?? ''}
|
||||
</ReactMarkdown>
|
||||
<GPTVis
|
||||
components={markdownComponents}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
>
|
||||
{result ?? ""}
|
||||
</GPTVis>
|
||||
</div>
|
||||
) : (
|
||||
<div className="px-4 md:px-6 py-4 text-sm">{err_msg}</div>
|
||||
@ -163,45 +172,68 @@ const ChatContent: React.FC<{
|
||||
[cachePluginContext],
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<div className="flex flex-1 gap-3 mt-6">
|
||||
{/* icon */}
|
||||
<div className="flex flex-shrink-0 items-start">{isRobot ? <RobotIcon model={model_name} /> : <UserIcon />}</div>
|
||||
<div className={`flex ${scene === 'chat_agent' && !thinking ? 'flex-1' : ''} overflow-hidden`}>
|
||||
<div className="flex flex-shrink-0 items-start">
|
||||
{isRobot ? <RobotIcon model={model_name} /> : <UserIcon />}
|
||||
</div>
|
||||
<div
|
||||
className={`flex ${
|
||||
scene === "chat_agent" && !thinking ? "flex-1" : ""
|
||||
} overflow-hidden`}
|
||||
>
|
||||
{/* 用户提问 */}
|
||||
{!isRobot && <div className="flex flex-1 items-center text-sm text-[#1c2533] dark:text-white">{typeof context === 'string' && context}</div>}
|
||||
{!isRobot && (
|
||||
<div className="flex flex-1 items-center text-sm text-[#1c2533] dark:text-white">
|
||||
{typeof context === "string" && context}
|
||||
</div>
|
||||
)}
|
||||
{/* ai回答 */}
|
||||
{isRobot && (
|
||||
<div className="flex flex-1 flex-col w-full">
|
||||
<div className="bg-white dark:bg-[rgba(255,255,255,0.16)] p-4 rounded-2xl rounded-tl-none mb-2">
|
||||
{typeof context === 'object' && (
|
||||
{typeof context === "object" && (
|
||||
<div>
|
||||
{`[${context.template_name}]: `}
|
||||
<span className="text-theme-primary cursor-pointer" onClick={onLinkClick}>
|
||||
<span
|
||||
className="text-theme-primary cursor-pointer"
|
||||
onClick={onLinkClick}
|
||||
>
|
||||
<CodeOutlined className="mr-1" />
|
||||
{context.template_introduce || 'More Details'}
|
||||
{context.template_introduce || "More Details"}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{typeof context === 'string' && scene === 'chat_agent' && (
|
||||
<ReactMarkdown components={{ ...markdownComponents }} rehypePlugins={[rehypeRaw]} remarkPlugins={[remarkGfm]}>
|
||||
{formatMarkdownValForAgent(value)}
|
||||
</ReactMarkdown>
|
||||
)}
|
||||
{typeof context === 'string' && scene !== 'chat_agent' && (
|
||||
<ReactMarkdown
|
||||
components={{ ...markdownComponents, ...extraMarkdownComponents }}
|
||||
{typeof context === "string" && scene === "chat_agent" && (
|
||||
<GPTVis
|
||||
components={{ ...markdownComponents }}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
>
|
||||
{formatMarkdownVal(value)}
|
||||
</ReactMarkdown>
|
||||
{formatMarkdownValForAgent(value)}
|
||||
</GPTVis>
|
||||
)}
|
||||
{typeof context === "string" && scene !== "chat_agent" && (
|
||||
<div>
|
||||
<GPTVis
|
||||
components={{
|
||||
...markdownComponents,
|
||||
...extraMarkdownComponents,
|
||||
}}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
>
|
||||
{formatMarkdownVal(value)}
|
||||
</GPTVis>
|
||||
</div>
|
||||
)}
|
||||
{/* 正在思考 */}
|
||||
{thinking && !context && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="flex text-sm text-[#1c2533] dark:text-white">{t('thinking')}</span>
|
||||
<span className="flex text-sm text-[#1c2533] dark:text-white">
|
||||
{t("thinking")}
|
||||
</span>
|
||||
<div className="flex">
|
||||
<div className="w-1 h-1 rounded-full mx-1 animate-pulse1"></div>
|
||||
<div className="w-1 h-1 rounded-full mx-1 animate-pulse2"></div>
|
||||
|
@ -1,14 +1,18 @@
|
||||
import markdownComponents from '@/components/chat/chat-content/config';
|
||||
import React from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { GPTVis } from '@antv/gpt-vis';
|
||||
import rehypeRaw from 'rehype-raw';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
|
||||
const MarkDownContext: React.FC<{ children: string }> = ({ children }) => {
|
||||
return (
|
||||
<ReactMarkdown components={{ ...markdownComponents }} rehypePlugins={[rehypeRaw]} remarkPlugins={[remarkGfm]}>
|
||||
<GPTVis
|
||||
components={{ ...markdownComponents }}
|
||||
rehypePlugins={[rehypeRaw]}
|
||||
remarkPlugins={[remarkGfm]}
|
||||
>
|
||||
{children}
|
||||
</ReactMarkdown>
|
||||
</GPTVis>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -46,7 +46,7 @@ const nextConfig = {
|
||||
}
|
||||
};
|
||||
|
||||
const withTM = require('next-transpile-modules')(['@berryv/g2-react','@antv/g2','react-syntax-highlighter']);
|
||||
const withTM = require('next-transpile-modules')(['@berryv/g2-react','@antv/g2','react-syntax-highlighter', '@antv/gpt-vis']);
|
||||
|
||||
module.exports = withTM({
|
||||
...nextConfig,
|
||||
|
@ -16,6 +16,7 @@
|
||||
"@ant-design/icons": "^5.2.5",
|
||||
"@antv/ava": "3.5.0-alpha.4",
|
||||
"@antv/g2": "^5.1.8",
|
||||
"@antv/gpt-vis": "^0.0.5",
|
||||
"@antv/s2": "^1.51.2",
|
||||
"@berryv/g2-react": "^0.1.0",
|
||||
"@emotion/react": "^11.11.4",
|
||||
@ -55,13 +56,12 @@
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-i18next": "^13.2.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-markdown-editor-lite": "^1.3.4",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-virtualized": "^9.22.5",
|
||||
"reactflow": "^11.10.3",
|
||||
"rehype-raw": "6.1.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"sequelize": "^6.33.0",
|
||||
"sql-formatter": "^12.2.4",
|
||||
"tailwindcss": "3.3.2",
|
||||
@ -69,8 +69,8 @@
|
||||
"typescript": "5.1.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/cytoscape": "^3.21.0",
|
||||
"@types/crypto-js": "^4.1.2",
|
||||
"@types/cytoscape": "^3.21.0",
|
||||
"@types/google-one-tap": "^1.2.4",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/markdown-it": "^14.1.1",
|
||||
@ -94,5 +94,20 @@
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"repository": "https://github.com/eosphoros-ai/DB-GPT-Web.git"
|
||||
"repository": "https://github.com/eosphoros-ai/DB-GPT-Web.git",
|
||||
"resolutions": {
|
||||
"d3-color": "2",
|
||||
"d3-array": "2",
|
||||
"d3-shape": "2",
|
||||
"d3-path": "2",
|
||||
"d3-dsv": "2",
|
||||
"d3-hierarchy": "2",
|
||||
"d3-scale-chromatic": "2",
|
||||
"d3-format": "2",
|
||||
"d3-timer": "2",
|
||||
"d3-dispatch": "2",
|
||||
"d3-quadtree": "2",
|
||||
"d3-force": "2",
|
||||
"d3-geo": "2"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user