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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions frontend/src/typings/entities/shareable.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,8 @@ namespace Shareable {
safetySettings?: SafetySetting[]

toolCallLimit?: ToolCallLimitConfig
/** Gemini Generate Content built-in tools enabled by the preset. */
geminiBuiltinTools?: Array<'googleSearch' | 'googleMaps' | 'codeExecution' | 'urlContext'>
}

interface SafetySetting {
Expand Down Expand Up @@ -212,6 +214,8 @@ namespace Shareable {
dynamicContextHistory?: 'use_system' | 'retain' | 'discard'
/** Built-in tool categories enabled for this preset. */
builtinToolCategories?: Array<'mcp-discovery' | 'mcp-management' | 'skill-management'>
/** Whether to start each turn without previous user/assistant conversation context. */
disableConversationContext?: boolean
/**
* 禁止系统prompt
*/
Expand Down
46 changes: 45 additions & 1 deletion frontend/src/views/presets/PresetFormModal.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,14 @@ const showModal = computed({
const groupContextOptions = [
{ label: '禁用', value: 'disable' },
{ label: '启用', value: 'enabled' },
{ label: '使用系统提示词', value: 'use_system' },
{ label: '使用全局配置', value: 'use_system' },
]

const geminiBuiltinToolOptions = [
{ label: 'Google Search', value: 'googleSearch' },
{ label: 'Google Maps', value: 'googleMaps' },
{ label: '代码执行', value: 'codeExecution' },
{ label: '网址上下文', value: 'urlContext' },
]

const dynamicContextHistoryOptions = [
Expand Down Expand Up @@ -124,6 +131,7 @@ const addPreset = ref<Partial<Shareable.PresetModel>>({
tools: [],
} as Shareable.ToolChoice,
toolGroupId: ['default_local'],
geminiBuiltinTools: [],
responseModalities: undefined,
safetySettings: undefined,
} as Shareable.SendMessageOption,
Expand All @@ -133,6 +141,7 @@ const addPreset = ref<Partial<Shareable.PresetModel>>({
dynamicContextHistory: 'use_system',
builtinToolCategories: ['mcp-discovery', 'mcp-management', 'skill-management'],
disableSystemInstructions: false,
disableConversationContext: false,
})

// Initialize form when in edit mode
Expand All @@ -153,6 +162,12 @@ watch(() => props.initialData, (newVal) => {
if (!Array.isArray(data.builtinToolCategories)) {
data.builtinToolCategories = ['mcp-discovery', 'mcp-management', 'skill-management']
}
if (!Array.isArray(data.sendMessageOption.geminiBuiltinTools)) {
data.sendMessageOption.geminiBuiltinTools = []
}
if (data.disableConversationContext === undefined) {
data.disableConversationContext = false
}

addPreset.value = data
}
Expand Down Expand Up @@ -214,6 +229,7 @@ watch(showModal, (val) => {
tools: [],
},
toolGroupId: ['default_local'],
geminiBuiltinTools: [],
responseModalities: undefined,
safetySettings: undefined,
},
Expand All @@ -222,6 +238,8 @@ watch(showModal, (val) => {
groupContext: 'use_system',
dynamicContextHistory: 'use_system',
builtinToolCategories: ['mcp-discovery', 'mcp-management', 'skill-management'],
disableSystemInstructions: false,
disableConversationContext: false,
}
}
})
Expand Down Expand Up @@ -290,6 +308,9 @@ watch(showModal, (val) => {
<NCheckbox v-model:checked="addPreset.sendMessageOption!!.disableHistorySave">
禁用历史记录保存
</NCheckbox>
<NCheckbox v-model:checked="addPreset.disableConversationContext">
禁用对话上下文
</NCheckbox>
</NSpace>
</NFormItemGridItem>

Expand Down Expand Up @@ -333,6 +354,29 @@ watch(showModal, (val) => {
</NCheckboxGroup>
</NFormItemGridItem>

<!-- Gemini 内置工具 -->
<NFormItemGridItem span="24" label="Gemini 内置工具" path="sendMessageOption.geminiBuiltinTools">
<NCheckboxGroup v-model:value="addPreset.sendMessageOption!!.geminiBuiltinTools">
<NSpace>
<NCheckbox
v-for="option in geminiBuiltinToolOptions"
:key="option.value"
:value="option.value"
>
{{ option.label }}
</NCheckbox>
</NSpace>
</NCheckboxGroup>
<NTooltip placement="top" trigger="hover">
<template #trigger>
<NIcon size="18" class="ml-2" style="margin-left: 8px; cursor: pointer;">
<InformationCircleOutline />
</NIcon>
</template>
<span>仅 Gemini Generate Content API 生效;留空时不会改变原有请求。</span>
</NTooltip>
</NFormItemGridItem>

<!-- 禁止系统prompt -->
<NFormItemGridItem span="12 s:12 m:12" label="禁止系统prompt" path="sendMessageOption.disableSystemInstructions">
<NCheckbox v-model:checked="addPreset.disableSystemInstructions">
Expand Down
41 changes: 39 additions & 2 deletions src/adapters/impl/gemini/GeminiClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,57 @@ export class GeminiClient extends AbstractClient {
}
messages.push(geminiContent)
}
const normalizedBaseUrl = this.baseUrl.replace(/\/+$/, '')
const hasExplicitApiVersion = /\/v1(?:alpha|beta)?$/i.test(normalizedBaseUrl)
const isGoogleApi = /:\/\/(?:generativelanguage|[a-z0-9-]+-aiplatform)\.googleapis\.com(?:\/|$)/i.test(normalizedBaseUrl)
const hasBuiltinTools = (options.geminiBuiltinTools?.length || 0) > 0
let apiVersion: string | undefined
if (hasBuiltinTools) {
if (hasExplicitApiVersion) {
apiVersion = ''
} else if (!isGoogleApi) {
apiVersion = 'v1'
}
}
const ai = new GoogleGenAI({
apiKey,
httpOptions: {
baseUrl: this.baseUrl,
headers: {
'x-request-from': 'node-chaite/' + VERSION,
},
// GoogleGenAI defaults to v1beta. GCLI2API exposes its native Gemini
// endpoint under /v1; opt into that only for custom endpoints when a
// Gemini built-in tool is explicitly enabled, preserving the default
// request path when the new feature is disabled.
...(apiVersion !== undefined ? { apiVersion } : {}),
},
})

const functionDeclarations = this.tools.map(toolConverter)
const tools: Tool[] | undefined = functionDeclarations.length > 0 ? [{
const functionTools: Tool[] = functionDeclarations.length > 0 ? [{
functionDeclarations,
}] : undefined
}] : []
const builtinTools: Tool[] = []
for (const builtinTool of options.geminiBuiltinTools || []) {
switch (builtinTool) {
case 'googleSearch':
builtinTools.push({ googleSearch: {} })
break
case 'googleMaps':
builtinTools.push({ googleMaps: {} })
break
case 'codeExecution':
builtinTools.push({ codeExecution: {} })
break
case 'urlContext':
builtinTools.push({ urlContext: {} })
break
}
}
const tools: Tool[] | undefined = functionTools.length > 0 || builtinTools.length > 0
? [...functionTools, ...builtinTools]
: undefined

const modeMap = {
'none': FunctionCallingConfigMode.NONE,
Expand Down
11 changes: 10 additions & 1 deletion src/channels/preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export class ChatPreset extends AbstractShareable<ChatPreset> {
this.dynamicContextHistory = params.dynamicContextHistory
this.builtinToolCategories = params.builtinToolCategories
this.disableSystemInstructions = params.disableSystemInstructions
this.disableConversationContext = params.disableConversationContext
}
}

Expand All @@ -34,6 +35,8 @@ export class ChatPreset extends AbstractShareable<ChatPreset> {
dynamicContextHistory?: 'use_system' | 'retain' | 'discard'
/** Built-in tool categories enabled for this preset. Undefined enables all for compatibility. */
builtinToolCategories?: Array<'mcp-discovery' | 'mcp-management' | 'skill-management'>
/** Whether to start each turn from a detached history branch. */
disableConversationContext?: boolean
/**
* 禁止系统prompt
*/
Expand All @@ -58,6 +61,7 @@ export class ChatPreset extends AbstractShareable<ChatPreset> {
dynamicContextHistory: this.dynamicContextHistory,
builtinToolCategories: this.builtinToolCategories,
disableSystemInstructions: this.disableSystemInstructions,
disableConversationContext: this.disableConversationContext,
})
}

Expand All @@ -81,6 +85,7 @@ export class ChatPreset extends AbstractShareable<ChatPreset> {
preset.dynamicContextHistory = raw.dynamicContextHistory
preset.builtinToolCategories = raw.builtinToolCategories
preset.disableSystemInstructions = raw.disableSystemInstructions
preset.disableConversationContext = raw.disableConversationContext

if (raw.sendMessageOption) {
preset.sendMessageOption = typeof raw.sendMessageOption === 'string'
Expand Down Expand Up @@ -147,7 +152,11 @@ export class ChatPreset extends AbstractShareable<ChatPreset> {
} else if (this.groupContext === 'enabled') {
base += '\n携带群聊上下文'
} else if (this.groupContext === 'use_system') {
base += '\n群聊上下文使用系统预设'
base += '\n群聊上下文使用全局配置'
}

if (this.disableConversationContext) {
base += '\n禁止对话上下文'
}

if (this.disableSystemInstructions) {
Expand Down
5 changes: 5 additions & 0 deletions src/types/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export class SendMessageOption implements Serializable, DeSerializable<SendMessa
this.planId = option.planId
this.skillName = option.skillName
this.builtinToolCategories = option.builtinToolCategories
this.geminiBuiltinTools = option.geminiBuiltinTools
}

static create(options?: SendMessageOption | Partial<SendMessageOption>): SendMessageOption {
Expand Down Expand Up @@ -128,6 +129,9 @@ export class SendMessageOption implements Serializable, DeSerializable<SendMessa
/** Preset-selected categories of built-in tools. Undefined keeps compatibility by enabling all. */
builtinToolCategories?: Array<'mcp-discovery' | 'mcp-management' | 'skill-management'>

/** Gemini Generate Content built-in tools enabled by the preset. */
geminiBuiltinTools?: Array<'googleSearch' | 'googleMaps' | 'codeExecution' | 'urlContext'>

/**
* 流模式的回调
* @param chunk
Expand Down Expand Up @@ -167,6 +171,7 @@ export class SendMessageOption implements Serializable, DeSerializable<SendMessa
safetySettings: this.safetySettings,
toolCallLimit: this.toolCallLimit,
builtinToolCategories: this.builtinToolCategories,
geminiBuiltinTools: this.geminiBuiltinTools,
}
return JSON.stringify(json)
}
Expand Down