Compare commits

...

17 Commits

Author SHA1 Message Date
GeorgeDong32
038da0de63 refactor(proxy): fix lint 2025-11-04 00:04:37 +08:00
GitHub Action
8924f954dd fix(i18n): Auto update translations for PR #11026 2025-11-03 14:57:42 +00:00
GeorgeDong32
06b108c40d feat(i18n): add bypassProxy and Bedrock auth strings 2025-11-03 22:54:58 +08:00
copilot-swe-agent[bot]
b1e63470a5 ♻️ refactor: Use ProxyManager bypass rules instead of custom dispatcher for S3
Co-authored-by: GeorgeDong32 <98630204+GeorgeDong32@users.noreply.github.com>
2025-11-03 22:36:25 +08:00
copilot-swe-agent[bot]
ea9e7fccda feat: Add configurable proxy bypass setting for S3 backups
Co-authored-by: GeorgeDong32 <98630204+GeorgeDong32@users.noreply.github.com>
2025-11-03 22:36:25 +08:00
copilot-swe-agent[bot]
97ab200657 📝 docs: Add detailed comments explaining S3 proxy bypass fix
Co-authored-by: GeorgeDong32 <98630204+GeorgeDong32@users.noreply.github.com>
2025-11-03 22:36:25 +08:00
copilot-swe-agent[bot]
7a49042d7f 🐛 fix: Configure S3 client to bypass proxy for reliable uploads
Co-authored-by: GeorgeDong32 <98630204+GeorgeDong32@users.noreply.github.com>
2025-11-03 22:36:25 +08:00
copilot-swe-agent[bot]
33983c26f4 Initial plan 2025-11-03 22:36:25 +08:00
Zephyr
abd5d3b96f feat: amazon bedrock request use bedrock api key (#10727)
* feat: amazon bedrock request use bedrock api key

* feat: ai-core/provider support bedrock api key

* refactor: extract AWS Bedrock auth type and remove redundant state

* feat: add bedrock reasoning support

Add AWS Bedrock-specific reasoning parameter handling to support Extended Thinking feature for Claude models via Bedrock API.

Changes:
- Add `buildBedrockProviderOptions` function in options.ts to handle Bedrock-specific provider options
- Add `getBedrockReasoningParams` function in reasoning.ts to generate reasoning config with budget tokens
- Register 'bedrock' case in provider options switch to route to Bedrock-specific builder
- Reuse `getAnthropicThinkingBudget` helper for consistent token budget calculation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* feat: add migration for Bedrock auth type and API key fields

* refactor: replace any type with BedrockRuntimeClientConfig in AWS Bedrock client

* fix: bug fix

* fix: lint error

* fix: bedrock reasoning

* chore: bump persisted reducer version to 171

* Update src/renderer/src/store/migrate.ts

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: icarus <eurfelux@gmail.com>
2025-11-03 21:05:10 +08:00
Phantom
49bd298d37 feat(InputbarTools): add reasoning effort button to quick panel (#10959)
Add new menu item with lightbulb icon that opens the reasoning effort quick panel when clicked
2025-11-03 20:36:52 +08:00
Phantom
714a28ac29 fix(QuickPanel): Hide the options that should be hidden in the quick panel. (#10931)
* feat(QuickPanel): add hidden property to list items

Add support for hiding QuickPanel items by introducing a hidden property. This allows conditional visibility of items like the knowledge base button based on application state.

* docs(types): clarify settings field comment in Assistant type
2025-11-03 20:34:24 +08:00
beyondkmp
0cf81c04c8 chore: update electron-builder.yml to exclude additional configuration files from build (#11129)
* chore: update electron-builder.yml to exclude additional configuration files from build

* delete all hide files
2025-11-03 17:54:29 +08:00
kangfenmao
4186e9c990 feat: add support for TopP in model capabilities and update parameter builder to utilize it 2025-11-03 16:37:12 +08:00
kangfenmao
d8f68a6056 feat: initialize painting model with first available option and update default provider to 'cherryin' 2025-11-03 15:12:58 +08:00
kangfenmao
11bf50e722 fix(i18n): improve label retrieval for paintings image size options 2025-11-03 14:45:21 +08:00
kangfenmao
32a84311aa feat: add SophNet LLM provider 2025-11-03 13:28:40 +08:00
beyondkmp
6eaa2b2461 refactor: remove main window dependency from PythonService and utilize WindowService for window management (#11116)
* refactor: remove main window dependency from PythonService and utilize WindowService for window management

* format code
2025-11-03 13:09:40 +08:00
44 changed files with 1453 additions and 2065 deletions

View File

@@ -21,6 +21,8 @@ files:
- "**/*"
- "!**/{.vscode,.yarn,.yarn-lock,.github,.cursorrules,.prettierrc}"
- "!electron.vite.config.{js,ts,mjs,cjs}}"
- "!.*"
- "!components.json"
- "!**/{.eslintignore,.eslintrc.js,.eslintrc.json,.eslintcache,root.eslint.config.js,eslint.config.js,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,eslint.config.mjs,dev-app-update.yml,CHANGELOG.md,README.md,biome.jsonc}"
- "!**/{.env,.env.*,.npmrc,pnpm-lock.yaml}"
- "!**/{tsconfig.json,tsconfig.tsbuildinfo,tsconfig.node.json,tsconfig.web.json}"

View File

@@ -113,9 +113,9 @@
"@ant-design/v5-patch-for-react-19": "^1.0.3",
"@anthropic-ai/sdk": "^0.41.0",
"@anthropic-ai/vertex-sdk": "patch:@anthropic-ai/vertex-sdk@npm%3A0.11.4#~/.yarn/patches/@anthropic-ai-vertex-sdk-npm-0.11.4-c19cb41edb.patch",
"@aws-sdk/client-bedrock": "^3.840.0",
"@aws-sdk/client-bedrock-runtime": "^3.840.0",
"@aws-sdk/client-s3": "^3.840.0",
"@aws-sdk/client-bedrock": "^3.910.0",
"@aws-sdk/client-bedrock-runtime": "^3.910.0",
"@aws-sdk/client-s3": "^3.910.0",
"@biomejs/biome": "2.2.4",
"@cherrystudio/ai-core": "workspace:^1.0.0-alpha.18",
"@cherrystudio/embedjs": "^0.1.31",

View File

@@ -115,9 +115,6 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) {
const appUpdater = new AppUpdater()
const notificationService = new NotificationService()
// Initialize Python service with main window
pythonService.setMainWindow(mainWindow)
const checkMainWindow = () => {
if (!mainWindow || mainWindow.isDestroyed()) {
throw new Error('Main window does not exist or has been destroyed')

View File

@@ -33,6 +33,7 @@ class BackupManager {
accessKeyId: string
secretAccessKey: string
root?: string
bypassProxy?: boolean
} | null = null
private cachedWebdavConnectionConfig: {
@@ -120,7 +121,8 @@ class BackupManager {
cachedConfig.bucket === config.bucket &&
cachedConfig.accessKeyId === config.accessKeyId &&
cachedConfig.secretAccessKey === config.secretAccessKey &&
cachedConfig.root === config.root
cachedConfig.root === config.root &&
cachedConfig.bypassProxy === config.bypassProxy
)
}
@@ -147,6 +149,11 @@ class BackupManager {
const configChanged = !this.isS3ConfigEqual(this.cachedS3ConnectionConfig, config)
if (configChanged || !this.s3Storage) {
// Destroy old instance to clean up bypass rules
if (this.s3Storage) {
this.s3Storage.destroy()
}
this.s3Storage = new S3Storage(config)
// 只缓存连接相关的配置字段
this.cachedS3ConnectionConfig = {
@@ -155,7 +162,8 @@ class BackupManager {
bucket: config.bucket,
accessKeyId: config.accessKeyId,
secretAccessKey: config.secretAccessKey,
root: config.root
root: config.root,
bypassProxy: config.bypassProxy
}
logger.debug('[BackupManager] Created new S3Storage instance')
} else {

View File

@@ -12,6 +12,8 @@ import { Dispatcher, EnvHttpProxyAgent, getGlobalDispatcher, setGlobalDispatcher
const logger = loggerService.withContext('ProxyManager')
let byPassRules: string[] = []
// Dynamic bypass rules that can be added/removed at runtime (e.g., for S3 endpoints)
const dynamicBypassRules: string[] = []
type HostnameMatchType = 'exact' | 'wildcardSubdomain' | 'generalWildcard'
@@ -222,7 +224,10 @@ export const updateByPassRules = (rules: string[]): void => {
byPassRules = rules
parsedByPassRules = []
for (const rule of rules) {
// Combine static bypass rules with dynamic ones
const allRules = [...rules, ...dynamicBypassRules]
for (const rule of allRules) {
const parsedRule = parseProxyBypassRule(rule)
if (parsedRule) {
parsedByPassRules.push(parsedRule)
@@ -232,6 +237,33 @@ export const updateByPassRules = (rules: string[]): void => {
}
}
/**
* Add a dynamic bypass rule at runtime (e.g., for S3 endpoints)
* @param rule - The bypass rule to add (e.g., hostname or domain pattern)
*/
export const addDynamicBypassRule = (rule: string): void => {
if (!dynamicBypassRules.includes(rule)) {
dynamicBypassRules.push(rule)
// Re-parse all rules with the new dynamic rule
updateByPassRules(byPassRules)
logger.info(`Added dynamic bypass rule: ${rule}`)
}
}
/**
* Remove a dynamic bypass rule
* @param rule - The bypass rule to remove
*/
export const removeDynamicBypassRule = (rule: string): void => {
const index = dynamicBypassRules.indexOf(rule)
if (index !== -1) {
dynamicBypassRules.splice(index, 1)
// Re-parse all rules without the removed dynamic rule
updateByPassRules(byPassRules)
logger.info(`Removed dynamic bypass rule: ${rule}`)
}
}
export const isByPass = (url: string) => {
if (parsedByPassRules.length === 0) {
return false
@@ -586,6 +618,22 @@ export class ProxyManager {
// set proxy for electron
app.setProxy(config)
}
/**
* Add a dynamic bypass rule for a specific endpoint
* @param rule - The bypass rule to add (e.g., hostname or domain pattern)
*/
addDynamicBypassRule(rule: string): void {
addDynamicBypassRule(rule)
}
/**
* Remove a dynamic bypass rule
* @param rule - The bypass rule to remove
*/
removeDynamicBypassRule(rule: string): void {
removeDynamicBypassRule(rule)
}
}
export const proxyManager = new ProxyManager()

View File

@@ -1,8 +1,9 @@
import { randomUUID } from 'node:crypto'
import type { BrowserWindow } from 'electron'
import { ipcMain } from 'electron'
import { windowService } from './WindowService'
interface PythonExecutionRequest {
id: string
script: string
@@ -21,7 +22,6 @@ interface PythonExecutionResponse {
*/
export class PythonService {
private static instance: PythonService | null = null
private mainWindow: BrowserWindow | null = null
private pendingRequests = new Map<string, { resolve: (value: string) => void; reject: (error: Error) => void }>()
private constructor() {
@@ -51,10 +51,6 @@ export class PythonService {
})
}
public setMainWindow(mainWindow: BrowserWindow) {
this.mainWindow = mainWindow
}
/**
* Execute Python code by sending request to renderer PyodideService
*/
@@ -63,8 +59,8 @@ export class PythonService {
context: Record<string, any> = {},
timeout: number = 60000
): Promise<string> {
if (!this.mainWindow) {
throw new Error('Main window not set in PythonService')
if (!windowService.getMainWindow()) {
throw new Error('Main window not found')
}
return new Promise((resolve, reject) => {
@@ -95,7 +91,7 @@ export class PythonService {
// Send request to renderer
const request: PythonExecutionRequest = { id: requestId, script, context, timeout }
this.mainWindow?.webContents.send('python-execution-request', request)
windowService.getMainWindow()?.webContents.send('python-execution-request', request)
})
}
}

View File

@@ -11,6 +11,8 @@ import type { S3Config } from '@types'
import * as net from 'net'
import { Readable } from 'stream'
import { proxyManager } from './ProxyManager'
const logger = loggerService.withContext('S3Storage')
/**
@@ -35,9 +37,12 @@ export default class S3Storage {
private client: S3Client
private bucket: string
private root: string
private endpoint: string
constructor(config: S3Config) {
const { endpoint, region, accessKeyId, secretAccessKey, bucket, root } = config
const { endpoint, region, accessKeyId, secretAccessKey, bucket, root, bypassProxy = true } = config
this.endpoint = endpoint
const usePathStyle = (() => {
if (!endpoint) return false
@@ -57,6 +62,22 @@ export default class S3Storage {
}
})()
// Use ProxyManager's dynamic bypass rules instead of custom dispatcher
// When bypassProxy is true (default), add the S3 endpoint to proxy bypass rules
// to avoid proxy interference with large file uploads that can cause incomplete transfers
// Error example: "Io error: put_object write size < data.size(), w_size=15728640, data.size=16396159"
if (bypassProxy && endpoint) {
try {
const url = new URL(endpoint)
const hostname = url.hostname
// Add the hostname to dynamic bypass rules
proxyManager.addDynamicBypassRule(hostname)
logger.debug(`[S3Storage] Added S3 endpoint to bypass rules: ${hostname}`)
} catch (e) {
logger.warn(`[S3Storage] Failed to add endpoint to bypass rules: ${endpoint}`, e as Error)
}
}
this.client = new S3Client({
region,
endpoint: endpoint || undefined,
@@ -77,6 +98,22 @@ export default class S3Storage {
this.checkConnection = this.checkConnection.bind(this)
}
/**
* Clean up resources and remove bypass rules
*/
destroy(): void {
if (this.endpoint) {
try {
const url = new URL(this.endpoint)
const hostname = url.hostname
proxyManager.removeDynamicBypassRule(hostname)
logger.debug(`[S3Storage] Removed S3 endpoint from bypass rules: ${hostname}`)
} catch (e) {
logger.warn(`[S3Storage] Failed to remove endpoint from bypass rules: ${this.endpoint}`, e as Error)
}
}
}
/**
* 内部辅助方法,用来拼接带 root 的对象 key
*/

View File

@@ -1,6 +1,7 @@
import { BedrockClient, ListFoundationModelsCommand, ListInferenceProfilesCommand } from '@aws-sdk/client-bedrock'
import {
BedrockRuntimeClient,
type BedrockRuntimeClientConfig,
ConverseCommand,
InvokeModelCommand,
InvokeModelWithResponseStreamCommand
@@ -11,6 +12,8 @@ import { DEFAULT_MAX_TOKENS } from '@renderer/config/constant'
import { findTokenLimit, isReasoningModel } from '@renderer/config/models'
import {
getAwsBedrockAccessKeyId,
getAwsBedrockApiKey,
getAwsBedrockAuthType,
getAwsBedrockRegion,
getAwsBedrockSecretAccessKey
} from '@renderer/hooks/useAwsBedrock'
@@ -75,32 +78,48 @@ export class AwsBedrockAPIClient extends BaseApiClient<
}
const region = getAwsBedrockRegion()
const accessKeyId = getAwsBedrockAccessKeyId()
const secretAccessKey = getAwsBedrockSecretAccessKey()
const authType = getAwsBedrockAuthType()
if (!region) {
throw new Error('AWS region is required. Please configure AWS-Region in extra headers.')
throw new Error('AWS region is required. Please configure AWS region in settings.')
}
if (!accessKeyId || !secretAccessKey) {
throw new Error('AWS credentials are required. Please configure AWS-Access-Key-ID and AWS-Secret-Access-Key.')
// Build client configuration based on auth type
let clientConfig: BedrockRuntimeClientConfig
if (authType === 'iam') {
// IAM credentials authentication
const accessKeyId = getAwsBedrockAccessKeyId()
const secretAccessKey = getAwsBedrockSecretAccessKey()
if (!accessKeyId || !secretAccessKey) {
throw new Error('AWS credentials are required. Please configure Access Key ID and Secret Access Key.')
}
clientConfig = {
region,
credentials: {
accessKeyId,
secretAccessKey
}
}
} else {
// API Key authentication
const awsBedrockApiKey = getAwsBedrockApiKey()
if (!awsBedrockApiKey) {
throw new Error('AWS Bedrock API Key is required. Please configure API Key in settings.')
}
clientConfig = {
region,
token: { token: awsBedrockApiKey },
authSchemePreference: ['httpBearerAuth']
}
}
const client = new BedrockRuntimeClient({
region,
credentials: {
accessKeyId,
secretAccessKey
}
})
const bedrockClient = new BedrockClient({
region,
credentials: {
accessKeyId,
secretAccessKey
}
})
const client = new BedrockRuntimeClient(clientConfig)
const bedrockClient = new BedrockClient(clientConfig)
this.sdkInstance = { client, bedrockClient, region }
return this.sdkInstance

View File

@@ -85,6 +85,19 @@ export function supportsLargeFileUpload(model: Model): boolean {
})
}
/**
* 检查模型是否支持TopP
*/
export function supportsTopP(model: Model): boolean {
const provider = getProviderByModel(model)
if (provider?.type === 'anthropic' || model?.endpoint_type === 'anthropic') {
return false
}
return true
}
/**
* 获取提供商特定的文件大小限制
*/

View File

@@ -34,6 +34,7 @@ import { setupToolsConfig } from '../utils/mcp'
import { buildProviderOptions } from '../utils/options'
import { getAnthropicThinkingBudget } from '../utils/reasoning'
import { buildProviderBuiltinWebSearchConfig } from '../utils/websearch'
import { supportsTopP } from './modelCapabilities'
import { getTemperature, getTopP } from './modelParameters'
const logger = loggerService.withContext('parameterBuilder')
@@ -176,20 +177,27 @@ export async function buildStreamTextParams(
messages: sdkMessages,
maxOutputTokens: maxTokens,
temperature: getTemperature(assistant, model),
topP: getTopP(assistant, model),
abortSignal: options.requestOptions?.signal,
headers: options.requestOptions?.headers,
providerOptions,
stopWhen: stepCountIs(20),
maxRetries: 0
}
if (supportsTopP(model)) {
params.topP = getTopP(assistant, model)
}
if (tools) {
params.tools = tools
}
if (assistant.prompt) {
params.system = await replacePromptVariables(assistant.prompt, model.name)
}
logger.debug('params', params)
return {
params,
modelId: model.id,

View File

@@ -14,6 +14,8 @@ import {
} from '@renderer/config/providers'
import {
getAwsBedrockAccessKeyId,
getAwsBedrockApiKey,
getAwsBedrockAuthType,
getAwsBedrockRegion,
getAwsBedrockSecretAccessKey
} from '@renderer/hooks/useAwsBedrock'
@@ -192,9 +194,15 @@ export function providerToAiSdkConfig(
// bedrock
if (aiSdkProviderId === 'bedrock') {
const authType = getAwsBedrockAuthType()
extraOptions.region = getAwsBedrockRegion()
extraOptions.accessKeyId = getAwsBedrockAccessKeyId()
extraOptions.secretAccessKey = getAwsBedrockSecretAccessKey()
if (authType === 'apiKey') {
extraOptions.apiKey = getAwsBedrockApiKey()
} else {
extraOptions.accessKeyId = getAwsBedrockAccessKeyId()
extraOptions.secretAccessKey = getAwsBedrockSecretAccessKey()
}
}
// google-vertex
if (aiSdkProviderId === 'google-vertex' || aiSdkProviderId === 'google-vertex-anthropic') {

View File

@@ -17,6 +17,7 @@ import { getAiSdkProviderId } from '../provider/factory'
import { buildGeminiGenerateImageParams } from './image'
import {
getAnthropicReasoningParams,
getBedrockReasoningParams,
getCustomParameters,
getGeminiReasoningParams,
getOpenAIReasoningParams,
@@ -127,6 +128,9 @@ export function buildProviderOptions(
case 'google-vertex-anthropic':
providerSpecificOptions = buildAnthropicProviderOptions(assistant, model, capabilities)
break
case 'bedrock':
providerSpecificOptions = buildBedrockProviderOptions(assistant, model, capabilities)
break
default:
// 对于其他 provider使用通用的构建逻辑
providerSpecificOptions = {
@@ -266,6 +270,32 @@ function buildXAIProviderOptions(
return providerOptions
}
/**
* Build Bedrock providerOptions
*/
function buildBedrockProviderOptions(
assistant: Assistant,
model: Model,
capabilities: {
enableReasoning: boolean
enableWebSearch: boolean
enableGenerateImage: boolean
}
): Record<string, any> {
const { enableReasoning } = capabilities
let providerOptions: Record<string, any> = {}
if (enableReasoning) {
const reasoningParams = getBedrockReasoningParams(assistant, model)
providerOptions = {
...providerOptions,
...reasoningParams
}
}
return providerOptions
}
/**
* 构建通用的 providerOptions用于其他 provider
*/

View File

@@ -485,6 +485,34 @@ export function getXAIReasoningParams(assistant: Assistant, model: Model): Recor
}
}
/**
* Get Bedrock reasoning parameters
*/
export function getBedrockReasoningParams(assistant: Assistant, model: Model): Record<string, any> {
if (!isReasoningModel(model)) {
return {}
}
const reasoningEffort = assistant?.settings?.reasoning_effort
if (reasoningEffort === undefined) {
return {}
}
// Only apply thinking budget for Claude reasoning models
if (!isSupportedThinkingTokenClaudeModel(model)) {
return {}
}
const budgetTokens = getAnthropicThinkingBudget(assistant, model)
return {
reasoningConfig: {
type: 'enabled',
budgetTokens: budgetTokens
}
}
}
/**
* 获取自定义参数
* 从 assistant 设置中提取自定义参数

View File

@@ -0,0 +1,13 @@
<svg xmlns="http://www.w3.org/2000/svg" width="33" height="38" viewBox="0 0 33 38" fill="none">
<g clip-path="url(#clip0_4321_9943)">
<path d="M1.51221 6.59813C1.51221 4.09263 3.54331 2.06152 6.04881 2.06152H27.9757C30.4812 2.06152 32.5123 4.09263 32.5123 6.59813C32.5123 9.10362 30.4812 11.1347 27.9757 11.1347H6.04881C3.54331 11.1347 1.51221 9.10362 1.51221 6.59813Z" fill="#6200EE"/>
<path d="M3.38905 3.56467C5.26076 1.89906 8.12831 2.06615 9.79391 3.93785L22.1493 17.8221C23.8149 19.6938 23.6478 22.5614 21.7761 24.227C19.9044 25.8926 17.0369 25.7255 15.3713 23.8538L3.01586 9.96953C1.35026 8.09782 1.51734 5.23027 3.38905 3.56467Z" fill="#6200EE"/>
<path d="M1.51221 20.9643C1.51221 18.4588 3.54331 16.4277 6.04881 16.4277H18.9025C21.408 16.4277 23.4391 18.4588 23.4391 20.9643C23.4391 23.4698 21.408 25.5009 18.9025 25.5009H6.04881C3.54331 25.5009 1.51221 23.4698 1.51221 20.9643Z" fill="#6200EE"/>
<path d="M10.5854 32.3052C10.5854 34.8107 8.55431 36.8418 6.04881 36.8418C3.54331 36.8418 1.51221 34.8107 1.51221 32.3052C1.51221 29.7997 3.54331 27.7686 6.04881 27.7686C8.55431 27.7686 10.5854 29.7997 10.5854 32.3052Z" fill="#BF7AFF"/>
</g>
<defs>
<clipPath id="clip0_4321_9943">
<rect width="32.5124" height="36.9029" fill="white" transform="translate(0 0.548828)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -64,6 +64,7 @@ export type QuickPanelListItem = {
isSelected?: boolean
isMenu?: boolean
disabled?: boolean
hidden?: boolean
/**
* 固定显示项:不参与过滤,始终出现在列表顶部。
* 例如“清除”按钮可设置为 alwaysVisible从而在有匹配项时始终可见

View File

@@ -143,7 +143,8 @@ export const QuickPanelView: React.FC<Props> = ({ setInputText }) => {
prevSymbolRef.current = ctx.symbol
// 固定项置顶 + 过滤后的普通项
return [...pinnedItems, ...filteredNormalItems]
const pinnedFiltered = [...pinnedItems, ...filteredNormalItems]
return pinnedFiltered.filter((item) => !item.hidden)
}, [ctx.isVisible, ctx.symbol, ctx.list, searchText])
const canForwardAndBackward = useMemo(() => {

View File

@@ -27,6 +27,7 @@ export const SYSTEM_MODELS: Record<SystemProviderId | 'defaultModel', Model[]> =
],
cherryin: [],
vertexai: [],
sophnet: [],
'302ai': [
{
id: 'deepseek-chat',

View File

@@ -46,6 +46,7 @@ import Ph8ProviderLogo from '@renderer/assets/images/providers/ph8.png'
import PPIOProviderLogo from '@renderer/assets/images/providers/ppio.png'
import QiniuProviderLogo from '@renderer/assets/images/providers/qiniu.webp'
import SiliconFlowProviderLogo from '@renderer/assets/images/providers/silicon.png'
import SophnetProviderLogo from '@renderer/assets/images/providers/sophnet.svg'
import StepProviderLogo from '@renderer/assets/images/providers/step.png'
import TencentCloudProviderLogo from '@renderer/assets/images/providers/tencent-cloud-ti.png'
import TogetherProviderLogo from '@renderer/assets/images/providers/together.png'
@@ -246,6 +247,16 @@ export const SYSTEM_PROVIDERS_CONFIG: Record<SystemProviderId, SystemProvider> =
isSystem: true,
enabled: false
},
sophnet: {
id: 'sophnet',
name: 'SophNet',
type: 'openai',
apiKey: '',
apiHost: 'https://www.sophnet.com/api/open-apis/v1',
models: [],
isSystem: true,
enabled: false
},
ppio: {
id: 'ppio',
name: 'PPIO',
@@ -729,7 +740,8 @@ export const PROVIDER_LOGO_MAP: AtLeast<SystemProviderId, string> = {
poe: 'poe', // use svg icon component
aionly: AiOnlyProviderLogo,
longcat: LongCatProviderLogo,
huggingface: HuggingfaceProviderLogo
huggingface: HuggingfaceProviderLogo,
sophnet: SophnetProviderLogo
} as const
export function getProviderLogo(providerId: string) {
@@ -808,6 +820,17 @@ export const PROVIDER_URLS: Record<SystemProviderId, ProviderUrls> = {
models: 'https://ai.burncloud.com/pricing'
}
},
sophnet: {
api: {
url: 'https://www.sophnet.com/api/open-apis/v1'
},
websites: {
official: 'https://sophnet.com',
apiKey: 'https://sophnet.com/#/project/key',
docs: 'https://sophnet.com/docs/component/introduce.html',
models: 'https://sophnet.com/#/model/list'
}
},
ppio: {
api: {
url: 'https://api.ppinfra.com/v3/openai'

View File

@@ -1,5 +1,12 @@
import store, { useAppSelector } from '@renderer/store'
import { setAwsBedrockAccessKeyId, setAwsBedrockRegion, setAwsBedrockSecretAccessKey } from '@renderer/store/llm'
import {
setAwsBedrockAccessKeyId,
setAwsBedrockApiKey,
setAwsBedrockAuthType,
setAwsBedrockRegion,
setAwsBedrockSecretAccessKey
} from '@renderer/store/llm'
import type { AwsBedrockAuthType } from '@renderer/types'
import { useDispatch } from 'react-redux'
export function useAwsBedrockSettings() {
@@ -8,8 +15,10 @@ export function useAwsBedrockSettings() {
return {
...settings,
setAuthType: (authType: AwsBedrockAuthType) => dispatch(setAwsBedrockAuthType(authType)),
setAccessKeyId: (accessKeyId: string) => dispatch(setAwsBedrockAccessKeyId(accessKeyId)),
setSecretAccessKey: (secretAccessKey: string) => dispatch(setAwsBedrockSecretAccessKey(secretAccessKey)),
setApiKey: (apiKey: string) => dispatch(setAwsBedrockApiKey(apiKey)),
setRegion: (region: string) => dispatch(setAwsBedrockRegion(region))
}
}
@@ -18,6 +27,10 @@ export function getAwsBedrockSettings() {
return store.getState().llm.settings.awsBedrock
}
export function getAwsBedrockAuthType() {
return store.getState().llm.settings.awsBedrock.authType
}
export function getAwsBedrockAccessKeyId() {
return store.getState().llm.settings.awsBedrock.accessKeyId
}
@@ -26,6 +39,10 @@ export function getAwsBedrockSecretAccessKey() {
return store.getState().llm.settings.awsBedrock.secretAccessKey
}
export function getAwsBedrockApiKey() {
return store.getState().llm.settings.awsBedrock.apiKey
}
export function getAwsBedrockRegion() {
return store.getState().llm.settings.awsBedrock.region
}

View File

@@ -85,7 +85,8 @@ const providerKeyMap = {
poe: 'provider.poe',
aionly: 'provider.aionly',
longcat: 'provider.longcat',
huggingface: 'provider.huggingface'
huggingface: 'provider.huggingface',
sophnet: 'provider.sophnet'
} as const
/**
@@ -238,7 +239,7 @@ const paintingsImageSizeOptionsKeyMap = {
} as const
export const getPaintingsImageSizeOptionsLabel = (key: string): string => {
return getLabel(paintingsImageSizeOptionsKeyMap, key)
return paintingsImageSizeOptionsKeyMap[key] ? getLabel(paintingsImageSizeOptionsKeyMap, key) : key
}
const paintingsQualityOptionsKeyMap = {

View File

@@ -2482,6 +2482,7 @@
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",
"silicon": "SiliconFlow",
"sophnet": "SophNet",
"stepfun": "StepFun",
"tencent-cloud-ti": "Tencent Cloud TI",
"together": "Together",
@@ -3335,6 +3336,10 @@
"label": "Bucket",
"placeholder": "Bucket, e.g: example"
},
"bypassProxy": {
"help": "When enabled, S3 requests bypass the application proxy to prevent data corruption during large file uploads. Recommended to keep enabled unless your S3 endpoint requires proxy access.",
"label": "Bypass Proxy"
},
"endpoint": {
"label": "API Endpoint",
"placeholder": "https://s3.example.com"
@@ -4259,6 +4264,12 @@
"aws-bedrock": {
"access_key_id": "AWS Access Key ID",
"access_key_id_help": "Your AWS Access Key ID for accessing AWS Bedrock services",
"api_key": "Bedrock API Key",
"api_key_help": "Your AWS Bedrock API Key for authentication",
"auth_type": "Authentication Type",
"auth_type_api_key": "Bedrock API Key",
"auth_type_help": "Choose between IAM credentials or Bedrock API Key authentication",
"auth_type_iam": "IAM Credentials",
"description": "AWS Bedrock is Amazon's fully managed foundation model service that supports various advanced large language models",
"region": "AWS Region",
"region_help": "Your AWS service region, e.g., us-east-1",

View File

@@ -2482,6 +2482,7 @@
"qiniu": "七牛云 AI 推理",
"qwenlm": "QwenLM",
"silicon": "硅基流动",
"sophnet": "SophNet",
"stepfun": "阶跃星辰",
"tencent-cloud-ti": "腾讯云 TI",
"together": "Together",
@@ -3335,6 +3336,10 @@
"label": "存储桶",
"placeholder": "Bucket, 例如: example"
},
"bypassProxy": {
"help": "开启后S3 请求将绕过应用程序代理,以防止大文件上传时的数据损坏。建议保持开启,除非您的 S3 端点需要通过代理访问。",
"label": "绕过代理"
},
"endpoint": {
"label": "API 地址",
"placeholder": "https://s3.example.com"
@@ -4259,6 +4264,12 @@
"aws-bedrock": {
"access_key_id": "AWS 访问密钥 ID",
"access_key_id_help": "您的 AWS 访问密钥 ID用于访问 AWS Bedrock 服务",
"api_key": "Bedrock API 密钥",
"api_key_help": "您的 AWS Bedrock API 密钥,用于身份验证",
"auth_type": "认证方式",
"auth_type_api_key": "Bedrock API 密钥",
"auth_type_help": "选择使用 IAM 凭证或 Bedrock API 密钥进行身份验证",
"auth_type_iam": "IAM 凭证",
"description": "AWS Bedrock 是亚马逊提供的全托管基础模型服务,支持多种先进的大语言模型",
"region": "AWS 区域",
"region_help": "您的 AWS 服务区域,例如 us-east-1",

View File

@@ -2482,6 +2482,7 @@
"qiniu": "七牛雲 AI 推理",
"qwenlm": "QwenLM",
"silicon": "SiliconFlow",
"sophnet": "SophNet",
"stepfun": "StepFun",
"tencent-cloud-ti": "騰訊雲 TI",
"together": "Together",
@@ -3335,6 +3336,10 @@
"label": "儲存桶",
"placeholder": "Bucket例如: example"
},
"bypassProxy": {
"help": "開啟後S3 請求將繞過應用程式代理,以防止大檔案上傳時的資料損壞。建議保持開啟,除非您的 S3 端點需要透過代理存取。",
"label": "繞過代理"
},
"endpoint": {
"label": "API 位址",
"placeholder": "https://s3.example.com"
@@ -4259,6 +4264,12 @@
"aws-bedrock": {
"access_key_id": "AWS 存取密鑰 ID",
"access_key_id_help": "您的 AWS 存取密鑰 ID用於存取 AWS Bedrock 服務",
"api_key": "Bedrock API 金鑰",
"api_key_help": "您的 AWS Bedrock API 金鑰,用於身份驗證",
"auth_type": "認證方式",
"auth_type_api_key": "Bedrock API 金鑰",
"auth_type_help": "選擇使用 IAM 憑證或 Bedrock API 金鑰進行身份驗證",
"auth_type_iam": "IAM 憑證",
"description": "AWS Bedrock 是亞馬遜提供的全托管基础模型服務,支持多種先進的大語言模型",
"region": "AWS 區域",
"region_help": "您的 AWS 服務區域,例如 us-east-1",

View File

@@ -2476,12 +2476,13 @@
"openrouter": "OpenRouter",
"ovms": "Intel OVMS",
"perplexity": "Perplexity",
"ph8": "PH8 Großmodell-Plattform",
"ph8": "PH8",
"poe": "Poe",
"ppio": "PPIO Cloud",
"qiniu": "Qiniu Cloud KI-Inferenz",
"qwenlm": "QwenLM",
"silicon": "SiliconFlow",
"sophnet": "SophNet",
"stepfun": "StepFun",
"tencent-cloud-ti": "Tencent Cloud TI",
"together": "Together",
@@ -3335,6 +3336,10 @@
"label": "Bucket",
"placeholder": "Bucket, z. B.: example"
},
"bypassProxy": {
"help": "Wenn aktiviert, umgehen S3-Anfragen den Anwendungs-Proxy, um Datenbeschädigung beim Hochladen großer Dateien zu verhindern. Es wird empfohlen, diese Option aktiviert zu lassen, es sei denn, Ihr S3-Endpunkt erfordert Proxy-Zugriff.",
"label": "Proxy umgehen"
},
"endpoint": {
"label": "API-Adresse",
"placeholder": "https://s3.example.com"
@@ -4259,6 +4264,12 @@
"aws-bedrock": {
"access_key_id": "AWS-Zugriffsschlüssel-ID",
"access_key_id_help": "Ihre AWS-Zugriffsschlüssel-ID, um auf AWS Bedrock-Dienste zuzugreifen",
"api_key": "Bedrock-API-Schlüssel",
"api_key_help": "Ihr AWS-Bedrock-API-Schlüssel für die Authentifizierung",
"auth_type": "Authentifizierungstyp",
"auth_type_api_key": "Bedrock-API-Schlüssel",
"auth_type_help": "Wähle zwischen IAM-Anmeldeinformationen oder Bedrock-API-Schlüssel-Authentifizierung",
"auth_type_iam": "IAM-Anmeldeinformationen",
"description": "AWS Bedrock ist ein vollständig verwalteter Basismodell-Dienst von Amazon, der eine Vielzahl moderner großer Sprachmodelle unterstützt",
"region": "AWS-Region",
"region_help": "Ihre AWS-Serviceregion, z.B. us-east-1",

View File

@@ -2476,12 +2476,13 @@
"openrouter": "OpenRouter",
"ovms": "Intel OVMS",
"perplexity": "Perplexity",
"ph8": "Πλατφόρμα Ανοιχτής Μεγάλης Μοντέλου PH8",
"ph8": "PH8",
"poe": "Poe",
"ppio": "PPIO Piao Yun",
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",
"silicon": "Σιδηρική Παρουσία",
"sophnet": "SophNet",
"stepfun": "Βήμα Ουράς",
"tencent-cloud-ti": "Tencent Cloud TI",
"together": "Together",
@@ -3335,6 +3336,10 @@
"label": "Δοχείο",
"placeholder": "Bucket, π.χ.: example"
},
"bypassProxy": {
"help": "Όταν είναι ενεργοποιημένο, τα αιτήματα S3 παρακάμπτουν τον διαμεσολαβητή εφαρμογής για να αποτρέψουν τη διαφθορά δεδομένων κατά τη μεταφόρτωση μεγάλων αρχείων. Συνιστάται να παραμένει ενεργοποιημένο, εκτός εάν το τελικό σημείο S3 απαιτεί πρόσβαση μέσω διαμεσολαβητή.",
"label": "Παράκαμψη διαμεσολαβητή"
},
"endpoint": {
"label": "Διεύθυνση API",
"placeholder": "https://s3.example.com"
@@ -4259,6 +4264,12 @@
"aws-bedrock": {
"access_key_id": "Αναγνωριστικό κλειδιού πρόσβασης AWS",
"access_key_id_help": "Το ID του κλειδιού πρόσβασης AWS που χρησιμοποιείται για την πρόσβαση στην υπηρεσία AWS Bedrock",
"api_key": "Κλειδί API Bedrock",
"api_key_help": "Το κλειδί API του AWS Bedrock για έλεγχο ταυτότητας",
"auth_type": "Τύπος Πιστοποίησης",
"auth_type_api_key": "Κλειδί API Bedrock",
"auth_type_help": "Επιλέξτε μεταξύ πιστοποιητικών IAM ή πιστοποίησης με κλειδί API Bedrock",
"auth_type_iam": "Διαπιστευτήρια IAM",
"description": "Η AWS Bedrock είναι μια πλήρως διαχειριζόμενη υπηρεσία βασικών μοντέλων που παρέχεται από την Amazon και υποστηρίζει διάφορα προηγμένα μεγάλα γλωσσικά μοντέλα.",
"region": "Περιοχές AWS",
"region_help": "Η περιοχή υπηρεσίας AWS σας, για παράδειγμα us-east-1",

View File

@@ -2476,12 +2476,13 @@
"openrouter": "OpenRouter",
"ovms": "Intel OVMS",
"perplexity": "Perplejidad",
"ph8": "Plataforma Abierta de Grandes Modelos PH8",
"ph8": "PH8",
"poe": "Poe",
"ppio": "PPIO Cloud Piao",
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",
"silicon": "Silicio Fluido",
"sophnet": "SophNet",
"stepfun": "Función Salto",
"tencent-cloud-ti": "Tencent Nube TI",
"together": "Juntos",
@@ -3335,6 +3336,10 @@
"label": "Bucket",
"placeholder": "Bucket, por ejemplo: example"
},
"bypassProxy": {
"help": "Cuando está habilitado, las solicitudes a S3 omiten el proxy de la aplicación para evitar la corrupción de datos durante las cargas de archivos grandes. Se recomienda mantenerlo habilitado a menos que tu punto de enlace de S3 requiera acceso al proxy.",
"label": "Omitir Proxy"
},
"endpoint": {
"label": "Dirección API",
"placeholder": "https://s3.example.com"
@@ -4259,6 +4264,12 @@
"aws-bedrock": {
"access_key_id": "ID de clave de acceso de AWS",
"access_key_id_help": "Su ID de clave de acceso de AWS, utilizado para acceder al servicio AWS Bedrock",
"api_key": "Clave de API de Bedrock",
"api_key_help": "Tu clave de API de AWS Bedrock para la autenticación",
"auth_type": "Tipo de autenticación",
"auth_type_api_key": "Clave de API de Bedrock",
"auth_type_help": "Elige entre credenciales IAM o autenticación con clave API de Bedrock",
"auth_type_iam": "Credenciales IAM",
"description": "AWS Bedrock es un servicio de modelos fundamentales completamente gestionado proporcionado por Amazon, que admite diversos modelos avanzados de lenguaje de gran tamaño.",
"region": "Región de AWS",
"region_help": "Su región de servicio AWS, por ejemplo us-east-1",

View File

@@ -2476,12 +2476,13 @@
"openrouter": "OpenRouter",
"ovms": "Intel OVMS",
"perplexity": "Perplexité",
"ph8": "Plateforme ouverte de grands modèles PH8",
"ph8": "PH8",
"poe": "Poe",
"ppio": "PPIO Cloud Piou",
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",
"silicon": "Silicium Fluide",
"sophnet": "SophNet",
"stepfun": "Échelon Étoile",
"tencent-cloud-ti": "Tencent Cloud TI",
"together": "Ensemble",
@@ -3335,6 +3336,10 @@
"label": "Bucket",
"placeholder": "Bucket, par exemple : example"
},
"bypassProxy": {
"help": "Lorsquil est activé, les requêtes S3 contournent le proxy de lapplication pour éviter la corruption des données lors du téléversement de gros fichiers. Il est recommandé de le laisser activé sauf si votre point de terminaison S3 nécessite un accès par proxy.",
"label": "Contourner le proxy"
},
"endpoint": {
"label": "Adresse API",
"placeholder": "https://s3.example.com"
@@ -4259,6 +4264,12 @@
"aws-bedrock": {
"access_key_id": "Identifiant de clé d'accès AWS",
"access_key_id_help": "Votre identifiant de clé d'accès AWS, utilisé pour accéder au service AWS Bedrock",
"api_key": "Clé API Bedrock",
"api_key_help": "Votre clé API AWS Bedrock pour l'authentification",
"auth_type": "Type d'authentification",
"auth_type_api_key": "Clé API Bedrock",
"auth_type_help": "Choisissez entre l'authentification par identifiants IAM ou par clé API Bedrock",
"auth_type_iam": "Identifiants IAM",
"description": "AWS Bedrock est un service de modèles de base entièrement géré proposé par Amazon, prenant en charge divers grands modèles linguistiques avancés.",
"region": "Région AWS",
"region_help": "Votre région de service AWS, par exemple us-east-1",

View File

@@ -2482,6 +2482,7 @@
"qiniu": "七牛云 AI 推理",
"qwenlm": "QwenLM",
"silicon": "SiliconFlow",
"sophnet": "SophNet",
"stepfun": "StepFun",
"tencent-cloud-ti": "Tencent Cloud TI",
"together": "Together",
@@ -3335,6 +3336,10 @@
"label": "バケット",
"placeholder": "Bucket、例: example"
},
"bypassProxy": {
"help": "有効にすると、S3リクエストはアプリケーションプロキシをバイパスし、大容量ファイルのアップロード中のデータ破損を防ぎます。S3エンドポイントがプロキシアクセスを必要としない限り、有効のままにすることを推奨します。",
"label": "プロキシをバイパス"
},
"endpoint": {
"label": "APIエンドポイント",
"placeholder": "https://s3.example.com"
@@ -4259,6 +4264,12 @@
"aws-bedrock": {
"access_key_id": "AWS アクセスキー ID",
"access_key_id_help": "あなたの AWS アクセスキー ID は、AWS Bedrock サービスへのアクセスに使用されます",
"api_key": "Bedrock APIキー",
"api_key_help": "認証用のAWS Bedrock APIキー",
"auth_type": "認証タイプ",
"auth_type_api_key": "Bedrock APIキー",
"auth_type_help": "IAM認証情報とBedrock APIキー認証のどちらかを選択してください",
"auth_type_iam": "IAM認証情報",
"description": "AWS Bedrock は、Amazon が提供する完全に管理されたベースモデルサービスで、さまざまな最先端の大言語モデルをサポートしています",
"region": "AWS リージョン",
"region_help": "あなたの AWS サービスリージョン、例us-east-1",

View File

@@ -2476,12 +2476,13 @@
"openrouter": "OpenRouter",
"ovms": "Intel OVMS",
"perplexity": "Perplexidade",
"ph8": "Plataforma Aberta de Grandes Modelos PH8",
"ph8": "PH8",
"poe": "Poe",
"ppio": "PPIO Nuvem Piao",
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",
"silicon": "Silício em Fluxo",
"sophnet": "SophNet",
"stepfun": "Função de Passo Estelar",
"tencent-cloud-ti": "Nuvem TI da Tencent",
"together": "Juntos",
@@ -3335,6 +3336,10 @@
"label": "Bucket",
"placeholder": "Bucket, por exemplo: example"
},
"bypassProxy": {
"help": "Quando ativado, as solicitações S3 ignoram o proxy da aplicação para evitar corrupção de dados durante uploads de arquivos grandes. Recomenda-se manter ativado, a menos que seu endpoint S3 exija acesso por proxy.",
"label": "Ignorar Proxy"
},
"endpoint": {
"label": "Endereço da API",
"placeholder": "https://s3.example.com"
@@ -4259,6 +4264,12 @@
"aws-bedrock": {
"access_key_id": "ID da chave de acesso da AWS",
"access_key_id_help": "O seu ID da chave de acesso AWS, utilizado para aceder ao serviço AWS Bedrock",
"api_key": "Chave de API do Bedrock",
"api_key_help": "Sua Chave de API AWS Bedrock para autenticação",
"auth_type": "Tipo de Autenticação",
"auth_type_api_key": "Chave de API do Bedrock",
"auth_type_help": "Escolha entre credenciais IAM ou autenticação por chave de API do Bedrock",
"auth_type_iam": "Credenciais IAM",
"description": "A AWS Bedrock é um serviço de modelos fundamentais totalmente gerido fornecido pela Amazon, que suporta diversos modelos avançados de linguagem.",
"region": "Região da AWS",
"region_help": "A sua região de serviço da AWS, por exemplo, us-east-1",

View File

@@ -2482,6 +2482,7 @@
"qiniu": "Qiniu AI",
"qwenlm": "QwenLM",
"silicon": "SiliconFlow",
"sophnet": "SophNet",
"stepfun": "StepFun",
"tencent-cloud-ti": "Tencent Cloud TI",
"together": "Together",
@@ -3335,6 +3336,10 @@
"label": "Корзина",
"placeholder": "Корзина, например: example"
},
"bypassProxy": {
"help": "При включении запросы к S3 обходят прокси-приложение, чтобы предотвратить повреждение данных при загрузке больших файлов. Рекомендуется оставить включённым, если только ваш S3-эндпоинт не требует доступа через прокси.",
"label": "Обход прокси"
},
"endpoint": {
"label": "Конечная точка API",
"placeholder": "https://s3.example.com"
@@ -4259,6 +4264,12 @@
"aws-bedrock": {
"access_key_id": "AWS Ключ доступа ID",
"access_key_id_help": "Ваш AWS Ключ доступа ID для доступа к AWS Bedrock",
"api_key": "Ключ API Bedrock",
"api_key_help": "Ваш API-ключ AWS Bedrock для аутентификации",
"auth_type": "Тип аутентификации",
"auth_type_api_key": "Ключ API Bedrock",
"auth_type_help": "Выберите между аутентификацией с помощью учетных данных IAM или ключа API Bedrock",
"auth_type_iam": "Учетные данные IAM",
"description": "AWS Bedrock — это полное управляемое сервисное предложение для моделей, поддерживающее различные современные модели языка",
"region": "AWS регион",
"region_help": "Ваш регион AWS, например us-east-1",

View File

@@ -2,6 +2,7 @@ import type { DropResult } from '@hello-pangea/dnd'
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd'
import { loggerService } from '@logger'
import { ActionIconButton } from '@renderer/components/Buttons'
import { MdiLightbulbOn } from '@renderer/components/Icons'
import type { QuickPanelListItem } from '@renderer/components/QuickPanel'
import {
isAnthropicModel,
@@ -230,6 +231,15 @@ const InputbarTools = ({
quickPhrasesButtonRef.current?.openQuickPanel()
}
},
{
label: t('assistants.settings.reasoning_effort.label'),
description: '',
icon: <MdiLightbulbOn />,
isMenu: true,
action: () => {
thinkingButtonRef.current?.openQuickPanel()
}
},
{
label: t('assistants.presets.edit.model.select.title'),
description: '',
@@ -245,6 +255,7 @@ const InputbarTools = ({
icon: <FileSearch />,
isMenu: true,
disabled: files.length > 0,
hidden: !showKnowledgeBaseButton,
action: () => {
knowledgeBaseButtonRef.current?.openQuickPanel()
}
@@ -312,7 +323,7 @@ const InputbarTools = ({
translate()
}
}
]
] satisfies QuickPanelListItem[]
}
const handleDragEnd = (result: DropResult) => {

View File

@@ -482,6 +482,13 @@ const NewApiPage: FC<{ Options: string[] }> = ({ Options }) => {
}
}, [])
// if painting.model is not set, set it to the first model in modelOptions
useEffect(() => {
if (!painting.model && modelOptions.length > 0) {
updatePaintingState({ model: modelOptions[0].value })
}
}, [modelOptions, painting.model, updatePaintingState])
return (
<Container>
<Navbar>

View File

@@ -3,6 +3,7 @@ import { isNewApiProvider } from '@renderer/config/providers'
import { useAllProviders } from '@renderer/hooks/useProvider'
import { useAppDispatch } from '@renderer/store'
import { setDefaultPaintingProvider } from '@renderer/store/settings'
import { updateTab } from '@renderer/store/tabs'
import type { PaintingProvider, SystemProviderId } from '@renderer/types'
import type { FC } from 'react'
import { useEffect, useMemo, useState } from 'react'
@@ -25,11 +26,11 @@ const PaintingsRoutePage: FC = () => {
const provider = params['*']
const dispatch = useAppDispatch()
const providers = useAllProviders()
const Options = useMemo(() => {
return [...BASE_OPTIONS, ...providers.filter((p) => isNewApiProvider(p)).map((p) => p.id)]
}, [providers])
const [ovmsStatus, setOvmsStatus] = useState<'not-installed' | 'not-running' | 'running'>('not-running')
const Options = useMemo(() => [...BASE_OPTIONS, ...providers.filter(isNewApiProvider).map((p) => p.id)], [providers])
const newApiProviders = useMemo(() => providers.filter(isNewApiProvider), [providers])
useEffect(() => {
const checkStatus = async () => {
const status = await window.api.ovms.getStatus()
@@ -44,25 +45,24 @@ const PaintingsRoutePage: FC = () => {
logger.debug(`defaultPaintingProvider: ${provider}`)
if (provider && validOptions.includes(provider)) {
dispatch(setDefaultPaintingProvider(provider as PaintingProvider))
dispatch(updateTab({ id: 'paintings', updates: { path: `/paintings/${provider}` } }))
}
}, [provider, dispatch, validOptions])
return (
<Routes>
<Route path="*" element={<ZhipuPage Options={validOptions} />} />
<Route path="*" element={<NewApiPage Options={validOptions} />} />
<Route path="/zhipu" element={<ZhipuPage Options={validOptions} />} />
<Route path="/aihubmix" element={<AihubmixPage Options={validOptions} />} />
<Route path="/silicon" element={<SiliconPage Options={validOptions} />} />
<Route path="/dmxapi" element={<DmxapiPage Options={validOptions} />} />
<Route path="/tokenflux" element={<TokenFluxPage Options={validOptions} />} />
<Route path="/ovms" element={<OvmsPage Options={validOptions} />} />
{/* new-api family providers are mounted dynamically below */}
{providers
.filter((p) => isNewApiProvider(p))
.map((p) => (
<Route key={p.id} path={`/${p.id}`} element={<NewApiPage Options={validOptions} />} />
))}
<Route path="/new-api" element={<NewApiPage Options={validOptions} />} />
{/* new-api family providers are mounted dynamically below */}
{newApiProviders.map((p) => (
<Route key={p.id} path={`/${p.id}`} element={<NewApiPage Options={validOptions} />} />
))}
</Routes>
)
}

View File

@@ -31,7 +31,8 @@ const S3Settings: FC = () => {
root: s3RootInit = '',
syncInterval: s3SyncIntervalInit = 0,
maxBackups: s3MaxBackupsInit = 5,
skipBackupFile: s3SkipBackupFileInit = false
skipBackupFile: s3SkipBackupFileInit = false,
bypassProxy: s3BypassProxyInit = true
} = s3
const [endpoint, setEndpoint] = useState<string | undefined>(s3EndpointInit)
@@ -41,6 +42,7 @@ const S3Settings: FC = () => {
const [secretAccessKey, setSecretAccessKey] = useState<string | undefined>(s3SecretAccessKeyInit)
const [root, setRoot] = useState<string | undefined>(s3RootInit)
const [skipBackupFile, setSkipBackupFile] = useState<boolean>(s3SkipBackupFileInit)
const [bypassProxy, setBypassProxy] = useState<boolean>(s3BypassProxyInit)
const [backupManagerVisible, setBackupManagerVisible] = useState(false)
const [syncInterval, setSyncInterval] = useState<number>(s3SyncIntervalInit)
@@ -82,6 +84,11 @@ const S3Settings: FC = () => {
dispatch(setS3Partial({ skipBackupFile: value }))
}
const onBypassProxyChange = (value: boolean) => {
setBypassProxy(value)
dispatch(setS3Partial({ bypassProxy: value }))
}
const renderSyncStatus = () => {
if (!endpoint) return null
@@ -261,6 +268,14 @@ const S3Settings: FC = () => {
<SettingRow>
<SettingHelpText>{t('settings.data.s3.skipBackupFile.help')}</SettingHelpText>
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.data.s3.bypassProxy.label')}</SettingRowTitle>
<Switch checked={bypassProxy} onChange={onBypassProxyChange} />
</SettingRow>
<SettingRow>
<SettingHelpText>{t('settings.data.s3.bypassProxy.help')}</SettingHelpText>
</SettingRow>
{syncInterval > 0 && (
<>
<SettingDivider />

View File

@@ -1,7 +1,7 @@
import { HStack } from '@renderer/components/Layout'
import { PROVIDER_URLS } from '@renderer/config/providers'
import { useAwsBedrockSettings } from '@renderer/hooks/useAwsBedrock'
import { Alert, Input } from 'antd'
import { Alert, Input, Radio } from 'antd'
import type { FC } from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -10,14 +10,25 @@ import { SettingHelpLink, SettingHelpText, SettingHelpTextRow, SettingSubtitle }
const AwsBedrockSettings: FC = () => {
const { t } = useTranslation()
const { accessKeyId, secretAccessKey, region, setAccessKeyId, setSecretAccessKey, setRegion } =
useAwsBedrockSettings()
const {
authType,
accessKeyId,
secretAccessKey,
apiKey,
region,
setAuthType,
setAccessKeyId,
setSecretAccessKey,
setApiKey,
setRegion
} = useAwsBedrockSettings()
const providerConfig = PROVIDER_URLS['aws-bedrock']
const apiKeyWebsite = providerConfig?.websites?.apiKey
const [localAccessKeyId, setLocalAccessKeyId] = useState(accessKeyId)
const [localSecretAccessKey, setLocalSecretAccessKey] = useState(secretAccessKey)
const [localApiKey, setLocalApiKey] = useState(apiKey)
const [localRegion, setLocalRegion] = useState(region)
return (
@@ -25,39 +36,75 @@ const AwsBedrockSettings: FC = () => {
<SettingSubtitle style={{ marginTop: 5 }}>{t('settings.provider.aws-bedrock.title')}</SettingSubtitle>
<Alert type="info" style={{ marginTop: 5 }} message={t('settings.provider.aws-bedrock.description')} showIcon />
<SettingSubtitle style={{ marginTop: 5 }}>{t('settings.provider.aws-bedrock.access_key_id')}</SettingSubtitle>
<Input
value={localAccessKeyId}
placeholder="Access Key ID"
onChange={(e) => setLocalAccessKeyId(e.target.value)}
onBlur={() => setAccessKeyId(localAccessKeyId)}
style={{ marginTop: 5 }}
/>
{/* Authentication Type Selector */}
<SettingSubtitle style={{ marginTop: 15 }}>{t('settings.provider.aws-bedrock.auth_type')}</SettingSubtitle>
<Radio.Group value={authType} onChange={(e) => setAuthType(e.target.value)} style={{ marginTop: 5 }}>
<Radio value="iam">{t('settings.provider.aws-bedrock.auth_type_iam')}</Radio>
<Radio value="apiKey">{t('settings.provider.aws-bedrock.auth_type_api_key')}</Radio>
</Radio.Group>
<SettingHelpTextRow>
<SettingHelpText>{t('settings.provider.aws-bedrock.access_key_id_help')}</SettingHelpText>
<SettingHelpText>{t('settings.provider.aws-bedrock.auth_type_help')}</SettingHelpText>
</SettingHelpTextRow>
<SettingSubtitle style={{ marginTop: 5 }}>{t('settings.provider.aws-bedrock.secret_access_key')}</SettingSubtitle>
<Input.Password
value={localSecretAccessKey}
placeholder="Secret Access Key"
onChange={(e) => setLocalSecretAccessKey(e.target.value)}
onBlur={() => setSecretAccessKey(localSecretAccessKey)}
style={{ marginTop: 5 }}
spellCheck={false}
/>
{apiKeyWebsite && (
<SettingHelpTextRow style={{ justifyContent: 'space-between' }}>
<HStack>
<SettingHelpLink target="_blank" href={apiKeyWebsite}>
{t('settings.provider.get_api_key')}
</SettingHelpLink>
</HStack>
<SettingHelpText>{t('settings.provider.aws-bedrock.secret_access_key_help')}</SettingHelpText>
</SettingHelpTextRow>
{/* IAM Credentials Fields */}
{authType === 'iam' && (
<>
<SettingSubtitle style={{ marginTop: 15 }}>
{t('settings.provider.aws-bedrock.access_key_id')}
</SettingSubtitle>
<Input
value={localAccessKeyId}
placeholder="Access Key ID"
onChange={(e) => setLocalAccessKeyId(e.target.value)}
onBlur={() => setAccessKeyId(localAccessKeyId)}
style={{ marginTop: 5 }}
/>
<SettingHelpTextRow>
<SettingHelpText>{t('settings.provider.aws-bedrock.access_key_id_help')}</SettingHelpText>
</SettingHelpTextRow>
<SettingSubtitle style={{ marginTop: 15 }}>
{t('settings.provider.aws-bedrock.secret_access_key')}
</SettingSubtitle>
<Input.Password
value={localSecretAccessKey}
placeholder="Secret Access Key"
onChange={(e) => setLocalSecretAccessKey(e.target.value)}
onBlur={() => setSecretAccessKey(localSecretAccessKey)}
style={{ marginTop: 5 }}
spellCheck={false}
/>
{apiKeyWebsite && (
<SettingHelpTextRow style={{ justifyContent: 'space-between' }}>
<HStack>
<SettingHelpLink target="_blank" href={apiKeyWebsite}>
{t('settings.provider.get_api_key')}
</SettingHelpLink>
</HStack>
<SettingHelpText>{t('settings.provider.aws-bedrock.secret_access_key_help')}</SettingHelpText>
</SettingHelpTextRow>
)}
</>
)}
<SettingSubtitle style={{ marginTop: 5 }}>{t('settings.provider.aws-bedrock.region')}</SettingSubtitle>
{authType === 'apiKey' && (
<>
<SettingSubtitle style={{ marginTop: 15 }}>{t('settings.provider.aws-bedrock.api_key')}</SettingSubtitle>
<Input.Password
value={localApiKey}
placeholder="Bedrock API Key"
onChange={(e) => setLocalApiKey(e.target.value)}
onBlur={() => setApiKey(localApiKey)}
style={{ marginTop: 5 }}
spellCheck={false}
/>
<SettingHelpTextRow>
<SettingHelpText>{t('settings.provider.aws-bedrock.api_key_help')}</SettingHelpText>
</SettingHelpTextRow>
</>
)}
<SettingSubtitle style={{ marginTop: 15 }}>{t('settings.provider.aws-bedrock.region')}</SettingSubtitle>
<Input
value={localRegion}
placeholder="us-east-1"

View File

@@ -131,10 +131,11 @@ const PopupContainer: React.FC<Props> = ({ providerId, resolve }) => {
(model: Model) => {
if (!isEmpty(model.name)) {
if (isNewApiProvider(provider)) {
if (model.supported_endpoint_types && model.supported_endpoint_types.length > 0) {
const endpointTypes = model.supported_endpoint_types
if (endpointTypes && endpointTypes.length > 0) {
addModel({
...model,
endpoint_type: model.supported_endpoint_types[0],
endpoint_type: endpointTypes.includes('image-generation') ? 'image-generation' : endpointTypes[0],
supported_text_delta: !isNotSupportedTextDelta(model)
})
} else {

View File

@@ -230,8 +230,10 @@ vi.mock('@renderer/store/llm.ts', () => {
location: ''
},
awsBedrock: {
authType: 'iam',
accessKeyId: '',
secretAccessKey: '',
apiKey: '',
region: ''
}
}

View File

@@ -67,7 +67,7 @@ const persistedReducer = persistReducer(
{
key: 'cherry-studio',
storage,
version: 169,
version: 171,
blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs', 'toolPermissions'],
migrate
},

View File

@@ -3,7 +3,7 @@ import { createSlice } from '@reduxjs/toolkit'
import { isLocalAi } from '@renderer/config/env'
import { SYSTEM_MODELS } from '@renderer/config/models'
import { SYSTEM_PROVIDERS } from '@renderer/config/providers'
import type { Model, Provider } from '@renderer/types'
import type { AwsBedrockAuthType, Model, Provider } from '@renderer/types'
import { uniqBy } from 'lodash'
type LlmSettings = {
@@ -25,8 +25,10 @@ type LlmSettings = {
location: string
}
awsBedrock: {
authType: AwsBedrockAuthType
accessKeyId: string
secretAccessKey: string
apiKey: string
region: string
}
}
@@ -68,8 +70,10 @@ export const initialState: LlmState = {
location: ''
},
awsBedrock: {
authType: 'iam',
accessKeyId: '',
secretAccessKey: '',
apiKey: '',
region: ''
}
}
@@ -197,12 +201,18 @@ const llmSlice = createSlice({
setVertexAIServiceAccountClientEmail: (state, action: PayloadAction<string>) => {
state.settings.vertexai.serviceAccount.clientEmail = action.payload
},
setAwsBedrockAuthType: (state, action: PayloadAction<AwsBedrockAuthType>) => {
state.settings.awsBedrock.authType = action.payload
},
setAwsBedrockAccessKeyId: (state, action: PayloadAction<string>) => {
state.settings.awsBedrock.accessKeyId = action.payload
},
setAwsBedrockSecretAccessKey: (state, action: PayloadAction<string>) => {
state.settings.awsBedrock.secretAccessKey = action.payload
},
setAwsBedrockApiKey: (state, action: PayloadAction<string>) => {
state.settings.awsBedrock.apiKey = action.payload
},
setAwsBedrockRegion: (state, action: PayloadAction<string>) => {
state.settings.awsBedrock.region = action.payload
},
@@ -242,8 +252,10 @@ export const {
setVertexAILocation,
setVertexAIServiceAccountPrivateKey,
setVertexAIServiceAccountClientEmail,
setAwsBedrockAuthType,
setAwsBedrockAccessKeyId,
setAwsBedrockSecretAccessKey,
setAwsBedrockApiKey,
setAwsBedrockRegion,
updateModel
} = llmSlice.actions

View File

@@ -2783,6 +2783,28 @@ const migrateConfig = {
logger.error('migrate 169 error', error as Error)
return state
}
},
'170': (state: RootState) => {
try {
addProvider(state, 'sophnet')
state.llm.providers = moveProvider(state.llm.providers, 'sophnet', 17)
state.settings.defaultPaintingProvider = 'cherryin'
return state
} catch (error) {
logger.error('migrate 170 error', error as Error)
return state
}
},
'171': (state: RootState) => {
try {
addProvider(state, 'sophnet')
state.llm.providers = moveProvider(state.llm.providers, 'sophnet', 17)
state.settings.defaultPaintingProvider = 'cherryin'
return state
} catch (error) {
logger.error('migrate 171 error', error as Error)
return state
}
}
}

View File

@@ -390,7 +390,7 @@ export const initialState: SettingsState = {
localBackupSyncInterval: 0,
localBackupMaxBackups: 0,
localBackupSkipBackupFile: false,
defaultPaintingProvider: 'zhipu',
defaultPaintingProvider: 'cherryin',
s3: {
endpoint: '',
region: '',
@@ -401,7 +401,8 @@ export const initialState: SettingsState = {
autoSync: false,
syncInterval: 0,
maxBackups: 0,
skipBackupFile: false
skipBackupFile: false,
bypassProxy: true
},
// Developer mode

View File

@@ -36,6 +36,7 @@ export type Assistant = {
description?: string
model?: Model
defaultModel?: Model
// This field should be considered as not Partial and not optional in v2
settings?: Partial<AssistantSettings>
messages?: AssistantMessage[]
/** enableWebSearch 代表使用模型内置网络搜索功能 */
@@ -280,7 +281,7 @@ export type PaintingParams = {
providerId?: string
}
export type PaintingProvider = 'zhipu' | 'aihubmix' | 'silicon' | 'dmxapi' | 'new-api' | 'ovms'
export type PaintingProvider = 'zhipu' | 'aihubmix' | 'silicon' | 'dmxapi' | 'new-api' | 'ovms' | 'cherryin'
export interface Painting extends PaintingParams {
model?: string
@@ -882,6 +883,7 @@ export type S3Config = {
autoSync: boolean
syncInterval: number
maxBackups: number
bypassProxy?: boolean // Whether to bypass proxy for S3 requests (default: true)
}
export type { Message } from './newMessage'

View File

@@ -73,6 +73,17 @@ export function isServiceTier(tier: string): tier is ServiceTier {
return isGroqServiceTier(tier) || isOpenAIServiceTier(tier)
}
export const AwsBedrockAuthTypes = {
iam: 'iam',
apiKey: 'apiKey'
} as const
export type AwsBedrockAuthType = keyof typeof AwsBedrockAuthTypes
export function isAwsBedrockAuthType(type: string): type is AwsBedrockAuthType {
return Object.hasOwn(AwsBedrockAuthTypes, type)
}
export type Provider = {
id: string
type: ProviderType
@@ -123,6 +134,7 @@ export const SystemProviderIds = {
cephalon: 'cephalon',
lanyun: 'lanyun',
ph8: 'ph8',
sophnet: 'sophnet',
openrouter: 'openrouter',
ollama: 'ollama',
ovms: 'ovms',

2821
yarn.lock

File diff suppressed because it is too large Load Diff