mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-24 18:50:56 +08:00
Merge branch 'main' of into v2
This commit is contained in:
commit
5fdfa5a594
@ -111,8 +111,10 @@
|
||||
"@agentic/searxng": "^7.3.3",
|
||||
"@agentic/tavily": "^7.3.3",
|
||||
"@ai-sdk/amazon-bedrock": "^3.0.53",
|
||||
"@ai-sdk/anthropic": "^2.0.44",
|
||||
"@ai-sdk/cerebras": "^1.0.31",
|
||||
"@ai-sdk/gateway": "^2.0.9",
|
||||
"@ai-sdk/google": "^2.0.32",
|
||||
"@ai-sdk/google-vertex": "^3.0.62",
|
||||
"@ai-sdk/huggingface": "patch:@ai-sdk/huggingface@npm%3A0.0.8#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.8-d4d0aaac93.patch",
|
||||
"@ai-sdk/mistral": "^2.0.23",
|
||||
@ -138,7 +140,7 @@
|
||||
"@cherrystudio/embedjs-ollama": "^0.1.31",
|
||||
"@cherrystudio/embedjs-openai": "^0.1.31",
|
||||
"@cherrystudio/extension-table-plus": "workspace:^",
|
||||
"@cherrystudio/openai": "^6.5.0",
|
||||
"@cherrystudio/openai": "^6.9.0",
|
||||
"@cherrystudio/ui": "workspace:*",
|
||||
"@dnd-kit/core": "^6.3.1",
|
||||
"@dnd-kit/modifiers": "^9.0.0",
|
||||
@ -168,7 +170,7 @@
|
||||
"@opentelemetry/sdk-trace-base": "^2.0.0",
|
||||
"@opentelemetry/sdk-trace-node": "^2.0.0",
|
||||
"@opentelemetry/sdk-trace-web": "^2.0.0",
|
||||
"@opeoginni/github-copilot-openai-compatible": "0.1.19",
|
||||
"@opeoginni/github-copilot-openai-compatible": "0.1.21",
|
||||
"@playwright/test": "^1.52.0",
|
||||
"@radix-ui/react-context-menu": "^2.2.16",
|
||||
"@reduxjs/toolkit": "^2.2.5",
|
||||
|
||||
@ -199,7 +199,7 @@ export enum FeedUrl {
|
||||
|
||||
export enum UpdateConfigUrl {
|
||||
GITHUB = 'https://raw.githubusercontent.com/CherryHQ/cherry-studio/refs/heads/x-files/app-upgrade-config/app-upgrade-config.json',
|
||||
GITCODE = 'https://raw.gitcode.com/CherryHQ/cherry-studio/raw/x-files/app-upgrade-config/app-upgrade-config.json'
|
||||
GITCODE = 'https://raw.gitcode.com/CherryHQ/cherry-studio/raw/x-files%2Fapp-upgrade-config/app-upgrade-config.json'
|
||||
}
|
||||
|
||||
// export enum UpgradeChannel {
|
||||
|
||||
@ -21,6 +21,7 @@ type ApiResponse<T> = {
|
||||
type BatchUploadResponse = {
|
||||
batch_id: string
|
||||
file_urls: string[]
|
||||
headers?: Record<string, string>[]
|
||||
}
|
||||
|
||||
type ExtractProgress = {
|
||||
@ -55,7 +56,7 @@ type QuotaResponse = {
|
||||
export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
constructor(provider: PreprocessProvider, userId?: string) {
|
||||
super(provider, userId)
|
||||
// todo:免费期结束后删除
|
||||
// TODO: remove after free period ends
|
||||
this.provider.apiKey = this.provider.apiKey || import.meta.env.MAIN_VITE_MINERU_API_KEY
|
||||
}
|
||||
|
||||
@ -68,21 +69,21 @@ export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
logger.info(`MinerU preprocess processing started: ${filePath}`)
|
||||
await this.validateFile(filePath)
|
||||
|
||||
// 1. 获取上传URL并上传文件
|
||||
// 1. Get upload URL and upload file
|
||||
const batchId = await this.uploadFile(file)
|
||||
logger.info(`MinerU file upload completed: batch_id=${batchId}`)
|
||||
|
||||
// 2. 等待处理完成并获取结果
|
||||
// 2. Wait for completion and fetch results
|
||||
const extractResult = await this.waitForCompletion(sourceId, batchId, file.origin_name)
|
||||
logger.info(`MinerU processing completed for batch: ${batchId}`)
|
||||
|
||||
// 3. 下载并解压文件
|
||||
// 3. Download and extract output
|
||||
const { path: outputPath } = await this.downloadAndExtractFile(extractResult.full_zip_url!, file)
|
||||
|
||||
// 4. check quota
|
||||
const quota = await this.checkQuota()
|
||||
|
||||
// 5. 创建处理后的文件信息
|
||||
// 5. Create processed file metadata
|
||||
return {
|
||||
processedFile: this.createProcessedFileInfo(file, outputPath),
|
||||
quota
|
||||
@ -115,23 +116,48 @@ export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
}
|
||||
|
||||
private async validateFile(filePath: string): Promise<void> {
|
||||
// Phase 1: check file size (without loading into memory)
|
||||
logger.info(`Validating PDF file: ${filePath}`)
|
||||
const stats = await fs.promises.stat(filePath)
|
||||
const fileSizeBytes = stats.size
|
||||
|
||||
// Ensure file size is under 200MB
|
||||
if (fileSizeBytes >= 200 * 1024 * 1024) {
|
||||
const fileSizeMB = Math.round(fileSizeBytes / (1024 * 1024))
|
||||
throw new Error(`PDF file size (${fileSizeMB}MB) exceeds the limit of 200MB`)
|
||||
}
|
||||
|
||||
// Phase 2: check page count (requires reading file with error handling)
|
||||
const pdfBuffer = await fs.promises.readFile(filePath)
|
||||
|
||||
const doc = await this.readPdf(pdfBuffer)
|
||||
try {
|
||||
const doc = await this.readPdf(pdfBuffer)
|
||||
|
||||
// 文件页数小于600页
|
||||
if (doc.numPages >= 600) {
|
||||
throw new Error(`PDF page count (${doc.numPages}) exceeds the limit of 600 pages`)
|
||||
}
|
||||
// 文件大小小于200MB
|
||||
if (pdfBuffer.length >= 200 * 1024 * 1024) {
|
||||
const fileSizeMB = Math.round(pdfBuffer.length / (1024 * 1024))
|
||||
throw new Error(`PDF file size (${fileSizeMB}MB) exceeds the limit of 200MB`)
|
||||
// Ensure page count is under 600 pages
|
||||
if (doc.numPages >= 600) {
|
||||
throw new Error(`PDF page count (${doc.numPages}) exceeds the limit of 600 pages`)
|
||||
}
|
||||
|
||||
logger.info(`PDF validation passed: ${doc.numPages} pages, ${Math.round(fileSizeBytes / (1024 * 1024))}MB`)
|
||||
} catch (error: any) {
|
||||
// If the page limit is exceeded, rethrow immediately
|
||||
if (error.message.includes('exceeds the limit')) {
|
||||
throw error
|
||||
}
|
||||
|
||||
// If PDF parsing fails, log a detailed warning but continue processing
|
||||
logger.warn(
|
||||
`Failed to parse PDF structure (file may be corrupted or use non-standard format). ` +
|
||||
`Skipping page count validation. Will attempt to process with MinerU API. ` +
|
||||
`Error details: ${error.message}. ` +
|
||||
`Suggestion: If processing fails, try repairing the PDF using tools like Adobe Acrobat or online PDF repair services.`
|
||||
)
|
||||
// Do not throw; continue processing
|
||||
}
|
||||
}
|
||||
|
||||
private createProcessedFileInfo(file: FileMetadata, outputPath: string): FileMetadata {
|
||||
// 查找解压后的主要文件
|
||||
// Locate the main extracted file
|
||||
let finalPath = ''
|
||||
let finalName = file.origin_name.replace('.pdf', '.md')
|
||||
|
||||
@ -143,14 +169,14 @@ export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
const originalMdPath = path.join(outputPath, mdFile)
|
||||
const newMdPath = path.join(outputPath, finalName)
|
||||
|
||||
// 重命名文件为原始文件名
|
||||
// Rename the file to match the original name
|
||||
try {
|
||||
fs.renameSync(originalMdPath, newMdPath)
|
||||
finalPath = newMdPath
|
||||
logger.info(`Renamed markdown file from ${mdFile} to ${finalName}`)
|
||||
} catch (renameError) {
|
||||
logger.warn(`Failed to rename file ${mdFile} to ${finalName}: ${renameError}`)
|
||||
// 如果重命名失败,使用原文件
|
||||
// If renaming fails, fall back to the original file
|
||||
finalPath = originalMdPath
|
||||
finalName = mdFile
|
||||
}
|
||||
@ -178,7 +204,7 @@ export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
logger.info(`Downloading MinerU result to: ${zipPath}`)
|
||||
|
||||
try {
|
||||
// 下载ZIP文件
|
||||
// Download the ZIP file
|
||||
const response = await net.fetch(zipUrl, { method: 'GET' })
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP ${response.status}: ${response.statusText}`)
|
||||
@ -187,17 +213,17 @@ export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
fs.writeFileSync(zipPath, Buffer.from(arrayBuffer))
|
||||
logger.info(`Downloaded ZIP file: ${zipPath}`)
|
||||
|
||||
// 确保提取目录存在
|
||||
// Ensure the extraction directory exists
|
||||
if (!fs.existsSync(extractPath)) {
|
||||
fs.mkdirSync(extractPath, { recursive: true })
|
||||
}
|
||||
|
||||
// 解压文件
|
||||
// Extract the ZIP contents
|
||||
const zip = new AdmZip(zipPath)
|
||||
zip.extractAllTo(extractPath, true)
|
||||
logger.info(`Extracted files to: ${extractPath}`)
|
||||
|
||||
// 删除临时ZIP文件
|
||||
// Remove the temporary ZIP file
|
||||
fs.unlinkSync(zipPath)
|
||||
|
||||
return { path: extractPath }
|
||||
@ -209,11 +235,11 @@ export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
|
||||
private async uploadFile(file: FileMetadata): Promise<string> {
|
||||
try {
|
||||
// 步骤1: 获取上传URL
|
||||
const { batchId, fileUrls } = await this.getBatchUploadUrls(file)
|
||||
// 步骤2: 上传文件到获取的URL
|
||||
// Step 1: obtain the upload URL
|
||||
const { batchId, fileUrls, uploadHeaders } = await this.getBatchUploadUrls(file)
|
||||
// Step 2: upload the file to the obtained URL
|
||||
const filePath = fileStorage.getFilePathById(file)
|
||||
await this.putFileToUrl(filePath, fileUrls[0])
|
||||
await this.putFileToUrl(filePath, fileUrls[0], file.origin_name, uploadHeaders?.[0])
|
||||
logger.info(`File uploaded successfully: ${filePath}`, { batchId, fileUrls })
|
||||
|
||||
return batchId
|
||||
@ -223,7 +249,9 @@ export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private async getBatchUploadUrls(file: FileMetadata): Promise<{ batchId: string; fileUrls: string[] }> {
|
||||
private async getBatchUploadUrls(
|
||||
file: FileMetadata
|
||||
): Promise<{ batchId: string; fileUrls: string[]; uploadHeaders?: Record<string, string>[] }> {
|
||||
const endpoint = `${this.provider.apiHost}/api/v4/file-urls/batch`
|
||||
|
||||
const payload = {
|
||||
@ -254,10 +282,11 @@ export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
if (response.ok) {
|
||||
const data: ApiResponse<BatchUploadResponse> = await response.json()
|
||||
if (data.code === 0 && data.data) {
|
||||
const { batch_id, file_urls } = data.data
|
||||
const { batch_id, file_urls, headers: uploadHeaders } = data.data
|
||||
return {
|
||||
batchId: batch_id,
|
||||
fileUrls: file_urls
|
||||
fileUrls: file_urls,
|
||||
uploadHeaders
|
||||
}
|
||||
} else {
|
||||
throw new Error(`API returned error: ${data.msg || JSON.stringify(data)}`)
|
||||
@ -271,18 +300,28 @@ export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private async putFileToUrl(filePath: string, uploadUrl: string): Promise<void> {
|
||||
private async putFileToUrl(
|
||||
filePath: string,
|
||||
uploadUrl: string,
|
||||
fileName?: string,
|
||||
headers?: Record<string, string>
|
||||
): Promise<void> {
|
||||
try {
|
||||
const fileBuffer = await fs.promises.readFile(filePath)
|
||||
const fileSize = fileBuffer.byteLength
|
||||
const displayName = fileName ?? path.basename(filePath)
|
||||
|
||||
logger.info(`Uploading file to MinerU OSS: ${displayName} (${fileSize} bytes)`)
|
||||
|
||||
// https://mineru.net/apiManage/docs
|
||||
const response = await net.fetch(uploadUrl, {
|
||||
method: 'PUT',
|
||||
body: fileBuffer as unknown as BodyInit
|
||||
headers,
|
||||
body: new Uint8Array(fileBuffer)
|
||||
})
|
||||
|
||||
if (!response.ok) {
|
||||
// 克隆 response 以避免消费 body stream
|
||||
// Clone the response to avoid consuming the body stream
|
||||
const responseClone = response.clone()
|
||||
|
||||
try {
|
||||
@ -353,20 +392,20 @@ export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
try {
|
||||
const result = await this.getExtractResults(batchId)
|
||||
|
||||
// 查找对应文件的处理结果
|
||||
// Find the corresponding file result
|
||||
const fileResult = result.extract_result.find((item) => item.file_name === fileName)
|
||||
if (!fileResult) {
|
||||
throw new Error(`File ${fileName} not found in batch results`)
|
||||
}
|
||||
|
||||
// 检查处理状态
|
||||
// Check the processing state
|
||||
if (fileResult.state === 'done' && fileResult.full_zip_url) {
|
||||
logger.info(`Processing completed for file: ${fileName}`)
|
||||
return fileResult
|
||||
} else if (fileResult.state === 'failed') {
|
||||
throw new Error(`Processing failed for file: ${fileName}, error: ${fileResult.err_msg}`)
|
||||
} else if (fileResult.state === 'running') {
|
||||
// 发送进度更新
|
||||
// Send progress updates
|
||||
if (fileResult.extract_progress) {
|
||||
const progress = Math.round(
|
||||
(fileResult.extract_progress.extracted_pages / fileResult.extract_progress.total_pages) * 100
|
||||
@ -374,7 +413,7 @@ export default class MineruPreprocessProvider extends BasePreprocessProvider {
|
||||
await this.sendPreprocessProgress(sourceId, progress)
|
||||
logger.info(`File ${fileName} processing progress: ${progress}%`)
|
||||
} else {
|
||||
// 如果没有具体进度信息,发送一个通用进度
|
||||
// If no detailed progress information is available, send a generic update
|
||||
await this.sendPreprocessProgress(sourceId, 50)
|
||||
logger.info(`File ${fileName} is still processing...`)
|
||||
}
|
||||
|
||||
@ -53,18 +53,43 @@ export default class OpenMineruPreprocessProvider extends BasePreprocessProvider
|
||||
}
|
||||
|
||||
private async validateFile(filePath: string): Promise<void> {
|
||||
// 第一阶段:检查文件大小(无需读取文件到内存)
|
||||
logger.info(`Validating PDF file: ${filePath}`)
|
||||
const stats = await fs.promises.stat(filePath)
|
||||
const fileSizeBytes = stats.size
|
||||
|
||||
// File size must be less than 200MB
|
||||
if (fileSizeBytes >= 200 * 1024 * 1024) {
|
||||
const fileSizeMB = Math.round(fileSizeBytes / (1024 * 1024))
|
||||
throw new Error(`PDF file size (${fileSizeMB}MB) exceeds the limit of 200MB`)
|
||||
}
|
||||
|
||||
// 第二阶段:检查页数(需要读取文件,带错误处理)
|
||||
const pdfBuffer = await fs.promises.readFile(filePath)
|
||||
|
||||
const doc = await this.readPdf(pdfBuffer)
|
||||
try {
|
||||
const doc = await this.readPdf(pdfBuffer)
|
||||
|
||||
// File page count must be less than 600 pages
|
||||
if (doc.numPages >= 600) {
|
||||
throw new Error(`PDF page count (${doc.numPages}) exceeds the limit of 600 pages`)
|
||||
}
|
||||
// File size must be less than 200MB
|
||||
if (pdfBuffer.length >= 200 * 1024 * 1024) {
|
||||
const fileSizeMB = Math.round(pdfBuffer.length / (1024 * 1024))
|
||||
throw new Error(`PDF file size (${fileSizeMB}MB) exceeds the limit of 200MB`)
|
||||
// File page count must be less than 600 pages
|
||||
if (doc.numPages >= 600) {
|
||||
throw new Error(`PDF page count (${doc.numPages}) exceeds the limit of 600 pages`)
|
||||
}
|
||||
|
||||
logger.info(`PDF validation passed: ${doc.numPages} pages, ${Math.round(fileSizeBytes / (1024 * 1024))}MB`)
|
||||
} catch (error: any) {
|
||||
// 如果是页数超限错误,直接抛出
|
||||
if (error.message.includes('exceeds the limit')) {
|
||||
throw error
|
||||
}
|
||||
|
||||
// PDF 解析失败,记录详细警告但允许继续处理
|
||||
logger.warn(
|
||||
`Failed to parse PDF structure (file may be corrupted or use non-standard format). ` +
|
||||
`Skipping page count validation. Will attempt to process with MinerU API. ` +
|
||||
`Error details: ${error.message}. ` +
|
||||
`Suggestion: If processing fails, try repairing the PDF using tools like Adobe Acrobat or online PDF repair services.`
|
||||
)
|
||||
// 不抛出错误,允许继续处理
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,8 +97,8 @@ export default class OpenMineruPreprocessProvider extends BasePreprocessProvider
|
||||
// Find the main file after extraction
|
||||
let finalPath = ''
|
||||
let finalName = file.origin_name.replace('.pdf', '.md')
|
||||
// Find the corresponding folder by file name
|
||||
outputPath = path.join(outputPath, `${file.origin_name.replace('.pdf', '')}`)
|
||||
// Find the corresponding folder by file id
|
||||
outputPath = path.join(outputPath, file.id)
|
||||
try {
|
||||
const files = fs.readdirSync(outputPath)
|
||||
|
||||
@ -125,7 +150,7 @@ export default class OpenMineruPreprocessProvider extends BasePreprocessProvider
|
||||
formData.append('return_md', 'true')
|
||||
formData.append('response_format_zip', 'true')
|
||||
formData.append('files', fileBuffer, {
|
||||
filename: file.origin_name
|
||||
filename: file.name
|
||||
})
|
||||
|
||||
while (retries < maxRetries) {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { cacheService } from '@data/CacheService'
|
||||
import { loggerService } from '@logger'
|
||||
import {
|
||||
getModelSupportedVerbosity,
|
||||
isFunctionCallingModel,
|
||||
isNotSupportTemperatureAndTopP,
|
||||
isOpenAIModel,
|
||||
@ -242,12 +243,18 @@ export abstract class BaseApiClient<
|
||||
return serviceTierSetting
|
||||
}
|
||||
|
||||
protected getVerbosity(): OpenAIVerbosity {
|
||||
protected getVerbosity(model?: Model): OpenAIVerbosity {
|
||||
try {
|
||||
const state = window.store?.getState()
|
||||
const verbosity = state?.settings?.openAI?.verbosity
|
||||
|
||||
if (verbosity && ['low', 'medium', 'high'].includes(verbosity)) {
|
||||
// If model is provided, check if the verbosity is supported by the model
|
||||
if (model) {
|
||||
const supportedVerbosity = getModelSupportedVerbosity(model)
|
||||
// Use user's verbosity if supported, otherwise use the first supported option
|
||||
return supportedVerbosity.includes(verbosity) ? verbosity : supportedVerbosity[0]
|
||||
}
|
||||
return verbosity
|
||||
}
|
||||
} catch (error) {
|
||||
|
||||
@ -35,6 +35,7 @@ import {
|
||||
isSupportedThinkingTokenModel,
|
||||
isSupportedThinkingTokenQwenModel,
|
||||
isSupportedThinkingTokenZhipuModel,
|
||||
isSupportVerbosityModel,
|
||||
isVisionModel,
|
||||
MODEL_SUPPORTED_REASONING_EFFORT,
|
||||
ZHIPU_RESULT_TOKENS
|
||||
@ -733,6 +734,13 @@ export class OpenAIAPIClient extends OpenAIBaseClient<
|
||||
...modalities,
|
||||
// groq 有不同的 service tier 配置,不符合 openai 接口类型
|
||||
service_tier: this.getServiceTier(model) as OpenAIServiceTier,
|
||||
...(isSupportVerbosityModel(model)
|
||||
? {
|
||||
text: {
|
||||
verbosity: this.getVerbosity(model)
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
...this.getProviderSpecificParameters(assistant, model),
|
||||
...reasoningEffort,
|
||||
...getOpenAIWebSearchParams(model, enableWebSearch),
|
||||
|
||||
@ -48,9 +48,8 @@ export abstract class OpenAIBaseClient<
|
||||
}
|
||||
|
||||
// 仅适用于openai
|
||||
override getBaseURL(): string {
|
||||
const host = this.provider.apiHost
|
||||
return formatApiHost(host)
|
||||
override getBaseURL(isSupportedAPIVerion: boolean = true): string {
|
||||
return formatApiHost(this.provider.apiHost, isSupportedAPIVerion)
|
||||
}
|
||||
|
||||
override async generateImage({
|
||||
@ -144,6 +143,11 @@ export abstract class OpenAIBaseClient<
|
||||
}
|
||||
|
||||
let apiKeyForSdkInstance = this.apiKey
|
||||
let baseURLForSdkInstance = this.getBaseURL()
|
||||
let headersForSdkInstance = {
|
||||
...this.defaultHeaders(),
|
||||
...this.provider.extra_headers
|
||||
}
|
||||
|
||||
if (this.provider.id === 'copilot') {
|
||||
const defaultHeaders = store.getState().copilot.defaultHeaders
|
||||
@ -151,6 +155,11 @@ export abstract class OpenAIBaseClient<
|
||||
// this.provider.apiKey不允许修改
|
||||
// this.provider.apiKey = token
|
||||
apiKeyForSdkInstance = token
|
||||
baseURLForSdkInstance = this.getBaseURL(false)
|
||||
headersForSdkInstance = {
|
||||
...headersForSdkInstance,
|
||||
...COPILOT_DEFAULT_HEADERS
|
||||
}
|
||||
}
|
||||
|
||||
if (this.provider.id === 'azure-openai' || this.provider.type === 'azure-openai') {
|
||||
@ -164,12 +173,8 @@ export abstract class OpenAIBaseClient<
|
||||
this.sdkInstance = new OpenAI({
|
||||
dangerouslyAllowBrowser: true,
|
||||
apiKey: apiKeyForSdkInstance,
|
||||
baseURL: this.getBaseURL(),
|
||||
defaultHeaders: {
|
||||
...this.defaultHeaders(),
|
||||
...this.provider.extra_headers,
|
||||
...(this.provider.id === 'copilot' ? COPILOT_DEFAULT_HEADERS : {})
|
||||
}
|
||||
baseURL: baseURLForSdkInstance,
|
||||
defaultHeaders: headersForSdkInstance
|
||||
}) as TSdkInstance
|
||||
}
|
||||
return this.sdkInstance
|
||||
|
||||
@ -297,7 +297,31 @@ export class OpenAIResponseAPIClient extends OpenAIBaseClient<
|
||||
|
||||
private convertResponseToMessageContent(response: OpenAI.Responses.Response): ResponseInput {
|
||||
const content: OpenAI.Responses.ResponseInput = []
|
||||
content.push(...response.output)
|
||||
response.output.forEach((item) => {
|
||||
if (item.type !== 'apply_patch_call' && item.type !== 'apply_patch_call_output') {
|
||||
content.push(item)
|
||||
} else if (item.type === 'apply_patch_call') {
|
||||
if (item.operation !== undefined) {
|
||||
const applyPatchToolCall: OpenAI.Responses.ResponseInputItem.ApplyPatchCall = {
|
||||
...item,
|
||||
operation: item.operation
|
||||
}
|
||||
content.push(applyPatchToolCall)
|
||||
} else {
|
||||
logger.warn('Undefined tool call operation for ApplyPatchToolCall.')
|
||||
}
|
||||
} else if (item.type === 'apply_patch_call_output') {
|
||||
if (item.output !== undefined) {
|
||||
const applyPatchToolCallOutput: OpenAI.Responses.ResponseInputItem.ApplyPatchCallOutput = {
|
||||
...item,
|
||||
output: item.output === null ? undefined : item.output
|
||||
}
|
||||
content.push(applyPatchToolCallOutput)
|
||||
} else {
|
||||
logger.warn('Undefined tool call operation for ApplyPatchToolCall.')
|
||||
}
|
||||
}
|
||||
})
|
||||
return content
|
||||
}
|
||||
|
||||
@ -496,7 +520,7 @@ export class OpenAIResponseAPIClient extends OpenAIBaseClient<
|
||||
...(isSupportVerbosityModel(model)
|
||||
? {
|
||||
text: {
|
||||
verbosity: this.getVerbosity()
|
||||
verbosity: this.getVerbosity(model)
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
|
||||
13
src/renderer/src/aiCore/prepareParams/header.ts
Normal file
13
src/renderer/src/aiCore/prepareParams/header.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { isClaude45ReasoningModel } from '@renderer/config/models'
|
||||
import type { Assistant, Model } from '@renderer/types'
|
||||
import { isToolUseModeFunction } from '@renderer/utils/assistant'
|
||||
|
||||
const INTERLEAVED_THINKING_HEADER = 'interleaved-thinking-2025-05-14'
|
||||
|
||||
export function addAnthropicHeaders(assistant: Assistant, model: Model): string[] {
|
||||
const anthropicHeaders: string[] = []
|
||||
if (isClaude45ReasoningModel(model) && isToolUseModeFunction(assistant)) {
|
||||
anthropicHeaders.push(INTERLEAVED_THINKING_HEADER)
|
||||
}
|
||||
return anthropicHeaders
|
||||
}
|
||||
@ -7,10 +7,12 @@ import { anthropic } from '@ai-sdk/anthropic'
|
||||
import { google } from '@ai-sdk/google'
|
||||
import { vertexAnthropic } from '@ai-sdk/google-vertex/anthropic/edge'
|
||||
import { vertex } from '@ai-sdk/google-vertex/edge'
|
||||
import { combineHeaders } from '@ai-sdk/provider-utils'
|
||||
import type { WebSearchPluginConfig } from '@cherrystudio/ai-core/built-in/plugins'
|
||||
import { isBaseProvider } from '@cherrystudio/ai-core/core/providers/schemas'
|
||||
import { loggerService } from '@logger'
|
||||
import {
|
||||
isAnthropicModel,
|
||||
isGenerateImageModel,
|
||||
isOpenRouterBuiltInWebSearchModel,
|
||||
isReasoningModel,
|
||||
@ -19,6 +21,8 @@ import {
|
||||
isSupportedThinkingTokenModel,
|
||||
isWebSearchModel
|
||||
} from '@renderer/config/models'
|
||||
import { isAwsBedrockProvider } from '@renderer/config/providers'
|
||||
import { isVertexProvider } from '@renderer/hooks/useVertexAI'
|
||||
import { getAssistantSettings, getDefaultModel } from '@renderer/services/AssistantService'
|
||||
import store from '@renderer/store'
|
||||
import type { CherryWebSearchConfig } from '@renderer/store/websearch'
|
||||
@ -34,6 +38,7 @@ import { setupToolsConfig } from '../utils/mcp'
|
||||
import { buildProviderOptions } from '../utils/options'
|
||||
import { getAnthropicThinkingBudget } from '../utils/reasoning'
|
||||
import { buildProviderBuiltinWebSearchConfig } from '../utils/websearch'
|
||||
import { addAnthropicHeaders } from './header'
|
||||
import { supportsTopP } from './modelCapabilities'
|
||||
import { getTemperature, getTopP } from './modelParameters'
|
||||
|
||||
@ -172,13 +177,21 @@ export async function buildStreamTextParams(
|
||||
}
|
||||
}
|
||||
|
||||
let headers: Record<string, string | undefined> = options.requestOptions?.headers ?? {}
|
||||
|
||||
// https://docs.claude.com/en/docs/build-with-claude/extended-thinking#interleaved-thinking
|
||||
if (!isVertexProvider(provider) && !isAwsBedrockProvider(provider) && isAnthropicModel(model)) {
|
||||
const newBetaHeaders = { 'anthropic-beta': addAnthropicHeaders(assistant, model).join(',') }
|
||||
headers = combineHeaders(headers, newBetaHeaders)
|
||||
}
|
||||
|
||||
// 构建基础参数
|
||||
const params: StreamTextParams = {
|
||||
messages: sdkMessages,
|
||||
maxOutputTokens: maxTokens,
|
||||
temperature: getTemperature(assistant, model),
|
||||
abortSignal: options.requestOptions?.signal,
|
||||
headers: options.requestOptions?.headers,
|
||||
headers,
|
||||
providerOptions,
|
||||
stopWhen: stepCountIs(20),
|
||||
maxRetries: 0
|
||||
|
||||
@ -1,5 +1,12 @@
|
||||
import { baseProviderIdSchema, customProviderIdSchema } from '@cherrystudio/ai-core/provider'
|
||||
import { isOpenAIModel, isQwenMTModel, isSupportFlexServiceTierModel } from '@renderer/config/models'
|
||||
import { loggerService } from '@logger'
|
||||
import {
|
||||
getModelSupportedVerbosity,
|
||||
isOpenAIModel,
|
||||
isQwenMTModel,
|
||||
isSupportFlexServiceTierModel,
|
||||
isSupportVerbosityModel
|
||||
} from '@renderer/config/models'
|
||||
import { isSupportServiceTierProvider } from '@renderer/config/providers'
|
||||
import { mapLanguageToQwenMTModel } from '@renderer/config/translate'
|
||||
import type { Assistant, Model, Provider } from '@renderer/types'
|
||||
@ -26,6 +33,8 @@ import {
|
||||
} from './reasoning'
|
||||
import { getWebSearchParams } from './websearch'
|
||||
|
||||
const logger = loggerService.withContext('aiCore.utils.options')
|
||||
|
||||
// copy from BaseApiClient.ts
|
||||
const getServiceTier = (model: Model, provider: Provider) => {
|
||||
const serviceTierSetting = provider.serviceTier
|
||||
@ -70,6 +79,7 @@ export function buildProviderOptions(
|
||||
enableGenerateImage: boolean
|
||||
}
|
||||
): Record<string, any> {
|
||||
logger.debug('buildProviderOptions', { assistant, model, actualProvider, capabilities })
|
||||
const rawProviderId = getAiSdkProviderId(actualProvider)
|
||||
// 构建 provider 特定的选项
|
||||
let providerSpecificOptions: Record<string, any> = {}
|
||||
@ -187,6 +197,23 @@ function buildOpenAIProviderOptions(
|
||||
...reasoningParams
|
||||
}
|
||||
}
|
||||
|
||||
if (isSupportVerbosityModel(model)) {
|
||||
const state = window.store?.getState()
|
||||
const userVerbosity = state?.settings?.openAI?.verbosity
|
||||
|
||||
if (userVerbosity && ['low', 'medium', 'high'].includes(userVerbosity)) {
|
||||
const supportedVerbosity = getModelSupportedVerbosity(model)
|
||||
// Use user's verbosity if supported, otherwise use the first supported option
|
||||
const verbosity = supportedVerbosity.includes(userVerbosity) ? userVerbosity : supportedVerbosity[0]
|
||||
|
||||
providerOptions = {
|
||||
...providerOptions,
|
||||
textVerbosity: verbosity
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return providerOptions
|
||||
}
|
||||
|
||||
|
||||
@ -1,3 +1,7 @@
|
||||
import type { BedrockProviderOptions } from '@ai-sdk/amazon-bedrock'
|
||||
import type { AnthropicProviderOptions } from '@ai-sdk/anthropic'
|
||||
import type { GoogleGenerativeAIProviderOptions } from '@ai-sdk/google'
|
||||
import type { XaiProviderOptions } from '@ai-sdk/xai'
|
||||
import { loggerService } from '@logger'
|
||||
import { DEFAULT_MAX_TOKENS } from '@renderer/config/constant'
|
||||
import {
|
||||
@ -7,6 +11,7 @@ import {
|
||||
isDeepSeekHybridInferenceModel,
|
||||
isDoubaoSeedAfter251015,
|
||||
isDoubaoThinkingAutoModel,
|
||||
isGPT51SeriesModel,
|
||||
isGrok4FastReasoningModel,
|
||||
isGrokReasoningModel,
|
||||
isOpenAIDeepResearchModel,
|
||||
@ -56,13 +61,20 @@ export function getReasoningEffort(assistant: Assistant, model: Model): Reasonin
|
||||
}
|
||||
const reasoningEffort = assistant?.settings?.reasoning_effort
|
||||
|
||||
if (!reasoningEffort) {
|
||||
// Handle undefined and 'none' reasoningEffort.
|
||||
// TODO: They should be separated.
|
||||
if (!reasoningEffort || reasoningEffort === 'none') {
|
||||
// openrouter: use reasoning
|
||||
if (model.provider === SystemProviderIds.openrouter) {
|
||||
// Don't disable reasoning for Gemini models that support thinking tokens
|
||||
if (isSupportedThinkingTokenGeminiModel(model) && !GEMINI_FLASH_MODEL_REGEX.test(model.id)) {
|
||||
return {}
|
||||
}
|
||||
// 'none' is not an available value for effort for now.
|
||||
// I think they should resolve this issue soon, so I'll just go ahead and use this value.
|
||||
if (isGPT51SeriesModel(model) && reasoningEffort === 'none') {
|
||||
return { reasoning: { effort: 'none' } }
|
||||
}
|
||||
// Don't disable reasoning for models that require it
|
||||
if (
|
||||
isGrokReasoningModel(model) ||
|
||||
@ -117,6 +129,13 @@ export function getReasoningEffort(assistant: Assistant, model: Model): Reasonin
|
||||
return { thinking: { type: 'disabled' } }
|
||||
}
|
||||
|
||||
// Specially for GPT-5.1. Suppose this is a OpenAI Compatible provider
|
||||
if (isGPT51SeriesModel(model) && reasoningEffort === 'none') {
|
||||
return {
|
||||
reasoningEffort: 'none'
|
||||
}
|
||||
}
|
||||
|
||||
return {}
|
||||
}
|
||||
|
||||
@ -371,7 +390,7 @@ export function getOpenAIReasoningParams(assistant: Assistant, model: Model): Re
|
||||
|
||||
export function getAnthropicThinkingBudget(assistant: Assistant, model: Model): number {
|
||||
const { maxTokens, reasoning_effort: reasoningEffort } = getAssistantSettings(assistant)
|
||||
if (reasoningEffort === undefined) {
|
||||
if (reasoningEffort === undefined || reasoningEffort === 'none') {
|
||||
return 0
|
||||
}
|
||||
const effortRatio = EFFORT_RATIO[reasoningEffort]
|
||||
@ -393,14 +412,17 @@ export function getAnthropicThinkingBudget(assistant: Assistant, model: Model):
|
||||
* 获取 Anthropic 推理参数
|
||||
* 从 AnthropicAPIClient 中提取的逻辑
|
||||
*/
|
||||
export function getAnthropicReasoningParams(assistant: Assistant, model: Model): Record<string, any> {
|
||||
export function getAnthropicReasoningParams(
|
||||
assistant: Assistant,
|
||||
model: Model
|
||||
): Pick<AnthropicProviderOptions, 'thinking'> {
|
||||
if (!isReasoningModel(model)) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const reasoningEffort = assistant?.settings?.reasoning_effort
|
||||
|
||||
if (reasoningEffort === undefined) {
|
||||
if (reasoningEffort === undefined || reasoningEffort === 'none') {
|
||||
return {
|
||||
thinking: {
|
||||
type: 'disabled'
|
||||
@ -429,7 +451,10 @@ export function getAnthropicReasoningParams(assistant: Assistant, model: Model):
|
||||
* 注意:Gemini/GCP 端点所使用的 thinkingBudget 等参数应该按照驼峰命名法传递
|
||||
* 而在 Google 官方提供的 OpenAI 兼容端点中则使用蛇形命名法 thinking_budget
|
||||
*/
|
||||
export function getGeminiReasoningParams(assistant: Assistant, model: Model): Record<string, any> {
|
||||
export function getGeminiReasoningParams(
|
||||
assistant: Assistant,
|
||||
model: Model
|
||||
): Pick<GoogleGenerativeAIProviderOptions, 'thinkingConfig'> {
|
||||
if (!isReasoningModel(model)) {
|
||||
return {}
|
||||
}
|
||||
@ -438,7 +463,7 @@ export function getGeminiReasoningParams(assistant: Assistant, model: Model): Re
|
||||
|
||||
// Gemini 推理参数
|
||||
if (isSupportedThinkingTokenGeminiModel(model)) {
|
||||
if (reasoningEffort === undefined) {
|
||||
if (reasoningEffort === undefined || reasoningEffort === 'none') {
|
||||
return {
|
||||
thinkingConfig: {
|
||||
includeThoughts: false,
|
||||
@ -478,27 +503,35 @@ export function getGeminiReasoningParams(assistant: Assistant, model: Model): Re
|
||||
* @param model - The model being used
|
||||
* @returns XAI-specific reasoning parameters
|
||||
*/
|
||||
export function getXAIReasoningParams(assistant: Assistant, model: Model): Record<string, any> {
|
||||
export function getXAIReasoningParams(assistant: Assistant, model: Model): Pick<XaiProviderOptions, 'reasoningEffort'> {
|
||||
if (!isSupportedReasoningEffortGrokModel(model)) {
|
||||
return {}
|
||||
}
|
||||
|
||||
const { reasoning_effort: reasoningEffort } = getAssistantSettings(assistant)
|
||||
|
||||
if (!reasoningEffort) {
|
||||
if (!reasoningEffort || reasoningEffort === 'none') {
|
||||
return {}
|
||||
}
|
||||
|
||||
// For XAI provider Grok models, use reasoningEffort parameter directly
|
||||
return {
|
||||
reasoningEffort
|
||||
switch (reasoningEffort) {
|
||||
case 'auto':
|
||||
case 'minimal':
|
||||
case 'medium':
|
||||
return { reasoningEffort: 'low' }
|
||||
case 'low':
|
||||
case 'high':
|
||||
return { reasoningEffort }
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Bedrock reasoning parameters
|
||||
*/
|
||||
export function getBedrockReasoningParams(assistant: Assistant, model: Model): Record<string, any> {
|
||||
export function getBedrockReasoningParams(
|
||||
assistant: Assistant,
|
||||
model: Model
|
||||
): Pick<BedrockProviderOptions, 'reasoningConfig'> {
|
||||
if (!isReasoningModel(model)) {
|
||||
return {}
|
||||
}
|
||||
@ -509,6 +542,14 @@ export function getBedrockReasoningParams(assistant: Assistant, model: Model): R
|
||||
return {}
|
||||
}
|
||||
|
||||
if (reasoningEffort === 'none') {
|
||||
return {
|
||||
reasoningConfig: {
|
||||
type: 'disabled'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only apply thinking budget for Claude reasoning models
|
||||
if (!isSupportedThinkingTokenClaudeModel(model)) {
|
||||
return {}
|
||||
|
||||
BIN
src/renderer/src/assets/images/models/gpt-5.1-chat.png
Normal file
BIN
src/renderer/src/assets/images/models/gpt-5.1-chat.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
BIN
src/renderer/src/assets/images/models/gpt-5.1-codex-mini.png
Normal file
BIN
src/renderer/src/assets/images/models/gpt-5.1-codex-mini.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 21 KiB |
BIN
src/renderer/src/assets/images/models/gpt-5.1-codex.png
Normal file
BIN
src/renderer/src/assets/images/models/gpt-5.1-codex.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
BIN
src/renderer/src/assets/images/models/gpt-5.1.png
Normal file
BIN
src/renderer/src/assets/images/models/gpt-5.1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
@ -59,6 +59,10 @@ import {
|
||||
} from '@renderer/assets/images/models/gpt_dark.png'
|
||||
import ChatGPTImageModelLogo from '@renderer/assets/images/models/gpt_image_1.png'
|
||||
import ChatGPTo1ModelLogo from '@renderer/assets/images/models/gpt_o1.png'
|
||||
import GPT51ModelLogo from '@renderer/assets/images/models/gpt-5.1.png'
|
||||
import GPT51ChatModelLogo from '@renderer/assets/images/models/gpt-5.1-chat.png'
|
||||
import GPT51CodexModelLogo from '@renderer/assets/images/models/gpt-5.1-codex.png'
|
||||
import GPT51CodexMiniModelLogo from '@renderer/assets/images/models/gpt-5.1-codex-mini.png'
|
||||
import GPT5ModelLogo from '@renderer/assets/images/models/gpt-5.png'
|
||||
import GPT5ChatModelLogo from '@renderer/assets/images/models/gpt-5-chat.png'
|
||||
import GPT5CodexModelLogo from '@renderer/assets/images/models/gpt-5-codex.png'
|
||||
@ -182,6 +186,10 @@ export function getModelLogoById(modelId: string): string | undefined {
|
||||
'gpt-5-nano': GPT5NanoModelLogo,
|
||||
'gpt-5-chat': GPT5ChatModelLogo,
|
||||
'gpt-5-codex': GPT5CodexModelLogo,
|
||||
'gpt-5.1-codex-mini': GPT51CodexMiniModelLogo,
|
||||
'gpt-5.1-codex': GPT51CodexModelLogo,
|
||||
'gpt-5.1-chat': GPT51ChatModelLogo,
|
||||
'gpt-5.1': GPT51ModelLogo,
|
||||
'gpt-5': GPT5ModelLogo,
|
||||
gpts: isLight ? ChatGPT4ModelLogo : ChatGPT4ModelLogoDark,
|
||||
'gpt-oss(?:-[\\w-]+)': isLight ? ChatGptModelLogo : ChatGptModelLogoDark,
|
||||
|
||||
@ -8,7 +8,7 @@ import type {
|
||||
import { getLowerBaseModelName, isUserSelectedModelType } from '@renderer/utils'
|
||||
|
||||
import { isEmbeddingModel, isRerankModel } from './embedding'
|
||||
import { isGPT5SeriesModel } from './utils'
|
||||
import { isGPT5ProModel, isGPT5SeriesModel, isGPT51SeriesModel } from './utils'
|
||||
import { isTextToImageModel } from './vision'
|
||||
import { GEMINI_FLASH_MODEL_REGEX, isOpenAIDeepResearchModel } from './websearch'
|
||||
|
||||
@ -24,6 +24,9 @@ export const MODEL_SUPPORTED_REASONING_EFFORT: ReasoningEffortConfig = {
|
||||
openai_deep_research: ['medium'] as const,
|
||||
gpt5: ['minimal', 'low', 'medium', 'high'] as const,
|
||||
gpt5_codex: ['low', 'medium', 'high'] as const,
|
||||
gpt5_1: ['none', 'low', 'medium', 'high'] as const,
|
||||
gpt5_1_codex: ['none', 'medium', 'high'] as const,
|
||||
gpt5pro: ['high'] as const,
|
||||
grok: ['low', 'high'] as const,
|
||||
grok4_fast: ['auto'] as const,
|
||||
gemini: ['low', 'medium', 'high', 'auto'] as const,
|
||||
@ -41,24 +44,27 @@ export const MODEL_SUPPORTED_REASONING_EFFORT: ReasoningEffortConfig = {
|
||||
|
||||
// 模型类型到支持选项的映射表
|
||||
export const MODEL_SUPPORTED_OPTIONS: ThinkingOptionConfig = {
|
||||
default: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.default] as const,
|
||||
default: ['none', ...MODEL_SUPPORTED_REASONING_EFFORT.default] as const,
|
||||
o: MODEL_SUPPORTED_REASONING_EFFORT.o,
|
||||
openai_deep_research: MODEL_SUPPORTED_REASONING_EFFORT.openai_deep_research,
|
||||
gpt5: [...MODEL_SUPPORTED_REASONING_EFFORT.gpt5] as const,
|
||||
gpt5pro: MODEL_SUPPORTED_REASONING_EFFORT.gpt5pro,
|
||||
gpt5_codex: MODEL_SUPPORTED_REASONING_EFFORT.gpt5_codex,
|
||||
gpt5_1: MODEL_SUPPORTED_REASONING_EFFORT.gpt5_1,
|
||||
gpt5_1_codex: MODEL_SUPPORTED_REASONING_EFFORT.gpt5_1_codex,
|
||||
grok: MODEL_SUPPORTED_REASONING_EFFORT.grok,
|
||||
grok4_fast: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.grok4_fast] as const,
|
||||
gemini: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.gemini] as const,
|
||||
grok4_fast: ['none', ...MODEL_SUPPORTED_REASONING_EFFORT.grok4_fast] as const,
|
||||
gemini: ['none', ...MODEL_SUPPORTED_REASONING_EFFORT.gemini] as const,
|
||||
gemini_pro: MODEL_SUPPORTED_REASONING_EFFORT.gemini_pro,
|
||||
qwen: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.qwen] as const,
|
||||
qwen: ['none', ...MODEL_SUPPORTED_REASONING_EFFORT.qwen] as const,
|
||||
qwen_thinking: MODEL_SUPPORTED_REASONING_EFFORT.qwen_thinking,
|
||||
doubao: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao] as const,
|
||||
doubao_no_auto: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao_no_auto] as const,
|
||||
doubao: ['none', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao] as const,
|
||||
doubao_no_auto: ['none', ...MODEL_SUPPORTED_REASONING_EFFORT.doubao_no_auto] as const,
|
||||
doubao_after_251015: MODEL_SUPPORTED_REASONING_EFFORT.doubao_after_251015,
|
||||
hunyuan: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.hunyuan] as const,
|
||||
zhipu: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.zhipu] as const,
|
||||
hunyuan: ['none', ...MODEL_SUPPORTED_REASONING_EFFORT.hunyuan] as const,
|
||||
zhipu: ['none', ...MODEL_SUPPORTED_REASONING_EFFORT.zhipu] as const,
|
||||
perplexity: MODEL_SUPPORTED_REASONING_EFFORT.perplexity,
|
||||
deepseek_hybrid: ['off', ...MODEL_SUPPORTED_REASONING_EFFORT.deepseek_hybrid] as const
|
||||
deepseek_hybrid: ['none', ...MODEL_SUPPORTED_REASONING_EFFORT.deepseek_hybrid] as const
|
||||
} as const
|
||||
|
||||
const withModelIdAndNameAsId = <T>(model: Model, fn: (model: Model) => T): { idResult: T; nameResult: T } => {
|
||||
@ -75,11 +81,20 @@ const _getThinkModelType = (model: Model): ThinkingModelType => {
|
||||
if (isOpenAIDeepResearchModel(model)) {
|
||||
return 'openai_deep_research'
|
||||
}
|
||||
if (isGPT5SeriesModel(model)) {
|
||||
if (isGPT51SeriesModel(model)) {
|
||||
if (modelId.includes('codex')) {
|
||||
thinkingModelType = 'gpt5_1_codex'
|
||||
} else {
|
||||
thinkingModelType = 'gpt5_1'
|
||||
}
|
||||
} else if (isGPT5SeriesModel(model)) {
|
||||
if (modelId.includes('codex')) {
|
||||
thinkingModelType = 'gpt5_codex'
|
||||
} else {
|
||||
thinkingModelType = 'gpt5'
|
||||
if (isGPT5ProModel(model)) {
|
||||
thinkingModelType = 'gpt5pro'
|
||||
}
|
||||
}
|
||||
} else if (isSupportedReasoningEffortOpenAIModel(model)) {
|
||||
thinkingModelType = 'o'
|
||||
@ -526,7 +541,7 @@ export function isSupportedReasoningEffortOpenAIModel(model: Model): boolean {
|
||||
modelId.includes('o3') ||
|
||||
modelId.includes('o4') ||
|
||||
modelId.includes('gpt-oss') ||
|
||||
(isGPT5SeriesModel(model) && !modelId.includes('chat'))
|
||||
((isGPT5SeriesModel(model) || isGPT51SeriesModel(model)) && !modelId.includes('chat'))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ export function isSupportedFlexServiceTier(model: Model): boolean {
|
||||
|
||||
export function isSupportVerbosityModel(model: Model): boolean {
|
||||
const modelId = getLowerBaseModelName(model.id)
|
||||
return isGPT5SeriesModel(model) && !modelId.includes('chat')
|
||||
return (isGPT5SeriesModel(model) || isGPT51SeriesModel(model)) && !modelId.includes('chat')
|
||||
}
|
||||
|
||||
export function isOpenAIChatCompletionOnlyModel(model: Model): boolean {
|
||||
@ -227,12 +227,32 @@ export const isNotSupportSystemMessageModel = (model: Model): boolean => {
|
||||
|
||||
export const isGPT5SeriesModel = (model: Model) => {
|
||||
const modelId = getLowerBaseModelName(model.id)
|
||||
return modelId.includes('gpt-5')
|
||||
return modelId.includes('gpt-5') && !modelId.includes('gpt-5.1')
|
||||
}
|
||||
|
||||
export const isGPT5SeriesReasoningModel = (model: Model) => {
|
||||
const modelId = getLowerBaseModelName(model.id)
|
||||
return modelId.includes('gpt-5') && !modelId.includes('chat')
|
||||
return isGPT5SeriesModel(model) && !modelId.includes('chat')
|
||||
}
|
||||
|
||||
export const isGPT51SeriesModel = (model: Model) => {
|
||||
const modelId = getLowerBaseModelName(model.id)
|
||||
return modelId.includes('gpt-5.1')
|
||||
}
|
||||
|
||||
// GPT-5 verbosity configuration
|
||||
// gpt-5-pro only supports 'high', other GPT-5 models support all levels
|
||||
export const MODEL_SUPPORTED_VERBOSITY: Record<string, ('low' | 'medium' | 'high')[]> = {
|
||||
'gpt-5-pro': ['high'],
|
||||
default: ['low', 'medium', 'high']
|
||||
}
|
||||
|
||||
export const getModelSupportedVerbosity = (model: Model): ('low' | 'medium' | 'high')[] => {
|
||||
const modelId = getLowerBaseModelName(model.id)
|
||||
if (modelId.includes('gpt-5-pro')) {
|
||||
return MODEL_SUPPORTED_VERBOSITY['gpt-5-pro']
|
||||
}
|
||||
return MODEL_SUPPORTED_VERBOSITY.default
|
||||
}
|
||||
|
||||
export const isGeminiModel = (model: Model) => {
|
||||
@ -251,3 +271,8 @@ export const ZHIPU_RESULT_TOKENS = ['<|begin_of_box|>', '<|end_of_box|>'] as con
|
||||
export const agentModelFilter = (model: Model): boolean => {
|
||||
return !isEmbeddingModel(model) && !isRerankModel(model) && !isTextToImageModel(model)
|
||||
}
|
||||
|
||||
export const isGPT5ProModel = (model: Model) => {
|
||||
const modelId = getLowerBaseModelName(model.id)
|
||||
return modelId.includes('gpt-5-pro')
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ export function isWebSearchModel(model: Model): boolean {
|
||||
// bedrock和vertex不支持
|
||||
if (
|
||||
isAnthropicModel(model) &&
|
||||
(provider.id === SystemProviderIds['aws-bedrock'] || provider.id === SystemProviderIds.vertexai)
|
||||
!(provider.id === SystemProviderIds['aws-bedrock'] || provider.id === SystemProviderIds.vertexai)
|
||||
) {
|
||||
return CLAUDE_SUPPORTED_WEBSEARCH_REGEX.test(modelId)
|
||||
}
|
||||
|
||||
@ -67,7 +67,7 @@ import type {
|
||||
SystemProvider,
|
||||
SystemProviderId
|
||||
} from '@renderer/types'
|
||||
import { isSystemProvider, OpenAIServiceTiers } from '@renderer/types'
|
||||
import { isSystemProvider, OpenAIServiceTiers, SystemProviderIds } from '@renderer/types'
|
||||
|
||||
import { TOKENFLUX_HOST } from './constant'
|
||||
import { glm45FlashModel, qwen38bModel, SYSTEM_MODELS } from './models'
|
||||
@ -1519,7 +1519,10 @@ const SUPPORT_URL_CONTEXT_PROVIDER_TYPES = [
|
||||
] as const satisfies ProviderType[]
|
||||
|
||||
export const isSupportUrlContextProvider = (provider: Provider) => {
|
||||
return SUPPORT_URL_CONTEXT_PROVIDER_TYPES.some((type) => type === provider.type)
|
||||
return (
|
||||
SUPPORT_URL_CONTEXT_PROVIDER_TYPES.some((type) => type === provider.type) ||
|
||||
provider.id === SystemProviderIds.cherryin
|
||||
)
|
||||
}
|
||||
|
||||
const SUPPORT_GEMINI_NATIVE_WEB_SEARCH_PROVIDERS = ['gemini', 'vertexai'] as const satisfies SystemProviderId[]
|
||||
@ -1570,6 +1573,10 @@ export function isAIGatewayProvider(provider: Provider): boolean {
|
||||
return provider.type === 'ai-gateway'
|
||||
}
|
||||
|
||||
export function isAwsBedrockProvider(provider: Provider): boolean {
|
||||
return provider.type === 'aws-bedrock'
|
||||
}
|
||||
|
||||
const NOT_SUPPORT_API_VERSION_PROVIDERS = ['github', 'copilot', 'perplexity'] as const satisfies SystemProviderId[]
|
||||
|
||||
export const isSupportAPIVersionProvider = (provider: Provider) => {
|
||||
|
||||
@ -123,9 +123,9 @@ export function useAssistant(id: string) {
|
||||
}
|
||||
|
||||
updateAssistantSettings({
|
||||
reasoning_effort: fallbackOption === 'off' ? undefined : fallbackOption,
|
||||
reasoning_effort_cache: fallbackOption === 'off' ? undefined : fallbackOption,
|
||||
qwenThinkMode: fallbackOption === 'off' ? undefined : true
|
||||
reasoning_effort: fallbackOption === 'none' ? undefined : fallbackOption,
|
||||
reasoning_effort_cache: fallbackOption === 'none' ? undefined : fallbackOption,
|
||||
qwenThinkMode: fallbackOption === 'none' ? undefined : true
|
||||
})
|
||||
} else {
|
||||
// 对于支持的选项, 不再更新 cache.
|
||||
|
||||
@ -311,7 +311,7 @@ export const getHttpMessageLabel = (key: string): string => {
|
||||
}
|
||||
|
||||
const reasoningEffortOptionsKeyMap: Record<ThinkingOption, string> = {
|
||||
off: 'assistants.settings.reasoning_effort.off',
|
||||
none: 'assistants.settings.reasoning_effort.off',
|
||||
minimal: 'assistants.settings.reasoning_effort.minimal',
|
||||
high: 'assistants.settings.reasoning_effort.high',
|
||||
low: 'assistants.settings.reasoning_effort.low',
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
"null_id": "Agent ID is null."
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"placeholder": "Enter your message here, send with {{key}} - @ select path, / select command"
|
||||
},
|
||||
"list": {
|
||||
"error": {
|
||||
"failed": "Failed to list agents."
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
"null_id": "智能体 ID 为空。"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"placeholder": "在这里输入消息,按 {{key}} 发送 - @ 选择路径, / 选择命令"
|
||||
},
|
||||
"list": {
|
||||
"error": {
|
||||
"failed": "获取智能体列表失败"
|
||||
@ -4478,7 +4481,7 @@
|
||||
"confirm": "确认",
|
||||
"forward": "前进",
|
||||
"multiple": "多选",
|
||||
"noResult": "[to be translated]:No results found",
|
||||
"noResult": "未找到结果",
|
||||
"page": "翻页",
|
||||
"select": "选择",
|
||||
"title": "快捷菜单"
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
"null_id": "代理程式 ID 為空。"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"placeholder": "[to be translated]:Enter your message here, send with {{key}} - @ select path, / select command"
|
||||
},
|
||||
"list": {
|
||||
"error": {
|
||||
"failed": "無法列出代理程式。"
|
||||
@ -4478,7 +4481,7 @@
|
||||
"confirm": "確認",
|
||||
"forward": "前進",
|
||||
"multiple": "多選",
|
||||
"noResult": "[to be translated]:No results found",
|
||||
"noResult": "未找到結果",
|
||||
"page": "翻頁",
|
||||
"select": "選擇",
|
||||
"title": "快捷選單"
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
"null_id": "Agent ID ist leer."
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"placeholder": "[to be translated]:Enter your message here, send with {{key}} - @ select path, / select command"
|
||||
},
|
||||
"list": {
|
||||
"error": {
|
||||
"failed": "Agent-Liste abrufen fehlgeschlagen"
|
||||
@ -4478,7 +4481,7 @@
|
||||
"confirm": "Bestätigen",
|
||||
"forward": "Vorwärts",
|
||||
"multiple": "Mehrfachauswahl",
|
||||
"noResult": "[to be translated]:No results found",
|
||||
"noResult": "Keine Ergebnisse gefunden",
|
||||
"page": "Seite umblättern",
|
||||
"select": "Auswählen",
|
||||
"title": "Schnellmenü"
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
"null_id": "Το ID του πράκτορα είναι null."
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"placeholder": "[to be translated]:Enter your message here, send with {{key}} - @ select path, / select command"
|
||||
},
|
||||
"list": {
|
||||
"error": {
|
||||
"failed": "Αποτυχία καταχώρησης πρακτόρων."
|
||||
@ -4478,7 +4481,7 @@
|
||||
"confirm": "Επιβεβαίωση",
|
||||
"forward": "Μπρος",
|
||||
"multiple": "Πολλαπλή επιλογή",
|
||||
"noResult": "[to be translated]:No results found",
|
||||
"noResult": "Δεν βρέθηκαν αποτελέσματα",
|
||||
"page": "Σελίδα",
|
||||
"select": "Επιλογή",
|
||||
"title": "Γρήγορη Πρόσβαση"
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
"null_id": "El ID del agente es nulo."
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"placeholder": "[to be translated]:Enter your message here, send with {{key}} - @ select path, / select command"
|
||||
},
|
||||
"list": {
|
||||
"error": {
|
||||
"failed": "Error al listar agentes."
|
||||
@ -4478,7 +4481,7 @@
|
||||
"confirm": "Confirmar",
|
||||
"forward": "Adelante",
|
||||
"multiple": "Selección múltiple",
|
||||
"noResult": "[to be translated]:No results found",
|
||||
"noResult": "No se encontraron resultados",
|
||||
"page": "Página",
|
||||
"select": "Seleccionar",
|
||||
"title": "Menú de acceso rápido"
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
"null_id": "L'ID de l'agent est nul."
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"placeholder": "[to be translated]:Enter your message here, send with {{key}} - @ select path, / select command"
|
||||
},
|
||||
"list": {
|
||||
"error": {
|
||||
"failed": "Échec de la liste des agents."
|
||||
@ -4478,7 +4481,7 @@
|
||||
"confirm": "Подтвердить",
|
||||
"forward": "Вперед",
|
||||
"multiple": "Множественный выбор",
|
||||
"noResult": "[to be translated]:No results found",
|
||||
"noResult": "Aucun résultat trouvé",
|
||||
"page": "Перелистнуть страницу",
|
||||
"select": "Выбрать",
|
||||
"title": "Быстрое меню"
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
"null_id": "エージェント ID が null です。"
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"placeholder": "[to be translated]:Enter your message here, send with {{key}} - @ select path, / select command"
|
||||
},
|
||||
"list": {
|
||||
"error": {
|
||||
"failed": "エージェントの一覧取得に失敗しました。"
|
||||
@ -4478,7 +4481,7 @@
|
||||
"confirm": "確認",
|
||||
"forward": "進む",
|
||||
"multiple": "複数選択",
|
||||
"noResult": "[to be translated]:No results found",
|
||||
"noResult": "結果が見つかりません",
|
||||
"page": "ページ",
|
||||
"select": "選択",
|
||||
"title": "クイックメニュー"
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
"null_id": "O ID do agente é nulo."
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"placeholder": "[to be translated]:Enter your message here, send with {{key}} - @ select path, / select command"
|
||||
},
|
||||
"list": {
|
||||
"error": {
|
||||
"failed": "Falha ao listar agentes."
|
||||
@ -4478,7 +4481,7 @@
|
||||
"confirm": "Confirmar",
|
||||
"forward": "Avançar",
|
||||
"multiple": "Múltipla Seleção",
|
||||
"noResult": "[to be translated]:No results found",
|
||||
"noResult": "Nenhum resultado encontrado",
|
||||
"page": "Página",
|
||||
"select": "Selecionar",
|
||||
"title": "Menu de Atalho"
|
||||
|
||||
@ -27,6 +27,9 @@
|
||||
"null_id": "ID агента равен null."
|
||||
}
|
||||
},
|
||||
"input": {
|
||||
"placeholder": "[to be translated]:Enter your message here, send with {{key}} - @ select path, / select command"
|
||||
},
|
||||
"list": {
|
||||
"error": {
|
||||
"failed": "Не удалось получить список агентов."
|
||||
@ -4478,7 +4481,7 @@
|
||||
"confirm": "Подтвердить",
|
||||
"forward": "Вперед",
|
||||
"multiple": "Множественный выбор",
|
||||
"noResult": "[to be translated]:No results found",
|
||||
"noResult": "Результаты не найдены",
|
||||
"page": "Страница",
|
||||
"select": "Выбрать",
|
||||
"title": "Быстрое меню"
|
||||
|
||||
@ -470,7 +470,7 @@ const AgentSessionInputbarInner: FC<InnerProps> = ({ assistant, agentId, session
|
||||
)
|
||||
const placeholderText = useMemo(
|
||||
() =>
|
||||
t('chat.input.placeholder', {
|
||||
t('agent.input.placeholder', {
|
||||
key: getSendMessageShortcutLabel(sendMessageShortcut)
|
||||
}),
|
||||
[sendMessageShortcut, t]
|
||||
|
||||
@ -36,7 +36,7 @@ const ThinkingButton: FC<Props> = ({ quickPanel, model, assistantId }): ReactEle
|
||||
const { assistant, updateAssistantSettings } = useAssistant(assistantId)
|
||||
|
||||
const currentReasoningEffort = useMemo(() => {
|
||||
return assistant.settings?.reasoning_effort || 'off'
|
||||
return assistant.settings?.reasoning_effort || 'none'
|
||||
}, [assistant.settings?.reasoning_effort])
|
||||
|
||||
// 确定当前模型支持的选项类型
|
||||
@ -46,21 +46,21 @@ const ThinkingButton: FC<Props> = ({ quickPanel, model, assistantId }): ReactEle
|
||||
const supportedOptions: ThinkingOption[] = useMemo(() => {
|
||||
if (modelType === 'doubao') {
|
||||
if (isDoubaoThinkingAutoModel(model)) {
|
||||
return ['off', 'auto', 'high']
|
||||
return ['none', 'auto', 'high']
|
||||
}
|
||||
return ['off', 'high']
|
||||
return ['none', 'high']
|
||||
}
|
||||
return MODEL_SUPPORTED_OPTIONS[modelType]
|
||||
}, [model, modelType])
|
||||
|
||||
const onThinkingChange = useCallback(
|
||||
(option?: ThinkingOption) => {
|
||||
const isEnabled = option !== undefined && option !== 'off'
|
||||
const isEnabled = option !== undefined && option !== 'none'
|
||||
// 然后更新设置
|
||||
if (!isEnabled) {
|
||||
updateAssistantSettings({
|
||||
reasoning_effort: undefined,
|
||||
reasoning_effort_cache: undefined,
|
||||
reasoning_effort: option,
|
||||
reasoning_effort_cache: option,
|
||||
qwenThinkMode: false
|
||||
})
|
||||
return
|
||||
@ -96,10 +96,10 @@ const ThinkingButton: FC<Props> = ({ quickPanel, model, assistantId }): ReactEle
|
||||
}))
|
||||
}, [currentReasoningEffort, supportedOptions, onThinkingChange])
|
||||
|
||||
const isThinkingEnabled = currentReasoningEffort !== undefined && currentReasoningEffort !== 'off'
|
||||
const isThinkingEnabled = currentReasoningEffort !== undefined && currentReasoningEffort !== 'none'
|
||||
|
||||
const disableThinking = useCallback(() => {
|
||||
onThinkingChange('off')
|
||||
onThinkingChange('none')
|
||||
}, [onThinkingChange])
|
||||
|
||||
const openQuickPanel = useCallback(() => {
|
||||
@ -116,7 +116,7 @@ const ThinkingButton: FC<Props> = ({ quickPanel, model, assistantId }): ReactEle
|
||||
return
|
||||
}
|
||||
|
||||
if (isThinkingEnabled && supportedOptions.includes('off')) {
|
||||
if (isThinkingEnabled && supportedOptions.includes('none')) {
|
||||
disableThinking()
|
||||
return
|
||||
}
|
||||
@ -144,15 +144,16 @@ const ThinkingButton: FC<Props> = ({ quickPanel, model, assistantId }): ReactEle
|
||||
|
||||
return (
|
||||
<Tooltip
|
||||
content={
|
||||
isThinkingEnabled && supportedOptions.includes('off')
|
||||
placement="top"
|
||||
title={
|
||||
isThinkingEnabled && supportedOptions.includes('none')
|
||||
? t('common.close')
|
||||
: t('assistants.settings.reasoning_effort.label')
|
||||
}
|
||||
closeDelay={0}>
|
||||
<ActionIconButton
|
||||
onClick={handleOpenQuickPanel}
|
||||
active={currentReasoningEffort !== 'off'}
|
||||
active={currentReasoningEffort !== 'none'}
|
||||
icon={ThinkingIcon(currentReasoningEffort)}
|
||||
/>
|
||||
</Tooltip>
|
||||
@ -178,7 +179,7 @@ const ThinkingIcon = (option?: ThinkingOption) => {
|
||||
case 'auto':
|
||||
IconComponent = MdiLightbulbAutoOutline
|
||||
break
|
||||
case 'off':
|
||||
case 'none':
|
||||
IconComponent = MdiLightbulbOffOutline
|
||||
break
|
||||
default:
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { isGeminiModel } from '@renderer/config/models'
|
||||
import { isAnthropicModel, isGeminiModel } from '@renderer/config/models'
|
||||
import { isSupportUrlContextProvider } from '@renderer/config/providers'
|
||||
import { defineTool, registerTool, TopicType } from '@renderer/pages/home/Inputbar/types'
|
||||
import { getProviderByModel } from '@renderer/services/AssistantService'
|
||||
@ -10,9 +10,8 @@ const urlContextTool = defineTool({
|
||||
label: (t) => t('chat.input.url_context'),
|
||||
visibleInScopes: [TopicType.Chat],
|
||||
condition: ({ model }) => {
|
||||
if (!isGeminiModel(model)) return false
|
||||
const provider = getProviderByModel(model)
|
||||
return !!provider && isSupportUrlContextProvider(provider)
|
||||
return !!provider && isSupportUrlContextProvider(provider) && (isGeminiModel(model) || isAnthropicModel(model))
|
||||
},
|
||||
render: ({ assistant }) => <UrlContextButton assistantId={assistant.id} />
|
||||
})
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
import { cn } from '@renderer/utils'
|
||||
import type { CollapseProps } from 'antd'
|
||||
import { Card } from 'antd'
|
||||
import { CheckCircle, Circle, Clock, ListTodo } from 'lucide-react'
|
||||
@ -11,23 +10,27 @@ const getStatusConfig = (status: TodoItem['status']) => {
|
||||
switch (status) {
|
||||
case 'completed':
|
||||
return {
|
||||
color: 'success' as const,
|
||||
icon: <CheckCircle className="h-3 w-3" />
|
||||
color: 'var(--color-status-success)',
|
||||
opacity: 0.6,
|
||||
icon: <CheckCircle className="h-4 w-4" strokeWidth={2.5} />
|
||||
}
|
||||
case 'in_progress':
|
||||
return {
|
||||
color: 'primary' as const,
|
||||
icon: <Clock className="h-3 w-3" />
|
||||
color: 'var(--color-primary)',
|
||||
opacity: 0.9,
|
||||
icon: <Clock className="h-4 w-4" strokeWidth={2.5} />
|
||||
}
|
||||
case 'pending':
|
||||
return {
|
||||
color: 'default' as const,
|
||||
icon: <Circle className="h-3 w-3" />
|
||||
color: 'var(--color-border)',
|
||||
opacity: 0.4,
|
||||
icon: <Circle className="h-4 w-4" strokeWidth={2.5} />
|
||||
}
|
||||
default:
|
||||
return {
|
||||
color: 'default' as const,
|
||||
icon: <Circle className="h-3 w-3" />
|
||||
color: 'var(--color-border)',
|
||||
opacity: 0.4,
|
||||
icon: <Circle className="h-4 w-4" strokeWidth={2.5} />
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -64,10 +67,8 @@ export function TodoWriteTool({
|
||||
<div className="p-2">
|
||||
<div className="flex items-center justify-center gap-3">
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center justify-center rounded-full border bg-opacity-50 p-2',
|
||||
`bg-${statusConfig.color}`
|
||||
)}>
|
||||
className="flex items-center justify-center rounded-full border p-1"
|
||||
style={{ backgroundColor: statusConfig.color, opacity: statusConfig.opacity }}>
|
||||
{statusConfig.icon}
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { HelpTooltip } from '@cherrystudio/ui'
|
||||
import Selector from '@renderer/components/Selector'
|
||||
import {
|
||||
getModelSupportedVerbosity,
|
||||
isSupportedReasoningEffortOpenAIModel,
|
||||
isSupportFlexServiceTierModel,
|
||||
isSupportVerbosityModel
|
||||
@ -79,20 +80,24 @@ const OpenAISettingsGroup: FC<Props> = ({ model, providerId, SettingGroup, Setti
|
||||
}
|
||||
]
|
||||
|
||||
const verbosityOptions = [
|
||||
{
|
||||
value: 'low',
|
||||
label: t('settings.openai.verbosity.low')
|
||||
},
|
||||
{
|
||||
value: 'medium',
|
||||
label: t('settings.openai.verbosity.medium')
|
||||
},
|
||||
{
|
||||
value: 'high',
|
||||
label: t('settings.openai.verbosity.high')
|
||||
}
|
||||
]
|
||||
const verbosityOptions = useMemo(() => {
|
||||
const allOptions = [
|
||||
{
|
||||
value: 'low',
|
||||
label: t('settings.openai.verbosity.low')
|
||||
},
|
||||
{
|
||||
value: 'medium',
|
||||
label: t('settings.openai.verbosity.medium')
|
||||
},
|
||||
{
|
||||
value: 'high',
|
||||
label: t('settings.openai.verbosity.high')
|
||||
}
|
||||
]
|
||||
const supportedVerbosityLevels = getModelSupportedVerbosity(model)
|
||||
return allOptions.filter((option) => supportedVerbosityLevels.includes(option.value as any))
|
||||
}, [model, t])
|
||||
|
||||
const serviceTierOptions = useMemo(() => {
|
||||
let baseOptions: { value: ServiceTier; label: string }[]
|
||||
@ -154,6 +159,15 @@ const OpenAISettingsGroup: FC<Props> = ({ model, providerId, SettingGroup, Setti
|
||||
}
|
||||
}, [provider.id, serviceTierMode, serviceTierOptions, setServiceTierMode])
|
||||
|
||||
useEffect(() => {
|
||||
if (verbosity && !verbosityOptions.some((option) => option.value === verbosity)) {
|
||||
const supportedVerbosityLevels = getModelSupportedVerbosity(model)
|
||||
// Default to the highest supported verbosity level
|
||||
const defaultVerbosity = supportedVerbosityLevels[supportedVerbosityLevels.length - 1]
|
||||
setVerbosity(defaultVerbosity)
|
||||
}
|
||||
}, [model, verbosity, verbosityOptions, setVerbosity])
|
||||
|
||||
if (!isOpenAIReasoning && !isSupportServiceTier && !isSupportVerbosity) {
|
||||
return null
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ const persistedReducer = persistReducer(
|
||||
{
|
||||
key: 'cherry-studio',
|
||||
storage,
|
||||
version: 174,
|
||||
version: 175,
|
||||
blacklist: ['runtime', 'messages', 'messageBlocks', 'tabs', 'toolPermissions'],
|
||||
migrate
|
||||
},
|
||||
|
||||
@ -2824,6 +2824,25 @@ const migrateConfig = {
|
||||
logger.error('migrate 174 error', error as Error)
|
||||
return state
|
||||
}
|
||||
},
|
||||
'175': (state: RootState) => {
|
||||
try {
|
||||
state.assistants.assistants.forEach((assistant) => {
|
||||
// @ts-expect-error removed type 'off'
|
||||
if (assistant.settings?.reasoning_effort === 'off') {
|
||||
assistant.settings.reasoning_effort = 'none'
|
||||
}
|
||||
// @ts-expect-error removed type 'off'
|
||||
if (assistant.settings?.reasoning_effort_cache === 'off') {
|
||||
assistant.settings.reasoning_effort_cache = 'none'
|
||||
}
|
||||
})
|
||||
logger.info('migrate 175 success')
|
||||
return state
|
||||
} catch (error) {
|
||||
logger.error('migrate 175 error', error as Error)
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -83,7 +83,10 @@ const ThinkModelTypes = [
|
||||
'o',
|
||||
'openai_deep_research',
|
||||
'gpt5',
|
||||
'gpt5_1',
|
||||
'gpt5_codex',
|
||||
'gpt5_1_codex',
|
||||
'gpt5pro',
|
||||
'grok',
|
||||
'grok4_fast',
|
||||
'gemini',
|
||||
@ -100,7 +103,7 @@ const ThinkModelTypes = [
|
||||
] as const
|
||||
|
||||
export type ReasoningEffortOption = NonNullable<OpenAI.ReasoningEffort> | 'auto'
|
||||
export type ThinkingOption = ReasoningEffortOption | 'off'
|
||||
export type ThinkingOption = ReasoningEffortOption
|
||||
export type ThinkingModelType = (typeof ThinkModelTypes)[number]
|
||||
export type ThinkingOptionConfig = Record<ThinkingModelType, ThinkingOption[]>
|
||||
export type ReasoningEffortConfig = Record<ThinkingModelType, ReasoningEffortOption[]>
|
||||
@ -111,6 +114,7 @@ export function isThinkModelType(type: string): type is ThinkingModelType {
|
||||
}
|
||||
|
||||
export const EFFORT_RATIO: EffortRatio = {
|
||||
none: 0.01,
|
||||
minimal: 0.05,
|
||||
low: 0.05,
|
||||
medium: 0.5,
|
||||
|
||||
@ -126,6 +126,10 @@ export type OpenAIExtraBody = {
|
||||
source_lang: 'auto'
|
||||
target_lang: string
|
||||
}
|
||||
// for gpt-5 series models verbosity control
|
||||
text?: {
|
||||
verbosity?: 'low' | 'medium' | 'high'
|
||||
}
|
||||
}
|
||||
// image is for openrouter. audio is ignored for now
|
||||
export type OpenAIModality = OpenAI.ChatCompletionModality | 'image'
|
||||
|
||||
254
yarn.lock
254
yarn.lock
@ -102,7 +102,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ai-sdk/anthropic@npm:2.0.44":
|
||||
"@ai-sdk/anthropic@npm:2.0.44, @ai-sdk/anthropic@npm:^2.0.44":
|
||||
version: 2.0.44
|
||||
resolution: "@ai-sdk/anthropic@npm:2.0.44"
|
||||
dependencies:
|
||||
@ -206,6 +206,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ai-sdk/google@npm:^2.0.32":
|
||||
version: 2.0.32
|
||||
resolution: "@ai-sdk/google@npm:2.0.32"
|
||||
dependencies:
|
||||
"@ai-sdk/provider": "npm:2.0.0"
|
||||
"@ai-sdk/provider-utils": "npm:3.0.17"
|
||||
peerDependencies:
|
||||
zod: ^3.25.76 || ^4.1.8
|
||||
checksum: 10c0/052de16f1f66188e126168c8a9cc903448104528c7e44d6867bbf555c9067b9d6d44a4c4e0e014838156ba39095cb417f1b76363eb65212ca4d005f3651e58d2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@ai-sdk/google@patch:@ai-sdk/google@npm%3A2.0.31#~/.yarn/patches/@ai-sdk-google-npm-2.0.31-b0de047210.patch":
|
||||
version: 2.0.31
|
||||
resolution: "@ai-sdk/google@patch:@ai-sdk/google@npm%3A2.0.31#~/.yarn/patches/@ai-sdk-google-npm-2.0.31-b0de047210.patch::version=2.0.31&hash=9f3835"
|
||||
@ -1404,7 +1416,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@aws-sdk/types@npm:3.910.0, @aws-sdk/types@npm:^3.222.0":
|
||||
"@aws-sdk/types@npm:3.910.0":
|
||||
version: 3.910.0
|
||||
resolution: "@aws-sdk/types@npm:3.910.0"
|
||||
dependencies:
|
||||
@ -1414,6 +1426,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@aws-sdk/types@npm:^3.222.0":
|
||||
version: 3.840.0
|
||||
resolution: "@aws-sdk/types@npm:3.840.0"
|
||||
dependencies:
|
||||
"@smithy/types": "npm:^4.3.1"
|
||||
tslib: "npm:^2.6.2"
|
||||
checksum: 10c0/292d38f5087c3aa925addd890f8ae2bf650282c2cf4997d971a341dc0249dfca7ce02d69a4af09da2562b78a4232232d2a3b88105f34f66aee608d52aac238d1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@aws-sdk/util-arn-parser@npm:3.893.0":
|
||||
version: 3.893.0
|
||||
resolution: "@aws-sdk/util-arn-parser@npm:3.893.0"
|
||||
@ -1683,18 +1705,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.27.5, @babel/parser@npm:^7.28.0, @babel/parser@npm:^7.28.3, @babel/parser@npm:^7.28.4":
|
||||
version: 7.28.4
|
||||
resolution: "@babel/parser@npm:7.28.4"
|
||||
dependencies:
|
||||
"@babel/types": "npm:^7.28.4"
|
||||
bin:
|
||||
parser: ./bin/babel-parser.js
|
||||
checksum: 10c0/58b239a5b1477ac7ed7e29d86d675cc81075ca055424eba6485872626db2dc556ce63c45043e5a679cd925e999471dba8a3ed4864e7ab1dbf64306ab72c52707
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/parser@npm:^7.28.5":
|
||||
"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.28.3, @babel/parser@npm:^7.28.4, @babel/parser@npm:^7.28.5":
|
||||
version: 7.28.5
|
||||
resolution: "@babel/parser@npm:7.28.5"
|
||||
dependencies:
|
||||
@ -1705,6 +1716,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/parser@npm:^7.25.4, @babel/parser@npm:^7.27.2, @babel/parser@npm:^7.27.5, @babel/parser@npm:^7.28.0":
|
||||
version: 7.28.0
|
||||
resolution: "@babel/parser@npm:7.28.0"
|
||||
dependencies:
|
||||
"@babel/types": "npm:^7.28.0"
|
||||
bin:
|
||||
parser: ./bin/babel-parser.js
|
||||
checksum: 10c0/c2ef81d598990fa949d1d388429df327420357cb5200271d0d0a2784f1e6d54afc8301eb8bdf96d8f6c77781e402da93c7dc07980fcc136ac5b9d5f1fce701b5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/plugin-transform-arrow-functions@npm:^7.27.1":
|
||||
version: 7.27.1
|
||||
resolution: "@babel/plugin-transform-arrow-functions@npm:7.27.1"
|
||||
@ -1716,13 +1738,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.10.4, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.6, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.4, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.24.8, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.26.7, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7":
|
||||
"@babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.10.4, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.6, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.4, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.24.8, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.26.7":
|
||||
version: 7.28.3
|
||||
resolution: "@babel/runtime@npm:7.28.3"
|
||||
checksum: 10c0/b360f82c2c5114f2a062d4d143d7b4ec690094764853937110585a9497977aed66c102166d0e404766c274e02a50ffb8f6d77fef7251ecf3f607f0e03e6397bc
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.7":
|
||||
version: 7.28.2
|
||||
resolution: "@babel/runtime@npm:7.28.2"
|
||||
checksum: 10c0/c20afe253629d53a405a610b12a62ac74d341a2c1e0fb202bbef0c118f6b5c84f94bf16039f58fd0483dd256901259930a43976845bdeb180cab1f882c21b6e0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/template@npm:^7.27.2":
|
||||
version: 7.27.2
|
||||
resolution: "@babel/template@npm:7.27.2"
|
||||
@ -1764,7 +1793,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.25.4, @babel/types@npm:^7.27.1, @babel/types@npm:^7.28.1, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4":
|
||||
"@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.28.4":
|
||||
version: 7.28.4
|
||||
resolution: "@babel/types@npm:7.28.4"
|
||||
dependencies:
|
||||
@ -1784,6 +1813,26 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/types@npm:^7.25.4, @babel/types@npm:^7.27.1, @babel/types@npm:^7.28.0":
|
||||
version: 7.28.1
|
||||
resolution: "@babel/types@npm:7.28.1"
|
||||
dependencies:
|
||||
"@babel/helper-string-parser": "npm:^7.27.1"
|
||||
"@babel/helper-validator-identifier": "npm:^7.27.1"
|
||||
checksum: 10c0/5e99b346c11ee42ffb0cadc28159fe0b184d865a2cc1593df79b199772a534f6453969b4942aa5e4a55a3081863096e1cc3fc1c724d826926dc787cf229b845d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@babel/types@npm:^7.28.1, @babel/types@npm:^7.28.2":
|
||||
version: 7.28.2
|
||||
resolution: "@babel/types@npm:7.28.2"
|
||||
dependencies:
|
||||
"@babel/helper-string-parser": "npm:^7.27.1"
|
||||
"@babel/helper-validator-identifier": "npm:^7.27.1"
|
||||
checksum: 10c0/24b11c9368e7e2c291fe3c1bcd1ed66f6593a3975f479cbb9dd7b8c8d8eab8a962b0d2fca616c043396ce82500ac7d23d594fbbbd013828182c01596370a0b10
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@bcoe/v8-coverage@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "@bcoe/v8-coverage@npm:1.0.2"
|
||||
@ -2176,9 +2225,9 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"@cherrystudio/openai@npm:^6.5.0":
|
||||
version: 6.5.0
|
||||
resolution: "@cherrystudio/openai@npm:6.5.0"
|
||||
"@cherrystudio/openai@npm:^6.9.0":
|
||||
version: 6.9.0
|
||||
resolution: "@cherrystudio/openai@npm:6.9.0"
|
||||
peerDependencies:
|
||||
ws: ^8.18.0
|
||||
zod: ^3.25 || ^4.0
|
||||
@ -2189,7 +2238,7 @@ __metadata:
|
||||
optional: true
|
||||
bin:
|
||||
openai: bin/cli
|
||||
checksum: 10c0/0f6cafb97aec17037d5ddcccc88e4b4a9c8de77a989a35bab2394b682a1a69e8a9343e8ee5eb8107d5c495970dbf3567642f154c033f7afc3bf078078666a92e
|
||||
checksum: 10c0/9c51ef33c5b9d08041a115e3d6a8158412a379998a0eae186923d5bdcc808b634c1fef4471a1d499bb8c624b04c075167bc90a1a60a805005c0657ecebbb58d0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -6788,15 +6837,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@opeoginni/github-copilot-openai-compatible@npm:0.1.19":
|
||||
version: 0.1.19
|
||||
resolution: "@opeoginni/github-copilot-openai-compatible@npm:0.1.19"
|
||||
"@opeoginni/github-copilot-openai-compatible@npm:0.1.21":
|
||||
version: 0.1.21
|
||||
resolution: "@opeoginni/github-copilot-openai-compatible@npm:0.1.21"
|
||||
dependencies:
|
||||
"@ai-sdk/openai": "npm:^2.0.42"
|
||||
"@ai-sdk/openai-compatible": "npm:^1.0.19"
|
||||
"@ai-sdk/provider": "npm:^2.1.0-beta.4"
|
||||
"@ai-sdk/provider-utils": "npm:^3.0.10"
|
||||
checksum: 10c0/dfb01832d7c704b2eb080fc09d31b07fc26e5ac4e648ce219dc0d80cf044ef3cae504427781ec2ce3c5a2459c9c81d043046a255642108d5b3de0f83f4a9f20a
|
||||
checksum: 10c0/05b73d935dc7f24123330ade919698b486ac2a25a7d607c1d3789471f782ead4c803ce6ffd3d97b9ca3f1aadaf6b5c1ea52363c9d24b36894fcfc403fda9cef3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@ -12195,7 +12244,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:*, @types/node@npm:>=10.0.0, @types/node@npm:>=13.7.0":
|
||||
"@types/node@npm:*, @types/node@npm:>=13.7.0":
|
||||
version: 22.15.29
|
||||
resolution: "@types/node@npm:22.15.29"
|
||||
dependencies:
|
||||
undici-types: "npm:~6.21.0"
|
||||
checksum: 10c0/602cc88c6150780cd9b5b44604754e0ce13983ae876a538861d6ecfb1511dff289e5576fffd26c841cde2142418d4bb76e2a72a382b81c04557ccb17cff29e1d
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/node@npm:>=10.0.0":
|
||||
version: 24.3.1
|
||||
resolution: "@types/node@npm:24.3.1"
|
||||
dependencies:
|
||||
@ -12289,7 +12347,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react@npm:*, @types/react@npm:^19.0.12":
|
||||
"@types/react@npm:*":
|
||||
version: 19.1.12
|
||||
resolution: "@types/react@npm:19.1.12"
|
||||
dependencies:
|
||||
@ -12298,6 +12356,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/react@npm:^19.0.12":
|
||||
version: 19.1.2
|
||||
resolution: "@types/react@npm:19.1.2"
|
||||
dependencies:
|
||||
csstype: "npm:^3.0.2"
|
||||
checksum: 10c0/76ffe71395c713d4adc3c759465012d3c956db00af35ab7c6d0d91bd07b274b7ce69caa0478c0760311587bd1e38c78ffc9688ebc629f2b266682a19d8750947
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@types/resolve@npm:^1.20.2":
|
||||
version: 1.20.6
|
||||
resolution: "@types/resolve@npm:1.20.6"
|
||||
@ -13505,8 +13572,10 @@ __metadata:
|
||||
"@agentic/searxng": "npm:^7.3.3"
|
||||
"@agentic/tavily": "npm:^7.3.3"
|
||||
"@ai-sdk/amazon-bedrock": "npm:^3.0.53"
|
||||
"@ai-sdk/anthropic": "npm:^2.0.44"
|
||||
"@ai-sdk/cerebras": "npm:^1.0.31"
|
||||
"@ai-sdk/gateway": "npm:^2.0.9"
|
||||
"@ai-sdk/google": "npm:^2.0.32"
|
||||
"@ai-sdk/google-vertex": "npm:^3.0.62"
|
||||
"@ai-sdk/huggingface": "patch:@ai-sdk/huggingface@npm%3A0.0.8#~/.yarn/patches/@ai-sdk-huggingface-npm-0.0.8-d4d0aaac93.patch"
|
||||
"@ai-sdk/mistral": "npm:^2.0.23"
|
||||
@ -13533,7 +13602,7 @@ __metadata:
|
||||
"@cherrystudio/embedjs-ollama": "npm:^0.1.31"
|
||||
"@cherrystudio/embedjs-openai": "npm:^0.1.31"
|
||||
"@cherrystudio/extension-table-plus": "workspace:^"
|
||||
"@cherrystudio/openai": "npm:^6.5.0"
|
||||
"@cherrystudio/openai": "npm:^6.9.0"
|
||||
"@cherrystudio/ui": "workspace:*"
|
||||
"@dnd-kit/core": "npm:^6.3.1"
|
||||
"@dnd-kit/modifiers": "npm:^9.0.0"
|
||||
@ -13566,7 +13635,7 @@ __metadata:
|
||||
"@opentelemetry/sdk-trace-base": "npm:^2.0.0"
|
||||
"@opentelemetry/sdk-trace-node": "npm:^2.0.0"
|
||||
"@opentelemetry/sdk-trace-web": "npm:^2.0.0"
|
||||
"@opeoginni/github-copilot-openai-compatible": "npm:0.1.19"
|
||||
"@opeoginni/github-copilot-openai-compatible": "npm:0.1.21"
|
||||
"@paymoapp/electron-shutdown-handler": "npm:^1.1.2"
|
||||
"@playwright/test": "npm:^1.52.0"
|
||||
"@radix-ui/react-context-menu": "npm:^2.2.16"
|
||||
@ -16547,13 +16616,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"decimal.js@npm:^10.4.3, decimal.js@npm:^10.5.0":
|
||||
"decimal.js@npm:^10.4.3":
|
||||
version: 10.6.0
|
||||
resolution: "decimal.js@npm:10.6.0"
|
||||
checksum: 10c0/07d69fbcc54167a340d2d97de95f546f9ff1f69d2b45a02fd7a5292412df3cd9eb7e23065e532a318f5474a2e1bccf8392fdf0443ef467f97f3bf8cb0477e5aa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"decimal.js@npm:^10.5.0":
|
||||
version: 10.5.0
|
||||
resolution: "decimal.js@npm:10.5.0"
|
||||
checksum: 10c0/785c35279df32762143914668df35948920b6c1c259b933e0519a69b7003fc0a5ed2a766b1e1dda02574450c566b21738a45f15e274b47c2ac02072c0d1f3ac3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"decode-named-character-reference@npm:^1.0.0":
|
||||
version: 1.1.0
|
||||
resolution: "decode-named-character-reference@npm:1.1.0"
|
||||
@ -16807,13 +16883,27 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-libc@npm:^2.0.0, detect-libc@npm:^2.0.1, detect-libc@npm:^2.0.3, detect-libc@npm:^2.0.4":
|
||||
"detect-libc@npm:^2.0.0":
|
||||
version: 2.1.2
|
||||
resolution: "detect-libc@npm:2.1.2"
|
||||
checksum: 10c0/acc675c29a5649fa1fb6e255f993b8ee829e510b6b56b0910666949c80c364738833417d0edb5f90e4e46be17228b0f2b66a010513984e18b15deeeac49369c4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-libc@npm:^2.0.1":
|
||||
version: 2.0.3
|
||||
resolution: "detect-libc@npm:2.0.3"
|
||||
checksum: 10c0/88095bda8f90220c95f162bf92cad70bd0e424913e655c20578600e35b91edc261af27531cf160a331e185c0ced93944bc7e09939143225f56312d7fd800fdb7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-libc@npm:^2.0.3, detect-libc@npm:^2.0.4":
|
||||
version: 2.0.4
|
||||
resolution: "detect-libc@npm:2.0.4"
|
||||
checksum: 10c0/c15541f836eba4b1f521e4eecc28eefefdbc10a94d3b8cb4c507689f332cc111babb95deda66f2de050b22122113189986d5190be97d51b5a2b23b938415e67c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"detect-node-es@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "detect-node-es@npm:1.1.0"
|
||||
@ -18216,13 +18306,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eventsource-parser@npm:^3.0.0, eventsource-parser@npm:^3.0.1, eventsource-parser@npm:^3.0.5":
|
||||
"eventsource-parser@npm:^3.0.0, eventsource-parser@npm:^3.0.5":
|
||||
version: 3.0.5
|
||||
resolution: "eventsource-parser@npm:3.0.5"
|
||||
checksum: 10c0/5cb75e3f84ff1cfa1cee6199d4fd430c4544855ab03e953ddbe5927e7b31bc2af3933ab8aba6440ba160ed2c48972b6c317f27b8a1d0764c7b12e34e249de631
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eventsource-parser@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "eventsource-parser@npm:3.0.1"
|
||||
checksum: 10c0/146ce5ae8325d07645a49bbc54d7ac3aef42f5138bfbbe83d5cf96293b50eab2219926d6cf41eed0a0f90132578089652ba9286a19297662900133a9da6c2fd0
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"eventsource-parser@npm:^3.0.6":
|
||||
version: 3.0.6
|
||||
resolution: "eventsource-parser@npm:3.0.6"
|
||||
@ -18447,7 +18544,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-xml-parser@npm:5.2.5, fast-xml-parser@npm:^5.2.0":
|
||||
"fast-xml-parser@npm:5.2.5":
|
||||
version: 5.2.5
|
||||
resolution: "fast-xml-parser@npm:5.2.5"
|
||||
dependencies:
|
||||
@ -18469,6 +18566,17 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-xml-parser@npm:^5.2.0":
|
||||
version: 5.2.0
|
||||
resolution: "fast-xml-parser@npm:5.2.0"
|
||||
dependencies:
|
||||
strnum: "npm:^2.0.5"
|
||||
bin:
|
||||
fxparser: src/cli/cli.js
|
||||
checksum: 10c0/9d65ea8edeff114200313a7df7b9b1c62ff848099529ab82ed2de75ef789ebf1d7105442a12a1a51899f4dae3961f776adc6ddd85dd461272bc3f1e62211fc37
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fastq@npm:^1.6.0":
|
||||
version: 1.19.1
|
||||
resolution: "fastq@npm:1.19.1"
|
||||
@ -18518,6 +18626,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fdir@npm:^6.4.4":
|
||||
version: 6.4.4
|
||||
resolution: "fdir@npm:6.4.4"
|
||||
peerDependencies:
|
||||
picomatch: ^3 || ^4
|
||||
peerDependenciesMeta:
|
||||
picomatch:
|
||||
optional: true
|
||||
checksum: 10c0/6ccc33be16945ee7bc841e1b4178c0b4cf18d3804894cb482aa514651c962a162f96da7ffc6ebfaf0df311689fb70091b04dd6caffe28d56b9ebdc0e7ccadfdd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fdir@npm:^6.5.0":
|
||||
version: 6.5.0
|
||||
resolution: "fdir@npm:6.5.0"
|
||||
@ -19126,7 +19246,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-tsconfig@npm:^4.10.1, get-tsconfig@npm:^4.12.0, get-tsconfig@npm:^4.7.0, get-tsconfig@npm:^4.7.5":
|
||||
"get-tsconfig@npm:^4.10.1, get-tsconfig@npm:^4.7.0, get-tsconfig@npm:^4.7.5":
|
||||
version: 4.10.1
|
||||
resolution: "get-tsconfig@npm:4.10.1"
|
||||
dependencies:
|
||||
resolve-pkg-maps: "npm:^1.0.0"
|
||||
checksum: 10c0/7f8e3dabc6a49b747920a800fb88e1952fef871cdf51b79e98db48275a5de6cdaf499c55ee67df5fa6fe7ce65f0063e26de0f2e53049b408c585aa74d39ffa21
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"get-tsconfig@npm:^4.12.0":
|
||||
version: 4.12.0
|
||||
resolution: "get-tsconfig@npm:4.12.0"
|
||||
dependencies:
|
||||
@ -20484,16 +20613,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"jiti@npm:^2.4.2":
|
||||
version: 2.6.1
|
||||
resolution: "jiti@npm:2.6.1"
|
||||
bin:
|
||||
jiti: lib/jiti-cli.mjs
|
||||
checksum: 10c0/79b2e96a8e623f66c1b703b98ec1b8be4500e1d217e09b09e343471bbb9c105381b83edbb979d01cef18318cc45ce6e153571b6c83122170eefa531c64b6789b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"jiti@npm:^2.5.1":
|
||||
"jiti@npm:^2.4.2, jiti@npm:^2.5.1":
|
||||
version: 2.5.1
|
||||
resolution: "jiti@npm:2.5.1"
|
||||
bin:
|
||||
@ -21591,7 +21711,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"magic-string@npm:^0.30.0, magic-string@npm:^0.30.17, magic-string@npm:^0.30.18, magic-string@npm:^0.30.19":
|
||||
"magic-string@npm:^0.30.0, magic-string@npm:^0.30.19":
|
||||
version: 0.30.19
|
||||
resolution: "magic-string@npm:0.30.19"
|
||||
dependencies:
|
||||
@ -21600,6 +21720,24 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"magic-string@npm:^0.30.17":
|
||||
version: 0.30.17
|
||||
resolution: "magic-string@npm:0.30.17"
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec": "npm:^1.5.0"
|
||||
checksum: 10c0/16826e415d04b88378f200fe022b53e638e3838b9e496edda6c0e086d7753a44a6ed187adc72d19f3623810589bf139af1a315541cd6a26ae0771a0193eaf7b8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"magic-string@npm:^0.30.18":
|
||||
version: 0.30.18
|
||||
resolution: "magic-string@npm:0.30.18"
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec": "npm:^1.5.5"
|
||||
checksum: 10c0/80fba01e13ce1f5c474a0498a5aa462fa158eb56567310747089a0033e432d83a2021ee2c109ac116010cd9dcf90a5231d89fbe3858165f73c00a50a74dbefcd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"magicast@npm:^0.3.5":
|
||||
version: 0.3.5
|
||||
resolution: "magicast@npm:0.3.5"
|
||||
@ -27749,6 +27887,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"strnum@npm:^2.0.5":
|
||||
version: 2.0.5
|
||||
resolution: "strnum@npm:2.0.5"
|
||||
checksum: 10c0/856026ef65eaf15359d340a313ece25822b6472377b3029201b00f2657a1a3fa1cd7a7ce349dad35afdd00faf451344153dbb3d8478f082b7af8c17a64799ea6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"strnum@npm:^2.1.0":
|
||||
version: 2.1.1
|
||||
resolution: "strnum@npm:2.1.1"
|
||||
@ -28257,7 +28402,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.14, tinyglobby@npm:^0.2.15":
|
||||
"tinyglobby@npm:^0.2.12, tinyglobby@npm:^0.2.15":
|
||||
version: 0.2.15
|
||||
resolution: "tinyglobby@npm:0.2.15"
|
||||
dependencies:
|
||||
@ -28267,6 +28412,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinyglobby@npm:^0.2.14":
|
||||
version: 0.2.14
|
||||
resolution: "tinyglobby@npm:0.2.14"
|
||||
dependencies:
|
||||
fdir: "npm:^6.4.4"
|
||||
picomatch: "npm:^4.0.2"
|
||||
checksum: 10c0/f789ed6c924287a9b7d3612056ed0cda67306cd2c80c249fd280cf1504742b12583a2089b61f4abbd24605f390809017240e250241f09938054c9b363e51c0a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tinypool@npm:^1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "tinypool@npm:1.1.1"
|
||||
@ -30208,13 +30363,20 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^3.25.76 || ^4, zod@npm:^4.1.5":
|
||||
"zod@npm:^3.25.76 || ^4":
|
||||
version: 4.1.12
|
||||
resolution: "zod@npm:4.1.12"
|
||||
checksum: 10c0/b64c1feb19e99d77075261eaf613e0b2be4dfcd3551eff65ad8b4f2a079b61e379854d066f7d447491fcf193f45babd8095551a9d47973d30b46b6d8e2c46774
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^4.1.5":
|
||||
version: 4.1.5
|
||||
resolution: "zod@npm:4.1.5"
|
||||
checksum: 10c0/7826fb931bc71d4d0fff2fbb72f1a1cf30a6672cf9dbe6933a216bbb60242ef1c3bdfbcd3c5b27e806235a35efaad7a4a9897ff4d3621452f9ea278bce6fd42a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zustand@npm:^4.4.0":
|
||||
version: 4.5.6
|
||||
resolution: "zustand@npm:4.5.6"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user