mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-07 12:00:44 +08:00
feat(agent): add BGPT structured literature evidence search tool (#16050)
## Summary Adds a first-class **BGPT** Agent tool (backend + UI) in response to [#15997](https://github.com/infiniflow/ragflow/issues/15997#issuecomment-4703864227). BGPT calls `POST https://bgpt.pro/api/mcp-search` and returns structured study evidence from full-text papers — not just titles/abstracts. Each result is formatted for RAGFlow citations with: - methods - sample size / population - results - limitations - conflicts of interest - data availability - study blind spots - `how_to_falsify` ## Why this shape - Mirrors existing literature tools (`PubMed`, `ArXiv`) and HTTP tools (`SearXNG`). - Works on the free tier (no API key required for first 50 results). - Optional `api_key` and `days_back` in the node/tool config. - Surfaces both `formalized_content` and raw `json` outputs (like SearXNG). ## Files - `agent/tools/bgpt.py` — REST client + evidence formatter - Frontend: Operator enum, forms, tool picker, canvas accordion, en/zh locales, icon ## Demo / docs Runnable claim-interrogation demo: https://github.com/connerlambden/bgpt-mcp/blob/main/EVIDENCE_DEMO.md ## Test plan - [ ] Add BGPT node on Agent canvas, run query `GLP-1 alcohol craving`, verify `formalized_content` includes limitations/COI fields - [ ] Add BGPT as Agent sub-tool under Search, verify tool-calling works - [ ] Confirm empty query / try-run returns gracefully - [ ] Optional: paid-tier `api_key` path --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: Zhichang Yu <yuzhichang@gmail.com>
This commit is contained in:
188
agent/tools/bgpt.py
Normal file
188
agent/tools/bgpt.py
Normal file
@@ -0,0 +1,188 @@
|
||||
#
|
||||
# Copyright 2024 The InfiniFlow Authors. All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
import logging
|
||||
import os
|
||||
import time
|
||||
from abc import ABC
|
||||
|
||||
import requests
|
||||
|
||||
from agent.tools.base import ToolBase, ToolMeta, ToolParamBase
|
||||
from common.connection_utils import timeout
|
||||
|
||||
BGPT_SEARCH_URL = "https://bgpt.pro/api/mcp-search"
|
||||
|
||||
|
||||
class BGPTParam(ToolParamBase):
|
||||
"""Define the BGPT component parameters."""
|
||||
|
||||
def __init__(self):
|
||||
self.meta: ToolMeta = {
|
||||
"name": "bgpt_search",
|
||||
"description": (
|
||||
"BGPT searches scientific papers and returns structured evidence extracted from full-text studies: "
|
||||
"methods, sample sizes, results, limitations, conflicts of interest, data/code availability, "
|
||||
"study blind spots, quality scores, and falsification prompts. "
|
||||
"Useful when the agent must judge a scientific claim, not just find abstracts."
|
||||
),
|
||||
"parameters": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": ("Natural-language scientific search query. Use the most important terms from the user's request."),
|
||||
"default": "{sys.query}",
|
||||
"required": True,
|
||||
}
|
||||
},
|
||||
}
|
||||
super().__init__()
|
||||
self.top_n = 10
|
||||
self.api_key = ""
|
||||
self.days_back = None
|
||||
|
||||
def check(self):
|
||||
try:
|
||||
if isinstance(self.top_n, str):
|
||||
self.top_n = int(self.top_n.strip())
|
||||
except Exception:
|
||||
pass
|
||||
self.check_positive_integer(self.top_n, "Top N")
|
||||
if self.days_back not in (None, ""):
|
||||
try:
|
||||
self.days_back = int(self.days_back)
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise ValueError("days_back must be an integer") from exc
|
||||
self.check_positive_integer(self.days_back, "Days back")
|
||||
|
||||
def get_input_form(self) -> dict[str, dict]:
|
||||
return {"query": {"name": "Query", "type": "line"}}
|
||||
|
||||
|
||||
class BGPT(ToolBase, ABC):
|
||||
component_name = "BGPT"
|
||||
|
||||
@timeout(int(os.environ.get("COMPONENT_EXEC_TIMEOUT", 30)))
|
||||
def _invoke(self, **kwargs):
|
||||
if self.check_if_canceled("BGPT processing"):
|
||||
return
|
||||
|
||||
query = kwargs.get("query")
|
||||
if not query or not isinstance(query, str) or not query.strip():
|
||||
self.set_output("formalized_content", "")
|
||||
return ""
|
||||
|
||||
payload = {"query": query.strip(), "num_results": self._param.top_n}
|
||||
if self._param.api_key:
|
||||
payload["api_key"] = self._param.api_key
|
||||
if self._param.days_back:
|
||||
payload["days_back"] = self._param.days_back
|
||||
|
||||
last_e = ""
|
||||
for _ in range(self._param.max_retries + 1):
|
||||
if self.check_if_canceled("BGPT processing"):
|
||||
return
|
||||
|
||||
try:
|
||||
response = requests.post(BGPT_SEARCH_URL, json=payload, timeout=25)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
if not data or not isinstance(data, dict):
|
||||
raise ValueError("Invalid response from BGPT")
|
||||
|
||||
results = data.get("results", [])
|
||||
if not isinstance(results, list):
|
||||
raise ValueError("Invalid results format from BGPT")
|
||||
|
||||
if self.check_if_canceled("BGPT processing"):
|
||||
return
|
||||
|
||||
self._retrieve_chunks(
|
||||
results,
|
||||
get_title=lambda paper: paper.get("title") or "Untitled",
|
||||
get_url=lambda paper: paper.get("url") or paper.get("doi") or "",
|
||||
get_content=lambda paper: self._format_bgpt_paper(paper),
|
||||
)
|
||||
self.set_output("json", results)
|
||||
return self.output("formalized_content")
|
||||
|
||||
except requests.HTTPError as e:
|
||||
# Non-retryable 4xx (e.g. 400/401/403/404) should fail fast
|
||||
# rather than wasting retries on bad requests or auth failures.
|
||||
status = e.response.status_code if e.response is not None else None
|
||||
if status is not None and 400 <= status < 500 and status != 429:
|
||||
last_e = f"HTTP error: {e}"
|
||||
logging.exception("BGPT non-retryable HTTP error: %s", e)
|
||||
break
|
||||
if self.check_if_canceled("BGPT processing"):
|
||||
return
|
||||
|
||||
last_e = f"Network error: {e}"
|
||||
logging.exception("BGPT network error: %s", e)
|
||||
time.sleep(self._param.delay_after_error)
|
||||
except requests.RequestException as e:
|
||||
if self.check_if_canceled("BGPT processing"):
|
||||
return
|
||||
|
||||
last_e = f"Network error: {e}"
|
||||
logging.exception("BGPT network error: %s", e)
|
||||
time.sleep(self._param.delay_after_error)
|
||||
except Exception as e:
|
||||
if self.check_if_canceled("BGPT processing"):
|
||||
return
|
||||
|
||||
last_e = str(e)
|
||||
logging.exception("BGPT error: %s", e)
|
||||
time.sleep(self._param.delay_after_error)
|
||||
|
||||
if last_e:
|
||||
self.set_output("_ERROR", last_e)
|
||||
return f"BGPT error: {last_e}"
|
||||
|
||||
assert False, self.output()
|
||||
|
||||
def _format_bgpt_paper(self, paper: dict) -> str:
|
||||
def field(*names: str) -> str:
|
||||
for name in names:
|
||||
value = paper.get(name)
|
||||
if value is None:
|
||||
continue
|
||||
if isinstance(value, (dict, list)):
|
||||
value = str(value)
|
||||
text = str(value).strip()
|
||||
if text:
|
||||
return text
|
||||
return "-"
|
||||
|
||||
lines = [
|
||||
f"Title: {field('title')}",
|
||||
f"Authors: {field('authors')}",
|
||||
f"Journal: {field('journal')}",
|
||||
f"Year: {field('year')}",
|
||||
f"DOI: {field('doi')}",
|
||||
f"Abstract: {field('abstract')}",
|
||||
f"Methods: {field('methods_and_experimental_techniques', 'methods')}",
|
||||
f"Sample size / population: {field('sample_size_and_population_characteristics', 'sample_size_and_population')}",
|
||||
f"Results: {field('results_and_conclusions', 'results')}",
|
||||
f"Limitations: {field('paper_limitations_and_biases', 'limitations')}",
|
||||
f"Conflicts of interest: {field('conflict_of_interest_statements', 'conflict_of_interest')}",
|
||||
f"Data availability: {field('data_availability_statements', 'data_availability')}",
|
||||
f"Blind spots: {field('study_blindspots')}",
|
||||
f"How to falsify: {field('how_to_falsify')}",
|
||||
]
|
||||
return "\n".join(lines)
|
||||
|
||||
def thoughts(self) -> str:
|
||||
return "Searching BGPT for structured scientific evidence on `{}`.".format(self.get_input().get("query", "-_-!"))
|
||||
8
web/src/assets/svg/bgpt.svg
Normal file
8
web/src/assets/svg/bgpt.svg
Normal file
@@ -0,0 +1,8 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/>
|
||||
<path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>
|
||||
<path d="M8 7h8"/>
|
||||
<path d="M8 11h8"/>
|
||||
<path d="M8 15h5"/>
|
||||
<circle cx="17" cy="7" r="2.5" fill="currentColor" stroke="none"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 425 B |
@@ -109,6 +109,7 @@ export enum Operator {
|
||||
UserFillUp = 'UserFillUp',
|
||||
StringTransform = 'StringTransform',
|
||||
SearXNG = 'SearXNG',
|
||||
BGPT = 'BGPT',
|
||||
KeenableSearch = 'KeenableSearch',
|
||||
DocGenerator = 'DocGenerator',
|
||||
Browser = 'Browser',
|
||||
|
||||
@@ -2234,6 +2234,13 @@ Best for: Documents with flowing, contextually connected content — such as boo
|
||||
pubMed: 'PubMed',
|
||||
pubMedDescription:
|
||||
'A component that searches from https://pubmed.ncbi.nlm.nih.gov/, allowing you to specify the number of search results using TopN. It supplements the existing datasets.',
|
||||
bGPT: 'BGPT',
|
||||
bGPTDescription:
|
||||
'Search scientific papers via BGPT and return structured evidence from full-text studies: methods, sample sizes, limitations, conflicts of interest, data availability, blind spots, and falsification prompts. Optional API key after the free tier.',
|
||||
bgptApiKey: 'API key',
|
||||
bgptApiKeyTip: 'Optional. Leave blank for the free tier (first 50 results).',
|
||||
bgptDaysBack: 'Days back',
|
||||
bgptDaysBackTip: 'Optional recency filter (e.g. 365 for the last year).',
|
||||
email: 'Email',
|
||||
emailTip:
|
||||
'E-mail is a required field. You must input an E-mail address here.',
|
||||
|
||||
@@ -1908,6 +1908,13 @@ NER:使用 spaCy NER 和基于规则的关键词提取来抽取实体和关系
|
||||
pubMed: 'PubMed',
|
||||
pubMedDescription:
|
||||
'此组件用于从 https://pubmed.ncbi.nlm.nih.gov/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数。电子邮件是必填字段。',
|
||||
bGPT: 'BGPT',
|
||||
bGPTDescription:
|
||||
'通过 BGPT 搜索科学论文,并返回全文提取的结构化证据:方法、样本量、局限性、利益冲突、数据可用性、研究盲点和可证伪性提示。免费额度后可选 API key。',
|
||||
bgptApiKey: 'API key',
|
||||
bgptApiKeyTip: '可选。留空使用免费额度(前 50 条结果)。',
|
||||
bgptDaysBack: '天数限制',
|
||||
bgptDaysBackTip: '可选的时效过滤(例如 365 表示最近一年)。',
|
||||
arXiv: 'ArXiv',
|
||||
arXivDescription:
|
||||
'此组件用于从 https://arxiv.org/ 获取搜索结果。通常,它作为知识库的补充。Top N 指定您需要调整的搜索结果数量。',
|
||||
|
||||
@@ -118,6 +118,7 @@ export function AccordionOperators({
|
||||
Operator.GoogleScholar,
|
||||
Operator.ArXiv,
|
||||
Operator.PubMed,
|
||||
Operator.BGPT,
|
||||
Operator.GitHub,
|
||||
Operator.Invoke,
|
||||
Operator.WenCai,
|
||||
|
||||
@@ -239,6 +239,23 @@ export const initialPubMedValues = {
|
||||
},
|
||||
};
|
||||
|
||||
export const initialBGPTValues = {
|
||||
top_n: 10,
|
||||
api_key: '',
|
||||
days_back: '',
|
||||
query: AgentGlobals.SysQuery,
|
||||
outputs: {
|
||||
formalized_content: {
|
||||
value: '',
|
||||
type: 'string',
|
||||
},
|
||||
json: {
|
||||
value: [],
|
||||
type: 'Array<Object>',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const initialArXivValues = {
|
||||
top_n: 12,
|
||||
sort_by: 'relevance',
|
||||
@@ -682,6 +699,7 @@ export const RestrictedUpstreamMap = {
|
||||
[Operator.DuckDuckGo]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.Wikipedia]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.PubMed]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.BGPT]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.ArXiv]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.Google]: [Operator.Begin, Operator.Retrieval],
|
||||
[Operator.Bing]: [Operator.Begin, Operator.Retrieval],
|
||||
@@ -734,6 +752,7 @@ export const NodeMap = {
|
||||
[Operator.DuckDuckGo]: 'ragNode',
|
||||
[Operator.Wikipedia]: 'ragNode',
|
||||
[Operator.PubMed]: 'ragNode',
|
||||
[Operator.BGPT]: 'ragNode',
|
||||
[Operator.ArXiv]: 'ragNode',
|
||||
[Operator.Google]: 'ragNode',
|
||||
[Operator.Bing]: 'ragNode',
|
||||
|
||||
@@ -25,6 +25,7 @@ import LoopForm from '../form/loop-form';
|
||||
import MessageForm from '../form/message-form';
|
||||
import ParserForm from '../form/parser-form';
|
||||
import PubMedForm from '../form/pubmed-form';
|
||||
import BGPTForm from '../form/bgpt-form';
|
||||
import RetrievalForm from '../form/retrieval-form/next';
|
||||
import RewriteQuestionForm from '../form/rewrite-question-form';
|
||||
import SearXNGForm from '../form/searxng-form';
|
||||
@@ -80,6 +81,9 @@ export const FormConfigMap = {
|
||||
[Operator.PubMed]: {
|
||||
component: PubMedForm,
|
||||
},
|
||||
[Operator.BGPT]: {
|
||||
component: BGPTForm,
|
||||
},
|
||||
[Operator.ArXiv]: {
|
||||
component: ArXivForm,
|
||||
},
|
||||
|
||||
@@ -31,6 +31,7 @@ const Menus = [
|
||||
Operator.KeenableSearch,
|
||||
Operator.YahooFinance,
|
||||
Operator.PubMed,
|
||||
Operator.BGPT,
|
||||
Operator.GoogleScholar,
|
||||
Operator.ArXiv,
|
||||
Operator.WenCai,
|
||||
|
||||
99
web/src/pages/agent/form/bgpt-form/index.tsx
Normal file
99
web/src/pages/agent/form/bgpt-form/index.tsx
Normal file
@@ -0,0 +1,99 @@
|
||||
import { FormContainer } from '@/components/form-container';
|
||||
import { TopNFormField } from '@/components/top-n-item';
|
||||
import {
|
||||
Form,
|
||||
FormControl,
|
||||
FormField,
|
||||
FormItem,
|
||||
FormLabel,
|
||||
FormMessage,
|
||||
} from '@/components/ui/form';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { useTranslate } from '@/hooks/common-hooks';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { memo } from 'react';
|
||||
import { useForm, useFormContext } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { initialBGPTValues } from '../../constant';
|
||||
import { useFormValues } from '../../hooks/use-form-values';
|
||||
import { useWatchFormChange } from '../../hooks/use-watch-form-change';
|
||||
import { INextOperatorForm } from '../../interface';
|
||||
import { buildOutputList } from '../../utils/build-output-list';
|
||||
import { FormWrapper } from '../components/form-wrapper';
|
||||
import { Output } from '../components/output';
|
||||
import { QueryVariable } from '../components/query-variable';
|
||||
|
||||
export const BGPTFormPartialSchema = {
|
||||
top_n: z.number(),
|
||||
api_key: z.string().optional(),
|
||||
days_back: z.union([z.number(), z.string()]).optional(),
|
||||
};
|
||||
|
||||
export const FormSchema = z.object({
|
||||
...BGPTFormPartialSchema,
|
||||
query: z.string(),
|
||||
});
|
||||
|
||||
export function BGPTFormWidgets() {
|
||||
const form = useFormContext();
|
||||
const { t } = useTranslate('flow');
|
||||
|
||||
return (
|
||||
<>
|
||||
<TopNFormField></TopNFormField>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="api_key"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('bgptApiKey')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} type="password" placeholder={t('bgptApiKeyTip')} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="days_back"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>{t('bgptDaysBack')}</FormLabel>
|
||||
<FormControl>
|
||||
<Input {...field} placeholder={t('bgptDaysBackTip')} />
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const outputList = buildOutputList(initialBGPTValues.outputs);
|
||||
|
||||
function BGPTForm({ node }: INextOperatorForm) {
|
||||
const defaultValues = useFormValues(initialBGPTValues, node);
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
defaultValues,
|
||||
resolver: zodResolver(FormSchema),
|
||||
});
|
||||
|
||||
useWatchFormChange(form);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<FormContainer>
|
||||
<QueryVariable></QueryVariable>
|
||||
<BGPTFormWidgets></BGPTFormWidgets>
|
||||
<Output list={outputList}></Output>
|
||||
</FormContainer>
|
||||
</FormWrapper>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(BGPTForm);
|
||||
35
web/src/pages/agent/form/tool-form/bgpt-form/index.tsx
Normal file
35
web/src/pages/agent/form/tool-form/bgpt-form/index.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { FormContainer } from '@/components/form-container';
|
||||
import { Form } from '@/components/ui/form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { memo } from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { BGPTFormPartialSchema, BGPTFormWidgets } from '../../bgpt-form';
|
||||
import { FormWrapper } from '../../components/form-wrapper';
|
||||
import { useValues } from '../use-values';
|
||||
import { useWatchFormChange } from '../use-watch-change';
|
||||
|
||||
function BGPTToolForm() {
|
||||
const values = useValues();
|
||||
|
||||
const FormSchema = z.object(BGPTFormPartialSchema);
|
||||
|
||||
const form = useForm<z.infer<typeof FormSchema>>({
|
||||
defaultValues: values,
|
||||
resolver: zodResolver(FormSchema),
|
||||
});
|
||||
|
||||
useWatchFormChange(form);
|
||||
|
||||
return (
|
||||
<Form {...form}>
|
||||
<FormWrapper>
|
||||
<FormContainer>
|
||||
<BGPTFormWidgets></BGPTFormWidgets>
|
||||
</FormContainer>
|
||||
</FormWrapper>
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
export default memo(BGPTToolForm);
|
||||
@@ -10,6 +10,7 @@ import GoogleForm from './google-form';
|
||||
import GoogleScholarForm from './google-scholar-form';
|
||||
import KeenableForm from './keenable-form';
|
||||
import PubMedForm from './pubmed-form';
|
||||
import BGPTForm from './bgpt-form';
|
||||
import RetrievalForm from './retrieval-form';
|
||||
import SearXNGForm from './searxng-form';
|
||||
import TavilyForm from './tavily-form';
|
||||
@@ -23,6 +24,7 @@ export const ToolFormConfigMap = {
|
||||
[Operator.DuckDuckGo]: DuckDuckGoForm,
|
||||
[Operator.Wikipedia]: WikipediaForm,
|
||||
[Operator.PubMed]: PubMedForm,
|
||||
[Operator.BGPT]: BGPTForm,
|
||||
[Operator.ArXiv]: ArXivForm,
|
||||
[Operator.Google]: GoogleForm,
|
||||
[Operator.Bing]: BingForm,
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
initialNoteValues,
|
||||
initialParserValues,
|
||||
initialPubMedValues,
|
||||
initialBGPTValues,
|
||||
initialRetrievalValues,
|
||||
initialRewriteQuestionValues,
|
||||
initialSearXNGValues,
|
||||
@@ -143,6 +144,7 @@ export const useInitializeOperatorParams = () => {
|
||||
[Operator.DuckDuckGo]: initialDuckValues,
|
||||
[Operator.Wikipedia]: initialWikipediaValues,
|
||||
[Operator.PubMed]: initialPubMedValues,
|
||||
[Operator.BGPT]: initialBGPTValues,
|
||||
[Operator.ArXiv]: initialArXivValues,
|
||||
[Operator.Google]: initialGoogleValues,
|
||||
[Operator.Bing]: initialBingValues,
|
||||
|
||||
@@ -51,6 +51,8 @@ export function useAgentToolInitialValues() {
|
||||
return pick(initialValues, 'top_n', 'sort_by');
|
||||
case Operator.PubMed:
|
||||
return pick(initialValues, 'top_n', 'email');
|
||||
case Operator.BGPT:
|
||||
return pick(initialValues, 'top_n', 'api_key', 'days_back');
|
||||
case Operator.GitHub:
|
||||
return pick(initialValues, 'top_n');
|
||||
case Operator.WenCai:
|
||||
|
||||
@@ -18,6 +18,7 @@ const clearSensitiveFields = <T,>(obj: T): T =>
|
||||
Operator.TavilyExtract,
|
||||
Operator.Google,
|
||||
Operator.KeenableSearch,
|
||||
Operator.BGPT,
|
||||
].includes(value.component_name) &&
|
||||
get(value, 'params.api_key')
|
||||
) {
|
||||
|
||||
@@ -25,6 +25,7 @@ type IToolIcon =
|
||||
| Operator.Google
|
||||
| Operator.GoogleScholar
|
||||
| Operator.PubMed
|
||||
| Operator.BGPT
|
||||
| Operator.TavilyExtract
|
||||
| Operator.TavilySearch
|
||||
| Operator.KeenableSearch
|
||||
|
||||
@@ -45,6 +45,7 @@ export const SVGIconMap = {
|
||||
[Operator.Google]: 'google',
|
||||
[Operator.GoogleScholar]: 'google-scholar',
|
||||
[Operator.PubMed]: 'pubmed',
|
||||
[Operator.BGPT]: 'bgpt',
|
||||
[Operator.SearXNG]: 'searxng',
|
||||
[Operator.KeenableSearch]: 'keenable',
|
||||
[Operator.TavilyExtract]: 'tavily',
|
||||
|
||||
Reference in New Issue
Block a user