Compare commits

..

18 Commits

Author SHA1 Message Date
Archer
c5db894e3a update doc (#4460)
* update preview action

* update doc

* remove

* update

* schema
2025-04-07 17:12:53 +08:00
archer
4fc96f6534 preview doc action
add docs preview permission

update preview action

udpate action
2025-04-07 13:57:54 +08:00
Archer
7ee616515d fix: ts (#4458) 2025-04-07 10:55:53 +08:00
archer
4207ecaaaa action 2025-04-07 10:29:19 +08:00
heheer
32ae14f281 fix external var ui (#4444) 2025-04-07 09:55:15 +08:00
archer
9670cba607 Update doc 2025-04-07 09:55:15 +08:00
archer
95d5c01db1 update doc 2025-04-07 09:55:14 +08:00
Archer
915569fe79 Test mongo log (#4443)
* feat: mongodb-log (#4426)

* perf: mongo log

* feat: completions stop reasoner

* mongo db log

---------

Co-authored-by: Finley Ge <32237950+FinleyGe@users.noreply.github.com>
2025-04-07 09:55:14 +08:00
archer
252ad8a611 package 2025-04-07 09:55:14 +08:00
Archer
85ce23869d Test completion v2 (#4438)
* add v2 completions (#4364)

* add v2 completions

* completion config

* config version

* fix

* frontend

* doc

* fix

* fix: completions v2 api

---------

Co-authored-by: heheer <heheer@sealos.io>
2025-04-07 09:55:13 +08:00
Archer
e4c4941a50 perf: mobile voice input (#4437)
* update:Mobile voice interaction (#4362)

* Add files via upload

* Add files via upload

* Update ollama.md

* Update ollama.md

* Add files via upload

* Update useSpeech.ts

* Update ChatInput.tsx

* Update useSpeech.ts

* Update ChatInput.tsx

* Update useSpeech.ts

* Update constants.ts

* Add files via upload

* Update ChatInput.tsx

* Update useSpeech.ts

* Update useSpeech.ts

* Update useSpeech.ts

* Update ChatInput.tsx

* Add files via upload

* Update common.json

* Update VoiceInput.tsx

* Update ChatInput.tsx

* Update VoiceInput.tsx

* Update useSpeech.ts

* Update useSpeech.ts

* Update common.json

* Update common.json

* Update common.json

* Update VoiceInput.tsx

* Update VoiceInput.tsx

* Update ChatInput.tsx

* Update VoiceInput.tsx

* Update ChatInput.tsx

* Update VoiceInput.tsx

* Update ChatInput.tsx

* Update useSpeech.ts

* Update common.json

* Update chat.json

* Update common.json

* Update chat.json

* Update common.json

* Update chat.json

* Update VoiceInput.tsx

* Update ChatInput.tsx

* Update useSpeech.ts

* Update VoiceInput.tsx

* speech ui

* 优化语音输入组件,调整输入框显示逻辑,修复语音输入遮罩层样式,更新画布背景透明度,增强用户交互体验。 (#4435)

* perf: mobil voice input

---------

Co-authored-by: dreamer6680 <1468683855@qq.com>
2025-04-07 09:55:13 +08:00
Archer
c2e088cf39 Redis cache (#4436)
* perf: add Redis cache for vector counting (#4432)

* feat: cache

* perf: get cache key

---------

Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com>
2025-04-07 09:55:13 +08:00
archer
dc73f47486 feat: config chat file expired time 2025-04-07 09:55:13 +08:00
archer
2f9b83d924 perf: remove loading ui 2025-04-07 09:55:12 +08:00
Archer
9fe95da126 pro migration (#4388) (#4433)
* pro migration

* reuse customPdfParseType

Co-authored-by: gggaaallleee <91131304+gggaaallleee@users.noreply.github.com>
2025-04-07 09:55:12 +08:00
Archer
d171b2d3d8 website sync feature (#4429)
* perf: introduce BullMQ for website sync (#4403)

* perf: introduce BullMQ for website sync

* feat: new redis module

* fix: remove graceful shutdown

* perf: improve UI in dataset detail

- Updated the "change" icon SVG file.
- Modified i18n strings.
- Added new i18n string "immediate_sync".
- Improved UI in dataset detail page, including button icons and
background colors.

* refactor: Add chunkSettings to DatasetSchema

* perf: website sync ux

* env template

* fix: clean up website dataset when updating chunk settings (#4420)

* perf: check setting updated

* perf: worker currency

* feat: init script for website sync refactor (#4425)

* website feature doc

---------

Co-authored-by: a.e. <49438478+I-Info@users.noreply.github.com>
2025-04-07 09:55:11 +08:00
archer
e54fe1eed6 doc 2025-04-07 09:55:10 +08:00
Archer
27332743c7 Training status (#4424)
* dataset data training state (#4311)

* dataset data training state

* fix

* fix ts

* fix

* fix api format

* fix

* fix

* perf: count training

* format

* fix: dataset training state (#4417)

* fix

* add test

* fix

* fix

* fix test

* fix test

* perf: training count

* count

* loading status

---------

Co-authored-by: heheer <heheer@sealos.io>
2025-04-07 09:55:07 +08:00
335 changed files with 3173 additions and 10189 deletions

View File

@@ -1,4 +1,4 @@
yangchuansheng/fastgpt-imgs: yangchuansheng/fastgpt-imgs:
- source: docSite/assets/imgs/ - source: docSite/assets/imgs/
dest: imgs/ dest: imgs/
deleteOrphaned: true deleteOrphaned: true

View File

@@ -73,7 +73,7 @@ jobs:
update-docs-image: update-docs-image:
needs: build-fastgpt-docs-images needs: build-fastgpt-docs-images
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
if: github.repository == 'labring/FastGPT' if: github.repository == 'labring/FastGPT'
steps: steps:
- name: Checkout code - name: Checkout code

View File

@@ -1,4 +1,4 @@
name: Deploy doc image to cf name: Deploy doc image to vercel
on: on:
workflow_dispatch: workflow_dispatch:
@@ -18,7 +18,7 @@ jobs:
url: ${{ steps.vercel-action.outputs.preview-url }} url: ${{ steps.vercel-action.outputs.preview-url }}
# The type of runner that the job will run on # The type of runner that the job will run on
runs-on: ubuntu-24.04 runs-on: ubuntu-22.04
permissions: permissions:
contents: write contents: write
@@ -63,6 +63,18 @@ jobs:
- name: Build - name: Build
run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs@6d0568e && hugo -v --minify run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs@6d0568e && hugo -v --minify
# Step 5 - Push our generated site to Vercel
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
id: vercel-action
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }} # Required
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }} #Required
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }} #Required
github-comment: false
vercel-args: '--prod --local-config ../vercel.json' # Optional
working-directory: docSite/public
- name: Deploy to GitHub Pages - name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v4 uses: peaceiris/actions-gh-pages@v4
if: github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/main'

View File

@@ -1,7 +1,7 @@
name: Preview FastGPT docs name: Preview FastGPT docs
on: on:
pull_request_target: pull_request:
paths: paths:
- 'docSite/**' - 'docSite/**'
workflow_dispatch: workflow_dispatch:
@@ -22,7 +22,7 @@ jobs:
url: ${{ steps.vercel-action.outputs.preview-url }} url: ${{ steps.vercel-action.outputs.preview-url }}
# The type of runner that the job will run on # The type of runner that the job will run on
runs-on: ubuntu-24.04 runs-on: ubuntu-22.04
# Job outputs # Job outputs
outputs: outputs:
@@ -50,6 +50,10 @@ jobs:
- 'docSite/content/docs/**' - 'docSite/content/docs/**'
base: main base: main
- name: Add cdn for images
run: |
sed -i "s#\](/imgs/#\](https://cdn.jsdelivr.net/gh/yangchuansheng/fastgpt-imgs@main/imgs/#g" $(grep -rl "\](/imgs/" docSite/content/zh-cn/docs)
# Step 3 - Install Hugo (specific version) # Step 3 - Install Hugo (specific version)
- name: Install Hugo - name: Install Hugo
uses: peaceiris/actions-hugo@v2 uses: peaceiris/actions-hugo@v2
@@ -61,6 +65,9 @@ jobs:
- name: Build - name: Build
run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs@6d0568e && hugo -v --minify run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs@6d0568e && hugo -v --minify
- name: Test
run: ls ./docSite/public
# Step 5 - Push our generated site to Cloudflare # Step 5 - Push our generated site to Cloudflare
- name: Deploy to Cloudflare Pages - name: Deploy to Cloudflare Pages
id: deploy id: deploy

View File

@@ -14,7 +14,7 @@ jobs:
contents: read contents: read
attestations: write attestations: write
id-token: write id-token: write
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
if: github.repository != 'labring/FastGPT' if: github.repository != 'labring/FastGPT'
steps: steps:
- name: Checkout - name: Checkout

View File

@@ -1,14 +1,12 @@
name: Build FastGPT images name: Build FastGPT images
on: on:
workflow_dispatch: workflow_dispatch:
push: push:
paths: paths:
- "projects/app/**" - 'projects/app/**'
- "packages/**" - 'packages/**'
tags: tags:
- "v*" - 'v*'
jobs: jobs:
build-fastgpt-images: build-fastgpt-images:
permissions: permissions:
@@ -16,156 +14,260 @@ jobs:
contents: read contents: read
attestations: write attestations: write
id-token: write id-token: write
strategy: runs-on: ubuntu-20.04
matrix:
sub_routes:
- repo: fastgpt
base_url: ""
- repo: fastgpt-sub-route
base_url: "/fastai"
- repo: fastgpt-sub-route-gchat
base_url: "/gchat"
archs:
- arch: amd64
- arch: arm64
runs-on: ubuntu-24.04-arm
runs-on: ${{ matrix.archs.runs-on || 'ubuntu-24.04' }}
steps: steps:
# install env # install env
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v3
with: with:
fetch-depth: 1 fetch-depth: 1
- name: Install Dependencies
run: |
sudo apt update && sudo apt install -y nodejs npm
- name: Set up QEMU (optional)
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v2
with: with:
driver-opts: network=host driver-opts: network=host
- name: Cache Docker layers - name: Cache Docker layers
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: /tmp/.buildx-cache path: /tmp/.buildx-cache
key: ${{ runner.os }}-${{ matrix.sub_routes.repo }}-buildx-${{ github.sha }} key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: | restore-keys: |
${{ runner.os }}-${{ matrix.sub_routes.repo }}-buildx- ${{ runner.os }}-buildx-
# login docker # login docker
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Ali Hub - name: Login to Ali Hub
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
registry: registry.cn-hangzhou.aliyuncs.com registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALI_HUB_USERNAME }} username: ${{ secrets.ALI_HUB_USERNAME }}
password: ${{ secrets.ALI_HUB_PASSWORD }} password: ${{ secrets.ALI_HUB_PASSWORD }}
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKER_HUB_NAME }} username: ${{ secrets.DOCKER_HUB_NAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }} password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Build for ${{ matrix.archs.arch }} # Set tag
id: build - name: Set image name and tag
uses: docker/build-push-action@v6
with:
context: .
file: projects/app/Dockerfile
platforms: linux/${{ matrix.archs.arch }}
build-args: |
${{ matrix.sub_routes.base_url && format('base_url={0}', matrix.sub_routes.base_url) || '' }}
labels: |
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.description=${{ matrix.sub_routes.repo }} image
outputs: type=image,"name=ghcr.io/${{ github.repository_owner }}/${{ matrix.sub_routes.repo }},${{ secrets.ALI_IMAGE_NAME }}/${{ matrix.sub_routes.repo }},${{ secrets.DOCKER_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}",push-by-digest=true,push=true
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
- name: Export digest
run: | run: |
mkdir -p ${{ runner.temp }}/digests/${{ matrix.sub_routes.repo }} if [[ "${{ github.ref_name }}" == "main" ]]; then
digest="${{ steps.build.outputs.digest }}" echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
touch "${{ runner.temp }}/digests/${{ matrix.sub_routes.repo }}/${digest#sha256:}" echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
else
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt:latest" >> $GITHUB_ENV
fi
- name: Upload digest - name: Build and publish image for main branch or tag push event
uses: actions/upload-artifact@v4 env:
with: DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
name: digests-${{ matrix.sub_routes.repo }}-${{ github.sha }}-${{ matrix.archs.arch }} run: |
path: ${{ runner.temp }}/digests/${{ matrix.sub_routes.repo }}/* docker buildx build \
if-no-files-found: error -f projects/app/Dockerfile \
retention-days: 1 --platform linux/amd64,linux/arm64 \
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
release-fastgpt-images: --label "org.opencontainers.image.description=fastgpt image" \
--push \
--cache-from=type=local,src=/tmp/.buildx-cache \
--cache-to=type=local,dest=/tmp/.buildx-cache \
-t ${Git_Tag} \
-t ${Git_Latest} \
-t ${Ali_Tag} \
-t ${Ali_Latest} \
-t ${Docker_Hub_Tag} \
-t ${Docker_Hub_Latest} \
.
build-fastgpt-images-sub-route:
permissions: permissions:
packages: write packages: write
contents: read contents: read
attestations: write attestations: write
id-token: write id-token: write
needs: build-fastgpt-images runs-on: ubuntu-20.04
strategy:
matrix:
sub_routes:
- repo: fastgpt
- repo: fastgpt-sub-route
- repo: fastgpt-sub-route-gchat
runs-on: ubuntu-24.04
steps: steps:
# install env
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Install Dependencies
run: |
sudo apt update && sudo apt install -y nodejs npm
- name: Set up QEMU (optional)
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
# login docker
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Ali Hub - name: Login to Ali Hub
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
registry: registry.cn-hangzhou.aliyuncs.com registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALI_HUB_USERNAME }} username: ${{ secrets.ALI_HUB_USERNAME }}
password: ${{ secrets.ALI_HUB_PASSWORD }} password: ${{ secrets.ALI_HUB_PASSWORD }}
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKER_HUB_NAME }} username: ${{ secrets.DOCKER_HUB_NAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }} password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Download digests # Set tag
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-${{ matrix.sub_routes.repo }}-${{ github.sha }}-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set image name and tag - name: Set image name and tag
run: | run: |
if [[ "${{ github.ref_name }}" == "main" ]]; then if [[ "${{ github.ref_name }}" == "main" ]]; then
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
else else
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/${{ matrix.sub_routes.repo }}:${{ github.ref_name }}" >> $GITHUB_ENV echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-sub-route:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:${{ github.ref_name }}" >> $GITHUB_ENV echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:${{ github.ref_name }}" >> $GITHUB_ENV echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sub-route:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
fi fi
- name: Create manifest list and push - name: Build and publish image for main branch or tag push event
working-directory: ${{ runner.temp }}/digests env:
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
run: | run: |
TAGS="$(echo -e "${Git_Tag}\n${Git_Latest}\n${Ali_Tag}\n${Ali_Latest}\n${Docker_Hub_Tag}\n${Docker_Hub_Latest}")" docker buildx build \
for TAG in $TAGS; do -f projects/app/Dockerfile \
docker buildx imagetools create -t $TAG \ --platform linux/amd64,linux/arm64 \
$(printf 'ghcr.io/${{ github.repository_owner }}/${{ matrix.sub_routes.repo }}@sha256:%s ' *) --build-arg base_url=/fastai \
sleep 5 --label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
done --label "org.opencontainers.image.description=fastgpt image" \
--push \
--cache-from=type=local,src=/tmp/.buildx-cache \
--cache-to=type=local,dest=/tmp/.buildx-cache \
-t ${Git_Tag} \
-t ${Git_Latest} \
-t ${Ali_Tag} \
-t ${Ali_Latest} \
-t ${Docker_Hub_Tag} \
-t ${Docker_Hub_Latest} \
.
build-fastgpt-images-sub-route-gchat:
permissions:
packages: write
contents: read
attestations: write
id-token: write
runs-on: ubuntu-20.04
steps:
# install env
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 1
- name: Install Dependencies
run: |
sudo apt update && sudo apt install -y nodejs npm
- name: Set up QEMU (optional)
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
driver-opts: network=host
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
# login docker
- name: Login to GitHub Container Registry
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Ali Hub
uses: docker/login-action@v2
with:
registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALI_HUB_USERNAME }}
password: ${{ secrets.ALI_HUB_PASSWORD }}
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_NAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
# Set tag
- name: Set image name and tag
run: |
if [[ "${{ github.ref_name }}" == "main" ]]; then
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-sub-route-gchat:latest" >> $GITHUB_ENV
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-sub-route-gchat:latest" >> $GITHUB_ENV
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route-gchat:latest" >> $GITHUB_ENV
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route-gchat:latest" >> $GITHUB_ENV
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sub-route-gchat:latest" >> $GITHUB_ENV
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sub-route-gchat:latest" >> $GITHUB_ENV
else
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-sub-route-gchat:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-sub-route-gchat:latest" >> $GITHUB_ENV
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route-gchat:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route-gchat:latest" >> $GITHUB_ENV
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sub-route-gchat:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sub-route-gchat:latest" >> $GITHUB_ENV
fi
- name: Build and publish image for main branch or tag push event
env:
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
run: |
docker buildx build \
-f projects/app/Dockerfile \
--platform linux/amd64,linux/arm64 \
--build-arg base_url=/gchat \
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
--label "org.opencontainers.image.description=fastgpt-sub-route-gchat image" \
--push \
--cache-from=type=local,src=/tmp/.buildx-cache \
--cache-to=type=local,dest=/tmp/.buildx-cache \
-t ${Git_Tag} \
-t ${Git_Latest} \
-t ${Ali_Tag} \
-t ${Ali_Latest} \
-t ${Docker_Hub_Tag} \
-t ${Docker_Hub_Latest} \
.

View File

@@ -12,33 +12,26 @@ jobs:
id-token: write id-token: write
pull-requests: write pull-requests: write
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
strategy:
matrix:
image: [fastgpt, sandbox, mcp_server]
fail-fast: false # 即使一个镜像构建失败,也继续构建其他镜像
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
ref: ${{ github.event.pull_request.head.ref }} ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }} repository: ${{ github.event.pull_request.head.repo.full_name }}
fetch-depth: 0 fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
token: ${{ secrets.GITHUB_TOKEN }} token: ${{ secrets.GITHUB_TOKEN }}
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2 uses: docker/setup-buildx-action@v2
with: with:
driver-opts: network=host driver-opts: network=host
- name: Cache Docker layers - name: Cache Docker layers
uses: actions/cache@v3 uses: actions/cache@v3
with: with:
path: /tmp/.buildx-cache path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}-${{ matrix.image }} key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: | restore-keys: |
${{ runner.os }}-buildx-${{ github.sha }}-
${{ runner.os }}-buildx- ${{ runner.os }}-buildx-
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
@@ -48,35 +41,24 @@ jobs:
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Set image config - name: Set DOCKER_REPO_TAGGED based on branch or tag
id: config
run: | run: |
if [[ "${{ matrix.image }}" == "fastgpt" ]]; then echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV
echo "DOCKERFILE=projects/app/Dockerfile" >> $GITHUB_OUTPUT
echo "DESCRIPTION=fastgpt-pr image" >> $GITHUB_OUTPUT
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:fatsgpt_${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
elif [[ "${{ matrix.image }}" == "sandbox" ]]; then
echo "DOCKERFILE=projects/sandbox/Dockerfile" >> $GITHUB_OUTPUT
echo "DESCRIPTION=fastgpt-sandbox-pr image" >> $GITHUB_OUTPUT
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:fatsgpt_sandbox_${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
elif [[ "${{ matrix.image }}" == "mcp_server" ]]; then
echo "DOCKERFILE=projects/mcp_server/Dockerfile" >> $GITHUB_OUTPUT
echo "DESCRIPTION=fastgpt-mcp_server-pr image" >> $GITHUB_OUTPUT
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:fatsgpt_mcp_server_${{ github.event.pull_request.head.sha }}" >> $GITHUB_OUTPUT
fi
- name: Build ${{ matrix.image }} image for PR - name: Build image for PR
env:
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
run: | run: |
docker buildx build \ docker buildx build \
-f ${{ steps.config.outputs.DOCKERFILE }} \ -f projects/app/Dockerfile \
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \ --label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
--label "org.opencontainers.image.description=${{ steps.config.outputs.DESCRIPTION }}" \ --label "org.opencontainers.image.description=fastgpt-pr image" \
--label "org.opencontainers.image.licenses=Apache" \
--push \ --push \
--cache-from=type=local,src=/tmp/.buildx-cache \ --cache-from=type=local,src=/tmp/.buildx-cache \
--cache-to=type=local,dest=/tmp/.buildx-cache \ --cache-to=type=local,dest=/tmp/.buildx-cache \
-t ${{ steps.config.outputs.DOCKER_REPO_TAGGED }} \ -t ${DOCKER_REPO_TAGGED} \
. .
- uses: actions/github-script@v7 - uses: actions/github-script@v7
with: with:
github-token: ${{secrets.GITHUB_TOKEN}} github-token: ${{secrets.GITHUB_TOKEN}}
@@ -85,5 +67,5 @@ jobs:
issue_number: context.issue.number, issue_number: context.issue.number,
owner: context.repo.owner, owner: context.repo.owner,
repo: context.repo.repo, repo: context.repo.repo,
body: 'Preview ${{ matrix.image }} Image: `${{ steps.config.outputs.DOCKER_REPO_TAGGED }}`' body: 'Preview Image: `${{ env.DOCKER_REPO_TAGGED }}`'
}) })

View File

@@ -15,9 +15,6 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- uses: pnpm/action-setup@v4 - uses: pnpm/action-setup@v4
with: with:
version: 10 version: 10

View File

@@ -13,7 +13,7 @@ jobs:
contents: read contents: read
attestations: write attestations: write
id-token: write id-token: write
runs-on: ubuntu-24.04 runs-on: ubuntu-20.04
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4

View File

@@ -1,151 +0,0 @@
name: Build fastgpt-mcp-server images
on:
workflow_dispatch:
push:
paths:
- 'projects/sandbox/**'
tags:
- 'v*'
jobs:
build-fastgpt-mcp_server-images:
permissions:
packages: write
contents: read
attestations: write
id-token: write
strategy:
matrix:
include:
- arch: amd64
- arch: arm64
runs-on: ubuntu-24.04-arm
runs-on: ${{ matrix.runs-on || 'ubuntu-24.04' }}
steps:
# install env
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
driver-opts: network=host
- name: Cache Docker layers
uses: actions/cache@v4
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-mcp-server-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-mcp_server-buildx-
# login docker
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Ali Hub
uses: docker/login-action@v3
with:
registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALI_HUB_USERNAME }}
password: ${{ secrets.ALI_HUB_PASSWORD }}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_NAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Build for ${{ matrix.arch }}
id: build
uses: docker/build-push-action@v6
with:
context: .
file: projects/mcp_server/Dockerfile
platforms: linux/${{ matrix.arch }}
labels: |
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.description=fastgpt-mcp_server image
outputs: type=image,"name=ghcr.io/${{ github.repository_owner }}/fastgpt-mcp_server,${{ secrets.ALI_IMAGE_NAME }}/fastgpt-mcp_server,${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-mcp_server",push-by-digest=true,push=true
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-fastgpt-mcp_server-${{ github.sha }}-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
release-fastgpt-mcp_server-images:
permissions:
packages: write
contents: read
attestations: write
id-token: write
needs: build-fastgpt-mcp_server-images
runs-on: ubuntu-24.04
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Ali Hub
uses: docker/login-action@v3
with:
registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALI_HUB_USERNAME }}
password: ${{ secrets.ALI_HUB_PASSWORD }}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_NAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Download digests
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-fastgpt-mcp_server-${{ github.sha }}-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set image name and tag
run: |
if [[ "${{ github.ref_name }}" == "main" ]]; then
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-mcp_server:latest" >> $GITHUB_ENV
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-mcp_server:latest" >> $GITHUB_ENV
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-mcp_server:latest" >> $GITHUB_ENV
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-mcp_server:latest" >> $GITHUB_ENV
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-mcp_server:latest" >> $GITHUB_ENV
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-mcp_server:latest" >> $GITHUB_ENV
else
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-mcp_server:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-mcp_server:latest" >> $GITHUB_ENV
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-mcp_server:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-mcp_server:latest" >> $GITHUB_ENV
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-mcp_server:${{ github.ref_name }}" >> $GITHUB_ENV
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-mcp_server:latest" >> $GITHUB_ENV
fi
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
run: |
TAGS="$(echo -e "${Git_Tag}\n${Git_Latest}\n${Ali_Tag}\n${Ali_Latest}\n${Docker_Hub_Tag}\n${Docker_Hub_Latest}")"
for TAG in $TAGS; do
docker buildx imagetools create -t $TAG \
$(printf 'ghcr.io/${{ github.repository_owner }}/fastgpt-mcp_server@sha256:%s ' *)
sleep 5
done

View File

@@ -13,115 +13,50 @@ jobs:
contents: read contents: read
attestations: write attestations: write
id-token: write id-token: write
strategy: runs-on: ubuntu-20.04
matrix:
include:
- arch: amd64
- arch: arm64
runs-on: ubuntu-24.04-arm
runs-on: ${{ matrix.runs-on || 'ubuntu-24.04' }}
steps: steps:
# install env # install env
- name: Checkout - name: Checkout
uses: actions/checkout@v3 uses: actions/checkout@v3
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Install Dependencies
run: |
sudo apt update && sudo apt install -y nodejs npm
- name: Set up QEMU (optional)
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx - name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3 uses: docker/setup-buildx-action@v2
with: with:
driver-opts: network=host driver-opts: network=host
- name: Cache Docker layers - name: Cache Docker layers
uses: actions/cache@v4 uses: actions/cache@v4
with: with:
path: /tmp/.buildx-cache path: /tmp/.buildx-cache
key: ${{ runner.os }}-sandbox-buildx-${{ github.sha }} key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: | restore-keys: |
${{ runner.os }}-sandbox-buildx- ${{ runner.os }}-buildx-
# login docker # login docker
- name: Login to GitHub Container Registry - name: Login to GitHub Container Registry
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
registry: ghcr.io registry: ghcr.io
username: ${{ github.repository_owner }} username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Ali Hub - name: Login to Ali Hub
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
registry: registry.cn-hangzhou.aliyuncs.com registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALI_HUB_USERNAME }} username: ${{ secrets.ALI_HUB_USERNAME }}
password: ${{ secrets.ALI_HUB_PASSWORD }} password: ${{ secrets.ALI_HUB_PASSWORD }}
- name: Login to Docker Hub - name: Login to Docker Hub
uses: docker/login-action@v3 uses: docker/login-action@v2
with: with:
username: ${{ secrets.DOCKER_HUB_NAME }} username: ${{ secrets.DOCKER_HUB_NAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }} password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Build for ${{ matrix.arch }} # Set tag
id: build
uses: docker/build-push-action@v6
with:
context: .
file: projects/sandbox/Dockerfile
platforms: linux/${{ matrix.arch }}
labels: |
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.description=fastgpt-sandbox image
outputs: type=image,"name=ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox,${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sandbox,${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox",push-by-digest=true,push=true
cache-from: type=local,src=/tmp/.buildx-cache
cache-to: type=local,dest=/tmp/.buildx-cache
- name: Export digest
run: |
mkdir -p ${{ runner.temp }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-fastgpt-sandbox-${{ github.sha }}-${{ matrix.arch }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
release-fastgpt-sandbox-images:
permissions:
packages: write
contents: read
attestations: write
id-token: write
needs: build-fastgpt-sandbox-images
runs-on: ubuntu-24.04
steps:
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Login to Ali Hub
uses: docker/login-action@v3
with:
registry: registry.cn-hangzhou.aliyuncs.com
username: ${{ secrets.ALI_HUB_USERNAME }}
password: ${{ secrets.ALI_HUB_PASSWORD }}
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_HUB_NAME }}
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
- name: Download digests
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-fastgpt-sandbox-${{ github.sha }}-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Set image name and tag - name: Set image name and tag
run: | run: |
if [[ "${{ github.ref_name }}" == "main" ]]; then if [[ "${{ github.ref_name }}" == "main" ]]; then
@@ -140,12 +75,27 @@ jobs:
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:latest" >> $GITHUB_ENV echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
fi fi
- name: Create manifest list and push - name: Build and publish image for main branch or tag push event
working-directory: ${{ runner.temp }}/digests env:
Git_Tag: ${{ env.Git_Tag }}
Git_Latest: ${{ env.Git_Latest }}
Ali_Tag: ${{ env.Ali_Tag }}
Ali_Latest: ${{ env.Ali_Latest }}
Docker_Hub_Tag: ${{ env.Docker_Hub_Tag }}
Docker_Hub_Latest: ${{ env.Docker_Hub_Latest }}
run: | run: |
TAGS="$(echo -e "${Git_Tag}\n${Git_Latest}\n${Ali_Tag}\n${Ali_Latest}\n${Docker_Hub_Tag}\n${Docker_Hub_Latest}")" docker buildx build \
for TAG in $TAGS; do -f projects/sandbox/Dockerfile \
docker buildx imagetools create -t $TAG \ --platform linux/amd64,linux/arm64 \
$(printf 'ghcr.io/${{ github.repository_owner }}/fastgpt-sandbox@sha256:%s ' *) --label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/fastgpt-sandbox" \
sleep 5 --label "org.opencontainers.image.description=fastgpt-sandbox image" \
done --push \
--cache-from=type=local,src=/tmp/.buildx-cache \
--cache-to=type=local,dest=/tmp/.buildx-cache \
-t ${Git_Tag} \
-t ${Git_Latest} \
-t ${Ali_Tag} \
-t ${Ali_Latest} \
-t ${Docker_Hub_Tag} \
-t ${Docker_Hub_Latest} \
.

View File

@@ -52,17 +52,71 @@
"description": "FastGPT usecontext template" "description": "FastGPT usecontext template"
}, },
"Vitest test case template": { "Jest test template": {
"scope": "typescript", "scope": "typescriptreact",
"prefix": "template_test", "prefix": "jesttest",
"body": [ "body": [
"import { describe, it, expect } from 'vitest';", "import '@/pages/api/__mocks__/base';",
"import { root } from '@/pages/api/__mocks__/db/init';",
"import { getTestRequest } from '@fastgpt/service/test/utils'; ;",
"import { AppErrEnum } from '@fastgpt/global/common/error/code/app';",
"import handler from './demo';",
"", "",
"describe('authType2UsageSource', () => {", "// Import the schema",
" it('Test description', () => {", "import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';",
" expect().toBe();", "",
" });", "beforeAll(async () => {",
" // await MongoOutLink.create({",
" // shareId: 'aaa',",
" // appId: root.appId,",
" // tmbId: root.tmbId,",
" // teamId: root.teamId,",
" // type: 'share',",
" // name: 'aaa'",
" // })",
"});",
"",
"test('Should return a list of outLink', async () => {",
" // Mock request",
" const res = (await handler(",
" ...getTestRequest({",
" query: {",
" appId: root.appId,",
" type: 'share'",
" },",
" user: root",
" })",
" )) as any;",
"",
" expect(res.code).toBe(200);",
" expect(res.data.length).toBe(2);",
"});",
"",
"test('appId is required', async () => {",
" const res = (await handler(",
" ...getTestRequest({",
" query: {",
" type: 'share'",
" },",
" user: root",
" })",
" )) as any;",
" expect(res.code).toBe(500);",
" expect(res.error).toBe(AppErrEnum.unExist);",
"});",
"",
"test('if type is not provided, return nothing', async () => {",
" const res = (await handler(",
" ...getTestRequest({",
" query: {",
" appId: root.appId",
" },",
" user: root",
" })",
" )) as any;",
" expect(res.code).toBe(200);",
" expect(res.data.length).toBe(0);",
"});" "});"
] ]
} }
} }

View File

@@ -17,7 +17,7 @@ dev:
build: build:
ifeq ($(proxy), taobao) ifeq ($(proxy), taobao)
docker build -f $(filePath) -t $(image) . --build-arg proxy=taobao docker build -f $(filePath) -t $(image) . --build-arg proxy=taobao
else ifeq ($(proxy), clash) else ifeq ($(proxy), clash)
docker build -f $(filePath) -t $(image) . --network host --build-arg HTTP_PROXY=http://127.0.0.1:7890 --build-arg HTTPS_PROXY=http://127.0.0.1:7890 docker build -f $(filePath) -t $(image) . --network host --build-arg HTTP_PROXY=http://127.0.0.1:7890 --build-arg HTTPS_PROXY=http://127.0.0.1:7890
else else

View File

@@ -10,7 +10,7 @@
<a href="./README_ja.md">日语</a> <a href="./README_ja.md">日语</a>
</p> </p>
FastGPT 是一个 AI Agent 构建平台,提供开箱即用的数据处理、模型调用等能力同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的应用场景! FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景!
</div> </div>

View File

@@ -126,15 +126,15 @@ services:
# fastgpt # fastgpt
sandbox: sandbox:
container_name: sandbox container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.5 # git image: ghcr.io/labring/fastgpt-sandbox:v4.9.3 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.5 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.3 # 阿里云
networks: networks:
- fastgpt - fastgpt
restart: always restart: always
fastgpt: fastgpt:
container_name: fastgpt container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.5 # git image: ghcr.io/labring/fastgpt:v4.9.3 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.5 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.3 # 阿里云
ports: ports:
- 3000:3000 - 3000:3000
networks: networks:
@@ -184,8 +184,6 @@ services:
- ALLOWED_ORIGINS= - ALLOWED_ORIGINS=
# 是否开启IP限制默认不开启 # 是否开启IP限制默认不开启
- USE_IP_LIMIT=false - USE_IP_LIMIT=false
# 对话文件过期天数
- CHAT_FILE_EXPIRE_TIME=7
volumes: volumes:
- ./config.json:/app/data/config.json - ./config.json:/app/data/config.json

View File

@@ -1,202 +0,0 @@
# 数据库的默认账号和密码仅首次运行时设置有效
# 如果修改了账号密码,记得改数据库和项目连接参数,别只改一处~
# 该配置文件只是给快速启动,测试使用。正式使用,记得务必修改账号密码,以及调整合适的知识库参数,共享内存等。
# 如何无法访问 dockerhub 和 git可以用阿里云阿里云没有arm包
version: '3.3'
services:
# vector db
ob:
image: oceanbase/oceanbase-ce # docker hub
# image: quay.io/oceanbase/oceanbase-ce:4.3.5.1-101000042025031818 # 镜像
container_name: ob
restart: always
# ports: # 生产环境建议不要暴露
# - 2881:2881
networks:
- fastgpt
environment:
# 这里的配置只有首次运行生效。修改后,重启镜像是不会生效的。需要把持久化数据删除再重启,才有效果
- OB_SYS_PASSWORD=obsyspassword
# 不同于传统数据库OceanBase 数据库的账号包含更多字段,包括用户名、租户名和集群名。经典格式为“用户名@租户名#集群名”
# 比如用mysql客户端连接时根据本文件的默认配置应该指定 “-uroot@tenantname”
- OB_TENANT_NAME=tenantname
- OB_TENANT_PASSWORD=tenantpassword
# MODE分为MINI和NORMAL 后者会最大程度使用主机资源
- MODE=NORMAL
- OB_SERVER_IP=127.0.0.1
# 更多环境变量配置见oceanbase官方文档 https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000002013494
volumes:
- ./ob/data:/root/ob
- ./ob/config:/root/.obd/cluster
- ./init.sql:/root/boot/init.d/init.sql
healthcheck:
# obclient -h127.0.0.1 -P2881 -uroot@tenantname -ptenantpassword -e "SELECT 1;"
test: ["CMD-SHELL", "obclient -h$OB_SERVER_IP -P2881 -uroot@$OB_TENANT_NAME -p$OB_TENANT_PASSWORD -e \"SELECT 1;\""]
interval: 30s
timeout: 10s
retries: 1000
start_period: 10s
mongo:
image: mongo:5.0.18 # dockerhub
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/mongo:5.0.18 # 阿里云
# image: mongo:4.4.29 # cpu不支持AVX时候使用
container_name: mongo
restart: always
# ports:
# - 27017:27017
networks:
- fastgpt
command: mongod --keyFile /data/mongodb.key --replSet rs0
environment:
- MONGO_INITDB_ROOT_USERNAME=myusername
- MONGO_INITDB_ROOT_PASSWORD=mypassword
volumes:
- ./mongo/data:/data/db
entrypoint:
- bash
- -c
- |
openssl rand -base64 128 > /data/mongodb.key
chmod 400 /data/mongodb.key
chown 999:999 /data/mongodb.key
echo 'const isInited = rs.status().ok === 1
if(!isInited){
rs.initiate({
_id: "rs0",
members: [
{ _id: 0, host: "mongo:27017" }
]
})
}' > /data/initReplicaSet.js
# 启动MongoDB服务
exec docker-entrypoint.sh "$$@" &
# 等待MongoDB服务启动
until mongo -u myusername -p mypassword --authenticationDatabase admin --eval "print('waited for connection')"; do
echo "Waiting for MongoDB to start..."
sleep 2
done
# 执行初始化副本集的脚本
mongo -u myusername -p mypassword --authenticationDatabase admin /data/initReplicaSet.js
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
wait $$!
# fastgpt
sandbox:
container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.3 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.3 # 阿里云
networks:
- fastgpt
restart: always
fastgpt:
container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.3 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.3 # 阿里云
ports:
- 3000:3000
networks:
- fastgpt
depends_on:
mongo:
condition: service_started
ob:
condition: service_healthy
sandbox:
condition: service_started
restart: always
environment:
# 前端外部可访问的地址,用于自动补全文件资源路径。例如 https:fastgpt.cn不能填 localhost。这个值可以不填不填则发给模型的图片会是一个相对路径而不是全路径模型可能伪造Host。
- FE_DOMAIN=
# root 密码,用户名为: root。如果需要修改 root 密码,直接修改这个环境变量,并重启即可。
- DEFAULT_ROOT_PSW=1234
# # AI Proxy 的地址,如果配了该地址,优先使用
# - AIPROXY_API_ENDPOINT=http://aiproxy:3000
# # AI Proxy 的 Admin Token与 AI Proxy 中的环境变量 ADMIN_KEY
# - AIPROXY_API_TOKEN=aiproxy
# 模型中转地址(如果用了 AI Proxy下面 2 个就不需要了,旧版 OneAPI 用户,使用下面的变量)
- # openai 基本地址,可用作中转。
- OPENAI_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
- # OpenAI API Key
- CHAT_API_KEY=sk-8990fa15a34b464a805237cfe9561f11
# 数据库最大连接数
- DB_MAX_LINK=30
# 登录凭证密钥
- TOKEN_KEY=any
# root的密钥常用于升级时候的初始化请求
- ROOT_KEY=root_key
# 文件阅读加密
- FILE_TOKEN_KEY=filetoken
# MongoDB 连接参数. 用户名myusername,密码mypassword。
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
# OceanBase 向量库连接参数
- OCEANBASE_URL=mysql://root%40tenantname:tenantpassword@ob:2881/test
# sandbox 地址
- SANDBOX_URL=http://sandbox:3000
# 日志等级: debug, info, warn, error
- LOG_LEVEL=info
- STORE_LOG_LEVEL=warn
# 工作流最大运行次数
- WORKFLOW_MAX_RUN_TIMES=1000
# 批量执行节点,最大输入长度
- WORKFLOW_MAX_LOOP_TIMES=100
# 自定义跨域,不配置时,默认都允许跨域(多个域名通过逗号分割)
- ALLOWED_ORIGINS=
# 是否开启IP限制默认不开启
- USE_IP_LIMIT=false
volumes:
- ./config.json:/app/data/config.json
# AI Proxy
aiproxy:
image: ghcr.io/labring/aiproxy:v0.1.5
# image: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.3 # 阿里云
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
networks:
fastgpt:

View File

@@ -1,2 +0,0 @@
ALTER SYSTEM SET ob_vector_memory_limit_percentage = 30;

View File

@@ -85,15 +85,15 @@ services:
# fastgpt # fastgpt
sandbox: sandbox:
container_name: sandbox container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.5 # git image: ghcr.io/labring/fastgpt-sandbox:v4.9.3 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.5 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.3 # 阿里云
networks: networks:
- fastgpt - fastgpt
restart: always restart: always
fastgpt: fastgpt:
container_name: fastgpt container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.5 # git image: ghcr.io/labring/fastgpt:v4.9.3 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.5 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.3 # 阿里云
ports: ports:
- 3000:3000 - 3000:3000
networks: networks:
@@ -142,8 +142,6 @@ services:
- ALLOWED_ORIGINS= - ALLOWED_ORIGINS=
# 是否开启IP限制默认不开启 # 是否开启IP限制默认不开启
- USE_IP_LIMIT=false - USE_IP_LIMIT=false
# 对话文件过期天数
- CHAT_FILE_EXPIRE_TIME=7
volumes: volumes:
- ./config.json:/app/data/config.json - ./config.json:/app/data/config.json

View File

@@ -66,15 +66,15 @@ services:
sandbox: sandbox:
container_name: sandbox container_name: sandbox
image: ghcr.io/labring/fastgpt-sandbox:v4.9.5 # git image: ghcr.io/labring/fastgpt-sandbox:v4.9.3 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.5 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.3 # 阿里云
networks: networks:
- fastgpt - fastgpt
restart: always restart: always
fastgpt: fastgpt:
container_name: fastgpt container_name: fastgpt
image: ghcr.io/labring/fastgpt:v4.9.5 # git image: ghcr.io/labring/fastgpt:v4.9.3 # git
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.5 # 阿里云 # image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.3 # 阿里云
ports: ports:
- 3000:3000 - 3000:3000
networks: networks:
@@ -123,8 +123,6 @@ services:
- ALLOWED_ORIGINS= - ALLOWED_ORIGINS=
# 是否开启IP限制默认不开启 # 是否开启IP限制默认不开启
- USE_IP_LIMIT=false - USE_IP_LIMIT=false
# 对话文件过期天数
- CHAT_FILE_EXPIRE_TIME=7
volumes: volumes:
- ./config.json:/app/data/config.json - ./config.json:/app/data/config.json

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@@ -135,9 +135,6 @@ curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data
# pgvector 版本(测试推荐,简单快捷) # pgvector 版本(测试推荐,简单快捷)
curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-pgvector.yml curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-pgvector.yml
# oceanbase 版本需要将init.sql和docker-compose.yml放在同一个文件夹方便挂载
# curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-oceanbase/docker-compose.yml
# curl -o init.sql https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-oceanbase/init.sql
# milvus 版本 # milvus 版本
# curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-milvus.yml # curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-milvus.yml
# zilliz 版本 # zilliz 版本
@@ -154,13 +151,6 @@ curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/mai
无需操作 无需操作
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="Oceanbase版本" >}}
{{< markdownify >}}
无需操作
{{< /markdownify >}} {{< /markdownify >}}
{{< /tab >}} {{< /tab >}}
{{< tab tabName="Milvus版本" >}} {{< tab tabName="Milvus版本" >}}

View File

@@ -34,94 +34,6 @@ weight: 852
### 请求 ### 请求
{{< tabs tabTotal="3" >}}
{{< tab tabName="基础请求示例" >}}
{{< markdownify >}}
```bash
curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
--header 'Authorization: Bearer fastgpt-xxxxxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"chatId": "my_chatId",
"stream": false,
"detail": false,
"responseChatItemId": "my_responseChatItemId",
"variables": {
"uid": "asdfadsfasfd2323",
"name": "张三"
},
"messages": [
{
"role": "user",
"content": "导演是谁"
}
]
}'
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="图片/文件请求示例" >}}
{{< markdownify >}}
*`messages`有部分区别,其他参数一致。
* 目前不支持上传文件,需上传到自己的对象存储中,获取对应的文件链接。
```bash
curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
--header 'Authorization: Bearer fastgpt-xxxxxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"chatId": "abcd",
"stream": false,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "导演是谁"
},
{
"type": "image_url",
"image_url": {
"url": "图片链接"
}
},
{
"type": "file_url",
"name": "文件名",
"url": "文档链接,支持 txt md html word pdf ppt csv excel"
}
]
}
]
}'
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="参数说明" >}}
{{< markdownify >}}
{{% alert context="info" %}}
- headers.Authorization: Bearer {{apikey}}
- chatId: string | undefined 。
-`undefined` 时(不传入),不使用 FastGpt 提供的上下文功能,完全通过传入的 messages 构建上下文。
-`非空字符串`时,意味着使用 chatId 进行对话,自动从 FastGpt 数据库取历史记录,并使用 messages 数组最后一个内容作为用户问题,其余 message 会被忽略。请自行确保 chatId 唯一长度小于250通常可以是自己系统的对话框ID。
- messages: 结构与 [GPT接口](https://platform.openai.com/docs/api-reference/chat/object) chat模式一致。
- responseChatItemId: string | undefined 。如果传入,则会将该值作为本次对话的响应消息的 IDFastGPT 会自动将该 ID 存入数据库。请确保,在当前`chatId`下,`responseChatItemId`是唯一的。
- detail: 是否返回中间值(模块状态,响应的完整结果等),`stream模式`下会通过`event`进行区分,`非stream模式`结果保存在`responseData`中。
- variables: 模块变量,一个对象,会替换模块中,输入框内容里的`{{key}}`
{{% /alert %}}
{{< /markdownify >}}
{{< /tab >}}
{{< /tabs >}}
<!-- #### v2 <!-- #### v2
v1,v2 接口请求参数一致,仅请求地址不一样。 v1,v2 接口请求参数一致,仅请求地址不一样。
@@ -216,7 +128,93 @@ curl --location --request POST 'http://localhost:3000/api/v2/chat/completions' \
#### v1 #### v1
{{< tabs tabTotal="3" >}}
{{< tab tabName="基础请求示例" >}}
{{< markdownify >}}
```bash
curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
--header 'Authorization: Bearer fastgpt-xxxxxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"chatId": "my_chatId",
"stream": false,
"detail": false,
"responseChatItemId": "my_responseChatItemId",
"variables": {
"uid": "asdfadsfasfd2323",
"name": "张三"
},
"messages": [
{
"role": "user",
"content": "导演是谁"
}
]
}'
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="图片/文件请求示例" >}}
{{< markdownify >}}
*`messages`有部分区别,其他参数一致。
* 目前不支持上传文件,需上传到自己的对象存储中,获取对应的文件链接。
```bash
curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
--header 'Authorization: Bearer fastgpt-xxxxxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"chatId": "abcd",
"stream": false,
"messages": [
{
"role": "user",
"content": [
{
"type": "text",
"text": "导演是谁"
},
{
"type": "image_url",
"image_url": {
"url": "图片链接"
}
},
{
"type": "file_url",
"name": "文件名",
"url": "文档链接,支持 txt md html word pdf ppt csv excel"
}
]
}
]
}'
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="参数说明" >}}
{{< markdownify >}}
{{% alert context="info" %}}
- headers.Authorization: Bearer {{apikey}}
- chatId: string | undefined 。
-`undefined` 时(不传入),不使用 FastGpt 提供的上下文功能,完全通过传入的 messages 构建上下文。
-`非空字符串`时,意味着使用 chatId 进行对话,自动从 FastGpt 数据库取历史记录,并使用 messages 数组最后一个内容作为用户问题,其余 message 会被忽略。请自行确保 chatId 唯一长度小于250通常可以是自己系统的对话框ID。
- messages: 结构与 [GPT接口](https://platform.openai.com/docs/api-reference/chat/object) chat模式一致。
- responseChatItemId: string | undefined 。如果传入,则会将该值作为本次对话的响应消息的 IDFastGPT 会自动将该 ID 存入数据库。请确保,在当前`chatId`下,`responseChatItemId`是唯一的。
- detail: 是否返回中间值(模块状态,响应的完整结果等),`stream模式`下会通过`event`进行区分,`非stream模式`结果保存在`responseData`中。
- variables: 模块变量,一个对象,会替换模块中,输入框内容里的`{{key}}`
{{% /alert %}}
{{< /markdownify >}}
{{< /tab >}}
{{< /tabs >}}
### 响应 ### 响应
@@ -747,6 +745,8 @@ curl --location --request POST 'https://api.fastgpt.in/api/v1/chat/completions'
### 请求示例 ### 请求示例
#### v1
```bash ```bash
curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \ curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
--header 'Authorization: Bearer test-xxxxx' \ --header 'Authorization: Bearer test-xxxxx' \
@@ -760,8 +760,25 @@ curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
}' }'
``` ```
#### v2
```bash
curl --location --request POST 'http://localhost:3000/api/v2/chat/completions' \
--header 'Authorization: Bearer test-xxxxx' \
--header 'Content-Type: application/json' \
--data-raw '{
"stream": false,
"chatId": "test",
"variables": {
"query":"你好"
}
}'
```
### 响应示例 ### 响应示例
#### v1
{{< tabs tabTotal="3" >}} {{< tabs tabTotal="3" >}}
{{< tab tabName="detail=true,stream=false 响应" >}} {{< tab tabName="detail=true,stream=false 响应" >}}
@@ -920,6 +937,151 @@ event取值
{{< /tab >}} {{< /tab >}}
{{< /tabs >}} {{< /tabs >}}
#### v2
{{< tabs tabTotal="3" >}}
{{< tab tabName="detail=true,stream=false 响应" >}}
{{< markdownify >}}
* 插件的输出可以通过查找`responseData`中, `moduleType=pluginOutput`的元素,其`pluginOutput`是插件的输出。
* 流输出,仍可以通过`choices`进行获取。
```json
{
"responseData": [
{
"id": "bsH1ZdbYkz9iJwYa",
"nodeId": "pluginInput",
"moduleName": "workflow:template.plugin_start",
"moduleType": "pluginInput",
"runningTime": 0
},
{
"id": "zDgfqSPhbYZFHVIn",
"nodeId": "h4Gr4lJtFVQ6qI4c",
"moduleName": "AI 对话",
"moduleType": "chatNode",
"runningTime": 1.44,
"totalPoints": 0,
"model": "GPT-4o-mini",
"tokens": 34,
"inputTokens": 8,
"outputTokens": 26,
"query": "你好",
"reasoningText": "",
"historyPreview": [
{
"obj": "Human",
"value": "你好"
},
{
"obj": "AI",
"value": "你好!有什么我可以帮助你的吗?"
}
],
"contextTotalLen": 2
},
{
"id": "uLLwKKRZvufXzgF4",
"nodeId": "pluginOutput",
"moduleName": "common:core.module.template.self_output",
"moduleType": "pluginOutput",
"runningTime": 0,
"totalPoints": 0,
"pluginOutput": {
"result": "你好!有什么我可以帮助你的吗?"
}
}
],
"newVariables": {
},
"id": "test",
"model": "",
"usage": {
"prompt_tokens": 1,
"completion_tokens": 1,
"total_tokens": 1
},
"choices": [
{
"message": {
"role": "assistant",
"content": "你好!有什么我可以帮助你的吗?"
},
"finish_reason": "stop",
"index": 0
}
]
}
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="detail=true,stream=true 响应" >}}
{{< markdownify >}}
* 插件的输出可以通过获取`event=flowResponses`中的字符串,并将其反序列化后得到一个数组。同样的,查找 `moduleType=pluginOutput`的元素,其`pluginOutput`是插件的输出。
* 流输出,仍和对话接口一样获取。
```bash
data: {"event":"flowNodeResponse","data":"{\"id\":\"q8ablUOqHGgqLIRM\",\"nodeId\":\"pluginInput\",\"moduleName\":\"workflow:template.plugin_start\",\"moduleType\":\"pluginInput\",\"runningTime\":0}"}
data: {"event":"flowNodeStatus","data":"{\"status\":\"running\",\"name\":\"AI 对话\"}"}
data: {"event":"answer","data":"{\"id\":\"\",\"object\":\"\",\"created\":0,\"model\":\"\",\"choices\":[{\"delta\":{\"role\":\"assistant\",\"content\":\"你好\"},\"index\":0,\"finish_reason\":null}]}"}
data: {"event":"answer","data":"{\"id\":\"\",\"object\":\"\",\"created\":0,\"model\":\"\",\"choices\":[{\"delta\":{\"role\":\"assistant\",\"content\":\"\"},\"index\":0,\"finish_reason\":null}]}"}
data: {"event":"answer","data":"{\"id\":\"\",\"object\":\"\",\"created\":0,\"model\":\"\",\"choices\":[{\"delta\":{\"role\":\"assistant\",\"content\":\"有什么\"},\"index\":0,\"finish_reason\":null}]}"}
data: {"event":"answer","data":"{\"id\":\"\",\"object\":\"\",\"created\":0,\"model\":\"\",\"choices\":[{\"delta\":{\"role\":\"assistant\",\"content\":\"我\"},\"index\":0,\"finish_reason\":null}]}"}
data: {"event":"answer","data":"{\"id\":\"\",\"object\":\"\",\"created\":0,\"model\":\"\",\"choices\":[{\"delta\":{\"role\":\"assistant\",\"content\":\"可以\"},\"index\":0,\"finish_reason\":null}]}"}
data: {"event":"answer","data":"{\"id\":\"\",\"object\":\"\",\"created\":0,\"model\":\"\",\"choices\":[{\"delta\":{\"role\":\"assistant\",\"content\":\"帮助\"},\"index\":0,\"finish_reason\":null}]}"}
data: {"event":"answer","data":"{\"id\":\"\",\"object\":\"\",\"created\":0,\"model\":\"\",\"choices\":[{\"delta\":{\"role\":\"assistant\",\"content\":\"你\"},\"index\":0,\"finish_reason\":null}]}"}
data: {"event":"answer","data":"{\"id\":\"\",\"object\":\"\",\"created\":0,\"model\":\"\",\"choices\":[{\"delta\":{\"role\":\"assistant\",\"content\":\"的吗\"},\"index\":0,\"finish_reason\":null}]}"}
data: {"event":"answer","data":"{\"id\":\"\",\"object\":\"\",\"created\":0,\"model\":\"\",\"choices\":[{\"delta\":{\"role\":\"assistant\",\"content\":\"\"},\"index\":0,\"finish_reason\":null}]}"}
data: {"event":"flowNodeResponse","data":"{\"id\":\"rqlXLUap8QeiN7Kf\",\"nodeId\":\"h4Gr4lJtFVQ6qI4c\",\"moduleName\":\"AI 对话\",\"moduleType\":\"chatNode\",\"runningTime\":1.79,\"totalPoints\":0,\"model\":\"GPT-4o-mini\",\"tokens\":137,\"inputTokens\":111,\"outputTokens\":26,\"query\":\"你好\",\"reasoningText\":\"\",\"historyPreview\":[{\"obj\":\"Human\",\"value\":\"[{\\\"renderTypeList\\\":[\\\"reference\\\"],\\\"selectedTypeInde\\n\\n...[hide 174 chars]...\\n\\ncanSelectImg\\\":true,\\\"required\\\":false,\\\"value\\\":\\\"你好\\\"}]\"},{\"obj\":\"AI\",\"value\":\"你好!有什么我可以帮助你的吗?\"},{\"obj\":\"Human\",\"value\":\"你好\"},{\"obj\":\"AI\",\"value\":\"你好!有什么我可以帮助你的吗?\"}],\"contextTotalLen\":4}"}
data: {"event":"flowNodeResponse","data":"{\"id\":\"lHCpHI0MrM00HQlX\",\"nodeId\":\"pluginOutput\",\"moduleName\":\"common:core.module.template.self_output\",\"moduleType\":\"pluginOutput\",\"runningTime\":0,\"totalPoints\":0,\"pluginOutput\":{\"result\":\"你好!有什么我可以帮助你的吗?\"}}"}
data: {"event":"answer","data":"{\"id\":\"\",\"object\":\"\",\"created\":0,\"model\":\"\",\"choices\":[{\"delta\":{\"role\":\"assistant\",\"content\":null},\"index\":0,\"finish_reason\":\"stop\"}]}"}
data: {"event":"answer","data":"[DONE]"}
```
{{< /markdownify >}}
{{< /tab >}}
{{< tab tabName="输出获取" >}}
{{< markdownify >}}
event取值
- answer: 返回给客户端的文本(最终会算作回答)
- fastAnswer: 指定回复返回给客户端的文本(最终会算作回答)
- toolCall: 执行工具
- toolParams: 工具参数
- toolResponse: 工具返回
- flowNodeStatus: 运行到的节点状态
- flowNodeResponse: 单个节点详细响应
- updateVariables: 更新变量
- error: 报错
{{< /markdownify >}}
{{< /tab >}}
{{< /tabs >}}
# 对话 CRUD # 对话 CRUD
{{% alert icon="🤖 " context="success" %}} {{% alert icon="🤖 " context="success" %}}

View File

@@ -138,7 +138,7 @@ FastGPT 商业版共包含了2个应用fastgpt, fastgpt-plus和2个数据
SYSTEM_NAME=FastGPT SYSTEM_NAME=FastGPT
SYSTEM_DESCRIPTION= SYSTEM_DESCRIPTION=
SYSTEM_FAVICON=/favicon.ico SYSTEM_FAVICON=/favicon.ico
HOME_URL=/dashboard/apps HOME_URL=/app/list
``` ```
SYSTEM_FAVICON 可以是一个网络地址 SYSTEM_FAVICON 可以是一个网络地址

View File

@@ -1,5 +1,5 @@
--- ---
title: 'V4.9.4' title: 'V4.9.4(进行中)'
description: 'FastGPT V4.9.4 更新说明' description: 'FastGPT V4.9.4 更新说明'
icon: 'upgrade' icon: 'upgrade'
draft: false draft: false
@@ -11,7 +11,7 @@ weight: 796
### 1. 做好数据备份 ### 1. 做好数据备份
### 2. 安装 Redis ### 1. 安装 Redis
* docker 部署的用户,参考最新的 `docker-compose.yml` 文件增加 Redis 配置。增加一个 redis 容器,并配置`fastgpt`,`fastgpt-pro`的环境变量,增加 `REDIS_URL` 环境变量。 * docker 部署的用户,参考最新的 `docker-compose.yml` 文件增加 Redis 配置。增加一个 redis 容器,并配置`fastgpt`,`fastgpt-pro`的环境变量,增加 `REDIS_URL` 环境变量。
* Sealos 部署的用户,在数据库里新建一个`redis`数据库,并复制`内网地址的 connection` 作为 `redis` 的链接串。然后配置`fastgpt`,`fastgpt-pro`的环境变量,增加 `REDIS_URL` 环境变量。 * Sealos 部署的用户,在数据库里新建一个`redis`数据库,并复制`内网地址的 connection` 作为 `redis` 的链接串。然后配置`fastgpt`,`fastgpt-pro`的环境变量,增加 `REDIS_URL` 环境变量。
@@ -20,14 +20,10 @@ weight: 796
| --- | --- | --- | | --- | --- | --- |
| ![](/imgs/sealos-redis1.png) | ![](/imgs/sealos-redis2.png) | ![](/imgs/sealos-redis3.png) | | ![](/imgs/sealos-redis1.png) | ![](/imgs/sealos-redis2.png) | ![](/imgs/sealos-redis3.png) |
### 3. 更新镜像 tag ### 2. 更新镜像 tag
- 更新 FastGPT 镜像 tag: v4.9.4
- 更新 FastGPT 商业版镜像 tag: v4.9.4
- Sandbox 无需更新
- AIProxy 无需更新
### 4. 执行升级脚本 ### 3. 执行升级脚本
该脚本仅需商业版用户执行。 该脚本仅需商业版用户执行。
@@ -49,9 +45,8 @@ curl --location --request POST 'https://{{host}}/api/admin/initv494' \
2. SMTP 发送邮件插件 2. SMTP 发送邮件插件
3. BullMQ 消息队列。 3. BullMQ 消息队列。
4. 利用 redis 进行部分数据缓存。 4. 利用 redis 进行部分数据缓存。
5. 站点同步支持配置训练参数和增量同步 5. 站点同步支持配置训练参数。
6. AI 对话/工具调用,增加返回模型 finish_reason 字段,便于追踪模型输出中断原因 6. AI 对话/工具调用,增加返回模型 finish_reason 字段。
7. 移动端语音输入交互调整
## ⚙️ 优化 ## ⚙️ 优化
@@ -61,6 +56,4 @@ curl --location --request POST 'https://{{host}}/api/admin/initv494' \
## 🐛 修复 ## 🐛 修复
1. 搜索应用/知识库时,无法点击目录进入下一层。 1. 搜索应用/知识库时,无法点击目录进入下一层。
2. 重新训练时,参数未成功初始化。
3. package/service 部分请求在多 app 中不一致。

View File

@@ -1,39 +0,0 @@
---
title: 'V4.9.5'
description: 'FastGPT V4.9.5 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 795
---
## 升级指南
### 1. 做好数据备份
### 2. 更新镜像 tag
- 更新 FastGPT 镜像 tag: v4.9.5
- 更新 FastGPT 商业版镜像 tag: v4.9.5
- Sandbox 无需更新
- AIProxy 无需更新
## 🚀 新增内容
1. 团队成员权限细分,可分别控制是否可创建在根目录应用/知识库以及 API Key
2. 支持交互节点在嵌套工作流中使用。
3. 团队成员操作日志。
4. 用户输入节点支持多选框。
## ⚙️ 优化
1. 繁体中文翻译。
2. Arm 镜像打包
## 🐛 修复
1. password 检测规则错误。
2. 分享链接无法隐藏知识库检索结果。
3. IOS 低版本正则兼容问题。
4. 修复问答提取队列错误后,计数器未清零问题,导致问答提取队列失效。
5. Debug 模式交互节点下一步可能造成死循环。

View File

@@ -1,32 +0,0 @@
---
title: 'V4.9.6(进行中)'
description: 'FastGPT V4.9.6 更新说明'
icon: 'upgrade'
draft: false
toc: true
weight: 794
---
## 🚀 新增内容
1. 以 MCP 方式对外提供应用调用。
2. 支持以 MCP SSE 协议创建工具。
3. 批量执行节点支持交互节点,可实现每一轮循环都人工参与。
4. 增加工作台二级菜单,合并工具箱。
5. 增加 grok3、GPT4.1、Gemini2.5 模型系统配置。
## ⚙️ 优化
1. 工作流数据类型转化鲁棒性和兼容性增强。
2. Python sandbox 代码,支持大数据输入。
3. 路径组件支持配置最后一步是否可点击。
4. 知识库工具调用结果,自动补充图片域名。
5. Github action runner 升级成 unbuntu24
## 🐛 修复
1. 修复子工作流包含交互节点时,未成功恢复子工作流所有数据。
2. completion v1 接口,未接受 interactive 参数,导致 API 调用失败。

View File

@@ -5,207 +5,4 @@ icon: "group"
draft: false draft: false
toc: true toc: true
weight: 450 weight: 450
--- ---
# 团队 & 成员组 & 权限
## 权限系统简介
FastGPT
权限系统融合了基于**属性**和基于**角色**的权限管理范式,为团队协作提供精细化的权限控制方案。通过**成员、部门和群组**三种管理模式,您可以灵活配置对团队、应用和知识库等资源的访问权限。
## 团队
每位用户可以同时归属于多个团队,系统默认为每位用户创建一个初始团队。目前暂不支持用户手动创建额外团队。
## 权限管理
FastGPT 提供三种权限管理维度:
**成员权限**:最高优先级,直接赋予个人的权限
**部门与群组权限**:采用权限并集原则,优先级低于成员权限
权限判定遵循以下逻辑:
首先检查用户的个人成员权限
其次检查用户所属部门和群组的权限(取并集)
最终权限为上述结果的组合
鉴权逻辑如下:
![](/imgs/guide/team_permissions/team_roles_permissions/image1.jpeg)
### 资源权限
对于不同的**资源**,有不同的权限。
这里说的资源,是指应用、知识库、团队等等概念。
下表为不同资源,可以进行管理的权限。
<table>
<thead>
<tr>
<th>资源</th>
<th>可管理权限</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="4">团队</td>
<td>创建应用</td>
<td>创建,删除等基础操作</td>
</tr>
<tr>
<td>创建知识库</td>
<td>创建,删除等基础操作</td>
</tr>
<tr>
<td>创建团队 APIKey</td>
<td>创建,删除等基础操作</td>
</tr>
<tr>
<td>管理成员</td>
<td>邀请、移除用户,创建群组等</td>
</tr>
<tr>
<td rowspan="3">应用</td>
<td>可使用</td>
<td>允许进行对话交互</td>
</tr>
<tr>
<td>可编辑</td>
<td>修改基本信息,进行流程编排等</td>
</tr>
<tr>
<td>可管理</td>
<td>添加或删除协作者</td>
</tr>
<tr>
<td rowspan="3">知识库</td>
<td>可使用</td>
<td>可以在应用中调用该知识库</td>
</tr>
<tr>
<td>可编辑</td>
<td>修改知识库的内容</td>
</tr>
<tr>
<td>可管理</td>
<td>添加或删除协作者</td>
</tr>
</tbody>
</table>
### 协作者
必须先添加**协作者**,才能对其进行权限管理:
![](/imgs/guide/team_permissions/team_roles_permissions/image2.png)
管理团队权限时,需先选择成员/组织/群组,再进行权限配置。
![](/imgs/guide/team_permissions/team_roles_permissions/image3.png)
对于应用和知识库等资源,可直接修改成员权限。
![](/imgs/guide/team_permissions/team_roles_permissions/image4.png)
团队权限在专门的权限页面进行设置
![](/imgs/guide/team_permissions/team_roles_permissions/image5.png)
## 特殊权限说明
### 管理员权限
管理员主要负责管理资源的协作关系,但有以下限制:
- 不能修改或移除自身权限
- 不能修改或移除其他管理员权限
-不能将管理员权限赋予其他协作者
### Owner 权限
每个资源都有唯一的 Owner拥有该资源的最高权限。Owner
可以转移所有权,但转移后原 Owner 将失去对资源的权限。
### Root 权限
Root
作为系统唯一的超级管理员账号,对所有团队的所有资源拥有完全访问和管理权限。
## 使用技巧
### 1. 设置团队默认权限
利用\"全员群组\"可快速为整个团队设置基础权限。例如,为应用设置全员可访问权限。
**注意**:个人成员权限会覆盖全员组权限。例如,应用 A
设置了全员编辑权限,而用户 M 被单独设置为使用权限,则用户 M
只能使用而无法编辑该应用。
### 2. 批量权限管理
通过创建群组或组织,可以高效管理多用户的权限配置。先将用户添加到群组,再对群组整体授权。
### 开发者参考
> 以下内容面向开发者,如不涉及二次开发可跳过。
#### 权限设计原理
FastGPT 权限系统参考 Linux 权限设计,采用二进制方式存储权限位。权限位为
1 表示拥有该权限,为 0 表示无权限。Owner 权限特殊标记为全 1。
#### 权限表
权限信息存储在 MongoDB 的 resource_permissions 集合中,其主要字段包括:
- teamId: 团队标识
- tmbId/groupId/orgId: 权限主体(三选一)
- resourceType: 资源类型(team/app/dataset)
- permission: 权限值(数字)
- resourceId: 资源ID(团队资源为null)
系统通过这一数据结构实现了灵活而精确的权限控制。
对于这个表的 Schema 定义在 packages/service/support/permission/schema.ts
文件中。定义如下:
```typescript
export const ResourcePermissionSchema = new Schema({
teamId: {
type: Schema.Types.ObjectId,
ref: TeamCollectionName
},
tmbId: {
type: Schema.Types.ObjectId,
ref: TeamMemberCollectionName
},
groupId: {
type: Schema.Types.ObjectId,
ref: MemberGroupCollectionName
},
orgId: {
type: Schema.Types.ObjectId,
ref: OrgCollectionName
},
resourceType: {
type: String,
enum: Object.values(PerResourceTypeEnum),
required: true
},
permission: {
type: Number,
required: true
},
// Resrouce ID: App or DataSet or any other resource type.
// It is null if the resourceType is team.
resourceId: {
type: Schema.Types.ObjectId
}
});
```

View File

@@ -7,64 +7,76 @@ toc: true
weight: -10 weight: -10
--- ---
FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,将智能对话与可视化编排完美结合,让 AI 应用开发变得简单自然。无论您是开发者还是业务人员,都能轻松打造专属的 AI 应用。 FastGPT 是一个AI Agent 构建平台,提供开箱即用的数据处理、模型调用等能力,同时可以通过 Flow 可视化进行工作流编排从而实现复杂的应用场景t
{{% alert icon="🤖 " context="success" %}} {{% alert icon="🤖 " context="success" %}}
快速开始体验 FastGPT 在线使用:[https://tryfastgpt.ai](https://tryfastgpt.ai)
- 海外版:[https://tryfastgpt.ai](https://tryfastgpt.ai)
- 国内版:[https://fastgpt.cn](https://fastgpt.cn)
{{% /alert %}} {{% /alert %}}
| | | | | |
| --------------------- | --------------------------------- | | --------------------- | --------------------- |
| ![](/imgs/intro/image1.png) | ![](/imgs/intro/image2.png) | | ![](/imgs/intro1.webp) | ![](/imgs/intro2.png) |
| ![](/imgs/intro3.png) | ![](/imgs/intro4.png) |
# FastGPT 的优势 ## FastGPT 能力
## 1. 简单灵活,像搭积木一样简单 🧱
像搭乐高一样简单有趣FastGPT 提供丰富的功能模块,通过简单拖拽就能搭建出个性化的 AI 应用,零代码也能实现复杂的业务流程。
## 2. 让数据更智能 🧠
FastGPT 提供完整的数据智能化解决方案,从数据导入、预处理到知识匹配,再到智能问答,全流程自动化。配合可视化的工作流设计,轻松打造专业级 AI 应用。
## 3. 开源开放,易于集成 🔗
FastGPT 基于 Apache 2.0 协议开源,支持二次开发。通过标准 API 即可快速接入,无需修改源码。支持 ChatGPT、Claude、DeepSeek 和文心一言等主流模型,持续迭代优化,始终保持产品活力。
--- ### 1. 专属 AI 客服
# FastGPT 能做什么 通过导入文档或已有问答对进行训练,让 AI 模型能根据你的文档以交互式对话方式回答问题。
## 1. 全能知识库
可轻松导入各式各样的文档及数据,能自动对其开展知识结构化处理工作。同时,具备支持多轮上下文理解的智能问答功能,还可为用户带来持续优化的知识库管理体验。
![](/imgs/intro/image3.png)
## 2. 可视化工作流 ![](/imgs/ability1.png)
FastGPT直观的拖拽式界面设计可零代码搭建复杂业务流程。还拥有丰富的功能节点组件能应对多种业务需求有着灵活的流程编排能力按需定制业务流程。
![](/imgs/intro/image4.png)
## 3. 数据智能解析 ### 2. 简单易用的可视化界面
FastGPT知识库系统对导入数据的处理极为灵活可以智能处理PDF文档的复杂结构保留图片、表格和LaTeX公式自动识别扫描文件并将内容结构化为清晰的Markdown格式。同时支持图片自动标注和索引让视觉内容可被理解和检索确保知识在AI问答中能被完整、准确地呈现和应用。
![](/imgs/intro/image5.png) FastGPT 采用直观的可视化界面设计,为各种应用场景提供了丰富实用的功能。通过简洁易懂的操作步骤,可以轻松完成 AI 客服的创建和训练流程。
![](/imgs/ability5.png)
### 3. 自动数据预处理
提供手动输入、直接分段、LLM 自动处理和 CSV 等多种数据导入途径,其中“直接分段”支持通过 PDF、WORD、Markdown 和 CSV 文档内容作为上下文。FastGPT 会自动对文本数据进行预处理、向量化和 QA 分割,节省手动训练时间,提升效能。
![](/imgs/ability2.png)
### 4. 工作流编排
## 4. 工作流编排
基于 Flow 模块的工作流编排,可以帮助你设计更加复杂的问答流程。例如查询数据库、查询库存、预约实验室等。 基于 Flow 模块的工作流编排,可以帮助你设计更加复杂的问答流程。例如查询数据库、查询库存、预约实验室等。
![](/imgs/intro/image6.png) ![](/imgs/ability3.png)
## 5. 强大的 API 集成 ### 5. 强大的 API 集成
FastGPT 完全对齐 OpenAI 官方接口,支持一键接入企业微信、公众号、飞书、钉钉等平台,让 AI 能力轻松融入您的业务场景。
![](/imgs/intro/image7.png) FastGPT 对外的 API 接口对齐了 OpenAI 官方接口,可以直接接入现有的 GPT 应用,也可以轻松集成到企业微信、公众号、飞书等平台。
--- ![](/imgs/ability4.png)
# 核心特性 ## FastGPT 特点
- 开箱即用的知识库系统 1. **项目开源**
- 可视化的低代码工作流编排
- 支持主流大模型
- 简单易用的 API 接口
- 灵活的数据处理能力
--- FastGPT 遵循**附加条件 Apache License 2.0 开源协议**,你可以 [Fork](https://github.com/labring/FastGPT/fork) 之后进行二次开发和发布。FastGPT 社区版将保留核心功能,商业版仅在社区版基础上使用 API 的形式进行扩展,不影响学习使用。
# 知识库核心流程图 2. **独特的 QA 结构**
针对客服问答场景设计的 QA 结构,提高在大量数据场景中的问答准确性。
3. **可视化工作流**
通过 Flow 模块展示了从问题输入到模型输出的完整流程,便于调试和设计复杂流程。
4. **无限扩展**
基于 API 进行扩展,无需修改 FastGPT 源码,也可快速接入现有的程序中。
5. **便于调试**
提供搜索测试、引用修改、完整对话预览等多种调试途径。
6. **支持多种模型**
支持 GPT、Claude、文心一言等多种 LLM 模型,未来也将支持自定义的向量模型。
## 知识库核心流程图
![](/imgs/functional-arch.webp)
![](/imgs/intro/image8.png)

View File

@@ -12,29 +12,27 @@
"previewIcon": "node ./scripts/icon/index.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", "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", "test": "vitest run --exclude 'test/cases/spec'",
"test:all": "vitest run",
"test:workflow": "vitest run workflow" "test:workflow": "vitest run workflow"
}, },
"devDependencies": { "devDependencies": {
"@chakra-ui/cli": "^2.4.1", "@chakra-ui/cli": "^2.4.1",
"@vitest/coverage-v8": "^3.0.9", "@vitest/coverage-v8": "^3.0.2",
"husky": "^8.0.3", "husky": "^8.0.3",
"i18next": "23.16.8", "i18next": "23.16.8",
"lint-staged": "^13.3.0", "lint-staged": "^13.3.0",
"next-i18next": "15.4.2", "next-i18next": "15.4.2",
"prettier": "3.2.4", "prettier": "3.2.4",
"react-i18next": "14.1.2", "react-i18next": "14.1.2",
"vitest": "^3.0.9", "vitest": "^3.0.2",
"mongodb-memory-server": "^10.1.4", "vitest-mongodb": "^1.0.1",
"zhlint": "^0.7.4" "zhlint": "^0.7.4"
}, },
"lint-staged": { "lint-staged": {
"./**/**/*.{ts,tsx,scss}": "npm run format-code", "./**/**/*.{ts,tsx,scss}": "npm run format-code",
"./docSite/**/**/*.md": "npm run format-doc" "./docSite/**/**/*.md": "npm run format-doc"
}, },
"resolutions": {
"mdast-util-gfm-autolink-literal": "2.0.0"
},
"engines": { "engines": {
"node": ">=18.16.0", "node": ">=18.16.0",
"pnpm": ">=9.0.0" "pnpm": ">=9.0.0"

View File

@@ -5,7 +5,6 @@ import { ErrType } from '../errorCode';
const startCode = 507000; const startCode = 507000;
export enum CommonErrEnum { export enum CommonErrEnum {
invalidParams = 'invalidParams', invalidParams = 'invalidParams',
invalidResource = 'invalidResource',
fileNotFound = 'fileNotFound', fileNotFound = 'fileNotFound',
unAuthFile = 'unAuthFile', unAuthFile = 'unAuthFile',
missingParams = 'missingParams', missingParams = 'missingParams',
@@ -16,10 +15,6 @@ const datasetErr = [
statusText: CommonErrEnum.fileNotFound, statusText: CommonErrEnum.fileNotFound,
message: i18nT('common:error.invalid_params') message: i18nT('common:error.invalid_params')
}, },
{
statusText: CommonErrEnum.invalidResource,
message: i18nT('common:error_invalid_resource')
},
{ {
statusText: CommonErrEnum.fileNotFound, statusText: CommonErrEnum.fileNotFound,
message: 'error.fileNotFound' message: 'error.fileNotFound'

View File

@@ -27,8 +27,7 @@ export enum TeamErrEnum {
userNotActive = 'userNotActive', userNotActive = 'userNotActive',
invitationLinkInvalid = 'invitationLinkInvalid', invitationLinkInvalid = 'invitationLinkInvalid',
youHaveBeenInTheTeam = 'youHaveBeenInTheTeam', youHaveBeenInTheTeam = 'youHaveBeenInTheTeam',
tooManyInvitations = 'tooManyInvitations', tooManyInvitations = 'tooManyInvitations'
unPermission = 'unPermission'
} }
const teamErr = [ const teamErr = [
@@ -36,10 +35,6 @@ const teamErr = [
statusText: TeamErrEnum.notUser, statusText: TeamErrEnum.notUser,
message: i18nT('common:code_error.team_error.not_user') message: i18nT('common:code_error.team_error.not_user')
}, },
{
statusText: TeamErrEnum.unPermission,
message: i18nT('common:error_un_permission')
},
{ {
statusText: TeamErrEnum.teamOverSize, statusText: TeamErrEnum.teamOverSize,
message: i18nT('common:code_error.team_error.over_size') message: i18nT('common:code_error.team_error.over_size')

View File

@@ -49,7 +49,6 @@ export type FastGPTFeConfigsType = {
find_password_method?: ['email' | 'phone']; find_password_method?: ['email' | 'phone'];
bind_notification_method?: ['email' | 'phone']; bind_notification_method?: ['email' | 'phone'];
googleClientVerKey?: string; googleClientVerKey?: string;
mcpServerProxyEndpoint?: string;
show_emptyChat?: boolean; show_emptyChat?: boolean;
show_appStore?: boolean; show_appStore?: boolean;

View File

@@ -11,9 +11,7 @@ export enum AppTypeEnum {
simple = 'simple', simple = 'simple',
workflow = 'advanced', workflow = 'advanced',
plugin = 'plugin', plugin = 'plugin',
httpPlugin = 'httpPlugin', httpPlugin = 'httpPlugin'
toolSet = 'toolSet',
tool = 'tool'
} }
export const AppFolderTypeList = [AppTypeEnum.folder, AppTypeEnum.httpPlugin]; export const AppFolderTypeList = [AppTypeEnum.folder, AppTypeEnum.httpPlugin];
@@ -55,10 +53,7 @@ export enum AppTemplateTypeEnum {
imageGeneration = 'image-generation', imageGeneration = 'image-generation',
webSearch = 'web-search', webSearch = 'web-search',
roleplay = 'roleplay', roleplay = 'roleplay',
officeServices = 'office-services', officeServices = 'office-services'
// special type
contribute = 'contribute'
} }
export const defaultDatasetMaxTokens = 16000; export const defaultDatasetMaxTokens = 16000;

View File

@@ -1,97 +0,0 @@
import { NodeOutputKeyEnum, WorkflowIOValueTypeEnum } from '../../workflow/constants';
import {
FlowNodeInputTypeEnum,
FlowNodeOutputTypeEnum,
FlowNodeTypeEnum
} from '../../workflow/node/constant';
import { nanoid } from 'nanoid';
import { ToolType } from '../type';
import { i18nT } from '../../../../web/i18n/utils';
import { RuntimeNodeItemType } from '../../workflow/runtime/type';
export const getMCPToolSetRuntimeNode = ({
url,
toolList,
name,
avatar
}: {
url: string;
toolList: ToolType[];
name?: string;
avatar?: string;
}): RuntimeNodeItemType => {
return {
nodeId: nanoid(16),
flowNodeType: FlowNodeTypeEnum.toolSet,
avatar,
intro: 'MCP Tools',
inputs: [
{
key: 'toolSetData',
label: 'Tool Set Data',
valueType: WorkflowIOValueTypeEnum.object,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
value: { url, toolList }
}
],
outputs: [],
name: name || '',
version: ''
};
};
export const getMCPToolRuntimeNode = ({
tool,
url,
avatar = 'core/app/type/mcpToolsFill'
}: {
tool: ToolType;
url: string;
avatar?: string;
}): RuntimeNodeItemType => {
return {
nodeId: nanoid(16),
flowNodeType: FlowNodeTypeEnum.tool,
avatar,
intro: tool.description,
inputs: [
{
key: 'toolData',
label: 'Tool Data',
valueType: WorkflowIOValueTypeEnum.object,
renderTypeList: [FlowNodeInputTypeEnum.hidden],
value: { ...tool, url }
},
...Object.entries(tool.inputSchema?.properties || {}).map(([key, value]) => ({
key,
label: key,
valueType: value.type as WorkflowIOValueTypeEnum,
description: value.description,
toolDescription: value.description || key,
required: tool.inputSchema?.required?.includes(key) || false,
renderTypeList: [
value.type === 'string'
? FlowNodeInputTypeEnum.input
: value.type === 'number'
? FlowNodeInputTypeEnum.numberInput
: value.type === 'boolean'
? FlowNodeInputTypeEnum.switch
: FlowNodeInputTypeEnum.JSONEditor
]
}))
],
outputs: [
{
id: NodeOutputKeyEnum.rawResponse,
key: NodeOutputKeyEnum.rawResponse,
required: true,
label: i18nT('workflow:raw_response'),
description: i18nT('workflow:tool_raw_response_description'),
valueType: WorkflowIOValueTypeEnum.any,
type: FlowNodeOutputTypeEnum.static
}
],
name: tool.name,
version: ''
};
};

View File

@@ -16,16 +16,6 @@ import { FlowNodeInputTypeEnum } from '../../core/workflow/node/constant';
import { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type'; import { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/type';
import { SourceMemberType } from '../../support/user/type'; import { SourceMemberType } from '../../support/user/type';
export type ToolType = {
name: string;
description: string;
inputSchema: {
type: string;
properties?: Record<string, { type: string; description?: string }>;
required?: string[];
};
};
export type AppSchema = { export type AppSchema = {
_id: string; _id: string;
parentId?: ParentIdType; parentId?: ParentIdType;

View File

@@ -140,9 +140,7 @@ export const appWorkflow2Form = ({
); );
} else if ( } else if (
node.flowNodeType === FlowNodeTypeEnum.pluginModule || node.flowNodeType === FlowNodeTypeEnum.pluginModule ||
node.flowNodeType === FlowNodeTypeEnum.appModule || node.flowNodeType === FlowNodeTypeEnum.appModule
node.flowNodeType === FlowNodeTypeEnum.tool ||
node.flowNodeType === FlowNodeTypeEnum.toolSet
) { ) {
if (!node.pluginId) return; if (!node.pluginId) return;

View File

@@ -38,8 +38,7 @@ export enum ChatSourceEnum {
team = 'team', team = 'team',
feishu = 'feishu', feishu = 'feishu',
official_account = 'official_account', official_account = 'official_account',
wecom = 'wecom', wecom = 'wecom'
mcp = 'mcp'
} }
export const ChatSourceMap = { export const ChatSourceMap = {
@@ -69,9 +68,6 @@ export const ChatSourceMap = {
}, },
[ChatSourceEnum.wecom]: { [ChatSourceEnum.wecom]: {
name: i18nT('common:core.chat.logs.wecom') name: i18nT('common:core.chat.logs.wecom')
},
[ChatSourceEnum.mcp]: {
name: i18nT('common:core.chat.logs.mcp')
} }
}; };

View File

@@ -77,13 +77,6 @@ export const getHistoryPreview = (
}); });
}; };
export const filterModuleTypeList: any[] = [
FlowNodeTypeEnum.pluginModule,
FlowNodeTypeEnum.datasetSearchNode,
FlowNodeTypeEnum.tools,
FlowNodeTypeEnum.pluginOutput
];
export const filterPublicNodeResponseData = ({ export const filterPublicNodeResponseData = ({
flowResponses = [], flowResponses = [],
responseDetail = false responseDetail = false
@@ -94,6 +87,12 @@ export const filterPublicNodeResponseData = ({
const filedList = responseDetail const filedList = responseDetail
? ['quoteList', 'moduleType', 'pluginOutput', 'runningTime'] ? ['quoteList', 'moduleType', 'pluginOutput', 'runningTime']
: ['moduleType', 'pluginOutput', 'runningTime']; : ['moduleType', 'pluginOutput', 'runningTime'];
const filterModuleTypeList: any[] = [
FlowNodeTypeEnum.pluginModule,
FlowNodeTypeEnum.datasetSearchNode,
FlowNodeTypeEnum.tools,
FlowNodeTypeEnum.pluginOutput
];
return flowResponses return flowResponses
.filter((item) => filterModuleTypeList.includes(item.moduleType)) .filter((item) => filterModuleTypeList.includes(item.moduleType))
@@ -154,55 +153,25 @@ export const getChatSourceByPublishChannel = (publishChannel: PublishChannelEnum
/* /*
Merge chat responseData Merge chat responseData
1. Same tool mergeSignId (Interactive tool node) 1. Same tool mergeSignId (Interactive tool node)
2. Recursively merge plugin details with same mergeSignId
*/ */
export const mergeChatResponseData = ( export const mergeChatResponseData = (responseDataList: ChatHistoryItemResType[]) => {
responseDataList: ChatHistoryItemResType[]
): ChatHistoryItemResType[] => {
// Merge children reponse data(Children has interactive response)
const responseWithMergedPlugins = responseDataList.map((item) => {
if (item.pluginDetail && item.pluginDetail.length > 1) {
return {
...item,
pluginDetail: mergeChatResponseData(item.pluginDetail)
};
}
return item;
});
let lastResponse: ChatHistoryItemResType | undefined = undefined; let lastResponse: ChatHistoryItemResType | undefined = undefined;
let hasMerged = false;
const firstPassResult = responseWithMergedPlugins.reduce<ChatHistoryItemResType[]>( return responseDataList.reduce<ChatHistoryItemResType[]>((acc, curr) => {
(acc, curr) => { if (lastResponse && lastResponse.mergeSignId && curr.mergeSignId === lastResponse.mergeSignId) {
if ( // 替换 lastResponse
lastResponse && const concatResponse: ChatHistoryItemResType = {
lastResponse.mergeSignId && ...curr,
curr.mergeSignId === lastResponse.mergeSignId runningTime: +((lastResponse.runningTime || 0) + (curr.runningTime || 0)).toFixed(2),
) { totalPoints: (lastResponse.totalPoints || 0) + (curr.totalPoints || 0),
const concatResponse: ChatHistoryItemResType = { childTotalPoints: (lastResponse.childTotalPoints || 0) + (curr.childTotalPoints || 0),
...curr, toolCallTokens: (lastResponse.toolCallTokens || 0) + (curr.toolCallTokens || 0),
runningTime: +((lastResponse.runningTime || 0) + (curr.runningTime || 0)).toFixed(2), toolDetail: [...(lastResponse.toolDetail || []), ...(curr.toolDetail || [])]
totalPoints: (lastResponse.totalPoints || 0) + (curr.totalPoints || 0), };
childTotalPoints: (lastResponse.childTotalPoints || 0) + (curr.childTotalPoints || 0), return [...acc.slice(0, -1), concatResponse];
toolCallTokens: (lastResponse.toolCallTokens || 0) + (curr.toolCallTokens || 0), } else {
toolDetail: [...(lastResponse.toolDetail || []), ...(curr.toolDetail || [])], lastResponse = curr;
loopDetail: [...(lastResponse.loopDetail || []), ...(curr.loopDetail || [])], return [...acc, curr];
pluginDetail: [...(lastResponse.pluginDetail || []), ...(curr.pluginDetail || [])] }
}; }, []);
hasMerged = true;
return [...acc.slice(0, -1), concatResponse];
} else {
lastResponse = curr;
return [...acc, curr];
}
},
[]
);
if (hasMerged && firstPassResult.length > 1) {
return mergeChatResponseData(firstPassResult);
}
return firstPassResult;
}; };

View File

@@ -51,8 +51,7 @@ export const DatasetTypeMap = {
export enum DatasetStatusEnum { export enum DatasetStatusEnum {
active = 'active', active = 'active',
syncing = 'syncing', syncing = 'syncing',
waiting = 'waiting', waiting = 'waiting'
error = 'error'
} }
export const DatasetStatusMap = { export const DatasetStatusMap = {
[DatasetStatusEnum.active]: { [DatasetStatusEnum.active]: {
@@ -63,9 +62,6 @@ export const DatasetStatusMap = {
}, },
[DatasetStatusEnum.waiting]: { [DatasetStatusEnum.waiting]: {
label: i18nT('common:core.dataset.status.waiting') label: i18nT('common:core.dataset.status.waiting')
},
[DatasetStatusEnum.error]: {
label: i18nT('dataset:status_error')
} }
}; };

View File

@@ -209,7 +209,6 @@ export type DatasetListItemType = {
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel' | 'vlmModel'> & { export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel' | 'vlmModel'> & {
status: `${DatasetStatusEnum}`; status: `${DatasetStatusEnum}`;
errorMsg?: string;
vectorModel: EmbeddingModelItemType; vectorModel: EmbeddingModelItemType;
agentModel: LLMModelItemType; agentModel: LLMModelItemType;
vlmModel?: LLMModelItemType; vlmModel?: LLMModelItemType;

View File

@@ -7,7 +7,6 @@ export enum FlowNodeInputTypeEnum { // render ui
numberInput = 'numberInput', numberInput = 'numberInput',
switch = 'switch', // true/false switch = 'switch', // true/false
select = 'select', select = 'select',
multipleSelect = 'multipleSelect',
// editor // editor
JSONEditor = 'JSONEditor', JSONEditor = 'JSONEditor',
@@ -47,9 +46,6 @@ export const FlowNodeInputMap: Record<
[FlowNodeInputTypeEnum.select]: { [FlowNodeInputTypeEnum.select]: {
icon: 'core/workflow/inputType/option' icon: 'core/workflow/inputType/option'
}, },
[FlowNodeInputTypeEnum.multipleSelect]: {
icon: 'core/workflow/inputType/option'
},
[FlowNodeInputTypeEnum.switch]: { [FlowNodeInputTypeEnum.switch]: {
icon: 'core/workflow/inputType/switch' icon: 'core/workflow/inputType/switch'
}, },
@@ -140,9 +136,7 @@ export enum FlowNodeTypeEnum {
loopStart = 'loopStart', loopStart = 'loopStart',
loopEnd = 'loopEnd', loopEnd = 'loopEnd',
formInput = 'formInput', formInput = 'formInput',
comment = 'comment', comment = 'comment'
tool = 'tool',
toolSet = 'toolSet'
} }
// node IO value type // node IO value type

View File

@@ -23,7 +23,7 @@ import { WorkflowResponseType } from '../../../../service/core/workflow/dispatch
import { AiChatQuoteRoleType } from '../template/system/aiChat/type'; import { AiChatQuoteRoleType } from '../template/system/aiChat/type';
import { LafAccountType, OpenaiAccountType } from '../../../support/user/team/type'; import { LafAccountType, OpenaiAccountType } from '../../../support/user/team/type';
import { CompletionFinishReason } from '../../ai/type'; import { CompletionFinishReason } from '../../ai/type';
import { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
export type ExternalProviderType = { export type ExternalProviderType = {
openaiAccount?: OpenaiAccountType; openaiAccount?: OpenaiAccountType;
externalWorkflowVariables?: Record<string, string>; externalWorkflowVariables?: Record<string, string>;
@@ -55,14 +55,12 @@ export type ChatDispatchProps = {
variables: Record<string, any>; // global variable variables: Record<string, any>; // global variable
query: UserChatItemValueItemType[]; // trigger query query: UserChatItemValueItemType[]; // trigger query
chatConfig: AppSchema['chatConfig']; chatConfig: AppSchema['chatConfig'];
lastInteractive?: WorkflowInteractiveResponseType; // last interactive response
stream: boolean; stream: boolean;
maxRunTimes: number; maxRunTimes: number;
isToolCall?: boolean; isToolCall?: boolean;
workflowStreamResponse?: WorkflowResponseType; workflowStreamResponse?: WorkflowResponseType;
workflowDispatchDeep?: number; workflowDispatchDeep?: number;
version?: 'v1' | 'v2'; version?: 'v1' | 'v2';
responseDetail?: boolean;
}; };
export type ModuleDispatchProps<T> = ChatDispatchProps & { export type ModuleDispatchProps<T> = ChatDispatchProps & {
@@ -217,8 +215,6 @@ export type DispatchNodeResponseType = {
// tool params // tool params
toolParamsResult?: Record<string, any>; toolParamsResult?: Record<string, any>;
toolRes?: any;
// abandon // abandon
extensionModel?: string; extensionModel?: string;
extensionResult?: string; extensionResult?: string;

View File

@@ -10,23 +10,7 @@ import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type'; import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants'; import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
import { replaceVariable, valToStr } from '../../../common/string/tools'; import { replaceVariable, valToStr } from '../../../common/string/tools';
import json5 from 'json5';
import {
InteractiveNodeResponseType,
WorkflowInteractiveResponseType
} from '../template/system/interactive/type';
export const extractDeepestInteractive = (
interactive: WorkflowInteractiveResponseType
): WorkflowInteractiveResponseType => {
if (
(interactive?.type === 'childrenInteractive' || interactive?.type === 'loopInteractive') &&
interactive.params?.childrenResponse
) {
return extractDeepestInteractive(interactive.params.childrenResponse);
}
return interactive;
};
export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => { export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number => {
let limit = 10; let limit = 10;
nodes.forEach((node) => { nodes.forEach((node) => {
@@ -44,122 +28,13 @@ export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number
return limit * 2; return limit * 2;
}; };
/* value type format */
export const valueTypeFormat = (value: any, type?: WorkflowIOValueTypeEnum) => {
const isObjectString = (value: any) => {
if (typeof value === 'string' && value !== 'false' && value !== 'true') {
const trimmedValue = value.trim();
const isJsonString =
(trimmedValue.startsWith('{') && trimmedValue.endsWith('}')) ||
(trimmedValue.startsWith('[') && trimmedValue.endsWith(']'));
return isJsonString;
}
return false;
};
// 1. any值忽略格式化
if (value === undefined || value === null) return value;
if (!type || type === WorkflowIOValueTypeEnum.any) return value;
// 2. 如果值已经符合目标类型,直接返回
if (
(type === WorkflowIOValueTypeEnum.string && typeof value === 'string') ||
(type === WorkflowIOValueTypeEnum.number && typeof value === 'number') ||
(type === WorkflowIOValueTypeEnum.boolean && typeof value === 'boolean') ||
(type.startsWith('array') && Array.isArray(value)) ||
(type === WorkflowIOValueTypeEnum.object && typeof value === 'object') ||
(type === WorkflowIOValueTypeEnum.chatHistory &&
(Array.isArray(value) || typeof value === 'number')) ||
(type === WorkflowIOValueTypeEnum.datasetQuote && Array.isArray(value)) ||
(type === WorkflowIOValueTypeEnum.selectDataset && Array.isArray(value)) ||
(type === WorkflowIOValueTypeEnum.selectApp && typeof value === 'object')
) {
return value;
}
// 4. 按目标类型,进行格式转化
// 4.1 基本类型转换
if (type === WorkflowIOValueTypeEnum.string) {
return typeof value === 'object' ? JSON.stringify(value) : String(value);
}
if (type === WorkflowIOValueTypeEnum.number) {
return Number(value);
}
if (type === WorkflowIOValueTypeEnum.boolean) {
if (typeof value === 'string') {
return value.toLowerCase() === 'true';
}
return Boolean(value);
}
// 4.3 字符串转对象
if (
(type === WorkflowIOValueTypeEnum.object || type.startsWith('array')) &&
typeof value === 'string' &&
value.trim()
) {
const trimmedValue = value.trim();
const isJsonString = isObjectString(trimmedValue);
if (isJsonString) {
try {
const parsed = json5.parse(trimmedValue);
// 检测解析结果与目标类型是否一致
if (type.startsWith('array') && Array.isArray(parsed)) return parsed;
if (type === WorkflowIOValueTypeEnum.object && typeof parsed === 'object') return parsed;
} catch (error) {}
}
}
// 4.4 数组类型(这里 value 不是数组类型)TODO: 嵌套数据类型转化)
if (type.startsWith('array')) {
return [value];
}
// 4.5 特殊类型处理
if (
[WorkflowIOValueTypeEnum.datasetQuote, WorkflowIOValueTypeEnum.selectDataset].includes(type)
) {
if (isObjectString(value)) {
try {
return json5.parse(value);
} catch (error) {
return [];
}
}
return [];
}
if (
[WorkflowIOValueTypeEnum.selectApp, WorkflowIOValueTypeEnum.object].includes(type) &&
typeof value === 'string'
) {
if (isObjectString(value)) {
try {
return json5.parse(value);
} catch (error) {
return {};
}
}
return {};
}
// Invalid history type
if (type === WorkflowIOValueTypeEnum.chatHistory) {
return 0;
}
// 5. 默认返回原值
return value;
};
/* /*
Get interaction information (if any) from the last AI message. Get interaction information (if any) from the last AI message.
What can be done: What can be done:
1. Get the interactive data 1. Get the interactive data
2. Check that the workflow starts at the interaction node 2. Check that the workflow starts at the interaction node
*/ */
export const getLastInteractiveValue = ( export const getLastInteractiveValue = (histories: ChatItemType[]) => {
histories: ChatItemType[]
): WorkflowInteractiveResponseType | undefined => {
const lastAIMessage = [...histories].reverse().find((item) => item.obj === ChatRoleEnum.AI); const lastAIMessage = [...histories].reverse().find((item) => item.obj === ChatRoleEnum.AI);
if (lastAIMessage) { if (lastAIMessage) {
@@ -170,14 +45,7 @@ export const getLastInteractiveValue = (
lastValue.type !== ChatItemValueTypeEnum.interactive || lastValue.type !== ChatItemValueTypeEnum.interactive ||
!lastValue.interactive !lastValue.interactive
) { ) {
return; return null;
}
if (
lastValue.interactive.type === 'childrenInteractive' ||
lastValue.interactive.type === 'loopInteractive'
) {
return lastValue.interactive;
} }
// Check is user select // Check is user select
@@ -194,29 +62,38 @@ export const getLastInteractiveValue = (
} }
} }
return; return null;
}; };
export const storeEdges2RuntimeEdges = ( export const initWorkflowEdgeStatus = (
edges: StoreEdgeItemType[], edges: StoreEdgeItemType[] | RuntimeEdgeItemType[],
lastInteractive?: WorkflowInteractiveResponseType histories?: ChatItemType[]
): RuntimeEdgeItemType[] => { ): RuntimeEdgeItemType[] => {
if (lastInteractive) { // If there is a history, use the last interactive value
const memoryEdges = lastInteractive.memoryEdges || []; if (histories && histories.length > 0) {
const memoryEdges = getLastInteractiveValue(histories)?.memoryEdges;
if (memoryEdges && memoryEdges.length > 0) { if (memoryEdges && memoryEdges.length > 0) {
return memoryEdges; return memoryEdges;
} }
} }
return edges?.map((edge) => ({ ...edge, status: 'waiting' })) || []; return (
edges?.map((edge) => ({
...edge,
status: 'waiting'
})) || []
);
}; };
export const getWorkflowEntryNodeIds = ( export const getWorkflowEntryNodeIds = (
nodes: (StoreNodeItemType | RuntimeNodeItemType)[], nodes: (StoreNodeItemType | RuntimeNodeItemType)[],
lastInteractive?: WorkflowInteractiveResponseType histories?: ChatItemType[]
) => { ) => {
if (lastInteractive) { // If there is a history, use the last interactive entry node
const entryNodeIds = lastInteractive.entryNodeIds || []; if (histories && histories.length > 0) {
const entryNodeIds = getLastInteractiveValue(histories)?.entryNodeIds;
if (Array.isArray(entryNodeIds) && entryNodeIds.length > 0) { if (Array.isArray(entryNodeIds) && entryNodeIds.length > 0) {
return entryNodeIds; return entryNodeIds;
} }
@@ -228,12 +105,7 @@ export const getWorkflowEntryNodeIds = (
FlowNodeTypeEnum.pluginInput FlowNodeTypeEnum.pluginInput
]; ];
return nodes return nodes
.filter( .filter((node) => entryList.includes(node.flowNodeType as any))
(node) =>
entryList.includes(node.flowNodeType as any) ||
(!nodes.some((item) => entryList.includes(item.flowNodeType as any)) &&
node.flowNodeType === FlowNodeTypeEnum.tool)
)
.map((item) => item.nodeId); .map((item) => item.nodeId);
}; };
@@ -431,6 +303,7 @@ export const formatVariableValByType = (val: any, valueType?: WorkflowIOValueTyp
if ( if (
[ [
WorkflowIOValueTypeEnum.object, WorkflowIOValueTypeEnum.object,
WorkflowIOValueTypeEnum.chatHistory,
WorkflowIOValueTypeEnum.datasetQuote, WorkflowIOValueTypeEnum.datasetQuote,
WorkflowIOValueTypeEnum.selectApp, WorkflowIOValueTypeEnum.selectApp,
WorkflowIOValueTypeEnum.selectDataset WorkflowIOValueTypeEnum.selectDataset
@@ -523,10 +396,10 @@ export const textAdaptGptResponse = ({
/* Update runtimeNode's outputs with interactive data from history */ /* Update runtimeNode's outputs with interactive data from history */
export function rewriteNodeOutputByHistories( export function rewriteNodeOutputByHistories(
runtimeNodes: RuntimeNodeItemType[], histories: ChatItemType[],
lastInteractive?: InteractiveNodeResponseType runtimeNodes: RuntimeNodeItemType[]
) { ) {
const interactive = lastInteractive; const interactive = getLastInteractiveValue(histories);
if (!interactive?.nodeOutputs) { if (!interactive?.nodeOutputs) {
return runtimeNodes; return runtimeNodes;
} }

View File

@@ -34,8 +34,6 @@ import { LoopStartNode } from './system/loop/loopStart';
import { LoopEndNode } from './system/loop/loopEnd'; import { LoopEndNode } from './system/loop/loopEnd';
import { FormInputNode } from './system/interactive/formInput'; import { FormInputNode } from './system/interactive/formInput';
import { ToolParamsNode } from './system/toolParams'; import { ToolParamsNode } from './system/toolParams';
import { RunToolNode } from './system/runTool';
import { RunToolSetNode } from './system/runToolSet';
const systemNodes: FlowNodeTemplateType[] = [ const systemNodes: FlowNodeTemplateType[] = [
AiChatModule, AiChatModule,
@@ -86,7 +84,5 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
RunAppNode, RunAppNode,
RunAppModule, RunAppModule,
LoopStartNode, LoopStartNode,
LoopEndNode, LoopEndNode
RunToolNode,
RunToolSetNode
]; ];

View File

@@ -8,7 +8,7 @@ import { i18nT } from '../../../../web/i18n/utils';
export const Input_Template_History: FlowNodeInputItemType = { export const Input_Template_History: FlowNodeInputItemType = {
key: NodeInputKeyEnum.history, key: NodeInputKeyEnum.history,
renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference], renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference],
valueType: WorkflowIOValueTypeEnum.chatHistory, // Array / Number valueType: WorkflowIOValueTypeEnum.chatHistory,
label: i18nT('common:core.module.input.label.chat history'), label: i18nT('common:core.module.input.label.chat history'),
description: i18nT('workflow:max_dialog_rounds'), description: i18nT('workflow:max_dialog_rounds'),

View File

@@ -1,5 +1,6 @@
import type { NodeOutputItemType } from '../../../../chat/type'; import type { NodeOutputItemType } from '../../../../chat/type';
import type { FlowNodeOutputItemType } from '../../../type/io'; import type { FlowNodeOutputItemType } from '../../../type/io';
import type { RuntimeEdgeItemType } from '../../../runtime/type';
import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant'; import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant';
import { WorkflowIOValueTypeEnum } from 'core/workflow/constants'; import { WorkflowIOValueTypeEnum } from 'core/workflow/constants';
import type { ChatCompletionMessageParam } from '../../../../ai/type'; import type { ChatCompletionMessageParam } from '../../../../ai/type';
@@ -8,6 +9,7 @@ type InteractiveBasicType = {
entryNodeIds: string[]; entryNodeIds: string[];
memoryEdges: RuntimeEdgeItemType[]; memoryEdges: RuntimeEdgeItemType[];
nodeOutputs: NodeOutputItemType[]; nodeOutputs: NodeOutputItemType[];
toolParams?: { toolParams?: {
entryNodeIds: string[]; // 记录工具中,交互节点的 Id而不是起始工作流的入口 entryNodeIds: string[]; // 记录工具中,交互节点的 Id而不是起始工作流的入口
memoryMessages: ChatCompletionMessageParam[]; // 这轮工具中,产生的新的 messages memoryMessages: ChatCompletionMessageParam[]; // 这轮工具中,产生的新的 messages
@@ -21,22 +23,6 @@ type InteractiveNodeType = {
nodeOutputs?: NodeOutputItemType[]; nodeOutputs?: NodeOutputItemType[];
}; };
type ChildrenInteractive = InteractiveNodeType & {
type: 'childrenInteractive';
params: {
childrenResponse?: WorkflowInteractiveResponseType;
};
};
type LoopInteractive = InteractiveNodeType & {
type: 'loopInteractive';
params: {
loopResult: any[];
childrenResponse: WorkflowInteractiveResponseType;
currentIndex: number;
};
};
export type UserSelectOptionItemType = { export type UserSelectOptionItemType = {
key: string; key: string;
value: string; value: string;
@@ -76,11 +62,5 @@ type UserInputInteractive = InteractiveNodeType & {
submitted?: boolean; submitted?: boolean;
}; };
}; };
export type InteractiveNodeResponseType = UserSelectInteractive | UserInputInteractive;
export type InteractiveNodeResponseType =
| UserSelectInteractive
| UserInputInteractive
| ChildrenInteractive
| LoopInteractive;
export type WorkflowInteractiveResponseType = InteractiveBasicType & InteractiveNodeResponseType; export type WorkflowInteractiveResponseType = InteractiveBasicType & InteractiveNodeResponseType;

View File

@@ -1,19 +0,0 @@
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { FlowNodeTypeEnum } from '../../node/constant';
import { FlowNodeTemplateType } from '../../type/node';
import { getHandleConfig } from '../utils';
export const RunToolNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.tool,
templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.tool,
sourceHandle: getHandleConfig(true, true, true, true),
targetHandle: getHandleConfig(true, true, true, true),
intro: '',
name: '',
showStatus: false,
isTool: true,
version: '4.9.6',
inputs: [],
outputs: []
};

View File

@@ -1,19 +0,0 @@
import { FlowNodeTemplateTypeEnum } from '../../constants';
import { FlowNodeTypeEnum } from '../../node/constant';
import { FlowNodeTemplateType } from '../../type/node';
import { getHandleConfig } from '../utils';
export const RunToolSetNode: FlowNodeTemplateType = {
id: FlowNodeTypeEnum.toolSet,
templateType: FlowNodeTemplateTypeEnum.other,
flowNodeType: FlowNodeTypeEnum.toolSet,
sourceHandle: getHandleConfig(false, false, false, false),
targetHandle: getHandleConfig(false, false, false, false),
intro: '',
name: '',
showStatus: false,
isTool: true,
version: '4.9.6',
inputs: [],
outputs: []
};

View File

@@ -311,38 +311,6 @@ export const appData2FlowNodeIO = ({
}; };
}; };
export const toolData2FlowNodeIO = ({
nodes
}: {
nodes: StoreNodeItemType[];
}): {
inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[];
} => {
const toolNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.tool);
return {
inputs: toolNode?.inputs || [],
outputs: toolNode?.outputs || []
};
};
export const toolSetData2FlowNodeIO = ({
nodes
}: {
nodes: StoreNodeItemType[];
}): {
inputs: FlowNodeInputItemType[];
outputs: FlowNodeOutputItemType[];
} => {
const toolSetNode = nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.toolSet);
return {
inputs: toolSetNode?.inputs || [],
outputs: toolSetNode?.outputs || []
};
};
export const formatEditorVariablePickerIcon = ( export const formatEditorVariablePickerIcon = (
variables: { key: string; label: string; type?: `${VariableInputEnum}`; required?: boolean }[] variables: { key: string; label: string; type?: `${VariableInputEnum}`; required?: boolean }[]
): EditorVariablePickerType[] => { ): EditorVariablePickerType[] => {

View File

@@ -1,14 +0,0 @@
export type McpKeyType = {
_id: string;
key: string;
teamId: string;
tmbId: string;
apps: McpAppType[];
name: string;
};
export type McpAppType = {
appId: string;
toolName: string;
description: string;
};

View File

@@ -1,14 +0,0 @@
export enum OperationLogEventEnum {
LOGIN = 'LOGIN',
CREATE_INVITATION_LINK = 'CREATE_INVITATION_LINK',
JOIN_TEAM = 'JOIN_TEAM',
CHANGE_MEMBER_NAME = 'CHANGE_MEMBER_NAME',
KICK_OUT_TEAM = 'KICK_OUT_TEAM',
CREATE_DEPARTMENT = 'CREATE_DEPARTMENT',
CHANGE_DEPARTMENT = 'CHANGE_DEPARTMENT',
DELETE_DEPARTMENT = 'DELETE_DEPARTMENT',
RELOCATE_DEPARTMENT = 'RELOCATE_DEPARTMENT',
CREATE_GROUP = 'CREATE_GROUP',
DELETE_GROUP = 'DELETE_GROUP',
ASSIGN_PERMISSION = 'ASSIGN_PERMISSION'
}

View File

@@ -1,19 +0,0 @@
import { SourceMemberType } from '../user/type';
import { OperationLogEventEnum } from './constants';
export type OperationLogSchema = {
_id: string;
tmbId: string;
teamId: string;
timestamp: Date;
event: `${OperationLogEventEnum}`;
metadata?: Record<string, string>;
};
export type OperationListItemType = {
_id: string;
sourceMember: SourceMemberType;
event: `${OperationLogEventEnum}`;
timestamp: Date;
metadata: Record<string, string>;
};

View File

@@ -13,15 +13,12 @@ export type CollaboratorItemType = {
orgId: string; orgId: string;
}>; }>;
export type UpdateClbPermissionProps<addOnly = false> = { export type UpdateClbPermissionProps = {
members?: string[]; members?: string[];
groups?: string[]; groups?: string[];
orgs?: string[]; orgs?: string[];
} & (addOnly extends true permission: PermissionValueType;
? {} };
: {
permission: PermissionValueType;
});
export type DeletePermissionQuery = RequireOnlyOne<{ export type DeletePermissionQuery = RequireOnlyOne<{
tmbId?: string; tmbId?: string;

View File

@@ -5,16 +5,15 @@ export type PerConstructPros = {
per?: PermissionValueType; per?: PermissionValueType;
isOwner?: boolean; isOwner?: boolean;
permissionList?: PermissionListType; permissionList?: PermissionListType;
childUpdatePermissionCallback?: () => void;
}; };
// the Permission helper class // the Permission helper class
export class Permission { export class Permission {
value: PermissionValueType; value: PermissionValueType;
isOwner: boolean = false; isOwner: boolean;
hasManagePer: boolean = false; hasManagePer: boolean;
hasWritePer: boolean = false; hasWritePer: boolean;
hasReadPer: boolean = false; hasReadPer: boolean;
_permissionList: PermissionListType; _permissionList: PermissionListType;
constructor(props?: PerConstructPros) { constructor(props?: PerConstructPros) {
@@ -25,8 +24,11 @@ export class Permission {
this.value = per; this.value = per;
} }
this.isOwner = isOwner;
this._permissionList = permissionList; this._permissionList = permissionList;
this.updatePermissions(); this.hasManagePer = this.checkPer(this._permissionList['manage'].value);
this.hasWritePer = this.checkPer(this._permissionList['write'].value);
this.hasReadPer = this.checkPer(this._permissionList['read'].value);
} }
// add permission(s) // add permission(s)
@@ -66,21 +68,10 @@ export class Permission {
return (this.value & perm) === perm; return (this.value & perm) === perm;
} }
private updatePermissionCallback?: () => void;
setUpdatePermissionCallback(callback: () => void) {
callback();
this.updatePermissionCallback = callback;
}
private updatePermissions() { private updatePermissions() {
this.isOwner = this.value === OwnerPermissionVal; this.isOwner = this.value === OwnerPermissionVal;
this.hasManagePer = this.checkPer(this._permissionList['manage'].value); this.hasManagePer = this.checkPer(this._permissionList['manage'].value);
this.hasWritePer = this.checkPer(this._permissionList['write'].value); this.hasWritePer = this.checkPer(this._permissionList['write'].value);
this.hasReadPer = this.checkPer(this._permissionList['read'].value); this.hasReadPer = this.checkPer(this._permissionList['read'].value);
this.updatePermissionCallback?.();
}
toBinary() {
return this.value.toString(2);
} }
} }

View File

@@ -17,23 +17,23 @@ type GroupMemberSchemaType = {
role: `${GroupMemberRole}`; role: `${GroupMemberRole}`;
}; };
type MemberGroupListItemType<WithMembers extends boolean | undefined> = MemberGroupSchemaType & { type MemberGroupListItemType<T extends boolean | undefined> = MemberGroupSchemaType & {
members: WithMembers extends true members: T extends true
? { ? {
tmbId: string; tmbId: string;
name: string; name: string;
avatar: string; avatar: string;
}[] }[]
: undefined; : undefined;
count: WithMembers extends true ? number : undefined; count: T extends true ? number : undefined;
owner?: WithMembers extends true owner?: T extends true
? { ? {
tmbId: string; tmbId: string;
name: string; name: string;
avatar: string; avatar: string;
} }
: undefined; : undefined;
permission: WithMembers extends true ? Permission : undefined; permission: T extends true ? Permission : undefined;
}; };
type GroupMemberItemType = { type GroupMemberItemType = {

View File

@@ -1,50 +1,22 @@
import { PermissionKeyEnum } from '../constant'; import { PermissionKeyEnum } from '../constant';
import { PermissionListType } from '../type'; import { PermissionListType } from '../type';
import { PermissionList } from '../constant'; import { PermissionList } from '../constant';
import { i18nT } from '../../../../web/i18n/utils'; export const TeamPermissionList: PermissionListType = {
export enum TeamPermissionKeyEnum {
appCreate = 'appCreate',
datasetCreate = 'datasetCreate',
apikeyCreate = 'apikeyCreate'
}
export const TeamPermissionList: PermissionListType<TeamPermissionKeyEnum> = {
[PermissionKeyEnum.read]: { [PermissionKeyEnum.read]: {
...PermissionList[PermissionKeyEnum.read], ...PermissionList[PermissionKeyEnum.read],
value: 0b000100 value: 0b100
}, },
[PermissionKeyEnum.write]: { [PermissionKeyEnum.write]: {
...PermissionList[PermissionKeyEnum.write], ...PermissionList[PermissionKeyEnum.write],
value: 0b000010 value: 0b010
}, },
[PermissionKeyEnum.manage]: { [PermissionKeyEnum.manage]: {
...PermissionList[PermissionKeyEnum.manage], ...PermissionList[PermissionKeyEnum.manage],
value: 0b000001 value: 0b001
},
[TeamPermissionKeyEnum.appCreate]: {
checkBoxType: 'multiple',
description: '',
name: i18nT('account_team:permission_appCreate'),
value: 0b001000
},
[TeamPermissionKeyEnum.datasetCreate]: {
checkBoxType: 'multiple',
description: '',
name: i18nT('account_team:permission_datasetCreate'),
value: 0b010000
},
[TeamPermissionKeyEnum.apikeyCreate]: {
checkBoxType: 'multiple',
description: '',
name: i18nT('account_team:permission_apikeyCreate'),
value: 0b100000
} }
}; };
export const TeamReadPermissionVal = TeamPermissionList['read'].value; export const TeamReadPermissionVal = TeamPermissionList['read'].value;
export const TeamWritePermissionVal = TeamPermissionList['write'].value; export const TeamWritePermissionVal = TeamPermissionList['write'].value;
export const TeamManagePermissionVal = TeamPermissionList['manage'].value; export const TeamManagePermissionVal = TeamPermissionList['manage'].value;
export const TeamAppCreatePermissionVal = TeamPermissionList['appCreate'].value;
export const TeamDatasetCreatePermissionVal = TeamPermissionList['datasetCreate'].value;
export const TeamApikeyCreatePermissionVal = TeamPermissionList['apikeyCreate'].value;
export const TeamDefaultPermissionVal = TeamReadPermissionVal; export const TeamDefaultPermissionVal = TeamReadPermissionVal;

View File

@@ -1,17 +1,7 @@
import { PerConstructPros, Permission } from '../controller'; import { PerConstructPros, Permission } from '../controller';
import { import { TeamDefaultPermissionVal, TeamPermissionList } from './constant';
TeamApikeyCreatePermissionVal,
TeamAppCreatePermissionVal,
TeamDatasetCreatePermissionVal,
TeamDefaultPermissionVal,
TeamPermissionList
} from './constant';
export class TeamPermission extends Permission { export class TeamPermission extends Permission {
hasAppCreatePer: boolean = false;
hasDatasetCreatePer: boolean = false;
hasApikeyCreatePer: boolean = false;
constructor(props?: PerConstructPros) { constructor(props?: PerConstructPros) {
if (!props) { if (!props) {
props = { props = {
@@ -22,11 +12,5 @@ export class TeamPermission extends Permission {
} }
props.permissionList = TeamPermissionList; props.permissionList = TeamPermissionList;
super(props); super(props);
this.setUpdatePermissionCallback(() => {
this.hasAppCreatePer = this.checkPer(TeamAppCreatePermissionVal);
this.hasDatasetCreatePer = this.checkPer(TeamDatasetCreatePermissionVal);
this.hasApikeyCreatePer = this.checkPer(TeamApikeyCreatePermissionVal);
});
} }
} }

View File

@@ -11,8 +11,7 @@ export enum UsageSourceEnum {
feishu = 'feishu', feishu = 'feishu',
dingtalk = 'dingtalk', dingtalk = 'dingtalk',
official_account = 'official_account', official_account = 'official_account',
pdfParse = 'pdfParse', pdfParse = 'pdfParse'
mcp = 'mcp'
} }
export const UsageSourceMap = { export const UsageSourceMap = {
@@ -48,8 +47,5 @@ export const UsageSourceMap = {
}, },
[UsageSourceEnum.pdfParse]: { [UsageSourceEnum.pdfParse]: {
label: i18nT('account_usage:pdf_parse') label: i18nT('account_usage:pdf_parse')
},
[UsageSourceEnum.mcp]: {
label: i18nT('account_usage:mcp')
} }
}; };

View File

@@ -1,6 +1,7 @@
import { POST } from './plusRequest';
export const postTextCensor = (data: { text: string }) => export const postTextCensor = (data: { text: string }) =>
global POST<{ code?: number; message: string }>('/common/censor/check', data)
.textCensorHandler(data)
.then((res) => { .then((res) => {
if (res?.code === 5000) { if (res?.code === 5000) {
return Promise.reject(res); return Promise.reject(res);

View File

@@ -1,29 +0,0 @@
import { FeishuServer, YuqueServer } from '@fastgpt/global/core/dataset/apiDataset';
import {
DeepRagSearchProps,
SearchDatasetDataResponse
} from '../../core/dataset/search/controller';
import { AuthOpenApiLimitProps } from '../../support/openapi/auth';
import { CreateUsageProps, ConcatUsageProps } from '@fastgpt/global/support/wallet/usage/api';
import {
GetProApiDatasetFileContentParams,
GetProApiDatasetFileListParams,
GetProApiDatasetFilePreviewUrlParams
} from '../../core/dataset/apiDataset/proApi';
declare global {
var textCensorHandler: (params: { text: string }) => Promise<{ code: number; message?: string }>;
var deepRagHandler: (data: DeepRagSearchProps) => Promise<SearchDatasetDataResponse>;
var authOpenApiHandler: (data: AuthOpenApiLimitProps) => Promise<any>;
var createUsageHandler: (data: CreateUsageProps) => Promise<void>;
var concatUsageHandler: (data: ConcatUsageProps) => Promise<void>;
// API dataset
var getProApiDatasetFileList: (data: GetProApiDatasetFileListParams) => Promise<APIFileItem[]>;
var getProApiDatasetFileContent: (
data: GetProApiDatasetFileContentParams
) => Promise<ApiFileReadContentResponse>;
var getProApiDatasetFilePreviewUrl: (
data: GetProApiDatasetFilePreviewUrlParams
) => Promise<string>;
}

View File

@@ -69,11 +69,6 @@ export function getWorker<DataType, ReturnType = void>(
newWorker.on('error', (error) => { newWorker.on('error', (error) => {
addLog.error(`MQ Worker [${name}]: ${error.message}`, error); addLog.error(`MQ Worker [${name}]: ${error.message}`, error);
}); });
newWorker.on('failed', (jobId, error) => {
addLog.error(`MQ Worker [${name}]: ${error.message}`, error);
});
workers.set(name, newWorker); workers.set(name, newWorker);
return newWorker; return newWorker;
} }
export * from 'bullmq';

View File

@@ -32,13 +32,3 @@ export const getImageBase64 = async (url: string) => {
return Promise.reject(error); return Promise.reject(error);
} }
}; };
export const addEndpointToImageUrl = (text: string) => {
const baseURL = process.env.FE_DOMAIN;
if (!baseURL) return text;
// 匹配 /api/system/img/xxx.xx 的图片链接,并追加 baseURL
return text.replace(
/(?<!https?:\/\/[^\s]*)(?:\/api\/system\/img\/[^\s.]*\.[^\s]*)/g,
(match) => `${baseURL}${match}`
);
};

View File

@@ -69,7 +69,7 @@ const addCommonMiddleware = (schema: mongoose.Schema) => {
export const getMongoModel = <T>(name: string, schema: mongoose.Schema) => { export const getMongoModel = <T>(name: string, schema: mongoose.Schema) => {
if (connectionMongo.models[name]) return connectionMongo.models[name] as Model<T>; if (connectionMongo.models[name]) return connectionMongo.models[name] as Model<T>;
if (process.env.NODE_ENV !== 'test') console.log('Load model======', name); console.log('Load model======', name);
addCommonMiddleware(schema); addCommonMiddleware(schema);
const model = connectionMongo.model<T>(name, schema); const model = connectionMongo.model<T>(name, schema);

View File

@@ -1,3 +1,4 @@
export const FastGPTProUrl = process.env.PRO_URL ? `${process.env.PRO_URL}/api` : ''; export const FastGPTProUrl = process.env.PRO_URL ? `${process.env.PRO_URL}/api` : '';
export const isFastGPTMainService = !!process.env.PRO_URL;
// @ts-ignore // @ts-ignore
export const isFastGPTProService = () => !!global.systemConfig; export const isFastGPTProService = () => !!global.systemConfig;

View File

@@ -7,8 +7,6 @@ import { EmbeddingModelItemType } from '@fastgpt/global/core/ai/model.d';
import { MILVUS_ADDRESS, PG_ADDRESS, OCEANBASE_ADDRESS } from './constants'; import { MILVUS_ADDRESS, PG_ADDRESS, OCEANBASE_ADDRESS } from './constants';
import { MilvusCtrl } from './milvus/class'; import { MilvusCtrl } from './milvus/class';
import { setRedisCache, getRedisCache, delRedisCache, CacheKeyEnum } from '../redis/cache'; import { setRedisCache, getRedisCache, delRedisCache, CacheKeyEnum } from '../redis/cache';
import { throttle } from 'lodash';
import { retryFn } from '@fastgpt/global/common/system/utils';
const getVectorObj = () => { const getVectorObj = () => {
if (PG_ADDRESS) return new PgVectorCtrl(); if (PG_ADDRESS) return new PgVectorCtrl();
@@ -17,12 +15,7 @@ const getVectorObj = () => {
return new PgVectorCtrl(); return new PgVectorCtrl();
}; };
const getChcheKey = (teamId: string) => `${CacheKeyEnum.team_vector_count}:${teamId}`; const getChcheKey = (teamId: string) => `${CacheKeyEnum.team_vector_count}:${teamId}`;
const onDelCache = throttle((teamId: string) => delRedisCache(getChcheKey(teamId)), 30000, {
leading: true,
trailing: true
});
const Vector = getVectorObj(); const Vector = getVectorObj();
@@ -56,28 +49,26 @@ export const insertDatasetDataVector = async ({
query: string; query: string;
model: EmbeddingModelItemType; model: EmbeddingModelItemType;
}) => { }) => {
return retryFn(async () => { const { vectors, tokens } = await getVectorsByText({
const { vectors, tokens } = await getVectorsByText({ model,
model, input: query,
input: query, type: 'db'
type: 'db'
});
const { insertId } = await Vector.insert({
...props,
vector: vectors[0]
});
onDelCache(props.teamId);
return {
tokens,
insertId
};
}); });
const { insertId } = await Vector.insert({
...props,
vector: vectors[0]
});
delRedisCache(getChcheKey(props.teamId));
return {
tokens,
insertId
};
}; };
export const deleteDatasetDataVector = async (props: DelDatasetVectorCtrlProps) => { export const deleteDatasetDataVector = async (props: DelDatasetVectorCtrlProps) => {
const result = await Vector.delete(props); const result = await Vector.delete(props);
onDelCache(props.teamId); delRedisCache(getChcheKey(props.teamId));
return result; return result;
}; };

View File

@@ -1,30 +1,6 @@
{ {
"provider": "Gemini", "provider": "Gemini",
"list": [ "list": [
{
"model": "gemini-2.5-pro-exp-03-25",
"name": "gemini-2.5-pro-exp-03-25",
"maxContext": 1000000,
"maxResponse": 63000,
"quoteMaxToken": 1000000,
"maxTemperature": 1,
"vision": true,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm",
"showTopP": true,
"showStopSign": true
},
{ {
"model": "gemini-2.0-flash", "model": "gemini-2.0-flash",
"name": "gemini-2.0-flash", "name": "gemini-2.0-flash",

View File

@@ -1,54 +1,6 @@
{ {
"provider": "Grok", "provider": "Grok",
"list": [ "list": [
{
"model": "grok-3-mini",
"name": "grok-3-mini",
"maxContext": 128000,
"maxResponse": 8000,
"quoteMaxToken": 128000,
"maxTemperature": 1,
"showTopP": true,
"showStopSign": true,
"vision": false,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "grok-3-mini-fast",
"name": "grok-3-mini-fast",
"maxContext": 128000,
"maxResponse": 8000,
"quoteMaxToken": 128000,
"maxTemperature": 1,
"showTopP": true,
"showStopSign": true,
"vision": false,
"toolChoice": true,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{ {
"model": "grok-3", "model": "grok-3",
"name": "grok-3", "name": "grok-3",
@@ -59,31 +11,7 @@
"showTopP": true, "showTopP": true,
"showStopSign": true, "showStopSign": true,
"vision": false, "vision": false,
"toolChoice": true, "toolChoice": false,
"functionCall": false,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"usedInQueryExtension": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "grok-3-fast",
"name": "grok-3-fast",
"maxContext": 128000,
"maxResponse": 8000,
"quoteMaxToken": 128000,
"maxTemperature": 1,
"showTopP": true,
"showStopSign": true,
"vision": false,
"toolChoice": true,
"functionCall": false, "functionCall": false,
"defaultSystemChatPrompt": "", "defaultSystemChatPrompt": "",
"datasetProcess": true, "datasetProcess": true,

View File

@@ -1,78 +1,6 @@
{ {
"provider": "OpenAI", "provider": "OpenAI",
"list": [ "list": [
{
"model": "gpt-4.1",
"name": "gpt-4.1",
"maxContext": 1000000,
"maxResponse": 32000,
"quoteMaxToken": 1000000,
"maxTemperature": 1.2,
"showTopP": true,
"responseFormatList": ["text", "json_object", "json_schema"],
"showStopSign": true,
"vision": true,
"toolChoice": true,
"functionCall": true,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "gpt-4.1-mini",
"name": "gpt-4.1-mini",
"maxContext": 1000000,
"maxResponse": 32000,
"quoteMaxToken": 1000000,
"maxTemperature": 1.2,
"showTopP": true,
"responseFormatList": ["text", "json_object", "json_schema"],
"showStopSign": true,
"vision": true,
"toolChoice": true,
"functionCall": true,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{
"model": "gpt-4.1-nano",
"name": "gpt-4.1-nano",
"maxContext": 1000000,
"maxResponse": 32000,
"quoteMaxToken": 1000000,
"maxTemperature": 1.2,
"showTopP": true,
"responseFormatList": ["text", "json_object", "json_schema"],
"showStopSign": true,
"vision": true,
"toolChoice": true,
"functionCall": true,
"defaultSystemChatPrompt": "",
"datasetProcess": true,
"usedInClassify": true,
"customCQPrompt": "",
"usedInExtractFields": true,
"customExtractPrompt": "",
"usedInToolCall": true,
"defaultConfig": {},
"fieldMap": {},
"type": "llm"
},
{ {
"model": "gpt-4o-mini", "model": "gpt-4o-mini",
"name": "GPT-4o-mini", "name": "GPT-4o-mini",
@@ -81,7 +9,11 @@
"quoteMaxToken": 60000, "quoteMaxToken": 60000,
"maxTemperature": 1.2, "maxTemperature": 1.2,
"showTopP": true, "showTopP": true,
"responseFormatList": ["text", "json_object", "json_schema"], "responseFormatList": [
"text",
"json_object",
"json_schema"
],
"showStopSign": true, "showStopSign": true,
"vision": true, "vision": true,
"toolChoice": true, "toolChoice": true,
@@ -105,7 +37,11 @@
"quoteMaxToken": 60000, "quoteMaxToken": 60000,
"maxTemperature": 1.2, "maxTemperature": 1.2,
"showTopP": true, "showTopP": true,
"responseFormatList": ["text", "json_object", "json_schema"], "responseFormatList": [
"text",
"json_object",
"json_schema"
],
"showStopSign": true, "showStopSign": true,
"vision": true, "vision": true,
"toolChoice": true, "toolChoice": true,
@@ -339,4 +275,4 @@
"type": "stt" "type": "stt"
} }
] ]
} }

View File

@@ -86,19 +86,3 @@ export async function findAppAndAllChildren({
return [app, ...childDatasets]; return [app, ...childDatasets];
} }
export const getAppBasicInfoByIds = async ({ teamId, ids }: { teamId: string; ids: string[] }) => {
const apps = await MongoApp.find(
{
teamId,
_id: { $in: ids }
},
'_id name avatar'
).lean();
return apps.map((item) => ({
id: item._id,
name: item.name,
avatar: item.avatar
}));
};

View File

@@ -1,11 +1,6 @@
import { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node.d'; import { FlowNodeTemplateType } from '@fastgpt/global/core/workflow/type/node.d';
import { FlowNodeTypeEnum, defaultNodeVersion } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum, defaultNodeVersion } from '@fastgpt/global/core/workflow/node/constant';
import { import { appData2FlowNodeIO, pluginData2FlowNodeIO } from '@fastgpt/global/core/workflow/utils';
appData2FlowNodeIO,
pluginData2FlowNodeIO,
toolData2FlowNodeIO,
toolSetData2FlowNodeIO
} from '@fastgpt/global/core/workflow/utils';
import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants'; import { PluginSourceEnum } from '@fastgpt/global/core/plugin/constants';
import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants'; import { FlowNodeTemplateTypeEnum } from '@fastgpt/global/core/workflow/constants';
import { getHandleConfig } from '@fastgpt/global/core/workflow/template/utils'; import { getHandleConfig } from '@fastgpt/global/core/workflow/template/utils';
@@ -133,41 +128,11 @@ export async function getChildAppPreviewNode({
(node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput (node) => node.flowNodeType === FlowNodeTypeEnum.pluginInput
); );
const isTool =
!!app.workflow.nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.tool) &&
app.workflow.nodes.length === 1;
const isToolSet =
!!app.workflow.nodes.find((node) => node.flowNodeType === FlowNodeTypeEnum.toolSet) &&
app.workflow.nodes.length === 1;
const { flowNodeType, nodeIOConfig } = (() => {
if (isToolSet)
return {
flowNodeType: FlowNodeTypeEnum.toolSet,
nodeIOConfig: toolSetData2FlowNodeIO({ nodes: app.workflow.nodes })
};
if (isTool)
return {
flowNodeType: FlowNodeTypeEnum.tool,
nodeIOConfig: toolData2FlowNodeIO({ nodes: app.workflow.nodes })
};
if (isPlugin)
return {
flowNodeType: FlowNodeTypeEnum.pluginModule,
nodeIOConfig: pluginData2FlowNodeIO({ nodes: app.workflow.nodes })
};
return {
flowNodeType: FlowNodeTypeEnum.appModule,
nodeIOConfig: appData2FlowNodeIO({ chatConfig: app.workflow.chatConfig })
};
})();
return { return {
id: getNanoid(), id: getNanoid(),
pluginId: app.id, pluginId: app.id,
templateType: app.templateType, templateType: app.templateType,
flowNodeType, flowNodeType: isPlugin ? FlowNodeTypeEnum.pluginModule : FlowNodeTypeEnum.appModule,
avatar: app.avatar, avatar: app.avatar,
name: app.name, name: app.name,
intro: app.intro, intro: app.intro,
@@ -176,13 +141,11 @@ export async function getChildAppPreviewNode({
showStatus: app.showStatus, showStatus: app.showStatus,
isTool: true, isTool: true,
version: app.version, version: app.version,
sourceHandle: isToolSet sourceHandle: getHandleConfig(true, true, true, true),
? getHandleConfig(false, false, false, false) targetHandle: getHandleConfig(true, true, true, true),
: getHandleConfig(true, true, true, true), ...(isPlugin
targetHandle: isToolSet ? pluginData2FlowNodeIO({ nodes: app.workflow.nodes })
? getHandleConfig(false, false, false, false) : appData2FlowNodeIO({ chatConfig: app.workflow.chatConfig }))
: getHandleConfig(true, true, true, true),
...nodeIOConfig
}; };
} }

View File

@@ -16,7 +16,6 @@ import { mergeChatResponseData } from '@fastgpt/global/core/chat/utils';
import { pushChatLog } from './pushChatLog'; import { pushChatLog } from './pushChatLog';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { extractDeepestInteractive } from '@fastgpt/global/core/workflow/runtime/utils';
type Props = { type Props = {
chatId: string; chatId: string;
@@ -210,24 +209,34 @@ export const updateInteractiveChat = async ({
} }
})(); })();
let finalInteractive = extractDeepestInteractive(interactiveValue.interactive); if (interactiveValue.interactive.type === 'userSelect') {
interactiveValue.interactive = {
if (finalInteractive.type === 'userSelect') { ...interactiveValue.interactive,
finalInteractive.params.userSelectedVal = userInteractiveVal; params: {
...interactiveValue.interactive.params,
userSelectedVal: userInteractiveVal
}
};
} else if ( } else if (
finalInteractive.type === 'userInput' && interactiveValue.interactive.type === 'userInput' &&
typeof parsedUserInteractiveVal === 'object' typeof parsedUserInteractiveVal === 'object'
) { ) {
finalInteractive.params.inputForm = finalInteractive.params.inputForm.map((item) => { interactiveValue.interactive = {
const itemValue = parsedUserInteractiveVal[item.label]; ...interactiveValue.interactive,
return itemValue !== undefined params: {
? { ...interactiveValue.interactive.params,
...item, inputForm: interactiveValue.interactive.params.inputForm.map((item) => {
value: itemValue const itemValue = parsedUserInteractiveVal[item.label];
} return itemValue !== undefined
: item; ? {
}); ...item,
finalInteractive.params.submitted = true; value: itemValue
}
: item;
}),
submitted: true
}
};
} }
if (aiResponse.customFeedbacks) { if (aiResponse.customFeedbacks) {

View File

@@ -11,7 +11,7 @@ import axios from 'axios';
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants'; import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
import { i18nT } from '../../../web/i18n/utils'; import { i18nT } from '../../../web/i18n/utils';
import { addLog } from '../../common/system/log'; import { addLog } from '../../common/system/log';
import { addEndpointToImageUrl, getImageBase64 } from '../../common/file/image/utils'; import { getImageBase64 } from '../../common/file/image/utils';
export const filterGPTMessageByMaxContext = async ({ export const filterGPTMessageByMaxContext = async ({
messages = [], messages = [],
@@ -87,17 +87,26 @@ export const loadRequestMessages = async ({
useVision?: boolean; useVision?: boolean;
origin?: string; origin?: string;
}) => { }) => {
const replaceLinkUrl = (text: string) => {
const baseURL = process.env.FE_DOMAIN;
if (!baseURL) return text;
// 匹配 /api/system/img/xxx.xx 的图片链接,并追加 baseURL
return text.replace(
/(?<!https?:\/\/[^\s]*)(?:\/api\/system\/img\/[^\s.]*\.[^\s]*)/g,
(match) => `${baseURL}${match}`
);
};
const parseSystemMessage = ( const parseSystemMessage = (
content: string | ChatCompletionContentPartText[] content: string | ChatCompletionContentPartText[]
): string | ChatCompletionContentPartText[] | undefined => { ): string | ChatCompletionContentPartText[] | undefined => {
if (typeof content === 'string') { if (typeof content === 'string') {
if (!content) return; if (!content) return;
return addEndpointToImageUrl(content); return replaceLinkUrl(content);
} }
const arrayContent = content const arrayContent = content
.filter((item) => item.text) .filter((item) => item.text)
.map((item) => ({ ...item, text: addEndpointToImageUrl(item.text) })); .map((item) => ({ ...item, text: replaceLinkUrl(item.text) }));
if (arrayContent.length === 0) return; if (arrayContent.length === 0) return;
return arrayContent; return arrayContent;
}; };

View File

@@ -1,25 +0,0 @@
import { ParentIdType } from '@fastgpt/global/common/parentFolder/type';
import { FeishuServer, YuqueServer } from '@fastgpt/global/core/dataset/apiDataset';
export enum ProApiDatasetOperationTypeEnum {
LIST = 'list',
READ = 'read',
CONTENT = 'content'
}
export type ProApiDatasetCommonParams = {
feishuServer?: FeishuServer;
yuqueServer?: YuqueServer;
};
export type GetProApiDatasetFileListParams = ProApiDatasetCommonParams & {
parentId?: ParentIdType;
};
export type GetProApiDatasetFileContentParams = ProApiDatasetCommonParams & {
apiFileId: string;
};
export type GetProApiDatasetFilePreviewUrlParams = ProApiDatasetCommonParams & {
apiFileId: string;
};

View File

@@ -9,6 +9,7 @@ import { readRawContentByFileBuffer } from '../../common/file/read/utils';
import { parseFileExtensionFromUrl } from '@fastgpt/global/common/string/tools'; import { parseFileExtensionFromUrl } from '@fastgpt/global/common/string/tools';
import { APIFileServer, FeishuServer, YuqueServer } from '@fastgpt/global/core/dataset/apiDataset'; import { APIFileServer, FeishuServer, YuqueServer } from '@fastgpt/global/core/dataset/apiDataset';
import { useApiDatasetRequest } from './apiDataset/api'; import { useApiDatasetRequest } from './apiDataset/api';
import { POST } from '../../common/api/plusRequest';
export const readFileRawTextByUrl = async ({ export const readFileRawTextByUrl = async ({
teamId, teamId,
@@ -167,7 +168,11 @@ export const readApiServerFileContent = async ({
} }
if (feishuServer || yuqueServer) { if (feishuServer || yuqueServer) {
return global.getProApiDatasetFileContent({ return POST<{
title?: string;
rawText: string;
}>(`/core/dataset/systemApiDataset`, {
type: 'content',
feishuServer, feishuServer,
yuqueServer, yuqueServer,
apiFileId apiFileId

View File

@@ -24,6 +24,7 @@ import { MongoDatasetCollectionTags } from '../tag/schema';
import { readFromSecondary } from '../../../common/mongo/utils'; import { readFromSecondary } from '../../../common/mongo/utils';
import { MongoDatasetDataText } from '../data/dataTextSchema'; import { MongoDatasetDataText } from '../data/dataTextSchema';
import { ChatItemType } from '@fastgpt/global/core/chat/type'; import { ChatItemType } from '@fastgpt/global/core/chat/type';
import { POST } from '../../../common/api/plusRequest';
import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { NodeInputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { datasetSearchQueryExtension } from './utils'; import { datasetSearchQueryExtension } from './utils';
import type { RerankModelItemType } from '@fastgpt/global/core/ai/model.d'; import type { RerankModelItemType } from '@fastgpt/global/core/ai/model.d';
@@ -849,4 +850,5 @@ export type DeepRagSearchProps = SearchDatasetDataProps & {
[NodeInputKeyEnum.datasetDeepSearchMaxTimes]?: number; [NodeInputKeyEnum.datasetDeepSearchMaxTimes]?: number;
[NodeInputKeyEnum.datasetDeepSearchBg]?: string; [NodeInputKeyEnum.datasetDeepSearchBg]?: string;
}; };
export const deepRagSearch = (data: DeepRagSearchProps) => global.deepRagHandler(data); export const deepRagSearch = (data: DeepRagSearchProps) =>
POST<SearchDatasetDataResponse>('/core/dataset/deepRag', data);

View File

@@ -34,44 +34,23 @@ export const addWebsiteSyncJob = (data: WebsiteSyncJobData) => {
export const getWebsiteSyncDatasetStatus = async (datasetId: string) => { export const getWebsiteSyncDatasetStatus = async (datasetId: string) => {
const jobId = await websiteSyncQueue.getDeduplicationJobId(datasetId); const jobId = await websiteSyncQueue.getDeduplicationJobId(datasetId);
if (!jobId) { if (!jobId) {
return { return DatasetStatusEnum.active;
status: DatasetStatusEnum.active,
errorMsg: undefined
};
} }
const job = await websiteSyncQueue.getJob(jobId); const job = await websiteSyncQueue.getJob(jobId);
if (!job) { if (!job) {
return { return DatasetStatusEnum.active;
status: DatasetStatusEnum.active,
errorMsg: undefined
};
} }
const jobState = await job.getState(); const jobState = await job.getState();
if (jobState === 'failed' || jobState === 'unknown') {
return {
status: DatasetStatusEnum.error,
errorMsg: job.failedReason
};
}
if (['waiting-children', 'waiting'].includes(jobState)) { if (['waiting-children', 'waiting'].includes(jobState)) {
return { return DatasetStatusEnum.waiting;
status: DatasetStatusEnum.waiting,
errorMsg: undefined
};
} }
if (jobState === 'active') { if (jobState === 'active') {
return { return DatasetStatusEnum.syncing;
status: DatasetStatusEnum.syncing,
errorMsg: undefined
};
} }
return { return DatasetStatusEnum.active;
status: DatasetStatusEnum.active,
errorMsg: undefined
};
}; };
// Scheduler setting // Scheduler setting

View File

@@ -7,7 +7,7 @@ import { ChatRoleEnum } from '@fastgpt/global/core/chat/constants';
import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants'; import { SseResponseEventEnum } from '@fastgpt/global/core/workflow/runtime/constants';
import { import {
getWorkflowEntryNodeIds, getWorkflowEntryNodeIds,
storeEdges2RuntimeEdges, initWorkflowEdgeStatus,
storeNodes2RuntimeNodes, storeNodes2RuntimeNodes,
textAdaptGptResponse textAdaptGptResponse
} from '@fastgpt/global/core/workflow/runtime/utils'; } from '@fastgpt/global/core/workflow/runtime/utils';
@@ -70,7 +70,7 @@ export const dispatchAppRequest = async (props: Props): Promise<Response> => {
appData.modules, appData.modules,
getWorkflowEntryNodeIds(appData.modules) getWorkflowEntryNodeIds(appData.modules)
), ),
runtimeEdges: storeEdges2RuntimeEdges(appData.edges), runtimeEdges: initWorkflowEdgeStatus(appData.edges),
histories: chatHistories, histories: chatHistories,
query: runtimePrompt2ChatsValue({ query: runtimePrompt2ChatsValue({
files, files,

View File

@@ -22,16 +22,16 @@ import { formatModelChars2Points } from '../../../../../support/wallet/usage/uti
import { getHistoryPreview } from '@fastgpt/global/core/chat/utils'; import { getHistoryPreview } from '@fastgpt/global/core/chat/utils';
import { runToolWithFunctionCall } from './functionCall'; import { runToolWithFunctionCall } from './functionCall';
import { runToolWithPromptCall } from './promptCall'; import { runToolWithPromptCall } from './promptCall';
import { getNanoid, replaceVariable } from '@fastgpt/global/common/string/tools'; import { replaceVariable } from '@fastgpt/global/common/string/tools';
import { getMultiplePrompt, Prompt_Tool_Call } from './constants'; import { getMultiplePrompt, Prompt_Tool_Call } from './constants';
import { filterToolResponseToPreview } from './utils'; import { filterToolResponseToPreview } from './utils';
import { InteractiveNodeResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type'; import { InteractiveNodeResponseType } from '@fastgpt/global/core/workflow/template/system/interactive/type';
import { getFileContentFromLinks, getHistoryFileLinks } from '../../tools/readFiles'; import { getFileContentFromLinks, getHistoryFileLinks } from '../../tools/readFiles';
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools'; import { parseUrlToFileType } from '@fastgpt/global/common/file/tools';
import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import { postTextCensor } from '../../../../../common/api/requestPlusApi';
import { ModelTypeEnum } from '@fastgpt/global/core/ai/model'; import { ModelTypeEnum } from '@fastgpt/global/core/ai/model';
import { getDocumentQuotePrompt } from '@fastgpt/global/core/ai/prompt/AIChat'; import { getDocumentQuotePrompt } from '@fastgpt/global/core/ai/prompt/AIChat';
import { postTextCensor } from '../../../../chat/postTextCensor';
type Response = DispatchNodeResultType<{ type Response = DispatchNodeResultType<{
[NodeOutputKeyEnum.answerText]: string; [NodeOutputKeyEnum.answerText]: string;
@@ -188,8 +188,6 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
if (toolModel.toolChoice) { if (toolModel.toolChoice) {
return runToolWithToolChoice({ return runToolWithToolChoice({
...props, ...props,
runtimeNodes,
runtimeEdges,
toolNodes, toolNodes,
toolModel, toolModel,
maxRunToolTimes: 30, maxRunToolTimes: 30,
@@ -200,8 +198,6 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
if (toolModel.functionCall) { if (toolModel.functionCall) {
return runToolWithFunctionCall({ return runToolWithFunctionCall({
...props, ...props,
runtimeNodes,
runtimeEdges,
toolNodes, toolNodes,
toolModel, toolModel,
messages: adaptMessages, messages: adaptMessages,
@@ -230,8 +226,6 @@ export const dispatchRunTools = async (props: DispatchToolModuleProps): Promise<
return runToolWithPromptCall({ return runToolWithPromptCall({
...props, ...props,
runtimeNodes,
runtimeEdges,
toolNodes, toolNodes,
toolModel, toolModel,
messages: adaptMessages, messages: adaptMessages,

View File

@@ -13,6 +13,7 @@ import type {
} from '@fastgpt/global/core/ai/type.d'; } from '@fastgpt/global/core/ai/type.d';
import { formatModelChars2Points } from '../../../../support/wallet/usage/utils'; import { formatModelChars2Points } from '../../../../support/wallet/usage/utils';
import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d'; import type { LLMModelItemType } from '@fastgpt/global/core/ai/model.d';
import { postTextCensor } from '../../../../common/api/requestPlusApi';
import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants'; import { ChatCompletionRequestMessageRoleEnum } from '@fastgpt/global/core/ai/constants';
import type { import type {
ChatDispatchProps, ChatDispatchProps,
@@ -50,7 +51,6 @@ import { getFileContentFromLinks, getHistoryFileLinks } from '../tools/readFiles
import { parseUrlToFileType } from '@fastgpt/global/common/file/tools'; import { parseUrlToFileType } from '@fastgpt/global/common/file/tools';
import { i18nT } from '../../../../../web/i18n/utils'; import { i18nT } from '../../../../../web/i18n/utils';
import { ModelTypeEnum } from '@fastgpt/global/core/ai/model'; import { ModelTypeEnum } from '@fastgpt/global/core/ai/model';
import { postTextCensor } from '../../../chat/postTextCensor';
export type ChatProps = ModuleDispatchProps< export type ChatProps = ModuleDispatchProps<
AIChatNodeProps & { AIChatNodeProps & {

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