feat: add provider and model image

This commit is contained in:
kangfenmao 2024-07-09 19:29:47 +08:00
parent 9b92b191ae
commit e3dcfef024
22 changed files with 137 additions and 11 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

View File

@ -0,0 +1,3 @@
<svg viewBox='0 0 320 320' xmlns='http://www.w3.org/2000/svg'>
<path d='m297.06 130.97c7.26-21.79 4.76-45.66-6.85-65.48-17.46-30.4-52.56-46.04-86.84-38.68-15.25-17.18-37.16-26.95-60.13-26.81-35.04-.08-66.13 22.48-76.91 55.82-22.51 4.61-41.94 18.7-53.31 38.67-17.59 30.32-13.58 68.54 9.92 94.54-7.26 21.79-4.76 45.66 6.85 65.48 17.46 30.4 52.56 46.04 86.84 38.68 15.24 17.18 37.16 26.95 60.13 26.8 35.06.09 66.16-22.49 76.94-55.86 22.51-4.61 41.94-18.7 53.31-38.67 17.57-30.32 13.55-68.51-9.94-94.51zm-120.28 168.11c-14.03.02-27.62-4.89-38.39-13.88.49-.26 1.34-.73 1.89-1.07l63.72-36.8c3.26-1.85 5.26-5.32 5.24-9.07v-89.83l26.93 15.55c.29.14.48.42.52.74v74.39c-.04 33.08-26.83 59.9-59.91 59.97zm-128.84-55.03c-7.03-12.14-9.56-26.37-7.15-40.18.47.28 1.3.79 1.89 1.13l63.72 36.8c3.23 1.89 7.23 1.89 10.47 0l77.79-44.92v31.1c.02.32-.13.63-.38.83l-64.41 37.19c-28.69 16.52-65.33 6.7-81.92-21.95zm-16.77-139.09c7-12.16 18.05-21.46 31.21-26.29 0 .55-.03 1.52-.03 2.2v73.61c-.02 3.74 1.98 7.21 5.23 9.06l77.79 44.91-26.93 15.55c-.27.18-.61.21-.91.08l-64.42-37.22c-28.63-16.58-38.45-53.21-21.95-81.89zm221.26 51.49-77.79-44.92 26.93-15.54c.27-.18.61-.21.91-.08l64.42 37.19c28.68 16.57 38.51 53.26 21.94 81.94-7.01 12.14-18.05 21.44-31.2 26.28v-75.81c.03-3.74-1.96-7.2-5.2-9.06zm26.8-40.34c-.47-.29-1.3-.79-1.89-1.13l-63.72-36.8c-3.23-1.89-7.23-1.89-10.47 0l-77.79 44.92v-31.1c-.02-.32.13-.63.38-.83l64.41-37.16c28.69-16.55 65.37-6.7 81.91 22 6.99 12.12 9.52 26.31 7.15 40.1zm-168.51 55.43-26.94-15.55c-.29-.14-.48-.42-.52-.74v-74.39c.02-33.12 26.89-59.96 60.01-59.94 14.01 0 27.57 4.92 38.34 13.88-.49.26-1.33.73-1.89 1.07l-63.72 36.8c-3.26 1.85-5.26 5.31-5.24 9.06l-.04 89.79zm14.63-31.54 34.65-20.01 34.65 20v40.01l-34.65 20-34.65-20z'/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

View File

@ -0,0 +1,7 @@
<svg width="600" height="600" viewBox="0 0 600 600" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="300" cy="300" r="300" fill="white"/>
<rect x="409.733" y="340.032" width="42.3862" height="151.648" rx="21.1931" fill="#003425"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M422.005 133.354C413.089 125.771 399.714 126.851 392.131 135.768L273.699 275.021C270.643 278.614 268.994 282.932 268.698 287.302C268.532 288.371 268.446 289.466 268.446 290.581V468.603C268.446 480.308 277.934 489.796 289.639 489.796C301.344 489.796 310.832 480.308 310.832 468.603V296.784L424.419 163.228C432.002 154.312 430.921 140.937 422.005 133.354Z" fill="#003425"/>
<rect x="113.972" y="134.25" width="42.3862" height="174.745" rx="21.1931" transform="rotate(-39.3441 113.972 134.25)" fill="#003425"/>
<circle cx="460.126" cy="279.278" r="25.9027" fill="#00DD20"/>
</svg>

After

Width:  |  Height:  |  Size: 869 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -0,0 +1,7 @@
<svg width="600" height="600" viewBox="0 0 600 600" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="300" cy="300" r="300" fill="#003425"/>
<rect x="409.733" y="340.031" width="42.3862" height="151.648" rx="21.1931" fill="white"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M422.005 133.354C413.089 125.771 399.714 126.851 392.131 135.767L273.699 275.021C270.643 278.614 268.994 282.932 268.698 287.302C268.532 288.371 268.446 289.466 268.446 290.581V468.603C268.446 480.308 277.934 489.796 289.639 489.796C301.344 489.796 310.832 480.308 310.832 468.603V296.784L424.419 163.228C432.002 154.312 430.921 140.937 422.005 133.354Z" fill="white"/>
<rect x="113.972" y="134.25" width="42.3862" height="174.745" rx="21.1931" transform="rotate(-39.3441 113.972 134.25)" fill="white"/>
<circle cx="460.126" cy="279.278" r="25.9027" fill="#00FF25"/>
</svg>

