Compare commits
133 Commits
v4.4.1
...
v4.6.6-alp
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5b676ff4ad | ||
|
|
759a2330e6 | ||
|
|
86286efb54 | ||
|
|
99e8ba2256 | ||
|
|
f84fd93cbb | ||
|
|
cd682d4275 | ||
|
|
41115a96c0 | ||
|
|
b14a1db2f9 | ||
|
|
703583fff7 | ||
|
|
d33c99f564 | ||
|
|
05bf1b2265 | ||
|
|
dd7b4b98ae | ||
|
|
34656dfda0 | ||
|
|
7b5c35018b | ||
|
|
7630417679 | ||
|
|
63ce76413e | ||
|
|
1c1305fcb6 | ||
|
|
c3437b9367 | ||
|
|
e18c79ca71 | ||
|
|
d2d7eac9e0 | ||
|
|
84cf6b5658 | ||
|
|
b58249fc3a | ||
|
|
54d52d8d25 | ||
|
|
f298b90b69 | ||
|
|
e01c38efe0 | ||
|
|
71afe71192 | ||
|
|
62e87551ac | ||
|
|
c3ae38df8b | ||
|
|
9c77dfbddd | ||
|
|
7fc05af09e | ||
|
|
a9ae270335 | ||
|
|
b916183848 | ||
|
|
007fce2deb | ||
|
|
abc1e576b7 | ||
|
|
a74e1d7166 | ||
|
|
e765c3bf95 | ||
|
|
933c3fdfd6 | ||
|
|
f818260711 | ||
|
|
3acbf1ab17 | ||
|
|
9cb4280a16 | ||
|
|
60f752629f | ||
|
|
0558379ddb | ||
|
|
9c4eabfc9e | ||
|
|
b05dd0fde1 | ||
|
|
0df5152202 | ||
|
|
e044d3583d | ||
|
|
c5664c7e90 | ||
|
|
70f3373246 | ||
|
|
af16817a4a | ||
|
|
4358b6de4d | ||
|
|
f6aea484ce | ||
|
|
fbe1d8cfed | ||
|
|
16103029f5 | ||
|
|
cd3acb44ab | ||
|
|
bfd8be5df0 | ||
|
|
592e1a93a2 | ||
|
|
2b8ff7d32c | ||
|
|
4593eef2ff | ||
|
|
d91551e6be | ||
|
|
0a0fe31d3c | ||
|
|
9f889d8806 | ||
|
|
8bb5588305 | ||
|
|
661ee79943 | ||
|
|
60ee160131 | ||
|
|
008d0af010 | ||
|
|
f2fb0aedfd | ||
|
|
1dca5edcc6 | ||
|
|
1942cb0d67 | ||
|
|
bf6dbfb245 | ||
|
|
d37433eacd | ||
|
|
a3534407bf | ||
|
|
3091a90df6 | ||
|
|
41b8f4443c | ||
|
|
777f089423 | ||
|
|
b23e00f3e5 | ||
|
|
3b776b6639 | ||
|
|
dd8f2744bf | ||
|
|
7db8d3ea0f | ||
|
|
ad7a17bf40 | ||
|
|
76ac5238b6 | ||
|
|
add73aa2c5 | ||
|
|
bcf9491999 | ||
|
|
d0041a98b4 | ||
|
|
29d152784f | ||
|
|
cd7214ba8d | ||
|
|
6a84e73a82 | ||
|
|
98ce5103a0 | ||
|
|
c65a36d3ab | ||
|
|
b6e49da288 | ||
|
|
45998f9cf5 | ||
|
|
4197f63751 | ||
|
|
ace8134a16 | ||
|
|
7f1fecb84e | ||
|
|
bf172fab81 | ||
|
|
36f5648cae | ||
|
|
ab57bfcc4a | ||
|
|
11848b8f44 | ||
|
|
a11e0bd9c3 | ||
|
|
f6552d0d4f | ||
|
|
38d4db5d5f | ||
|
|
63cd379682 | ||
|
|
9136c9306a | ||
|
|
c9db9f33ea | ||
|
|
3d7178d06f | ||
|
|
a4ff5a3f73 | ||
|
|
814c5b3d3c | ||
|
|
e7e0677291 | ||
|
|
823f4b7ad1 | ||
|
|
a3c77480f7 | ||
|
|
e367265dbb | ||
|
|
7e0deb29e0 | ||
|
|
0d94db4331 | ||
|
|
177482b33a | ||
|
|
63b183a9fe | ||
|
|
858117f8c0 | ||
|
|
ac4355d2e1 | ||
|
|
ce7da2db66 | ||
|
|
0a4a1def1e | ||
|
|
35f4deca76 | ||
|
|
ba1451a0e9 | ||
|
|
40d69e6e20 | ||
|
|
b8ba947ba8 | ||
|
|
06be57815e | ||
|
|
81e37a5736 | ||
|
|
b8ea546b3f | ||
|
|
0bb31b985d | ||
|
|
453824260f | ||
|
|
a8fdffc3e9 | ||
|
|
24164d9454 | ||
|
|
4365a94ea9 | ||
|
|
7c1ec04380 | ||
|
|
09b6365321 | ||
|
|
eb2e383cc7 |
@@ -8,4 +8,5 @@ README.md
|
||||
|
||||
.yalc/
|
||||
yalc.lock
|
||||
testApi/
|
||||
testApi/
|
||||
*.local.*
|
||||
2
.github/ISSUE_TEMPLATE/bugs.md
vendored
@@ -11,7 +11,7 @@ assignees: ''
|
||||
[//]: # '方框内填 x 表示打钩'
|
||||
|
||||
- [ ] 我已确认目前没有类似 issue
|
||||
- [ ] 我已完整查看过项目 README,以及[项目文档](https://doc.fastgpt.run/docs/intro/)
|
||||
- [ ] 我已完整查看过项目 README,以及[项目文档](https://doc.fastgpt.in/docs/intro/)
|
||||
- [ ] 我使用了自己的 key,并确认我的 key 是可正常使用的
|
||||
- [ ] 我理解并愿意跟进此 issue,协助测试和提供反馈
|
||||
- [x] 我理解并认可上述内容,并理解项目维护者精力有限,**不遵循规则的 issue 可能会被无视或直接关闭**
|
||||
|
||||
2
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,5 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: 微信交流群
|
||||
url: https://doc.fastgpt.run/wechat-fastgpt.webp
|
||||
url: https://doc.fastgpt.in/wechat-fastgpt.webp
|
||||
about: FastGPT 全是问题群
|
||||
|
||||
15
.github/imgs/logo-left.svg
vendored
Normal file
|
After Width: | Height: | Size: 10 KiB |
19
.github/workflows/bot-issues-translator.yml
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
name: 'Github Rebot for issues-translator'
|
||||
on:
|
||||
issues:
|
||||
types: [ opened ]
|
||||
issue_comment:
|
||||
types: [ created ]
|
||||
jobs:
|
||||
translate:
|
||||
permissions:
|
||||
issues: write
|
||||
discussions: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: usthe/issues-translate-action@v2.7
|
||||
with:
|
||||
IS_MODIFY_TITLE: true
|
||||
BOT_GITHUB_TOKEN: ${{ secrets.GH_PAT }}
|
||||
CUSTOM_BOT_NOTE: Bot detected the issue body's language is not English, translate it automatically. 👯👭🏻🧑🤝🧑👫🧑🏿🤝🧑🏻👩🏾🤝👨🏿👬🏿
|
||||
2
.github/workflows/deploy-docs.yml
vendored
@@ -55,8 +55,6 @@ jobs:
|
||||
# Step 4 - Builds the site using Hugo
|
||||
- name: Build
|
||||
run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs && hugo -v --minify
|
||||
env:
|
||||
HUGO_BASEURL: ${{ vars.BASE_URL }}
|
||||
|
||||
# Step 5 - Push our generated site to Vercel
|
||||
- name: Deploy to Vercel
|
||||
|
||||
6
.github/workflows/deploy-preview.yml
vendored
@@ -2,6 +2,10 @@ name: deploy-docs-preview
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'docSite/**'
|
||||
branches:
|
||||
- 'main'
|
||||
workflow_dispatch:
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
@@ -51,8 +55,6 @@ jobs:
|
||||
# Step 4 - Builds the site using Hugo
|
||||
- name: Build
|
||||
run: cd docSite && hugo mod get -u github.com/colinwilson/lotusdocs && hugo -v --minify
|
||||
env:
|
||||
HUGO_BASEURL: ${{ vars.BASE_URL }}
|
||||
|
||||
# Step 5 - Push our generated site to Vercel
|
||||
- name: Deploy to Vercel
|
||||
|
||||
98
.github/workflows/docs-image.yml
vendored
Normal file
@@ -0,0 +1,98 @@
|
||||
name: Build FastGPT docs images and copy image to docker hub
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'docSite/**'
|
||||
branches:
|
||||
- 'main'
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
jobs:
|
||||
build-fastgpt-docs-images:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- 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@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ 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
|
||||
run: |
|
||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-docs:latest" >> $GITHUB_ENV
|
||||
else
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-docs:${{ github.ref_name }}" >> $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 \
|
||||
--build-arg name=app \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--label "org.opencontainers.image.source= https://github.com/ ${{ github.repository_owner }}/FastGPT" \
|
||||
--label "org.opencontainers.image.description=fastgpt image" \
|
||||
--label "org.opencontainers.image.licenses=Apache" \
|
||||
--push \
|
||||
--cache-from=type=local,src=/tmp/.buildx-cache \
|
||||
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
||||
-t ${DOCKER_REPO_TAGGED} \
|
||||
-f docSite/Dockerfile \
|
||||
.
|
||||
push-to-docker-hub:
|
||||
needs: build-fastgpt-docs-images
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'labring/FastGPT'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_HUB_NAME }}
|
||||
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||
- name: Set DOCKER_REPO_TAGGED based on branch or tag
|
||||
run: |
|
||||
if [[ "${{ github.ref_name }}" == "main" ]]; then
|
||||
echo "IMAGE_TAG=latest" >> $GITHUB_ENV
|
||||
else
|
||||
echo "IMAGE_TAG=${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
fi
|
||||
- name: Pull image from GitHub Container Registry
|
||||
run: docker pull ghcr.io/${{ github.repository_owner }}/fastgpt-docs:${{env.IMAGE_TAG}}
|
||||
- name: Tag image with Docker Hub repository name and version tag
|
||||
run: docker tag ghcr.io/${{ github.repository_owner }}/fastgpt-docs:${{env.IMAGE_TAG}} ${{ secrets.DOCKER_IMAGE_NAME }}:${{env.IMAGE_TAG}}
|
||||
- name: Push image to Docker Hub
|
||||
run: docker push ${{ secrets.DOCKER_IMAGE_NAME }}:${{env.IMAGE_TAG}}
|
||||
|
||||
update-docs-image:
|
||||
needs: build-fastgpt-docs-images
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'labring/FastGPT'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
- uses: actions-hub/kubectl@master
|
||||
env:
|
||||
KUBE_CONFIG: ${{ secrets.KUBE_CONFIG }}
|
||||
with:
|
||||
args: rollout restart deployment fastgpt-docs
|
||||
52
.github/workflows/fastgpt-image-personal.yml
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
name: Build FastGPT images in Personal warehouse
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'projects/app/**'
|
||||
- 'packages/**'
|
||||
branches:
|
||||
- 'main'
|
||||
jobs:
|
||||
build-fastgpt-images:
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.repository != 'labring/FastGPT'
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- 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 }}
|
||||
restore-keys: |
|
||||
${{ 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
|
||||
run: |
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt:latest" >> $GITHUB_ENV
|
||||
- name: Build and publish image for main branch or tag push event
|
||||
env:
|
||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||
run: |
|
||||
docker buildx build \
|
||||
--build-arg name=app \
|
||||
--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 ${DOCKER_REPO_TAGGED} \
|
||||
-f Dockerfile \
|
||||
.
|
||||
24
.github/workflows/fastgpt-image.yml
vendored
@@ -1,11 +1,10 @@
|
||||
name: Build fastgpt images and copy image to docker hub
|
||||
name: Build FastGPT images and copy image to docker hub
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
paths:
|
||||
- 'client/**'
|
||||
branches:
|
||||
- 'main'
|
||||
- 'projects/app/**'
|
||||
- 'packages/**'
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
jobs:
|
||||
@@ -25,6 +24,13 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v2
|
||||
with:
|
||||
driver-opts: network=host
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: /tmp/.buildx-cache
|
||||
key: ${{ runner.os }}-buildx-${{ github.sha }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-buildx-
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
@@ -38,24 +44,25 @@ jobs:
|
||||
else
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt:${{ github.ref_name }}" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Build and publish image for main branch or tag push event
|
||||
env:
|
||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||
run: |
|
||||
cd client && \
|
||||
docker buildx build \
|
||||
--build-arg name=app \
|
||||
--platform linux/amd64,linux/arm64 \
|
||||
--label "org.opencontainers.image.source= https://github.com/ ${{ github.repository_owner }}/FastGPT" \
|
||||
--label "org.opencontainers.image.source=https://github.com/${{ github.repository_owner }}/FastGPT" \
|
||||
--label "org.opencontainers.image.description=fastgpt image" \
|
||||
--label "org.opencontainers.image.licenses=MIT" \
|
||||
--push \
|
||||
--cache-from=type=local,src=/tmp/.buildx-cache \
|
||||
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
||||
-t ${DOCKER_REPO_TAGGED} \
|
||||
-f Dockerfile \
|
||||
.
|
||||
push-to-docker-hub:
|
||||
needs: build-fastgpt-images
|
||||
runs-on: ubuntu-20.04
|
||||
if: github.repository == 'labring/FastGPT'
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
@@ -79,6 +86,7 @@ jobs:
|
||||
run: docker push ${{ secrets.DOCKER_IMAGE_NAME }}:${{env.IMAGE_TAG}}
|
||||
push-to-ali-hub:
|
||||
needs: build-fastgpt-images
|
||||
if: github.repository == 'labring/FastGPT'
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
|
||||
56
.github/workflows/preview-image.yml
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
name: Preview FastGPT images
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'projects/app/**'
|
||||
- 'packages/**'
|
||||
branches:
|
||||
- 'main'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build-fastgpt-images:
|
||||
runs-on: ubuntu-20.04
|
||||
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
|
||||
- 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 }}
|
||||
restore-keys: |
|
||||
${{ 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
|
||||
run: |
|
||||
echo "DOCKER_REPO_TAGGED=ghcr.io/${{ github.repository_owner }}/fastgpt-pr:${{ github.event.pull_request.number }}" >> $GITHUB_ENV
|
||||
- name: Build image for PR
|
||||
env:
|
||||
DOCKER_REPO_TAGGED: ${{ env.DOCKER_REPO_TAGGED }}
|
||||
run: |
|
||||
docker buildx build \
|
||||
--build-arg name=app \
|
||||
--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" \
|
||||
--push \
|
||||
--cache-from=type=local,src=/tmp/.buildx-cache \
|
||||
--cache-to=type=local,dest=/tmp/.buildx-cache \
|
||||
-t ${DOCKER_REPO_TAGGED} \
|
||||
-f Dockerfile \
|
||||
.
|
||||
5
.gitignore
vendored
@@ -33,4 +33,7 @@ dist/
|
||||
|
||||
# hugo
|
||||
**/.hugo_build.lock
|
||||
docSite/public/
|
||||
docSite/public/
|
||||
docSite/resources/_gen/
|
||||
docSite/.vercel
|
||||
*.local.*
|
||||
4
.vscode/settings.json
vendored
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"editor.formatOnSave": true,
|
||||
"editor.mouseWheelZoom": true,
|
||||
"typescript.tsdk": "client/node_modules/typescript/lib",
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"prettier.prettierPath": "./node_modules/prettier",
|
||||
"i18n-ally.localesPaths": [
|
||||
"client/public/locales"
|
||||
"projects/app/public/locales"
|
||||
],
|
||||
"i18n-ally.enabledParsers": ["json"],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
|
||||
95
Dockerfile
Normal file
@@ -0,0 +1,95 @@
|
||||
# --------- install dependence -----------
|
||||
FROM node:18.17-alpine AS mainDeps
|
||||
WORKDIR /app
|
||||
|
||||
ARG name
|
||||
ARG proxy
|
||||
|
||||
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
|
||||
RUN apk add --no-cache libc6-compat && npm install -g pnpm@8.6.0
|
||||
# if proxy exists, set proxy
|
||||
RUN [ -z "$proxy" ] || pnpm config set registry https://registry.npm.taobao.org
|
||||
|
||||
# copy packages and one project
|
||||
COPY pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
COPY ./packages ./packages
|
||||
COPY ./projects/$name/package.json ./projects/$name/package.json
|
||||
|
||||
RUN [ -f pnpm-lock.yaml ] || (echo "Lockfile not found." && exit 1)
|
||||
|
||||
RUN pnpm i
|
||||
|
||||
# --------- install dependence -----------
|
||||
FROM node:18.17-alpine AS workerDeps
|
||||
WORKDIR /app
|
||||
|
||||
ARG proxy
|
||||
|
||||
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
|
||||
RUN apk add --no-cache libc6-compat && npm install -g pnpm@8.6.0
|
||||
# if proxy exists, set proxy
|
||||
RUN [ -z "$proxy" ] || pnpm config set registry https://registry.npm.taobao.org
|
||||
|
||||
COPY ./worker /app/worker
|
||||
RUN cd /app/worker && pnpm i --production --ignore-workspace
|
||||
|
||||
# --------- builder -----------
|
||||
FROM node:18.17-alpine AS builder
|
||||
WORKDIR /app
|
||||
|
||||
ARG name
|
||||
ARG proxy
|
||||
|
||||
# copy common node_modules and one project node_modules
|
||||
COPY package.json pnpm-workspace.yaml ./
|
||||
COPY --from=mainDeps /app/node_modules ./node_modules
|
||||
COPY --from=mainDeps /app/packages ./packages
|
||||
COPY ./projects/$name ./projects/$name
|
||||
COPY --from=mainDeps /app/projects/$name/node_modules ./projects/$name/node_modules
|
||||
|
||||
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
|
||||
|
||||
RUN apk add --no-cache libc6-compat && npm install -g pnpm@8.6.0
|
||||
RUN pnpm --filter=$name build
|
||||
|
||||
# --------- runner -----------
|
||||
FROM node:18.17-alpine AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ARG name
|
||||
ARG proxy
|
||||
|
||||
# create user and use it
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
RUN [ -z "$proxy" ] || sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories
|
||||
RUN apk add --no-cache curl ca-certificates \
|
||||
&& update-ca-certificates
|
||||
|
||||
# copy running files
|
||||
COPY --from=builder /app/projects/$name/public /app/projects/$name/public
|
||||
COPY --from=builder /app/projects/$name/next.config.js /app/projects/$name/next.config.js
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/projects/$name/.next/standalone /app/
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/projects/$name/.next/static /app/projects/$name/.next/static
|
||||
# copy package.json to version file
|
||||
COPY --from=builder /app/projects/$name/package.json ./package.json
|
||||
# copy woker
|
||||
COPY --from=workerDeps /app/worker /app/worker
|
||||
# copy config
|
||||
COPY ./projects/$name/data/config.json /app/data/config.json
|
||||
COPY ./projects/$name/data/pluginTemplates /app/data/pluginTemplates
|
||||
COPY ./projects/$name/data/simpleTemplates /app/data/simpleTemplates
|
||||
|
||||
|
||||
ENV NODE_ENV production
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
ENV PORT=3000
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
USER nextjs
|
||||
|
||||
ENV serverPath=./projects/$name/server.js
|
||||
|
||||
ENTRYPOINT ["sh","-c","node ${serverPath}"]
|
||||
185
README.md
@@ -4,84 +4,116 @@
|
||||
|
||||
# FastGPT
|
||||
|
||||
<p align="center">
|
||||
<a href="./README_en.md">English</a> |
|
||||
<a href="./README.md">简体中文</a> |
|
||||
<a href="./README_ja.md">日语</a>
|
||||
</p>
|
||||
|
||||
FastGPT 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景!
|
||||
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fastgpt.run/">线上体验</a>
|
||||
·
|
||||
<a href="https://doc.fastgpt.run/docs/intro">相关文档</a>
|
||||
·
|
||||
<a href="https://doc.fastgpt.run/docs/development">本地开发</a>
|
||||
·
|
||||
<a href="https://github.com/labring/FastGPT#-%E7%9B%B8%E5%85%B3%E9%A1%B9%E7%9B%AE">相关项目</a>
|
||||
<a href="https://fastgpt.run/">
|
||||
<img height="21" src="https://img.shields.io/badge/在线使用-d4eaf7?style=flat-square&logo=spoj&logoColor=7d09f1" alt="cloud">
|
||||
</a>
|
||||
<a href="https://doc.fastgpt.in/docs/intro">
|
||||
<img height="21" src="https://img.shields.io/badge/相关文档-7d09f1?style=flat-square" alt="document">
|
||||
</a>
|
||||
<a href="https://doc.fastgpt.in/docs/development">
|
||||
<img height="21" src="https://img.shields.io/badge/本地开发-%23d4eaf7?style=flat-square&logo=xcode&logoColor=7d09f1" alt="development">
|
||||
</a>
|
||||
<a href="/#-%E7%9B%B8%E5%85%B3%E9%A1%B9%E7%9B%AE">
|
||||
<img height="21" src="https://img.shields.io/badge/相关项目-7d09f1?style=flat-square" alt="project">
|
||||
</a>
|
||||
<a href="https://github.com/labring/FastGPT/blob/main/LICENSE">
|
||||
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409bd33f6d4
|
||||
|
||||
## 🛸 在线体验
|
||||
## 🛸 在线使用
|
||||
|
||||
[fastgpt.run](https://fastgpt.run/)(服务器在新加坡,部分地区可能无法直连)
|
||||
- 🌐 国内版:[ai.fastgpt.in](https://ai.fastgpt.in/)
|
||||
- 🌍 海外版:[fastgpt.run](https://fastgpt.run/)
|
||||
|
||||
| | |
|
||||
| ---------------------------------- | ---------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 💡 功能
|
||||
|
||||
1. 强大的可视化编排,轻松构建 AI 应用
|
||||
`1` 强大的可视化编排,轻松构建 AI 应用
|
||||
- [x] 提供简易模式,无需操作编排
|
||||
- [x] 用户对话前引导, 全局字符串变量
|
||||
- [x] 用户对话前引导,全局字符串变量
|
||||
- [x] 知识库搜索
|
||||
- [x] 多 LLM 模型对话
|
||||
- [x] 文本内容提取成结构化数据
|
||||
- [x] HTTP 扩展
|
||||
- [ ] 嵌入 Laf,实现在线编写 HTTP 模块
|
||||
- [ ] 连续对话引导
|
||||
- [ ] 嵌入 [Laf](https://github.com/labring/laf),实现在线编写 HTTP 模块
|
||||
- [x] 对话下一步指引
|
||||
- [ ] 对话多路线选择
|
||||
- [x] 源文件引用追踪
|
||||
- [ ] 自定义文件阅读器
|
||||
2. 丰富的知识库预处理
|
||||
- [x] 模块封装,实现多级复用
|
||||
|
||||
`2` 丰富的知识库预处理
|
||||
- [x] 多库复用,混用
|
||||
- [x] chunk 记录修改和删除
|
||||
- [x] 支持 手动输入, 直接分段, QA 拆分导入
|
||||
- [x] 支持 url 读取、 CSV 批量导入
|
||||
- [x] 支持手动输入,直接分段,QA 拆分导入
|
||||
- [x] 支持 url 读取、CSV 批量导入
|
||||
- [x] 支持知识库单独设置向量模型
|
||||
- [x] 源文件存储
|
||||
- [ ] 文件学习 Agent
|
||||
3. 多种效果测试渠道
|
||||
|
||||
`3` 多种效果测试渠道
|
||||
- [x] 知识库单点搜索测试
|
||||
- [x] 对话时反馈引用并可修改与删除
|
||||
- [x] 完整上下文呈现
|
||||
- [ ] 完整模块中间值呈现
|
||||
4. OpenAPI
|
||||
- [x] completions 接口(对齐 GPT 接口)
|
||||
- [x] 完整模块中间值呈现
|
||||
|
||||
`4` OpenAPI
|
||||
- [x] completions 接口 (对齐 GPT 接口)
|
||||
- [ ] 知识库 CRUD
|
||||
5. 运营功能
|
||||
|
||||
`5` 运营功能
|
||||
- [x] 免登录分享窗口
|
||||
- [x] Iframe 一键嵌入
|
||||
- [x] 统一查阅对话记录,并对数据进行标注
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 👨💻 开发
|
||||
|
||||
项目技术栈: NextJs + TS + ChakraUI + Mongo + Postgres(Vector 插件)
|
||||
项目技术栈:NextJs + TS + ChakraUI + Mongo + Postgres (Vector 插件)
|
||||
|
||||
- **⚡ 快速部署**
|
||||
|
||||
> Sealos 的服务器在国外,不需要额外处理网络问题,无需服务器、无需魔法、无需域名,支持高并发 & 动态伸缩。点击以下按钮即可一键部署 👇
|
||||
> [Sealos](https://sealos.io) 的服务器在国外,不需要额外处理网络问题,无需服务器、无需魔法、无需域名,支持高并发 & 动态伸缩。点击以下按钮即可一键部署 👇
|
||||
|
||||
[](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
|
||||
|
||||
由于需要部署数据库,部署完后需要等待 2~4 分钟才能正常访问。默认用了最低配置,首次访问时会有些慢。
|
||||
|
||||
* [快开始本地开发](https://doc.fastgpt.run/docs/development/intro/)
|
||||
* [部署 FastGPT](https://doc.fastgpt.run/docs/installation)
|
||||
* [系统配置文件说明](https://doc.fastgpt.run/docs/development/configuration/)
|
||||
* [多模型配置](https://doc.fastgpt.run/docs/installation/one-api/)
|
||||
* [版本升级](https://doc.fastgpt.run/docs/installation/upgrading)
|
||||
* [API 文档](https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh?pre_pathname=%2Fdrive%2Fhome%2F)
|
||||
* [快开始本地开发](https://doc.fastgpt.in/docs/development/intro/)
|
||||
* [部署 FastGPT](https://doc.fastgpt.in/docs/development/sealos)
|
||||
* [系统配置文件说明](https://doc.fastgpt.in/docs/development/configuration/)
|
||||
* [多模型配置](https://doc.fastgpt.in/docs/development/one-api/)
|
||||
* [版本更新/升级介绍](https://doc.fastgpt.in/docs/development/upgrading)
|
||||
* [OpenAPI API 文档](https://doc.fastgpt.in/docs/development/openapi/)
|
||||
* [知识库结构详解](https://doc.fastgpt.in/docs/use-cases/datasetengine/)
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 🏘️ 社区交流群
|
||||
|
||||
@@ -89,33 +121,94 @@ https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409b
|
||||
|
||||

|
||||
|
||||
## 👀 其他
|
||||
|
||||
- [FastGPT 常见问题](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
- [docker 部署教程视频](https://www.bilibili.com/video/BV1jo4y147fT/)
|
||||
- [公众号接入视频教程](https://www.bilibili.com/video/BV1xh4y1t7fy/)
|
||||
- [FastGPT 知识库演示](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 💪 相关项目
|
||||
|
||||
- [Laf: 3 分钟快速接入三方应用](https://github.com/labring/laf)
|
||||
- [Sealos: 快速部署集群应用](https://github.com/labring/sealos)
|
||||
- [One API: 多模型管理,支持 Azure、文心一言等](https://github.com/songquanpeng/one-api)
|
||||
- [TuShan: 5 分钟搭建后台管理系统](https://github.com/msgbyte/tushan)
|
||||
- [Laf:3 分钟快速接入三方应用](https://github.com/labring/laf)
|
||||
- [Sealos:快速部署集群应用](https://github.com/labring/sealos)
|
||||
- [One API:多模型管理,支持 Azure、文心一言等](https://github.com/songquanpeng/one-api)
|
||||
- [TuShan:5 分钟搭建后台管理系统](https://github.com/msgbyte/tushan)
|
||||
|
||||
## 🤝 第三方生态
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
- [luolinAI: 企微机器人,开箱即用](https://github.com/luolin-ai/FastGPT-Enterprise-WeChatbot)
|
||||
## 👀 其他
|
||||
|
||||
- [保姆级 FastGPT 教程](https://www.bilibili.com/video/BV1n34y1A7Bo/?spm_id_from=333.999.0.0)
|
||||
- [接入飞书](https://www.bilibili.com/video/BV1Su4y1r7R3/?spm_id_from=333.999.0.0)
|
||||
- [接入企微](https://www.bilibili.com/video/BV1Tp4y1n72T/?spm_id_from=333.999.0.0)
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 🌿 第三方生态
|
||||
|
||||
- [OnWeChat 个人微信/企微机器人](https://doc.fastgpt.in/docs/use-cases/onwechat/)
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 🤝 参与贡献
|
||||
|
||||
我们非常欢迎各种形式的贡献。如果你对贡献代码感兴趣,可以查看我们的 GitHub [Issues](https://github.com/labring/FastGPT/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc),大展身手,向我们展示你的奇思妙想。
|
||||
|
||||
<a href="https://github.com/labring/FastGPT/graphs/contributors" target="_blank">
|
||||
<table>
|
||||
<tr>
|
||||
<th colspan="2">
|
||||
<br><img src="https://contrib.rocks/image?repo=labring/FastGPT"><br><br>
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=active&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=dark">
|
||||
<img alt="Active participants of labring - past 28 days" src="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=active&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=light">
|
||||
</picture>
|
||||
</td>
|
||||
<td rowspan="2">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-org-participants-growth/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=4x7&color_scheme=dark">
|
||||
<img alt="New trends of labring" src="https://next.ossinsight.io/widgets/official/compose-org-participants-growth/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=4x7&color_scheme=light">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=dark">
|
||||
<img alt="New participants of labring - past 28 days" src="https://next.ossinsight.io/widgets/official/compose-org-active-contributors/thumbnail.png?activity=new&period=past_28_days&owner_id=102226726&repo_ids=605673387&image_size=2x3&color_scheme=light">
|
||||
</picture>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</a>
|
||||
|
||||
## 🌟 Star History
|
||||
|
||||
[](https://star-history.com/#labring/FastGPT&Date)
|
||||
<a href="https://github.com/labring/FastGPT/stargazers" target="_blank" style="display: block" align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=labring/FastGPT&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=labring/FastGPT&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=labring/FastGPT&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-返回顶部-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 使用协议
|
||||
|
||||
本仓库遵循 [FastGPT Open Source License](./LICENSE) 开源协议。
|
||||
|
||||
1. 允许作为后台服务直接商用,但不允许直接使用 saas 服务商用。
|
||||
2. 需保留相关版权信息。
|
||||
1. 允许作为后台服务直接商用,但不允许提供 SaaS 服务。
|
||||
2. 未经商业授权,任何形式的商用服务均需保留相关版权信息。
|
||||
3. 完整请查看 [FastGPT Open Source License](./LICENSE)
|
||||
4. 联系方式:yujinlong@sealos.io, [点击查看定价策略](https://fael3z0zfze.feishu.cn/docx/F155dbirfo8vDDx2WgWc6extnwf)
|
||||
4. 联系方式:yujinlong@sealos.io,[点击查看商业版定价策略](https://doc.fastgpt.in/docs/commercial)
|
||||
120
README_en.md
@@ -1,25 +1,40 @@
|
||||
<div align="center">
|
||||
|
||||
<a href="https://fastgpt.run/"><img src="/.github/imgs/logo.svg" width="120" height="120" alt="fastgpt logo"></a>
|
||||
|
||||
# FastGPT
|
||||
|
||||
FastGPT is a knowledge-based question answering system built on the LLM. It offers out-of-the-box data processing and model invocation capabilities. Moreover, it allows for workflow orchestration through Flow visualization, thereby enabling complex question and answer scenarios!
|
||||
<p align="center">
|
||||
<a href="./README_en.md">English</a> |
|
||||
<a href="./README.md">简体中文</a> |
|
||||
<a href="./README_ja.md">日语</a>
|
||||
</p>
|
||||
|
||||
FastGPT is a knowledge-based Q&A system built on the LLM, offers out-of-the-box data processing and model invocation capabilities, allows for workflow orchestration through Flow visualization!
|
||||
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fastgpt.run/">Online</a>
|
||||
·
|
||||
<a href="https://doc.fastgpt.run/docs/intro">Document</a>
|
||||
·
|
||||
<a href="https://doc.fastgpt.run/docs/development">Development</a>
|
||||
·
|
||||
<a href="https://doc.fastgpt.run/docs/installation">Deploy</a>
|
||||
·
|
||||
<a href="#powered-by">Power By</a>
|
||||
<a href="https://fastgpt.run/">
|
||||
<img height="21" src="https://img.shields.io/badge/在线使用-d4eaf7?style=flat-square&logo=spoj&logoColor=7d09f1" alt="cloud">
|
||||
</a>
|
||||
<a href="https://doc.fastgpt.run/docs/intro">
|
||||
<img height="21" src="https://img.shields.io/badge/相关文档-7d09f1?style=flat-square" alt="document">
|
||||
</a>
|
||||
<a href="https://doc.fastgpt.run/docs/development">
|
||||
<img height="21" src="https://img.shields.io/badge/本地开发-%23d4eaf7?style=flat-square&logo=xcode&logoColor=7d09f1" alt="development">
|
||||
</a>
|
||||
<a href="/#-%E7%9B%B8%E5%85%B3%E9%A1%B9%E7%9B%AE">
|
||||
<img height="21" src="https://img.shields.io/badge/相关项目-7d09f1?style=flat-square" alt="project">
|
||||
</a>
|
||||
<a href="https://github.com/labring/FastGPT/blob/main/LICENSE">
|
||||
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## 🛸 Online
|
||||
https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409bd33f6d4
|
||||
|
||||
## 🛸 Use Cloud Services
|
||||
|
||||
[fastgpt.run](https://fastgpt.run/)
|
||||
| | |
|
||||
@@ -27,37 +42,40 @@ FastGPT is a knowledge-based question answering system built on the LLM. It offe
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 💡 Features
|
||||
|
||||
1. Powerful visual orchestration for easy AI application building
|
||||
1. Powerful visual workflows: Effortlessly craft AI applications
|
||||
|
||||
- [x] Provides a simple mode without the need for orchestration operations
|
||||
- [x] Simple mode on deck - no need for manual arrangement
|
||||
- [x] User dialogue pre-guidance
|
||||
- [x] Global variables
|
||||
- [x] Knowledge base search
|
||||
- [x] Multi-LLM model dialogue
|
||||
- [x] Extraction of text content into structured data
|
||||
- [x] HTTP extension
|
||||
- [ ] Sandbox JS runtime module
|
||||
- [ ] Continuous dialogue guidance
|
||||
- [ ] Dialogue multi-path selection
|
||||
- [ ] Source file reference tracking
|
||||
- [x] Dialogue via multiple LLM models
|
||||
- [x] Text magic - convert to structured data
|
||||
- [x] Extend with HTTP
|
||||
- [ ] Embed Laf for on-the-fly HTTP module crafting
|
||||
- [x] Directions for the next dialogue steps
|
||||
- [x] Tracking source file references
|
||||
- [ ] Custom file reader
|
||||
- [ ] Modules are packaged into plug-ins to achieve reuse
|
||||
|
||||
2. Rich knowledge base preprocessing
|
||||
2. Extensive knowledge base preprocessing
|
||||
|
||||
- [x] Multiple library reuse and mixing
|
||||
- [x] Chunk record modification and deletion
|
||||
- [x] Supports direct segment import
|
||||
- [x] Supports QA split import
|
||||
- [x] Supports manual input content
|
||||
- [ ] Supports URL import reading
|
||||
- [x] Supports batch import of Q&A pairs in CSV format
|
||||
- [ ] Supports separate vector model settings for knowledge bases
|
||||
- [ ] Source file storage
|
||||
- [x] Reuse and mix multiple knowledge bases
|
||||
- [x] Track chunk modifications and deletions
|
||||
- [x] Supports manual entries, direct segmentation, and QA split imports
|
||||
- [x] Supports URL fetching and batch CSV imports
|
||||
- [x] Supports Set unique vector models for knowledge bases
|
||||
- [x] Store original files
|
||||
- [ ] File learning Agent
|
||||
|
||||
3. Multiple effect testing channels
|
||||
|
||||
- [x] Knowledge base single point search testing
|
||||
- [x] Single-point knowledge base search test
|
||||
- [x] Feedback references and ability to modify and delete during dialogue
|
||||
- [x] Complete context presentation
|
||||
- [ ] Complete module intermediate value presentation
|
||||
@@ -73,15 +91,25 @@ FastGPT is a knowledge-based question answering system built on the LLM. It offe
|
||||
- [x] One-click embedding with Iframe
|
||||
- [ ] Unified access to dialogue records
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 👨💻 Development
|
||||
|
||||
Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
|
||||
|
||||
- **⚡ Deployment**
|
||||
|
||||
[](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
|
||||
|
||||
Give it a 2-4 minute wait after deployment as it sets up the database. Initially, it might be a tad slow since we're using the basic settings.
|
||||
|
||||
- [Getting Started with Local Development](https://doc.fastgpt.run/docs/development)
|
||||
- [Deploying FastGPT](https://doc.fastgpt.run/docs/installation)
|
||||
- [System Configuration File Explanation](https://doc.fastgpt.run/docs/installation/reference)
|
||||
- [Multi-model Configuration](https://doc.fastgpt.run/docs/installation/reference/models)
|
||||
- [V3 Upgrade V4 Initialization](https://doc.fastgpt.run/docs/installation/upgrading)
|
||||
- [Guide on System Configs](https://doc.fastgpt.run/docs/installation/reference)
|
||||
- [Configuring Multiple Models](https://doc.fastgpt.run/docs/installation/reference/models)
|
||||
- [Version Updates & Upgrades](https://doc.fastgpt.run/docs/installation/upgrading)
|
||||
|
||||
<!-- ## :point_right: RoadMap
|
||||
- [FastGPT RoadMap](https://kjqvjse66l.feishu.cn/docx/RVUxdqE2WolDYyxEKATcM0XXnte) -->
|
||||
@@ -92,6 +120,10 @@ Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
|
||||
| ------------------------------------------------- | ---------------------------------------------- |
|
||||
|  |  | -->
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 👀 Others
|
||||
|
||||
- [FastGPT FAQ](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
@@ -99,6 +131,10 @@ Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
|
||||
- [Official Account Integration Video Tutorial](https://www.bilibili.com/video/BV1xh4y1t7fy/)
|
||||
- [FastGPT Knowledge Base Demo](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 💪 Related Projects
|
||||
|
||||
- [Laf: 3-minute quick access to third-party applications](https://github.com/labring/laf)
|
||||
@@ -106,10 +142,24 @@ Project tech stack: NextJs + TS + ChakraUI + Mongo + Postgres (Vector plugin)
|
||||
- [One API: Multi-model management, supports Azure, Wenxin Yiyuan, etc.](https://github.com/songquanpeng/one-api)
|
||||
- [TuShan: Build a backend management system in 5 minutes](https://github.com/msgbyte/tushan)
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 🤝 Third-party Ecosystem
|
||||
|
||||
- [luolinAI: Enterprise WeChat bot, ready to use](https://github.com/luolin-ai/FastGPT-Enterprise-WeChatbot)
|
||||
|
||||
<a href="#readme">
|
||||
<img src="https://img.shields.io/badge/-Back_to_Top-7d09f1.svg" alt="#" align="right">
|
||||
</a>
|
||||
|
||||
## 🌟 Star History
|
||||
|
||||
[](https://star-history.com/#labring/FastGPT&Date)
|
||||
<a href="https://github.com/labring/FastGPT/stargazers" target="_blank" style="display: block" align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=labring/FastGPT&type=Date&theme=dark" />
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=labring/FastGPT&type=Date" />
|
||||
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=labring/FastGPT&type=Date" />
|
||||
</picture>
|
||||
</a>
|
||||
|
||||
135
README_ja.md
Normal file
@@ -0,0 +1,135 @@
|
||||
<div align="center">
|
||||
|
||||
<a href="https://fastgpt.run/"><img src="/.github/imgs/logo.svg" width="120" height="120" alt="fastgpt logo"></a>
|
||||
|
||||
# FastGPT
|
||||
|
||||
<p align="center">
|
||||
<a href="./README_en.md">English</a> |
|
||||
<a href="./README.md">简体中文</a> |
|
||||
<a href="./README_ja.md">日语</a>
|
||||
</p>
|
||||
|
||||
FastGPT は、LLM 上 に 構築 された 知識 ベースの Q&A システムで、すぐに 使 えるデータ 処理 とモデル 呼 び 出 し 機能 を 提供 し、Flow の 可視化 を 通 じてワークフローのオーケストレーションを 可能 にします!
|
||||
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://fastgpt.run/">
|
||||
<img height="21" src="https://img.shields.io/badge/在线使用-d4eaf7?style=flat-square&logo=spoj&logoColor=7d09f1" alt="cloud">
|
||||
</a>
|
||||
<a href="https://doc.fastgpt.run/docs/intro">
|
||||
<img height="21" src="https://img.shields.io/badge/相关文档-7d09f1?style=flat-square" alt="document">
|
||||
</a>
|
||||
<a href="https://doc.fastgpt.run/docs/development">
|
||||
<img height="21" src="https://img.shields.io/badge/本地开发-%23d4eaf7?style=flat-square&logo=xcode&logoColor=7d09f1" alt="development">
|
||||
</a>
|
||||
<a href="/#-%E7%9B%B8%E5%85%B3%E9%A1%B9%E7%9B%AE">
|
||||
<img height="21" src="https://img.shields.io/badge/相关项目-7d09f1?style=flat-square" alt="project">
|
||||
</a>
|
||||
<a href="https://github.com/labring/FastGPT/blob/main/LICENSE">
|
||||
<img height="21" src="https://img.shields.io/badge/License-Apache--2.0-ffffff?style=flat-square&labelColor=d4eaf7&color=7d09f1" alt="license">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
https://github.com/labring/FastGPT/assets/15308462/7d3a38df-eb0e-4388-9250-2409bd33f6d4
|
||||
|
||||
## 🛸 クラウドサービスの 利用
|
||||
|
||||
[fastgpt.run](https://fastgpt.run/)
|
||||
| | |
|
||||
| ---------------------------------- | ---------------------------------- |
|
||||
|  |  |
|
||||
|  |  |
|
||||
|
||||
## 💡 機能
|
||||
|
||||
1. パワフルなビジュアルワークフロー:AI アプリケーションを 簡単 に 作成
|
||||
|
||||
- [x] デッキのシンプルモード - マニュアルアレンジ 不要
|
||||
- [x] ユーザ 対話事前 ガイダンス
|
||||
- [x] グローバル 変数
|
||||
- [x] ナレッジベース 検索
|
||||
- [x] 複数 の LLM モデルによる 対話
|
||||
- [x] テキストマジック - 構造化 データへの 変換
|
||||
- [x] HTTP による 拡張
|
||||
- [ ] on-the-fly HTTP モジュールのための 埋 め 込 みLaf
|
||||
- [x] 次 の 対話 ステップへの 指示
|
||||
- [x] ソースファイル 参照 の 追跡
|
||||
- [ ] カスタムファイルリーダー
|
||||
- [ ] モジュールをプラグインにパッケージして 再利用 する
|
||||
|
||||
2. 広範 なナレッジベースの 前処理
|
||||
|
||||
- [x] 複数 のナレッジベースの 再利用 と 混合
|
||||
- [x] チャンクの 変更 と 削除 を 追跡
|
||||
- [x] 手動入力、直接分割、QA 分割 インポートをサポート
|
||||
- [x] URL フェッチとバッチ CSV インポートをサポート
|
||||
- [x] ナレッジベースにユニークなベクトルモデルを 設定可能
|
||||
- [x] オリジナルファイルの 保存
|
||||
- [ ] ファイル 学習 エージェント
|
||||
|
||||
3. 複数 の 効果測定 チャンネル
|
||||
|
||||
- [x] シングルポイントナレッジベース 検索 テスト
|
||||
- [x] 対話中 のフィードバック 参照 と 修正 ・ 削除機能
|
||||
- [x] 完全 なコンテキストの 提示
|
||||
- [ ] 完全 なモジュール 中間値提示
|
||||
|
||||
4. OpenAPI
|
||||
|
||||
- [x] 補完 インターフェイス (GPT インターフェイスに 合 わせる)
|
||||
- [ ] ナレッジベース CRUD
|
||||
|
||||
5. オペレーション 機能
|
||||
|
||||
- [x] ログイン 不要 の 共有 ウィンドウ
|
||||
- [x] Iframe によるワンクリック 埋 め 込 み
|
||||
- [ ] 対話記録 への 統一 されたアクセス
|
||||
|
||||
## 👨💻 開発
|
||||
|
||||
プロジェクトの 技術 スタック:NextJs + TS + ChakraUI + Mongo + Postgres (Vector プラグイン)
|
||||
|
||||
- **⚡ デプロイ**
|
||||
|
||||
[](https://cloud.sealos.io/?openapp=system-fastdeploy%3FtemplateName%3Dfastgpt)
|
||||
|
||||
デプロイ 後、データベースをセットアップするので、2~4分待 ってください。基本設定 を 使 っているので、最初 は 少 し 遅 いかもしれません。
|
||||
|
||||
- [ローカル 開発入門](https://doc.fastgpt.run/docs/development)
|
||||
- [FastGPT のデプロイ](https://doc.fastgpt.run/docs/installation)
|
||||
- [システム 設定 ガイド](https://doc.fastgpt.run/docs/installation/reference)
|
||||
- [複数 モデルの 設定](https://doc.fastgpt.run/docs/installation/reference/models)
|
||||
- [バージョン 更新 とアップグレード](https://doc.fastgpt.run/docs/installation/upgrading)
|
||||
|
||||
<!-- ## :point_right: ロードマップ
|
||||
- [FastGPT ロードマップ](https://kjqvjse66l.feishu.cn/docx/RVUxdqE2WolDYyxEKATcM0XXnte) -->
|
||||
|
||||
<!-- ## 🏘️ コミュニティ
|
||||
|
||||
| コミュニティグループ | アシスタント |
|
||||
| ------------------------------------------------- | ---------------------------------------------- |
|
||||
|  |  | -->
|
||||
|
||||
## 👀 その 他
|
||||
|
||||
- [FastGPT FAQ](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
- [Docker 導入 チュートリアル 動画](https://www.bilibili.com/video/BV1jo4y147fT/)
|
||||
- [公式 アカウント 統合 ビデオチュートリアル](https://www.bilibili.com/video/BV1xh4y1t7fy/)
|
||||
- [FastGPT ナレッジベースデモ](https://www.bilibili.com/video/BV1Wo4y1p7i1/)
|
||||
|
||||
## 💪 関連 プロジェクト
|
||||
|
||||
- [Laf:サードパーティ 製 アプリケーションに 3 分 でクイックアクセス](https://github.com/labring/laf)
|
||||
- [Sealos:クラスタアプリケーションの 迅速 な 展開](https://github.com/labring/sealos)
|
||||
- [One API:マルチモデル 管理、Azure、Wenxin Yiyuan などをサポートします。](https://github.com/songquanpeng/one-api)
|
||||
- [TuShan:5 分 でバックエンド 管理 システムを 構築](https://github.com/msgbyte/tushan)
|
||||
|
||||
## 🤝 サードパーティエコシステム
|
||||
|
||||
- [luolinAI:すぐに 使 える 企業向 け WeChat ボット](https://github.com/luolin-ai/FastGPT-Enterprise-WeChatbot)
|
||||
|
||||
## 🌟 Star History
|
||||
|
||||
[](https://star-history.com/#labring/FastGPT&Date)
|
||||
@@ -1,65 +0,0 @@
|
||||
# Install dependencies only when needed
|
||||
FROM node:current-alpine AS deps
|
||||
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
|
||||
RUN apk add --no-cache libc6-compat && npm install -g pnpm
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies based on the preferred package manager
|
||||
COPY package.json ./
|
||||
COPY pnpm-lock.yaml* ./
|
||||
RUN \
|
||||
[ -f pnpm-lock.yaml ] && pnpm fetch || \
|
||||
(echo "Lockfile not found." && exit 1)
|
||||
|
||||
# Rebuild the source code only when needed
|
||||
FROM node:current-alpine AS builder
|
||||
WORKDIR /app
|
||||
COPY --from=deps /app/node_modules ./node_modules
|
||||
COPY pnpm-lock.yaml* ./
|
||||
COPY package.json ./
|
||||
COPY . .
|
||||
|
||||
# Next.js collects completely anonymous telemetry data about general usage.
|
||||
# Learn more here: https://nextjs.org/telemetry
|
||||
# Uncomment the following line in case you want to disable telemetry during the build.
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
RUN npm install -g pnpm
|
||||
RUN \
|
||||
[ -f pnpm-lock.yaml ] && (pnpm --offline install && pnpm run build) || \
|
||||
(echo "Lockfile not found." && exit 1)
|
||||
|
||||
# Production image, copy all the files and run next
|
||||
FROM node:current-alpine AS runner
|
||||
WORKDIR /app
|
||||
|
||||
ENV NODE_ENV production
|
||||
# Uncomment the following line in case you want to disable telemetry during runtime.
|
||||
ENV NEXT_TELEMETRY_DISABLED 1
|
||||
|
||||
RUN addgroup --system --gid 1001 nodejs
|
||||
RUN adduser --system --uid 1001 nextjs
|
||||
|
||||
RUN sed -i 's/https/http/' /etc/apk/repositories
|
||||
RUN apk add curl \
|
||||
&& apk add ca-certificates \
|
||||
&& update-ca-certificates
|
||||
|
||||
# You only need to copy next.config.js if you are NOT using the default configuration
|
||||
# COPY --from=builder /app/next.config.js ./
|
||||
COPY --from=builder /app/public ./public
|
||||
COPY --from=builder /app/package.json ./package.json
|
||||
# COPY --from=builder /app/.env* .
|
||||
|
||||
# Automatically leverage output traces to reduce image size
|
||||
# https://nextjs.org/docs/advanced-features/output-file-tracing
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||
|
||||
USER nextjs
|
||||
|
||||
ENV PORT=3000
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
@@ -1,66 +0,0 @@
|
||||
{
|
||||
"FeConfig": {
|
||||
"show_emptyChat": true,
|
||||
"show_register": false,
|
||||
"show_appStore": false,
|
||||
"show_userDetail": false,
|
||||
"show_contact": true,
|
||||
"show_git": true,
|
||||
"show_doc": true,
|
||||
"systemTitle": "FastGPT",
|
||||
"authorText": "Made by FastGPT Team.",
|
||||
"limit": {
|
||||
"exportLimitMinutes": 0
|
||||
},
|
||||
"scripts": []
|
||||
},
|
||||
"SystemParams": {
|
||||
"vectorMaxProcess": 15,
|
||||
"qaMaxProcess": 15,
|
||||
"pgIvfflatProbe": 20
|
||||
},
|
||||
"ChatModels": [
|
||||
{
|
||||
"model": "gpt-3.5-turbo",
|
||||
"name": "GPT35-4k",
|
||||
"contextMaxToken": 4000,
|
||||
"quoteMaxToken": 2000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 0,
|
||||
"defaultSystem": ""
|
||||
},
|
||||
{
|
||||
"model": "gpt-3.5-turbo-16k",
|
||||
"name": "GPT35-16k",
|
||||
"contextMaxToken": 16000,
|
||||
"quoteMaxToken": 8000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 0,
|
||||
"defaultSystem": ""
|
||||
},
|
||||
{
|
||||
"model": "gpt-4",
|
||||
"name": "GPT4-8k",
|
||||
"contextMaxToken": 8000,
|
||||
"quoteMaxToken": 4000,
|
||||
"maxTemperature": 1.2,
|
||||
"price": 0,
|
||||
"defaultSystem": ""
|
||||
}
|
||||
],
|
||||
"VectorModels": [
|
||||
{
|
||||
"model": "text-embedding-ada-002",
|
||||
"name": "Embedding-2",
|
||||
"price": 0,
|
||||
"defaultToken": 500,
|
||||
"maxToken": 3000
|
||||
}
|
||||
],
|
||||
"QAModel": {
|
||||
"model": "gpt-3.5-turbo-16k",
|
||||
"name": "GPT35-16k",
|
||||
"maxToken": 16000,
|
||||
"price": 0
|
||||
}
|
||||
}
|
||||
5
client/next-env.d.ts
vendored
@@ -1,5 +0,0 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
||||
@@ -1,12 +0,0 @@
|
||||
//next-i18next.config.js
|
||||
/**
|
||||
* @type {import('next-i18next').UserConfig}
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
i18n: {
|
||||
defaultLocale: 'zh',
|
||||
locales: ['en', 'zh', 'zh-Hans', 'zh-CN'],
|
||||
localeDetection: false
|
||||
}
|
||||
};
|
||||
@@ -1,40 +0,0 @@
|
||||
/** @type {import('next').NextConfig} */
|
||||
const { i18n } = require('./next-i18next.config');
|
||||
|
||||
const nextConfig = {
|
||||
i18n,
|
||||
output: 'standalone',
|
||||
reactStrictMode: false,
|
||||
compress: true,
|
||||
|
||||
webpack(config, { isServer }) {
|
||||
if (!isServer) {
|
||||
config.resolve = {
|
||||
...config.resolve,
|
||||
fallback: {
|
||||
...config.resolve.fallback,
|
||||
fs: false
|
||||
}
|
||||
};
|
||||
}
|
||||
config.experiments = {
|
||||
asyncWebAssembly: true,
|
||||
layers: true
|
||||
};
|
||||
config.module = {
|
||||
...config.module,
|
||||
rules: config.module.rules.concat([
|
||||
{
|
||||
test: /\.svg$/i,
|
||||
issuer: /\.[jt]sx?$/,
|
||||
use: ['@svgr/webpack']
|
||||
}
|
||||
]),
|
||||
exprContextCritical: false
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
12504
client/pnpm-lock.yaml
generated
@@ -1,22 +0,0 @@
|
||||
### 常见问题
|
||||
|
||||
- [**Git 地址**,点击查看项目地址](https://github.com/labring/FastGPT)
|
||||
- [本地部署 FastGPT](https://doc.fastgpt.run/docs/installation)
|
||||
- [API 文档](https://kjqvjse66l.feishu.cn/docx/DmLedTWtUoNGX8xui9ocdUEjnNh?pre_pathname=%2Fdrive%2Fhome%2F)
|
||||
- **反馈问卷**: 如果你遇到任何使用问题或有期望的功能,可以[填写该问卷](https://www.wjx.cn/vm/rLIw1uD.aspx#)
|
||||
- **问题文档**: [先看文档,再提问](https://kjqvjse66l.feishu.cn/docx/HtrgdT0pkonP4kxGx8qcu6XDnGh)
|
||||
- [点击查看商业版文档](https://fael3z0zfze.feishu.cn/docx/F155dbirfo8vDDx2WgWc6extnwf)
|
||||
|
||||
**价格表**
|
||||
| 计费项 | 价格: 元/ 1K tokens(包含上下文)|
|
||||
| --- | --- |
|
||||
| 知识库 - 索引 | 0.002 |
|
||||
| FastAI4k - 对话 | 0.015 |
|
||||
| FastAI16k - 对话 | 0.03 |
|
||||
| FastAI-Plus - 对话 | 0.45 |
|
||||
| 文件 QA 拆分 | 0.03 |
|
||||
|
||||
**其他问题**
|
||||
| 交流群 | 小助手 |
|
||||
| ----------------------- | -------------------- |
|
||||
|  |  |
|
||||
@@ -1,7 +0,0 @@
|
||||
### Fast GPT V4.4.1
|
||||
|
||||
1. 新增 - 知识库目录结构
|
||||
2. 新增 - 分享链接支持配置 IP 限流、过期时间、最大额度等
|
||||
3. 优化 - [使用文档](https://doc.fastgpt.run/docs/intro/)
|
||||
4. [点击查看高级编排介绍文档](https://doc.fastgpt.run/docs/workflow)
|
||||
5. [点击查看商业版](https://fael3z0zfze.feishu.cn/docx/F155dbirfo8vDDx2WgWc6extnwf)
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1440" height="900" viewBox="0 0 1440 900" fill="none"><defs><path id="path_0"
|
||||
transform="translate(0 0) rotate(0 720 450)"
|
||||
d="M0,900L1440,900L1440,0L0,0L0,900Z" /><linearGradient id="linear_0" x1="45.776671171188354%" y1="3.4534157006028896%" x2="45.77667520943754%" y2="95.15074645348403%" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#4D3AF9" stop-opacity="1" /><stop offset="0.4571722174852774" stop-color="#3B4DFE" stop-opacity="1" /><stop offset="1" stop-color="#1A68FF" stop-opacity="1" /></linearGradient><linearGradient id="linear_1" x1="50%" y1="2.535076530612245%" x2="51%" y2="100%" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#4581FF" stop-opacity="1" /><stop offset="1" stop-color="#1C57FE" stop-opacity="1" /></linearGradient><linearGradient id="linear_2" x1="50%" y1="2.535076530612245%" x2="51%" y2="100%" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#4581FF" stop-opacity="1" /><stop offset="1" stop-color="#1C57FE" stop-opacity="1" /></linearGradient><linearGradient id="linear_3" x1="50%" y1="0%" x2="51%" y2="100%" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#29BFA8" stop-opacity="1" /><stop offset="1" stop-color="#34D1B6" stop-opacity="1" /></linearGradient><linearGradient id="linear_4" x1="50%" y1="2.535076530612245%" x2="51%" y2="100%" gradientUnits="objectBoundingBox"><stop offset="0" stop-color="#4581FF" stop-opacity="1" /><stop offset="1" stop-color="#1C57FE" stop-opacity="1" /></linearGradient></defs><g opacity="1" transform="translate(0 0) rotate(0 720 450)"><mask id="mask-0" fill="white"><use xlink:href="#path_0"></use></mask><g mask="url(#mask-0)"><path id="Mask" fill-rule="evenodd" fill="url(#linear_0)" transform="translate(0 0) rotate(0 720 450)" opacity="1" d="M0,900L1440,900L1440,0L0,0L0,900Z " /></g><g mask="url(#mask-0)"><path id="Oval" fill-rule="evenodd" fill="url(#linear_1)" transform="translate(51.66666666666667 412.5) rotate(0 37.5 37.5)" opacity="1" d="M75,37.5C75,16.79 58.21,0 37.5,0C16.79,0 0,16.79 0,37.5C0,58.21 16.79,75 37.5,75C58.21,75 75,58.21 75,37.5Z " /></g><g mask="url(#mask-0)"><path id="Oval Copy 2" fill-rule="evenodd" fill="url(#linear_2)" transform="translate(1049.166666666667 -3.333333333333333) rotate(0 30.833333333333336 30.833333333333336)" opacity="0.69" d="M61.67,30.83C61.67,13.81 47.86,0 30.83,0C13.81,0 0,13.81 0,30.83C0,47.86 13.81,61.67 30.83,61.67C47.86,61.67 61.67,47.86 61.67,30.83Z " /></g><g mask="url(#mask-0)"><g opacity="0.55" transform="translate(-64.16666666666667 547.4999999999999) rotate(0 50 226.5)"><path id="Rectangle 2" fill-rule="evenodd" style="fill:#5088FF" transform="translate(14.63769413070223 15.28431509376342) rotate(-45 35.20283757354478 34.876453649023716)" opacity="1" d="M8.17,61.88L70.41,69.75L47.22,22.97L0,0L8.17,61.88Z " /><path id="Rectangle 3" fill-rule="evenodd" fill="url(#linear_3)" transform="translate(0 50.20419426048581) rotate(0 26.487523940001577 201.14790286975716)" opacity="1" d="M0,402.3L51.51,402.3L52.98,23.25L0,0L0,402.3Z " /><path id="Rectangle 3 Copy" fill-rule="evenodd" fill="url(#linear_4)" transform="translate(48.48484848484848 50.20419426048581) rotate(0 25.75757575757576 201.14790286975716)" opacity="1" d="M0,402.3L51.52,402.3L51.52,0L0,21.68L0,402.3Z " /></g></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1694327751771" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4992" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M0 0h1024v1024H0V0z" fill="#202425" opacity=".01" p-id="4993"></path><path d="M136.533333 68.266667a68.266667 68.266667 0 0 0-68.266666 68.266666v428.305067a17.066667 17.066667 0 0 0 28.842666 12.356267l237.738667-226.440534a34.133333 34.133333 0 0 1 42.496-3.6864l268.288 178.858667a17.066667 17.066667 0 0 0 22.766933-3.447467L951.978667 171.4176A17.066667 17.066667 0 0 0 955.733333 160.699733V136.533333a68.266667 68.266667 0 0 0-68.266666-68.266666H136.533333z m819.2 255.3856a17.066667 17.066667 0 0 0-30.344533-10.717867l-221.866667 274.705067a17.066667 17.066667 0 0 0-3.7888 10.717866v340.309334a17.066667 17.066667 0 0 0 17.066667 17.066666h170.666667a68.266667 68.266667 0 0 0 68.266666-68.266666V323.652267zM614.4 955.733333a17.066667 17.066667 0 0 0 17.066667-17.066666v-330.990934a17.066667 17.066667 0 0 0-7.611734-14.199466l-204.8-136.533334a17.066667 17.066667 0 0 0-26.5216 14.199467V938.666667a17.066667 17.066667 0 0 0 17.066667 17.066666h204.8z m-307.2 0a17.066667 17.066667 0 0 0 17.066667-17.066666v-443.733334a17.066667 17.066667 0 0 0-28.842667-12.356266l-221.866667 211.285333a17.066667 17.066667 0 0 0-5.290666 12.3904V887.466667a68.266667 68.266667 0 0 0 68.266666 68.266666h170.666667z" fill="#FFAA44" p-id="4994"></path><path d="M73.557333 693.8624a17.066667 17.066667 0 0 0-5.290666 12.3904V887.466667a68.266667 68.266667 0 0 0 68.266666 68.266666h170.666667a17.066667 17.066667 0 0 0 17.066667-17.066666v-443.733334a17.066667 17.066667 0 0 0-28.842667-12.356266l-221.866667 211.285333zM392.533333 938.666667a17.066667 17.066667 0 0 0 17.066667 17.066666h204.8a17.066667 17.066667 0 0 0 17.066667-17.066666v-330.990934a17.066667 17.066667 0 0 0-7.611734-14.199466l-204.8-136.533334a17.066667 17.066667 0 0 0-26.5216 14.199467V938.666667z m307.2 0a17.066667 17.066667 0 0 0 17.066667 17.066666h170.666667a68.266667 68.266667 0 0 0 68.266666-68.266666V323.6864a17.066667 17.066667 0 0 0-30.344533-10.752l-221.866667 274.705067a17.066667 17.066667 0 0 0-3.7888 10.717866v340.309334z" fill="#11AA66" p-id="4995"></path></svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1692418843591" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4084" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M511.5 82c-236.6 0-429 192.4-429 429 0 236.5 192.5 429 429 429 236.6 0 429-192.4 429-429 0-236.5-192.4-429-429-429z m377.6 403.8H734.3c-4-139.9-41.4-259.9-97.5-331.9C776.5 203 879 332 889.1 485.8z m-402.8-349v349h-147c5.5-175.5 68.6-322.6 147-349z m0 399.4v349c-78.4-26.4-141.4-173.5-147-349h147z m50.5 349v-349h147c-5.6 175.5-68.6 322.6-147 349z m0-399.4v-349c78.4 26.4 141.4 173.5 147 349h-147zM386.3 153.9c-56.1 72-93.5 192-97.5 331.9H133.9C144.1 332 246.5 203 386.3 153.9zM133.9 536.2h154.8c4 139.9 41.4 259.9 97.5 331.9C246.5 819 144.1 690 133.9 536.2z m502.8 331.9c56.1-72 93.5-192 97.5-331.9H889C879 690 776.5 819 636.7 868.1z" fill="#5F9BEB" p-id="4085"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1006 B |
@@ -1,89 +0,0 @@
|
||||
async function embedChatbot() {
|
||||
const chatBtnId = 'fastgpt-chatbot-button';
|
||||
const chatWindowId = 'fastgpt-chatbot-window';
|
||||
const script = document.getElementById('fastgpt-iframe');
|
||||
const botSrc = script?.getAttribute('data-src');
|
||||
const primaryColor = script?.getAttribute('data-color') || '#4e83fd';
|
||||
|
||||
if (!botSrc) {
|
||||
console.error(`Can't find appid`);
|
||||
return;
|
||||
}
|
||||
if (document.getElementById(chatBtnId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const MessageIcon = `<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1690532785664" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4132" xmlns:xlink="http://www.w3.org/1999/xlink" ><path d="M512 32C247.04 32 32 224 32 464A410.24 410.24 0 0 0 172.48 768L160 965.12a25.28 25.28 0 0 0 39.04 22.4l168-112A528.64 528.64 0 0 0 512 896c264.96 0 480-192 480-432S776.96 32 512 32z m244.8 416l-361.6 301.76a12.48 12.48 0 0 1-19.84-12.48l59.2-233.92h-160a12.48 12.48 0 0 1-7.36-23.36l361.6-301.76a12.48 12.48 0 0 1 19.84 12.48l-59.2 233.92h160a12.48 12.48 0 0 1 8 22.08z" fill=${primaryColor} p-id="4133"></path></svg>`;
|
||||
|
||||
const CloseIcon = `<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1690535441526" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6367" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M512 1024A512 512 0 1 1 512 0a512 512 0 0 1 0 1024zM305.956571 370.395429L447.488 512 305.956571 653.604571a45.568 45.568 0 1 0 64.438858 64.438858L512 576.512l141.604571 141.531429a45.568 45.568 0 0 0 64.438858-64.438858L576.512 512l141.531429-141.604571a45.568 45.568 0 1 0-64.438858-64.438858L512 447.488 370.395429 305.956571a45.568 45.568 0 0 0-64.438858 64.438858z" fill=${primaryColor} p-id="6368"></path></svg>`;
|
||||
|
||||
const ChatBtn = document.createElement('div');
|
||||
ChatBtn.id = chatBtnId;
|
||||
ChatBtn.style.cssText =
|
||||
'position: fixed; bottom: 1rem; right: 1rem; width: 40px; height: 40px; cursor: pointer; z-index: 2147483647; transition: 0;';
|
||||
|
||||
const ChatBtnDiv = document.createElement('div');
|
||||
ChatBtnDiv.innerHTML = MessageIcon;
|
||||
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.allow = 'fullscreen;microphone';
|
||||
iframe.title = 'FastGPT Chat Window';
|
||||
iframe.id = chatWindowId;
|
||||
iframe.src = botSrc;
|
||||
|
||||
iframe.style.cssText =
|
||||
'visibility: hidden; border: none; position: fixed; flex-direction: column; justify-content: space-between; box-shadow: rgba(150, 150, 150, 0.2) 0px 10px 30px 0px, rgba(150, 150, 150, 0.2) 0px 0px 0px 1px; bottom: 4rem; right: 1rem; width: 24rem; height: 40rem; max-width: 90vw; max-height: 85vh; border-radius: 0.75rem; display: flex; z-index: 2147483647; overflow: hidden; left: unset; background-color: #F3F4F6;';
|
||||
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
let chatBtnDragged = false;
|
||||
let chatBtnDown = false;
|
||||
let chatBtnMouseX;
|
||||
let chatBtnMouseY;
|
||||
ChatBtn.addEventListener('click', function () {
|
||||
if (chatBtnDragged) {
|
||||
chatBtnDragged = false;
|
||||
return;
|
||||
}
|
||||
const chatWindow = document.getElementById(chatWindowId);
|
||||
|
||||
if (!chatWindow) return;
|
||||
const visibilityVal = chatWindow.style.visibility;
|
||||
if (visibilityVal === 'hidden') {
|
||||
chatWindow.style.visibility = 'unset';
|
||||
ChatBtnDiv.innerHTML = CloseIcon;
|
||||
} else {
|
||||
chatWindow.style.visibility = 'hidden';
|
||||
ChatBtnDiv.innerHTML = MessageIcon;
|
||||
}
|
||||
});
|
||||
|
||||
ChatBtn.addEventListener('mousedown', (e) => {
|
||||
if (!chatBtnMouseX && !chatBtnMouseY) {
|
||||
chatBtnMouseX = e.clientX;
|
||||
chatBtnMouseY = e.clientY;
|
||||
}
|
||||
|
||||
chatBtnDown = true;
|
||||
});
|
||||
ChatBtn.addEventListener('mousemove', (e) => {
|
||||
if (!chatBtnDown) return;
|
||||
chatBtnDragged = true;
|
||||
const transformX = e.clientX - chatBtnMouseX;
|
||||
const transformY = e.clientY - chatBtnMouseY;
|
||||
|
||||
ChatBtn.style.transform = `translate3d(${transformX}px, ${transformY}px, 0)`;
|
||||
|
||||
e.stopPropagation();
|
||||
});
|
||||
ChatBtn.addEventListener('mouseup', (e) => {
|
||||
chatBtnDown = false;
|
||||
});
|
||||
ChatBtn.addEventListener('mouseleave', (e) => {
|
||||
chatBtnDown = false;
|
||||
});
|
||||
|
||||
ChatBtn.appendChild(ChatBtnDiv);
|
||||
document.body.appendChild(ChatBtn);
|
||||
}
|
||||
document.body.onload = embedChatbot;
|
||||
15792
client/public/js/pdf.js
64521
client/public/js/pdf.worker.js
vendored
@@ -1,262 +0,0 @@
|
||||
{
|
||||
"App": "App",
|
||||
"Cancel": "No",
|
||||
"Confirm": "Yes",
|
||||
"Create New": "Create",
|
||||
"Dataset": "Dataset",
|
||||
"Export": "Export",
|
||||
"Folder": "Folder",
|
||||
"Move": "Move",
|
||||
"Name": "Name",
|
||||
"Rename": "Rename",
|
||||
"Running": "Running",
|
||||
"Select value is empty": "Select value is empty",
|
||||
"UnKnow": "UnKnow",
|
||||
"Warning": "Warning",
|
||||
"app": {
|
||||
"Advance App TestTip": "The current application is advanced editing mode \n. If you need to switch to [simple mode], please click the save button on the left",
|
||||
"App Detail": "App Detail",
|
||||
"Chat Logs Tips": "Logs record the app's online, shared, and API(chatId is existing) conversations",
|
||||
"Chat logs": "Chat Logs",
|
||||
"Confirm Del App Tip": "Confirm to delete the app and all its chats",
|
||||
"Confirm Save App Tip": "The application may be in advanced orchestration mode, and the advanced orchestration configuration will be overwritten after saving, please confirm!",
|
||||
"Connection is invalid": "Connecting is invalid",
|
||||
"Connection type is different": "Connection type is different",
|
||||
"Copy Module Config": "Copy config",
|
||||
"Export Config Successful": "The configuration has been copied. Please check for important data",
|
||||
"Export Configs": "Export Configs",
|
||||
"Feedback Count": "User Feedback",
|
||||
"Import Config": "Import Config",
|
||||
"Import Config Failed": "Failed to import the configuration, please ensure that the configuration is normal!",
|
||||
"Import Configs": "Import Configs",
|
||||
"Input Field Settings": "Input Field Settings",
|
||||
"Logs Empty": "Logs is empty",
|
||||
"Logs Message Total": "Message Count",
|
||||
"Logs Source": "Source",
|
||||
"Logs Time": "Time",
|
||||
"Logs Title": "Title",
|
||||
"Mark Count": "Mark Count",
|
||||
"My Apps": "My Apps",
|
||||
"Output Field Settings": "Output Field Settings",
|
||||
"Paste Config": "Paste Config",
|
||||
"Variable Key Repeat Tip": "Variable Key Repeat"
|
||||
},
|
||||
"chat": {
|
||||
"Admin Mark Content": "Corrected response",
|
||||
"Complete Response": "Complete Response",
|
||||
"Confirm to clear history": "Confirm to clear history?",
|
||||
"Confirm to clear share chat histroy": " Are you sure to delete all chats?",
|
||||
"Exit Chat": "Exit",
|
||||
"Feedback Close": "Close Feedback",
|
||||
"Feedback Failed": "Feedback Failed",
|
||||
"Feedback Mark": "Mark",
|
||||
"Feedback Modal": "Feedback",
|
||||
"Feedback Modal Tip": "Enter what you find unsatisfactory",
|
||||
"Feedback Submit": "Submit",
|
||||
"Feedback Success": "Feedback Success",
|
||||
"Feedback Update Failed": "Feedback Update Failed",
|
||||
"History": "History",
|
||||
"Mark": "Mark",
|
||||
"Mark Description": "The annotation feature is currently in beta. \n\n After clicking Add annotation, you need to select a knowledge base in order to store annotation data. You can use this feature to quickly annotate questions and expected answers to guide the model to the next answer. At present, the annotation function, like other data in the knowledge base, is affected by the model, which does not mean that the annotation meets 100% expectations. The \n\n annotation data is only unidirectional synchronization with the knowledge base. If the knowledge base modifies the annotation data, the annotation data displayed in the log cannot be synchronized",
|
||||
"Mark Description Title": "Mark Description",
|
||||
"New Chat": "New Chat",
|
||||
"Read Mark Description": "Read mark description",
|
||||
"Read User Feedback": "Read user feedback",
|
||||
"Select Mark Kb": "Select Dataset",
|
||||
"Select Mark Kb Desc": "Select a dataset to store the expected answers",
|
||||
"You need to a chat app": "You need to a chat app",
|
||||
"logs": {
|
||||
"api": "API",
|
||||
"online": "Online Chat",
|
||||
"share": "Share",
|
||||
"test": "Test Chat "
|
||||
},
|
||||
"retry": "Retry"
|
||||
},
|
||||
"common": {
|
||||
"Add": "Add",
|
||||
"Cancel": "Cancel",
|
||||
"Collect": "Collect",
|
||||
"Copy": "Copy",
|
||||
"Copy Successful": "Copy Successful",
|
||||
"Course": "",
|
||||
"Delete": "Delete",
|
||||
"Delete Failed": "Delete Failed",
|
||||
"Delete Success": "Delete Successful",
|
||||
"Delete Warning": "Warning",
|
||||
"Edit": "Edit",
|
||||
"Expired Time": "Expired",
|
||||
"Filed is repeat": "Filed is repeated",
|
||||
"Filed is repeated": "",
|
||||
"Input": "Input",
|
||||
"Name is empty": "Name is empty",
|
||||
"Output": "Output",
|
||||
"Password inconsistency": "Password inconsistency",
|
||||
"Rename": "Rename",
|
||||
"Search": "Search",
|
||||
"Status": "Status",
|
||||
"Update Successful": "Update Successful",
|
||||
"export": ""
|
||||
},
|
||||
"dataset": {
|
||||
"Confirm to delete the data": "Confirm to delete the data?",
|
||||
"Export": "Export",
|
||||
"Queue Desc": "This data refers to the current amount of training for the entire system. FastGPT uses queued training, and if you have too much data to train, you may need to wait for a while",
|
||||
"System Data Queue": "Data Queue"
|
||||
},
|
||||
"file": {
|
||||
"Click to download CSV template": "Click to download CSV template",
|
||||
"Click to view file": "Click to view file",
|
||||
"Create File": "Create File",
|
||||
"Create file": "Create file",
|
||||
"Drag and drop": "Drag and drop files here",
|
||||
"Embedding": "Embedding",
|
||||
"Fetch Url": "Fetch Url",
|
||||
"If the imported file is garbled, please convert CSV to UTF-8 encoding format": "If the imported file is garbled, please convert CSV to UTF-8 encoding format",
|
||||
"Parse": "{{name}} Parsing...",
|
||||
"Ready": "Ready",
|
||||
"Release the mouse to upload the file": "Release the mouse to upload the file",
|
||||
"Select a maximum of 10 files": "Select a maximum of 10 files",
|
||||
"Uploading": "Uploading: {{name}}, Progress: {{percent}}%",
|
||||
"max 10": "Max 10 files",
|
||||
"select a document": "select a document",
|
||||
"support": "support {{fileExtension}} file",
|
||||
"upload error description": "Only upload multiple files or one folder at a time"
|
||||
},
|
||||
"home": {
|
||||
"AI Assistant": "AI Assistant",
|
||||
"AI Assistant Desc": "",
|
||||
"Advanced Settings": "",
|
||||
"Advanced Settings Desc": "",
|
||||
"Choice Debug": "Convenient Debugging",
|
||||
"Choice Debug Desc": "Search testing, reference modification, full conversation preview and many other debugging ways",
|
||||
"Choice Desc": "FastGPT follows the Apache License 2.0 open source protocol",
|
||||
"Choice Extension": "Infinite Extension",
|
||||
"Choice Extension Desc": "HTTP based extension, easy to achieve custom functions",
|
||||
"Choice Fast": "Fast",
|
||||
"Choice Fast Desc": "{{title}} provides out-of-the-box visual actions to build AI applications point-by-point",
|
||||
"Choice Models": "Multiple Models",
|
||||
"Choice Models Desc": "Supports multiple models such as GPT, Claude, Spark, and ChatGLM",
|
||||
"Choice Open": "Open",
|
||||
"Choice Open Desc": "{{title}} follows the Apache License 2.0 open source protocol",
|
||||
"Choice QA": "QA Struceture",
|
||||
"Choice QA Desc": "The index is constructed with the structure of QA pairs, and ADAPTS to various scenarios such as Q&A and reading",
|
||||
"Choice Visual": "Visual workflow",
|
||||
"Choice Visual Desc": "Visualize modular operations, easily implement complex workflows, and make your AI no longer monolithic",
|
||||
"Community": "Community",
|
||||
"Dateset": "",
|
||||
"Dateset Desc": "",
|
||||
"Docs": "Docs",
|
||||
"FastGPT Ability": "{{title}} Ability",
|
||||
"FastGPT Desc": "{{title}} is a knowledgebase question answering system based on LLM large language model, which provides out-of-the-box data processing, model invocation and other capabilities. At the same time, workflow orchestration can be performed through Flow visualization to achieve complex Q&A scenarios!",
|
||||
"Features": "Features",
|
||||
"Footer Developer": "Developer",
|
||||
"Footer Docs": "Docs",
|
||||
"Footer FastGPT Cloud": "{{title}} Cloud",
|
||||
"Footer Feedback": "Feedback",
|
||||
"Footer Git": "Code",
|
||||
"Footer Product": "Product",
|
||||
"Footer Support": "Support",
|
||||
"Login": "Login",
|
||||
"Open": "",
|
||||
"OpenAPI": "OpenAPI",
|
||||
"OpenAPI Desc": "",
|
||||
"Quickly build AI question and answer library": "Quickly build AI question and answer library",
|
||||
"Start Now": "Start Now",
|
||||
"Visual AI orchestration": "Visual AI orchestration",
|
||||
"Why FastGPT": "Why {{title}}",
|
||||
"desc": "AI knowledge base question and answer platform based on LLM large model",
|
||||
"slogan": "Let the AI know more about you"
|
||||
},
|
||||
"kb": {
|
||||
"Chunk Length": "Chunk Length",
|
||||
"Confirm move the folder": "Confirm Move",
|
||||
"Confirm to delete the file": "Are you sure to delete the file and all its data?",
|
||||
"Create Folder": "Create Folder",
|
||||
"Delete Dataset Error": "Delete dataset failed",
|
||||
"Edit Folder": "Edit Folder",
|
||||
"File Size": "File Size",
|
||||
"Filename": "Filename",
|
||||
"Files": "{{total}} Files",
|
||||
"Folder Name": "Input folder name",
|
||||
"Move Failed": "Move Failed",
|
||||
"My Dataset": "My Dataset",
|
||||
"No Folder": "No Folder",
|
||||
"Other Data": "Other Data",
|
||||
"Select Dataset": "Select Dataset",
|
||||
"Select Folder": "Enter folder",
|
||||
"Upload Time": "Upload Time",
|
||||
"deleteDatasetTips": "Are you sure to delete the knowledge base? Data cannot be recovered after deletion, please confirm!",
|
||||
"deleteFolderTips": "Are you sure to delete this folder and all the knowledge bases it contains? Data cannot be recovered after deletion, please confirm!"
|
||||
},
|
||||
"navbar": {
|
||||
"Account": "Account",
|
||||
"Apps": "Apps",
|
||||
"Chat": "Chat",
|
||||
"Datasets": "DataSets",
|
||||
"Store": "Store",
|
||||
"Tools": "Tools"
|
||||
},
|
||||
"outlink": {
|
||||
"Copy Iframe": "Copy Iframe",
|
||||
"Copy Link": "Copy",
|
||||
"Create Ifrme Window": "Create Iframe Link",
|
||||
"Create Share Window": "Create Share Window",
|
||||
"Delete Link": "Delete",
|
||||
"Edit Ifrme Link": "Edit Iframe Link",
|
||||
"Edit Link": "Edit",
|
||||
"Edit Share Window": "Edit Share Window",
|
||||
"Link Name": "Link Name",
|
||||
"Link is empty": "",
|
||||
"Max credit": "Credit",
|
||||
"Max credit tips": "What is the maximum amount of money that can be consumed by the link? If the link is exceeded, it will be banned. -1 indicates no limit.",
|
||||
"QPM": "QPM",
|
||||
"QPM Tips": "The maximum number of queries per IP address per minute",
|
||||
"QPM is empty": "QPM is empty",
|
||||
"Response Detail": "Detail",
|
||||
"Response Detail tips": "Whether detailed data such as references and full context need to be returned"
|
||||
},
|
||||
"system": {
|
||||
"Help Document": "Document"
|
||||
},
|
||||
"user": {
|
||||
"Account": "Account",
|
||||
"Amount of earnings": "Earnings",
|
||||
"Amount of inviter": "Inviter",
|
||||
"Application Name": "Application Name",
|
||||
"Avatar": "Avatar",
|
||||
"Balance": "Balance",
|
||||
"Bill Detail": "Bill Detail",
|
||||
"Change": "Change",
|
||||
"Copy invite url": "Copy invitation link",
|
||||
"Invite Url": "Invite Url",
|
||||
"Invite url tip": "Friends who register through this link will be permanently bound to you, and you will get a certain balance reward when they recharge. In addition, when friends register with their mobile phone number, you will get 5 yuan reward immediately.",
|
||||
"Language": "Language",
|
||||
"Notice": "Notice",
|
||||
"Old password is error": "Old password is error",
|
||||
"OpenAI Account Setting": "OpenAI Account Setting",
|
||||
"Password": "Password",
|
||||
"Pay": "Pay",
|
||||
"Personal Information": "Personal",
|
||||
"Promotion": "Promotion",
|
||||
"Promotion Rate": "Promotion Rate",
|
||||
"Promotion Record": "Promotion",
|
||||
"Promotion rate tip": "You will be rewarded with a percentage of the balance when your friends top up",
|
||||
"Recharge Record": "Recharge",
|
||||
"Replace": "Replace",
|
||||
"Set OpenAI Account Failed": "Set OpenAI account failed",
|
||||
"Sign Out": "Sign Out",
|
||||
"Source": "Source",
|
||||
"Time": "Time",
|
||||
"Timezone": "Timezone",
|
||||
"Total Amount": "Total Amount",
|
||||
"Update Password": "Update Password",
|
||||
"Update password failed": "Update password failed",
|
||||
"Update password succseful": "Update password succseful",
|
||||
"Usage Record": "Usage",
|
||||
"promotion": {
|
||||
"pay": "",
|
||||
"register": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,262 +0,0 @@
|
||||
{
|
||||
"App": "应用",
|
||||
"Cancel": "取消",
|
||||
"Confirm": "确认",
|
||||
"Create New": "新建",
|
||||
"Dataset": "知识库",
|
||||
"Export": "导出",
|
||||
"Folder": "文件夹",
|
||||
"Move": "移动",
|
||||
"Name": "名称",
|
||||
"Rename": "重命名",
|
||||
"Running": "运行中",
|
||||
"Select value is empty": "选择的内容为空",
|
||||
"UnKnow": "未知",
|
||||
"Warning": "提示",
|
||||
"app": {
|
||||
"Advance App TestTip": "当前应用为高级编排模式\n如需切换为【简易模式】请点击左侧保存按键",
|
||||
"App Detail": "应用详情",
|
||||
"Chat Logs Tips": "日志会记录该应用的在线、分享和 API(需填写 chatId) 对话记录",
|
||||
"Chat logs": "对话日志",
|
||||
"Confirm Del App Tip": "确认删除该应用及其所有聊天记录?",
|
||||
"Confirm Save App Tip": "该应用可能为高级编排模式,保存后将会覆盖高级编排配置,请确认!",
|
||||
"Connection is invalid": "连接无效",
|
||||
"Connection type is different": "连接的类型不一致",
|
||||
"Copy Module Config": "复制配置",
|
||||
"Export Config Successful": "已复制配置,请注意检查是否有重要数据",
|
||||
"Export Configs": "导出配置",
|
||||
"Feedback Count": "用户反馈",
|
||||
"Import Config": "导入配置",
|
||||
"Import Config Failed": "导入配置失败,请确保配置正常!",
|
||||
"Import Configs": "导入配置",
|
||||
"Input Field Settings": "输入字段编辑",
|
||||
"Logs Empty": "还没有日志噢~",
|
||||
"Logs Message Total": "消息总数",
|
||||
"Logs Source": "来源",
|
||||
"Logs Time": "时间",
|
||||
"Logs Title": "标题",
|
||||
"Mark Count": "标注答案数量",
|
||||
"My Apps": "我的应用",
|
||||
"Output Field Settings": "输出字段编辑",
|
||||
"Paste Config": "粘贴配置",
|
||||
"Variable Key Repeat Tip": "变量 key 重复"
|
||||
},
|
||||
"chat": {
|
||||
"Admin Mark Content": "纠正后的回复",
|
||||
"Complete Response": "完整响应",
|
||||
"Confirm to clear history": "确认清空该应用的在线聊天记录?分享和 API 调用的记录不会被清空。",
|
||||
"Confirm to clear share chat histroy": "确认删除所有聊天记录?",
|
||||
"Exit Chat": "退出聊天",
|
||||
"Feedback Close": "关闭反馈",
|
||||
"Feedback Failed": "提交反馈异常",
|
||||
"Feedback Mark": "标注",
|
||||
"Feedback Modal": "结果反馈",
|
||||
"Feedback Modal Tip": "输入你觉得回答不满意的地方",
|
||||
"Feedback Submit": "提交反馈",
|
||||
"Feedback Success": "反馈成功!",
|
||||
"Feedback Update Failed": "更新反馈状态失败",
|
||||
"History": "记录",
|
||||
"Mark": "标注预期回答",
|
||||
"Mark Description": "当前标注功能为测试版。\n\n点击添加标注后,需要选择一个知识库,以便存储标注数据。你可以通过该功能快速的标注问题和预期回答,以便引导模型下次的回答。\n\n目前,标注功能同知识库其他数据一样,受模型的影响,不代表标注后 100% 符合预期。\n\n标注数据仅单向与知识库同步,如果知识库修改了该标注数据,日志展示的标注数据无法同步",
|
||||
"Mark Description Title": "标注功能介绍",
|
||||
"New Chat": "新对话",
|
||||
"Read Mark Description": "查看标注功能介绍",
|
||||
"Read User Feedback": "查看用户反馈",
|
||||
"Select Mark Kb": "选择知识库",
|
||||
"Select Mark Kb Desc": "选择一个知识库存储预期答案",
|
||||
"You need to a chat app": "你需要创建一个应用",
|
||||
"logs": {
|
||||
"api": "API 调用",
|
||||
"online": "在线使用",
|
||||
"share": "外部链接调用",
|
||||
"test": "测试"
|
||||
},
|
||||
"retry": "重新生成"
|
||||
},
|
||||
"common": {
|
||||
"Add": "添加",
|
||||
"Cancel": "取消",
|
||||
"Collect": "收藏",
|
||||
"Copy": "复制",
|
||||
"Copy Successful": "复制成功",
|
||||
"Course": "",
|
||||
"Delete": "删除",
|
||||
"Delete Failed": "删除失败",
|
||||
"Delete Success": "删除成功",
|
||||
"Delete Warning": "删除警告",
|
||||
"Edit": "编辑",
|
||||
"Expired Time": "过期时间",
|
||||
"Filed is repeat": "",
|
||||
"Filed is repeated": "字段重复了",
|
||||
"Input": "输入",
|
||||
"Name is empty": "名称不能为空",
|
||||
"Output": "输出",
|
||||
"Password inconsistency": "两次密码不一致",
|
||||
"Rename": "重命名",
|
||||
"Search": "搜索",
|
||||
"Status": "状态",
|
||||
"Update Successful": "更新成功",
|
||||
"export": ""
|
||||
},
|
||||
"dataset": {
|
||||
"Confirm to delete the data": "确认删除该数据?",
|
||||
"Export": "导出",
|
||||
"Queue Desc": "该数据是指整个系统当前待训练的数量。{{title}} 采用排队训练的方式,如果待训练的数据过多,可能需要等待一段时间",
|
||||
"System Data Queue": "排队长度"
|
||||
},
|
||||
"file": {
|
||||
"Click to download CSV template": "点击下载 CSV 模板",
|
||||
"Click to view file": "点击查看原始文件",
|
||||
"Create File": "创建新文件",
|
||||
"Create file": "创建文件",
|
||||
"Drag and drop": "拖拽文件至此",
|
||||
"Embedding": "索引中",
|
||||
"Fetch Url": "链接读取",
|
||||
"If the imported file is garbled, please convert CSV to UTF-8 encoding format": "如果导入文件乱码,请将 CSV 转成 UTF-8 编码格式",
|
||||
"Parse": "{{name}} 解析中...",
|
||||
"Ready": "可用",
|
||||
"Release the mouse to upload the file": "松开鼠标上传文件",
|
||||
"Select a maximum of 10 files": "最多选择10个文件",
|
||||
"Uploading": "正在上传 {{name}},进度: {{percent}}%",
|
||||
"max 10": "最多选择 10 个文件",
|
||||
"select a document": "选择文件",
|
||||
"support": "支持 {{fileExtension}} 文件",
|
||||
"upload error description": "单次只支持上传多个文件或者一个文件夹"
|
||||
},
|
||||
"home": {
|
||||
"AI Assistant": "AI 客服",
|
||||
"AI Assistant Desc": "无论对内还是对外,AI 将 24 小时为您的用户提供服务",
|
||||
"Advanced Settings": "高级编排",
|
||||
"Advanced Settings Desc": "基于 Flow 的流程编排模式,让你的 AI 轻松实现数据库查询、IO 操作、联网通信等扩展能力",
|
||||
"Choice Debug": "调试便捷",
|
||||
"Choice Debug Desc": "拥有搜索测试、引用修改、完整对话预览等多种调试途径",
|
||||
"Choice Desc": "",
|
||||
"Choice Extension": "无限扩展",
|
||||
"Choice Extension Desc": "基于 HTTP 实现扩展,轻松实现定制功能",
|
||||
"Choice Fast": "开箱即用",
|
||||
"Choice Fast Desc": "{{title}} 提供开箱即用的可视化操作,点点点即可构建 AI 应用",
|
||||
"Choice Models": "支持多种模型",
|
||||
"Choice Models Desc": "支持 GPT、Claude、Spark、ChatGLM等多模型",
|
||||
"Choice Open": "更开放",
|
||||
"Choice Open Desc": "{{title}} 遵循 Apache License 2.0 开源协议",
|
||||
"Choice QA": "独特的 QA 结构",
|
||||
"Choice QA Desc": "采用 QA 对的结构构建索引,适应问答、阅读等多种场景",
|
||||
"Choice Visual": "可视化工作流",
|
||||
"Choice Visual Desc": "可视化模块操作,轻松实现复杂工作流,让你的 AI 不再单一",
|
||||
"Community": "社区",
|
||||
"Dateset": "自动数据预处理",
|
||||
"Dateset Desc": "提供手动输入、直接分段、LLM 自动处理和 CSV 等多种数据导入途径",
|
||||
"Docs": "文档",
|
||||
"FastGPT Ability": "{{title}} 能力",
|
||||
"FastGPT Desc": "{{title}} 是一个基于 LLM 大语言模型的知识库问答系统,提供开箱即用的数据处理、模型调用等能力。同时可以通过 Flow 可视化进行工作流编排,从而实现复杂的问答场景!",
|
||||
"Features": "特点",
|
||||
"Footer Developer": "开发者",
|
||||
"Footer Docs": "文档",
|
||||
"Footer FastGPT Cloud": "{{title}} 线上服务",
|
||||
"Footer Feedback": "反馈",
|
||||
"Footer Git": "源码",
|
||||
"Footer Product": "产品",
|
||||
"Footer Support": "支持",
|
||||
"Login": "登录",
|
||||
"Open": "",
|
||||
"OpenAPI": "OpenAPI",
|
||||
"OpenAPI Desc": "与 GPT API 一致的对外接口,助你轻松接入已有应用",
|
||||
"Quickly build AI question and answer library": "快速搭建 AI 问答系统",
|
||||
"Start Now": "立即开始",
|
||||
"Visual AI orchestration": "可视化 AI 编排",
|
||||
"Why FastGPT": "为什么选择 {{title}}",
|
||||
"desc": "基于 LLM 大模型的 AI 知识库问答平台",
|
||||
"slogan": "让 AI 更懂你的知识"
|
||||
},
|
||||
"kb": {
|
||||
"Chunk Length": "数据总量",
|
||||
"Confirm move the folder": "确认移动到该目录",
|
||||
"Confirm to delete the file": "确认删除该文件及其所有数据?",
|
||||
"Create Folder": "创建文件夹",
|
||||
"Delete Dataset Error": "删除知识库异常",
|
||||
"Edit Folder": "编辑文件夹",
|
||||
"File Size": "文件大小",
|
||||
"Filename": "文件名",
|
||||
"Files": "文件: {{total}}个",
|
||||
"Folder Name": "输入文件夹名称",
|
||||
"Move Failed": "移动出现错误~",
|
||||
"My Dataset": "我的知识库",
|
||||
"No Folder": "没有子目录了~",
|
||||
"Other Data": "其他数据",
|
||||
"Select Dataset": "选择该知识库",
|
||||
"Select Folder": "进入文件夹",
|
||||
"Upload Time": "上传时间",
|
||||
"deleteDatasetTips": "确认删除该知识库?删除后数据无法恢复,请确认!",
|
||||
"deleteFolderTips": "确认删除该文件夹及其包含的所有知识库?删除后数据无法恢复,请确认!"
|
||||
},
|
||||
"navbar": {
|
||||
"Account": "账号",
|
||||
"Apps": "应用",
|
||||
"Chat": "聊天",
|
||||
"Datasets": "知识库",
|
||||
"Store": "应用市场",
|
||||
"Tools": "工具"
|
||||
},
|
||||
"outlink": {
|
||||
"Copy Iframe": "复制嵌入",
|
||||
"Copy Link": "复制",
|
||||
"Create Ifrme Window": "创建嵌入链接",
|
||||
"Create Share Window": "创建免登录窗口",
|
||||
"Delete Link": "删除链接",
|
||||
"Edit Ifrme Link": "更新嵌入链接",
|
||||
"Edit Link": "编辑",
|
||||
"Edit Share Window": "更新分享窗口",
|
||||
"Link Name": "分享链接的名字",
|
||||
"Link is empty": "",
|
||||
"Max credit": "最大金额",
|
||||
"Max credit tips": "该链接最大可消耗多少金额,超出后链接将被禁止使用。-1 代表无限制。",
|
||||
"QPM": "",
|
||||
"QPM Tips": "每个 IP 每分钟最多提问多少次",
|
||||
"QPM is empty": "QPM 不能为空",
|
||||
"Response Detail": "返回详情",
|
||||
"Response Detail tips": "是否需要返回引用、完整上下文等详细数据"
|
||||
},
|
||||
"system": {
|
||||
"Help Document": "帮助文档"
|
||||
},
|
||||
"user": {
|
||||
"Account": "账号",
|
||||
"Amount of earnings": "收益(¥)",
|
||||
"Amount of inviter": "累计邀请人数",
|
||||
"Application Name": "应用名",
|
||||
"Avatar": "头像",
|
||||
"Balance": "余额",
|
||||
"Bill Detail": "账单详情",
|
||||
"Change": "变更",
|
||||
"Copy invite url": "复制邀请链接",
|
||||
"Invite Url": "邀请链接",
|
||||
"Invite url tip": "通过该链接注册的好友将永久与你绑定,其充值时你会获得一定余额奖励。\n此外,好友使用手机号注册时,你将立即获得 5 元奖励。",
|
||||
"Language": "语言",
|
||||
"Notice": "通知",
|
||||
"Old password is error": "旧密码错误",
|
||||
"OpenAI Account Setting": "OpenAI 账号配置",
|
||||
"Password": "密码",
|
||||
"Pay": "充值",
|
||||
"Personal Information": "个人信息",
|
||||
"Promotion": "",
|
||||
"Promotion Rate": "返现比例",
|
||||
"Promotion Record": "推广记录",
|
||||
"Promotion rate tip": "好友充值时你将获得一定比例的余额奖励",
|
||||
"Recharge Record": "充值记录",
|
||||
"Replace": "更换",
|
||||
"Set OpenAI Account Failed": "设置 OpenAI 账号异常",
|
||||
"Sign Out": "登出",
|
||||
"Source": "来源",
|
||||
"Time": "时间",
|
||||
"Timezone": "时区",
|
||||
"Total Amount": "总金额",
|
||||
"Update Password": "修改密码",
|
||||
"Update password failed": "修改密码异常",
|
||||
"Update password succseful": "修改密码成功",
|
||||
"Usage Record": "使用记录",
|
||||
"promotion": {
|
||||
"pay": "好友充值",
|
||||
"register": "好友注册"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import { GET, POST, DELETE, PUT } from './request';
|
||||
import type { AppSchema } from '@/types/mongoSchema';
|
||||
import type { AppListItemType, AppUpdateParams } from '@/types/app';
|
||||
import { RequestPaging } from '../types/index';
|
||||
import type { Props as CreateAppProps } from '@/pages/api/app/create';
|
||||
import { addDays } from 'date-fns';
|
||||
import { GetAppChatLogsParams } from './request/app';
|
||||
|
||||
/**
|
||||
* 获取模型列表
|
||||
*/
|
||||
export const getMyModels = () => GET<AppListItemType[]>('/app/myApps');
|
||||
|
||||
/**
|
||||
* 创建一个模型
|
||||
*/
|
||||
export const postCreateApp = (data: CreateAppProps) => POST<string>('/app/create', data);
|
||||
|
||||
/**
|
||||
* 根据 ID 删除模型
|
||||
*/
|
||||
export const delModelById = (id: string) => DELETE(`/app/del?appId=${id}`);
|
||||
|
||||
/**
|
||||
* 根据 ID 获取模型
|
||||
*/
|
||||
export const getModelById = (id: string) => GET<AppSchema>(`/app/detail?appId=${id}`);
|
||||
|
||||
/**
|
||||
* 根据 ID 更新模型
|
||||
*/
|
||||
export const putAppById = (id: string, data: AppUpdateParams) =>
|
||||
PUT(`/app/update?appId=${id}`, data);
|
||||
|
||||
/* 共享市场 */
|
||||
/**
|
||||
* 获取共享市场模型
|
||||
*/
|
||||
export const getShareModelList = (data: { searchText?: string } & RequestPaging) =>
|
||||
POST(`/app/share/getModels`, data);
|
||||
|
||||
/**
|
||||
* 收藏/取消收藏模型
|
||||
*/
|
||||
export const triggerModelCollection = (appId: string) =>
|
||||
POST<number>(`/app/share/collection?appId=${appId}`);
|
||||
|
||||
// ====================== data
|
||||
export const getAppTotalUsage = (data: { appId: string }) =>
|
||||
POST<{ date: String; total: number }[]>(`/app/data/totalUsage`, {
|
||||
...data,
|
||||
start: addDays(new Date(), -13),
|
||||
end: addDays(new Date(), 1)
|
||||
}).then((res) => (res.length === 0 ? [{ date: new Date(), total: 0 }] : res));
|
||||
|
||||
export const getAppChatLogs = (data: GetAppChatLogsParams) => POST(`/app/getChatLogs`, data);
|
||||
@@ -1,45 +0,0 @@
|
||||
import { GET, POST, DELETE, PUT } from './request';
|
||||
import type { ChatHistoryItemType } from '@/types/chat';
|
||||
import type { InitChatResponse } from './response/chat';
|
||||
import { RequestPaging } from '../types/index';
|
||||
import type { Props as UpdateHistoryProps } from '@/pages/api/chat/history/updateChatHistory';
|
||||
import { AdminUpdateFeedbackParams } from './request/chat';
|
||||
|
||||
/**
|
||||
* 获取初始化聊天内容
|
||||
*/
|
||||
export const getInitChatSiteInfo = (data: { appId: string; chatId?: string }) =>
|
||||
GET<InitChatResponse>(`/chat/init`, data);
|
||||
|
||||
/**
|
||||
* 获取历史记录
|
||||
*/
|
||||
export const getChatHistory = (data: RequestPaging & { appId?: string }) =>
|
||||
POST<ChatHistoryItemType[]>('/chat/history/getHistory', data);
|
||||
|
||||
/**
|
||||
* 删除一条历史记录
|
||||
*/
|
||||
export const delChatHistoryById = (chatId: string) => DELETE(`/chat/removeHistory`, { chatId });
|
||||
/**
|
||||
* clear all history by appid
|
||||
*/
|
||||
export const clearChatHistoryByAppId = (appId: string) => DELETE(`/chat/removeHistory`, { appId });
|
||||
|
||||
/**
|
||||
* 删除一句对话
|
||||
*/
|
||||
export const delChatRecordById = (data: { chatId: string; contentId: string }) =>
|
||||
DELETE(`/chat/delChatRecordByContentId`, data);
|
||||
|
||||
/**
|
||||
* 修改历史记录: 标题/置顶
|
||||
*/
|
||||
export const putChatHistory = (data: UpdateHistoryProps) =>
|
||||
PUT('/chat/history/updateChatHistory', data);
|
||||
|
||||
export const userUpdateChatFeedback = (data: { chatItemId: string; userFeedback?: string }) =>
|
||||
POST('/chat/feedback/userUpdate', data);
|
||||
|
||||
export const adminUpdateChatFeedback = (data: AdminUpdateFeedbackParams) =>
|
||||
POST('/chat/feedback/adminUpdate', data);
|
||||
8
client/src/api/core/dataset/file.d.ts
vendored
@@ -1,8 +0,0 @@
|
||||
import { RequestPaging } from '../../../types/index';
|
||||
|
||||
export type GetFileListProps = RequestPaging & {
|
||||
kbId: string;
|
||||
searchText: string;
|
||||
};
|
||||
|
||||
export type UpdateFileProps = { id: string; name?: string; datasetUsed?: boolean };
|
||||
@@ -1,15 +0,0 @@
|
||||
import { GET, POST, PUT, DELETE } from '@/api/request';
|
||||
import type { FileInfo, KbFileItemType } from '@/types/plugin';
|
||||
|
||||
import type { GetFileListProps, UpdateFileProps } from './file.d';
|
||||
|
||||
export const getDatasetFiles = (data: GetFileListProps) =>
|
||||
POST<KbFileItemType[]>(`/core/dataset/file/list`, data);
|
||||
export const delDatasetFileById = (params: { fileId: string; kbId: string }) =>
|
||||
DELETE(`/core/dataset/file/delById`, params);
|
||||
export const getFileInfoById = (fileId: string) =>
|
||||
GET<FileInfo>(`/core/dataset/file/detail`, { fileId });
|
||||
export const delDatasetEmptyFiles = (kbId: string) =>
|
||||
DELETE(`/core/dataset/file/delEmptyFiles`, { kbId });
|
||||
|
||||
export const updateDatasetFile = (data: UpdateFileProps) => PUT(`/core/dataset/file/update`, data);
|
||||
@@ -1,16 +0,0 @@
|
||||
import { GET, POST, DELETE } from './request';
|
||||
import { UserOpenApiKey } from '@/types/openapi';
|
||||
/**
|
||||
* crete a api key
|
||||
*/
|
||||
export const createAOpenApiKey = () => POST<string>('/openapi/postKey');
|
||||
|
||||
/**
|
||||
* get api keys
|
||||
*/
|
||||
export const getOpenApiKeys = () => GET<UserOpenApiKey[]>('/openapi/getKeys');
|
||||
|
||||
/**
|
||||
* delete api by id
|
||||
*/
|
||||
export const delOpenApiById = (id: string) => DELETE(`/openapi/delKey?id=${id}`);
|
||||
@@ -1,6 +0,0 @@
|
||||
import { GET, POST, PUT, DELETE } from '../request';
|
||||
|
||||
import type { FetchResultItem } from '@/types/plugin';
|
||||
|
||||
export const fetchUrls = (urlList: string[]) =>
|
||||
POST<FetchResultItem[]>(`/plugins/urlFetch`, { urlList });
|
||||
@@ -1,93 +0,0 @@
|
||||
import { GET, POST, PUT, DELETE } from '../request';
|
||||
import type { DatasetItemType, KbItemType, KbListItemType, KbPathItemType } from '@/types/plugin';
|
||||
import { TrainingModeEnum } from '@/constants/plugin';
|
||||
import {
|
||||
Props as PushDataProps,
|
||||
Response as PushDateResponse
|
||||
} from '@/pages/api/openapi/kb/pushData';
|
||||
import {
|
||||
Props as SearchTestProps,
|
||||
Response as SearchTestResponse
|
||||
} from '@/pages/api/openapi/kb/searchTest';
|
||||
import { Props as UpdateDataProps } from '@/pages/api/openapi/kb/updateData';
|
||||
import type { KbUpdateParams, CreateKbParams, GetKbDataListProps } from '../request/kb';
|
||||
import { QuoteItemType } from '@/types/chat';
|
||||
import { KbTypeEnum } from '@/constants/kb';
|
||||
|
||||
/* knowledge base */
|
||||
export const getKbList = (data: { parentId?: string; type?: `${KbTypeEnum}` }) =>
|
||||
GET<KbListItemType[]>(`/plugins/kb/list`, data);
|
||||
export const getAllDataset = () => GET<KbListItemType[]>(`/plugins/kb/allDataset`);
|
||||
|
||||
export const getKbPaths = (parentId?: string) =>
|
||||
GET<KbPathItemType[]>('/plugins/kb/paths', { parentId });
|
||||
|
||||
export const getKbById = (id: string) => GET<KbItemType>(`/plugins/kb/detail?id=${id}`);
|
||||
|
||||
export const postCreateKb = (data: CreateKbParams) => POST<string>(`/plugins/kb/create`, data);
|
||||
|
||||
export const putKbById = (data: KbUpdateParams) => PUT(`/plugins/kb/update`, data);
|
||||
|
||||
export const delKbById = (id: string) => DELETE(`/plugins/kb/delete?id=${id}`);
|
||||
|
||||
/* kb data */
|
||||
export const getKbDataList = (data: GetKbDataListProps) =>
|
||||
POST(`/plugins/kb/data/getDataList`, data);
|
||||
|
||||
/**
|
||||
* 获取导出数据(不分页)
|
||||
*/
|
||||
export const getExportDataList = (data: { kbId: string }) =>
|
||||
GET<[string, string, string][]>(`/plugins/kb/data/exportModelData`, data, {
|
||||
timeout: 600000
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取模型正在拆分数据的数量
|
||||
*/
|
||||
export const getTrainingData = (data: { kbId: string; init: boolean }) =>
|
||||
POST<{
|
||||
qaListLen: number;
|
||||
vectorListLen: number;
|
||||
}>(`/plugins/kb/data/getTrainingData`, data);
|
||||
|
||||
/* get length of system training queue */
|
||||
export const getTrainingQueueLen = () => GET<number>(`/plugins/kb/data/getQueueLen`);
|
||||
|
||||
export const getKbDataItemById = (dataId: string) =>
|
||||
GET<QuoteItemType>(`/plugins/kb/data/getDataById`, { dataId });
|
||||
|
||||
/**
|
||||
* 直接push数据
|
||||
*/
|
||||
export const postKbDataFromList = (data: PushDataProps) =>
|
||||
POST<PushDateResponse>(`/openapi/kb/pushData`, data);
|
||||
|
||||
/**
|
||||
* insert one data to dataset
|
||||
*/
|
||||
export const insertData2Kb = (data: { kbId: string; data: DatasetItemType }) =>
|
||||
POST<string>(`/plugins/kb/data/insertData`, data);
|
||||
|
||||
/**
|
||||
* 更新一条数据
|
||||
*/
|
||||
export const putKbDataById = (data: UpdateDataProps) => PUT('/openapi/kb/updateData', data);
|
||||
/**
|
||||
* 删除一条知识库数据
|
||||
*/
|
||||
export const delOneKbDataByDataId = (dataId: string) =>
|
||||
DELETE(`/openapi/kb/delDataById?dataId=${dataId}`);
|
||||
|
||||
/**
|
||||
* 拆分数据
|
||||
*/
|
||||
export const postSplitData = (data: {
|
||||
kbId: string;
|
||||
chunks: string[];
|
||||
prompt: string;
|
||||
mode: `${TrainingModeEnum}`;
|
||||
}) => POST(`/openapi/text/pushData`, data);
|
||||
|
||||
export const searchText = (data: SearchTestProps) =>
|
||||
POST<SearchTestResponse>(`/openapi/kb/searchTest`, data);
|
||||
6
client/src/api/request/chat.d.ts
vendored
@@ -1,6 +0,0 @@
|
||||
export type AdminUpdateFeedbackParams = {
|
||||
chatItemId: string;
|
||||
kbId: string;
|
||||
dataId: string;
|
||||
content: string;
|
||||
};
|
||||
24
client/src/api/request/kb.d.ts
vendored
@@ -1,24 +0,0 @@
|
||||
import { KbTypeEnum } from '@/constants/kb';
|
||||
import type { RequestPaging } from '@/types';
|
||||
|
||||
export type KbUpdateParams = {
|
||||
id: string;
|
||||
parentId?: string;
|
||||
tags?: string;
|
||||
name?: string;
|
||||
avatar?: string;
|
||||
};
|
||||
export type CreateKbParams = {
|
||||
parentId?: string;
|
||||
name: string;
|
||||
tags: string[];
|
||||
avatar: string;
|
||||
vectorModel?: string;
|
||||
type: `${KbTypeEnum}`;
|
||||
};
|
||||
|
||||
export type GetKbDataListProps = RequestPaging & {
|
||||
kbId: string;
|
||||
searchText: string;
|
||||
fileId: string;
|
||||
};
|
||||
6
client/src/api/response/app.d.ts
vendored
@@ -1,6 +0,0 @@
|
||||
import { AppListItemType } from '@/types/app';
|
||||
|
||||
export type AppListResponse = {
|
||||
myApps: AppListItemType[];
|
||||
myCollectionApps: AppListItemType[];
|
||||
};
|
||||
25
client/src/api/response/chat.d.ts
vendored
@@ -1,25 +0,0 @@
|
||||
import type { AppSchema } from '@/types/mongoSchema';
|
||||
import type { ChatItemType } from '@/types/chat';
|
||||
import { VariableItemType } from '@/types/app';
|
||||
|
||||
export interface InitChatResponse {
|
||||
chatId: string;
|
||||
appId: string;
|
||||
app: {
|
||||
variableModules?: VariableItemType[];
|
||||
welcomeText?: string;
|
||||
chatModels?: string[];
|
||||
name: string;
|
||||
avatar: string;
|
||||
intro: string;
|
||||
canUse?: boolean;
|
||||
};
|
||||
title: string;
|
||||
variables: Record<string, any>;
|
||||
history: ChatItemType[];
|
||||
}
|
||||
|
||||
export interface InitShareChatResponse {
|
||||
userAvatar: string;
|
||||
app: InitChatResponse['app'];
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { GET, POST } from './request';
|
||||
|
||||
export const textCensor = (data: { text: string }) =>
|
||||
POST<{ code?: number; message: string }>('/plugins/censor/text_baidu', data).then((res) => {
|
||||
if (res?.code === 5000) {
|
||||
return Promise.reject(res.message);
|
||||
}
|
||||
});
|
||||
@@ -1,18 +0,0 @@
|
||||
import { GET, POST } from '../request';
|
||||
|
||||
import { AxiosProgressEvent } from 'axios';
|
||||
|
||||
export const uploadImg = (base64Img: string) => POST<string>('/system/uploadImage', { base64Img });
|
||||
|
||||
export const postUploadFiles = (
|
||||
data: FormData,
|
||||
onUploadProgress: (progressEvent: AxiosProgressEvent) => void
|
||||
) =>
|
||||
POST<string[]>('/support/file/upload', data, {
|
||||
onUploadProgress,
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data; charset=utf-8'
|
||||
}
|
||||
});
|
||||
|
||||
export const getFileViewUrl = (fileId: string) => GET<string>('/support/file/readUrl', { fileId });
|
||||
@@ -1,4 +0,0 @@
|
||||
import { GET, POST, PUT } from './request';
|
||||
import type { InitDateResponse } from '@/pages/api/system/getInitData';
|
||||
|
||||
export const getInitData = () => GET<InitDateResponse>('/system/getInitData');
|
||||
@@ -1,107 +0,0 @@
|
||||
import { GET, POST, PUT } from './request';
|
||||
import { createHashPassword } from '@/utils/tools';
|
||||
import type { ResLogin, PromotionRecordType } from './response/user';
|
||||
import { UserAuthTypeEnum } from '@/constants/common';
|
||||
import { UserBillType, UserType, UserUpdateParams } from '@/types/user';
|
||||
import type { PagingData, RequestPaging } from '@/types';
|
||||
import { informSchema, PaySchema } from '@/types/mongoSchema';
|
||||
import { OAuthEnum } from '@/constants/user';
|
||||
|
||||
export const sendAuthCode = (data: {
|
||||
username: string;
|
||||
type: `${UserAuthTypeEnum}`;
|
||||
googleToken: string;
|
||||
}) => POST(`/plusApi/user/inform/sendAuthCode`, data);
|
||||
|
||||
export const getTokenLogin = () => GET<UserType>('/user/account/tokenLogin');
|
||||
export const oauthLogin = (params: {
|
||||
type: `${OAuthEnum}`;
|
||||
code: string;
|
||||
callbackUrl: string;
|
||||
inviterId?: string;
|
||||
}) => POST<ResLogin>('/plusApi/user/account/login/oauth', params);
|
||||
|
||||
export const postRegister = ({
|
||||
username,
|
||||
password,
|
||||
code,
|
||||
inviterId
|
||||
}: {
|
||||
username: string;
|
||||
code: string;
|
||||
password: string;
|
||||
inviterId?: string;
|
||||
}) =>
|
||||
POST<ResLogin>(`/plusApi/user/account/register/emailAndPhone`, {
|
||||
username,
|
||||
code,
|
||||
inviterId,
|
||||
password: createHashPassword(password)
|
||||
});
|
||||
|
||||
export const postFindPassword = ({
|
||||
username,
|
||||
code,
|
||||
password
|
||||
}: {
|
||||
username: string;
|
||||
code: string;
|
||||
password: string;
|
||||
}) =>
|
||||
POST<ResLogin>(`/plusApi/user/account/password/updateByCode`, {
|
||||
username,
|
||||
code,
|
||||
password: createHashPassword(password)
|
||||
});
|
||||
|
||||
export const updatePasswordByOld = ({ oldPsw, newPsw }: { oldPsw: string; newPsw: string }) =>
|
||||
POST('/user/account/updatePasswordByOld', {
|
||||
oldPsw: createHashPassword(oldPsw),
|
||||
newPsw: createHashPassword(newPsw)
|
||||
});
|
||||
|
||||
export const postLogin = ({ username, password }: { username: string; password: string }) =>
|
||||
POST<ResLogin>('/user/account/loginByPassword', {
|
||||
username,
|
||||
password: createHashPassword(password)
|
||||
});
|
||||
|
||||
export const loginOut = () => GET('/user/account/loginout');
|
||||
|
||||
export const putUserInfo = (data: UserUpdateParams) => PUT('/user/account/update', data);
|
||||
|
||||
export const getUserBills = (data: RequestPaging) =>
|
||||
POST<PagingData<UserBillType>>(`/user/getBill`, data);
|
||||
|
||||
export const getPayOrders = () => GET<PaySchema[]>(`/user/getPayOrders`);
|
||||
|
||||
export const getPayCode = (amount: number) =>
|
||||
GET<{
|
||||
codeUrl: string;
|
||||
payId: string;
|
||||
}>(`/plusApi/user/pay/getPayCode`, { amount });
|
||||
|
||||
export const checkPayResult = (payId: string) =>
|
||||
GET<number>(`/plusApi/user/pay/checkPayResult`, { payId }).then(() => {
|
||||
try {
|
||||
GET('/user/account/paySuccess');
|
||||
} catch (error) {}
|
||||
return 'success';
|
||||
});
|
||||
|
||||
export const getInforms = (data: RequestPaging) =>
|
||||
POST<PagingData<informSchema>>(`/user/inform/list`, data);
|
||||
|
||||
export const getUnreadCount = () => GET<number>(`/user/inform/countUnread`);
|
||||
export const readInform = (id: string) => GET(`/user/inform/read`, { id });
|
||||
|
||||
/* get promotion init data */
|
||||
export const getPromotionInitData = () =>
|
||||
GET<{
|
||||
invitedAmount: number;
|
||||
earningsAmount: number;
|
||||
}>('/user/promotion/getPromotionData');
|
||||
|
||||
/* promotion records */
|
||||
export const getPromotionRecords = (data: RequestPaging) =>
|
||||
POST<PromotionRecordType>(`/user/promotion/getPromotions`, data);
|
||||
@@ -1,143 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Flex,
|
||||
ModalFooter,
|
||||
ModalBody,
|
||||
Table,
|
||||
Thead,
|
||||
Tbody,
|
||||
Tr,
|
||||
Th,
|
||||
Td,
|
||||
TableContainer,
|
||||
IconButton
|
||||
} from '@chakra-ui/react';
|
||||
import { getOpenApiKeys, createAOpenApiKey, delOpenApiById } from '@/api/openapi';
|
||||
import { useQuery, useMutation } from '@tanstack/react-query';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import dayjs from 'dayjs';
|
||||
import { AddIcon, DeleteIcon } from '@chakra-ui/icons';
|
||||
import { getErrText, useCopyData } from '@/utils/tools';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import MyIcon from '../Icon';
|
||||
import MyModal from '../MyModal';
|
||||
|
||||
const APIKeyModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { Loading } = useLoading();
|
||||
const { toast } = useToast();
|
||||
const {
|
||||
data: apiKeys = [],
|
||||
isLoading: isGetting,
|
||||
refetch
|
||||
} = useQuery(['getOpenApiKeys'], getOpenApiKeys);
|
||||
const [apiKey, setApiKey] = useState('');
|
||||
const { copyData } = useCopyData();
|
||||
|
||||
const { mutate: onclickCreateApiKey, isLoading: isCreating } = useMutation({
|
||||
mutationFn: () => createAOpenApiKey(),
|
||||
onSuccess(res) {
|
||||
setApiKey(res);
|
||||
refetch();
|
||||
},
|
||||
onError(err) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(err)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const { mutate: onclickRemove, isLoading: isDeleting } = useMutation({
|
||||
mutationFn: async (id: string) => delOpenApiById(id),
|
||||
onSuccess() {
|
||||
refetch();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<MyModal isOpen onClose={onClose} w={'600px'}>
|
||||
<Box py={3} px={5}>
|
||||
<Box fontWeight={'bold'} fontSize={'2xl'}>
|
||||
API 秘钥管理
|
||||
</Box>
|
||||
<Box fontSize={'sm'} color={'myGray.600'}>
|
||||
如果你不想 API 秘钥被滥用,请勿将秘钥直接放置在前端使用~
|
||||
</Box>
|
||||
</Box>
|
||||
<ModalBody minH={'300px'} maxH={['70vh', '500px']} overflow={'overlay'}>
|
||||
<TableContainer mt={2} position={'relative'}>
|
||||
<Table>
|
||||
<Thead>
|
||||
<Tr>
|
||||
<Th>Api Key</Th>
|
||||
<Th>创建时间</Th>
|
||||
<Th>最后一次使用时间</Th>
|
||||
<Th />
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody fontSize={'sm'}>
|
||||
{apiKeys.map(({ id, apiKey, createTime, lastUsedTime }) => (
|
||||
<Tr key={id}>
|
||||
<Td>{apiKey}</Td>
|
||||
<Td>{dayjs(createTime).format('YYYY/MM/DD HH:mm:ss')}</Td>
|
||||
<Td>
|
||||
{lastUsedTime
|
||||
? dayjs(lastUsedTime).format('YYYY/MM/DD HH:mm:ss')
|
||||
: '没有使用过'}
|
||||
</Td>
|
||||
<Td>
|
||||
<IconButton
|
||||
icon={<DeleteIcon />}
|
||||
size={'xs'}
|
||||
aria-label={'delete'}
|
||||
variant={'base'}
|
||||
colorScheme={'gray'}
|
||||
onClick={() => onclickRemove(id)}
|
||||
/>
|
||||
</Td>
|
||||
</Tr>
|
||||
))}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</ModalBody>
|
||||
|
||||
<ModalFooter>
|
||||
<Button
|
||||
variant="base"
|
||||
leftIcon={<AddIcon color={'myGray.600'} fontSize={'sm'} />}
|
||||
onClick={() => onclickCreateApiKey()}
|
||||
>
|
||||
新建秘钥
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
<Loading loading={isGetting || isCreating || isDeleting} fixed={false} />
|
||||
<MyModal isOpen={!!apiKey} w={'400px'} onClose={() => setApiKey('')}>
|
||||
<Box py={3} px={5}>
|
||||
<Box fontWeight={'bold'} fontSize={'2xl'}>
|
||||
新的 API 秘钥
|
||||
</Box>
|
||||
<Box fontSize={'sm'} color={'myGray.600'}>
|
||||
请保管好你的秘钥,秘钥不会再次展示~
|
||||
</Box>
|
||||
</Box>
|
||||
<ModalBody>
|
||||
<Flex bg={'myGray.100'} px={3} py={2} cursor={'pointer'} onClick={() => copyData(apiKey)}>
|
||||
<Box flex={1}>{apiKey}</Box>
|
||||
<MyIcon name={'copy'} w={'16px'}></MyIcon>
|
||||
</Flex>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant="base" onClick={() => setApiKey('')}>
|
||||
好的
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default APIKeyModal;
|
||||
@@ -1,151 +0,0 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { ModalBody, Box, useTheme } from '@chakra-ui/react';
|
||||
import { getKbDataItemById } from '@/api/plugins/kb';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { getErrText } from '@/utils/tools';
|
||||
import { QuoteItemType } from '@/types/chat';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import InputDataModal, { RawFileText } from '@/pages/kb/detail/components/InputDataModal';
|
||||
import MyModal from '../MyModal';
|
||||
import { KbDataItemType } from '@/types/plugin';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
type SearchType = KbDataItemType & {
|
||||
kb_id?: string;
|
||||
};
|
||||
|
||||
const QuoteModal = ({
|
||||
onUpdateQuote,
|
||||
rawSearch = [],
|
||||
onClose
|
||||
}: {
|
||||
onUpdateQuote: (quoteId: string, sourceText?: string) => Promise<void>;
|
||||
rawSearch: SearchType[];
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const { setIsLoading, Loading } = useLoading();
|
||||
const [editDataItem, setEditDataItem] = useState<QuoteItemType>();
|
||||
|
||||
const isShare = useMemo(() => router.pathname === '/chat/share', [router.pathname]);
|
||||
|
||||
/**
|
||||
* click edit, get new kbDataItem
|
||||
*/
|
||||
const onclickEdit = useCallback(
|
||||
async (item: SearchType) => {
|
||||
if (!item.id) return;
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const data = await getKbDataItemById(item.id);
|
||||
|
||||
if (!data) {
|
||||
onUpdateQuote(item.id, '已删除');
|
||||
throw new Error('该数据已被删除');
|
||||
}
|
||||
|
||||
setEditDataItem(data);
|
||||
} catch (err) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: getErrText(err)
|
||||
});
|
||||
}
|
||||
setIsLoading(false);
|
||||
},
|
||||
[setIsLoading, toast, onUpdateQuote]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<MyModal
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
h={['90vh', '80vh']}
|
||||
isCentered
|
||||
minW={['90vw', '600px']}
|
||||
title={
|
||||
<>
|
||||
知识库引用({rawSearch.length}条)
|
||||
<Box fontSize={['xs', 'sm']} fontWeight={'normal'}>
|
||||
注意: 修改知识库内容成功后,此处不会显示变更情况。点击编辑后,会显示知识库最新的内容。
|
||||
</Box>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<ModalBody
|
||||
pt={0}
|
||||
whiteSpace={'pre-wrap'}
|
||||
textAlign={'justify'}
|
||||
wordBreak={'break-all'}
|
||||
fontSize={'sm'}
|
||||
>
|
||||
{rawSearch.map((item, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
flex={'1 0 0'}
|
||||
p={2}
|
||||
borderRadius={'lg'}
|
||||
border={theme.borders.base}
|
||||
_notLast={{ mb: 2 }}
|
||||
position={'relative'}
|
||||
_hover={{ '& .edit': { display: 'flex' } }}
|
||||
overflow={'hidden'}
|
||||
>
|
||||
{item.source && !isShare && (
|
||||
<RawFileText filename={item.source} fileId={item.file_id} />
|
||||
)}
|
||||
<Box>{item.q}</Box>
|
||||
<Box>{item.a}</Box>
|
||||
{item.id && !isShare && (
|
||||
<Box
|
||||
className="edit"
|
||||
display={'none'}
|
||||
position={'absolute'}
|
||||
right={0}
|
||||
top={0}
|
||||
bottom={0}
|
||||
w={'40px'}
|
||||
bg={'rgba(255,255,255,0.9)'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
boxShadow={'-10px 0 10px rgba(255,255,255,1)'}
|
||||
>
|
||||
<MyIcon
|
||||
name={'edit'}
|
||||
w={'18px'}
|
||||
h={'18px'}
|
||||
cursor={'pointer'}
|
||||
color={'myGray.600'}
|
||||
_hover={{
|
||||
color: 'myBlue.700'
|
||||
}}
|
||||
onClick={() => onclickEdit(item)}
|
||||
/>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
))}
|
||||
</ModalBody>
|
||||
<Loading fixed={false} />
|
||||
</MyModal>
|
||||
{editDataItem && (
|
||||
<InputDataModal
|
||||
onClose={() => setEditDataItem(undefined)}
|
||||
onSuccess={() => onUpdateQuote(editDataItem.id)}
|
||||
onDelete={() => onUpdateQuote(editDataItem.id, '已删除')}
|
||||
kbId={editDataItem.kb_id}
|
||||
defaultValues={{
|
||||
...editDataItem,
|
||||
dataId: editDataItem.id
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default QuoteModal;
|
||||
@@ -1,55 +0,0 @@
|
||||
import React from 'react';
|
||||
import { ModalBody, ModalFooter, Button } from '@chakra-ui/react';
|
||||
import MyModal from '../MyModal';
|
||||
import { useRequest } from '@/hooks/useRequest';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { userUpdateChatFeedback } from '@/api/chat';
|
||||
|
||||
const ReadFeedbackModal = ({
|
||||
chatItemId,
|
||||
content,
|
||||
isMarked,
|
||||
onMark,
|
||||
onSuccess,
|
||||
onClose
|
||||
}: {
|
||||
chatItemId: string;
|
||||
content: string;
|
||||
isMarked: boolean;
|
||||
onMark: () => void;
|
||||
onSuccess: () => void;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const { mutate, isLoading } = useRequest({
|
||||
mutationFn: async () => {
|
||||
return userUpdateChatFeedback({
|
||||
chatItemId,
|
||||
userFeedback: undefined
|
||||
});
|
||||
},
|
||||
onSuccess() {
|
||||
onSuccess();
|
||||
},
|
||||
errorToast: t('chat.Feedback Update Failed')
|
||||
});
|
||||
|
||||
return (
|
||||
<MyModal isOpen={true} onClose={onClose} title={t('chat.Feedback Modal')}>
|
||||
<ModalBody>{content}</ModalBody>
|
||||
<ModalFooter>
|
||||
{!isMarked && (
|
||||
<Button variant={'base'} mr={2} onClick={onMark}>
|
||||
{t('chat.Feedback Mark')}
|
||||
</Button>
|
||||
)}
|
||||
<Button isLoading={isLoading} onClick={mutate}>
|
||||
{t('chat.Feedback Close')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(ReadFeedbackModal);
|
||||
@@ -1,108 +0,0 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { ChatModuleEnum } from '@/constants/chat';
|
||||
import { ChatHistoryItemResType, ChatItemType, QuoteItemType } from '@/types/chat';
|
||||
import { Flex, BoxProps, useDisclosure } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
import dynamic from 'next/dynamic';
|
||||
import Tag from '../Tag';
|
||||
import MyTooltip from '../MyTooltip';
|
||||
const QuoteModal = dynamic(() => import('./QuoteModal'), { ssr: false });
|
||||
const ContextModal = dynamic(() => import('./ContextModal'), { ssr: false });
|
||||
const WholeResponseModal = dynamic(() => import('./WholeResponseModal'), { ssr: false });
|
||||
|
||||
const ResponseTags = ({
|
||||
chatId,
|
||||
contentId,
|
||||
responseData = []
|
||||
}: {
|
||||
chatId?: string;
|
||||
contentId?: string;
|
||||
responseData?: ChatHistoryItemResType[];
|
||||
}) => {
|
||||
const { isPc } = useGlobalStore();
|
||||
const { t } = useTranslation();
|
||||
const [quoteModalData, setQuoteModalData] = useState<QuoteItemType[]>();
|
||||
const [contextModalData, setContextModalData] = useState<ChatItemType[]>();
|
||||
const {
|
||||
isOpen: isOpenWholeModal,
|
||||
onOpen: onOpenWholeModal,
|
||||
onClose: onCloseWholeModal
|
||||
} = useDisclosure();
|
||||
|
||||
const {
|
||||
quoteList = [],
|
||||
completeMessages = [],
|
||||
tokens = 0
|
||||
} = useMemo(() => {
|
||||
const chatData = responseData.find((item) => item.moduleName === ChatModuleEnum.AIChat);
|
||||
if (!chatData) return {};
|
||||
return {
|
||||
quoteList: chatData.quoteList,
|
||||
completeMessages: chatData.completeMessages,
|
||||
tokens: responseData.reduce((sum, item) => sum + (item.tokens || 0), 0)
|
||||
};
|
||||
}, [responseData]);
|
||||
|
||||
const updateQuote = useCallback(async (quoteId: string, sourceText?: string) => {}, []);
|
||||
|
||||
const TagStyles: BoxProps = {
|
||||
mr: 2,
|
||||
bg: 'transparent'
|
||||
};
|
||||
|
||||
return responseData.length === 0 ? null : (
|
||||
<Flex alignItems={'center'} mt={2} flexWrap={'wrap'}>
|
||||
{quoteList.length > 0 && (
|
||||
<MyTooltip label="查看引用">
|
||||
<Tag
|
||||
colorSchema="blue"
|
||||
cursor={'pointer'}
|
||||
{...TagStyles}
|
||||
onClick={() => setQuoteModalData(quoteList)}
|
||||
>
|
||||
{quoteList.length}条引用
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{completeMessages.length > 0 && (
|
||||
<MyTooltip label={'点击查看完整对话记录'}>
|
||||
<Tag
|
||||
colorSchema="green"
|
||||
cursor={'pointer'}
|
||||
{...TagStyles}
|
||||
onClick={() => setContextModalData(completeMessages)}
|
||||
>
|
||||
{completeMessages.length}条上下文
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
)}
|
||||
{isPc && tokens > 0 && (
|
||||
<Tag colorSchema="purple" cursor={'default'} {...TagStyles}>
|
||||
{tokens}Tokens
|
||||
</Tag>
|
||||
)}
|
||||
<MyTooltip label={'点击查看完整响应值'}>
|
||||
<Tag colorSchema="gray" cursor={'pointer'} {...TagStyles} onClick={onOpenWholeModal}>
|
||||
{t('chat.Complete Response')}
|
||||
</Tag>
|
||||
</MyTooltip>
|
||||
|
||||
{!!quoteModalData && (
|
||||
<QuoteModal
|
||||
rawSearch={quoteModalData}
|
||||
onUpdateQuote={updateQuote}
|
||||
onClose={() => setQuoteModalData(undefined)}
|
||||
/>
|
||||
)}
|
||||
{!!contextModalData && (
|
||||
<ContextModal context={contextModalData} onClose={() => setContextModalData(undefined)} />
|
||||
)}
|
||||
{isOpenWholeModal && (
|
||||
<WholeResponseModal response={responseData} onClose={onCloseWholeModal} />
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResponseTags;
|
||||
@@ -1,117 +0,0 @@
|
||||
import React, { useRef, useState } from 'react';
|
||||
import {
|
||||
ModalBody,
|
||||
useTheme,
|
||||
ModalFooter,
|
||||
Button,
|
||||
ModalHeader,
|
||||
Box,
|
||||
Card,
|
||||
Flex
|
||||
} from '@chakra-ui/react';
|
||||
import MyModal from '../MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useDatasetStore } from '@/store/dataset';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import Avatar from '../Avatar';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
|
||||
const SelectDataset = ({
|
||||
onSuccess,
|
||||
onClose
|
||||
}: {
|
||||
onSuccess: (kbId: string) => void;
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const { isPc } = useGlobalStore();
|
||||
const { toast } = useToast();
|
||||
const { myKbList, loadKbList } = useDatasetStore();
|
||||
const [selectedId, setSelectedId] = useState<string>();
|
||||
|
||||
useQuery(['loadKbList'], () => loadKbList());
|
||||
|
||||
return (
|
||||
<MyModal isOpen={true} onClose={onClose} w={'100%'} maxW={['90vw', '900px']} isCentered={!isPc}>
|
||||
<Flex flexDirection={'column'} h={['90vh', 'auto']}>
|
||||
<ModalHeader>
|
||||
<Box>{t('chat.Select Mark Kb')}</Box>
|
||||
<Box fontSize={'sm'} color={'myGray.500'} fontWeight={'normal'}>
|
||||
{t('chat.Select Mark Kb Desc')}
|
||||
</Box>
|
||||
</ModalHeader>
|
||||
<ModalBody
|
||||
flex={['1 0 0', '0 0 auto']}
|
||||
maxH={'80vh'}
|
||||
overflowY={'auto'}
|
||||
display={'grid'}
|
||||
gridTemplateColumns={['repeat(1,1fr)', 'repeat(2,1fr)', 'repeat(3,1fr)']}
|
||||
gridGap={3}
|
||||
userSelect={'none'}
|
||||
>
|
||||
{myKbList.map((item) =>
|
||||
(() => {
|
||||
const selected = selectedId === item._id;
|
||||
return (
|
||||
<Card
|
||||
key={item._id}
|
||||
p={3}
|
||||
border={theme.borders.base}
|
||||
boxShadow={'sm'}
|
||||
h={'80px'}
|
||||
cursor={'pointer'}
|
||||
_hover={{
|
||||
boxShadow: 'md'
|
||||
}}
|
||||
{...(selected
|
||||
? {
|
||||
bg: 'myBlue.300'
|
||||
}
|
||||
: {})}
|
||||
onClick={() => {
|
||||
setSelectedId(item._id);
|
||||
}}
|
||||
>
|
||||
<Flex alignItems={'center'} h={'38px'}>
|
||||
<Avatar src={item.avatar} w={['24px', '28px', '32px']}></Avatar>
|
||||
<Box ml={3} fontWeight={'bold'} fontSize={['md', 'lg', 'xl']}>
|
||||
{item.name}
|
||||
</Box>
|
||||
</Flex>
|
||||
<Flex justifyContent={'flex-end'} alignItems={'center'} fontSize={'sm'}>
|
||||
<MyIcon mr={1} name="kbTest" w={'12px'} />
|
||||
<Box color={'myGray.500'}>{item.vectorModel.name}</Box>
|
||||
</Flex>
|
||||
</Card>
|
||||
);
|
||||
})()
|
||||
)}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button variant={'base'} mr={2} onClick={onClose}>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (!selectedId) {
|
||||
return toast({
|
||||
status: 'warning',
|
||||
title: t('Select value is empty')
|
||||
});
|
||||
}
|
||||
|
||||
onSuccess(selectedId);
|
||||
}}
|
||||
>
|
||||
{t('Confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Flex>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default SelectDataset;
|
||||
@@ -1,71 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { Box, ModalBody, useTheme, Flex } from '@chakra-ui/react';
|
||||
import type { ChatHistoryItemResType } from '@/types/chat';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import MyModal from '../MyModal';
|
||||
import MyTooltip from '../MyTooltip';
|
||||
import { QuestionOutlineIcon } from '@chakra-ui/icons';
|
||||
|
||||
const ResponseModal = ({
|
||||
response,
|
||||
onClose
|
||||
}: {
|
||||
response: ChatHistoryItemResType[];
|
||||
onClose: () => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const theme = useTheme();
|
||||
|
||||
const formatResponse = useMemo(
|
||||
() =>
|
||||
response.map((item) => {
|
||||
const copy = { ...item };
|
||||
delete copy.completeMessages;
|
||||
delete copy.quoteList;
|
||||
return copy;
|
||||
}),
|
||||
[response]
|
||||
);
|
||||
|
||||
return (
|
||||
<MyModal
|
||||
isOpen={true}
|
||||
onClose={onClose}
|
||||
h={['90vh', '80vh']}
|
||||
minW={['90vw', '600px']}
|
||||
title={
|
||||
<Flex alignItems={'center'}>
|
||||
{t('chat.Complete Response')}
|
||||
<MyTooltip
|
||||
label={
|
||||
'moduleName: 模型名\nprice: 价格,倍率:100000\nmodel?: 模型名\ntokens?: token 消耗\n\nanswer?: 回答内容\nquestion?: 问题\ntemperature?: 温度\nmaxToken?: 最大 tokens\n\nsimilarity?: 相似度\nlimit?: 单次搜索结果\n\ncqList?: 问题分类列表\ncqResult?: 分类结果\n\nextractDescription?: 内容提取描述\nextractResult?: 提取结果'
|
||||
}
|
||||
>
|
||||
<QuestionOutlineIcon ml={2} />
|
||||
</MyTooltip>
|
||||
</Flex>
|
||||
}
|
||||
isCentered
|
||||
>
|
||||
<ModalBody>
|
||||
{formatResponse.map((item, i) => (
|
||||
<Box
|
||||
key={i}
|
||||
p={2}
|
||||
pt={[0, 2]}
|
||||
borderRadius={'lg'}
|
||||
border={theme.borders.base}
|
||||
_notLast={{ mb: 2 }}
|
||||
position={'relative'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
>
|
||||
{JSON.stringify(item, null, 2)}
|
||||
</Box>
|
||||
))}
|
||||
</ModalBody>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ResponseModal;
|
||||
@@ -1,43 +0,0 @@
|
||||
.stopIcon {
|
||||
animation: zoomStopIcon 0.4s infinite alternate;
|
||||
}
|
||||
@keyframes zoomStopIcon {
|
||||
0% {
|
||||
transform: scale(0.8);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
}
|
||||
|
||||
.newChat {
|
||||
.modelListContainer {
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.modelList {
|
||||
border-radius: 6px;
|
||||
}
|
||||
&:hover {
|
||||
.modelListContainer {
|
||||
height: 60vh;
|
||||
}
|
||||
.modelList {
|
||||
box-shadow: 0 0 5px rgba($color: #000000, $alpha: 0.05);
|
||||
border: 1px solid #dee0e2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.statusAnimation {
|
||||
animation: statusBox 0.8s linear infinite alternate;
|
||||
}
|
||||
@keyframes statusBox {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0.11;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { SystemInputEnum } from '@/constants/app';
|
||||
import { FlowModuleTypeEnum } from '@/constants/flow';
|
||||
import { getChatModel } from '@/service/utils/data';
|
||||
import { AppModuleItemType, VariableItemType } from '@/types/app';
|
||||
|
||||
export const getSpecialModule = (modules: AppModuleItemType[]) => {
|
||||
const welcomeText: string =
|
||||
modules
|
||||
.find((item) => item.flowType === FlowModuleTypeEnum.userGuide)
|
||||
?.inputs?.find((item) => item.key === SystemInputEnum.welcomeText)?.value || '';
|
||||
|
||||
const variableModules: VariableItemType[] =
|
||||
modules
|
||||
.find((item) => item.flowType === FlowModuleTypeEnum.variable)
|
||||
?.inputs.find((item) => item.key === SystemInputEnum.variables)?.value || [];
|
||||
|
||||
return {
|
||||
welcomeText,
|
||||
variableModules
|
||||
};
|
||||
};
|
||||
export const getChatModelNameList = (modules: AppModuleItemType[]): string[] => {
|
||||
const chatModules = modules.filter((item) => item.flowType === FlowModuleTypeEnum.chatNode);
|
||||
return chatModules
|
||||
.map(
|
||||
(item) => getChatModel(item.inputs.find((input) => input.key === 'model')?.value)?.name || ''
|
||||
)
|
||||
.filter((item) => item);
|
||||
};
|
||||
@@ -1,4 +0,0 @@
|
||||
.datePicker {
|
||||
--rdp-background-color: #d6e8ff;
|
||||
--rdp-accent-color: #0000ff;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691412437336" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2312" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M512 14.8973037a497.1026963 497.1026963 0 1 0 497.1026963 497.1026963A497.1026963 497.1026963 0 0 0 512 14.8973037z m165.70089876 581.8863224l-207.12612345 124.27567408L346.29910124 796.45320912V255.16360691l125.38034629 75.39390894 207.12612347 124.27567407 118.47614261 70.97521873z" p-id="2313"></path></svg>
|
||||
|
Before Width: | Height: | Size: 642 B |
|
Before Width: | Height: | Size: 6.1 KiB |
@@ -1,3 +0,0 @@
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M8.3 5.7a1 1 0 011.4-1.4l7.71 7.7-7.7 7.7a1 1 0 11-1.42-1.4l6.3-6.3-6.3-6.3z" fill-rule="nonzero"></path>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 151 B |
@@ -1 +0,0 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1683450443331" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1727" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><path d="M58.15222827 227.09272427L492.1075808-23.4520384a39.00952427 39.00952427 0 0 1 39.00952427 0l433.95657066 250.54476267a39.00952427 39.00952427 0 0 1 19.50476267 33.78346666v501.0895232a39.00952427 39.00952427 0 0 1-19.50476267 33.78224747l-433.95657066 250.54476267a39.00952427 39.00952427 0 0 1-39.00952427 0L58.15100907 795.7479616a39.00952427 39.00952427 0 0 1-19.5047616-33.78224747V260.87619093a39.00952427 39.00952427 0 0 1 19.5047616-33.78346666z m63.494096 53.5503232a9.7523808 9.7523808 0 0 0-4.87619094 8.4467808v444.66224746a9.7523808 9.7523808 0 0 0 4.87619094 8.44556267l385.08982826 222.3311232a9.7523808 9.7523808 0 0 0 9.7523808 0l385.08860907-222.329904a9.7523808 9.7523808 0 0 0 4.87619093-8.44678187V289.08982827a9.7523808 9.7523808 0 0 0-4.87619093-8.4467808L516.48853333 58.3131424a9.7523808 9.7523808 0 0 0-9.7523808 0l-385.08982826 222.32990507z m389.56129493 190.72l300.3611424-173.4131808c18.65752427-10.77150507 42.51550507-4.3788192 53.28822933 14.27870506 10.77150507 18.65752427 4.3788192 42.51550507-14.27870506 53.28822827L551.00952427 538.4728384V881.37142827c0 21.54422827-17.46529493 39.00952427-39.00952427 39.00952426-21.54422827 0-39.00952427-17.46529493-39.00952427-39.00952426V539.38712427L172.89386667 366.12632427c-18.65752427-10.77272427-25.05142827-34.63070507-14.27870507-53.28822934 10.77272427-18.65752427 34.63070507-25.05142827 53.28822933-14.278704L511.2076192 471.36304747z" p-id="1728"></path></svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,110 +0,0 @@
|
||||
import React from 'react';
|
||||
import type { IconProps } from '@chakra-ui/react';
|
||||
import { Icon } from '@chakra-ui/react';
|
||||
|
||||
const map = {
|
||||
appFill: require('./icons/fill/app.svg').default,
|
||||
appLight: require('./icons/light/app.svg').default,
|
||||
copy: require('./icons/copy.svg').default,
|
||||
chatSend: require('./icons/chatSend.svg').default,
|
||||
delete: require('./icons/delete.svg').default,
|
||||
stop: require('./icons/stop.svg').default,
|
||||
collectionLight: require('./icons/collectionLight.svg').default,
|
||||
collectionSolid: require('./icons/collectionSolid.svg').default,
|
||||
empty: require('./icons/empty.svg').default,
|
||||
back: require('./icons/back.svg').default,
|
||||
backFill: require('./icons/fill/back.svg').default,
|
||||
more: require('./icons/more.svg').default,
|
||||
tabbarChat: require('./icons/phoneTabbar/chat.svg').default,
|
||||
tabbarModel: require('./icons/phoneTabbar/app.svg').default,
|
||||
tabbarMore: require('./icons/phoneTabbar/more.svg').default,
|
||||
tabbarMe: require('./icons/phoneTabbar/me.svg').default,
|
||||
closeSolid: require('./icons/closeSolid.svg').default,
|
||||
wx: require('./icons/wx.svg').default,
|
||||
out: require('./icons/out.svg').default,
|
||||
git: require('./icons/git.svg').default,
|
||||
gitFill: require('./icons/fill/git.svg').default,
|
||||
googleFill: require('./icons/fill/google.svg').default,
|
||||
menu: require('./icons/menu.svg').default,
|
||||
edit: require('./icons/edit.svg').default,
|
||||
inform: require('./icons/inform.svg').default,
|
||||
export: require('./icons/export.svg').default,
|
||||
text: require('./icons/text.svg').default,
|
||||
history: require('./icons/history.svg').default,
|
||||
kbTest: require('./icons/kbTest.svg').default,
|
||||
date: require('./icons/date.svg').default,
|
||||
apikey: require('./icons/apikey.svg').default,
|
||||
save: require('./icons/save.svg').default,
|
||||
minus: require('./icons/minus.svg').default,
|
||||
chat: require('./icons/light/chat.svg').default,
|
||||
chatFill: require('./icons/fill/chat.svg').default,
|
||||
clear: require('./icons/light/clear.svg').default,
|
||||
apiLight: require('./icons/light/appApi.svg').default,
|
||||
overviewLight: require('./icons/light/overview.svg').default,
|
||||
settingLight: require('./icons/light/setting.svg').default,
|
||||
shareLight: require('./icons/light/share.svg').default,
|
||||
dbLight: require('./icons/light/db.svg').default,
|
||||
dbFill: require('./icons/fill/db.svg').default,
|
||||
appStoreLight: require('./icons/light/appStore.svg').default,
|
||||
appStoreFill: require('./icons/fill/appStore.svg').default,
|
||||
meLight: require('./icons/light/me.svg').default,
|
||||
meFill: require('./icons/fill/me.svg').default,
|
||||
welcomeText: require('./icons/modules/welcomeText.svg').default,
|
||||
variable: require('./icons/modules/variable.svg').default,
|
||||
setTop: require('./icons/light/setTop.svg').default,
|
||||
fullScreenLight: require('./icons/light/fullScreen.svg').default,
|
||||
voice: require('./icons/voice.svg').default,
|
||||
html: require('./icons/file/html.svg').default,
|
||||
pdf: require('./icons/file/pdf.svg').default,
|
||||
markdown: require('./icons/file/markdown.svg').default,
|
||||
importLight: require('./icons/light/import.svg').default,
|
||||
manualImport: require('./icons/file/manualImport.svg').default,
|
||||
indexImport: require('./icons/file/indexImport.svg').default,
|
||||
csvImport: require('./icons/file/csv.svg').default,
|
||||
qaImport: require('./icons/file/qaImport.svg').default,
|
||||
uploadFile: require('./icons/file/uploadFile.svg').default,
|
||||
closeLight: require('./icons/light/close.svg').default,
|
||||
customTitle: require('./icons/light/customTitle.svg').default,
|
||||
billRecordLight: require('./icons/light/billRecord.svg').default,
|
||||
informLight: require('./icons/light/inform.svg').default,
|
||||
payRecordLight: require('./icons/light/payRecord.svg').default,
|
||||
loginoutLight: require('./icons/light/loginout.svg').default,
|
||||
chatModelTag: require('./icons/light/chatModelTag.svg').default,
|
||||
language_en: require('./icons/language/en.svg').default,
|
||||
language_zh: require('./icons/language/zh.svg').default,
|
||||
outlink_share: require('./icons/outlink/share.svg').default,
|
||||
outlink_iframe: require('./icons/outlink/iframe.svg').default,
|
||||
addCircle: require('./icons/circle/add.svg').default,
|
||||
playFill: require('./icons/fill/play.svg').default,
|
||||
courseLight: require('./icons/light/course.svg').default,
|
||||
promotionLight: require('./icons/light/promotion.svg').default,
|
||||
logsLight: require('./icons/light/logs.svg').default,
|
||||
badLight: require('./icons/light/bad.svg').default,
|
||||
markLight: require('./icons/light/mark.svg').default,
|
||||
retryLight: require('./icons/light/retry.svg').default,
|
||||
rightArrowLight: require('./icons/light/rightArrow.svg').default,
|
||||
searchLight: require('./icons/light/search.svg').default,
|
||||
plusFill: require('./icons/fill/plus.svg').default,
|
||||
moveLight: require('./icons/light/move.svg').default
|
||||
};
|
||||
|
||||
export type IconName = keyof typeof map;
|
||||
|
||||
const MyIcon = (
|
||||
{ name, w = 'auto', h = 'auto', ...props }: { name: IconName } & IconProps,
|
||||
ref: any
|
||||
) => {
|
||||
return map[name] ? (
|
||||
<Icon
|
||||
as={map[name]}
|
||||
w={w}
|
||||
h={h}
|
||||
boxSizing={'content-box'}
|
||||
verticalAlign={'top'}
|
||||
fill={'currentcolor'}
|
||||
{...props}
|
||||
/>
|
||||
) : null;
|
||||
};
|
||||
|
||||
export default React.forwardRef(MyIcon);
|
||||
@@ -1,48 +0,0 @@
|
||||
import React, { useState } from 'react';
|
||||
import { Menu, MenuButton, MenuItem, MenuList, MenuButtonProps } from '@chakra-ui/react';
|
||||
import { getLangStore, LangEnum, setLangStore, langMap } from '@/utils/i18n';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
const Language = (props: MenuButtonProps) => {
|
||||
const router = useRouter();
|
||||
const { i18n } = useTranslation();
|
||||
|
||||
const [language, setLanguage] = useState<`${LangEnum}`>(getLangStore());
|
||||
|
||||
return (
|
||||
<Menu autoSelect={false}>
|
||||
<MenuButton
|
||||
{...props}
|
||||
sx={{
|
||||
'& span': {
|
||||
flex: 0
|
||||
}
|
||||
}}
|
||||
>
|
||||
<MyIcon name={langMap[language].icon as any} w={['18px', '22px']} />
|
||||
</MenuButton>
|
||||
<MenuList w="max-content" minW="120px">
|
||||
{Object.entries(langMap).map(([key, lang]) => (
|
||||
<MenuItem
|
||||
key={key}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
onClick={() => {
|
||||
const lang = key as `${LangEnum}`;
|
||||
setLangStore(lang);
|
||||
setLanguage(lang);
|
||||
i18n?.changeLanguage?.(lang);
|
||||
router.reload();
|
||||
}}
|
||||
>
|
||||
{lang.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuList>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(Language);
|
||||
@@ -1,28 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Spinner, Flex, Box } from '@chakra-ui/react';
|
||||
|
||||
const Loading = ({ fixed = true, text = '' }: { fixed?: boolean; text?: string }) => {
|
||||
return (
|
||||
<Flex
|
||||
position={fixed ? 'fixed' : 'absolute'}
|
||||
zIndex={1000}
|
||||
backgroundColor={'rgba(255,255,255,0.5)'}
|
||||
top={0}
|
||||
left={0}
|
||||
right={0}
|
||||
bottom={0}
|
||||
alignItems={'center'}
|
||||
justifyContent={'center'}
|
||||
flexDirection={'column'}
|
||||
>
|
||||
<Spinner thickness="4px" speed="0.65s" emptyColor="myGray.100" color="myBlue.600" size="xl" />
|
||||
{text && (
|
||||
<Box mt={2} color="myBlue.700" fontWeight={'bold'}>
|
||||
{text}
|
||||
</Box>
|
||||
)}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
|
||||
export default Loading;
|
||||
@@ -1,70 +0,0 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import RemarkGfm from 'remark-gfm';
|
||||
import RemarkMath from 'remark-math';
|
||||
import RehypeKatex from 'rehype-katex';
|
||||
import RemarkBreaks from 'remark-breaks';
|
||||
|
||||
import 'katex/dist/katex.min.css';
|
||||
import styles from './index.module.scss';
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
import CodeLight from './CodeLight';
|
||||
|
||||
const MermaidCodeBlock = dynamic(() => import('./img/MermaidCodeBlock'));
|
||||
const MdImage = dynamic(() => import('./img/Image'));
|
||||
const ChatGuide = dynamic(() => import('./chat/Guide'));
|
||||
const EChartsCodeBlock = dynamic(() => import('./img/EChartsCodeBlock'));
|
||||
|
||||
function Code({ inline, className, children }: any) {
|
||||
const match = /language-(\w+)/.exec(className || '');
|
||||
const codeType = match?.[1];
|
||||
|
||||
if (codeType === 'mermaid') {
|
||||
return <MermaidCodeBlock code={String(children)} />;
|
||||
}
|
||||
|
||||
if (codeType === 'guide') {
|
||||
return <ChatGuide text={String(children)} />;
|
||||
}
|
||||
if (codeType === 'echarts') {
|
||||
return <EChartsCodeBlock code={String(children)} />;
|
||||
}
|
||||
return (
|
||||
<CodeLight className={className} inline={inline} match={match}>
|
||||
{children}
|
||||
</CodeLight>
|
||||
);
|
||||
}
|
||||
function Image({ src }: { src?: string }) {
|
||||
return <MdImage src={src} />;
|
||||
}
|
||||
|
||||
const Markdown = ({ source, isChatting = false }: { source: string; isChatting?: boolean }) => {
|
||||
const components = useMemo(
|
||||
() => ({
|
||||
img: Image,
|
||||
pre: 'div',
|
||||
p: 'div',
|
||||
code: Code
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<ReactMarkdown
|
||||
className={`markdown ${styles.markdown}
|
||||
${isChatting ? (source === '' ? styles.waitingAnimation : styles.animation) : ''}
|
||||
`}
|
||||
remarkPlugins={[RemarkGfm, RemarkMath, RemarkBreaks]}
|
||||
rehypePlugins={[RehypeKatex]}
|
||||
// @ts-ignore
|
||||
components={components}
|
||||
linkTarget={'_blank'}
|
||||
>
|
||||
{source}
|
||||
</ReactMarkdown>
|
||||
);
|
||||
};
|
||||
|
||||
export default Markdown;
|
||||
@@ -1,54 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalCloseButton,
|
||||
ModalContentProps,
|
||||
Box
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
interface Props extends ModalContentProps {
|
||||
showCloseBtn?: boolean;
|
||||
title?: any;
|
||||
isCentered?: boolean;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const MyModal = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
title,
|
||||
children,
|
||||
showCloseBtn = true,
|
||||
isCentered,
|
||||
w = 'auto',
|
||||
maxW = ['90vw', '600px'],
|
||||
...props
|
||||
}: Props) => {
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} autoFocus={false} isCentered={isCentered}>
|
||||
<ModalOverlay />
|
||||
<ModalContent
|
||||
display={'flex'}
|
||||
flexDirection={'column'}
|
||||
w={w}
|
||||
minW={['90vw', '400px']}
|
||||
maxW={maxW}
|
||||
position={'relative'}
|
||||
maxH={'90vh'}
|
||||
{...props}
|
||||
>
|
||||
{!!title && <ModalHeader>{title}</ModalHeader>}
|
||||
<Box overflow={'overlay'} h={'100%'}>
|
||||
{showCloseBtn && <ModalCloseButton />}
|
||||
{children}
|
||||
</Box>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default MyModal;
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Box, useTheme, type BoxProps } from '@chakra-ui/react';
|
||||
|
||||
const PageContainer = ({ children, ...props }: BoxProps) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Box bg={'myGray.100'} h={'100%'} p={[0, 5]} px={[0, 6]} {...props}>
|
||||
<Box
|
||||
h={'100%'}
|
||||
bg={'white'}
|
||||
borderRadius={[0, '2xl']}
|
||||
border={['none', theme.borders.lg]}
|
||||
overflow={'overlay'}
|
||||
>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default PageContainer;
|
||||
@@ -1,124 +0,0 @@
|
||||
import React, { useRef, forwardRef } from 'react';
|
||||
import {
|
||||
Menu,
|
||||
Box,
|
||||
MenuList,
|
||||
MenuItem,
|
||||
Button,
|
||||
useDisclosure,
|
||||
useOutsideClick
|
||||
} from '@chakra-ui/react';
|
||||
import type { ButtonProps } from '@chakra-ui/react';
|
||||
import { ChevronDownIcon } from '@chakra-ui/icons';
|
||||
interface Props extends ButtonProps {
|
||||
value?: string;
|
||||
placeholder?: string;
|
||||
list: {
|
||||
label: string;
|
||||
value: string;
|
||||
}[];
|
||||
onchange?: (val: string) => void;
|
||||
}
|
||||
|
||||
const MySelect = (
|
||||
{ placeholder, value, width = '100%', list, onchange, ...props }: Props,
|
||||
selectRef: any
|
||||
) => {
|
||||
const ref = useRef<HTMLButtonElement>(null);
|
||||
const SelectRef = useRef<HTMLDivElement>(null);
|
||||
const menuItemStyles = {
|
||||
borderRadius: 'sm',
|
||||
py: 2,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
_hover: {
|
||||
backgroundColor: 'myWhite.600'
|
||||
}
|
||||
};
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
useOutsideClick({
|
||||
ref: SelectRef,
|
||||
handler: () => {
|
||||
onClose();
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Menu autoSelect={false} isOpen={isOpen} onOpen={onOpen} onClose={onClose}>
|
||||
<Box
|
||||
ref={SelectRef}
|
||||
position={'relative'}
|
||||
onClick={() => {
|
||||
isOpen ? onClose() : onOpen();
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
ref={ref}
|
||||
width={width}
|
||||
px={3}
|
||||
variant={'base'}
|
||||
display={'flex'}
|
||||
alignItems={'center'}
|
||||
justifyContent={'space-between'}
|
||||
_active={{
|
||||
transform: ''
|
||||
}}
|
||||
{...(isOpen
|
||||
? {
|
||||
boxShadow: '0px 0px 4px #A8DBFF',
|
||||
borderColor: 'myBlue.600'
|
||||
}
|
||||
: {})}
|
||||
{...props}
|
||||
>
|
||||
{list.find((item) => item.value === value)?.label || placeholder}
|
||||
<Box flex={1} />
|
||||
<ChevronDownIcon />
|
||||
</Button>
|
||||
|
||||
<MenuList
|
||||
minW={(() => {
|
||||
const w = ref.current?.clientWidth;
|
||||
if (w) {
|
||||
return `${w}px !important`;
|
||||
}
|
||||
return Array.isArray(width)
|
||||
? width.map((item) => `${item} !important`)
|
||||
: `${width} !important`;
|
||||
})()}
|
||||
p={'6px'}
|
||||
border={'1px solid #fff'}
|
||||
boxShadow={
|
||||
'0px 2px 4px rgba(161, 167, 179, 0.25), 0px 0px 1px rgba(121, 141, 159, 0.25);'
|
||||
}
|
||||
zIndex={99}
|
||||
transform={'translateY(35px) !important'}
|
||||
maxH={'40vh'}
|
||||
overflowY={'auto'}
|
||||
>
|
||||
{list.map((item) => (
|
||||
<MenuItem
|
||||
key={item.value}
|
||||
{...menuItemStyles}
|
||||
{...(value === item.value
|
||||
? {
|
||||
color: 'myBlue.600'
|
||||
}
|
||||
: {})}
|
||||
onClick={() => {
|
||||
if (onchange && value !== item.value) {
|
||||
onchange(item.value);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{item.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</MenuList>
|
||||
</Box>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(forwardRef(MySelect));
|
||||
@@ -1,18 +0,0 @@
|
||||
/* app */
|
||||
export enum SystemInputEnum {
|
||||
'welcomeText' = 'welcomeText',
|
||||
'variables' = 'variables',
|
||||
'switch' = 'switch', // a trigger switch
|
||||
'history' = 'history',
|
||||
'userChatInput' = 'userChatInput'
|
||||
}
|
||||
|
||||
export enum VariableInputEnum {
|
||||
input = 'input',
|
||||
select = 'select'
|
||||
}
|
||||
|
||||
export enum AppTypeEnum {
|
||||
basic = 'basic',
|
||||
advanced = 'advanced'
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
export enum sseResponseEventEnum {
|
||||
error = 'error',
|
||||
answer = 'answer',
|
||||
moduleStatus = 'moduleStatus',
|
||||
appStreamResponse = 'appStreamResponse' // sse response request
|
||||
}
|
||||
|
||||
export enum ChatRoleEnum {
|
||||
System = 'System',
|
||||
Human = 'Human',
|
||||
AI = 'AI'
|
||||
}
|
||||
|
||||
export enum TaskResponseKeyEnum {
|
||||
'answerText' = 'answerText', // answer module text key
|
||||
'responseData' = 'responseData'
|
||||
}
|
||||
|
||||
export const ChatRoleMap = {
|
||||
[ChatRoleEnum.System]: {
|
||||
name: '系统提示词'
|
||||
},
|
||||
[ChatRoleEnum.Human]: {
|
||||
name: '用户'
|
||||
},
|
||||
[ChatRoleEnum.AI]: {
|
||||
name: 'AI'
|
||||
}
|
||||
};
|
||||
|
||||
export enum ChatSourceEnum {
|
||||
test = 'test',
|
||||
online = 'online',
|
||||
share = 'share',
|
||||
api = 'api'
|
||||
}
|
||||
|
||||
export const ChatSourceMap = {
|
||||
[ChatSourceEnum.test]: {
|
||||
name: 'chat.logs.test'
|
||||
},
|
||||
[ChatSourceEnum.online]: {
|
||||
name: 'chat.logs.online'
|
||||
},
|
||||
[ChatSourceEnum.share]: {
|
||||
name: 'chat.logs.share'
|
||||
},
|
||||
[ChatSourceEnum.api]: {
|
||||
name: 'chat.logs.api'
|
||||
}
|
||||
};
|
||||
|
||||
export enum ChatModuleEnum {
|
||||
'AIChat' = 'AI Chat',
|
||||
'KBSearch' = 'KB Search',
|
||||
'CQ' = 'Classify Question',
|
||||
'Extract' = 'Content Extract',
|
||||
'Http' = 'Http'
|
||||
}
|
||||
|
||||
export enum OutLinkTypeEnum {
|
||||
'share' = 'share',
|
||||
'iframe' = 'iframe'
|
||||
}
|
||||
|
||||
export const HUMAN_ICON = `/icon/human.png`;
|
||||
export const LOGO_ICON = `/icon/logo.svg`;
|
||||
@@ -1,14 +0,0 @@
|
||||
export enum ContextExtractEnum {
|
||||
extractKeys = 'extractKeys',
|
||||
content = 'content',
|
||||
description = 'description',
|
||||
success = 'success',
|
||||
failed = 'failed',
|
||||
fields = 'fields'
|
||||
}
|
||||
|
||||
export enum HttpPropsEnum {
|
||||
url = 'url',
|
||||
failed = 'failed',
|
||||
finish = 'finish'
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
import type { BoxProps } from '@chakra-ui/react';
|
||||
|
||||
export enum FlowInputItemTypeEnum {
|
||||
systemInput = 'systemInput', // history, userChatInput, variableInput
|
||||
input = 'input',
|
||||
textarea = 'textarea',
|
||||
numberInput = 'numberInput',
|
||||
select = 'select',
|
||||
slider = 'slider',
|
||||
custom = 'custom',
|
||||
target = 'target',
|
||||
none = 'none',
|
||||
hidden = 'hidden'
|
||||
}
|
||||
|
||||
export enum FlowOutputItemTypeEnum {
|
||||
answer = 'answer',
|
||||
source = 'source',
|
||||
none = 'none',
|
||||
hidden = 'hidden'
|
||||
}
|
||||
|
||||
export enum FlowModuleTypeEnum {
|
||||
empty = 'empty',
|
||||
variable = 'variable',
|
||||
userGuide = 'userGuide',
|
||||
questionInput = 'questionInput',
|
||||
historyNode = 'historyNode',
|
||||
chatNode = 'chatNode',
|
||||
kbSearchNode = 'kbSearchNode',
|
||||
tfSwitchNode = 'tfSwitchNode',
|
||||
answerNode = 'answerNode',
|
||||
classifyQuestion = 'classifyQuestion',
|
||||
contentExtract = 'contentExtract',
|
||||
httpRequest = 'httpRequest'
|
||||
}
|
||||
|
||||
export enum SpecialInputKeyEnum {
|
||||
'answerText' = 'text',
|
||||
'agents' = 'agents' // cq agent key
|
||||
}
|
||||
|
||||
export enum FlowValueTypeEnum {
|
||||
'string' = 'string',
|
||||
'number' = 'number',
|
||||
'boolean' = 'boolean',
|
||||
'chatHistory' = 'chat_history',
|
||||
'kbQuote' = 'kb_quote',
|
||||
'any' = 'any'
|
||||
}
|
||||
|
||||
export const FlowValueTypeStyle: Record<`${FlowValueTypeEnum}`, BoxProps> = {
|
||||
[FlowValueTypeEnum.string]: {
|
||||
background: '#36ADEF'
|
||||
},
|
||||
[FlowValueTypeEnum.number]: {
|
||||
background: '#FB7C3C'
|
||||
},
|
||||
[FlowValueTypeEnum.boolean]: {
|
||||
background: '#E7D118'
|
||||
},
|
||||
[FlowValueTypeEnum.chatHistory]: {
|
||||
background: '#00A9A6'
|
||||
},
|
||||
[FlowValueTypeEnum.kbQuote]: {
|
||||
background: '#A558C9'
|
||||
},
|
||||
[FlowValueTypeEnum.any]: {
|
||||
background: '#9CA2A8'
|
||||
}
|
||||
};
|
||||
|
||||
export const initModuleType: Record<string, boolean> = {
|
||||
[FlowModuleTypeEnum.historyNode]: true,
|
||||
[FlowModuleTypeEnum.questionInput]: true
|
||||
};
|
||||
|
||||
export const edgeOptions = {
|
||||
style: {
|
||||
strokeWidth: 1.5,
|
||||
stroke: '#5A646Es'
|
||||
}
|
||||
};
|
||||
export const connectionLineStyle = { strokeWidth: 1.5, stroke: '#5A646Es' };
|
||||
@@ -1,25 +0,0 @@
|
||||
import { FlowInputItemType } from '@/types/flow';
|
||||
import { SystemInputEnum } from '../app';
|
||||
import { FlowInputItemTypeEnum, FlowValueTypeEnum } from './index';
|
||||
|
||||
export const Input_Template_TFSwitch: FlowInputItemType = {
|
||||
key: SystemInputEnum.switch,
|
||||
type: FlowInputItemTypeEnum.target,
|
||||
label: '触发器',
|
||||
valueType: FlowValueTypeEnum.any
|
||||
};
|
||||
|
||||
export const Input_Template_History: FlowInputItemType = {
|
||||
key: SystemInputEnum.history,
|
||||
type: FlowInputItemTypeEnum.target,
|
||||
label: '聊天记录',
|
||||
valueType: FlowValueTypeEnum.chatHistory
|
||||
};
|
||||
|
||||
export const Input_Template_UserChatInput: FlowInputItemType = {
|
||||
key: SystemInputEnum.userChatInput,
|
||||
type: FlowInputItemTypeEnum.target,
|
||||
label: '用户问题',
|
||||
required: true,
|
||||
valueType: FlowValueTypeEnum.string
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
import type { KbItemType } from '@/types/plugin';
|
||||
|
||||
export const defaultKbDetail: KbItemType = {
|
||||
_id: '',
|
||||
userId: '',
|
||||
avatar: '/icon/logo.svg',
|
||||
name: '',
|
||||
tags: '',
|
||||
vectorModel: {
|
||||
model: 'text-embedding-ada-002',
|
||||
name: 'Embedding-2',
|
||||
price: 0.2,
|
||||
defaultToken: 500,
|
||||
maxToken: 3000
|
||||
}
|
||||
};
|
||||
|
||||
export enum KbTypeEnum {
|
||||
folder = 'folder',
|
||||
dataset = 'dataset'
|
||||
}
|
||||
export enum FileStatusEnum {
|
||||
embedding = 'embedding',
|
||||
ready = 'ready'
|
||||
}
|
||||
|
||||
export const KbTypeMap = {
|
||||
[KbTypeEnum.folder]: {
|
||||
name: 'folder'
|
||||
},
|
||||
[KbTypeEnum.dataset]: {
|
||||
name: 'dataset'
|
||||
}
|
||||
};
|
||||
|
||||
export const FolderAvatarSrc = '/imgs/files/folder.svg';
|
||||
export const OtherFileId = 'other';
|
||||
@@ -1,27 +0,0 @@
|
||||
import type { AppSchema } from '@/types/mongoSchema';
|
||||
import type { OutLinkEditType } from '@/types/support/outLink';
|
||||
|
||||
export const defaultApp: AppSchema = {
|
||||
_id: '',
|
||||
userId: 'userId',
|
||||
name: '模型加载中',
|
||||
type: 'basic',
|
||||
avatar: '/icon/logo.svg',
|
||||
intro: '',
|
||||
updateTime: Date.now(),
|
||||
share: {
|
||||
isShare: false,
|
||||
isShareDetail: false,
|
||||
collection: 0
|
||||
},
|
||||
modules: []
|
||||
};
|
||||
|
||||
export const defaultOutLinkForm: OutLinkEditType = {
|
||||
name: '',
|
||||
responseDetail: false,
|
||||
limit: {
|
||||
QPM: 100,
|
||||
credit: -1
|
||||
}
|
||||
};
|
||||
@@ -1,10 +0,0 @@
|
||||
export enum TrainingModeEnum {
|
||||
'qa' = 'qa',
|
||||
'index' = 'index'
|
||||
}
|
||||
export const TrainingTypeMap = {
|
||||
[TrainingModeEnum.qa]: 'qa',
|
||||
[TrainingModeEnum.index]: 'index'
|
||||
};
|
||||
|
||||
export const PgDatasetTableName = 'modeldata';
|
||||
@@ -1,304 +0,0 @@
|
||||
import { extendTheme, defineStyleConfig, ComponentStyleConfig } from '@chakra-ui/react';
|
||||
// @ts-ignore
|
||||
import { modalAnatomy, switchAnatomy, selectAnatomy, numberInputAnatomy } from '@chakra-ui/anatomy';
|
||||
// @ts-ignore
|
||||
import { createMultiStyleConfigHelpers } from '@chakra-ui/styled-system';
|
||||
|
||||
const { definePartsStyle, defineMultiStyleConfig } = createMultiStyleConfigHelpers(
|
||||
modalAnatomy.keys
|
||||
);
|
||||
const { definePartsStyle: switchPart, defineMultiStyleConfig: switchMultiStyle } =
|
||||
createMultiStyleConfigHelpers(switchAnatomy.keys);
|
||||
const { definePartsStyle: selectPart, defineMultiStyleConfig: selectMultiStyle } =
|
||||
createMultiStyleConfigHelpers(selectAnatomy.keys);
|
||||
const { definePartsStyle: numInputPart, defineMultiStyleConfig: numInputMultiStyle } =
|
||||
createMultiStyleConfigHelpers(numberInputAnatomy.keys);
|
||||
|
||||
// 按键
|
||||
const Button = defineStyleConfig({
|
||||
baseStyle: {
|
||||
_active: {
|
||||
transform: 'scale(0.98)'
|
||||
}
|
||||
},
|
||||
sizes: {
|
||||
xs: {
|
||||
fontSize: 'xs',
|
||||
px: 3,
|
||||
py: 0,
|
||||
fontWeight: 'normal',
|
||||
height: '22px',
|
||||
borderRadius: '2px'
|
||||
},
|
||||
sm: {
|
||||
fontSize: 'sm',
|
||||
px: 4,
|
||||
py: 0,
|
||||
fontWeight: 'normal',
|
||||
height: '26px',
|
||||
borderRadius: '2px'
|
||||
},
|
||||
md: {
|
||||
fontSize: 'md',
|
||||
px: 6,
|
||||
py: 0,
|
||||
height: '32px',
|
||||
fontWeight: 'normal',
|
||||
borderRadius: '4px'
|
||||
},
|
||||
lg: {
|
||||
fontSize: 'lg',
|
||||
px: 8,
|
||||
py: 0,
|
||||
height: '42px',
|
||||
fontWeight: 'normal',
|
||||
borderRadius: '8px'
|
||||
}
|
||||
},
|
||||
variants: {
|
||||
primary: {
|
||||
backgroundImage:
|
||||
'linear-gradient(to bottom right, #2152d9 0%,#3370ff 40%, #4e83fd 100%) !important',
|
||||
color: 'white',
|
||||
border: 'none',
|
||||
_hover: {
|
||||
filter: 'brightness(115%)'
|
||||
},
|
||||
_disabled: {
|
||||
bg: '#3370ff !important'
|
||||
}
|
||||
},
|
||||
base: {
|
||||
color: 'myGray.900',
|
||||
border: '1px solid',
|
||||
borderColor: 'myGray.200',
|
||||
bg: 'transparent',
|
||||
transition: 'background 0.3s',
|
||||
_hover: {
|
||||
color: 'myBlue.600',
|
||||
bg: 'myWhite.400'
|
||||
},
|
||||
_active: {
|
||||
color: 'myBlue.700'
|
||||
},
|
||||
_disabled: { bg: 'myGray.100 !important', color: 'myGray.700 !important' }
|
||||
}
|
||||
},
|
||||
defaultProps: {
|
||||
size: 'md',
|
||||
variant: 'primary'
|
||||
}
|
||||
});
|
||||
|
||||
const Input: ComponentStyleConfig = {
|
||||
baseStyle: {},
|
||||
variants: {
|
||||
outline: {
|
||||
field: {
|
||||
backgroundColor: 'transparent',
|
||||
border: '1px solid',
|
||||
borderRadius: 'base',
|
||||
borderColor: 'myGray.200',
|
||||
_focus: {
|
||||
borderColor: 'myBlue.600',
|
||||
boxShadow: '0px 0px 4px #A8DBFF',
|
||||
bg: 'white'
|
||||
},
|
||||
_disabled: {
|
||||
color: 'myGray.400',
|
||||
bg: 'myWhite.300'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
defaultProps: {
|
||||
size: 'md',
|
||||
variant: 'outline'
|
||||
}
|
||||
};
|
||||
|
||||
const NumberInput = numInputMultiStyle({
|
||||
variants: {
|
||||
outline: numInputPart({
|
||||
field: {
|
||||
bg: 'myWhite.300',
|
||||
border: '1px solid',
|
||||
borderRadius: 'base',
|
||||
borderColor: 'myGray.200',
|
||||
_focus: {
|
||||
borderColor: 'myBlue.600 !important',
|
||||
boxShadow: '0px 0px 4px #A8DBFF !important',
|
||||
bg: 'transparent'
|
||||
},
|
||||
_disabled: {
|
||||
color: 'myGray.400 !important',
|
||||
bg: 'myWhite.300 !important'
|
||||
}
|
||||
},
|
||||
stepper: {
|
||||
bg: 'transparent',
|
||||
border: 'none',
|
||||
color: 'myGray.600',
|
||||
_active: {
|
||||
color: 'myBlue.600'
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
defaultProps: {
|
||||
variant: 'outline'
|
||||
}
|
||||
});
|
||||
|
||||
const Textarea: ComponentStyleConfig = {
|
||||
variants: {
|
||||
outline: {
|
||||
border: '1px solid',
|
||||
borderRadius: 'base',
|
||||
borderColor: 'myGray.200',
|
||||
_hover: {
|
||||
borderColor: ''
|
||||
},
|
||||
_focus: {
|
||||
borderColor: 'myBlue.600',
|
||||
boxShadow: '0px 0px 4px #A8DBFF',
|
||||
bg: 'white'
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
defaultProps: {
|
||||
size: 'md',
|
||||
variant: 'outline'
|
||||
}
|
||||
};
|
||||
|
||||
const Switch = switchMultiStyle({
|
||||
baseStyle: switchPart({
|
||||
track: {
|
||||
bg: 'myGray.100',
|
||||
_checked: {
|
||||
bg: 'myBlue.700'
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const Select = selectMultiStyle({
|
||||
variants: {
|
||||
outline: selectPart({
|
||||
field: {
|
||||
borderColor: 'myGray.100',
|
||||
|
||||
_focusWithin: {
|
||||
boxShadow: '0px 0px 4px #A8DBFF',
|
||||
borderColor: 'myBlue.600'
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
// 全局主题
|
||||
export const theme = extendTheme({
|
||||
styles: {
|
||||
global: {
|
||||
'html, body': {
|
||||
color: 'myGray.900',
|
||||
fontSize: 'md',
|
||||
fontWeight: 400,
|
||||
height: '100%',
|
||||
overflow: 'hidden'
|
||||
},
|
||||
a: {
|
||||
color: 'myBlue.700'
|
||||
}
|
||||
}
|
||||
},
|
||||
colors: {
|
||||
myWhite: {
|
||||
100: '#FEFEFE',
|
||||
200: '#FDFDFE',
|
||||
300: '#FBFBFC',
|
||||
400: '#F8FAFB',
|
||||
500: '#F6F8F9',
|
||||
600: '#F4F6F8',
|
||||
700: '#C3C5C6',
|
||||
800: '#929495',
|
||||
900: '#626263',
|
||||
1000: '#313132'
|
||||
},
|
||||
myGray: {
|
||||
100: '#EFF0F1',
|
||||
200: '#DEE0E2',
|
||||
300: '#BDC1C5',
|
||||
400: '#9CA2A8',
|
||||
500: '#7B838B',
|
||||
600: '#5A646E',
|
||||
700: '#485058',
|
||||
800: '#363C42',
|
||||
900: '#24282C',
|
||||
1000: '#121416'
|
||||
},
|
||||
myBlue: {
|
||||
100: '#f0f7ff',
|
||||
200: '#EBF7FD',
|
||||
300: '#d6e8ff',
|
||||
400: '#adceff',
|
||||
500: '#85b1ff',
|
||||
600: '#4e83fd',
|
||||
700: '#3370ff',
|
||||
800: '#2152d9',
|
||||
900: '#1237b3',
|
||||
1000: '#07228c'
|
||||
},
|
||||
myRead: {
|
||||
600: '#ff4d4f'
|
||||
}
|
||||
},
|
||||
fonts: {
|
||||
body: '-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"'
|
||||
},
|
||||
fontSizes: {
|
||||
xs: '10px',
|
||||
sm: '12px',
|
||||
md: '14px',
|
||||
lg: '16px',
|
||||
xl: '16px',
|
||||
'2xl': '18px',
|
||||
'3xl': '20px'
|
||||
},
|
||||
borders: {
|
||||
sm: '1px solid #EFF0F1',
|
||||
base: '1px solid #DEE0E2',
|
||||
md: '1px solid #DAE0E2',
|
||||
lg: '1px solid #D0E0E2'
|
||||
},
|
||||
shadows: {
|
||||
sm: '0 0 5px rgba(0,0,0,0.1)',
|
||||
md: '0 0 8px rgba(0,0,0,0.1)',
|
||||
base: '0 0 10px rgba(0,0,0,0.15)',
|
||||
lg: '0 0 10px rgba(0,0,0,0.2)'
|
||||
},
|
||||
breakpoints: {
|
||||
sm: '900px',
|
||||
md: '1200px',
|
||||
lg: '1500px',
|
||||
xl: '1800px',
|
||||
'2xl': '2100px'
|
||||
},
|
||||
lgColor: {
|
||||
activeBlueGradient: 'linear-gradient(to bottom right, #d6e8ff 0%, #f0f7ff 100%)',
|
||||
hoverBlueGradient: 'linear-gradient(to top left, #d6e8ff 0%, #f0f7ff 100%)',
|
||||
primary: 'linear-gradient(to bottom right, #2152d9 0%,#3370ff 40%, #4e83fd 100%)',
|
||||
primary2: 'linear-gradient(to bottom right, #2152d9 0%,#3370ff 30%,#4e83fd 80%, #85b1ff 100%)'
|
||||
},
|
||||
components: {
|
||||
Button,
|
||||
Input,
|
||||
Textarea,
|
||||
Switch,
|
||||
Select,
|
||||
NumberInput
|
||||
}
|
||||
});
|
||||
@@ -1,40 +0,0 @@
|
||||
export enum OAuthEnum {
|
||||
github = 'github',
|
||||
google = 'google'
|
||||
}
|
||||
export enum BillSourceEnum {
|
||||
fastgpt = 'fastgpt',
|
||||
api = 'api',
|
||||
shareLink = 'shareLink'
|
||||
}
|
||||
export enum PageTypeEnum {
|
||||
login = 'login',
|
||||
register = 'register',
|
||||
forgetPassword = 'forgetPassword'
|
||||
}
|
||||
|
||||
export const BillSourceMap: Record<`${BillSourceEnum}`, string> = {
|
||||
[BillSourceEnum.fastgpt]: 'FastGPT 平台',
|
||||
[BillSourceEnum.api]: 'Api',
|
||||
[BillSourceEnum.shareLink]: '免登录链接'
|
||||
};
|
||||
|
||||
export enum PromotionEnum {
|
||||
register = 'register',
|
||||
pay = 'pay'
|
||||
}
|
||||
|
||||
export enum InformTypeEnum {
|
||||
system = 'system'
|
||||
}
|
||||
|
||||
export const InformTypeMap = {
|
||||
[InformTypeEnum.system]: {
|
||||
label: '系统通知'
|
||||
}
|
||||
};
|
||||
|
||||
export enum MyModelsTypeEnum {
|
||||
my = 'my',
|
||||
collection = 'collection'
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogBody,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogContent,
|
||||
AlertDialogOverlay,
|
||||
useDisclosure,
|
||||
Button
|
||||
} from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
|
||||
export const useConfirm = (props: { title?: string | null; content?: string | null }) => {
|
||||
const { t } = useTranslation();
|
||||
const { title = t('Warning'), content } = props;
|
||||
const [customContent, setCustomContent] = useState(content);
|
||||
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
const cancelRef = useRef(null);
|
||||
const confirmCb = useRef<any>();
|
||||
const cancelCb = useRef<any>();
|
||||
|
||||
return {
|
||||
openConfirm: useCallback(
|
||||
(confirm?: any, cancel?: any, customContent?: string) => {
|
||||
confirmCb.current = confirm;
|
||||
cancelCb.current = cancel;
|
||||
|
||||
customContent && setCustomContent(customContent);
|
||||
|
||||
return onOpen;
|
||||
},
|
||||
[onOpen]
|
||||
),
|
||||
ConfirmModal: useCallback(
|
||||
() => (
|
||||
<AlertDialog
|
||||
isOpen={isOpen}
|
||||
leastDestructiveRef={cancelRef}
|
||||
autoFocus={false}
|
||||
onClose={onClose}
|
||||
>
|
||||
<AlertDialogOverlay>
|
||||
<AlertDialogContent maxW={'min(90vw,400px)'}>
|
||||
<AlertDialogHeader fontSize="lg" fontWeight="bold">
|
||||
{title}
|
||||
</AlertDialogHeader>
|
||||
|
||||
<AlertDialogBody>{customContent}</AlertDialogBody>
|
||||
|
||||
<AlertDialogFooter>
|
||||
<Button
|
||||
variant={'base'}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
typeof cancelCb.current === 'function' && cancelCb.current();
|
||||
}}
|
||||
>
|
||||
{t('Cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
ml={4}
|
||||
onClick={() => {
|
||||
onClose();
|
||||
typeof confirmCb.current === 'function' && confirmCb.current();
|
||||
}}
|
||||
>
|
||||
{t('Confirm')}
|
||||
</Button>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialogOverlay>
|
||||
</AlertDialog>
|
||||
),
|
||||
[customContent, isOpen, onClose, t, title]
|
||||
)
|
||||
};
|
||||
};
|
||||
@@ -1,58 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await authUser({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
|
||||
const { limit = 1000 } = req.body as { limit: number };
|
||||
let skip = 0;
|
||||
const total = await Chat.countDocuments({
|
||||
chatId: { $exists: false }
|
||||
});
|
||||
let promise = Promise.resolve();
|
||||
console.log(total);
|
||||
|
||||
for (let i = 0; i < total; i += limit) {
|
||||
const skipVal = skip;
|
||||
skip += limit;
|
||||
promise = promise
|
||||
.then(() => init(limit, skipVal))
|
||||
.then(() => {
|
||||
console.log(skipVal);
|
||||
});
|
||||
}
|
||||
|
||||
await promise;
|
||||
|
||||
jsonRes(res, {});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function init(limit: number, skip: number) {
|
||||
// 遍历 app
|
||||
const chats = await Chat.find(
|
||||
{
|
||||
chatId: { $exists: false }
|
||||
},
|
||||
'_id'
|
||||
).limit(limit);
|
||||
|
||||
await Promise.all(
|
||||
chats.map((chat) =>
|
||||
Chat.findByIdAndUpdate(chat._id, {
|
||||
chatId: String(chat._id),
|
||||
source: 'online'
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { connectToDatabase, Chat, ChatItem } from '@/service/mongo';
|
||||
import { customAlphabet } from 'nanoid';
|
||||
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await authUser({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
|
||||
const { limit = 100 } = req.body as { limit: number };
|
||||
let skip = 0;
|
||||
|
||||
const total = await Chat.countDocuments({
|
||||
content: { $exists: true, $not: { $size: 0 } },
|
||||
isInit: { $ne: true }
|
||||
});
|
||||
const totalChat = await Chat.aggregate([
|
||||
{
|
||||
$project: {
|
||||
contentLength: { $size: '$content' }
|
||||
}
|
||||
},
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
totalLength: { $sum: '$contentLength' }
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
console.log('chatLen:', total, totalChat);
|
||||
|
||||
let promise = Promise.resolve();
|
||||
|
||||
for (let i = 0; i < total; i += limit) {
|
||||
const skipVal = skip;
|
||||
skip += limit;
|
||||
promise = promise
|
||||
.then(() => init(limit))
|
||||
.then(() => {
|
||||
console.log(skipVal);
|
||||
});
|
||||
}
|
||||
|
||||
await promise;
|
||||
|
||||
jsonRes(res, {});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function init(limit: number) {
|
||||
// 遍历 app
|
||||
const chats = await Chat.find(
|
||||
{
|
||||
content: { $exists: true, $not: { $size: 0 } },
|
||||
isInit: { $ne: true }
|
||||
},
|
||||
'_id userId appId chatId content'
|
||||
)
|
||||
.sort({ updateTime: -1 })
|
||||
.limit(limit);
|
||||
|
||||
await Promise.all(
|
||||
chats.map(async (chat) => {
|
||||
const inserts = chat.content
|
||||
.map((item) => ({
|
||||
dataId: nanoid(),
|
||||
chatId: chat.chatId,
|
||||
userId: chat.userId,
|
||||
appId: chat.appId,
|
||||
obj: item.obj,
|
||||
value: item.value,
|
||||
responseData: item.responseData
|
||||
}))
|
||||
.filter((item) => item.chatId && item.userId && item.appId && item.obj && item.value);
|
||||
|
||||
try {
|
||||
await Promise.all(inserts.map((item) => ChatItem.create(item)));
|
||||
await Chat.findByIdAndUpdate(chat._id, {
|
||||
isInit: true
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
|
||||
await ChatItem.deleteMany({ chatId: chat.chatId });
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { connectToDatabase, OutLink } from '@/service/mongo';
|
||||
import { OutLinkTypeEnum } from '@/constants/chat';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await authUser({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
|
||||
await OutLink.updateMany(
|
||||
{},
|
||||
{
|
||||
$set: { type: OutLinkTypeEnum.share }
|
||||
}
|
||||
);
|
||||
|
||||
jsonRes(res, {});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,446 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { connectToDatabase, App } from '@/service/mongo';
|
||||
import { FlowModuleTypeEnum, SpecialInputKeyEnum } from '@/constants/flow';
|
||||
import { TaskResponseKeyEnum } from '@/constants/chat';
|
||||
import { FlowInputItemType } from '@/types/flow';
|
||||
|
||||
const chatModelInput = ({
|
||||
model,
|
||||
temperature,
|
||||
maxToken,
|
||||
systemPrompt,
|
||||
limitPrompt,
|
||||
kbList
|
||||
}: {
|
||||
model: string;
|
||||
temperature: number;
|
||||
maxToken: number;
|
||||
systemPrompt: string;
|
||||
limitPrompt: string;
|
||||
kbList: { kbId: string }[];
|
||||
}): FlowInputItemType[] => [
|
||||
{
|
||||
key: 'model',
|
||||
value: model,
|
||||
type: 'custom',
|
||||
label: '对话模型',
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'temperature',
|
||||
value: temperature,
|
||||
label: '温度',
|
||||
type: 'slider',
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'maxToken',
|
||||
value: maxToken,
|
||||
type: 'custom',
|
||||
label: '回复上限',
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'systemPrompt',
|
||||
value: systemPrompt,
|
||||
type: 'textarea',
|
||||
label: '系统提示词',
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'limitPrompt',
|
||||
label: '限定词',
|
||||
type: 'textarea',
|
||||
value: limitPrompt,
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'switch',
|
||||
type: 'target',
|
||||
label: '触发器',
|
||||
connected: kbList.length > 0
|
||||
},
|
||||
{
|
||||
key: 'quoteQA',
|
||||
type: 'target',
|
||||
label: '引用内容',
|
||||
connected: kbList.length > 0
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
type: 'target',
|
||||
label: '聊天记录',
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
type: 'target',
|
||||
label: '用户问题',
|
||||
connected: true
|
||||
}
|
||||
];
|
||||
const chatTemplate = ({
|
||||
model,
|
||||
temperature,
|
||||
maxToken,
|
||||
systemPrompt,
|
||||
limitPrompt
|
||||
}: {
|
||||
model: string;
|
||||
temperature: number;
|
||||
maxToken: number;
|
||||
systemPrompt: string;
|
||||
limitPrompt: string;
|
||||
}) => {
|
||||
return [
|
||||
{
|
||||
flowType: FlowModuleTypeEnum.questionInput,
|
||||
inputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
position: {
|
||||
x: 464.32198615344566,
|
||||
y: 1602.2698463081606
|
||||
},
|
||||
moduleId: 'userChatInput'
|
||||
},
|
||||
{
|
||||
flowType: FlowModuleTypeEnum.historyNode,
|
||||
inputs: [
|
||||
{
|
||||
key: 'maxContext',
|
||||
value: 10,
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'history',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'history'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
position: {
|
||||
x: 452.5466249541586,
|
||||
y: 1276.3930310334215
|
||||
},
|
||||
moduleId: 'history'
|
||||
},
|
||||
{
|
||||
flowType: FlowModuleTypeEnum.chatNode,
|
||||
inputs: chatModelInput({
|
||||
model,
|
||||
temperature,
|
||||
maxToken,
|
||||
systemPrompt,
|
||||
limitPrompt,
|
||||
kbList: []
|
||||
}),
|
||||
outputs: [
|
||||
{
|
||||
key: TaskResponseKeyEnum.answerText,
|
||||
targets: []
|
||||
}
|
||||
],
|
||||
position: {
|
||||
x: 981.9682828103937,
|
||||
y: 890.014595014464
|
||||
},
|
||||
moduleId: 'chatModule'
|
||||
}
|
||||
];
|
||||
};
|
||||
const kbTemplate = ({
|
||||
model,
|
||||
temperature,
|
||||
maxToken,
|
||||
systemPrompt,
|
||||
limitPrompt,
|
||||
kbList = [],
|
||||
searchSimilarity,
|
||||
searchLimit,
|
||||
searchEmptyText
|
||||
}: {
|
||||
model: string;
|
||||
temperature: number;
|
||||
maxToken: number;
|
||||
systemPrompt: string;
|
||||
limitPrompt: string;
|
||||
kbList: { kbId: string }[];
|
||||
searchSimilarity: number;
|
||||
searchLimit: number;
|
||||
searchEmptyText: string;
|
||||
}) => {
|
||||
return [
|
||||
{
|
||||
flowType: FlowModuleTypeEnum.questionInput,
|
||||
inputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'userChatInput',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'userChatInput'
|
||||
},
|
||||
{
|
||||
moduleId: 'kbSearch',
|
||||
key: 'userChatInput'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
position: {
|
||||
x: 464.32198615344566,
|
||||
y: 1602.2698463081606
|
||||
},
|
||||
moduleId: 'userChatInput'
|
||||
},
|
||||
{
|
||||
flowType: FlowModuleTypeEnum.historyNode,
|
||||
inputs: [
|
||||
{
|
||||
key: 'maxContext',
|
||||
value: 10,
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'history',
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'history',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'history'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
position: {
|
||||
x: 452.5466249541586,
|
||||
y: 1276.3930310334215
|
||||
},
|
||||
moduleId: 'history'
|
||||
},
|
||||
{
|
||||
flowType: FlowModuleTypeEnum.kbSearchNode,
|
||||
inputs: [
|
||||
{
|
||||
key: 'kbList',
|
||||
value: kbList,
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'similarity',
|
||||
value: searchSimilarity,
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'limit',
|
||||
value: searchLimit,
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: 'switch',
|
||||
connected: false
|
||||
},
|
||||
{
|
||||
key: 'userChatInput',
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [
|
||||
{
|
||||
key: 'isEmpty',
|
||||
targets: searchEmptyText
|
||||
? [
|
||||
{
|
||||
moduleId: 'emptyText',
|
||||
key: 'switch'
|
||||
}
|
||||
]
|
||||
: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'switch'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'unEmpty',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'switch'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'quoteQA',
|
||||
targets: [
|
||||
{
|
||||
moduleId: 'chatModule',
|
||||
key: 'quoteQA'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
position: {
|
||||
x: 956.0838440206068,
|
||||
y: 887.462827870246
|
||||
},
|
||||
moduleId: 'kbSearch'
|
||||
},
|
||||
...(searchEmptyText
|
||||
? [
|
||||
{
|
||||
flowType: FlowModuleTypeEnum.answerNode,
|
||||
inputs: [
|
||||
{
|
||||
key: 'switch',
|
||||
connected: true
|
||||
},
|
||||
{
|
||||
key: SpecialInputKeyEnum.answerText,
|
||||
value: searchEmptyText,
|
||||
connected: true
|
||||
}
|
||||
],
|
||||
outputs: [],
|
||||
position: {
|
||||
x: 1553.5815811529146,
|
||||
y: 637.8753731306779
|
||||
},
|
||||
moduleId: 'emptyText'
|
||||
}
|
||||
]
|
||||
: []),
|
||||
{
|
||||
flowType: FlowModuleTypeEnum.chatNode,
|
||||
inputs: chatModelInput({ model, temperature, maxToken, systemPrompt, limitPrompt, kbList }),
|
||||
outputs: [
|
||||
{
|
||||
key: TaskResponseKeyEnum.answerText,
|
||||
targets: []
|
||||
}
|
||||
],
|
||||
position: {
|
||||
x: 1551.71405495818,
|
||||
y: 977.4911578918461
|
||||
},
|
||||
moduleId: 'chatModule'
|
||||
}
|
||||
];
|
||||
};
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await authUser({ req, authRoot: true });
|
||||
await connectToDatabase();
|
||||
|
||||
const { limit = 1000 } = req.body as { limit: number };
|
||||
let skip = 0;
|
||||
const total = await App.countDocuments();
|
||||
let promise = Promise.resolve();
|
||||
console.log(total);
|
||||
|
||||
for (let i = 0; i < total; i += limit) {
|
||||
const skipVal = skip;
|
||||
skip += limit;
|
||||
promise = promise
|
||||
.then(() => init(limit, skipVal))
|
||||
.then(() => {
|
||||
console.log(skipVal);
|
||||
});
|
||||
}
|
||||
|
||||
await promise;
|
||||
|
||||
jsonRes(res, {});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async function init(limit: number, skip: number) {
|
||||
// 遍历 app
|
||||
const apps = await App.find(
|
||||
{
|
||||
chat: { $ne: null },
|
||||
modules: { $exists: false }
|
||||
// userId: '63f9a14228d2a688d8dc9e1b'
|
||||
},
|
||||
'_id chat'
|
||||
).limit(limit);
|
||||
|
||||
return Promise.all(
|
||||
apps.map(async (app) => {
|
||||
if (!app.chat) return app;
|
||||
const modules = (() => {
|
||||
if (app.chat.relatedKbs.length === 0) {
|
||||
return chatTemplate({
|
||||
model: app.chat.chatModel,
|
||||
temperature: app.chat.temperature,
|
||||
maxToken: app.chat.maxToken,
|
||||
systemPrompt: app.chat.systemPrompt,
|
||||
limitPrompt: app.chat.limitPrompt
|
||||
});
|
||||
} else {
|
||||
return kbTemplate({
|
||||
model: app.chat.chatModel,
|
||||
temperature: app.chat.temperature,
|
||||
maxToken: app.chat.maxToken,
|
||||
systemPrompt: app.chat.systemPrompt,
|
||||
limitPrompt: app.chat.limitPrompt,
|
||||
kbList: app.chat.relatedKbs.map((id) => ({ kbId: id })),
|
||||
searchEmptyText: app.chat.searchEmptyText,
|
||||
searchLimit: app.chat.searchLimit,
|
||||
searchSimilarity: app.chat.searchSimilarity
|
||||
});
|
||||
}
|
||||
})();
|
||||
|
||||
await App.findByIdAndUpdate(app.id, {
|
||||
modules
|
||||
});
|
||||
return modules;
|
||||
})
|
||||
);
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { PgClient } from '@/service/pg';
|
||||
import { PgDatasetTableName } from '@/constants/plugin';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await authUser({ req, authRoot: true });
|
||||
|
||||
const { rowCount } = await PgClient.query(`SELECT 1
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = '${PgDatasetTableName}'
|
||||
AND column_name = 'file_id'`);
|
||||
|
||||
if (rowCount > 0) {
|
||||
return jsonRes(res, {
|
||||
data: '已经存在file_id字段'
|
||||
});
|
||||
}
|
||||
|
||||
jsonRes(res, {
|
||||
data: await PgClient.query(
|
||||
`ALTER TABLE ${PgDatasetTableName} ADD COLUMN file_id VARCHAR(100)`
|
||||
)
|
||||
});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { connectToDatabase, KB } from '@/service/mongo';
|
||||
import { KbTypeEnum } from '@/constants/kb';
|
||||
import { PgClient } from '@/service/pg';
|
||||
import { PgDatasetTableName } from '@/constants/plugin';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
await authUser({ req, authRoot: true });
|
||||
|
||||
await KB.updateMany(
|
||||
{
|
||||
type: { $exists: false }
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
type: KbTypeEnum.dataset,
|
||||
parentId: null
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const response = await PgClient.update(PgDatasetTableName, {
|
||||
where: [['file_id', 'undefined']],
|
||||
values: [{ key: 'file_id', value: '' }]
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: response.rowCount
|
||||
});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import mongoose from 'mongoose';
|
||||
import { PgClient } from '@/service/pg';
|
||||
import { PgDatasetTableName } from '@/constants/plugin';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
await authUser({ req, authRoot: true });
|
||||
|
||||
const data = await mongoose.connection.db
|
||||
.collection('dataset.files')
|
||||
.updateMany({}, { $set: { 'metadata.datasetUsed': true } });
|
||||
|
||||
// update pg data
|
||||
const pg = await PgClient.query(`UPDATE ${PgDatasetTableName}
|
||||
SET file_id = ''
|
||||
WHERE (file_id = 'undefined' OR LENGTH(file_id) < 20) AND file_id != '';`);
|
||||
|
||||
jsonRes(res, {
|
||||
data: {
|
||||
data,
|
||||
pg
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { App } from '@/service/models/app';
|
||||
import { AppModuleItemType } from '@/types/app';
|
||||
|
||||
export type Props = {
|
||||
name: string;
|
||||
avatar?: string;
|
||||
modules: AppModuleItemType[];
|
||||
};
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { name, avatar, modules } = req.body as Props;
|
||||
|
||||
if (!name || !Array.isArray(modules)) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
// 上限校验
|
||||
const authCount = await App.countDocuments({
|
||||
userId
|
||||
});
|
||||
if (authCount >= 50) {
|
||||
throw new Error('上限 50 个应用');
|
||||
}
|
||||
|
||||
// 创建模型
|
||||
const response = await App.create({
|
||||
avatar,
|
||||
name,
|
||||
userId,
|
||||
modules
|
||||
});
|
||||
|
||||
jsonRes(res, {
|
||||
data: response._id
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { Chat, App, connectToDatabase, Collection, OutLink } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { authApp } from '@/service/utils/auth';
|
||||
|
||||
/* 获取我的模型 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
||||
if (!appId) {
|
||||
throw new Error('参数错误');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
// 验证是否是该用户的 app
|
||||
await authApp({
|
||||
appId,
|
||||
userId
|
||||
});
|
||||
|
||||
// 删除对应的聊天
|
||||
await Chat.deleteMany({
|
||||
appId
|
||||
});
|
||||
|
||||
// 删除收藏列表
|
||||
await Collection.deleteMany({
|
||||
modelId: appId
|
||||
});
|
||||
|
||||
// 删除分享链接
|
||||
await OutLink.deleteMany({
|
||||
appId
|
||||
});
|
||||
|
||||
// 删除模型
|
||||
await App.deleteOne({
|
||||
_id: appId,
|
||||
userId
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, App } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { AppListItemType } from '@/types/app';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
// 凭证校验
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
// 根据 userId 获取模型信息
|
||||
const myApps = await App.find(
|
||||
{
|
||||
userId
|
||||
},
|
||||
'_id avatar name intro'
|
||||
).sort({
|
||||
updateTime: -1
|
||||
});
|
||||
|
||||
jsonRes<AppListItemType[]>(res, {
|
||||
data: myApps
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Collection, App } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
|
||||
/* 模型收藏切换 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
||||
if (!appId) {
|
||||
throw new Error('缺少参数');
|
||||
}
|
||||
// 凭证校验
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
const collectionRecord = await Collection.findOne({
|
||||
userId,
|
||||
modelId: appId
|
||||
});
|
||||
|
||||
if (collectionRecord) {
|
||||
await Collection.findByIdAndRemove(collectionRecord._id);
|
||||
} else {
|
||||
await Collection.create({
|
||||
userId,
|
||||
modelId: appId
|
||||
});
|
||||
}
|
||||
|
||||
await App.findByIdAndUpdate(appId, {
|
||||
'share.collection': await Collection.countDocuments({ modelId: appId })
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, App } from '@/service/mongo';
|
||||
import type { PagingData } from '@/types';
|
||||
import type { ShareAppItem } from '@/types/app';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { Types } from 'mongoose';
|
||||
|
||||
/* 获取模型列表 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const {
|
||||
searchText = '',
|
||||
pageNum = 1,
|
||||
pageSize = 20
|
||||
} = req.body as { searchText: string; pageNum: number; pageSize: number };
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
const regex = new RegExp(searchText, 'i');
|
||||
|
||||
const where = {
|
||||
$and: [
|
||||
{ 'share.isShare': true },
|
||||
{
|
||||
$or: [{ name: { $regex: regex } }, { intro: { $regex: regex } }]
|
||||
}
|
||||
]
|
||||
};
|
||||
const pipeline = [
|
||||
{
|
||||
$match: where
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: 'collections',
|
||||
let: { modelId: '$_id' },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: {
|
||||
$and: [
|
||||
{ $eq: ['$modelId', '$$modelId'] },
|
||||
{
|
||||
$eq: ['$userId', userId ? new Types.ObjectId(userId) : new Types.ObjectId()]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
as: 'collections'
|
||||
}
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 1,
|
||||
avatar: { $ifNull: ['$avatar', '/icon/logo.svg'] },
|
||||
name: 1,
|
||||
userId: 1,
|
||||
intro: 1,
|
||||
share: 1,
|
||||
isCollection: {
|
||||
$cond: {
|
||||
if: { $gt: [{ $size: '$collections' }, 0] },
|
||||
then: true,
|
||||
else: false
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
$sort: { 'share.topNum': -1, 'share.collection': -1 }
|
||||
},
|
||||
{
|
||||
$skip: (pageNum - 1) * pageSize
|
||||
},
|
||||
{
|
||||
$limit: pageSize
|
||||
}
|
||||
];
|
||||
|
||||
// 获取被分享的模型
|
||||
const [models, total] = await Promise.all([
|
||||
// @ts-ignore
|
||||
App.aggregate(pipeline),
|
||||
App.countDocuments(where)
|
||||
]);
|
||||
|
||||
jsonRes<PagingData<ShareAppItem>>(res, {
|
||||
data: {
|
||||
pageNum,
|
||||
pageSize,
|
||||
data: models,
|
||||
total
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { App } from '@/service/models/app';
|
||||
import type { AppUpdateParams } from '@/types/app';
|
||||
import { authApp } from '@/service/utils/auth';
|
||||
|
||||
/* 获取我的模型 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
const { name, avatar, type, chat, share, intro, modules } = req.body as AppUpdateParams;
|
||||
const { appId } = req.query as { appId: string };
|
||||
|
||||
if (!appId) {
|
||||
throw new Error('appId is empty');
|
||||
}
|
||||
|
||||
// 凭证校验
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
await authApp({
|
||||
appId,
|
||||
userId
|
||||
});
|
||||
|
||||
// 更新模型
|
||||
await App.updateOne(
|
||||
{
|
||||
_id: appId,
|
||||
userId
|
||||
},
|
||||
{
|
||||
name,
|
||||
type,
|
||||
avatar,
|
||||
intro,
|
||||
chat,
|
||||
...(share && {
|
||||
'share.isShare': share.isShare,
|
||||
'share.isShareDetail': share.isShareDetail
|
||||
}),
|
||||
...(modules && { modules })
|
||||
}
|
||||
);
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, ChatItem } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { chatId, contentId } = req.query as { chatId: string; contentId: string };
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
// 凭证校验
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
// 删除一条数据库记录
|
||||
await ChatItem.deleteOne({
|
||||
dataId: contentId,
|
||||
chatId,
|
||||
userId
|
||||
});
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, ChatItem } from '@/service/mongo';
|
||||
import { AdminUpdateFeedbackParams } from '@/api/request/chat';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
|
||||
/* 初始化我的聊天框,需要身份验证 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { chatItemId, kbId, dataId, content = undefined } = req.body as AdminUpdateFeedbackParams;
|
||||
|
||||
if (!chatItemId || !kbId || !dataId || !content) {
|
||||
throw new Error('missing parameter');
|
||||
}
|
||||
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
await ChatItem.findOneAndUpdate(
|
||||
{
|
||||
userId,
|
||||
dataId: chatItemId
|
||||
},
|
||||
{
|
||||
adminFeedback: {
|
||||
kbId,
|
||||
dataId,
|
||||
content
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, ChatItem } from '@/service/mongo';
|
||||
|
||||
/* 初始化我的聊天框,需要身份验证 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const { chatItemId, userFeedback = undefined } = req.body as {
|
||||
chatItemId: string;
|
||||
userFeedback?: string;
|
||||
};
|
||||
|
||||
if (!chatItemId) {
|
||||
throw new Error('chatItemId is required');
|
||||
}
|
||||
|
||||
await ChatItem.findOneAndUpdate(
|
||||
{
|
||||
dataId: chatItemId
|
||||
},
|
||||
{
|
||||
...(userFeedback ? { userFeedback } : { $unset: { userFeedback: '' } })
|
||||
}
|
||||
);
|
||||
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import type { ChatHistoryItemType } from '@/types/chat';
|
||||
import { ChatSourceEnum } from '@/constants/chat';
|
||||
|
||||
/* 获取历史记录 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { appId } = req.body as { appId?: string };
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
const data = await Chat.find(
|
||||
{
|
||||
userId,
|
||||
source: ChatSourceEnum.online,
|
||||
...(appId && { appId })
|
||||
},
|
||||
'chatId title top customTitle appId updateTime'
|
||||
)
|
||||
.sort({ top: -1, updateTime: -1 })
|
||||
.limit(20);
|
||||
|
||||
jsonRes<ChatHistoryItemType[]>(res, {
|
||||
data: data.map((item) => ({
|
||||
chatId: item.chatId,
|
||||
updateTime: item.updateTime,
|
||||
appId: item.appId,
|
||||
customTitle: item.customTitle,
|
||||
title: item.title,
|
||||
top: item.top
|
||||
}))
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
|
||||
export type Props = {
|
||||
chatId: string;
|
||||
customTitle?: string;
|
||||
top?: boolean;
|
||||
};
|
||||
|
||||
/* 更新聊天标题 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { chatId, customTitle, top } = req.body as Props;
|
||||
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
await Chat.findOneAndUpdate(
|
||||
{
|
||||
chatId,
|
||||
userId
|
||||
},
|
||||
{
|
||||
...(customTitle ? { customTitle } : {}),
|
||||
...(top ? { top } : { top: null })
|
||||
}
|
||||
);
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { Chat, ChatItem } from '@/service/mongo';
|
||||
import type { InitChatResponse } from '@/api/response/chat';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { ChatItemType } from '@/types/chat';
|
||||
import { authApp } from '@/service/utils/auth';
|
||||
import type { ChatSchema } from '@/types/mongoSchema';
|
||||
import { getSpecialModule, getChatModelNameList } from '@/components/ChatBox/utils';
|
||||
import { TaskResponseKeyEnum } from '@/constants/chat';
|
||||
|
||||
/* 初始化我的聊天框,需要身份验证 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
let { appId, chatId } = req.query as {
|
||||
appId: '' | string;
|
||||
chatId: '' | string;
|
||||
};
|
||||
|
||||
if (!appId) {
|
||||
return jsonRes(res, {
|
||||
code: 501,
|
||||
message: "You don't have an app yet"
|
||||
});
|
||||
}
|
||||
|
||||
// 校验使用权限
|
||||
const app = (
|
||||
await authApp({
|
||||
appId,
|
||||
userId,
|
||||
authUser: false,
|
||||
authOwner: false
|
||||
})
|
||||
).app;
|
||||
|
||||
// get app and history
|
||||
const { chat, history = [] }: { chat?: ChatSchema; history?: ChatItemType[] } =
|
||||
await (async () => {
|
||||
if (chatId) {
|
||||
// auth chatId
|
||||
const [chat, history] = await Promise.all([
|
||||
Chat.findOne(
|
||||
{
|
||||
chatId,
|
||||
userId
|
||||
},
|
||||
'title variables'
|
||||
),
|
||||
ChatItem.find(
|
||||
{
|
||||
chatId,
|
||||
userId
|
||||
},
|
||||
`dataId obj value adminFeedback userFeedback ${TaskResponseKeyEnum.responseData}`
|
||||
)
|
||||
.sort({ _id: -1 })
|
||||
.limit(30)
|
||||
]);
|
||||
if (!chat) {
|
||||
throw new Error('聊天框不存在');
|
||||
}
|
||||
history.reverse();
|
||||
return { app, history, chat };
|
||||
}
|
||||
return {};
|
||||
})();
|
||||
|
||||
if (!app) {
|
||||
throw new Error('Auth App Error');
|
||||
}
|
||||
|
||||
const isOwner = String(app.userId) === userId;
|
||||
|
||||
jsonRes<InitChatResponse>(res, {
|
||||
data: {
|
||||
chatId,
|
||||
appId,
|
||||
app: {
|
||||
...getSpecialModule(app.modules),
|
||||
chatModels: getChatModelNameList(app.modules),
|
||||
name: app.name,
|
||||
avatar: app.avatar,
|
||||
intro: app.intro,
|
||||
canUse: app.share.isShare || isOwner
|
||||
},
|
||||
title: chat?.title || '新对话',
|
||||
variables: chat?.variables || {},
|
||||
history
|
||||
}
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
bodyParser: {
|
||||
sizeLimit: '10mb'
|
||||
}
|
||||
}
|
||||
};
|
||||