feat: support custon endpint for Firecrawl

This commit is contained in:
AnotiaWang
2025-02-18 14:36:59 +08:00
parent f593546864
commit daf65889b3
5 changed files with 58 additions and 11 deletions

View File

@ -11,6 +11,7 @@
const { const {
config, config,
aiApiBase, aiApiBase,
webSearchApiBase,
showConfigManager: showModal, showConfigManager: showModal,
} = storeToRefs(useConfigStore()) } = storeToRefs(useConfigStore())
const { t } = useI18n() const { t } = useI18n()
@ -66,6 +67,7 @@
// Only kept for easy reference in i18n Ally // Only kept for easy reference in i18n Ally
_help: t('settings.webSearch.providers.firecrawl.help'), _help: t('settings.webSearch.providers.firecrawl.help'),
link: 'https://www.firecrawl.dev/app/api-keys', link: 'https://www.firecrawl.dev/app/api-keys',
supportsCustomApiBase: true,
}, },
]) ])
const selectedAiProvider = computed(() => const selectedAiProvider = computed(() =>
@ -121,6 +123,7 @@
config.value.ai.model = model config.value.ai.model = model
} }
// Automatically fetch AI models list
watch( watch(
() => [ () => [
config.value.ai.provider, config.value.ai.provider,
@ -134,6 +137,24 @@
}, },
{ immediate: true }, { immediate: true },
) )
// Reset AI config when provider changed
watch(
() => config.value.ai.provider,
() => {
config.value.ai.apiKey = ''
config.value.ai.apiBase = ''
config.value.ai.model = ''
config.value.ai.contextSize = undefined
},
)
// Reset web search config when provider changed
watch(
() => config.value.webSearch.provider,
() => {
config.value.webSearch.apiKey = ''
config.value.webSearch.apiBase = ''
},
)
defineExpose({ defineExpose({
show() { show() {
@ -250,17 +271,30 @@
</template> </template>
<USelect <USelect
v-model="config.webSearch.provider" v-model="config.webSearch.provider"
class="w-auto" class="w-30"
:items="webSearchProviderOptions" :items="webSearchProviderOptions"
/> />
</UFormField> </UFormField>
<UFormField :label="$t('settings.webSearch.apiKey')" required> <UFormField
:label="$t('settings.webSearch.apiKey')"
:required="!config.webSearch.apiBase"
>
<PasswordInput <PasswordInput
v-model="config.webSearch.apiKey" v-model="config.webSearch.apiKey"
class="w-full" class="w-full"
:placeholder="$t('settings.webSearch.apiKey')" :placeholder="$t('settings.webSearch.apiKey')"
/> />
</UFormField> </UFormField>
<UFormField
v-if="selectedWebSearchProvider?.supportsCustomApiBase"
:label="$t('settings.webSearch.apiBase')"
>
<UInput
v-model="config.webSearch.apiBase"
class="w-full"
:placeholder="webSearchApiBase"
/>
</UFormField>
<UFormField :label="$t('settings.webSearch.queryLanguage')"> <UFormField :label="$t('settings.webSearch.queryLanguage')">
<template #help> <template #help>
<i18n-t <i18n-t

View File

@ -19,12 +19,13 @@ type WebSearchFunction = (
) => Promise<WebSearchResult[]> ) => Promise<WebSearchResult[]>
export const useWebSearch = (): WebSearchFunction => { export const useWebSearch = (): WebSearchFunction => {
const { config } = useConfigStore() const { config, webSearchApiBase } = useConfigStore()
switch (config.webSearch.provider) { switch (config.webSearch.provider) {
case 'firecrawl': { case 'firecrawl': {
const fc = new Firecrawl({ const fc = new Firecrawl({
apiKey: config.webSearch.apiKey, apiKey: config.webSearch.apiKey,
apiUrl: webSearchApiBase,
}) })
return async (q: string, o: WebSearchOptions) => { return async (q: string, o: WebSearchOptions) => {
const results = await fc.search(q, o) const results = await fc.search(q, o)

View File

@ -40,11 +40,12 @@
"help": "Similar to Firecrawl, but provides 1000 free credits / month. Get one API key at {0}." "help": "Similar to Firecrawl, but provides 1000 free credits / month. Get one API key at {0}."
}, },
"firecrawl": { "firecrawl": {
"help": "Get one API key at {0}." "help": "Get one API key at {0} if you are using the official service."
} }
}, },
"concurrencyLimitHelp": "Limit the concurrent search tasks. This is useful to avoid overloading the search provider and causing requests to fail.", "concurrencyLimitHelp": "Limit the concurrent search tasks. This is useful to avoid overloading the search provider and causing requests to fail.",
"concurrencyLimit": "Concurrency Limit" "concurrencyLimit": "Concurrency Limit",
"apiBase": "API Base URL"
} }
}, },
"researchTopic": { "researchTopic": {
@ -74,7 +75,6 @@
"clickToView": "Click a child node to view details.", "clickToView": "Click a child node to view details.",
"nodeDetails": "Node Details", "nodeDetails": "Node Details",
"startNode": { "startNode": {
"label": "Start",
"description": "This is the beginning of your deep research journey!" "description": "This is the beginning of your deep research journey!"
}, },
"researchGoal": "Research Goal", "researchGoal": "Research Goal",

View File

@ -37,14 +37,15 @@
"queryLanguageHelp": "修改搜索词的语言。如果你想获取不同的搜索结果(比如查询高质量的英文资料),可以在这里修改。\nAI 模型在总结的时候仍然会使用当前网页的语言。", "queryLanguageHelp": "修改搜索词的语言。如果你想获取不同的搜索结果(比如查询高质量的英文资料),可以在这里修改。\nAI 模型在总结的时候仍然会使用当前网页的语言。",
"providers": { "providers": {
"firecrawl": { "firecrawl": {
"help": "在 {0} 获取一个 API key。" "help": "如果你使用的是官方服务,请在 {0} 获取 API key。"
}, },
"tavily": { "tavily": {
"help": "和 Firecrawl 类似,不过提供了每月 1000 次免费搜索。在 {0} 获取一个 API key。" "help": "和 Firecrawl 类似,不过提供了每月 1000 次免费搜索。在 {0} 获取一个 API key。"
} }
}, },
"concurrencyLimit": "并发数", "concurrencyLimit": "并发数",
"concurrencyLimitHelp": "限制同时进行的搜索数量。这样可以避免被 API 服务限流,导致请求失败。" "concurrencyLimitHelp": "限制同时进行的搜索数量。这样可以避免被 API 服务限流,导致请求失败。",
"apiBase": "API Base URL"
} }
}, },
"researchTopic": { "researchTopic": {
@ -74,7 +75,6 @@
"clickToView": "点击下面的节点查看搜索详情。", "clickToView": "点击下面的节点查看搜索详情。",
"nodeDetails": "节点详情", "nodeDetails": "节点详情",
"startNode": { "startNode": {
"label": "Start",
"description": "这是本次研究的起点" "description": "这是本次研究的起点"
}, },
"researchGoal": "研究目标", "researchGoal": "研究目标",

View File

@ -20,6 +20,8 @@ export interface ConfigAi {
export interface ConfigWebSearch { export interface ConfigWebSearch {
provider: ConfigWebSearchProvider provider: ConfigWebSearchProvider
apiKey?: string apiKey?: string
/** API base. Currently only works with Firecrawl */
apiBase?: 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 */ /** Limit the number of concurrent tasks globally */
@ -78,6 +80,15 @@ export const useConfigStore = defineStore('config', () => {
} }
return ai.apiBase || 'https://api.openai.com/v1' return ai.apiBase || 'https://api.openai.com/v1'
}) })
const webSearchApiBase = computed(() => {
const { webSearch } = config.value
if (webSearch.provider === 'tavily') {
return
}
if (webSearch.provider === 'firecrawl') {
return webSearch.apiBase || 'https://api.firecrawl.dev'
}
})
const showConfigManager = ref(false) const showConfigManager = ref(false)
@ -85,6 +96,7 @@ export const useConfigStore = defineStore('config', () => {
config: skipHydrate(config), config: skipHydrate(config),
isConfigValid, isConfigValid,
aiApiBase, aiApiBase,
webSearchApiBase,
showConfigManager, showConfigManager,
dismissUpdateVersion: skipHydrate(dismissUpdateVersion), dismissUpdateVersion: skipHydrate(dismissUpdateVersion),
} }