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:
kangfenmao 2024-09-19 16:56:44 +08:00
parent f1c8922752
commit 33ac0937df
6 changed files with 77 additions and 55 deletions

View File

@ -113,6 +113,7 @@ const resources = {
file: 'File',
name: 'Name',
size: 'Size',
count: 'Count',
created_at: 'Created At'
},
agents: {
@ -385,6 +386,7 @@ const resources = {
file: '文件',
name: '文件名',
size: '大小',
count: '文件数',
created_at: '创建时间'
},
agents: {

View File

@ -22,6 +22,7 @@ const FilesPage: FC = () => {
file: isImage ? ImageView : <FileNameText className="text-nowrap">{file.origin_name}</FileNameText>,
name: <a href={'file://' + getFileDirectory(file.path)}>{file.origin_name}</a>,
size: `${(file.size / 1024 / 1024).toFixed(2)} MB`,
count: file.count,
created_at: dayjs(file.created_at).format('MM-DD HH:mm')
}
})
@ -44,6 +45,12 @@ const FilesPage: FC = () => {
key: 'size',
width: '100px'
},
{
title: t('files.count'),
dataIndex: 'count',
key: 'count',
width: '100px'
},
{
title: t('files.created_at'),
dataIndex: 'created_at',

View File

@ -17,7 +17,6 @@ import { useAssistant } from '@renderer/hooks/useAssistant'
import useAvatar from '@renderer/hooks/useAvatar'
import { useModel } from '@renderer/hooks/useModel'
import { useSettings } from '@renderer/hooks/useSettings'
import { useRuntime } from '@renderer/hooks/useStore'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
import { Message, Model } from '@renderer/types'
import { firstLetter, removeLeadingEmoji, removeTrailingDoubleSpaces } from '@renderer/utils'
@ -31,6 +30,7 @@ import styled from 'styled-components'
import SelectModelDropdown from '../components/SelectModelDropdown'
import Markdown from '../Markdown/Markdown'
import MessageAttachments from './MessageAttachments'
import MessgeTokens from './MessageTokens'
interface Props {
message: Message
@ -152,9 +152,10 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
</MessageHeader>
<MessageContentContainer style={{ fontFamily, fontSize }}>
<MessageContent message={message} />
<MessageFooter style={{ border: messageBorder }}>
<MessageFooter style={{ border: messageBorder, flexDirection: isLastMessage ? 'row-reverse' : undefined }}>
<MessgeTokens message={message} />
{showMenu && (
<MenusBar className={`menubar ${isLastMessage && 'show'} ${(!isLastMessage || isUserMessage) && 'user'}`}>
<MenusBar className={`menubar ${isLastMessage && 'show'}`}>
{message.role === 'user' && (
<Tooltip title="Edit" mouseEnterDelay={0.8}>
<ActionButton onClick={onEdit}>
@ -204,39 +205,12 @@ const MessageItem: FC<Props> = ({ message, index, showMenu, onDeleteMessage }) =
)}
</MenusBar>
)}
<MessgeTokens message={message} />
</MessageFooter>
</MessageContentContainer>
</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 { t } = useTranslation()
@ -358,13 +332,6 @@ const MenusBar = styled.div`
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`
cursor: pointer;
border-radius: 8px;

View 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

View File

@ -77,20 +77,22 @@ const GeneralSettings: FC = () => {
]}
/>
</SettingRow>
<SettingDivider />
{isMac && (
<SettingRow>
<SettingRowTitle>{t('settings.theme.window.style.title')}</SettingRowTitle>
<Select
defaultValue={windowStyle || 'opaque'}
style={{ width: 180 }}
onChange={setWindowStyle}
options={[
{ value: 'transparent', label: t('settings.theme.window.style.transparent') },
{ value: 'opaque', label: t('settings.theme.window.style.opaque') }
]}
/>
</SettingRow>
<>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.theme.window.style.title')}</SettingRowTitle>
<Select
defaultValue={windowStyle || 'opaque'}
style={{ width: 180 }}
onChange={setWindowStyle}
options={[
{ value: 'transparent', label: t('settings.theme.window.style.transparent') },
{ value: 'opaque', label: t('settings.theme.window.style.opaque') }
]}
/>
</SettingRow>
</>
)}
<SettingDivider />
<SettingRow>
@ -108,7 +110,7 @@ const GeneralSettings: FC = () => {
<SettingDivider />
{topicPosition === 'left' && (
<>
<SettingRow>
<SettingRow style={{ minHeight: 32 }}>
<SettingRowTitle>{t('settings.advanced.click_assistant_switch_to_topics')}</SettingRowTitle>
<Switch
checked={clickAssistantToShowTopic}

View File

@ -1,8 +1,9 @@
import { isLocalAi } from '@renderer/config/env'
import { isVisionModel } from '@renderer/config/models'
import { getAssistantSettings, getDefaultModel, getTopNamingModel } from '@renderer/services/assistant'
import { EVENT_NAMES } from '@renderer/services/event'
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 { first, takeRight } from 'lodash'
import OpenAI from 'openai'
@ -33,7 +34,12 @@ export default class OpenAIProvider extends BaseProvider {
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') {
return {
role: message.role,
@ -49,7 +55,7 @@ export default class OpenAIProvider extends BaseProvider {
]
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)
parts.push({
type: 'image_url',
@ -83,7 +89,7 @@ export default class OpenAIProvider extends BaseProvider {
onFilterMessages(_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