mirror of
https://github.com/lobehub/lobehub
synced 2026-04-21 09:37:28 +00:00
🔧 chore: Update setting
This commit introduces new components, modules, and features related to chat, sessions, and settings. It includes modifications to configuration files, updates to dependencies, adjustments to styles and layouts, and additions of new components and modules. The changes also involve updates to functions, interfaces, selectors, actions, and reducers. Additionally, there are modifications to TypeScript interfaces and types, as well as changes to parameters and functions in certain files.
This commit is contained in:
parent
30da537618
commit
4e522a6e2b
72 changed files with 631 additions and 447 deletions
1
.changelogrc.js
Normal file
1
.changelogrc.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('@lobehub/lint').changelog;
|
||||||
1
.commitlintrc.js
Normal file
1
.commitlintrc.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('@lobehub/lint').commitlint;
|
||||||
16
.editorconfig
Normal file
16
.editorconfig
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# http://editorconfig.org
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
||||||
|
|
||||||
|
[Makefile]
|
||||||
|
indent_style = tab
|
||||||
32
.eslintignore
Normal file
32
.eslintignore
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Eslintignore for LobeHub
|
||||||
|
################################################################
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# ci
|
||||||
|
coverage
|
||||||
|
.coverage
|
||||||
|
|
||||||
|
# test
|
||||||
|
jest*
|
||||||
|
_test_
|
||||||
|
__test__
|
||||||
|
*.test.ts
|
||||||
|
|
||||||
|
# umi
|
||||||
|
.umi
|
||||||
|
.umi-production
|
||||||
|
.umi-test
|
||||||
|
.dumi/tmp*
|
||||||
|
!.dumirc.ts
|
||||||
|
|
||||||
|
# production
|
||||||
|
dist
|
||||||
|
es
|
||||||
|
lib
|
||||||
|
logs
|
||||||
|
|
||||||
|
# misc
|
||||||
|
# add other ignore file below
|
||||||
|
.next
|
||||||
11
.eslintrc.js
11
.eslintrc.js
|
|
@ -3,13 +3,4 @@ const config = require('@lobehub/lint').eslint;
|
||||||
config.extends.push('plugin:@next/next/recommended');
|
config.extends.push('plugin:@next/next/recommended');
|
||||||
//config.extends.push('plugin:@next/next/core-web-vitals');
|
//config.extends.push('plugin:@next/next/core-web-vitals');
|
||||||
|
|
||||||
module.exports = {
|
module.exports = config;
|
||||||
...config,
|
|
||||||
rules: {
|
|
||||||
...config.rules,
|
|
||||||
'react/jsx-sort-props': 'off',
|
|
||||||
'sort-keys-fix/sort-keys-fix': 'off',
|
|
||||||
'typescript-sort-keys/interface': 'off',
|
|
||||||
'unicorn/switch-case-braces': 'off',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
|
||||||
20
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
20
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
|
|
@ -1,11 +1,11 @@
|
||||||
name: "🐛 反馈缺陷 Bug Report"
|
name: '🐛 反馈缺陷 Bug Report'
|
||||||
description: "反馈一个问题缺陷 | Report an bug"
|
description: '反馈一个问题缺陷 | Report an bug'
|
||||||
title: "[Bug] "
|
title: '[Bug] '
|
||||||
labels: "🐛 Bug"
|
labels: '🐛 Bug'
|
||||||
body:
|
body:
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
attributes:
|
attributes:
|
||||||
label: "💻 系统环境 | Operating System"
|
label: '💻 系统环境 | Operating System'
|
||||||
options:
|
options:
|
||||||
- Windows
|
- Windows
|
||||||
- macOS
|
- macOS
|
||||||
|
|
@ -16,7 +16,7 @@ body:
|
||||||
required: true
|
required: true
|
||||||
- type: dropdown
|
- type: dropdown
|
||||||
attributes:
|
attributes:
|
||||||
label: "🌐 浏览器 | Browser"
|
label: '🌐 浏览器 | Browser'
|
||||||
options:
|
options:
|
||||||
- Chrome
|
- Chrome
|
||||||
- Edge
|
- Edge
|
||||||
|
|
@ -27,19 +27,19 @@ body:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "🐛 问题描述 | Bug Description"
|
label: '🐛 问题描述 | Bug Description'
|
||||||
description: A clear and concise description of the bug.
|
description: A clear and concise description of the bug.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "🚦 期望结果 | Expected Behavior"
|
label: '🚦 期望结果 | Expected Behavior'
|
||||||
description: A clear and concise description of what you expected to happen.
|
description: A clear and concise description of what you expected to happen.
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "📷 复现步骤 | Recurrence Steps"
|
label: '📷 复现步骤 | Recurrence Steps'
|
||||||
description: A clear and concise description of how to recurrence.
|
description: A clear and concise description of how to recurrence.
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "📝 补充信息 | Additional Information"
|
label: '📝 补充信息 | Additional Information'
|
||||||
description: If your problem needs further explanation, or if the issue you're seeing cannot be reproduced in a gist, please add more information here.
|
description: If your problem needs further explanation, or if the issue you're seeing cannot be reproduced in a gist, please add more information here.
|
||||||
|
|
|
||||||
14
.github/ISSUE_TEMPLATE/2_feature_request.yml
vendored
14
.github/ISSUE_TEMPLATE/2_feature_request.yml
vendored
|
|
@ -1,21 +1,21 @@
|
||||||
name: "🌠 功能需求 Feature Request"
|
name: '🌠 功能需求 Feature Request'
|
||||||
description: "需求或建议 | Suggest an idea"
|
description: '需求或建议 | Suggest an idea'
|
||||||
title: "[Request] "
|
title: '[Request] '
|
||||||
labels: "🌠 Feature Request"
|
labels: '🌠 Feature Request'
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "🥰 需求描述 | Feature Description"
|
label: '🥰 需求描述 | Feature Description'
|
||||||
description: Please add a clear and concise description of the problem you are seeking to solve with this feature request.
|
description: Please add a clear and concise description of the problem you are seeking to solve with this feature request.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "🧐 解决方案 | Proposed Solution"
|
label: '🧐 解决方案 | Proposed Solution'
|
||||||
description: Describe the solution you'd like in a clear and concise manner.
|
description: Describe the solution you'd like in a clear and concise manner.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "📝 补充信息 | Additional Information"
|
label: '📝 补充信息 | Additional Information'
|
||||||
description: Add any other context about the problem here.
|
description: Add any other context about the problem here.
|
||||||
|
|
|
||||||
12
.github/ISSUE_TEMPLATE/3_question.yml
vendored
12
.github/ISSUE_TEMPLATE/3_question.yml
vendored
|
|
@ -1,15 +1,15 @@
|
||||||
name: "😇 疑问或帮助 Help Wanted"
|
name: '😇 疑问或帮助 Help Wanted'
|
||||||
description: "疑问或需要帮助 | Need help"
|
description: '疑问或需要帮助 | Need help'
|
||||||
title: "[Question] "
|
title: '[Question] '
|
||||||
labels: "😇 Help Wanted"
|
labels: '😇 Help Wanted'
|
||||||
body:
|
body:
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "🧐 问题描述 | Proposed Solution"
|
label: '🧐 问题描述 | Proposed Solution'
|
||||||
description: A clear and concise description of the proplem.
|
description: A clear and concise description of the proplem.
|
||||||
validations:
|
validations:
|
||||||
required: true
|
required: true
|
||||||
- type: textarea
|
- type: textarea
|
||||||
attributes:
|
attributes:
|
||||||
label: "📝 补充信息 | Additional Information"
|
label: '📝 补充信息 | Additional Information'
|
||||||
description: Add any other context about the problem here.
|
description: Add any other context about the problem here.
|
||||||
|
|
|
||||||
10
.github/dependabot.yml
vendored
10
.github/dependabot.yml
vendored
|
|
@ -1,17 +1,17 @@
|
||||||
version: 2
|
version: 2
|
||||||
updates:
|
updates:
|
||||||
- package-ecosystem: npm
|
- package-ecosystem: npm
|
||||||
directory: "/"
|
directory: '/'
|
||||||
schedule:
|
schedule:
|
||||||
interval: weekly
|
interval: weekly
|
||||||
time: "19:00"
|
time: '19:00'
|
||||||
timezone: 'Asia/Shanghai'
|
timezone: 'Asia/Shanghai'
|
||||||
open-pull-requests-limit: 10
|
open-pull-requests-limit: 10
|
||||||
versioning-strategy: increase
|
versioning-strategy: increase
|
||||||
|
|
||||||
- package-ecosystem: "github-actions"
|
- package-ecosystem: 'github-actions'
|
||||||
directory: "/"
|
directory: '/'
|
||||||
schedule:
|
schedule:
|
||||||
interval: monthly
|
interval: monthly
|
||||||
time: "19:00"
|
time: '19:00'
|
||||||
timezone: 'Asia/Shanghai'
|
timezone: 'Asia/Shanghai'
|
||||||
|
|
|
||||||
2
.github/workflows/contributor-help.yml
vendored
2
.github/workflows/contributor-help.yml
vendored
|
|
@ -2,7 +2,7 @@ name: Contributor Helper
|
||||||
|
|
||||||
on:
|
on:
|
||||||
# 🌏 Think about the planet! No need to update stats too frequently
|
# 🌏 Think about the planet! No need to update stats too frequently
|
||||||
schedule: [{cron: "0 18 * * *"}]
|
schedule: [{ cron: '0 18 * * *' }]
|
||||||
# 💡 The following line lets you run workflow manually from the action tab!
|
# 💡 The following line lets you run workflow manually from the action tab!
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
jobs:
|
jobs:
|
||||||
|
|
|
||||||
8
.github/workflows/issue-check-inactive.yml
vendored
8
.github/workflows/issue-check-inactive.yml
vendored
|
|
@ -2,7 +2,7 @@ name: Issue Check Inactive
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 */15 * *"
|
- cron: '0 0 */15 * *'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
@ -10,8 +10,8 @@ permissions:
|
||||||
jobs:
|
jobs:
|
||||||
issue-check-inactive:
|
issue-check-inactive:
|
||||||
permissions:
|
permissions:
|
||||||
issues: write # for actions-cool/issues-helper to update issues
|
issues: write # for actions-cool/issues-helper to update issues
|
||||||
pull-requests: write # for actions-cool/issues-helper to update PRs
|
pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: check-inactive
|
- name: check-inactive
|
||||||
|
|
@ -19,4 +19,4 @@ jobs:
|
||||||
with:
|
with:
|
||||||
actions: 'check-inactive'
|
actions: 'check-inactive'
|
||||||
inactive-label: 'Inactive'
|
inactive-label: 'Inactive'
|
||||||
inactive-day: 30
|
inactive-day: 30
|
||||||
|
|
|
||||||
6
.github/workflows/issue-close-require.yml
vendored
6
.github/workflows/issue-close-require.yml
vendored
|
|
@ -2,7 +2,7 @@ name: Issue Close Require
|
||||||
|
|
||||||
on:
|
on:
|
||||||
schedule:
|
schedule:
|
||||||
- cron: "0 0 * * *"
|
- cron: '0 0 * * *'
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|
@ -10,8 +10,8 @@ permissions:
|
||||||
jobs:
|
jobs:
|
||||||
issue-close-require:
|
issue-close-require:
|
||||||
permissions:
|
permissions:
|
||||||
issues: write # for actions-cool/issues-helper to update issues
|
issues: write # for actions-cool/issues-helper to update issues
|
||||||
pull-requests: write # for actions-cool/issues-helper to update PRs
|
pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: need reproduce
|
- name: need reproduce
|
||||||
|
|
|
||||||
6
.github/workflows/issue-remove-inactive.yml
vendored
6
.github/workflows/issue-remove-inactive.yml
vendored
|
|
@ -12,8 +12,8 @@ permissions:
|
||||||
jobs:
|
jobs:
|
||||||
issue-remove-inactive:
|
issue-remove-inactive:
|
||||||
permissions:
|
permissions:
|
||||||
issues: write # for actions-cool/issues-helper to update issues
|
issues: write # for actions-cool/issues-helper to update issues
|
||||||
pull-requests: write # for actions-cool/issues-helper to update PRs
|
pull-requests: write # for actions-cool/issues-helper to update PRs
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: remove inactive
|
- name: remove inactive
|
||||||
|
|
@ -22,4 +22,4 @@ jobs:
|
||||||
with:
|
with:
|
||||||
actions: 'remove-labels'
|
actions: 'remove-labels'
|
||||||
issue-number: ${{ github.event.issue.number }}
|
issue-number: ${{ github.event.issue.number }}
|
||||||
labels: 'Inactive'
|
labels: 'Inactive'
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -53,4 +53,3 @@ test-output
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
.next
|
.next
|
||||||
.env
|
|
||||||
|
|
|
||||||
3
.gitpod.yml
Normal file
3
.gitpod.yml
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
tasks:
|
||||||
|
- init: pnpm install
|
||||||
|
command: pnpm run start
|
||||||
9
.npmrc
9
.npmrc
|
|
@ -1,2 +1,11 @@
|
||||||
lockfile=false
|
lockfile=false
|
||||||
resolution-mode=highest
|
resolution-mode=highest
|
||||||
|
public-hoist-pattern[]=*@umijs/lint*
|
||||||
|
public-hoist-pattern[]=*changelog*
|
||||||
|
public-hoist-pattern[]=*commitlint*
|
||||||
|
public-hoist-pattern[]=*eslint*
|
||||||
|
public-hoist-pattern[]=*postcss*
|
||||||
|
public-hoist-pattern[]=*prettier*
|
||||||
|
public-hoist-pattern[]=*remark*
|
||||||
|
public-hoist-pattern[]=*semantic-release*
|
||||||
|
public-hoist-pattern[]=*stylelint*
|
||||||
|
|
|
||||||
|
|
@ -1,28 +1,63 @@
|
||||||
**/*.svg
|
# Prettierignore for LobeHub
|
||||||
.umi
|
################################################################
|
||||||
.umi-production
|
|
||||||
/dist
|
# general
|
||||||
.dockerignore
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.eslintignore
|
|
||||||
*.png
|
|
||||||
*.jpg
|
|
||||||
*.webp
|
|
||||||
*.toml
|
|
||||||
*.py
|
|
||||||
docker
|
|
||||||
.editorconfig
|
.editorconfig
|
||||||
Dockerfile*
|
|
||||||
.gitignore
|
|
||||||
.prettierignore
|
|
||||||
LICENSE
|
|
||||||
.eslintcache
|
|
||||||
*.lock
|
|
||||||
yarn-error.log
|
|
||||||
.idea
|
.idea
|
||||||
|
.vscode
|
||||||
|
.history
|
||||||
|
.temp
|
||||||
|
.env.local
|
||||||
.husky
|
.husky
|
||||||
.npmrc
|
.npmrc
|
||||||
.env.local
|
.gitkeep
|
||||||
.next
|
venv
|
||||||
|
temp
|
||||||
|
tmp
|
||||||
|
LICENSE
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules
|
||||||
|
*.log
|
||||||
|
*.lock
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
|
# ci
|
||||||
|
coverage
|
||||||
|
.coverage
|
||||||
|
.eslintcache
|
||||||
|
.stylelintcache
|
||||||
|
test-output
|
||||||
__snapshots__
|
__snapshots__
|
||||||
.snap
|
*.snap
|
||||||
|
|
||||||
|
# production
|
||||||
|
dist
|
||||||
|
es
|
||||||
|
lib
|
||||||
|
logs
|
||||||
|
|
||||||
|
# umi
|
||||||
|
.umi
|
||||||
|
.umi-production
|
||||||
|
.umi-test
|
||||||
|
.dumi/tmp*
|
||||||
|
|
||||||
|
# ignore files
|
||||||
|
.*ignore
|
||||||
|
|
||||||
|
# docker
|
||||||
|
docker
|
||||||
|
Dockerfile*
|
||||||
|
|
||||||
|
# image
|
||||||
|
*.webp
|
||||||
|
*.gif
|
||||||
|
*.png
|
||||||
|
*.jpg
|
||||||
|
*.svg
|
||||||
|
|
||||||
|
# misc
|
||||||
|
# add other ignore file below
|
||||||
|
.next
|
||||||
|
|
@ -1,10 +1 @@
|
||||||
module.exports = {
|
module.exports = require('@lobehub/lint').prettier;
|
||||||
printWidth: 120,
|
|
||||||
singleQuote: true,
|
|
||||||
trailingComma: 'all',
|
|
||||||
proseWrap: 'never',
|
|
||||||
endOfLine: 'lf',
|
|
||||||
overrides: [{ files: '.prettierrc', options: { parser: 'json' } }],
|
|
||||||
plugins: [require.resolve('prettier-plugin-packagejson'), require.resolve('prettier-plugin-organize-imports')],
|
|
||||||
pluginSearchDirs: false,
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1 @@
|
||||||
module.exports = {
|
module.exports = require('@lobehub/lint').semanticRelease;
|
||||||
extends: ['semantic-release-config-gitmoji'],
|
|
||||||
branches: ['master'],
|
|
||||||
};
|
|
||||||
|
|
|
||||||
1
.remarkrc.js
Normal file
1
.remarkrc.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('@lobehub/lint').remarklint;
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
|
const config = require('@lobehub/lint').stylelint;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
extends: ['stylelint-config-recommended', 'stylelint-config-clean-order'],
|
...config,
|
||||||
files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
|
|
||||||
plugins: ['stylelint-order'],
|
|
||||||
customSyntax: 'postcss-styled-syntax',
|
|
||||||
rules: {
|
rules: {
|
||||||
'no-empty-source': null,
|
'selector-id-pattern': null,
|
||||||
'no-invalid-double-slash-comments': null,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ Or clone it for local development:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ git clone https://github.com/lobehub/lobe-chat.git
|
$ git clone https://github.com/lobehub/lobe-chat.git
|
||||||
$ cd canisminor-template
|
$ cd lobe-chat
|
||||||
$ pnpm install
|
$ pnpm install
|
||||||
$ pnpm start
|
$ pnpm start
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
"sideEffects": false,
|
"sideEffects": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"dev": "PORT=3010 next dev",
|
"dev": "next dev -p 3010",
|
||||||
"lint": "eslint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
|
"lint": "eslint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
|
||||||
"lint:md": "remark . --quiet --frail --output",
|
"lint:md": "remark . --quiet --frail --output",
|
||||||
"lint:style": "stylelint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
|
"lint:style": "stylelint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
|
||||||
|
|
@ -88,7 +88,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@commitlint/cli": "^17",
|
"@commitlint/cli": "^17",
|
||||||
"@lobehub/lint": "^1",
|
"@lobehub/lint": "latest",
|
||||||
"@next/eslint-plugin-next": "^13",
|
"@next/eslint-plugin-next": "^13",
|
||||||
"@testing-library/jest-dom": "^5",
|
"@testing-library/jest-dom": "^5",
|
||||||
"@testing-library/react": "^14",
|
"@testing-library/react": "^14",
|
||||||
|
|
@ -110,8 +110,8 @@
|
||||||
"node-fetch": "^3",
|
"node-fetch": "^3",
|
||||||
"postcss-styled-syntax": "^0.4",
|
"postcss-styled-syntax": "^0.4",
|
||||||
"prettier": "^2",
|
"prettier": "^2",
|
||||||
"prettier-plugin-organize-imports": "^3",
|
"remark": "^14",
|
||||||
"prettier-plugin-packagejson": "^2",
|
"remark-cli": "^11",
|
||||||
"semantic-release": "^21",
|
"semantic-release": "^21",
|
||||||
"semantic-release-config-gitmoji": "^1",
|
"semantic-release-config-gitmoji": "^1",
|
||||||
"stylelint": "^15",
|
"stylelint": "^15",
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,26 @@ export const useStyles = createStyles(({ css, token }) => ({
|
||||||
|
|
||||||
export default ({ children }: PropsWithChildren) => {
|
export default ({ children }: PropsWithChildren) => {
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
const [sessionsWidth, sessionExpandable] = useSettings((s) => [s.sessionsWidth, s.sessionExpandable], shallow);
|
const [sessionsWidth, sessionExpandable] = useSettings(
|
||||||
|
(s) => [s.sessionsWidth, s.sessionExpandable],
|
||||||
|
shallow,
|
||||||
|
);
|
||||||
const [tmpWidth, setWidth] = useState(sessionsWidth);
|
const [tmpWidth, setWidth] = useState(sessionsWidth);
|
||||||
if (tmpWidth !== sessionsWidth) setWidth(sessionsWidth);
|
if (tmpWidth !== sessionsWidth) setWidth(sessionsWidth);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DraggablePanel
|
<DraggablePanel
|
||||||
placement="left"
|
className={styles.panel}
|
||||||
|
defaultSize={{ width: tmpWidth }}
|
||||||
|
expand={sessionExpandable}
|
||||||
maxWidth={400}
|
maxWidth={400}
|
||||||
minWidth={256}
|
minWidth={256}
|
||||||
defaultSize={{ width: tmpWidth }}
|
onExpandChange={(expand) => {
|
||||||
size={{ width: sessionsWidth, height: '100vh' }}
|
useSettings.setState({
|
||||||
|
sessionExpandable: expand,
|
||||||
|
sessionsWidth: expand ? 320 : 0,
|
||||||
|
});
|
||||||
|
}}
|
||||||
onSizeChange={(_, size) => {
|
onSizeChange={(_, size) => {
|
||||||
if (!size) return;
|
if (!size) return;
|
||||||
|
|
||||||
|
|
@ -36,14 +45,8 @@ export default ({ children }: PropsWithChildren) => {
|
||||||
setWidth(nextWidth);
|
setWidth(nextWidth);
|
||||||
useSettings.setState({ sessionsWidth: nextWidth });
|
useSettings.setState({ sessionsWidth: nextWidth });
|
||||||
}}
|
}}
|
||||||
expand={sessionExpandable}
|
placement="left"
|
||||||
onExpandChange={(expand) => {
|
size={{ height: '100vh', width: sessionsWidth }}
|
||||||
useSettings.setState({
|
|
||||||
sessionsWidth: expand ? 320 : 0,
|
|
||||||
sessionExpandable: expand,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
className={styles.panel}
|
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</DraggablePanel>
|
</DraggablePanel>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { getInputVariablesFromMessages } from '@/helpers/prompt';
|
|
||||||
import { ChatMessage } from '@lobehub/ui';
|
import { ChatMessage } from '@lobehub/ui';
|
||||||
|
|
||||||
|
import { getInputVariablesFromMessages } from '@/helpers/prompt';
|
||||||
|
|
||||||
describe('getInputVariablesFromMessages 方法', () => {
|
describe('getInputVariablesFromMessages 方法', () => {
|
||||||
it('应当在输入为空数组时返回空数组', () => {
|
it('应当在输入为空数组时返回空数组', () => {
|
||||||
const result = getInputVariablesFromMessages([]);
|
const result = getInputVariablesFromMessages([]);
|
||||||
|
|
|
||||||
|
|
@ -11,14 +11,17 @@ export const getChatPromptTemplate = (chatMessages: ChatMessage[]) =>
|
||||||
chatMessages.map((m) => {
|
chatMessages.map((m) => {
|
||||||
switch (m.role) {
|
switch (m.role) {
|
||||||
default:
|
default:
|
||||||
case 'user':
|
case 'user': {
|
||||||
return HumanMessagePromptTemplate.fromTemplate(m.content);
|
return HumanMessagePromptTemplate.fromTemplate(m.content);
|
||||||
|
}
|
||||||
|
|
||||||
case 'system':
|
case 'system': {
|
||||||
return SystemMessagePromptTemplate.fromTemplate(m.content);
|
return SystemMessagePromptTemplate.fromTemplate(m.content);
|
||||||
|
}
|
||||||
|
|
||||||
case 'assistant':
|
case 'assistant': {
|
||||||
return AIMessagePromptTemplate.fromTemplate(m.content);
|
return AIMessagePromptTemplate.fromTemplate(m.content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
import { Compressor } from '@/utils/compass';
|
|
||||||
import { ChatMessage } from '@lobehub/ui';
|
import { ChatMessage } from '@lobehub/ui';
|
||||||
|
|
||||||
|
import { Compressor } from '@/utils/compass';
|
||||||
|
|
||||||
export const genShareMessagesUrl = (messages: ChatMessage[], systemRole?: string) => {
|
export const genShareMessagesUrl = (messages: ChatMessage[], systemRole?: string) => {
|
||||||
const compassedMsg = systemRole ? [{ role: 'system', content: systemRole }, ...messages] : messages;
|
const compassedMsg = systemRole
|
||||||
|
? [{ content: systemRole, role: 'system' }, ...messages]
|
||||||
|
: messages;
|
||||||
|
|
||||||
return `/share?messages=${Compressor.compress(JSON.stringify(compassedMsg))}`;
|
return `/share?messages=${Compressor.compress(JSON.stringify(compassedMsg))}`;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
import { ThemeProvider } from '@lobehub/ui';
|
import { ThemeProvider } from '@lobehub/ui';
|
||||||
import { App, ConfigProvider } from 'antd';
|
import { App, ConfigProvider } from 'antd';
|
||||||
import 'antd/dist/reset.css';
|
import 'antd/dist/reset.css';
|
||||||
|
|
||||||
import Zh_CN from 'antd/locale/zh_CN';
|
import Zh_CN from 'antd/locale/zh_CN';
|
||||||
import { PropsWithChildren, useEffect } from 'react';
|
import { PropsWithChildren, useEffect } from 'react';
|
||||||
|
|
||||||
import { useChatStore } from 'src/store/session';
|
import { useChatStore } from 'src/store/session';
|
||||||
|
|
||||||
import { GlobalStyle, useStyles } from './style';
|
import { GlobalStyle, useStyles } from './style';
|
||||||
|
|
||||||
const Layout = ({ children }: PropsWithChildren) => {
|
const Layout = ({ children }: PropsWithChildren) => {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ export const useStyles = createStyles(({ css, token }) => ({
|
||||||
background-image: linear-gradient(
|
background-image: linear-gradient(
|
||||||
180deg,
|
180deg,
|
||||||
${token.colorBgContainer} 0%,
|
${token.colorBgContainer} 0%,
|
||||||
rgba(255, 255, 255, 0) 20%
|
rgba(255, 255, 255, 0%) 20%
|
||||||
);
|
);
|
||||||
|
|
||||||
:has(#ChatLayout, #FlowLayout) {
|
:has(#ChatLayout, #FlowLayout) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import Layout from '@/layout';
|
|
||||||
import { Analytics } from '@vercel/analytics/react';
|
import { Analytics } from '@vercel/analytics/react';
|
||||||
import type { AppProps } from 'next/app';
|
import type { AppProps } from 'next/app';
|
||||||
|
|
||||||
|
import Layout from '@/layout';
|
||||||
|
|
||||||
function MyApp({ Component, pageProps }: AppProps) {
|
function MyApp({ Component, pageProps }: AppProps) {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { extractStaticStyle, StyleProvider } from 'antd-style';
|
import { StyleProvider, extractStaticStyle } from 'antd-style';
|
||||||
import Document, { DocumentContext, Head, Html, Main, NextScript } from 'next/document';
|
import Document, { DocumentContext, Head, Html, Main, NextScript } from 'next/document';
|
||||||
|
|
||||||
class MyDocument extends Document {
|
class MyDocument extends Document {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { LangChainParams } from '@/types/langchain';
|
|
||||||
import { LLMChain } from 'langchain/chains';
|
import { LLMChain } from 'langchain/chains';
|
||||||
import { ChatOpenAI } from 'langchain/chat_models/openai';
|
import { ChatOpenAI } from 'langchain/chat_models/openai';
|
||||||
import {
|
import {
|
||||||
|
|
@ -8,6 +7,8 @@ import {
|
||||||
SystemMessagePromptTemplate,
|
SystemMessagePromptTemplate,
|
||||||
} from 'langchain/prompts';
|
} from 'langchain/prompts';
|
||||||
|
|
||||||
|
import { LangChainParams } from '@/types/langchain';
|
||||||
|
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL;
|
const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL;
|
||||||
|
|
||||||
|
|
@ -19,13 +20,16 @@ export function LangChainStream(payload: LangChainParams) {
|
||||||
prompts.map((m) => {
|
prompts.map((m) => {
|
||||||
switch (m.role) {
|
switch (m.role) {
|
||||||
default:
|
default:
|
||||||
case 'user':
|
case 'user': {
|
||||||
return HumanMessagePromptTemplate.fromTemplate(m.content);
|
return HumanMessagePromptTemplate.fromTemplate(m.content);
|
||||||
case 'system':
|
}
|
||||||
|
case 'system': {
|
||||||
return SystemMessagePromptTemplate.fromTemplate(m.content);
|
return SystemMessagePromptTemplate.fromTemplate(m.content);
|
||||||
|
}
|
||||||
|
|
||||||
case 'assistant':
|
case 'assistant': {
|
||||||
return AIMessagePromptTemplate.fromTemplate(m.content);
|
return AIMessagePromptTemplate.fromTemplate(m.content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
@ -43,8 +47,7 @@ export function LangChainStream(payload: LangChainParams) {
|
||||||
{
|
{
|
||||||
streaming: true,
|
streaming: true,
|
||||||
...llm,
|
...llm,
|
||||||
// 暂时设定不重试 ,后续看是否需要支持重试
|
|
||||||
maxRetries: 0,
|
|
||||||
callbacks: [
|
callbacks: [
|
||||||
{
|
{
|
||||||
handleLLMNewToken(token) {
|
handleLLMNewToken(token) {
|
||||||
|
|
@ -60,14 +63,13 @@ export function LangChainStream(payload: LangChainParams) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
// 暂时设定不重试 ,后续看是否需要支持重试
|
||||||
|
maxRetries: 0,
|
||||||
},
|
},
|
||||||
isDev && OPENAI_PROXY_URL ? { basePath: OPENAI_PROXY_URL } : undefined,
|
isDev && OPENAI_PROXY_URL ? { basePath: OPENAI_PROXY_URL } : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const chain = new LLMChain({
|
const chain = new LLMChain({
|
||||||
prompt: chatPrompt,
|
|
||||||
llm: chat,
|
|
||||||
verbose: true,
|
|
||||||
callbacks: [
|
callbacks: [
|
||||||
{
|
{
|
||||||
handleChainError(err: Error): Promise<void> | void {
|
handleChainError(err: Error): Promise<void> | void {
|
||||||
|
|
@ -75,6 +77,9 @@ export function LangChainStream(payload: LangChainParams) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
llm: chat,
|
||||||
|
prompt: chatPrompt,
|
||||||
|
verbose: true,
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
// 使用转换后的聊天消息作为输入开始聊天
|
// 使用转换后的聊天消息作为输入开始聊天
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
import { OpenAIChatMessage } from '@/types/openai';
|
|
||||||
import { ChatOpenAI } from 'langchain/chat_models/openai';
|
import { ChatOpenAI } from 'langchain/chat_models/openai';
|
||||||
import { AIChatMessage, HumanChatMessage, SystemChatMessage } from 'langchain/schema';
|
import { AIChatMessage, HumanChatMessage, SystemChatMessage } from 'langchain/schema';
|
||||||
|
|
||||||
|
import { OpenAIChatMessage } from '@/types/openai';
|
||||||
|
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL;
|
const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL;
|
||||||
|
|
||||||
|
|
@ -10,13 +11,36 @@ const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL;
|
||||||
*/
|
*/
|
||||||
export interface OpenAIStreamPayload {
|
export interface OpenAIStreamPayload {
|
||||||
/**
|
/**
|
||||||
* @title 模型名称
|
* @title 控制生成文本中的惩罚系数,用于减少重复性
|
||||||
|
* @default 0
|
||||||
*/
|
*/
|
||||||
model: string;
|
frequency_penalty?: number;
|
||||||
|
/**
|
||||||
|
* @title 生成文本的最大长度
|
||||||
|
*/
|
||||||
|
max_tokens?: number;
|
||||||
/**
|
/**
|
||||||
* @title 聊天信息列表
|
* @title 聊天信息列表
|
||||||
*/
|
*/
|
||||||
messages: OpenAIChatMessage[];
|
messages: OpenAIChatMessage[];
|
||||||
|
/**
|
||||||
|
* @title 模型名称
|
||||||
|
*/
|
||||||
|
model: string;
|
||||||
|
/**
|
||||||
|
* @title 返回的文本数量
|
||||||
|
*/
|
||||||
|
n?: number;
|
||||||
|
/**
|
||||||
|
* @title 控制生成文本中的惩罚系数,用于减少主题的变化
|
||||||
|
* @default 0
|
||||||
|
*/
|
||||||
|
presence_penalty?: number;
|
||||||
|
/**
|
||||||
|
* @title 是否开启流式请求
|
||||||
|
* @default true
|
||||||
|
*/
|
||||||
|
stream?: boolean;
|
||||||
/**
|
/**
|
||||||
* @title 生成文本的随机度量,用于控制文本的创造性和多样性
|
* @title 生成文本的随机度量,用于控制文本的创造性和多样性
|
||||||
* @default 0.5
|
* @default 0.5
|
||||||
|
|
@ -27,29 +51,6 @@ export interface OpenAIStreamPayload {
|
||||||
* @default 1
|
* @default 1
|
||||||
*/
|
*/
|
||||||
top_p?: number;
|
top_p?: number;
|
||||||
/**
|
|
||||||
* @title 控制生成文本中的惩罚系数,用于减少重复性
|
|
||||||
* @default 0
|
|
||||||
*/
|
|
||||||
frequency_penalty?: number;
|
|
||||||
/**
|
|
||||||
* @title 控制生成文本中的惩罚系数,用于减少主题的变化
|
|
||||||
* @default 0
|
|
||||||
*/
|
|
||||||
presence_penalty?: number;
|
|
||||||
/**
|
|
||||||
* @title 生成文本的最大长度
|
|
||||||
*/
|
|
||||||
max_tokens?: number;
|
|
||||||
/**
|
|
||||||
* @title 是否开启流式请求
|
|
||||||
* @default true
|
|
||||||
*/
|
|
||||||
stream?: boolean;
|
|
||||||
/**
|
|
||||||
* @title 返回的文本数量
|
|
||||||
*/
|
|
||||||
n?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function OpenAIStream(payload: OpenAIStreamPayload) {
|
export function OpenAIStream(payload: OpenAIStreamPayload) {
|
||||||
|
|
@ -59,13 +60,16 @@ export function OpenAIStream(payload: OpenAIStreamPayload) {
|
||||||
const chatMessages = messages.map((m) => {
|
const chatMessages = messages.map((m) => {
|
||||||
switch (m.role) {
|
switch (m.role) {
|
||||||
default:
|
default:
|
||||||
case 'user':
|
case 'user': {
|
||||||
return new HumanChatMessage(m.content);
|
return new HumanChatMessage(m.content);
|
||||||
case 'system':
|
}
|
||||||
|
case 'system': {
|
||||||
return new SystemChatMessage(m.content);
|
return new SystemChatMessage(m.content);
|
||||||
|
}
|
||||||
|
|
||||||
case 'assistant':
|
case 'assistant': {
|
||||||
return new AIChatMessage(m.content);
|
return new AIChatMessage(m.content);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -82,9 +86,7 @@ export function OpenAIStream(payload: OpenAIStreamPayload) {
|
||||||
{
|
{
|
||||||
streaming: true,
|
streaming: true,
|
||||||
...params,
|
...params,
|
||||||
// 暂时设定不重试 ,后续看是否需要支持重试
|
|
||||||
maxRetries: 0,
|
|
||||||
verbose: true,
|
|
||||||
callbacks: [
|
callbacks: [
|
||||||
{
|
{
|
||||||
handleLLMNewToken(token) {
|
handleLLMNewToken(token) {
|
||||||
|
|
@ -100,6 +102,9 @@ export function OpenAIStream(payload: OpenAIStreamPayload) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
// 暂时设定不重试 ,后续看是否需要支持重试
|
||||||
|
maxRetries: 0,
|
||||||
|
verbose: true,
|
||||||
},
|
},
|
||||||
isDev && OPENAI_PROXY_URL ? { basePath: OPENAI_PROXY_URL } : undefined,
|
isDev && OPENAI_PROXY_URL ? { basePath: OPENAI_PROXY_URL } : undefined,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,13 @@ export type ConfigCellProps = ConfigItem;
|
||||||
export const ConfigCell = memo<ConfigCellProps>(({ icon, label, value }) => {
|
export const ConfigCell = memo<ConfigCellProps>(({ icon, label, value }) => {
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
return (
|
return (
|
||||||
<Flexbox horizontal distribution={'space-between'} padding={'10px 12px'} className={styles.container}>
|
<Flexbox
|
||||||
<Flexbox horizontal gap={8}>
|
className={styles.container}
|
||||||
|
distribution={'space-between'}
|
||||||
|
horizontal
|
||||||
|
padding={'10px 12px'}
|
||||||
|
>
|
||||||
|
<Flexbox gap={8} horizontal>
|
||||||
<Icon icon={icon} />
|
<Icon icon={icon} />
|
||||||
<Flexbox>{label}</Flexbox>
|
<Flexbox>{label}</Flexbox>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
|
|
@ -45,13 +50,13 @@ export const ConfigCellGroup = memo<CellGroupProps>(({ items }) => {
|
||||||
<Flexbox className={styles.container}>
|
<Flexbox className={styles.container}>
|
||||||
{items.map(({ label, icon, value }, index) => (
|
{items.map(({ label, icon, value }, index) => (
|
||||||
<Flexbox
|
<Flexbox
|
||||||
key={label}
|
|
||||||
horizontal
|
|
||||||
distribution={'space-between'}
|
|
||||||
padding={'10px 12px'}
|
|
||||||
className={items.length === index + 1 ? undefined : styles.split}
|
className={items.length === index + 1 ? undefined : styles.split}
|
||||||
|
distribution={'space-between'}
|
||||||
|
horizontal
|
||||||
|
key={label}
|
||||||
|
padding={'10px 12px'}
|
||||||
>
|
>
|
||||||
<Flexbox horizontal gap={8}>
|
<Flexbox gap={8} horizontal>
|
||||||
<Icon icon={icon} />
|
<Icon icon={icon} />
|
||||||
<Flexbox>{label}</Flexbox>
|
<Flexbox>{label}</Flexbox>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { agentSelectors, sessionSelectors, useChatStore } from '@/store/session';
|
|
||||||
import { Avatar } from '@lobehub/ui';
|
import { Avatar } from '@lobehub/ui';
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles } from 'antd-style';
|
||||||
import isEqual from 'fast-deep-equal';
|
import isEqual from 'fast-deep-equal';
|
||||||
|
|
@ -7,19 +6,21 @@ import { memo } from 'react';
|
||||||
import { Center, Flexbox } from 'react-layout-kit';
|
import { Center, Flexbox } from 'react-layout-kit';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
|
import { agentSelectors, sessionSelectors, useChatStore } from '@/store/session';
|
||||||
|
|
||||||
import { ConfigCell, ConfigCellGroup } from './ConfigCell';
|
import { ConfigCell, ConfigCellGroup } from './ConfigCell';
|
||||||
|
|
||||||
const useStyles = createStyles(({ css, token }) => ({
|
const useStyles = createStyles(({ css, token }) => ({
|
||||||
title: css`
|
|
||||||
font-size: ${token.fontSizeHeading4}px;
|
|
||||||
font-weight: bold;
|
|
||||||
`,
|
|
||||||
desc: css`
|
desc: css`
|
||||||
color: ${token.colorText};
|
color: ${token.colorText};
|
||||||
`,
|
`,
|
||||||
model: css`
|
model: css`
|
||||||
color: ${token.colorTextTertiary};
|
color: ${token.colorTextTertiary};
|
||||||
`,
|
`,
|
||||||
|
title: css`
|
||||||
|
font-size: ${token.fontSizeHeading4}px;
|
||||||
|
font-weight: bold;
|
||||||
|
`,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const ReadMode = memo(() => {
|
const ReadMode = memo(() => {
|
||||||
|
|
@ -30,13 +31,13 @@ const ReadMode = memo(() => {
|
||||||
const model = useChatStore(agentSelectors.currentAgentModel, shallow);
|
const model = useChatStore(agentSelectors.currentAgentModel, shallow);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Center style={{ marginTop: 8 }} gap={12} padding={'32px 16px'}>
|
<Center gap={12} padding={'32px 16px'} style={{ marginTop: 8 }}>
|
||||||
<Avatar size={100} avatar={avatar} />
|
<Avatar avatar={avatar} size={100} />
|
||||||
<Flexbox className={styles.title}>{title || '默认对话'}</Flexbox>
|
<Flexbox className={styles.title}>{title || '默认对话'}</Flexbox>
|
||||||
<Flexbox className={styles.model}>{model}</Flexbox>
|
<Flexbox className={styles.model}>{model}</Flexbox>
|
||||||
<Flexbox className={styles.desc}>{session.meta.description}</Flexbox>
|
<Flexbox className={styles.desc}>{session.meta.description}</Flexbox>
|
||||||
|
|
||||||
<Flexbox gap={12} flex={1} width={'100%'}>
|
<Flexbox flex={1} gap={12} width={'100%'}>
|
||||||
<ConfigCell icon={LucideBrain} label={'提示词'} />
|
<ConfigCell icon={LucideBrain} label={'提示词'} />
|
||||||
|
|
||||||
<ConfigCellGroup
|
<ConfigCellGroup
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
|
import { ActionIcon, DraggablePanel } from '@lobehub/ui';
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles } from 'antd-style';
|
||||||
|
import { LucideEdit, LucideX } from 'lucide-react';
|
||||||
import { Flexbox } from 'react-layout-kit';
|
import { Flexbox } from 'react-layout-kit';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
import { useChatStore } from '@/store/session';
|
import { useChatStore } from '@/store/session';
|
||||||
import { ActionIcon, DraggablePanel } from '@lobehub/ui';
|
|
||||||
import { LucideEdit, LucideX } from 'lucide-react';
|
|
||||||
import ReadMode from './ReadMode';
|
import ReadMode from './ReadMode';
|
||||||
|
|
||||||
const useStyles = createStyles(({ css, token }) => ({
|
const useStyles = createStyles(({ css, token }) => ({
|
||||||
|
|
@ -18,29 +19,38 @@ const useStyles = createStyles(({ css, token }) => ({
|
||||||
|
|
||||||
const Config = () => {
|
const Config = () => {
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
const [showAgentSettings, toggleConfig] = useChatStore((s) => [s.showAgentSettings, s.toggleConfig], shallow);
|
const [showAgentSettings, toggleConfig] = useChatStore(
|
||||||
|
(s) => [s.showAgentSettings, s.toggleConfig],
|
||||||
|
shallow,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DraggablePanel
|
<DraggablePanel
|
||||||
|
className={styles.drawer}
|
||||||
|
expand={showAgentSettings}
|
||||||
expandable={false}
|
expandable={false}
|
||||||
placement={'right'}
|
minWidth={400}
|
||||||
mode={'float'}
|
mode={'float'}
|
||||||
pin
|
pin
|
||||||
|
placement={'right'}
|
||||||
resize={{ left: false }}
|
resize={{ left: false }}
|
||||||
minWidth={400}
|
|
||||||
expand={showAgentSettings}
|
|
||||||
className={styles.drawer}
|
|
||||||
>
|
>
|
||||||
<Flexbox padding={16} horizontal align={'center'} distribution={'space-between'} className={styles.header}>
|
<Flexbox
|
||||||
|
align={'center'}
|
||||||
|
className={styles.header}
|
||||||
|
distribution={'space-between'}
|
||||||
|
horizontal
|
||||||
|
padding={16}
|
||||||
|
>
|
||||||
<Flexbox>会话设置</Flexbox>
|
<Flexbox>会话设置</Flexbox>
|
||||||
<Flexbox horizontal>
|
<Flexbox horizontal>
|
||||||
<ActionIcon icon={LucideEdit} title={'编辑'} />
|
<ActionIcon icon={LucideEdit} title={'编辑'} />
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
icon={LucideX}
|
icon={LucideX}
|
||||||
title={'关闭'}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleConfig(false);
|
toggleConfig(false);
|
||||||
}}
|
}}
|
||||||
|
title={'关闭'}
|
||||||
/>
|
/>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
|
|
|
||||||
|
|
@ -7,20 +7,25 @@ import { chatSelectors, useChatStore } from '@/store/session';
|
||||||
|
|
||||||
const List = () => {
|
const List = () => {
|
||||||
const data = useChatStore(chatSelectors.currentChats, isEqual);
|
const data = useChatStore(chatSelectors.currentChats, isEqual);
|
||||||
const [deleteMessage, resendMessage] = useChatStore((s) => [s.deleteMessage, s.resendMessage], shallow);
|
const [deleteMessage, resendMessage] = useChatStore(
|
||||||
|
(s) => [s.deleteMessage, s.resendMessage],
|
||||||
|
shallow,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChatList
|
<ChatList
|
||||||
data={data}
|
data={data}
|
||||||
onActionClick={(key, id) => {
|
onActionClick={(key, id) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case 'delete':
|
case 'delete': {
|
||||||
deleteMessage(id);
|
deleteMessage(id);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case 'regenerate':
|
case 'regenerate': {
|
||||||
resendMessage(id);
|
resendMessage(id);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
style={{ marginTop: 24 }}
|
style={{ marginTop: 24 }}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,12 @@ const ChatInput = () => {
|
||||||
|
|
||||||
const [inputHeight] = useSettings((s) => [s.inputHeight], shallow);
|
const [inputHeight] = useSettings((s) => [s.inputHeight], shallow);
|
||||||
const [totalToken, model, sendMessage, clearMessage] = useChatStore(
|
const [totalToken, model, sendMessage, clearMessage] = useChatStore(
|
||||||
(s) => [chatSelectors.totalTokenCount(s), agentSelectors.currentAgentModel(s), s.sendMessage, s.clearMessage],
|
(s) => [
|
||||||
|
chatSelectors.totalTokenCount(s),
|
||||||
|
agentSelectors.currentAgentModel(s),
|
||||||
|
s.sendMessage,
|
||||||
|
s.clearMessage,
|
||||||
|
],
|
||||||
shallow,
|
shallow,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -25,14 +30,14 @@ const ChatInput = () => {
|
||||||
expandable={false}
|
expandable={false}
|
||||||
fullscreen={expand}
|
fullscreen={expand}
|
||||||
minHeight={200}
|
minHeight={200}
|
||||||
placement="bottom"
|
|
||||||
size={{ width: '100%', height: inputHeight }}
|
|
||||||
onSizeChange={(_, size) => {
|
onSizeChange={(_, size) => {
|
||||||
if (!size) return;
|
if (!size) return;
|
||||||
useSettings.setState({
|
useSettings.setState({
|
||||||
inputHeight: typeof size.height === 'string' ? Number.parseInt(size.height) : size.height,
|
inputHeight: typeof size.height === 'string' ? Number.parseInt(size.height) : size.height,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
placement="bottom"
|
||||||
|
size={{ height: inputHeight, width: '100%' }}
|
||||||
>
|
>
|
||||||
<ChatInputArea
|
<ChatInputArea
|
||||||
actions={
|
actions={
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { memo } from 'react';
|
|
||||||
|
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles } from 'antd-style';
|
||||||
|
import { memo } from 'react';
|
||||||
import { Flexbox } from 'react-layout-kit';
|
import { Flexbox } from 'react-layout-kit';
|
||||||
|
|
||||||
import ChatList from './ChatList';
|
import ChatList from './ChatList';
|
||||||
import ChatInput from './Input';
|
import ChatInput from './Input';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
import { sessionSelectors, useChatStore } from '@/store/session';
|
|
||||||
import { ActionIcon, Avatar } from '@lobehub/ui';
|
import { ActionIcon, Avatar } from '@lobehub/ui';
|
||||||
import { createStyles, useTheme } from 'antd-style';
|
import { createStyles, useTheme } from 'antd-style';
|
||||||
import { ArchiveIcon, MoreVerticalIcon, Share2Icon } from 'lucide-react';
|
import { ArchiveIcon, MoreVerticalIcon, Share2Icon } from 'lucide-react';
|
||||||
|
|
@ -6,15 +5,17 @@ import { memo } from 'react';
|
||||||
import { Flexbox } from 'react-layout-kit';
|
import { Flexbox } from 'react-layout-kit';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
|
import { sessionSelectors, useChatStore } from '@/store/session';
|
||||||
|
|
||||||
const useStyles = createStyles(({ css, token }) => ({
|
const useStyles = createStyles(({ css, token }) => ({
|
||||||
title: css`
|
|
||||||
font-weight: bold;
|
|
||||||
color: ${token.colorText};
|
|
||||||
`,
|
|
||||||
desc: css`
|
desc: css`
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: ${token.colorTextTertiary};
|
color: ${token.colorTextTertiary};
|
||||||
`,
|
`,
|
||||||
|
title: css`
|
||||||
|
font-weight: bold;
|
||||||
|
color: ${token.colorText};
|
||||||
|
`,
|
||||||
}));
|
}));
|
||||||
const Header = memo(() => {
|
const Header = memo(() => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
@ -38,9 +39,9 @@ const Header = memo(() => {
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
return (
|
return (
|
||||||
<Flexbox
|
<Flexbox
|
||||||
horizontal
|
|
||||||
align={'center'}
|
align={'center'}
|
||||||
distribution={'space-between'}
|
distribution={'space-between'}
|
||||||
|
horizontal
|
||||||
padding={8}
|
padding={8}
|
||||||
paddingInline={16}
|
paddingInline={16}
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -48,22 +49,22 @@ const Header = memo(() => {
|
||||||
gridArea: 'header',
|
gridArea: 'header',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Flexbox horizontal align={'center'} gap={12}>
|
<Flexbox align={'center'} gap={12} horizontal>
|
||||||
<Avatar size={40} title={'123'} avatar={meta && sessionSelectors.getAgentAvatar(meta)} />
|
<Avatar avatar={meta && sessionSelectors.getAgentAvatar(meta)} size={40} title={'123'} />
|
||||||
<Flexbox>
|
<Flexbox>
|
||||||
<Flexbox className={styles.title}>{meta?.title}</Flexbox>
|
<Flexbox className={styles.title}>{meta?.title}</Flexbox>
|
||||||
<Flexbox className={styles.desc}>{meta?.description || '暂无描述'}</Flexbox>
|
<Flexbox className={styles.desc}>{meta?.description || '暂无描述'}</Flexbox>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
<Flexbox horizontal gap={16}>
|
<Flexbox gap={16} horizontal>
|
||||||
<ActionIcon
|
<ActionIcon
|
||||||
title={'分享'}
|
|
||||||
icon={Share2Icon}
|
icon={Share2Icon}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// genShareUrl();
|
// genShareUrl();
|
||||||
}}
|
}}
|
||||||
|
title={'分享'}
|
||||||
/>
|
/>
|
||||||
<ActionIcon title={'归档'} icon={ArchiveIcon} />
|
<ActionIcon icon={ArchiveIcon} title={'归档'} />
|
||||||
<ActionIcon icon={MoreVerticalIcon} onClick={toggleConfig} />
|
<ActionIcon icon={MoreVerticalIcon} onClick={toggleConfig} />
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
import { ActionIcon, Logo, SearchBar } from '@lobehub/ui';
|
import { ActionIcon, Logo, SearchBar } from '@lobehub/ui';
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles } from 'antd-style';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
|
import Link from 'next/link';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { Flexbox } from 'react-layout-kit';
|
import { Flexbox } from 'react-layout-kit';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
import { useChatStore } from '@/store/session';
|
import { useChatStore } from '@/store/session';
|
||||||
import Link from 'next/link';
|
|
||||||
|
|
||||||
export const useStyles = createStyles(({ css, token }) => ({
|
export const useStyles = createStyles(({ css, token }) => ({
|
||||||
logo: css`
|
logo: css`
|
||||||
|
|
@ -21,22 +21,25 @@ export const useStyles = createStyles(({ css, token }) => ({
|
||||||
const Header = memo(() => {
|
const Header = memo(() => {
|
||||||
const { styles } = useStyles();
|
const { styles } = useStyles();
|
||||||
|
|
||||||
const [keywords, createSession] = useChatStore((s) => [s.searchKeywords, s.createSession], shallow);
|
const [keywords, createSession] = useChatStore(
|
||||||
|
(s) => [s.searchKeywords, s.createSession],
|
||||||
|
shallow,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flexbox gap={16} padding={'16px 12px 0 16px'} className={styles.top}>
|
<Flexbox className={styles.top} gap={16} padding={'16px 12px 0 16px'}>
|
||||||
<Flexbox horizontal distribution={'space-between'}>
|
<Flexbox distribution={'space-between'} horizontal>
|
||||||
<Link href={'/'}>
|
<Link href={'/'}>
|
||||||
<Logo type={'text'} size={36} className={styles.logo} />
|
<Logo className={styles.logo} size={36} type={'text'} />
|
||||||
</Link>
|
</Link>
|
||||||
<ActionIcon title={'新对话'} icon={Plus} style={{ minWidth: 32 }} onClick={createSession} />
|
<ActionIcon icon={Plus} onClick={createSession} style={{ minWidth: 32 }} title={'新对话'} />
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
<SearchBar
|
<SearchBar
|
||||||
allowClear
|
allowClear
|
||||||
value={keywords}
|
onChange={(e) => useChatStore.setState({ searchKeywords: e.target.value })}
|
||||||
placeholder="Search..."
|
placeholder="Search..."
|
||||||
type={'block'}
|
type={'block'}
|
||||||
onChange={(e) => useChatStore.setState({ searchKeywords: e.target.value })}
|
value={keywords}
|
||||||
/>
|
/>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,12 @@
|
||||||
|
import { CloseOutlined } from '@ant-design/icons';
|
||||||
import { Avatar, List } from '@lobehub/ui';
|
import { Avatar, List } from '@lobehub/ui';
|
||||||
|
import { Popconfirm } from 'antd';
|
||||||
import { createStyles } from 'antd-style';
|
import { createStyles } from 'antd-style';
|
||||||
import { FC, memo } from 'react';
|
import { FC, memo } from 'react';
|
||||||
import { Flexbox } from 'react-layout-kit';
|
import { Flexbox } from 'react-layout-kit';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
import { sessionSelectors, useChatStore } from '@/store/session';
|
import { sessionSelectors, useChatStore } from '@/store/session';
|
||||||
import { CloseOutlined } from '@ant-design/icons';
|
|
||||||
import { Popconfirm } from 'antd';
|
|
||||||
|
|
||||||
const useStyles = createStyles(({ css, cx }) => {
|
const useStyles = createStyles(({ css, cx }) => {
|
||||||
const closeCtn = css`
|
const closeCtn = css`
|
||||||
|
|
@ -23,6 +23,10 @@ const useStyles = createStyles(({ css, cx }) => {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
`;
|
`;
|
||||||
return {
|
return {
|
||||||
|
active: css`
|
||||||
|
opacity: 1;
|
||||||
|
`,
|
||||||
|
closeCtn,
|
||||||
container: css`
|
container: css`
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
|
|
@ -35,10 +39,6 @@ const useStyles = createStyles(({ css, cx }) => {
|
||||||
time: css`
|
time: css`
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
`,
|
`,
|
||||||
closeCtn,
|
|
||||||
active: css`
|
|
||||||
opacity: 1;
|
|
||||||
`,
|
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -51,43 +51,51 @@ interface SessionItemProps {
|
||||||
|
|
||||||
const SessionItem: FC<SessionItemProps> = memo(({ id, active, simple = true, loading }) => {
|
const SessionItem: FC<SessionItemProps> = memo(({ id, active, simple = true, loading }) => {
|
||||||
const { styles, theme, cx } = useStyles();
|
const { styles, theme, cx } = useStyles();
|
||||||
const [title, systemRole, avatar, avatarBackground, updateAt, switchAgent, removeSession] = useChatStore((s) => {
|
const [title, systemRole, avatar, avatarBackground, updateAt, switchAgent, removeSession] =
|
||||||
const session = sessionSelectors.getSessionById(id)(s);
|
useChatStore((s) => {
|
||||||
const meta = session.meta;
|
const session = sessionSelectors.getSessionById(id)(s);
|
||||||
|
const meta = session.meta;
|
||||||
|
|
||||||
const systemRole = session.config.systemRole;
|
const systemRole = session.config.systemRole;
|
||||||
|
|
||||||
return [
|
return [
|
||||||
meta.title || systemRole || '默认角色',
|
meta.title || systemRole || '默认角色',
|
||||||
systemRole,
|
systemRole,
|
||||||
sessionSelectors.getAgentAvatar(meta),
|
sessionSelectors.getAgentAvatar(meta),
|
||||||
meta.backgroundColor,
|
meta.backgroundColor,
|
||||||
session?.updateAt,
|
session?.updateAt,
|
||||||
s.switchSession,
|
s.switchSession,
|
||||||
s.removeSession,
|
s.removeSession,
|
||||||
];
|
];
|
||||||
}, shallow);
|
}, shallow);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Flexbox gap={4} paddingBlock={4} className={styles.container}>
|
<Flexbox className={styles.container} gap={4} paddingBlock={4}>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={'即将删除该会话,删除后该将无法找回,请确认你的操作。'}
|
|
||||||
placement={'right'}
|
|
||||||
arrow={false}
|
arrow={false}
|
||||||
overlayStyle={{ width: 280 }}
|
|
||||||
okButtonProps={{ danger: true }}
|
okButtonProps={{ danger: true }}
|
||||||
onConfirm={() => removeSession(id)}
|
onConfirm={() => removeSession(id)}
|
||||||
|
overlayStyle={{ width: 280 }}
|
||||||
|
placement={'right'}
|
||||||
|
title={'即将删除该会话,删除后该将无法找回,请确认你的操作。'}
|
||||||
>
|
>
|
||||||
<CloseOutlined className={cx(styles.closeCtn, active && styles.active)} />
|
<CloseOutlined className={cx(styles.closeCtn, active && styles.active)} />
|
||||||
</Popconfirm>
|
</Popconfirm>
|
||||||
<List.Item
|
<List.Item
|
||||||
loading={loading}
|
|
||||||
title={title}
|
|
||||||
description={simple ? undefined : systemRole}
|
|
||||||
active={active}
|
active={active}
|
||||||
date={updateAt}
|
avatar={
|
||||||
|
<Avatar
|
||||||
|
avatar={avatar}
|
||||||
|
background={avatarBackground}
|
||||||
|
shape="circle"
|
||||||
|
size={46}
|
||||||
|
title={title}
|
||||||
|
/>
|
||||||
|
}
|
||||||
classNames={{ time: styles.time }}
|
classNames={{ time: styles.time }}
|
||||||
avatar={<Avatar avatar={avatar} size={46} shape="circle" title={title} background={avatarBackground} />}
|
date={updateAt}
|
||||||
|
description={simple ? undefined : systemRole}
|
||||||
|
loading={loading}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
switchAgent(id);
|
switchAgent(id);
|
||||||
}}
|
}}
|
||||||
|
|
@ -95,6 +103,7 @@ const SessionItem: FC<SessionItemProps> = memo(({ id, active, simple = true, loa
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
color: theme.colorText,
|
color: theme.colorText,
|
||||||
}}
|
}}
|
||||||
|
title={title}
|
||||||
/>
|
/>
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export const useStyles = createStyles(({ css, token }) => ({
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding: 12px 12px;
|
padding: 12px;
|
||||||
|
|
||||||
background: ${rgba(token.colorBgLayout, 0.5)};
|
background: ${rgba(token.colorBgLayout, 0.5)};
|
||||||
backdrop-filter: blur(8px);
|
backdrop-filter: blur(8px);
|
||||||
|
|
@ -35,7 +35,13 @@ const SessionList = memo(() => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{list.map(({ id }) => (
|
{list.map(({ id }) => (
|
||||||
<SessionItem key={id} active={activeId === id} id={id} simple={false} loading={loading && id === activeId} />
|
<SessionItem
|
||||||
|
active={activeId === id}
|
||||||
|
id={id}
|
||||||
|
key={id}
|
||||||
|
loading={loading && id === activeId}
|
||||||
|
simple={false}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { memo } from 'react';
|
||||||
import { Flexbox } from 'react-layout-kit';
|
import { Flexbox } from 'react-layout-kit';
|
||||||
|
|
||||||
import FolderPanel from '@/features/FolderPanel';
|
import FolderPanel from '@/features/FolderPanel';
|
||||||
|
|
||||||
import Header from './Header';
|
import Header from './Header';
|
||||||
import SessionList from './List';
|
import SessionList from './List';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,33 @@
|
||||||
import { useSettings } from '@/store/settings';
|
|
||||||
import { ActionIcon, Logo, SideNav } from '@lobehub/ui';
|
import { ActionIcon, Logo, SideNav } from '@lobehub/ui';
|
||||||
import { Album, MessageSquare, Settings2 } from 'lucide-react';
|
import { Album, MessageSquare, Settings2 } from 'lucide-react';
|
||||||
import { memo } from 'react';
|
import { memo } from 'react';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
|
|
||||||
|
import { useSettings } from '@/store/settings';
|
||||||
|
|
||||||
const Sidebar = memo(() => {
|
const Sidebar = memo(() => {
|
||||||
const [tab, setTab] = useSettings((s) => [s.sidebarKey, s.switchSideBar], shallow);
|
const [tab, setTab] = useSettings((s) => [s.sidebarKey, s.switchSideBar], shallow);
|
||||||
return (
|
return (
|
||||||
<SideNav
|
<SideNav
|
||||||
avatar={<Logo size={40} />}
|
avatar={<Logo size={40} />}
|
||||||
|
bottomActions={<ActionIcon icon={Settings2} />}
|
||||||
style={{ height: '100vh' }}
|
style={{ height: '100vh' }}
|
||||||
topActions={
|
topActions={
|
||||||
<>
|
<>
|
||||||
<ActionIcon icon={MessageSquare} size="large" active={tab === 'chat'} onClick={() => setTab('chat')} />
|
<ActionIcon
|
||||||
<ActionIcon icon={Album} size="large" active={tab === 'market'} onClick={() => setTab('market')} />
|
active={tab === 'chat'}
|
||||||
|
icon={MessageSquare}
|
||||||
|
onClick={() => setTab('chat')}
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
|
<ActionIcon
|
||||||
|
active={tab === 'market'}
|
||||||
|
icon={Album}
|
||||||
|
onClick={() => setTab('market')}
|
||||||
|
size="large"
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
bottomActions={<ActionIcon icon={Settings2} />}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -38,12 +38,15 @@ const ChatLayout = () => {
|
||||||
<Head>
|
<Head>
|
||||||
<title>{title ? `${title} - LobeChat` : 'LobeChat'}</title>
|
<title>{title ? `${title} - LobeChat` : 'LobeChat'}</title>
|
||||||
</Head>
|
</Head>
|
||||||
<Flexbox width={'100%'} horizontal>
|
<Flexbox horizontal width={'100%'}>
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<Sessions />
|
<Sessions />
|
||||||
<Flexbox flex={1}>
|
<Flexbox flex={1}>
|
||||||
<Header />
|
<Header />
|
||||||
<Flexbox id={'lobe-conversion-container'} style={{ position: 'relative', height: 'calc(100vh - 64px)' }}>
|
<Flexbox
|
||||||
|
id={'lobe-conversion-container'}
|
||||||
|
style={{ height: 'calc(100vh - 64px)', position: 'relative' }}
|
||||||
|
>
|
||||||
<Conversation />
|
<Conversation />
|
||||||
<Config />
|
<Config />
|
||||||
</Flexbox>
|
</Flexbox>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1 @@
|
||||||
|
export { default } from './[id].page';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export {default} from './[id].page';
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1 @@
|
||||||
|
export { default } from './chat/index.page';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export {default} from './chat/index.page';
|
|
||||||
|
|
|
||||||
|
|
@ -4,30 +4,30 @@ import { OpenAIStreamPayload } from '@/pages/api/OpenAIStream';
|
||||||
export const promptSummaryAgentName = (content: string): Partial<OpenAIStreamPayload> => ({
|
export const promptSummaryAgentName = (content: string): Partial<OpenAIStreamPayload> => ({
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
role: 'system',
|
|
||||||
content: `你是一名擅长起名的起名大师,你需要将用户的描述总结为 20 个字以内的角色,格式要求如下:
|
content: `你是一名擅长起名的起名大师,你需要将用户的描述总结为 20 个字以内的角色,格式要求如下:
|
||||||
输入: {文本作为JSON引用字符串}
|
输入: {文本作为JSON引用字符串}
|
||||||
输出: {角色名}
|
输出: {角色名}
|
||||||
`,
|
`,
|
||||||
|
role: 'system',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'user',
|
|
||||||
content: `输入: {你是一名专业的前端开发者,擅长结合 vitest 和\`testing-library/react\` 书写单元测试。接下来用户会输入一串 ts 代码,你需要给出完善的单元测试。\n你需要注意,单元测试代码中,不应该使用 jest 。如果需要使用 \`jest.fn\`,请使用 \`vi.fn\` 替换}`,
|
content: `输入: {你是一名专业的前端开发者,擅长结合 vitest 和\`testing-library/react\` 书写单元测试。接下来用户会输入一串 ts 代码,你需要给出完善的单元测试。\n你需要注意,单元测试代码中,不应该使用 jest 。如果需要使用 \`jest.fn\`,请使用 \`vi.fn\` 替换}`,
|
||||||
},
|
|
||||||
{ role: 'assistant', content: '前端 vitest 测试专家' },
|
|
||||||
{
|
|
||||||
role: 'user',
|
role: 'user',
|
||||||
|
},
|
||||||
|
{ content: '前端 vitest 测试专家', role: 'assistant' },
|
||||||
|
{
|
||||||
content: `输入: {你是一名前端专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
content: `输入: {你是一名前端专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
|
||||||
},
|
|
||||||
{ role: 'assistant', content: 'js 转 ts 专家' },
|
|
||||||
{
|
|
||||||
role: 'user',
|
role: 'user',
|
||||||
|
},
|
||||||
|
{ content: 'js 转 ts 专家', role: 'assistant' },
|
||||||
|
{
|
||||||
content: `输入:{你是一名擅长比喻和隐喻的UX Writter。用户会输入文案,你需要给出3个优化后的结果,使用 markdown格式的文本。下面是一个例子:
|
content: `输入:{你是一名擅长比喻和隐喻的UX Writter。用户会输入文案,你需要给出3个优化后的结果,使用 markdown格式的文本。下面是一个例子:
|
||||||
输入:页面加载中
|
输入:页面加载中
|
||||||
输出:页面似乎在思考,一会儿才能准备好}`,
|
输出:页面似乎在思考,一会儿才能准备好}`,
|
||||||
|
role: 'user',
|
||||||
},
|
},
|
||||||
{ role: 'assistant', content: '文案比喻优化专家' },
|
{ content: '文案比喻优化专家', role: 'assistant' },
|
||||||
{ role: 'user', content: `输入: {${content}}` },
|
{ content: `输入: {${content}}`, role: 'user' },
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -35,7 +35,6 @@ export const promptSummaryAgentName = (content: string): Partial<OpenAIStreamPay
|
||||||
export const promptPickEmoji = (content: string): Partial<OpenAIStreamPayload> => ({
|
export const promptPickEmoji = (content: string): Partial<OpenAIStreamPayload> => ({
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
role: 'system',
|
|
||||||
content: `你是一名非常懂设计与时尚的设计师,你需要从用户的描述中匹配一个合适的 emoji。
|
content: `你是一名非常懂设计与时尚的设计师,你需要从用户的描述中匹配一个合适的 emoji。
|
||||||
输入:你是一名精通体验设计的设计系统设计师,设计系统存在诸多类别的 token,比如品牌色、成功色等,你需要为各个类别的 token 提供说明文案。
|
输入:你是一名精通体验设计的设计系统设计师,设计系统存在诸多类别的 token,比如品牌色、成功色等,你需要为各个类别的 token 提供说明文案。
|
||||||
输出: 💅
|
输出: 💅
|
||||||
|
|
@ -43,10 +42,11 @@ export const promptPickEmoji = (content: string): Partial<OpenAIStreamPayload> =
|
||||||
输入:用户会输入一串 ts 代码,为了确保所有功能和分支的 100% 的覆盖率,你需要给出需要考虑哪些数据场景。
|
输入:用户会输入一串 ts 代码,为了确保所有功能和分支的 100% 的覆盖率,你需要给出需要考虑哪些数据场景。
|
||||||
输出: 🧪
|
输出: 🧪
|
||||||
`,
|
`,
|
||||||
|
role: 'system',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'user',
|
|
||||||
content: `输入:${content}`,
|
content: `输入:${content}`,
|
||||||
|
role: 'user',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,29 +1,37 @@
|
||||||
import { OpenAIStreamPayload } from '@/pages/api/OpenAIStream';
|
import { OpenAIStreamPayload } from '@/pages/api/OpenAIStream';
|
||||||
import { OpenAIChatMessage } from '@/types/openai';
|
import { OpenAIChatMessage } from '@/types/openai';
|
||||||
|
|
||||||
export const promptSummaryTitle = (messages: OpenAIChatMessage[]): Partial<OpenAIStreamPayload> => ({
|
export const promptSummaryTitle = (
|
||||||
|
messages: OpenAIChatMessage[],
|
||||||
|
): Partial<OpenAIStreamPayload> => ({
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
|
content:
|
||||||
|
'你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题,不需要包含标点符号',
|
||||||
role: 'system',
|
role: 'system',
|
||||||
content: '你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题,不需要包含标点符号',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
role: 'user',
|
|
||||||
content: `${messages.map((message) => `${message.role}: ${message.content}`).join('\n')}
|
content: `${messages.map((message) => `${message.role}: ${message.content}`).join('\n')}
|
||||||
|
|
||||||
请总结上述对话为10个字以内的标题,不需要包含标点符号`,
|
请总结上述对话为10个字以内的标题,不需要包含标点符号`,
|
||||||
|
role: 'user',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
export const promptSummaryDescription = (messages: OpenAIChatMessage[]): Partial<OpenAIStreamPayload> => ({
|
export const promptSummaryDescription = (
|
||||||
|
messages: OpenAIChatMessage[],
|
||||||
|
): Partial<OpenAIStreamPayload> => ({
|
||||||
messages: [
|
messages: [
|
||||||
{ role: 'system', content: '你是一名擅长会话的助理,你需要将用户的会话做一个3句话以内的总结。' },
|
|
||||||
{
|
{
|
||||||
role: 'user',
|
content: '你是一名擅长会话的助理,你需要将用户的会话做一个3句话以内的总结。',
|
||||||
|
role: 'system',
|
||||||
|
},
|
||||||
|
{
|
||||||
content: `${messages.map((message) => `${message.role}: ${message.content}`).join('\n')}
|
content: `${messages.map((message) => `${message.role}: ${message.content}`).join('\n')}
|
||||||
|
|
||||||
请总结上述对话内容,不超过 50 个字。`,
|
请总结上述对话内容,不超过 50 个字。`,
|
||||||
|
role: 'user',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import type { OpenAIStreamPayload } from '@/pages/api/OpenAIStream';
|
|
||||||
import { merge } from 'lodash-es';
|
import { merge } from 'lodash-es';
|
||||||
|
|
||||||
|
import type { OpenAIStreamPayload } from '@/pages/api/OpenAIStream';
|
||||||
|
|
||||||
import { URLS } from './url';
|
import { URLS } from './url';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -13,18 +14,18 @@ export const fetchChatModel = (
|
||||||
const payload = merge(
|
const payload = merge(
|
||||||
{
|
{
|
||||||
model: 'gpt-3.5-turbo',
|
model: 'gpt-3.5-turbo',
|
||||||
temperature: 0.6,
|
|
||||||
stream: true,
|
stream: true,
|
||||||
|
temperature: 0.6,
|
||||||
},
|
},
|
||||||
params,
|
params,
|
||||||
);
|
);
|
||||||
|
|
||||||
return fetch(URLS.openai, {
|
return fetch(URLS.openai, {
|
||||||
method: 'POST',
|
body: JSON.stringify(payload),
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify(payload),
|
method: 'POST',
|
||||||
signal,
|
signal,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -8,11 +8,11 @@ import { fetchAIFactory } from '@/utils/fetch';
|
||||||
export const fetchLangChain = fetchAIFactory(
|
export const fetchLangChain = fetchAIFactory(
|
||||||
(params: LangChainParams, signal?: AbortSignal | undefined) =>
|
(params: LangChainParams, signal?: AbortSignal | undefined) =>
|
||||||
fetch(URLS.chain, {
|
fetch(URLS.chain, {
|
||||||
method: 'POST',
|
body: JSON.stringify(params),
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify(params),
|
method: 'POST',
|
||||||
signal,
|
signal,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,6 @@ const isDev = process.env.NODE_ENV === 'development';
|
||||||
const prefix = isDev ? '-dev' : '';
|
const prefix = isDev ? '-dev' : '';
|
||||||
|
|
||||||
export const URLS = {
|
export const URLS = {
|
||||||
openai: '/api/openai' + prefix,
|
|
||||||
chain: '/api/chain' + prefix,
|
chain: '/api/chain' + prefix,
|
||||||
|
openai: '/api/openai' + prefix,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { SessionStore } from '@/store/session';
|
import { SessionStore } from '@/store/session';
|
||||||
|
|
||||||
import { LanguageModel } from '@/types/llm';
|
import { LanguageModel } from '@/types/llm';
|
||||||
|
|
||||||
import { sessionSelectors } from '../session';
|
import { sessionSelectors } from '../session';
|
||||||
|
|
||||||
const currentAgentTitle = (s: SessionStore) => {
|
const currentAgentTitle = (s: SessionStore) => {
|
||||||
|
|
@ -31,8 +31,8 @@ const currentAgentModel = (s: SessionStore): LanguageModel => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const agentSelectors = {
|
export const agentSelectors = {
|
||||||
currentAgentConfig,
|
|
||||||
currentAgentAvatar,
|
currentAgentAvatar,
|
||||||
|
currentAgentConfig,
|
||||||
currentAgentModel,
|
currentAgentModel,
|
||||||
currentAgentTitle,
|
currentAgentTitle,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ const LOADING_FLAT = '...';
|
||||||
|
|
||||||
export interface ChatAction {
|
export interface ChatAction {
|
||||||
clearMessage: () => void;
|
clearMessage: () => void;
|
||||||
|
deleteMessage: (id: string) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title 派发消息
|
* @title 派发消息
|
||||||
* @param payload - 消息分发
|
* @param payload - 消息分发
|
||||||
|
|
@ -20,36 +22,40 @@ export interface ChatAction {
|
||||||
dispatchMessage: (payload: MessageDispatch) => void;
|
dispatchMessage: (payload: MessageDispatch) => void;
|
||||||
|
|
||||||
generateMessage: (messages: ChatMessage[], options: FetchSSEOptions) => Promise<void>;
|
generateMessage: (messages: ChatMessage[], options: FetchSSEOptions) => Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title 处理消息编辑
|
* @title 处理消息编辑
|
||||||
* @param index - 消息索引或空
|
* @param index - 消息索引或空
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
handleMessageEditing: (messageId: string | undefined) => void;
|
handleMessageEditing: (messageId: string | undefined) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title 重发消息
|
* @title 重发消息
|
||||||
* @param index - 消息索引
|
* @param index - 消息索引
|
||||||
* @returns Promise<void>
|
* @returns Promise<void>
|
||||||
*/
|
*/
|
||||||
resendMessage: (id: string) => Promise<void>;
|
resendMessage: (id: string) => Promise<void>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title 发送消息
|
* @title 发送消息
|
||||||
* @returns Promise<void>
|
* @returns Promise<void>
|
||||||
*/
|
*/
|
||||||
sendMessage: (text: string) => Promise<void>;
|
sendMessage: (text: string) => Promise<void>;
|
||||||
deleteMessage: (id: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createChatSlice: StateCreator<SessionStore, [['zustand/devtools', never]], [], ChatAction> = (
|
export const createChatSlice: StateCreator<
|
||||||
set,
|
SessionStore,
|
||||||
get,
|
[['zustand/devtools', never]],
|
||||||
) => ({
|
[],
|
||||||
|
ChatAction
|
||||||
|
> = (set, get) => ({
|
||||||
clearMessage: () => {
|
clearMessage: () => {
|
||||||
get().dispatchMessage({ type: 'resetMessages' });
|
get().dispatchMessage({ type: 'resetMessages' });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
deleteMessage: (id) => {
|
||||||
|
get().dispatchMessage({ id, type: 'deleteMessage' });
|
||||||
|
},
|
||||||
|
|
||||||
dispatchMessage: (payload) => {
|
dispatchMessage: (payload) => {
|
||||||
const { activeId } = get();
|
const { activeId } = get();
|
||||||
const session = sessionSelectors.currentSession(get());
|
const session = sessionSelectors.currentSession(get());
|
||||||
|
|
@ -183,8 +189,4 @@ export const createChatSlice: StateCreator<SessionStore, [['zustand/devtools', n
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
deleteMessage: (id) => {
|
|
||||||
get().dispatchMessage({ type: 'deleteMessage', id });
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
export interface ChatState {
|
export interface ChatState {
|
||||||
editingMessageId?: string;
|
|
||||||
chatLoading: boolean;
|
chatLoading: boolean;
|
||||||
|
editingMessageId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initialChatState: ChatState = {
|
export const initialChatState: ChatState = {
|
||||||
|
|
|
||||||
|
|
@ -6,18 +6,18 @@ import { MetaData } from '@/types/meta';
|
||||||
import { nanoid } from '@/utils/uuid';
|
import { nanoid } from '@/utils/uuid';
|
||||||
|
|
||||||
interface AddMessage {
|
interface AddMessage {
|
||||||
type: 'addMessage';
|
|
||||||
message: string;
|
|
||||||
role: LLMRoleType;
|
|
||||||
id?: string;
|
id?: string;
|
||||||
quotaId?: string;
|
message: string;
|
||||||
parentId?: string;
|
|
||||||
meta?: MetaData;
|
meta?: MetaData;
|
||||||
|
parentId?: string;
|
||||||
|
quotaId?: string;
|
||||||
|
role: LLMRoleType;
|
||||||
|
type: 'addMessage';
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DeleteMessage {
|
interface DeleteMessage {
|
||||||
type: 'deleteMessage';
|
|
||||||
id: string;
|
id: string;
|
||||||
|
type: 'deleteMessage';
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ResetMessages {
|
interface ResetMessages {
|
||||||
|
|
@ -25,38 +25,43 @@ interface ResetMessages {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateMessage {
|
interface UpdateMessage {
|
||||||
type: 'updateMessage';
|
|
||||||
id: string;
|
id: string;
|
||||||
key: keyof ChatMessage;
|
key: keyof ChatMessage;
|
||||||
|
type: 'updateMessage';
|
||||||
value: ChatMessage[keyof ChatMessage];
|
value: ChatMessage[keyof ChatMessage];
|
||||||
}
|
}
|
||||||
|
|
||||||
export type MessageDispatch = AddMessage | DeleteMessage | ResetMessages | UpdateMessage;
|
export type MessageDispatch = AddMessage | DeleteMessage | ResetMessages | UpdateMessage;
|
||||||
|
|
||||||
export const messagesReducer = (state: ChatMessageMap, payload: MessageDispatch): ChatMessageMap => {
|
export const messagesReducer = (
|
||||||
|
state: ChatMessageMap,
|
||||||
|
payload: MessageDispatch,
|
||||||
|
): ChatMessageMap => {
|
||||||
switch (payload.type) {
|
switch (payload.type) {
|
||||||
case 'addMessage':
|
case 'addMessage': {
|
||||||
return produce(state, (draftState) => {
|
return produce(state, (draftState) => {
|
||||||
const mid = payload.id || nanoid();
|
const mid = payload.id || nanoid();
|
||||||
|
|
||||||
draftState[mid] = {
|
draftState[mid] = {
|
||||||
id: mid,
|
|
||||||
role: payload.role,
|
|
||||||
content: payload.message,
|
content: payload.message,
|
||||||
meta: payload.meta || {},
|
|
||||||
quotaId: payload.quotaId,
|
|
||||||
parentId: payload.parentId,
|
|
||||||
updateAt: Date.now(),
|
|
||||||
createAt: Date.now(),
|
createAt: Date.now(),
|
||||||
|
id: mid,
|
||||||
|
meta: payload.meta || {},
|
||||||
|
parentId: payload.parentId,
|
||||||
|
quotaId: payload.quotaId,
|
||||||
|
role: payload.role,
|
||||||
|
updateAt: Date.now(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case 'deleteMessage':
|
case 'deleteMessage': {
|
||||||
return produce(state, (draftState) => {
|
return produce(state, (draftState) => {
|
||||||
delete draftState[payload.id];
|
delete draftState[payload.id];
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case 'updateMessage':
|
case 'updateMessage': {
|
||||||
return produce(state, (draftState) => {
|
return produce(state, (draftState) => {
|
||||||
const { id, key, value } = payload;
|
const { id, key, value } = payload;
|
||||||
const message = draftState[id];
|
const message = draftState[id];
|
||||||
|
|
@ -66,11 +71,14 @@ export const messagesReducer = (state: ChatMessageMap, payload: MessageDispatch)
|
||||||
message[key] = value;
|
message[key] = value;
|
||||||
message.updateAt = Date.now();
|
message.updateAt = Date.now();
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case 'resetMessages':
|
case 'resetMessages': {
|
||||||
return {};
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default: {
|
||||||
throw new Error('暂未实现的 type,请检查 reducer');
|
throw new Error('暂未实现的 type,请检查 reducer');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import { ChatMessage } from '@/types/chatMessage';
|
|
||||||
import { encode } from 'gpt-tokenizer';
|
import { encode } from 'gpt-tokenizer';
|
||||||
|
|
||||||
|
import { ChatMessage } from '@/types/chatMessage';
|
||||||
|
|
||||||
import type { SessionStore } from '../../store';
|
import type { SessionStore } from '../../store';
|
||||||
import { sessionSelectors } from '../session';
|
import { sessionSelectors } from '../session';
|
||||||
|
|
||||||
|
|
@ -40,8 +41,8 @@ const systemRoleTokenCount = (s: SessionStore) => systemRoleTokens(s).length;
|
||||||
export const chatSelectors = {
|
export const chatSelectors = {
|
||||||
currentChats: currentChatsSel,
|
currentChats: currentChatsSel,
|
||||||
systemRole: systemRoleSel,
|
systemRole: systemRoleSel,
|
||||||
totalTokens,
|
|
||||||
totalTokenCount,
|
|
||||||
systemRoleTokens,
|
|
||||||
systemRoleTokenCount,
|
systemRoleTokenCount,
|
||||||
|
systemRoleTokens,
|
||||||
|
totalTokenCount,
|
||||||
|
totalTokens,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,12 @@ export interface SessionAction {
|
||||||
* @returns void
|
* @returns void
|
||||||
*/
|
*/
|
||||||
createSession: () => Promise<void>;
|
createSession: () => Promise<void>;
|
||||||
|
/**
|
||||||
|
* 分发聊天记录
|
||||||
|
* @param payload - 聊天记录
|
||||||
|
*/
|
||||||
|
dispatchSession: (payload: SessionDispatch) => void;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title 删除会话
|
* @title 删除会话
|
||||||
* @param index - 会话索引
|
* @param index - 会话索引
|
||||||
|
|
@ -29,12 +35,6 @@ export interface SessionAction {
|
||||||
*/
|
*/
|
||||||
switchSession: (sessionId?: string | 'new') => void;
|
switchSession: (sessionId?: string | 'new') => void;
|
||||||
|
|
||||||
/**
|
|
||||||
* 分发聊天记录
|
|
||||||
* @param payload - 聊天记录
|
|
||||||
*/
|
|
||||||
dispatchSession: (payload: SessionDispatch) => void;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成压缩后的消息
|
* 生成压缩后的消息
|
||||||
* @returns 压缩后的消息
|
* @returns 压缩后的消息
|
||||||
|
|
@ -42,48 +42,50 @@ export interface SessionAction {
|
||||||
// genShareUrl: () => string;
|
// genShareUrl: () => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createSessionSlice: StateCreator<SessionStore, [['zustand/devtools', never]], [], SessionAction> = (
|
export const createSessionSlice: StateCreator<
|
||||||
set,
|
SessionStore,
|
||||||
get,
|
[['zustand/devtools', never]],
|
||||||
) => ({
|
[],
|
||||||
dispatchSession: (payload) => {
|
SessionAction
|
||||||
const { type, ...res } = payload;
|
> = (set, get) => ({
|
||||||
set({ sessions: sessionsReducer(get().sessions, payload) }, false, {
|
|
||||||
type: `dispatchChat/${type}`,
|
|
||||||
payload: res,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
createSession: async () => {
|
createSession: async () => {
|
||||||
const { dispatchSession, switchSession } = get();
|
const { dispatchSession, switchSession } = get();
|
||||||
|
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
|
|
||||||
const newSession: LobeAgentSession = {
|
const newSession: LobeAgentSession = {
|
||||||
id: uuid(),
|
|
||||||
createAt: timestamp,
|
|
||||||
updateAt: timestamp,
|
|
||||||
type: LobeSessionType.Agent,
|
|
||||||
chats: {},
|
chats: {},
|
||||||
meta: {
|
|
||||||
title: '默认对话',
|
|
||||||
},
|
|
||||||
config: {
|
config: {
|
||||||
model: LanguageModel.GPT3_5,
|
model: LanguageModel.GPT3_5,
|
||||||
systemRole: '',
|
|
||||||
params: {
|
params: {
|
||||||
temperature: 0.6,
|
temperature: 0.6,
|
||||||
},
|
},
|
||||||
|
systemRole: '',
|
||||||
},
|
},
|
||||||
|
createAt: timestamp,
|
||||||
|
id: uuid(),
|
||||||
|
meta: {
|
||||||
|
title: '默认对话',
|
||||||
|
},
|
||||||
|
type: LobeSessionType.Agent,
|
||||||
|
updateAt: timestamp,
|
||||||
};
|
};
|
||||||
|
|
||||||
dispatchSession({ type: 'addSession', session: newSession });
|
dispatchSession({ session: newSession, type: 'addSession' });
|
||||||
|
|
||||||
switchSession(newSession.id);
|
switchSession(newSession.id);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
dispatchSession: (payload) => {
|
||||||
|
const { type, ...res } = payload;
|
||||||
|
set({ sessions: sessionsReducer(get().sessions, payload) }, false, {
|
||||||
|
payload: res,
|
||||||
|
type: `dispatchChat/${type}`,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
removeSession: (sessionId) => {
|
removeSession: (sessionId) => {
|
||||||
get().dispatchSession({ type: 'removeSession', id: sessionId });
|
get().dispatchSession({ id: sessionId, type: 'removeSession' });
|
||||||
Router.push('/');
|
Router.push('/');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,53 @@
|
||||||
|
import { produce } from 'immer';
|
||||||
|
|
||||||
import { ChatMessageMap } from '@/types/chatMessage';
|
import { ChatMessageMap } from '@/types/chatMessage';
|
||||||
import { MetaData } from '@/types/meta';
|
import { MetaData } from '@/types/meta';
|
||||||
import { LobeAgentConfig, LobeAgentSession, LobeSessions } from '@/types/session';
|
import { LobeAgentConfig, LobeAgentSession, LobeSessions } from '@/types/session';
|
||||||
import { produce } from 'immer';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title 添加会话
|
* @title 添加会话
|
||||||
*/
|
*/
|
||||||
interface AddSession {
|
interface AddSession {
|
||||||
|
/**
|
||||||
|
* @param session - 会话信息
|
||||||
|
*/
|
||||||
|
session: LobeAgentSession;
|
||||||
/**
|
/**
|
||||||
* @param type - 操作类型
|
* @param type - 操作类型
|
||||||
* @default 'addChat'
|
* @default 'addChat'
|
||||||
*/
|
*/
|
||||||
type: 'addSession';
|
type: 'addSession';
|
||||||
/**
|
|
||||||
* @param session - 会话信息
|
|
||||||
*/
|
|
||||||
session: LobeAgentSession;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RemoveSession {
|
interface RemoveSession {
|
||||||
type: 'removeSession';
|
|
||||||
id: string;
|
id: string;
|
||||||
|
type: 'removeSession';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title 更新会话聊天上下文
|
* @title 更新会话聊天上下文
|
||||||
*/
|
*/
|
||||||
interface UpdateSessionChat {
|
interface UpdateSessionChat {
|
||||||
type: 'updateSessionChat';
|
chats: ChatMessageMap;
|
||||||
/**
|
/**
|
||||||
* 会话 ID
|
* 会话 ID
|
||||||
*/
|
*/
|
||||||
id: string;
|
id: string;
|
||||||
|
|
||||||
chats: ChatMessageMap;
|
type: 'updateSessionChat';
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateSessionMeta {
|
interface UpdateSessionMeta {
|
||||||
type: 'updateSessionMeta';
|
|
||||||
id: string;
|
id: string;
|
||||||
key: keyof MetaData;
|
key: keyof MetaData;
|
||||||
|
type: 'updateSessionMeta';
|
||||||
value: any;
|
value: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UpdateSessionAgentConfig {
|
interface UpdateSessionAgentConfig {
|
||||||
type: 'updateSessionConfig';
|
|
||||||
id: string;
|
|
||||||
config: Partial<LobeAgentConfig>;
|
config: Partial<LobeAgentConfig>;
|
||||||
|
id: string;
|
||||||
|
type: 'updateSessionConfig';
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SessionDispatch =
|
export type SessionDispatch =
|
||||||
|
|
@ -58,17 +59,19 @@ export type SessionDispatch =
|
||||||
|
|
||||||
export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch): LobeSessions => {
|
export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch): LobeSessions => {
|
||||||
switch (payload.type) {
|
switch (payload.type) {
|
||||||
case 'addSession':
|
case 'addSession': {
|
||||||
return produce(state, (draft) => {
|
return produce(state, (draft) => {
|
||||||
draft[payload.session.id] = payload.session;
|
draft[payload.session.id] = payload.session;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case 'removeSession':
|
case 'removeSession': {
|
||||||
return produce(state, (draft) => {
|
return produce(state, (draft) => {
|
||||||
delete draft[payload.id];
|
delete draft[payload.id];
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case 'updateSessionMeta':
|
case 'updateSessionMeta': {
|
||||||
return produce(state, (draft) => {
|
return produce(state, (draft) => {
|
||||||
const chat = draft[payload.id];
|
const chat = draft[payload.id];
|
||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
|
|
@ -77,16 +80,18 @@ export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch):
|
||||||
|
|
||||||
chat.meta[key] = value;
|
chat.meta[key] = value;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case 'updateSessionChat':
|
case 'updateSessionChat': {
|
||||||
return produce(state, (draft) => {
|
return produce(state, (draft) => {
|
||||||
const chat = draft[payload.id];
|
const chat = draft[payload.id];
|
||||||
if (!chat) return;
|
if (!chat) return;
|
||||||
|
|
||||||
chat.chats = payload.chats;
|
chat.chats = payload.chats;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
case 'updateSessionConfig':
|
case 'updateSessionConfig': {
|
||||||
return produce(state, (draft) => {
|
return produce(state, (draft) => {
|
||||||
const { id, config } = payload;
|
const { id, config } = payload;
|
||||||
const chat = draft[id];
|
const chat = draft[id];
|
||||||
|
|
@ -94,8 +99,10 @@ export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch):
|
||||||
|
|
||||||
chat.config = { ...chat.config, ...config };
|
chat.config = { ...chat.config, ...config };
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default: {
|
||||||
return produce(state, () => {});
|
return produce(state, () => {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,20 @@
|
||||||
import { getAgentAvatar } from './chat';
|
import { getAgentAvatar } from './chat';
|
||||||
import { chatListSel, currentSessionSafe, currentSessionSel, getSessionById, getSessionMetaById } from './list';
|
import {
|
||||||
|
chatListSel,
|
||||||
|
currentSessionSafe,
|
||||||
|
currentSessionSel,
|
||||||
|
getSessionById,
|
||||||
|
getSessionMetaById,
|
||||||
|
} from './list';
|
||||||
|
|
||||||
export const sessionSelectors = {
|
export const sessionSelectors = {
|
||||||
|
chatList: chatListSel,
|
||||||
currentSession: currentSessionSel,
|
currentSession: currentSessionSel,
|
||||||
currentSessionSafe,
|
currentSessionSafe,
|
||||||
chatList: chatListSel,
|
|
||||||
|
getAgentAvatar,
|
||||||
|
|
||||||
|
getSessionById,
|
||||||
// sessionTree: sessionTreeSel,
|
// sessionTree: sessionTreeSel,
|
||||||
getSessionMetaById,
|
getSessionMetaById,
|
||||||
getSessionById,
|
|
||||||
getAgentAvatar,
|
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { SessionStore } from '@/store/session';
|
import { SessionStore } from '@/store/session';
|
||||||
import { LobeAgentSession } from '@/types/session';
|
|
||||||
|
|
||||||
import { MetaData } from '@/types/meta';
|
import { MetaData } from '@/types/meta';
|
||||||
|
import { LobeAgentSession } from '@/types/session';
|
||||||
import { filterWithKeywords } from '@/utils/filter';
|
import { filterWithKeywords } from '@/utils/filter';
|
||||||
|
|
||||||
import { initLobeSession } from '../initialState';
|
import { initLobeSession } from '../initialState';
|
||||||
|
|
|
||||||
|
|
@ -7,26 +7,26 @@ import { ConfigSettings } from '@/types/exportConfig';
|
||||||
export type SidebarTabKey = 'chat' | 'market';
|
export type SidebarTabKey = 'chat' | 'market';
|
||||||
|
|
||||||
interface SettingsStore {
|
interface SettingsStore {
|
||||||
sessionsWidth: number;
|
|
||||||
inputHeight: number;
|
|
||||||
avatar?: string;
|
avatar?: string;
|
||||||
sessionExpandable?: boolean;
|
|
||||||
sidebarKey: SidebarTabKey;
|
|
||||||
themeMode?: ThemeMode;
|
|
||||||
importSettings: (settings: ConfigSettings) => void;
|
importSettings: (settings: ConfigSettings) => void;
|
||||||
|
inputHeight: number;
|
||||||
|
sessionExpandable?: boolean;
|
||||||
|
sessionsWidth: number;
|
||||||
|
sidebarKey: SidebarTabKey;
|
||||||
switchSideBar: (key: SidebarTabKey) => void;
|
switchSideBar: (key: SidebarTabKey) => void;
|
||||||
|
themeMode?: ThemeMode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useSettings = create<SettingsStore>()(
|
export const useSettings = create<SettingsStore>()(
|
||||||
persist<SettingsStore>(
|
persist<SettingsStore>(
|
||||||
(set) => ({
|
(set) => ({
|
||||||
sessionsWidth: 320,
|
|
||||||
inputHeight: 200,
|
|
||||||
sessionExpandable: true,
|
|
||||||
sidebarKey: 'chat',
|
|
||||||
importSettings: (settings) => {
|
importSettings: (settings) => {
|
||||||
set({ ...settings });
|
set({ ...settings });
|
||||||
},
|
},
|
||||||
|
inputHeight: 200,
|
||||||
|
sessionExpandable: true,
|
||||||
|
sessionsWidth: 320,
|
||||||
|
sidebarKey: 'chat',
|
||||||
switchSideBar: (key) => {
|
switchSideBar: (key) => {
|
||||||
set({ sidebarKey: key });
|
set({ sidebarKey: key });
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,7 @@ export interface ChatMessageError {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ChatMessage extends BaseDataModel {
|
export interface ChatMessage extends BaseDataModel {
|
||||||
/**
|
archive?: boolean;
|
||||||
* 角色
|
|
||||||
* @description 消息发送者的角色
|
|
||||||
*/
|
|
||||||
role: LLMRoleType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @title 内容
|
* @title 内容
|
||||||
|
|
@ -27,20 +23,24 @@ export interface ChatMessage extends BaseDataModel {
|
||||||
content: string;
|
content: string;
|
||||||
error?: any;
|
error?: any;
|
||||||
|
|
||||||
archive?: boolean;
|
|
||||||
|
|
||||||
parentId?: string;
|
|
||||||
// 引用
|
|
||||||
quotaId?: string;
|
|
||||||
// 扩展字段
|
// 扩展字段
|
||||||
extra?: {
|
extra?: {
|
||||||
// 翻译
|
// 翻译
|
||||||
translate: {
|
translate: {
|
||||||
to: string;
|
|
||||||
target: string;
|
target: string;
|
||||||
|
to: string;
|
||||||
};
|
};
|
||||||
// 语音
|
// 语音
|
||||||
} & Record<string, any>;
|
} & Record<string, any>;
|
||||||
|
|
||||||
|
parentId?: string;
|
||||||
|
// 引用
|
||||||
|
quotaId?: string;
|
||||||
|
/**
|
||||||
|
* 角色
|
||||||
|
* @description 消息发送者的角色
|
||||||
|
*/
|
||||||
|
role: LLMRoleType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ChatMessageMap = Record<string, ChatMessage>;
|
export type ChatMessageMap = Record<string, ChatMessage>;
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,19 @@ import { ChatMessage } from '@lobehub/ui';
|
||||||
|
|
||||||
export interface LangChainParams {
|
export interface LangChainParams {
|
||||||
llm: {
|
llm: {
|
||||||
|
/**
|
||||||
|
* 控制生成文本中的惩罚系数,用于减少重复性
|
||||||
|
*/
|
||||||
|
frequency_penalty?: number;
|
||||||
|
/**
|
||||||
|
* 生成文本的最大长度
|
||||||
|
*/
|
||||||
|
max_tokens?: number;
|
||||||
model: string;
|
model: string;
|
||||||
|
/**
|
||||||
|
* 控制生成文本中的惩罚系数,用于减少主题的变化
|
||||||
|
*/
|
||||||
|
presence_penalty?: number;
|
||||||
/**
|
/**
|
||||||
* 生成文本的随机度量,用于控制文本的创造性和多样性
|
* 生成文本的随机度量,用于控制文本的创造性和多样性
|
||||||
* @default 0.6
|
* @default 0.6
|
||||||
|
|
@ -12,18 +24,6 @@ export interface LangChainParams {
|
||||||
* 控制生成文本中最高概率的单个令牌
|
* 控制生成文本中最高概率的单个令牌
|
||||||
*/
|
*/
|
||||||
top_p?: number;
|
top_p?: number;
|
||||||
/**
|
|
||||||
* 控制生成文本中的惩罚系数,用于减少重复性
|
|
||||||
*/
|
|
||||||
frequency_penalty?: number;
|
|
||||||
/**
|
|
||||||
* 控制生成文本中的惩罚系数,用于减少主题的变化
|
|
||||||
*/
|
|
||||||
presence_penalty?: number;
|
|
||||||
/**
|
|
||||||
* 生成文本的最大长度
|
|
||||||
*/
|
|
||||||
max_tokens?: number;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,4 @@
|
||||||
export interface MetaData {
|
export interface MetaData {
|
||||||
/**
|
|
||||||
* 名称
|
|
||||||
* @description 可选参数,如果不传则使用默认名称
|
|
||||||
*/
|
|
||||||
title?: string;
|
|
||||||
description?: string;
|
|
||||||
tag?: string[];
|
|
||||||
/**
|
/**
|
||||||
* 角色头像
|
* 角色头像
|
||||||
* @description 可选参数,如果不传则使用默认头像
|
* @description 可选参数,如果不传则使用默认头像
|
||||||
|
|
@ -16,11 +9,18 @@ export interface MetaData {
|
||||||
* @description 可选参数,如果不传则使用默认背景色
|
* @description 可选参数,如果不传则使用默认背景色
|
||||||
*/
|
*/
|
||||||
backgroundColor?: string;
|
backgroundColor?: string;
|
||||||
|
description?: string;
|
||||||
|
tag?: string[];
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
* @description 可选参数,如果不传则使用默认名称
|
||||||
|
*/
|
||||||
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BaseDataModel {
|
export interface BaseDataModel {
|
||||||
|
createAt: number;
|
||||||
id: string;
|
id: string;
|
||||||
meta: MetaData;
|
meta: MetaData;
|
||||||
updateAt: number;
|
updateAt: number;
|
||||||
createAt: number;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,22 @@ export enum LobeSessionType {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LobeSessionBase extends BaseDataModel {
|
interface LobeSessionBase extends BaseDataModel {
|
||||||
/**
|
|
||||||
* 每个会话的类别
|
|
||||||
*/
|
|
||||||
type: LobeSessionType;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 聊天记录
|
* 聊天记录
|
||||||
*/
|
*/
|
||||||
chats: ChatMessageMap;
|
chats: ChatMessageMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 每个会话的类别
|
||||||
|
*/
|
||||||
|
type: LobeSessionType;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LobeAgentConfig {
|
export interface LobeAgentConfig {
|
||||||
|
/**
|
||||||
|
* 语言模型示例
|
||||||
|
*/
|
||||||
|
example?: LLMExample;
|
||||||
/**
|
/**
|
||||||
* 角色所使用的语言模型
|
* 角色所使用的语言模型
|
||||||
* @default gpt-3.5-turbo
|
* @default gpt-3.5-turbo
|
||||||
|
|
@ -39,21 +43,17 @@ export interface LobeAgentConfig {
|
||||||
* 系统角色
|
* 系统角色
|
||||||
*/
|
*/
|
||||||
systemRole: string;
|
systemRole: string;
|
||||||
/**
|
|
||||||
* 语言模型示例
|
|
||||||
*/
|
|
||||||
example?: LLMExample;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lobe Agent会话
|
* Lobe Agent会话
|
||||||
*/
|
*/
|
||||||
export interface LobeAgentSession extends LobeSessionBase {
|
export interface LobeAgentSession extends LobeSessionBase {
|
||||||
type: LobeSessionType.Agent;
|
|
||||||
/**
|
/**
|
||||||
* 语言模型角色设定
|
* 语言模型角色设定
|
||||||
*/
|
*/
|
||||||
config: LobeAgentConfig;
|
config: LobeAgentConfig;
|
||||||
|
type: LobeSessionType.Agent;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type LobeSessions = Record<string, LobeAgentSession>;
|
export type LobeSessions = Record<string, LobeAgentSession>;
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,16 @@
|
||||||
* @template T - 状态类型
|
* @template T - 状态类型
|
||||||
*/
|
*/
|
||||||
export interface Migration<T = any> {
|
export interface Migration<T = any> {
|
||||||
/**
|
|
||||||
* 迁移版本号
|
|
||||||
*/
|
|
||||||
version: number;
|
|
||||||
/**
|
/**
|
||||||
* 迁移数据
|
* 迁移数据
|
||||||
* @param data - 迁移数据
|
* @param data - 迁移数据
|
||||||
* @returns 迁移后的数据
|
* @returns 迁移后的数据
|
||||||
*/
|
*/
|
||||||
migrate(data: MigrationData<T>): MigrationData;
|
migrate(data: MigrationData<T>): MigrationData;
|
||||||
|
/**
|
||||||
|
* 迁移版本号
|
||||||
|
*/
|
||||||
|
version: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,8 @@ export class StrCompressor {
|
||||||
* @ignore
|
* @ignore
|
||||||
*/
|
*/
|
||||||
private instance!: {
|
private instance!: {
|
||||||
decompress(buf: Uint8Array): Uint8Array;
|
|
||||||
compress(buf: Uint8Array, options?: any): Uint8Array;
|
compress(buf: Uint8Array, options?: any): Uint8Array;
|
||||||
|
decompress(buf: Uint8Array): Uint8Array;
|
||||||
};
|
};
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
|
|
|
||||||
|
|
@ -21,8 +21,8 @@ const codeMessage: Record<number, string> = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface FetchSSEOptions {
|
export interface FetchSSEOptions {
|
||||||
onMessageHandle?: (text: string) => void;
|
|
||||||
onErrorHandle?: (error: ChatMessageError) => void;
|
onErrorHandle?: (error: ChatMessageError) => void;
|
||||||
|
onMessageHandle?: (text: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -68,15 +68,7 @@ export const fetchSSE = async (fetchFn: () => Promise<Response>, options: FetchS
|
||||||
};
|
};
|
||||||
|
|
||||||
interface FetchAITaskResultParams<T> {
|
interface FetchAITaskResultParams<T> {
|
||||||
/**
|
abortController?: AbortController;
|
||||||
* 请求对象
|
|
||||||
*/
|
|
||||||
params: T;
|
|
||||||
/**
|
|
||||||
* 消息处理函数
|
|
||||||
* @param text - 消息内容
|
|
||||||
*/
|
|
||||||
onMessageHandle?: (text: string) => void;
|
|
||||||
/**
|
/**
|
||||||
* 错误处理函数
|
* 错误处理函数
|
||||||
*/
|
*/
|
||||||
|
|
@ -86,13 +78,27 @@ interface FetchAITaskResultParams<T> {
|
||||||
* @param loading - 是否处于加载状态
|
* @param loading - 是否处于加载状态
|
||||||
*/
|
*/
|
||||||
onLoadingChange?: (loading: boolean) => void;
|
onLoadingChange?: (loading: boolean) => void;
|
||||||
|
/**
|
||||||
|
* 消息处理函数
|
||||||
|
* @param text - 消息内容
|
||||||
|
*/
|
||||||
|
onMessageHandle?: (text: string) => void;
|
||||||
|
|
||||||
abortController?: AbortController;
|
/**
|
||||||
|
* 请求对象
|
||||||
|
*/
|
||||||
|
params: T;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const fetchAIFactory =
|
export const fetchAIFactory =
|
||||||
<T>(fetcher: (params: T, signal?: AbortSignal) => Promise<Response>) =>
|
<T>(fetcher: (params: T, signal?: AbortSignal) => Promise<Response>) =>
|
||||||
async ({ params, onMessageHandle, onError, onLoadingChange, abortController }: FetchAITaskResultParams<T>) => {
|
async ({
|
||||||
|
params,
|
||||||
|
onMessageHandle,
|
||||||
|
onError,
|
||||||
|
onLoadingChange,
|
||||||
|
abortController,
|
||||||
|
}: FetchAITaskResultParams<T>) => {
|
||||||
const errorHandle = (error: Error) => {
|
const errorHandle = (error: Error) => {
|
||||||
onLoadingChange?.(false);
|
onLoadingChange?.(false);
|
||||||
if (abortController?.signal.aborted) {
|
if (abortController?.signal.aborted) {
|
||||||
|
|
@ -112,10 +118,10 @@ export const fetchAIFactory =
|
||||||
onLoadingChange?.(true);
|
onLoadingChange?.(true);
|
||||||
|
|
||||||
const data = await fetchSSE(() => fetcher(params, abortController?.signal), {
|
const data = await fetchSSE(() => fetcher(params, abortController?.signal), {
|
||||||
onMessageHandle,
|
|
||||||
onErrorHandle: (error) => {
|
onErrorHandle: (error) => {
|
||||||
errorHandle(new Error(error.message));
|
errorHandle(new Error(error.message));
|
||||||
},
|
},
|
||||||
|
onMessageHandle,
|
||||||
}).catch(errorHandle);
|
}).catch(errorHandle);
|
||||||
|
|
||||||
onLoadingChange?.(false);
|
onLoadingChange?.(false);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,4 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// generate('1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 16); //=> "4f90d13a42"
|
// generate('1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 16); //=> "4f90d13a42"
|
||||||
|
|
||||||
import { customAlphabet } from 'nanoid/non-secure';
|
import { customAlphabet } from 'nanoid/non-secure';
|
||||||
|
|
||||||
export const nanoid = customAlphabet(
|
export const nanoid = customAlphabet(
|
||||||
|
|
@ -10,4 +6,4 @@ export const nanoid = customAlphabet(
|
||||||
8,
|
8,
|
||||||
);
|
);
|
||||||
|
|
||||||
export {v4 as uuid} from 'uuid';
|
export { v4 as uuid } from 'uuid';
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,6 @@
|
||||||
"@/*": ["src/*"]
|
"@/*": ["src/*"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
"exclude": ["node_modules"],
|
||||||
"exclude": ["node_modules"]
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue