Compare commits

..

64 Commits

Author SHA1 Message Date
Archer
3e21030536 hiden dataset source (#4152)
* hiden dataset source

* perf: reader
2025-03-13 21:30:40 +08:00
Finley Ge
7ec4ba7067 fix(i18n): namespace (#4143) 2025-03-13 19:40:54 +08:00
Finley Ge
2d22af3cce pref: copy link (#4147) 2025-03-13 19:40:53 +08:00
Archer
4346c5703a fix: prompt toolcall ui (#4139)
* load log error adapt

* fix: prompt toolcall ui

* perf: commercial function tip

* update package
2025-03-13 19:40:53 +08:00
Archer
f71ab0caeb perf: model test;perf: sidebar trigger (#4127)
* fix: import dataset step error;perf: ai proxy avatar (#4074)

* perf: pg config params

* perf: ai proxy avatar

* fix: import dataset step error

* feat: data input ux

* perf: app dataset rewite

* perf: model test

* perf: sidebar trigger

* lock

* update nanoid version

* fix: select component ux

* fix: ts

* fix: vitest

* remove test
2025-03-13 19:40:52 +08:00
heheer
c131c2a7dc fix chat quote reader (#4125) 2025-03-13 19:40:52 +08:00
Archer
d052d0de53 fix: model test channel id;fix: quote reader (#4123)
* fix: model test channel id

* fix: quote reader
2025-03-13 19:40:51 +08:00
Archer
d1ce3e2936 perf: invite member code (#4118)
* perf: invite member code

* fix: ts
2025-03-13 19:40:51 +08:00
Finley Ge
c301dafca7 feat: invitation link (#3979)
* feat: invitation link schema and apis

* feat: add invitation link

* feat: member status: active, leave, forbidden

* fix: expires show hours and minutes

* feat: invalid invitation link hint

* fix: typo

* chore: fix typo & i18n

* fix

* pref: fe

* feat: add ttl index for 30-day-clean-up
2025-03-13 19:40:50 +08:00
Archer
1a3613cd2c feat: api dataset support pdf parse;fix: chunk reader auth (#4117)
* feat: api dataset support pdf parse

* fix: chunk reader auth
2025-03-13 19:40:50 +08:00
Archer
30f83f848d perf: chunk read (#4109)
* package

* perf: chunk read
2025-03-13 19:40:49 +08:00
heheer
ac7091f8d6 chat quote reader (#3912)
* init chat quote full text reader

* linked structure

* dataset data linked

* optimize code

* fix ts build

* test finish

* delete log

* fix

* fix ts

* fix ts

* remove nextId

* initial scroll

* fix

* fix
2025-03-13 19:40:49 +08:00
Archer
16832caaf6 perf: think tag parse (#4102) 2025-03-13 19:40:49 +08:00
Archer
a3df9ea531 update doc ;perf: model test (#4098)
* perf: extract array

* update doc

* perf: model test

* perf: model test
2025-03-13 19:40:48 +08:00
shilin
bcd0b010a6 fix: 文本提取不支持arrayString,arrayNumber等jsonSchema (#4079) 2025-03-13 19:40:48 +08:00
Archer
3f794baf2e fix: import dataset step error;perf: ai proxy avatar (#4074)
* perf: pg config params

* perf: ai proxy avatar

* fix: import dataset step error

* feat: data input ux

* perf: app dataset rewite
2025-03-13 19:40:48 +08:00
archer
92b2ecc381 update package 2025-03-13 19:40:47 +08:00
gggaaallleee
4dbe41db0e fix :Get application bound knowledge base information logical rewrite (#4057)
* fix :Get application bound knowledge base information logical rewrite

* fix :Get application bound knowledge base information logical rewrite

* fix :Get application bound knowledge base information logical rewrite

* fix :Get application bound knowledge base information logical rewrite
2025-03-13 19:40:47 +08:00
Finley Ge
f9dd170895 fix: remove DefaultTeam (#4037) 2025-03-13 19:40:44 +08:00
Archer
a7b09461be Update official_account.md (#4132) 2025-03-12 21:26:17 +08:00
dependabot[bot]
cd2cb3f6ea chore(deps): bump gradio in /plugins/model/tts-cosevoice (#4130)
Bumps [gradio](https://github.com/gradio-app/gradio) from 4.32.2 to 5.11.0.
- [Release notes](https://github.com/gradio-app/gradio/releases)
- [Changelog](https://github.com/gradio-app/gradio/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gradio-app/gradio/compare/gradio@4.32.2...gradio@5.11.0)

---
updated-dependencies:
- dependency-name: gradio
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 19:33:10 +08:00
dependabot[bot]
56f77b58c9 chore(deps): bump lightning in /plugins/model/tts-cosevoice (#4129)
Bumps [lightning](https://github.com/Lightning-AI/lightning) from 2.2.4 to 2.3.3.
- [Release notes](https://github.com/Lightning-AI/lightning/releases)
- [Commits](https://github.com/Lightning-AI/lightning/compare/2.2.4...2.3.3)

---
updated-dependencies:
- dependency-name: lightning
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 19:33:02 +08:00
dependabot[bot]
1d697f97d7 chore(deps): bump deepspeed in /plugins/model/tts-cosevoice (#4128)
Bumps [deepspeed](https://github.com/deepspeedai/DeepSpeed) from 0.14.2 to 0.15.1.
- [Release notes](https://github.com/deepspeedai/DeepSpeed/releases)
- [Commits](https://github.com/deepspeedai/DeepSpeed/compare/v0.14.2...v0.15.1)

---
updated-dependencies:
- dependency-name: deepspeed
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-12 19:32:46 +08:00
Finley Ge
bb30ca4859 chore: vitest support (#4026)
* chore: vitest

* chore: move test files

* chore: support vitest

* fix: exclude test files

* chore(ci): add test workflow

* feat: remove read env
2025-03-12 19:27:53 +08:00
zijiren
139e934345 feat: aiproxy repo moved (#4116)
* feat: aiproxy repo

* fix
2025-03-12 12:41:04 +08:00
dependabot[bot]
bf69aa6e3d chore(deps): bump next from 14.2.5 to 14.2.21 (#4091)
Bumps [next](https://github.com/vercel/next.js) from 14.2.5 to 14.2.21.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.5...v14.2.21)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-11 12:27:10 +08:00
dependabot[bot]
3a730a23cb chore(deps): bump next from 14.2.5 to 14.2.21 in /projects/app (#4087)
Bumps [next](https://github.com/vercel/next.js) from 14.2.5 to 14.2.21.
- [Release notes](https://github.com/vercel/next.js/releases)
- [Changelog](https://github.com/vercel/next.js/blob/canary/release.js)
- [Commits](https://github.com/vercel/next.js/compare/v14.2.5...v14.2.21)

---
updated-dependencies:
- dependency-name: next
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-11 11:39:44 +08:00
dependabot[bot]
75cb46796a chore(deps): bump axios from 1.7.2 to 1.8.2 (#4088)
Bumps [axios](https://github.com/axios/axios) from 1.7.2 to 1.8.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.7.2...v1.8.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-11 11:38:19 +08:00
dependabot[bot]
effdb5884b chore(deps): bump jsonpath-plus from 10.1.0 to 10.3.0 (#4089)
Bumps [jsonpath-plus](https://github.com/s3u/JSONPath) from 10.1.0 to 10.3.0.
- [Release notes](https://github.com/s3u/JSONPath/releases)
- [Changelog](https://github.com/JSONPath-Plus/JSONPath/blob/main/CHANGES.md)
- [Commits](https://github.com/s3u/JSONPath/compare/v10.1.0...v10.3.0)

---
updated-dependencies:
- dependency-name: jsonpath-plus
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-11 11:37:58 +08:00
Archer
9523ba92f3 perf: HTTP node jsonBody support \n (#4070) 2025-03-10 13:56:34 +08:00
archer
2f522aff90 perf: HTTP node jsonBody support \n 2025-03-10 13:55:51 +08:00
Wenhao Zhu
0dccfd176d fix: 修复最大响应 tokens 提示显示错误的问题 (#4048) 2025-03-10 10:01:25 +08:00
Wenhao Zhu
867e8acf27 fix: 修复部分参数丢失的问题 (#4040)
部分参数 (Reasoning, TopP, StopSign, ResponseFormat, JsonSchema) 保存完后重新进入不显示,并且在某些情况(包括但不限于保存应用操作)会导致用空值覆盖数据库的值从而丢失这个参数值
2025-03-08 18:27:58 +08:00
Wenhao Zhu
36da8c862f fix: 修复知识库问题优化无效的BUG (#4033) 2025-03-07 16:00:01 +08:00
Archer
b50cf49cc7 更新 4820.md (#4028) 2025-03-07 13:36:02 +08:00
Archer
2270e149eb Update 490.md (#4022) 2025-03-07 11:15:52 +08:00
Archer
4957bdcba1 Update official_account.md (#4021) 2025-03-07 11:12:08 +08:00
ENg-122
bca5cf738a 翻译缺失 (#4010) 2025-03-06 22:33:09 +08:00
Archer
c35bb5841c version doc (#4013) 2025-03-06 22:31:56 +08:00
Archer
6e045093b1 qwq config (#4009) 2025-03-06 20:21:57 +08:00
Archer
a1b114e426 feat: qwen qwq model config (#4008) 2025-03-06 20:18:33 +08:00
Archer
54fde7630c V4.9.0 feature (#4006)
* Add image index and pdf parse (#3956)

* feat: think tag parse

* feat: parse think tag test

* feat: pdf parse ux

* feat: doc2x parse

* perf: rewrite training mode setting

* feat: image parse queue

* perf: image index

* feat: image parse process

* feat: add init sh

* fix: ts

* feat: pg vector 0.8.0;perf: app pdf enhance parse (#3962)

* perf: app pdf enhance parse

* feat: pg vector 0.8.0

* update schema default

* model sort and default image

* perf: i18n

* perf: ui tip

* Add markdown format; Update doc (#3969)

* update doc

* markdown

* ai proxy docker compose & doc (#3947)

* feat: prompt call tool support reason;perf: ai proxy doc (#3982)

* update schema

* perf: ai proxy doc

* feat: prompt call tool support reason

* doc

* update init sh

* simple mode tool reason (#3984)

* simple mode tool reason

* model config cannot set empty

* perf: read files code

* perf: mongo gridfs chunks

* perf: doc

* fix: link (#3987)

* pdf parse doc (#3990)

* update action

* doc

* markerv0.2 (#3992)

* markerv0.2

* marker2

* perf: retry to load image;perf: default index check (#4004)

* perf: retry to load image

* perf: default index check

* perf: doc (#4005)

* perf: doc

* doc

* version doc

* README

---------

Co-authored-by: heheer <heheer@sealos.io>
Co-authored-by: YeYuheng <57035043+YYH211@users.noreply.github.com>
2025-03-06 19:42:58 +08:00
archer
467c408ad7 README 2025-03-06 18:39:51 +08:00
archer
c005a94454 version doc 2025-03-06 18:39:00 +08:00
Archer
c8a35822d6 perf: doc (#4005)
* perf: doc

* doc
2025-03-06 18:35:02 +08:00
Archer
d05259dedd perf: retry to load image;perf: default index check (#4004)
* perf: retry to load image

* perf: default index check
2025-03-06 18:28:09 +08:00
YeYuheng
8980664b8a markerv0.2 (#3992)
* markerv0.2

* marker2
2025-03-06 18:28:09 +08:00
archer
43f30b3790 doc 2025-03-06 18:28:09 +08:00
archer
3ddbb37612 update action 2025-03-06 18:28:08 +08:00
Archer
7c419a26b3 pdf parse doc (#3990) 2025-03-06 18:28:08 +08:00
Archer
e131465d25 fix: link (#3987) 2025-03-06 18:28:08 +08:00
Archer
a345e56508 simple mode tool reason (#3984)
* simple mode tool reason

* model config cannot set empty

* perf: read files code

* perf: mongo gridfs chunks

* perf: doc
2025-03-06 18:28:07 +08:00
archer
32ce032995 update init sh 2025-03-06 18:28:07 +08:00
archer
0bc075aa4e doc 2025-03-06 18:28:06 +08:00
Archer
3e3f2165db feat: prompt call tool support reason;perf: ai proxy doc (#3982)
* update schema

* perf: ai proxy doc

* feat: prompt call tool support reason
2025-03-06 18:28:05 +08:00
heheer
e1aa068858 ai proxy docker compose & doc (#3947) 2025-03-06 18:28:05 +08:00
Archer
e98d6f1d30 Add markdown format; Update doc (#3969)
* update doc

* markdown
2025-03-06 18:28:04 +08:00
Archer
54eb5c0547 feat: pg vector 0.8.0;perf: app pdf enhance parse (#3962)
* perf: app pdf enhance parse

* feat: pg vector 0.8.0

* update schema default

* model sort and default image

* perf: i18n

* perf: ui tip
2025-03-06 18:28:04 +08:00
Archer
adf5377ebe Add image index and pdf parse (#3956)
* feat: think tag parse

* feat: parse think tag test

* feat: pdf parse ux

* feat: doc2x parse

* perf: rewrite training mode setting

* feat: image parse queue

* perf: image index

* feat: image parse process

* feat: add init sh

* fix: ts
2025-03-06 18:28:03 +08:00
Finley Ge
08b6f594df pref: set strictQuery to throw. (#3994) 2025-03-06 18:24:58 +08:00
Fmaverick
90d13ee3df Update README.md (#3999)
加了锚点
2025-03-06 18:24:39 +08:00
Archer
5c718abd50 Merge pull request #4003 from labring/v4.8.23
perf: http body check
2025-03-06 18:24:06 +08:00
archer
2d351c3654 perf: http body check 2025-03-06 18:22:42 +08:00
Finley Ge
662a4a4671 fix: remove defaultTeam (#3989) 2025-03-06 00:26:32 +08:00
273 changed files with 7370 additions and 3274 deletions

View File

@@ -6,8 +6,6 @@ on:
- 'docSite/**'
branches:
- 'main'
tags:
- 'v*.*.*'
jobs:
build-fastgpt-docs-images:

View File

@@ -7,8 +7,6 @@ on:
- 'docSite/**'
branches:
- 'main'
tags:
- 'v*.*.*'
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:

View File

@@ -4,8 +4,6 @@ on:
pull_request_target:
paths:
- 'docSite/**'
branches:
- 'main'
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel

29
.github/workflows/fastgpt-test.yaml vendored Normal file
View File

@@ -0,0 +1,29 @@
name: 'FastGPT-Test'
on:
pull_request:
workflow_dispatch:
jobs:
test:
runs-on: ubuntu-latest
permissions:
# Required to checkout the code
contents: read
# Required to put a comment into the pull-request
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- name: 'Install Deps'
run: pnpm install
- name: 'Test'
run: pnpm run test
- name: 'Report Coverage'
# Set if: always() to also generate the report if tests are failing
# Only works if you set `reportOnFailure: true` in your vite config as specified above
if: always()
uses: davelosert/vitest-coverage-report-action@v2

1
.gitignore vendored
View File

@@ -44,3 +44,4 @@ files/helm/fastgpt/fastgpt-0.1.0.tgz
files/helm/fastgpt/charts/*.tgz
tmp/
coverage

View File

@@ -5,4 +5,5 @@ node_modules
docSite/
*.md
pnpm-lock.yaml
cl100l_base.ts

View File

@@ -130,6 +130,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
## 🌿 第三方生态
- [AI Proxy国内模型聚合服务](https://sealos.run/aiproxy/?k=fastgpt-github/)
- [SiliconCloud (硅基流动) —— 开源模型在线体验平台](https://cloud.siliconflow.cn/i/TR9Ym0c4)
- [COW 个人微信/企微机器人](https://doc.tryfastgpt.ai/docs/use-cases/external-integration/onwechat/)

View File

@@ -69,7 +69,7 @@ Project tech stack: NextJs + TS + ChakraUI + MongoDB + PostgreSQL (PG Vector plu
> When using [Sealos](https://sealos.io) services, there is no need to purchase servers or domain names. It supports high concurrency and dynamic scaling, and the database application uses the kubeblocks database, which far exceeds the simple Docker container deployment in terms of IO performance.
<div align="center">
[![](https://cdn.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
[![](https://cdn.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt&uid=fnWRt09fZP)
</div>
Give it a 2-4 minute wait after deployment as it sets up the database. Initially, it might be a too slow since we're using the basic settings.

View File

@@ -94,7 +94,7 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
- **⚡ デプロイ**
[![](https://cdn.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
[![](https://cdn.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg)](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt&uid=fnWRt09fZP)
デプロイ 後、データベースをセットアップするので、24分待 ってください。基本設定 を 使 っているので、最初 は 少 し 遅 いかもしれません。

View File

@@ -100,7 +100,7 @@ services:
exec docker-entrypoint.sh "$$@" &
# 等待MongoDB服务启动
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')" > /dev/null 2>&1; do
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
echo "Waiting for MongoDB to start..."
sleep 2
done
@@ -114,15 +114,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.0 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
image: ghcr.io/labring/fastgpt:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.0 # 阿里云
ports:
- 3000:3000
networks:
@@ -175,14 +175,12 @@ services:
# AI Proxy
aiproxy:
image: 'ghcr.io/labring/sealos-aiproxy-service:latest'
image: 'ghcr.io/labring/aiproxy:latest'
container_name: aiproxy
restart: unless-stopped
depends_on:
aiproxy_pg:
condition: service_healthy
ports:
- '3002:3000'
networks:
- fastgpt
environment:
@@ -193,7 +191,7 @@ services:
# 数据库连接地址
- SQL_DSN=postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
# 最大重试次数
- RetryTimes=3
- RETRY_TIMES=3
# 不需要计费
- BILLING_ENABLED=false
# 不需要严格检测模型
@@ -204,8 +202,8 @@ services:
timeout: 5s
retries: 10
aiproxy_pg:
# image: pgvector/pgvector:0.8.0-pg15 # docker hub
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
image: pgvector/pgvector:0.8.0-pg15 # docker hub
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
restart: unless-stopped
container_name: aiproxy_pg
volumes:

View File

@@ -28,8 +28,8 @@ services:
# image: mongo:4.4.29 # cpu不支持AVX时候使用
container_name: mongo
restart: always
ports:
- 27017:27017
# ports:
# - 27017:27017
networks:
- fastgpt
command: mongod --keyFile /data/mongodb.key --replSet rs0
@@ -58,7 +58,7 @@ services:
exec docker-entrypoint.sh "$$@" &
# 等待MongoDB服务启动
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')" > /dev/null 2>&1; do
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
echo "Waiting for MongoDB to start..."
sleep 2
done
@@ -72,15 +72,15 @@ services:
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.0 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
image: ghcr.io/labring/fastgpt:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.0 # 阿里云
ports:
- 3000:3000
networks:
@@ -132,14 +132,12 @@ services:
# AI Proxy
aiproxy:
image: 'ghcr.io/labring/sealos-aiproxy-service:latest'
image: 'ghcr.io/labring/aiproxy:latest'
container_name: aiproxy
restart: unless-stopped
depends_on:
aiproxy_pg:
condition: service_healthy
ports:
- '3002:3000'
networks:
- fastgpt
environment:
@@ -150,7 +148,7 @@ services:
# 数据库连接地址
- SQL_DSN=postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
# 最大重试次数
- RetryTimes=3
- RETRY_TIMES=3
# 不需要计费
- BILLING_ENABLED=false
# 不需要严格检测模型
@@ -161,8 +159,8 @@ services:
timeout: 5s
retries: 10
aiproxy_pg:
# image: pgvector/pgvector:0.8.0-pg15 # docker hub
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
image: pgvector/pgvector:0.8.0-pg15 # docker hub
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
restart: unless-stopped
container_name: aiproxy_pg
volumes:

View File

@@ -41,7 +41,7 @@ services:
exec docker-entrypoint.sh "$$@" &
# 等待MongoDB服务启动
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')" > /dev/null 2>&1; do
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
echo "Waiting for MongoDB to start..."
sleep 2
done
@@ -53,15 +53,15 @@ services:
wait $$!
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.8.23-fix # 阿里云
image: ghcr.io/labring/fastgpt-sandbox:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.0 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.8.23-fix # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.8.23-fix # 阿里云
image: ghcr.io/labring/fastgpt:v4.9.0 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.0 # 阿里云
ports:
- 3000:3000
networks:
@@ -113,14 +113,12 @@ services:
# AI Proxy
aiproxy:
image: 'ghcr.io/labring/sealos-aiproxy-service:latest'
image: 'ghcr.io/labring/aiproxy:latest'
container_name: aiproxy
restart: unless-stopped
depends_on:
aiproxy_pg:
condition: service_healthy
ports:
- '3002:3000'
networks:
- fastgpt
environment:
@@ -131,7 +129,7 @@ services:
# 数据库连接地址
- SQL_DSN=postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
# 最大重试次数
- RetryTimes=3
- RETRY_TIMES=3
# 不需要计费
- BILLING_ENABLED=false
# 不需要严格检测模型
@@ -142,8 +140,8 @@ services:
timeout: 5s
retries: 10
aiproxy_pg:
# image: pgvector/pgvector:0.8.0-pg15 # docker hub
image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
image: pgvector/pgvector:0.8.0-pg15 # docker hub
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
restart: unless-stopped
container_name: aiproxy_pg
volumes:

View File

@@ -44,7 +44,7 @@ weight: 707
#### 1. 申请 Sealos AI proxy API Key
[点击打开 Sealos Pdf parser 官网](https://cloud.sealos.run/?uid=fnWRt09fZP&openapp=system-aiproxy),并进行对应 API Key 的申请。
[点击打开 Sealos Pdf parser 官网](https://hzh.sealos.run/?uid=fnWRt09fZP&openapp=system-aiproxy),并进行对应 API Key 的申请。
#### 2. 修改 FastGPT 配置文件

View File

@@ -24,10 +24,9 @@ PDF 是一个相对复杂的文件格式,在 FastGPT 内置的 pdf 解析器
这里介绍快速 Docker 安装的方法:
```dockerfile
docker pull crpi-h3snc261q1dosroc.cn-hangzhou.personal.cr.aliyuncs.com/marker11/marker_images:latest
docker run --gpus all -itd -p 7231:7231 --name model_pdf_v1 crpi-h3snc261q1dosroc.cn-hangzhou.personal.cr.aliyuncs.com/marker11/marker_images:latest
docker pull crpi-h3snc261q1dosroc.cn-hangzhou.personal.cr.aliyuncs.com/marker11/marker_images:v0.2
docker run --gpus all -itd -p 7231:7232 --name model_pdf_v2 -e PROCESSES_PER_GPU="2" crpi-h3snc261q1dosroc.cn-hangzhou.personal.cr.aliyuncs.com/marker11/marker_images:v0.2
```
### 2. 添加 FastGPT 文件配置
```json
@@ -36,7 +35,7 @@ docker run --gpus all -itd -p 7231:7231 --name model_pdf_v1 crpi-h3snc261q1dosro
"systemEnv": {
xxx
"customPdfParse": {
"url": "http://xxxx.com/v1/parse/file", // 自定义 PDF 解析服务地址
"url": "http://xxxx.com/v2/parse/file", // 自定义 PDF 解析服务地址 marker v0.2
"key": "", // 自定义 PDF 解析服务密钥
"doc2xKey": "", // doc2x 服务密钥
"price": 0 // PDF 解析服务价格
@@ -80,4 +79,25 @@ docker run --gpus all -itd -p 7231:7231 --name model_pdf_v1 crpi-h3snc261q1dosro
上图是分块后的结果,下图是 pdf 原文。整体图片、公式、表格都可以提取出来,效果还是杠杠的。
不过要注意的是,[Marker](https://github.com/VikParuchuri/marker) 的协议是`GPL-3.0 license`,请在遵守协议的前提下使用。
不过要注意的是,[Marker](https://github.com/VikParuchuri/marker) 的协议是`GPL-3.0 license`,请在遵守协议的前提下使用。
## 旧版 Marker 使用方法
FastGPT V4.9.0 版本之前,可以用以下方式,试用 Marker 解析服务。
安装和运行 Marker 服务:
```dockerfile
docker pull crpi-h3snc261q1dosroc.cn-hangzhou.personal.cr.aliyuncs.com/marker11/marker_images:v0.1
docker run --gpus all -itd -p 7231:7231 --name model_pdf_v1 -e PROCESSES_PER_GPU="2" crpi-h3snc261q1dosroc.cn-hangzhou.personal.cr.aliyuncs.com/marker11/marker_images:v0.1
```
并修改 FastGPT 环境变量:
```
CUSTOM_READ_FILE_URL=http://xxxx.com/v1/parse/file
CUSTOM_READ_FILE_EXTENSION=pdf
```
* CUSTOM_READ_FILE_URL - 自定义解析服务的地址, host改成解析服务的访问地址path 不能变动。
* CUSTOM_READ_FILE_EXTENSION - 支持的文件后缀,多个文件类型,可用逗号隔开。

View File

@@ -29,7 +29,7 @@ weight: 744
{{% alert icon=" " context="info" %}}
- [SiliconCloud(硅基流动)](https://cloud.siliconflow.cn/i/TR9Ym0c4): 提供开源模型调用的平台。
- [Sealos AIProxy](https://cloud.sealos.run/?uid=fnWRt09fZP&openapp=system-aiproxy): 提供国内各家模型代理,无需逐一申请 api。
- [Sealos AIProxy](https://hzh.sealos.run/?uid=fnWRt09fZP&openapp=system-aiproxy): 提供国内各家模型代理,无需逐一申请 api。
{{% /alert %}}
在 OneAPI 配置好模型后,你就可以打开 FastGPT 页面,启用对应模型了。

View File

@@ -23,7 +23,7 @@ FastGPT 目前采用模型分离的部署方案FastGPT 中只兼容 OpenAI
### Sealos 版本
* 北京区: [点击部署 OneAPI](https://hzh.sealos.run/?openapp=system-template%3FtemplateName%3Done-api)
* 新加坡区(可用 GPT) [点击部署 OneAPI](https://cloud.sealos.io/?openapp=system-template%3FtemplateName%3Done-api)
* 新加坡区(可用 GPT) [点击部署 OneAPI](https://cloud.sealos.io/?openapp=system-template%3FtemplateName%3Done-api&uid=fnWRt09fZP)
![alt text](/imgs/image-59.png)

View File

@@ -1063,10 +1063,12 @@ curl --location --request DELETE 'http://localhost:3000/api/core/dataset/collect
| 字段 | 类型 | 说明 | 必填 |
| --- | --- | --- | --- |
| defaultIndex | Boolean | 是否为默认索引 | |
| dataId | String | 关联的向量ID | |
| type | String | 可选索引类型default-默认索引; custom-自定义索引; summary-总结索引; question-问题索引; image-图片索引 | |
| dataId | String | 关联的向量ID,变更数据时候传入该 ID会进行差量更新而不是全量更新 | |
| text | String | 文本内容 | ✅ |
`type` 不填则默认为 `custom` 索引,还会基于 q/a 组成一个默认索引。如果传入了默认索引,则不会额外创建。
### 为集合批量添加添加数据
注意,每次最多推送 200 组数据。
@@ -1298,8 +1300,7 @@ curl --location --request GET 'http://localhost:3000/api/core/dataset/data/detai
"chunkIndex": 0,
"indexes": [
{
"defaultIndex": true,
"type": "chunk",
"type": "default",
"dataId": "3720083",
"text": "N o . 2 0 2 2 1 2中 国 信 息 通 信 研 究 院京东探索研究院2022年 9月人工智能生成内容AIGC白皮书(2022 年)版权声明本白皮书版权属于中国信息通信研究院和京东探索研究院,并受法律保护。转载、摘编或利用其它方式使用本白皮书文字或者观点的,应注明“来源:中国信息通信研究院和京东探索研究院”。违反上述声明者,编者将追究其相关法律责任。前 言习近平总书记曾指出“数字技术正以新理念、新业态、新模式全面融入人类经济、政治、文化、社会、生态文明建设各领域和全过程”。在当前数字世界和物理世界加速融合的大背景下人工智能生成内容Artificial Intelligence Generated Content简称 AIGC正在悄然引导着一场深刻的变革重塑甚至颠覆数字内容的生产方式和消费模式将极大地丰富人们的数字生活是未来全面迈向数字文明新时代不可或缺的支撑力量。",
"_id": "65abd4b29d1448617cba61dc"
@@ -1335,12 +1336,18 @@ curl --location --request PUT 'http://localhost:3000/api/core/dataset/data/updat
"a":"sss",
"indexes":[
{
"dataId": "xxx",
"defaultIndex":false,
"text":"自定义索引1"
"dataId": "xxxx",
"type": "default",
"text": "默认索引"
},
{
"text":"修改后的自定义索引2。会删除原来的自定义索引2并插入新的自定义索引2"
"dataId": "xxx",
"type": "custom",
"text": "旧的自定义索引1"
},
{
"type":"custom",
"text":"新增的自定义索引"
}
]
}'

View File

@@ -9,7 +9,7 @@ weight: 951
## 登录 Sealos
[Sealos](https://cloud.sealos.io/)
[Sealos](https://cloud.sealos.io?uid=fnWRt09fZP)
## 创建应用

View File

@@ -26,13 +26,13 @@ FastGPT 使用了 one-api 项目来管理模型池,其可以兼容 OpenAI 、A
新加披区的服务器在国外,可以直接访问 OpenAI但国内用户需要梯子才可以正常访问新加坡区。国际区价格稍贵点击下面按键即可部署👇
<a href="https://template.cloud.sealos.io/deploy?templateName=fastgpt" rel="external" target="_blank"><img src="https://cdn.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg" alt="Deploy on Sealos"/></a>
<a href="https://template.cloud.sealos.io/deploy?templateName=fastgpt&uid=fnWRt09fZP" rel="external" target="_blank"><img src="https://cdn.jsdelivr.net/gh/labring-actions/templates@main/Deploy-on-Sealos.svg" alt="Deploy on Sealos"/></a>
### 北京区
北京区服务提供商为火山云,国内用户可以稳定访问,但无法访问 OpenAI 等境外服务,价格约为新加坡区的 1/4。点击下面按键即可部署👇
<a href="https://bja.sealos.run/?openapp=system-template%3FtemplateName%3Dfastgpt" rel="external" target="_blank"><img src="https://raw.githubusercontent.com/labring-actions/templates/main/Deploy-on-Sealos.svg" alt="Deploy on Sealos"/></a>
<a href="https://bja.sealos.run/?openapp=system-template%3FtemplateName%3Dfastgpt&uid=fnWRt09fZP" rel="external" target="_blank"><img src="https://raw.githubusercontent.com/labring-actions/templates/main/Deploy-on-Sealos.svg" alt="Deploy on Sealos"/></a>
### 1. 开始部署

View File

@@ -13,7 +13,7 @@ FastGPT V4.5 引入 PgVector0.5 版本的 HNSW 索引,极大的提高了知识
## PgVector升级Sealos 部署方案
1. 点击[Sealos桌面](https://cloud.sealos.io)的数据库应用。
1. 点击[Sealos桌面](https://cloud.sealos.io?uid=fnWRt09fZP)的数据库应用。
2. 点击【pg】数据库的详情。
3. 点击右上角的重启,等待重启完成。
4. 点击左侧的一键链接,等待打开 Terminal。

View File

@@ -35,7 +35,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv4820' \
## 完整更新内容
1. 新增 - 可视化模型参数配置,取代原配置文件配置模型。预设超过 100 个模型配置。同时支持所有类型模型的一键测试。(预计下个版本会完全支持在页面上配置渠道)。
1. 新增 - 可视化模型参数配置,取代原配置文件配置模型。预设超过 100 个模型配置。同时支持所有类型模型的一键测试。(预计下个版本会完全支持在页面上配置渠道)。[点击查看模型配置方案](/docs/development/modelconfig/intro/)
2. 新增 - DeepSeek resoner 模型支持输出思考过程。
3. 新增 - 使用记录导出和仪表盘。
4. 新增 - markdown 语法扩展,支持音视频(代码块 audio 和 video

View File

@@ -4,7 +4,7 @@ description: 'FastGPT V4.8.23 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 802
weight: 801
---
## 更新指南

View File

@@ -1,10 +1,10 @@
---
title: 'V4.9.0(进行中)'
title: 'V4.9.0(包含升级脚本)'
description: 'FastGPT V4.9.0 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 801
weight: 800
---
@@ -12,9 +12,141 @@ weight: 801
### 1. 做好数据库备份
### 2. 更新镜像
### 2. 更新镜像和 PG 容器
### 3. 运行升级脚本
- 更新 FastGPT 镜像 tag: v4.9.0
- 更新 FastGPT 商业版镜像 tag: v4.9.0
- Sandbox 镜像,可以不更新
- 更新 PG 容器为 v0.8.0-pg15, 可以查看[最新的 yml](https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-pgvector.yml)
### 3. 替换 OneAPI可选
如果需要使用 [AI Proxy](https://github.com/labring/aiproxy) 替换 OneAPI 的用户可执行该步骤。
#### 1. 修改 yml 文件
参考[最新的 yml](https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-pgvector.yml) 文件。里面已移除 OneAPI 并添加了 AIProxy配置。包含一个服务和一个 PgSQL 数据库。将 `aiproxy` 的配置`追加`到 OneAPI 的配置后面(先不要删除 OneAPI有一个初始化会自动同步 OneAPI 的配置)
{{% details title="AI Proxy Yml 配置" closed="true" %}}
```
# AI Proxy
aiproxy:
image: 'ghcr.io/labring/aiproxy:latest'
container_name: aiproxy
restart: unless-stopped
depends_on:
aiproxy_pg:
condition: service_healthy
networks:
- fastgpt
environment:
# 对应 fastgpt 里的AIPROXY_API_TOKEN
- ADMIN_KEY=aiproxy
# 错误日志详情保存时间(小时)
- LOG_DETAIL_STORAGE_HOURS=1
# 数据库连接地址
- SQL_DSN=postgres://postgres:aiproxy@aiproxy_pg:5432/aiproxy
# 最大重试次数
- RETRY_TIMES=3
# 不需要计费
- BILLING_ENABLED=false
# 不需要严格检测模型
- DISABLE_MODEL_CONFIG=true
healthcheck:
test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/status']
interval: 5s
timeout: 5s
retries: 10
aiproxy_pg:
image: pgvector/pgvector:0.8.0-pg15 # docker hub
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/pgvector:v0.8.0-pg15 # 阿里云
restart: unless-stopped
container_name: aiproxy_pg
volumes:
- ./aiproxy_pg:/var/lib/postgresql/data
networks:
- fastgpt
environment:
TZ: Asia/Shanghai
POSTGRES_USER: postgres
POSTGRES_DB: aiproxy
POSTGRES_PASSWORD: aiproxy
healthcheck:
test: ['CMD', 'pg_isready', '-U', 'postgres', '-d', 'aiproxy']
interval: 5s
timeout: 5s
retries: 10
```
{{% /details %}}
#### 2. 增加 FastGPT 环境变量:
修改 yml 文件中fastgpt 容器的环境变量:
```
# AI Proxy 的地址,如果配了该地址,优先使用
- AIPROXY_API_ENDPOINT=http://aiproxy:3000
# AI Proxy 的 Admin Token与 AI Proxy 中的环境变量 ADMIN_KEY
- AIPROXY_API_TOKEN=aiproxy
```
#### 3. 重载服务
`docker-compose down` 停止服务,然后 `docker-compose up -d` 启动服务,此时会追加 `aiproxy` 服务,并修改 FastGPT 的配置。
#### 4. 执行OneAPI迁移AI proxy脚本
- 可联网方案:
```bash
# 进入 aiproxy 容器
docker exec -it aiproxy sh
# 安装 curl
apk add curl
# 执行脚本
curl --location --request POST 'http://localhost:3000/api/channels/import/oneapi' \
--header 'Authorization: Bearer aiproxy' \
--header 'Content-Type: application/json' \
--data-raw '{
"dsn": "mysql://root:oneapimmysql@tcp(mysql:3306)/oneapi"
}'
# 返回 {"data":[],"success":true} 代表成功
```
- 无法联网时,可打开`aiproxy`的外网暴露端口,然后在本地执行脚本。
aiProxy 暴露端口3003:3000修改后重新 `docker-compose up -d` 启动服务。
```bash
# 在终端执行脚本
curl --location --request POST 'http://localhost:3003/api/channels/import/oneapi' \
--header 'Authorization: Bearer aiproxy' \
--header 'Content-Type: application/json' \
--data-raw '{
"dsn": "mysql://root:oneapimmysql@tcp(mysql:3306)/oneapi"
}'
# 返回 {"data":[],"success":true} 代表成功
```
- 如果不熟悉 docker 操作,建议不要走脚本迁移,直接删除 OneAPI 所有内容,然后手动重新添加渠道。
#### 5. 进入 FastGPT 检查`AI Proxy` 服务是否正常启动。
登录 root 账号后,在`账号-模型提供商`页面,可以看到多出了`模型渠道``调用日志`两个选项,打开模型渠道,可以看到之前 OneAPI 的渠道,说明迁移完成,此时可以手动再检查下渠道是否正常。
#### 6. 删除 OneAPI 服务
```bash
# 停止服务,或者针对性停止 OneAPI 和其 Mysql
docker-compose down
# yml 文件中删除 OneAPI 和其 Mysql 依赖
# 重启服务
docker-compose up -d
```
### 4. 运行 FastGPT 升级脚本
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`{{host}} 替换成**FastGPT 域名**。
@@ -28,7 +160,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv490' \
1. 升级 PG Vector 插件版本
2. 全量更新知识库集合字段。
3. 全量更新知识库数据中index 的 type 类型。(时间较长)
3. 全量更新知识库数据中index 的 type 类型。(时间较长,最后可能提示 timeout可忽略数据库不崩都会一直增量执行
## 兼容 & 弃用
@@ -42,6 +174,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv490' \
1. PDF增强解析交互添加到页面上。同时内嵌 Doc2x 服务,可直接使用 Doc2x 服务解析 PDF 文件。
2. 图片自动标注,同时修改知识库文件上传部分数据逻辑和交互。
3. pg vector 插件升级 0.8.0 版本,引入迭代搜索,减少部分数据无法被检索的情况。
4. 新增 qwen-qwq 系列模型配置。
## ⚙️ 优化
@@ -49,8 +182,9 @@ curl --location --request POST 'https://{{host}}/api/admin/initv490' \
2. Markdown 解析,增加链接后中文标点符号检测,增加空格。
3. Prompt 模式工具调用,支持思考模型。同时优化其格式检测,减少空输出的概率。
4. Mongo 文件读取流合并,减少计算量。同时优化存储 chunks极大提高大文件读取速度。50M PDF 读取时间提高 3 倍。
5. HTTP Body 适配,增加对字符串对象的适配。
## 🐛 修复
1. 增加网页抓取安全链接校验。
2. 批量运行时,全局变量未进一步传递到下一次运行中,导致最终变量更新错误。
2. 批量运行时,全局变量未进一步传递到下一次运行中,导致最终变量更新错误。

View File

@@ -0,0 +1,34 @@
---
title: 'V4.9.1'
description: 'FastGPT V4.9.1 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 799
---
## 🚀 新增内容
1. 商业版支持单团队模式,更好的管理内部成员。
2. 知识库分块阅读器。
3. API 知识库支持 PDF 增强解析。
4. 邀请团队成员,改为邀请链接模式。
## ⚙️ 优化
1. 知识库数据输入框交互
2. 应用拉取绑定知识库数据交由后端处理。
3. 增加依赖包安全版本检测,并升级部分依赖包。
4. 模型测试代码。
5. 优化思考过程解析逻辑:只要配置了模型支持思考,均会解析 <think> 标签,不会因为对话时,关闭思考而不解析。
## 🐛 修复
1. 最大响应 tokens 提示显示错误的问题。
2. HTTP Node 中,字符串包含换行符时,会解析失败。
3. 知识库问题优化中,未传递历史记录。
4. 错误提示翻译缺失。
5. 内容提取节点array 类型 schema 错误。
6. 模型渠道测试时,实际未指定渠道测试。
7. 新增自定义模型时,会把默认模型字段也保存,导致默认模型误判。
8. 修复 promp 模式工具调用,未判空思考链,导致 UI 错误展示。

View File

@@ -30,7 +30,7 @@ FastGPT 升级包括两个步骤:
## Sealos 修改镜像
1. 打开 [Sealos Cloud](https://cloud.sealos.io/) 找到桌面上的应用管理
1. 打开 [Sealos Cloud](https://cloud.sealos.io?uid=fnWRt09fZP) 找到桌面上的应用管理
![](/imgs/updateImageSealos1.jpg)

View File

@@ -14,7 +14,7 @@ weight: 303
这里介绍在 Sealos 中部署 SearXNG 的方法。Docker 部署,可以直接参考 [SearXNG 官方教程](https://github.com/searxng/searxng)。
点击打开 [Sealos 北京区](https://bja.sealos.run/),点击应用部署,并新建一个应用:
点击打开 [Sealos 北京区](https://bja.sealos.run?uid=fnWRt09fZP),点击应用部署,并新建一个应用:
| 打开应用部署 | 点击新建应用 |
| --- | --- |
@@ -130,7 +130,7 @@ doi_resolvers:
default_doi_resolver: 'oadoi.org'
```
国内目前只有 Bing 引擎可以正常用,所以上面的配置只配置了 bing 引擎。如果在海外部署,可以使用[Sealos 新加坡可用区](https://cloud.sealos.io/),并配置其他搜索引擎,可以参考[SearXNG 默认配置文件](https://github.com/searxng/searxng/blob/master/searx/settings.yml), 从里面复制一些 engine 配置。例如:
国内目前只有 Bing 引擎可以正常用,所以上面的配置只配置了 bing 引擎。如果在海外部署,可以使用[Sealos 新加坡可用区](https://cloud.sealos.io?uid=fnWRt09fZP),并配置其他搜索引擎,可以参考[SearXNG 默认配置文件](https://github.com/searxng/searxng/blob/master/searx/settings.yml), 从里面复制一些 engine 配置。例如:
```
- name: duckduckgo

View File

@@ -89,6 +89,12 @@ weight: 506
47.99.59.223
112.124.46.5
121.40.46.247
120.26.145.73
120.26.147.199
121.43.125.163
121.196.228.45
121.43.126.202
120.26.144.37
```
## 4. 获取AES Key选择加密方式

View File

@@ -27,7 +27,7 @@ weight: 510
## sealos部署服务
[访问sealos](https://cloud.sealos.run/) 登录进来之后打开「应用管理」-> 「新建应用」。
[访问sealos](https://hzh.sealos.run?uid=fnWRt09fZP) 登录进来之后打开「应用管理」-> 「新建应用」。
- 应用名:称随便填写
- 镜像名:私人微信填写 aibotk/wechat-assistant 企业微信填写 aibotk/worker-assistant
- cpu和内存建议 1c1g

View File

@@ -11,16 +11,22 @@
"initIcon": "node ./scripts/icon/init.js",
"previewIcon": "node ./scripts/icon/index.js",
"api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html",
"create:i18n": "node ./scripts/i18n/index.js"
"create:i18n": "node ./scripts/i18n/index.js",
"test": "vitest run --exclude './projects/app/src/test/**'",
"test:all": "vitest run",
"test:workflow": "vitest run workflow"
},
"devDependencies": {
"@chakra-ui/cli": "^2.4.1",
"@vitest/coverage-v8": "^3.0.2",
"husky": "^8.0.3",
"lint-staged": "^13.3.0",
"i18next": "23.11.5",
"lint-staged": "^13.3.0",
"next-i18next": "15.3.0",
"react-i18next": "14.1.2",
"prettier": "3.2.4",
"react-i18next": "14.1.2",
"vitest": "^3.0.2",
"vitest-mongodb": "^1.0.1",
"zhlint": "^0.7.4"
},
"lint-staged": {

View File

@@ -24,7 +24,10 @@ export enum TeamErrEnum {
cannotModifyRootOrg = 'cannotModifyRootOrg',
cannotDeleteNonEmptyOrg = 'cannotDeleteNonEmptyOrg',
cannotDeleteDefaultGroup = 'cannotDeleteDefaultGroup',
userNotActive = 'userNotActive'
userNotActive = 'userNotActive',
invitationLinkInvalid = 'invitationLinkInvalid',
youHaveBeenInTheTeam = 'youHaveBeenInTheTeam',
tooManyInvitations = 'tooManyInvitations'
}
const teamErr = [
@@ -112,6 +115,18 @@ const teamErr = [
{
statusText: TeamErrEnum.cannotDeleteNonEmptyOrg,
message: i18nT('common:code_error.team_error.cannot_delete_non_empty_org')
},
{
statusText: TeamErrEnum.invitationLinkInvalid,
message: i18nT('common:code_error.team_error.invitation_link_invalid')
},
{
statusText: TeamErrEnum.youHaveBeenInTheTeam,
message: i18nT('common:code_error.team_error.you_have_been_in_the_team')
},
{
statusText: TeamErrEnum.tooManyInvitations,
message: i18nT('common:code_error.team_error.too_many_invitations')
}
];

View File

@@ -168,7 +168,7 @@ export const markdownProcess = async ({
return simpleMarkdownText(imageProcess);
};
export const matchMdImgTextAndUpload = (text: string) => {
export const matchMdImg = (text: string) => {
const base64Regex = /!\[([^\]]*)\]\((data:image\/[^;]+;base64[^)]+)\)/g;
const imageList: ImageType[] = [];

View File

@@ -93,7 +93,7 @@ ${mdSplitString}
/*
1. 自定义分隔符:不需要重叠,不需要小块合并
2. Markdown 标题:不需要重叠;标题嵌套共享,需要小块合并
2. Markdown 标题:不需要重叠;标题嵌套共享,需要小块合并
3. 特殊 markdown 语法:不需要重叠,需要小块合并
4. 段落:尽可能保证它是一个完整的段落。
5. 标点分割:重叠
@@ -227,7 +227,7 @@ const commonSplit = (props: SplitProps): SplitResponse => {
}): string[] => {
const isMarkdownStep = checkIsMarkdownSplit(step);
const isCustomStep = checkIsCustomStep(step);
const forbidConcat = isMarkdownStep || isCustomStep; // forbid=true时候lastText肯定为空
const forbidConcat = isCustomStep; // forbid=true时候lastText肯定为空
// oversize
if (step >= stepReges.length) {

View File

@@ -70,6 +70,26 @@ export const appWorkflow2Form = ({
node.inputs,
NodeInputKeyEnum.history
);
defaultAppForm.aiSettings.aiChatReasoning = findInputValueByKey(
node.inputs,
NodeInputKeyEnum.aiChatReasoning
);
defaultAppForm.aiSettings.aiChatTopP = findInputValueByKey(
node.inputs,
NodeInputKeyEnum.aiChatTopP
);
defaultAppForm.aiSettings.aiChatStopSign = findInputValueByKey(
node.inputs,
NodeInputKeyEnum.aiChatStopSign
);
defaultAppForm.aiSettings.aiChatResponseFormat = findInputValueByKey(
node.inputs,
NodeInputKeyEnum.aiChatResponseFormat
);
defaultAppForm.aiSettings.aiChatJsonSchema = findInputValueByKey(
node.inputs,
NodeInputKeyEnum.aiChatJsonSchema
);
} else if (node.flowNodeType === FlowNodeTypeEnum.datasetSearchNode) {
defaultAppForm.dataset.datasets = findInputValueByKey(
node.inputs,

View File

@@ -256,7 +256,7 @@ export const GPTMessages2Chats = (
) {
const value: AIChatItemValueItemType[] = [];
if (typeof item.reasoning_text === 'string') {
if (typeof item.reasoning_text === 'string' && item.reasoning_text) {
value.push({
type: ChatItemValueTypeEnum.reasoning,
reasoning: {
@@ -323,7 +323,7 @@ export const GPTMessages2Chats = (
interactive: item.interactive
});
}
if (typeof item.content === 'string') {
if (typeof item.content === 'string' && item.content) {
const lastValue = value[value.length - 1];
if (lastValue && lastValue.type === ChatItemValueTypeEnum.text && lastValue.text) {
lastValue.text.content += item.content;

View File

@@ -134,6 +134,7 @@ export type ChatItemType = (UserChatItemType | SystemChatItemType | AIChatItemTy
// Frontend type
export type ChatSiteItemType = (UserChatItemType | SystemChatItemType | AIChatItemType) & {
_id?: string;
dataId: string;
status: `${ChatStatusEnum}`;
moduleName?: string;

View File

@@ -16,23 +16,23 @@ export const DatasetDataIndexMap: Record<
}
> = {
[DatasetDataIndexTypeEnum.default]: {
label: i18nT('dataset:data_index_default'),
label: i18nT('common:data_index_default'),
color: 'gray'
},
[DatasetDataIndexTypeEnum.custom]: {
label: i18nT('dataset:data_index_custom'),
label: i18nT('common:data_index_custom'),
color: 'blue'
},
[DatasetDataIndexTypeEnum.summary]: {
label: i18nT('dataset:data_index_summary'),
label: i18nT('common:data_index_summary'),
color: 'green'
},
[DatasetDataIndexTypeEnum.question]: {
label: i18nT('dataset:data_index_question'),
label: i18nT('common:data_index_question'),
color: 'red'
},
[DatasetDataIndexTypeEnum.image]: {
label: i18nT('dataset:data_index_image'),
label: i18nT('common:data_index_image'),
color: 'purple'
}
};

View File

@@ -112,12 +112,15 @@ export type DatasetDataSchemaType = {
tmbId: string;
datasetId: string;
collectionId: string;
datasetId: string;
collectionId: string;
chunkIndex: number;
updateTime: Date;
q: string; // large chunks or question
a: string; // answer or custom content
history?: {
q: string;
a: string;
updateTime: Date;
}[];
forbid?: boolean;
fullTextToken: string;
indexes: DatasetDataIndexItemType[];

View File

@@ -1,7 +1,12 @@
import { EmbeddingModelItemType } from '../ai/model.d';
import { NodeInputKeyEnum } from './constants';
export type SelectedDatasetType = { datasetId: string }[];
export type SelectedDatasetType = {
datasetId: string;
avatar: string;
name: string;
vectorModel: EmbeddingModelItemType;
}[];
export type HttpBodyType<T = Record<string, any>> = {
// [NodeInputKeyEnum.addInputParam]: Record<string, any>;

View File

@@ -4,7 +4,10 @@ export type ContextExtractAgentItemType = {
valueType:
| WorkflowIOValueTypeEnum.string
| WorkflowIOValueTypeEnum.number
| WorkflowIOValueTypeEnum.boolean;
| WorkflowIOValueTypeEnum.boolean
| WorkflowIOValueTypeEnum.arrayString
| WorkflowIOValueTypeEnum.arrayNumber
| WorkflowIOValueTypeEnum.arrayBoolean;
desc: string;
key: string;
required: boolean;

View File

@@ -3,14 +3,14 @@
"version": "1.0.0",
"dependencies": {
"@apidevtools/swagger-parser": "^10.1.0",
"axios": "^1.5.1",
"axios": "^1.8.2",
"cron-parser": "^4.9.0",
"dayjs": "^1.11.7",
"encoding": "^0.1.13",
"js-yaml": "^4.1.0",
"jschardet": "3.1.1",
"nanoid": "^4.0.1",
"next": "14.2.5",
"nanoid": "^5.1.3",
"next": "14.2.21",
"openai": "4.61.0",
"openapi-types": "^12.1.3",
"json5": "^2.2.3",

View File

@@ -63,6 +63,8 @@ export type OutLinkSchema<T extends OutlinkAppType = undefined> = {
responseDetail: boolean;
// whether to hide the node status
showNodeStatus?: boolean;
// wheter to show the full text reader
// showFullText?: boolean;
// whether to show the complete quote
showRawSource?: boolean;
@@ -89,6 +91,7 @@ export type OutLinkEditType<T = undefined> = {
name: string;
responseDetail?: OutLinkSchema<T>['responseDetail'];
showNodeStatus?: OutLinkSchema<T>['showNodeStatus'];
// showFullText?: OutLinkSchema<T>['showFullText'];
showRawSource?: OutLinkSchema<T>['showRawSource'];
// response when request
immediateResponse?: string;

View File

@@ -14,29 +14,28 @@ export const TeamMemberRoleMap = {
};
export enum TeamMemberStatusEnum {
waiting = 'waiting',
active = 'active',
reject = 'reject',
leave = 'leave'
leave = 'leave',
forbidden = 'forbidden'
}
export const TeamMemberStatusMap = {
[TeamMemberStatusEnum.waiting]: {
label: 'user.team.member.waiting',
color: 'orange.600'
},
[TeamMemberStatusEnum.active]: {
label: 'user.team.member.active',
color: 'green.600'
},
[TeamMemberStatusEnum.reject]: {
label: 'user.team.member.reject',
color: 'red.600'
},
[TeamMemberStatusEnum.leave]: {
label: 'user.team.member.leave',
color: 'red.600'
},
[TeamMemberStatusEnum.forbidden]: {
label: 'user.team.member.forbidden',
color: 'red.600'
}
};
export const notLeaveStatus = { $ne: TeamMemberStatusEnum.leave };
export const notLeaveStatus = {
$not: {
$in: [TeamMemberStatusEnum.leave, TeamMemberStatusEnum.forbidden]
}
};

View File

@@ -10,7 +10,6 @@ export type AuthTeamRoleProps = {
export type CreateTeamProps = {
name: string;
avatar?: string;
defaultTeam?: boolean;
memberName?: string;
memberAvatar?: string;
notificationAccount?: string;
@@ -41,11 +40,6 @@ export type UpdateInviteProps = {
status: TeamMemberSchema['status'];
};
export type UpdateStatusProps = {
tmbId: string;
status: TeamMemberSchema['status'];
};
export type InviteMemberResponse = Record<
'invite' | 'inValid' | 'inTeam',
{ username: string; userId: string }[]

View File

@@ -47,7 +47,6 @@ export type TeamMemberSchema = {
role: `${TeamMemberRoleEnum}`;
status: `${TeamMemberStatusEnum}`;
avatar: string;
defaultTeam: boolean;
};
export type TeamMemberWithTeamAndUserSchema = TeamMemberSchema & {
@@ -65,7 +64,6 @@ export type TeamTmbItemType = {
balance?: number;
tmbId: string;
teamDomain: string;
defaultTeam: boolean;
role: `${TeamMemberRoleEnum}`;
status: `${TeamMemberStatusEnum}`;
notificationAccount?: string;

View File

@@ -5,7 +5,7 @@
"dependencies": {
"cheerio": "1.0.0-rc.12",
"@types/pg": "^8.6.6",
"axios": "^1.5.1",
"axios": "^1.8.2",
"duck-duck-scrape": "^2.2.5",
"echarts": "5.4.1",
"expr-eval": "^2.0.2",

View File

@@ -6,6 +6,7 @@ import { guessBase64ImageType } from '../utils';
import { readFromSecondary } from '../../mongo/utils';
import { addHours } from 'date-fns';
import { imageFileType } from '@fastgpt/global/common/file/constants';
import { retryFn } from '@fastgpt/global/common/system/utils';
export const maxImgSize = 1024 * 1024 * 12;
const base64MimeRegex = /data:image\/([^\)]+);base64/;
@@ -40,13 +41,15 @@ export async function uploadMongoImg({
return Promise.reject(`Invalid image file type: ${mime}`);
}
const { _id } = await MongoImage.create({
teamId,
binary,
metadata: Object.assign({ mime }, metadata),
shareId,
expiredTime: forever ? undefined : addHours(new Date(), 1)
});
const { _id } = await retryFn(() =>
MongoImage.create({
teamId,
binary,
metadata: Object.assign({ mime }, metadata),
shareId,
expiredTime: forever ? undefined : addHours(new Date(), 1)
})
);
return `${process.env.NEXT_PUBLIC_BASE_URL || ''}${imageBaseUrl}${String(_id)}.${extension}`;
}

View File

@@ -2,23 +2,30 @@ import axios from 'axios';
import { addLog } from '../../system/log';
import { serverRequestBaseUrl } from '../../api/serverRequest';
import { getFileContentTypeFromHeader, guessBase64ImageType } from '../utils';
import { retryFn } from '@fastgpt/global/common/system/utils';
export const getImageBase64 = async (url: string) => {
addLog.debug(`Load image to base64: ${url}`);
try {
const response = await axios.get(url, {
baseURL: serverRequestBaseUrl,
responseType: 'arraybuffer',
proxy: false
});
const response = await retryFn(() =>
axios.get(url, {
baseURL: serverRequestBaseUrl,
responseType: 'arraybuffer',
proxy: false
})
);
const base64 = Buffer.from(response.data, 'binary').toString('base64');
const imageType =
getFileContentTypeFromHeader(response.headers['content-type']) ||
guessBase64ImageType(base64);
return `data:${imageType};base64,${base64}`;
return {
completeBase64: `data:${imageType};base64,${base64}`,
base64,
mime: imageType
};
} catch (error) {
addLog.debug(`Load image to base64 failed: ${url}`);
console.log(error);

View File

@@ -6,11 +6,12 @@ import type { ImageType, ReadFileResponse } from '../../../worker/readFile/type'
import axios from 'axios';
import { addLog } from '../../system/log';
import { batchRun } from '@fastgpt/global/common/system/utils';
import { htmlTable2Md, matchMdImgTextAndUpload } from '@fastgpt/global/common/string/markdown';
import { htmlTable2Md, matchMdImg } from '@fastgpt/global/common/string/markdown';
import { createPdfParseUsage } from '../../../support/wallet/usage/controller';
import { getErrText } from '@fastgpt/global/common/error/utils';
import { delay } from '@fastgpt/global/common/system/utils';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { getImageBase64 } from '../image/utils';
export type readRawTextByLocalFileParams = {
teamId: string;
@@ -99,7 +100,7 @@ export const readRawContentByFileBuffer = async ({
addLog.info(`Custom file parsing is complete, time: ${Date.now() - start}ms`);
const rawText = response.markdown;
const { text, imageList } = matchMdImgTextAndUpload(rawText);
const { text, imageList } = matchMdImg(rawText);
createPdfParseUsage({
teamId,
@@ -120,8 +121,8 @@ export const readRawContentByFileBuffer = async ({
const parseTextImage = async (text: string) => {
// Extract image links and convert to base64
const imageList: { id: string; url: string }[] = [];
const processedText = text.replace(/!\[.*?\]\((http[^)]+)\)/g, (match, url) => {
const id = getNanoid();
let processedText = text.replace(/!\[.*?\]\((http[^)]+)\)/g, (match, url) => {
const id = `IMAGE_${getNanoid()}_IMAGE`;
imageList.push({
id,
url
@@ -129,22 +130,24 @@ export const readRawContentByFileBuffer = async ({
return `![](${id})`;
});
// Get base64 from image url
let resultImageList: ImageType[] = [];
await Promise.all(
imageList.map(async (item) => {
await batchRun(
imageList,
async (item) => {
try {
const response = await axios.get(item.url, { responseType: 'arraybuffer' });
const mime = response.headers['content-type'] || 'image/jpeg';
const base64 = response.data.toString('base64');
const { base64, mime } = await getImageBase64(item.url);
resultImageList.push({
uuid: item.id,
mime,
base64
});
} catch (error) {
processedText = processedText.replace(item.id, item.url);
addLog.warn(`Failed to get image from ${item.url}: ${getErrText(error)}`);
}
})
},
5
);
return {
@@ -297,6 +300,9 @@ export const readRawContentByFileBuffer = async ({
return systemParse();
};
const start = Date.now();
addLog.debug(`Start parse file`, { extension });
let { rawText, formatText, imageList } = await (async () => {
if (extension === 'pdf') {
return await pdfParseFn();
@@ -304,6 +310,8 @@ export const readRawContentByFileBuffer = async ({
return await systemParse();
})();
addLog.debug(`Parse file success, time: ${Date.now() - start}ms. Uploading file image.`);
// markdown data format
if (imageList) {
await batchRun(imageList, async (item) => {
@@ -312,14 +320,14 @@ export const readRawContentByFileBuffer = async ({
return await uploadMongoImg({
base64Img: `data:${item.mime};base64,${item.base64}`,
teamId,
// expiredTime: addHours(new Date(), 1),
metadata: {
...metadata,
mime: item.mime
}
});
} catch (error) {
return '';
addLog.warn('Upload file image error', { error });
return 'Upload load image error';
}
})();
rawText = rawText.replace(item.uuid, src);
@@ -338,5 +346,7 @@ export const readRawContentByFileBuffer = async ({
}
}
addLog.debug(`Upload file image success, time: ${Date.now() - start}ms`);
return { rawText, formatText, imageList };
};

View File

@@ -19,7 +19,7 @@ export async function connectMongo(): Promise<Mongoose> {
// Remove existing listeners to prevent duplicates
connectionMongo.connection.removeAllListeners('error');
connectionMongo.connection.removeAllListeners('disconnected');
connectionMongo.set('strictQuery', false);
connectionMongo.set('strictQuery', 'throw');
connectionMongo.connection.on('error', async (error) => {
console.log('mongo error', error);

View File

@@ -38,6 +38,27 @@ export class PgVectorCtrl {
await PgClient.query(
`CREATE INDEX CONCURRENTLY IF NOT EXISTS create_time_index ON ${DatasetVectorTableName} USING btree(createtime);`
);
// 10w rows
// await PgClient.query(`
// ALTER TABLE modeldata SET (
// autovacuum_vacuum_scale_factor = 0.1,
// autovacuum_analyze_scale_factor = 0.05,
// autovacuum_vacuum_threshold = 50,
// autovacuum_analyze_threshold = 50,
// autovacuum_vacuum_cost_delay = 20,
// autovacuum_vacuum_cost_limit = 200
// );`);
// 100w rows
// await PgClient.query(`
// ALTER TABLE modeldata SET (
// autovacuum_vacuum_scale_factor = 0.01,
// autovacuum_analyze_scale_factor = 0.02,
// autovacuum_vacuum_threshold = 1000,
// autovacuum_analyze_threshold = 1000,
// autovacuum_vacuum_cost_delay = 10,
// autovacuum_vacuum_cost_limit = 2000
// );`)
addLog.info('init pg successful');
} catch (error) {

View File

@@ -6,10 +6,12 @@ import { getSTTModel } from '../model';
export const aiTranscriptions = async ({
model,
fileStream
fileStream,
headers
}: {
model: string;
fileStream: fs.ReadStream;
headers?: Record<string, string>;
}) => {
const data = new FormData();
data.append('model', model);
@@ -30,7 +32,8 @@ export const aiTranscriptions = async ({
Authorization: modelData.requestAuth
? `Bearer ${modelData.requestAuth}`
: aiAxiosConfig.authorization,
...data.getHeaders()
...data.getHeaders(),
...headers
},
data: data
});

View File

@@ -76,6 +76,10 @@ export const createChatCompletion = async ({
timeout: formatTimeout
});
addLog.debug(`Start create chat completion`, {
model: body.model
});
const response = await ai.chat.completions.create(body, {
...options,
...(modelConstantsData.requestUrl ? { path: modelConstantsData.requestUrl } : {}),

View File

@@ -76,7 +76,7 @@
{
"model": "qwen-max",
"name": "Qwen-max",
"maxContext": 8000,
"maxContext": 32000,
"maxResponse": 4000,
"quoteMaxToken": 6000,
"maxTemperature": 1,
@@ -122,6 +122,56 @@
"showTopP": true,
"showStopSign": true
},
{
"model": "qwq-plus",
"name": "qwq-plus",
"maxContext": 128000,
"maxResponse": 8000,
"quoteMaxToken": 100000,
"maxTemperature": null,
"vision": false,
"reasoning": true,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": false,
"usedInClassify": false,
"customCQPrompt": "",
"usedInExtractFields": false,
"usedInQueryExtension": false,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm",
"showTopP": false,
"showStopSign": false
},
{
"model": "qwq-32b",
"name": "qwq-32b",
"maxContext": 128000,
"maxResponse": 8000,
"quoteMaxToken": 100000,
"maxTemperature": null,
"vision": false,
"reasoning": true,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": false,
"usedInClassify": false,
"customCQPrompt": "",
"usedInExtractFields": false,
"usedInQueryExtension": false,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm",
"showTopP": false,
"showStopSign": false
},
{
"model": "qwen-coder-turbo",
"name": "qwen-coder-turbo",

View File

@@ -8,10 +8,11 @@ type GetVectorProps = {
model: EmbeddingModelItemType;
input: string;
type?: `${EmbeddingTypeEnm}`;
headers?: Record<string, string>;
};
// text to vector
export async function getVectorsByText({ model, input, type }: GetVectorProps) {
export async function getVectorsByText({ model, input, type, headers }: GetVectorProps) {
if (!input) {
return Promise.reject({
code: 500,
@@ -35,13 +36,12 @@ export async function getVectorsByText({ model, input, type }: GetVectorProps) {
model.requestUrl
? {
path: model.requestUrl,
headers: model.requestAuth
? {
Authorization: `Bearer ${model.requestAuth}`
}
: undefined
headers: {
...(model.requestAuth ? { Authorization: `Bearer ${model.requestAuth}` } : {}),
...headers
}
}
: {}
: { headers }
)
.then(async (res) => {
if (!res.data) {

View File

@@ -16,11 +16,13 @@ type ReRankCallResult = { id: string; score?: number }[];
export function reRankRecall({
model = getDefaultRerankModel(),
query,
documents
documents,
headers
}: {
model?: ReRankModelItemType;
query: string;
documents: { id: string; text: string }[];
headers?: Record<string, string>;
}): Promise<ReRankCallResult> {
if (!model) {
return Promise.reject('no rerank model');
@@ -41,7 +43,8 @@ export function reRankRecall({
},
{
headers: {
Authorization: model.requestAuth ? `Bearer ${model.requestAuth}` : authorization
Authorization: model.requestAuth ? `Bearer ${model.requestAuth}` : authorization,
...headers
},
timeout: 30000
}

View File

@@ -1,5 +1,5 @@
import '@/pages/api/__mocks__/base';
import { parseReasoningStreamContent } from '@fastgpt/service/core/ai/utils';
import { parseReasoningStreamContent } from './utils';
import { expect, test } from 'vitest';
test('Parse reasoning stream content test', async () => {
const partList = [

View File

@@ -132,7 +132,7 @@ export const parseReasoningStreamContent = () => {
let endTagBuffer = '';
/*
parseReasoning - 只控制是否主动解析 <think></think>,如果接口已经解析了,仍然会返回 think 内容
parseThinkTag - 只控制是否主动解析 <think></think>,如果接口已经解析了,则不再解析
*/
const parsePart = (
part: {
@@ -143,13 +143,13 @@ export const parseReasoningStreamContent = () => {
};
}[];
},
parseReasoning = false
parseThinkTag = false
): [string, string] => {
const content = part.choices?.[0]?.delta?.content || '';
// @ts-ignore
const reasoningContent = part.choices?.[0]?.delta?.reasoning_content || '';
if (reasoningContent || !parseReasoning) {
if (reasoningContent || !parseThinkTag) {
isInThinkTag = false;
return [reasoningContent, content];
}

View File

@@ -0,0 +1,149 @@
import { MongoDataset } from '../dataset/schema';
import { getEmbeddingModel } from '../ai/model';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import type { StoreNodeItemType } from '@fastgpt/global/core/workflow/type/node';
export async function listAppDatasetDataByTeamIdAndDatasetIds({
teamId,
datasetIdList
}: {
teamId?: string;
datasetIdList: string[];
}) {
const myDatasets = await MongoDataset.find({
_id: { $in: datasetIdList },
...(teamId && { teamId })
}).lean();
return myDatasets.map((item) => ({
datasetId: String(item._id),
avatar: item.avatar,
name: item.name,
vectorModel: getEmbeddingModel(item.vectorModel)
}));
}
export async function rewriteAppWorkflowToDetail({
nodes,
teamId,
isRoot
}: {
nodes: StoreNodeItemType[];
teamId: string;
isRoot: boolean;
}) {
const datasetIdSet = new Set<string>();
// Get all dataset ids from nodes
nodes.forEach((node) => {
if (node.flowNodeType !== FlowNodeTypeEnum.datasetSearchNode) return;
const input = node.inputs.find((item) => item.key === NodeInputKeyEnum.datasetSelectList);
if (!input) return;
const rawValue = input.value as undefined | { datasetId: string }[] | { datasetId: string };
if (!rawValue) return;
const datasetIds = Array.isArray(rawValue)
? rawValue.map((v) => v?.datasetId).filter((id) => !!id && typeof id === 'string')
: rawValue?.datasetId
? [String(rawValue.datasetId)]
: [];
datasetIds.forEach((id) => datasetIdSet.add(id));
});
if (datasetIdSet.size === 0) return;
// Load dataset list
const datasetList = await listAppDatasetDataByTeamIdAndDatasetIds({
teamId: isRoot ? undefined : teamId,
datasetIdList: Array.from(datasetIdSet)
});
const datasetMap = new Map(datasetList.map((ds) => [String(ds.datasetId), ds]));
// Rewrite dataset ids, add dataset info to nodes
if (datasetList.length > 0) {
nodes.forEach((node) => {
if (node.flowNodeType !== FlowNodeTypeEnum.datasetSearchNode) return;
node.inputs.forEach((item) => {
if (item.key !== NodeInputKeyEnum.datasetSelectList) return;
const val = item.value as undefined | { datasetId: string }[] | { datasetId: string };
if (Array.isArray(val)) {
item.value = val
.map((v) => {
const data = datasetMap.get(String(v.datasetId));
if (!data)
return {
datasetId: v.datasetId,
avatar: '',
name: 'Dataset not found',
vectorModel: ''
};
return {
datasetId: data.datasetId,
avatar: data.avatar,
name: data.name,
vectorModel: data.vectorModel
};
})
.filter(Boolean);
} else if (typeof val === 'object' && val !== null) {
const data = datasetMap.get(String(val.datasetId));
if (!data) {
item.value = [
{
datasetId: val.datasetId,
avatar: '',
name: 'Dataset not found',
vectorModel: ''
}
];
} else {
item.value = [
{
datasetId: data.datasetId,
avatar: data.avatar,
name: data.name,
vectorModel: data.vectorModel
}
];
}
}
});
});
}
return nodes;
}
export async function rewriteAppWorkflowToSimple(formatNodes: StoreNodeItemType[]) {
formatNodes.forEach((node) => {
if (node.flowNodeType !== FlowNodeTypeEnum.datasetSearchNode) return;
node.inputs.forEach((input) => {
if (input.key === NodeInputKeyEnum.datasetSelectList) {
const val = input.value as undefined | { datasetId: string }[] | { datasetId: string };
if (!val) {
input.value = [];
} else if (Array.isArray(val)) {
input.value = val
.map((dataset: { datasetId: string }) => ({
datasetId: dataset.datasetId
}))
.filter((item) => !!item.datasetId);
} else if (typeof val === 'object' && val !== null) {
input.value = [
{
datasetId: val.datasetId
}
];
}
}
});
});
}

View File

@@ -15,6 +15,7 @@ import { AppChatConfigType } from '@fastgpt/global/core/app/type';
import { mergeChatResponseData } from '@fastgpt/global/core/chat/utils';
import { pushChatLog } from './pushChatLog';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
type Props = {
chatId: string;
@@ -73,9 +74,44 @@ export async function saveChat({
(node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput
)?.inputs;
// Format save chat content: Remove quote q/a
const processedContent = content.map((item) => {
if (item.obj === ChatRoleEnum.AI) {
const nodeResponse = item[DispatchNodeResponseKeyEnum.nodeResponse];
if (nodeResponse) {
return {
...item,
[DispatchNodeResponseKeyEnum.nodeResponse]: nodeResponse.map((responseItem) => {
if (
responseItem.moduleType === FlowNodeTypeEnum.datasetSearchNode &&
responseItem.quoteList
) {
return {
...responseItem,
quoteList: responseItem.quoteList.map((quote: any) => ({
id: quote.id,
chunkIndex: quote.chunkIndex,
datasetId: quote.datasetId,
collectionId: quote.collectionId,
sourceId: quote.sourceId,
sourceName: quote.sourceName,
score: quote.score,
tokens: quote.tokens
}))
};
}
return responseItem;
})
};
}
}
return item;
});
await mongoSessionRun(async (session) => {
const [{ _id: chatItemIdHuman }, { _id: chatItemIdAi }] = await MongoChatItem.insertMany(
content.map((item) => ({
processedContent.map((item) => ({
chatId,
teamId,
tmbId,

View File

@@ -165,7 +165,7 @@ export const loadRequestMessages = async ({
try {
// If imgUrl is a local path, load image from local, and set url to base64
if (imgUrl.startsWith('/') || process.env.MULTIPLE_DATA_TO_BASE64 === 'true') {
const base64 = await getImageBase64(imgUrl);
const { completeBase64: base64 } = await getImageBase64(imgUrl);
return {
...item,

View File

@@ -111,11 +111,13 @@ export const useApiDatasetRequest = ({ apiServer }: { apiServer: APIFileServer }
const getFileContent = async ({
teamId,
tmbId,
apiFileId
apiFileId,
customPdfParse
}: {
teamId: string;
tmbId: string;
apiFileId: string;
customPdfParse?: boolean;
}) => {
const data = await request<APIFileContentResponse>(
`/v1/file/content`,
@@ -133,7 +135,8 @@ export const useApiDatasetRequest = ({ apiServer }: { apiServer: APIFileServer }
teamId,
tmbId,
url: previewUrl,
relatedId: apiFileId
relatedId: apiFileId,
customPdfParse
});
return rawText;
}

View File

@@ -40,6 +40,15 @@ const DatasetDataSchema = new Schema({
type: String,
default: ''
},
history: {
type: [
{
q: String,
a: String,
updateTime: Date
}
]
},
indexes: {
type: [
{
@@ -89,15 +98,11 @@ try {
chunkIndex: 1,
updateTime: -1
});
// FullText tmp full text index
// DatasetDataSchema.index({ teamId: 1, datasetId: 1, fullTextToken: 'text' });
// Recall vectors after data matching
DatasetDataSchema.index({ teamId: 1, datasetId: 1, collectionId: 1, 'indexes.dataId': 1 });
DatasetDataSchema.index({ updateTime: 1 });
// rebuild data
DatasetDataSchema.index({ rebuilding: 1, teamId: 1, datasetId: 1 });
DatasetDataSchema.index({ initFullText: 1 });
} catch (error) {
console.log(error);
}

View File

@@ -127,7 +127,8 @@ export const readApiServerFileContent = async ({
yuqueServer,
apiFileId,
teamId,
tmbId
tmbId,
customPdfParse
}: {
apiServer?: APIFileServer;
feishuServer?: FeishuServer;
@@ -135,9 +136,15 @@ export const readApiServerFileContent = async ({
apiFileId: string;
teamId: string;
tmbId: string;
customPdfParse?: boolean;
}) => {
if (apiServer) {
return useApiDatasetRequest({ apiServer }).getFileContent({ teamId, tmbId, apiFileId });
return useApiDatasetRequest({ apiServer }).getFileContent({
teamId,
tmbId,
apiFileId,
customPdfParse
});
}
if (feishuServer || yuqueServer) {

View File

@@ -787,6 +787,7 @@ export const defaultSearchDatasetData = async ({
...props
}: DefaultSearchDatasetDataProps): Promise<SearchDatasetDataResponse> => {
const query = props.queries[0];
const histories = props.histories;
const extensionModel = datasetSearchUsingExtensionQuery
? getLLMModel(datasetSearchExtensionModel)
@@ -796,7 +797,8 @@ export const defaultSearchDatasetData = async ({
await datasetSearchQueryExtension({
query,
extensionModel,
extensionBg: datasetSearchExtensionBg
extensionBg: datasetSearchExtensionBg,
histories
});
const result = await searchDatasetData({

View File

@@ -9,7 +9,11 @@ import {
import { ChatItemValueTypeEnum, ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { createChatCompletion } from '../../../ai/config';
import type { ContextExtractAgentItemType } from '@fastgpt/global/core/workflow/template/system/contextExtract/type';
import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import {
NodeInputKeyEnum,
NodeOutputKeyEnum,
toolValueTypeList
} from '@fastgpt/global/core/workflow/constants';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import type { ModuleDispatchProps } from '@fastgpt/global/core/workflow/runtime/type';
import { Prompt_ExtractJson } from '@fastgpt/global/core/ai/prompt/agent';
@@ -192,10 +196,13 @@ ${description ? `- ${description}` : ''}
}
> = {};
extractKeys.forEach((item) => {
const jsonSchema = (
toolValueTypeList.find((type) => type.value === item.valueType) || toolValueTypeList[0]
)?.jsonSchema;
properties[item.key] = {
type: item.valueType || 'string',
...jsonSchema,
description: item.desc,
...(item.enum ? { enum: item.enum.split('\n') } : {})
...(item.enum ? { enum: item.enum.split('\n').filter(Boolean) } : {})
};
});
// function body

View File

@@ -1,11 +1,6 @@
import { createChatCompletion } from '../../../../ai/config';
import { filterGPTMessageByMaxContext, loadRequestMessages } from '../../../../chat/utils';
import {
ChatCompletion,
StreamChatType,
ChatCompletionMessageParam,
ChatCompletionAssistantMessageParam
} from '@fastgpt/global/core/ai/type';
import { StreamChatType, ChatCompletionMessageParam } from '@fastgpt/global/core/ai/type';
import { NextApiResponse } from 'next';
import { responseWriteController } from '../../../../../common/response';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';

View File

@@ -208,6 +208,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
res,
stream: response,
aiChatReasoning,
parseThinkTag: modelConstantsData.reasoning,
isResponseAnswerText,
workflowStreamResponse
});
@@ -264,7 +265,7 @@ export const dispatchChatCompletion = async (props: ChatProps): Promise<ChatResp
}
})();
if (!answerText) {
if (!answerText && !reasoningText) {
return Promise.reject(getEmptyResponseTip());
}
@@ -513,12 +514,14 @@ async function streamResponse({
stream,
workflowStreamResponse,
aiChatReasoning,
parseThinkTag,
isResponseAnswerText
}: {
res: NextApiResponse;
stream: StreamChatType;
workflowStreamResponse?: WorkflowResponseType;
aiChatReasoning?: boolean;
parseThinkTag?: boolean;
isResponseAnswerText?: boolean;
}) {
const write = responseWriteController({
@@ -535,7 +538,7 @@ async function streamResponse({
break;
}
const [reasoningContent, content] = parsePart(part, aiChatReasoning);
const [reasoningContent, content] = parsePart(part, parseThinkTag);
answer += content;
reasoning += reasoningContent;

View File

@@ -21,7 +21,7 @@ import {
FlowNodeInputTypeEnum,
FlowNodeTypeEnum
} from '@fastgpt/global/core/workflow/node/constant';
import { getNanoid, replaceVariable } from '@fastgpt/global/common/string/tools';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import { getSystemTime } from '@fastgpt/global/common/time/timezone';
import { dispatchWorkflowStart } from './init/workflowStart';
@@ -426,6 +426,14 @@ export async function dispatchWorkFlow(data: Props): Promise<DispatchFlowRespons
})();
if (!nodeRunResult) return [];
if (res?.closed) {
addLog.warn('Request is closed', {
appId: props.runningAppInfo.id,
nodeId: node.nodeId,
nodeName: node.name
});
return [];
}
/*
特殊情况:

View File

@@ -120,27 +120,145 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
2. Replace newline strings
*/
const replaceJsonBodyString = (text: string) => {
const valToStr = (val: any) => {
// Check if the variable is in quotes
const isVariableInQuotes = (text: string, variable: string) => {
const index = text.indexOf(variable);
if (index === -1) return false;
// 计算变量前面的引号数量
const textBeforeVar = text.substring(0, index);
const matches = textBeforeVar.match(/"/g) || [];
// 如果引号数量为奇数,则变量在引号内
return matches.length % 2 === 1;
};
const valToStr = (val: any, isQuoted = false) => {
if (val === undefined) return 'null';
if (val === null) return 'null';
if (typeof val === 'object') return JSON.stringify(val);
if (typeof val === 'string') {
if (isQuoted) {
// Replace newlines with escaped newlines
return val.replace(/\n/g, '\\n').replace(/(?<!\\)"/g, '\\"');
}
try {
const parsed = JSON.parse(val);
if (typeof parsed === 'object') {
return JSON.stringify(parsed);
}
JSON.parse(val);
return val;
} catch (error) {
const str = JSON.stringify(val);
return str.startsWith('"') && str.endsWith('"') ? str.slice(1, -1) : str;
}
}
return String(val);
};
// Test cases for variable replacement in JSON body
// const bodyTest = () => {
// const testData = [
// // 基本字符串替换
// {
// body: `{"name":"{{name}}","age":"18"}`,
// variables: [{ key: '{{name}}', value: '测试' }],
// result: `{"name":"测试","age":"18"}`
// },
// // 特殊字符处理
// {
// body: `{"text":"{{text}}"}`,
// variables: [{ key: '{{text}}', value: '包含"引号"和\\反斜杠' }],
// result: `{"text":"包含\\"引号\\"和\\反斜杠"}`
// },
// // 数字类型处理
// {
// body: `{"count":{{count}},"price":{{price}}}`,
// variables: [
// { key: '{{count}}', value: '42' },
// { key: '{{price}}', value: '99.99' }
// ],
// result: `{"count":42,"price":99.99}`
// },
// // 布尔值处理
// {
// body: `{"isActive":{{isActive}},"hasData":{{hasData}}}`,
// variables: [
// { key: '{{isActive}}', value: 'true' },
// { key: '{{hasData}}', value: 'false' }
// ],
// result: `{"isActive":true,"hasData":false}`
// },
// // 对象类型处理
// {
// body: `{"user":{{user}},"user2":"{{user2}}"}`,
// variables: [
// { key: '{{user}}', value: `{"id":1,"name":"张三"}` },
// { key: '{{user2}}', value: `{"id":1,"name":"张三"}` }
// ],
// result: `{"user":{"id":1,"name":"张三"},"user2":"{\\"id\\":1,\\"name\\":\\"张三\\"}"}`
// },
// // 数组类型处理
// {
// body: `{"items":{{items}}}`,
// variables: [{ key: '{{items}}', value: '[1, 2, 3]' }],
// result: `{"items":[1,2,3]}`
// },
// // null 和 undefined 处理
// {
// body: `{"nullValue":{{nullValue}},"undefinedValue":{{undefinedValue}}}`,
// variables: [
// { key: '{{nullValue}}', value: 'null' },
// { key: '{{undefinedValue}}', value: 'undefined' }
// ],
// result: `{"nullValue":null,"undefinedValue":null}`
// },
// // 嵌套JSON结构
// {
// body: `{"data":{"nested":{"value":"{{nestedValue}}"}}}`,
// variables: [{ key: '{{nestedValue}}', value: '嵌套值' }],
// result: `{"data":{"nested":{"value":"嵌套值"}}}`
// },
// // 多变量替换
// {
// body: `{"first":"{{first}}","second":"{{second}}","third":{{third}}}`,
// variables: [
// { key: '{{first}}', value: '第一' },
// { key: '{{second}}', value: '第二' },
// { key: '{{third}}', value: '3' }
// ],
// result: `{"first":"第一","second":"第二","third":3}`
// },
// // JSON字符串作为变量值
// {
// body: `{"config":{{config}}}`,
// variables: [{ key: '{{config}}', value: '{"setting":"enabled","mode":"advanced"}' }],
// result: `{"config":{"setting":"enabled","mode":"advanced"}}`
// }
// ];
// for (let i = 0; i < testData.length; i++) {
// const item = testData[i];
// let bodyStr = item.body;
// for (const variable of item.variables) {
// const isQuote = isVariableInQuotes(bodyStr, variable.key);
// bodyStr = bodyStr.replace(variable.key, valToStr(variable.value, isQuote));
// }
// bodyStr = bodyStr.replace(/(".*?")\s*:\s*undefined\b/g, '$1:null');
// console.log(bodyStr === item.result, i);
// if (bodyStr !== item.result) {
// console.log(bodyStr);
// console.log(item.result);
// } else {
// try {
// JSON.parse(item.result);
// } catch (error) {
// console.log('反序列化异常', i, item.result);
// }
// }
// }
// };
// bodyTest();
// 1. Replace {{key.key}} variables
const regex1 = /\{\{\$([^.]+)\.([^$]+)\$\}\}/g;
@@ -148,6 +266,10 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
matches1.forEach((match) => {
const nodeId = match[1];
const id = match[2];
const fullMatch = match[0];
// 检查变量是否在引号内
const isInQuotes = isVariableInQuotes(text, fullMatch);
const variableVal = (() => {
if (nodeId === VARIABLE_NODE_ID) {
@@ -165,9 +287,9 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
return getReferenceVariableValue({ value: input.value, nodes: runtimeNodes, variables });
})();
const formatVal = valToStr(variableVal);
const formatVal = valToStr(variableVal, isInQuotes);
const regex = new RegExp(`\\{\\{\\$(${nodeId}\\.${id})\\$\\}\\}`, 'g');
const regex = new RegExp(`\\{\\{\\$(${nodeId}\\.${id})\\$\\}\\}`, '');
text = text.replace(regex, () => formatVal);
});
@@ -176,10 +298,16 @@ export const dispatchHttp468Request = async (props: HttpRequestProps): Promise<H
const matches2 = text.match(regex2) || [];
const uniqueKeys2 = [...new Set(matches2.map((match) => match.slice(2, -2)))];
for (const key of uniqueKeys2) {
text = text.replace(new RegExp(`{{(${key})}}`, 'g'), () => valToStr(allVariables[key]));
const fullMatch = `{{${key}}}`;
// 检查变量是否在引号内
const isInQuotes = isVariableInQuotes(text, fullMatch);
text = text.replace(new RegExp(`{{(${key})}}`, ''), () =>
valToStr(allVariables[key], isInQuotes)
);
}
return text.replace(/(".*?")\s*:\s*undefined\b/g, '$1: null');
return text.replace(/(".*?")\s*:\s*undefined\b/g, '$1:null');
};
httpReqUrl = replaceStringVariables(httpReqUrl);

View File

@@ -6,10 +6,10 @@
"@node-rs/jieba": "1.10.0",
"@xmldom/xmldom": "^0.8.10",
"@zilliz/milvus2-sdk-node": "2.4.2",
"axios": "^1.5.1",
"axios": "^1.8.2",
"chalk": "^5.3.0",
"cheerio": "1.0.0-rc.12",
"cookie": "^0.5.0",
"cookie": "^0.7.1",
"date-fns": "2.30.0",
"dayjs": "^1.11.7",
"decompress": "^4.2.1",
@@ -20,13 +20,13 @@
"iconv-lite": "^0.6.3",
"joplin-turndown-plugin-gfm": "^1.0.12",
"json5": "^2.2.3",
"jsonpath-plus": "^10.1.0",
"jsonpath-plus": "^10.3.0",
"jsonwebtoken": "^9.0.2",
"lodash": "^4.17.21",
"mammoth": "^1.6.0",
"mongoose": "^8.10.1",
"multer": "1.4.5-lts.1",
"next": "14.2.5",
"next": "14.2.21",
"nextjs-cors": "^2.2.0",
"node-cron": "^3.0.3",
"node-xlsx": "^0.24.0",

View File

@@ -51,6 +51,9 @@ const OutLinkSchema = new Schema({
type: Boolean,
default: true
},
// showFullText: {
// type: Boolean
// },
showRawSource: {
type: Boolean
},

View File

@@ -43,7 +43,6 @@ async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemTyp
teamDomain: tmb.team?.teamDomain,
role: tmb.role,
status: tmb.status,
defaultTeam: tmb.defaultTeam,
permission: new TeamPermission({
per: Per ?? TeamDefaultPermissionVal,
isOwner: tmb.role === TeamMemberRoleEnum.owner
@@ -71,8 +70,7 @@ export async function getUserDefaultTeam({ userId }: { userId: string }) {
return Promise.reject('tmbId or userId is required');
}
return getTeamMember({
userId: new Types.ObjectId(userId),
defaultTeam: true
userId: new Types.ObjectId(userId)
});
}
@@ -89,8 +87,7 @@ export async function createDefaultTeam({
}) {
// auth default team
const tmb = await MongoTeamMember.findOne({
userId: new Types.ObjectId(userId),
defaultTeam: true
userId: new Types.ObjectId(userId)
});
if (!tmb) {
@@ -115,8 +112,7 @@ export async function createDefaultTeam({
name: 'Owner',
role: TeamMemberRoleEnum.owner,
status: TeamMemberStatusEnum.active,
createTime: new Date(),
defaultTeam: true
createTime: new Date()
}
],
{ session }

View File

@@ -0,0 +1 @@
export const MaxInvitationLinksAmount = 10;

View File

@@ -0,0 +1,49 @@
import {
TeamCollectionName,
TeamMemberCollectionName
} from '@fastgpt/global/support/user/team/constant';
import { connectionMongo, getMongoModel } from '../../../../common/mongo';
import { InvitationSchemaType } from './type';
import addDays from 'date-fns/esm/fp/addDays/index.js';
const { Schema } = connectionMongo;
export const InvitationCollectionName = 'team_invitation_links';
const InvitationSchema = new Schema({
teamId: {
type: Schema.Types.ObjectId,
ref: TeamCollectionName,
required: true
},
usedTimesLimit: {
type: Number,
default: 1,
enum: [1, -1]
},
forbidden: Boolean,
expires: Date,
description: String,
members: {
type: [String],
default: []
}
});
InvitationSchema.virtual('team', {
ref: TeamCollectionName,
localField: 'teamId',
foreignField: '_id',
justOne: true
});
try {
InvitationSchema.index({ teamId: 1 });
InvitationSchema.index({ expires: 1 }, { expireAfterSeconds: 30 * 24 * 60 * 60 });
} catch (error) {
console.log(error);
}
export const MongoInvitationLink = getMongoModel<InvitationSchemaType>(
InvitationCollectionName,
InvitationSchema
);

View File

@@ -0,0 +1,37 @@
import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
export type InvitationSchemaType = {
_id: string;
teamId: string;
usedTimesLimit?: number;
forbidden?: boolean;
expires: Date;
description: string;
members: string[];
};
export type InvitationType = Omit<InvitationSchemaType, 'members'> & {
members: {
tmbId: string;
avatar: string;
name: string;
}[];
};
export type InvitationLinkExpiresType = '30m' | '7d' | '1y';
export type InvitationLinkCreateType = {
description: string;
expires: InvitationLinkExpiresType;
usedTimesLimit: 1 | -1;
};
export type InvitationLinkUpdateType = Partial<
Omit<InvitationSchemaType, 'members' | 'teamId' | '_id'>
> & {
linkId: string;
};
export type InvitationInfoType = InvitationSchemaType & {
teamAvatar: string;
teamName: string;
};

View File

@@ -39,14 +39,14 @@ const TeamMemberSchema = new Schema({
updateTime: {
type: Date
},
defaultTeam: {
type: Boolean,
default: false
},
// Abandoned
role: {
type: String
},
// Abandoned
defaultTeam: {
type: Boolean
}
});

View File

@@ -1,94 +0,0 @@
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
export type TestTokenType = {
userId: string;
teamId: string;
tmbId: string;
isRoot: boolean;
};
export type TestRequest = {
headers: {
cookie?: {
token?: TestTokenType;
};
authorization?: string; // testkey
rootkey?: string; // rootkey
};
query: {
[key: string]: string;
};
body: {
[key: string]: string;
};
};
export function getTestRequest<Q = any, B = any>({
query = {},
body = {},
authToken = true,
// authRoot = false,
// authApiKey = false,
user
}: {
query?: Partial<Q>;
body?: Partial<B>;
authToken?: boolean;
authRoot?: boolean;
authApiKey?: boolean;
user?: {
uid: string;
tmbId: string;
teamId: string;
isRoot: boolean;
};
}): [any, any] {
const headers: TestRequest['headers'] = {};
if (authToken) {
headers.cookie = {
token: {
userId: String(user?.uid || ''),
teamId: String(user?.teamId || ''),
tmbId: String(user?.tmbId || ''),
isRoot: user?.isRoot || false
}
};
}
return [
{
headers,
query,
body
},
{}
];
}
export const parseHeaderCertMock = async ({
req,
authToken = true,
authRoot = false,
authApiKey = false
}: {
req: TestRequest;
authToken?: boolean;
authRoot?: boolean;
authApiKey?: boolean;
}): Promise<TestTokenType> => {
if (authToken) {
const token = req.headers?.cookie?.token;
if (!token) {
return Promise.reject(ERROR_ENUM.unAuthorization);
}
return token;
}
// if (authRoot) {
// // TODO: unfinished
// return req.headers.rootkey;
// }
// if (authApiKey) {
// // TODO: unfinished
// return req.headers.authorization;
// }
return {} as any;
};

View File

@@ -1,6 +1,6 @@
import TurndownService from 'turndown';
import { ImageType } from '../readFile/type';
import { matchMdImgTextAndUpload } from '@fastgpt/global/common/string/markdown';
import { matchMdImg } from '@fastgpt/global/common/string/markdown';
import { getNanoid } from '@fastgpt/global/common/string/tools';
// @ts-ignore
const turndownPluginGfm = require('joplin-turndown-plugin-gfm');
@@ -46,7 +46,7 @@ export const html2md = (
// Base64 img to id, otherwise it will occupy memory when going to md
const { processedHtml, images } = processBase64Images(html);
const md = turndownService.turndown(processedHtml);
const { text, imageList } = matchMdImgTextAndUpload(md);
const { text, imageList } = matchMdImg(md);
return {
rawText: text,

View File

@@ -11,3 +11,22 @@ type PaginationResponse<T = {}> = {
total: number;
list: T[];
};
type LinkedPaginationProps<T = {}> = T & {
pageSize: number;
} & RequireOnlyOne<{
initialId: string;
nextId: string;
prevId: string;
}> &
RequireOnlyOne<{
initialIndex: number;
nextIndex: number;
prevIndex: number;
}>;
type LinkedListResponse<T = {}> = {
list: Array<T & { _id: string; index: number }>;
hasMorePrev: boolean;
hasMoreNext: boolean;
};

View File

@@ -10,15 +10,7 @@ import { Box, Flex } from '@chakra-ui/react';
* @param [groupId] - group id to make the key unique
* @returns
*/
function AvatarGroup({
avatars,
max = 3,
groupId
}: {
max?: number;
avatars: string[];
groupId?: string;
}) {
function AvatarGroup({ avatars, max = 3 }: { max?: number; avatars: string[] }) {
return (
<Flex position="relative">
{avatars.slice(0, max).map((avatar, index) => (

View File

@@ -9,6 +9,7 @@ type Props = FlexProps & {
onClick?: () => void;
hoverColor?: string;
tip?: string;
isLoading?: boolean;
};
const MyIconButton = ({
@@ -17,11 +18,13 @@ const MyIconButton = ({
hoverColor = 'primary.600',
size = '1rem',
tip,
isLoading = false,
...props
}: Props) => {
return (
<MyTooltip label={tip}>
<Flex
position={'relative'}
p={1}
color={'myGray.500'}
rounded={'sm'}
@@ -33,10 +36,14 @@ const MyIconButton = ({
bg: 'myGray.05',
color: hoverColor
}}
onClick={onClick}
onClick={() => {
if (isLoading) return;
onClick?.();
}}
sx={{ userSelect: 'none' }}
{...props}
>
<MyIcon name={icon as any} w={size} />
<MyIcon name={isLoading ? 'common/loading' : (icon as any)} w={size} />
</Flex>
</MyTooltip>
);

View File

@@ -35,10 +35,13 @@ export const iconPaths = {
'common/dingtalkFill': () => import('./icons/common/dingtalkFill.svg'),
'common/disable': () => import('./icons/common/disable.svg'),
'common/downArrowFill': () => import('./icons/common/downArrowFill.svg'),
'common/download': () => import('./icons/common/download.svg'),
'common/edit': () => import('./icons/common/edit.svg'),
'common/editor/resizer': () => import('./icons/common/editor/resizer.svg'),
'common/enable': () => import('./icons/common/enable.svg'),
'common/errorFill': () => import('./icons/common/errorFill.svg'),
'common/file/move': () => import('./icons/common/file/move.svg'),
'common/fileNotFound': () => import('./icons/common/fileNotFound.svg'),
'common/folderFill': () => import('./icons/common/folderFill.svg'),
'common/folderImport': () => import('./icons/common/folderImport.svg'),
'common/fullScreenLight': () => import('./icons/common/fullScreenLight.svg'),
@@ -58,6 +61,8 @@ export const iconPaths = {
'common/leftArrowLight': () => import('./icons/common/leftArrowLight.svg'),
'common/line': () => import('./icons/common/line.svg'),
'common/lineChange': () => import('./icons/common/lineChange.svg'),
'common/lineStop': () => import('./icons/common/lineStop.svg'),
'common/link': () => import('./icons/common/link.svg'),
'common/linkBlue': () => import('./icons/common/linkBlue.svg'),
'common/list': () => import('./icons/common/list.svg'),
'common/loading': () => import('./icons/common/loading.svg'),
@@ -86,7 +91,9 @@ export const iconPaths = {
'common/selectLight': () => import('./icons/common/selectLight.svg'),
'common/setting': () => import('./icons/common/setting.svg'),
'common/settingLight': () => import('./icons/common/settingLight.svg'),
'common/solidChevronDown': () => import('./icons/common/solidChevronDown.svg'),
'common/solidChevronRight': () => import('./icons/common/solidChevronRight.svg'),
'common/solidChevronUp': () => import('./icons/common/solidChevronUp.svg'),
'common/subtract': () => import('./icons/common/subtract.svg'),
'common/templateMarket': () => import('./icons/common/templateMarket.svg'),
'common/text/t': () => import('./icons/common/text/t.svg'),
@@ -96,6 +103,7 @@ export const iconPaths = {
'common/trash': () => import('./icons/common/trash.svg'),
'common/upRightArrowLight': () => import('./icons/common/upRightArrowLight.svg'),
'common/uploadFileFill': () => import('./icons/common/uploadFileFill.svg'),
'common/upperRight': () => import('./icons/common/upperRight.svg'),
'common/userInfo': () => import('./icons/common/userInfo.svg'),
'common/variable': () => import('./icons/common/variable.svg'),
'common/viewLight': () => import('./icons/common/viewLight.svg'),
@@ -373,9 +381,13 @@ export const iconPaths = {
'modal/teamPlans': () => import('./icons/modal/teamPlans.svg'),
'model/BAAI': () => import('./icons/model/BAAI.svg'),
'model/alicloud': () => import('./icons/model/alicloud.svg'),
'model/aws': () => import('./icons/model/aws.svg'),
'model/azure': () => import('./icons/model/azure.svg'),
'model/baichuan': () => import('./icons/model/baichuan.svg'),
'model/chatglm': () => import('./icons/model/chatglm.svg'),
'model/claude': () => import('./icons/model/claude.svg'),
'model/cloudflare': () => import('./icons/model/cloudflare.svg'),
'model/cohere': () => import('./icons/model/cohere.svg'),
'model/deepseek': () => import('./icons/model/deepseek.svg'),
'model/doubao': () => import('./icons/model/doubao.svg'),
'model/ernie': () => import('./icons/model/ernie.svg'),

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" >
<path fill-rule="evenodd" clip-rule="evenodd" d="M15.2703 9.04924C15.2703 10.8968 13.8022 12.4015 11.9689 12.4602V12.462H11.7009C11.3327 12.462 11.0342 12.1635 11.0342 11.7953C11.0342 11.4271 11.3327 11.1287 11.7009 11.1287H11.8689C13.0121 11.1226 13.937 10.1939 13.937 9.04924C13.937 8.07583 13.2669 7.25561 12.361 7.03087L11.5956 6.84096L11.3934 6.0787C10.9949 4.57696 9.62484 3.47188 8.00003 3.47188C6.37523 3.47188 5.00513 4.57696 4.60668 6.0787L4.40444 6.84096L3.63901 7.03087C2.73317 7.25561 2.06307 8.07583 2.06307 9.04924C2.06307 10.1977 2.99405 11.1287 4.14248 11.1287L4.14588 11.1286L4.33524 11.1287C4.70343 11.1287 5.00191 11.4271 5.00191 11.7953C5.00191 12.1635 4.70343 12.462 4.33524 12.462H4.14248H4.06858V12.4612C2.2179 12.4219 0.729736 10.9093 0.729736 9.04924C0.729736 7.44874 1.83149 6.10557 3.31794 5.73677C3.86757 3.6652 5.75552 2.13855 8.00003 2.13855C10.2445 2.13855 12.1325 3.6652 12.6821 5.73677C14.1686 6.10557 15.2703 7.44874 15.2703 9.04924ZM8.00002 7.30026C7.63183 7.30026 7.33335 7.59874 7.33335 7.96693V11.8435L6.96682 11.477C6.74816 11.2583 6.39364 11.2583 6.17498 11.477C5.95632 11.6957 5.95632 12.0502 6.17498 12.2688L7.50545 13.5993C7.52049 13.6159 7.53634 13.6318 7.55296 13.6468L7.60322 13.6971C7.82237 13.9162 8.17767 13.9162 8.39682 13.6971L8.44709 13.6468C8.4637 13.6318 8.47955 13.6159 8.49457 13.5993L9.82418 12.2697C10.0433 12.0506 10.0433 11.6953 9.82418 11.4761C9.60503 11.257 9.24973 11.257 9.03058 11.4761L8.66668 11.84V7.96693C8.66668 7.59874 8.36821 7.30026 8.00002 7.30026Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12" fill="none">
<path d="M0.246607 10.0893C0.241922 10.0968 0.237518 10.1045 0.23341 10.1124C0.194824 10.1867 0.194824 10.2843 0.194824 10.4795V10.7803C0.194824 10.9755 0.194824 11.0731 0.23341 11.1473C0.265926 11.2099 0.316964 11.261 0.37956 11.2935C0.453841 11.3321 0.55143 11.3321 0.746607 11.3321H1.04222C1.20929 11.3321 1.30485 11.3321 1.3755 11.3079C1.38016 11.3066 1.38481 11.3052 1.38944 11.3037C1.50916 11.2658 1.61267 11.1623 1.81968 10.9553L8.4032 4.37179L7.15796 3.12655L0.574444 9.71007C0.397236 9.88728 0.29587 9.98864 0.246607 10.0893Z" />
<path d="M9.43317 3.34182L8.18793 2.09658L9.34895 0.935566C9.36436 0.920152 9.37233 0.912183 9.37902 0.905775C9.71036 0.588537 10.2328 0.588537 10.5641 0.905775C10.5708 0.91219 10.5786 0.919938 10.594 0.935383L10.5946 0.935931C10.6099 0.951255 10.6176 0.958969 10.624 0.965642C10.9412 1.29698 10.9412 1.81939 10.624 2.15073C10.6175 2.15745 10.6098 2.16524 10.5942 2.1808L9.43317 3.34182Z" />
<path d="M4.47921 10.6383C4.47921 10.2552 4.78981 9.9446 5.17296 9.9446H11.1114C11.4946 9.9446 11.8052 10.2552 11.8052 10.6383C11.8052 11.0215 11.4946 11.3321 11.1114 11.3321H5.17296C4.78981 11.3321 4.47921 11.0215 4.47921 10.6383Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,7 @@
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32" fill="none">
<path d="M4 5H26V10H4V5Z" fill="#ECECEC"/>
<path d="M4 12H26V17H4V12Z" fill="#ECECEC"/>
<path d="M4 19H26V24H4V19Z" fill="#ECECEC"/>
<path d="M21.8982 27.7967C18.0945 27.7967 15 24.7021 15 20.8983C15 17.0947 18.0945 14 21.8982 14C25.7019 14 28.7965 17.0947 28.7965 20.8983C28.7965 24.702 25.7019 27.7967 21.8982 27.7967ZM21.8982 15.698C19.0307 15.698 16.698 18.0309 16.698 20.8983C16.698 23.7658 19.0307 26.0988 21.8982 26.0988C24.7657 26.0988 27.0986 23.7658 27.0986 20.8983C27.0986 18.0309 24.7657 15.698 21.8982 15.698Z" fill="#BEC2C9"/>
<path d="M29.9551 30C29.6876 30 29.4203 29.8979 29.2162 29.694L25.6122 26.0902C25.2042 25.6821 25.2042 25.0205 25.6122 24.6124C26.0204 24.2044 26.6819 24.2044 27.09 24.6124L30.694 28.2162C31.102 28.6242 31.102 29.2859 30.694 29.694C30.4899 29.8979 30.2225 30 29.9551 30Z" fill="#BEC2C9"/>
</svg>

After

Width:  |  Height:  |  Size: 942 B

View File

@@ -0,0 +1,10 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_17994_4)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.98584 2.23348C5.80108 2.23348 3.21932 4.81524 3.21932 8C3.21932 11.1848 5.80108 13.7665 8.98584 13.7665C12.1706 13.7665 14.7524 11.1848 14.7524 8C14.7524 4.81524 12.1706 2.23348 8.98584 2.23348ZM1.93787 8C1.93787 4.10751 5.09335 0.952026 8.98584 0.952026C12.8783 0.952026 16.0338 4.10751 16.0338 8C16.0338 11.8925 12.8783 15.048 8.98584 15.048C5.09335 15.048 1.93787 11.8925 1.93787 8ZM6.42294 6.07782C6.42294 5.72396 6.7098 5.4371 7.06366 5.4371H10.908C11.2619 5.4371 11.5487 5.72396 11.5487 6.07782V9.92217C11.5487 10.276 11.2619 10.5629 10.908 10.5629H7.06366C6.7098 10.5629 6.42294 10.276 6.42294 9.92217V6.07782ZM7.70439 6.71855V9.28145H10.2673V6.71855H7.70439Z" fill="#485264"/>
</g>
<defs>
<clipPath id="clip0_17994_4">
<rect width="16" height="16" fill="white" transform="translate(0.98584)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1013 B

View File

@@ -0,0 +1,3 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.98608 2.34313C10.5482 0.78103 13.0808 0.78103 14.6429 2.34313C16.205 3.90522 16.205 6.43788 14.6429 7.99998L13.7001 8.94279C13.4398 9.20314 13.0177 9.20314 12.7573 8.94279C12.497 8.68244 12.497 8.26033 12.7573 7.99998L13.7001 7.05717C14.7415 6.01577 14.7415 4.32733 13.7001 3.28594C12.6587 2.24454 10.9703 2.24454 9.92889 3.28594L8.98608 4.22875C8.72573 4.48909 8.30362 4.48909 8.04327 4.22875C7.78292 3.9684 7.78292 3.54629 8.04327 3.28594L8.98608 2.34313ZM11.7908 5.19523C12.0512 5.45558 12.0512 5.87769 11.7908 6.13804L7.12415 10.8047C6.8638 11.0651 6.44169 11.0651 6.18134 10.8047C5.92099 10.5444 5.92099 10.1222 6.18134 9.8619L10.848 5.19523C11.1084 4.93488 11.5305 4.93488 11.7908 5.19523ZM5.21484 7.05717C5.47519 7.31752 5.47519 7.73963 5.21484 7.99998L4.27204 8.94279C3.23064 9.98419 3.23064 11.6726 4.27204 12.714C5.31343 13.7554 7.00187 13.7554 8.04327 12.714L8.98608 11.7712C9.24643 11.5109 9.66854 11.5109 9.92889 11.7712C10.1892 12.0316 10.1892 12.4537 9.92889 12.714L8.98608 13.6568C7.42398 15.2189 4.89132 15.2189 3.32923 13.6568C1.76713 12.0947 1.76713 9.56208 3.32923 7.99998L4.27204 7.05717C4.53239 6.79682 4.9545 6.79682 5.21484 7.05717Z" fill="#485264"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19 18" fill="none">
<path d="M13.4032 6C14.7396 6 15.4088 7.61571 14.4639 8.56066L10.6388 12.3858C10.053 12.9715 9.10324 12.9715 8.51745 12.3858L4.69235 8.56066C3.7474 7.61571 4.41665 6 5.75301 6H13.4032Z" />
</svg>

After

Width:  |  Height:  |  Size: 270 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19 18" fill="none">
<path d="M13.4032 12.0751C14.7396 12.0751 15.4088 10.4594 14.4639 9.51441L10.6388 5.68931C10.053 5.10352 9.10324 5.10352 8.51745 5.68931L4.69235 9.51441C3.7474 10.4594 4.41665 12.0751 5.75301 12.0751H13.4032Z" />
</svg>

After

Width:  |  Height:  |  Size: 294 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="none">
<path d="M5.44766 4.22876C5.44765 4.59695 5.74613 4.89542 6.11432 4.89542L10.1617 4.89542L3.7573 11.2998C3.49695 11.5602 3.49695 11.9823 3.7573 12.2426C4.01765 12.503 4.43976 12.503 4.70011 12.2426L11.1045 5.83823L11.1045 9.88561C11.1045 10.2538 11.403 10.5523 11.7712 10.5523C12.1394 10.5523 12.4378 10.2538 12.4378 9.88561L12.4378 4.22876C12.4378 3.86057 12.1394 3.56209 11.7712 3.56209L6.11432 3.56209C5.74613 3.56209 5.44765 3.86057 5.44766 4.22876Z"/>
</svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@@ -1,3 +1,3 @@
<svg viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.237 6.21967C4.5299 5.92678 5.00477 5.92678 5.29766 6.21967L9.26733 10.1893L13.237 6.21967C13.5299 5.92678 14.0048 5.92678 14.2977 6.21967C14.5906 6.51256 14.5906 6.98744 14.2977 7.28033L9.79766 11.7803C9.50477 12.0732 9.0299 12.0732 8.737 11.7803L4.237 7.28033C3.94411 6.98744 3.94411 6.51256 4.237 6.21967Z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" >
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.52858 5.52876C3.78892 5.26841 4.21103 5.26841 4.47138 5.52876L7.99998 9.05735L11.5286 5.52876C11.7889 5.26841 12.211 5.26841 12.4714 5.52876C12.7317 5.78911 12.7317 6.21122 12.4714 6.47157L8.47138 10.4716C8.21103 10.7319 7.78892 10.7319 7.52858 10.4716L3.52858 6.47157C3.26823 6.21122 3.26823 5.78911 3.52858 5.52876Z" />
</svg>

Before

Width:  |  Height:  |  Size: 444 B

After

Width:  |  Height:  |  Size: 445 B

View File

@@ -1,3 +1,3 @@
<svg viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.96967 11.7803C4.26256 12.0732 4.73744 12.0732 5.03033 11.7803L9 7.81066L12.9697 11.7803C13.2626 12.0732 13.7374 12.0732 14.0303 11.7803C14.3232 11.4874 14.3232 11.0126 14.0303 10.7197L9.53033 6.21967C9.23744 5.92678 8.76256 5.92678 8.46967 6.21967L3.96967 10.7197C3.67678 11.0126 3.67678 11.4874 3.96967 11.7803Z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" >
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.52858 10.4712C3.78892 10.7316 4.21103 10.7316 4.47138 10.4712L7.99998 6.94265L11.5286 10.4712C11.7889 10.7316 12.211 10.7316 12.4714 10.4712C12.7317 10.2109 12.7317 9.78878 12.4714 9.52843L8.47138 5.52843C8.21103 5.26808 7.78892 5.26808 7.52858 5.52843L3.52858 9.52843C3.26823 9.78878 3.26823 10.2109 3.52858 10.4712Z" />
</svg>

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 445 B

View File

@@ -1 +1,3 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1698497259520" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="10081" xmlns:xlink="http://www.w3.org/1999/xlink" width="128" height="128"><path d="M156.09136 606.57001a457.596822 457.596822 0 0 1 221.680239-392.516385 50.844091 50.844091 0 1 1 50.844091 86.943396 355.90864 355.90864 0 0 0-138.804369 152.532274h16.77855a152.532274 152.532274 0 1 1-152.532274 152.532274z m406.752731 0a457.596822 457.596822 0 0 1 221.680239-392.007944 50.844091 50.844091 0 1 1 50.844091 86.943396 355.90864 355.90864 0 0 0-138.804369 152.532274h16.77855a152.532274 152.532274 0 1 1-152.532274 152.532274z" fill="#E67E22" p-id="10082"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none">
<path d="M11.2308 13.6099V18.2252C11.2308 18.8663 11.0064 19.4111 10.5577 19.8599C10.109 20.3086 9.5641 20.5329 8.92307 20.5329H4.30769C3.66667 20.5329 3.12179 20.3086 2.67308 19.8599C2.22436 19.4111 2 18.8663 2 18.2252V9.76371C2 8.93038 2.16226 8.1351 2.48678 7.37789C2.8113 6.62068 3.25 5.96563 3.80288 5.41275C4.35577 4.85986 5.01082 4.42116 5.76803 4.09664C6.52524 3.77212 7.32051 3.60986 8.15384 3.60986H8.92307C9.13141 3.60986 9.3117 3.68598 9.46394 3.83823C9.61618 3.99047 9.6923 4.17076 9.6923 4.37909V5.91756C9.6923 6.12589 9.61618 6.30618 9.46394 6.45842C9.3117 6.61066 9.13141 6.68679 8.92307 6.68679H8.15384C7.30448 6.68679 6.57933 6.98727 5.97836 7.58823C5.3774 8.18919 5.07692 8.91435 5.07692 9.76371V10.1483C5.07692 10.4688 5.1891 10.7413 5.41346 10.9656C5.63782 11.19 5.91025 11.3022 6.23077 11.3022H8.92307C9.5641 11.3022 10.109 11.5265 10.5577 11.9752C11.0064 12.424 11.2308 12.9688 11.2308 13.6099ZM22 13.6099V18.2252C22 18.8663 21.7756 19.4111 21.3269 19.8599C20.8782 20.3086 20.3333 20.5329 19.6923 20.5329H15.0769C14.4359 20.5329 13.891 20.3086 13.4423 19.8599C12.9936 19.4111 12.7692 18.8663 12.7692 18.2252V9.76371C12.7692 8.93038 12.9315 8.1351 13.256 7.37789C13.5805 6.62068 14.0192 5.96563 14.5721 5.41275C15.125 4.85986 15.78 4.42116 16.5373 4.09664C17.2945 3.77212 18.0897 3.60986 18.9231 3.60986H19.6923C19.9006 3.60986 20.0809 3.68598 20.2332 3.83823C20.3854 3.99047 20.4615 4.17076 20.4615 4.37909V5.91756C20.4615 6.12589 20.3854 6.30618 20.2332 6.45842C20.0809 6.61066 19.9006 6.68679 19.6923 6.68679H18.9231C18.0737 6.68679 17.3486 6.98727 16.7476 7.58823C16.1466 8.18919 15.8461 8.91435 15.8461 9.76371V10.1483C15.8461 10.4688 15.9583 10.7413 16.1827 10.9656C16.407 11.19 16.6795 11.3022 17 11.3022H19.6923C20.3333 11.3022 20.8782 11.5265 21.3269 11.9752C21.7756 12.424 22 12.9688 22 13.6099Z" />
</svg>

Before

Width:  |  Height:  |  Size: 819 B

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1 @@
<svg t="1741332650600" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6423" width="64" height="64"><path d="M288.695 427.886c0 12.653 1.316 22.893 3.73 30.427 2.706 7.534 6.144 15.726 10.972 24.576a14.848 14.848 0 0 1 2.34 7.9c0 3.364-2.048 6.802-6.51 10.24l-21.43 14.336a16.31 16.31 0 0 1-8.85 3.072c-3.438 0-6.803-1.683-10.24-4.755a105.618 105.618 0 0 1-12.289-16.091 263.607 263.607 0 0 1-10.532-20.114c-26.551 31.451-59.977 47.104-100.206 47.104-28.526 0-51.42-8.192-68.023-24.576-16.75-16.384-25.307-38.254-25.307-65.61 0-29.11 10.24-52.662 31.012-70.436s48.42-26.697 83.456-26.697c11.63 0 23.552 1.024 36.133 2.78 12.58 1.681 25.6 4.388 39.204 7.533V322.56c0-26.039-5.485-44.105-16.091-54.71-10.825-10.607-29.257-15.727-55.442-15.727-11.923 0-24.21 1.39-36.791 4.389a269.897 269.897 0 0 0-36.791 11.703 97.573 97.573 0 0 1-11.922 4.388 20.7 20.7 0 0 1-5.486 1.024c-4.754 0-7.168-3.364-7.168-10.605v-16.53c0-5.486 0.731-9.582 2.414-11.996a25.454 25.454 0 0 1 9.508-7.168c11.923-6.144 26.259-11.337 42.935-15.36 16.677-4.462 34.377-6.583 53.175-6.583 40.521 0 70.217 9.29 89.234 27.794 18.725 18.36 28.307 46.446 28.307 84.042v110.665h0.658z m-138.313 51.931c11.264 0 22.82-2.048 35.108-6.144 12.215-4.096 23.113-11.63 32.33-21.87a54.272 54.272 0 0 0 11.63-21.942 122.88 122.88 0 0 0 3.364-29.696v-14.336a255.927 255.927 0 0 0-63.342-7.9c-22.82 0-39.497 4.462-50.761 13.751-11.264 9.216-16.677 22.162-16.677 39.278 0 16.091 4.023 28.013 12.58 36.205 8.193 8.558 20.115 12.654 35.768 12.654z m273.554 36.937c-6.144 0-10.24-1.024-12.946-3.437-2.707-2.048-5.12-6.803-7.168-13.312l-80.019-264.192a59.83 59.83 0 0 1-3.072-13.678c0-5.486 2.707-8.558 8.12-8.558h33.426c6.436 0 10.898 1.024 13.312 3.438 2.706 2.048 4.754 6.802 6.802 13.312l57.198 226.23 53.174-226.23c1.683-6.876 3.73-11.264 6.437-13.312a23.406 23.406 0 0 1 13.605-3.438h27.282c6.51 0 10.971 1.024 13.604 3.438 2.78 2.048 5.12 6.802 6.51 13.312l53.833 228.937 58.953-228.937a29.403 29.403 0 0 1 6.803-13.312 22.235 22.235 0 0 1 13.239-3.438h31.744c5.412 0 8.484 2.706 8.484 8.558 0 1.682-0.366 3.364-0.731 5.412a50.103 50.103 0 0 1-2.34 8.558L648.118 500.37c-2.048 6.803-4.389 11.264-7.168 13.312a21.723 21.723 0 0 1-12.946 3.438h-29.258c-6.582 0-10.971-1.024-13.677-3.438-2.707-2.413-5.12-6.875-6.437-13.677l-52.809-220.453-52.517 220.16c-1.682 6.802-3.73 11.19-6.436 13.605s-7.534 3.437-13.605 3.437h-29.33z m437.76 9.216c-17.7 0-35.401-2.048-52.443-6.144a155.063 155.063 0 0 1-39.205-13.677c-5.413-3.072-9.216-6.51-10.533-9.509a24.137 24.137 0 0 1-2.048-9.655v-17.408c0-7.168 2.707-10.606 7.827-10.606 2.048 0 4.096 0.366 6.144 1.024 2.048 0.732 5.12 2.048 8.484 3.438a188.428 188.428 0 0 0 37.45 11.996c13.677 2.706 26.99 4.096 40.594 4.096 21.43 0 38.107-3.804 49.737-11.264a36.937 36.937 0 0 0 17.7-32.549 33.353 33.353 0 0 0-9.216-23.918c-6.144-6.51-17.7-12.288-34.377-17.773l-49.444-15.36c-24.869-7.9-43.228-19.456-54.492-34.89a81.554 81.554 0 0 1-7.826-87.478 87.771 87.771 0 0 1 24.503-28.014c10.24-7.9 21.87-13.678 35.474-17.774 13.605-4.096 27.94-5.851 42.935-5.851 7.46 0 15.36 0.365 22.82 1.39 7.827 1.023 14.995 2.413 22.09 3.73 6.875 1.755 13.312 3.437 19.456 5.485 6.144 2.048 10.898 4.096 14.336 6.144a29.477 29.477 0 0 1 10.24 8.558 18.432 18.432 0 0 1 2.999 11.264v16.092c0 7.168-2.707 10.971-7.827 10.971a35.109 35.109 0 0 1-12.946-4.17 155.502 155.502 0 0 0-65.39-13.311c-19.456 0-34.743 3.072-45.348 9.582-10.533 6.51-16.019 16.384-16.019 30.427 0 9.509 3.438 17.774 10.24 24.283 6.803 6.51 19.456 12.947 37.523 18.798l48.347 15.36c24.503 7.9 42.204 18.798 52.81 32.841 10.532 13.97 15.652 30.062 15.652 47.836 0 14.701-3.072 28.013-8.777 39.643-6.071 11.703-14.629 21.943-24.942 30.062a109.714 109.714 0 0 1-37.815 19.163c-15.36 4.755-31.305 7.168-48.713 7.168z" fill="#252F3E" p-id="6424"></path><path d="M926.062 692.15C814.08 775.169 651.118 819.2 511.122 819.2c-196.242 0-373.028-72.777-506.587-193.829-10.533-9.508-1.024-22.528 11.63-14.994C160.549 694.491 338.725 745.4 523.045 745.4c124.342 0 260.973-26.039 386.706-79.36 18.724-8.485 34.743 12.361 16.31 26.039z" fill="#FF9900" p-id="6425"></path><path d="M972.8 638.757c-14.336-18.432-94.793-8.85-131.218-4.389-10.972 1.317-12.58-8.265-2.707-15.36 64-45.202 169.326-32.183 181.541-17.115 12.288 15.36-3.365 120.978-63.342 171.593-9.216 7.826-18.066 3.73-13.97-6.583 13.605-33.792 43.886-110.007 29.623-128.146z" fill="#FF9900" p-id="6426"></path></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -0,0 +1 @@
<svg t="1741332590124" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2637" width="64" height="64"><path d="M384.032 128h227.264l-235.904 741.792a36.48 36.48 0 0 1-34.336 26.208H164.16A37.312 37.312 0 0 1 128 857.632a41.6 41.6 0 0 1 1.92-12.32l219.84-691.168A36.48 36.48 0 0 1 384.032 128" fill="#01579B" p-id="2638"></path><path d="M714.24 640H353.92a17.184 17.184 0 0 0-16.704 17.6 18.24 18.24 0 0 0 5.312 13.056l231.552 214.912a35.2 35.2 0 0 0 24.8 10.4h204.032z" fill="#1976D2" p-id="2639"></path><path d="M674.272 154.112A36.48 36.48 0 0 0 640.032 128h-253.28a36.48 36.48 0 0 1 34.304 26.08l219.776 691.168a39.04 39.04 0 0 1-22.72 48.64 35.2 35.2 0 0 1-11.584 2.048h253.28A37.312 37.312 0 0 0 896 857.6a41.6 41.6 0 0 0-1.92-12.32L674.304 154.144z" fill="#29B6F6" p-id="2640"></path></svg>

After

Width:  |  Height:  |  Size: 838 B

View File

@@ -0,0 +1 @@
<svg t="1741333125459" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="11365" width="64" height="64"><path d="M704.38432 718.72c6.304-21.632 3.872-41.408-6.624-56.128-9.568-13.504-25.792-21.28-45.28-22.208l-369.472-4.8h-0.096a6.688 6.688 0 0 1-5.568-3.008l-0.032-0.032a8.192 8.192 0 0 1-0.896-6.624 10.24 10.24 0 0 1 8.672-6.656l372.736-4.8c44.16-2.08 92.16-37.824 108.96-81.632l21.28-55.52a11.584 11.584 0 0 0 0.64-7.168 242.4 242.4 0 0 0-236.8-189.664 242.656 242.656 0 0 0-229.856 164.768 110.176 110.176 0 0 0-76.544-21.28 109.28 109.28 0 0 0-94.816 135.648 155.04 155.04 0 0 0-150.656 155.168c0 7.456 0.608 15.008 1.504 22.496a7.456 7.456 0 0 0 7.2 6.304h681.888a9.312 9.312 0 0 0 8.672-6.624l5.12-18.208z m117.6-237.376c-3.296 0-6.88 0-10.176 0.48-2.4 0-4.512 1.76-5.408 4.16l-14.4 50.112c-6.304 21.632-3.904 41.408 6.592 56.16 9.632 13.504 25.824 21.248 45.344 22.176l78.656 4.832c2.368 0 4.512 1.12 5.664 3.008a8.64 8.64 0 0 1 0.928 6.656 10.24 10.24 0 0 1-8.704 6.624l-81.952 4.8c-44.416 2.08-92.096 37.824-108.928 81.664l-5.984 15.296c-1.216 3.04 0.928 6.048 4.192 6.048h281.504a7.36 7.36 0 0 0 7.2-5.376c4.8-17.408 7.488-35.712 7.488-54.624 0-111.04-90.656-201.696-202.016-201.696z" fill="#F38020" p-id="11366"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1 @@
<svg t="1741332939210" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7576" width="64" height="64"><path d="M331.712601 609.625898c27.560944 0 82.426848-1.535904 158.112785-32.723288 88.31448-36.349728 264.004833-102.3936 390.716913-170.144032 88.655792-47.35704 127.480032-110.158448 127.480033-194.547841A212.168073 212.168073 0 0 0 795.811595 0H304.791617A304.791617 304.791617 0 0 0 0 304.791617c0 168.309481 127.736016 304.834281 331.712601 304.834281z" fill="#39594D" p-id="7577"></path><path d="M414.736746 819.490115a204.147241 204.147241 0 0 1 125.8588-188.574881l154.699665-64.166656c156.406225-64.84928 328.598129 50.044872 328.598129 219.420953a237.510489 237.510489 0 0 1-237.638481 237.510489h-167.413537a204.147241 204.147241 0 0 1-204.104576-204.189905z" fill="#D18EE2" p-id="7578"></path><path d="M175.690353 649.772723A175.690353 175.690353 0 0 0 0 825.548403v22.739912a175.690353 175.690353 0 0 0 351.423369 0v-22.739912a175.690353 175.690353 0 0 0-175.733016-175.690352z" fill="#FF7759" p-id="7579"></path></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -24,6 +24,7 @@ import MyIcon from '../Icon';
import { useRequest2 } from '../../../hooks/useRequest';
import MyDivider from '../MyDivider';
import { useScrollPagination } from '../../../hooks/useScrollPagination';
import Avatar from '../Avatar';
/** 选择组件 Props 类型
* value: 选中的值
@@ -32,20 +33,21 @@ import { useScrollPagination } from '../../../hooks/useScrollPagination';
* isLoading: 是否加载中
* ScrollData: 分页滚动数据控制器 [useScrollPagination] 的返回值
* */
export type SelectProps<T = any> = ButtonProps & {
export type SelectProps<T = any> = Omit<ButtonProps, 'onChange'> & {
value?: T;
placeholder?: string;
isSearch?: boolean;
list: {
alias?: string;
icon?: string;
iconSize?: string;
label: string | React.ReactNode;
description?: string;
value: T;
showBorder?: boolean;
}[];
isLoading?: boolean;
onchange?: (val: T) => any | Promise<any>;
onChange?: (val: T) => any | Promise<any>;
ScrollData?: ReturnType<typeof useScrollPagination>['ScrollData'];
};
@@ -56,7 +58,7 @@ const MySelect = <T = any,>(
isSearch = false,
width = '100%',
list = [],
onchange,
onChange,
isLoading = false,
ScrollData,
...props
@@ -115,7 +117,7 @@ const MySelect = <T = any,>(
}
}, [isSearch, isOpen]);
const { runAsync: onChange, loading } = useRequest2((val: T) => onchange?.(val));
const { runAsync: onclickChange, loading } = useRequest2((val: T) => onChange?.(val));
const ListRender = useMemo(() => {
return (
@@ -135,16 +137,17 @@ const MySelect = <T = any,>(
color: 'myGray.900'
})}
onClick={() => {
if (onChange && value !== item.value) {
onChange(item.value);
if (value !== item.value) {
onclickChange(item.value);
}
}}
whiteSpace={'pre-wrap'}
fontSize={'sm'}
display={'block'}
mb={0.5}
>
<Flex alignItems={'center'}>
{item.icon && <MyIcon mr={2} name={item.icon as any} w={'1rem'} />}
{item.icon && <Avatar mr={2} src={item.icon as any} w={item.iconSize ?? '1rem'} />}
{item.label}
</Flex>
{item.description && (
@@ -224,7 +227,9 @@ const MySelect = <T = any,>(
/>
) : (
<>
{selectItem?.icon && <MyIcon mr={2} name={selectItem.icon as any} w={'1rem'} />}
{selectItem?.icon && (
<Avatar mr={2} src={selectItem.icon as any} w={selectItem.iconSize ?? '1rem'} />
)}
{selectItem?.alias || selectItem?.label || placeholder}
</>
)}

View File

@@ -0,0 +1,238 @@
import { useEffect, useRef, useState, ReactNode, useCallback } from 'react';
import { LinkedListResponse, LinkedPaginationProps } from '../common/fetch/type';
import { Box, BoxProps } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { useScroll, useMemoizedFn, useDebounceEffect } from 'ahooks';
import MyBox from '../components/common/MyBox';
import { useRequest2 } from './useRequest';
import { delay } from '../../global/common/system/utils';
const threshold = 100;
export function useLinkedScroll<
TParams extends LinkedPaginationProps & { isInitialLoad?: boolean },
TData extends LinkedListResponse
>(
api: (data: TParams) => Promise<TData>,
{
pageSize = 15,
params = {},
currentData
}: {
pageSize?: number;
params?: Record<string, any>;
currentData?: { id: string; index: number };
}
) {
const { t } = useTranslation();
const [dataList, setDataList] = useState<TData['list']>([]);
const [hasMorePrev, setHasMorePrev] = useState(true);
const [hasMoreNext, setHasMoreNext] = useState(true);
const anchorRef = useRef({
top: null as { _id: string; index: number } | null,
bottom: null as { _id: string; index: number } | null
});
const containerRef = useRef<HTMLDivElement>(null);
const itemRefs = useRef<(HTMLElement | null)[]>([]);
const scrollToItem = async (id: string, retry = 3) => {
const itemIndex = dataList.findIndex((item) => item._id === id);
if (itemIndex === -1) return;
const element = itemRefs.current[itemIndex];
if (!element || !containerRef.current) {
if (retry > 0) {
await delay(500);
return scrollToItem(id, retry - 1);
}
return;
}
const elementRect = element.getBoundingClientRect();
const containerRect = containerRef.current.getBoundingClientRect();
const scrollTop = containerRef.current.scrollTop + elementRect.top - containerRect.top;
containerRef.current.scrollTo({
top: scrollTop
});
};
const { runAsync: callApi, loading: isLoading } = useRequest2(api);
let scroolSign = useRef(false);
const { runAsync: loadInitData } = useRequest2(
async (scrollWhenFinish = true) => {
if (!currentData || isLoading) return;
const item = dataList.find((item) => item._id === currentData.id);
if (item) {
scrollToItem(item._id);
return;
}
const response = await callApi({
initialId: currentData.id,
initialIndex: currentData.index,
pageSize,
...params
} as TParams);
setHasMorePrev(response.hasMorePrev);
setHasMoreNext(response.hasMoreNext);
scroolSign.current = scrollWhenFinish;
setDataList(response.list);
if (response.list.length > 0) {
anchorRef.current.top = response.list[0];
anchorRef.current.bottom = response.list[response.list.length - 1];
}
},
{
refreshDeps: [currentData],
manual: false
}
);
useEffect(() => {
if (scroolSign.current && currentData) {
scroolSign.current = false;
scrollToItem(currentData.id);
}
}, [dataList]);
const { runAsync: loadPrevData, loading: prevLoading } = useRequest2(
async (scrollRef = containerRef) => {
if (!anchorRef.current.top || !hasMorePrev || isLoading) return;
const prevScrollTop = scrollRef?.current?.scrollTop || 0;
const prevScrollHeight = scrollRef?.current?.scrollHeight || 0;
const response = await callApi({
prevId: anchorRef.current.top._id,
prevIndex: anchorRef.current.top.index,
pageSize,
...params
} as TParams);
if (!response) return;
setHasMorePrev(response.hasMorePrev);
if (response.list.length > 0) {
setDataList((prev) => [...response.list, ...prev]);
anchorRef.current.top = response.list[0];
setTimeout(() => {
if (scrollRef?.current) {
const newHeight = scrollRef.current.scrollHeight;
const heightDiff = newHeight - prevScrollHeight;
scrollRef.current.scrollTop = prevScrollTop + heightDiff;
}
}, 0);
}
return response;
},
{
refreshDeps: [hasMorePrev, isLoading, params, pageSize]
}
);
const { runAsync: loadNextData, loading: nextLoading } = useRequest2(
async (scrollRef = containerRef) => {
if (!anchorRef.current.bottom || !hasMoreNext || isLoading) return;
const prevScrollTop = scrollRef?.current?.scrollTop || 0;
const response = await callApi({
nextId: anchorRef.current.bottom._id,
nextIndex: anchorRef.current.bottom.index,
pageSize,
...params
} as TParams);
if (!response) return;
setHasMoreNext(response.hasMoreNext);
if (response.list.length > 0) {
setDataList((prev) => [...prev, ...response.list]);
anchorRef.current.bottom = response.list[response.list.length - 1];
setTimeout(() => {
if (scrollRef?.current) {
scrollRef.current.scrollTop = prevScrollTop;
}
}, 0);
}
return response;
},
{
refreshDeps: [hasMoreNext, isLoading, params, pageSize]
}
);
const ScrollData = useMemoizedFn(
({
children,
ScrollContainerRef,
...props
}: {
children: ReactNode;
ScrollContainerRef?: React.RefObject<HTMLDivElement>;
} & BoxProps) => {
const ref = ScrollContainerRef || containerRef;
const scroll = useScroll(ref);
useDebounceEffect(
() => {
if (!ref?.current || isLoading) return;
const { scrollTop, scrollHeight, clientHeight } = ref.current;
// 滚动到底部附近,加载更多下方数据
if (scrollTop + clientHeight >= scrollHeight - threshold) {
loadNextData(ref);
}
// 滚动到顶部附近,加载更多上方数据
if (scrollTop <= threshold) {
loadPrevData(ref);
}
},
[scroll],
{ wait: 200 }
);
return (
<MyBox ref={ref} h={'100%'} overflow={'auto'} isLoading={isLoading} {...props}>
{hasMorePrev && prevLoading && (
<Box mt={2} fontSize={'xs'} color={'blackAlpha.500'} textAlign={'center'}>
{t('common:common.is_requesting')}
</Box>
)}
{children}
{hasMoreNext && nextLoading && (
<Box mt={2} fontSize={'xs'} color={'blackAlpha.500'} textAlign={'center'}>
{t('common:common.is_requesting')}
</Box>
)}
</MyBox>
);
}
);
return {
dataList,
setDataList,
isLoading,
loadInitData,
ScrollData,
itemRefs,
scrollToItem
};
}

View File

@@ -200,7 +200,7 @@ export function usePagination<DataT, ResT = {}>(
// Watch scroll position
useThrottleEffect(
() => {
if (!ref?.current || type !== 'scroll' || noMore) return;
if (!ref?.current || type !== 'scroll' || noMore || isLoading) return;
const { scrollTop, scrollHeight, clientHeight } = ref.current;
if (
@@ -211,7 +211,7 @@ export function usePagination<DataT, ResT = {}>(
fetchData(pageNum + 1, ref);
}
},
[scroll],
[scroll, isLoading],
{ wait: 50 }
);

Some files were not shown because too many files have changed in this diff Show More