diff --git a/README.md b/README.md
index db0bc68..61736e9 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,13 @@
# Deep Research Web UI
-This is a web UI for https://github.com/dzhng/deep-research.
+This is a web UI for https://github.com/dzhng/deep-research, with several improvements and fixes.
Features:
- **Realtime feedback**: Stream AI responses and reflect on the UI in real-time
- **Search visualization**: Shows the research process using a tree structure
- **Export as PDF**: Export the final research report as a PDF
+- **Search in different languages**: Useful when you want to get search results in a different language
- **Bring Your Own API Key**: Everything (config, API requests, ...) happens in your browser
- **Supports more models**: Uses plain prompts instead of newer, less widely supported features like Structured Outputs. This ensures to work with more providers that haven't caught up with the latest OpenAI capabilities.
diff --git a/components/ConfigManager.vue b/components/ConfigManager.vue
index c98cfa0..904cd92 100644
--- a/components/ConfigManager.vue
+++ b/components/ConfigManager.vue
@@ -137,7 +137,7 @@
-
+
{{ $t('settings.webSearch.provider') }}
@@ -164,6 +164,20 @@
:placeholder="$t('settings.webSearch.apiKey')"
/>
+
+
+
+
+
+
diff --git a/components/DeepResearch.vue b/components/DeepResearch.vue
index bb06a60..61e6de2 100644
--- a/components/DeepResearch.vue
+++ b/components/DeepResearch.vue
@@ -9,6 +9,7 @@
import { marked } from 'marked'
const { t, locale } = useI18n()
+ const { config } = storeToRefs(useConfigStore())
const emit = defineEmits<{
(e: 'complete', results: ResearchResult): void
}>()
@@ -146,11 +147,15 @@
searchResults.value = {}
isLoading.value = true
try {
+ const searchLanguage = config.value.webSearch.searchLanguage
+ ? t('language', {}, { locale: config.value.webSearch.searchLanguage })
+ : undefined
await deepResearch({
query,
maxDepth: depth,
breadth,
language: t('language', {}, { locale: locale.value }),
+ searchLanguage,
onProgress: handleResearchProgress,
})
} catch (error) {
diff --git a/components/LangSwitcher.vue b/components/LangSwitcher.vue
index f9be4a2..21ffb23 100644
--- a/components/LangSwitcher.vue
+++ b/components/LangSwitcher.vue
@@ -1,17 +1,37 @@
diff --git a/i18n/en.json b/i18n/en.json
index 18df3a8..08760bc 100644
--- a/i18n/en.json
+++ b/i18n/en.json
@@ -27,7 +27,9 @@
"webSearch": {
"provider": "Web Search Provider",
"providerHelp": "Currently only supports Tavily. It provides lots of free quota (1000 credits / month).\nGet one API key at {0}.",
- "apiKey": "API Key"
+ "apiKey": "API Key",
+ "queryLanguage": "Query Language",
+ "queryLanguageHelp": "The language of the search query. Useful if you want to get search results in a different language.\nWhen writing conclusions, the AI model will still use the language same as the web UI."
}
},
"researchTopic": {
diff --git a/i18n/zh.json b/i18n/zh.json
index a8f6323..f95db5e 100644
--- a/i18n/zh.json
+++ b/i18n/zh.json
@@ -27,7 +27,9 @@
"webSearch": {
"provider": "联网搜索服务",
"providerHelp": "目前仅支持 Tavily,每个月可以免费搜索 1000 次。\n请在 {0} 生成一个 API 密钥。",
- "apiKey": "API 密钥"
+ "apiKey": "API 密钥",
+ "queryLanguage": "使用语言",
+ "queryLanguageHelp": "修改搜索词的语言。如果你想获取不同的搜索结果(比如查询高质量的英文资料),可以在这里修改。\nAI 模型在总结的时候仍然会使用当前网页的语言。"
}
},
"researchTopic": {
diff --git a/lib/deep-research.ts b/lib/deep-research.ts
index 37b2bb4..741dc18 100644
--- a/lib/deep-research.ts
+++ b/lib/deep-research.ts
@@ -73,12 +73,15 @@ export function generateSearchQueries({
numQueries = 3,
learnings,
language,
+ searchLanguage,
}: {
query: string
language: string
numQueries?: number
// optional, if provided, the research will continue from the last learning
learnings?: string[]
+ /** Force the LLM to generate serp queries in a certain language */
+ searchLanguage?: string
}) {
const schema = z.object({
queries: z
@@ -95,7 +98,11 @@ export function generateSearchQueries({
.describe(`List of SERP queries, max of ${numQueries}`),
})
const jsonSchema = JSON.stringify(zodToJsonSchema(schema))
+ let lp = languagePrompt(language)
+ if (searchLanguage !== language) {
+ lp += `Use ${searchLanguage} for the SERP queries.`
+ }
const prompt = [
`Given the following prompt from the user, generate a list of SERP queries to research the topic. Return a maximum of ${numQueries} queries, but feel free to return less if the original prompt is clear. Make sure each query is unique and not similar to each other: ${query}\n\n`,
learnings
@@ -104,7 +111,7 @@ export function generateSearchQueries({
)}`
: '',
`You MUST respond in JSON with the following schema: ${jsonSchema}`,
- languagePrompt(language),
+ lp,
].join('\n\n')
return streamText({
model: useAiModel(),
@@ -213,6 +220,7 @@ export async function deepResearch({
onProgress,
currentDepth = 1,
nodeId = '0',
+ searchLanguage,
}: {
query: string
breadth: number
@@ -223,6 +231,8 @@ export async function deepResearch({
onProgress: (step: ResearchStep) => void
currentDepth?: number
nodeId?: string
+ /** Force the LLM to generate serp queries in a certain language */
+ searchLanguage?: string
}): Promise {
try {
const searchQueriesResult = generateSearchQueries({
@@ -230,6 +240,7 @@ export async function deepResearch({
learnings,
numQueries: breadth,
language,
+ searchLanguage,
})
const limit = pLimit(ConcurrencyLimit)
diff --git a/lib/prompt.ts b/lib/prompt.ts
index 50d0ce6..07feaec 100644
--- a/lib/prompt.ts
+++ b/lib/prompt.ts
@@ -20,7 +20,7 @@ export const systemPrompt = () => {
* @param language the language of the prompt, e.g. `English`
*/
export const languagePrompt = (language: string) => {
- let languagePrompt = `- Respond in ${language}.`
+ let languagePrompt = `Respond in ${language}.`
if (language === '中文') {
languagePrompt +=
diff --git a/stores/config.ts b/stores/config.ts
index 100ea15..339915c 100644
--- a/stores/config.ts
+++ b/stores/config.ts
@@ -1,4 +1,5 @@
import { skipHydrate } from 'pinia'
+import type { Locale } from '~/components/LangSwitcher.vue'
export type ConfigAiProvider = 'openai-compatible'
export interface ConfigAi {
@@ -11,6 +12,8 @@ export interface ConfigAi {
export interface ConfigWebSearch {
provider: 'tavily'
apiKey?: string
+ /** Force the LLM to generate serp queries in a certain language */
+ searchLanguage?: Locale
}
export interface Config {