Compare commits
54 Commits
v4.9.2
...
v4.9.6-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
952412f648 | ||
|
|
ab799e13cd | ||
|
|
ba422b73b3 | ||
|
|
c7c79b400a | ||
|
|
47f674666b | ||
|
|
0c9e56c1ee | ||
|
|
97a6c6749a | ||
|
|
565b3e4319 | ||
|
|
efad4c101f | ||
|
|
bed68718e8 | ||
|
|
7a9cf4ce9e | ||
|
|
b943d05d5a | ||
|
|
2af3cd83f2 | ||
|
|
ea74c669ee | ||
|
|
38597d8f04 | ||
|
|
88ed019717 | ||
|
|
16a22bc76a | ||
|
|
b51a87f5b7 | ||
|
|
bc1ca66b66 | ||
|
|
c9e12bb608 | ||
|
|
4e7fa29087 | ||
|
|
ec3bcfa124 | ||
|
|
199f454b6b | ||
|
|
80f41dd2a9 | ||
|
|
4343eecaaf | ||
|
|
c02864facc | ||
|
|
e4629a5c8c | ||
|
|
2dc3cb75fe | ||
|
|
431390fe42 | ||
|
|
1f5709eda6 | ||
|
|
86988e31d9 | ||
|
|
675e8ccedb | ||
|
|
9dfafb13bf | ||
|
|
f642c9603b | ||
|
|
5839325f77 | ||
|
|
73c997f7c5 | ||
|
|
ff92dced98 | ||
|
|
7a0747947c | ||
|
|
5ad383bc6e | ||
|
|
c85b719384 | ||
|
|
aeedc2fada | ||
|
|
be34b69f9b | ||
|
|
944774ec5f | ||
|
|
5b21b4b674 | ||
|
|
b0f0afabd2 | ||
|
|
d9aea53d13 | ||
|
|
73db92e4ad | ||
|
|
267cc5702c | ||
|
|
540f321fc9 | ||
|
|
a37c75159f | ||
|
|
0ed99d8c9a | ||
|
|
2d3ae7f944 | ||
|
|
565a966d19 | ||
|
|
8323c2d27e |
@@ -1,4 +1,4 @@
|
||||
yangchuansheng/fastgpt-imgs:
|
||||
- source: docSite/assets/imgs/
|
||||
dest: imgs/
|
||||
deleteOrphaned: true
|
||||
deleteOrphaned: true
|
||||
30
.github/gh-bot.yml
vendored
@@ -1,30 +0,0 @@
|
||||
version: v1
|
||||
debug: true
|
||||
action:
|
||||
printConfig: false
|
||||
release:
|
||||
retry: 15s
|
||||
actionName: Release
|
||||
allowOps:
|
||||
- cuisongliu
|
||||
bot:
|
||||
prefix: /
|
||||
spe: _
|
||||
allowOps:
|
||||
- sealos-ci-robot
|
||||
- sealos-release-robot
|
||||
email: sealos-ci-robot@sealos.io
|
||||
username: sealos-ci-robot
|
||||
repo:
|
||||
org: false
|
||||
|
||||
message:
|
||||
success: |
|
||||
🤖 says: Hooray! The action {{.Body}} has been completed successfully. 🎉
|
||||
format_error: |
|
||||
🤖 says: ‼️ There is a formatting issue with the action, kindly verify the action's format.
|
||||
permission_error: |
|
||||
🤖 says: ‼️ The action doesn't have permission to trigger.
|
||||
release_error: |
|
||||
🤖 says: ‼️ Release action failed.
|
||||
Error details: {{.Error}}
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Deploy doc image to vercel
|
||||
name: Deploy doc image to cf
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -18,7 +18,12 @@ jobs:
|
||||
url: ${{ steps.vercel-action.outputs.preview-url }}
|
||||
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
|
||||
# Job outputs
|
||||
outputs:
|
||||
@@ -58,20 +63,9 @@ jobs:
|
||||
- name: Build
|
||||
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
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
if: github.ref == 'refs/heads/main'
|
||||
with:
|
||||
github_token: ${{ secrets.GH_PAT }}
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: docSite/public
|
||||
21
.github/workflows/docs-deploy-kubeconfig.yml
vendored
@@ -10,6 +10,13 @@ on:
|
||||
jobs:
|
||||
build-fastgpt-docs-images:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -27,7 +34,6 @@ jobs:
|
||||
with:
|
||||
# list of Docker images to use as base name for tags
|
||||
images: |
|
||||
${{ secrets.DOCKER_HUB_NAME }}/fastgpt-docs
|
||||
ghcr.io/${{ github.repository_owner }}/fastgpt-docs
|
||||
registry.cn-hangzhou.aliyuncs.com/${{ secrets.ALI_HUB_USERNAME }}/fastgpt-docs
|
||||
tags: |
|
||||
@@ -40,18 +46,12 @@ jobs:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_NAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GH_PAT }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Login to Aliyun
|
||||
uses: docker/login-action@v3
|
||||
@@ -70,9 +70,10 @@ jobs:
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
outputs:
|
||||
tags: ${{ steps.datetime.outputs.datetime }}
|
||||
|
||||
update-docs-image:
|
||||
needs: build-fastgpt-docs-images
|
||||
runs-on: ubuntu-20.04
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository == 'labring/FastGPT'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
||||
77
.github/workflows/docs-preview.yml
vendored
@@ -10,13 +10,19 @@ on:
|
||||
jobs:
|
||||
# This workflow contains jobs "deploy-production"
|
||||
deploy-preview:
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
# The environment this job references
|
||||
environment:
|
||||
name: Preview
|
||||
url: ${{ steps.vercel-action.outputs.preview-url }}
|
||||
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
|
||||
# Job outputs
|
||||
outputs:
|
||||
@@ -32,6 +38,7 @@ jobs:
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
submodules: recursive # Fetch submodules
|
||||
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Step 2 Detect changes to Docs Content
|
||||
- name: Detect changes in doc content
|
||||
@@ -43,10 +50,6 @@ jobs:
|
||||
- 'docSite/content/docs/**'
|
||||
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)
|
||||
- name: Install Hugo
|
||||
uses: peaceiris/actions-hugo@v2
|
||||
@@ -58,39 +61,35 @@ jobs:
|
||||
- name: Build
|
||||
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
|
||||
# Step 5 - Push our generated site to Cloudflare
|
||||
- name: Deploy to Cloudflare Pages
|
||||
id: deploy
|
||||
uses: cloudflare/wrangler-action@v3
|
||||
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: '--local-config ../vercel.json' # Optional
|
||||
working-directory: docSite/public
|
||||
alias-domains: | #Optional
|
||||
fastgpt-staging.vercel.app
|
||||
docsOutput:
|
||||
needs: [deploy-preview]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
- name: Write md
|
||||
run: |
|
||||
echo "# 🤖 Generated by deploy action" > report.md
|
||||
echo "[👀 Visit Preview](${{ needs.deploy-preview.outputs.url }})" >> report.md
|
||||
cat report.md
|
||||
- name: Gh Rebot for Sealos
|
||||
uses: labring/gh-rebot@v0.0.6
|
||||
if: ${{ (github.event_name == 'pull_request_target') }}
|
||||
with:
|
||||
version: v0.0.6
|
||||
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
command: pages deploy ./docSite/public --project-name=fastgpt-doc
|
||||
packageManager: npm
|
||||
|
||||
- name: Create deployment status comment
|
||||
if: always()
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GH_PAT }}'
|
||||
SEALOS_TYPE: 'pr_comment'
|
||||
SEALOS_FILENAME: 'report.md'
|
||||
SEALOS_REPLACE_TAG: 'DEFAULT_REPLACE_DEPLOY'
|
||||
JOB_STATUS: ${{ job.status }}
|
||||
PREVIEW_URL: ${{ steps.deploy.outputs.deployment-url }}
|
||||
uses: actions/github-script@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
const success = process.env.JOB_STATUS === 'success';
|
||||
const deploymentUrl = `${process.env.PREVIEW_URL}`;
|
||||
const status = success ? '✅ Success' : '❌ Failed';
|
||||
console.log(process.env.JOB_STATUS);
|
||||
|
||||
const commentBody = `**Deployment Status: ${status}**
|
||||
${success ? `🔗 Preview URL: ${deploymentUrl}` : ''}`;
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
...context.repo,
|
||||
issue_number: context.payload.pull_request.number,
|
||||
body: commentBody
|
||||
});
|
||||
|
||||
11
.github/workflows/docs-sync_imgs.yml
vendored
@@ -1,6 +1,6 @@
|
||||
name: Sync images
|
||||
on:
|
||||
pull_request_target:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
@@ -15,13 +15,6 @@ jobs:
|
||||
sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
if: ${{ (github.event_name == 'pull_request_target') }}
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
@@ -32,4 +25,4 @@ jobs:
|
||||
CONFIG_PATH: .github/sync_imgs.yml
|
||||
ORIGINAL_MESSAGE: true
|
||||
SKIP_PR: true
|
||||
COMMIT_EACH_FILE: false
|
||||
COMMIT_EACH_FILE: false
|
||||
|
||||
@@ -9,7 +9,12 @@ on:
|
||||
- 'main'
|
||||
jobs:
|
||||
build-fastgpt-images:
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
runs-on: ubuntu-24.04
|
||||
if: github.repository != 'labring/FastGPT'
|
||||
steps:
|
||||
- name: Checkout
|
||||
@@ -32,7 +37,7 @@ jobs:
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GH_PAT }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
||||
run: |
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
||||
|
||||
301
.github/workflows/fastgpt-build-image.yml
vendored
@@ -1,258 +1,171 @@
|
||||
name: Build FastGPT images
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'projects/app/**'
|
||||
- 'packages/**'
|
||||
- "projects/app/**"
|
||||
- "packages/**"
|
||||
tags:
|
||||
- 'v*'
|
||||
- "v*"
|
||||
|
||||
jobs:
|
||||
build-fastgpt-images:
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
strategy:
|
||||
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:
|
||||
# install env
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
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
|
||||
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 }}-buildx-${{ github.sha }}
|
||||
key: ${{ runner.os }}-${{ matrix.sub_routes.repo }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
${{ runner.os }}-${{ matrix.sub_routes.repo }}-buildx-
|
||||
|
||||
# login docker
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GH_PAT }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login to Ali Hub
|
||||
uses: docker/login-action@v2
|
||||
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@v2
|
||||
uses: docker/login-action@v3
|
||||
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:latest" >> $GITHUB_ENV
|
||||
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: Build for ${{ matrix.archs.arch }}
|
||||
id: build
|
||||
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: Build and publish image for main branch or tag push event
|
||||
env:
|
||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||
- name: Export digest
|
||||
run: |
|
||||
docker buildx build \
|
||||
-f projects/app/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
|
||||
--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:
|
||||
runs-on: ubuntu-20.04
|
||||
mkdir -p ${{ runner.temp }}/digests/${{ matrix.sub_routes.repo }}
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "${{ runner.temp }}/digests/${{ matrix.sub_routes.repo }}/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-${{ matrix.sub_routes.repo }}-${{ github.sha }}-${{ matrix.archs.arch }}
|
||||
path: ${{ runner.temp }}/digests/${{ matrix.sub_routes.repo }}/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
release-fastgpt-images:
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
needs: build-fastgpt-images
|
||||
strategy:
|
||||
matrix:
|
||||
sub_routes:
|
||||
- repo: fastgpt
|
||||
- repo: fastgpt-sub-route
|
||||
- repo: fastgpt-sub-route-gchat
|
||||
runs-on: ubuntu-24.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
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GH_PAT }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login to Ali Hub
|
||||
uses: docker/login-action@v2
|
||||
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@v2
|
||||
uses: docker/login-action@v3
|
||||
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:latest" >> $GITHUB_ENV
|
||||
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
|
||||
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
|
||||
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route: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 }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/fastgpt-sub-route:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/fastgpt-sub-route:latest" >> $GITHUB_ENV
|
||||
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/fastgpt-sub-route:latest" >> $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 }}/fastgpt-sub-route: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=/fastai \
|
||||
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
|
||||
--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:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
# install env
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- name: Download digests
|
||||
uses: actions/download-artifact@v4
|
||||
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
|
||||
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@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-
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# login docker
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GH_PAT }}
|
||||
- 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
|
||||
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV
|
||||
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV
|
||||
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV
|
||||
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV
|
||||
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV
|
||||
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}: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
|
||||
echo "Git_Tag=ghcr.io/${{ github.repository_owner }}/${{ matrix.sub_routes.repo }}:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
echo "Git_Latest=ghcr.io/${{ github.repository_owner }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV
|
||||
echo "Ali_Tag=${{ secrets.ALI_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
echo "Ali_Latest=${{ secrets.ALI_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV
|
||||
echo "Docker_Hub_Tag=${{ secrets.DOCKER_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/${{ matrix.sub_routes.repo }}:latest" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Build and publish image for main branch or tag push event
|
||||
env:
|
||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
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} \
|
||||
.
|
||||
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 }}/${{ matrix.sub_routes.repo }}@sha256:%s ' *)
|
||||
sleep 5
|
||||
done
|
||||
|
||||
80
.github/workflows/fastgpt-preview-image.yml
vendored
@@ -5,63 +5,85 @@ on:
|
||||
|
||||
jobs:
|
||||
preview-fastgpt-images:
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
attestations: write
|
||||
id-token: write
|
||||
pull-requests: write
|
||||
|
||||
runs-on: ubuntu-24.04
|
||||
strategy:
|
||||
matrix:
|
||||
image: [fastgpt, sandbox, mcp_server]
|
||||
fail-fast: false # 即使一个镜像构建失败,也继续构建其他镜像
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.ref }}
|
||||
repository: ${{ github.event.pull_request.head.repo.full_name }}
|
||||
submodules: recursive # Fetch submodules
|
||||
fetch-depth: 0 # Fetch all history for .GitInfo and .Lastmod
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
driver-opts: network=host
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}-${{ matrix.image }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-${{ github.sha }}-
|
||||
${{ runner.os }}-buildx-
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GH_PAT }}
|
||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set image config
|
||||
id: config
|
||||
run: |
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:${{ github.event.pull_request.head.sha }}" >> $GITHUB_ENV
|
||||
- name: Build image for PR
|
||||
env:
|
||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||
if [[ "${{ matrix.image }}" == "fastgpt" ]]; then
|
||||
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
|
||||
run: |
|
||||
docker buildx build \
|
||||
-f projects/app/Dockerfile \
|
||||
-f ${{ steps.config.outputs.DOCKERFILE }} \
|
||||
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
|
||||
--label "org.opencontainers.image.description=fastgpt-pr image" \
|
||||
--label "org.opencontainers.image.licenses=Apache" \
|
||||
--label "org.opencontainers.image.description=${{ steps.config.outputs.DESCRIPTION }}" \
|
||||
--push \
|
||||
--cache-from=type=local,src=/tmp/.buildx-cache \
|
||||
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
||||
-t ${DOCKER_REPO_TAGGED} \
|
||||
-t ${{ steps.config.outputs.DOCKER_REPO_TAGGED }} \
|
||||
.
|
||||
# Add write md step after build
|
||||
- name: Write md
|
||||
run: |
|
||||
echo "# 🤖 Generated by deploy action" > report.md
|
||||
echo "📦 Preview Image: \`${DOCKER_REPO_TAGGED}\`" >> report.md
|
||||
cat report.md
|
||||
|
||||
- name: Gh Rebot for Sealos
|
||||
uses: labring/gh-rebot@v0.0.6
|
||||
if: ${{ (github.event_name == 'pull_request_target') }}
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
version: v0.0.6
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GH_PAT }}'
|
||||
SEALOS_TYPE: 'pr_comment'
|
||||
SEALOS_FILENAME: 'report.md'
|
||||
SEALOS_REPLACE_TAG: 'DEFAULT_REPLACE_DEPLOY'
|
||||
github-token: ${{secrets.GITHUB_TOKEN}}
|
||||
script: |
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: 'Preview ${{ matrix.image }} Image: `${{ steps.config.outputs.DOCKER_REPO_TAGGED }}`'
|
||||
})
|
||||
|
||||
3
.github/workflows/fastgpt-test.yaml
vendored
@@ -15,6 +15,9 @@ jobs:
|
||||
|
||||
steps:
|
||||
- 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
|
||||
with:
|
||||
version: 10
|
||||
|
||||
9
.github/workflows/helm-release.yaml
vendored
@@ -8,7 +8,12 @@ on:
|
||||
|
||||
jobs:
|
||||
helm:
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
attestations: write
|
||||
id-token: write
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
@@ -20,7 +25,7 @@ jobs:
|
||||
run: echo "tag=$(git describe --tags)" >> $GITHUB_OUTPUT
|
||||
- name: Release Helm
|
||||
run: |
|
||||
echo ${{ secrets.GH_PAT }} | helm registry login ghcr.io -u ${{ github.repository_owner }} --password-stdin
|
||||
echo ${{ secrets.GITHUB_TOKEN }} | helm registry login ghcr.io -u ${{ github.repository_owner }} --password-stdin
|
||||
export APP_VERSION=${{ steps.vars.outputs.tag }}
|
||||
export HELM_VERSION=${{ steps.vars.outputs.tag }}
|
||||
export HELM_REPO=ghcr.io/${{ github.repository_owner }}
|
||||
|
||||
151
.github/workflows/mcp_server-build-image.yml
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
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
|
||||
129
.github/workflows/sandbox-build-image.yml
vendored
@@ -8,50 +8,120 @@ on:
|
||||
- 'v*'
|
||||
jobs:
|
||||
build-fastgpt-sandbox-images:
|
||||
runs-on: ubuntu-20.04
|
||||
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: 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
|
||||
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 }}-buildx-${{ github.sha }}
|
||||
key: ${{ runner.os }}-sandbox-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
${{ runner.os }}-sandbox-buildx-
|
||||
|
||||
# login docker
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GH_PAT }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Login to Ali Hub
|
||||
uses: docker/login-action@v2
|
||||
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@v2
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_NAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||
|
||||
# Set tag
|
||||
- name: Build for ${{ matrix.arch }}
|
||||
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
|
||||
run: |
|
||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||
@@ -70,27 +140,12 @@ jobs:
|
||||
echo "Docker_Hub_Latest=${{ secrets.DOCKER_IMAGE_NAME }}/fastgpt-sandbox:latest" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Build and publish image for main branch or tag push event
|
||||
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 }}
|
||||
- name: Create manifest list and push
|
||||
working-directory: ${{ runner.temp }}/digests
|
||||
run: |
|
||||
docker buildx build \
|
||||
-f projects/sandbox/Dockerfile \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/fastgpt-sandbox" \
|
||||
--label "org.opencontainers.image.description=fastgpt-sandbox 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} \
|
||||
.
|
||||
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-sandbox@sha256:%s ' *)
|
||||
sleep 5
|
||||
done
|
||||
|
||||
39
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Next.js: debug server-side",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "pnpm run dev",
|
||||
"cwd": "${workspaceFolder}/projects/app"
|
||||
},
|
||||
{
|
||||
"name": "Next.js: debug client-side",
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:3000"
|
||||
},
|
||||
{
|
||||
"name": "Next.js: debug client-side (Edge)",
|
||||
"type": "msedge",
|
||||
"request": "launch",
|
||||
"url": "http://localhost:3000"
|
||||
},
|
||||
{
|
||||
"name": "Next.js: debug full stack",
|
||||
"type": "node-terminal",
|
||||
"request": "launch",
|
||||
"command": "pnpm run dev",
|
||||
"cwd": "${workspaceFolder}/projects/app",
|
||||
"skipFiles": ["<node_internals>/**"],
|
||||
"serverReadyAction": {
|
||||
"action": "debugWithEdge",
|
||||
"killOnServerStop": true,
|
||||
"pattern": "- Local:.+(https?://.+)",
|
||||
"uriFormat": "%s",
|
||||
"webRoot": "${workspaceFolder}/projects/app"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
72
.vscode/nextapi.code-snippets
vendored
@@ -52,71 +52,17 @@
|
||||
"description": "FastGPT usecontext template"
|
||||
},
|
||||
|
||||
"Jest test template": {
|
||||
"scope": "typescriptreact",
|
||||
"prefix": "jesttest",
|
||||
"Vitest test case template": {
|
||||
"scope": "typescript",
|
||||
"prefix": "template_test",
|
||||
"body": [
|
||||
"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';",
|
||||
"import { describe, it, expect } from 'vitest';",
|
||||
"",
|
||||
"// Import the schema",
|
||||
"import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';",
|
||||
"",
|
||||
"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);",
|
||||
"describe('authType2UsageSource', () => {",
|
||||
" it('Test description', () => {",
|
||||
" expect().toBe();",
|
||||
" });",
|
||||
"});"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
2
Makefile
@@ -17,7 +17,7 @@ dev:
|
||||
|
||||
build:
|
||||
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)
|
||||
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
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
<a href="./README_ja.md">日语</a>
|
||||
</p>
|
||||
|
||||
FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景!
|
||||
FastGPT 是一个 AI Agent 构建平台,提供开箱即用的数据处理、模型调用等能力,同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的应用场景!
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@@ -110,19 +110,31 @@ services:
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: redis
|
||||
# ports:
|
||||
# - 6379:6379
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.5 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.5 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -157,6 +169,8 @@ services:
|
||||
# zilliz 连接参数
|
||||
- MILVUS_ADDRESS=http://milvusStandalone:19530
|
||||
- MILVUS_TOKEN=none
|
||||
# Redis 地址
|
||||
- REDIS_URL=redis://default:mypassword@redis:6379
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
@@ -170,6 +184,8 @@ services:
|
||||
- ALLOWED_ORIGINS=
|
||||
# 是否开启IP限制,默认不开启
|
||||
- USE_IP_LIMIT=false
|
||||
# 对话文件过期天数
|
||||
- CHAT_FILE_EXPIRE_TIME=7
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
|
||||
|
||||
202
deploy/docker/docker-compose-oceanbase/docker-compose.yml
Normal file
@@ -0,0 +1,202 @@
|
||||
# 数据库的默认账号和密码仅首次运行时设置有效
|
||||
# 如果修改了账号密码,记得改数据库和项目连接参数,别只改一处~
|
||||
# 该配置文件只是给快速启动,测试使用。正式使用,记得务必修改账号密码,以及调整合适的知识库参数,共享内存等。
|
||||
# 如何无法访问 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:
|
||||
2
deploy/docker/docker-compose-oceanbase/init.sql
Normal file
@@ -0,0 +1,2 @@
|
||||
ALTER SYSTEM SET ob_vector_memory_limit_percentage = 30;
|
||||
|
||||
@@ -69,18 +69,31 @@ services:
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: redis
|
||||
# ports:
|
||||
# - 6379:6379
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
|
||||
# fastgpt
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.5 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.5 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -114,6 +127,8 @@ services:
|
||||
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
# pg 连接参数
|
||||
- PG_URL=postgresql://username:password@pg:5432/postgres
|
||||
# Redis 连接参数
|
||||
- REDIS_URL=redis://default:mypassword@redis:6379
|
||||
# sandbox 地址
|
||||
- SANDBOX_URL=http://sandbox:3000
|
||||
# 日志等级: debug, info, warn, error
|
||||
@@ -127,12 +142,14 @@ services:
|
||||
- ALLOWED_ORIGINS=
|
||||
# 是否开启IP限制,默认不开启
|
||||
- USE_IP_LIMIT=false
|
||||
# 对话文件过期天数
|
||||
- CHAT_FILE_EXPIRE_TIME=7
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
|
||||
# AI Proxy
|
||||
aiproxy:
|
||||
image: ghcr.io/labring/aiproxy:v0.1.3
|
||||
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
|
||||
|
||||
@@ -51,17 +51,30 @@ services:
|
||||
|
||||
# 等待docker-entrypoint.sh脚本执行的MongoDB服务进程
|
||||
wait $$!
|
||||
redis:
|
||||
image: redis:7.2-alpine
|
||||
container_name: redis
|
||||
# ports:
|
||||
# - 6379:6379
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
command: |
|
||||
redis-server --requirepass mypassword --loglevel warning --maxclients 10000 --appendonly yes --save 60 10 --maxmemory 4gb --maxmemory-policy noeviction
|
||||
volumes:
|
||||
- ./redis/data:/data
|
||||
|
||||
sandbox:
|
||||
container_name: sandbox
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.1-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt-sandbox:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt-sandbox:v4.9.5 # 阿里云
|
||||
networks:
|
||||
- fastgpt
|
||||
restart: always
|
||||
fastgpt:
|
||||
container_name: fastgpt
|
||||
image: ghcr.io/labring/fastgpt:v4.9.1-fix2 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.1-fix2 # 阿里云
|
||||
image: ghcr.io/labring/fastgpt:v4.9.5 # git
|
||||
# image: registry.cn-hangzhou.aliyuncs.com/fastgpt/fastgpt:v4.9.5 # 阿里云
|
||||
ports:
|
||||
- 3000:3000
|
||||
networks:
|
||||
@@ -92,6 +105,8 @@ services:
|
||||
- FILE_TOKEN_KEY=filetoken
|
||||
# MongoDB 连接参数. 用户名myusername,密码mypassword。
|
||||
- MONGODB_URI=mongodb://myusername:mypassword@mongo:27017/fastgpt?authSource=admin
|
||||
# Redis 连接参数
|
||||
- REDIS_URI=redis://default:mypassword@redis:6379
|
||||
# zilliz 连接参数
|
||||
- MILVUS_ADDRESS=zilliz_cloud_address
|
||||
- MILVUS_TOKEN=zilliz_cloud_token
|
||||
@@ -108,6 +123,8 @@ services:
|
||||
- ALLOWED_ORIGINS=
|
||||
# 是否开启IP限制,默认不开启
|
||||
- USE_IP_LIMIT=false
|
||||
# 对话文件过期天数
|
||||
- CHAT_FILE_EXPIRE_TIME=7
|
||||
volumes:
|
||||
- ./config.json:/app/data/config.json
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 205 KiB |
BIN
docSite/assets/imgs/chunkReader1.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
docSite/assets/imgs/chunkReader2.jpg
Normal file
|
After Width: | Height: | Size: 159 KiB |
BIN
docSite/assets/imgs/chunkReader3.webp
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
docSite/assets/imgs/chunkReader4.jpg
Normal file
|
After Width: | Height: | Size: 139 KiB |
BIN
docSite/assets/imgs/chunkReader5.jpg
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
docSite/assets/imgs/chunkReader6.png
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
docSite/assets/imgs/chunkReader7.jpg
Normal file
|
After Width: | Height: | Size: 44 KiB |
BIN
docSite/assets/imgs/chunkReader8.png
Normal file
|
After Width: | Height: | Size: 197 KiB |
BIN
docSite/assets/imgs/chunkReader9.jpg
Normal file
|
After Width: | Height: | Size: 120 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 172 KiB |
|
After Width: | Height: | Size: 93 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 73 KiB |
BIN
docSite/assets/imgs/intro/image1.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
docSite/assets/imgs/intro/image2.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
docSite/assets/imgs/intro/image3.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
docSite/assets/imgs/intro/image4.png
Normal file
|
After Width: | Height: | Size: 469 KiB |
BIN
docSite/assets/imgs/intro/image5.png
Normal file
|
After Width: | Height: | Size: 193 KiB |
BIN
docSite/assets/imgs/intro/image6.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
docSite/assets/imgs/intro/image7.png
Normal file
|
After Width: | Height: | Size: 318 KiB |
BIN
docSite/assets/imgs/intro/image8.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
|
Before Width: | Height: | Size: 199 KiB |
|
Before Width: | Height: | Size: 167 KiB |
|
Before Width: | Height: | Size: 239 KiB |
|
Before Width: | Height: | Size: 98 KiB |
BIN
docSite/assets/imgs/sealos-redis1.png
Normal file
|
After Width: | Height: | Size: 284 KiB |
BIN
docSite/assets/imgs/sealos-redis2.png
Normal file
|
After Width: | Height: | Size: 294 KiB |
BIN
docSite/assets/imgs/sealos-redis3.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
@@ -31,9 +31,9 @@ weight: 920
|
||||
|
||||
3 个模型代码分别为:
|
||||
|
||||
1. [https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-base](https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-base)
|
||||
2. [https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-large](https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-large)
|
||||
3. [https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-v2-m3](https://github.com/labring/FastGPT/tree/main/plugins/rerank-bge/bge-reranker-v2-m3)
|
||||
1. [https://github.com/labring/FastGPT/tree/main/plugins/model/rerank-bge/bge-reranker-base](https://github.com/labring/FastGPT/tree/main/plugins/model/rerank-bge/bge-reranker-base)
|
||||
2. [https://github.com/labring/FastGPT/tree/main/plugins/model/rerank-bge/bge-reranker-large](https://github.com/labring/FastGPT/tree/main/plugins/model/rerank-bge/bge-reranker-large)
|
||||
3. [https://github.com/labring/FastGPT/tree/main/plugins/model/rerank-bge/bge-reranker-v2-m3](https://github.com/labring/FastGPT/tree/main/plugins/model/rerank-bge/bge-reranker-v2-m3)
|
||||
|
||||
### 3. 安装依赖
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ ChatGLM2-6B 是开源中英双语对话模型 ChatGLM-6B 的第二代版本,
|
||||
### 源码部署
|
||||
|
||||
1. 根据上面的环境配置配置好环境,具体教程自行 GPT;
|
||||
2. 下载 [python 文件](https://github.com/labring/FastGPT/blob/main/files/models/ChatGLM2/openai_api.py)
|
||||
2. 下载 [python 文件](https://github.com/labring/FastGPT/blob/main/plugins/model/llm-ChatGLM2/openai_api.py)
|
||||
3. 在命令行输入命令 `pip install -r requirements.txt`;
|
||||
4. 打开你需要启动的 py 文件,在代码的 `verify_token` 方法中配置 token,这里的 token 只是加一层验证,防止接口被人盗用;
|
||||
5. 执行命令 `python openai_api.py --model_name 16`。这里的数字根据上面的配置进行选择。
|
||||
|
||||
@@ -135,6 +135,9 @@ curl -O https://raw.githubusercontent.com/labring/FastGPT/main/projects/app/data
|
||||
|
||||
# pgvector 版本(测试推荐,简单快捷)
|
||||
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 版本
|
||||
# curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/main/deploy/docker/docker-compose-milvus.yml
|
||||
# zilliz 版本
|
||||
@@ -151,6 +154,13 @@ curl -o docker-compose.yml https://raw.githubusercontent.com/labring/FastGPT/mai
|
||||
|
||||
无需操作
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< tab tabName="Oceanbase版本" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
无需操作
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< tab tabName="Milvus版本" >}}
|
||||
|
||||
@@ -18,12 +18,14 @@ weight: 852
|
||||
{{% alert icon="🤖 " context="success" %}}
|
||||
* 该接口的 API Key 需使用`应用特定的 key`,否则会报错。
|
||||
|
||||
<!-- * 对话现在有`v1`和`v2`两个接口,可以按需使用,v2 自 4.9.4 版本新增,v1 接口同时不再维护 -->
|
||||
|
||||
* 有些包调用时,`BaseUrl`需要添加`v1`路径,有些不需要,如果出现404情况,可补充`v1`重试。
|
||||
{{% /alert %}}
|
||||
|
||||
## 请求简易应用和工作流
|
||||
|
||||
对话接口兼容`GPT`的接口!如果你的项目使用的是标准的`GPT`官方接口,可以直接通过修改`BaseUrl`和 `Authorization`来访问 FastGpt 应用,不过需要注意下面几个规则:
|
||||
`v1`对话接口兼容`GPT`的接口!如果你的项目使用的是标准的`GPT`官方接口,可以直接通过修改`BaseUrl`和 `Authorization`来访问 FastGpt 应用,不过需要注意下面几个规则:
|
||||
|
||||
{{% alert icon="🤖 " context="success" %}}
|
||||
* 传入的`model`,`temperature`等参数字段均无效,这些字段由编排决定,不会根据 API 参数改变。
|
||||
@@ -65,7 +67,7 @@ curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
|
||||
{{< markdownify >}}
|
||||
|
||||
* 仅`messages`有部分区别,其他参数一致。
|
||||
* 目前不支持上次文件,需上传到自己的对象存储中,获取对应的文件链接。
|
||||
* 目前不支持上传文件,需上传到自己的对象存储中,获取对应的文件链接。
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
|
||||
@@ -116,14 +118,284 @@ curl --location --request POST 'http://localhost:3000/api/v1/chat/completions' \
|
||||
- variables: 模块变量,一个对象,会替换模块中,输入框内容里的`{{key}}`
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
<!-- #### v2
|
||||
|
||||
v1,v2 接口请求参数一致,仅请求地址不一样。
|
||||
|
||||
{{< tabs tabTotal="3" >}}
|
||||
{{< tab tabName="基础请求示例" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'http://localhost:3000/api/v2/chat/completions' \
|
||||
--header 'Authorization: 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/v2/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 。如果传入,则会将该值作为本次对话的响应消息的 ID,FastGPT 会自动将该 ID 存入数据库。请确保,在当前`chatId`下,`responseChatItemId`是唯一的。
|
||||
- detail: 是否返回中间值(模块状态,响应的完整结果等),`stream模式`下会通过`event`进行区分,`非stream模式`结果保存在`responseData`中。
|
||||
- variables: 模块变量,一个对象,会替换模块中,输入框内容里的`{{key}}`
|
||||
{{% /alert %}}
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
#### v1
|
||||
|
||||
|
||||
|
||||
### 响应
|
||||
|
||||
#### v2
|
||||
|
||||
v2 接口比起 v1,主要变变化在于:会在每个节点运行结束后及时返回 response,而不是等工作流结束后再统一返回。
|
||||
|
||||
{{< tabs tabTotal="5" >}}
|
||||
{{< tab tabName="detail=false,stream=false 响应" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "",
|
||||
"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=false,stream=true 响应" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
|
||||
```bash
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"你好"},"index":0,"finish_reason":null}]}
|
||||
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"!"},"index":0,"finish_reason":null}]}
|
||||
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"今天"},"index":0,"finish_reason":null}]}
|
||||
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"过得怎么样?"},"index":0,"finish_reason":null}]}
|
||||
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":null},"index":0,"finish_reason":"stop"}]}
|
||||
|
||||
data: [DONE]
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="detail=true,stream=false 响应" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
```json
|
||||
{
|
||||
"responseData": [
|
||||
{
|
||||
"id": "iSol79OFrBH1I9kC",
|
||||
"nodeId": "448745",
|
||||
"moduleName": "common:core.module.template.work_start",
|
||||
"moduleType": "workflowStart",
|
||||
"runningTime": 0
|
||||
},
|
||||
{
|
||||
"id": "t1T94WCy6Su3BK4V",
|
||||
"nodeId": "fjLpE3XPegmoGtbU",
|
||||
"moduleName": "AI 对话",
|
||||
"moduleType": "chatNode",
|
||||
"runningTime": 1.46,
|
||||
"totalPoints": 0,
|
||||
"model": "GPT-4o-mini",
|
||||
"tokens": 64,
|
||||
"inputTokens": 10,
|
||||
"outputTokens": 54,
|
||||
"query": "你是谁",
|
||||
"reasoningText": "",
|
||||
"historyPreview": [
|
||||
{
|
||||
"obj": "Human",
|
||||
"value": "你是谁"
|
||||
},
|
||||
{
|
||||
"obj": "AI",
|
||||
"value": "我是一个人工智能助手,旨在帮助回答问题和提供信息。如果你有任何问题或需要帮助,请告诉我!"
|
||||
}
|
||||
],
|
||||
"contextTotalLen": 2
|
||||
}
|
||||
],
|
||||
"newVariables": {
|
||||
|
||||
},
|
||||
"id": "",
|
||||
"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 >}}
|
||||
|
||||
```bash
|
||||
event: flowNodeResponse
|
||||
data: {"id":"iYv2uA9rCWAtulWo","nodeId":"workflowStartNodeId","moduleName":"流程开始","moduleType":"workflowStart","runningTime":0}
|
||||
|
||||
event: flowNodeStatus
|
||||
data: {"status":"running","name":"AI 对话"}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"你好"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"!"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"今天"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":"过得怎么样?"},"index":0,"finish_reason":null}]}
|
||||
|
||||
event: flowNodeResponse
|
||||
data: {"id":"pVzLBF7M3Ol4n7s6","nodeId":"ixe20AHN3jy74pKf","moduleName":"AI 对话","moduleType":"chatNode","runningTime":1.48,"totalPoints":0.0042,"model":"Qwen-plus","tokens":28,"inputTokens":8,"outputTokens":20,"query":"你好","reasoningText":"","historyPreview":[{"obj":"Human","value":"你好"},{"obj":"AI","value":"你好!今天过得怎么样?"}],"contextTotalLen":2}
|
||||
|
||||
event: answer
|
||||
data: {"id":"","object":"","created":0,"model":"","choices":[{"delta":{"role":"assistant","content":null},"index":0,"finish_reason":"stop"}]}
|
||||
|
||||
event: answer
|
||||
data: [DONE]
|
||||
```
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
|
||||
{{< tab tabName="event值" >}}
|
||||
{{< markdownify >}}
|
||||
|
||||
event取值:
|
||||
|
||||
- answer: 返回给客户端的文本(最终会算作回答)
|
||||
- fastAnswer: 指定回复返回给客户端的文本(最终会算作回答)
|
||||
- toolCall: 执行工具
|
||||
- toolParams: 工具参数
|
||||
- toolResponse: 工具返回
|
||||
- flowNodeStatus: 运行到的节点状态
|
||||
- flowNodeResponse: 单个节点详细响应
|
||||
- updateVariables: 更新变量
|
||||
- error: 报错
|
||||
|
||||
{{< /markdownify >}}
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
#### v1 -->
|
||||
|
||||
{{< tabs tabTotal="5" >}}
|
||||
{{< tab tabName="detail=false,stream=false 响应" >}}
|
||||
{{< markdownify >}}
|
||||
@@ -648,8 +920,6 @@ event取值:
|
||||
{{< /tab >}}
|
||||
{{< /tabs >}}
|
||||
|
||||
|
||||
|
||||
# 对话 CRUD
|
||||
|
||||
{{% alert icon="🤖 " context="success" %}}
|
||||
|
||||
@@ -138,7 +138,7 @@ FastGPT 商业版共包含了2个应用(fastgpt, fastgpt-plus)和2个数据
|
||||
SYSTEM_NAME=FastGPT
|
||||
SYSTEM_DESCRIPTION=
|
||||
SYSTEM_FAVICON=/favicon.ico
|
||||
HOME_URL=/app/list
|
||||
HOME_URL=/dashboard/apps
|
||||
```
|
||||
|
||||
SYSTEM_FAVICON 可以是一个网络地址
|
||||
|
||||
@@ -39,7 +39,7 @@ curl --location --request POST 'https://{{host}}/api/admin/initv491' \
|
||||
3. API 知识库支持 PDF 增强解析。
|
||||
4. 邀请团队成员,改为邀请链接模式。
|
||||
5. 支持混合检索权重设置。
|
||||
6. 支持重排模型选择和权重设置,同时调整了知识库搜索权重计算方式,改成 搜索权重 + 重排权重,而不是向量检索权重+全文检索权重+重排权重。
|
||||
6. 支持重排模型选择和权重设置,同时调整了知识库搜索权重计算方式,改成 搜索权重 + 重排权重,而不是向量检索权重+全文检索权重+重排权重。会对检索结果有一定影响,可以通过调整相关权重来进行数据适配。
|
||||
|
||||
## ⚙️ 优化
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: 'V4.9.2(进行中)'
|
||||
title: 'V4.9.2'
|
||||
description: 'FastGPT V4.9.2 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
@@ -8,20 +8,31 @@ weight: 798
|
||||
---
|
||||
## 更新指南
|
||||
|
||||
### 配置参数变更
|
||||
可直接升级v4.9.3,v4.9.2存在一个工作流数据类型转化错误。
|
||||
|
||||
修改`config.json`文件中`systemEnv.pgHNSWEfSearch`参数名,改成`hnswEfSearch`。
|
||||
商业版用户直接在后台`系统配置-基础配置`中进行变更。
|
||||
### 1. 做好数据库备份
|
||||
|
||||
### SSO 迁移
|
||||
### 2. SSO 迁移
|
||||
|
||||
使用了 SSO 或成员同步的商业版用户,并且是对接`钉钉`、`企微`的,需要迁移已有的 SSO 相关配置:
|
||||
|
||||
参考:[SSO & 外部成员同步](/docs/guide/admin/sso.md)中的配置进行`sso-service`的部署和配置。
|
||||
参考:[SSO & 外部成员同步](/docs/guide/admin/sso)中的配置进行`sso-service`的部署和配置。
|
||||
|
||||
1. 先将原商业版后台中的相关配置项复制备份出来(以企微为例,将 AppId, Secret 等复制出来)再进行镜像升级。
|
||||
2. 参考上述文档,部署 SSO 服务,配置相关的环境变量
|
||||
3. 如果原先使用企微组织架构同步的用户,在商业版后台切换团队模式为“同步模式”
|
||||
3. 如果原先使用企微组织架构同步的用户,升级完镜像后,需要在商业版后台切换团队模式为“同步模式”
|
||||
|
||||
### 3. 配置参数变更
|
||||
|
||||
修改`config.json`文件中`systemEnv.pgHNSWEfSearch`参数名,改成`hnswEfSearch`。
|
||||
商业版用户升级镜像后,直接在后台`系统配置-基础配置`中进行变更。
|
||||
|
||||
### 4. 更新镜像
|
||||
|
||||
- 更新 FastGPT 镜像 tag: v4.9.2
|
||||
- 更新 FastGPT 商业版镜像 tag: v4.9.2
|
||||
- Sandbox 镜像,可以不更新
|
||||
- AIProxy 镜像修改为: registry.cn-hangzhou.aliyuncs.com/labring/aiproxy:v0.1.4
|
||||
|
||||
## 重要更新
|
||||
|
||||
@@ -35,6 +46,8 @@ weight: 798
|
||||
4. 集合同步时,支持同步修改标题。
|
||||
5. 团队成员管理重构,抽离主流 IM SSO(企微、飞书、钉钉),并支持通过自定义 SSO 接入 FastGPT。同时完善与外部系统的成员同步。
|
||||
6. 支持 `oceanbase` 向量数据库。填写环境变量`OCEANBASE_URL`即可。
|
||||
7. 基于 mistral-ocr 的 PDF 解析示例。
|
||||
8. 基于 miner-u 的 PDF 解析示例。
|
||||
|
||||
## ⚙️ 优化
|
||||
|
||||
|
||||
29
docSite/content/zh-cn/docs/development/upgrading/493.md
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: 'V4.9.3'
|
||||
description: 'FastGPT V4.9.3 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 797
|
||||
---
|
||||
|
||||
## 更新指南
|
||||
|
||||
### 1. 做好数据库备份
|
||||
|
||||
### 2. 更新镜像
|
||||
|
||||
- 更新 FastGPT 镜像 tag: v4.9.3
|
||||
- 更新 FastGPT 商业版镜像 tag: v4.9.3
|
||||
- Sandbox 镜像tag: v4.9.3
|
||||
- AIProxy 镜像tag: v0.1.5
|
||||
|
||||
|
||||
## 🚀 新增内容
|
||||
|
||||
1. 工作流 debug 模式支持交互节点。
|
||||
2. 代码运行支持 Python3 代码。
|
||||
|
||||
## 🐛 修复
|
||||
|
||||
1. 工作流格式转化异常。
|
||||
66
docSite/content/zh-cn/docs/development/upgrading/494.md
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: 'V4.9.4'
|
||||
description: 'FastGPT V4.9.4 更新说明'
|
||||
icon: 'upgrade'
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 796
|
||||
---
|
||||
|
||||
## 升级指南
|
||||
|
||||
### 1. 做好数据备份
|
||||
|
||||
### 2. 安装 Redis
|
||||
|
||||
* docker 部署的用户,参考最新的 `docker-compose.yml` 文件增加 Redis 配置。增加一个 redis 容器,并配置`fastgpt`,`fastgpt-pro`的环境变量,增加 `REDIS_URL` 环境变量。
|
||||
* Sealos 部署的用户,在数据库里新建一个`redis`数据库,并复制`内网地址的 connection` 作为 `redis` 的链接串。然后配置`fastgpt`,`fastgpt-pro`的环境变量,增加 `REDIS_URL` 环境变量。
|
||||
|
||||
| | | |
|
||||
| --- | --- | --- |
|
||||
|  |  |  |
|
||||
|
||||
### 3. 更新镜像 tag
|
||||
|
||||
- 更新 FastGPT 镜像 tag: v4.9.4
|
||||
- 更新 FastGPT 商业版镜像 tag: v4.9.4
|
||||
- Sandbox 无需更新
|
||||
- AIProxy 无需更新
|
||||
|
||||
### 4. 执行升级脚本
|
||||
|
||||
该脚本仅需商业版用户执行。
|
||||
|
||||
从任意终端,发起 1 个 HTTP 请求。其中 {{rootkey}} 替换成环境变量里的 `rootkey`;{{host}} 替换成**FastGPT 域名**。
|
||||
|
||||
```bash
|
||||
curl --location --request POST 'https://{{host}}/api/admin/initv494' \
|
||||
--header 'rootkey: {{rootkey}}' \
|
||||
--header 'Content-Type: application/json'
|
||||
```
|
||||
|
||||
**脚本功能**
|
||||
|
||||
1. 更新站点同步定时器
|
||||
|
||||
## 🚀 新增内容
|
||||
|
||||
1. 集合数据训练状态展示
|
||||
2. SMTP 发送邮件插件
|
||||
3. BullMQ 消息队列。
|
||||
4. 利用 redis 进行部分数据缓存。
|
||||
5. 站点同步支持配置训练参数和增量同步。
|
||||
6. AI 对话/工具调用,增加返回模型 finish_reason 字段,便于追踪模型输出中断原因。
|
||||
7. 移动端语音输入交互调整
|
||||
|
||||
## ⚙️ 优化
|
||||
|
||||
1. Admin 模板渲染调整。
|
||||
2. 支持环境变量配置对话文件过期时间。
|
||||
3. MongoDB log 库可独立部署。
|
||||
|
||||
## 🐛 修复
|
||||
|
||||
1. 搜索应用/知识库时,无法点击目录进入下一层。
|
||||
2. 重新训练时,参数未成功初始化。
|
||||
3. package/service 部分请求在多 app 中不一致。
|
||||
39
docSite/content/zh-cn/docs/development/upgrading/495.md
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
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 模式交互节点下一步可能造成死循环。
|
||||
32
docSite/content/zh-cn/docs/development/upgrading/496.md
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
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 调用失败。
|
||||
88
docSite/content/zh-cn/docs/guide/DialogBoxes/quoteList.md
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
title: '知识库引用分块阅读器'
|
||||
description: 'FastGPT 分块阅读器功能介绍'
|
||||
icon: 'description'
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 480
|
||||
---
|
||||
在企业 AI 应用落地过程中,文档知识引用的精确性和透明度一直是用户关注的焦点。FastGPT 4.9.1 版本带来的知识库分块阅读器,巧妙解决了这一痛点,让 AI 引用不再是"黑盒"。
|
||||
|
||||
# 为什么需要分块阅读器?
|
||||
|
||||
传统的 AI 对话中,当模型引用企业知识库内容时,用户往往只能看到被引用的片段,无法获取完整语境,这给内容验证和深入理解带来了挑战。分块阅读器的出现,让用户可以在对话中直接查看引用内容的完整文档,并精确定位到引用位置,实现了引用的"可解释性"。
|
||||
|
||||
## 传统引用体验的局限
|
||||
|
||||
以往在知识库中上传文稿后,当我们在工作流中输入问题时,传统的引用方式只会展示引用到的分块,无法确认分块在文章中的上下文:
|
||||
|
||||
| 问题 | 引用 |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
## FastGPT 分块阅读器:精准定位,无缝阅读
|
||||
|
||||
而在 FastGPT 全新的分块式阅读器中,同样的知识库内容和问题,呈现方式发生了质的飞跃
|
||||
|
||||

|
||||
|
||||
当 AI 引用知识库内容时,用户只需点击引用链接,即可打开一个浮窗,呈现完整的原文内容,并通过醒目的高亮标记精确显示引用的文本片段。这既保证了回答的可溯源性,又提供了便捷的原文查阅体验。
|
||||
|
||||
# 核心功能
|
||||
|
||||
## 全文展示与定位
|
||||
|
||||
"分块阅读器" 让用户能直观查看AI回答引用的知识来源。
|
||||
|
||||
在对话界面中,当 AI 引用了知识库内容,系统会在回复下方展示出处信息。用户只需点击这些引用链接,即可打开一个优雅的浮窗,呈现完整的原文内容,并通过醒目的高亮标记精确显示 AI 引用的文本片段。
|
||||
|
||||
这一设计既保证了回答的可溯源性,又提供了便捷的原文查阅体验,让用户能轻松验证AI回答的准确性和相关上下文。
|
||||
|
||||

|
||||
|
||||
|
||||
## 便捷引用导航
|
||||
|
||||
分块阅读器右上角设计了简洁实用的导航控制,用户可以通过这对按钮轻松在多个引用间切换浏览。导航区还直观显示当前查看的引用序号及总引用数量(如 "7/10"),帮助用户随时了解浏览进度和引用内容的整体规模。
|
||||
|
||||

|
||||
|
||||
## 引用质量评分
|
||||
|
||||
每条引用内容旁边都配有智能评分标签,直观展示该引用在所有知识片段中的相关性排名。用户只需将鼠标悬停在评分标签上,即可查看完整的评分详情,了解这段引用内容为何被AI选中以及其相关性的具体构成。
|
||||
|
||||

|
||||
|
||||
|
||||
## 文档内容一键导出
|
||||
|
||||
分块阅读器贴心配备了内容导出功能,让有效信息不再流失。只要用户拥有相应知识库的阅读权限,便可通过简单点击将引用涉及的全文直接保存到本地设备。
|
||||
|
||||

|
||||
|
||||
# 进阶特性
|
||||
|
||||
## 灵活的可见度控制
|
||||
|
||||
FastGPT提供灵活的引用可见度设置,让知识共享既开放又安全。以免登录链接为例,管理员可精确控制外部访问者能看到的信息范围。
|
||||
|
||||
当设置为"仅引用内容可见"时,外部用户点击引用链接将只能查看 AI 引用的特定文本片段,而非完整原文档。如图所示,分块阅读器此时智能调整显示模式,仅呈现相关引用内容。
|
||||
|
||||
| | |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
## 即时标注优化
|
||||
|
||||
在浏览过程中,授权用户可以直接对引用内容进行即时标注和修正,系统会智能处理这些更新而不打断当前的对话体验。所有修改过的内容会通过醒目的"已更新"标签清晰标识,既保证了引用的准确性,又维持了对话历史的完整性。
|
||||
|
||||
这一无缝的知识优化流程特别适合团队协作场景,让知识库能在实际使用过程中持续进化,确保AI回答始终基于最新、最准确的信息源。
|
||||
|
||||
## 智能文档性能优化
|
||||
|
||||
面对现实业务中可能包含成千上万分块的超长文档,FastGPT采用了先进的性能优化策略,确保分块阅读器始终保持流畅响应。
|
||||
|
||||
系统根据引用相关性排序和数据库索引进行智能加载管理,实现了"按需渲染"机制——根据索引排序和数据库 id,只有当用户实际需要查看的内容才会被加载到内存中。这意味着无论是快速跳转到特定引用,还是自然滚动浏览文档,都能获得丝滑的用户体验,不会因为文档体积庞大而出现卡顿或延迟。
|
||||
|
||||
这一技术优化使FastGPT能够轻松应对企业级的大规模知识库场景,让即使是包含海量信息的专业文档也能高效展示和查阅。
|
||||
|
||||
@@ -61,11 +61,12 @@ FastGPT-SSO-Service 是为了聚合不同来源的 SSO 和成员同步接口,
|
||||
|
||||
#### 1. 配置环境变量
|
||||
|
||||
环境变量中的 `EXTERNAL_USER_SERVICE_BASE_URL` 为内网地址,例如上述例子中的配置,环境变量应该设置为
|
||||
环境变量中的 `EXTERNAL_USER_SYSTEM_BASE_URL` 为内网地址,例如上述例子中的配置,环境变量应该设置为
|
||||
|
||||
```yaml
|
||||
EXTERNAL_USER_SERVICE_BASE_URL=http://fastgpt-sso:3000
|
||||
EXTERNAL_USER_SERVICE_AUTH_TOKEN=xxxxx
|
||||
env:
|
||||
- EXTERNAL_USER_SYSTEM_BASE_URL=http://fastgpt-sso:3000
|
||||
- EXTERNAL_USER_SYSTEM_AUTH_TOKEN=xxxxx
|
||||
```
|
||||
|
||||
#### 2. 在商业版后台配置按钮文字,图标等。
|
||||
@@ -89,7 +90,8 @@ EXTERNAL_USER_SERVICE_AUTH_TOKEN=xxxxx
|
||||
设置 fastgpt-pro 环境变量则可开启自动成员同步
|
||||
|
||||
```bash
|
||||
SYNC_MEMBER_CRON="0 0 * * *" # Cron 表达式,每天 0 点执行
|
||||
env:
|
||||
- "SYNC_MEMBER_CRON=0 0 * * *" # Cron 表达式,每天 0 点执行
|
||||
```
|
||||
|
||||
## 内置的通用协议/IM 配置示例
|
||||
|
||||
@@ -5,4 +5,207 @@ icon: "group"
|
||||
draft: false
|
||||
toc: true
|
||||
weight: 450
|
||||
---
|
||||
---
|
||||
|
||||
# 团队 & 成员组 & 权限
|
||||
|
||||
## 权限系统简介
|
||||
|
||||
FastGPT
|
||||
权限系统融合了基于**属性**和基于**角色**的权限管理范式,为团队协作提供精细化的权限控制方案。通过**成员、部门和群组**三种管理模式,您可以灵活配置对团队、应用和知识库等资源的访问权限。
|
||||
|
||||
## 团队
|
||||
|
||||
每位用户可以同时归属于多个团队,系统默认为每位用户创建一个初始团队。目前暂不支持用户手动创建额外团队。
|
||||
|
||||
## 权限管理
|
||||
|
||||
FastGPT 提供三种权限管理维度:
|
||||
|
||||
**成员权限**:最高优先级,直接赋予个人的权限
|
||||
|
||||
**部门与群组权限**:采用权限并集原则,优先级低于成员权限
|
||||
|
||||
权限判定遵循以下逻辑:
|
||||
|
||||
首先检查用户的个人成员权限
|
||||
|
||||
其次检查用户所属部门和群组的权限(取并集)
|
||||
|
||||
最终权限为上述结果的组合
|
||||
|
||||
鉴权逻辑如下:
|
||||
|
||||

|
||||
|
||||
### 资源权限
|
||||
|
||||
对于不同的**资源**,有不同的权限。
|
||||
|
||||
这里说的资源,是指应用、知识库、团队等等概念。
|
||||
|
||||
下表为不同资源,可以进行管理的权限。
|
||||
|
||||
<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>
|
||||
|
||||
### 协作者
|
||||
|
||||
必须先添加**协作者**,才能对其进行权限管理:
|
||||
|
||||

|
||||
|
||||
管理团队权限时,需先选择成员/组织/群组,再进行权限配置。
|
||||
|
||||

|
||||
|
||||
对于应用和知识库等资源,可直接修改成员权限。
|
||||
|
||||

|
||||
|
||||
团队权限在专门的权限页面进行设置
|
||||
|
||||

|
||||
|
||||
## 特殊权限说明
|
||||
|
||||
### 管理员权限
|
||||
|
||||
管理员主要负责管理资源的协作关系,但有以下限制:
|
||||
|
||||
- 不能修改或移除自身权限
|
||||
- 不能修改或移除其他管理员权限
|
||||
-不能将管理员权限赋予其他协作者
|
||||
|
||||
### 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
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
@@ -7,76 +7,64 @@ toc: true
|
||||
weight: -10
|
||||
---
|
||||
|
||||
FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景!
|
||||
FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,将智能对话与可视化编排完美结合,让 AI 应用开发变得简单自然。无论您是开发者还是业务人员,都能轻松打造专属的 AI 应用。
|
||||
|
||||
{{% alert icon="🤖 " context="success" %}}
|
||||
FastGPT 在线使用:[https://tryfastgpt.ai](https://tryfastgpt.ai)
|
||||
快速开始体验
|
||||
- 海外版:[https://tryfastgpt.ai](https://tryfastgpt.ai)
|
||||
- 国内版:[https://fastgpt.cn](https://fastgpt.cn)
|
||||
{{% /alert %}}
|
||||
|
||||
| | |
|
||||
| --------------------- | --------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
| | |
|
||||
| --------------------- | --------------------------------- |
|
||||
|  |  |
|
||||
|
||||
## FastGPT 能力
|
||||
# FastGPT 的优势
|
||||
## 1. 简单灵活,像搭积木一样简单 🧱
|
||||
像搭乐高一样简单有趣,FastGPT 提供丰富的功能模块,通过简单拖拽就能搭建出个性化的 AI 应用,零代码也能实现复杂的业务流程。
|
||||
## 2. 让数据更智能 🧠
|
||||
FastGPT 提供完整的数据智能化解决方案,从数据导入、预处理到知识匹配,再到智能问答,全流程自动化。配合可视化的工作流设计,轻松打造专业级 AI 应用。
|
||||
## 3. 开源开放,易于集成 🔗
|
||||
FastGPT 基于 Apache 2.0 协议开源,支持二次开发。通过标准 API 即可快速接入,无需修改源码。支持 ChatGPT、Claude、DeepSeek 和文心一言等主流模型,持续迭代优化,始终保持产品活力。
|
||||
|
||||
### 1. 专属 AI 客服
|
||||
---
|
||||
|
||||
通过导入文档或已有问答对进行训练,让 AI 模型能根据你的文档以交互式对话方式回答问题。
|
||||
# FastGPT 能做什么
|
||||
## 1. 全能知识库
|
||||
可轻松导入各式各样的文档及数据,能自动对其开展知识结构化处理工作。同时,具备支持多轮上下文理解的智能问答功能,还可为用户带来持续优化的知识库管理体验。
|
||||

|
||||
|
||||

|
||||
## 2. 可视化工作流
|
||||
FastGPT直观的拖拽式界面设计,可零代码搭建复杂业务流程。还拥有丰富的功能节点组件,能应对多种业务需求,有着灵活的流程编排能力,按需定制业务流程。
|
||||

|
||||
|
||||
### 2. 简单易用的可视化界面
|
||||
## 3. 数据智能解析
|
||||
FastGPT知识库系统对导入数据的处理极为灵活,可以智能处理PDF文档的复杂结构,保留图片、表格和LaTeX公式,自动识别扫描文件,并将内容结构化为清晰的Markdown格式。同时支持图片自动标注和索引,让视觉内容可被理解和检索,确保知识在AI问答中能被完整、准确地呈现和应用。
|
||||
|
||||
FastGPT 采用直观的可视化界面设计,为各种应用场景提供了丰富实用的功能。通过简洁易懂的操作步骤,可以轻松完成 AI 客服的创建和训练流程。
|
||||
|
||||

|
||||
|
||||
### 3. 自动数据预处理
|
||||
|
||||
提供手动输入、直接分段、LLM 自动处理和 CSV 等多种数据导入途径,其中“直接分段”支持通过 PDF、WORD、Markdown 和 CSV 文档内容作为上下文。FastGPT 会自动对文本数据进行预处理、向量化和 QA 分割,节省手动训练时间,提升效能。
|
||||
|
||||

|
||||
|
||||
### 4. 工作流编排
|
||||

|
||||
|
||||
## 4. 工作流编排
|
||||
基于 Flow 模块的工作流编排,可以帮助你设计更加复杂的问答流程。例如查询数据库、查询库存、预约实验室等。
|
||||
|
||||

|
||||

|
||||
|
||||
### 5. 强大的 API 集成
|
||||
## 5. 强大的 API 集成
|
||||
FastGPT 完全对齐 OpenAI 官方接口,支持一键接入企业微信、公众号、飞书、钉钉等平台,让 AI 能力轻松融入您的业务场景。
|
||||
|
||||
FastGPT 对外的 API 接口对齐了 OpenAI 官方接口,可以直接接入现有的 GPT 应用,也可以轻松集成到企业微信、公众号、飞书等平台。
|
||||

|
||||
|
||||

|
||||
---
|
||||
|
||||
## 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 模型,未来也将支持自定义的向量模型。
|
||||
|
||||
## 知识库核心流程图
|
||||
|
||||

|
||||
# 知识库核心流程图
|
||||
|
||||

|
||||
|
||||
40
env.d.ts
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
declare global {
|
||||
namespace NodeJS {
|
||||
interface ProcessEnv {
|
||||
LOG_DEPTH: string;
|
||||
DEFAULT_ROOT_PSW: string;
|
||||
DB_MAX_LINK: string;
|
||||
TOKEN_KEY: string;
|
||||
FILE_TOKEN_KEY: string;
|
||||
ROOT_KEY: string;
|
||||
OPENAI_BASE_URL: string;
|
||||
CHAT_API_KEY: string;
|
||||
AIPROXY_API_ENDPOINT: string;
|
||||
AIPROXY_API_TOKEN: string;
|
||||
MULTIPLE_DATA_TO_BASE64: string;
|
||||
MONGODB_URI: string;
|
||||
MONGODB_LOG_URI?: string;
|
||||
PG_URL: string;
|
||||
OCEANBASE_URL: string;
|
||||
MILVUS_ADDRESS: string;
|
||||
MILVUS_TOKEN: string;
|
||||
SANDBOX_URL: string;
|
||||
PRO_URL: string;
|
||||
FE_DOMAIN: string;
|
||||
FILE_DOMAIN: string;
|
||||
NEXT_PUBLIC_BASE_URL: string;
|
||||
LOG_LEVEL: string;
|
||||
STORE_LOG_LEVEL: string;
|
||||
USE_IP_LIMIT: string;
|
||||
WORKFLOW_MAX_RUN_TIMES: string;
|
||||
WORKFLOW_MAX_LOOP_TIMES: string;
|
||||
CHECK_INTERNAL_IP: string;
|
||||
CHAT_LOG_URL: string;
|
||||
CHAT_LOG_INTERVAL: string;
|
||||
CHAT_LOG_SOURCE_ID_PREFIX: string;
|
||||
ALLOWED_ORIGINS: string;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
12
package.json
@@ -12,27 +12,29 @@
|
||||
"previewIcon": "node ./scripts/icon/index.js",
|
||||
"api:gen": "tsc ./scripts/openapi/index.ts && node ./scripts/openapi/index.js && npx @redocly/cli build-docs ./scripts/openapi/openapi.json -o ./projects/app/public/openapi/index.html",
|
||||
"create:i18n": "node ./scripts/i18n/index.js",
|
||||
"test": "vitest run --exclude 'test/cases/spec'",
|
||||
"test:all": "vitest run",
|
||||
"test": "vitest run",
|
||||
"test:workflow": "vitest run workflow"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chakra-ui/cli": "^2.4.1",
|
||||
"@vitest/coverage-v8": "^3.0.2",
|
||||
"@vitest/coverage-v8": "^3.0.9",
|
||||
"husky": "^8.0.3",
|
||||
"i18next": "23.16.8",
|
||||
"lint-staged": "^13.3.0",
|
||||
"next-i18next": "15.4.2",
|
||||
"prettier": "3.2.4",
|
||||
"react-i18next": "14.1.2",
|
||||
"vitest": "^3.0.2",
|
||||
"vitest-mongodb": "^1.0.1",
|
||||
"vitest": "^3.0.9",
|
||||
"mongodb-memory-server": "^10.1.4",
|
||||
"zhlint": "^0.7.4"
|
||||
},
|
||||
"lint-staged": {
|
||||
"./**/**/*.{ts,tsx,scss}": "npm run format-code",
|
||||
"./docSite/**/**/*.md": "npm run format-doc"
|
||||
},
|
||||
"resolutions": {
|
||||
"mdast-util-gfm-autolink-literal": "2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.16.0",
|
||||
"pnpm": ">=9.0.0"
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ErrType } from '../errorCode';
|
||||
const startCode = 507000;
|
||||
export enum CommonErrEnum {
|
||||
invalidParams = 'invalidParams',
|
||||
invalidResource = 'invalidResource',
|
||||
fileNotFound = 'fileNotFound',
|
||||
unAuthFile = 'unAuthFile',
|
||||
missingParams = 'missingParams',
|
||||
@@ -15,6 +16,10 @@ const datasetErr = [
|
||||
statusText: CommonErrEnum.fileNotFound,
|
||||
message: i18nT('common:error.invalid_params')
|
||||
},
|
||||
{
|
||||
statusText: CommonErrEnum.invalidResource,
|
||||
message: i18nT('common:error_invalid_resource')
|
||||
},
|
||||
{
|
||||
statusText: CommonErrEnum.fileNotFound,
|
||||
message: 'error.fileNotFound'
|
||||
|
||||
@@ -27,7 +27,8 @@ export enum TeamErrEnum {
|
||||
userNotActive = 'userNotActive',
|
||||
invitationLinkInvalid = 'invitationLinkInvalid',
|
||||
youHaveBeenInTheTeam = 'youHaveBeenInTheTeam',
|
||||
tooManyInvitations = 'tooManyInvitations'
|
||||
tooManyInvitations = 'tooManyInvitations',
|
||||
unPermission = 'unPermission'
|
||||
}
|
||||
|
||||
const teamErr = [
|
||||
@@ -35,6 +36,10 @@ const teamErr = [
|
||||
statusText: TeamErrEnum.notUser,
|
||||
message: i18nT('common:code_error.team_error.not_user')
|
||||
},
|
||||
{
|
||||
statusText: TeamErrEnum.unPermission,
|
||||
message: i18nT('common:error_un_permission')
|
||||
},
|
||||
{
|
||||
statusText: TeamErrEnum.teamOverSize,
|
||||
message: i18nT('common:code_error.team_error.over_size')
|
||||
|
||||
16
packages/global/common/system/types/index.d.ts
vendored
@@ -49,6 +49,7 @@ export type FastGPTFeConfigsType = {
|
||||
find_password_method?: ['email' | 'phone'];
|
||||
bind_notification_method?: ['email' | 'phone'];
|
||||
googleClientVerKey?: string;
|
||||
mcpServerProxyEndpoint?: string;
|
||||
|
||||
show_emptyChat?: boolean;
|
||||
show_appStore?: boolean;
|
||||
@@ -118,11 +119,12 @@ export type SystemEnvType = {
|
||||
oneapiUrl?: string;
|
||||
chatApiKey?: string;
|
||||
|
||||
customPdfParse?: {
|
||||
url?: string;
|
||||
key?: string;
|
||||
|
||||
doc2xKey?: string;
|
||||
price?: number; // n points/1 page
|
||||
};
|
||||
customPdfParse?: customPdfParseType;
|
||||
};
|
||||
|
||||
export type customPdfParseType = {
|
||||
url?: string;
|
||||
key?: string;
|
||||
doc2xKey?: string;
|
||||
price?: number;
|
||||
};
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { i18nT } from '../../../web/i18n/utils';
|
||||
|
||||
export enum ChatCompletionRequestMessageRoleEnum {
|
||||
'System' = 'system',
|
||||
'User' = 'user',
|
||||
@@ -28,3 +30,13 @@ export enum EmbeddingTypeEnm {
|
||||
query = 'query',
|
||||
db = 'db'
|
||||
}
|
||||
|
||||
export const completionFinishReasonMap = {
|
||||
close: i18nT('chat:completion_finish_close'),
|
||||
stop: i18nT('chat:completion_finish_stop'),
|
||||
length: i18nT('chat:completion_finish_length'),
|
||||
tool_calls: i18nT('chat:completion_finish_tool_calls'),
|
||||
content_filter: i18nT('chat:completion_finish_content_filter'),
|
||||
function_call: i18nT('chat:completion_finish_function_call'),
|
||||
null: i18nT('chat:completion_finish_null')
|
||||
};
|
||||
|
||||
9
packages/global/core/ai/type.d.ts
vendored
@@ -73,6 +73,15 @@ export type ChatCompletionMessageFunctionCall =
|
||||
export type StreamChatType = Stream<openai.Chat.Completions.ChatCompletionChunk>;
|
||||
export type UnStreamChatType = openai.Chat.Completions.ChatCompletion;
|
||||
|
||||
export type CompletionFinishReason =
|
||||
| 'close'
|
||||
| 'stop'
|
||||
| 'length'
|
||||
| 'tool_calls'
|
||||
| 'content_filter'
|
||||
| 'function_call'
|
||||
| null;
|
||||
|
||||
export default openai;
|
||||
export * from 'openai';
|
||||
|
||||
|
||||
@@ -11,7 +11,9 @@ export enum AppTypeEnum {
|
||||
simple = 'simple',
|
||||
workflow = 'advanced',
|
||||
plugin = 'plugin',
|
||||
httpPlugin = 'httpPlugin'
|
||||
httpPlugin = 'httpPlugin',
|
||||
toolSet = 'toolSet',
|
||||
tool = 'tool'
|
||||
}
|
||||
|
||||
export const AppFolderTypeList = [AppTypeEnum.folder, AppTypeEnum.httpPlugin];
|
||||
@@ -53,7 +55,10 @@ export enum AppTemplateTypeEnum {
|
||||
imageGeneration = 'image-generation',
|
||||
webSearch = 'web-search',
|
||||
roleplay = 'roleplay',
|
||||
officeServices = 'office-services'
|
||||
officeServices = 'office-services',
|
||||
|
||||
// special type
|
||||
contribute = 'contribute'
|
||||
}
|
||||
|
||||
export const defaultDatasetMaxTokens = 16000;
|
||||
|
||||
97
packages/global/core/app/mcpTools/utils.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
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: ''
|
||||
};
|
||||
};
|
||||
10
packages/global/core/app/type.d.ts
vendored
@@ -16,6 +16,16 @@ import { FlowNodeInputTypeEnum } from '../../core/workflow/node/constant';
|
||||
import { WorkflowTemplateBasicType } from '@fastgpt/global/core/workflow/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 = {
|
||||
_id: string;
|
||||
parentId?: ParentIdType;
|
||||
|
||||
@@ -140,7 +140,9 @@ export const appWorkflow2Form = ({
|
||||
);
|
||||
} else if (
|
||||
node.flowNodeType === FlowNodeTypeEnum.pluginModule ||
|
||||
node.flowNodeType === FlowNodeTypeEnum.appModule
|
||||
node.flowNodeType === FlowNodeTypeEnum.appModule ||
|
||||
node.flowNodeType === FlowNodeTypeEnum.tool ||
|
||||
node.flowNodeType === FlowNodeTypeEnum.toolSet
|
||||
) {
|
||||
if (!node.pluginId) return;
|
||||
|
||||
|
||||
@@ -38,7 +38,8 @@ export enum ChatSourceEnum {
|
||||
team = 'team',
|
||||
feishu = 'feishu',
|
||||
official_account = 'official_account',
|
||||
wecom = 'wecom'
|
||||
wecom = 'wecom',
|
||||
mcp = 'mcp'
|
||||
}
|
||||
|
||||
export const ChatSourceMap = {
|
||||
@@ -68,6 +69,9 @@ export const ChatSourceMap = {
|
||||
},
|
||||
[ChatSourceEnum.wecom]: {
|
||||
name: i18nT('common:core.chat.logs.wecom')
|
||||
},
|
||||
[ChatSourceEnum.mcp]: {
|
||||
name: i18nT('common:core.chat.logs.mcp')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -77,6 +77,13 @@ export const getHistoryPreview = (
|
||||
});
|
||||
};
|
||||
|
||||
export const filterModuleTypeList: any[] = [
|
||||
FlowNodeTypeEnum.pluginModule,
|
||||
FlowNodeTypeEnum.datasetSearchNode,
|
||||
FlowNodeTypeEnum.tools,
|
||||
FlowNodeTypeEnum.pluginOutput
|
||||
];
|
||||
|
||||
export const filterPublicNodeResponseData = ({
|
||||
flowResponses = [],
|
||||
responseDetail = false
|
||||
@@ -87,12 +94,6 @@ export const filterPublicNodeResponseData = ({
|
||||
const filedList = responseDetail
|
||||
? ['quoteList', 'moduleType', 'pluginOutput', 'runningTime']
|
||||
: ['moduleType', 'pluginOutput', 'runningTime'];
|
||||
const filterModuleTypeList: any[] = [
|
||||
FlowNodeTypeEnum.pluginModule,
|
||||
FlowNodeTypeEnum.datasetSearchNode,
|
||||
FlowNodeTypeEnum.tools,
|
||||
FlowNodeTypeEnum.pluginOutput
|
||||
];
|
||||
|
||||
return flowResponses
|
||||
.filter((item) => filterModuleTypeList.includes(item.moduleType))
|
||||
@@ -153,25 +154,55 @@ export const getChatSourceByPublishChannel = (publishChannel: PublishChannelEnum
|
||||
/*
|
||||
Merge chat responseData
|
||||
1. Same tool mergeSignId (Interactive tool node)
|
||||
2. Recursively merge plugin details with same mergeSignId
|
||||
*/
|
||||
export const mergeChatResponseData = (responseDataList: ChatHistoryItemResType[]) => {
|
||||
let lastResponse: ChatHistoryItemResType | undefined = undefined;
|
||||
|
||||
return responseDataList.reduce<ChatHistoryItemResType[]>((acc, curr) => {
|
||||
if (lastResponse && lastResponse.mergeSignId && curr.mergeSignId === lastResponse.mergeSignId) {
|
||||
// 替换 lastResponse
|
||||
const concatResponse: ChatHistoryItemResType = {
|
||||
...curr,
|
||||
runningTime: +((lastResponse.runningTime || 0) + (curr.runningTime || 0)).toFixed(2),
|
||||
totalPoints: (lastResponse.totalPoints || 0) + (curr.totalPoints || 0),
|
||||
childTotalPoints: (lastResponse.childTotalPoints || 0) + (curr.childTotalPoints || 0),
|
||||
toolCallTokens: (lastResponse.toolCallTokens || 0) + (curr.toolCallTokens || 0),
|
||||
toolDetail: [...(lastResponse.toolDetail || []), ...(curr.toolDetail || [])]
|
||||
export const mergeChatResponseData = (
|
||||
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 [...acc.slice(0, -1), concatResponse];
|
||||
} else {
|
||||
lastResponse = curr;
|
||||
return [...acc, curr];
|
||||
}
|
||||
}, []);
|
||||
return item;
|
||||
});
|
||||
|
||||
let lastResponse: ChatHistoryItemResType | undefined = undefined;
|
||||
let hasMerged = false;
|
||||
|
||||
const firstPassResult = responseWithMergedPlugins.reduce<ChatHistoryItemResType[]>(
|
||||
(acc, curr) => {
|
||||
if (
|
||||
lastResponse &&
|
||||
lastResponse.mergeSignId &&
|
||||
curr.mergeSignId === lastResponse.mergeSignId
|
||||
) {
|
||||
const concatResponse: ChatHistoryItemResType = {
|
||||
...curr,
|
||||
runningTime: +((lastResponse.runningTime || 0) + (curr.runningTime || 0)).toFixed(2),
|
||||
totalPoints: (lastResponse.totalPoints || 0) + (curr.totalPoints || 0),
|
||||
childTotalPoints: (lastResponse.childTotalPoints || 0) + (curr.childTotalPoints || 0),
|
||||
toolCallTokens: (lastResponse.toolCallTokens || 0) + (curr.toolCallTokens || 0),
|
||||
toolDetail: [...(lastResponse.toolDetail || []), ...(curr.toolDetail || [])],
|
||||
loopDetail: [...(lastResponse.loopDetail || []), ...(curr.loopDetail || [])],
|
||||
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;
|
||||
};
|
||||
|
||||
3
packages/global/core/dataset/api.d.ts
vendored
@@ -15,7 +15,6 @@ export type DatasetUpdateBody = {
|
||||
name?: string;
|
||||
avatar?: string;
|
||||
intro?: string;
|
||||
status?: DatasetSchemaType['status'];
|
||||
|
||||
agentModel?: string;
|
||||
vlmModel?: string;
|
||||
@@ -26,6 +25,7 @@ export type DatasetUpdateBody = {
|
||||
apiServer?: DatasetSchemaType['apiServer'];
|
||||
yuqueServer?: DatasetSchemaType['yuqueServer'];
|
||||
feishuServer?: DatasetSchemaType['feishuServer'];
|
||||
chunkSettings?: DatasetSchemaType['chunkSettings'];
|
||||
|
||||
// sync schedule
|
||||
autoSync?: boolean;
|
||||
@@ -141,7 +141,6 @@ export type PushDatasetDataChunkProps = {
|
||||
|
||||
export type PostWebsiteSyncParams = {
|
||||
datasetId: string;
|
||||
billId: string;
|
||||
};
|
||||
|
||||
export type PushDatasetDataProps = {
|
||||
|
||||
@@ -50,7 +50,9 @@ export const DatasetTypeMap = {
|
||||
|
||||
export enum DatasetStatusEnum {
|
||||
active = 'active',
|
||||
syncing = 'syncing'
|
||||
syncing = 'syncing',
|
||||
waiting = 'waiting',
|
||||
error = 'error'
|
||||
}
|
||||
export const DatasetStatusMap = {
|
||||
[DatasetStatusEnum.active]: {
|
||||
@@ -58,6 +60,12 @@ export const DatasetStatusMap = {
|
||||
},
|
||||
[DatasetStatusEnum.syncing]: {
|
||||
label: i18nT('common:core.dataset.status.syncing')
|
||||
},
|
||||
[DatasetStatusEnum.waiting]: {
|
||||
label: i18nT('common:core.dataset.status.waiting')
|
||||
},
|
||||
[DatasetStatusEnum.error]: {
|
||||
label: i18nT('dataset:status_error')
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
25
packages/global/core/dataset/type.d.ts
vendored
@@ -17,6 +17,20 @@ import { SourceMemberType } from 'support/user/type';
|
||||
import { DatasetDataIndexTypeEnum } from './data/constants';
|
||||
import { ChunkSettingModeEnum } from './constants';
|
||||
|
||||
export type ChunkSettingsType = {
|
||||
trainingType: DatasetCollectionDataProcessModeEnum;
|
||||
autoIndexes?: boolean;
|
||||
imageIndex?: boolean;
|
||||
|
||||
chunkSettingMode?: ChunkSettingModeEnum;
|
||||
chunkSplitMode?: DataChunkSplitModeEnum;
|
||||
|
||||
chunkSize?: number;
|
||||
indexSize?: number;
|
||||
chunkSplitter?: string;
|
||||
qaPrompt?: string;
|
||||
};
|
||||
|
||||
export type DatasetSchemaType = {
|
||||
_id: string;
|
||||
parentId?: string;
|
||||
@@ -29,7 +43,6 @@ export type DatasetSchemaType = {
|
||||
name: string;
|
||||
intro: string;
|
||||
type: `${DatasetTypeEnum}`;
|
||||
status: `${DatasetStatusEnum}`;
|
||||
|
||||
vectorModel: string;
|
||||
agentModel: string;
|
||||
@@ -39,14 +52,16 @@ export type DatasetSchemaType = {
|
||||
url: string;
|
||||
selector: string;
|
||||
};
|
||||
|
||||
chunkSettings?: ChunkSettingsType;
|
||||
|
||||
inheritPermission: boolean;
|
||||
apiServer?: APIFileServer;
|
||||
feishuServer?: FeishuServer;
|
||||
yuqueServer?: YuqueServer;
|
||||
|
||||
autoSync?: boolean;
|
||||
|
||||
// abandon
|
||||
autoSync?: boolean;
|
||||
externalReadUrl?: string;
|
||||
defaultPermission?: number;
|
||||
};
|
||||
@@ -163,6 +178,7 @@ export type DatasetTrainingSchemaType = {
|
||||
weight: number;
|
||||
indexes: Omit<DatasetDataIndexItemType, 'dataId'>[];
|
||||
retryCount: number;
|
||||
errorMsg?: string;
|
||||
};
|
||||
|
||||
export type CollectionWithDatasetType = DatasetCollectionSchemaType & {
|
||||
@@ -192,6 +208,8 @@ export type DatasetListItemType = {
|
||||
};
|
||||
|
||||
export type DatasetItemType = Omit<DatasetSchemaType, 'vectorModel' | 'agentModel' | 'vlmModel'> & {
|
||||
status: `${DatasetStatusEnum}`;
|
||||
errorMsg?: string;
|
||||
vectorModel: EmbeddingModelItemType;
|
||||
agentModel: LLMModelItemType;
|
||||
vlmModel?: LLMModelItemType;
|
||||
@@ -216,6 +234,7 @@ export type DatasetCollectionItemType = CollectionWithDatasetType & {
|
||||
file?: DatasetFileSchema;
|
||||
permission: DatasetPermission;
|
||||
indexAmount: number;
|
||||
errorCount?: number;
|
||||
};
|
||||
|
||||
/* ================= data ===================== */
|
||||
|
||||
@@ -7,6 +7,7 @@ export enum FlowNodeInputTypeEnum { // render ui
|
||||
numberInput = 'numberInput',
|
||||
switch = 'switch', // true/false
|
||||
select = 'select',
|
||||
multipleSelect = 'multipleSelect',
|
||||
|
||||
// editor
|
||||
JSONEditor = 'JSONEditor',
|
||||
@@ -46,6 +47,9 @@ export const FlowNodeInputMap: Record<
|
||||
[FlowNodeInputTypeEnum.select]: {
|
||||
icon: 'core/workflow/inputType/option'
|
||||
},
|
||||
[FlowNodeInputTypeEnum.multipleSelect]: {
|
||||
icon: 'core/workflow/inputType/option'
|
||||
},
|
||||
[FlowNodeInputTypeEnum.switch]: {
|
||||
icon: 'core/workflow/inputType/switch'
|
||||
},
|
||||
@@ -136,7 +140,9 @@ export enum FlowNodeTypeEnum {
|
||||
loopStart = 'loopStart',
|
||||
loopEnd = 'loopEnd',
|
||||
formInput = 'formInput',
|
||||
comment = 'comment'
|
||||
comment = 'comment',
|
||||
tool = 'tool',
|
||||
toolSet = 'toolSet'
|
||||
}
|
||||
|
||||
// node IO value type
|
||||
|
||||
@@ -5,6 +5,7 @@ export enum SseResponseEventEnum {
|
||||
answer = 'answer', // animation stream
|
||||
fastAnswer = 'fastAnswer', // direct answer text, not animation
|
||||
flowNodeStatus = 'flowNodeStatus', // update node status
|
||||
flowNodeResponse = 'flowNodeResponse', // node response
|
||||
|
||||
toolCall = 'toolCall', // tool start
|
||||
toolParams = 'toolParams', // tool params return
|
||||
|
||||
10
packages/global/core/workflow/runtime/type.d.ts
vendored
@@ -22,7 +22,8 @@ import { UserSelectOptionType } from '../template/system/userSelect/type';
|
||||
import { WorkflowResponseType } from '../../../../service/core/workflow/dispatch/type';
|
||||
import { AiChatQuoteRoleType } from '../template/system/aiChat/type';
|
||||
import { LafAccountType, OpenaiAccountType } from '../../../support/user/team/type';
|
||||
|
||||
import { CompletionFinishReason } from '../../ai/type';
|
||||
import { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
|
||||
export type ExternalProviderType = {
|
||||
openaiAccount?: OpenaiAccountType;
|
||||
externalWorkflowVariables?: Record<string, string>;
|
||||
@@ -40,6 +41,7 @@ export type ChatDispatchProps = {
|
||||
id: string; // May be the id of the system plug-in (cannot be used directly to look up the table)
|
||||
teamId: string;
|
||||
tmbId: string; // App tmbId
|
||||
isChildApp?: boolean;
|
||||
};
|
||||
runningUserInfo: {
|
||||
teamId: string;
|
||||
@@ -53,11 +55,14 @@ export type ChatDispatchProps = {
|
||||
variables: Record<string, any>; // global variable
|
||||
query: UserChatItemValueItemType[]; // trigger query
|
||||
chatConfig: AppSchema['chatConfig'];
|
||||
lastInteractive?: WorkflowInteractiveResponseType; // last interactive response
|
||||
stream: boolean;
|
||||
maxRunTimes: number;
|
||||
isToolCall?: boolean;
|
||||
workflowStreamResponse?: WorkflowResponseType;
|
||||
workflowDispatchDeep?: number;
|
||||
version?: 'v1' | 'v2';
|
||||
responseDetail?: boolean;
|
||||
};
|
||||
|
||||
export type ModuleDispatchProps<T> = ChatDispatchProps & {
|
||||
@@ -128,6 +133,7 @@ export type DispatchNodeResponseType = {
|
||||
obj: `${ChatRoleEnum}`;
|
||||
value: string;
|
||||
}[]; // completion context array. history will slice
|
||||
finishReason?: CompletionFinishReason;
|
||||
|
||||
// dataset search
|
||||
similarity?: number;
|
||||
@@ -211,6 +217,8 @@ export type DispatchNodeResponseType = {
|
||||
// tool params
|
||||
toolParamsResult?: Record<string, any>;
|
||||
|
||||
toolRes?: any;
|
||||
|
||||
// abandon
|
||||
extensionModel?: string;
|
||||
extensionResult?: string;
|
||||
|
||||
@@ -10,8 +10,23 @@ import { FlowNodeOutputItemType, ReferenceValueType } from '../type/io';
|
||||
import { ChatItemType, NodeOutputItemType } from '../../../core/chat/type';
|
||||
import { ChatItemValueTypeEnum, ChatRoleEnum } from '../../../core/chat/constants';
|
||||
import { replaceVariable, valToStr } from '../../../common/string/tools';
|
||||
import { ChatCompletionChunk } from 'openai/resources';
|
||||
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 => {
|
||||
let limit = 10;
|
||||
nodes.forEach((node) => {
|
||||
@@ -29,13 +44,122 @@ export const getMaxHistoryLimitFromNodes = (nodes: StoreNodeItemType[]): number
|
||||
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.
|
||||
What can be done:
|
||||
1. Get the interactive data
|
||||
2. Check that the workflow starts at the interaction node
|
||||
*/
|
||||
export const getLastInteractiveValue = (histories: ChatItemType[]) => {
|
||||
export const getLastInteractiveValue = (
|
||||
histories: ChatItemType[]
|
||||
): WorkflowInteractiveResponseType | undefined => {
|
||||
const lastAIMessage = [...histories].reverse().find((item) => item.obj === ChatRoleEnum.AI);
|
||||
|
||||
if (lastAIMessage) {
|
||||
@@ -46,7 +170,14 @@ export const getLastInteractiveValue = (histories: ChatItemType[]) => {
|
||||
lastValue.type !== ChatItemValueTypeEnum.interactive ||
|
||||
!lastValue.interactive
|
||||
) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
lastValue.interactive.type === 'childrenInteractive' ||
|
||||
lastValue.interactive.type === 'loopInteractive'
|
||||
) {
|
||||
return lastValue.interactive;
|
||||
}
|
||||
|
||||
// Check is user select
|
||||
@@ -63,38 +194,29 @@ export const getLastInteractiveValue = (histories: ChatItemType[]) => {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return;
|
||||
};
|
||||
|
||||
export const initWorkflowEdgeStatus = (
|
||||
export const storeEdges2RuntimeEdges = (
|
||||
edges: StoreEdgeItemType[],
|
||||
histories?: ChatItemType[]
|
||||
lastInteractive?: WorkflowInteractiveResponseType
|
||||
): RuntimeEdgeItemType[] => {
|
||||
// If there is a history, use the last interactive value
|
||||
if (histories && histories.length > 0) {
|
||||
const memoryEdges = getLastInteractiveValue(histories)?.memoryEdges;
|
||||
|
||||
if (lastInteractive) {
|
||||
const memoryEdges = lastInteractive.memoryEdges || [];
|
||||
if (memoryEdges && memoryEdges.length > 0) {
|
||||
return memoryEdges;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
edges?.map((edge) => ({
|
||||
...edge,
|
||||
status: 'waiting'
|
||||
})) || []
|
||||
);
|
||||
return edges?.map((edge) => ({ ...edge, status: 'waiting' })) || [];
|
||||
};
|
||||
|
||||
export const getWorkflowEntryNodeIds = (
|
||||
nodes: (StoreNodeItemType | RuntimeNodeItemType)[],
|
||||
histories?: ChatItemType[]
|
||||
lastInteractive?: WorkflowInteractiveResponseType
|
||||
) => {
|
||||
// If there is a history, use the last interactive entry node
|
||||
if (histories && histories.length > 0) {
|
||||
const entryNodeIds = getLastInteractiveValue(histories)?.entryNodeIds;
|
||||
|
||||
if (lastInteractive) {
|
||||
const entryNodeIds = lastInteractive.entryNodeIds || [];
|
||||
if (Array.isArray(entryNodeIds) && entryNodeIds.length > 0) {
|
||||
return entryNodeIds;
|
||||
}
|
||||
@@ -106,7 +228,12 @@ export const getWorkflowEntryNodeIds = (
|
||||
FlowNodeTypeEnum.pluginInput
|
||||
];
|
||||
return nodes
|
||||
.filter((node) => entryList.includes(node.flowNodeType as any))
|
||||
.filter(
|
||||
(node) =>
|
||||
entryList.includes(node.flowNodeType as any) ||
|
||||
(!nodes.some((item) => entryList.includes(item.flowNodeType as any)) &&
|
||||
node.flowNodeType === FlowNodeTypeEnum.tool)
|
||||
)
|
||||
.map((item) => item.nodeId);
|
||||
};
|
||||
|
||||
@@ -304,7 +431,6 @@ export const formatVariableValByType = (val: any, valueType?: WorkflowIOValueTyp
|
||||
if (
|
||||
[
|
||||
WorkflowIOValueTypeEnum.object,
|
||||
WorkflowIOValueTypeEnum.chatHistory,
|
||||
WorkflowIOValueTypeEnum.datasetQuote,
|
||||
WorkflowIOValueTypeEnum.selectApp,
|
||||
WorkflowIOValueTypeEnum.selectDataset
|
||||
@@ -397,10 +523,10 @@ export const textAdaptGptResponse = ({
|
||||
|
||||
/* Update runtimeNode's outputs with interactive data from history */
|
||||
export function rewriteNodeOutputByHistories(
|
||||
histories: ChatItemType[],
|
||||
runtimeNodes: RuntimeNodeItemType[]
|
||||
runtimeNodes: RuntimeNodeItemType[],
|
||||
lastInteractive?: InteractiveNodeResponseType
|
||||
) {
|
||||
const interactive = getLastInteractiveValue(histories);
|
||||
const interactive = lastInteractive;
|
||||
if (!interactive?.nodeOutputs) {
|
||||
return runtimeNodes;
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ import { LoopStartNode } from './system/loop/loopStart';
|
||||
import { LoopEndNode } from './system/loop/loopEnd';
|
||||
import { FormInputNode } from './system/interactive/formInput';
|
||||
import { ToolParamsNode } from './system/toolParams';
|
||||
import { RunToolNode } from './system/runTool';
|
||||
import { RunToolSetNode } from './system/runToolSet';
|
||||
|
||||
const systemNodes: FlowNodeTemplateType[] = [
|
||||
AiChatModule,
|
||||
@@ -84,5 +86,7 @@ export const moduleTemplatesFlat: FlowNodeTemplateType[] = [
|
||||
RunAppNode,
|
||||
RunAppModule,
|
||||
LoopStartNode,
|
||||
LoopEndNode
|
||||
LoopEndNode,
|
||||
RunToolNode,
|
||||
RunToolSetNode
|
||||
];
|
||||
|
||||
@@ -8,7 +8,7 @@ import { i18nT } from '../../../../web/i18n/utils';
|
||||
export const Input_Template_History: FlowNodeInputItemType = {
|
||||
key: NodeInputKeyEnum.history,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.numberInput, FlowNodeInputTypeEnum.reference],
|
||||
valueType: WorkflowIOValueTypeEnum.chatHistory,
|
||||
valueType: WorkflowIOValueTypeEnum.chatHistory, // Array / Number
|
||||
label: i18nT('common:core.module.input.label.chat history'),
|
||||
description: i18nT('workflow:max_dialog_rounds'),
|
||||
|
||||
|
||||
@@ -1,14 +1,54 @@
|
||||
import type { NodeOutputItemType } from '../../../../chat/type';
|
||||
import type { FlowNodeOutputItemType } from '../../../type/io';
|
||||
import type { RuntimeEdgeItemType } from '../../../runtime/type';
|
||||
import { FlowNodeInputTypeEnum } from 'core/workflow/node/constant';
|
||||
import { WorkflowIOValueTypeEnum } from 'core/workflow/constants';
|
||||
import type { ChatCompletionMessageParam } from '../../../../ai/type';
|
||||
|
||||
type InteractiveBasicType = {
|
||||
entryNodeIds: string[];
|
||||
memoryEdges: RuntimeEdgeItemType[];
|
||||
nodeOutputs: NodeOutputItemType[];
|
||||
toolParams?: {
|
||||
entryNodeIds: string[]; // 记录工具中,交互节点的 Id,而不是起始工作流的入口
|
||||
memoryMessages: ChatCompletionMessageParam[]; // 这轮工具中,产生的新的 messages
|
||||
toolCallId: string; // 记录对应 tool 的id,用于后续交互节点可以替换掉 tool 的 response
|
||||
};
|
||||
};
|
||||
|
||||
type InteractiveNodeType = {
|
||||
entryNodeIds?: string[];
|
||||
memoryEdges?: RuntimeEdgeItemType[];
|
||||
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 = {
|
||||
key: string;
|
||||
value: string;
|
||||
};
|
||||
type UserSelectInteractive = InteractiveNodeType & {
|
||||
type: 'userSelect';
|
||||
params: {
|
||||
description: string;
|
||||
userSelectOptions: UserSelectOptionItemType[];
|
||||
userSelectedVal?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type UserInputFormItemType = {
|
||||
type: FlowNodeInputTypeEnum;
|
||||
@@ -28,29 +68,7 @@ export type UserInputFormItemType = {
|
||||
// select
|
||||
list?: { label: string; value: string }[];
|
||||
};
|
||||
|
||||
type InteractiveBasicType = {
|
||||
entryNodeIds: string[];
|
||||
memoryEdges: RuntimeEdgeItemType[];
|
||||
nodeOutputs: NodeOutputItemType[];
|
||||
|
||||
toolParams?: {
|
||||
entryNodeIds: string[]; // 记录工具中,交互节点的 Id,而不是起始工作流的入口
|
||||
memoryMessages: ChatCompletionMessageParam[]; // 这轮工具中,产生的新的 messages
|
||||
toolCallId: string; // 记录对应 tool 的id,用于后续交互节点可以替换掉 tool 的 response
|
||||
};
|
||||
};
|
||||
|
||||
type UserSelectInteractive = {
|
||||
type: 'userSelect';
|
||||
params: {
|
||||
description: string;
|
||||
userSelectOptions: UserSelectOptionItemType[];
|
||||
userSelectedVal?: string;
|
||||
};
|
||||
};
|
||||
|
||||
type UserInputInteractive = {
|
||||
type UserInputInteractive = InteractiveNodeType & {
|
||||
type: 'userInput';
|
||||
params: {
|
||||
description: string;
|
||||
@@ -59,5 +77,10 @@ type UserInputInteractive = {
|
||||
};
|
||||
};
|
||||
|
||||
export type InteractiveNodeResponseType = UserSelectInteractive | UserInputInteractive;
|
||||
export type InteractiveNodeResponseType =
|
||||
| UserSelectInteractive
|
||||
| UserInputInteractive
|
||||
| ChildrenInteractive
|
||||
| LoopInteractive;
|
||||
|
||||
export type WorkflowInteractiveResponseType = InteractiveBasicType & InteractiveNodeResponseType;
|
||||
|
||||
19
packages/global/core/workflow/template/system/runTool.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
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: []
|
||||
};
|
||||
19
packages/global/core/workflow/template/system/runToolSet.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
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: []
|
||||
};
|
||||
@@ -1,7 +1,23 @@
|
||||
export const JS_TEMPLATE = `function main({data1, data2}){
|
||||
|
||||
return {
|
||||
result: data1,
|
||||
data2
|
||||
}
|
||||
return {
|
||||
result: data1,
|
||||
data2
|
||||
}
|
||||
}`;
|
||||
|
||||
export const PY_TEMPLATE = `def main(data1, data2):
|
||||
return {
|
||||
"result": data1,
|
||||
"data2": data2
|
||||
}
|
||||
`;
|
||||
|
||||
export enum SandboxCodeTypeEnum {
|
||||
js = 'js',
|
||||
py = 'py'
|
||||
}
|
||||
export const SNADBOX_CODE_TEMPLATE = {
|
||||
[SandboxCodeTypeEnum.js]: JS_TEMPLATE,
|
||||
[SandboxCodeTypeEnum.py]: PY_TEMPLATE
|
||||
};
|
||||
|
||||
@@ -68,12 +68,14 @@ export const CodeNode: FlowNodeTemplateType = {
|
||||
key: NodeInputKeyEnum.codeType,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.hidden],
|
||||
label: '',
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
value: 'js'
|
||||
},
|
||||
{
|
||||
key: NodeInputKeyEnum.code,
|
||||
renderTypeList: [FlowNodeInputTypeEnum.custom],
|
||||
label: '',
|
||||
valueType: WorkflowIOValueTypeEnum.string,
|
||||
value: JS_TEMPLATE
|
||||
}
|
||||
],
|
||||
|
||||
2
packages/global/core/workflow/type/node.d.ts
vendored
@@ -23,6 +23,7 @@ import { NextApiResponse } from 'next';
|
||||
import { AppDetailType, AppSchema } from '../../app/type';
|
||||
import { ParentIdType } from 'common/parentFolder/type';
|
||||
import { AppTypeEnum } from 'core/app/constants';
|
||||
import { WorkflowInteractiveResponseType } from '../template/system/interactive/type';
|
||||
|
||||
export type FlowNodeCommonType = {
|
||||
parentNodeId?: string;
|
||||
@@ -120,6 +121,7 @@ export type FlowNodeItemType = FlowNodeTemplateType & {
|
||||
showResult?: boolean; // show and hide result modal
|
||||
response?: ChatHistoryItemResType;
|
||||
isExpired?: boolean;
|
||||
workflowInteractiveResponse?: WorkflowInteractiveResponseType;
|
||||
};
|
||||
isFolded?: boolean;
|
||||
};
|
||||
|
||||
@@ -311,6 +311,38 @@ 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 = (
|
||||
variables: { key: string; label: string; type?: `${VariableInputEnum}`; required?: boolean }[]
|
||||
): EditorVariablePickerType[] => {
|
||||
|
||||