Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a290369fc6 | ||
|
|
8817e4d2db | ||
|
|
c9ee6fabe4 | ||
|
|
24319fe860 | ||
|
|
87c5cb6bca | ||
|
|
746b9af2de | ||
|
|
176c5a4d79 | ||
|
|
0cde9a10a8 | ||
|
|
59ddf09b94 | ||
|
|
cdee91bec1 | ||
|
|
d36a7cb177 | ||
|
|
2fc31a706d | ||
|
|
2fce76202a | ||
|
|
7fe39c2515 | ||
|
|
e818cb037f |
@@ -8,6 +8,7 @@ CREATE TABLE IF NOT EXISTS modelData (
|
||||
vector VECTOR(1536) NOT NULL,
|
||||
user_id VARCHAR(50) NOT NULL,
|
||||
kb_id VARCHAR(50) NOT NULL,
|
||||
source VARCHAR(100),
|
||||
q TEXT NOT NULL,
|
||||
a TEXT NOT NULL
|
||||
);
|
||||
|
||||
@@ -55,7 +55,6 @@
|
||||
"remark-math": "^5.1.1",
|
||||
"request-ip": "^3.3.0",
|
||||
"sass": "^1.58.3",
|
||||
"sharp": "^0.31.3",
|
||||
"tunnel": "^0.0.6",
|
||||
"wxpay-v3": "^3.0.2",
|
||||
"zustand": "^4.3.5"
|
||||
|
||||
253
pnpm-lock.yaml
generated
253
pnpm-lock.yaml
generated
@@ -64,7 +64,6 @@ specifiers:
|
||||
remark-math: ^5.1.1
|
||||
request-ip: ^3.3.0
|
||||
sass: ^1.58.3
|
||||
sharp: ^0.31.3
|
||||
tunnel: ^0.0.6
|
||||
typescript: 4.9.5
|
||||
wxpay-v3: ^3.0.2
|
||||
@@ -115,7 +114,6 @@ dependencies:
|
||||
remark-math: registry.npmmirror.com/remark-math/5.1.1
|
||||
request-ip: 3.3.0
|
||||
sass: registry.npmmirror.com/sass/1.58.3
|
||||
sharp: registry.npmmirror.com/sharp/0.31.3
|
||||
tunnel: registry.npmmirror.com/tunnel/0.0.6
|
||||
wxpay-v3: registry.npmmirror.com/wxpay-v3/3.0.2
|
||||
zustand: registry.npmmirror.com/zustand/4.3.5_immer@9.0.19+react@18.2.0
|
||||
@@ -5827,16 +5825,6 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/bl/4.1.0:
|
||||
resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/bl/-/bl-4.1.0.tgz}
|
||||
name: bl
|
||||
version: 4.1.0
|
||||
dependencies:
|
||||
buffer: registry.npmmirror.com/buffer/5.7.1
|
||||
inherits: registry.npmmirror.com/inherits/2.0.4
|
||||
readable-stream: registry.npmmirror.com/readable-stream/3.6.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/bluebird/3.4.7:
|
||||
resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/bluebird/-/bluebird-3.4.7.tgz}
|
||||
name: bluebird
|
||||
@@ -6017,12 +6005,6 @@ packages:
|
||||
fsevents: 2.3.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/chownr/1.1.4:
|
||||
resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/chownr/-/chownr-1.1.4.tgz}
|
||||
name: chownr
|
||||
version: 1.1.4
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/clean-stack/2.2.0:
|
||||
resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/clean-stack/-/clean-stack-2.2.0.tgz}
|
||||
name: clean-stack
|
||||
@@ -6079,6 +6061,7 @@ packages:
|
||||
engines: {node: '>=7.0.0'}
|
||||
dependencies:
|
||||
color-name: registry.npmmirror.com/color-name/1.1.4
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/color-name/1.1.3:
|
||||
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/color-name/-/color-name-1.1.3.tgz}
|
||||
@@ -6089,25 +6072,7 @@ packages:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz}
|
||||
name: color-name
|
||||
version: 1.1.4
|
||||
|
||||
registry.npmmirror.com/color-string/1.9.1:
|
||||
resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/color-string/-/color-string-1.9.1.tgz}
|
||||
name: color-string
|
||||
version: 1.9.1
|
||||
dependencies:
|
||||
color-name: registry.npmmirror.com/color-name/1.1.4
|
||||
simple-swizzle: registry.npmmirror.com/simple-swizzle/0.2.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/color/4.2.3:
|
||||
resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/color/-/color-4.2.3.tgz}
|
||||
name: color
|
||||
version: 4.2.3
|
||||
engines: {node: '>=12.5.0'}
|
||||
dependencies:
|
||||
color-convert: registry.npmmirror.com/color-convert/2.0.1
|
||||
color-string: registry.npmmirror.com/color-string/1.9.1
|
||||
dev: false
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/color2k/2.0.2:
|
||||
resolution: {integrity: sha512-kJhwH5nAwb34tmyuqq/lgjEKzlFXn1U99NlnB6Ws4qVaERcRUYeYP1cBw6BJ4vxaWStAUEef4WMr7WjOCnBt8w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/color2k/-/color2k-2.0.2.tgz}
|
||||
@@ -6362,15 +6327,6 @@ packages:
|
||||
character-entities: registry.npmmirror.com/character-entities/2.0.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/decompress-response/6.0.0:
|
||||
resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz}
|
||||
name: decompress-response
|
||||
version: 6.0.0
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
mimic-response: registry.npmmirror.com/mimic-response/3.1.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/deep-equal/2.2.0:
|
||||
resolution: {integrity: sha512-RdpzE0Hv4lhowpIUKKMJfeH6C1pXdtT1/it80ubgWqwI3qpuxUBpC1S4hnHg+zjnuOoDkzUtUCEEkG+XG5l3Mw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/deep-equal/-/deep-equal-2.2.0.tgz}
|
||||
name: deep-equal
|
||||
@@ -6395,13 +6351,6 @@ packages:
|
||||
which-typed-array: registry.npmmirror.com/which-typed-array/1.1.9
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/deep-extend/0.6.0:
|
||||
resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/deep-extend/-/deep-extend-0.6.0.tgz}
|
||||
name: deep-extend
|
||||
version: 0.6.0
|
||||
engines: {node: '>=4.0.0'}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/deep-is/0.1.4:
|
||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz}
|
||||
name: deep-is
|
||||
@@ -6480,13 +6429,6 @@ packages:
|
||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/detect-libc/2.0.1:
|
||||
resolution: {integrity: sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.1.tgz}
|
||||
name: detect-libc
|
||||
version: 2.0.1
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/detect-node-es/1.1.0:
|
||||
resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/detect-node-es/-/detect-node-es-1.1.0.tgz}
|
||||
name: detect-node-es
|
||||
@@ -7171,13 +7113,6 @@ packages:
|
||||
strip-final-newline: registry.npmmirror.com/strip-final-newline/3.0.0
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/expand-template/2.0.3:
|
||||
resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/expand-template/-/expand-template-2.0.3.tgz}
|
||||
name: expand-template
|
||||
version: 2.0.3
|
||||
engines: {node: '>=6'}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/extend-shallow/2.0.1:
|
||||
resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/extend-shallow/-/extend-shallow-2.0.1.tgz}
|
||||
name: extend-shallow
|
||||
@@ -7399,12 +7334,6 @@ packages:
|
||||
tslib: registry.npmmirror.com/tslib/2.4.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/fs-constants/1.0.0:
|
||||
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/fs-constants/-/fs-constants-1.0.0.tgz}
|
||||
name: fs-constants
|
||||
version: 1.0.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/fs-extra/8.1.0:
|
||||
resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz}
|
||||
name: fs-extra
|
||||
@@ -7517,12 +7446,6 @@ packages:
|
||||
- supports-color
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/github-from-package/0.0.0:
|
||||
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/github-from-package/-/github-from-package-0.0.0.tgz}
|
||||
name: github-from-package
|
||||
version: 0.0.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/glob-parent/5.1.2:
|
||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz}
|
||||
name: glob-parent
|
||||
@@ -8062,12 +7985,6 @@ packages:
|
||||
name: is-arrayish
|
||||
version: 0.2.1
|
||||
|
||||
registry.npmmirror.com/is-arrayish/0.3.2:
|
||||
resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/is-arrayish/-/is-arrayish-0.3.2.tgz}
|
||||
name: is-arrayish
|
||||
version: 0.3.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/is-bigint/1.0.4:
|
||||
resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/is-bigint/-/is-bigint-1.0.4.tgz}
|
||||
name: is-bigint
|
||||
@@ -9291,13 +9208,6 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/mimic-response/3.1.0:
|
||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz}
|
||||
name: mimic-response
|
||||
version: 3.1.0
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/minimatch/3.1.2:
|
||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz}
|
||||
name: minimatch
|
||||
@@ -9311,12 +9221,6 @@ packages:
|
||||
name: minimist
|
||||
version: 1.2.8
|
||||
|
||||
registry.npmmirror.com/mkdirp-classic/0.5.3:
|
||||
resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz}
|
||||
name: mkdirp-classic
|
||||
version: 0.5.3
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/mkdirp/0.5.6:
|
||||
resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz}
|
||||
name: mkdirp
|
||||
@@ -9436,12 +9340,6 @@ packages:
|
||||
hasBin: true
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/napi-build-utils/1.0.2:
|
||||
resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/napi-build-utils/-/napi-build-utils-1.0.2.tgz}
|
||||
name: napi-build-utils
|
||||
version: 1.0.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/natural-compare/1.4.0:
|
||||
resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz}
|
||||
name: natural-compare
|
||||
@@ -9503,21 +9401,6 @@ packages:
|
||||
- babel-plugin-macros
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/node-abi/3.33.0:
|
||||
resolution: {integrity: sha512-7GGVawqyHF4pfd0YFybhv/eM9JwTtPqx0mAanQ146O3FlSh3pA24zf9IRQTOsfTSqXTNzPSP5iagAJ94jjuVog==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/node-abi/-/node-abi-3.33.0.tgz}
|
||||
name: node-abi
|
||||
version: 3.33.0
|
||||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
semver: registry.npmmirror.com/semver/7.3.8
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/node-addon-api/5.1.0:
|
||||
resolution: {integrity: sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/node-addon-api/-/node-addon-api-5.1.0.tgz}
|
||||
name: node-addon-api
|
||||
version: 5.1.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/node-releases/2.0.10:
|
||||
resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/node-releases/-/node-releases-2.0.10.tgz}
|
||||
name: node-releases
|
||||
@@ -10027,27 +9910,6 @@ packages:
|
||||
dependencies:
|
||||
xtend: registry.npmmirror.com/xtend/4.0.2
|
||||
|
||||
registry.npmmirror.com/prebuild-install/7.1.1:
|
||||
resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/prebuild-install/-/prebuild-install-7.1.1.tgz}
|
||||
name: prebuild-install
|
||||
version: 7.1.1
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
detect-libc: registry.npmmirror.com/detect-libc/2.0.1
|
||||
expand-template: registry.npmmirror.com/expand-template/2.0.3
|
||||
github-from-package: registry.npmmirror.com/github-from-package/0.0.0
|
||||
minimist: registry.npmmirror.com/minimist/1.2.8
|
||||
mkdirp-classic: registry.npmmirror.com/mkdirp-classic/0.5.3
|
||||
napi-build-utils: registry.npmmirror.com/napi-build-utils/1.0.2
|
||||
node-abi: registry.npmmirror.com/node-abi/3.33.0
|
||||
pump: registry.npmmirror.com/pump/3.0.0
|
||||
rc: registry.npmmirror.com/rc/1.2.8
|
||||
simple-get: registry.npmmirror.com/simple-get/4.0.1
|
||||
tar-fs: registry.npmmirror.com/tar-fs/2.1.1
|
||||
tunnel-agent: registry.npmmirror.com/tunnel-agent/0.6.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/prelude-ls/1.1.2:
|
||||
resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.1.2.tgz}
|
||||
name: prelude-ls
|
||||
@@ -10194,18 +10056,6 @@ packages:
|
||||
unpipe: registry.npmmirror.com/unpipe/1.0.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/rc/1.2.8:
|
||||
resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/rc/-/rc-1.2.8.tgz}
|
||||
name: rc
|
||||
version: 1.2.8
|
||||
hasBin: true
|
||||
dependencies:
|
||||
deep-extend: registry.npmmirror.com/deep-extend/0.6.0
|
||||
ini: registry.npmmirror.com/ini/1.3.8
|
||||
minimist: registry.npmmirror.com/minimist/1.2.8
|
||||
strip-json-comments: registry.npmmirror.com/strip-json-comments/2.0.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/react-clientside-effect/1.2.6_react@18.2.0:
|
||||
resolution: {integrity: sha512-XGGGRQAKY+q25Lz9a/4EPqom7WRjz3z9R2k4jhVKA/puQFH/5Nt27vFZYql4m4NVNdUvX8PS3O7r/Zzm7cjUlg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/react-clientside-effect/-/react-clientside-effect-1.2.6.tgz}
|
||||
id: registry.npmmirror.com/react-clientside-effect/1.2.6
|
||||
@@ -10429,17 +10279,6 @@ packages:
|
||||
util-deprecate: registry.npmmirror.com/util-deprecate/1.0.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/readable-stream/3.6.1:
|
||||
resolution: {integrity: sha512-+rQmrWMYGA90yenhTYsLWAsLsqVC8osOw6PKE1HDYiO0gdPeKe/xDHNzIAIn4C91YQ6oenEhfYqqc1883qHbjQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.1.tgz}
|
||||
name: readable-stream
|
||||
version: 3.6.1
|
||||
engines: {node: '>= 6'}
|
||||
dependencies:
|
||||
inherits: registry.npmmirror.com/inherits/2.0.4
|
||||
string_decoder: registry.npmmirror.com/string_decoder/1.3.0
|
||||
util-deprecate: registry.npmmirror.com/util-deprecate/1.0.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/readdirp/3.6.0:
|
||||
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz}
|
||||
name: readdirp
|
||||
@@ -10780,23 +10619,6 @@ packages:
|
||||
version: 1.2.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/sharp/0.31.3:
|
||||
resolution: {integrity: sha512-XcR4+FCLBFKw1bdB+GEhnUNXNXvnt0tDo4WsBsraKymuo/IAuPuCBVAL2wIkUw2r/dwFW5Q5+g66Kwl2dgDFVg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/sharp/-/sharp-0.31.3.tgz}
|
||||
name: sharp
|
||||
version: 0.31.3
|
||||
engines: {node: '>=14.15.0'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
color: registry.npmmirror.com/color/4.2.3
|
||||
detect-libc: registry.npmmirror.com/detect-libc/2.0.1
|
||||
node-addon-api: registry.npmmirror.com/node-addon-api/5.1.0
|
||||
prebuild-install: registry.npmmirror.com/prebuild-install/7.1.1
|
||||
semver: registry.npmmirror.com/semver/7.3.8
|
||||
simple-get: registry.npmmirror.com/simple-get/4.0.1
|
||||
tar-fs: registry.npmmirror.com/tar-fs/2.1.1
|
||||
tunnel-agent: registry.npmmirror.com/tunnel-agent/0.6.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/shebang-command/2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz}
|
||||
name: shebang-command
|
||||
@@ -10834,30 +10656,6 @@ packages:
|
||||
version: 3.0.7
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/simple-concat/1.0.1:
|
||||
resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/simple-concat/-/simple-concat-1.0.1.tgz}
|
||||
name: simple-concat
|
||||
version: 1.0.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/simple-get/4.0.1:
|
||||
resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/simple-get/-/simple-get-4.0.1.tgz}
|
||||
name: simple-get
|
||||
version: 4.0.1
|
||||
dependencies:
|
||||
decompress-response: registry.npmmirror.com/decompress-response/6.0.0
|
||||
once: registry.npmmirror.com/once/1.4.0
|
||||
simple-concat: registry.npmmirror.com/simple-concat/1.0.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/simple-swizzle/0.2.2:
|
||||
resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz}
|
||||
name: simple-swizzle
|
||||
version: 0.2.2
|
||||
dependencies:
|
||||
is-arrayish: registry.npmmirror.com/is-arrayish/0.3.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/slash/3.0.0:
|
||||
resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/slash/-/slash-3.0.0.tgz}
|
||||
name: slash
|
||||
@@ -11103,14 +10901,6 @@ packages:
|
||||
safe-buffer: registry.npmmirror.com/safe-buffer/5.1.2
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/string_decoder/1.3.0:
|
||||
resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/string_decoder/-/string_decoder-1.3.0.tgz}
|
||||
name: string_decoder
|
||||
version: 1.3.0
|
||||
dependencies:
|
||||
safe-buffer: registry.npmmirror.com/safe-buffer/5.2.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/strip-ansi/6.0.1:
|
||||
resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz}
|
||||
name: strip-ansi
|
||||
@@ -11143,13 +10933,6 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/strip-json-comments/2.0.1:
|
||||
resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz}
|
||||
name: strip-json-comments
|
||||
version: 2.0.1
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/strip-json-comments/3.1.1:
|
||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz}
|
||||
name: strip-json-comments
|
||||
@@ -11260,30 +11043,6 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
dev: true
|
||||
|
||||
registry.npmmirror.com/tar-fs/2.1.1:
|
||||
resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tar-fs/-/tar-fs-2.1.1.tgz}
|
||||
name: tar-fs
|
||||
version: 2.1.1
|
||||
dependencies:
|
||||
chownr: registry.npmmirror.com/chownr/1.1.4
|
||||
mkdirp-classic: registry.npmmirror.com/mkdirp-classic/0.5.3
|
||||
pump: registry.npmmirror.com/pump/3.0.0
|
||||
tar-stream: registry.npmmirror.com/tar-stream/2.2.0
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/tar-stream/2.2.0:
|
||||
resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tar-stream/-/tar-stream-2.2.0.tgz}
|
||||
name: tar-stream
|
||||
version: 2.2.0
|
||||
engines: {node: '>=6'}
|
||||
dependencies:
|
||||
bl: registry.npmmirror.com/bl/4.1.0
|
||||
end-of-stream: registry.npmmirror.com/end-of-stream/1.4.4
|
||||
fs-constants: registry.npmmirror.com/fs-constants/1.0.0
|
||||
inherits: registry.npmmirror.com/inherits/2.0.4
|
||||
readable-stream: registry.npmmirror.com/readable-stream/3.6.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/text-table/0.2.0:
|
||||
resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/text-table/-/text-table-0.2.0.tgz}
|
||||
name: text-table
|
||||
@@ -11424,14 +11183,6 @@ packages:
|
||||
tslib: registry.npmmirror.com/tslib/1.14.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/tunnel-agent/0.6.0:
|
||||
resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz}
|
||||
name: tunnel-agent
|
||||
version: 0.6.0
|
||||
dependencies:
|
||||
safe-buffer: registry.npmmirror.com/safe-buffer/5.2.1
|
||||
dev: false
|
||||
|
||||
registry.npmmirror.com/tunnel/0.0.6:
|
||||
resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/tunnel/-/tunnel-0.0.6.tgz}
|
||||
name: tunnel
|
||||
|
||||
@@ -14,14 +14,13 @@
|
||||
如果使用了自己的 Api Key,不会计费。可以在账号页,看到详细账单。
|
||||
| 计费项 | 价格: 元/ 1K tokens(包含上下文)|
|
||||
| --- | --- |
|
||||
| claude - 对话 | 免费 |
|
||||
| 知识库 - 索引 | 免费 |
|
||||
| chatgpt - 对话 | 0.025 |
|
||||
| gpt4 - 对话 | 0.5 |
|
||||
| 文件拆分 | 0.025 |
|
||||
|
||||
**其他问题**
|
||||
请 WX 联系: fastgpt123
|
||||
请 WX 联系: YNyiqi
|
||||
| 交流群 | 小助手 |
|
||||
| ----------------------- | -------------------- |
|
||||
|  |  |
|
||||
|
||||
@@ -18,7 +18,6 @@ FastGpt 项目完全开源,可随意私有化部署,去除平台风险忧虑
|
||||
如果使用了自己的 Api Key,不会计费。可以在账号页,看到详细账单。
|
||||
| 计费项 | 价格: 元/ 1K tokens(包含上下文)|
|
||||
| --- | --- |
|
||||
| claude - 对话 | 免费 |
|
||||
| 知识库 - 索引 | 免费 |
|
||||
| chatgpt - 对话 | 0.025 |
|
||||
| gpt4 - 对话 | 0.5 |
|
||||
@@ -27,7 +26,7 @@ FastGpt 项目完全开源,可随意私有化部署,去除平台风险忧虑
|
||||
### 交流群/问题反馈
|
||||
|
||||
如果群满了,可加个小助手,定时拉
|
||||
wx 号: fastgpt123
|
||||
wx 号: YNyiqi
|
||||
|
||||
| 交流群 | 小助手 |
|
||||
| ------------------------------------------------- | ---------------------------------------------- |
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
### Fast GPT V3.8
|
||||
### Fast GPT V3.8.1
|
||||
|
||||
- 新增 - 知识库引用反馈。
|
||||
- 新增 - 知识库与 AI 助手对多对关系,一个知识库可以被多个 AI 助手关联,一个 AI 助手可以关联多个知识库。
|
||||
1. 新增 - 自定义历史记录标题。
|
||||
2. 新增 - 置顶历史记录。
|
||||
3. 新增 - 自动置顶最近聊天的模型。
|
||||
4. 优化 - 索引和 QA 队列,支持多节点和并发(目前线上大概 2500 条/分钟)
|
||||
5. 优化 - 随机任务,不再按线性等待。
|
||||
6. 优化 - 内容分段导入和进度查看,不再担心大文件无法导入
|
||||
7. 优化 - 导出的 csv 大小。最大支持 100M 导出。
|
||||
8. 知识库数量说明,由于线上数据太多,没法创建索引,所以目前如果超过 5w 组数据,大概率会失败。
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 52 KiB |
@@ -1,11 +1,12 @@
|
||||
import { GET, POST, DELETE } from './request';
|
||||
import { GET, POST, DELETE, PUT } from './request';
|
||||
import type { HistoryItemType } from '@/types/chat';
|
||||
import type { InitChatResponse, InitShareChatResponse } from './response/chat';
|
||||
import { RequestPaging } from '../types/index';
|
||||
import type { ShareChatSchema } from '@/types/mongoSchema';
|
||||
import type { ShareChatEditType } from '@/types/model';
|
||||
import { Obj2Query } from '@/utils/tools';
|
||||
import { QuoteItemType } from '@/pages/api/openapi/kb/appKbSearch';
|
||||
import type { QuoteItemType } from '@/pages/api/openapi/kb/appKbSearch';
|
||||
import type { Props as UpdateHistoryProps } from '@/pages/api/chat/history/updateChatHistory';
|
||||
|
||||
/**
|
||||
* 获取初始化聊天内容
|
||||
@@ -17,7 +18,7 @@ export const getInitChatSiteInfo = (modelId: '' | string, chatId: '' | string) =
|
||||
* 获取历史记录
|
||||
*/
|
||||
export const getChatHistory = (data: RequestPaging) =>
|
||||
POST<HistoryItemType[]>('/chat/getHistory', data);
|
||||
POST<HistoryItemType[]>('/chat/history/getHistory', data);
|
||||
|
||||
/**
|
||||
* 删除一条历史记录
|
||||
@@ -28,7 +29,7 @@ export const delChatHistoryById = (id: string) => GET(`/chat/removeHistory?id=${
|
||||
* get history quotes
|
||||
*/
|
||||
export const getHistoryQuote = (params: { chatId: string; historyId: string }) =>
|
||||
GET<(QuoteItemType & { _id: string })[]>(`/chat/getHistoryQuote`, params);
|
||||
GET<(QuoteItemType & { _id: string })[]>(`/chat/history/getHistoryQuote`, params);
|
||||
|
||||
/**
|
||||
* update history quote status
|
||||
@@ -37,7 +38,7 @@ export const updateHistoryQuote = (params: {
|
||||
chatId: string;
|
||||
historyId: string;
|
||||
quoteId: string;
|
||||
}) => GET(`/chat/updateHistoryQuote`, params);
|
||||
}) => GET(`/chat/history/updateHistoryQuote`, params);
|
||||
|
||||
/**
|
||||
* 删除一句对话
|
||||
@@ -45,6 +46,12 @@ export const updateHistoryQuote = (params: {
|
||||
export const delChatRecordByIndex = (chatId: string, contentId: string) =>
|
||||
DELETE(`/chat/delChatRecordByContentId?chatId=${chatId}&contentId=${contentId}`);
|
||||
|
||||
/**
|
||||
* 修改历史记录: 标题/置顶
|
||||
*/
|
||||
export const putChatHistory = (data: UpdateHistoryProps) =>
|
||||
PUT('/chat/history/updateChatHistory', data);
|
||||
|
||||
/**
|
||||
* create a shareChat
|
||||
*/
|
||||
@@ -55,7 +62,7 @@ export const createShareChat = (
|
||||
) => POST<string>(`/chat/shareChat/create`, data);
|
||||
|
||||
/**
|
||||
* get shareChat
|
||||
* get shareChat
|
||||
*/
|
||||
export const getShareChatList = (modelId: string) =>
|
||||
GET<ShareChatSchema[]>(`/chat/shareChat/list?modelId=${modelId}`);
|
||||
|
||||
@@ -50,7 +50,7 @@ export const streamFetch = ({ url, data, onMessage, abortSignal }: StreamFetchPr
|
||||
read();
|
||||
} catch (err: any) {
|
||||
if (err?.message === 'The user aborted a request.') {
|
||||
return resolve({ responseText, newChatId, quoteLen: 0, systemPrompt: '' });
|
||||
return resolve({ responseText, newChatId, quoteLen, systemPrompt: '' });
|
||||
}
|
||||
reject(typeof err === 'string' ? err : err?.message || '请求异常');
|
||||
}
|
||||
|
||||
1
src/api/response/chat.d.ts
vendored
1
src/api/response/chat.d.ts
vendored
@@ -16,6 +16,7 @@ export interface InitChatResponse {
|
||||
|
||||
export interface InitShareChatResponse {
|
||||
maxContext: number;
|
||||
userAvatar: string;
|
||||
model: {
|
||||
name: string;
|
||||
avatar: string;
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
import React from 'react';
|
||||
import { Image } from '@chakra-ui/react';
|
||||
import type { ImageProps } from '@chakra-ui/react';
|
||||
import { LOGO_ICON } from '@/constants/chat';
|
||||
|
||||
const Avatar = ({ w = '30px', ...props }: ImageProps) => {
|
||||
return (
|
||||
<Image
|
||||
fallbackSrc="/icon/logo.png"
|
||||
fallbackSrc={LOGO_ICON}
|
||||
borderRadius={'50%'}
|
||||
objectFit={'cover'}
|
||||
alt=""
|
||||
w={w}
|
||||
h={w}
|
||||
p={'1px'}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ import MyIcon from '../Icon';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import Avatar from '../Avatar';
|
||||
import { HUMAN_ICON } from '@/constants/chat';
|
||||
|
||||
export enum NavbarTypeEnum {
|
||||
normal = 'normal',
|
||||
@@ -83,7 +84,7 @@ const Navbar = () => {
|
||||
cursor={'pointer'}
|
||||
onClick={() => router.push('/number')}
|
||||
>
|
||||
<Avatar w={'36px'} h={'36px'} src={userInfo?.avatar} fallbackSrc={'/icon/human.png'} />
|
||||
<Avatar w={'36px'} h={'36px'} src={userInfo?.avatar} fallbackSrc={HUMAN_ICON} />
|
||||
</Box>
|
||||
{/* 导航列表 */}
|
||||
<Box flex={1}>
|
||||
|
||||
@@ -31,7 +31,7 @@ const WxConcat = ({ onClose }: { onClose: () => void }) => {
|
||||
<Box mt={2}>
|
||||
微信号:
|
||||
<Box as={'span'} userSelect={'all'}>
|
||||
fastgpt123
|
||||
YNyiqi
|
||||
</Box>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
|
||||
File diff suppressed because one or more lines are too long
91
src/hooks/useEditInfo.tsx
Normal file
91
src/hooks/useEditInfo.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
import React, { useCallback, useRef } from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalFooter,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
Input,
|
||||
useDisclosure,
|
||||
Button
|
||||
} from '@chakra-ui/react';
|
||||
|
||||
export const useEditInfo = ({
|
||||
title,
|
||||
placeholder = ''
|
||||
}: {
|
||||
title: string;
|
||||
placeholder?: string;
|
||||
}) => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
|
||||
const inputRef = useRef<HTMLInputElement | null>(null);
|
||||
const onSuccessCb = useRef<(content: string) => void | Promise<void>>();
|
||||
const onErrorCb = useRef<(err: any) => void>();
|
||||
const defaultValue = useRef('');
|
||||
|
||||
const onOpenModal = useCallback(
|
||||
({
|
||||
defaultVal,
|
||||
onSuccess,
|
||||
onError
|
||||
}: {
|
||||
defaultVal: string;
|
||||
onSuccess: (content: string) => any;
|
||||
onError?: (err: any) => void;
|
||||
}) => {
|
||||
onOpen();
|
||||
onSuccessCb.current = onSuccess;
|
||||
onErrorCb.current = onError;
|
||||
defaultValue.current = defaultVal;
|
||||
},
|
||||
[onOpen]
|
||||
);
|
||||
|
||||
const onclickConfirm = useCallback(async () => {
|
||||
if (!inputRef.current) return;
|
||||
try {
|
||||
const val = inputRef.current.value;
|
||||
await onSuccessCb.current?.(val);
|
||||
onClose();
|
||||
} catch (err) {
|
||||
onErrorCb.current?.(err);
|
||||
}
|
||||
}, [onClose]);
|
||||
|
||||
// eslint-disable-next-line react/display-name
|
||||
const EditModal = useCallback(
|
||||
() => (
|
||||
<Modal isOpen={isOpen} onClose={onClose}>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader>{title}</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody>
|
||||
<Input
|
||||
ref={inputRef}
|
||||
defaultValue={defaultValue.current}
|
||||
placeholder={placeholder}
|
||||
autoFocus
|
||||
maxLength={20}
|
||||
/>
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button mr={3} variant={'outline'} onClick={onClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button onClick={onclickConfirm}>确认</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
),
|
||||
[isOpen, onClose, onclickConfirm, placeholder, title]
|
||||
);
|
||||
|
||||
return {
|
||||
onOpenModal,
|
||||
EditModal
|
||||
};
|
||||
};
|
||||
@@ -50,6 +50,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
|
||||
// 读取对话内容
|
||||
const prompts = [...content, prompt[0]];
|
||||
|
||||
const {
|
||||
code = 200,
|
||||
systemPrompts = [],
|
||||
@@ -61,7 +62,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const { code, searchPrompts, rawSearch, guidePrompt } = await appKbSearch({
|
||||
model,
|
||||
userId,
|
||||
prompts,
|
||||
fixedQuote: content[content.length - 1]?.quote || [],
|
||||
prompt: prompt[0],
|
||||
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity
|
||||
});
|
||||
|
||||
@@ -114,7 +116,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
return res.end(response);
|
||||
}
|
||||
|
||||
prompts.splice(prompts.length - 3, 0, ...systemPrompts);
|
||||
prompts.unshift(...systemPrompts);
|
||||
|
||||
// content check
|
||||
await sensitiveCheck({
|
||||
@@ -127,7 +129,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
);
|
||||
|
||||
// 发出 chat 请求
|
||||
const { streamResponse } = await modelServiceToolMap[model.chat.chatModel].chatCompletion({
|
||||
const { streamResponse, responseMessages } = await modelServiceToolMap[
|
||||
model.chat.chatModel
|
||||
].chatCompletion({
|
||||
apiKey: userOpenAiKey || systemAuthKey,
|
||||
temperature: +temperature,
|
||||
messages: prompts,
|
||||
@@ -145,7 +149,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
model: model.chat.chatModel,
|
||||
res,
|
||||
chatResponse: streamResponse,
|
||||
prompts
|
||||
prompts: responseMessages
|
||||
});
|
||||
|
||||
// save chat
|
||||
|
||||
39
src/pages/api/chat/history/getHistory.ts
Normal file
39
src/pages/api/chat/history/getHistory.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
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 { HistoryItemType } from '@/types/chat';
|
||||
|
||||
/* 获取历史记录 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const { userId } = await authUser({ req, authToken: true });
|
||||
|
||||
await connectToDatabase();
|
||||
|
||||
const data = await Chat.find(
|
||||
{
|
||||
userId
|
||||
},
|
||||
'_id title top customTitle modelId updateTime latestChat'
|
||||
)
|
||||
.sort({ top: -1, updateTime: -1 })
|
||||
.limit(20);
|
||||
|
||||
jsonRes<HistoryItemType[]>(res, {
|
||||
data: data.map((item) => ({
|
||||
_id: item._id,
|
||||
updateTime: item.updateTime,
|
||||
modelId: item.modelId,
|
||||
title: item.customTitle || item.title,
|
||||
latestChat: item.latestChat,
|
||||
top: item.top
|
||||
}))
|
||||
});
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error: err
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -3,25 +3,32 @@ 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();
|
||||
|
||||
const data = await Chat.find(
|
||||
await Chat.findOneAndUpdate(
|
||||
{
|
||||
_id: chatId,
|
||||
userId
|
||||
},
|
||||
'_id title modelId updateTime latestChat'
|
||||
)
|
||||
.sort({ updateTime: -1 })
|
||||
.limit(20);
|
||||
|
||||
jsonRes(res, {
|
||||
data
|
||||
});
|
||||
{
|
||||
...(customTitle ? { customTitle } : {}),
|
||||
...(top ? { top } : { top: null })
|
||||
}
|
||||
);
|
||||
jsonRes(res);
|
||||
} catch (err) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
@@ -27,7 +27,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
'content.$.quote.$[quoteElem].isEdit': true
|
||||
'content.$.quote.$[quoteElem].source': '手动修改'
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { ChatItemType } from '@/types/chat';
|
||||
import { connectToDatabase, Chat } from '@/service/mongo';
|
||||
import { connectToDatabase, Chat, Model } from '@/service/mongo';
|
||||
import { authModel } from '@/service/utils/auth';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import mongoose from 'mongoose';
|
||||
@@ -51,42 +51,51 @@ export async function saveChat({
|
||||
userId
|
||||
}: Props & { userId: string }) {
|
||||
await connectToDatabase();
|
||||
await authModel({ modelId, userId, authOwner: false });
|
||||
const { model } = await authModel({ modelId, userId, authOwner: false });
|
||||
|
||||
const content = prompts.map((item) => ({
|
||||
_id: item._id ? new mongoose.Types.ObjectId(item._id) : undefined,
|
||||
obj: item.obj,
|
||||
value: item.value,
|
||||
systemPrompt: item.systemPrompt,
|
||||
quote:
|
||||
item.quote?.map((item) => ({
|
||||
...item,
|
||||
isEdit: false
|
||||
})) || []
|
||||
quote: item.quote || []
|
||||
}));
|
||||
|
||||
// 没有 chatId, 创建一个对话
|
||||
if (!chatId) {
|
||||
const { _id } = await Chat.create({
|
||||
_id: newChatId ? new mongoose.Types.ObjectId(newChatId) : undefined,
|
||||
userId,
|
||||
modelId,
|
||||
content,
|
||||
title: content[0].value.slice(0, 20),
|
||||
latestChat: content[1].value
|
||||
});
|
||||
return _id;
|
||||
} else {
|
||||
// 已经有记录,追加入库
|
||||
await Chat.findByIdAndUpdate(chatId, {
|
||||
$push: {
|
||||
content: {
|
||||
$each: content
|
||||
}
|
||||
},
|
||||
title: content[0].value.slice(0, 20),
|
||||
latestChat: content[1].value,
|
||||
updateTime: new Date()
|
||||
});
|
||||
}
|
||||
const [id] = await Promise.all([
|
||||
...(chatId // update chat
|
||||
? [
|
||||
Chat.findByIdAndUpdate(chatId, {
|
||||
$push: {
|
||||
content: {
|
||||
$each: content
|
||||
}
|
||||
},
|
||||
title: content[0].value.slice(0, 20),
|
||||
latestChat: content[1].value,
|
||||
updateTime: new Date()
|
||||
}).then(() => '')
|
||||
]
|
||||
: [
|
||||
Chat.create({
|
||||
_id: newChatId ? new mongoose.Types.ObjectId(newChatId) : undefined,
|
||||
userId,
|
||||
modelId,
|
||||
content,
|
||||
title: content[0].value.slice(0, 20),
|
||||
latestChat: content[1].value
|
||||
}).then((res) => res._id)
|
||||
]),
|
||||
// update model
|
||||
...(String(model.userId) === userId
|
||||
? [
|
||||
Model.findByIdAndUpdate(modelId, {
|
||||
updateTime: new Date()
|
||||
})
|
||||
]
|
||||
: [])
|
||||
]);
|
||||
|
||||
return {
|
||||
id
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,7 +47,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
const { code, searchPrompts } = await appKbSearch({
|
||||
model,
|
||||
userId,
|
||||
prompts,
|
||||
fixedQuote: [],
|
||||
prompt: prompts[prompts.length - 1],
|
||||
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity
|
||||
});
|
||||
|
||||
@@ -74,7 +75,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
return res.send(systemPrompts[0]?.value);
|
||||
}
|
||||
|
||||
prompts.splice(prompts.length - 3, 0, ...systemPrompts);
|
||||
prompts.unshift(...systemPrompts);
|
||||
|
||||
// content check
|
||||
await sensitiveCheck({
|
||||
@@ -87,7 +88,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
);
|
||||
|
||||
// 发出请求
|
||||
const { streamResponse } = await modelServiceToolMap[model.chat.chatModel].chatCompletion({
|
||||
const { streamResponse, responseMessages } = await modelServiceToolMap[
|
||||
model.chat.chatModel
|
||||
].chatCompletion({
|
||||
apiKey: userOpenAiKey || systemAuthKey,
|
||||
temperature: +temperature,
|
||||
messages: prompts,
|
||||
@@ -105,7 +108,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
model: model.chat.chatModel,
|
||||
res,
|
||||
chatResponse: streamResponse,
|
||||
prompts
|
||||
prompts: responseMessages
|
||||
});
|
||||
|
||||
res.end();
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, ShareChat } from '@/service/mongo';
|
||||
import { connectToDatabase, ShareChat, User } from '@/service/mongo';
|
||||
import type { InitShareChatResponse } from '@/api/response/chat';
|
||||
import { authModel } from '@/service/utils/auth';
|
||||
import { hashPassword } from '@/service/utils/tools';
|
||||
import { HUMAN_ICON } from '@/constants/chat';
|
||||
|
||||
/* 初始化我的聊天框,需要身份验证 */
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
@@ -40,9 +41,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
authOwner: false
|
||||
});
|
||||
|
||||
const user = await User.findById(shareChat.userId, 'avatar');
|
||||
|
||||
jsonRes<InitShareChatResponse>(res, {
|
||||
data: {
|
||||
maxContext: shareChat.maxContext,
|
||||
userAvatar: user?.avatar || HUMAN_ICON,
|
||||
model: {
|
||||
name: model.name,
|
||||
avatar: model.avatar,
|
||||
|
||||
@@ -20,7 +20,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
},
|
||||
'_id avatar name chat.systemPrompt'
|
||||
).sort({
|
||||
_id: -1
|
||||
updateTime: -1
|
||||
}),
|
||||
Collection.find({ userId })
|
||||
.populate({
|
||||
|
||||
@@ -75,10 +75,11 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
// 使用了知识库搜索
|
||||
if (model.chat.relatedKbs.length > 0) {
|
||||
const { code, searchPrompts } = await appKbSearch({
|
||||
prompts,
|
||||
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity,
|
||||
model,
|
||||
userId
|
||||
userId,
|
||||
fixedQuote: [],
|
||||
prompt: prompts[prompts.length - 1],
|
||||
similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity
|
||||
});
|
||||
|
||||
// search result is empty
|
||||
@@ -101,7 +102,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
];
|
||||
}
|
||||
|
||||
prompts.splice(prompts.length - 3, 0, ...systemPrompts);
|
||||
prompts.unshift(...systemPrompts);
|
||||
|
||||
// content check
|
||||
await sensitiveCheck({
|
||||
@@ -139,7 +140,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
model: model.chat.chatModel,
|
||||
res,
|
||||
chatResponse: streamResponse,
|
||||
prompts
|
||||
prompts: responseMessages
|
||||
});
|
||||
res.end();
|
||||
return {
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ChatRoleEnum } from '@/constants/chat';
|
||||
import { openaiEmbedding } from '../plugin/openaiEmbedding';
|
||||
import { modelToolMap } from '@/utils/plugin';
|
||||
|
||||
export type QuoteItemType = { id: string; q: string; a: string; isEdit: boolean };
|
||||
export type QuoteItemType = { id: string; q: string; a: string; source?: string };
|
||||
type Props = {
|
||||
prompts: ChatItemSimpleType[];
|
||||
similarity: number;
|
||||
@@ -49,10 +49,11 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
});
|
||||
|
||||
const result = await appKbSearch({
|
||||
model,
|
||||
userId,
|
||||
prompts,
|
||||
similarity,
|
||||
model
|
||||
fixedQuote: [],
|
||||
prompt: prompts[prompts.length - 1],
|
||||
similarity
|
||||
});
|
||||
|
||||
jsonRes<Response>(res, {
|
||||
@@ -70,67 +71,53 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
export async function appKbSearch({
|
||||
model,
|
||||
userId,
|
||||
prompts,
|
||||
fixedQuote,
|
||||
prompt,
|
||||
similarity
|
||||
}: {
|
||||
userId: string;
|
||||
prompts: ChatItemSimpleType[];
|
||||
similarity: number;
|
||||
model: ModelSchema;
|
||||
userId: string;
|
||||
fixedQuote: QuoteItemType[];
|
||||
prompt: ChatItemSimpleType;
|
||||
similarity: number;
|
||||
}): Promise<Response> {
|
||||
const modelConstantsData = ChatModelMap[model.chat.chatModel];
|
||||
|
||||
// search two times.
|
||||
const userPrompts = prompts.filter((item) => item.obj === 'Human');
|
||||
|
||||
const input: string[] = [
|
||||
userPrompts[userPrompts.length - 1].value,
|
||||
userPrompts[userPrompts.length - 2]?.value
|
||||
].filter((item) => item);
|
||||
|
||||
// get vector
|
||||
const promptVectors = await openaiEmbedding({
|
||||
const promptVector = await openaiEmbedding({
|
||||
userId,
|
||||
input,
|
||||
input: [prompt.value],
|
||||
type: 'chat'
|
||||
});
|
||||
|
||||
// search kb
|
||||
const searchRes = await Promise.all(
|
||||
promptVectors.map((promptVector) =>
|
||||
PgClient.select<QuoteItemType>('modelData', {
|
||||
fields: ['id', 'q', 'a'],
|
||||
where: [
|
||||
`kb_id IN (${model.chat.relatedKbs.map((item) => `'${item}'`).join(',')})`,
|
||||
'AND',
|
||||
`vector <=> '[${promptVector}]' < ${similarity}`
|
||||
],
|
||||
order: [{ field: 'vector', mode: `<=> '[${promptVector}]'` }],
|
||||
limit: promptVectors.length === 1 ? 15 : 10
|
||||
}).then((res) => res.rows)
|
||||
)
|
||||
);
|
||||
const { rows: searchRes } = await PgClient.select<QuoteItemType>('modelData', {
|
||||
fields: ['id', 'q', 'a', 'source'],
|
||||
where: [
|
||||
`kb_id IN (${model.chat.relatedKbs.map((item) => `'${item}'`).join(',')})`,
|
||||
'AND',
|
||||
`vector <=> '[${promptVector[0]}]' < ${similarity}`
|
||||
],
|
||||
order: [{ field: 'vector', mode: `<=> '[${promptVector[0]}]'` }],
|
||||
limit: 8
|
||||
});
|
||||
|
||||
// filter same search result
|
||||
const idSet = new Set<string>();
|
||||
const filterSearch = searchRes.map((search) =>
|
||||
search.filter((item) => {
|
||||
if (idSet.has(item.id)) {
|
||||
return false;
|
||||
}
|
||||
idSet.add(item.id);
|
||||
return true;
|
||||
})
|
||||
);
|
||||
const filterSearch = [
|
||||
...searchRes.slice(0, 3),
|
||||
...fixedQuote.slice(0, 2),
|
||||
...searchRes.slice(3),
|
||||
...fixedQuote.slice(2, 5)
|
||||
].filter((item) => {
|
||||
if (idSet.has(item.id)) {
|
||||
return false;
|
||||
}
|
||||
idSet.add(item.id);
|
||||
return true;
|
||||
});
|
||||
|
||||
// slice search result by rate.
|
||||
const sliceRateMap: Record<number, number[]> = {
|
||||
1: [1],
|
||||
2: [0.7, 0.3]
|
||||
};
|
||||
const sliceRate = sliceRateMap[searchRes.length] || sliceRateMap[0];
|
||||
// 计算固定提示词的 token 数量
|
||||
|
||||
const guidePrompt = model.chat.systemPrompt // user system prompt
|
||||
? {
|
||||
obj: ChatRoleEnum.System,
|
||||
@@ -154,24 +141,21 @@ export async function appKbSearch({
|
||||
const fixedSystemTokens = modelToolMap[model.chat.chatModel].countTokens({
|
||||
messages: [guidePrompt]
|
||||
});
|
||||
const maxTokens = modelConstantsData.systemMaxToken - fixedSystemTokens;
|
||||
const sliceResult = sliceRate.map((rate, i) =>
|
||||
modelToolMap[model.chat.chatModel]
|
||||
.tokenSlice({
|
||||
maxToken: Math.round(maxTokens * rate),
|
||||
messages: filterSearch[i].map((item) => ({
|
||||
obj: ChatRoleEnum.System,
|
||||
value: `${item.q}\n${item.a}`
|
||||
}))
|
||||
})
|
||||
.map((item) => item.value)
|
||||
);
|
||||
const sliceResult = modelToolMap[model.chat.chatModel]
|
||||
.tokenSlice({
|
||||
maxToken: modelConstantsData.systemMaxToken - fixedSystemTokens,
|
||||
messages: filterSearch.map((item) => ({
|
||||
obj: ChatRoleEnum.System,
|
||||
value: `${item.q}\n${item.a}`
|
||||
}))
|
||||
})
|
||||
.map((item) => item.value);
|
||||
|
||||
// slice filterSearch
|
||||
const sliceSearch = filterSearch.map((item, i) => item.slice(0, sliceResult[i].length)).flat();
|
||||
const rawSearch = filterSearch.slice(0, sliceResult.length);
|
||||
|
||||
// system prompt
|
||||
const systemPrompt = sliceResult.flat().join('\n').trim();
|
||||
const systemPrompt = sliceResult.join('\n').trim();
|
||||
|
||||
/* 高相似度+不回复 */
|
||||
if (!systemPrompt && model.chat.searchMode === appVectorSearchModeEnum.hightSimilarity) {
|
||||
@@ -206,7 +190,7 @@ export async function appKbSearch({
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
rawSearch: sliceSearch,
|
||||
rawSearch,
|
||||
guidePrompt: guidePrompt.value || '',
|
||||
searchPrompts: [
|
||||
{
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import type { KbDataItemType } from '@/types/plugin';
|
||||
import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase, TrainingData } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
@@ -9,9 +8,11 @@ import { TrainingModeEnum } from '@/constants/plugin';
|
||||
import { startQueue } from '@/service/utils/tools';
|
||||
import { PgClient } from '@/service/pg';
|
||||
|
||||
type DateItemType = { a: string; q: string; source?: string };
|
||||
|
||||
export type Props = {
|
||||
kbId: string;
|
||||
data: { a: KbDataItemType['a']; q: KbDataItemType['q'] }[];
|
||||
data: DateItemType[];
|
||||
mode: `${TrainingModeEnum}`;
|
||||
prompt?: string;
|
||||
};
|
||||
@@ -63,10 +64,7 @@ export async function pushDataToKb({
|
||||
|
||||
// 过滤重复的 qa 内容
|
||||
const set = new Set();
|
||||
const filterData: {
|
||||
a: string;
|
||||
q: string;
|
||||
}[] = [];
|
||||
const filterData: DateItemType[] = [];
|
||||
|
||||
data.forEach((item) => {
|
||||
const text = item.q + item.a;
|
||||
@@ -79,11 +77,12 @@ export async function pushDataToKb({
|
||||
// 数据库去重
|
||||
const insertData = (
|
||||
await Promise.allSettled(
|
||||
filterData.map(async ({ q, a = '' }) => {
|
||||
filterData.map(async ({ q, a = '', source }) => {
|
||||
if (mode !== TrainingModeEnum.index) {
|
||||
return Promise.resolve({
|
||||
q,
|
||||
a
|
||||
a,
|
||||
source
|
||||
});
|
||||
}
|
||||
|
||||
@@ -112,19 +111,21 @@ export async function pushDataToKb({
|
||||
}
|
||||
return Promise.resolve({
|
||||
q,
|
||||
a
|
||||
a,
|
||||
source
|
||||
});
|
||||
})
|
||||
)
|
||||
)
|
||||
.filter((item) => item.status === 'fulfilled')
|
||||
.map<{ q: string; a: string }>((item: any) => item.value);
|
||||
.map<DateItemType>((item: any) => item.value);
|
||||
|
||||
// 插入记录
|
||||
await TrainingData.insertMany(
|
||||
insertData.map((item) => ({
|
||||
q: item.q,
|
||||
a: item.a,
|
||||
source: item.source,
|
||||
userId,
|
||||
kbId,
|
||||
mode,
|
||||
|
||||
@@ -32,6 +32,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
|
||||
await PgClient.update('modelData', {
|
||||
where: [['id', dataId], 'AND', ['user_id', userId]],
|
||||
values: [
|
||||
{ key: 'source', value: '手动修改' },
|
||||
{ key: 'a', value: a.replace(/'/g, '"') },
|
||||
...(q
|
||||
? [
|
||||
|
||||
@@ -3,7 +3,7 @@ import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { PgClient } from '@/service/pg';
|
||||
import type { PgKBDataItemType } from '@/types/pg';
|
||||
import type { KbDataItemType } from '@/types/plugin';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
@@ -21,8 +21,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
|
||||
const where: any = [['user_id', userId], 'AND', ['id', dataId]];
|
||||
|
||||
const searchRes = await PgClient.select<PgKBDataItemType>('modelData', {
|
||||
fields: ['id', 'q', 'a'],
|
||||
const searchRes = await PgClient.select<KbDataItemType>('modelData', {
|
||||
fields: ['id', 'q', 'a', 'source'],
|
||||
where,
|
||||
limit: 1
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import { jsonRes } from '@/service/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { authUser } from '@/service/utils/auth';
|
||||
import { PgClient } from '@/service/pg';
|
||||
import type { PgKBDataItemType } from '@/types/pg';
|
||||
import type { KbDataItemType } from '@/types/plugin';
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||
try {
|
||||
@@ -31,11 +31,16 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
['user_id', userId],
|
||||
'AND',
|
||||
['kb_id', kbId],
|
||||
...(searchText ? ['AND', `(q LIKE '%${searchText}%' OR a LIKE '%${searchText}%')`] : [])
|
||||
...(searchText
|
||||
? [
|
||||
'AND',
|
||||
`(q LIKE '%${searchText}%' OR a LIKE '%${searchText}%' OR source LIKE '%${searchText}%')`
|
||||
]
|
||||
: [])
|
||||
];
|
||||
|
||||
const searchRes = await PgClient.select<PgKBDataItemType>('modelData', {
|
||||
fields: ['id', 'q', 'a'],
|
||||
const searchRes = await PgClient.select<KbDataItemType>('modelData', {
|
||||
fields: ['id', 'q', 'a', 'source'],
|
||||
where,
|
||||
order: [{ field: 'id', mode: 'DESC' }],
|
||||
limit: pageSize,
|
||||
|
||||
@@ -2,7 +2,6 @@ import React from 'react';
|
||||
import { Card, Box, Flex } from '@chakra-ui/react';
|
||||
import { useMarkdown } from '@/hooks/useMarkdown';
|
||||
import Markdown from '@/components/Markdown';
|
||||
import { LOGO_ICON } from '@/constants/chat';
|
||||
import Avatar from '@/components/Avatar';
|
||||
|
||||
const Empty = ({
|
||||
|
||||
@@ -16,14 +16,16 @@ import { useQuery } from '@tanstack/react-query';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { useUserStore } from '@/store/user';
|
||||
import { formatTimeToChatTime } from '@/utils/tools';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import type { HistoryItemType, ExportChatType } from '@/types/chat';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import ModelList from './ModelList';
|
||||
import { useGlobalStore } from '@/store/global';
|
||||
|
||||
import styles from '../index.module.scss';
|
||||
import { useEditInfo } from '@/hooks/useEditInfo';
|
||||
import { putChatHistory } from '@/api/chat';
|
||||
import { useToast } from '@/hooks/useToast';
|
||||
import { formatTimeToChatTime, getErrText } from '@/utils/tools';
|
||||
|
||||
const PcSliderBar = ({
|
||||
onclickDelHistory,
|
||||
@@ -33,12 +35,13 @@ const PcSliderBar = ({
|
||||
onclickExportChat: (type: ExportChatType) => void;
|
||||
}) => {
|
||||
const router = useRouter();
|
||||
const { toast } = useToast();
|
||||
const { modelId = '', chatId = '' } = router.query as { modelId: string; chatId: string };
|
||||
const ContextMenuRef = useRef(null);
|
||||
|
||||
const theme = useTheme();
|
||||
const { isPc } = useGlobalStore();
|
||||
|
||||
const ContextMenuRef = useRef(null);
|
||||
|
||||
const { Loading, setIsLoading } = useLoading();
|
||||
const [contextMenuData, setContextMenuData] = useState<{
|
||||
left: number;
|
||||
@@ -52,7 +55,12 @@ const PcSliderBar = ({
|
||||
() => [...myModels, ...myCollectionModels],
|
||||
[myCollectionModels, myModels]
|
||||
);
|
||||
useQuery(['loadModels'], () => loadMyModels(false));
|
||||
|
||||
// custom title edit
|
||||
const { onOpenModal, EditModal: EditTitleModal } = useEditInfo({
|
||||
title: '自定义历史记录标题',
|
||||
placeholder: '如果设置为空,会自动跟随聊天记录。'
|
||||
});
|
||||
|
||||
// close contextMenu
|
||||
useOutsideClick({
|
||||
@@ -60,13 +68,9 @@ const PcSliderBar = ({
|
||||
handler: () =>
|
||||
setTimeout(() => {
|
||||
setContextMenuData(undefined);
|
||||
})
|
||||
}, 10)
|
||||
});
|
||||
|
||||
const { isLoading: isLoadingHistory } = useQuery(['loadingHistory'], () =>
|
||||
loadHistory({ pageNum: 1 })
|
||||
);
|
||||
|
||||
const onclickContextMenu = useCallback(
|
||||
(e: MouseEvent<HTMLDivElement>, history: HistoryItemType) => {
|
||||
e.preventDefault(); // 阻止默认右键菜单
|
||||
@@ -82,6 +86,12 @@ const PcSliderBar = ({
|
||||
[isPc]
|
||||
);
|
||||
|
||||
useQuery(['loadModels'], () => loadMyModels(false));
|
||||
|
||||
const { isLoading: isLoadingHistory } = useQuery(['loadingHistory'], () =>
|
||||
loadHistory({ pageNum: 1 })
|
||||
);
|
||||
|
||||
return (
|
||||
<Flex
|
||||
position={'relative'}
|
||||
@@ -149,14 +159,16 @@ const PcSliderBar = ({
|
||||
borderLeft={['none', '5px solid transparent']}
|
||||
userSelect={'none'}
|
||||
_hover={{
|
||||
backgroundColor: ['', '#dee0e3']
|
||||
bg: ['', '#dee0e3']
|
||||
}}
|
||||
{...(item._id === chatId
|
||||
? {
|
||||
backgroundColor: '#eff0f1',
|
||||
bg: 'myGray.100 !important',
|
||||
borderLeftColor: 'myBlue.600 !important'
|
||||
}
|
||||
: {})}
|
||||
: {
|
||||
bg: item.top ? 'myBlue.200' : ''
|
||||
})}
|
||||
onClick={() => {
|
||||
if (item._id === chatId) return;
|
||||
if (isPc) {
|
||||
@@ -216,6 +228,19 @@ const PcSliderBar = ({
|
||||
<Box ref={ContextMenuRef}></Box>
|
||||
<Menu isOpen>
|
||||
<MenuList>
|
||||
<MenuItem
|
||||
onClick={async () => {
|
||||
try {
|
||||
await putChatHistory({
|
||||
chatId: contextMenuData.history._id,
|
||||
top: !contextMenuData.history.top
|
||||
});
|
||||
loadHistory({ pageNum: 1, init: true });
|
||||
} catch (error) {}
|
||||
}}
|
||||
>
|
||||
{contextMenuData.history.top ? '取消置顶' : '置顶'}
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={async () => {
|
||||
setIsLoading(true);
|
||||
@@ -232,6 +257,33 @@ const PcSliderBar = ({
|
||||
>
|
||||
删除记录
|
||||
</MenuItem>
|
||||
<MenuItem
|
||||
onClick={() =>
|
||||
onOpenModal({
|
||||
defaultVal: contextMenuData.history.title,
|
||||
onSuccess: async (val: string) => {
|
||||
await putChatHistory({
|
||||
chatId: contextMenuData.history._id,
|
||||
customTitle: val,
|
||||
top: contextMenuData.history.top
|
||||
});
|
||||
toast({
|
||||
title: '自定义标题成功',
|
||||
status: 'success'
|
||||
});
|
||||
loadHistory({ pageNum: 1, init: true });
|
||||
},
|
||||
onError(err) {
|
||||
toast({
|
||||
title: getErrText(err),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
})
|
||||
}
|
||||
>
|
||||
自定义标题
|
||||
</MenuItem>
|
||||
<MenuItem onClick={() => onclickExportChat('html')}>导出HTML格式</MenuItem>
|
||||
<MenuItem onClick={() => onclickExportChat('pdf')}>导出PDF格式</MenuItem>
|
||||
<MenuItem onClick={() => onclickExportChat('md')}>导出Markdown格式</MenuItem>
|
||||
@@ -239,7 +291,7 @@ const PcSliderBar = ({
|
||||
</Menu>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<EditTitleModal />
|
||||
<Loading loading={isLoadingHistory} fixed={false} />
|
||||
</Flex>
|
||||
);
|
||||
|
||||
@@ -126,7 +126,7 @@ const QuoteModal = ({
|
||||
position={'relative'}
|
||||
_hover={{ '& .edit': { display: 'flex' } }}
|
||||
>
|
||||
{item.isEdit && <Box color={'myGray.600'}>(编辑过)</Box>}
|
||||
{item.source && <Box color={'myGray.600'}>({item.source})</Box>}
|
||||
<Box>{item.q}</Box>
|
||||
<Box>{item.a}</Box>
|
||||
<Box
|
||||
|
||||
@@ -36,7 +36,6 @@ import { streamFetch } from '@/api/fetch';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { throttle } from 'lodash';
|
||||
import { Types } from 'mongoose';
|
||||
import { LOGO_ICON } from '@/constants/chat';
|
||||
import { ChatModelMap } from '@/constants/model';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
@@ -49,6 +48,7 @@ import SideBar from '@/components/SideBar';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import Empty from './components/Empty';
|
||||
import QuoteModal from './components/QuoteModal';
|
||||
import { HUMAN_ICON } from '@/constants/chat';
|
||||
|
||||
const PhoneSliderBar = dynamic(() => import('./components/PhoneSliderBar'), {
|
||||
ssr: false
|
||||
@@ -105,7 +105,7 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
const { copyData } = useCopyData();
|
||||
const { isPc } = useGlobalStore();
|
||||
const { Loading, setIsLoading } = useLoading();
|
||||
const { userInfo } = useUserStore();
|
||||
const { userInfo, loadMyModels } = useUserStore();
|
||||
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
||||
|
||||
// close contextMenu
|
||||
@@ -211,8 +211,6 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
router.replace(`/chat?modelId=${modelId}&chatId=${newChatId}`);
|
||||
}
|
||||
|
||||
abortSignal.signal.aborted && (await delay(600));
|
||||
|
||||
// 设置聊天内容为完成状态
|
||||
setChatData((state) => ({
|
||||
...state,
|
||||
@@ -228,13 +226,21 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
})
|
||||
}));
|
||||
|
||||
// refresh history
|
||||
setTimeout(() => {
|
||||
loadHistory({ pageNum: 1, init: true });
|
||||
generatingMessage();
|
||||
}, 100);
|
||||
// refresh data
|
||||
generatingMessage();
|
||||
loadHistory({ pageNum: 1, init: true });
|
||||
loadMyModels(true);
|
||||
},
|
||||
[chatId, setForbidLoadChatData, generatingMessage, loadHistory, modelId, router, setChatData]
|
||||
[
|
||||
chatId,
|
||||
modelId,
|
||||
setChatData,
|
||||
loadHistory,
|
||||
loadMyModels,
|
||||
generatingMessage,
|
||||
setForbidLoadChatData,
|
||||
router
|
||||
]
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -458,14 +464,14 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
async ({
|
||||
modelId,
|
||||
chatId,
|
||||
isLoading = false
|
||||
loading = false
|
||||
}: {
|
||||
modelId: string;
|
||||
chatId: string;
|
||||
isLoading?: boolean;
|
||||
loading?: boolean;
|
||||
}) => {
|
||||
isLoading && setIsLoading(true);
|
||||
try {
|
||||
loading && setIsLoading(true);
|
||||
const res = await getInitChatSiteInfo(modelId, chatId);
|
||||
|
||||
setChatData({
|
||||
@@ -500,18 +506,18 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
return null;
|
||||
},
|
||||
[
|
||||
router,
|
||||
loadHistory,
|
||||
setForbidLoadChatData,
|
||||
scrollToBottom,
|
||||
setChatData,
|
||||
setIsLoading,
|
||||
setChatData,
|
||||
scrollToBottom,
|
||||
setForbidLoadChatData,
|
||||
router,
|
||||
setLastChatModelId,
|
||||
setLastChatId,
|
||||
setLastChatModelId
|
||||
loadHistory
|
||||
]
|
||||
);
|
||||
// 初始化聊天框
|
||||
const { isLoading } = useQuery(['init', modelId, chatId], () => {
|
||||
useQuery(['init', modelId, chatId], () => {
|
||||
// pc: redirect to latest model chat
|
||||
if (!modelId && lastChatModelId) {
|
||||
router.replace(`/chat?modelId=${lastChatModelId}&chatId=${lastChatId}`);
|
||||
@@ -529,7 +535,8 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
|
||||
return loadChatInfo({
|
||||
modelId,
|
||||
chatId
|
||||
chatId,
|
||||
loading: true
|
||||
});
|
||||
});
|
||||
|
||||
@@ -704,8 +711,8 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
className="avatar"
|
||||
src={
|
||||
item.obj === 'Human'
|
||||
? userInfo?.avatar || '/icon/human.png'
|
||||
: chatData.model.avatar || LOGO_ICON
|
||||
? userInfo?.avatar || HUMAN_ICON
|
||||
: chatData.model.avatar
|
||||
}
|
||||
w={['20px', '34px']}
|
||||
h={['20px', '34px']}
|
||||
@@ -874,7 +881,7 @@ const Chat = ({ modelId, chatId }: { modelId: string; chatId: string }) => {
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Loading loading={isLoading} fixed={false} />
|
||||
<Loading fixed={false} />
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
|
||||
@@ -38,7 +38,6 @@ import { streamFetch } from '@/api/fetch';
|
||||
import MyIcon from '@/components/Icon';
|
||||
import { throttle } from 'lodash';
|
||||
import { Types } from 'mongoose';
|
||||
import { LOGO_ICON } from '@/constants/chat';
|
||||
import { useChatStore } from '@/store/chat';
|
||||
import { useLoading } from '@/hooks/useLoading';
|
||||
import { fileDownload } from '@/utils/file';
|
||||
@@ -49,6 +48,7 @@ import Markdown from '@/components/Markdown';
|
||||
import SideBar from '@/components/SideBar';
|
||||
import Avatar from '@/components/Avatar';
|
||||
import Empty from './components/Empty';
|
||||
import { HUMAN_ICON } from '@/constants/chat';
|
||||
|
||||
const ShareHistory = dynamic(() => import('./components/ShareHistory'), {
|
||||
loading: () => <Loading fixed={false} />,
|
||||
@@ -101,7 +101,6 @@ const Chat = ({ shareId, historyId }: { shareId: string; historyId: string }) =>
|
||||
const { copyData } = useCopyData();
|
||||
const { isPc } = useGlobalStore();
|
||||
const { Loading, setIsLoading } = useLoading();
|
||||
const { userInfo } = useUserStore();
|
||||
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
|
||||
const {
|
||||
isOpen: isOpenPassword,
|
||||
@@ -628,8 +627,8 @@ const Chat = ({ shareId, historyId }: { shareId: string; historyId: string }) =>
|
||||
<Avatar
|
||||
src={
|
||||
item.obj === 'Human'
|
||||
? userInfo?.avatar || '/icon/human.png'
|
||||
: shareChatData.model.avatar || LOGO_ICON
|
||||
? shareChatData.userAvatar || HUMAN_ICON
|
||||
: shareChatData.model.avatar
|
||||
}
|
||||
w={['20px', '34px']}
|
||||
h={['20px', '34px']}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useCallback, useState, useRef } from 'react';
|
||||
import React, { useCallback, useState, useRef, useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
TableContainer,
|
||||
@@ -56,7 +56,7 @@ const DataCard = ({ kbId }: { kbId: string }) => {
|
||||
const { toast } = useToast();
|
||||
|
||||
const {
|
||||
data: modelDataList,
|
||||
data: kbDataList,
|
||||
isLoading,
|
||||
Pagination,
|
||||
total,
|
||||
@@ -72,11 +72,6 @@ const DataCard = ({ kbId }: { kbId: string }) => {
|
||||
defaultRequest: false
|
||||
});
|
||||
|
||||
useQuery(['getKbData', kbId], () => {
|
||||
getData(1);
|
||||
return null;
|
||||
});
|
||||
|
||||
const [editInputData, setEditInputData] = useState<InputDataType>();
|
||||
|
||||
const {
|
||||
@@ -101,20 +96,14 @@ const DataCard = ({ kbId }: { kbId: string }) => {
|
||||
);
|
||||
|
||||
const refetchData = useCallback(
|
||||
(num = 1) => {
|
||||
(num = pageNum) => {
|
||||
getData(num);
|
||||
refetch();
|
||||
return null;
|
||||
},
|
||||
[getData, refetch]
|
||||
[getData, pageNum, refetch]
|
||||
);
|
||||
|
||||
// interval get data
|
||||
useQuery(['refetchData'], () => refetchData(pageNum), {
|
||||
refetchInterval: 5000,
|
||||
enabled: qaListLen > 0 || vectorListLen > 0
|
||||
});
|
||||
|
||||
// get al data and export csv
|
||||
const { mutate: onclickExport, isLoading: isLoadingExport = false } = useMutation({
|
||||
mutationFn: () => getExportDataList(kbId),
|
||||
@@ -148,6 +137,17 @@ const DataCard = ({ kbId }: { kbId: string }) => {
|
||||
}
|
||||
});
|
||||
|
||||
// interval get data
|
||||
useQuery(['refetchData'], () => refetchData(1), {
|
||||
refetchInterval: 5000,
|
||||
enabled: qaListLen > 0 || vectorListLen > 0
|
||||
});
|
||||
useQuery(['getKbData', kbId], () => {
|
||||
setSearchText('');
|
||||
getData(1);
|
||||
return null;
|
||||
});
|
||||
|
||||
return (
|
||||
<Box position={'relative'}>
|
||||
<Flex>
|
||||
@@ -205,10 +205,10 @@ const DataCard = ({ kbId }: { kbId: string }) => {
|
||||
)}
|
||||
<Box flex={1} />
|
||||
<Input
|
||||
maxW={'240px'}
|
||||
maxW={['90%', '300px']}
|
||||
size={'sm'}
|
||||
value={searchText}
|
||||
placeholder="搜索相关问题和答案,回车确认"
|
||||
placeholder="搜索匹配知识,补充知识和来源,回车确认"
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
onBlur={() => {
|
||||
if (searchText === lastSearch.current) return;
|
||||
@@ -239,18 +239,22 @@ const DataCard = ({ kbId }: { kbId: string }) => {
|
||||
</Tooltip>
|
||||
</Th>
|
||||
<Th>补充知识</Th>
|
||||
<Th>来源</Th>
|
||||
<Th>操作</Th>
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{modelDataList.map((item) => (
|
||||
<Tr key={item.id}>
|
||||
{kbDataList.map((item) => (
|
||||
<Tr key={item.id} fontSize={'sm'}>
|
||||
<Td>
|
||||
<Box {...tdStyles.current}>{item.q}</Box>
|
||||
</Td>
|
||||
<Td>
|
||||
<Box {...tdStyles.current}>{item.a || '-'}</Box>
|
||||
</Td>
|
||||
<Td maxW={'15%'} whiteSpace={'pre-wrap'} userSelect={'all'}>
|
||||
{item.source?.trim() || '-'}
|
||||
</Td>
|
||||
<Td>
|
||||
<IconButton
|
||||
mr={5}
|
||||
|
||||
@@ -56,13 +56,14 @@ const InputDataModal = ({
|
||||
try {
|
||||
const { insertLen } = await postKbDataFromList({
|
||||
kbId,
|
||||
mode: TrainingModeEnum.index,
|
||||
data: [
|
||||
{
|
||||
a: e.a,
|
||||
q: e.q
|
||||
q: e.q,
|
||||
source: '手动录入'
|
||||
}
|
||||
],
|
||||
mode: TrainingModeEnum.index
|
||||
]
|
||||
});
|
||||
|
||||
if (insertLen === 0) {
|
||||
|
||||
@@ -37,6 +37,7 @@ const SelectJsonModal = ({
|
||||
const { toast } = useToast();
|
||||
const { File, onOpen } = useSelectFile({ fileType: '.csv', multiple: false });
|
||||
const [fileData, setFileData] = useState<{ q: string; a: string }[]>([]);
|
||||
const [fileName, setFileName] = useState('');
|
||||
const [successData, setSuccessData] = useState(0);
|
||||
const { openConfirm, ConfirmChild } = useConfirm({
|
||||
content: '确认导入该数据集?'
|
||||
@@ -46,6 +47,7 @@ const SelectJsonModal = ({
|
||||
async (e: File[]) => {
|
||||
const file = e[0];
|
||||
setSelecting(true);
|
||||
setFileName(file.name);
|
||||
try {
|
||||
const { header, data } = await readCsvContent(file);
|
||||
if (header[0] !== 'question' || header[1] !== 'answer') {
|
||||
@@ -75,11 +77,14 @@ const SelectJsonModal = ({
|
||||
let success = 0;
|
||||
|
||||
// subsection import
|
||||
const step = 50;
|
||||
const step = 100;
|
||||
for (let i = 0; i < fileData.length; i += step) {
|
||||
const { insertLen } = await postKbDataFromList({
|
||||
kbId,
|
||||
data: fileData.slice(i, i + step),
|
||||
data: fileData.slice(i, i + step).map((item) => ({
|
||||
...item,
|
||||
source: fileName
|
||||
})),
|
||||
mode: TrainingModeEnum.index
|
||||
});
|
||||
success += insertLen || 0;
|
||||
@@ -129,13 +134,14 @@ const SelectJsonModal = ({
|
||||
>
|
||||
点击下载csv模板
|
||||
</Box>
|
||||
<Flex alignItems={'center'}>
|
||||
<Box>
|
||||
<Button isLoading={selecting} isDisabled={uploading} onClick={onOpen}>
|
||||
选择 csv 问答对
|
||||
</Button>
|
||||
|
||||
<Box ml={4}>一共 {fileData.length} 组数据(下面最多展示100组)</Box>
|
||||
</Flex>
|
||||
<Box mt={4}>
|
||||
【{fileName}】一共有 {fileData.length} 组数据(下面最多展示100组)
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box flex={'3 0 0'} h={'100%'} overflow={'auto'} p={2} backgroundColor={'blackAlpha.50'}>
|
||||
{fileData.slice(0, 100).map((item, index) => (
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useCallback, useMemo } from 'react';
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import {
|
||||
Box,
|
||||
Flex,
|
||||
@@ -54,18 +54,20 @@ const SelectFileModal = ({
|
||||
const [prompt, setPrompt] = useState('');
|
||||
const { File, onOpen } = useSelectFile({ fileType: fileExtension, multiple: true });
|
||||
const [mode, setMode] = useState<`${TrainingModeEnum}`>(TrainingModeEnum.index);
|
||||
const [fileTextArr, setFileTextArr] = useState<string[]>(['']);
|
||||
const [files, setFiles] = useState<{ filename: string; text: string }[]>([
|
||||
{ filename: '文本1', text: '' }
|
||||
]);
|
||||
const [splitRes, setSplitRes] = useState<{
|
||||
tokens: number;
|
||||
chunks: string[];
|
||||
chunks: { filename: string; value: string }[];
|
||||
successChunks: number;
|
||||
}>({
|
||||
tokens: 0,
|
||||
chunks: [],
|
||||
successChunks: 0
|
||||
successChunks: 0,
|
||||
chunks: []
|
||||
});
|
||||
const { openConfirm, ConfirmChild } = useConfirm({
|
||||
content: `确认导入该文件,需要一定时间进行拆解,该任务无法终止!如果余额不足,未完成的任务会被直接清除。一共 ${
|
||||
content: `确认导入该文件,需要一定时间进行拆解,该任务无法终止!QA 拆分仅能使用余额,如果余额不足,未完成的任务会被直接清除。一共 ${
|
||||
splitRes.chunks.length
|
||||
} 组。${splitRes.tokens ? `大约 ${splitRes.tokens} 个tokens。` : ''}`
|
||||
});
|
||||
@@ -78,21 +80,21 @@ const SelectFileModal = ({
|
||||
files.forEach((file) => {
|
||||
promise = promise.then(async () => {
|
||||
const extension = file?.name?.split('.')?.pop()?.toLowerCase();
|
||||
let text = '';
|
||||
switch (extension) {
|
||||
case 'txt':
|
||||
case 'md':
|
||||
text = await readTxtContent(file);
|
||||
break;
|
||||
case 'pdf':
|
||||
text = await readPdfContent(file);
|
||||
break;
|
||||
case 'doc':
|
||||
case 'docx':
|
||||
text = await readDocContent(file);
|
||||
break;
|
||||
}
|
||||
text && setFileTextArr((state) => [text].concat(state));
|
||||
const text = await (async () => {
|
||||
switch (extension) {
|
||||
case 'txt':
|
||||
case 'md':
|
||||
return readTxtContent(file);
|
||||
case 'pdf':
|
||||
return readPdfContent(file);
|
||||
case 'doc':
|
||||
case 'docx':
|
||||
return readDocContent(file);
|
||||
}
|
||||
return '';
|
||||
})();
|
||||
|
||||
text && setFiles((state) => [{ filename: file.name, text }].concat(state));
|
||||
return;
|
||||
});
|
||||
});
|
||||
@@ -115,11 +117,13 @@ const SelectFileModal = ({
|
||||
|
||||
// subsection import
|
||||
let success = 0;
|
||||
const step = 50;
|
||||
const step = 100;
|
||||
for (let i = 0; i < splitRes.chunks.length; i += step) {
|
||||
const { insertLen } = await postKbDataFromList({
|
||||
kbId,
|
||||
data: splitRes.chunks.slice(i, i + step).map((text) => ({ q: text, a: '' })),
|
||||
data: splitRes.chunks
|
||||
.slice(i, i + step)
|
||||
.map((item) => ({ q: item.value, a: '', source: item.filename })),
|
||||
prompt: `下面是"${prompt || '一段长文本'}"`,
|
||||
mode
|
||||
});
|
||||
@@ -149,26 +153,32 @@ const SelectFileModal = ({
|
||||
const onclickImport = useCallback(async () => {
|
||||
setBtnLoading(true);
|
||||
try {
|
||||
let promise = Promise.resolve();
|
||||
|
||||
const splitRes = await Promise.all(
|
||||
fileTextArr
|
||||
.filter((item) => item)
|
||||
.map((item) =>
|
||||
splitText_token({
|
||||
text: item,
|
||||
...modeMap[mode]
|
||||
})
|
||||
)
|
||||
);
|
||||
const splitRes = files
|
||||
.map((item) =>
|
||||
splitText_token({
|
||||
text: item.text,
|
||||
...modeMap[mode]
|
||||
})
|
||||
)
|
||||
.map((item, i) => ({
|
||||
...item,
|
||||
filename: files[i].filename
|
||||
}))
|
||||
.filter((item) => item.tokens > 0);
|
||||
|
||||
setSplitRes({
|
||||
tokens: splitRes.reduce((sum, item) => sum + item.tokens, 0),
|
||||
chunks: splitRes.map((item) => item.chunks).flat(),
|
||||
chunks: splitRes
|
||||
.map((item) =>
|
||||
item.chunks.map((chunk) => ({
|
||||
filename: item.filename,
|
||||
value: chunk
|
||||
}))
|
||||
)
|
||||
.flat(),
|
||||
successChunks: 0
|
||||
});
|
||||
|
||||
await promise;
|
||||
openConfirm(mutate)();
|
||||
} catch (error) {
|
||||
toast({
|
||||
@@ -177,7 +187,7 @@ const SelectFileModal = ({
|
||||
});
|
||||
}
|
||||
setBtnLoading(false);
|
||||
}, [fileTextArr, mode, mutate, openConfirm, toast]);
|
||||
}, [files, mode, mutate, openConfirm, toast]);
|
||||
|
||||
return (
|
||||
<Modal isOpen={true} onClose={onClose} isCentered>
|
||||
@@ -204,7 +214,7 @@ const SelectFileModal = ({
|
||||
>
|
||||
<Box mt={2} px={5} maxW={['100%', '70%']} textAlign={'justify'} color={'blackAlpha.600'}>
|
||||
支持 {fileExtension} 文件。Gpt会自动对文本进行 QA 拆分,需要较长训练时间,拆分需要消耗
|
||||
tokens,账号余额不足时,未拆分的数据会被删除。一个{fileTextArr.length}个文本。
|
||||
tokens,账号余额不足时,未拆分的数据会被删除。一个{files.length}个文本。
|
||||
</Box>
|
||||
{/* 拆分模式 */}
|
||||
<Flex w={'100%'} px={5} alignItems={'center'} mt={4}>
|
||||
@@ -235,26 +245,26 @@ const SelectFileModal = ({
|
||||
)}
|
||||
{/* 文本内容 */}
|
||||
<Box flex={'1 0 0'} px={5} h={0} w={'100%'} overflowY={'auto'} mt={4}>
|
||||
{fileTextArr.slice(0, 100).map((item, i) => (
|
||||
{files.slice(0, 100).map((item, i) => (
|
||||
<Box key={i} mb={5}>
|
||||
<Box mb={1}>文本{i + 1}</Box>
|
||||
<Box mb={1}>{item.filename}</Box>
|
||||
<Textarea
|
||||
placeholder="文件内容,空内容会自动忽略"
|
||||
maxLength={-1}
|
||||
rows={10}
|
||||
fontSize={'xs'}
|
||||
whiteSpace={'pre-wrap'}
|
||||
value={item}
|
||||
value={item.text}
|
||||
onChange={(e) => {
|
||||
setFileTextArr([
|
||||
...fileTextArr.slice(0, i),
|
||||
e.target.value,
|
||||
...fileTextArr.slice(i + 1)
|
||||
setFiles([
|
||||
...files.slice(0, i),
|
||||
{ ...item, text: e.target.value },
|
||||
...files.slice(i + 1)
|
||||
]);
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
if (fileTextArr.length > 1 && e.target.value === '') {
|
||||
setFileTextArr((state) => [...state.slice(0, i), ...state.slice(i + 1)]);
|
||||
if (files.length > 1 && e.target.value === '') {
|
||||
setFiles((state) => [...state.slice(0, i), ...state.slice(i + 1)]);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@@ -272,7 +282,7 @@ const SelectFileModal = ({
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
isDisabled={uploading || btnLoading || fileTextArr[0] === ''}
|
||||
isDisabled={uploading || btnLoading || files[0]?.text === ''}
|
||||
onClick={onclickImport}
|
||||
>
|
||||
{uploading ? (
|
||||
|
||||
@@ -136,7 +136,7 @@ const NumberSetting = () => {
|
||||
</Button>
|
||||
</Flex>
|
||||
<Box fontSize={'xs'} color={'blackAlpha.500'}>
|
||||
如果填写了自己的 openai 账号,将不会计费
|
||||
如果填写了自己的 openai 账号,网页上 openai 模型对话不会计费。
|
||||
</Box>
|
||||
</Box>
|
||||
<Flex mt={6} alignItems={'center'}>
|
||||
|
||||
@@ -27,7 +27,8 @@ export const openaiError: Record<string, string> = {
|
||||
export const openaiAccountError: Record<string, string> = {
|
||||
insufficient_quota: 'API 余额不足',
|
||||
invalid_api_key: 'openai 账号异常',
|
||||
account_deactivated: '账号已停用'
|
||||
account_deactivated: '账号已停用',
|
||||
invalid_request_error: '无效请求'
|
||||
};
|
||||
export const proxyError: Record<string, boolean> = {
|
||||
ECONNABORTED: true,
|
||||
|
||||
@@ -61,7 +61,8 @@ export async function generateQA(): Promise<any> {
|
||||
userId: 1,
|
||||
kbId: 1,
|
||||
prompt: 1,
|
||||
q: 1
|
||||
q: 1,
|
||||
source: 1
|
||||
});
|
||||
|
||||
// task preemption
|
||||
@@ -75,10 +76,11 @@ export async function generateQA(): Promise<any> {
|
||||
const kbId = String(data.kbId);
|
||||
|
||||
// 余额校验并获取 openapi Key
|
||||
const { userOpenAiKey, systemAuthKey } = await getApiKey({
|
||||
const { systemAuthKey } = await getApiKey({
|
||||
model: OpenAiChatEnum.GPT35,
|
||||
userId,
|
||||
type: 'training'
|
||||
type: 'training',
|
||||
mustPay: true
|
||||
});
|
||||
|
||||
const startTime = Date.now();
|
||||
@@ -88,7 +90,7 @@ export async function generateQA(): Promise<any> {
|
||||
[data.q].map((text) =>
|
||||
modelServiceToolMap[OpenAiChatEnum.GPT35]
|
||||
.chatCompletion({
|
||||
apiKey: userOpenAiKey || systemAuthKey,
|
||||
apiKey: systemAuthKey,
|
||||
temperature: 0.8,
|
||||
messages: [
|
||||
{
|
||||
@@ -113,7 +115,7 @@ A2:
|
||||
console.log(`split result length: `, result.length);
|
||||
// 计费
|
||||
pushSplitDataBill({
|
||||
isPay: !userOpenAiKey && result.length > 0,
|
||||
isPay: result.length > 0,
|
||||
userId: data.userId,
|
||||
type: BillTypeEnum.QA,
|
||||
textLen: responseMessages.map((item) => item.value).join('').length,
|
||||
@@ -137,7 +139,10 @@ A2:
|
||||
// 创建 向量生成 队列
|
||||
await pushDataToKb({
|
||||
kbId,
|
||||
data: responseList,
|
||||
data: responseList.map((item) => ({
|
||||
...item,
|
||||
source: data.source
|
||||
})),
|
||||
userId,
|
||||
mode: TrainingModeEnum.index
|
||||
});
|
||||
|
||||
@@ -57,7 +57,8 @@ export async function generateVector(): Promise<any> {
|
||||
userId: 1,
|
||||
kbId: 1,
|
||||
q: 1,
|
||||
a: 1
|
||||
a: 1,
|
||||
source: 1
|
||||
});
|
||||
|
||||
// task preemption
|
||||
@@ -91,6 +92,7 @@ export async function generateVector(): Promise<any> {
|
||||
data: vectors.map((vector, i) => ({
|
||||
q: dataItems[i].q,
|
||||
a: dataItems[i].a,
|
||||
source: data.source,
|
||||
vector
|
||||
}))
|
||||
});
|
||||
@@ -110,6 +112,7 @@ export async function generateVector(): Promise<any> {
|
||||
} else {
|
||||
console.log('生成向量错误:', err);
|
||||
}
|
||||
|
||||
// message error or openai account error
|
||||
if (
|
||||
err?.message === 'invalid message format' ||
|
||||
|
||||
@@ -31,10 +31,17 @@ const ChatSchema = new Schema({
|
||||
type: String,
|
||||
default: '历史记录'
|
||||
},
|
||||
customTitle: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
latestChat: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
top: {
|
||||
type: Boolean
|
||||
},
|
||||
content: {
|
||||
type: [
|
||||
{
|
||||
@@ -53,7 +60,7 @@ const ChatSchema = new Schema({
|
||||
id: String,
|
||||
q: String,
|
||||
a: String,
|
||||
isEdit: Boolean
|
||||
source: String
|
||||
}
|
||||
],
|
||||
default: []
|
||||
|
||||
@@ -38,8 +38,9 @@ const TrainingDataSchema = new Schema({
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
vectorList: {
|
||||
type: Object
|
||||
source: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Schema, model, models, Model } from 'mongoose';
|
||||
import { hashPassword } from '@/service/utils/tools';
|
||||
import { PRICE_SCALE } from '@/constants/common';
|
||||
import { UserModelSchema } from '@/types/mongoSchema';
|
||||
|
||||
const UserSchema = new Schema({
|
||||
username: {
|
||||
// 可以是手机/邮箱,新的验证都只用手机
|
||||
|
||||
@@ -37,6 +37,7 @@ export async function connectToDatabase(): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
// 初始化队列
|
||||
global.qaQueueLen = 0;
|
||||
global.vectorQueueLen = 0;
|
||||
|
||||
|
||||
@@ -6,13 +6,14 @@ export const connectPg = async () => {
|
||||
return global.pgClient;
|
||||
}
|
||||
|
||||
const maxLink = Number(process.env.VECTOR_MAX_PROCESS || 10);
|
||||
global.pgClient = new Pool({
|
||||
host: process.env.PG_HOST,
|
||||
port: process.env.PG_PORT ? +process.env.PG_PORT : 5432,
|
||||
user: process.env.PG_USER,
|
||||
password: process.env.PG_PASSWORD,
|
||||
database: process.env.PG_DB_NAME,
|
||||
max: 20,
|
||||
max: maxLink,
|
||||
idleTimeoutMillis: 60000,
|
||||
connectionTimeoutMillis: 20000
|
||||
});
|
||||
@@ -171,12 +172,14 @@ export const insertKbItem = ({
|
||||
vector: number[];
|
||||
q: string;
|
||||
a: string;
|
||||
source?: string;
|
||||
}[];
|
||||
}) => {
|
||||
return PgClient.insert('modelData', {
|
||||
values: data.map((item) => [
|
||||
{ key: 'user_id', value: userId },
|
||||
{ key: 'kb_id', value: kbId },
|
||||
{ key: 'source', value: item.source?.slice(0, 30)?.trim() || '' },
|
||||
{ key: 'q', value: item.q.replace(/'/g, '"') },
|
||||
{ key: 'a', value: item.a.replace(/'/g, '"') },
|
||||
{ key: 'vector', value: `[${item.vector}]` }
|
||||
|
||||
@@ -280,7 +280,8 @@ export const authChat = async ({
|
||||
{
|
||||
$project: {
|
||||
obj: '$content.obj',
|
||||
value: '$content.value'
|
||||
value: '$content.value',
|
||||
quote: '$content.quote'
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
@@ -89,39 +89,55 @@ export const ChatContextFilter = ({
|
||||
prompts: ChatItemSimpleType[];
|
||||
maxTokens: number;
|
||||
}) => {
|
||||
const systemPrompts: ChatItemSimpleType[] = [];
|
||||
const chatPrompts: ChatItemSimpleType[] = [];
|
||||
|
||||
let rawTextLen = 0;
|
||||
const formatPrompts = prompts.map<ChatItemSimpleType>((item) => {
|
||||
prompts.forEach((item) => {
|
||||
const val = simplifyStr(item.value);
|
||||
rawTextLen += val.length;
|
||||
return {
|
||||
|
||||
const data = {
|
||||
obj: item.obj,
|
||||
value: val
|
||||
};
|
||||
|
||||
if (item.obj === ChatRoleEnum.System) {
|
||||
systemPrompts.push(data);
|
||||
} else {
|
||||
chatPrompts.push(data);
|
||||
}
|
||||
});
|
||||
|
||||
// 长度太小时,不需要进行 token 截断
|
||||
if (formatPrompts.length <= 2 || rawTextLen < maxTokens * 0.5) {
|
||||
return formatPrompts;
|
||||
if (rawTextLen < maxTokens * 0.5) {
|
||||
return [...systemPrompts, ...chatPrompts];
|
||||
}
|
||||
|
||||
// 去掉 system 的 token
|
||||
maxTokens -= modelToolMap[model].countTokens({
|
||||
messages: systemPrompts
|
||||
});
|
||||
|
||||
// 根据 tokens 截断内容
|
||||
const chats: ChatItemSimpleType[] = [];
|
||||
|
||||
// 从后往前截取对话内容
|
||||
for (let i = formatPrompts.length - 1; i >= 0; i--) {
|
||||
chats.unshift(formatPrompts[i]);
|
||||
for (let i = chatPrompts.length - 1; i >= 0; i--) {
|
||||
chats.unshift(chatPrompts[i]);
|
||||
|
||||
const tokens = modelToolMap[model].countTokens({
|
||||
messages: chats
|
||||
});
|
||||
|
||||
/* 整体 tokens 超出范围, system必须保留 */
|
||||
if (tokens >= maxTokens && formatPrompts[i].obj !== ChatRoleEnum.System) {
|
||||
return chats.slice(1);
|
||||
if (tokens >= maxTokens) {
|
||||
chats.shift();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return chats;
|
||||
return [...systemPrompts, ...chats];
|
||||
};
|
||||
|
||||
/* stream response */
|
||||
|
||||
@@ -104,6 +104,7 @@ export const openAiStreamResponse = async ({
|
||||
obj: ChatRoleEnum.AI,
|
||||
value: responseContent
|
||||
});
|
||||
|
||||
const totalTokens = modelToolMap[model].countTokens({
|
||||
messages: finishMessages
|
||||
});
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
ShareChatType
|
||||
} from '@/types/chat';
|
||||
import { getChatHistory } from '@/api/chat';
|
||||
import { HUMAN_ICON } from '@/constants/chat';
|
||||
|
||||
type SetShareChatHistoryItem = {
|
||||
historyId: string;
|
||||
@@ -57,6 +58,7 @@ const defaultChatData = {
|
||||
};
|
||||
const defaultShareChatData: ShareChatType = {
|
||||
maxContext: 5,
|
||||
userAvatar: HUMAN_ICON,
|
||||
model: {
|
||||
name: '',
|
||||
avatar: '/icon/logo.png',
|
||||
|
||||
1
src/types/chat.d.ts
vendored
1
src/types/chat.d.ts
vendored
@@ -33,6 +33,7 @@ export type HistoryItemType = {
|
||||
modelId: string;
|
||||
title: string;
|
||||
latestChat: string;
|
||||
top: boolean;
|
||||
};
|
||||
|
||||
export type ShareChatHistoryItemType = {
|
||||
|
||||
2
src/types/index.d.ts
vendored
2
src/types/index.d.ts
vendored
@@ -1,6 +1,7 @@
|
||||
import type { Mongoose } from 'mongoose';
|
||||
import type { Agent } from 'http';
|
||||
import type { Pool } from 'pg';
|
||||
import type { Tiktoken } from '@dqbd/tiktoken';
|
||||
|
||||
declare global {
|
||||
var mongodb: Mongoose | string | null;
|
||||
@@ -11,6 +12,7 @@ declare global {
|
||||
var QRCode: any;
|
||||
var qaQueueLen: number;
|
||||
var vectorQueueLen: number;
|
||||
var OpenAiEncMap: Record<string, Tiktoken>;
|
||||
|
||||
interface Window {
|
||||
['pdfjs-dist/build/pdf']: any;
|
||||
|
||||
6
src/types/mongoSchema.d.ts
vendored
6
src/types/mongoSchema.d.ts
vendored
@@ -78,6 +78,7 @@ export interface TrainingDataSchema {
|
||||
prompt: string;
|
||||
q: string;
|
||||
a: string;
|
||||
source: string;
|
||||
}
|
||||
|
||||
export interface ChatSchema {
|
||||
@@ -85,8 +86,11 @@ export interface ChatSchema {
|
||||
userId: string;
|
||||
modelId: string;
|
||||
expiredTime: number;
|
||||
loadAmount: number;
|
||||
updateTime: Date;
|
||||
title: string;
|
||||
customTitle: string;
|
||||
latestChat: string;
|
||||
top: boolean;
|
||||
content: ChatItemType[];
|
||||
}
|
||||
export interface ChatPopulate extends ChatSchema {
|
||||
|
||||
7
src/types/pg.d.ts
vendored
7
src/types/pg.d.ts
vendored
@@ -1,7 +0,0 @@
|
||||
export interface PgKBDataItemType {
|
||||
id: string;
|
||||
q: string;
|
||||
a: string;
|
||||
user_id: string;
|
||||
kb_id: string;
|
||||
}
|
||||
|
||||
3
src/types/plugin.d.ts
vendored
3
src/types/plugin.d.ts
vendored
@@ -10,8 +10,7 @@ export interface KbDataItemType {
|
||||
id: string;
|
||||
q: string; // 提问词
|
||||
a: string; // 原文
|
||||
kbId: string;
|
||||
userId: string;
|
||||
source: string;
|
||||
}
|
||||
|
||||
export type TextPluginRequestParams = {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import mammoth from 'mammoth';
|
||||
import Papa from 'papaparse';
|
||||
import { getOpenAiEncMap } from './plugin/openai';
|
||||
import { getErrText } from './tools';
|
||||
|
||||
/**
|
||||
* 读取 txt 文件内容
|
||||
@@ -145,7 +146,7 @@ export const fileDownload = ({
|
||||
* slideLen - The size of the before and after Text
|
||||
* maxLen > slideLen
|
||||
*/
|
||||
export const splitText_token = async ({
|
||||
export const splitText_token = ({
|
||||
text,
|
||||
maxLen,
|
||||
slideLen
|
||||
@@ -184,8 +185,8 @@ export const splitText_token = async ({
|
||||
chunks,
|
||||
tokens
|
||||
};
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
} catch (err) {
|
||||
throw new Error(getErrText(err));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,66 @@ import Graphemer from 'graphemer';
|
||||
const textDecoder = new TextDecoder();
|
||||
const graphemer = new Graphemer();
|
||||
|
||||
export const getOpenAiEncMap = () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.OpenAiEncMap = window.OpenAiEncMap || {
|
||||
'gpt-3.5-turbo': encoding_for_model('gpt-3.5-turbo', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
}),
|
||||
'gpt-4': encoding_for_model('gpt-4', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
}),
|
||||
'gpt-4-32k': encoding_for_model('gpt-4-32k', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
})
|
||||
};
|
||||
return window.OpenAiEncMap;
|
||||
}
|
||||
if (typeof global !== 'undefined') {
|
||||
global.OpenAiEncMap = global.OpenAiEncMap || {
|
||||
'gpt-3.5-turbo': encoding_for_model('gpt-3.5-turbo', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
}),
|
||||
'gpt-4': encoding_for_model('gpt-4', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
}),
|
||||
'gpt-4-32k': encoding_for_model('gpt-4-32k', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
})
|
||||
};
|
||||
return global.OpenAiEncMap;
|
||||
}
|
||||
return {
|
||||
'gpt-3.5-turbo': encoding_for_model('gpt-3.5-turbo', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
}),
|
||||
'gpt-4': encoding_for_model('gpt-4', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
}),
|
||||
'gpt-4-32k': encoding_for_model('gpt-4-32k', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
})
|
||||
};
|
||||
};
|
||||
|
||||
export const adaptChatItem_openAI = ({
|
||||
messages
|
||||
}: {
|
||||
@@ -24,29 +84,6 @@ export const adaptChatItem_openAI = ({
|
||||
}));
|
||||
};
|
||||
|
||||
/* count openai chat token*/
|
||||
let OpenAiEncMap: Record<string, Tiktoken>;
|
||||
export const getOpenAiEncMap = () => {
|
||||
if (OpenAiEncMap) return OpenAiEncMap;
|
||||
OpenAiEncMap = {
|
||||
'gpt-3.5-turbo': encoding_for_model('gpt-3.5-turbo', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
}),
|
||||
'gpt-4': encoding_for_model('gpt-4', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
}),
|
||||
'gpt-4-32k': encoding_for_model('gpt-4-32k', {
|
||||
'<|im_start|>': 100264,
|
||||
'<|im_end|>': 100265,
|
||||
'<|im_sep|>': 100266
|
||||
})
|
||||
};
|
||||
return OpenAiEncMap;
|
||||
};
|
||||
export function countOpenAIToken({
|
||||
messages,
|
||||
model
|
||||
|
||||
Reference in New Issue
Block a user