From 37218eef4f3f886402a96214046e31aebaf294ae Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Tue, 23 Sep 2025 19:18:42 +0800 Subject: [PATCH] feat: enable cherryin provider --- .github/workflows/nightly-build.yml | 6 +-- .github/workflows/release.yml | 6 +-- .oxlintrc.json | 2 +- eslint.config.mjs | 2 +- packages/shared/IpcChannel.ts | 4 +- src/main/config.ts | 2 +- src/main/integration/cherryai/index.js | 1 + src/main/integration/cherryin/index.js | 1 - src/main/ipc.ts | 6 +-- src/preload/index.ts | 4 +- .../aiCore/legacy/clients/ApiClientFactory.ts | 6 +-- .../index.clientCompatibilityTypes.test.ts | 8 ++- .../CherryAiAPIClient.ts} | 8 +-- .../common/ErrorHandlerMiddleware.ts | 4 +- .../src/aiCore/provider/providerConfig.ts | 4 +- .../src/assets/images/providers/aiOnly.png | Bin 41426 -> 0 bytes .../src/assets/images/providers/aiOnly.webp | Bin 0 -> 10588 bytes .../src/components/FreeTrialModelTag.tsx | 2 +- .../Popups/SelectModelPopup/popup.tsx | 12 +++-- .../src/config/__test__/models.test.ts | 6 +-- src/renderer/src/config/models/default.ts | 19 ++----- src/renderer/src/config/providers.ts | 28 +++++++--- src/renderer/src/hooks/useModel.ts | 5 +- src/renderer/src/hooks/useProvider.ts | 9 ++-- src/renderer/src/hooks/useStore.ts | 7 ++- src/renderer/src/i18n/locales/zh-cn.json | 2 +- src/renderer/src/i18n/locales/zh-tw.json | 2 +- src/renderer/src/pages/code/CodeToolsPage.tsx | 2 +- .../home/components/SelectModelButton.tsx | 4 +- .../ProviderSettings/ModelList/ModelList.tsx | 40 +++++++-------- .../ProviderSettings/ProviderOAuth.tsx | 2 +- .../ProviderSettings/ProviderSetting.tsx | 2 +- src/renderer/src/services/ApiService.ts | 2 +- src/renderer/src/services/AssistantService.ts | 11 ++-- src/renderer/src/services/ModelService.ts | 9 ++-- src/renderer/src/services/ProviderService.ts | 8 +-- src/renderer/src/store/index.ts | 2 +- src/renderer/src/store/migrate.ts | 48 +++++++++++++++++- src/renderer/src/utils/model.ts | 2 +- tsconfig.web.json | 2 +- 40 files changed, 173 insertions(+), 117 deletions(-) create mode 100644 src/main/integration/cherryai/index.js delete mode 100644 src/main/integration/cherryin/index.js rename src/renderer/src/aiCore/legacy/clients/{cherryin/CherryinAPIClient.ts => cherryai/CherryAiAPIClient.ts} (86%) delete mode 100644 src/renderer/src/assets/images/providers/aiOnly.png create mode 100644 src/renderer/src/assets/images/providers/aiOnly.webp diff --git a/.github/workflows/nightly-build.yml b/.github/workflows/nightly-build.yml index ce07892bc..7f7100dc5 100644 --- a/.github/workflows/nightly-build.yml +++ b/.github/workflows/nightly-build.yml @@ -98,7 +98,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 - MAIN_VITE_CHERRYIN_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYIN_CLIENT_SECRET }} + MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} @@ -115,7 +115,7 @@ jobs: APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 - MAIN_VITE_CHERRYIN_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYIN_CLIENT_SECRET }} + MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} @@ -127,7 +127,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 - MAIN_VITE_CHERRYIN_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYIN_CLIENT_SECRET }} + MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7428aa031..c4a772ad6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -85,7 +85,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 - MAIN_VITE_CHERRYIN_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYIN_CLIENT_SECRET }} + MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} @@ -103,7 +103,7 @@ jobs: APPLE_TEAM_ID: ${{ vars.APPLE_TEAM_ID }} GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 - MAIN_VITE_CHERRYIN_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYIN_CLIENT_SECRET }} + MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} @@ -115,7 +115,7 @@ jobs: env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} NODE_OPTIONS: --max-old-space-size=8192 - MAIN_VITE_CHERRYIN_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYIN_CLIENT_SECRET }} + MAIN_VITE_CHERRYAI_CLIENT_SECRET: ${{ secrets.MAIN_VITE_CHERRYAI_CLIENT_SECRET }} MAIN_VITE_MINERU_API_KEY: ${{ vars.MAIN_VITE_MINERU_API_KEY }} RENDERER_VITE_AIHUBMIX_SECRET: ${{ vars.RENDERER_VITE_AIHUBMIX_SECRET }} RENDERER_VITE_PPIO_APP_SECRET: ${{ vars.RENDERER_VITE_PPIO_APP_SECRET }} diff --git a/.oxlintrc.json b/.oxlintrc.json index 6f4accbec..0a6a9764b 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -15,7 +15,7 @@ ".gitignore", "scripts/cloudflare-worker.js", "src/main/integration/nutstore/sso/lib/**", - "src/main/integration/cherryin/index.js", + "src/main/integration/cherryai/index.js", "src/main/integration/nutstore/sso/lib/**", "src/renderer/src/ui/**", "packages/**/dist", diff --git a/eslint.config.mjs b/eslint.config.mjs index f9e6c0750..be4a95cd6 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -59,7 +59,7 @@ export default defineConfig([ '.gitignore', 'scripts/cloudflare-worker.js', 'src/main/integration/nutstore/sso/lib/**', - 'src/main/integration/cherryin/index.js', + 'src/main/integration/cherryai/index.js', 'src/main/integration/nutstore/sso/lib/**', 'src/renderer/src/ui/**', 'packages/**/dist' diff --git a/packages/shared/IpcChannel.ts b/packages/shared/IpcChannel.ts index 7f766d21e..0eb0dd279 100644 --- a/packages/shared/IpcChannel.ts +++ b/packages/shared/IpcChannel.ts @@ -330,6 +330,6 @@ export enum IpcChannel { // OCR OCR_ocr = 'ocr:ocr', - // Cherryin - Cherryin_GetSignature = 'cherryin:get-signature' + // CherryAI + Cherryai_GetSignature = 'cherryai:get-signature' } diff --git a/src/main/config.ts b/src/main/config.ts index 0cffcd176..b4859bafb 100644 --- a/src/main/config.ts +++ b/src/main/config.ts @@ -21,4 +21,4 @@ export const titleBarOverlayLight = { symbolColor: '#000' } -global.CHERRYIN_CLIENT_SECRET = import.meta.env.MAIN_VITE_CHERRYIN_CLIENT_SECRET +global.CHERRYAI_CLIENT_SECRET = import.meta.env.MAIN_VITE_CHERRYAI_CLIENT_SECRET diff --git a/src/main/integration/cherryai/index.js b/src/main/integration/cherryai/index.js new file mode 100644 index 000000000..eccd3b85b --- /dev/null +++ b/src/main/integration/cherryai/index.js @@ -0,0 +1 @@ +var _0xe15d9a;const crypto=require("\u0063\u0072\u0079\u0070\u0074\u006F");_0xe15d9a=(988194^988194)+(417607^417603);var _0x9b_0x742=(247379^247387)+(371889^371892);const CLIENT_ID="\u0063\u0068\u0065\u0072\u0072\u0079\u002D\u0073\u0074\u0075\u0064\u0069\u006F";_0x9b_0x742=(202849^202856)+(796590^796585);var _0xa971e=(422203^422203)+(167917^167919);const CLIENT_SECRET_SUFFIX="\u0047\u0076\u0049\u0036\u0049\u0035\u005A\u0072\u0045\u0048\u0063\u0047\u004F\u0057\u006A\u004F\u0035\u0041\u004B\u0068\u004A\u004B\u0047\u006D\u006E\u0077\u0077\u0047\u0066\u004D\u0036\u0032\u0058\u004B\u0070\u0057\u0071\u006B\u006A\u0068\u0076\u007A\u0052\u0055\u0032\u004E\u005A\u0049\u0069\u006E\u004D\u0037\u0037\u0061\u0054\u0047\u0049\u0071\u0068\u0071\u0079\u0073\u0030\u0067";_0xa971e=(607707^607705)+(127822^127823);const CLIENT_SECRET=global['\u0043\u0048\u0045\u0052\u0052\u0059\u0041\u0049\u005F\u0043\u004C\u0049\u0045\u004E\u0054\u005F\u0053\u0045\u0043\u0052\u0045\u0054']+"\u002E"+CLIENT_SECRET_SUFFIX;class SignatureClient{constructor(clientId,clientSecret){this['\u0063\u006C\u0069\u0065\u006E\u0074\u0049\u0064']=clientId||CLIENT_ID;this['\u0063\u006C\u0069\u0065\u006E\u0074\u0053\u0065\u0063\u0072\u0065\u0074']=clientSecret||CLIENT_SECRET;this['\u0067\u0065\u006E\u0065\u0072\u0061\u0074\u0065\u0053\u0069\u0067\u006E\u0061\u0074\u0075\u0072\u0065']=this['\u0067\u0065\u006E\u0065\u0072\u0061\u0074\u0065\u0053\u0069\u0067\u006E\u0061\u0074\u0075\u0072\u0065']['\u0062\u0069\u006E\u0064'](this);}generateSignature(options){const{'\u006D\u0065\u0074\u0068\u006F\u0064':method,'\u0070\u0061\u0074\u0068':path,'\u0071\u0075\u0065\u0072\u0079':query='','\u0062\u006F\u0064\u0079':body=''}=options;var _0x99a7f=(735625^735624)+(520507^520508);const timestamp=Math['\u0066\u006C\u006F\u006F\u0072'](Date['\u006E\u006F\u0077']()/(351300^352172))['\u0074\u006F\u0053\u0074\u0072\u0069\u006E\u0067']();_0x99a7f=376728^376729;var _0x733a=(876666^876671)+(658949^658944);let bodyString='';_0x733a="kgclcd".split("").reverse().join("");if(body){if(typeof body==="tcejbo".split("").reverse().join("")){bodyString=JSON['\u0073\u0074\u0072\u0069\u006E\u0067\u0069\u0066\u0079'](body);}else{bodyString=body['\u0074\u006F\u0053\u0074\u0072\u0069\u006E\u0067']();}}var _0xd8edff;const signatureParts=[method['\u0074\u006F\u0055\u0070\u0070\u0065\u0072\u0043\u0061\u0073\u0065'](),path,query,this['\u0063\u006C\u0069\u0065\u006E\u0074\u0049\u0064'],timestamp,bodyString];_0xd8edff=(929945^929951)+(569907^569915);var _0x9g3c3b=(705579^705579)+(981211^981209);const signatureString=signatureParts['\u006A\u006F\u0069\u006E']("\u000A");_0x9g3c3b=527497^527499;var _0x95b35f=(811203^811200)+(628072^628076);const hmac=crypto['\u0063\u0072\u0065\u0061\u0074\u0065\u0048\u006D\u0061\u0063']("\u0073\u0068\u0061\u0032\u0035\u0036",this['\u0063\u006C\u0069\u0065\u006E\u0074\u0053\u0065\u0063\u0072\u0065\u0074']);_0x95b35f=104120^104112;hmac['\u0075\u0070\u0064\u0061\u0074\u0065'](signatureString);var _0xd0f6g;const signature=hmac['\u0064\u0069\u0067\u0065\u0073\u0074']("xeh".split("").reverse().join(""));_0xd0f6g=(615019^615018)+(266997^266992);return{'X-Client-ID':this['\u0063\u006C\u0069\u0065\u006E\u0074\u0049\u0064'],"\u0058\u002D\u0054\u0069\u006D\u0065\u0073\u0074\u0061\u006D\u0070":timestamp,'X-Signature':signature};}}const signatureClient=new SignatureClient();const generateSignature=signatureClient['\u0067\u0065\u006E\u0065\u0072\u0061\u0074\u0065\u0053\u0069\u0067\u006E\u0061\u0074\u0075\u0072\u0065'];module['\u0065\u0078\u0070\u006F\u0072\u0074\u0073']={'\u0053\u0069\u0067\u006E\u0061\u0074\u0075\u0072\u0065\u0043\u006C\u0069\u0065\u006E\u0074':SignatureClient,"generateSignature":generateSignature}; \ No newline at end of file diff --git a/src/main/integration/cherryin/index.js b/src/main/integration/cherryin/index.js deleted file mode 100644 index af185389e..000000000 --- a/src/main/integration/cherryin/index.js +++ /dev/null @@ -1 +0,0 @@ -var _0x6gg;const crypto=require("\u0063\u0072\u0079\u0070\u0074\u006F");_0x6gg='\u006D\u006F\u006C\u006A\u0065\u0065';var _0x111cbe;const CLIENT_ID="oiduts-yrrehc".split("").reverse().join("");_0x111cbe=(977158^977167)+(164595^164594);var _0x6d6adc=(756649^756650)+(497587^497587);const CLIENT_SECRET_SUFFIX="\u0047\u0076\u0049\u0036\u0049\u0035\u005A\u0072\u0045\u0048\u0063\u0047\u004F\u0057\u006A\u004F\u0035\u0041\u004B\u0068\u004A\u004B\u0047\u006D\u006E\u0077\u0077\u0047\u0066\u004D\u0036\u0032\u0058\u004B\u0070\u0057\u0071\u006B\u006A\u0068\u0076\u007A\u0052\u0055\u0032\u004E\u005A\u0049\u0069\u006E\u004D\u0037\u0037\u0061\u0054\u0047\u0049\u0071\u0068\u0071\u0079\u0073\u0030\u0067";_0x6d6adc=233169^233176;const CLIENT_SECRET=global['\u0043\u0048\u0045\u0052\u0052\u0059\u0049\u004E\u005F\u0043\u004C\u0049\u0045\u004E\u0054\u005F\u0053\u0045\u0043\u0052\u0045\u0054']+"\u002E"+CLIENT_SECRET_SUFFIX;class SignatureClient{constructor(clientId,clientSecret){this['\u0063\u006C\u0069\u0065\u006E\u0074\u0049\u0064']=clientId||CLIENT_ID;this['\u0063\u006C\u0069\u0065\u006E\u0074\u0053\u0065\u0063\u0072\u0065\u0074']=clientSecret||CLIENT_SECRET;this['\u0067\u0065\u006E\u0065\u0072\u0061\u0074\u0065\u0053\u0069\u0067\u006E\u0061\u0074\u0075\u0072\u0065']=this['\u0067\u0065\u006E\u0065\u0072\u0061\u0074\u0065\u0053\u0069\u0067\u006E\u0061\u0074\u0075\u0072\u0065']['\u0062\u0069\u006E\u0064'](this);}generateSignature(options){const{"method":method,"path":path,"query":query='',"body":body=''}=options;const timestamp=Math['\u0066\u006C\u006F\u006F\u0072'](Date['\u006E\u006F\u0077']()/(110765^111429))['\u0074\u006F\u0053\u0074\u0072\u0069\u006E\u0067']();var _0xe08cc=(212246^212244)+(773521^773523);let bodyString='';_0xe08cc=(606778^606776)+(962748^962740);if(body){if(typeof body==="\u006F\u0062\u006A\u0065\u0063\u0074"){bodyString=JSON['\u0073\u0074\u0072\u0069\u006E\u0067\u0069\u0066\u0079'](body);}else{bodyString=body['\u0074\u006F\u0053\u0074\u0072\u0069\u006E\u0067']();}}const signatureParts=[method['\u0074\u006F\u0055\u0070\u0070\u0065\u0072\u0043\u0061\u0073\u0065'](),path,query,this['\u0063\u006C\u0069\u0065\u006E\u0074\u0049\u0064'],timestamp,bodyString];var _0x5693g=(936664^936668)+(685268^685277);const signatureString=signatureParts['\u006A\u006F\u0069\u006E']("\u000A");_0x5693g=(266582^266576)+(337322^337315);const hmac=crypto['\u0063\u0072\u0065\u0061\u0074\u0065\u0048\u006D\u0061\u0063']("\u0073\u0068\u0061\u0032\u0035\u0036",this['\u0063\u006C\u0069\u0065\u006E\u0074\u0053\u0065\u0063\u0072\u0065\u0074']);hmac['\u0075\u0070\u0064\u0061\u0074\u0065'](signatureString);var _0x5fba=(354480^354481)+(537437^537434);const signature=hmac['\u0064\u0069\u0067\u0065\u0073\u0074']("\u0068\u0065\u0078");_0x5fba=(249614^249610)+(915906^915914);return{'X-Client-ID':this['\u0063\u006C\u0069\u0065\u006E\u0074\u0049\u0064'],'X-Timestamp':timestamp,'X-Signature':signature};}}const signatureClient=new SignatureClient();const generateSignature=signatureClient['\u0067\u0065\u006E\u0065\u0072\u0061\u0074\u0065\u0053\u0069\u0067\u006E\u0061\u0074\u0075\u0072\u0065'];module['\u0065\u0078\u0070\u006F\u0072\u0074\u0073']={'\u0053\u0069\u0067\u006E\u0061\u0074\u0075\u0072\u0065\u0043\u006C\u0069\u0065\u006E\u0074':SignatureClient,'\u0067\u0065\u006E\u0065\u0072\u0061\u0074\u0065\u0053\u0069\u0067\u006E\u0061\u0074\u0075\u0072\u0065':generateSignature}; \ No newline at end of file diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 7ca6853e3..0ef9cca2f 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -4,7 +4,7 @@ import path from 'node:path' import { loggerService } from '@logger' import { isLinux, isMac, isPortable, isWin } from '@main/constant' -import { generateSignature } from '@main/integration/cherryin' +import { generateSignature } from '@main/integration/cherryai' import anthropicService from '@main/services/AnthropicService' import { getBinaryPath, isBinaryExists, runInstallScript } from '@main/utils/process' import { handleZoomFactor } from '@main/utils/zoom' @@ -841,6 +841,6 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { ocrService.ocr(file, provider) ) - // CherryIN - ipcMain.handle(IpcChannel.Cherryin_GetSignature, (_, params) => generateSignature(params)) + // CherryAI + ipcMain.handle(IpcChannel.Cherryai_GetSignature, (_, params) => generateSignature(params)) } diff --git a/src/preload/index.ts b/src/preload/index.ts index a77e47e78..faa0335ae 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -455,9 +455,9 @@ const api = { ocr: (file: SupportedOcrFile, provider: OcrProvider): Promise => ipcRenderer.invoke(IpcChannel.OCR_ocr, file, provider) }, - cherryin: { + cherryai: { generateSignature: (params: { method: string; path: string; query: string; body: Record }) => - ipcRenderer.invoke(IpcChannel.Cherryin_GetSignature, params) + ipcRenderer.invoke(IpcChannel.Cherryai_GetSignature, params) }, windowControls: { minimize: (): Promise => ipcRenderer.invoke(IpcChannel.Windows_Minimize), diff --git a/src/renderer/src/aiCore/legacy/clients/ApiClientFactory.ts b/src/renderer/src/aiCore/legacy/clients/ApiClientFactory.ts index 31a911533..b38ab5953 100644 --- a/src/renderer/src/aiCore/legacy/clients/ApiClientFactory.ts +++ b/src/renderer/src/aiCore/legacy/clients/ApiClientFactory.ts @@ -5,7 +5,7 @@ import { AihubmixAPIClient } from './aihubmix/AihubmixAPIClient' import { AnthropicAPIClient } from './anthropic/AnthropicAPIClient' import { AwsBedrockAPIClient } from './aws/AwsBedrockAPIClient' import { BaseApiClient } from './BaseApiClient' -import { CherryinAPIClient } from './cherryin/CherryinAPIClient' +import { CherryAiAPIClient } from './cherryai/CherryAiAPIClient' import { GeminiAPIClient } from './gemini/GeminiAPIClient' import { VertexAPIClient } from './gemini/VertexAPIClient' import { NewAPIClient } from './newapi/NewAPIClient' @@ -34,8 +34,8 @@ export class ApiClientFactory { let instance: BaseApiClient // 首先检查特殊的 Provider ID - if (provider.id === 'cherryin') { - instance = new CherryinAPIClient(provider) as BaseApiClient + if (provider.id === 'cherryai') { + instance = new CherryAiAPIClient(provider) as BaseApiClient return instance } diff --git a/src/renderer/src/aiCore/legacy/clients/__tests__/index.clientCompatibilityTypes.test.ts b/src/renderer/src/aiCore/legacy/clients/__tests__/index.clientCompatibilityTypes.test.ts index 343bc4d54..d70d9c58f 100644 --- a/src/renderer/src/aiCore/legacy/clients/__tests__/index.clientCompatibilityTypes.test.ts +++ b/src/renderer/src/aiCore/legacy/clients/__tests__/index.clientCompatibilityTypes.test.ts @@ -35,10 +35,16 @@ vi.mock('@renderer/config/models', () => ({ findTokenLimit: vi.fn().mockReturnValue(4096), isFunctionCallingModel: vi.fn().mockReturnValue(false), DEFAULT_MAX_TOKENS: 4096, + qwen38bModel: { + id: 'Qwen/Qwen3-8B', + name: 'Qwen3-8B', + provider: 'cherryai', + group: 'Qwen' + }, glm45FlashModel: { id: 'glm-4.5-flash', name: 'GLM-4.5-Flash', - provider: 'cherryin', + provider: 'cherryai', group: 'GLM-4.5' } })) diff --git a/src/renderer/src/aiCore/legacy/clients/cherryin/CherryinAPIClient.ts b/src/renderer/src/aiCore/legacy/clients/cherryai/CherryAiAPIClient.ts similarity index 86% rename from src/renderer/src/aiCore/legacy/clients/cherryin/CherryinAPIClient.ts rename to src/renderer/src/aiCore/legacy/clients/cherryai/CherryAiAPIClient.ts index bf3ed7d71..8f8969bd5 100644 --- a/src/renderer/src/aiCore/legacy/clients/cherryin/CherryinAPIClient.ts +++ b/src/renderer/src/aiCore/legacy/clients/cherryai/CherryAiAPIClient.ts @@ -4,7 +4,7 @@ import OpenAI from 'openai' import { OpenAIAPIClient } from '../openai/OpenAIApiClient' -export class CherryinAPIClient extends OpenAIAPIClient { +export class CherryAiAPIClient extends OpenAIAPIClient { constructor(provider: Provider) { super(provider) } @@ -17,7 +17,7 @@ export class CherryinAPIClient extends OpenAIAPIClient { options = options || {} options.headers = options.headers || {} - const signature = await window.api.cherryin.generateSignature({ + const signature = await window.api.cherryai.generateSignature({ method: 'POST', path: '/chat/completions', query: '', @@ -34,7 +34,7 @@ export class CherryinAPIClient extends OpenAIAPIClient { } override getClientCompatibilityType(): string[] { - return ['CherryinAPIClient'] + return ['CherryAiAPIClient'] } public async listModels(): Promise { @@ -43,7 +43,7 @@ export class CherryinAPIClient extends OpenAIAPIClient { const created = Date.now() return models.map((id) => ({ id, - owned_by: 'cherryin', + owned_by: 'cherryai', object: 'model' as const, created })) diff --git a/src/renderer/src/aiCore/legacy/middleware/common/ErrorHandlerMiddleware.ts b/src/renderer/src/aiCore/legacy/middleware/common/ErrorHandlerMiddleware.ts index d80c9d2f8..dde98cbd1 100644 --- a/src/renderer/src/aiCore/legacy/middleware/common/ErrorHandlerMiddleware.ts +++ b/src/renderer/src/aiCore/legacy/middleware/common/ErrorHandlerMiddleware.ts @@ -1,6 +1,6 @@ import { loggerService } from '@logger' import { isZhipuModel } from '@renderer/config/models' -import store from '@renderer/store' +import { getStoreProviders } from '@renderer/hooks/useStore' import { Chunk } from '@renderer/types/chunk' import { CompletionsParams, CompletionsResult } from '../schemas' @@ -87,7 +87,7 @@ function handleError(error: any, params: CompletionsParams): any { * 2. 绘画功能(enableGenerateImage为true)使用通用错误处理 */ function handleZhipuError(error: any): any { - const provider = store.getState().llm.providers.find((p) => p.id === 'zhipu') + const provider = getStoreProviders().find((p) => p.id === 'zhipu') const logger = loggerService.withContext('handleZhipuError') // 定义错误模式映射 diff --git a/src/renderer/src/aiCore/provider/providerConfig.ts b/src/renderer/src/aiCore/provider/providerConfig.ts index 33116272e..eaaef1521 100644 --- a/src/renderer/src/aiCore/provider/providerConfig.ts +++ b/src/renderer/src/aiCore/provider/providerConfig.ts @@ -250,10 +250,10 @@ export async function prepareSpecialProviderConfig( config.options.apiKey = token break } - case 'cherryin': { + case 'cherryai': { config.options.fetch = async (url, options) => { // 在这里对最终参数进行签名 - const signature = await window.api.cherryin.generateSignature({ + const signature = await window.api.cherryai.generateSignature({ method: 'POST', path: '/chat/completions', query: '', diff --git a/src/renderer/src/assets/images/providers/aiOnly.png b/src/renderer/src/assets/images/providers/aiOnly.png deleted file mode 100644 index a521f3bcb87c1f352868fd3774bf3f16640e902c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 41426 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 SsFg2)e`-qFily>*L;gSV8mck? diff --git a/src/renderer/src/assets/images/providers/aiOnly.webp b/src/renderer/src/assets/images/providers/aiOnly.webp new file mode 100644 index 0000000000000000000000000000000000000000..f86f74543b3255e5c27eee989286a4545e03eb3a GIT binary patch literal 10588 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 literal 0 HcmV?d00001 diff --git a/src/renderer/src/components/FreeTrialModelTag.tsx b/src/renderer/src/components/FreeTrialModelTag.tsx index 0ce63ade3..b86de9970 100644 --- a/src/renderer/src/components/FreeTrialModelTag.tsx +++ b/src/renderer/src/components/FreeTrialModelTag.tsx @@ -15,7 +15,7 @@ interface Props { } export const FreeTrialModelTag: FC = ({ model, showLabel = true }) => { - if (model.provider !== 'cherryin') { + if (model.provider !== 'cherryai') { return null } diff --git a/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx b/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx index 1cd792614..45560bcd6 100644 --- a/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx +++ b/src/renderer/src/components/Popups/SelectModelPopup/popup.tsx @@ -1,5 +1,6 @@ import { PushpinOutlined } from '@ant-design/icons' import { FreeTrialModelTag } from '@renderer/components/FreeTrialModelTag' +import { HStack } from '@renderer/components/Layout' import ModelTagsWithLabel from '@renderer/components/ModelTagsWithLabel' import { TopView } from '@renderer/components/TopView' import { DynamicVirtualList, type DynamicVirtualListRef } from '@renderer/components/VirtualList' @@ -102,16 +103,18 @@ const PopupContainer: React.FC = ({ model, filter: baseFilter, showTagFil (model: Model, provider: Provider, isPinned: boolean): FlatListModel => { const modelId = getModelUniqId(model) const groupName = getFancyProviderName(provider) - const isCherryin = provider.id === 'cherryin' + const isCherryAi = provider.id === 'cherryai' return { key: isPinned ? `${modelId}_pinned` : modelId, type: 'model', name: ( - {model.name} - {isPinned && | {groupName}} - {isCherryin && } + + {model.name} + {isPinned && | {groupName}} + + {isCherryAi && } ), tags: ( @@ -542,6 +545,7 @@ const ModelItemLeft = styled.div` const ModelName = styled.div` display: flex; flex-direction: row; + justify-content: space-between; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; diff --git a/src/renderer/src/config/__test__/models.test.ts b/src/renderer/src/config/__test__/models.test.ts index 547ff42ef..926ac3547 100644 --- a/src/renderer/src/config/__test__/models.test.ts +++ b/src/renderer/src/config/__test__/models.test.ts @@ -16,7 +16,7 @@ describe('Qwen Model Detection', () => { initialState: {} })) vi.mock('@renderer/services/AssistantService', () => ({ - getProviderByModel: vi.fn().mockReturnValue({ id: 'cherryin' }) + getProviderByModel: vi.fn().mockReturnValue({ id: 'cherryai' }) })) }) test('isQwenReasoningModel', () => { @@ -52,7 +52,7 @@ describe('Vision Model Detection', () => { initialState: {} })) vi.mock('@renderer/services/AssistantService', () => ({ - getProviderByModel: vi.fn().mockReturnValue({ id: 'cherryin' }) + getProviderByModel: vi.fn().mockReturnValue({ id: 'cherryai' }) })) }) test('isVisionModel', () => { @@ -81,7 +81,7 @@ describe('Web Search Model Detection', () => { initialState: {} })) vi.mock('@renderer/services/AssistantService', () => ({ - getProviderByModel: vi.fn().mockReturnValue({ id: 'cherryin' }) + getProviderByModel: vi.fn().mockReturnValue({ id: 'cherryai' }) })) }) test('isWebSearchModel', () => { diff --git a/src/renderer/src/config/models/default.ts b/src/renderer/src/config/models/default.ts index 2f13a5e99..02bf37af9 100644 --- a/src/renderer/src/config/models/default.ts +++ b/src/renderer/src/config/models/default.ts @@ -3,14 +3,14 @@ import { Model, SystemProviderId } from '@renderer/types' export const glm45FlashModel: Model = { id: 'glm-4.5-flash', name: 'GLM-4.5-Flash', - provider: 'cherryin', + provider: 'cherryai', group: 'GLM-4.5' } export const qwen38bModel: Model = { id: 'Qwen/Qwen3-8B', name: 'Qwen3-8B', - provider: 'cherryin', + provider: 'cherryai', group: 'Qwen' } @@ -25,20 +25,7 @@ export const SYSTEM_MODELS: Record = // Default quick assistant model glm45FlashModel ], - cherryin: [ - { - id: 'glm-4.5-flash', - name: 'GLM-4.5-Flash', - provider: 'cherryin', - group: 'GLM-4.5' - }, - { - id: 'Qwen/Qwen3-8B', - name: 'Qwen3-8B', - provider: 'cherryin', - group: 'Qwen' - } - ], + cherryin: [], vertexai: [], '302ai': [ { diff --git a/src/renderer/src/config/providers.ts b/src/renderer/src/config/providers.ts index e07414e2c..80ea9bdf7 100644 --- a/src/renderer/src/config/providers.ts +++ b/src/renderer/src/config/providers.ts @@ -3,7 +3,7 @@ import HunyuanProviderLogo from '@renderer/assets/images/models/hunyuan.png' import AzureProviderLogo from '@renderer/assets/images/models/microsoft.png' import Ai302ProviderLogo from '@renderer/assets/images/providers/302ai.webp' import AiHubMixProviderLogo from '@renderer/assets/images/providers/aihubmix.webp' -import AiOnlyProviderLogo from '@renderer/assets/images/providers/aiOnly.png' +import AiOnlyProviderLogo from '@renderer/assets/images/providers/aiOnly.webp' import AlayaNewProviderLogo from '@renderer/assets/images/providers/alayanew.webp' import AnthropicProviderLogo from '@renderer/assets/images/providers/anthropic.png' import AwsProviderLogo from '@renderer/assets/images/providers/aws-bedrock.webp' @@ -64,7 +64,18 @@ import { } from '@renderer/types' import { TOKENFLUX_HOST } from './constant' -import { SYSTEM_MODELS } from './models' +import { glm45FlashModel, qwen38bModel, SYSTEM_MODELS } from './models' + +export const CHERRYAI_PROVIDER: SystemProvider = { + id: 'cherryai' as SystemProviderId, + name: 'CherryAI', + type: 'openai', + apiKey: '', + apiHost: 'https://api.cherry-ai.com/', + models: [glm45FlashModel, qwen38bModel], + isSystem: true, + enabled: true +} export const SYSTEM_PROVIDERS_CONFIG: Record = { cherryin: { @@ -72,8 +83,8 @@ export const SYSTEM_PROVIDERS_CONFIG: Record = name: 'CherryIN', type: 'openai', apiKey: '', - apiHost: 'https://api.cherry-ai.com/', - models: SYSTEM_MODELS.cherryin, + apiHost: 'https://open.cherryin.ai', + models: [], isSystem: true, enabled: true }, @@ -699,12 +710,13 @@ type ProviderUrls = { export const PROVIDER_URLS: Record = { cherryin: { api: { - url: 'https://api.cherry-ai.com' + url: 'https://open.cherryin.ai' }, websites: { - official: 'https://cherry-ai.com', - docs: 'https://docs.cherry-ai.com', - models: 'https://docs.cherry-ai.com/pre-basic/providers/cherryin' + official: 'https://open.cherryin.ai', + apiKey: 'https://open.cherryin.ai/console/token', + docs: 'https://open.cherryin.ai', + models: 'https://open.cherryin.ai/pricing' } }, ph8: { diff --git a/src/renderer/src/hooks/useModel.ts b/src/renderer/src/hooks/useModel.ts index 27962e7ce..75119d0f8 100644 --- a/src/renderer/src/hooks/useModel.ts +++ b/src/renderer/src/hooks/useModel.ts @@ -1,6 +1,5 @@ -import store from '@renderer/store' - import { useProviders } from './useProvider' +import { getStoreProviders } from './useStore' export function useModel(id?: string, providerId?: string) { const { providers } = useProviders() @@ -15,7 +14,7 @@ export function useModel(id?: string, providerId?: string) { } export function getModel(id?: string, providerId?: string) { - const providers = store.getState().llm.providers + const providers = getStoreProviders() const allModels = providers.map((p) => p.models).flat() return allModels.find((m) => { if (providerId) { diff --git a/src/renderer/src/hooks/useProvider.ts b/src/renderer/src/hooks/useProvider.ts index 9182db377..96120d4dc 100644 --- a/src/renderer/src/hooks/useProvider.ts +++ b/src/renderer/src/hooks/useProvider.ts @@ -1,4 +1,5 @@ import { createSelector } from '@reduxjs/toolkit' +import { CHERRYAI_PROVIDER } from '@renderer/config/providers' import { getDefaultProvider } from '@renderer/services/AssistantService' import { useAppDispatch, useAppSelector } from '@renderer/store' import { @@ -16,7 +17,7 @@ import { useDefaultModel } from './useAssistant' const selectEnabledProviders = createSelector( (state) => state.llm.providers, - (providers) => providers.filter((p) => p.enabled) + (providers) => providers.filter((p) => p.enabled).concat(CHERRYAI_PROVIDER) ) export function useProviders() { @@ -24,7 +25,7 @@ export function useProviders() { const dispatch = useAppDispatch() return { - providers: providers || {}, + providers: providers || [], addProvider: (provider: Provider) => dispatch(addProvider(provider)), removeProvider: (provider: Provider) => dispatch(removeProvider(provider)), updateProvider: (updates: Partial & { id: string }) => dispatch(updateProvider(updates)), @@ -45,7 +46,9 @@ export function useAllProviders() { } export function useProvider(id: string) { - const provider = useAppSelector((state) => state.llm.providers.find((p) => p.id === id)) || getDefaultProvider() + const provider = + useAppSelector((state) => state.llm.providers.concat([CHERRYAI_PROVIDER]).find((p) => p.id === id)) || + getDefaultProvider() const dispatch = useAppDispatch() return { diff --git a/src/renderer/src/hooks/useStore.ts b/src/renderer/src/hooks/useStore.ts index 1b731e74c..53e464645 100644 --- a/src/renderer/src/hooks/useStore.ts +++ b/src/renderer/src/hooks/useStore.ts @@ -1,4 +1,5 @@ -import { useAppDispatch, useAppSelector } from '@renderer/store' +import { CHERRYAI_PROVIDER } from '@renderer/config/providers' +import store, { useAppDispatch, useAppSelector } from '@renderer/store' import { setAssistantsTabSortType, setShowAssistants, @@ -39,3 +40,7 @@ export function useAssistantsTabSortType() { setAssistantsTabSortType: (sortType: AssistantsSortType) => dispatch(setAssistantsTabSortType(sortType)) } } + +export function getStoreProviders() { + return store.getState().llm.providers.concat([CHERRYAI_PROVIDER]) +} diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index defbfe9dd..dd3fa37be 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -2018,7 +2018,7 @@ "provider": { "302ai": "302.AI", "aihubmix": "AiHubMix", - "aionly": "唯一AI(AiOnly)", + "aionly": "唯一AI (AiOnly)", "alayanew": "Alaya NeW", "anthropic": "Anthropic", "aws-bedrock": "AWS Bedrock", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index b5ac09bb7..4e162abda 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -2018,7 +2018,7 @@ "provider": { "302ai": "302.AI", "aihubmix": "AiHubMix", - "aionly": "唯一AI(AiOnly)", + "aionly": "唯一AI (AiOnly)", "alayanew": "Alaya NeW", "anthropic": "Anthropic", "aws-bedrock": "AWS Bedrock", diff --git a/src/renderer/src/pages/code/CodeToolsPage.tsx b/src/renderer/src/pages/code/CodeToolsPage.tsx index 14f540f5d..69d9fb728 100644 --- a/src/renderer/src/pages/code/CodeToolsPage.tsx +++ b/src/renderer/src/pages/code/CodeToolsPage.tsx @@ -70,7 +70,7 @@ const CodeToolsPage: FC = () => { if (isEmbeddingModel(m) || isRerankModel(m) || isTextToImageModel(m)) { return false } - if (m.provider === 'cherryin') { + if (m.provider === 'cherryai') { return false } if (selectedCliTool === codeTools.claudeCode) { diff --git a/src/renderer/src/pages/home/components/SelectModelButton.tsx b/src/renderer/src/pages/home/components/SelectModelButton.tsx index bd6af86f7..c9fa63d28 100644 --- a/src/renderer/src/pages/home/components/SelectModelButton.tsx +++ b/src/renderer/src/pages/home/components/SelectModelButton.tsx @@ -3,8 +3,8 @@ import SelectModelPopup from '@renderer/components/Popups/SelectModelPopup' import { isLocalAi } from '@renderer/config/env' import { isEmbeddingModel, isRerankModel, isWebSearchModel } from '@renderer/config/models' import { useAssistant } from '@renderer/hooks/useAssistant' +import { useProvider } from '@renderer/hooks/useProvider' import { getProviderName } from '@renderer/services/ProviderService' -import { useAppSelector } from '@renderer/store' import { Assistant, Model } from '@renderer/types' import { Button, Tag } from 'antd' import { ChevronsUpDown } from 'lucide-react' @@ -20,7 +20,7 @@ const SelectModelButton: FC = ({ assistant }) => { const { model, updateAssistant } = useAssistant(assistant.id) const { t } = useTranslation() const timerRef = useRef(undefined) - const provider = useAppSelector((state) => state.llm.providers.find((p) => p.id === model?.provider)) + const provider = useProvider(model?.provider) const modelFilter = (model: Model) => !isEmbeddingModel(model) && !isRerankModel(model) diff --git a/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelList.tsx b/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelList.tsx index d0e1304a2..58468f09b 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelList.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ModelList/ModelList.tsx @@ -50,7 +50,6 @@ const ModelList: React.FC = ({ providerId }) => { const providerConfig = PROVIDER_URLS[provider.id] const docsWebsite = providerConfig?.websites?.docs const modelsWebsite = providerConfig?.websites?.models - const editable = provider.id !== 'cherryin' const [searchText, _setSearchText] = useState('') const [displayedModelGroups, setDisplayedModelGroups] = useState(() => { @@ -113,17 +112,15 @@ const ModelList: React.FC = ({ providerId }) => { tooltip={t('models.search.tooltip')} /> - {editable && ( - - - - - - )} + + + + ) } diff --git a/src/renderer/src/pages/settings/ProviderSettings/ProviderOAuth.tsx b/src/renderer/src/pages/settings/ProviderSettings/ProviderOAuth.tsx index 5fdafa5f2..2d8b38be8 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ProviderOAuth.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ProviderOAuth.tsx @@ -1,6 +1,6 @@ import AI302ProviderLogo from '@renderer/assets/images/providers/302ai.webp' import AiHubMixProviderLogo from '@renderer/assets/images/providers/aihubmix.webp' -import AiOnlyProviderLogo from '@renderer/assets/images/providers/aiOnly.png' +import AiOnlyProviderLogo from '@renderer/assets/images/providers/aiOnly.webp' import PPIOProviderLogo from '@renderer/assets/images/providers/ppio.png' import SiliconFlowProviderLogo from '@renderer/assets/images/providers/silicon.png' import TokenFluxProviderLogo from '@renderer/assets/images/providers/tokenflux.png' diff --git a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx index 68bfc67ba..ea40f6d9a 100644 --- a/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx +++ b/src/renderer/src/pages/settings/ProviderSettings/ProviderSetting.tsx @@ -68,7 +68,7 @@ const ProviderSetting: FC = ({ providerId }) => { const isAzureOpenAI = provider.id === 'azure-openai' || provider.type === 'azure-openai' const isDmxapi = provider.id === 'dmxapi' - const hideApiInput = ['vertexai', 'aws-bedrock', 'cherryin'].includes(provider.id) + const hideApiInput = ['vertexai', 'aws-bedrock'].includes(provider.id) const providerConfig = PROVIDER_URLS[provider.id] const officialWebsite = providerConfig?.websites?.official diff --git a/src/renderer/src/services/ApiService.ts b/src/renderer/src/services/ApiService.ts index d954fc1f8..64e2c1ae3 100644 --- a/src/renderer/src/services/ApiService.ts +++ b/src/renderer/src/services/ApiService.ts @@ -337,7 +337,7 @@ export async function fetchGenerate({ export function hasApiKey(provider: Provider) { if (!provider) return false - if (['ollama', 'lmstudio', 'vertexai', 'cherryin'].includes(provider.id)) return true + if (['ollama', 'lmstudio', 'vertexai', 'cherryai'].includes(provider.id)) return true return !isEmpty(provider.apiKey) } diff --git a/src/renderer/src/services/AssistantService.ts b/src/renderer/src/services/AssistantService.ts index 8a86a9593..ea2dd3ef3 100644 --- a/src/renderer/src/services/AssistantService.ts +++ b/src/renderer/src/services/AssistantService.ts @@ -7,7 +7,9 @@ import { UNLIMITED_CONTEXT_COUNT } from '@renderer/config/constant' import { isQwenMTModel } from '@renderer/config/models' +import { CHERRYAI_PROVIDER } from '@renderer/config/providers' import { UNKNOWN } from '@renderer/config/translate' +import { getStoreProviders } from '@renderer/hooks/useStore' import i18n from '@renderer/i18n' import store from '@renderer/store' import { addAssistant } from '@renderer/store/assistants' @@ -126,26 +128,25 @@ export function getTranslateModel() { } export function getAssistantProvider(assistant: Assistant): Provider { - const providers = store.getState().llm.providers + const providers = getStoreProviders() const provider = providers.find((p) => p.id === assistant.model?.provider) return provider || getDefaultProvider() } export function getProviderByModel(model?: Model): Provider { - const providers = store.getState().llm.providers + const providers = getStoreProviders() const provider = providers.find((p) => p.id === model?.provider) if (!provider) { const defaultProvider = providers.find((p) => p.id === getDefaultModel()?.provider) - const cherryinProvider = providers.find((p) => p.id === 'cherryin') - return defaultProvider || cherryinProvider || providers[0] + return defaultProvider || CHERRYAI_PROVIDER || providers[0] } return provider } export function getProviderByModelId(modelId?: string) { - const providers = store.getState().llm.providers + const providers = getStoreProviders() const _modelId = modelId || getDefaultModel().id return providers.find((p) => p.models.find((m) => m.id === _modelId)) as Provider } diff --git a/src/renderer/src/services/ModelService.ts b/src/renderer/src/services/ModelService.ts index 52049f229..9cb1c5cc4 100644 --- a/src/renderer/src/services/ModelService.ts +++ b/src/renderer/src/services/ModelService.ts @@ -1,4 +1,4 @@ -import store from '@renderer/store' +import { getStoreProviders } from '@renderer/hooks/useStore' import { Model } from '@renderer/types' import { pick } from 'lodash' @@ -9,9 +9,8 @@ export const getModelUniqId = (m?: Model) => { } export const hasModel = (m?: Model) => { - const allModels = store - .getState() - .llm.providers.filter((p) => p.enabled) + const allModels = getStoreProviders() + .filter((p) => p.enabled) .map((p) => p.models) .flat() @@ -19,7 +18,7 @@ export const hasModel = (m?: Model) => { } export function getModelName(model?: Model) { - const provider = store.getState().llm.providers.find((p) => p.id === model?.provider) + const provider = getStoreProviders().find((p) => p.id === model?.provider) const modelName = model?.name || model?.id || '' if (provider) { diff --git a/src/renderer/src/services/ProviderService.ts b/src/renderer/src/services/ProviderService.ts index da6df82ad..e0c6b3885 100644 --- a/src/renderer/src/services/ProviderService.ts +++ b/src/renderer/src/services/ProviderService.ts @@ -1,4 +1,4 @@ -import store from '@renderer/store' +import { getStoreProviders } from '@renderer/hooks/useStore' import { Model, Provider } from '@renderer/types' import { getFancyProviderName } from '@renderer/utils' @@ -14,9 +14,9 @@ export function getProviderName(model?: Model) { export function getProviderByModel(model?: Model) { const id = model?.provider - const provider = store.getState().llm.providers.find((p) => p.id === id) + const provider = getStoreProviders().find((p) => p.id === id) - if (provider?.id === 'cherryin') { + if (provider?.id === 'cherryai') { const map = { 'glm-4.5-flash': 'zhipu', 'Qwen/Qwen3-8B': 'silicon' @@ -43,5 +43,5 @@ export function isProviderSupportCharge(provider: Provider) { } export function getProviderById(id: string) { - return store.getState().llm.providers.find((p) => p.id === id) + return getStoreProviders().find((p) => p.id === id) } diff --git a/src/renderer/src/store/index.ts b/src/renderer/src/store/index.ts index 3b3cfa3f0..ba532ecc6 100644 --- a/src/renderer/src/store/index.ts +++ b/src/renderer/src/store/index.ts @@ -67,7 +67,7 @@ const persistedReducer = persistReducer( { key: 'cherry-studio', storage, - version: 156, + version: 157, blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs'], migrate }, diff --git a/src/renderer/src/store/migrate.ts b/src/renderer/src/store/migrate.ts index f7fc92ee4..8063f7ec9 100644 --- a/src/renderer/src/store/migrate.ts +++ b/src/renderer/src/store/migrate.ts @@ -2479,7 +2479,6 @@ const migrateConfig = { }, '156': (state: RootState) => { try { - addProvider(state, 'aionly') state.llm.providers.forEach((provider) => { if (provider.id === SystemProviderIds.anthropic) { if (provider.apiHost.endsWith('/')) { @@ -2492,6 +2491,53 @@ const migrateConfig = { logger.error('migrate 156 error', error as Error) return state } + }, + '157': (state: RootState) => { + try { + addProvider(state, 'aionly') + + const cherryinProvider = state.llm.providers.find((provider) => provider.id === 'cherryin') + + if (cherryinProvider) { + updateProvider(state, 'cherryin', { apiHost: 'https://open.cherryin.ai', models: [] }) + } + + if (state.llm.defaultModel?.provider === 'cherryin') { + state.llm.defaultModel.provider = 'cherryai' + } + + if (state.llm.quickModel?.provider === 'cherryin') { + state.llm.quickModel.provider = 'cherryai' + } + + if (state.llm.translateModel?.provider === 'cherryin') { + state.llm.translateModel.provider = 'cherryai' + } + + state.assistants.assistants.forEach((assistant) => { + if (assistant.model?.provider === 'cherryin') { + assistant.model.provider = 'cherryai' + } + if (assistant.defaultModel?.provider === 'cherryin') { + assistant.defaultModel.provider = 'cherryai' + } + }) + + state.agents.agents.forEach((agent) => { + // @ts-ignore model is not defined in Agent + if (agent.model?.provider === 'cherryin') { + // @ts-ignore model is not defined in Agent + agent.model.provider = 'cherryai' + } + if (agent.defaultModel?.provider === 'cherryin') { + agent.defaultModel.provider = 'cherryai' + } + }) + return state + } catch (error) { + logger.error('migrate 157 error', error as Error) + return state + } } } diff --git a/src/renderer/src/utils/model.ts b/src/renderer/src/utils/model.ts index 9795b72d2..e918c8457 100644 --- a/src/renderer/src/utils/model.ts +++ b/src/renderer/src/utils/model.ts @@ -64,7 +64,7 @@ export const getModelTags = (models: Model[]): Record => { } export function isFreeModel(model: Model) { - if (model.provider === 'cherryin') { + if (model.provider === 'cherryai') { return true } diff --git a/tsconfig.web.json b/tsconfig.web.json index 07c1b4106..120419225 100644 --- a/tsconfig.web.json +++ b/tsconfig.web.json @@ -8,7 +8,7 @@ "tests/__mocks__/**/*", "packages/mcp-trace/**/*", "packages/aiCore/src/**/*", - "src/main/integration/cherryin/index.js", + "src/main/integration/cherryai/index.js", "packages/extension-table-plus/**/*" ], "compilerOptions": {