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',
|
||||
name: 'Name',
|
||||
size: 'Size',
|
||||
count: 'Count',
|
||||
created_at: 'Created At'
|
||||
},
|
||||
agents: {
|
||||
@ -385,6 +386,7 @@ const resources = {
|
||||
file: '文件',
|
||||
name: '文件名',
|
||||
size: '大小',
|
||||
count: '文件数',
|
||||
created_at: '创建时间'
|
||||
},
|
||||
agents: {
|
||||
|
||||
@ -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',
|
||||
|
||||
@ -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;
|
||||
|
||||
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,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}
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user