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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 16 additions & 8 deletions src/main/presenter/configPresenter/providerModelSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,8 @@ export const providerModelSettings: Record<string, { models: ProviderModelSettin
match: ['gemini-2.5-pro'],
vision: true,
functionCall: true,
reasoning: true
reasoning: true,
enableSearch: false
},
{
id: 'google/gemini-2.5-flash-image-preview',
Expand All @@ -330,7 +331,8 @@ export const providerModelSettings: Record<string, { models: ProviderModelSettin
match: ['models/gemini-2.5-flash-lite-preview-06-17', 'gemini-2.5-flash-lite-preview'],
vision: true,
functionCall: true,
reasoning: true
reasoning: true,
enableSearch: false
},
{
id: 'models/gemini-2.5-flash-lite',
Expand All @@ -341,7 +343,8 @@ export const providerModelSettings: Record<string, { models: ProviderModelSettin
match: ['models/gemini-2.5-flash-lite', 'gemini-2.5-flash-lite'],
vision: true,
functionCall: true,
reasoning: true
reasoning: true,
enableSearch: false
},
{
id: 'models/gemini-2.5-flash',
Expand All @@ -352,7 +355,8 @@ export const providerModelSettings: Record<string, { models: ProviderModelSettin
match: ['models/gemini-2.5-flash', 'gemini-2.5-flash'],
vision: true,
functionCall: true,
reasoning: true
reasoning: true,
enableSearch: false
},
{
id: 'models/gemini-2.0-flash-preview-image-generation',
Expand All @@ -378,7 +382,8 @@ export const providerModelSettings: Record<string, { models: ProviderModelSettin
match: ['models/gemini-2.0-flash-lite', 'gemini-2.0-flash-lite'],
vision: true,
functionCall: true,
reasoning: false
reasoning: false,
enableSearch: false
},
{
id: 'models/gemini-2.0-flash',
Expand All @@ -389,7 +394,8 @@ export const providerModelSettings: Record<string, { models: ProviderModelSettin
match: ['models/gemini-2.0-flash', 'gemini-2.0-flash'],
vision: true,
functionCall: true,
reasoning: true
reasoning: true,
enableSearch: false
},
{
id: 'models/gemini-1.5-flash',
Expand All @@ -400,7 +406,8 @@ export const providerModelSettings: Record<string, { models: ProviderModelSettin
match: ['models/gemini-1.5-flash', 'gemini-1.5-flash'],
vision: true,
functionCall: true,
reasoning: false
reasoning: false,
enableSearch: false
},
{
id: 'models/gemini-1.5-pro',
Expand All @@ -411,7 +418,8 @@ export const providerModelSettings: Record<string, { models: ProviderModelSettin
match: ['models/gemini-1.5-pro', 'gemini-1.5-pro'],
vision: true,
functionCall: true,
reasoning: false
reasoning: false,
enableSearch: false
}
]
},
Expand Down
130 changes: 66 additions & 64 deletions src/main/presenter/llmProviderPresenter/providers/geminiProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@ import {
FunctionCallingConfigMode,
GenerateContentParameters,
GenerateContentResponseUsageMetadata,
GenerationConfig,
GoogleGenAI,
HarmBlockThreshold,
HarmCategory,
Modality,
Part,
SafetySetting
SafetySetting,
Tool,
GoogleSearch,
GenerateContentConfig
} from '@google/genai'
import { ModelType } from '@shared/model'
import {
Expand Down Expand Up @@ -269,7 +271,7 @@ export class GeminiProvider extends BaseLLMProvider {
const result = await this.genAI.models.generateContent({
model: modelId,
contents: [{ role: 'user', parts: [{ text: prompt }] }],
config: this.getGenerationConfig(0.4, undefined, modelId, false)
config: this.getGenerateContentConfig(0.4, undefined, modelId, false)
})

return result.text?.trim() || 'New Conversation'
Expand Down Expand Up @@ -398,39 +400,40 @@ export class GeminiProvider extends BaseLLMProvider {
}

// 获取生成配置,不再创建模型实例
private getGenerationConfig(
private getGenerateContentConfig(
temperature?: number,
maxTokens?: number,
modelId?: string,
reasoning?: boolean,
thinkingBudget?: number
): GenerationConfig {
const generationConfig: GenerationConfig = {
): GenerateContentConfig {
const config: GenerateContentConfig = {
temperature,
maxOutputTokens: maxTokens
maxOutputTokens: maxTokens,
topP: 1 // topP默认为1.0
}

// 从当前模型列表中查找指定的模型
if (modelId && this.models) {
const model = this.models.find((m) => m.id === modelId)
if (model && model.type === ModelType.ImageGeneration) {
generationConfig.responseModalities = [Modality.TEXT, Modality.IMAGE]
config.responseModalities = [Modality.TEXT, Modality.IMAGE]
}
}

// 正确配置思考功能
if (reasoning) {
generationConfig.thinkingConfig = {
config.thinkingConfig = {
includeThoughts: true
}

// 仅对支持 thinkingBudget 的 Gemini 2.5 系列模型添加 thinkingBudget 参数
if (modelId && this.supportsThinkingBudget(modelId) && thinkingBudget !== undefined) {
generationConfig.thinkingConfig.thinkingBudget = thinkingBudget
config.thinkingConfig.thinkingBudget = thinkingBudget
}
}

return generationConfig
return config
}

// 将 ChatMessage 转换为 Gemini 格式的消息
Expand Down Expand Up @@ -644,24 +647,23 @@ export class GeminiProvider extends BaseLLMProvider {

const { systemInstruction, contents } = this.formatGeminiMessages(messages)

// 创建基本请求参数
const generationConfig: GenerationConfig = {
temperature: temperature || 0.7,
maxOutputTokens: maxTokens
// 创建 GenerateContentConfig
const generateContentConfig: GenerateContentConfig = this.getGenerateContentConfig(
temperature || 0.7,
maxTokens,
modelId,
false // completions 方法中不处理 reasoning
)

if (systemInstruction) {
generateContentConfig.systemInstruction = systemInstruction
}

// 执行请求
// 一次性创建 requestParams
const requestParams: GenerateContentParameters = {
model: modelId,
contents,
config: generationConfig
}

if (systemInstruction) {
requestParams.config = {
...generationConfig,
systemInstruction
}
config: generateContentConfig
}

const result = await this.genAI.models.generateContent(requestParams)
Expand Down Expand Up @@ -771,7 +773,7 @@ export class GeminiProvider extends BaseLLMProvider {
const result = await this.genAI.models.generateContent({
model: modelId,
contents: [{ role: 'user', parts: [{ text: prompt }] }],
config: this.getGenerationConfig(temperature, maxTokens, modelId, false)
config: this.getGenerateContentConfig(temperature, maxTokens, modelId, false)
})

return this.processGeminiResponse(result)
Expand Down Expand Up @@ -799,7 +801,7 @@ export class GeminiProvider extends BaseLLMProvider {
const result = await this.genAI.models.generateContent({
model: modelId,
contents: [{ role: 'user', parts: [{ text: prompt }] }],
config: this.getGenerationConfig(temperature, maxTokens, modelId, false)
config: this.getGenerateContentConfig(temperature, maxTokens, modelId, false)
})

return this.processGeminiResponse(result)
Expand Down Expand Up @@ -829,7 +831,7 @@ export class GeminiProvider extends BaseLLMProvider {
const result = await this.genAI.models.generateContent({
model: modelId,
contents: [{ role: 'user', parts: [{ text: prompt }] }],
config: this.getGenerationConfig(temperature, maxTokens, modelId, false)
config: this.getGenerateContentConfig(temperature, maxTokens, modelId, false)
})

const responseText = result.text || ''
Expand Down Expand Up @@ -890,56 +892,56 @@ export class GeminiProvider extends BaseLLMProvider {
const safetySettings = await this.getFormattedSafetySettings()
console.log('safetySettings', safetySettings)

// 将MCP工具转换为Gemini格式的工具(所有Gemini模型都支持原生工具调用)
const geminiTools =
mcpTools.length > 0
? await presenter.mcpPresenter.mcpToolsToGeminiTools(mcpTools, this.provider.id)
: undefined
// 添加Gemini工具调用
let geminiTools: Tool[] = []

// 注意:googleSearch内置工具与外部工具是互斥的
if (modelConfig.enableSearch) {
geminiTools.push({ googleSearch: {} as GoogleSearch })
} else {
if (mcpTools.length > 0)
geminiTools = await presenter.mcpPresenter.mcpToolsToGeminiTools(mcpTools, this.provider.id)
}

// 格式化消息为Gemini格式
const formattedParts = this.formatGeminiMessages(messages)

// 创建请求参数
const requestParams: GenerateContentParameters = {
model: modelId,
contents: formattedParts.contents,
config: this.getGenerationConfig(
temperature,
maxTokens,
modelId,
modelConfig.reasoning,
modelConfig.thinkingBudget
)
}
console.log('requestParams', requestParams)
// 1. 获取基础 config
const generateContentConfig: GenerateContentConfig = this.getGenerateContentConfig(
temperature,
maxTokens,
modelId,
modelConfig.reasoning,
modelConfig.thinkingBudget
)

// 2. 在本地变量上添加其他属性
if (formattedParts.systemInstruction) {
requestParams.config = {
...requestParams.config,
systemInstruction: formattedParts.systemInstruction
}
generateContentConfig.systemInstruction = formattedParts.systemInstruction
}

// 添加工具配置
if (geminiTools && geminiTools.length > 0) {
requestParams.config = {
...requestParams.config,
tools: geminiTools,
toolConfig: {
functionCallingConfig: {
mode: FunctionCallingConfigMode.AUTO // 允许模型自动决定是否调用工具
}
if (geminiTools.length > 0) {
generateContentConfig.tools = geminiTools
generateContentConfig.toolConfig = {
functionCallingConfig: {
mode: FunctionCallingConfigMode.AUTO // 允许模型自动决定是否调用工具
}
}
}

// 添加安全设置
if (safetySettings) {
requestParams.config = {
...requestParams.config,
safetySettings
}
generateContentConfig.safetySettings = safetySettings
}

// 3. 一次性创建完整的 requestParams
const requestParams: GenerateContentParameters = {
model: modelId,
contents: formattedParts.contents,
config: generateContentConfig
}

console.log('requestParams', requestParams)

// 发送流式请求
const result = await this.genAI.models.generateContentStream(requestParams)

Expand Down Expand Up @@ -1124,7 +1126,7 @@ export class GeminiProvider extends BaseLLMProvider {
const result = await this.genAI.models.generateContentStream({
model: modelId,
contents: [{ role: 'user', parts }],
config: this.getGenerationConfig(temperature, maxTokens, modelId, false) // 图像生成不需要reasoning
config: this.getGenerateContentConfig(temperature, maxTokens, modelId, false) // 图像生成不需要reasoning
})

// 处理流式响应
Expand Down
33 changes: 33 additions & 0 deletions src/renderer/src/components/settings/ModelConfigDialog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,19 @@
</div>
</div>

<!-- 联网搜索 (Gemini 支持搜索的模型) -->
<div v-if="showGeminiSearch" class="space-y-4">
<div class="flex items-center justify-between">
<div class="space-y-0.5">
<Label>{{ t('settings.model.modelConfig.enableSearch.label') }}</Label>
<p class="text-xs text-muted-foreground">
{{ t('settings.model.modelConfig.enableSearch.description') }}
</p>
</div>
<Switch v-model:checked="config.enableSearch" />
</div>
</div>

<!-- 思考预算 (Qwen3 模型) -->
<div v-if="showQwen3ThinkingBudget" class="space-y-4">
<div class="flex items-center justify-between">
Expand Down Expand Up @@ -851,6 +864,26 @@ const showGrokSearch = computed(() => {
return isGrok && isSupported
})

// 是否显示Gemini搜索配置
const showGeminiSearch = computed(() => {
const isGemini = props.providerId === 'gemini'
const modelId = props.modelId.toLowerCase()
const supportedSearchModels = [
'gemini-2.5-pro',
'gemini-2.5-flash',
'gemini-2.5-flash-lite',
'gemini-2.5-flash-lite-preview-06-17',
'gemini-2.0-flash',
'gemini-2.0-flash-lite',
'gemini-1.5-pro',
'gemini-1.5-flash'
]
const isSupported =
supportedSearchModels.some((supportedModel) => modelId.includes(supportedModel)) ||
modelId.includes('gemini')
return isGemini && isSupported
})

// 思考预算范围
const thinkingBudgetRange = computed(() => {
const modelConfig = getThinkingBudgetConfig(props.modelId)
Expand Down