| | |
| | | import { LoadingOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons' |
| | | // import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' |
| | | // import { dark } from 'react-syntax-highlighter/dist/esm/styles/prism' |
| | | import { fetchEventSource } from '@microsoft/fetch-event-source' |
| | | import moment from 'moment' |
| | | |
| | | import Api from '@/api' |
| | |
| | | message.success('复制成功。') |
| | | } |
| | | |
| | | // eslint-disable-next-line |
| | | // let html = marked(item.content, { |
| | | // highlight: (code, language) => { |
| | | // // 通过时间戳生成唯一标识 |
| | | // const codeIndex = parseInt(Date.now() + "") + Math.floor(Math.random() * 10000000) |
| | | // // 格式化第一行是右侧language和 “复制” 按钮; |
| | | // let html = ` |
| | | // <div class=${styles["code-block-header"]}> |
| | | // <span>${language}</span> |
| | | // <span id='copy-btn'data-clipboard-action="copy" data-clipboard-target="#copy${codeIndex}">复制代码</span> |
| | | // </div> |
| | | // ` |
| | | |
| | | // //代码部分 |
| | | // if (code) { |
| | | // try { |
| | | // // 使用 highlight.js 对代码进行高亮显示 |
| | | // const preCode = hljs.highlightAuto(code).value; |
| | | // // 将代码包裹在 textarea 中,由于防止textarea渲染出现问题,这里将 "<" 用 "<" 代替,不影响复制功能 |
| | | // return `<pre class='${styles["hljs-customer"]} hljs'> |
| | | // <code>${preCode}</code> |
| | | // </pre> |
| | | // <textarea style="position: absolute;top: -9999px;left: -9999px;z-index: -9999;" id="copy${codeIndex}">${code.replace(/<\/textarea>/g,"</textarea>")}</textarea>`; |
| | | // } catch (error) { |
| | | |
| | | // } |
| | | // } |
| | | // } |
| | | // }) |
| | | // <SyntaxHighlighter language="javascript" style={dark}> |
| | | // {item.content} |
| | | // </SyntaxHighlighter> |
| | | |
| | | return ( |
| | | <div className="assist-wrap" {...restProps}> |
| | | <DeepSeekLogo/> |
| | | {item.type === 'deepseek-reasoner' ? <div className="top-level"> |
| | | <div className="top-level"> |
| | | <DeepSeekIcon /> |
| | | {item.loading ? '思考中...' : '已深度思考'} |
| | | </div> : null} |
| | | </div> |
| | | {item.loading ? <LoadingOutlined /> : null} |
| | | <div dangerouslySetInnerHTML={{ __html: item.content }}></div> |
| | | {item.oriText ? <MkCopy onClick={copy}/> : null} |
| | |
| | | UserID: '', |
| | | LoginUID: '', |
| | | textInput: '', |
| | | type: 'deepseek-reasoner', // deepseek-chat deepseek-reasoner |
| | | type: 'DeepSeek-R1-Distill-Qwen-32B', // deepseek-chat deepseek-reasoner |
| | | currentChat: {id: '', list: [], title: ''}, |
| | | dpChat: null, |
| | | chats: [] |
| | | } |
| | | |
| | |
| | | id: cell.id, |
| | | content: _content, |
| | | oriText: content, |
| | | type: cell.typecharone || '', |
| | | type: 'deepseek-reasoner', |
| | | role |
| | | } |
| | | }) |
| | |
| | | }) |
| | | } |
| | | |
| | | changeType = () => { |
| | | const { type } = this.state |
| | | // changeType = () => { |
| | | // const { type } = this.state |
| | | |
| | | this.setState({type: type === 'deepseek-reasoner' ? 'deepseek-chat' : 'deepseek-reasoner'}) |
| | | } |
| | | // this.setState({type: type === 'deepseek-reasoner' ? 'deepseek-chat' : 'deepseek-reasoner'}) |
| | | // } |
| | | |
| | | submit = () => { |
| | | const { textInput, currentChat, UserID, LoginUID, loading, type } = this.state |
| | | const { textInput, currentChat, loading, type /*, UserID, LoginUID*/ } = this.state |
| | | |
| | | let val = textInput.replace(/\t+|\v+/g, '').replace(/^\s+|\s+$/g, '') |
| | | |
| | |
| | | let node = document.getElementById('mk-input') |
| | | node && node.blur() |
| | | |
| | | let isNew = false |
| | | // let isNew = false |
| | | const that = this |
| | | let chat = fromJS(currentChat).toJS() |
| | | |
| | | if (!chat.id) { |
| | | chat.id = Utils.getuuid() |
| | | isNew = true |
| | | } |
| | | |
| | | chat.list.push({ role: 'user', content: val, id: Utils.getuuid() }) |
| | | chat.list.push({ role: 'assistant', content: '', type: type, loading: true, id: Utils.getuuid() }) |
| | | |
| | | if (!chat.title) { |
| | | chat.title = val.substr(0, 32) |
| | | } |
| | | chat.loading = true |
| | | |
| | | this.setState({textInput: '', currentChat: chat}) |
| | | let reschat = { role: 'assistant', content: '', type: type, loading: true, id: Utils.getuuid() } |
| | | |
| | | let list = [] |
| | | chat.list.forEach(item => { |
| | | if (!item.loading) { |
| | | list.push({ |
| | | role: item.role, |
| | | content: item.oriText || item.content |
| | | }) |
| | | } |
| | | }) |
| | | this.setState({textInput: '', currentChat: fromJS(chat).toJS(), dpChat: reschat}) |
| | | |
| | | Api.directRequest({ |
| | | url: burl + '/chat/completions', |
| | | method: 'post', |
| | | headers: { 'Content-Type': 'application/json' }, |
| | | data: { |
| | | model: type, |
| | | messages: list, |
| | | stream: false |
| | | } |
| | | }).then(res => { |
| | | let _chat = fromJS(chat).toJS() |
| | | // let list = [] |
| | | // chat.list.forEach(item => { |
| | | // if (!item.loading) { |
| | | // list.push({ |
| | | // role: item.role, |
| | | // content: item.oriText || item.content |
| | | // }) |
| | | // } |
| | | // }) |
| | | |
| | | delete _chat.loading |
| | | _chat.list.pop() |
| | | // const ctrlAbout = new AbortController() |
| | | // const { signal } = ctrlAbout |
| | | let contents = [] |
| | | let timer = setInterval(() => { |
| | | if (contents.length) { |
| | | let _cont = contents.shift() |
| | | |
| | | let _val = '服务器繁忙,请稍后再试。' |
| | | let _html = _val |
| | | let tokens_count = 0 |
| | | if (res.success && res.choices && res.choices[0]) { |
| | | _val = res.choices[0].message.content |
| | | _html = this.getAssistVal(_val) |
| | | } else if (!res.success && res.rawStatusCode === 400 && res.message && /This\s*model's\s*maximum\s*context\s*length/.test(res.message)) { |
| | | _html = `当前对话已超出${type === 'deepseek-reasoner' ? '深度思考的' : ''}最大长度限制,开启一个新对话继续思考吧~` |
| | | _val = _html |
| | | } |
| | | |
| | | if (res.usage && res.usage.total_tokens) { |
| | | tokens_count = res.usage.total_tokens |
| | | } |
| | | |
| | | _chat.list = _chat.list.map(item => { |
| | | delete item.loading |
| | | delete item.step |
| | | return item |
| | | }) |
| | | |
| | | _chat.list.push({ role: 'assistant', content: _html, oriText: _val, type: type, step: true, id: Utils.getuuid() }) |
| | | |
| | | this.setState({currentChat: _chat}) |
| | | |
| | | Api.genericInterface({ |
| | | func: 's_deepseek_ai', |
| | | rduri: burl + '/webapi/dostars', |
| | | file_url: '', |
| | | userid: UserID, |
| | | LoginUID: LoginUID, |
| | | u_id: sessionStorage.getItem('UserID'), |
| | | content: window.btoa(window.encodeURIComponent(_val)), |
| | | title: window.btoa(window.encodeURIComponent(chat.title)), |
| | | data_type: 'reply', |
| | | tokens_count: tokens_count, |
| | | version: type, |
| | | ID: chat.id |
| | | }).then(r => { |
| | | if (!r.status) { |
| | | notification.error({ |
| | | top: 92, |
| | | message: r.message, |
| | | duration: 10 |
| | | }) |
| | | } else if (isNew) { |
| | | this.getList() |
| | | if (_cont === '[CLOSE]') { |
| | | clearInterval(timer) |
| | | chat.list.push(reschat) |
| | | that.setState({dpChat: null, currentChat: chat, loading: false}) |
| | | } else { |
| | | reschat.content += _cont |
| | | |
| | | if (_cont === '</think>') { |
| | | delete reschat.loading |
| | | } |
| | | |
| | | that.setState({dpChat: reschat}) |
| | | } |
| | | }) |
| | | }) |
| | | } |
| | | }, 30) |
| | | |
| | | Api.genericInterface({ |
| | | func: 's_deepseek_ai', |
| | | rduri: burl + '/webapi/dostars', |
| | | file_url: '', |
| | | userid: UserID, |
| | | LoginUID: LoginUID, |
| | | u_id: sessionStorage.getItem('UserID'), |
| | | content: window.btoa(window.encodeURIComponent(val)), |
| | | title: window.btoa(window.encodeURIComponent(chat.title)), |
| | | data_type: 'request', |
| | | version: type, |
| | | tokens_count: 0, |
| | | ID: chat.id |
| | | }).then(result => { |
| | | if (!result.status) { |
| | | notification.error({ |
| | | top: 92, |
| | | message: result.message, |
| | | duration: 10 |
| | | }) |
| | | fetchEventSource(burl + '/chat', { |
| | | mode: 'cors', |
| | | method: 'POST', |
| | | headers: { |
| | | 'Content-Type': 'application/json', |
| | | 'Accept': 'text/event-stream' |
| | | }, |
| | | body: JSON.stringify({ |
| | | chat_session_id: chat.id || '', |
| | | model: type, |
| | | prompt: val, |
| | | stream: true |
| | | }), |
| | | onmessage(event) { |
| | | if (event.data) { |
| | | if (event.data === '[CLOSE]') { |
| | | contents.push(event.data) |
| | | } else { |
| | | let data = JSON.parse(event.data) |
| | | if (!chat.id && data.id) { |
| | | chat.id = data.id |
| | | } |
| | | contents.push(data.content) |
| | | } |
| | | } |
| | | }, |
| | | onclose() {}, |
| | | onerror(error) { |
| | | console.info(error) |
| | | } |
| | | }) |
| | | |
| | | // Api.directRequest({ |
| | | // url: burl + '/chat/completions', |
| | | // method: 'post', |
| | | // headers: { 'Content-Type': 'application/json' }, |
| | | // data: { |
| | | // model: type, |
| | | // messages: list, |
| | | // stream: false |
| | | // } |
| | | // }).then(res => { |
| | | // let _chat = fromJS(chat).toJS() |
| | | |
| | | // delete _chat.loading |
| | | // _chat.list.pop() |
| | | |
| | | // let _val = '服务器繁忙,请稍后再试。' |
| | | // let _html = _val |
| | | // let tokens_count = 0 |
| | | // if (res.success && res.choices && res.choices[0]) { |
| | | // _val = res.choices[0].message.content |
| | | // _html = this.getAssistVal(_val) |
| | | // } else if (!res.success && res.rawStatusCode === 400 && res.message && /This\s*model's\s*maximum\s*context\s*length/.test(res.message)) { |
| | | // _html = `当前对话已超出${type === 'deepseek-reasoner' ? '深度思考的' : ''}最大长度限制,开启一个新对话继续思考吧~` |
| | | // _val = _html |
| | | // } |
| | | |
| | | // if (res.usage && res.usage.total_tokens) { |
| | | // tokens_count = res.usage.total_tokens |
| | | // } |
| | | |
| | | // _chat.list = _chat.list.map(item => { |
| | | // delete item.loading |
| | | // delete item.step |
| | | // return item |
| | | // }) |
| | | |
| | | // _chat.list.push({ role: 'assistant', content: _html, oriText: _val, type: type, step: true, id: Utils.getuuid() }) |
| | | |
| | | // this.setState({currentChat: _chat}) |
| | | |
| | | // Api.genericInterface({ |
| | | // func: 's_deepseek_ai', |
| | | // rduri: burl + '/webapi/dostars', |
| | | // file_url: '', |
| | | // userid: UserID, |
| | | // LoginUID: LoginUID, |
| | | // u_id: sessionStorage.getItem('UserID'), |
| | | // content: window.btoa(window.encodeURIComponent(_val)), |
| | | // title: window.btoa(window.encodeURIComponent(chat.title)), |
| | | // data_type: 'reply', |
| | | // tokens_count: tokens_count, |
| | | // version: type, |
| | | // ID: chat.id |
| | | // }).then(r => { |
| | | // if (!r.status) { |
| | | // notification.error({ |
| | | // top: 92, |
| | | // message: r.message, |
| | | // duration: 10 |
| | | // }) |
| | | // } else if (isNew) { |
| | | // this.getList() |
| | | // } |
| | | // }) |
| | | // }) |
| | | |
| | | // Api.genericInterface({ |
| | | // func: 's_deepseek_ai', |
| | | // rduri: burl + '/webapi/dostars', |
| | | // file_url: '', |
| | | // userid: UserID, |
| | | // LoginUID: LoginUID, |
| | | // u_id: sessionStorage.getItem('UserID'), |
| | | // content: window.btoa(window.encodeURIComponent(val)), |
| | | // title: window.btoa(window.encodeURIComponent(chat.title)), |
| | | // data_type: 'request', |
| | | // version: type, |
| | | // tokens_count: 0, |
| | | // ID: chat.id |
| | | // }).then(result => { |
| | | // if (!result.status) { |
| | | // notification.error({ |
| | | // top: 92, |
| | | // message: result.message, |
| | | // duration: 10 |
| | | // }) |
| | | // } |
| | | // }) |
| | | } |
| | | |
| | | getAssistVal = (val) => { |
| | |
| | | } |
| | | |
| | | render () { |
| | | const { loading, textInput, type, currentChat, chats } = this.state |
| | | const { loading, textInput, currentChat, chats, dpChat } = this.state |
| | | |
| | | let able = textInput && !/^\s+$/.test(textInput) |
| | | let empty = !currentChat.id |
| | | let empty = currentChat.list.length === 0 |
| | | |
| | | return ( |
| | | <div className="mk-deepseek-wrap"> |
| | |
| | | return <UserChat key={item.id} item={item}/> |
| | | } |
| | | })} |
| | | {dpChat ? <DpChat key='chat' item={dpChat}/> : null} |
| | | </div> : null} |
| | | {!empty ? <div className="chat-reset"> |
| | | <div className="wrap" onClick={this.newContent}> |
| | |
| | | <TextArea id="mk-input" value={textInput} autoSize={{minRows: 2, maxRows: 12}} placeholder="给 DeepSeek 发送消息 " onChange={this.changeVal} onPressEnter={this.submit}/> |
| | | </div> |
| | | <div className="ec4f5d61"> |
| | | {type === 'deepseek-chat' ? <Tooltip placement="left" title="调用新模型 DeekSeek-R1,解决推理问题"> |
| | | {/* {type === 'deepseek-chat' ? <Tooltip placement="left" title="调用新模型 DeekSeek-R1,解决推理问题"> |
| | | <div className="ds-button" onClick={this.changeType}> |
| | | <DeepSeekIcon/> |
| | | <span className="text">深度思考 (R1)</span> |
| | |
| | | </Tooltip> : <div className="ds-button active" onClick={this.changeType}> |
| | | <DeepSeekIcon/> |
| | | <span className="text">深度思考 (R1)</span> |
| | | </div>} |
| | | </div>} */} |
| | | {/* <div className="ds-button"> |
| | | <span className="ds-icon"> |
| | | <svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="currentColor" strokeWidth="1.8"></circle><path d="M10 1c1.657 0 3 4.03 3 9s-1.343 9-3 9M10 19c-1.657 0-3-4.03-3-9s1.343-9 3-9M1 10h18" stroke="currentColor" strokeWidth="1.8"></path></svg> |