feat: support setting concurrency limits in web search
This commit is contained in:
@ -24,6 +24,10 @@ Please give a 🌟 Star if you like this project!
|
|||||||
|
|
||||||
## Recent updates
|
## Recent updates
|
||||||
|
|
||||||
|
25/02/17
|
||||||
|
|
||||||
|
- Added: set rate limits for web search
|
||||||
|
|
||||||
25/02/16
|
25/02/16
|
||||||
|
|
||||||
- Refactored the search visualization using VueFlow
|
- Refactored the search visualization using VueFlow
|
||||||
@ -48,7 +52,6 @@ Please give a 🌟 Star if you like this project!
|
|||||||
- Added Docker support
|
- Added Docker support
|
||||||
- Fixed "export as PDF" issues
|
- Fixed "export as PDF" issues
|
||||||
|
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
|
||||||
Live demo: <a href="https://deep-research.ataw.top" target="_blank">https://deep-research.ataw.top</a>
|
Live demo: <a href="https://deep-research.ataw.top" target="_blank">https://deep-research.ataw.top</a>
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
25/02/15
|
25/02/15
|
||||||
|
|
||||||
- AI 提供商支持 DeepSeek,OpenRouter 和 Ollama,联网搜素支持 Firecrawl
|
- AI 提供商支持 DeepSeek,OpenRouter 和 Ollama,联网搜索支持 Firecrawl
|
||||||
- 支持检查项目更新
|
- 支持检查项目更新
|
||||||
- 支持重新生成报告
|
- 支持重新生成报告
|
||||||
- 一般性优化和改进
|
- 一般性优化和改进
|
||||||
|
@ -239,6 +239,19 @@
|
|||||||
private
|
private
|
||||||
/>
|
/>
|
||||||
</UFormField>
|
</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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, defineProps } from 'vue'
|
|
||||||
import { Handle, Position } from '@vue-flow/core'
|
import { Handle, Position } from '@vue-flow/core'
|
||||||
import type { ButtonProps } from '@nuxt/ui'
|
import type { ButtonProps } from '@nuxt/ui'
|
||||||
import type { SearchNodeData } from './SearchFlow.vue'
|
import type { SearchNodeData } from './SearchFlow.vue'
|
||||||
|
30
composables/usePLimit.ts
Normal file
30
composables/usePLimit.ts
Normal 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
|
||||||
|
}
|
@ -38,7 +38,9 @@
|
|||||||
"firecrawl": {
|
"firecrawl": {
|
||||||
"help": "Get one API key at {0}."
|
"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": {
|
"researchTopic": {
|
||||||
|
@ -38,7 +38,9 @@
|
|||||||
"tavily": {
|
"tavily": {
|
||||||
"help": "和 Firecrawl 类似,不过提供了每月 1000 次免费搜索。在 {0} 获取一个 API key。"
|
"help": "和 Firecrawl 类似,不过提供了每月 1000 次免费搜索。在 {0} 获取一个 API key。"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"concurrencyLimit": "并发数",
|
||||||
|
"concurrencyLimitHelp": "限制同时进行的搜索数量。这样可以避免被 API 服务限流,导致请求失败。"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"researchTopic": {
|
"researchTopic": {
|
||||||
|
@ -61,9 +61,6 @@ export type ResearchStep =
|
|||||||
| { type: 'error'; message: string; nodeId: string }
|
| { type: 'error'; message: string; nodeId: string }
|
||||||
| { type: 'complete'; learnings: string[]; visitedUrls: 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
|
* Schema for {@link generateSearchQueries} without dynamic descriptions
|
||||||
*/
|
*/
|
||||||
@ -242,6 +239,7 @@ export async function deepResearch({
|
|||||||
}): Promise<ResearchResult> {
|
}): Promise<ResearchResult> {
|
||||||
const { t } = useNuxtApp().$i18n
|
const { t } = useNuxtApp().$i18n
|
||||||
const language = t('language', {}, { locale: languageCode })
|
const language = t('language', {}, { locale: languageCode })
|
||||||
|
const globalLimit = usePLimit()
|
||||||
|
|
||||||
onProgress({
|
onProgress({
|
||||||
type: 'generating_query',
|
type: 'generating_query',
|
||||||
@ -257,7 +255,6 @@ export async function deepResearch({
|
|||||||
language,
|
language,
|
||||||
searchLanguage,
|
searchLanguage,
|
||||||
})
|
})
|
||||||
const limit = pLimit(ConcurrencyLimit)
|
|
||||||
|
|
||||||
let searchQueries: PartialSearchQuery[] = []
|
let searchQueries: PartialSearchQuery[] = []
|
||||||
|
|
||||||
@ -322,7 +319,7 @@ export async function deepResearch({
|
|||||||
// Run in parallel and limit the concurrency
|
// Run in parallel and limit the concurrency
|
||||||
const results = await Promise.all(
|
const results = await Promise.all(
|
||||||
searchQueries.map((searchQuery, i) =>
|
searchQueries.map((searchQuery, i) =>
|
||||||
limit(async () => {
|
globalLimit(async () => {
|
||||||
if (!searchQuery?.query) {
|
if (!searchQuery?.query) {
|
||||||
return {
|
return {
|
||||||
learnings: [],
|
learnings: [],
|
||||||
@ -433,17 +430,26 @@ export async function deepResearch({
|
|||||||
.join('')}
|
.join('')}
|
||||||
`.trim()
|
`.trim()
|
||||||
|
|
||||||
return deepResearch({
|
// Add concurrency by 1, and do next recursive search
|
||||||
query: nextQuery,
|
globalLimit.concurrency++
|
||||||
breadth: nextBreadth,
|
try {
|
||||||
maxDepth,
|
const r = await deepResearch({
|
||||||
learnings: allLearnings,
|
query: nextQuery,
|
||||||
visitedUrls: allUrls,
|
breadth: nextBreadth,
|
||||||
onProgress,
|
maxDepth,
|
||||||
currentDepth: nextDepth,
|
learnings: allLearnings,
|
||||||
nodeId: childNodeId(nodeId, i),
|
visitedUrls: allUrls,
|
||||||
languageCode,
|
onProgress,
|
||||||
})
|
currentDepth: nextDepth,
|
||||||
|
nodeId: childNodeId(nodeId, i),
|
||||||
|
languageCode,
|
||||||
|
})
|
||||||
|
return r
|
||||||
|
} catch (error) {
|
||||||
|
throw error
|
||||||
|
} finally {
|
||||||
|
globalLimit.concurrency--
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return {
|
return {
|
||||||
learnings: allLearnings,
|
learnings: allLearnings,
|
||||||
|
@ -21,6 +21,8 @@ export interface ConfigWebSearch {
|
|||||||
apiKey?: string
|
apiKey?: string
|
||||||
/** Force the LLM to generate serp queries in a certain language */
|
/** Force the LLM to generate serp queries in a certain language */
|
||||||
searchLanguage?: Locale
|
searchLanguage?: Locale
|
||||||
|
/** Limit the number of concurrent tasks globally */
|
||||||
|
concurrencyLimit?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Config {
|
export interface Config {
|
||||||
@ -37,8 +39,9 @@ export const useConfigStore = defineStore('config', () => {
|
|||||||
},
|
},
|
||||||
webSearch: {
|
webSearch: {
|
||||||
provider: 'tavily',
|
provider: 'tavily',
|
||||||
|
concurrencyLimit: 2,
|
||||||
},
|
},
|
||||||
})
|
} satisfies Config)
|
||||||
// The version user dismissed the update notification
|
// The version user dismissed the update notification
|
||||||
const dismissUpdateVersion = useLocalStorage<string>(
|
const dismissUpdateVersion = useLocalStorage<string>(
|
||||||
'dismiss-update-version',
|
'dismiss-update-version',
|
||||||
|
Reference in New Issue
Block a user