update settings for deep research mode

This commit is contained in:
duanfuxiang 2025-03-17 10:40:25 +08:00
parent 4aa321dffc
commit da488f1c39
5 changed files with 82 additions and 24 deletions

View File

@ -535,7 +535,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
} }
} }
} else if (toolArgs.type === 'search_web') { } else if (toolArgs.type === 'search_web') {
const results = await webSearch(toolArgs.query) const results = await webSearch(toolArgs.query, settings.serperApiKey)
const formattedContent = `[search_web for '${toolArgs.query}'] Result:\n${results}\n`; const formattedContent = `[search_web for '${toolArgs.query}'] Result:\n${results}\n`;
return { return {
type: 'search_web', type: 'search_web',
@ -551,7 +551,7 @@ const Chat = forwardRef<ChatRef, ChatProps>((props, ref) => {
} }
} }
} else if (toolArgs.type === 'fetch_urls_content') { } else if (toolArgs.type === 'fetch_urls_content') {
const results = await fetchUrlsContent(toolArgs.urls) const results = await fetchUrlsContent(toolArgs.urls, settings.jinaApiKey)
const formattedContent = `[ fetch_urls_content ] Result:\n${results}\n`; const formattedContent = `[ fetch_urls_content ] Result:\n${results}\n`;
return { return {
type: 'fetch_urls_content', type: 'fetch_urls_content',

View File

@ -32,7 +32,8 @@ export const OPENROUTER_BASE_URL = 'https://openrouter.ai/api/v1'
export const SILICONFLOW_BASE_URL = 'https://api.siliconflow.cn/v1' export const SILICONFLOW_BASE_URL = 'https://api.siliconflow.cn/v1'
export const ALIBABA_QWEN_BASE_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1' export const ALIBABA_QWEN_BASE_URL = 'https://dashscope.aliyuncs.com/compatible-mode/v1'
export const INFIO_BASE_URL = 'https://api.infio.com/api/raw_message' export const INFIO_BASE_URL = 'https://api.infio.com/api/raw_message'
export const JINA_BASE_URL = 'https://r.jina.ai'
export const SERPER_BASE_URL = 'https://serpapi.com/search'
// Pricing in dollars per million tokens // Pricing in dollars per million tokens
type ModelPricing = { type ModelPricing = {
input: number input: number

View File

@ -37,6 +37,7 @@ export class InfioSettingTab extends PluginSettingTab {
const { containerEl } = this const { containerEl } = this
containerEl.empty() containerEl.empty()
this.renderModelsSection(containerEl) this.renderModelsSection(containerEl)
this.renderDeepResearchSection(containerEl)
this.renderRAGSection(containerEl) this.renderRAGSection(containerEl)
this.renderAutoCompleteSection(containerEl) this.renderAutoCompleteSection(containerEl)
} }
@ -63,6 +64,58 @@ export class InfioSettingTab extends PluginSettingTab {
this.renderModelsContent(modelsDiv); this.renderModelsContent(modelsDiv);
} }
renderDeepResearchSection(containerEl: HTMLElement): void {
new Setting(containerEl)
.setHeading()
.setName('Deep Research')
new Setting(containerEl)
.setName('Serper Api Key')
.setDesc(createFragment(el => {
el.appendText('API key for web search functionality. Serper allows the plugin to search the internet for information, similar to a search engine. Get your key from ');
const a = el.createEl('a', {
href: 'https://serpapi.com/manage-api-key',
text: 'https://serpapi.com/manage-api-key'
});
a.setAttr('target', '_blank');
a.setAttr('rel', 'noopener');
}))
.setClass('setting-item-heading-smaller')
.addText((text) =>
text
.setValue(this.plugin.settings.serperApiKey)
.onChange(async (value) => {
await this.plugin.setSettings({
...this.plugin.settings,
serperApiKey: value,
})
}),
)
new Setting(containerEl)
.setName('Jina Api Key (Optional)')
.setDesc(createFragment(el => {
el.appendText('API key for parsing web pages into markdown format. If not provided, local parsing will be used. Get your key from ');
const a = el.createEl('a', {
href: 'https://jina.ai/api-key',
text: 'https://jina.ai/api-key'
});
a.setAttr('target', '_blank');
a.setAttr('rel', 'noopener');
}))
.setClass('setting-item-heading-smaller')
.addText((text) =>
text
.setValue(this.plugin.settings.jinaApiKey)
.onChange(async (value) => {
await this.plugin.setSettings({
...this.plugin.settings,
jinaApiKey: value,
})
}),
)
}
renderRAGSection(containerEl: HTMLElement): void { renderRAGSection(containerEl: HTMLElement): void {
new Setting(containerEl).setHeading().setName('RAG') new Setting(containerEl).setHeading().setName('RAG')
new Setting(containerEl) new Setting(containerEl)

View File

@ -222,6 +222,10 @@ export const InfioSettingsSchema = z.object({
// Mode // Mode
mode: z.string().catch('ask'), mode: z.string().catch('ask'),
// Web Search
serperApiKey: z.string().catch(''),
jinaApiKey: z.string().catch(''),
/// [compatible] /// [compatible]
// activeModels [compatible] // activeModels [compatible]
activeModels: z.array( activeModels: z.array(

View File

@ -2,9 +2,9 @@ import https from 'https';
import { htmlToMarkdown, requestUrl } from 'obsidian'; import { htmlToMarkdown, requestUrl } from 'obsidian';
import { YoutubeTranscript, isYoutubeUrl } from './youtube-transcript'; import { JINA_BASE_URL, SERPER_BASE_URL } from '../constants';
const SERPER_API_KEY = 'a6fd4dc4b79f10b1e5008b688c81bacef0d24b4d5cd4e52071afa8329a67497c' import { YoutubeTranscript, isYoutubeUrl } from './youtube-transcript';
interface SearchResult { interface SearchResult {
title: string; title: string;
@ -16,9 +16,9 @@ interface SearchResponse {
organic_results?: SearchResult[]; organic_results?: SearchResult[];
} }
export async function webSearch(query: string): Promise<string> { export async function webSearch(query: string, serperApiKey: string): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const url = `https://serpapi.com/search?q=${encodeURIComponent(query)}&engine=google&api_key=${SERPER_API_KEY}&num=20`; const url = `${SERPER_BASE_URL}?q=${encodeURIComponent(query)}&engine=google&api_key=${serperApiKey}&num=20`;
console.log(url) console.log(url)
@ -76,37 +76,37 @@ ${transcript.map((t) => `${t.offset}: ${t.text}`).join('\n')}`
return htmlToMarkdown(response.text) return htmlToMarkdown(response.text)
} }
const USE_JINA = true
export async function fetchUrlsContent(urls: string[]): Promise<string> { export async function fetchUrlsContent(urls: string[], apiKey: string): Promise<string> {
const use_jina = apiKey && apiKey != '' ? true : false
return new Promise((resolve) => { return new Promise((resolve) => {
const results = urls.map(async (url) => { const results = urls.map(async (url) => {
try { try {
const content = USE_JINA ? await fetchJina(url) : await getWebsiteContent(url); const content = use_jina ? await fetchJina(url, apiKey) : await getWebsiteContent(url);
return `<url_content url="${url}">\n${content}\n</url_content>`; return `<url_content url="${url}">\n${content}\n</url_content>`;
} catch (error) { } catch (error) {
console.error(`获取URL内容失败: ${url}`, error); console.error(`Failed to fetch URL content: ${url}`, error);
return `<url_content url="${url}">\n获取内容失败: ${error}\n</url_content>`; return `<url_content url="${url}">\n fetch content error: ${error}\n</url_content>`;
} }
}); });
console.log('fetchUrlsContent', results); console.log('fetchUrlsContent', results);
Promise.all(results).then((texts) => { Promise.all(results).then((texts) => {
resolve(texts.join('\n\n')); resolve(texts.join('\n\n'));
}).catch((error) => { }).catch((error) => {
console.error('获取URLs内容时出错', error); console.error('fetch urls content error', error);
resolve('fetch urls content error'); // 即使出错也返回一些内容 resolve('fetch urls content error'); // even if error, return some content
}); });
}); });
} }
function fetchJina(url: string): Promise<string> { function fetchJina(url: string, apiKey: string): Promise<string> {
return new Promise((resolve) => { return new Promise((resolve) => {
const jinaUrl = `https://r.jina.ai/${url}`; const jinaUrl = `${JINA_BASE_URL}/${url}`;
const jinaHeaders = { const jinaHeaders = {
'Authorization': 'Bearer jina_1d721eb8c4814a938b4351ae0c3a0f117FlTTAz1GOmpOsIDN7HvIyLbiOCe', 'Authorization': `Bearer ${apiKey}`,
'X-No-Cache': 'true', 'X-No-Cache': 'true',
}; };
@ -124,18 +124,18 @@ function fetchJina(url: string): Promise<string> {
res.on('end', () => { res.on('end', () => {
console.log(data); console.log(data);
try { try {
// 检查是否有错误响应 // check if there is an error response
const response = JSON.parse(data); const response = JSON.parse(data);
if (response.code && response.message) { if (response.code && response.message) {
console.error(`JINA API 错误: ${response.message}`); console.error(`JINA API error: ${response.message}`);
resolve(`无法获取内容: ${response.message}`); resolve(`fetch jina content error: ${response.message}`);
return; return;
} }
resolve(data); resolve(data);
} catch (e) { } catch (e) {
// 如果不是JSON格式可能是正常的内容 // if not json format, maybe normal content
resolve(data); resolve(data);
} }
}); });