feat: support setting concurrency limits in web search

This commit is contained in:
AnotiaWang
2025-02-17 16:20:25 +08:00
parent c10090d7d9
commit fc83d9387e
9 changed files with 80 additions and 22 deletions

View File

@ -24,6 +24,10 @@ Please give a 🌟 Star if you like this project!
## Recent updates
25/02/17
- Added: set rate limits for web search
25/02/16
- Refactored the search visualization using VueFlow
@ -48,7 +52,6 @@ Please give a 🌟 Star if you like this project!
- Added Docker support
- Fixed "export as PDF" issues
## How to use
Live demo: <a href="https://deep-research.ataw.top" target="_blank">https://deep-research.ataw.top</a>

View File

@ -25,7 +25,7 @@
25/02/15
- AI 提供商支持 DeepSeekOpenRouter 和 Ollama联网搜支持 Firecrawl
- AI 提供商支持 DeepSeekOpenRouter 和 Ollama联网搜支持 Firecrawl
- 支持检查项目更新
- 支持重新生成报告
- 一般性优化和改进

View File

@ -239,6 +239,19 @@
private
/>
</UFormField>
<UFormField :label="$t('settings.webSearch.concurrencyLimit')">
<template #help>
{{ $t('settings.webSearch.concurrencyLimitHelp') }}
</template>
<UInput
v-model="config.webSearch.concurrencyLimit"
class="w-15"
type="number"
:min="1"
:max="5"
:step="1"
/>
</UFormField>
</div>
</template>
<template #footer>

View File

@ -1,5 +1,4 @@
<script setup lang="ts">
import { computed, defineProps } from 'vue'
import { Handle, Position } from '@vue-flow/core'
import type { ButtonProps } from '@nuxt/ui'
import type { SearchNodeData } from './SearchFlow.vue'

30
composables/usePLimit.ts Normal file
View File

@ -0,0 +1,30 @@
import pLimit from 'p-limit'
/**
* The concurrency value used by the global limit.
* This represents the *actual* limit value.
* The value in `globalLimit` should not be used, because `deepResearch` uses recursive calls,
* and `globalLimit.concurrency` can be much higher than the actual one.
*/
let globalLimitConcurrency = 2
const globalLimit = pLimit(globalLimitConcurrency)
export function usePLimit() {
const { config } = useConfigStore()
if (
config.webSearch.concurrencyLimit &&
config.webSearch.concurrencyLimit >= 1 &&
globalLimitConcurrency !== config.webSearch.concurrencyLimit
) {
console.log(
`[usePLimit] Updating concurrency from ${globalLimitConcurrency} to ${config.webSearch.concurrencyLimit}. Current concurrency: ${globalLimit.concurrency}`,
)
let newLimit = config.webSearch.concurrencyLimit
let diff = newLimit - globalLimitConcurrency
globalLimitConcurrency = newLimit
globalLimit.concurrency += diff
}
return globalLimit
}

View File

@ -38,7 +38,9 @@
"firecrawl": {
"help": "Get one API key at {0}."
}
}
},
"concurrencyLimitHelp": "Limit the concurrent search tasks. This is useful to avoid overloading the search provider and causing requests to fail.",
"concurrencyLimit": "Concurrency Limit"
}
},
"researchTopic": {

View File

@ -38,7 +38,9 @@
"tavily": {
"help": "和 Firecrawl 类似,不过提供了每月 1000 次免费搜索。在 {0} 获取一个 API key。"
}
}
},
"concurrencyLimit": "并发数",
"concurrencyLimitHelp": "限制同时进行的搜索数量。这样可以避免被 API 服务限流,导致请求失败。"
}
},
"researchTopic": {

View File

@ -61,9 +61,6 @@ export type ResearchStep =
| { type: 'error'; message: string; nodeId: string }
| { type: 'complete'; learnings: string[]; visitedUrls: string[] }
// increase this if you have higher API rate limits
const ConcurrencyLimit = 2
/**
* Schema for {@link generateSearchQueries} without dynamic descriptions
*/
@ -242,6 +239,7 @@ export async function deepResearch({
}): Promise<ResearchResult> {
const { t } = useNuxtApp().$i18n
const language = t('language', {}, { locale: languageCode })
const globalLimit = usePLimit()
onProgress({
type: 'generating_query',
@ -257,7 +255,6 @@ export async function deepResearch({
language,
searchLanguage,
})
const limit = pLimit(ConcurrencyLimit)
let searchQueries: PartialSearchQuery[] = []
@ -322,7 +319,7 @@ export async function deepResearch({
// Run in parallel and limit the concurrency
const results = await Promise.all(
searchQueries.map((searchQuery, i) =>
limit(async () => {
globalLimit(async () => {
if (!searchQuery?.query) {
return {
learnings: [],
@ -433,7 +430,10 @@ export async function deepResearch({
.join('')}
`.trim()
return deepResearch({
// Add concurrency by 1, and do next recursive search
globalLimit.concurrency++
try {
const r = await deepResearch({
query: nextQuery,
breadth: nextBreadth,
maxDepth,
@ -444,6 +444,12 @@ export async function deepResearch({
nodeId: childNodeId(nodeId, i),
languageCode,
})
return r
} catch (error) {
throw error
} finally {
globalLimit.concurrency--
}
} else {
return {
learnings: allLearnings,

View File

@ -21,6 +21,8 @@ export interface ConfigWebSearch {
apiKey?: string
/** Force the LLM to generate serp queries in a certain language */
searchLanguage?: Locale
/** Limit the number of concurrent tasks globally */
concurrencyLimit?: number
}
export interface Config {
@ -37,8 +39,9 @@ export const useConfigStore = defineStore('config', () => {
},
webSearch: {
provider: 'tavily',
concurrencyLimit: 2,
},
})
} satisfies Config)
// The version user dismissed the update notification
const dismissUpdateVersion = useLocalStorage<string>(
'dismiss-update-version',