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
10 changes: 6 additions & 4 deletions src/main/presenter/configPresenter/providerModelSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2502,7 +2502,10 @@ export const providerModelSettings: Record<string, { models: ProviderModelSettin
match: ['qwen-max-latest'],
vision: false,
functionCall: true,
reasoning: false
reasoning: false,
enableSearch: false,
forcedSearch: false,
searchStrategy: 'turbo'
},
{
id: 'qwen-max-2024-09-19',
Expand Down Expand Up @@ -3084,13 +3087,12 @@ export function getProviderSpecificModelConfig(
verbosity: config.verbosity,
maxCompletionTokens: config.maxCompletionTokens,
thinkingBudget: config.thinkingBudget,
enableSearch: config.enableSearch || false,
forcedSearch: config.forcedSearch || false,
enableSearch: config.enableSearch ?? false,
forcedSearch: config.forcedSearch ?? false,
searchStrategy: config.searchStrategy || 'turbo'
}
}
}

// 如果没有找到匹配的配置,返回undefined
return undefined
}
14 changes: 13 additions & 1 deletion src/main/presenter/llmProviderPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -701,7 +701,10 @@ export class LLMProviderPresenter implements ILlmProviderPresenter {
enabledMcpTools?: string[],
thinkingBudget?: number,
reasoningEffort?: 'minimal' | 'low' | 'medium' | 'high',
verbosity?: 'low' | 'medium' | 'high'
verbosity?: 'low' | 'medium' | 'high',
enableSearch?: boolean,
forcedSearch?: boolean,
searchStrategy?: 'turbo' | 'max'
): AsyncGenerator<LLMAgentEvent, void, unknown> {
Comment on lines +704 to 708
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Avoid mutating shared modelConfig; use a per-request copy.

getModelConfig may return a shared object; in concurrent streams these mutations can leak across requests. Clone before overriding.

-    const modelConfig = this.configPresenter.getModelConfig(modelId, providerId)
+    const modelConfig = { ...this.configPresenter.getModelConfig(modelId, providerId) }

Also applies to: 730-738

🤖 Prompt for AI Agents
In src/main/presenter/llmProviderPresenter/index.ts around lines 704-708 (and
similarly 730-738), getModelConfig may return a shared object that is currently
mutated for each request; clone the returned modelConfig into a per-request copy
before applying overrides (e.g., const modelConfig = structuredClone(await
getModelConfig(...)) or use a deep-clone helper if structuredClone isn't
available), then mutate that local copy and pass it down so the original shared
config is never modified and concurrent streams cannot leak state across
requests; ensure the cloned object preserves the expected type/interface before
using.

console.log(`[Agent Loop] Starting agent loop for event: ${eventId} with model: ${modelId}`)
if (!this.canStartNewStream()) {
Expand All @@ -724,6 +727,15 @@ export class LLMProviderPresenter implements ILlmProviderPresenter {
if (verbosity !== undefined) {
modelConfig.verbosity = verbosity
}
if (enableSearch !== undefined) {
modelConfig.enableSearch = enableSearch
}
if (forcedSearch !== undefined) {
modelConfig.forcedSearch = forcedSearch
}
if (searchStrategy !== undefined) {
modelConfig.searchStrategy = searchStrategy
}

this.activeStreams.set(eventId, {
isGenerating: true,
Expand Down
20 changes: 15 additions & 5 deletions src/main/presenter/sqlitePresenter/importData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,10 @@ export class DataImporter {
enabled_mcp_tools,
thinking_budget,
reasoning_effort,
verbosity
verbosity,
enable_search,
forced_search,
search_strategy
FROM conversations`
)
.all() as any[]
Expand All @@ -110,7 +113,10 @@ export class DataImporter {
enabled_mcp_tools: null,
thinking_budget: null,
reasoning_effort: null,
verbosity: null
verbosity: null,
enable_search: null,
forced_search: null,
search_strategy: null
}))
} catch (fallbackError) {
throw new Error(
Expand Down Expand Up @@ -165,8 +171,9 @@ export class DataImporter {
conv_id, title, created_at, updated_at, system_prompt,
temperature, context_length, max_tokens, provider_id,
model_id, is_pinned, is_new, artifacts, enabled_mcp_tools,
thinking_budget, reasoning_effort, verbosity
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
thinking_budget, reasoning_effort, verbosity,
enable_search, forced_search, search_strategy
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
)
.run(
conv.conv_id,
Expand All @@ -185,7 +192,10 @@ export class DataImporter {
conv.enabled_mcp_tools || null,
conv.thinking_budget || null,
conv.reasoning_effort || null,
conv.verbosity || null
conv.verbosity || null,
conv.enable_search ?? null,
conv.forced_search ?? null,
conv.search_strategy ?? null
Comment on lines +195 to +198
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: verbosity may be dropped when value is 0 due to || null.

Use nullish coalescing to preserve 0. Current code would store NULL instead of 0.

Apply:

-          conv.verbosity || null,
+          conv.verbosity ?? null,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
conv.verbosity || null,
conv.enable_search ?? null,
conv.forced_search ?? null,
conv.search_strategy ?? null
conv.verbosity ?? null,
conv.enable_search ?? null,
conv.forced_search ?? null,
conv.search_strategy ?? null
🤖 Prompt for AI Agents
In src/main/presenter/sqlitePresenter/importData.ts around lines 195 to 198, the
code uses "conv.verbosity || null" which will coerce falsy values like 0 to
null; change that to use nullish coalescing (conv.verbosity ?? null) so numeric
zero is preserved, leaving the other fields (enable_search, forced_search,
search_strategy) unchanged.

)
} catch {
// 如果失败,使用基础字段的INSERT语句(兼容旧版本目标数据库)
Expand Down
59 changes: 51 additions & 8 deletions src/main/presenter/sqlitePresenter/tables/conversations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ type ConversationRow = {
thinking_budget: number | null
reasoning_effort: string | null
verbosity: string | null
enable_search: number | null
forced_search: number | null
search_strategy: string | null
}

// 解析 JSON 字段
Expand Down Expand Up @@ -106,12 +109,20 @@ export class ConversationsTable extends BaseTable {
ALTER TABLE conversations ADD COLUMN verbosity TEXT DEFAULT NULL;
`
}
if (version === 7) {
return `
-- 添加搜索相关字段
ALTER TABLE conversations ADD COLUMN enable_search INTEGER DEFAULT NULL;
ALTER TABLE conversations ADD COLUMN forced_search INTEGER DEFAULT NULL;
ALTER TABLE conversations ADD COLUMN search_strategy TEXT DEFAULT NULL;
`
}

return null
}

getLatestVersion(): number {
return 6
return 7
}

async create(title: string, settings: Partial<CONVERSATION_SETTINGS> = {}): Promise<string> {
Expand All @@ -133,9 +144,12 @@ export class ConversationsTable extends BaseTable {
enabled_mcp_tools,
thinking_budget,
reasoning_effort,
verbosity
verbosity,
enable_search,
forced_search,
search_strategy
)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
`)
const conv_id = nanoid()
const now = Date.now()
Expand All @@ -156,7 +170,10 @@ export class ConversationsTable extends BaseTable {
settings.enabledMcpTools ? JSON.stringify(settings.enabledMcpTools) : 'NULL',
settings.thinkingBudget !== undefined ? settings.thinkingBudget : null,
settings.reasoningEffort !== undefined ? settings.reasoningEffort : null,
settings.verbosity !== undefined ? settings.verbosity : null
settings.verbosity !== undefined ? settings.verbosity : null,
settings.enableSearch !== undefined ? (settings.enableSearch ? 1 : 0) : null,
settings.forcedSearch !== undefined ? (settings.forcedSearch ? 1 : 0) : null,
settings.searchStrategy !== undefined ? settings.searchStrategy : null
)
Comment on lines +173 to 177
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Tri-state semantics: allow reverting to NULL.

Create supports NULL (inherit defaults), but update path below only writes 0/1 and cannot clear back to NULL. This breaks “use provider default” after a user toggled the setting once.

Apply:

-      settings.enableSearch !== undefined ? (settings.enableSearch ? 1 : 0) : null,
-      settings.forcedSearch !== undefined ? (settings.forcedSearch ? 1 : 0) : null,
+      settings.enableSearch !== undefined ? (settings.enableSearch ? 1 : 0) : null,
+      settings.forcedSearch !== undefined ? (settings.forcedSearch ? 1 : 0) : null,
       settings.searchStrategy !== undefined ? settings.searchStrategy : null

And mirror the same tri-state logic in update() (see next comment) by supporting explicit NULL assignment.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/main/presenter/sqlitePresenter/tables/conversations.ts around lines
173-177, the create path already allows tri-state (value/0/1 or NULL) but the
update path currently only writes 0/1 and cannot clear to NULL; update the
update() assignments to mirror the create ternary logic so explicit NULL can be
stored when the incoming settings property is undefined vs. intentionally null —
specifically: for verbosity and searchStrategy assign settings.verbosity !==
undefined ? settings.verbosity : null and settings.searchStrategy !== undefined
? settings.searchStrategy : null, and for enableSearch and forcedSearch assign
settings.enableSearch !== undefined ? (settings.enableSearch ? 1 : 0) : null and
settings.forcedSearch !== undefined ? (settings.forcedSearch ? 1 : 0) : null so
update can clear back to NULL (use provider default).

return conv_id
}
Expand All @@ -182,7 +199,10 @@ export class ConversationsTable extends BaseTable {
enabled_mcp_tools,
thinking_budget,
reasoning_effort,
verbosity
verbosity,
enable_search,
forced_search,
search_strategy
FROM conversations
WHERE conv_id = ?
`
Expand Down Expand Up @@ -213,7 +233,12 @@ export class ConversationsTable extends BaseTable {
reasoningEffort: result.reasoning_effort
? (result.reasoning_effort as 'minimal' | 'low' | 'medium' | 'high')
: undefined,
verbosity: result.verbosity ? (result.verbosity as 'low' | 'medium' | 'high') : undefined
verbosity: result.verbosity ? (result.verbosity as 'low' | 'medium' | 'high') : undefined,
enableSearch: result.enable_search !== null ? Boolean(result.enable_search) : undefined,
forcedSearch: result.forced_search !== null ? Boolean(result.forced_search) : undefined,
searchStrategy: result.search_strategy
? (result.search_strategy as 'turbo' | 'max')
: undefined
}
}
}
Expand Down Expand Up @@ -282,6 +307,18 @@ export class ConversationsTable extends BaseTable {
updates.push('verbosity = ?')
params.push(data.settings.verbosity)
}
if (data.settings.enableSearch !== undefined) {
updates.push('enable_search = ?')
params.push(data.settings.enableSearch ? 1 : 0)
}
if (data.settings.forcedSearch !== undefined) {
updates.push('forced_search = ?')
params.push(data.settings.forcedSearch ? 1 : 0)
}
if (data.settings.searchStrategy !== undefined) {
updates.push('search_strategy = ?')
params.push(data.settings.searchStrategy)
}
Comment on lines +310 to +321
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Update path cannot clear values back to NULL.

Currently only true/false writes are possible; no way to “unset”. Support null (meaning revert to SQL NULL) in updates.

Apply:

-      if (data.settings.enableSearch !== undefined) {
+      if ('enableSearch' in data.settings) {
         updates.push('enable_search = ?')
-        params.push(data.settings.enableSearch ? 1 : 0)
+        params.push(
+          data.settings.enableSearch === null
+            ? null
+            : data.settings.enableSearch
+            ? 1
+            : 0
+        )
       }
-      if (data.settings.forcedSearch !== undefined) {
+      if ('forcedSearch' in data.settings) {
         updates.push('forced_search = ?')
-        params.push(data.settings.forcedSearch ? 1 : 0)
+        params.push(
+          data.settings.forcedSearch === null
+            ? null
+            : data.settings.forcedSearch
+            ? 1
+            : 0
+        )
       }
-      if (data.settings.searchStrategy !== undefined) {
+      if ('searchStrategy' in data.settings) {
         updates.push('search_strategy = ?')
-        params.push(data.settings.searchStrategy)
+        params.push(data.settings.searchStrategy ?? null)
       }

Follow-up: allow null in the corresponding TypeScript types used for updates.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (data.settings.enableSearch !== undefined) {
updates.push('enable_search = ?')
params.push(data.settings.enableSearch ? 1 : 0)
}
if (data.settings.forcedSearch !== undefined) {
updates.push('forced_search = ?')
params.push(data.settings.forcedSearch ? 1 : 0)
}
if (data.settings.searchStrategy !== undefined) {
updates.push('search_strategy = ?')
params.push(data.settings.searchStrategy)
}
if ('enableSearch' in data.settings) {
updates.push('enable_search = ?')
params.push(
data.settings.enableSearch === null
? null
: data.settings.enableSearch
? 1
: 0
)
}
if ('forcedSearch' in data.settings) {
updates.push('forced_search = ?')
params.push(
data.settings.forcedSearch === null
? null
: data.settings.forcedSearch
? 1
: 0
)
}
if ('searchStrategy' in data.settings) {
updates.push('search_strategy = ?')
params.push(data.settings.searchStrategy ?? null)
}

}
if (updates.length > 0 || data.updatedAt) {
updates.push('updated_at = ?')
Expand Down Expand Up @@ -329,7 +366,10 @@ export class ConversationsTable extends BaseTable {
enabled_mcp_tools,
thinking_budget,
reasoning_effort,
verbosity
verbosity,
enable_search,
forced_search,
search_strategy
FROM conversations
ORDER BY updated_at DESC
LIMIT ? OFFSET ?
Expand Down Expand Up @@ -359,7 +399,10 @@ export class ConversationsTable extends BaseTable {
reasoningEffort: row.reasoning_effort
? (row.reasoning_effort as 'minimal' | 'low' | 'medium' | 'high')
: undefined,
verbosity: row.verbosity ? (row.verbosity as 'low' | 'medium' | 'high') : undefined
verbosity: row.verbosity ? (row.verbosity as 'low' | 'medium' | 'high') : undefined,
enableSearch: row.enable_search !== null ? Boolean(row.enable_search) : undefined,
forcedSearch: row.forced_search !== null ? Boolean(row.forced_search) : undefined,
searchStrategy: row.search_strategy ? (row.search_strategy as 'turbo' | 'max') : undefined
}
}))
}
Expand Down
30 changes: 24 additions & 6 deletions src/main/presenter/threadPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1778,7 +1778,10 @@ export class ThreadPresenter implements IThreadPresenter {
enabledMcpTools: currentEnabledMcpTools,
thinkingBudget: currentThinkingBudget,
reasoningEffort: currentReasoningEffort,
verbosity: currentVerbosity
verbosity: currentVerbosity,
enableSearch: currentEnableSearch,
forcedSearch: currentForcedSearch,
searchStrategy: currentSearchStrategy
} = currentConversation.settings
const stream = this.llmProviderPresenter.startStreamCompletion(
currentProviderId, // 使用最新的设置
Expand All @@ -1790,7 +1793,10 @@ export class ThreadPresenter implements IThreadPresenter {
currentEnabledMcpTools,
currentThinkingBudget,
currentReasoningEffort,
currentVerbosity
currentVerbosity,
currentEnableSearch,
currentForcedSearch,
currentSearchStrategy
)
for await (const event of stream) {
const msg = event.data
Expand Down Expand Up @@ -1896,7 +1902,10 @@ export class ThreadPresenter implements IThreadPresenter {
enabledMcpTools,
thinkingBudget,
reasoningEffort,
verbosity
verbosity,
enableSearch,
forcedSearch,
searchStrategy
} = conversation.settings
const modelConfig = this.configPresenter.getModelConfig(modelId, providerId)

Expand Down Expand Up @@ -1968,7 +1977,10 @@ export class ThreadPresenter implements IThreadPresenter {
enabledMcpTools,
thinkingBudget,
reasoningEffort,
verbosity
verbosity,
enableSearch,
forcedSearch,
searchStrategy
)
for await (const event of stream) {
const msg = event.data
Expand Down Expand Up @@ -4011,7 +4023,10 @@ export class ThreadPresenter implements IThreadPresenter {
enabledMcpTools,
thinkingBudget,
reasoningEffort,
verbosity
verbosity,
enableSearch,
forcedSearch,
searchStrategy
} = conversation.settings
const modelConfig = this.configPresenter.getModelConfig(modelId, providerId)

Expand Down Expand Up @@ -4069,7 +4084,10 @@ export class ThreadPresenter implements IThreadPresenter {
enabledMcpTools,
thinkingBudget,
reasoningEffort,
verbosity
verbosity,
enableSearch,
forcedSearch,
searchStrategy
)

for await (const event of stream) {
Expand Down
Loading