### What problem does this PR solve? feat: Add bing and google operator #918 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.9.0
| @@ -0,0 +1,7 @@ | |||
| <svg t="1722318934204" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" | |||
| p-id="12570" width="200" height="200"> | |||
| <path d="M99.555556 99.555556h391.964444v391.964444H99.555556V99.555556z" fill="#F25022" p-id="12571"></path> | |||
| <path d="M532.48 99.555556H924.444444v391.964444H532.48V99.555556z" fill="#7FBA00" p-id="12572"></path> | |||
| <path d="M99.555556 532.48h391.964444V924.444444H99.555556V532.48z" fill="#00A4EF" p-id="12573"></path> | |||
| <path d="M532.48 532.48H924.444444V924.444444H532.48V532.48z" fill="#FFB900" p-id="12574"></path> | |||
| </svg> | |||
| @@ -0,0 +1,15 @@ | |||
| <svg t="1722306820309" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4272" | |||
| width="200" height="200"> | |||
| <path | |||
| d="M214.101333 512c0-32.512 5.546667-63.701333 15.36-92.928L57.173333 290.218667A491.861333 491.861333 0 0 0 4.693333 512c0 79.701333 18.858667 154.88 52.394667 221.610667l172.202667-129.066667A290.56 290.56 0 0 1 214.101333 512" | |||
| fill="#FBBC05" p-id="4273"></path> | |||
| <path | |||
| d="M516.693333 216.192c72.106667 0 137.258667 25.002667 188.458667 65.962667L854.101333 136.533333C763.349333 59.178667 646.997333 11.392 516.693333 11.392c-202.325333 0-376.234667 113.28-459.52 278.826667l172.373334 128.853333c39.68-118.016 152.832-202.88 287.146666-202.88" | |||
| fill="#EA4335" p-id="4274"></path> | |||
| <path | |||
| d="M516.693333 807.808c-134.357333 0-247.509333-84.864-287.232-202.88l-172.288 128.853333c83.242667 165.546667 257.152 278.826667 459.52 278.826667 124.842667 0 244.053333-43.392 333.568-124.757333l-163.584-123.818667c-46.122667 28.458667-104.234667 43.776-170.026666 43.776" | |||
| fill="#34A853" p-id="4275"></path> | |||
| <path | |||
| d="M1005.397333 512c0-29.568-4.693333-61.44-11.648-91.008H516.650667V614.4h274.602666c-13.696 65.962667-51.072 116.650667-104.533333 149.632l163.541333 123.818667c93.994667-85.418667 155.136-212.650667 155.136-375.850667" | |||
| fill="#4285F4" p-id="4276"></path> | |||
| </svg> | |||
| @@ -639,6 +639,15 @@ The above is the content you need to summarize.`, | |||
| submittedDate: 'Submitted date', | |||
| lastUpdatedDate: 'Last updated date', | |||
| relevance: 'Relevance', | |||
| google: 'Google', | |||
| googleTip: | |||
| 'This component is used to get search result fromhttps://www.google.com/ . Typically, it performs as a supplement to knowledgebases. Top N and SerpApi API key specifies the number of search results you need to adapt.', | |||
| bing: 'Bing', | |||
| bingTip: | |||
| 'This component is used to get search result from https://www.bing.com/. Typically, it performs as a supplement to knowledgebases. Top N and Bing Subscription-Key specifies the number of search results you need to adapt.', | |||
| apiKey: 'Api Key', | |||
| country: 'Country', | |||
| language: 'Language', | |||
| }, | |||
| footer: { | |||
| profile: 'All rights reserved @ React', | |||
| @@ -599,6 +599,15 @@ export default { | |||
| submittedDate: '提交日期', | |||
| lastUpdatedDate: '最後更新日期', | |||
| relevance: '關聯', | |||
| google: 'Google', | |||
| googleTip: | |||
| '此元件用於從https://www.google.com/取得搜尋結果。通常,它作為知識庫的補充。 Top N 和 SerpApi API 金鑰指定您需要調整的搜尋結果數量。', | |||
| bing: 'Bing', | |||
| bingTip: | |||
| '此元件用於從 https://www.bing.com/ 取得搜尋結果。通常,它充當知識庫的補充。 Top N 和 Bing Subscription-Key 指定您需要適配的搜尋結果數量。', | |||
| apiKey: 'Api Key', | |||
| country: '國家', | |||
| language: '語言', | |||
| }, | |||
| footer: { | |||
| profile: '“保留所有權利 @ react”', | |||
| @@ -617,6 +617,15 @@ export default { | |||
| submittedDate: '提交日期', | |||
| lastUpdatedDate: '最后更新日期', | |||
| relevance: '关联', | |||
| google: 'Google', | |||
| googleTip: | |||
| '此组件用于从https://www.google.com/获取搜索结果。通常,它作为知识库的补充。Top N 和 SerpApi API 密钥指定您需要调整的搜索结果数量。', | |||
| bing: 'Bing', | |||
| bingTip: | |||
| '此组件用于从 https://www.bing.com/ 获取搜索结果。通常,它作为知识库的补充。Top N 和 Bing Subscription-Key 指定您需要调整的搜索结果数量。', | |||
| apiKey: 'Api Key', | |||
| country: '国家', | |||
| language: '语言', | |||
| }, | |||
| footer: { | |||
| profile: 'All rights reserved @ React', | |||
| @@ -0,0 +1,41 @@ | |||
| import TopNItem from '@/components/top-n-item'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { Form, Input, Select } from 'antd'; | |||
| import { useMemo } from 'react'; | |||
| import { BingCountryOptions, BingLanguageOptions } from '../constant'; | |||
| import { IOperatorForm } from '../interface'; | |||
| const BingForm = ({ onValuesChange, form }: IOperatorForm) => { | |||
| const { t } = useTranslate('flow'); | |||
| const options = useMemo(() => { | |||
| return ['Webpages', 'News'].map((x) => ({ label: x, value: x })); | |||
| }, []); | |||
| return ( | |||
| <Form | |||
| name="basic" | |||
| labelCol={{ span: 6 }} | |||
| wrapperCol={{ span: 18 }} | |||
| autoComplete="off" | |||
| form={form} | |||
| onValuesChange={onValuesChange} | |||
| > | |||
| <TopNItem initialValue={10}></TopNItem> | |||
| <Form.Item label={t('channel')} name={'channel'}> | |||
| <Select options={options}></Select> | |||
| </Form.Item> | |||
| <Form.Item label={t('apiKey')} name={'api_key'}> | |||
| <Input></Input> | |||
| </Form.Item> | |||
| <Form.Item label={t('country')} name={'country'}> | |||
| <Select options={BingCountryOptions}></Select> | |||
| </Form.Item> | |||
| <Form.Item label={t('language')} name={'language'}> | |||
| <Select options={BingLanguageOptions}></Select> | |||
| </Form.Item> | |||
| </Form> | |||
| ); | |||
| }; | |||
| export default BingForm; | |||
| @@ -24,6 +24,7 @@ import ChatDrawer from '../chat/drawer'; | |||
| import styles from './index.less'; | |||
| import { BeginNode } from './node/begin-node'; | |||
| import { CategorizeNode } from './node/categorize-node'; | |||
| import { LogicNode } from './node/logic-node'; | |||
| import { RelevantNode } from './node/relevant-node'; | |||
| const nodeTypes = { | |||
| @@ -31,6 +32,7 @@ const nodeTypes = { | |||
| categorizeNode: CategorizeNode, | |||
| beginNode: BeginNode, | |||
| relevantNode: RelevantNode, | |||
| logicNode: LogicNode, | |||
| }; | |||
| const edgeTypes = { | |||
| @@ -65,7 +65,7 @@ export function CategorizeNode({ id, data, selected }: NodeProps<NodeData>) { | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.ragNode, { | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| style={{ | |||
| @@ -8,8 +8,8 @@ | |||
| padding: 5px; | |||
| border-radius: 5px; | |||
| background: white; | |||
| width: 100px; | |||
| height: 100px; | |||
| width: 50px; | |||
| height: 50px; | |||
| border-radius: 50%; | |||
| display: flex; | |||
| // align-items: center; | |||
| @@ -48,6 +48,7 @@ | |||
| white-space: nowrap; | |||
| } | |||
| } | |||
| .selectedNode { | |||
| border: 1px solid rgb(59, 118, 244); | |||
| } | |||
| @@ -64,3 +65,54 @@ | |||
| max-width: 300px; | |||
| max-height: 500px; | |||
| } | |||
| .logicNode { | |||
| position: relative; | |||
| box-shadow: | |||
| -6px 0 12px 0 rgba(179, 177, 177, 0.08), | |||
| -3px 0 6px -4px rgba(0, 0, 0, 0.12), | |||
| -6px 0 16px 6px rgba(0, 0, 0, 0.05); | |||
| padding: 5px; | |||
| border-radius: 5px; | |||
| background: white; | |||
| width: 100px; | |||
| height: 100px; | |||
| border-radius: 50%; | |||
| display: flex; | |||
| // align-items: center; | |||
| // justify-self: center; | |||
| justify-content: center; | |||
| .nodeName { | |||
| font-size: 10px; | |||
| color: black; | |||
| } | |||
| label { | |||
| display: block; | |||
| color: #777; | |||
| font-size: 12px; | |||
| } | |||
| .type { | |||
| // font-size: 12px; | |||
| } | |||
| .description { | |||
| font-size: 10px; | |||
| } | |||
| .bottomBox { | |||
| position: absolute; | |||
| bottom: -34px; | |||
| background: white; | |||
| padding: 2px 5px; | |||
| border-radius: 5px; | |||
| box-shadow: | |||
| -6px 0 12px 0 rgba(179, 177, 177, 0.08), | |||
| -3px 0 6px -4px rgba(0, 0, 0, 0.12), | |||
| -6px 0 16px 6px rgba(0, 0, 0, 0.05); | |||
| } | |||
| .categorizeAnchorPointText { | |||
| position: absolute; | |||
| top: -4px; | |||
| left: 8px; | |||
| white-space: nowrap; | |||
| } | |||
| } | |||
| @@ -1,7 +1,6 @@ | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { Flex } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import lowerFirst from 'lodash/lowerFirst'; | |||
| import pick from 'lodash/pick'; | |||
| import { Handle, NodeProps, Position } from 'reactflow'; | |||
| import { Operator, operatorMap } from '../../constant'; | |||
| @@ -32,7 +31,9 @@ export function RagNode({ | |||
| className={classNames(styles.ragNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| style={pick(style, ['backgroundColor', 'width', 'height', 'color'])} | |||
| style={{ | |||
| ...pick(style, ['backgroundColor', 'color']), | |||
| }} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| @@ -54,24 +55,19 @@ export function RagNode({ | |||
| vertical | |||
| align="center" | |||
| justify={'space-around'} | |||
| gap={ZeroGapOperators.some((x) => x === data.label) ? 0 : 6} | |||
| // gap={ZeroGapOperators.some((x) => x === data.label) ? 0 : 6} | |||
| > | |||
| <Flex flex={1} justify="center" align="center"> | |||
| <label htmlFor=""> </label> | |||
| </Flex> | |||
| <Flex flex={1}> | |||
| <OperatorIcon | |||
| name={data.label as Operator} | |||
| fontSize={style?.iconFontSize ?? 24} | |||
| fontSize={style?.iconFontSize ?? 16} | |||
| width={style?.iconWidth} | |||
| ></OperatorIcon> | |||
| </Flex> | |||
| <Flex flex={1}> | |||
| <span | |||
| className={styles.type} | |||
| style={{ fontSize: style?.fontSize ?? 14 }} | |||
| > | |||
| {t(lowerFirst(data.label))} | |||
| </span> | |||
| </Flex> | |||
| <Flex flex={1}> | |||
| <NodeDropdown | |||
| id={id} | |||
| @@ -0,0 +1,89 @@ | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { Flex } from 'antd'; | |||
| import classNames from 'classnames'; | |||
| import lowerFirst from 'lodash/lowerFirst'; | |||
| import pick from 'lodash/pick'; | |||
| import { Handle, NodeProps, Position } from 'reactflow'; | |||
| import { Operator, operatorMap } from '../../constant'; | |||
| import { NodeData } from '../../interface'; | |||
| import OperatorIcon from '../../operator-icon'; | |||
| import NodeDropdown from './dropdown'; | |||
| import styles from './index.less'; | |||
| import NodePopover from './popover'; | |||
| const ZeroGapOperators = [ | |||
| Operator.RewriteQuestion, | |||
| Operator.KeywordExtract, | |||
| Operator.ArXiv, | |||
| ]; | |||
| export function LogicNode({ | |||
| id, | |||
| data, | |||
| isConnectable = true, | |||
| selected, | |||
| }: NodeProps<NodeData>) { | |||
| const style = operatorMap[data.label as Operator]; | |||
| const { t } = useTranslate('flow'); | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| style={pick(style, ['backgroundColor', 'width', 'height', 'color'])} | |||
| > | |||
| <Handle | |||
| id="c" | |||
| type="source" | |||
| position={Position.Left} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| ></Handle> | |||
| <Handle type="source" position={Position.Top} id="d" isConnectable /> | |||
| <Handle | |||
| type="source" | |||
| position={Position.Right} | |||
| isConnectable={isConnectable} | |||
| className={styles.handle} | |||
| id="b" | |||
| ></Handle> | |||
| <Handle type="source" position={Position.Bottom} id="a" isConnectable /> | |||
| <Flex | |||
| vertical | |||
| align="center" | |||
| justify={'space-around'} | |||
| gap={ZeroGapOperators.some((x) => x === data.label) ? 0 : 6} | |||
| > | |||
| <Flex flex={1} justify="center" align="center"> | |||
| <OperatorIcon | |||
| name={data.label as Operator} | |||
| fontSize={style?.iconFontSize ?? 24} | |||
| width={style?.iconWidth} | |||
| ></OperatorIcon> | |||
| </Flex> | |||
| <Flex flex={1}> | |||
| <span | |||
| className={styles.type} | |||
| style={{ fontSize: style?.fontSize ?? 14 }} | |||
| > | |||
| {t(lowerFirst(data.label))} | |||
| </span> | |||
| </Flex> | |||
| <Flex flex={1}> | |||
| <NodeDropdown | |||
| id={id} | |||
| iconFontColor={style?.moreIconColor} | |||
| ></NodeDropdown> | |||
| </Flex> | |||
| </Flex> | |||
| <section className={styles.bottomBox}> | |||
| <div className={styles.nodeName}>{data.name}</div> | |||
| </section> | |||
| </section> | |||
| </NodePopover> | |||
| ); | |||
| } | |||
| @@ -19,7 +19,7 @@ export function RelevantNode({ id, data, selected }: NodeProps<NodeData>) { | |||
| return ( | |||
| <NodePopover nodeId={id}> | |||
| <section | |||
| className={classNames(styles.ragNode, { | |||
| className={classNames(styles.logicNode, { | |||
| [styles.selectedNode]: selected, | |||
| })} | |||
| style={pick(style, ['backgroundColor', 'width', 'height', 'color'])} | |||
| @@ -7,10 +7,12 @@ import AnswerForm from '../answer-form'; | |||
| import ArXivForm from '../arxiv-form'; | |||
| import BaiduForm from '../baidu-form'; | |||
| import BeginForm from '../begin-form'; | |||
| import BingForm from '../bing-form'; | |||
| import CategorizeForm from '../categorize-form'; | |||
| import { Operator } from '../constant'; | |||
| import DuckDuckGoForm from '../duckduckgo-form'; | |||
| import GenerateForm from '../generate-form'; | |||
| import GoogleForm from '../google-form'; | |||
| import { useHandleFormValuesChange, useHandleNodeNameChange } from '../hooks'; | |||
| import KeywordExtractForm from '../keyword-extract-form'; | |||
| import MessageForm from '../message-form'; | |||
| @@ -42,6 +44,8 @@ const FormMap = { | |||
| [Operator.Wikipedia]: WikipediaForm, | |||
| [Operator.PubMed]: PubMedForm, | |||
| [Operator.ArXiv]: ArXivForm, | |||
| [Operator.Google]: GoogleForm, | |||
| [Operator.Bing]: BingForm, | |||
| }; | |||
| const EmptyContent = () => <div>empty</div>; | |||
| @@ -0,0 +1,33 @@ | |||
| import TopNItem from '@/components/top-n-item'; | |||
| import { useTranslate } from '@/hooks/common-hooks'; | |||
| import { Form, Input, Select } from 'antd'; | |||
| import { GoogleCountryOptions, GoogleLanguageOptions } from '../constant'; | |||
| import { IOperatorForm } from '../interface'; | |||
| const GoogleForm = ({ onValuesChange, form }: IOperatorForm) => { | |||
| const { t } = useTranslate('flow'); | |||
| return ( | |||
| <Form | |||
| name="basic" | |||
| labelCol={{ span: 6 }} | |||
| wrapperCol={{ span: 18 }} | |||
| autoComplete="off" | |||
| form={form} | |||
| onValuesChange={onValuesChange} | |||
| > | |||
| <TopNItem initialValue={10}></TopNItem> | |||
| <Form.Item label={t('apiKey')} name={'api_key'}> | |||
| <Input></Input> | |||
| </Form.Item> | |||
| <Form.Item label={t('country')} name={'country'}> | |||
| <Select options={GoogleCountryOptions}></Select> | |||
| </Form.Item> | |||
| <Form.Item label={t('language')} name={'language'}> | |||
| <Select options={GoogleLanguageOptions}></Select> | |||
| </Form.Item> | |||
| </Form> | |||
| ); | |||
| }; | |||
| export default GoogleForm; | |||
| @@ -30,12 +30,14 @@ import { | |||
| NodeMap, | |||
| Operator, | |||
| RestrictedUpstreamMap, | |||
| initialArxivValues, | |||
| initialArXivValues, | |||
| initialBaiduValues, | |||
| initialBeginValues, | |||
| initialBingValues, | |||
| initialCategorizeValues, | |||
| initialDuckValues, | |||
| initialGenerateValues, | |||
| initialGoogleValues, | |||
| initialKeywordExtractValues, | |||
| initialMessageValues, | |||
| initialPubMedValues, | |||
| @@ -92,7 +94,9 @@ export const useInitializeOperatorParams = () => { | |||
| [Operator.Baidu]: initialBaiduValues, | |||
| [Operator.Wikipedia]: initialWikipediaValues, | |||
| [Operator.PubMed]: initialPubMedValues, | |||
| [Operator.ArXiv]: initialArxivValues, | |||
| [Operator.ArXiv]: initialArXivValues, | |||
| [Operator.Google]: initialGoogleValues, | |||
| [Operator.Bing]: initialBingValues, | |||
| }; | |||
| }, [llmId]); | |||