feat: add ollama settings
This commit is contained in:
parent
167988927b
commit
5edb53ef7d
@ -1,3 +1,4 @@
|
||||
import { useAppInit } from '@renderer/hooks/useAppInit'
|
||||
import { message, Modal } from 'antd'
|
||||
import React, { PropsWithChildren, useCallback, useEffect, useRef, useState } from 'react'
|
||||
|
||||
@ -30,6 +31,8 @@ const TopViewContainer: React.FC<Props> = ({ children }) => {
|
||||
const [messageApi, messageContextHolder] = message.useMessage()
|
||||
const [modal, modalContextHolder] = Modal.useModal()
|
||||
|
||||
useAppInit()
|
||||
|
||||
useEffect(() => {
|
||||
window.message = messageApi
|
||||
window.modal = modal
|
||||
|
||||
18
src/renderer/src/hooks/useOllama.ts
Normal file
18
src/renderer/src/hooks/useOllama.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import store, { useAppSelector } from '@renderer/store'
|
||||
import { setOllamaKeepAliveTime } from '@renderer/store/llm'
|
||||
import { useDispatch } from 'react-redux'
|
||||
|
||||
export function useOllamaSettings() {
|
||||
const settings = useAppSelector((state) => state.llm.settings.ollama)
|
||||
const dispatch = useDispatch()
|
||||
|
||||
return { ...settings, setKeepAliveTime: (time: number) => dispatch(setOllamaKeepAliveTime(time)) }
|
||||
}
|
||||
|
||||
export function getOllamaSettings() {
|
||||
return store.getState().llm.settings.ollama
|
||||
}
|
||||
|
||||
export function getOllamaKeepAliveTime() {
|
||||
return store.getState().llm.settings.ollama.keepAliveTime + 'm'
|
||||
}
|
||||
@ -2,12 +2,10 @@ import { Assistant, Topic } from '@renderer/types'
|
||||
import { find } from 'lodash'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
const activeTopicsMap = new Map<string, Topic>()
|
||||
let _activeTopic: Topic
|
||||
|
||||
export function useActiveTopic(assistant: Assistant) {
|
||||
const [activeTopic, setActiveTopic] = useState(activeTopicsMap.get(assistant.id) || assistant?.topics[0])
|
||||
|
||||
activeTopicsMap.set(assistant.id, activeTopic)
|
||||
const [activeTopic, setActiveTopic] = useState(_activeTopic || assistant?.topics[0])
|
||||
|
||||
useEffect(() => {
|
||||
// activeTopic not in assistant.topics
|
||||
|
||||
@ -196,6 +196,12 @@ const resources = {
|
||||
italian: 'Italian',
|
||||
portuguese: 'Portuguese',
|
||||
arabic: 'Arabic'
|
||||
},
|
||||
ollama: {
|
||||
title: 'Ollama',
|
||||
'keep_alive_time.title': 'Keep Alive Time',
|
||||
'keep_alive_time.placeholder': 'Minutes',
|
||||
'keep_alive_time.description': 'The time in minutes to keep the connection alive, default is 5 minutes.'
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -393,6 +399,12 @@ const resources = {
|
||||
italian: '意大利文',
|
||||
portuguese: '葡萄牙文',
|
||||
arabic: '阿拉伯文'
|
||||
},
|
||||
ollama: {
|
||||
title: 'Ollama',
|
||||
'keep_alive_time.title': '保持活跃时间',
|
||||
'keep_alive_time.placeholder': '分钟',
|
||||
'keep_alive_time.description': '对话后模型在内存中保持的时间(默认:5分钟)'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,7 +40,7 @@ const SelectModelDropdown: FC<Props & PropsWithChildren> = ({ children, model, o
|
||||
menu={{ items, style: { maxHeight: '80vh', overflow: 'auto' } }}
|
||||
trigger={['click']}
|
||||
arrow
|
||||
placement="bottomCenter"
|
||||
placement="bottom"
|
||||
overlayClassName="chat-nav-dropdown"
|
||||
{...props}>
|
||||
{children}
|
||||
|
||||
@ -187,7 +187,7 @@ const Inputbar: FC<Props> = ({ assistant, setActiveTopic }) => {
|
||||
<Tag style={{ cursor: 'pointer' }}>{assistant?.settings?.contextCount ?? DEFAULT_CONEXTCOUNT}</Tag>
|
||||
</Tooltip>
|
||||
<Tooltip title={t('chat.input.estimated_tokens.tip')}>
|
||||
<Tag style={{ cursor: 'pointer' }}>↑{`${inputTokenCount} / ${estimateTokenCount}`}</Tag>
|
||||
<Tag style={{ cursor: 'pointer' }}>↑ {`${inputTokenCount} / ${estimateTokenCount}`}</Tag>
|
||||
</Tooltip>
|
||||
</TextCount>
|
||||
)}
|
||||
|
||||
@ -19,6 +19,7 @@ import { FC, useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import OllamSettings from '../providers/OllamaSettings'
|
||||
import { SettingContainer, SettingSubtitle, SettingTitle } from '.'
|
||||
import AddModelPopup from './AddModelPopup'
|
||||
import EditModelsPopup from './EditModelsPopup'
|
||||
@ -126,6 +127,7 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
|
||||
/>
|
||||
{apiEditable && <Button onClick={onReset}>{t('settings.provider.api.url.reset')}</Button>}
|
||||
</Space.Compact>
|
||||
{provider.id === 'ollama' && <OllamSettings />}
|
||||
<SettingSubtitle>{t('common.models')}</SettingSubtitle>
|
||||
{Object.keys(modelGroups).map((group) => (
|
||||
<Card key={group} type="inner" title={group} style={{ marginBottom: '10px' }} size="small">
|
||||
@ -182,14 +184,14 @@ const ModelListHeader = styled.div`
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const HelpTextRow = styled.div`
|
||||
export const HelpTextRow = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 5px 0;
|
||||
`
|
||||
|
||||
const HelpText = styled.div`
|
||||
export const HelpText = styled.div`
|
||||
font-size: 11px;
|
||||
color: var(--color-text);
|
||||
opacity: 0.4;
|
||||
|
||||
35
src/renderer/src/pages/settings/providers/OllamaSettings.tsx
Normal file
35
src/renderer/src/pages/settings/providers/OllamaSettings.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import { useOllamaSettings } from '@renderer/hooks/useOllama'
|
||||
import { InputNumber } from 'antd'
|
||||
import { FC, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import styled from 'styled-components'
|
||||
|
||||
import { SettingSubtitle } from '../components'
|
||||
import { HelpText, HelpTextRow } from '../components/ProviderSetting'
|
||||
|
||||
const OllamSettings: FC = () => {
|
||||
const { keepAliveTime, setKeepAliveTime } = useOllamaSettings()
|
||||
const [keepAliveMinutes, setKeepAliveMinutes] = useState(keepAliveTime)
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<SettingSubtitle>{t('ollama.keep_alive_time.title')}</SettingSubtitle>
|
||||
<InputNumber
|
||||
style={{ width: '100%' }}
|
||||
value={keepAliveMinutes}
|
||||
onChange={(e) => setKeepAliveMinutes(Number(e))}
|
||||
onBlur={() => setKeepAliveTime(keepAliveMinutes)}
|
||||
suffix={t('ollama.keep_alive_time.placeholder')}
|
||||
step={5}
|
||||
/>
|
||||
<HelpTextRow>
|
||||
<HelpText>{t('ollama.keep_alive_time.description')}</HelpText>
|
||||
</HelpTextRow>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
const Container = styled.div``
|
||||
|
||||
export default OllamSettings
|
||||
@ -1,5 +1,6 @@
|
||||
import Anthropic from '@anthropic-ai/sdk'
|
||||
import { MessageCreateParamsNonStreaming, MessageParam } from '@anthropic-ai/sdk/resources'
|
||||
import { getOllamaKeepAliveTime } from '@renderer/hooks/useOllama'
|
||||
import { Assistant, Message, Provider, Suggestion } from '@renderer/types'
|
||||
import { getAssistantSettings, removeQuotes } from '@renderer/utils'
|
||||
import { sum, takeRight } from 'lodash'
|
||||
@ -26,6 +27,10 @@ export default class ProviderSDK {
|
||||
return this.provider.id === 'anthropic'
|
||||
}
|
||||
|
||||
private get keepAliveTime() {
|
||||
return this.provider.id === 'ollama' ? getOllamaKeepAliveTime() : undefined
|
||||
}
|
||||
|
||||
public async completions(
|
||||
messages: Message[],
|
||||
assistant: Assistant,
|
||||
@ -61,11 +66,13 @@ export default class ProviderSDK {
|
||||
})
|
||||
)
|
||||
} else {
|
||||
// @ts-ignore key is not typed
|
||||
const stream = await this.openaiSdk.chat.completions.create({
|
||||
model: model.id,
|
||||
messages: [systemMessage, ...userMessages].filter(Boolean) as ChatCompletionMessageParam[],
|
||||
stream: true,
|
||||
temperature: assistant?.settings?.temperature
|
||||
temperature: assistant?.settings?.temperature,
|
||||
keep_alive: this.keepAliveTime
|
||||
})
|
||||
for await (const chunk of stream) {
|
||||
if (window.keyv.get(EVENT_NAMES.CHAT_COMPLETION_PAUSED)) break
|
||||
@ -92,10 +99,12 @@ export default class ProviderSDK {
|
||||
})
|
||||
return response.content[0].type === 'text' ? response.content[0].text : ''
|
||||
} else {
|
||||
// @ts-ignore key is not typed
|
||||
const response = await this.openaiSdk.chat.completions.create({
|
||||
model: model.id,
|
||||
messages: messages as ChatCompletionMessageParam[],
|
||||
stream: false
|
||||
stream: false,
|
||||
keep_alive: this.keepAliveTime
|
||||
})
|
||||
return response.choices[0].message?.content || ''
|
||||
}
|
||||
@ -124,11 +133,13 @@ export default class ProviderSDK {
|
||||
|
||||
return message.content[0].type === 'text' ? message.content[0].text : null
|
||||
} else {
|
||||
// @ts-ignore key is not typed
|
||||
const response = await this.openaiSdk.chat.completions.create({
|
||||
model: model.id,
|
||||
messages: [systemMessage, ...userMessages] as ChatCompletionMessageParam[],
|
||||
stream: false,
|
||||
max_tokens: 50
|
||||
max_tokens: 50,
|
||||
keep_alive: this.keepAliveTime
|
||||
})
|
||||
|
||||
return removeQuotes(response.choices[0].message?.content || '')
|
||||
|
||||
@ -22,7 +22,7 @@ const persistedReducer = persistReducer(
|
||||
{
|
||||
key: 'cherry-studio',
|
||||
storage,
|
||||
version: 18,
|
||||
version: 19,
|
||||
blacklist: ['runtime'],
|
||||
migrate
|
||||
},
|
||||
|
||||
@ -3,11 +3,18 @@ import { SYSTEM_MODELS } from '@renderer/config/models'
|
||||
import { Model, Provider } from '@renderer/types'
|
||||
import { uniqBy } from 'lodash'
|
||||
|
||||
type LlmSettings = {
|
||||
ollama: {
|
||||
keepAliveTime: number
|
||||
}
|
||||
}
|
||||
|
||||
export interface LlmState {
|
||||
providers: Provider[]
|
||||
defaultModel: Model
|
||||
topicNamingModel: Model
|
||||
translateModel: Model
|
||||
settings: LlmSettings
|
||||
}
|
||||
|
||||
const initialState: LlmState = {
|
||||
@ -132,7 +139,12 @@ const initialState: LlmState = {
|
||||
isSystem: true,
|
||||
enabled: false
|
||||
}
|
||||
]
|
||||
],
|
||||
settings: {
|
||||
ollama: {
|
||||
keepAliveTime: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const settingsSlice = createSlice({
|
||||
@ -179,6 +191,9 @@ const settingsSlice = createSlice({
|
||||
},
|
||||
setTranslateModel: (state, action: PayloadAction<{ model: Model }>) => {
|
||||
state.translateModel = action.payload.model
|
||||
},
|
||||
setOllamaKeepAliveTime: (state, action: PayloadAction<number>) => {
|
||||
state.settings.ollama.keepAliveTime = action.payload
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -192,7 +207,8 @@ export const {
|
||||
removeModel,
|
||||
setDefaultModel,
|
||||
setTopicNamingModel,
|
||||
setTranslateModel
|
||||
setTranslateModel,
|
||||
setOllamaKeepAliveTime
|
||||
} = settingsSlice.actions
|
||||
|
||||
export default settingsSlice.reducer
|
||||
|
||||
@ -272,11 +272,19 @@ const migrateConfig = {
|
||||
}
|
||||
}
|
||||
},
|
||||
'18': (state: RootState) => {
|
||||
'19': (state: RootState) => {
|
||||
return {
|
||||
...state,
|
||||
agents: {
|
||||
agents: []
|
||||
},
|
||||
llm: {
|
||||
...state.llm,
|
||||
settings: {
|
||||
ollama: {
|
||||
keepAliveTime: 5
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user