diff --git a/README.md b/README.md index e2a48fdd7..dc36bc08c 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,12 @@ Fast GPT 允许你使用自己的 openai API KEY 来快速的调用 openai 接 - [FastGpt V3.4 更新集合](https://www.bilibili.com/video/BV1Lo4y147Qh/?vd_source=92041a1a395f852f9d89158eaa3f61b4) - [FastGpt 知识库演示](https://www.bilibili.com/video/BV1Wo4y1p7i1/) +## Powered by + +- [TuShan 5 分钟搭建后台管理系统](https://github.com/msgbyte/tushan) +- [Laf 3 分钟快速接入三方应用](https://github.com/labring/laf) +- [Sealos 快速部署集群应用](https://github.com/labring/sealos) + ## 🌟 Star History [![Star History Chart](https://api.star-history.com/svg?repos=c121914yu/FastGPT&type=Date)](https://star-history.com/#c121914yu/FastGPT&Date) diff --git a/admin/service/route/app.js b/admin/service/route/app.js index 56ec433f1..556ecb492 100644 --- a/admin/service/route/app.js +++ b/admin/service/route/app.js @@ -38,13 +38,13 @@ export const useAppRoute = (app) => { id: model._id.toString(), userId: model.userId, name: model.name, + intro: model.intro, model: model.chat?.chatModel, relatedKbs: kbNames, // 将relatedKbs的id转换为相应的Kb名称 systemPrompt: model.chat?.systemPrompt || '', temperature: model.chat?.temperature || 0, 'share.topNum': model.share?.topNum || 0, 'share.isShare': model.share?.isShare || false, - 'share.intro': model.share?.intro, 'share.collection': model.share?.collection || 0 }; @@ -66,14 +66,15 @@ export const useAppRoute = (app) => { const _id = req.params.id; let { - share: { isShare, intro, topNum } + share: { isShare, topNum }, + intro } = req.body; await Model.findByIdAndUpdate(_id, { $set: { + intro: intro, 'share.topNum': Number(topNum), - 'share.isShare': isShare === 'true', - 'share.intro': intro + 'share.isShare': isShare === 'true' || isShare === true } }); diff --git a/admin/service/schema.js b/admin/service/schema.js index ffc019b88..d05a47811 100644 --- a/admin/service/schema.js +++ b/admin/service/schema.js @@ -61,9 +61,9 @@ const modelSchema = new mongoose.Schema({ name: String, avatar: String, status: String, + intro: String, chat: { relatedKbs: [mongoose.Schema.Types.ObjectId], - searchMode: String, systemPrompt: String, temperature: Number, chatModel: String diff --git a/admin/src/fields.ts b/admin/src/fields.ts index a7bf2d7b4..563766beb 100644 --- a/admin/src/fields.ts +++ b/admin/src/fields.ts @@ -2,7 +2,7 @@ import { createTextField, createNumberField } from 'tushan'; export const userFields = [ createTextField('id', { label: 'ID' }), - createTextField('username', { label: '用户名' }), + createTextField('username', { label: '用户名', edit: { hidden: true } }), createNumberField('balance', { label: '余额', list: { sort: true } }), createTextField('createTime', { label: 'Create Time', list: { sort: true } }), createTextField('password', { label: '密码', list: { hidden: true } }) @@ -19,20 +19,20 @@ export const payFields = [ export const kbFields = [ createTextField('id', { label: 'ID' }), - createTextField('userId', { label: '所属用户' }), + createTextField('userId', { label: '所属用户', edit: { hidden: true } }), createTextField('name', { label: '知识库' }), createTextField('tags', { label: 'Tags' }) ]; export const ModelFields = [ createTextField('id', { label: 'ID' }), - createTextField('userId', { label: '所属用户', list: { hidden: true } }), + createTextField('userId', { label: '所属用户', list: { hidden: true }, edit: { hidden: true } }), createTextField('name', { label: '名字' }), - createTextField('model', { label: '模型' }), + createTextField('model', { label: '模型', edit: { hidden: true } }), createTextField('share.collection', { label: '收藏数', list: { sort: true } }), createTextField('share.topNum', { label: '置顶等级', list: { sort: true } }), createTextField('share.isShare', { label: '是否分享(true,false)' }), - createTextField('share.intro', { label: '介绍', list: { width: 400 } }), + createTextField('intro', { label: '介绍', list: { width: 400 } }), createTextField('relatedKbs', { label: '引用的知识库', list: { hidden: true } }), createTextField('temperature', { label: '温度' }), createTextField('systemPrompt', { diff --git a/client/pnpm-lock.yaml b/client/pnpm-lock.yaml index 89c7b9ad8..afeb1c797 100644 --- a/client/pnpm-lock.yaml +++ b/client/pnpm-lock.yaml @@ -1,4 +1,8 @@ -lockfileVersion: '6.0' +lockfileVersion: '6.1' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false dependencies: '@alicloud/dysmsapi20170525': @@ -90,7 +94,7 @@ dependencies: version: registry.npmmirror.com/nanoid@4.0.1 next: specifier: 13.1.6 - version: registry.npmmirror.com/next@13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3) + version: registry.npmmirror.com/next@13.1.6(@babel/core@7.22.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3) nextjs-cors: specifier: ^2.1.2 version: registry.npmmirror.com/nextjs-cors@2.1.2(next@13.1.6) @@ -326,7 +330,6 @@ packages: dependencies: '@jridgewell/gen-mapping': registry.npmmirror.com/@jridgewell/gen-mapping@0.3.3 '@jridgewell/trace-mapping': registry.npmmirror.com/@jridgewell/trace-mapping@0.3.18 - dev: true registry.npmmirror.com/@aws-crypto/crc32@3.0.0: resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@aws-crypto/crc32/-/crc32-3.0.0.tgz} @@ -1307,7 +1310,6 @@ packages: name: '@babel/compat-data' version: 7.22.5 engines: {node: '>=6.9.0'} - dev: true registry.npmmirror.com/@babel/core@7.22.5: resolution: {integrity: sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/core/-/core-7.22.5.tgz} @@ -1332,7 +1334,6 @@ packages: semver: registry.npmmirror.com/semver@6.3.0 transitivePeerDependencies: - supports-color - dev: true registry.npmmirror.com/@babel/generator@7.22.5: resolution: {integrity: sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/generator/-/generator-7.22.5.tgz} @@ -1344,7 +1345,6 @@ packages: '@jridgewell/gen-mapping': registry.npmmirror.com/@jridgewell/gen-mapping@0.3.3 '@jridgewell/trace-mapping': registry.npmmirror.com/@jridgewell/trace-mapping@0.3.18 jsesc: registry.npmmirror.com/jsesc@2.5.2 - dev: true registry.npmmirror.com/@babel/helper-annotate-as-pure@7.22.5: resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz} @@ -1379,7 +1379,6 @@ packages: browserslist: registry.npmmirror.com/browserslist@4.21.7 lru-cache: registry.npmmirror.com/lru-cache@5.1.1 semver: registry.npmmirror.com/semver@6.3.0 - dev: true registry.npmmirror.com/@babel/helper-create-class-features-plugin@7.22.5(@babel/core@7.22.5): resolution: {integrity: sha512-xkb58MyOYIslxu3gKmVXmjTtUPvBU4odYzbiIQbWwLKIHCsx6UGZGX6F1IznMFVnDdirseUZopzN+ZRt8Xb33Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.5.tgz} @@ -1443,7 +1442,6 @@ packages: name: '@babel/helper-environment-visitor' version: 7.22.5 engines: {node: '>=6.9.0'} - dev: true registry.npmmirror.com/@babel/helper-function-name@7.22.5: resolution: {integrity: sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz} @@ -1453,7 +1451,6 @@ packages: dependencies: '@babel/template': registry.npmmirror.com/@babel/template@7.22.5 '@babel/types': registry.npmmirror.com/@babel/types@7.22.5 - dev: true registry.npmmirror.com/@babel/helper-hoist-variables@7.22.5: resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz} @@ -1462,7 +1459,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/types': registry.npmmirror.com/@babel/types@7.22.5 - dev: true registry.npmmirror.com/@babel/helper-member-expression-to-functions@7.22.5: resolution: {integrity: sha512-aBiH1NKMG0H2cGZqspNvsaBe6wNGjbJjuLy29aU+eDZjSbbN53BaxlpB02xm9v34pLTZ1nIQPFYn2qMZoa5BQQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.22.5.tgz} @@ -1497,7 +1493,6 @@ packages: '@babel/types': registry.npmmirror.com/@babel/types@7.22.5 transitivePeerDependencies: - supports-color - dev: true registry.npmmirror.com/@babel/helper-optimise-call-expression@7.22.5: resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz} @@ -1556,7 +1551,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/types': registry.npmmirror.com/@babel/types@7.22.5 - dev: true registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers@7.22.5: resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz} @@ -1574,7 +1568,6 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/types': registry.npmmirror.com/@babel/types@7.22.5 - dev: true registry.npmmirror.com/@babel/helper-string-parser@7.22.5: resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz} @@ -1593,7 +1586,6 @@ packages: name: '@babel/helper-validator-option' version: 7.22.5 engines: {node: '>=6.9.0'} - dev: true registry.npmmirror.com/@babel/helper-wrap-function@7.22.5: resolution: {integrity: sha512-bYqLIBSEshYcYQyfks8ewYA8S30yaGSeRslcvKMvoUk6HHPySbxHq9YRi6ghhzEU+yhQv9bP/jXnygkStOcqZw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.5.tgz} @@ -1620,7 +1612,6 @@ packages: '@babel/types': registry.npmmirror.com/@babel/types@7.22.5 transitivePeerDependencies: - supports-color - dev: true registry.npmmirror.com/@babel/highlight@7.22.5: resolution: {integrity: sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/highlight/-/highlight-7.22.5.tgz} @@ -1640,7 +1631,6 @@ packages: hasBin: true dependencies: '@babel/types': registry.npmmirror.com/@babel/types@7.22.5 - dev: true registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.5(@babel/core@7.22.5): resolution: {integrity: sha512-NP1M5Rf+u2Gw9qfSO4ihjcTGW5zXTi36ITLd4/EoAcEhIZ0yjMqmftDNl3QC19CX7olhrjpyU454g/2W7X0jvQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.22.5.tgz} @@ -2903,7 +2893,6 @@ packages: '@babel/code-frame': registry.npmmirror.com/@babel/code-frame@7.22.5 '@babel/parser': registry.npmmirror.com/@babel/parser@7.22.5 '@babel/types': registry.npmmirror.com/@babel/types@7.22.5 - dev: true registry.npmmirror.com/@babel/traverse@7.22.5: resolution: {integrity: sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/traverse/-/traverse-7.22.5.tgz} @@ -2923,7 +2912,6 @@ packages: globals: registry.npmmirror.com/globals@11.12.0 transitivePeerDependencies: - supports-color - dev: true registry.npmmirror.com/@babel/types@7.22.5: resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@babel/types/-/types-7.22.5.tgz} @@ -4501,33 +4489,28 @@ packages: '@jridgewell/set-array': registry.npmmirror.com/@jridgewell/set-array@1.1.2 '@jridgewell/sourcemap-codec': registry.npmmirror.com/@jridgewell/sourcemap-codec@1.4.15 '@jridgewell/trace-mapping': registry.npmmirror.com/@jridgewell/trace-mapping@0.3.18 - dev: true registry.npmmirror.com/@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz} name: '@jridgewell/resolve-uri' version: 3.1.0 engines: {node: '>=6.0.0'} - dev: true registry.npmmirror.com/@jridgewell/set-array@1.1.2: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.2.tgz} name: '@jridgewell/set-array' version: 1.1.2 engines: {node: '>=6.0.0'} - dev: true registry.npmmirror.com/@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz} name: '@jridgewell/sourcemap-codec' version: 1.4.14 - dev: true registry.npmmirror.com/@jridgewell/sourcemap-codec@1.4.15: resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz} name: '@jridgewell/sourcemap-codec' version: 1.4.15 - dev: true registry.npmmirror.com/@jridgewell/trace-mapping@0.3.18: resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz} @@ -4536,7 +4519,6 @@ packages: dependencies: '@jridgewell/resolve-uri': registry.npmmirror.com/@jridgewell/resolve-uri@3.1.0 '@jridgewell/sourcemap-codec': registry.npmmirror.com/@jridgewell/sourcemap-codec@1.4.14 - dev: true registry.npmmirror.com/@motionone/animation@10.15.1: resolution: {integrity: sha512-mZcJxLjHor+bhcPuIFErMDNyrdb2vJur8lSfMCsuCB4UyV8ILZLvK+t+pg56erv8ud9xQGK/1OGPt10agPrCyQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@motionone/animation/-/animation-10.15.1.tgz} @@ -5926,7 +5908,6 @@ packages: electron-to-chromium: registry.npmmirror.com/electron-to-chromium@1.4.425 node-releases: registry.npmmirror.com/node-releases@2.0.12 update-browserslist-db: registry.npmmirror.com/update-browserslist-db@1.0.11(browserslist@4.21.7) - dev: true registry.npmmirror.com/bson@4.7.2: resolution: {integrity: sha512-Ry9wCtIZ5kGqkJoi6aD8KjxFZEx78guTQDnpXWiNthsxzrxAK/i8E6pCHAIZTbaEFWcOCvbecMukfK7XUvyLpQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/bson/-/bson-4.7.2.tgz} @@ -7246,7 +7227,6 @@ packages: resolution: {integrity: sha512-wv1NufHxu11zfDbY4fglYQApMswleE9FL/DSeyOyauVXDZ+Kco96JK/tPfBUaDqfRarYp2WH2hJ/5UnVywp9Jg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.425.tgz} name: electron-to-chromium version: 1.4.425 - dev: true registry.npmmirror.com/emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz} @@ -7385,7 +7365,6 @@ packages: name: escalade version: 3.1.1 engines: {node: '>=6'} - dev: true registry.npmmirror.com/escape-html@1.0.3: resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz} @@ -8101,7 +8080,6 @@ packages: name: gensync version: 1.0.0-beta.2 engines: {node: '>=6.9.0'} - dev: true registry.npmmirror.com/get-intrinsic@1.2.1: resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz} @@ -8209,7 +8187,6 @@ packages: name: globals version: 11.12.0 engines: {node: '>=4'} - dev: true registry.npmmirror.com/globals@13.20.0: resolution: {integrity: sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/globals/-/globals-13.20.0.tgz} @@ -9020,7 +8997,6 @@ packages: version: 2.5.2 engines: {node: '>=4'} hasBin: true - dev: true registry.npmmirror.com/json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz} @@ -9054,7 +9030,6 @@ packages: version: 2.2.3 engines: {node: '>=6'} hasBin: true - dev: true registry.npmmirror.com/jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz} @@ -10044,7 +10019,7 @@ packages: engines: {node: '>= 0.4.0'} dev: false - registry.npmmirror.com/next@13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3): + registry.npmmirror.com/next@13.1.6(@babel/core@7.22.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3): resolution: {integrity: sha512-hHlbhKPj9pW+Cymvfzc15lvhaOZ54l+8sXDXJWm3OBNBzgrVj6hwGPmqqsXg40xO1Leq+kXpllzRPuncpC0Phw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/next/-/next-13.1.6.tgz} id: registry.npmmirror.com/next/13.1.6 name: next @@ -10072,7 +10047,7 @@ packages: react: registry.npmmirror.com/react@18.2.0 react-dom: registry.npmmirror.com/react-dom@18.2.0(react@18.2.0) sass: registry.npmmirror.com/sass@1.58.3 - styled-jsx: registry.npmmirror.com/styled-jsx@5.1.1(react@18.2.0) + styled-jsx: registry.npmmirror.com/styled-jsx@5.1.1(@babel/core@7.22.5)(react@18.2.0) optionalDependencies: '@next/swc-android-arm-eabi': registry.npmmirror.com/@next/swc-android-arm-eabi@13.1.6 '@next/swc-android-arm64': registry.npmmirror.com/@next/swc-android-arm64@13.1.6 @@ -10101,14 +10076,13 @@ packages: next: ^8.1.1-canary.54 || ^9.0.0 || ^10.0.0-0 || ^11.0.0 || ^12.0.0 || ^13.0.0 dependencies: cors: registry.npmmirror.com/cors@2.8.5 - next: registry.npmmirror.com/next@13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3) + next: registry.npmmirror.com/next@13.1.6(@babel/core@7.22.5)(react-dom@18.2.0)(react@18.2.0)(sass@1.58.3) dev: false registry.npmmirror.com/node-releases@2.0.12: resolution: {integrity: sha512-QzsYKWhXTWx8h1kIvqfnC++o0pEmpRQA/aenALsL2F4pqNVr7YzcdMlDij5WBnwftRbJCNJL/O7zdKaxKPHqgQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/node-releases/-/node-releases-2.0.12.tgz} name: node-releases version: 2.0.12 - dev: true registry.npmmirror.com/nodemailer@6.9.1: resolution: {integrity: sha512-qHw7dOiU5UKNnQpXktdgQ1d3OFgRAekuvbJLcdG5dnEo/GtcTHRYM7+UfJARdOFU9WUQO8OiIamgWPmiSFHYAA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/nodemailer/-/nodemailer-6.9.1.tgz} @@ -11318,7 +11292,6 @@ packages: name: semver version: 6.3.0 hasBin: true - dev: true registry.npmmirror.com/semver@7.5.1: resolution: {integrity: sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/semver/-/semver-7.5.1.tgz} @@ -11625,7 +11598,7 @@ packages: inline-style-parser: registry.npmmirror.com/inline-style-parser@0.1.1 dev: false - registry.npmmirror.com/styled-jsx@5.1.1(react@18.2.0): + registry.npmmirror.com/styled-jsx@5.1.1(@babel/core@7.22.5)(react@18.2.0): resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/styled-jsx/-/styled-jsx-5.1.1.tgz} id: registry.npmmirror.com/styled-jsx/5.1.1 name: styled-jsx @@ -11641,6 +11614,7 @@ packages: babel-plugin-macros: optional: true dependencies: + '@babel/core': registry.npmmirror.com/@babel/core@7.22.5 client-only: registry.npmmirror.com/client-only@0.0.1 react: registry.npmmirror.com/react@18.2.0 dev: false @@ -12073,7 +12047,6 @@ packages: browserslist: registry.npmmirror.com/browserslist@4.21.7 escalade: registry.npmmirror.com/escalade@3.1.1 picocolors: registry.npmmirror.com/picocolors@1.0.0 - dev: true registry.npmmirror.com/uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz} @@ -12429,7 +12402,3 @@ packages: name: zwitch version: 2.0.4 dev: false - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false diff --git a/client/src/components/APIKeyModal/index.tsx b/client/src/components/APIKeyModal/index.tsx new file mode 100644 index 000000000..ae6e577e8 --- /dev/null +++ b/client/src/components/APIKeyModal/index.tsx @@ -0,0 +1,160 @@ +import React, { useState } from 'react'; +import { + Box, + Button, + Modal, + ModalOverlay, + ModalContent, + Flex, + ModalFooter, + ModalBody, + ModalCloseButton, + 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'; + +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 ( + + + + + + API 秘钥管理 + + + 如果你不想 API 秘钥被滥用,请勿将秘钥直接放置在前端使用~ + + + + + + + + + + + + + + + {apiKeys.map(({ id, apiKey, createTime, lastUsedTime }) => ( + + + + + + + ))} + +
Api Key创建时间最后一次使用时间 +
{apiKey}{dayjs(createTime).format('YYYY/MM/DD HH:mm:ss')} + {lastUsedTime + ? dayjs(lastUsedTime).format('YYYY/MM/DD HH:mm:ss') + : '没有使用过'} + + } + size={'xs'} + aria-label={'delete'} + variant={'base'} + colorScheme={'gray'} + onClick={() => onclickRemove(id)} + /> +
+
+
+ + + + + + +
+ setApiKey('')}> + + + + + 新的 API 秘钥 + + + 请保管好你的秘钥,秘钥不会再次展示~ + + + + + copyData(apiKey)} + > + {apiKey} + + + + + + + + +
+ ); +}; + +export default APIKeyModal; diff --git a/client/src/components/Icon/icons/apikey.svg b/client/src/components/Icon/icons/apikey.svg new file mode 100644 index 000000000..48b8bc94d --- /dev/null +++ b/client/src/components/Icon/icons/apikey.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/src/components/Icon/icons/develop.svg b/client/src/components/Icon/icons/develop.svg deleted file mode 100644 index 7eb7b4bb6..000000000 --- a/client/src/components/Icon/icons/develop.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/client/src/components/Icon/index.tsx b/client/src/components/Icon/index.tsx index 86a03036d..59a498f55 100644 --- a/client/src/components/Icon/index.tsx +++ b/client/src/components/Icon/index.tsx @@ -6,7 +6,6 @@ const map = { model: require('./icons/model.svg').default, copy: require('./icons/copy.svg').default, chatSend: require('./icons/chatSend.svg').default, - develop: require('./icons/develop.svg').default, user: require('./icons/user.svg').default, delete: require('./icons/delete.svg').default, withdraw: require('./icons/withdraw.svg').default, @@ -34,7 +33,8 @@ const map = { text: require('./icons/text.svg').default, history: require('./icons/history.svg').default, kbTest: require('./icons/kbTest.svg').default, - date: require('./icons/date.svg').default + date: require('./icons/date.svg').default, + apikey: require('./icons/apikey.svg').default }; export type IconName = keyof typeof map; diff --git a/client/src/components/Iconfont/index.tsx b/client/src/components/Iconfont/index.tsx deleted file mode 100644 index f91480d0b..000000000 --- a/client/src/components/Iconfont/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -type TIconfont = { - name: string; - color?: string; - width?: number | string; - height?: number | string; - className?: string; -}; - -function Iconfont({ name, color = 'inherit', width = 16, height = 16, className = '' }: TIconfont) { - const style = { - fill: color, - width, - height - }; - - return ( - - ); -} - -export default Iconfont; diff --git a/client/src/components/Layout/navbar.tsx b/client/src/components/Layout/navbar.tsx index 88e648b30..b2673a325 100644 --- a/client/src/components/Layout/navbar.tsx +++ b/client/src/components/Layout/navbar.tsx @@ -44,12 +44,6 @@ const Navbar = ({ unread }: { unread: number }) => { link: '/model/share', activeLink: ['/model/share'] }, - { - label: '开发', - icon: 'develop', - link: '/openapi', - activeLink: ['/openapi'] - }, { label: '账号', icon: 'user', diff --git a/client/src/components/Select/index.tsx b/client/src/components/Select/index.tsx new file mode 100644 index 000000000..5faf81d58 --- /dev/null +++ b/client/src/components/Select/index.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { Menu, MenuButton, MenuList, MenuItem, Button, useDisclosure } 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; + id: string; + }[]; + onchange?: (val: string) => void; +} + +const MySelect = ({ placeholder, value, width = 'auto', list, onchange, ...props }: Props) => { + const menuItemStyles = { + borderRadius: 'sm', + py: 2, + display: 'flex', + alignItems: 'center', + _hover: { + backgroundColor: 'myWhite.600' + } + }; + const { isOpen, onOpen, onClose } = useDisclosure(); + + return ( + + + + + `${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} + > + {list.map((item) => ( + { + if (onchange && value !== item.id) { + onchange(item.id); + } + }} + > + {item.label} + + ))} + + + ); +}; + +export default MySelect; diff --git a/client/src/components/Slider/index.tsx b/client/src/components/Slider/index.tsx index 1316a34c7..87fff3c80 100644 --- a/client/src/components/Slider/index.tsx +++ b/client/src/components/Slider/index.tsx @@ -9,28 +9,30 @@ import { } from '@chakra-ui/react'; const MySlider = ({ - markList, + markList = [], setVal, activeVal, max = 100, min = 0, - step = 1 + step = 1, + width = '100%' }: { - markList: { + markList?: { label: string | number; value: number; }[]; - activeVal?: number; + activeVal: number; setVal: (index: number) => void; max?: number; min?: number; step?: number; + width?: string | string[] | number | number[]; }) => { const startEndPointStyle = { content: '""', - borderRadius: '10px', - width: '10px', - height: '10px', + borderRadius: '6px', + width: '6px', + height: '6px', backgroundColor: '#ffffff', border: '2px solid #D7DBE2', position: 'absolute', @@ -44,37 +46,62 @@ const MySlider = ({ }, [activeVal, markList]); return ( - - {markList.map((item, i) => ( + + {markList?.map((item, i) => ( {item.label} ))} + + {activeVal} + - + - + ); }; diff --git a/client/src/components/Tabs/index.tsx b/client/src/components/Tabs/index.tsx index 7ff5af04f..24e4495ec 100644 --- a/client/src/components/Tabs/index.tsx +++ b/client/src/components/Tabs/index.tsx @@ -24,13 +24,13 @@ const Tabs = ({ list, size = 'md', activeId, onChange, ...props }: Props) => { return { fontSize: 'md', outP: '4px', - inlineP: 2 + inlineP: 1 }; case 'lg': return { fontSize: 'lg', outP: '5px', - inlineP: 3 + inlineP: 2 }; } }, [size]); diff --git a/client/src/constants/model.ts b/client/src/constants/model.ts index 95efdf870..7fc3eae35 100644 --- a/client/src/constants/model.ts +++ b/client/src/constants/model.ts @@ -31,7 +31,7 @@ export const ChatModelMap = { [OpenAiChatEnum.GPT35]: { chatModel: OpenAiChatEnum.GPT35, name: 'Gpt35-4k', - contextMaxToken: 4096, + contextMaxToken: 4000, systemMaxToken: 2400, maxTemperature: 1.2, price: 2.2 @@ -80,70 +80,18 @@ export const getChatModelList = async () => { return list; }; -export enum ModelStatusEnum { - running = 'running', - training = 'training', - pending = 'pending', - closed = 'closed' -} - -export const formatModelStatus = { - [ModelStatusEnum.running]: { - colorTheme: 'green', - text: '运行中' - }, - [ModelStatusEnum.training]: { - colorTheme: 'blue', - text: '训练中' - }, - [ModelStatusEnum.pending]: { - colorTheme: 'gray', - text: '加载中' - }, - [ModelStatusEnum.closed]: { - colorTheme: 'red', - text: '已关闭' - } -}; - -/* 知识库搜索时的配置 */ -// 搜索方式 -export enum appVectorSearchModeEnum { - hightSimilarity = 'hightSimilarity', // 高相似度+禁止回复 - lowSimilarity = 'lowSimilarity', // 低相似度 - noContext = 'noContex' // 高相似度+无上下文回复 -} -export const ModelVectorSearchModeMap: Record< - `${appVectorSearchModeEnum}`, - { - text: string; - similarity: number; - } -> = { - [appVectorSearchModeEnum.hightSimilarity]: { - text: '高相似度, 无匹配时拒绝回复', - similarity: 0.8 - }, - [appVectorSearchModeEnum.noContext]: { - text: '高相似度,无匹配时直接回复', - similarity: 0.8 - }, - [appVectorSearchModeEnum.lowSimilarity]: { - text: '低相似度匹配', - similarity: 0.3 - } -}; - export const defaultModel: ModelSchema = { _id: 'modelId', userId: 'userId', name: '模型名称', avatar: '/icon/logo.png', - status: ModelStatusEnum.pending, + intro: '', updateTime: Date.now(), chat: { relatedKbs: [], - searchMode: appVectorSearchModeEnum.hightSimilarity, + searchSimilarity: 0.2, + searchLimit: 5, + searchEmptyText: '', systemPrompt: '', temperature: 0, chatModel: OpenAiChatEnum.GPT35 @@ -151,7 +99,6 @@ export const defaultModel: ModelSchema = { share: { isShare: false, isShareDetail: false, - intro: '', collection: 0 } }; diff --git a/client/src/constants/theme.ts b/client/src/constants/theme.ts index 93c05e7da..b2fce8cb7 100644 --- a/client/src/constants/theme.ts +++ b/client/src/constants/theme.ts @@ -15,9 +15,7 @@ const { definePartsStyle: selectPart, defineMultiStyleConfig: selectMultiStyle } // modal 弹窗 const ModalTheme = defineMultiStyleConfig({ baseStyle: definePartsStyle({ - dialog: { - width: '90%' - } + dialog: {} }) }); diff --git a/client/src/pages/_app.tsx b/client/src/pages/_app.tsx index 04d5c31a4..94672a09f 100644 --- a/client/src/pages/_app.tsx +++ b/client/src/pages/_app.tsx @@ -8,9 +8,9 @@ import { theme } from '@/constants/theme'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import NProgress from 'nprogress'; //nprogress module import Router from 'next/router'; -import 'nprogress/nprogress.css'; -import '../styles/reset.scss'; import { useGlobalStore } from '@/store/global'; +import 'nprogress/nprogress.css'; +import '@/styles/reset.scss'; //Binding events. Router.events.on('routeChangeStart', () => NProgress.start()); @@ -28,7 +28,7 @@ const queryClient = new QueryClient({ } }); -export default function App({ Component, pageProps }: AppProps) { +function App({ Component, pageProps }: AppProps) { const { loadInitData, initData: { googleVerKey } @@ -78,6 +78,5 @@ export default function App({ Component, pageProps }: AppProps) { ); } -// export function reportWebVitals(metric: NextWebVitalsMetric) { -// console.log(metric); -// } +// @ts-ignore +export default App; diff --git a/client/src/pages/api/chat/chat.ts b/client/src/pages/api/chat/chat.ts index e7119fe65..9da858460 100644 --- a/client/src/pages/api/chat/chat.ts +++ b/client/src/pages/api/chat/chat.ts @@ -4,7 +4,7 @@ import { authChat } from '@/service/utils/auth'; import { modelServiceToolMap } from '@/service/utils/chat'; import { ChatItemType } from '@/types/chat'; import { jsonRes } from '@/service/response'; -import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model'; +import { ChatModelMap } from '@/constants/model'; import { pushChatBill } from '@/service/events/pushBill'; import { resStreamResponse } from '@/service/utils/chat'; import { appKbSearch } from '../openapi/kb/appKbSearch'; @@ -48,36 +48,31 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const modelConstantsData = ChatModelMap[model.chat.chatModel]; - // 读取对话内容 - const prompts = [...content, prompt[0]]; - const { - code = 200, - systemPrompts = [], - quote = [], - guidePrompt = '' + rawSearch = [], + userSystemPrompt = [], + quotePrompt = [] } = await (async () => { // 使用了知识库搜索 if (model.chat.relatedKbs?.length > 0) { - const { code, searchPrompts, rawSearch, guidePrompt } = await appKbSearch({ + const { rawSearch, userSystemPrompt, quotePrompt } = await appKbSearch({ model, userId, fixedQuote: content[content.length - 1]?.quote || [], prompt: prompt[0], - similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity + similarity: model.chat.searchSimilarity, + limit: model.chat.searchLimit }); return { - code, - quote: rawSearch, - systemPrompts: searchPrompts, - guidePrompt + rawSearch: rawSearch, + userSystemPrompt: userSystemPrompt ? [userSystemPrompt] : [], + quotePrompt: [quotePrompt] }; } if (model.chat.systemPrompt) { return { - guidePrompt: model.chat.systemPrompt, - systemPrompts: [ + userSystemPrompt: [ { obj: ChatRoleEnum.System, value: model.chat.systemPrompt @@ -92,13 +87,14 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const conversationId = chatId || String(new Types.ObjectId()); !chatId && res.setHeader(NEW_CHATID_HEADER, conversationId); if (showModelDetail) { - guidePrompt && res.setHeader(GUIDE_PROMPT_HEADER, encodeURIComponent(guidePrompt)); - res.setHeader(QUOTE_LEN_HEADER, quote.length); + userSystemPrompt[0] && + res.setHeader(GUIDE_PROMPT_HEADER, encodeURIComponent(userSystemPrompt[0].value)); + res.setHeader(QUOTE_LEN_HEADER, rawSearch.length); } // search result is empty - if (code === 201) { - const response = systemPrompts[0]?.value; + if (model.chat.relatedKbs?.length > 0 && !quotePrompt[0]?.value && model.chat.searchEmptyText) { + const response = model.chat.searchEmptyText; await saveChat({ chatId, newChatId: conversationId, @@ -116,11 +112,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) return res.end(response); } - prompts.unshift(...systemPrompts); + // 读取对话内容 + const prompts = [...quotePrompt, ...content, ...userSystemPrompt, prompt[0]]; // content check await sensitiveCheck({ - input: [...systemPrompts, prompt[0]].map((item) => item.value).join('') + input: [...quotePrompt, ...userSystemPrompt, prompt[0]].map((item) => item.value).join('') }); // 计算温度 @@ -162,8 +159,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) { ...prompt[1], value: responseContent, - quote: showModelDetail ? quote : [], - systemPrompt: showModelDetail ? guidePrompt : '' + quote: showModelDetail ? rawSearch : [], + systemPrompt: showModelDetail ? userSystemPrompt[0]?.value : '' } ], userId diff --git a/client/src/pages/api/chat/init.ts b/client/src/pages/api/chat/init.ts index c768e123b..a9cf643d8 100644 --- a/client/src/pages/api/chat/init.ts +++ b/client/src/pages/api/chat/init.ts @@ -6,7 +6,6 @@ import { authUser } from '@/service/utils/auth'; import { ChatItemType } from '@/types/chat'; import { authModel } from '@/service/utils/auth'; import mongoose from 'mongoose'; -import { ModelStatusEnum } from '@/constants/model'; import type { ModelSchema } from '@/types/mongoSchema'; /* 初始化我的聊天框,需要身份验证 */ @@ -29,8 +28,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) if (!myModel) { const { _id } = await Model.create({ name: '应用1', - userId, - status: ModelStatusEnum.running + userId }); model = (await Model.findById(_id)) as ModelSchema; } else { @@ -95,7 +93,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) model: { name: model.name, avatar: model.avatar, - intro: model.share.intro, + intro: model.intro, canUse: model.share.isShare || String(model.userId) === userId }, chatModel: model.chat.chatModel, diff --git a/client/src/pages/api/chat/shareChat/chat.ts b/client/src/pages/api/chat/shareChat/chat.ts index 9b2ca6dc5..ff9161e7d 100644 --- a/client/src/pages/api/chat/shareChat/chat.ts +++ b/client/src/pages/api/chat/shareChat/chat.ts @@ -4,7 +4,7 @@ import { authShareChat } from '@/service/utils/auth'; import { modelServiceToolMap } from '@/service/utils/chat'; import { ChatItemSimpleType } from '@/types/chat'; import { jsonRes } from '@/service/response'; -import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model'; +import { ChatModelMap } from '@/constants/model'; import { pushChatBill, updateShareChatBill } from '@/service/events/pushBill'; import { resStreamResponse } from '@/service/utils/chat'; import { ChatRoleEnum } from '@/constants/chat'; @@ -40,26 +40,33 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }); const modelConstantsData = ChatModelMap[model.chat.chatModel]; + const prompt = prompts[prompts.length - 1]; - const { code = 200, systemPrompts = [] } = await (async () => { + const { + rawSearch = [], + userSystemPrompt = [], + quotePrompt = [] + } = await (async () => { // 使用了知识库搜索 if (model.chat.relatedKbs?.length > 0) { - const { code, searchPrompts } = await appKbSearch({ + const { rawSearch, userSystemPrompt, quotePrompt } = await appKbSearch({ model, userId, fixedQuote: [], - prompt: prompts[prompts.length - 1], - similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity + prompt: prompt, + similarity: model.chat.searchSimilarity, + limit: model.chat.searchLimit }); return { - code, - systemPrompts: searchPrompts + rawSearch: rawSearch, + userSystemPrompt: userSystemPrompt ? [userSystemPrompt] : [], + quotePrompt: [quotePrompt] }; } if (model.chat.systemPrompt) { return { - systemPrompts: [ + userSystemPrompt: [ { obj: ChatRoleEnum.System, value: model.chat.systemPrompt @@ -71,15 +78,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) })(); // search result is empty - if (code === 201) { - return res.send(systemPrompts[0]?.value); + if (model.chat.relatedKbs?.length > 0 && !quotePrompt[0]?.value && model.chat.searchEmptyText) { + const response = model.chat.searchEmptyText; + return res.end(response); } - prompts.unshift(...systemPrompts); + // 读取对话内容 + const completePrompts = [...quotePrompt, ...prompts.slice(0, -1), ...userSystemPrompt, prompt]; // content check await sensitiveCheck({ - input: [...systemPrompts, prompts[prompts.length - 1]].map((item) => item.value).join('') + input: [...quotePrompt, ...userSystemPrompt, prompt].map((item) => item.value).join('') }); // 计算温度 @@ -93,7 +102,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ].chatCompletion({ apiKey: userOpenAiKey || systemAuthKey, temperature: +temperature, - messages: prompts, + messages: completePrompts, stream: true, res, chatId: historyId diff --git a/client/src/pages/api/chat/shareChat/init.ts b/client/src/pages/api/chat/shareChat/init.ts index 5cc0fb703..3906b5658 100644 --- a/client/src/pages/api/chat/shareChat/init.ts +++ b/client/src/pages/api/chat/shareChat/init.ts @@ -50,7 +50,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) model: { name: model.name, avatar: model.avatar, - intro: model.share.intro + intro: model.intro }, chatModel: model.chat.chatModel } diff --git a/client/src/pages/api/model/create.ts b/client/src/pages/api/model/create.ts index 1c0c507ad..e9805a117 100644 --- a/client/src/pages/api/model/create.ts +++ b/client/src/pages/api/model/create.ts @@ -3,7 +3,6 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; import { connectToDatabase } from '@/service/mongo'; import { authUser } from '@/service/utils/auth'; -import { ModelStatusEnum } from '@/constants/model'; import { Model } from '@/service/models/model'; export default async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -32,8 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< // 创建模型 const response = await Model.create({ name, - userId, - status: ModelStatusEnum.running + userId }); jsonRes(res, { diff --git a/client/src/pages/api/model/share/getModels.ts b/client/src/pages/api/model/share/getModels.ts index 027963ae9..eda51df2c 100644 --- a/client/src/pages/api/model/share/getModels.ts +++ b/client/src/pages/api/model/share/getModels.ts @@ -31,7 +31,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< $and: [ { 'share.isShare': true }, { - $or: [{ name: { $regex: regex } }, { 'share.intro': { $regex: regex } }] + $or: [{ name: { $regex: regex } }, { intro: { $regex: regex } }] } ] }; @@ -66,6 +66,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< avatar: { $ifNull: ['$avatar', '/icon/logo.png'] }, name: 1, userId: 1, + intro: 1, share: 1, isCollection: { $cond: { diff --git a/client/src/pages/api/model/update.ts b/client/src/pages/api/model/update.ts index 06d9d4f29..c0b536d8b 100644 --- a/client/src/pages/api/model/update.ts +++ b/client/src/pages/api/model/update.ts @@ -9,10 +9,10 @@ import { authModel } from '@/service/utils/auth'; /* 获取我的模型 */ export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { - const { name, avatar, chat, share } = req.body as ModelUpdateParams; + const { name, avatar, chat, share, intro } = req.body as ModelUpdateParams; const { modelId } = req.query as { modelId: string }; - if (!name || !chat || !modelId) { + if (!modelId) { throw new Error('参数错误'); } @@ -35,10 +35,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< { name, avatar, + intro, chat, - 'share.isShare': share.isShare, - 'share.isShareDetail': share.isShareDetail, - 'share.intro': share.intro + ...(share && { + 'share.isShare': share.isShare, + 'share.isShareDetail': share.isShareDetail + }) } ); diff --git a/client/src/pages/api/openapi/chat/chat.ts b/client/src/pages/api/openapi/chat/chat.ts index 2c32445a0..700b87594 100644 --- a/client/src/pages/api/openapi/chat/chat.ts +++ b/client/src/pages/api/openapi/chat/chat.ts @@ -4,12 +4,11 @@ import { authUser, authModel, getApiKey } from '@/service/utils/auth'; import { modelServiceToolMap, resStreamResponse } from '@/service/utils/chat'; import { ChatItemSimpleType } from '@/types/chat'; import { jsonRes } from '@/service/response'; -import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model'; +import { ChatModelMap } from '@/constants/model'; import { pushChatBill } from '@/service/events/pushBill'; import { ChatRoleEnum } from '@/constants/chat'; import { withNextCors } from '@/service/utils/tools'; import { BillTypeEnum } from '@/constants/user'; -import { sensitiveCheck } from '../../openapi/text/sensitiveCheck'; import { NEW_CHATID_HEADER } from '@/constants/chat'; import { Types } from 'mongoose'; import { appKbSearch } from '../kb/appKbSearch'; @@ -66,48 +65,46 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex }); const modelConstantsData = ChatModelMap[model.chat.chatModel]; + const prompt = prompts[prompts.length - 1]; - let systemPrompts: { - obj: ChatRoleEnum; - value: string; - }[] = []; + const { userSystemPrompt = [], quotePrompt = [] } = await (async () => { + // 使用了知识库搜索 + if (model.chat.relatedKbs?.length > 0) { + const { userSystemPrompt, quotePrompt } = await appKbSearch({ + model, + userId, + fixedQuote: [], + prompt: prompt, + similarity: model.chat.searchSimilarity, + limit: model.chat.searchLimit + }); - // 使用了知识库搜索 - if (model.chat.relatedKbs?.length > 0) { - const { code, searchPrompts } = await appKbSearch({ - model, - userId, - fixedQuote: [], - prompt: prompts[prompts.length - 1], - similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity - }); - - // search result is empty - if (code === 201) { - return isStream - ? res.send(searchPrompts[0]?.value) - : jsonRes(res, { - data: searchPrompts[0]?.value, - message: searchPrompts[0]?.value - }); + return { + userSystemPrompt: userSystemPrompt ? [userSystemPrompt] : [], + quotePrompt: [quotePrompt] + }; } + if (model.chat.systemPrompt) { + return { + userSystemPrompt: [ + { + obj: ChatRoleEnum.System, + value: model.chat.systemPrompt + } + ] + }; + } + return {}; + })(); - systemPrompts = searchPrompts; - } else if (model.chat.systemPrompt) { - systemPrompts = [ - { - obj: ChatRoleEnum.System, - value: model.chat.systemPrompt - } - ]; + // search result is empty + if (model.chat.relatedKbs?.length > 0 && !quotePrompt[0]?.value && model.chat.searchEmptyText) { + const response = model.chat.searchEmptyText; + return res.end(response); } - prompts.unshift(...systemPrompts); - - // content check - await sensitiveCheck({ - input: [...systemPrompts, prompts[prompts.length - 1]].map((item) => item.value).join('') - }); + // 读取对话内容 + const completePrompts = [...quotePrompt, ...prompts.slice(0, -1), ...userSystemPrompt, prompt]; // 计算温度 const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed( @@ -123,7 +120,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex await modelServiceToolMap[model.chat.chatModel].chatCompletion({ apiKey, temperature: +temperature, - messages: prompts, + messages: completePrompts, stream: isStream, res, chatId: conversationId diff --git a/client/src/pages/api/openapi/getKeys.ts b/client/src/pages/api/openapi/getKeys.ts index 067ebb297..dafb3c3e6 100644 --- a/client/src/pages/api/openapi/getKeys.ts +++ b/client/src/pages/api/openapi/getKeys.ts @@ -18,7 +18,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) ({ _id, apiKey, createTime, lastUsedTime }) => { return { id: _id, - apiKey: `${apiKey.substring(0, 2)}******${apiKey.substring(apiKey.length - 2)}`, + apiKey: `******${apiKey.substring(apiKey.length - 4)}`, createTime, lastUsedTime }; diff --git a/client/src/pages/api/openapi/kb/appKbSearch.ts b/client/src/pages/api/openapi/kb/appKbSearch.ts index 69be8cd89..9bd785130 100644 --- a/client/src/pages/api/openapi/kb/appKbSearch.ts +++ b/client/src/pages/api/openapi/kb/appKbSearch.ts @@ -5,7 +5,6 @@ import { PgClient } from '@/service/pg'; import { withNextCors } from '@/service/utils/tools'; import type { ChatItemSimpleType } from '@/types/chat'; import type { ModelSchema } from '@/types/mongoSchema'; -import { appVectorSearchModeEnum } from '@/constants/model'; import { authModel } from '@/service/utils/auth'; import { ChatModelMap } from '@/constants/model'; import { ChatRoleEnum } from '@/constants/chat'; @@ -21,16 +20,19 @@ export type QuoteItemType = { type Props = { prompts: ChatItemSimpleType[]; similarity: number; + limit: number; appId: string; }; type Response = { - code: 200 | 201; rawSearch: QuoteItemType[]; - guidePrompt: string; - searchPrompts: { + userSystemPrompt: { obj: ChatRoleEnum; value: string; - }[]; + }; + quotePrompt: { + obj: ChatRoleEnum; + value: string; + }; }; export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { @@ -41,7 +43,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex throw new Error('userId is empty'); } - const { prompts, similarity, appId } = req.body as Props; + const { prompts, similarity, limit, appId } = req.body as Props; if (!similarity || !Array.isArray(prompts) || !appId) { throw new Error('params is error'); @@ -58,7 +60,8 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex userId, fixedQuote: [], prompt: prompts[prompts.length - 1], - similarity + similarity, + limit }); jsonRes(res, { @@ -78,13 +81,15 @@ export async function appKbSearch({ userId, fixedQuote, prompt, - similarity + similarity = 0.8, + limit = 5 }: { model: ModelSchema; userId: string; fixedQuote: QuoteItemType[]; prompt: ChatItemSimpleType; similarity: number; + limit: number; }): Promise { const modelConstantsData = ChatModelMap[model.chat.chatModel]; @@ -103,7 +108,7 @@ export async function appKbSearch({ .map((item) => `'${item}'`) .join(',')}) AND vector <#> '[${promptVector[0]}]' < -${similarity} order by vector <#> '[${ promptVector[0] - }]' limit 10; + }]' limit ${limit}; COMMIT;` ); @@ -115,7 +120,7 @@ export async function appKbSearch({ ...searchRes.slice(0, 3), ...fixedQuote.slice(0, 2), ...searchRes.slice(3), - ...fixedQuote.slice(2, 5) + ...fixedQuote.slice(2, 4) ].filter((item) => { if (idSet.has(item.id)) { return false; @@ -125,86 +130,44 @@ export async function appKbSearch({ }); // 计算固定提示词的 token 数量 - const guidePrompt = model.chat.systemPrompt // user system prompt + const userSystemPrompt = model.chat.systemPrompt // user system prompt ? { - obj: ChatRoleEnum.System, + obj: ChatRoleEnum.Human, value: model.chat.systemPrompt } - : model.chat.searchMode === appVectorSearchModeEnum.noContext - ? { - obj: ChatRoleEnum.System, - value: `知识库是关于"${model.name}"的内容,根据知识库内容回答问题.` - } : { - obj: ChatRoleEnum.System, - value: `玩一个问答游戏,规则为: -1.你完全忘记你已有的知识 -2.你只回答关于"${model.name}"的问题 -3.你只从知识库中选择内容进行回答 -4.如果问题不在知识库中,你会回答:"我不知道。" -请务必遵守规则` + obj: ChatRoleEnum.Human, + value: `知识库是关于 ${model.name} 的内容,参考知识库回答问题。与 "${model.name}" 无关内容,直接回复: "我不知道"。` }; const fixedSystemTokens = modelToolMap[model.chat.chatModel].countTokens({ - messages: [guidePrompt] + messages: [userSystemPrompt] }); + + // filter part quote by maxToken const sliceResult = modelToolMap[model.chat.chatModel] .tokenSlice({ maxToken: modelConstantsData.systemMaxToken - fixedSystemTokens, - messages: filterSearch.map((item) => ({ + messages: filterSearch.map((item, i) => ({ obj: ChatRoleEnum.System, - value: `${item.q}\n${item.a}` + value: `${i + 1}: [${item.q}\n${item.a}]` })) }) - .map((item) => item.value); + .map((item) => item.value) + .join('\n') + .trim(); // slice filterSearch const rawSearch = filterSearch.slice(0, sliceResult.length); - // system prompt - const systemPrompt = sliceResult.join('\n').trim(); - - /* 高相似度+不回复 */ - if (!systemPrompt && model.chat.searchMode === appVectorSearchModeEnum.hightSimilarity) { - return { - code: 201, - rawSearch: [], - guidePrompt: '', - searchPrompts: [ - { - obj: ChatRoleEnum.System, - value: '对不起,你的问题不在知识库中。' - } - ] - }; - } - /* 高相似度+无上下文,不添加额外知识,仅用系统提示词 */ - if (!systemPrompt && model.chat.searchMode === appVectorSearchModeEnum.noContext) { - return { - code: 200, - rawSearch: [], - guidePrompt: model.chat.systemPrompt || '', - searchPrompts: model.chat.systemPrompt - ? [ - { - obj: ChatRoleEnum.System, - value: model.chat.systemPrompt - } - ] - : [] - }; - } + const quoteText = sliceResult ? `知识库:\n${sliceResult}` : ''; return { - code: 200, rawSearch, - guidePrompt: guidePrompt.value || '', - searchPrompts: [ - { - obj: ChatRoleEnum.System, - value: `知识库:<${systemPrompt}>` - }, - guidePrompt - ] + userSystemPrompt, + quotePrompt: { + obj: ChatRoleEnum.System, + value: quoteText + } }; } diff --git a/client/src/pages/api/openapi/postKey.ts b/client/src/pages/api/openapi/postKey.ts index 24cd0f538..53f30fa59 100644 --- a/client/src/pages/api/openapi/postKey.ts +++ b/client/src/pages/api/openapi/postKey.ts @@ -4,7 +4,7 @@ import { jsonRes } from '@/service/response'; import { connectToDatabase, OpenApi } from '@/service/mongo'; import { authUser } from '@/service/utils/auth'; import { customAlphabet } from 'nanoid'; -const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890'); +const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24); export default async function handler(req: NextApiRequest, res: NextApiResponse) { try { @@ -14,11 +14,11 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) const count = await OpenApi.find({ userId }).countDocuments(); - if (count >= 5) { - throw new Error('最多 5 组API Key'); + if (count >= 10) { + throw new Error('最多 10 组 API 秘钥'); } - const apiKey = `${userId}-${nanoid()}`; + const apiKey = `fastgpt-${nanoid()}`; await OpenApi.create({ userId, diff --git a/client/src/pages/model/components/ModelList.tsx b/client/src/pages/model/components/ModelList.tsx index 235031a9a..d557f9d66 100644 --- a/client/src/pages/model/components/ModelList.tsx +++ b/client/src/pages/model/components/ModelList.tsx @@ -83,7 +83,7 @@ const ModelList = ({ modelId }: { modelId: string }) => { setSearchText(e.target.value)} /> @@ -111,7 +111,7 @@ const ModelList = ({ modelId }: { modelId: string }) => { /> - + { { {item.name} - - {item.systemPrompt || '这个 应用 没有设置提示词~'} - ))} diff --git a/client/src/pages/model/components/detail/components/API.tsx b/client/src/pages/model/components/detail/components/API.tsx new file mode 100644 index 000000000..c1a539dc5 --- /dev/null +++ b/client/src/pages/model/components/detail/components/API.tsx @@ -0,0 +1,82 @@ +import React, { useState } from 'react'; +import { Box, Divider, Flex, useTheme, Button, Skeleton, useDisclosure } from '@chakra-ui/react'; +import { useCopyData } from '@/utils/tools'; +import dynamic from 'next/dynamic'; +import MyIcon from '@/components/Icon'; + +const APIKeyModal = dynamic(() => import('@/components/APIKeyModal'), { + ssr: true +}); + +const baseUrl = 'https://fastgpt.run/api/openapi'; + +const API = ({ modelId }: { modelId: string }) => { + const theme = useTheme(); + const { copyData } = useCopyData(); + const { + isOpen: isOpenAPIModal, + onOpen: onOpenAPIModal, + onClose: onCloseAPIModal + } = useDisclosure(); + const [isLoaded, setIsLoaded] = useState(false); + + return ( + + + + AppId: + copyData(modelId, '已复制 AppId')} + > + {modelId} + + + copyData(baseUrl, '已复制 API 地址')} + > + + API服务器 + + + {baseUrl} + + + + + + + +