Compare commits
4 Commits
fix/input-
...
fix/openai
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
18777b3872 | ||
|
|
8e09da1ca0 | ||
|
|
b588a43405 | ||
|
|
5d0ad18e45 |
@@ -162,7 +162,7 @@
|
||||
"@langchain/core": "patch:@langchain/core@npm%3A1.0.2#~/.yarn/patches/@langchain-core-npm-1.0.2-183ef83fe4.patch",
|
||||
"@langchain/openai": "patch:@langchain/openai@npm%3A1.0.0#~/.yarn/patches/@langchain-openai-npm-1.0.0-474d0ad9d4.patch",
|
||||
"@mistralai/mistralai": "^1.7.5",
|
||||
"@modelcontextprotocol/sdk": "^1.23.0",
|
||||
"@modelcontextprotocol/sdk": "^1.17.5",
|
||||
"@mozilla/readability": "^0.6.0",
|
||||
"@notionhq/client": "^2.2.15",
|
||||
"@openrouter/ai-sdk-provider": "^1.2.8",
|
||||
|
||||
@@ -42,14 +42,11 @@ import {
|
||||
type MCPPrompt,
|
||||
type MCPResource,
|
||||
type MCPServer,
|
||||
type MCPTool,
|
||||
MCPToolInputSchema,
|
||||
MCPToolOutputSchema
|
||||
type MCPTool
|
||||
} from '@types'
|
||||
import { app, net } from 'electron'
|
||||
import { EventEmitter } from 'events'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import * as z from 'zod'
|
||||
|
||||
import { CacheService } from './CacheService'
|
||||
import DxtService from './DxtService'
|
||||
@@ -623,8 +620,6 @@ class McpService {
|
||||
tools.map((tool: SDKTool) => {
|
||||
const serverTool: MCPTool = {
|
||||
...tool,
|
||||
inputSchema: z.parse(MCPToolInputSchema, tool.inputSchema),
|
||||
outputSchema: tool.outputSchema ? z.parse(MCPToolOutputSchema, tool.outputSchema) : undefined,
|
||||
id: buildFunctionCallToolName(server.name, tool.name, server.id),
|
||||
serverId: server.id,
|
||||
serverName: server.name,
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
import { REFERENCE_PROMPT } from '@renderer/config/prompts'
|
||||
import { getLMStudioKeepAliveTime } from '@renderer/hooks/useLMStudio'
|
||||
import { getAssistantSettings } from '@renderer/services/AssistantService'
|
||||
import type { RootState } from '@renderer/store'
|
||||
import type {
|
||||
Assistant,
|
||||
GenerateImageParams,
|
||||
@@ -245,23 +246,20 @@ export abstract class BaseApiClient<
|
||||
|
||||
protected getVerbosity(model?: Model): OpenAIVerbosity {
|
||||
try {
|
||||
const state = window.store?.getState()
|
||||
const state = window.store?.getState() as RootState
|
||||
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
|
||||
// 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) {
|
||||
logger.warn('Failed to get verbosity from state:', error as Error)
|
||||
logger.warn('Failed to get verbosity from state. Fallback to undefined.', error as Error)
|
||||
return undefined
|
||||
}
|
||||
|
||||
return 'medium'
|
||||
}
|
||||
|
||||
protected getTimeout(model: Model) {
|
||||
|
||||
@@ -32,7 +32,6 @@ import {
|
||||
isSupportedThinkingTokenModel,
|
||||
isSupportedThinkingTokenQwenModel,
|
||||
isSupportedThinkingTokenZhipuModel,
|
||||
isSupportVerbosityModel,
|
||||
isVisionModel,
|
||||
MODEL_SUPPORTED_REASONING_EFFORT,
|
||||
ZHIPU_RESULT_TOKENS
|
||||
@@ -714,13 +713,8 @@ export class OpenAIAPIClient extends OpenAIBaseClient<
|
||||
...modalities,
|
||||
// groq 有不同的 service tier 配置,不符合 openai 接口类型
|
||||
service_tier: this.getServiceTier(model) as OpenAIServiceTier,
|
||||
...(isSupportVerbosityModel(model)
|
||||
? {
|
||||
text: {
|
||||
verbosity: this.getVerbosity(model)
|
||||
}
|
||||
}
|
||||
: {}),
|
||||
// verbosity. getVerbosity ensure the returned value is valid.
|
||||
verbosity: this.getVerbosity(model),
|
||||
...this.getProviderSpecificParameters(assistant, model),
|
||||
...reasoningEffort,
|
||||
// ...getOpenAIWebSearchParams(model, enableWebSearch),
|
||||
|
||||
@@ -222,18 +222,22 @@ describe('model utils', () => {
|
||||
|
||||
describe('getModelSupportedVerbosity', () => {
|
||||
it('returns only "high" for GPT-5 Pro models', () => {
|
||||
expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5-pro' }))).toEqual([undefined, 'high'])
|
||||
expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5-pro-2025-10-06' }))).toEqual([undefined, 'high'])
|
||||
expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5-pro' }))).toEqual([undefined, null, 'high'])
|
||||
expect(getModelSupportedVerbosity(createModel({ id: 'gpt-5-pro-2025-10-06' }))).toEqual([
|
||||
undefined,
|
||||
null,
|
||||
'high'
|
||||
])
|
||||
})
|
||||
|
||||
it('returns all levels for non-Pro GPT-5 models', () => {
|
||||
const previewModel = createModel({ id: 'gpt-5-preview' })
|
||||
expect(getModelSupportedVerbosity(previewModel)).toEqual([undefined, 'low', 'medium', 'high'])
|
||||
expect(getModelSupportedVerbosity(previewModel)).toEqual([undefined, null, 'low', 'medium', 'high'])
|
||||
})
|
||||
|
||||
it('returns all levels for GPT-5.1 models', () => {
|
||||
const gpt51Model = createModel({ id: 'gpt-5.1-preview' })
|
||||
expect(getModelSupportedVerbosity(gpt51Model)).toEqual([undefined, 'low', 'medium', 'high'])
|
||||
expect(getModelSupportedVerbosity(gpt51Model)).toEqual([undefined, null, 'low', 'medium', 'high'])
|
||||
})
|
||||
|
||||
it('returns only undefined for non-GPT-5 models', () => {
|
||||
|
||||
@@ -10,7 +10,8 @@ import {
|
||||
isGPT51SeriesModel,
|
||||
isOpenAIChatCompletionOnlyModel,
|
||||
isOpenAIOpenWeightModel,
|
||||
isOpenAIReasoningModel
|
||||
isOpenAIReasoningModel,
|
||||
isSupportVerbosityModel
|
||||
} from './openai'
|
||||
import { isQwenMTModel } from './qwen'
|
||||
import { isGenerateImageModel, isTextToImageModel, isVisionModel } from './vision'
|
||||
@@ -154,10 +155,10 @@ const MODEL_SUPPORTED_VERBOSITY: readonly {
|
||||
* For GPT-5-pro, only 'high' is supported; for other GPT-5 models, 'low', 'medium', and 'high' are supported.
|
||||
* For GPT-5.1 series models, 'low', 'medium', and 'high' are supported.
|
||||
* @param model - The model to check
|
||||
* @returns An array of supported verbosity levels, always including `undefined` as the first element
|
||||
* @returns An array of supported verbosity levels, always including `undefined` as the first element and `null` when applicable
|
||||
*/
|
||||
export const getModelSupportedVerbosity = (model: Model | undefined | null): OpenAIVerbosity[] => {
|
||||
if (!model) {
|
||||
if (!model || !isSupportVerbosityModel(model)) {
|
||||
return [undefined]
|
||||
}
|
||||
|
||||
@@ -165,7 +166,7 @@ export const getModelSupportedVerbosity = (model: Model | undefined | null): Ope
|
||||
|
||||
for (const { validator, values } of MODEL_SUPPORTED_VERBOSITY) {
|
||||
if (validator(model)) {
|
||||
supportedValues = [...values]
|
||||
supportedValues = [null, ...values]
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,12 +24,12 @@ import { useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
|
||||
type VerbosityOption = {
|
||||
value: NonNullable<OpenAIVerbosity> | 'undefined'
|
||||
value: NonNullable<OpenAIVerbosity> | 'undefined' | 'null'
|
||||
label: string
|
||||
}
|
||||
|
||||
type SummaryTextOption = {
|
||||
value: NonNullable<OpenAISummaryText> | 'undefined'
|
||||
value: NonNullable<OpenAISummaryText> | 'undefined' | 'null'
|
||||
label: string
|
||||
}
|
||||
|
||||
@@ -85,6 +85,10 @@ const OpenAISettingsGroup: FC<Props> = ({ model, providerId, SettingGroup, Setti
|
||||
value: 'undefined',
|
||||
label: t('common.ignore')
|
||||
},
|
||||
{
|
||||
value: 'null',
|
||||
label: t('common.off')
|
||||
},
|
||||
{
|
||||
value: 'auto',
|
||||
label: t('settings.openai.summary_text_mode.auto')
|
||||
@@ -105,6 +109,10 @@ const OpenAISettingsGroup: FC<Props> = ({ model, providerId, SettingGroup, Setti
|
||||
value: 'undefined',
|
||||
label: t('common.ignore')
|
||||
},
|
||||
{
|
||||
value: 'null',
|
||||
label: t('common.off')
|
||||
},
|
||||
{
|
||||
value: 'low',
|
||||
label: t('settings.openai.verbosity.low')
|
||||
@@ -203,9 +211,9 @@ const OpenAISettingsGroup: FC<Props> = ({ model, providerId, SettingGroup, Setti
|
||||
</Tooltip>
|
||||
</SettingRowTitleSmall>
|
||||
<Selector
|
||||
value={summaryText}
|
||||
value={toOptionValue(summaryText)}
|
||||
onChange={(value) => {
|
||||
setSummaryText(value as OpenAISummaryText)
|
||||
setSummaryText(toRealValue(value))
|
||||
}}
|
||||
options={summaryTextOptions}
|
||||
/>
|
||||
@@ -222,9 +230,9 @@ const OpenAISettingsGroup: FC<Props> = ({ model, providerId, SettingGroup, Setti
|
||||
</Tooltip>
|
||||
</SettingRowTitleSmall>
|
||||
<Selector
|
||||
value={verbosity}
|
||||
value={toOptionValue(verbosity)}
|
||||
onChange={(value) => {
|
||||
setVerbosity(value as OpenAIVerbosity)
|
||||
setVerbosity(toRealValue(value))
|
||||
}}
|
||||
options={verbosityOptions}
|
||||
/>
|
||||
|
||||
@@ -2906,6 +2906,23 @@ const migrateConfig = {
|
||||
logger.error('migrate 179 error', error as Error)
|
||||
return state
|
||||
}
|
||||
},
|
||||
'180': (state: RootState) => {
|
||||
try {
|
||||
// @ts-expect-error
|
||||
if (state.settings.openAI.summaryText === 'undefined') {
|
||||
state.settings.openAI.summaryText = undefined
|
||||
}
|
||||
// @ts-expect-error
|
||||
if (state.settings.openAI.verbosity === 'undefined') {
|
||||
state.settings.openAI.summaryText = undefined
|
||||
}
|
||||
logger.info('migrate 180 success')
|
||||
return state
|
||||
} catch (error) {
|
||||
logger.error('migrate 180 error', error as Error)
|
||||
return state
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import type OpenAI from '@cherrystudio/openai'
|
||||
import type { NotNull, NotUndefined } from '@types'
|
||||
import type { NotUndefined } from '@types'
|
||||
import type { ImageModel, LanguageModel } from 'ai'
|
||||
import type { generateObject, generateText, ModelMessage, streamObject, streamText } from 'ai'
|
||||
import * as z from 'zod'
|
||||
@@ -32,17 +32,15 @@ export type GenerateObjectParams = Omit<Parameters<typeof generateObject>[0], 'm
|
||||
export type AiSdkModel = LanguageModel | ImageModel
|
||||
|
||||
// The original type unite both undefined and null.
|
||||
// I pick undefined as the unique falsy type since they seem like share the same meaning according to OpenAI API docs.
|
||||
// Parameter would not be passed into request if it's undefined.
|
||||
export type OpenAIVerbosity = NotNull<OpenAI.Responses.ResponseTextConfig['verbosity']>
|
||||
export type OpenAIVerbosity = OpenAI.Responses.ResponseTextConfig['verbosity']
|
||||
export type ValidOpenAIVerbosity = NotUndefined<OpenAIVerbosity>
|
||||
|
||||
export type OpenAIReasoningEffort = OpenAI.ReasoningEffort
|
||||
|
||||
// The original type unite both undefined and null.
|
||||
// I pick undefined as the unique falsy type since they seem like share the same meaning according to OpenAI API docs.
|
||||
// Parameter would not be passed into request if it's undefined.
|
||||
export type OpenAISummaryText = NotNull<OpenAI.Reasoning['summary']>
|
||||
export type OpenAISummaryText = OpenAI.Reasoning['summary']
|
||||
|
||||
const AiSdkParamsSchema = z.enum([
|
||||
'maxOutputTokens',
|
||||
|
||||
@@ -128,10 +128,6 @@ 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'
|
||||
|
||||
@@ -34,15 +34,6 @@ export const MCPToolInputSchema = z
|
||||
required: z.array(z.string()).optional()
|
||||
})
|
||||
.loose()
|
||||
.transform((schema) => {
|
||||
if (!schema.properties) {
|
||||
schema.properties = {}
|
||||
}
|
||||
if (!schema.required) {
|
||||
schema.required = []
|
||||
}
|
||||
return schema
|
||||
})
|
||||
|
||||
export interface BuiltinTool extends BaseTool {
|
||||
inputSchema: z.infer<typeof MCPToolInputSchema>
|
||||
|
||||
@@ -136,10 +136,7 @@ export async function callMCPTool(
|
||||
topicId?: string,
|
||||
modelName?: string
|
||||
): Promise<MCPCallToolResponse> {
|
||||
logger.info(
|
||||
`Calling Tool: ${toolResponse.id} ${toolResponse.tool.serverName} ${toolResponse.tool.name}`,
|
||||
toolResponse.tool
|
||||
)
|
||||
logger.info(`Calling Tool: ${toolResponse.tool.serverName} ${toolResponse.tool.name}`, toolResponse.tool)
|
||||
try {
|
||||
const server = getMcpServerByTool(toolResponse.tool)
|
||||
|
||||
|
||||
61
yarn.lock
61
yarn.lock
@@ -4747,12 +4747,11 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"@modelcontextprotocol/sdk@npm:^1.23.0":
|
||||
version: 1.23.0
|
||||
resolution: "@modelcontextprotocol/sdk@npm:1.23.0"
|
||||
"@modelcontextprotocol/sdk@npm:^1.17.5":
|
||||
version: 1.17.5
|
||||
resolution: "@modelcontextprotocol/sdk@npm:1.17.5"
|
||||
dependencies:
|
||||
ajv: "npm:^8.17.1"
|
||||
ajv-formats: "npm:^3.0.1"
|
||||
ajv: "npm:^6.12.6"
|
||||
content-type: "npm:^1.0.5"
|
||||
cors: "npm:^2.8.5"
|
||||
cross-spawn: "npm:^7.0.5"
|
||||
@@ -4762,17 +4761,9 @@ __metadata:
|
||||
express-rate-limit: "npm:^7.5.0"
|
||||
pkce-challenge: "npm:^5.0.0"
|
||||
raw-body: "npm:^3.0.0"
|
||||
zod: "npm:^3.25 || ^4.0"
|
||||
zod-to-json-schema: "npm:^3.25.0"
|
||||
peerDependencies:
|
||||
"@cfworker/json-schema": ^4.1.1
|
||||
zod: ^3.25 || ^4.0
|
||||
peerDependenciesMeta:
|
||||
"@cfworker/json-schema":
|
||||
optional: true
|
||||
zod:
|
||||
optional: false
|
||||
checksum: 10c0/b0291f921ad9bda06bbf1a61b1bb61ceca1173da5d74d39a411c40428d6ca50a95f0de3a1631f25a44b439220b15c30c1306600bf48bef665ab7ad118d528260
|
||||
zod: "npm:^3.23.8"
|
||||
zod-to-json-schema: "npm:^3.24.1"
|
||||
checksum: 10c0/182b92b5e7c07da428fd23c6de22021c4f9a91f799c02a8ef15def07e4f9361d0fc22303548658fec2a700623535fd44a9dc4d010fb5d803a8f80e3c6c64a45e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -10055,7 +10046,7 @@ __metadata:
|
||||
"@libsql/client": "npm:0.14.0"
|
||||
"@libsql/win32-x64-msvc": "npm:^0.4.7"
|
||||
"@mistralai/mistralai": "npm:^1.7.5"
|
||||
"@modelcontextprotocol/sdk": "npm:^1.23.0"
|
||||
"@modelcontextprotocol/sdk": "npm:^1.17.5"
|
||||
"@mozilla/readability": "npm:^0.6.0"
|
||||
"@napi-rs/system-ocr": "patch:@napi-rs/system-ocr@npm%3A1.0.2#~/.yarn/patches/@napi-rs-system-ocr-npm-1.0.2-59e7a78e8b.patch"
|
||||
"@notionhq/client": "npm:^2.2.15"
|
||||
@@ -10412,20 +10403,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ajv-formats@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "ajv-formats@npm:3.0.1"
|
||||
dependencies:
|
||||
ajv: "npm:^8.0.0"
|
||||
peerDependencies:
|
||||
ajv: ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
ajv:
|
||||
optional: true
|
||||
checksum: 10c0/168d6bca1ea9f163b41c8147bae537e67bd963357a5488a1eaf3abe8baa8eec806d4e45f15b10767e6020679315c7e1e5e6803088dfb84efa2b4e9353b83dd0a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ajv-keywords@npm:^3.4.1":
|
||||
version: 3.5.2
|
||||
resolution: "ajv-keywords@npm:3.5.2"
|
||||
@@ -10435,7 +10412,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ajv@npm:^6.10.0, ajv@npm:^6.12.0, ajv@npm:^6.12.4":
|
||||
"ajv@npm:^6.10.0, ajv@npm:^6.12.0, ajv@npm:^6.12.4, ajv@npm:^6.12.6":
|
||||
version: 6.12.6
|
||||
resolution: "ajv@npm:6.12.6"
|
||||
dependencies:
|
||||
@@ -10447,7 +10424,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ajv@npm:^8.0.0, ajv@npm:^8.17.1, ajv@npm:^8.6.3":
|
||||
"ajv@npm:^8.0.0, ajv@npm:^8.6.3":
|
||||
version: 8.17.1
|
||||
resolution: "ajv@npm:8.17.1"
|
||||
dependencies:
|
||||
@@ -26376,15 +26353,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod-to-json-schema@npm:^3.25.0":
|
||||
version: 3.25.0
|
||||
resolution: "zod-to-json-schema@npm:3.25.0"
|
||||
peerDependencies:
|
||||
zod: ^3.25 || ^4
|
||||
checksum: 10c0/2d2cf6ca49752bf3dc5fb37bc8f275eddbbc4020e7958d9c198ea88cd197a5f527459118188a0081b889da6a6474d64c4134cd60951fa70178c125138761c680
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod-validation-error@npm:^3.4.0":
|
||||
version: 3.4.0
|
||||
resolution: "zod-validation-error@npm:3.4.0"
|
||||
@@ -26394,20 +26362,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^3.22.4, zod@npm:^3.24.1":
|
||||
"zod@npm:^3.22.4, zod@npm:^3.23.8, zod@npm:^3.24.1":
|
||||
version: 3.25.56
|
||||
resolution: "zod@npm:3.25.56"
|
||||
checksum: 10c0/3800f01d4b1df932b91354eb1e648f69cc7e5561549e6d2bf83827d930a5f33bbf92926099445f6fc1ebb64ca9c6513ef9ae5e5409cfef6325f354bcf6fc9a24
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^3.25 || ^4.0":
|
||||
version: 4.1.13
|
||||
resolution: "zod@npm:4.1.13"
|
||||
checksum: 10c0/d7e74e82dba81a91ffc3239cd85bc034abe193a28f7087a94ab258a3e48e9a7ca4141920cac979a0d781495b48fc547777394149f26be04c3dc642f58bbc3941
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"zod@npm:^3.25.0 || ^4.0.0, zod@npm:^3.25.76 || ^4":
|
||||
version: 4.1.12
|
||||
resolution: "zod@npm:4.1.12"
|
||||
|
||||
Reference in New Issue
Block a user