feat: Added translations, new column, and UI improvements.
- Added translations for a new field. - Added new column for file count in the FilesPage view. - Improved handling of message tokens in the UI. - Added functionality to display message tokens for messages with specific roles. - Added window style selection and styling adjustments to the General Settings page. - Added support for vision models in OpenAIProvider.
This commit is contained in:
parent
f1c8922752
commit
33ac0937df
@ -113,6 +113,7 @@ const resources = {
|
|||||||
file: 'File',
|
file: 'File',
|
||||||
name: 'Name',
|
name: 'Name',
|
||||||
size: 'Size',
|
size: 'Size',
|
||||||
|
count: 'Count',
|
||||||
created_at: 'Created At'
|
created_at: 'Created At'
|
||||||
},
|
},
|
||||||
agents: {
|
agents: {
|
||||||
@ -385,6 +386,7 @@ const resources = {
|
|||||||
file: '文件',
|
file: '文件',
|
||||||
name: '文件名',
|
name: '文件名',
|
||||||
size: '大小',
|
size: '大小',
|
||||||
|
count: '文件数',
|
||||||
created_at: '创建时间'
|
created_at: '创建时间'
|
||||||
},
|
},
|
||||||
agents: {
|
agents: {
|
||||||
|
|||||||
@ -22,6 +22,7 @@ const FilesPage: FC = () => {
|
|||||||
file: isImage ? ImageView : <FileNameText className="text-nowrap">{file.origin_name}</FileNameText>,
|
file: isImage ? ImageView : <FileNameText className="text-nowrap">{file.origin_name}</FileNameText>,
|
||||||
name: <a href={'file://' + getFileDirectory(file.path)}>{file.origin_name}</a>,
|
name: <a href={'file://' + getFileDirectory(file.path)}>{file.origin_name}</a>,
|
||||||
size: `${(file.size / 1024 / 1024).toFixed(2)} MB`,
|
size: `${(file.size / 1024 / 1024).toFixed(2)} MB`,
|
||||||
|
count: file.count,
|
||||||
created_at: dayjs(file.created_at).format('MM-DD HH:mm')
|
created_at: dayjs(file.created_at).format('MM-DD HH:mm')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -44,6 +45,12 @@ const FilesPage: FC = () => {
|
|||||||
key: 'size',
|
key: 'size',
|
||||||
width: '100px'
|
width: '100px'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('files.count'),
|
||||||
|
dataIndex: 'count',
|
||||||
|
key: 'count',
|
||||||
|
width: '100px'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('files.created_at'),
|
title: t('files.created_at'),
|
||||||
dataIndex: 'created_at',
|
dataIndex: 'created_at',
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import { useAssistant } from '@renderer/hooks/useAssistant'
|
|||||||
import useAvatar from '@renderer/hooks/useAvatar'
|
import useAvatar from '@renderer/hooks/useAvatar'
|
||||||
import { useModel } from '@renderer/hooks/useModel'
|
import { useModel } from '@renderer/hooks/useModel'
|
||||||
import { useSettings } from '@renderer/hooks/useSettings'
|
import { useSettings } from '@renderer/hooks/useSettings'
|
||||||
import { useRuntime } from '@renderer/hooks/useStore'
|
|
||||||
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
|
||||||
import { Message, Model } from '@renderer/types'
|
import { Message, Model } from '@renderer/types'
|
||||||
import { firstLetter, removeLeadingEmoji, removeTrailingDoubleSpaces } from '@renderer/utils'
|
import { firstLetter, removeLeadingEmoji, removeTrailingDoubleSpaces } from '@renderer/utils'
|
||||||
@ -31,6 +30,7 @@ import styled from 'styled-components'
|
|||||||
import SelectModelDropdown from '../components/SelectModelDropdown'
|
import SelectModelDropdown from '../components/SelectModelDropdown'
|
||||||
import Markdown from '../Markdown/Markdown'
|
import Markdown from '../Markdown/Markdown'
|
||||||
import MessageAttachments from './MessageAttachments'
|
import MessageAttachments from './MessageAttachments'
|
||||||
|
import MessgeTokens from './MessageTokens'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
message: Message
|
message: Message
|
||||||
@ -152,9 +152,10 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
|||||||
</MessageHeader>
|
</MessageHeader>
|
||||||
<MessageContentContainer style={{ fontFamily, fontSize }}>
|
<MessageContentContainer style={{ fontFamily, fontSize }}>
|
||||||
<MessageContent message={message} />
|
<MessageContent message={message} />
|
||||||
<MessageFooter style={{ border: messageBorder }}>
|
<MessageFooter style={{ border: messageBorder, flexDirection: isLastMessage ? 'row-reverse' : undefined }}>
|
||||||
|
<MessgeTokens message={message} />
|
||||||
{showMenu && (
|
{showMenu && (
|
||||||
<MenusBar className={`menubar ${isLastMessage && 'show'} ${(!isLastMessage || isUserMessage) && 'user'}`}>
|
<MenusBar className={`menubar ${isLastMessage && 'show'}`}>
|
||||||
{message.role === 'user' && (
|
{message.role === 'user' && (
|
||||||
<Tooltip title="Edit" mouseEnterDelay={0.8}>
|
<Tooltip title="Edit" mouseEnterDelay={0.8}>
|
||||||
<ActionButton onClick={onEdit}>
|
<ActionButton onClick={onEdit}>
|
||||||
@ -204,39 +205,12 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
|
|||||||
)}
|
)}
|
||||||
</MenusBar>
|
</MenusBar>
|
||||||
)}
|
)}
|
||||||
<MessgeTokens message={message} />
|
|
||||||
</MessageFooter>
|
</MessageFooter>
|
||||||
</MessageContentContainer>
|
</MessageContentContainer>
|
||||||
</MessageContainer>
|
</MessageContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MessgeTokens: React.FC<{ message: Message }> = ({ message }) => {
|
|
||||||
const { generating } = useRuntime()
|
|
||||||
|
|
||||||
if (!message.usage) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.role === 'user') {
|
|
||||||
return <MessageMetadata>Tokens: {message?.usage?.total_tokens}</MessageMetadata>
|
|
||||||
}
|
|
||||||
|
|
||||||
if (generating) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (message.role === 'assistant') {
|
|
||||||
return (
|
|
||||||
<MessageMetadata>
|
|
||||||
Tokens: {message?.usage?.total_tokens} | ↑{message?.usage?.prompt_tokens} | ↓{message?.usage?.completion_tokens}
|
|
||||||
</MessageMetadata>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
const MessageContent: React.FC<{ message: Message }> = ({ message }) => {
|
const MessageContent: React.FC<{ message: Message }> = ({ message }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
@ -358,13 +332,6 @@ const MenusBar = styled.div`
|
|||||||
margin-left: -5px;
|
margin-left: -5px;
|
||||||
`
|
`
|
||||||
|
|
||||||
const MessageMetadata = styled.div`
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--color-text-2);
|
|
||||||
user-select: text;
|
|
||||||
margin: 2px 0;
|
|
||||||
`
|
|
||||||
|
|
||||||
const ActionButton = styled.div`
|
const ActionButton = styled.div`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
38
src/renderer/src/pages/home/Messages/MessageTokens.tsx
Normal file
38
src/renderer/src/pages/home/Messages/MessageTokens.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { useRuntime } from '@renderer/hooks/useStore'
|
||||||
|
import { Message } from '@renderer/types'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
const MessgeTokens: React.FC<{ message: Message }> = ({ message }) => {
|
||||||
|
const { generating } = useRuntime()
|
||||||
|
|
||||||
|
if (!message.usage) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.role === 'user') {
|
||||||
|
return <MessageMetadata>Tokens: {message?.usage?.total_tokens}</MessageMetadata>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generating) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message.role === 'assistant') {
|
||||||
|
return (
|
||||||
|
<MessageMetadata>
|
||||||
|
Tokens: {message?.usage?.total_tokens} | ↑{message?.usage?.prompt_tokens} | ↓{message?.usage?.completion_tokens}
|
||||||
|
</MessageMetadata>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const MessageMetadata = styled.div`
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
user-select: text;
|
||||||
|
margin: 2px 0;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default MessgeTokens
|
||||||
@ -77,8 +77,9 @@ const GeneralSettings: FC = () => {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
<SettingDivider />
|
|
||||||
{isMac && (
|
{isMac && (
|
||||||
|
<>
|
||||||
|
<SettingDivider />
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
<SettingRowTitle>{t('settings.theme.window.style.title')}</SettingRowTitle>
|
<SettingRowTitle>{t('settings.theme.window.style.title')}</SettingRowTitle>
|
||||||
<Select
|
<Select
|
||||||
@ -91,6 +92,7 @@ const GeneralSettings: FC = () => {
|
|||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
</SettingRow>
|
</SettingRow>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
<SettingRow>
|
<SettingRow>
|
||||||
@ -108,7 +110,7 @@ const GeneralSettings: FC = () => {
|
|||||||
<SettingDivider />
|
<SettingDivider />
|
||||||
{topicPosition === 'left' && (
|
{topicPosition === 'left' && (
|
||||||
<>
|
<>
|
||||||
<SettingRow>
|
<SettingRow style={{ minHeight: 32 }}>
|
||||||
<SettingRowTitle>{t('settings.advanced.click_assistant_switch_to_topics')}</SettingRowTitle>
|
<SettingRowTitle>{t('settings.advanced.click_assistant_switch_to_topics')}</SettingRowTitle>
|
||||||
<Switch
|
<Switch
|
||||||
checked={clickAssistantToShowTopic}
|
checked={clickAssistantToShowTopic}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
import { isLocalAi } from '@renderer/config/env'
|
import { isLocalAi } from '@renderer/config/env'
|
||||||
|
import { isVisionModel } from '@renderer/config/models'
|
||||||
import { getAssistantSettings, getDefaultModel, getTopNamingModel } from '@renderer/services/assistant'
|
import { getAssistantSettings, getDefaultModel, getTopNamingModel } from '@renderer/services/assistant'
|
||||||
import { EVENT_NAMES } from '@renderer/services/event'
|
import { EVENT_NAMES } from '@renderer/services/event'
|
||||||
import { filterContextMessages, filterMessages } from '@renderer/services/messages'
|
import { filterContextMessages, filterMessages } from '@renderer/services/messages'
|
||||||
import { Assistant, FileTypes, Message, Provider, Suggestion } from '@renderer/types'
|
import { Assistant, FileTypes, Message, Model, Provider, Suggestion } from '@renderer/types'
|
||||||
import { removeQuotes } from '@renderer/utils'
|
import { removeQuotes } from '@renderer/utils'
|
||||||
import { first, takeRight } from 'lodash'
|
import { first, takeRight } from 'lodash'
|
||||||
import OpenAI from 'openai'
|
import OpenAI from 'openai'
|
||||||
@ -33,7 +34,12 @@ export default class OpenAIProvider extends BaseProvider {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getMessageParam(message: Message): Promise<OpenAI.Chat.Completions.ChatCompletionMessageParam> {
|
private async getMessageParam(
|
||||||
|
message: Message,
|
||||||
|
model: Model
|
||||||
|
): Promise<OpenAI.Chat.Completions.ChatCompletionMessageParam> {
|
||||||
|
const isVision = isVisionModel(model)
|
||||||
|
|
||||||
if (message.role !== 'user') {
|
if (message.role !== 'user') {
|
||||||
return {
|
return {
|
||||||
role: message.role,
|
role: message.role,
|
||||||
@ -49,7 +55,7 @@ export default class OpenAIProvider extends BaseProvider {
|
|||||||
]
|
]
|
||||||
|
|
||||||
for (const file of message.files || []) {
|
for (const file of message.files || []) {
|
||||||
if (file.type === FileTypes.IMAGE) {
|
if (file.type === FileTypes.IMAGE && isVision) {
|
||||||
const image = await window.api.file.base64Image(file.id + file.ext)
|
const image = await window.api.file.base64Image(file.id + file.ext)
|
||||||
parts.push({
|
parts.push({
|
||||||
type: 'image_url',
|
type: 'image_url',
|
||||||
@ -83,7 +89,7 @@ export default class OpenAIProvider extends BaseProvider {
|
|||||||
onFilterMessages(_messages)
|
onFilterMessages(_messages)
|
||||||
|
|
||||||
for (const message of _messages) {
|
for (const message of _messages) {
|
||||||
userMessages.push(await this.getMessageParam(message))
|
userMessages.push(await this.getMessageParam(message, model))
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore key is not typed
|
// @ts-ignore key is not typed
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user