feat: Add support for AIHubMix topup and charge functionality

This commit is contained in:
kangfenmao 2025-02-08 16:26:10 +08:00
parent 3049023266
commit ee46d2055a
5 changed files with 34 additions and 11 deletions

View File

@ -163,7 +163,11 @@ export class WindowService {
mainWindow.webContents.setWindowOpenHandler((details) => { mainWindow.webContents.setWindowOpenHandler((details) => {
const { url } = details const { url } = details
const oauthProviderUrls = ['https://account.siliconflow.cn/oauth', 'https://aihubmix.com/oauth'] const oauthProviderUrls = [
'https://account.siliconflow.cn/oauth',
'https://aihubmix.com/oauth',
'https://aihubmix.com/topup'
]
if (oauthProviderUrls.some((link) => url.startsWith(link))) { if (oauthProviderUrls.some((link) => url.startsWith(link))) {
return { return {

View File

@ -7,6 +7,7 @@ import {
PlusOutlined, PlusOutlined,
SettingOutlined SettingOutlined
} from '@ant-design/icons' } from '@ant-design/icons'
import { HStack } from '@renderer/components/Layout'
import ModelTags from '@renderer/components/ModelTags' import ModelTags from '@renderer/components/ModelTags'
import OAuthButton from '@renderer/components/OAuth/OAuthButton' import OAuthButton from '@renderer/components/OAuth/OAuthButton'
import { EMBEDDING_REGEX, getModelLogo, REASONING_REGEX, VISION_REGEX } from '@renderer/config/models' import { EMBEDDING_REGEX, getModelLogo, REASONING_REGEX, VISION_REGEX } from '@renderer/config/models'
@ -17,10 +18,11 @@ import { useProvider } from '@renderer/hooks/useProvider'
import i18n from '@renderer/i18n' import i18n from '@renderer/i18n'
import { isOpenAIProvider } from '@renderer/providers/ProviderFactory' import { isOpenAIProvider } from '@renderer/providers/ProviderFactory'
import { checkApi } from '@renderer/services/ApiService' import { checkApi } from '@renderer/services/ApiService'
import { isProviderSupportAuth } from '@renderer/services/ProviderService' import { isProviderSupportAuth, isProviderSupportCharge } from '@renderer/services/ProviderService'
import { useAppDispatch } from '@renderer/store' import { useAppDispatch } from '@renderer/store'
import { setModel } from '@renderer/store/assistants' import { setModel } from '@renderer/store/assistants'
import { Model, ModelType, Provider } from '@renderer/types' import { Model, ModelType, Provider } from '@renderer/types'
import { aihubmixCharge } from '@renderer/utils/oauth'
import { Avatar, Button, Card, Checkbox, Divider, Flex, Input, Popover, Space, Switch } from 'antd' import { Avatar, Button, Card, Checkbox, Divider, Flex, Input, Popover, Space, Switch } from 'antd'
import Link from 'antd/es/typography/Link' import Link from 'antd/es/typography/Link'
import { groupBy, isEmpty } from 'lodash' import { groupBy, isEmpty } from 'lodash'
@ -270,9 +272,14 @@ const ProviderSetting: FC<Props> = ({ provider: _provider }) => {
</Space.Compact> </Space.Compact>
{apiKeyWebsite && ( {apiKeyWebsite && (
<SettingHelpTextRow style={{ justifyContent: 'space-between' }}> <SettingHelpTextRow style={{ justifyContent: 'space-between' }}>
<SettingHelpLink target="_blank" href={apiKeyWebsite}> <HStack gap={10}>
{t('settings.provider.get_api_key')} <SettingHelpLink target="_blank" href={apiKeyWebsite}>
</SettingHelpLink> {t('settings.provider.get_api_key')}
</SettingHelpLink>
{isProviderSupportCharge(provider) && (
<SettingHelpLink onClick={aihubmixCharge}>{t('settings.provider.charge')}</SettingHelpLink>
)}
</HStack>
<SettingHelpText>{t('settings.provider.api_key.tip')}</SettingHelpText> <SettingHelpText>{t('settings.provider.api_key.tip')}</SettingHelpText>
</SettingHelpTextRow> </SettingHelpTextRow>
)} )}

View File

@ -164,10 +164,10 @@ export default class OpenAIProvider extends BaseProvider {
if (['o1', 'o1-2024-12-17'].includes(model.id) || model.id.startsWith('o3')) { if (['o1', 'o1-2024-12-17'].includes(model.id) || model.id.startsWith('o3')) {
systemMessage = { systemMessage = {
role: 'developer', role: 'developer',
content: `Formatting re-enabled${systemMessage ? "\n" + systemMessage.content : ""}` content: `Formatting re-enabled${systemMessage ? '\n' + systemMessage.content : ''}`
}; }
} }
const userMessages: ChatCompletionMessageParam[] = [] const userMessages: ChatCompletionMessageParam[] = []
const _messages = filterContextMessages(takeRight(messages, contextCount + 1)) const _messages = filterContextMessages(takeRight(messages, contextCount + 1))
@ -199,9 +199,7 @@ export default class OpenAIProvider extends BaseProvider {
// @ts-ignore key is not typed // @ts-ignore key is not typed
const stream = await this.sdk.chat.completions.create({ const stream = await this.sdk.chat.completions.create({
model: model.id, model: model.id,
messages: [systemMessage, ...userMessages].filter( messages: [systemMessage, ...userMessages].filter(Boolean) as ChatCompletionMessageParam[],
Boolean
) as ChatCompletionMessageParam[],
temperature: this.getTemperature(assistant, model), temperature: this.getTemperature(assistant, model),
top_p: this.getTopP(assistant, model), top_p: this.getTopP(assistant, model),
max_tokens: maxTokens, max_tokens: maxTokens,

View File

@ -19,3 +19,8 @@ export function isProviderSupportAuth(provider: Provider) {
const supportProviders = ['silicon', 'aihubmix'] const supportProviders = ['silicon', 'aihubmix']
return supportProviders.includes(provider.id) return supportProviders.includes(provider.id)
} }
export function isProviderSupportCharge(provider: Provider) {
const supportProviders = ['aihubmix']
return supportProviders.includes(provider.id)
}

View File

@ -57,3 +57,12 @@ export const oauthWithAihubmix = async (setKey) => {
window.removeEventListener('message', messageHandler) window.removeEventListener('message', messageHandler)
window.addEventListener('message', messageHandler) window.addEventListener('message', messageHandler)
} }
export const aihubmixCharge = async () => {
const chargeUrl = `https://aihubmix.com/topup?client_id=cherry_studio_oauth&lang=${getLanguageCode()}&aff=SJyh`
window.open(
chargeUrl,
'oauth',
'width=720,height=900,toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,alwaysOnTop=yes,alwaysRaised=yes'
)
}