feat: add web search for google gemini modal gemini-2.0-flash-exp
This commit is contained in:
parent
f312c5fc40
commit
2fae6e4a3e
@ -62,12 +62,7 @@ electronDownload:
|
|||||||
afterSign: scripts/notarize.js
|
afterSign: scripts/notarize.js
|
||||||
releaseInfo:
|
releaseInfo:
|
||||||
releaseNotes: |
|
releaseNotes: |
|
||||||
增加快捷键切换助手和话题显示
|
修复对话消息无法删除问题
|
||||||
历史消息懒加载 by @1355873789
|
支持 Gemini 模型联网搜索
|
||||||
更快的应用更新下载速度 by @1355873789
|
发送消息增加 Command + Enter 快捷键 by @duanyongcheng
|
||||||
更加清晰的模型分组
|
Windows 版本样式优化
|
||||||
修复部分代码块无法正常显示问题
|
|
||||||
增加应用更新内容显示
|
|
||||||
消息发送增加 Ctrl + Enter 快捷键
|
|
||||||
清除上下文消息点击可以撤销
|
|
||||||
增加 Top-P 设置选项
|
|
||||||
|
|||||||
@ -57,7 +57,7 @@
|
|||||||
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
|
"@electron-toolkit/eslint-config-prettier": "^2.0.0",
|
||||||
"@electron-toolkit/eslint-config-ts": "^1.0.1",
|
"@electron-toolkit/eslint-config-ts": "^1.0.1",
|
||||||
"@electron-toolkit/tsconfig": "^1.0.1",
|
"@electron-toolkit/tsconfig": "^1.0.1",
|
||||||
"@google/generative-ai": "^0.16.0",
|
"@google/generative-ai": "^0.21.0",
|
||||||
"@hello-pangea/dnd": "^16.6.0",
|
"@hello-pangea/dnd": "^16.6.0",
|
||||||
"@kangfenmao/keyv-storage": "^0.1.0",
|
"@kangfenmao/keyv-storage": "^0.1.0",
|
||||||
"@reduxjs/toolkit": "^2.2.5",
|
"@reduxjs/toolkit": "^2.2.5",
|
||||||
|
|||||||
15
src/renderer/src/components/Icons/WebSearchIcon.tsx
Normal file
15
src/renderer/src/components/Icons/WebSearchIcon.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { GlobalOutlined } from '@ant-design/icons'
|
||||||
|
import React, { FC } from 'react'
|
||||||
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
const WebSearchIcon: FC<React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>> = (props) => {
|
||||||
|
return <Icon {...(props as any)} />
|
||||||
|
}
|
||||||
|
|
||||||
|
const Icon = styled(GlobalOutlined)`
|
||||||
|
color: var(--color-link);
|
||||||
|
font-size: 12px;
|
||||||
|
margin-left: 4px;
|
||||||
|
`
|
||||||
|
|
||||||
|
export default WebSearchIcon
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { PushpinOutlined, SearchOutlined } from '@ant-design/icons'
|
import { PushpinOutlined, SearchOutlined } from '@ant-design/icons'
|
||||||
import VisionIcon from '@renderer/components/Icons/VisionIcon'
|
import VisionIcon from '@renderer/components/Icons/VisionIcon'
|
||||||
import { TopView } from '@renderer/components/TopView'
|
import { TopView } from '@renderer/components/TopView'
|
||||||
import { getModelLogo, isVisionModel } from '@renderer/config/models'
|
import { getModelLogo, isVisionModel, isWebSearchModel } from '@renderer/config/models'
|
||||||
import db from '@renderer/databases'
|
import db from '@renderer/databases'
|
||||||
import { useProviders } from '@renderer/hooks/useProvider'
|
import { useProviders } from '@renderer/hooks/useProvider'
|
||||||
import { getModelUniqId } from '@renderer/services/ModelService'
|
import { getModelUniqId } from '@renderer/services/ModelService'
|
||||||
@ -12,6 +12,7 @@ import { useEffect, useRef, useState } from 'react'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import styled from 'styled-components'
|
import styled from 'styled-components'
|
||||||
|
|
||||||
|
import WebSearchIcon from '../Icons/WebSearchIcon'
|
||||||
import { HStack } from '../Layout'
|
import { HStack } from '../Layout'
|
||||||
import Scrollbar from '../Scrollbar'
|
import Scrollbar from '../Scrollbar'
|
||||||
|
|
||||||
@ -73,7 +74,7 @@ const PopupContainer: React.FC<PopupContainerProps> = ({ model, resolve }) => {
|
|||||||
label: (
|
label: (
|
||||||
<ModelItem>
|
<ModelItem>
|
||||||
<span>
|
<span>
|
||||||
{m?.name} {isVisionModel(m) && <VisionIcon />}
|
{m?.name} {isVisionModel(m) && <VisionIcon />} {isWebSearchModel(m) && <WebSearchIcon />}
|
||||||
</span>
|
</span>
|
||||||
<PinIcon
|
<PinIcon
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
|
|||||||
@ -1056,3 +1056,7 @@ export function isVisionModel(model: Model): boolean {
|
|||||||
export function isSupportedModel(model: OpenAI.Models.Model): boolean {
|
export function isSupportedModel(model: OpenAI.Models.Model): boolean {
|
||||||
return !NOT_SUPPORTED_REGEX.test(model.id)
|
return !NOT_SUPPORTED_REGEX.test(model.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isWebSearchModel(model: Model): boolean {
|
||||||
|
return model?.provider === 'gemini' && model?.id === 'gemini-2.0-flash-exp'
|
||||||
|
}
|
||||||
|
|||||||
@ -80,6 +80,7 @@
|
|||||||
"input.topics": " Topics ",
|
"input.topics": " Topics ",
|
||||||
"input.translate": "Translate to English",
|
"input.translate": "Translate to English",
|
||||||
"input.upload": "Upload image or document file",
|
"input.upload": "Upload image or document file",
|
||||||
|
"input.web_search": "Enable web search",
|
||||||
"message.new.branch": "New Branch",
|
"message.new.branch": "New Branch",
|
||||||
"message.new.branch.created": "New Branch Created",
|
"message.new.branch.created": "New Branch Created",
|
||||||
"message.new.context": "New Context",
|
"message.new.context": "New Context",
|
||||||
|
|||||||
@ -80,6 +80,7 @@
|
|||||||
"input.topics": " Топики ",
|
"input.topics": " Топики ",
|
||||||
"input.translate": "Перевести на английский",
|
"input.translate": "Перевести на английский",
|
||||||
"input.upload": "Загрузить изображение или документ",
|
"input.upload": "Загрузить изображение или документ",
|
||||||
|
"input.web_search": "Включить веб-поиск",
|
||||||
"message.new.branch": "Новая ветка",
|
"message.new.branch": "Новая ветка",
|
||||||
"message.new.branch.created": "Новая ветка создана",
|
"message.new.branch.created": "Новая ветка создана",
|
||||||
"message.new.context": "Новый контекст",
|
"message.new.context": "Новый контекст",
|
||||||
|
|||||||
@ -80,6 +80,7 @@
|
|||||||
"input.topics": " 话题 ",
|
"input.topics": " 话题 ",
|
||||||
"input.translate": "翻译成英文",
|
"input.translate": "翻译成英文",
|
||||||
"input.upload": "上传图片或文档",
|
"input.upload": "上传图片或文档",
|
||||||
|
"input.web_search": "开启网络搜索",
|
||||||
"message.new.branch": "新分支",
|
"message.new.branch": "新分支",
|
||||||
"message.new.branch.created": "新分支已创建",
|
"message.new.branch.created": "新分支已创建",
|
||||||
"message.new.context": "清除上下文",
|
"message.new.context": "清除上下文",
|
||||||
|
|||||||
@ -80,6 +80,7 @@
|
|||||||
"input.topics": " 話題 ",
|
"input.topics": " 話題 ",
|
||||||
"input.translate": "翻譯成英文",
|
"input.translate": "翻譯成英文",
|
||||||
"input.upload": "上傳圖片或文檔",
|
"input.upload": "上傳圖片或文檔",
|
||||||
|
"input.web_search": "開啟網路搜索",
|
||||||
"message.new.branch": "新分支",
|
"message.new.branch": "新分支",
|
||||||
"message.new.branch.created": "新分支已建立",
|
"message.new.branch.created": "新分支已建立",
|
||||||
"message.new.context": "新上下文",
|
"message.new.context": "新上下文",
|
||||||
|
|||||||
@ -4,13 +4,14 @@ import {
|
|||||||
FormOutlined,
|
FormOutlined,
|
||||||
FullscreenExitOutlined,
|
FullscreenExitOutlined,
|
||||||
FullscreenOutlined,
|
FullscreenOutlined,
|
||||||
|
GlobalOutlined,
|
||||||
PauseCircleOutlined,
|
PauseCircleOutlined,
|
||||||
QuestionCircleOutlined
|
QuestionCircleOutlined
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import { PicCenterOutlined } from '@ant-design/icons'
|
import { PicCenterOutlined } from '@ant-design/icons'
|
||||||
import TranslateButton from '@renderer/components/TranslateButton'
|
import TranslateButton from '@renderer/components/TranslateButton'
|
||||||
import { isMac } from '@renderer/config/constant'
|
import { isMac } from '@renderer/config/constant'
|
||||||
import { isVisionModel } from '@renderer/config/models'
|
import { isVisionModel, isWebSearchModel } from '@renderer/config/models'
|
||||||
import db from '@renderer/databases'
|
import db from '@renderer/databases'
|
||||||
import { useAssistant } from '@renderer/hooks/useAssistant'
|
import { useAssistant } from '@renderer/hooks/useAssistant'
|
||||||
import { useRuntime } from '@renderer/hooks/useRuntime'
|
import { useRuntime } from '@renderer/hooks/useRuntime'
|
||||||
@ -48,10 +49,10 @@ interface Props {
|
|||||||
let _text = ''
|
let _text = ''
|
||||||
let _files: FileType[] = []
|
let _files: FileType[] = []
|
||||||
|
|
||||||
const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
const Inputbar: FC<Props> = ({ assistant: _assistant, setActiveTopic }) => {
|
||||||
const [text, setText] = useState(_text)
|
const [text, setText] = useState(_text)
|
||||||
const [inputFocus, setInputFocus] = useState(false)
|
const [inputFocus, setInputFocus] = useState(false)
|
||||||
const { addTopic, model, setModel } = useAssistant(assistant.id)
|
const { assistant, addTopic, model, setModel, updateAssistant } = useAssistant(_assistant.id)
|
||||||
const {
|
const {
|
||||||
sendMessageShortcut,
|
sendMessageShortcut,
|
||||||
fontSize,
|
fontSize,
|
||||||
@ -400,6 +401,17 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
|||||||
<FormOutlined />
|
<FormOutlined />
|
||||||
</ToolbarButton>
|
</ToolbarButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
{isWebSearchModel(model) && (
|
||||||
|
<Tooltip placement="top" title={t('chat.input.web_search')} arrow>
|
||||||
|
<ToolbarButton
|
||||||
|
type="text"
|
||||||
|
onClick={() => updateAssistant({ ...assistant, enableWebSearch: !assistant.enableWebSearch })}>
|
||||||
|
<GlobalOutlined
|
||||||
|
style={{ color: assistant.enableWebSearch ? 'var(--color-link)' : 'var(--color-icon)' }}
|
||||||
|
/>
|
||||||
|
</ToolbarButton>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
<Tooltip placement="top" title={t('chat.input.clear')} arrow>
|
<Tooltip placement="top" title={t('chat.input.clear')} arrow>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={t('chat.input.clear.content')}
|
title={t('chat.input.clear.content')}
|
||||||
|
|||||||
@ -8,7 +8,8 @@ import {
|
|||||||
SettingOutlined
|
SettingOutlined
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import VisionIcon from '@renderer/components/Icons/VisionIcon'
|
import VisionIcon from '@renderer/components/Icons/VisionIcon'
|
||||||
import { getModelLogo, isVisionModel, VISION_REGEX } from '@renderer/config/models'
|
import WebSearchIcon from '@renderer/components/Icons/WebSearchIcon'
|
||||||
|
import { getModelLogo, isVisionModel, isWebSearchModel, VISION_REGEX } from '@renderer/config/models'
|
||||||
import { PROVIDER_CONFIG } from '@renderer/config/providers'
|
import { PROVIDER_CONFIG } from '@renderer/config/providers'
|
||||||
import { useTheme } from '@renderer/context/ThemeProvider'
|
import { useTheme } from '@renderer/context/ThemeProvider'
|
||||||
import { useAssistants, useDefaultModel } from '@renderer/hooks/useAssistant'
|
import { useAssistants, useDefaultModel } from '@renderer/hooks/useAssistant'
|
||||||
@ -261,20 +262,24 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
|||||||
title={group}
|
title={group}
|
||||||
style={{ marginBottom: '10px', border: '0.5px solid var(--color-border)' }}
|
style={{ marginBottom: '10px', border: '0.5px solid var(--color-border)' }}
|
||||||
size="small">
|
size="small">
|
||||||
{modelGroups[group].map((model) => (
|
{modelGroups[group].map((model) => {
|
||||||
<ModelListItem key={model.id}>
|
console.debug(model)
|
||||||
<ModelListHeader>
|
return (
|
||||||
<Avatar src={getModelLogo(model.id)} size={22} style={{ marginRight: '8px' }}>
|
<ModelListItem key={model.id}>
|
||||||
{model.name[0].toUpperCase()}
|
<ModelListHeader>
|
||||||
</Avatar>
|
<Avatar src={getModelLogo(model.id)} size={22} style={{ marginRight: '8px' }}>
|
||||||
{model.name} {isVisionModel(model) && <VisionIcon />}
|
{model.name[0].toUpperCase()}
|
||||||
<Popover content={modelTypeContent(model)} title={t('model.type.select')} trigger="click">
|
</Avatar>
|
||||||
<SettingIcon />
|
{model.name} {isVisionModel(model) && <VisionIcon />}
|
||||||
</Popover>
|
{isWebSearchModel(model) && <WebSearchIcon />}
|
||||||
</ModelListHeader>
|
<Popover content={modelTypeContent(model)} title={t('model.type.select')} trigger="click">
|
||||||
<RemoveIcon onClick={() => removeModel(model)} />
|
<SettingIcon />
|
||||||
</ModelListItem>
|
</Popover>
|
||||||
))}
|
</ModelListHeader>
|
||||||
|
<RemoveIcon onClick={() => removeModel(model)} />
|
||||||
|
</ModelListItem>
|
||||||
|
)
|
||||||
|
})}
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
{docsWebsite && (
|
{docsWebsite && (
|
||||||
|
|||||||
@ -84,6 +84,8 @@ export default class GeminiProvider extends BaseProvider {
|
|||||||
{
|
{
|
||||||
model: model.id,
|
model: model.id,
|
||||||
systemInstruction: assistant.prompt,
|
systemInstruction: assistant.prompt,
|
||||||
|
// @ts-ignore googleSearch is not a valid tool for Gemini
|
||||||
|
tools: assistant.enableWebSearch ? [{ googleSearch: {} }] : [],
|
||||||
generationConfig: {
|
generationConfig: {
|
||||||
maxOutputTokens: maxTokens,
|
maxOutputTokens: maxTokens,
|
||||||
temperature: assistant?.settings?.temperature,
|
temperature: assistant?.settings?.temperature,
|
||||||
|
|||||||
@ -13,6 +13,7 @@ export type Assistant = {
|
|||||||
defaultModel?: Model
|
defaultModel?: Model
|
||||||
settings?: Partial<AssistantSettings>
|
settings?: Partial<AssistantSettings>
|
||||||
messages?: AssistantMessage[]
|
messages?: AssistantMessage[]
|
||||||
|
enableWebSearch?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AssistantMessage = {
|
export type AssistantMessage = {
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@ -798,10 +798,10 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@google/generative-ai@npm:^0.16.0":
|
"@google/generative-ai@npm:^0.21.0":
|
||||||
version: 0.16.1
|
version: 0.21.0
|
||||||
resolution: "@google/generative-ai@npm:0.16.1"
|
resolution: "@google/generative-ai@npm:0.21.0"
|
||||||
checksum: 10c0/bad13a040e210a48a44272b81ef78213d23c17491ff3cb0a937952518c79e96005b78d8c3324342b5b8613b6b13f56fe9bc53447c5d2c7da4a9cd26c6a8d7de4
|
checksum: 10c0/cff5946c5964f2380e5097d82bd563d79be27a1a5ac604aaaad3f9ba3382992e4f0a371bd255baabfba4e5bdf296d8ce1410cbd65424afa98e64b2590fe49f3b
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -2341,7 +2341,7 @@ __metadata:
|
|||||||
"@electron-toolkit/preload": "npm:^3.0.0"
|
"@electron-toolkit/preload": "npm:^3.0.0"
|
||||||
"@electron-toolkit/tsconfig": "npm:^1.0.1"
|
"@electron-toolkit/tsconfig": "npm:^1.0.1"
|
||||||
"@electron-toolkit/utils": "npm:^3.0.0"
|
"@electron-toolkit/utils": "npm:^3.0.0"
|
||||||
"@google/generative-ai": "npm:^0.16.0"
|
"@google/generative-ai": "npm:^0.21.0"
|
||||||
"@hello-pangea/dnd": "npm:^16.6.0"
|
"@hello-pangea/dnd": "npm:^16.6.0"
|
||||||
"@kangfenmao/keyv-storage": "npm:^0.1.0"
|
"@kangfenmao/keyv-storage": "npm:^0.1.0"
|
||||||
"@reduxjs/toolkit": "npm:^2.2.5"
|
"@reduxjs/toolkit": "npm:^2.2.5"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user