Compare commits
1 Commits
gru/projec
...
test-html
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d7a722a609 |
@@ -645,7 +645,7 @@ data 为集合的 ID。
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
### 创建一个外部文件库集合(弃用)
|
||||
### 创建一个外部文件库集合(商业版)
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="请求示例" >}}
|
||||
|
||||
@@ -13,7 +13,6 @@ const staticPluginList = [
|
||||
'WeWorkWebhook',
|
||||
'google',
|
||||
'bing',
|
||||
'bocha',
|
||||
'delay'
|
||||
];
|
||||
// Run in worker thread (Have npm packages)
|
||||
|
||||
@@ -1,677 +0,0 @@
|
||||
{
|
||||
"author": "",
|
||||
"name": "博查搜索",
|
||||
"avatar": "core/workflow/template/bocha",
|
||||
"intro": "使用博查AI搜索引擎进行网络搜索。",
|
||||
"showStatus": true,
|
||||
"weight": 10,
|
||||
"courseUrl": "",
|
||||
"isTool": true,
|
||||
"templateType": "search",
|
||||
"workflow": {
|
||||
"nodes": [
|
||||
{
|
||||
"nodeId": "pluginInput",
|
||||
"name": "workflow:template.plugin_start",
|
||||
"intro": "workflow:intro_plugin_input",
|
||||
"avatar": "core/workflow/template/workflowStart",
|
||||
"flowNodeType": "pluginInput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 636.3048409085379,
|
||||
"y": -238.61714728578016
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": [
|
||||
"input"
|
||||
],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "apiKey",
|
||||
"label": "apiKey",
|
||||
"description": "博查API密钥",
|
||||
"defaultValue": "",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"renderTypeList": [
|
||||
"input",
|
||||
"reference"
|
||||
],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"description": "搜索查询词",
|
||||
"defaultValue": "",
|
||||
"required": true,
|
||||
"toolDescription": "搜索查询词"
|
||||
},
|
||||
{
|
||||
"renderTypeList": [
|
||||
"input",
|
||||
"reference"
|
||||
],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "freshness",
|
||||
"label": "freshness",
|
||||
"description": "搜索指定时间范围内的网页。可填值:oneDay(一天内)、oneWeek(一周内)、oneMonth(一个月内)、oneYear(一年内)、noLimit(不限,默认)、YYYY-MM-DD..YYYY-MM-DD(日期范围)、YYYY-MM-DD(指定日期)",
|
||||
"defaultValue": "noLimit",
|
||||
"required": false,
|
||||
"toolDescription": "搜索时间范围"
|
||||
},
|
||||
{
|
||||
"renderTypeList": [
|
||||
"input",
|
||||
"reference"
|
||||
],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "boolean",
|
||||
"canEdit": true,
|
||||
"key": "summary",
|
||||
"label": "summary",
|
||||
"description": "是否显示文本摘要。true显示,false不显示(默认)",
|
||||
"defaultValue": false,
|
||||
"required": false,
|
||||
"toolDescription": "是否显示文本摘要"
|
||||
},
|
||||
{
|
||||
"renderTypeList": [
|
||||
"input",
|
||||
"reference"
|
||||
],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "include",
|
||||
"label": "include",
|
||||
"description": "指定搜索的site范围。多个域名使用|或,分隔,最多20个。例如:qq.com|m.163.com",
|
||||
"defaultValue": "",
|
||||
"required": false,
|
||||
"toolDescription": "指定搜索的site范围"
|
||||
},
|
||||
{
|
||||
"renderTypeList": [
|
||||
"input",
|
||||
"reference"
|
||||
],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "string",
|
||||
"canEdit": true,
|
||||
"key": "exclude",
|
||||
"label": "exclude",
|
||||
"description": "排除搜索的网站范围。多个域名使用|或,分隔,最多20个。例如:qq.com|m.163.com",
|
||||
"defaultValue": "",
|
||||
"required": false,
|
||||
"toolDescription": "排除搜索的网站范围"
|
||||
},
|
||||
{
|
||||
"renderTypeList": [
|
||||
"input",
|
||||
"reference"
|
||||
],
|
||||
"selectedTypeIndex": 0,
|
||||
"valueType": "number",
|
||||
"canEdit": true,
|
||||
"key": "count",
|
||||
"label": "count",
|
||||
"description": "返回结果的条数。可填范围:1-50,默认为10",
|
||||
"defaultValue": 10,
|
||||
"required": false,
|
||||
"min": 1,
|
||||
"max": 50,
|
||||
"toolDescription": "返回结果条数"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"id": "apiKey",
|
||||
"valueType": "string",
|
||||
"key": "apiKey",
|
||||
"label": "apiKey",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "query",
|
||||
"valueType": "string",
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "freshness",
|
||||
"valueType": "string",
|
||||
"key": "freshness",
|
||||
"label": "freshness",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "summary",
|
||||
"valueType": "boolean",
|
||||
"key": "summary",
|
||||
"label": "summary",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "include",
|
||||
"valueType": "string",
|
||||
"key": "include",
|
||||
"label": "include",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "exclude",
|
||||
"valueType": "string",
|
||||
"key": "exclude",
|
||||
"label": "exclude",
|
||||
"type": "hidden"
|
||||
},
|
||||
{
|
||||
"id": "count",
|
||||
"valueType": "number",
|
||||
"key": "count",
|
||||
"label": "count",
|
||||
"type": "hidden"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"nodeId": "pluginOutput",
|
||||
"name": "common:core.module.template.self_output",
|
||||
"intro": "workflow:intro_custom_plugin_output",
|
||||
"avatar": "core/workflow/template/pluginOutput",
|
||||
"flowNodeType": "pluginOutput",
|
||||
"showStatus": false,
|
||||
"position": {
|
||||
"x": 2764.1105686698083,
|
||||
"y": -30.617147285780163
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"renderTypeList": [
|
||||
"reference"
|
||||
],
|
||||
"valueType": "object",
|
||||
"canEdit": true,
|
||||
"key": "result",
|
||||
"label": "result",
|
||||
"isToolOutput": true,
|
||||
"description": "",
|
||||
"value": [
|
||||
"nyA6oA8mF1iW",
|
||||
"httpRawResponse"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"nodeId": "pluginConfig",
|
||||
"name": "common:core.module.template.system_config",
|
||||
"intro": "",
|
||||
"avatar": "core/workflow/template/systemConfig",
|
||||
"flowNodeType": "pluginConfig",
|
||||
"position": {
|
||||
"x": 184.66337662472682,
|
||||
"y": -216.05298493910115
|
||||
},
|
||||
"version": "4811",
|
||||
"inputs": [],
|
||||
"outputs": []
|
||||
},
|
||||
{
|
||||
"nodeId": "nyA6oA8mF1iW",
|
||||
"name": "HTTP 请求",
|
||||
"intro": "调用博查搜索API",
|
||||
"avatar": "core/workflow/template/httpRequest",
|
||||
"flowNodeType": "httpRequest468",
|
||||
"showStatus": true,
|
||||
"position": {
|
||||
"x": 1335.0647252518884,
|
||||
"y": -455.9043948565971
|
||||
},
|
||||
"version": "481",
|
||||
"inputs": [
|
||||
{
|
||||
"key": "system_addInputParam",
|
||||
"renderTypeList": [
|
||||
"addInputParam"
|
||||
],
|
||||
"valueType": "dynamic",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"description": "common:core.module.input.description.HTTP Dynamic Input",
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectDataset",
|
||||
"selectApp"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "system_httpMethod",
|
||||
"renderTypeList": [
|
||||
"custom"
|
||||
],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"value": "POST",
|
||||
"required": true,
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "system_httpTimeout",
|
||||
"renderTypeList": [
|
||||
"custom"
|
||||
],
|
||||
"valueType": "number",
|
||||
"label": "",
|
||||
"value": 30,
|
||||
"min": 5,
|
||||
"max": 600,
|
||||
"required": true,
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "system_httpReqUrl",
|
||||
"renderTypeList": [
|
||||
"hidden"
|
||||
],
|
||||
"valueType": "string",
|
||||
"label": "",
|
||||
"description": "common:core.module.input.description.Http Request Url",
|
||||
"placeholder": "https://api.ai.com/getInventory",
|
||||
"required": false,
|
||||
"value": "https://api.bochaai.com/v1/web-search",
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "system_httpHeader",
|
||||
"renderTypeList": [
|
||||
"custom"
|
||||
],
|
||||
"valueType": "any",
|
||||
"value": [
|
||||
{
|
||||
"key": "Authorization",
|
||||
"type": "string",
|
||||
"value": "Bearer {{$pluginInput.apiKey$}}"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"type": "string",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"label": "",
|
||||
"description": "common:core.module.input.description.Http Request Header",
|
||||
"placeholder": "common:core.module.input.description.Http Request Header",
|
||||
"required": false,
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "system_httpParams",
|
||||
"renderTypeList": [
|
||||
"hidden"
|
||||
],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"required": false,
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "system_httpJsonBody",
|
||||
"renderTypeList": [
|
||||
"hidden"
|
||||
],
|
||||
"valueType": "any",
|
||||
"value": "{\n \"query\": \"{{query}}\",\n \"freshness\": \"{{freshness}}\",\n \"summary\": {{summary}},\n \"include\": \"{{include}}\",\n \"exclude\": \"{{exclude}}\",\n \"count\": {{count}}\n}",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "system_httpFormBody",
|
||||
"renderTypeList": [
|
||||
"hidden"
|
||||
],
|
||||
"valueType": "any",
|
||||
"value": [],
|
||||
"label": "",
|
||||
"required": false,
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"key": "system_httpContentType",
|
||||
"renderTypeList": [
|
||||
"hidden"
|
||||
],
|
||||
"valueType": "string",
|
||||
"value": "json",
|
||||
"label": "",
|
||||
"required": false,
|
||||
"debugLabel": "",
|
||||
"toolDescription": ""
|
||||
},
|
||||
{
|
||||
"valueType": "string",
|
||||
"renderTypeList": [
|
||||
"reference"
|
||||
],
|
||||
"key": "query",
|
||||
"label": "query",
|
||||
"toolDescription": "博查搜索检索词",
|
||||
"required": true,
|
||||
"canEdit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"description": true
|
||||
},
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"value": [
|
||||
"pluginInput",
|
||||
"query"
|
||||
]
|
||||
},
|
||||
{
|
||||
"valueType": "string",
|
||||
"renderTypeList": [
|
||||
"reference"
|
||||
],
|
||||
"key": "freshness",
|
||||
"label": "freshness",
|
||||
"toolDescription": "搜索时间范围",
|
||||
"required": false,
|
||||
"canEdit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"description": true
|
||||
},
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"value": [
|
||||
"pluginInput",
|
||||
"freshness"
|
||||
]
|
||||
},
|
||||
{
|
||||
"valueType": "boolean",
|
||||
"renderTypeList": [
|
||||
"reference"
|
||||
],
|
||||
"key": "summary",
|
||||
"label": "summary",
|
||||
"toolDescription": "是否显示文本摘要",
|
||||
"required": false,
|
||||
"canEdit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"description": true
|
||||
},
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"value": [
|
||||
"pluginInput",
|
||||
"summary"
|
||||
]
|
||||
},
|
||||
{
|
||||
"valueType": "string",
|
||||
"renderTypeList": [
|
||||
"reference"
|
||||
],
|
||||
"key": "include",
|
||||
"label": "include",
|
||||
"toolDescription": "指定搜索的site范围",
|
||||
"required": false,
|
||||
"canEdit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"description": true
|
||||
},
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"value": [
|
||||
"pluginInput",
|
||||
"include"
|
||||
]
|
||||
},
|
||||
{
|
||||
"valueType": "string",
|
||||
"renderTypeList": [
|
||||
"reference"
|
||||
],
|
||||
"key": "exclude",
|
||||
"label": "exclude",
|
||||
"toolDescription": "排除搜索的网站范围",
|
||||
"required": false,
|
||||
"canEdit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"description": true
|
||||
},
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"value": [
|
||||
"pluginInput",
|
||||
"exclude"
|
||||
]
|
||||
},
|
||||
{
|
||||
"valueType": "number",
|
||||
"renderTypeList": [
|
||||
"reference"
|
||||
],
|
||||
"key": "count",
|
||||
"label": "count",
|
||||
"toolDescription": "返回结果条数",
|
||||
"required": false,
|
||||
"canEdit": true,
|
||||
"editField": {
|
||||
"key": true,
|
||||
"description": true
|
||||
},
|
||||
"customInputConfig": {
|
||||
"selectValueTypeList": [
|
||||
"string",
|
||||
"number",
|
||||
"boolean",
|
||||
"object",
|
||||
"arrayString",
|
||||
"arrayNumber",
|
||||
"arrayBoolean",
|
||||
"arrayObject",
|
||||
"arrayAny",
|
||||
"any",
|
||||
"chatHistory",
|
||||
"datasetQuote",
|
||||
"dynamic",
|
||||
"selectApp",
|
||||
"selectDataset"
|
||||
],
|
||||
"showDescription": false,
|
||||
"showDefaultValue": true
|
||||
},
|
||||
"value": [
|
||||
"pluginInput",
|
||||
"count"
|
||||
]
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"id": "error",
|
||||
"key": "error",
|
||||
"label": "workflow:request_error",
|
||||
"description": "HTTP请求错误信息,成功时返回空",
|
||||
"valueType": "object",
|
||||
"type": "static"
|
||||
},
|
||||
{
|
||||
"id": "httpRawResponse",
|
||||
"key": "httpRawResponse",
|
||||
"required": true,
|
||||
"label": "workflow:raw_response",
|
||||
"description": "HTTP请求的原始响应。只能接受字符串或JSON类型响应数据。",
|
||||
"valueType": "any",
|
||||
"type": "static"
|
||||
},
|
||||
{
|
||||
"id": "system_addOutputParam",
|
||||
"key": "system_addOutputParam",
|
||||
"type": "dynamic",
|
||||
"valueType": "dynamic",
|
||||
"label": "",
|
||||
"editField": {
|
||||
"key": true,
|
||||
"valueType": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"source": "pluginInput",
|
||||
"target": "nyA6oA8mF1iW",
|
||||
"sourceHandle": "pluginInput-source-right",
|
||||
"targetHandle": "nyA6oA8mF1iW-target-left"
|
||||
},
|
||||
{
|
||||
"source": "nyA6oA8mF1iW",
|
||||
"target": "pluginOutput",
|
||||
"sourceHandle": "nyA6oA8mF1iW-source-right",
|
||||
"targetHandle": "pluginOutput-target-left"
|
||||
}
|
||||
]
|
||||
},
|
||||
"chatConfig": {}
|
||||
}
|
||||
@@ -287,7 +287,6 @@ export const iconPaths = {
|
||||
'core/workflow/template/aiChat': () => import('./icons/core/workflow/template/aiChat.svg'),
|
||||
'core/workflow/template/baseChart': () => import('./icons/core/workflow/template/baseChart.svg'),
|
||||
'core/workflow/template/bing': () => import('./icons/core/workflow/template/bing.svg'),
|
||||
'core/workflow/template/bocha': () => import('./icons/core/workflow/template/bocha.svg'),
|
||||
'core/workflow/template/codeRun': () => import('./icons/core/workflow/template/codeRun.svg'),
|
||||
'core/workflow/template/customFeedback': () =>
|
||||
import('./icons/core/workflow/template/customFeedback.svg'),
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<svg width="113" height="97" viewBox="0 0 113 97" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 31.7259C1.80046 29.9255 3.82784 28.3872 5.96621 27.1988C8.10469 26.0103 10.3126 25.1947 12.4634 24.7992C14.6143 24.4037 16.6664 24.4361 18.5022 24.8938C20.2678 25.334 21.7994 26.1604 23.0183 27.3272L23.021 27.3245L47.189 51.4924L33.4778 65.2037L0 31.7259Z" fill="#C4DEFE"/>
|
||||
<path d="M9.15662 11.5625C11.3617 10.2893 13.7181 9.32825 16.0912 8.73374C18.4645 8.13923 20.8082 7.92284 22.9882 8.09751C25.1681 8.27217 27.1419 8.83457 28.7966 9.75182C30.3881 10.6341 31.6537 11.8287 32.529 13.2712L32.5316 13.2697L32.6082 13.4025C32.6162 13.4162 32.6251 13.4297 32.633 13.4435L49.886 43.3286L33.0941 53.0234L9.15662 11.5625Z" fill="#A6CBFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M31.1377 0C33.6839 4.40811e-05 36.2052 0.345872 38.5576 1.01758C40.9099 1.68929 43.0472 2.67394 44.8477 3.91504C46.6482 5.15627 48.0773 6.63021 49.0518 8.25195C49.9888 9.81168 50.4867 11.4792 50.5234 13.166H50.5273V21.4072C56.6623 17.6586 63.874 15.498 71.5898 15.498C93.9304 15.4984 112.042 33.6087 112.042 55.9492C112.042 78.29 93.9305 96.401 71.5898 96.4014C49.3907 96.4014 31.3704 78.5193 31.1426 56.374H31.1377V0ZM71.9473 35.0439C60.1187 35.0441 50.5295 44.6334 50.5293 56.4619C50.5293 63.5338 53.9569 69.8057 59.2412 73.7061C66.4989 79.0625 76.5515 75.3841 85.3955 77.1592C92.613 78.608 97.2369 82.6827 98.3652 83.7686C97.3562 82.731 93.791 78.7138 92.2715 72.3291C89.8011 61.9479 94.8744 49.6043 87.5771 41.8184C83.6695 37.6493 78.1122 35.0441 71.9473 35.0439Z" fill="#006EFF"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
7
plugins/webcrawler/SPIDER/package-lock.json
generated
7
plugins/webcrawler/SPIDER/package-lock.json
generated
@@ -4992,10 +4992,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/tar-fs": {
|
||||
"version": "3.0.9",
|
||||
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.9.tgz",
|
||||
"integrity": "sha512-XF4w9Xp+ZQgifKakjZYmFdkLoSWd34VGKcsTCwlNWM7QG3ZbaxnTsaBwnjFZqHRf/rROxaR8rXnbtwdvaDI+lA==",
|
||||
"license": "MIT",
|
||||
"version": "3.0.8",
|
||||
"resolved": "https://registry.npmmirror.com/tar-fs/-/tar-fs-3.0.8.tgz",
|
||||
"integrity": "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg==",
|
||||
"dependencies": {
|
||||
"pump": "^3.0.0",
|
||||
"tar-stream": "^3.1.5"
|
||||
|
||||
68
pnpm-lock.yaml
generated
68
pnpm-lock.yaml
generated
@@ -46,7 +46,7 @@ importers:
|
||||
version: 10.1.4(socks@2.8.4)
|
||||
next-i18next:
|
||||
specifier: 15.4.2
|
||||
version: 15.4.2(i18next@23.16.8)(next@14.2.28(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
version: 15.4.2(i18next@23.16.8)(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
prettier:
|
||||
specifier: 3.2.4
|
||||
version: 3.2.4
|
||||
@@ -343,7 +343,7 @@ importers:
|
||||
version: 2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
'@chakra-ui/next-js':
|
||||
specifier: 2.4.2
|
||||
version: 2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.28(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1)
|
||||
version: 2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1)
|
||||
'@chakra-ui/react':
|
||||
specifier: 2.10.7
|
||||
version: 2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -406,7 +406,7 @@ importers:
|
||||
version: 4.17.21
|
||||
next-i18next:
|
||||
specifier: 15.4.2
|
||||
version: 15.4.2(i18next@23.16.8)(next@14.2.28(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
version: 15.4.2(i18next@23.16.8)(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
papaparse:
|
||||
specifier: ^5.4.1
|
||||
version: 5.4.1
|
||||
@@ -467,7 +467,7 @@ importers:
|
||||
version: 2.1.1(@chakra-ui/system@2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
'@chakra-ui/next-js':
|
||||
specifier: 2.4.2
|
||||
version: 2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.28(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1)
|
||||
version: 2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1)
|
||||
'@chakra-ui/react':
|
||||
specifier: 2.10.7
|
||||
version: 2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
@@ -569,7 +569,7 @@ importers:
|
||||
version: 14.2.28(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1)
|
||||
next-i18next:
|
||||
specifier: 15.4.2
|
||||
version: 15.4.2(i18next@23.16.8)(next@14.2.28(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
version: 15.4.2(i18next@23.16.8)(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)
|
||||
nprogress:
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0
|
||||
@@ -612,6 +612,9 @@ importers:
|
||||
rehype-katex:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.1
|
||||
rehype-raw:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
remark-breaks:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
@@ -5879,9 +5882,15 @@ packages:
|
||||
hast-util-parse-selector@4.0.0:
|
||||
resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
|
||||
|
||||
hast-util-raw@9.1.0:
|
||||
resolution: {integrity: sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==}
|
||||
|
||||
hast-util-to-jsx-runtime@2.3.6:
|
||||
resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
|
||||
|
||||
hast-util-to-parse5@8.0.0:
|
||||
resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==}
|
||||
|
||||
hast-util-to-text@4.0.2:
|
||||
resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==}
|
||||
|
||||
@@ -5919,6 +5928,9 @@ packages:
|
||||
html-url-attributes@3.0.1:
|
||||
resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==}
|
||||
|
||||
html-void-elements@3.0.0:
|
||||
resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
|
||||
|
||||
htmlparser2@8.0.2:
|
||||
resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
|
||||
|
||||
@@ -8026,6 +8038,9 @@ packages:
|
||||
property-information@5.6.0:
|
||||
resolution: {integrity: sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==}
|
||||
|
||||
property-information@6.5.0:
|
||||
resolution: {integrity: sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==}
|
||||
|
||||
property-information@7.0.0:
|
||||
resolution: {integrity: sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==}
|
||||
|
||||
@@ -8361,6 +8376,9 @@ packages:
|
||||
rehype-katex@7.0.1:
|
||||
resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==}
|
||||
|
||||
rehype-raw@7.0.0:
|
||||
resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
|
||||
|
||||
remark-breaks@4.0.0:
|
||||
resolution: {integrity: sha512-IjEjJOkH4FuJvHZVIW0QCDWxcG96kCq7An/KVH2NfJe6rKZU2AsHeB3OEjPNRxi4QC34Xdx7I2KGYn6IpT7gxQ==}
|
||||
|
||||
@@ -10969,7 +10987,7 @@ snapshots:
|
||||
'@chakra-ui/system': 2.6.1(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(react@18.3.1)
|
||||
react: 18.3.1
|
||||
|
||||
'@chakra-ui/next-js@2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.28(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1)':
|
||||
'@chakra-ui/next-js@2.4.2(@chakra-ui/react@2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@chakra-ui/react': 2.10.7(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@emotion/styled@11.11.0(@emotion/react@11.11.1(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(react@18.3.1))(@types/react@18.3.1)(framer-motion@9.1.7(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@emotion/cache': 11.14.0
|
||||
@@ -16108,6 +16126,22 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
|
||||
hast-util-raw@9.1.0:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
'@types/unist': 3.0.3
|
||||
'@ungap/structured-clone': 1.3.0
|
||||
hast-util-from-parse5: 8.0.3
|
||||
hast-util-to-parse5: 8.0.0
|
||||
html-void-elements: 3.0.0
|
||||
mdast-util-to-hast: 13.2.0
|
||||
parse5: 7.2.1
|
||||
unist-util-position: 5.0.0
|
||||
unist-util-visit: 5.0.0
|
||||
vfile: 6.0.3
|
||||
web-namespaces: 2.0.1
|
||||
zwitch: 2.0.4
|
||||
|
||||
hast-util-to-jsx-runtime@2.3.6:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.6
|
||||
@@ -16128,6 +16162,16 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
hast-util-to-parse5@8.0.0:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
comma-separated-tokens: 2.0.3
|
||||
devlop: 1.1.0
|
||||
property-information: 6.5.0
|
||||
space-separated-tokens: 2.0.2
|
||||
web-namespaces: 2.0.1
|
||||
zwitch: 2.0.4
|
||||
|
||||
hast-util-to-text@4.0.2:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
@@ -16175,6 +16219,8 @@ snapshots:
|
||||
|
||||
html-url-attributes@3.0.1: {}
|
||||
|
||||
html-void-elements@3.0.0: {}
|
||||
|
||||
htmlparser2@8.0.2:
|
||||
dependencies:
|
||||
domelementtype: 2.3.0
|
||||
@@ -18238,7 +18284,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
next-i18next@15.4.2(i18next@23.16.8)(next@14.2.28(@babel/core@7.26.10)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
|
||||
next-i18next@15.4.2(i18next@23.16.8)(next@14.2.28(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sass@1.85.1))(react-i18next@14.1.2(i18next@23.16.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.10
|
||||
'@types/hoist-non-react-statics': 3.3.6
|
||||
@@ -18873,6 +18919,8 @@ snapshots:
|
||||
dependencies:
|
||||
xtend: 4.0.2
|
||||
|
||||
property-information@6.5.0: {}
|
||||
|
||||
property-information@7.0.0: {}
|
||||
|
||||
proto-list@1.2.4: {}
|
||||
@@ -19292,6 +19340,12 @@ snapshots:
|
||||
unist-util-visit-parents: 6.0.1
|
||||
vfile: 6.0.3
|
||||
|
||||
rehype-raw@7.0.0:
|
||||
dependencies:
|
||||
'@types/hast': 3.0.4
|
||||
hast-util-raw: 9.1.0
|
||||
vfile: 6.0.3
|
||||
|
||||
remark-breaks@4.0.0:
|
||||
dependencies:
|
||||
'@types/mdast': 4.0.4
|
||||
|
||||
@@ -11,6 +11,53 @@ const nextConfig = {
|
||||
output: 'standalone',
|
||||
reactStrictMode: isDev ? false : true,
|
||||
compress: true,
|
||||
|
||||
headers: async () => {
|
||||
const nonce = Buffer.from(crypto.randomUUID()).toString('base64');
|
||||
const csp = `'nonce-${nonce}'`;
|
||||
const scheme_source = 'data: mediastream: blob: filesystem:';
|
||||
const NECESSARY_DOMAINS = [
|
||||
'*.sentry.io',
|
||||
'http://localhost:*',
|
||||
'http://127.0.0.1:*',
|
||||
'https://analytics.google.com',
|
||||
'googletagmanager.com',
|
||||
'*.googletagmanager.com',
|
||||
'https://www.google-analytics.com',
|
||||
'https://api.github.com'
|
||||
].join(' ');
|
||||
|
||||
return [
|
||||
{
|
||||
source: '/chat/(.*)',
|
||||
headers: [
|
||||
{ key: 'X-Frame-Options', value: 'DENY' },
|
||||
{ key: 'X-Content-Type-Options', value: 'nosniff' },
|
||||
{ key: 'X-XSS-Protection', value: '1; mode=block' },
|
||||
{ key: 'Referrer-Policy', value: 'no-referrer' },
|
||||
{
|
||||
key: 'Content-Security-Policy',
|
||||
value: [
|
||||
`default-src 'self' ${scheme_source} ${NECESSARY_DOMAINS} ${csp}`,
|
||||
`script-src 'self' 'unsafe-inline' 'unsafe-eval' ${csp} ${NECESSARY_DOMAINS}`,
|
||||
`style-src 'self' 'unsafe-inline' ${csp} ${NECESSARY_DOMAINS}`,
|
||||
`media-src 'self' http: ${scheme_source} ${NECESSARY_DOMAINS} ${csp}`,
|
||||
`worker-src 'self' ${csp} ${NECESSARY_DOMAINS} ${scheme_source}`,
|
||||
`img-src * data: blob:`,
|
||||
`font-src 'self'`,
|
||||
`connect-src 'self' wss: https: ${scheme_source} ${NECESSARY_DOMAINS} ${csp}`,
|
||||
"object-src 'none'",
|
||||
"form-action 'self'",
|
||||
"base-uri 'self'",
|
||||
"frame-src 'self' 'allow-scripts'",
|
||||
'sandbox allow-scripts allow-same-origin allow-popups allow-forms',
|
||||
'upgrade-insecure-requests'
|
||||
].join('; ')
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
},
|
||||
webpack(config, { isServer, nextRuntime }) {
|
||||
Object.assign(config.resolve.alias, {
|
||||
'@mongodb-js/zstd': false,
|
||||
@@ -85,7 +132,7 @@ const nextConfig = {
|
||||
'pg',
|
||||
'bullmq',
|
||||
'@zilliz/milvus2-sdk-node',
|
||||
"tiktoken",
|
||||
'tiktoken'
|
||||
],
|
||||
outputFileTracingRoot: path.join(__dirname, '../../'),
|
||||
instrumentationHook: true
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
"recharts": "^2.15.0",
|
||||
"rehype-external-links": "^3.0.0",
|
||||
"rehype-katex": "^7.0.0",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-breaks": "^4.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-math": "^6.0.0",
|
||||
|
||||
35
projects/app/src/components/Markdown/errorBoundry.tsx
Normal file
35
projects/app/src/components/Markdown/errorBoundry.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import React from 'react';
|
||||
|
||||
interface ErrorBoundaryProps {
|
||||
children: React.ReactNode;
|
||||
fallback?: React.ReactNode;
|
||||
}
|
||||
|
||||
interface ErrorBoundaryState {
|
||||
hasError: boolean;
|
||||
error: Error | null;
|
||||
}
|
||||
|
||||
class ErrorBoundary extends React.Component<ErrorBoundaryProps, ErrorBoundaryState> {
|
||||
constructor(props: ErrorBoundaryProps) {
|
||||
super(props);
|
||||
this.state = { hasError: false, error: null };
|
||||
}
|
||||
|
||||
static getDerivedStateFromError(error: Error) {
|
||||
return { hasError: true, error };
|
||||
}
|
||||
|
||||
componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
|
||||
console.error('ErrorBoundary caught an error:', error, errorInfo);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (this.state.hasError) {
|
||||
return this.props.fallback || <div>Something went wrong while rendering Markdown.</div>;
|
||||
}
|
||||
return this.props.children;
|
||||
}
|
||||
}
|
||||
|
||||
export default ErrorBoundary;
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import React, { memo, useCallback, useMemo } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import 'katex/dist/katex.min.css';
|
||||
import RemarkMath from 'remark-math'; // Math syntax
|
||||
@@ -6,15 +6,15 @@ import RemarkBreaks from 'remark-breaks'; // Line break
|
||||
import RehypeKatex from 'rehype-katex'; // Math render
|
||||
import RemarkGfm from 'remark-gfm'; // Special markdown syntax
|
||||
import RehypeExternalLinks from 'rehype-external-links';
|
||||
|
||||
import RehypeRaw from 'rehype-raw';
|
||||
import styles from './index.module.scss';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { CodeClassNameEnum, mdTextFormat } from './utils';
|
||||
import ErrorBoundary from './errorBoundry';
|
||||
import SVGRenderer from './markdowSVG';
|
||||
import { useCreation } from 'ahooks';
|
||||
import type { AProps } from './A';
|
||||
|
||||
const CodeLight = dynamic(() => import('./codeBlock/CodeLight'), { ssr: false });
|
||||
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'), { ssr: false });
|
||||
const MdImage = dynamic(() => import('./img/Image'), { ssr: false });
|
||||
@@ -26,7 +26,40 @@ const AudioBlock = dynamic(() => import('./codeBlock/Audio'), { ssr: false });
|
||||
|
||||
const ChatGuide = dynamic(() => import('./chat/Guide'), { ssr: false });
|
||||
const QuestionGuide = dynamic(() => import('./chat/QuestionGuide'), { ssr: false });
|
||||
const A = dynamic(() => import('./A'), { ssr: false });
|
||||
|
||||
function isSafeHref(href: string): boolean {
|
||||
if (!href) return false;
|
||||
// allow http(s), mailto, tel, relative paths, #, data:image/audio/video
|
||||
return /^(https?:|mailto:|tel:|\/|#|data:(?:image|audio|video))/i.test(href.trim());
|
||||
}
|
||||
|
||||
const SafeA = (props: any) => {
|
||||
const href = props.href || '';
|
||||
const safeHref = isSafeHref(href) ? href : '#';
|
||||
|
||||
const ALLOWED_A_ATTRS = new Set([
|
||||
'href',
|
||||
'target',
|
||||
'rel',
|
||||
'className',
|
||||
'children',
|
||||
'style',
|
||||
'title'
|
||||
]);
|
||||
const safeProps = filterSafeProps(props, ALLOWED_A_ATTRS);
|
||||
|
||||
return (
|
||||
<a
|
||||
{...safeProps}
|
||||
href={safeHref}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="cursor-pointer underline !decoration-primary-700 decoration-dashed"
|
||||
>
|
||||
{props.children || 'Download'}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
|
||||
type Props = {
|
||||
source?: string;
|
||||
@@ -36,11 +69,9 @@ type Props = {
|
||||
} & AProps;
|
||||
const Markdown = (props: Props) => {
|
||||
const source = props.source || '';
|
||||
|
||||
if (source.length < 200000) {
|
||||
return <MarkdownRender {...props} />;
|
||||
}
|
||||
|
||||
return <Box whiteSpace={'pre-wrap'}>{source}</Box>;
|
||||
};
|
||||
const MarkdownRender = ({
|
||||
@@ -48,7 +79,6 @@ const MarkdownRender = ({
|
||||
showAnimation,
|
||||
isDisabled,
|
||||
forbidZhFormat,
|
||||
|
||||
chatAuthData,
|
||||
onOpenCiteModal
|
||||
}: Props) => {
|
||||
@@ -57,54 +87,116 @@ const MarkdownRender = ({
|
||||
img: Image,
|
||||
pre: RewritePre,
|
||||
code: Code,
|
||||
svg: SVGRenderer,
|
||||
script: ScriptBlock,
|
||||
a: (props: any) => (
|
||||
<A
|
||||
<SafeA
|
||||
{...props}
|
||||
showAnimation={showAnimation}
|
||||
chatAuthData={chatAuthData}
|
||||
onOpenCiteModal={onOpenCiteModal}
|
||||
showAnimation={showAnimation}
|
||||
/>
|
||||
)
|
||||
};
|
||||
}, [chatAuthData, onOpenCiteModal, showAnimation]);
|
||||
|
||||
const formatSource = useMemo(() => {
|
||||
if (showAnimation || forbidZhFormat) return source;
|
||||
return mdTextFormat(source);
|
||||
}, [forbidZhFormat, showAnimation, source]);
|
||||
|
||||
const urlTransform = useCallback((val: string) => {
|
||||
return val;
|
||||
}, []);
|
||||
const urlTransform = useCallback((val: string) => val, []);
|
||||
|
||||
return (
|
||||
<Box position={'relative'}>
|
||||
<ReactMarkdown
|
||||
className={`markdown ${styles.markdown}
|
||||
${showAnimation ? `${formatSource ? styles.waitingAnimation : styles.animation}` : ''}
|
||||
`}
|
||||
remarkPlugins={[RemarkMath, [RemarkGfm, { singleTilde: false }], RemarkBreaks]}
|
||||
rehypePlugins={[RehypeKatex, [RehypeExternalLinks, { target: '_blank' }]]}
|
||||
components={components}
|
||||
urlTransform={urlTransform}
|
||||
>
|
||||
{formatSource}
|
||||
</ReactMarkdown>
|
||||
<ErrorBoundary>
|
||||
<ReactMarkdown
|
||||
className={`markdown ${styles.markdown} ${
|
||||
showAnimation ? `${formatSource ? styles.waitingAnimation : styles.animation}` : ''
|
||||
}`}
|
||||
remarkPlugins={[RemarkMath, [RemarkGfm, { singleTilde: false }], RemarkBreaks]}
|
||||
rehypePlugins={[
|
||||
RehypeKatex,
|
||||
[RehypeExternalLinks, { target: '_blank', rel: ['noopener', 'noreferrer'] }],
|
||||
RehypeRaw as any,
|
||||
() => {
|
||||
return (tree) => {
|
||||
const iterate = (node: any) => {
|
||||
if (node.type === 'element') {
|
||||
// delete ref
|
||||
if (node.properties?.ref) delete node.properties.ref;
|
||||
|
||||
// handle invalid tag name
|
||||
if (!/^[a-z][a-z0-9]*$/i.test(node.tagName)) {
|
||||
node.type = 'text';
|
||||
node.value = `<${node.tagName}`;
|
||||
}
|
||||
|
||||
// handle properties, filter events
|
||||
if (node.properties) {
|
||||
Object.keys(node.properties).forEach((key) => {
|
||||
const keyLower = key.toLowerCase();
|
||||
// if event property (on开头)
|
||||
if (keyLower.startsWith('on')) {
|
||||
const value = node.properties[key];
|
||||
// if event value is not a function or contains suspicious content, delete the event
|
||||
if (
|
||||
typeof value === 'string' || // delete event handler in string format
|
||||
value === null ||
|
||||
value === undefined ||
|
||||
(typeof value === 'string' &&
|
||||
(value.includes('javascript:') ||
|
||||
value.includes('alert') ||
|
||||
value.includes('eval') ||
|
||||
value.includes('Function') ||
|
||||
/[\(\)\[\]\{\}]/.test(value))) // flag for executable code containing parentheses, etc.
|
||||
) {
|
||||
delete node.properties[key];
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// recursive handle child nodes
|
||||
if (node.children) node.children.forEach(iterate);
|
||||
};
|
||||
tree.children.forEach(iterate);
|
||||
};
|
||||
}
|
||||
]}
|
||||
disallowedElements={[
|
||||
'iframe',
|
||||
'head',
|
||||
'html',
|
||||
'meta',
|
||||
'link',
|
||||
'style',
|
||||
'body',
|
||||
'embed',
|
||||
'object',
|
||||
'param',
|
||||
'applet',
|
||||
'area',
|
||||
'map',
|
||||
'isindex'
|
||||
]}
|
||||
components={components}
|
||||
urlTransform={urlTransform}
|
||||
>
|
||||
{formatSource}
|
||||
</ReactMarkdown>
|
||||
</ErrorBoundary>
|
||||
{isDisabled && <Box position={'absolute'} top={0} right={0} left={0} bottom={0} />}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Markdown);
|
||||
|
||||
/* Custom dom */
|
||||
function Code(e: any) {
|
||||
const { className, codeBlock, children } = e;
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
const codeType = match?.[1]?.toLowerCase();
|
||||
|
||||
const strChildren = String(children);
|
||||
|
||||
const Component = useMemo(() => {
|
||||
if (codeType === CodeClassNameEnum.mermaid) {
|
||||
return <MermaidCodeBlock code={strChildren} />;
|
||||
@@ -134,22 +226,61 @@ function Code(e: any) {
|
||||
if (codeType === CodeClassNameEnum.audio) {
|
||||
return <AudioBlock code={strChildren} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<CodeLight className={className} codeBlock={codeBlock} match={match}>
|
||||
{children}
|
||||
</CodeLight>
|
||||
);
|
||||
}, [codeType, className, codeBlock, match, children, strChildren]);
|
||||
|
||||
return Component;
|
||||
}
|
||||
|
||||
function Image({ src }: { src?: string }) {
|
||||
return <MdImage src={src} />;
|
||||
function sanitizeImageSrc(src?: string): string | undefined {
|
||||
if (!src) return undefined;
|
||||
// remove leading and trailing spaces
|
||||
const trimmed = src.trim();
|
||||
// only allow http/https/data/blob protocols
|
||||
if (/^(https?:|data:|blob:)/i.test(trimmed)) {
|
||||
return trimmed;
|
||||
}
|
||||
// allow relative paths (not starting with javascript: or vbscript:)
|
||||
if (
|
||||
!/^(\w+:)/.test(trimmed) &&
|
||||
!trimmed.startsWith('javascript:') &&
|
||||
!trimmed.startsWith('vbscript:')
|
||||
) {
|
||||
return trimmed;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function RewritePre({ children }: any) {
|
||||
const ALLOWED_IMG_ATTRS = new Set([
|
||||
'alt',
|
||||
'width',
|
||||
'height',
|
||||
'className',
|
||||
'style',
|
||||
'title',
|
||||
'loading',
|
||||
'decoding',
|
||||
'crossOrigin',
|
||||
'referrerPolicy'
|
||||
]);
|
||||
|
||||
function Image({ src, ...rest }: { src?: string; [key: string]: any }) {
|
||||
const safeSrc = sanitizeImageSrc(src);
|
||||
if (!safeSrc) {
|
||||
console.warn(`Blocked unsafe image src: ${src}`);
|
||||
}
|
||||
// only allow whitelist attributes, and remove all on* events
|
||||
const safeProps = filterSafeProps(rest, ALLOWED_IMG_ATTRS);
|
||||
return <MdImage src={safeSrc} {...safeProps} />;
|
||||
}
|
||||
|
||||
function RewritePre({ children, ...rest }: any) {
|
||||
// only allow className, style, etc. safe attributes
|
||||
const ALLOWED_PRE_ATTRS = new Set(['className', 'style']);
|
||||
const safeProps = filterSafeProps(rest, ALLOWED_PRE_ATTRS);
|
||||
const modifiedChildren = React.Children.map(children, (child) => {
|
||||
if (React.isValidElement(child)) {
|
||||
// @ts-ignore
|
||||
@@ -157,6 +288,146 @@ function RewritePre({ children }: any) {
|
||||
}
|
||||
return child;
|
||||
});
|
||||
|
||||
return <>{modifiedChildren}</>;
|
||||
return <pre {...safeProps}>{modifiedChildren}</pre>;
|
||||
}
|
||||
|
||||
/**
|
||||
* general safe attribute filter
|
||||
* @param props input props object
|
||||
* @param allowedAttrs allowed attribute name Set
|
||||
* @param eventTypeCheck whether to check event type (e.g. onClick must be a function)
|
||||
*/
|
||||
export function filterSafeProps(
|
||||
props: Record<string, any>,
|
||||
allowedAttrs: Set<string>,
|
||||
eventTypeCheck: boolean = true
|
||||
) {
|
||||
// dangerous protocols
|
||||
const DANGEROUS_PROTOCOLS =
|
||||
/^(?:\s| | )*(?:javascript|vbscript|data(?!:(?:image|audio|video)))/i;
|
||||
|
||||
// dangerous event properties (including various possible ways)
|
||||
const DANGEROUS_EVENTS =
|
||||
/^(?:\s| | )*(?:on|formaction|data-|\[\[|\{\{|xlink:|href|src|action)/i;
|
||||
|
||||
// complete decode function
|
||||
function fullDecode(input: string): string {
|
||||
if (!input) return '';
|
||||
|
||||
let result = input;
|
||||
let lastResult = '';
|
||||
|
||||
// continue decoding until no more decoding can be done
|
||||
while (result !== lastResult) {
|
||||
lastResult = result;
|
||||
try {
|
||||
// HTML entity decode
|
||||
result = result.replace(/&(#?[\w\d]+);/g, (_, entity) => {
|
||||
try {
|
||||
const txt = document.createElement('textarea');
|
||||
txt.innerHTML = `&${entity};`;
|
||||
return txt.value;
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
});
|
||||
|
||||
// Unicode decode (\u0061 format)
|
||||
result = result.replace(/(?:\\|%5C|%5c)u([0-9a-f]{4})/gi, (_, hex) =>
|
||||
String.fromCharCode(parseInt(hex, 16))
|
||||
);
|
||||
|
||||
// URL encode decode
|
||||
result = result.replace(/%([0-9a-f]{2})/gi, (_, hex) =>
|
||||
String.fromCharCode(parseInt(hex, 16))
|
||||
);
|
||||
|
||||
// octal decode
|
||||
result = result.replace(/\\([0-7]{3})/gi, (_, oct) =>
|
||||
String.fromCharCode(parseInt(oct, 8))
|
||||
);
|
||||
|
||||
// hexadecimal decode (\x61 format)
|
||||
result = result.replace(/(?:\\|%5C|%5c)x([0-9a-f]{2})/gi, (_, hex) =>
|
||||
String.fromCharCode(parseInt(hex, 16))
|
||||
);
|
||||
|
||||
// handle whitespace and comments
|
||||
result = result.replace(/(?:\s|\/\*.*?\*\/|<!--.*?-->)+/g, '');
|
||||
} catch {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result.toLowerCase();
|
||||
}
|
||||
|
||||
// check if it contains dangerous content
|
||||
function containsDangerousContent(value: string): boolean {
|
||||
const decoded = fullDecode(value);
|
||||
|
||||
return (
|
||||
// check dangerous protocol
|
||||
DANGEROUS_PROTOCOLS.test(decoded) ||
|
||||
// check dangerous event
|
||||
DANGEROUS_EVENTS.test(decoded) ||
|
||||
// check inline event
|
||||
/on\w+\s*=/.test(decoded) ||
|
||||
// check javascript: link
|
||||
/javascript\s*:/.test(decoded) ||
|
||||
// check other possible injections
|
||||
/<\w+/i.test(decoded) ||
|
||||
/\(\s*\)/i.test(decoded) ||
|
||||
/\[\s*\]/i.test(decoded) ||
|
||||
/\{\s*\}/i.test(decoded)
|
||||
);
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries(props).filter(([key, value]) => {
|
||||
// 1. decode and check property name
|
||||
const decodedKey = fullDecode(key);
|
||||
|
||||
// 2. intercept all event related properties
|
||||
if (DANGEROUS_EVENTS.test(decodedKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. all properties not in the whitelist are rejected
|
||||
if (!allowedAttrs.has(key)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. check property value
|
||||
if (typeof value === 'string') {
|
||||
if (containsDangerousContent(value)) {
|
||||
return false;
|
||||
}
|
||||
} else if (typeof value === 'object' && value !== null) {
|
||||
// only allow simple style objects
|
||||
if (key !== 'style') {
|
||||
return false;
|
||||
}
|
||||
// check the value of the style object
|
||||
for (const styleKey in value) {
|
||||
if (containsDangerousContent(String(value[styleKey]))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (typeof value === 'function') {
|
||||
// only allow specific function properties (e.g. onClick)
|
||||
if (!eventTypeCheck || decodedKey !== 'onclick') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
const ScriptBlock = memo(({ node }: any) => {
|
||||
const scriptContent = node.children[0]?.value || '';
|
||||
return `<script>${scriptContent}</script>`;
|
||||
});
|
||||
ScriptBlock.displayName = 'ScriptBlock';
|
||||
|
||||
108
projects/app/src/components/Markdown/markdowSVG.tsx
Normal file
108
projects/app/src/components/Markdown/markdowSVG.tsx
Normal file
@@ -0,0 +1,108 @@
|
||||
import React from 'react';
|
||||
import ErrorBoundary from './errorBoundry';
|
||||
import { filterSafeProps } from './index';
|
||||
|
||||
interface SVGProps {
|
||||
children?: React.ReactNode;
|
||||
className?: string;
|
||||
style?: React.CSSProperties;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const SVG_ALLOWED_ATTRS = new Set([
|
||||
'width',
|
||||
'height',
|
||||
'viewBox',
|
||||
'fill',
|
||||
'stroke',
|
||||
'd',
|
||||
'x',
|
||||
'y',
|
||||
'cx',
|
||||
'cy',
|
||||
'r',
|
||||
'className',
|
||||
'style'
|
||||
]);
|
||||
|
||||
const SVGRenderer = ({ children, className, style, ...props }: SVGProps) => {
|
||||
// filter props
|
||||
const svgProps = { ...props, className, style };
|
||||
const sanitizedProps = filterSafeProps(svgProps, SVG_ALLOWED_ATTRS, false);
|
||||
|
||||
const sanitizeSVGContent = (content: string | React.ReactNode): string => {
|
||||
if (typeof content !== 'string') {
|
||||
return '';
|
||||
}
|
||||
|
||||
let cleaned = content;
|
||||
|
||||
cleaned = cleaned.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
|
||||
cleaned = cleaned.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '');
|
||||
cleaned = cleaned.replace(
|
||||
/<foreignObject\b[^<]*(?:(?!<\/foreignObject>)<[^<]*)*<\/foreignObject>/gi,
|
||||
''
|
||||
);
|
||||
|
||||
cleaned = cleaned.replace(/\son\w+="[^"]*"/gi, '');
|
||||
cleaned = cleaned.replace(/\son\w+='[^']*'/gi, '');
|
||||
cleaned = cleaned.replace(/url\s*\(\s*['"]?\s*javascript:[^)]+\)/gi, '');
|
||||
cleaned = cleaned.replace(/\bhref="javascript:[^"]*"/gi, '');
|
||||
cleaned = cleaned.replace(/\bhref='javascript:[^']*'/gi, '');
|
||||
cleaned = cleaned.replace(/\bxlink:href="javascript:[^"]*"/gi, '');
|
||||
cleaned = cleaned.replace(/\bxlink:href='javascript:[^']*'/gi, '');
|
||||
cleaned = cleaned.replace(/\bxmlns(:xlink)?=['"]?javascript:[^"']*['"]?/gi, '');
|
||||
cleaned = cleaned.replace(/style\s*=\s*(['"])(?:(?!\1).)*javascript:.*?\1/gi, '');
|
||||
|
||||
cleaned = cleaned.replace(/\bdata:[^,]*?;base64,[^"')]*["')]/gi, (match) => {
|
||||
return match.toLowerCase().includes('javascript') ? '' : match;
|
||||
});
|
||||
|
||||
const ALLOWED_ATTRS = new Set([
|
||||
'width',
|
||||
'height',
|
||||
'viewBox',
|
||||
'fill',
|
||||
'stroke',
|
||||
'd',
|
||||
'x',
|
||||
'y',
|
||||
'cx',
|
||||
'cy',
|
||||
'r',
|
||||
'class',
|
||||
'style'
|
||||
]);
|
||||
cleaned = cleaned.replace(/\s(\w+)=['"][^'"]*['"]/gi, (match, attr) => {
|
||||
return ALLOWED_ATTRS.has(attr.toLowerCase()) ? match : '';
|
||||
});
|
||||
|
||||
cleaned = cleaned.replace(/<!--[\s\S]*?-->/g, '');
|
||||
|
||||
return cleaned;
|
||||
};
|
||||
|
||||
const sanitizedContent = React.Children.map(children, (child) => {
|
||||
if (typeof child === 'string') {
|
||||
return sanitizeSVGContent(child);
|
||||
}
|
||||
return child;
|
||||
});
|
||||
|
||||
return (
|
||||
<ErrorBoundary fallback={<div>Something went wrong while rendering Markdown.</div>}>
|
||||
<svg
|
||||
{...sanitizedProps}
|
||||
className={className}
|
||||
style={style}
|
||||
dangerouslySetInnerHTML={
|
||||
typeof children === 'string' ? { __html: sanitizeSVGContent(children) } : undefined
|
||||
}
|
||||
>
|
||||
{typeof children !== 'string' && sanitizedContent}
|
||||
</svg>
|
||||
</ErrorBoundary>
|
||||
);
|
||||
};
|
||||
|
||||
export default SVGRenderer;
|
||||
@@ -1,128 +0,0 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { processChatTimeFilter } from '@/service/core/chat/utils';
|
||||
import type { DatasetCiteItemType } from '@fastgpt/global/core/dataset/type';
|
||||
|
||||
describe('processChatTimeFilter', () => {
|
||||
const baseTime = new Date('2025-01-01');
|
||||
|
||||
it('should return original item if no history', () => {
|
||||
const item: DatasetCiteItemType = {
|
||||
id: '1',
|
||||
q: 'original q',
|
||||
a: 'original a',
|
||||
updateTime: baseTime.getTime(),
|
||||
history: undefined
|
||||
};
|
||||
|
||||
const result = processChatTimeFilter([item], baseTime);
|
||||
expect(result).toEqual([item]);
|
||||
});
|
||||
|
||||
it('should return original item if updateTime <= chatTime', () => {
|
||||
const item: DatasetCiteItemType = {
|
||||
id: '1',
|
||||
q: 'original q',
|
||||
a: 'original a',
|
||||
updateTime: baseTime.getTime() - 1000,
|
||||
history: [
|
||||
{
|
||||
q: 'history q',
|
||||
a: 'history a',
|
||||
updateTime: baseTime.getTime() - 2000
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const result = processChatTimeFilter([item], baseTime);
|
||||
expect(result).toEqual([item]);
|
||||
});
|
||||
|
||||
it('should return history item if one exists before chatTime', () => {
|
||||
const item: DatasetCiteItemType = {
|
||||
id: '1',
|
||||
q: 'original q',
|
||||
a: 'original a',
|
||||
updateTime: baseTime.getTime() + 2000,
|
||||
history: [
|
||||
{
|
||||
q: 'history q',
|
||||
a: 'history a',
|
||||
updateTime: baseTime.getTime() - 1000
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const result = processChatTimeFilter([item], baseTime);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
...item,
|
||||
q: 'history q',
|
||||
a: 'history a',
|
||||
updateTime: baseTime.getTime() - 1000,
|
||||
updated: true
|
||||
}
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return original item if no history before chatTime', () => {
|
||||
const item: DatasetCiteItemType = {
|
||||
id: '1',
|
||||
q: 'original q',
|
||||
a: 'original a',
|
||||
updateTime: baseTime.getTime() + 2000,
|
||||
history: [
|
||||
{
|
||||
q: 'history q',
|
||||
a: 'history a',
|
||||
updateTime: baseTime.getTime() + 1000
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const result = processChatTimeFilter([item], baseTime);
|
||||
expect(result).toEqual([item]);
|
||||
});
|
||||
|
||||
it('should handle multiple items', () => {
|
||||
const items: DatasetCiteItemType[] = [
|
||||
{
|
||||
id: '1',
|
||||
q: 'original q1',
|
||||
a: 'original a1',
|
||||
updateTime: baseTime.getTime() + 2000,
|
||||
history: [
|
||||
{
|
||||
q: 'history q1',
|
||||
a: 'history a1',
|
||||
updateTime: baseTime.getTime() - 1000
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
q: 'original q2',
|
||||
a: 'original a2',
|
||||
updateTime: baseTime.getTime() - 1000,
|
||||
history: [
|
||||
{
|
||||
q: 'history q2',
|
||||
a: 'history a2',
|
||||
updateTime: baseTime.getTime() - 2000
|
||||
}
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
const result = processChatTimeFilter(items, baseTime);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
...items[0],
|
||||
q: 'history q1',
|
||||
a: 'history a1',
|
||||
updateTime: baseTime.getTime() - 1000,
|
||||
updated: true
|
||||
},
|
||||
items[1]
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user