* feat: stop toolCall and rename some field. (#46) * perf: node delete tip;pay tip * fix: toolCall cannot save child answer * feat: stop tool * fix: team modal * fix feckbackMoal auth bug (#47) * 简单的支持提示词运行tool。优化workflow模板 (#49) * remove templates * fix: request body undefined * feat: prompt tool run * feat: workflow tamplates modal * perf: plugin start * 4.7 (#50) * fix docker-compose download url (#994) original code is a bad url with '404 NOT FOUND' return. fix docker-compose download url, add 'v' before docker-compose version * Update ai_settings.md (#1000) * Update configuration.md * Update configuration.md * Fix history in classifyQuestion and extract modules (#1012) * Fix history in classifyQuestion and extract modules * Add chatValue2RuntimePrompt import and update text formatting * flow controller to packages * fix: rerank select * modal ui * perf: modal code path * point not sufficient * feat: http url support variable * fix http key * perf: prompt * perf: ai setting modal * simple edit ui --------- Co-authored-by: entorick <entorick11@qq.com> Co-authored-by: liujianglc <liujianglc@163.com> Co-authored-by: Fengrui Liu <liufengrui.work@bytedance.com> * fix team share redirect to login (#51) * feat: support openapi import plugins (#48) * feat: support openapi import plugins * feat: import from url * fix: add body params parse * fix build * fix * fix * fix * tool box ui (#52) * fix: training queue * feat: simple edit tool select * perf: simple edit dataset prompt * fix: chatbox tool ux * feat: quote prompt module * perf: plugin tools sign * perf: model avatar * tool selector ui * feat: max histories * perf: http plugin import (#53) * perf: plugin http import * chatBox ui * perf: name * fix: Node template card (#54) * fix: ts * setting modal * package * package * feat: add plugins search (#57) * feat: add plugins search * perf: change http plugin header input * Yjl (#56) * perf: prompt tool call * perf: chat box ux * doc * doc * price tip * perf: tool selector * ui' * fix: vector queue * fix: empty tool and empty response * fix: empty msg * perf: pg index * perf: ui tip * doc * tool tip --------- Co-authored-by: yst <77910600+yu-and-liu@users.noreply.github.com> Co-authored-by: entorick <entorick11@qq.com> Co-authored-by: liujianglc <liujianglc@163.com> Co-authored-by: Fengrui Liu <liufengrui.work@bytedance.com> Co-authored-by: heheer <71265218+newfish-cmyk@users.noreply.github.com>
159 lines
4.4 KiB
TypeScript
159 lines
4.4 KiB
TypeScript
import React, { useMemo } from 'react';
|
||
import ReactMarkdown from 'react-markdown';
|
||
import 'katex/dist/katex.min.css';
|
||
import RemarkMath from 'remark-math';
|
||
import RemarkBreaks from 'remark-breaks';
|
||
import RehypeKatex from 'rehype-katex';
|
||
import RemarkGfm from 'remark-gfm';
|
||
|
||
import styles from './index.module.scss';
|
||
import dynamic from 'next/dynamic';
|
||
|
||
import { Link, Button } from '@chakra-ui/react';
|
||
import MyTooltip from '../MyTooltip';
|
||
import { useTranslation } from 'next-i18next';
|
||
import { EventNameEnum, eventBus } from '@/web/common/utils/eventbus';
|
||
import MyIcon from '@fastgpt/web/components/common/Icon';
|
||
import { getFileAndOpen } from '@/web/core/dataset/utils';
|
||
import { MARKDOWN_QUOTE_SIGN } from '@fastgpt/global/core/chat/constants';
|
||
|
||
const CodeLight = dynamic(() => import('./CodeLight'));
|
||
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'));
|
||
const MdImage = dynamic(() => import('./img/Image'));
|
||
const EChartsCodeBlock = dynamic(() => import('./img/EChartsCodeBlock'));
|
||
|
||
const ChatGuide = dynamic(() => import('./chat/Guide'));
|
||
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'));
|
||
|
||
export enum CodeClassName {
|
||
guide = 'guide',
|
||
questionGuide = 'questionGuide',
|
||
mermaid = 'mermaid',
|
||
echarts = 'echarts',
|
||
quote = 'quote',
|
||
files = 'files'
|
||
}
|
||
|
||
const Markdown = ({
|
||
source = '',
|
||
showAnimation = false
|
||
}: {
|
||
source?: string;
|
||
showAnimation?: boolean;
|
||
}) => {
|
||
const components = useMemo<any>(
|
||
() => ({
|
||
img: Image,
|
||
pre: 'div',
|
||
p: (pProps: any) => <p {...pProps} dir="auto" />,
|
||
code: Code,
|
||
a: A
|
||
}),
|
||
[]
|
||
);
|
||
|
||
const formatSource = source
|
||
.replace(/\\n/g, '\n ')
|
||
.replace(/(http[s]?:\/\/[^\s,。]+)([。,])/g, '$1 $2')
|
||
.replace(/\n*(\[QUOTE SIGN\]\(.*\))/g, '$1');
|
||
|
||
return (
|
||
<ReactMarkdown
|
||
className={`markdown ${styles.markdown}
|
||
${showAnimation ? `${formatSource ? styles.waitingAnimation : styles.animation}` : ''}
|
||
`}
|
||
remarkPlugins={[RemarkMath, [RemarkGfm, { singleTilde: false }], RemarkBreaks]}
|
||
rehypePlugins={[RehypeKatex]}
|
||
components={components}
|
||
linkTarget={'_blank'}
|
||
>
|
||
{formatSource}
|
||
</ReactMarkdown>
|
||
);
|
||
};
|
||
|
||
export default React.memo(Markdown);
|
||
|
||
const Code = React.memo(function Code(e: any) {
|
||
const { inline, className, children } = e;
|
||
|
||
const match = /language-(\w+)/.exec(className || '');
|
||
const codeType = match?.[1];
|
||
|
||
const strChildren = String(children);
|
||
|
||
const Component = useMemo(() => {
|
||
if (codeType === CodeClassName.mermaid) {
|
||
return <MermaidCodeBlock code={strChildren} />;
|
||
}
|
||
|
||
if (codeType === CodeClassName.guide) {
|
||
return <ChatGuide text={strChildren} />;
|
||
}
|
||
if (codeType === CodeClassName.questionGuide) {
|
||
return <QuestionGuide text={strChildren} />;
|
||
}
|
||
if (codeType === CodeClassName.echarts) {
|
||
return <EChartsCodeBlock code={strChildren} />;
|
||
}
|
||
|
||
return (
|
||
<CodeLight className={className} inline={inline} match={match}>
|
||
{children}
|
||
</CodeLight>
|
||
);
|
||
}, [codeType, className, inline, match, children, strChildren]);
|
||
|
||
return Component;
|
||
});
|
||
|
||
const Image = React.memo(function Image({ src }: { src?: string }) {
|
||
return <MdImage src={src} />;
|
||
});
|
||
const A = React.memo(function A({ children, ...props }: any) {
|
||
const { t } = useTranslation();
|
||
|
||
// empty href link
|
||
if (!props.href && typeof children?.[0] === 'string') {
|
||
const text = useMemo(() => String(children), [children]);
|
||
|
||
return (
|
||
<MyTooltip label={t('core.chat.markdown.Quick Question')}>
|
||
<Button
|
||
variant={'whitePrimary'}
|
||
size={'xs'}
|
||
borderRadius={'md'}
|
||
my={1}
|
||
onClick={() => eventBus.emit(EventNameEnum.sendQuestion, { text })}
|
||
>
|
||
{text}
|
||
</Button>
|
||
</MyTooltip>
|
||
);
|
||
}
|
||
|
||
// quote link
|
||
if (children?.length === 1 && typeof children?.[0] === 'string') {
|
||
const text = String(children);
|
||
if (text === MARKDOWN_QUOTE_SIGN && props.href) {
|
||
return (
|
||
<MyTooltip label={props.href}>
|
||
<MyIcon
|
||
name={'core/chat/quoteSign'}
|
||
transform={'translateY(-2px)'}
|
||
w={'18px'}
|
||
color={'primary.500'}
|
||
cursor={'pointer'}
|
||
_hover={{
|
||
color: 'primary.700'
|
||
}}
|
||
onClick={() => getFileAndOpen(props.href)}
|
||
/>
|
||
</MyTooltip>
|
||
);
|
||
}
|
||
}
|
||
|
||
return <Link {...props}>{children}</Link>;
|
||
});
|