Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ipcBridge } from '@/common';
import { uuid } from '@/common/utils';
import { isGoogleApisHost } from '@/common/utils/urlValidation';
import ModalHOC from '@/renderer/utils/ui/ModalHOC';
import { Form, Input, Message, Select, Switch } from '@arco-design/web-react';
import { Form, Input, Link, Message, Select, Switch } from '@arco-design/web-react';
import { Loading, PreviewOpen, Refresh, Search } from '@icon-park/react';
import React, { useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
Expand All @@ -30,6 +30,7 @@ import {
} from '@/renderer/utils/model/modelPlatforms';
import { ProviderLogo } from '@/renderer/components/agent/ThemedLogo';
import type { DeepLinkAddProviderDetail } from '@/renderer/hooks/system/useDeepLink';
import { openExternalUrl } from '@/renderer/utils/platform';

/**
* Protocol icon configurations
Expand Down Expand Up @@ -521,7 +522,20 @@ const AddPlatformModal = ModalHOC<{
{/* API Key */}
<Form.Item
hidden={isBedrock}
label={t('settings.apiKey')}
label={
<span className='inline-flex items-center gap-6px'>
{t('settings.apiKey')}
{selectedPlatform?.apiKeyUrl && (
<Link
onClick={() => {
void openExternalUrl(selectedPlatform.apiKeyUrl!);
}}
>
{t('settings.getApiKey')}
</Link>
)}
</span>
}
required={!isBedrock}
rules={[{ required: !isBedrock }]}
field={'api_key'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2037,6 +2037,7 @@ export type I18nKey =
| 'settings.gemini'
| 'settings.geminiApiKey'
| 'settings.geminiBaseUrl'
| 'settings.getApiKey'
| 'settings.goToModelSettings'
| 'settings.googleCloudProjectPlaceholder'
| 'settings.googleLogin'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,7 @@
"baseUrlHint": "System fügt automatisch Anfragepfad an",
"modelNotFoundAfterUrlChange": "Modell {{model}} wurde unter der neuen Adresse nicht gefunden; wähle es erneut aus, wenn es nicht funktioniert.",
"apiKey": "API-Schlüssel",
"getApiKey": "API-Schlüssel abrufen",
"deleteModelConfirm": "Bist du sicher, dass du dieses Modell löschen möchtest?",
"deleteAllModelConfirm": "Bist du sicher, dass du alle Modelle löschen möchtest?",
"modelCount": "Modell",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@
"baseUrlHint": "System will auto-append request path",
"modelNotFoundAfterUrlChange": "Model {{model}} was not found at the new address; reselect it if it doesn't work.",
"apiKey": "API Key",
"getApiKey": "Get API Key",
"deleteModelConfirm": "Are you sure you want to delete this model?",
"deleteAllModelConfirm": "Are you sure you want to delete all models?",
"modelCount": "Model",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@
"baseUrlHint": "El sistema agregará automáticamente la ruta de solicitud",
"modelNotFoundAfterUrlChange": "El modelo {{model}} no se encontró en la nueva dirección; vuelve a seleccionarlo si no funciona.",
"apiKey": "Clave API",
"getApiKey": "Obtener clave API",
"deleteModelConfirm": "¿Estás seguro de que deseas eliminar este modelo?",
"deleteAllModelConfirm": "¿Estás seguro de que deseas eliminar todos los modelos?",
"modelCount": "Modelo",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@
"baseUrlHint": "سیستم به‌صورت خودکار مسیر درخواست را اضافه می‌کند",
"modelNotFoundAfterUrlChange": "مدل {{model}} در آدرس جدید پیدا نشد؛ اگر کار نکرد، دوباره آن را انتخاب کنید.",
"apiKey": "کلید API",
"getApiKey": "دریافت کلید API",
"deleteModelConfirm": "آیا مطمئن هستید که می‌خواهید این مدل را حذف کنید؟",
"deleteAllModelConfirm": "آیا مطمئن هستید که می‌خواهید همه مدل‌ها را حذف کنید؟",
"modelCount": "مدل",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@
"baseUrlHint": "Le système ajoutera automatiquement le chemin de la demande",
"modelNotFoundAfterUrlChange": "Le modèle {{model}} n'a pas été trouvé à la nouvelle adresse ; resélectionnez-le si cela ne fonctionne pas.",
"apiKey": "Clé API",
"getApiKey": "Obtenir une clé API",
"deleteModelConfirm": "Êtes-vous sûr de vouloir supprimer ce modèle ?",
"deleteAllModelConfirm": "Êtes-vous sûr de vouloir supprimer tous les modèles ?",
"modelCount": "Modèle",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@
"baseUrlHint": "システムが自動的にリクエストパスを追加します",
"modelNotFoundAfterUrlChange": "モデル {{model}} は新しいアドレスで見つかりませんでした。利用できない場合は選択し直してください。",
"apiKey": "API キー",
"getApiKey": "API キーを取得",
"deleteModelConfirm": "このモデルを削除してもよろしいですか?",
"deleteAllModelConfirm": "すべてのモデルを削除してもよろしいですか?",
"modelCount": "モデル",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@
"baseUrlHint": "시스템이 자동으로 요청 경로를 추가합니다",
"modelNotFoundAfterUrlChange": "모델 {{model}}을(를) 새 주소에서 찾을 수 없습니다. 사용할 수 없는 경우 다시 선택하세요.",
"apiKey": "API 키",
"getApiKey": "API 키 받기",
"deleteModelConfirm": "이 모델을 삭제하시겠습니까?",
"deleteAllModelConfirm": "모든 모델을 삭제하시겠습니까?",
"modelCount": "모델",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@
"baseUrlHint": "O sistema adicionará automaticamente o caminho da requisição",
"modelNotFoundAfterUrlChange": "O modelo {{model}} não foi encontrado no novo endereço; selecione-o novamente se não funcionar.",
"apiKey": "Chave de API",
"getApiKey": "Obter chave de API",
"deleteModelConfirm": "Tem certeza de que deseja excluir este modelo?",
"deleteAllModelConfirm": "Tem certeza de que deseja excluir todos os modelos?",
"modelCount": "Modelo",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@
"baseUrlHint": "Система автоматически добавит путь запроса",
"modelNotFoundAfterUrlChange": "Модель {{model}} не найдена по новому адресу; выберите её заново, если не работает.",
"apiKey": "API-ключ",
"getApiKey": "Получить API-ключ",
"deleteModelConfirm": "Вы уверены, что хотите удалить эту модель?",
"deleteAllModelConfirm": "Вы уверены, что хотите удалить все модели?",
"modelCount": "Модель",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@
"baseUrlHint": "Sistem otomatik olarak istek yolunu ekleyecektir",
"modelNotFoundAfterUrlChange": "{{model}} modeli yeni adreste bulunamadı; çalışmazsa yeniden seçin.",
"apiKey": "API Anahtarı",
"getApiKey": "API Anahtarı Al",
"deleteModelConfirm": "Bu modeli silmek istediğinizden emin misiniz?",
"deleteAllModelConfirm": "Tüm modelleri silmek istediğinizden emin misiniz?",
"modelCount": "Model",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,7 @@
"baseUrlHint": "Система автоматично додасть шлях запиту",
"modelNotFoundAfterUrlChange": "Модель {{model}} не знайдено за новою адресою; виберіть її знову, якщо не працює.",
"apiKey": "API-ключ",
"getApiKey": "Отримати API-ключ",
"deleteModelConfirm": "Ви впевнені, що хочете видалити цю модель?",
"deleteAllModelConfirm": "Ви впевнені, що хочете видалити всі моделі?",
"modelCount": "Модель",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@
"baseUrlHint": "系统会自动拼接请求路径",
"modelNotFoundAfterUrlChange": "模型 {{model}} 在新地址未找到,如不可用请重新选择。",
"apiKey": "API Key",
"getApiKey": "获取 API Key",
"deleteModelConfirm": "是否删除该模型?",
"deleteAllModelConfirm": "是否删除所有模型?",
"modelCount": "模型",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,7 @@
"baseUrlHint": "系統會自動拼接請求路徑",
"modelNotFoundAfterUrlChange": "模型 {{model}} 在新位址未找到,如不可用請重新選擇。",
"apiKey": "API Key",
"getApiKey": "取得 API Key",
"deleteModelConfirm": "是否刪除該模型?",
"deleteAllModelConfirm": "是否刪除所有模型?",
"modelCount": "模型",
Expand Down
10 changes: 10 additions & 0 deletions packages/desktop/src/renderer/utils/model/modelPlatforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export interface PlatformConfig {
base_url?: string;
/** 国际化 key(可选,用于需要翻译的平台名称) / i18n key (optional, for platform names that need translation) */
i18nKey?: string;
/** API Key 注册地址(可选) / API Key registration URL (optional) */
apiKeyUrl?: string;
}

/**
Expand Down Expand Up @@ -177,6 +179,14 @@ export const MODEL_PLATFORMS: PlatformConfig[] = [
platform: 'custom',
base_url: 'https://api.siliconflow.com/v1',
},
{
name: 'SSYCloud (胜算云)',
value: 'SSYCloud',
logo: buildLogoAssetUrl('ai-cloud/ssycloud.svg'),
platform: 'custom',
base_url: 'https://router.shengsuanyun.com/api/v1',
apiKeyUrl: 'https://www.shengsuanyun.com/?from=CH_VVVBZY1S',
},
{
name: 'Zhipu',
value: 'Zhipu',
Expand Down
19 changes: 19 additions & 0 deletions tests/unit/renderer/modelPlatforms.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,23 @@ describe('MODEL_PLATFORMS ordering', () => {
'https://api.moonshot.ai/v1',
]);
});

it('defines SSYCloud as an OpenAI-compatible preset with an API key registration link', () => {
const ssyCloudEntries = MODEL_PLATFORMS.filter((p) => p.value === 'SSYCloud');

expect(ssyCloudEntries).toHaveLength(1);
expect(ssyCloudEntries[0]).toMatchObject({
name: 'SSYCloud (胜算云)',
logo: '/api/assets/logos/ai-cloud/ssycloud.svg',
platform: 'custom',
base_url: 'https://router.shengsuanyun.com/api/v1',
apiKeyUrl: 'https://www.shengsuanyun.com/?from=CH_VVVBZY1S',
});
});

it('does not show the SSYCloud API key link for unrelated providers', () => {
const openAI = MODEL_PLATFORMS.find((p) => p.value === 'OpenAI');

expect(openAI?.apiKeyUrl).toBeUndefined();
});
});