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
76 changes: 76 additions & 0 deletions src/components/settings/sections/web-search-section.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ export function WebSearchSection() {
setTimeout(() => setSavedId((cur) => (cur === "anytxt" ? null : cur)), 1500)
}

function updateDeepResearch(
patch: Pick<SearchApiConfig, "deepResearchConcurrency" | "deepResearchMaxSources">,
) {
persist(resolveSearchConfig({ ...resolvedConfig, ...patch })).catch(() => {})
setSavedId("deepResearch")
setTimeout(() => setSavedId((cur) => (cur === "deepResearch" ? null : cur)), 1500)
}

return (
<div className="space-y-4">
<div>
Expand Down Expand Up @@ -273,6 +281,74 @@ export function WebSearchSection() {
</p>
</div>

<div className="space-y-3 rounded-lg border p-3">
<div className="flex items-start justify-between gap-3">
<div>
<Label>{t("settings.sections.webSearch.deepResearchTitle")}</Label>
<p className="mt-1 text-xs text-muted-foreground">
{t("settings.sections.webSearch.deepResearchDescription")}
</p>
</div>
{savedId === "deepResearch" && (
<span className="shrink-0 text-[10px] text-emerald-600">
{t("settings.sections.webSearch.savedBadge")}
</span>
)}
</div>
<div className="grid gap-3 md:grid-cols-2">
<div className="space-y-2">
<Label>{t("settings.sections.webSearch.deepResearchConcurrency")}</Label>
<Input
type="number"
min={1}
max={8}
step={1}
value={resolvedConfig.deepResearchConcurrency ?? ""}
placeholder="3"
onChange={(e) => {
const val = e.target.value.trim()
if (!val) {
updateDeepResearch({ deepResearchConcurrency: undefined })
return
}
const n = Math.floor(Number(val))
updateDeepResearch({
deepResearchConcurrency: Number.isFinite(n) ? Math.min(8, Math.max(1, n)) : undefined,
})
}}
/>
<p className="text-xs text-muted-foreground">
{t("settings.sections.webSearch.deepResearchConcurrencyHint")}
</p>
</div>
<div className="space-y-2">
<Label>{t("settings.sections.webSearch.deepResearchMaxSources")}</Label>
<Input
type="number"
min={1}
max={100}
step={1}
value={resolvedConfig.deepResearchMaxSources ?? ""}
placeholder="20"
onChange={(e) => {
const val = e.target.value.trim()
if (!val) {
updateDeepResearch({ deepResearchMaxSources: undefined })
return
}
const n = Math.floor(Number(val))
updateDeepResearch({
deepResearchMaxSources: Number.isFinite(n) ? Math.min(100, Math.max(1, n)) : undefined,
})
}}
/>
<p className="text-xs text-muted-foreground">
{t("settings.sections.webSearch.deepResearchMaxSourcesHint")}
</p>
</div>
</div>
</div>

<div className="space-y-2">
<Label>{t("settings.sections.webSearch.webProviders")}</Label>
{SEARCH_PROVIDERS.map((provider) => {
Expand Down
6 changes: 6 additions & 0 deletions src/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,12 @@
"sourceWeb": "Web Search",
"sourceAnyTxt": "AnyTXT",
"sourceBoth": "Both",
"deepResearchTitle": "Deep Research",
"deepResearchDescription": "Tune how many research tasks run at once and how many sources are merged into each page.",
"deepResearchConcurrency": "Concurrent research tasks",
"deepResearchConcurrencyHint": "How many Deep Research tasks run in parallel. Lower this on rate-limited or slow endpoints.",
"deepResearchMaxSources": "Max merged sources",
"deepResearchMaxSourcesHint": "Upper bound on the total web + local results merged into each research page.",
"webProviders": "Web Search providers",
"anyTxtTitle": "AnyTXT local file search",
"anyTxtDescription": "Use AnyTXT's local JSON-RPC API as a Deep Research source. Keep ATGUI.exe running before starting research.",
Expand Down
6 changes: 6 additions & 0 deletions src/i18n/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,12 @@
"sourceWeb": "网页搜索",
"sourceAnyTxt": "AnyTXT",
"sourceBoth": "两者都用",
"deepResearchTitle": "深度研究",
"deepResearchDescription": "调整同时运行的研究任务数量,以及合并到每个页面的来源数量。",
"deepResearchConcurrency": "并发研究任务",
"deepResearchConcurrencyHint": "同时并行运行的 Deep Research 任务数量。在速率受限或较慢的端点上请调低。",
"deepResearchMaxSources": "最大合并来源数",
"deepResearchMaxSourcesHint": "合并到每个研究页面的网页与本地结果总数上限。",
"webProviders": "网页搜索 Provider",
"anyTxtTitle": "AnyTXT 本地文件搜索",
"anyTxtDescription": "把 AnyTXT 的本地 JSON-RPC API 作为 Deep Research 的信息来源。开始研究前请保持 ATGUI.exe 运行。",
Expand Down
9 changes: 6 additions & 3 deletions src/lib/deep-research.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export async function collectResearchSources(
options: CollectResearchSourceOptions = {},
): Promise<ResearchSourceCollection> {
const resolvedSearchConfig = resolveSearchConfig(searchConfig)
const maxSources = resolvedSearchConfig.deepResearchMaxSources ?? MAX_RESEARCH_SOURCES
const sourceMode = resolvedSearchConfig.deepResearchSource ?? "web"
const useWeb = sourceMode === "web" || sourceMode === "both"
const useAnyTxt = hasAnyTxtSource(resolvedSearchConfig) && hasConfiguredAnyTxt(resolvedSearchConfig.anyTxt)
Expand All @@ -99,9 +100,9 @@ export async function collectResearchSources(

function addResults(results: import("./web-search").WebSearchResult[]) {
for (const r of results) {
if (allResults.length >= MAX_RESEARCH_SOURCES) {
if (allResults.length >= maxSources) {
if (!cappedWarned) {
console.info(`[DeepResearch] capped at ${MAX_RESEARCH_SOURCES} research sources; later results were truncated.`)
console.info(`[DeepResearch] capped at ${maxSources} research sources; later results were truncated.`)
cappedWarned = true
}
return
Expand Down Expand Up @@ -170,7 +171,9 @@ function processQueue(
) {
const store = useResearchStore.getState()
const running = store.getRunningCount()
const available = store.maxConcurrent - running
const resolved = resolveSearchConfig(searchConfig)
const concurrencyLimit = resolved.deepResearchConcurrency ?? store.maxConcurrent
const available = concurrencyLimit - running

for (let i = 0; i < available; i++) {
const next = useResearchStore.getState().getNextQueued()
Expand Down
6 changes: 6 additions & 0 deletions src/stores/wiki-store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ interface SearchApiConfig {
providerConfigs?: SearchProviderConfigs
deepResearchSource?: DeepResearchSource
anyTxt?: AnyTxtConfig
/** Max Deep Research tasks running in parallel. Falls back to 3 when unset. */
deepResearchConcurrency?: number
/** Upper bound on merged web + local sources per research page. Falls back to 20 when unset. */
deepResearchMaxSources?: number
}

interface EmbeddingConfig {
Expand Down Expand Up @@ -414,6 +418,8 @@ export const useWikiStore = create<WikiState>((set) => ({
searXngCategories: ["general"],
providerConfigs: {},
deepResearchSource: "web",
deepResearchConcurrency: 3,
deepResearchMaxSources: 20,
anyTxt: {
enabled: false,
endpoint: "http://127.0.0.1:9920",
Expand Down