style: markdown render
This commit is contained in:
@@ -70,7 +70,6 @@ const MarkdownViewer: React.FC<MarkdownViewerProps> = ({
|
|||||||
|
|
||||||
const renderItem = useCallback(
|
const renderItem = useCallback(
|
||||||
(token: any, render: any) => {
|
(token: any, render: any) => {
|
||||||
console.log('token====', token);
|
|
||||||
if (!reDefineTypes.includes(token.type)) {
|
if (!reDefineTypes.includes(token.type)) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
|
|||||||
@@ -138,5 +138,6 @@ export default {
|
|||||||
'playground.image.download': 'Download Image',
|
'playground.image.download': 'Download Image',
|
||||||
'playground.image.generate': 'Generate',
|
'playground.image.generate': 'Generate',
|
||||||
'playground.image.edit': 'Edit',
|
'playground.image.edit': 'Edit',
|
||||||
'playground.image.fitview': 'Fit View'
|
'playground.image.fitview': 'Fit View',
|
||||||
|
'playground.chat.aithought': 'CoT'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -133,5 +133,6 @@ export default {
|
|||||||
'playground.image.download': '下载图片',
|
'playground.image.download': '下载图片',
|
||||||
'playground.image.generate': '生成图片',
|
'playground.image.generate': '生成图片',
|
||||||
'playground.image.edit': '编辑图片',
|
'playground.image.edit': '编辑图片',
|
||||||
'playground.image.fitview': '适应视图'
|
'playground.image.fitview': '适应视图',
|
||||||
|
'playground.chat.aithought': '思考过程'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
|
|||||||
reasoningContent: string;
|
reasoningContent: string;
|
||||||
}) => {
|
}) => {
|
||||||
if (data.reasoningContent && !data.content) {
|
if (data.reasoningContent && !data.content) {
|
||||||
return `<think>${data.reasoningContent}${data.content}`;
|
return `<think>${data.reasoningContent}`;
|
||||||
}
|
}
|
||||||
if (data.reasoningContent && data.content) {
|
if (data.reasoningContent && data.content) {
|
||||||
return `<think>${data.reasoningContent}</think>${data.content}`;
|
return `<think>${data.reasoningContent}</think>${data.content}`;
|
||||||
@@ -127,15 +127,18 @@ const GroundLeft: React.FC<MessageProps> = forwardRef((props, ref) => {
|
|||||||
_.get(chunk, 'choices.0.delta.reasoning_content', '');
|
_.get(chunk, 'choices.0.delta.reasoning_content', '');
|
||||||
contentRef.current =
|
contentRef.current =
|
||||||
contentRef.current + _.get(chunk, 'choices.0.delta.content', '');
|
contentRef.current + _.get(chunk, 'choices.0.delta.content', '');
|
||||||
|
|
||||||
|
const content = formatContent({
|
||||||
|
content: contentRef.current,
|
||||||
|
reasoningContent: reasonContentRef.current
|
||||||
|
});
|
||||||
|
|
||||||
setMessageList([
|
setMessageList([
|
||||||
...messageList,
|
...messageList,
|
||||||
...currentMessageRef.current,
|
...currentMessageRef.current,
|
||||||
{
|
{
|
||||||
role: Roles.Assistant,
|
role: Roles.Assistant,
|
||||||
content: formatContent({
|
content: content,
|
||||||
content: contentRef.current,
|
|
||||||
reasoningContent: reasonContentRef.current
|
|
||||||
}),
|
|
||||||
uid: messageId.current
|
uid: messageId.current
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import _ from 'lodash';
|
|||||||
import 'overlayscrollbars/overlayscrollbars.css';
|
import 'overlayscrollbars/overlayscrollbars.css';
|
||||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import CompareContext from '../../config/compare-context';
|
import CompareContext from '../../config/compare-context';
|
||||||
import { MessageItem, ModelSelectionItem } from '../../config/types';
|
import {
|
||||||
|
MessageItem,
|
||||||
|
MessageItemAction,
|
||||||
|
ModelSelectionItem
|
||||||
|
} from '../../config/types';
|
||||||
import '../../style/multiple-chat.less';
|
import '../../style/multiple-chat.less';
|
||||||
import EmptyModels from '../empty-models';
|
import EmptyModels from '../empty-models';
|
||||||
import MessageInput from '../message-input';
|
import MessageInput from '../message-input';
|
||||||
@@ -42,6 +46,11 @@ const MultiCompare: React.FC<MultiCompareProps> = ({ modelList, loaded }) => {
|
|||||||
const modelRefs = useRef<any>({});
|
const modelRefs = useRef<any>({});
|
||||||
const chatListScrollRef = useRef<any>(null);
|
const chatListScrollRef = useRef<any>(null);
|
||||||
const boxHeight = 'calc(100vh - 72px)';
|
const boxHeight = 'calc(100vh - 72px)';
|
||||||
|
const [actions, setActions] = useState<MessageItemAction[]>([
|
||||||
|
'upload',
|
||||||
|
'delete',
|
||||||
|
'copy'
|
||||||
|
]);
|
||||||
|
|
||||||
const isLoading = useMemo(() => {
|
const isLoading = useMemo(() => {
|
||||||
const modelRefList = Object.getOwnPropertySymbols(loadingStatus);
|
const modelRefList = Object.getOwnPropertySymbols(loadingStatus);
|
||||||
@@ -228,6 +237,15 @@ const MultiCompare: React.FC<MultiCompareProps> = ({ modelList, loaded }) => {
|
|||||||
handleUpdateModelList(value);
|
handleUpdateModelList(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOnCheck = (e: any) => {
|
||||||
|
const checked = e.target.checked;
|
||||||
|
if (checked) {
|
||||||
|
setActions(['upload', 'delete', 'copy', 'markdown']);
|
||||||
|
} else {
|
||||||
|
setActions(['upload', 'delete', 'copy']);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
modelRefs.current = {};
|
modelRefs.current = {};
|
||||||
let list = _.take(modelList, spans.count);
|
let list = _.take(modelList, spans.count);
|
||||||
@@ -268,6 +286,7 @@ const MultiCompare: React.FC<MultiCompareProps> = ({ modelList, loaded }) => {
|
|||||||
globalParams,
|
globalParams,
|
||||||
loadingStatus,
|
loadingStatus,
|
||||||
modelFullList: modelList,
|
modelFullList: modelList,
|
||||||
|
actions,
|
||||||
handleApplySystemChangeToAll,
|
handleApplySystemChangeToAll,
|
||||||
setGlobalParams,
|
setGlobalParams,
|
||||||
setLoadingStatus: handleSetLoadingStatus,
|
setLoadingStatus: handleSetLoadingStatus,
|
||||||
@@ -302,6 +321,18 @@ const MultiCompare: React.FC<MultiCompareProps> = ({ modelList, loaded }) => {
|
|||||||
updateLayout={updateLayout}
|
updateLayout={updateLayout}
|
||||||
setModelSelections={handleUpdateModelSelections}
|
setModelSelections={handleUpdateModelSelections}
|
||||||
presetPrompt={handlePresetPrompt}
|
presetPrompt={handlePresetPrompt}
|
||||||
|
actions={[
|
||||||
|
'clear',
|
||||||
|
'layout',
|
||||||
|
'role',
|
||||||
|
'upload',
|
||||||
|
'add',
|
||||||
|
'paste',
|
||||||
|
'check'
|
||||||
|
]}
|
||||||
|
defaultChecked={false}
|
||||||
|
checkLabel="Markdown"
|
||||||
|
onCheck={handleOnCheck}
|
||||||
defaultSize={{
|
defaultSize={{
|
||||||
minRows: 5,
|
minRows: 5,
|
||||||
maxRows: 5
|
maxRows: 5
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
|||||||
handleDeleteModel,
|
handleDeleteModel,
|
||||||
handleApplySystemChangeToAll,
|
handleApplySystemChangeToAll,
|
||||||
modelFullList,
|
modelFullList,
|
||||||
loadingStatus
|
loadingStatus,
|
||||||
|
actions
|
||||||
} = useContext(CompareContext);
|
} = useContext(CompareContext);
|
||||||
const intl = useIntl();
|
const intl = useIntl();
|
||||||
const isApplyToAllModels = useRef(false);
|
const isApplyToAllModels = useRef(false);
|
||||||
@@ -93,7 +94,7 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
|||||||
reasoningContent: string;
|
reasoningContent: string;
|
||||||
}) => {
|
}) => {
|
||||||
if (data.reasoningContent && !data.content) {
|
if (data.reasoningContent && !data.content) {
|
||||||
return `<think>${data.reasoningContent}${data.content}`;
|
return `<think>${data.reasoningContent}`;
|
||||||
}
|
}
|
||||||
if (data.reasoningContent && data.content) {
|
if (data.reasoningContent && data.content) {
|
||||||
return `<think>${data.reasoningContent}</think>${data.content}`;
|
return `<think>${data.reasoningContent}</think>${data.content}`;
|
||||||
@@ -117,15 +118,16 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
|||||||
contentRef.current =
|
contentRef.current =
|
||||||
contentRef.current + _.get(chunk, 'choices.0.delta.content', '');
|
contentRef.current + _.get(chunk, 'choices.0.delta.content', '');
|
||||||
|
|
||||||
|
const content = formatContent({
|
||||||
|
content: contentRef.current,
|
||||||
|
reasoningContent: reasonContentRef.current
|
||||||
|
});
|
||||||
setMessageList([
|
setMessageList([
|
||||||
...messageList,
|
...messageList,
|
||||||
...currentMessageRef.current,
|
...currentMessageRef.current,
|
||||||
{
|
{
|
||||||
role: Roles.Assistant,
|
role: Roles.Assistant,
|
||||||
content: formatContent({
|
content,
|
||||||
content: contentRef.current,
|
|
||||||
reasoningContent: reasonContentRef.current
|
|
||||||
}),
|
|
||||||
uid: messageId.current
|
uid: messageId.current
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
@@ -476,6 +478,7 @@ const ModelItem: React.FC<ModelItemProps> = forwardRef(
|
|||||||
<MessageContent
|
<MessageContent
|
||||||
messageList={messageList}
|
messageList={messageList}
|
||||||
setMessageList={setMessageList}
|
setMessageList={setMessageList}
|
||||||
|
actions={actions}
|
||||||
editable={true}
|
editable={true}
|
||||||
/>
|
/>
|
||||||
<Spin
|
<Spin
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import IconFont from '@/components/icon-font';
|
import IconFont from '@/components/icon-font';
|
||||||
|
import { DownOutlined } from '@ant-design/icons';
|
||||||
|
import { useIntl } from '@umijs/max';
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
import React from 'react';
|
import React, { useState } from 'react';
|
||||||
import '../../style/think-content.less';
|
import '../../style/think-content.less';
|
||||||
|
|
||||||
interface ThinkContentProps {
|
interface ThinkContentProps {
|
||||||
@@ -8,15 +10,29 @@ interface ThinkContentProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ThinkContent: React.FC<ThinkContentProps> = ({ content }) => {
|
const ThinkContent: React.FC<ThinkContentProps> = ({ content }) => {
|
||||||
|
const intl = useIntl();
|
||||||
|
const [collapsed, setCollapsed] = useState(false);
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{content ? (
|
{content ? (
|
||||||
<div className="think-wrapper">
|
<div className="think-wrapper">
|
||||||
<IconFont type="icon-AIzhineng" className="m-l-4" />
|
<div className="btn-collapse">
|
||||||
<Button size="small" type="text">
|
<Button
|
||||||
AI Thought...
|
size="small"
|
||||||
</Button>
|
type="text"
|
||||||
<div className="think-content">{content}</div>
|
className="flex-center"
|
||||||
|
variant={collapsed ? 'filled' : undefined}
|
||||||
|
color="default"
|
||||||
|
onClick={() => setCollapsed(!collapsed)}
|
||||||
|
>
|
||||||
|
<IconFont type="icon-AIzhineng" />
|
||||||
|
<span>
|
||||||
|
{intl.formatMessage({ id: 'playground.chat.aithought' })}
|
||||||
|
</span>
|
||||||
|
<DownOutlined rotate={collapsed ? 0 : 180} className="m-l-10" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
{!collapsed && <div className="think-content">{content}</div>}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
class ThinkParser {
|
class ThinkParser {
|
||||||
|
private lastCheckedIndex: number;
|
||||||
|
private collecting: boolean;
|
||||||
thought: string;
|
thought: string;
|
||||||
result: string;
|
result: string;
|
||||||
collecting: boolean;
|
|
||||||
lastCheckedIndex: number;
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.thought = '';
|
this.thought = '';
|
||||||
@@ -12,40 +12,33 @@ class ThinkParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
parse(chunk: string) {
|
parse(chunk: string) {
|
||||||
if (this.lastCheckedIndex < chunk.length) {
|
while (this.lastCheckedIndex < chunk.length) {
|
||||||
const currentChunk = chunk.substring(this.lastCheckedIndex);
|
|
||||||
|
|
||||||
if (!this.collecting) {
|
if (!this.collecting) {
|
||||||
// find <think> tag
|
let startIndex = chunk.indexOf('<think>', this.lastCheckedIndex);
|
||||||
let startIndex = currentChunk.indexOf('<think>');
|
|
||||||
if (startIndex !== -1) {
|
if (startIndex !== -1) {
|
||||||
// handle text before <think> tag
|
this.result += chunk.substring(this.lastCheckedIndex, startIndex);
|
||||||
this.result += currentChunk.substring(0, startIndex);
|
|
||||||
// handle thought part
|
|
||||||
this.thought += currentChunk.substring(startIndex + 7);
|
|
||||||
this.collecting = true;
|
this.collecting = true;
|
||||||
|
this.lastCheckedIndex = startIndex + 7;
|
||||||
} else {
|
} else {
|
||||||
// if no <think> tag found, just append the whole chunk to result
|
this.result += chunk.substring(this.lastCheckedIndex);
|
||||||
this.result += currentChunk;
|
this.lastCheckedIndex = chunk.length;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// find </think> tag
|
let endIndex = chunk.indexOf('</think>', this.lastCheckedIndex);
|
||||||
let endIndex = currentChunk.indexOf('</think>');
|
|
||||||
if (endIndex !== -1) {
|
if (endIndex !== -1) {
|
||||||
// handle text before </think> tag
|
this.thought += chunk.substring(this.lastCheckedIndex, endIndex);
|
||||||
this.thought += currentChunk.substring(0, endIndex);
|
|
||||||
// handle result part
|
|
||||||
this.result += currentChunk.substring(endIndex + 8);
|
|
||||||
|
|
||||||
this.collecting = false;
|
this.collecting = false;
|
||||||
|
this.lastCheckedIndex = endIndex + 8;
|
||||||
} else {
|
} else {
|
||||||
this.thought += currentChunk;
|
this.thought += chunk.substring(this.lastCheckedIndex);
|
||||||
|
this.lastCheckedIndex = chunk.length;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
this.lastCheckedIndex = chunk.length;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { thought: this.thought.trim(), result: this.result };
|
return { thought: this.thought.trimStart(), result: this.result };
|
||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
import { MessageItemAction } from './types';
|
||||||
|
|
||||||
interface CompareContextProps {
|
interface CompareContextProps {
|
||||||
spans: {
|
spans: {
|
||||||
span: number;
|
span: number;
|
||||||
count: number;
|
count: number;
|
||||||
};
|
};
|
||||||
|
actions?: MessageItemAction[];
|
||||||
systemMessage?: string;
|
systemMessage?: string;
|
||||||
globalParams: Record<string, any>;
|
globalParams: Record<string, any>;
|
||||||
loadingStatus: Record<symbol, boolean>;
|
loadingStatus: Record<symbol, boolean>;
|
||||||
|
|||||||
@@ -1,6 +1,14 @@
|
|||||||
.content-item {
|
.content-item {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
|
||||||
|
.markdown-viewer {
|
||||||
|
h4 {
|
||||||
|
margin-top: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-size: var(--font-size-middle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&-role {
|
&-role {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
.think-wrapper {
|
.think-wrapper {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
color: var(--ant-color-text-tertiary);
|
color: var(--ant-color-text-tertiary);
|
||||||
border-left: 2px solid var(--ant-color-fill-secondary);
|
|
||||||
|
.btn-collapse {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
border-radius: var(--border-radius-base);
|
||||||
|
width: max-content;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.think-content {
|
.think-content {
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
padding: 6px 4px;
|
padding: 12px;
|
||||||
|
border-left: 2px solid var(--ant-color-fill-secondary);
|
||||||
background-color: var(--ant-color-fill-tertiary);
|
background-color: var(--ant-color-fill-tertiary);
|
||||||
color: var(--ant-color-text-tertiary);
|
color: var(--ant-color-text-tertiary);
|
||||||
border-radius: var(--border-radius-base);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user