feat: @model regenerate message
This commit is contained in:
parent
de41199f7e
commit
b0a3d705ff
@ -144,9 +144,11 @@
|
||||
height: auto;
|
||||
}
|
||||
|
||||
a {
|
||||
a,
|
||||
.link {
|
||||
color: var(--color-link);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
|
||||
@ -59,10 +59,9 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
||||
const onRegenerate = useCallback(
|
||||
(model: Model) => {
|
||||
setModel(model)
|
||||
onDeleteMessage?.(message)
|
||||
setTimeout(() => EventEmitter.emit(EVENT_NAMES.REGENERATE_MESSAGE), 100)
|
||||
setTimeout(() => EventEmitter.emit(EVENT_NAMES.REGENERATE_MESSAGE, model), 100)
|
||||
},
|
||||
[message, onDeleteMessage, setModel]
|
||||
[setModel]
|
||||
)
|
||||
|
||||
const getUserName = useCallback(() => {
|
||||
@ -137,6 +136,15 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
||||
{copied && <CheckOutlined style={{ color: 'var(--color-primary)' }} />}
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
{canRegenerate && (
|
||||
<SelectModelDropdown model={model} onSelect={onRegenerate} placement="topRight">
|
||||
<Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}>
|
||||
<ActionButton>
|
||||
<SyncOutlined />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
</SelectModelDropdown>
|
||||
)}
|
||||
<Popconfirm
|
||||
title={t('message.message.delete.content')}
|
||||
okButtonProps={{ danger: true }}
|
||||
@ -148,15 +156,6 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
</Popconfirm>
|
||||
{canRegenerate && (
|
||||
<SelectModelDropdown model={model} onSelect={onRegenerate} placement="topRight">
|
||||
<Tooltip title={t('common.regenerate')} mouseEnterDelay={0.8}>
|
||||
<ActionButton>
|
||||
<SyncOutlined />
|
||||
</ActionButton>
|
||||
</Tooltip>
|
||||
</SelectModelDropdown>
|
||||
)}
|
||||
{!isUserMessage && (
|
||||
<Dropdown menu={{ items: dropdownItems }} trigger={['click']} placement="topRight" arrow>
|
||||
<ActionButton>
|
||||
|
||||
@ -2,12 +2,13 @@ import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||
import { useProviderByAssistant } from '@renderer/hooks/useProvider'
|
||||
import { fetchChatCompletion, fetchMessagesSummary } from '@renderer/services/api'
|
||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||
import { filterAtMessages } from '@renderer/services/message'
|
||||
import LocalStorage from '@renderer/services/storage'
|
||||
import { Assistant, Message, Topic } from '@renderer/types'
|
||||
import { estimateHistoryTokenCount, runAsyncFunction } from '@renderer/utils'
|
||||
import { Assistant, Message, Model, Topic } from '@renderer/types'
|
||||
import { estimateHistoryTokenCount, getBriefInfo, runAsyncFunction, uuid } from '@renderer/utils'
|
||||
import { t } from 'i18next'
|
||||
import localforage from 'localforage'
|
||||
import { debounce, reverse } from 'lodash'
|
||||
import { debounce, last, reverse } from 'lodash'
|
||||
import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import styled from 'styled-components'
|
||||
|
||||
@ -75,8 +76,18 @@ const Messages: FC<Props> = ({ assistant, topic }) => {
|
||||
onSendMessage(msg)
|
||||
setTimeout(() => EventEmitter.emit(EVENT_NAMES.AI_AUTO_RENAME), 100)
|
||||
}),
|
||||
EventEmitter.on(EVENT_NAMES.REGENERATE_MESSAGE, async () => {
|
||||
fetchChatCompletion({ assistant, messages: messages, topic, onResponse: setLastMessage })
|
||||
EventEmitter.on(EVENT_NAMES.REGENERATE_MESSAGE, async (model: Model) => {
|
||||
const lastUserMessage = last(filterAtMessages(messages).filter((m) => m.role === 'user'))
|
||||
if (lastUserMessage) {
|
||||
const content = `[@${model.name}](#) ${getBriefInfo(lastUserMessage.content)}`
|
||||
onSendMessage({ ...lastUserMessage, id: uuid(), type: '@', content })
|
||||
fetchChatCompletion({
|
||||
assistant,
|
||||
topic,
|
||||
messages: [...messages, lastUserMessage],
|
||||
onResponse: setLastMessage
|
||||
})
|
||||
}
|
||||
}),
|
||||
EventEmitter.on(EVENT_NAMES.AI_AUTO_RENAME, autoRenameTopic),
|
||||
EventEmitter.on(EVENT_NAMES.CLEAR_MESSAGES, () => {
|
||||
|
||||
@ -1,7 +1,11 @@
|
||||
import { omit } from 'lodash'
|
||||
import React from 'react'
|
||||
|
||||
const Link: React.FC = (props) => {
|
||||
const Link: React.FC = (props: React.AnchorHTMLAttributes<HTMLAnchorElement>) => {
|
||||
if (props.href?.startsWith('#')) {
|
||||
return <span className="link">{props.children}</span>
|
||||
}
|
||||
|
||||
return <a {...omit(props, 'node')} target="_blank" rel="noreferrer" onClick={(e) => e.stopPropagation()} />
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
getTranslateModel
|
||||
} from './assistant'
|
||||
import { EVENT_NAMES, EventEmitter } from './event'
|
||||
import { filterAtMessages } from './message'
|
||||
import ProviderSDK from './ProviderSDK'
|
||||
|
||||
export async function fetchChatCompletion({
|
||||
@ -50,7 +51,7 @@ export async function fetchChatCompletion({
|
||||
onResponse({ ...message })
|
||||
|
||||
try {
|
||||
await providerSdk.completions(messages, assistant, ({ text, usage }) => {
|
||||
await providerSdk.completions(filterAtMessages(messages), assistant, ({ text, usage }) => {
|
||||
message.content = message.content + text || ''
|
||||
message.usage = usage
|
||||
onResponse({ ...message, status: 'pending' })
|
||||
|
||||
5
src/renderer/src/services/message.ts
Normal file
5
src/renderer/src/services/message.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import { Message } from '@renderer/types'
|
||||
|
||||
export const filterAtMessages = (messages: Message[]) => {
|
||||
return messages.filter((message) => message.type !== '@')
|
||||
}
|
||||
@ -25,6 +25,7 @@ export type Message = {
|
||||
createdAt: string
|
||||
status: 'sending' | 'pending' | 'success' | 'paused' | 'error'
|
||||
usage?: OpenAI.Completions.CompletionUsage
|
||||
type?: 'text' | '@'
|
||||
}
|
||||
|
||||
export type Topic = {
|
||||
|
||||
@ -230,3 +230,24 @@ export function convertMathFormula(input) {
|
||||
// 使用正则表达式匹配并替换公式格式
|
||||
return input.replaceAll(/\\\[/g, '$$$$').replaceAll(/\\\]/g, '$$$$')
|
||||
}
|
||||
|
||||
export function getBriefInfo(text: string, maxLength: number = 50): string {
|
||||
// 去除空行
|
||||
const noEmptyLinesText = text.replace(/\n\s*\n/g, '\n')
|
||||
|
||||
// 检查文本是否超过最大长度
|
||||
if (noEmptyLinesText.length <= maxLength) {
|
||||
return noEmptyLinesText
|
||||
}
|
||||
|
||||
// 找到最近的单词边界
|
||||
let truncatedText = noEmptyLinesText.slice(0, maxLength)
|
||||
const lastSpaceIndex = truncatedText.lastIndexOf(' ')
|
||||
|
||||
if (lastSpaceIndex !== -1) {
|
||||
truncatedText = truncatedText.slice(0, lastSpaceIndex)
|
||||
}
|
||||
|
||||
// 截取前面的内容,并在末尾添加 "..."
|
||||
return truncatedText + '...'
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user