After

Width:  |  Height:  |  Size: 865 B

View File

@ -94,3 +94,7 @@ body,
flex-direction: row; flex-direction: row;
flex: 1; flex: 1;
} }
#inputbar .ant-input {
resize: none;
}

View File

@ -100,7 +100,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
}, [assistant]) }, [assistant])
return ( return (
<Container> <Container id="inputbar">
<Toolbar> <Toolbar>
<ToolbarMenu> <ToolbarMenu>
<Tooltip placement="top" title=" New Chat " arrow> <Tooltip placement="top" title=" New Chat " arrow>
@ -144,7 +144,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
autoFocus autoFocus
contextMenu="true" contextMenu="true"
variant="borderless" variant="borderless"
styles={{ textarea: { resize: 'none', paddingLeft: 0 } }} styles={{ textarea: { paddingLeft: 0 } }}
allowClear allowClear
ref={inputRef} ref={inputRef}
/> />

View File

@ -2,12 +2,13 @@ import { Message } from '@renderer/types'
import { Avatar, Tooltip } from 'antd' import { Avatar, Tooltip } from 'antd'
import { FC } from 'react' import { FC } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import Logo from '@renderer/assets/images/logo.png'
import useAvatar from '@renderer/hooks/useAvatar' import useAvatar from '@renderer/hooks/useAvatar'
import { CopyOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons' import { CopyOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons'
import Markdown from 'react-markdown' import Markdown from 'react-markdown'
import CodeBlock from './CodeBlock' import CodeBlock from './CodeBlock'
import { EVENT_NAMES, EventEmitter } from '@renderer/services/event' import { EVENT_NAMES, EventEmitter } from '@renderer/services/event'
import { getModelLogo } from '@renderer/services/provider'
import Logo from '@renderer/assets/images/logo.png'
interface Props { interface Props {
message: Message message: Message
@ -40,7 +41,13 @@ const MessageItem: FC<Props> = ({ message, showMenu, onDeleteMessage }) => {
return ( return (
<MessageContainer key={message.id}> <MessageContainer key={message.id}>
<AvatarWrapper>{message.role === 'assistant' ? <Avatar src={Logo} /> : <Avatar src={avatar} />}</AvatarWrapper> <AvatarWrapper>
{message.role === 'assistant' ? (
<Avatar src={message.modelId ? getModelLogo(message.modelId) : Logo} />
) : (
<Avatar src={avatar} />
)}
</AvatarWrapper>
<MessageContent> <MessageContent>
<Markdown className="markdown" components={{ code: CodeBlock as any }}> <Markdown className="markdown" components={{ code: CodeBlock as any }}>
{message.content} {message.content}

View File

@ -16,7 +16,10 @@ const ModelSettings: FC = () => {
.map((p) => ({ .map((p) => ({
label: p.name, label: p.name,
title: p.name, title: p.name,
options: p.models.map((m) => ({ label: m.name, value: m.id })) options: p.models.map((m) => ({
label: m.name,
value: m.id
}))
})) }))
return ( return (

View File

@ -3,6 +3,8 @@ import { Provider } from '@renderer/types'
import { FC, useState } from 'react' import { FC, useState } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import ModalProviderSetting from './components/ModalProviderSetting' import ModalProviderSetting from './components/ModalProviderSetting'
import { Avatar } from 'antd'
import { getProviderLogo } from '@renderer/services/provider'
const ProviderSettings: FC = () => { const ProviderSettings: FC = () => {
const providers = useSystemProviders() const providers = useSystemProviders()
@ -16,7 +18,8 @@ const ProviderSettings: FC = () => {
key={JSON.stringify(provider)} key={JSON.stringify(provider)}
className={provider.id === selectedProvider?.id ? 'active' : ''} className={provider.id === selectedProvider?.id ? 'active' : ''}
onClick={() => setSelectedProvider(provider)}> onClick={() => setSelectedProvider(provider)}>
{provider.name} <Avatar src={getProviderLogo(provider.id)} size={22} />
<ProviderItemName>{provider.name}</ProviderItemName>
</ProviderListItem> </ProviderListItem>
))} ))}
</ProviderListContainer> </ProviderListContainer>
@ -60,4 +63,9 @@ const ProviderListItem = styled.div`
} }
` `
const ProviderItemName = styled.div`
margin-left: 10px;
font-weight: bold;
`
export default ProviderSettings export default ProviderSettings

View File

@ -1,11 +1,12 @@
import { Provider } from '@renderer/types' import { Provider } from '@renderer/types'
import { FC, useEffect, useState } from 'react' import { FC, useEffect, useState } from 'react'
import styled from 'styled-components' import styled from 'styled-components'
import { Button, Card, Divider, Input } from 'antd' import { Avatar, Button, Card, Divider, Input } from 'antd'
import { useProvider } from '@renderer/hooks/useProvider' import { useProvider } from '@renderer/hooks/useProvider'
import ModalListPopup from '@renderer/components/Popups/ModalListPopup' import ModalListPopup from '@renderer/components/Popups/ModalListPopup'
import { groupBy } from 'lodash' import { groupBy } from 'lodash'
import { SettingContainer, SettingSubtitle, SettingTitle } from './SettingComponent' import { SettingContainer, SettingSubtitle, SettingTitle } from './SettingComponent'
import { getModelLogo } from '@renderer/services/provider'
interface Props { interface Props {
provider: Provider provider: Provider
@ -58,7 +59,10 @@ const ModalProviderSetting: FC<Props> = ({ provider }) => {
{Object.keys(modelGroups).map((group) => ( {Object.keys(modelGroups).map((group) => (
<Card key={group} type="inner" title={group} style={{ marginBottom: '10px' }} size="small"> <Card key={group} type="inner" title={group} style={{ marginBottom: '10px' }} size="small">
{modelGroups[group].map((model) => ( {modelGroups[group].map((model) => (
<ModelListItem key={model.id}>{model.id}</ModelListItem> <ModelListItem key={model.id}>
<Avatar src={getModelLogo(model.id)} size={22} style={{ marginRight: '8px' }} />
{model.id}
</ModelListItem>
))} ))}
</Card> </Card>
))} ))}
@ -73,7 +77,7 @@ const ModelListItem = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: space-between; justify-content: flex-start;
padding: 5px 0; padding: 5px 0;
` `

View File

@ -19,6 +19,7 @@ export const SettingTitle = styled.div`
flex-direction: row; flex-direction: row;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
font-weight: 900;
` `
export const SettingSubtitle = styled.div` export const SettingSubtitle = styled.div`

View File

@ -34,7 +34,14 @@ export function getAssistantProvider(assistant: Assistant) {
return provider || getDefaultProvider() return provider || getDefaultProvider()
} }
export function getProviderByModel(model: Model) { export function getProviderByModel(model?: Model) {
const providers = store.getState().llm.providers const providers = store.getState().llm.providers
return providers.find((p) => p.id === model.provider) as Provider const providerId = model ? model.provider : getDefaultProvider().id
return providers.find((p) => p.id === providerId) as Provider
}
export function getProviderByModelId(modelId?: string) {
const providers = store.getState().llm.providers
const _modelId = modelId || getDefaultModel().id
return providers.find((p) => p.models.find((m) => m.id === _modelId)) as Provider
} }

View File

@ -0,0 +1,75 @@
import OpenAiProviderLogo from '@renderer/assets/images/providers/openai.jpeg'
import SiliconFlowProviderLogo from '@renderer/assets/images/providers/silicon.png'
import DeepSeekProviderLogo from '@renderer/assets/images/providers/deepseek.png'
import YiProviderLogo from '@renderer/assets/images/providers/yi.svg'
import GroqProviderLogo from '@renderer/assets/images/providers/groq.png'
import ChatGPTModelLogo from '@renderer/assets/images/models/chatgpt.svg'
import ChatGLMModelLogo from '@renderer/assets/images/models/chatglm.jpeg'
import DeepSeekModelLogo from '@renderer/assets/images/models/deepseek.png'
import GemmaModelLogo from '@renderer/assets/images/models/gemma.jpeg'
import QwenModelLogo from '@renderer/assets/images/models/qwen.jpeg'
import YiModelLogo from '@renderer/assets/images/models/yi.svg'
import LlamaModelLogo from '@renderer/assets/images/models/llama.jpeg'
import MixtralModelLogo from '@renderer/assets/images/models/mixtral.jpeg'
export function getProviderLogo(providerId: string) {
if (providerId === 'openai') {
return OpenAiProviderLogo
}
if (providerId === 'silicon') {
return SiliconFlowProviderLogo
}
if (providerId === 'deepseek') {
return DeepSeekProviderLogo
}
if (providerId === 'yi') {
return YiProviderLogo
}
if (providerId === 'groq') {
return GroqProviderLogo
}
return ''
}
export function getModelLogo(modelId: string) {
const _modelId = modelId.toLowerCase()
if (_modelId.includes('gpt')) {
return ChatGPTModelLogo
}
if (_modelId.includes('glm')) {
return ChatGLMModelLogo
}
if (_modelId.includes('deepseek')) {
return DeepSeekModelLogo
}
if (_modelId.includes('qwen')) {
return QwenModelLogo
}
if (_modelId.includes('gemma')) {
return GemmaModelLogo
}
if (_modelId.includes('yi-')) {
return YiModelLogo
}
if (_modelId.includes('llama')) {
return LlamaModelLogo
}
if (_modelId.includes('mixtral')) {
return MixtralModelLogo
}
return ''
}