diff --git a/.changelogrc.js b/.changelogrc.js
new file mode 100644
index 000000000000..9a2f5f98d8cd
--- /dev/null
+++ b/.changelogrc.js
@@ -0,0 +1 @@
+module.exports = require('@lobehub/lint').changelog;
diff --git a/.commitlintrc.js b/.commitlintrc.js
new file mode 100644
index 000000000000..9b8c6ace5a00
--- /dev/null
+++ b/.commitlintrc.js
@@ -0,0 +1 @@
+module.exports = require('@lobehub/lint').commitlint;
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 000000000000..7e3649acc2c1
--- /dev/null
+++ b/.editorconfig
@@ -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
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 000000000000..b24520330465
--- /dev/null
+++ b/.eslintignore
@@ -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
\ No newline at end of file
diff --git a/.eslintrc.js b/.eslintrc.js
index b2d8f1a1ff46..920af1d9c092 100644
--- a/.eslintrc.js
+++ b/.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/core-web-vitals');
-module.exports = {
- ...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',
- },
-};
+module.exports = config;
diff --git a/.github/ISSUE_TEMPLATE/1_bug_report.yml b/.github/ISSUE_TEMPLATE/1_bug_report.yml
index c8aa08e70d1e..d181c3879815 100644
--- a/.github/ISSUE_TEMPLATE/1_bug_report.yml
+++ b/.github/ISSUE_TEMPLATE/1_bug_report.yml
@@ -1,11 +1,11 @@
-name: "🐛 反馈缺陷 Bug Report"
-description: "反馈一个问题缺陷 | Report an bug"
-title: "[Bug] "
-labels: "🐛 Bug"
+name: '🐛 反馈缺陷 Bug Report'
+description: '反馈一个问题缺陷 | Report an bug'
+title: '[Bug] '
+labels: '🐛 Bug'
body:
- type: dropdown
attributes:
- label: "💻 系统环境 | Operating System"
+ label: '💻 系统环境 | Operating System'
options:
- Windows
- macOS
@@ -16,7 +16,7 @@ body:
required: true
- type: dropdown
attributes:
- label: "🌐 浏览器 | Browser"
+ label: '🌐 浏览器 | Browser'
options:
- Chrome
- Edge
@@ -27,19 +27,19 @@ body:
required: true
- type: textarea
attributes:
- label: "🐛 问题描述 | Bug Description"
+ label: '🐛 问题描述 | Bug Description'
description: A clear and concise description of the bug.
validations:
required: true
- type: textarea
attributes:
- label: "🚦 期望结果 | Expected Behavior"
+ label: '🚦 期望结果 | Expected Behavior'
description: A clear and concise description of what you expected to happen.
- type: textarea
attributes:
- label: "📷 复现步骤 | Recurrence Steps"
+ label: '📷 复现步骤 | Recurrence Steps'
description: A clear and concise description of how to recurrence.
- type: textarea
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.
diff --git a/.github/ISSUE_TEMPLATE/2_feature_request.yml b/.github/ISSUE_TEMPLATE/2_feature_request.yml
index b1a9d1e219d3..edcf7d0643cc 100644
--- a/.github/ISSUE_TEMPLATE/2_feature_request.yml
+++ b/.github/ISSUE_TEMPLATE/2_feature_request.yml
@@ -1,21 +1,21 @@
-name: "🌠 功能需求 Feature Request"
-description: "需求或建议 | Suggest an idea"
-title: "[Request] "
-labels: "🌠 Feature Request"
+name: '🌠 功能需求 Feature Request'
+description: '需求或建议 | Suggest an idea'
+title: '[Request] '
+labels: '🌠 Feature Request'
body:
- type: textarea
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.
validations:
required: true
- type: textarea
attributes:
- label: "🧐 解决方案 | Proposed Solution"
+ label: '🧐 解决方案 | Proposed Solution'
description: Describe the solution you'd like in a clear and concise manner.
validations:
required: true
- type: textarea
attributes:
- label: "📝 补充信息 | Additional Information"
+ label: '📝 补充信息 | Additional Information'
description: Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/3_question.yml b/.github/ISSUE_TEMPLATE/3_question.yml
index 687666d0f03f..f989f7d11be9 100644
--- a/.github/ISSUE_TEMPLATE/3_question.yml
+++ b/.github/ISSUE_TEMPLATE/3_question.yml
@@ -1,15 +1,15 @@
-name: "😇 疑问或帮助 Help Wanted"
-description: "疑问或需要帮助 | Need help"
-title: "[Question] "
-labels: "😇 Help Wanted"
+name: '😇 疑问或帮助 Help Wanted'
+description: '疑问或需要帮助 | Need help'
+title: '[Question] '
+labels: '😇 Help Wanted'
body:
- type: textarea
attributes:
- label: "🧐 问题描述 | Proposed Solution"
+ label: '🧐 问题描述 | Proposed Solution'
description: A clear and concise description of the proplem.
validations:
required: true
- type: textarea
attributes:
- label: "📝 补充信息 | Additional Information"
+ label: '📝 补充信息 | Additional Information'
description: Add any other context about the problem here.
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 5702943a41f7..1f8319428a3a 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,17 +1,17 @@
version: 2
updates:
- package-ecosystem: npm
- directory: "/"
+ directory: '/'
schedule:
interval: weekly
- time: "19:00"
+ time: '19:00'
timezone: 'Asia/Shanghai'
open-pull-requests-limit: 10
versioning-strategy: increase
- - package-ecosystem: "github-actions"
- directory: "/"
+ - package-ecosystem: 'github-actions'
+ directory: '/'
schedule:
interval: monthly
- time: "19:00"
+ time: '19:00'
timezone: 'Asia/Shanghai'
diff --git a/.github/workflows/contributor-help.yml b/.github/workflows/contributor-help.yml
index 993060660916..fd68fec3bd42 100644
--- a/.github/workflows/contributor-help.yml
+++ b/.github/workflows/contributor-help.yml
@@ -2,7 +2,7 @@ name: Contributor Helper
on:
# 🌏 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!
workflow_dispatch:
jobs:
diff --git a/.github/workflows/issue-check-inactive.yml b/.github/workflows/issue-check-inactive.yml
index 2621364ba55a..d37c4c307197 100644
--- a/.github/workflows/issue-check-inactive.yml
+++ b/.github/workflows/issue-check-inactive.yml
@@ -2,7 +2,7 @@ name: Issue Check Inactive
on:
schedule:
- - cron: "0 0 */15 * *"
+ - cron: '0 0 */15 * *'
permissions:
contents: read
@@ -10,8 +10,8 @@ permissions:
jobs:
issue-check-inactive:
permissions:
- issues: write # for actions-cool/issues-helper to update issues
- pull-requests: write # for actions-cool/issues-helper to update PRs
+ issues: write # for actions-cool/issues-helper to update issues
+ pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: check-inactive
@@ -19,4 +19,4 @@ jobs:
with:
actions: 'check-inactive'
inactive-label: 'Inactive'
- inactive-day: 30
\ No newline at end of file
+ inactive-day: 30
diff --git a/.github/workflows/issue-close-require.yml b/.github/workflows/issue-close-require.yml
index 1b9239790192..68d6b6cefe85 100644
--- a/.github/workflows/issue-close-require.yml
+++ b/.github/workflows/issue-close-require.yml
@@ -2,7 +2,7 @@ name: Issue Close Require
on:
schedule:
- - cron: "0 0 * * *"
+ - cron: '0 0 * * *'
permissions:
contents: read
@@ -10,8 +10,8 @@ permissions:
jobs:
issue-close-require:
permissions:
- issues: write # for actions-cool/issues-helper to update issues
- pull-requests: write # for actions-cool/issues-helper to update PRs
+ issues: write # for actions-cool/issues-helper to update issues
+ pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: need reproduce
diff --git a/.github/workflows/issue-remove-inactive.yml b/.github/workflows/issue-remove-inactive.yml
index 3b25e9d0eb52..dbe42ddb86d2 100644
--- a/.github/workflows/issue-remove-inactive.yml
+++ b/.github/workflows/issue-remove-inactive.yml
@@ -12,8 +12,8 @@ permissions:
jobs:
issue-remove-inactive:
permissions:
- issues: write # for actions-cool/issues-helper to update issues
- pull-requests: write # for actions-cool/issues-helper to update PRs
+ issues: write # for actions-cool/issues-helper to update issues
+ pull-requests: write # for actions-cool/issues-helper to update PRs
runs-on: ubuntu-latest
steps:
- name: remove inactive
@@ -22,4 +22,4 @@ jobs:
with:
actions: 'remove-labels'
issue-number: ${{ github.event.issue.number }}
- labels: 'Inactive'
\ No newline at end of file
+ labels: 'Inactive'
diff --git a/.gitignore b/.gitignore
index 4577ee4f2843..0d17fd564a7c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -53,4 +53,3 @@ test-output
*.tsbuildinfo
next-env.d.ts
.next
-.env
diff --git a/.gitpod.yml b/.gitpod.yml
new file mode 100644
index 000000000000..e605228076a1
--- /dev/null
+++ b/.gitpod.yml
@@ -0,0 +1,3 @@
+tasks:
+ - init: pnpm install
+ command: pnpm run start
diff --git a/.npmrc b/.npmrc
index 89cfc9f9cf10..d9ed3d371eb9 100644
--- a/.npmrc
+++ b/.npmrc
@@ -1,2 +1,11 @@
lockfile=false
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*
diff --git a/.prettierignore b/.prettierignore
index 2bfa66c3a0be..3e459cbe4796 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,28 +1,63 @@
-**/*.svg
-.umi
-.umi-production
-/dist
-.dockerignore
+# Prettierignore for LobeHub
+################################################################
+
+# general
.DS_Store
-.eslintignore
-*.png
-*.jpg
-*.webp
-*.toml
-*.py
-docker
.editorconfig
-Dockerfile*
-.gitignore
-.prettierignore
-LICENSE
-.eslintcache
-*.lock
-yarn-error.log
.idea
+.vscode
+.history
+.temp
+.env.local
.husky
.npmrc
-.env.local
-.next
+.gitkeep
+venv
+temp
+tmp
+LICENSE
+
+# dependencies
+node_modules
+*.log
+*.lock
+package-lock.json
+
+# ci
+coverage
+.coverage
+.eslintcache
+.stylelintcache
+test-output
__snapshots__
-.snap
\ No newline at end of file
+*.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
\ No newline at end of file
diff --git a/.prettierrc.js b/.prettierrc.js
index 8be3baf87a51..f0355a9c1a75 100644
--- a/.prettierrc.js
+++ b/.prettierrc.js
@@ -1,10 +1 @@
-module.exports = {
- 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,
-};
+module.exports = require('@lobehub/lint').prettier;
diff --git a/.releaserc.js b/.releaserc.js
index 7b278591ede9..37930011d9d4 100644
--- a/.releaserc.js
+++ b/.releaserc.js
@@ -1,4 +1 @@
-module.exports = {
- extends: ['semantic-release-config-gitmoji'],
- branches: ['master'],
-};
+module.exports = require('@lobehub/lint').semanticRelease;
diff --git a/.remarkrc.js b/.remarkrc.js
new file mode 100644
index 000000000000..b673c10ed1cc
--- /dev/null
+++ b/.remarkrc.js
@@ -0,0 +1 @@
+module.exports = require('@lobehub/lint').remarklint;
diff --git a/.stylelintrc.js b/.stylelintrc.js
index bbd3844f4c25..c40700dd91da 100644
--- a/.stylelintrc.js
+++ b/.stylelintrc.js
@@ -1,10 +1,8 @@
+const config = require('@lobehub/lint').stylelint;
+
module.exports = {
- extends: ['stylelint-config-recommended', 'stylelint-config-clean-order'],
- files: ['*.js', '*.jsx', '*.ts', '*.tsx'],
- plugins: ['stylelint-order'],
- customSyntax: 'postcss-styled-syntax',
+ ...config,
rules: {
- 'no-empty-source': null,
- 'no-invalid-double-slash-comments': null,
+ 'selector-id-pattern': null,
},
};
diff --git a/README.md b/README.md
index c07c25a46fac..57078fd04bdd 100644
--- a/README.md
+++ b/README.md
@@ -51,7 +51,7 @@ Or clone it for local development:
```bash
$ git clone https://github.com/lobehub/lobe-chat.git
-$ cd canisminor-template
+$ cd lobe-chat
$ pnpm install
$ pnpm start
```
diff --git a/package.json b/package.json
index 112d76c0b56f..7f5399f1b79d 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
"sideEffects": false,
"scripts": {
"build": "next build",
- "dev": "PORT=3010 next dev",
+ "dev": "next dev -p 3010",
"lint": "eslint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
"lint:md": "remark . --quiet --frail --output",
"lint:style": "stylelint \"{src,tests}/**/*.{js,jsx,ts,tsx}\" --fix",
@@ -88,7 +88,7 @@
},
"devDependencies": {
"@commitlint/cli": "^17",
- "@lobehub/lint": "^1",
+ "@lobehub/lint": "latest",
"@next/eslint-plugin-next": "^13",
"@testing-library/jest-dom": "^5",
"@testing-library/react": "^14",
@@ -110,8 +110,8 @@
"node-fetch": "^3",
"postcss-styled-syntax": "^0.4",
"prettier": "^2",
- "prettier-plugin-organize-imports": "^3",
- "prettier-plugin-packagejson": "^2",
+ "remark": "^14",
+ "remark-cli": "^11",
"semantic-release": "^21",
"semantic-release-config-gitmoji": "^1",
"stylelint": "^15",
diff --git a/src/features/FolderPanel/index.tsx b/src/features/FolderPanel/index.tsx
index d9f9c31e580d..9bd4c7181141 100644
--- a/src/features/FolderPanel/index.tsx
+++ b/src/features/FolderPanel/index.tsx
@@ -15,17 +15,26 @@ export const useStyles = createStyles(({ css, token }) => ({
export default ({ children }: PropsWithChildren) => {
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);
if (tmpWidth !== sessionsWidth) setWidth(sessionsWidth);
return (
{
+ useSettings.setState({
+ sessionExpandable: expand,
+ sessionsWidth: expand ? 320 : 0,
+ });
+ }}
onSizeChange={(_, size) => {
if (!size) return;
@@ -36,14 +45,8 @@ export default ({ children }: PropsWithChildren) => {
setWidth(nextWidth);
useSettings.setState({ sessionsWidth: nextWidth });
}}
- expand={sessionExpandable}
- onExpandChange={(expand) => {
- useSettings.setState({
- sessionsWidth: expand ? 320 : 0,
- sessionExpandable: expand,
- });
- }}
- className={styles.panel}
+ placement="left"
+ size={{ height: '100vh', width: sessionsWidth }}
>
{children}
diff --git a/src/helpers/prompt.test.ts b/src/helpers/prompt.test.ts
index ee180dcc7ec3..10a6036883e6 100644
--- a/src/helpers/prompt.test.ts
+++ b/src/helpers/prompt.test.ts
@@ -1,6 +1,7 @@
-import { getInputVariablesFromMessages } from '@/helpers/prompt';
import { ChatMessage } from '@lobehub/ui';
+import { getInputVariablesFromMessages } from '@/helpers/prompt';
+
describe('getInputVariablesFromMessages 方法', () => {
it('应当在输入为空数组时返回空数组', () => {
const result = getInputVariablesFromMessages([]);
diff --git a/src/helpers/prompt.ts b/src/helpers/prompt.ts
index ce72da1f4c2b..9719d7bef21b 100644
--- a/src/helpers/prompt.ts
+++ b/src/helpers/prompt.ts
@@ -11,14 +11,17 @@ export const getChatPromptTemplate = (chatMessages: ChatMessage[]) =>
chatMessages.map((m) => {
switch (m.role) {
default:
- case 'user':
+ case 'user': {
return HumanMessagePromptTemplate.fromTemplate(m.content);
+ }
- case 'system':
+ case 'system': {
return SystemMessagePromptTemplate.fromTemplate(m.content);
+ }
- case 'assistant':
+ case 'assistant': {
return AIMessagePromptTemplate.fromTemplate(m.content);
+ }
}
}),
);
diff --git a/src/helpers/url.ts b/src/helpers/url.ts
index f01e9d29f4a1..2b255303c884 100644
--- a/src/helpers/url.ts
+++ b/src/helpers/url.ts
@@ -1,8 +1,11 @@
-import { Compressor } from '@/utils/compass';
import { ChatMessage } from '@lobehub/ui';
+import { Compressor } from '@/utils/compass';
+
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))}`;
};
diff --git a/src/layout/index.tsx b/src/layout/index.tsx
index 8f40938eaea4..ea0c164c810d 100644
--- a/src/layout/index.tsx
+++ b/src/layout/index.tsx
@@ -1,11 +1,10 @@
import { ThemeProvider } from '@lobehub/ui';
import { App, ConfigProvider } from 'antd';
import 'antd/dist/reset.css';
-
import Zh_CN from 'antd/locale/zh_CN';
import { PropsWithChildren, useEffect } from 'react';
-
import { useChatStore } from 'src/store/session';
+
import { GlobalStyle, useStyles } from './style';
const Layout = ({ children }: PropsWithChildren) => {
diff --git a/src/layout/style.ts b/src/layout/style.ts
index fc2acd35e65f..b0e8dd5e9be1 100644
--- a/src/layout/style.ts
+++ b/src/layout/style.ts
@@ -16,7 +16,7 @@ export const useStyles = createStyles(({ css, token }) => ({
background-image: linear-gradient(
180deg,
${token.colorBgContainer} 0%,
- rgba(255, 255, 255, 0) 20%
+ rgba(255, 255, 255, 0%) 20%
);
:has(#ChatLayout, #FlowLayout) {
diff --git a/src/pages/_app.page.tsx b/src/pages/_app.page.tsx
index a99a22d46402..653c73be705b 100644
--- a/src/pages/_app.page.tsx
+++ b/src/pages/_app.page.tsx
@@ -1,7 +1,8 @@
-import Layout from '@/layout';
import { Analytics } from '@vercel/analytics/react';
import type { AppProps } from 'next/app';
+import Layout from '@/layout';
+
function MyApp({ Component, pageProps }: AppProps) {
return (
diff --git a/src/pages/_document.page.tsx b/src/pages/_document.page.tsx
index 09db63c5fcb2..5b19b2d82294 100644
--- a/src/pages/_document.page.tsx
+++ b/src/pages/_document.page.tsx
@@ -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';
class MyDocument extends Document {
diff --git a/src/pages/api/LangChainStream.ts b/src/pages/api/LangChainStream.ts
index 2b14a2767a1c..69f0c19d1afb 100644
--- a/src/pages/api/LangChainStream.ts
+++ b/src/pages/api/LangChainStream.ts
@@ -1,4 +1,3 @@
-import { LangChainParams } from '@/types/langchain';
import { LLMChain } from 'langchain/chains';
import { ChatOpenAI } from 'langchain/chat_models/openai';
import {
@@ -8,6 +7,8 @@ import {
SystemMessagePromptTemplate,
} from 'langchain/prompts';
+import { LangChainParams } from '@/types/langchain';
+
const isDev = process.env.NODE_ENV === 'development';
const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL;
@@ -19,13 +20,16 @@ export function LangChainStream(payload: LangChainParams) {
prompts.map((m) => {
switch (m.role) {
default:
- case 'user':
+ case 'user': {
return HumanMessagePromptTemplate.fromTemplate(m.content);
- case 'system':
+ }
+ case 'system': {
return SystemMessagePromptTemplate.fromTemplate(m.content);
+ }
- case 'assistant':
+ case 'assistant': {
return AIMessagePromptTemplate.fromTemplate(m.content);
+ }
}
}),
);
@@ -43,8 +47,7 @@ export function LangChainStream(payload: LangChainParams) {
{
streaming: true,
...llm,
- // 暂时设定不重试 ,后续看是否需要支持重试
- maxRetries: 0,
+
callbacks: [
{
handleLLMNewToken(token) {
@@ -60,14 +63,13 @@ export function LangChainStream(payload: LangChainParams) {
},
},
],
+ // 暂时设定不重试 ,后续看是否需要支持重试
+ maxRetries: 0,
},
isDev && OPENAI_PROXY_URL ? { basePath: OPENAI_PROXY_URL } : undefined,
);
const chain = new LLMChain({
- prompt: chatPrompt,
- llm: chat,
- verbose: true,
callbacks: [
{
handleChainError(err: Error): Promise | void {
@@ -75,6 +77,9 @@ export function LangChainStream(payload: LangChainParams) {
},
},
],
+ llm: chat,
+ prompt: chatPrompt,
+ verbose: true,
});
try {
// 使用转换后的聊天消息作为输入开始聊天
diff --git a/src/pages/api/OpenAIStream.ts b/src/pages/api/OpenAIStream.ts
index 678030de8d3a..8704de701ac9 100644
--- a/src/pages/api/OpenAIStream.ts
+++ b/src/pages/api/OpenAIStream.ts
@@ -1,7 +1,8 @@
-import { OpenAIChatMessage } from '@/types/openai';
import { ChatOpenAI } from 'langchain/chat_models/openai';
import { AIChatMessage, HumanChatMessage, SystemChatMessage } from 'langchain/schema';
+import { OpenAIChatMessage } from '@/types/openai';
+
const isDev = process.env.NODE_ENV === 'development';
const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL;
@@ -10,46 +11,46 @@ const OPENAI_PROXY_URL = process.env.OPENAI_PROXY_URL;
*/
export interface OpenAIStreamPayload {
/**
- * @title 模型名称
+ * @title 控制生成文本中的惩罚系数,用于减少重复性
+ * @default 0
*/
- model: string;
+ frequency_penalty?: number;
/**
- * @title 聊天信息列表
+ * @title 生成文本的最大长度
*/
- messages: OpenAIChatMessage[];
+ max_tokens?: number;
/**
- * @title 生成文本的随机度量,用于控制文本的创造性和多样性
- * @default 0.5
+ * @title 聊天信息列表
*/
- temperature: number;
+ messages: OpenAIChatMessage[];
/**
- * @title 控制生成文本中最高概率的单个令牌
- * @default 1
+ * @title 模型名称
*/
- top_p?: number;
+ model: string;
/**
- * @title 控制生成文本中的惩罚系数,用于减少重复性
- * @default 0
+ * @title 返回的文本数量
*/
- frequency_penalty?: number;
+ n?: number;
/**
* @title 控制生成文本中的惩罚系数,用于减少主题的变化
* @default 0
*/
presence_penalty?: number;
- /**
- * @title 生成文本的最大长度
- */
- max_tokens?: number;
/**
* @title 是否开启流式请求
* @default true
*/
stream?: boolean;
/**
- * @title 返回的文本数量
+ * @title 生成文本的随机度量,用于控制文本的创造性和多样性
+ * @default 0.5
*/
- n?: number;
+ temperature: number;
+ /**
+ * @title 控制生成文本中最高概率的单个令牌
+ * @default 1
+ */
+ top_p?: number;
}
export function OpenAIStream(payload: OpenAIStreamPayload) {
@@ -59,13 +60,16 @@ export function OpenAIStream(payload: OpenAIStreamPayload) {
const chatMessages = messages.map((m) => {
switch (m.role) {
default:
- case 'user':
+ case 'user': {
return new HumanChatMessage(m.content);
- case 'system':
+ }
+ case 'system': {
return new SystemChatMessage(m.content);
+ }
- case 'assistant':
+ case 'assistant': {
return new AIChatMessage(m.content);
+ }
}
});
@@ -82,9 +86,7 @@ export function OpenAIStream(payload: OpenAIStreamPayload) {
{
streaming: true,
...params,
- // 暂时设定不重试 ,后续看是否需要支持重试
- maxRetries: 0,
- verbose: true,
+
callbacks: [
{
handleLLMNewToken(token) {
@@ -100,6 +102,9 @@ export function OpenAIStream(payload: OpenAIStreamPayload) {
},
},
],
+ // 暂时设定不重试 ,后续看是否需要支持重试
+ maxRetries: 0,
+ verbose: true,
},
isDev && OPENAI_PROXY_URL ? { basePath: OPENAI_PROXY_URL } : undefined,
);
diff --git a/src/pages/chat/Config/ConfigCell.tsx b/src/pages/chat/Config/ConfigCell.tsx
index 93fa2fe6a6bf..d4c8c3c743c8 100644
--- a/src/pages/chat/Config/ConfigCell.tsx
+++ b/src/pages/chat/Config/ConfigCell.tsx
@@ -24,8 +24,13 @@ export type ConfigCellProps = ConfigItem;
export const ConfigCell = memo(({ icon, label, value }) => {
const { styles } = useStyles();
return (
-
-
+
+
{label}
@@ -45,13 +50,13 @@ export const ConfigCellGroup = memo(({ items }) => {
{items.map(({ label, icon, value }, index) => (
-
+
{label}
diff --git a/src/pages/chat/Config/ReadMode.tsx b/src/pages/chat/Config/ReadMode.tsx
index 55e291d47a50..ea0823160f75 100644
--- a/src/pages/chat/Config/ReadMode.tsx
+++ b/src/pages/chat/Config/ReadMode.tsx
@@ -1,4 +1,3 @@
-import { agentSelectors, sessionSelectors, useChatStore } from '@/store/session';
import { Avatar } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import isEqual from 'fast-deep-equal';
@@ -7,19 +6,21 @@ import { memo } from 'react';
import { Center, Flexbox } from 'react-layout-kit';
import { shallow } from 'zustand/shallow';
+import { agentSelectors, sessionSelectors, useChatStore } from '@/store/session';
+
import { ConfigCell, ConfigCellGroup } from './ConfigCell';
const useStyles = createStyles(({ css, token }) => ({
- title: css`
- font-size: ${token.fontSizeHeading4}px;
- font-weight: bold;
- `,
desc: css`
color: ${token.colorText};
`,
model: css`
color: ${token.colorTextTertiary};
`,
+ title: css`
+ font-size: ${token.fontSizeHeading4}px;
+ font-weight: bold;
+ `,
}));
const ReadMode = memo(() => {
@@ -30,13 +31,13 @@ const ReadMode = memo(() => {
const model = useChatStore(agentSelectors.currentAgentModel, shallow);
return (
-
-
+
+
{title || '默认对话'}
{model}
{session.meta.description}
-
+
({
@@ -18,29 +19,38 @@ const useStyles = createStyles(({ css, token }) => ({
const Config = () => {
const { styles } = useStyles();
- const [showAgentSettings, toggleConfig] = useChatStore((s) => [s.showAgentSettings, s.toggleConfig], shallow);
+ const [showAgentSettings, toggleConfig] = useChatStore(
+ (s) => [s.showAgentSettings, s.toggleConfig],
+ shallow,
+ );
return (
-
+
会话设置
{
toggleConfig(false);
}}
+ title={'关闭'}
/>
diff --git a/src/pages/chat/Conversation/ChatList.tsx b/src/pages/chat/Conversation/ChatList.tsx
index a9aa688f3e15..5b9cfdfca01f 100644
--- a/src/pages/chat/Conversation/ChatList.tsx
+++ b/src/pages/chat/Conversation/ChatList.tsx
@@ -7,20 +7,25 @@ import { chatSelectors, useChatStore } from '@/store/session';
const List = () => {
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 (
{
switch (key) {
- case 'delete':
+ case 'delete': {
deleteMessage(id);
break;
+ }
- case 'regenerate':
+ case 'regenerate': {
resendMessage(id);
break;
+ }
}
}}
style={{ marginTop: 24 }}
diff --git a/src/pages/chat/Conversation/Input.tsx b/src/pages/chat/Conversation/Input.tsx
index 30b11ae37e71..c52ef7b9a387 100644
--- a/src/pages/chat/Conversation/Input.tsx
+++ b/src/pages/chat/Conversation/Input.tsx
@@ -16,7 +16,12 @@ const ChatInput = () => {
const [inputHeight] = useSettings((s) => [s.inputHeight], shallow);
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,
);
@@ -25,14 +30,14 @@ const ChatInput = () => {
expandable={false}
fullscreen={expand}
minHeight={200}
- placement="bottom"
- size={{ width: '100%', height: inputHeight }}
onSizeChange={(_, size) => {
if (!size) return;
useSettings.setState({
inputHeight: typeof size.height === 'string' ? Number.parseInt(size.height) : size.height,
});
}}
+ placement="bottom"
+ size={{ height: inputHeight, width: '100%' }}
>
({
- title: css`
- font-weight: bold;
- color: ${token.colorText};
- `,
desc: css`
font-size: 12px;
color: ${token.colorTextTertiary};
`,
+ title: css`
+ font-weight: bold;
+ color: ${token.colorText};
+ `,
}));
const Header = memo(() => {
const theme = useTheme();
@@ -38,9 +39,9 @@ const Header = memo(() => {
const { styles } = useStyles();
return (
{
gridArea: 'header',
}}
>
-
-
+
+
{meta?.title}
{meta?.description || '暂无描述'}
-
+
{
// genShareUrl();
}}
+ title={'分享'}
/>
-
+
diff --git a/src/pages/chat/SessionList/Header.tsx b/src/pages/chat/SessionList/Header.tsx
index 42090646205e..c610698e648f 100644
--- a/src/pages/chat/SessionList/Header.tsx
+++ b/src/pages/chat/SessionList/Header.tsx
@@ -1,12 +1,12 @@
import { ActionIcon, Logo, SearchBar } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { Plus } from 'lucide-react';
+import Link from 'next/link';
import { memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import { shallow } from 'zustand/shallow';
import { useChatStore } from '@/store/session';
-import Link from 'next/link';
export const useStyles = createStyles(({ css, token }) => ({
logo: css`
@@ -21,22 +21,25 @@ export const useStyles = createStyles(({ css, token }) => ({
const Header = memo(() => {
const { styles } = useStyles();
- const [keywords, createSession] = useChatStore((s) => [s.searchKeywords, s.createSession], shallow);
+ const [keywords, createSession] = useChatStore(
+ (s) => [s.searchKeywords, s.createSession],
+ shallow,
+ );
return (
-
-
+
+
-
+
-
+
useChatStore.setState({ searchKeywords: e.target.value })}
placeholder="Search..."
type={'block'}
- onChange={(e) => useChatStore.setState({ searchKeywords: e.target.value })}
+ value={keywords}
/>
);
diff --git a/src/pages/chat/SessionList/List/SessionItem.tsx b/src/pages/chat/SessionList/List/SessionItem.tsx
index a30a06210bc2..7efe9af0bfc3 100644
--- a/src/pages/chat/SessionList/List/SessionItem.tsx
+++ b/src/pages/chat/SessionList/List/SessionItem.tsx
@@ -1,12 +1,12 @@
+import { CloseOutlined } from '@ant-design/icons';
import { Avatar, List } from '@lobehub/ui';
+import { Popconfirm } from 'antd';
import { createStyles } from 'antd-style';
import { FC, memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import { shallow } from 'zustand/shallow';
import { sessionSelectors, useChatStore } from '@/store/session';
-import { CloseOutlined } from '@ant-design/icons';
-import { Popconfirm } from 'antd';
const useStyles = createStyles(({ css, cx }) => {
const closeCtn = css`
@@ -23,6 +23,10 @@ const useStyles = createStyles(({ css, cx }) => {
opacity: 0;
`;
return {
+ active: css`
+ opacity: 1;
+ `,
+ closeCtn,
container: css`
position: relative;
@@ -35,10 +39,6 @@ const useStyles = createStyles(({ css, cx }) => {
time: css`
align-self: flex-start;
`,
- closeCtn,
- active: css`
- opacity: 1;
- `,
};
});
@@ -51,43 +51,51 @@ interface SessionItemProps {
const SessionItem: FC = memo(({ id, active, simple = true, loading }) => {
const { styles, theme, cx } = useStyles();
- const [title, systemRole, avatar, avatarBackground, updateAt, switchAgent, removeSession] = useChatStore((s) => {
- const session = sessionSelectors.getSessionById(id)(s);
- const meta = session.meta;
+ const [title, systemRole, avatar, avatarBackground, updateAt, switchAgent, removeSession] =
+ useChatStore((s) => {
+ const session = sessionSelectors.getSessionById(id)(s);
+ const meta = session.meta;
- const systemRole = session.config.systemRole;
+ const systemRole = session.config.systemRole;
- return [
- meta.title || systemRole || '默认角色',
- systemRole,
- sessionSelectors.getAgentAvatar(meta),
- meta.backgroundColor,
- session?.updateAt,
- s.switchSession,
- s.removeSession,
- ];
- }, shallow);
+ return [
+ meta.title || systemRole || '默认角色',
+ systemRole,
+ sessionSelectors.getAgentAvatar(meta),
+ meta.backgroundColor,
+ session?.updateAt,
+ s.switchSession,
+ s.removeSession,
+ ];
+ }, shallow);
return (
-
+
removeSession(id)}
+ overlayStyle={{ width: 280 }}
+ placement={'right'}
+ title={'即将删除该会话,删除后该将无法找回,请确认你的操作。'}
>
+ }
classNames={{ time: styles.time }}
- avatar={}
+ date={updateAt}
+ description={simple ? undefined : systemRole}
+ loading={loading}
onClick={() => {
switchAgent(id);
}}
@@ -95,6 +103,7 @@ const SessionItem: FC = memo(({ id, active, simple = true, loa
alignItems: 'center',
color: theme.colorText,
}}
+ title={title}
/>
);
diff --git a/src/pages/chat/SessionList/List/index.tsx b/src/pages/chat/SessionList/List/index.tsx
index ed1bf3e01fcc..58d60c8a91a0 100644
--- a/src/pages/chat/SessionList/List/index.tsx
+++ b/src/pages/chat/SessionList/List/index.tsx
@@ -19,7 +19,7 @@ export const useStyles = createStyles(({ css, token }) => ({
justify-content: center;
margin-top: 8px;
- padding: 12px 12px;
+ padding: 12px;
background: ${rgba(token.colorBgLayout, 0.5)};
backdrop-filter: blur(8px);
@@ -35,7 +35,13 @@ const SessionList = memo(() => {
return (
<>
{list.map(({ id }) => (
-
+
))}
>
);
diff --git a/src/pages/chat/SessionList/index.tsx b/src/pages/chat/SessionList/index.tsx
index 38b7667bfe42..f00dd9a2ad1b 100644
--- a/src/pages/chat/SessionList/index.tsx
+++ b/src/pages/chat/SessionList/index.tsx
@@ -3,6 +3,7 @@ import { memo } from 'react';
import { Flexbox } from 'react-layout-kit';
import FolderPanel from '@/features/FolderPanel';
+
import Header from './Header';
import SessionList from './List';
diff --git a/src/pages/chat/Sidebar.tsx b/src/pages/chat/Sidebar.tsx
index c2b0b3734385..f9f5c3a35fe3 100644
--- a/src/pages/chat/Sidebar.tsx
+++ b/src/pages/chat/Sidebar.tsx
@@ -1,22 +1,33 @@
-import { useSettings } from '@/store/settings';
import { ActionIcon, Logo, SideNav } from '@lobehub/ui';
import { Album, MessageSquare, Settings2 } from 'lucide-react';
import { memo } from 'react';
import { shallow } from 'zustand/shallow';
+import { useSettings } from '@/store/settings';
+
const Sidebar = memo(() => {
const [tab, setTab] = useSettings((s) => [s.sidebarKey, s.switchSideBar], shallow);
return (
}
+ bottomActions={}
style={{ height: '100vh' }}
topActions={
<>
- setTab('chat')} />
- setTab('market')} />
+ setTab('chat')}
+ size="large"
+ />
+ setTab('market')}
+ size="large"
+ />
>
}
- bottomActions={}
/>
);
});
diff --git a/src/pages/chat/[id].page.tsx b/src/pages/chat/[id].page.tsx
index d28127c1e822..d3f5c78e5929 100644
--- a/src/pages/chat/[id].page.tsx
+++ b/src/pages/chat/[id].page.tsx
@@ -38,12 +38,15 @@ const ChatLayout = () => {
{title ? `${title} - LobeChat` : 'LobeChat'}
-
+
-
+
diff --git a/src/pages/chat/index.page.tsx b/src/pages/chat/index.page.tsx
index 00f2c8ea583b..27ff4c12726f 100644
--- a/src/pages/chat/index.page.tsx
+++ b/src/pages/chat/index.page.tsx
@@ -1,5 +1 @@
-
-
-
-
-export {default} from './[id].page';
\ No newline at end of file
+export { default } from './[id].page';
diff --git a/src/pages/index.page.tsx b/src/pages/index.page.tsx
index b1b1e01b9816..ac9e2586cd1e 100644
--- a/src/pages/index.page.tsx
+++ b/src/pages/index.page.tsx
@@ -1,5 +1 @@
-
-
-
-
-export {default} from './chat/index.page';
\ No newline at end of file
+export { default } from './chat/index.page';
diff --git a/src/prompts/agent.ts b/src/prompts/agent.ts
index 2373a2c45387..575f51b23d64 100644
--- a/src/prompts/agent.ts
+++ b/src/prompts/agent.ts
@@ -4,30 +4,30 @@ import { OpenAIStreamPayload } from '@/pages/api/OpenAIStream';
export const promptSummaryAgentName = (content: string): Partial => ({
messages: [
{
- role: 'system',
content: `你是一名擅长起名的起名大师,你需要将用户的描述总结为 20 个字以内的角色,格式要求如下:
输入: {文本作为JSON引用字符串}
输出: {角色名}
`,
+ role: 'system',
},
{
- role: 'user',
content: `输入: {你是一名专业的前端开发者,擅长结合 vitest 和\`testing-library/react\` 书写单元测试。接下来用户会输入一串 ts 代码,你需要给出完善的单元测试。\n你需要注意,单元测试代码中,不应该使用 jest 。如果需要使用 \`jest.fn\`,请使用 \`vi.fn\` 替换}`,
+ role: 'user',
},
- { role: 'assistant', content: '前端 vitest 测试专家' },
+ { content: '前端 vitest 测试专家', role: 'assistant' },
{
- role: 'user',
content: `输入: {你是一名前端专家,请将下面的代码转成 ts,不要修改实现。如果原本 js 中没有定义的全局变量,需要补充 declare 的类型声明。}`,
+ role: 'user',
},
- { role: 'assistant', content: 'js 转 ts 专家' },
+ { content: 'js 转 ts 专家', role: 'assistant' },
{
- role: 'user',
content: `输入:{你是一名擅长比喻和隐喻的UX Writter。用户会输入文案,你需要给出3个优化后的结果,使用 markdown格式的文本。下面是一个例子:
输入:页面加载中
输出:页面似乎在思考,一会儿才能准备好}`,
+ role: 'user',
},
- { role: 'assistant', content: '文案比喻优化专家' },
- { role: 'user', content: `输入: {${content}}` },
+ { content: '文案比喻优化专家', role: 'assistant' },
+ { content: `输入: {${content}}`, role: 'user' },
],
});
@@ -35,7 +35,6 @@ export const promptSummaryAgentName = (content: string): Partial => ({
messages: [
{
- role: 'system',
content: `你是一名非常懂设计与时尚的设计师,你需要从用户的描述中匹配一个合适的 emoji。
输入:你是一名精通体验设计的设计系统设计师,设计系统存在诸多类别的 token,比如品牌色、成功色等,你需要为各个类别的 token 提供说明文案。
输出: 💅
@@ -43,10 +42,11 @@ export const promptPickEmoji = (content: string): Partial =
输入:用户会输入一串 ts 代码,为了确保所有功能和分支的 100% 的覆盖率,你需要给出需要考虑哪些数据场景。
输出: 🧪
`,
+ role: 'system',
},
{
- role: 'user',
content: `输入:${content}`,
+ role: 'user',
},
],
});
diff --git a/src/prompts/chat.ts b/src/prompts/chat.ts
index 99279fd72fa0..8aa5721e8b2e 100644
--- a/src/prompts/chat.ts
+++ b/src/prompts/chat.ts
@@ -1,29 +1,37 @@
import { OpenAIStreamPayload } from '@/pages/api/OpenAIStream';
import { OpenAIChatMessage } from '@/types/openai';
-export const promptSummaryTitle = (messages: OpenAIChatMessage[]): Partial => ({
+export const promptSummaryTitle = (
+ messages: OpenAIChatMessage[],
+): Partial => ({
messages: [
{
+ content:
+ '你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题,不需要包含标点符号',
role: 'system',
- content: '你是一名擅长会话的助理,你需要将用户的会话总结为 10 个字以内的标题,不需要包含标点符号',
},
{
- role: 'user',
content: `${messages.map((message) => `${message.role}: ${message.content}`).join('\n')}
请总结上述对话为10个字以内的标题,不需要包含标点符号`,
+ role: 'user',
},
],
});
-export const promptSummaryDescription = (messages: OpenAIChatMessage[]): Partial => ({
+export const promptSummaryDescription = (
+ messages: OpenAIChatMessage[],
+): Partial => ({
messages: [
- { role: 'system', content: '你是一名擅长会话的助理,你需要将用户的会话做一个3句话以内的总结。' },
{
- role: 'user',
+ content: '你是一名擅长会话的助理,你需要将用户的会话做一个3句话以内的总结。',
+ role: 'system',
+ },
+ {
content: `${messages.map((message) => `${message.role}: ${message.content}`).join('\n')}
请总结上述对话内容,不超过 50 个字。`,
+ role: 'user',
},
],
});
diff --git a/src/services/chatModel.ts b/src/services/chatModel.ts
index d5d173082bcb..4a54cf805459 100644
--- a/src/services/chatModel.ts
+++ b/src/services/chatModel.ts
@@ -1,6 +1,7 @@
-import type { OpenAIStreamPayload } from '@/pages/api/OpenAIStream';
import { merge } from 'lodash-es';
+import type { OpenAIStreamPayload } from '@/pages/api/OpenAIStream';
+
import { URLS } from './url';
/**
@@ -13,18 +14,18 @@ export const fetchChatModel = (
const payload = merge(
{
model: 'gpt-3.5-turbo',
- temperature: 0.6,
stream: true,
+ temperature: 0.6,
},
params,
);
return fetch(URLS.openai, {
- method: 'POST',
+ body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json',
},
- body: JSON.stringify(payload),
+ method: 'POST',
signal,
});
};
diff --git a/src/services/langChain.ts b/src/services/langChain.ts
index 68befd6c9324..fdc0f379fccd 100644
--- a/src/services/langChain.ts
+++ b/src/services/langChain.ts
@@ -8,11 +8,11 @@ import { fetchAIFactory } from '@/utils/fetch';
export const fetchLangChain = fetchAIFactory(
(params: LangChainParams, signal?: AbortSignal | undefined) =>
fetch(URLS.chain, {
- method: 'POST',
+ body: JSON.stringify(params),
headers: {
'Content-Type': 'application/json',
},
- body: JSON.stringify(params),
+ method: 'POST',
signal,
}),
);
diff --git a/src/services/url.ts b/src/services/url.ts
index bb13a2faf110..0bcc7de46cf6 100644
--- a/src/services/url.ts
+++ b/src/services/url.ts
@@ -3,6 +3,6 @@ const isDev = process.env.NODE_ENV === 'development';
const prefix = isDev ? '-dev' : '';
export const URLS = {
- openai: '/api/openai' + prefix,
chain: '/api/chain' + prefix,
+ openai: '/api/openai' + prefix,
};
diff --git a/src/store/session/slices/agentConfig/selectors.ts b/src/store/session/slices/agentConfig/selectors.ts
index 02c4ab20c287..99213d7f2156 100644
--- a/src/store/session/slices/agentConfig/selectors.ts
+++ b/src/store/session/slices/agentConfig/selectors.ts
@@ -1,6 +1,6 @@
import { SessionStore } from '@/store/session';
-
import { LanguageModel } from '@/types/llm';
+
import { sessionSelectors } from '../session';
const currentAgentTitle = (s: SessionStore) => {
@@ -31,8 +31,8 @@ const currentAgentModel = (s: SessionStore): LanguageModel => {
};
export const agentSelectors = {
- currentAgentConfig,
currentAgentAvatar,
+ currentAgentConfig,
currentAgentModel,
currentAgentTitle,
};
diff --git a/src/store/session/slices/chat/action.ts b/src/store/session/slices/chat/action.ts
index 2e38544533d8..b4dec6d97d07 100644
--- a/src/store/session/slices/chat/action.ts
+++ b/src/store/session/slices/chat/action.ts
@@ -12,6 +12,8 @@ const LOADING_FLAT = '...';
export interface ChatAction {
clearMessage: () => void;
+ deleteMessage: (id: string) => void;
+
/**
* @title 派发消息
* @param payload - 消息分发
@@ -20,36 +22,40 @@ export interface ChatAction {
dispatchMessage: (payload: MessageDispatch) => void;
generateMessage: (messages: ChatMessage[], options: FetchSSEOptions) => Promise;
-
/**
* @title 处理消息编辑
* @param index - 消息索引或空
* @returns void
*/
handleMessageEditing: (messageId: string | undefined) => void;
+
/**
* @title 重发消息
* @param index - 消息索引
* @returns Promise
*/
resendMessage: (id: string) => Promise;
-
/**
* @title 发送消息
* @returns Promise
*/
sendMessage: (text: string) => Promise;
- deleteMessage: (id: string) => void;
}
-export const createChatSlice: StateCreator = (
- set,
- get,
-) => ({
+export const createChatSlice: StateCreator<
+ SessionStore,
+ [['zustand/devtools', never]],
+ [],
+ ChatAction
+> = (set, get) => ({
clearMessage: () => {
get().dispatchMessage({ type: 'resetMessages' });
},
+ deleteMessage: (id) => {
+ get().dispatchMessage({ id, type: 'deleteMessage' });
+ },
+
dispatchMessage: (payload) => {
const { activeId } = get();
const session = sessionSelectors.currentSession(get());
@@ -183,8 +189,4 @@ export const createChatSlice: StateCreator {
- get().dispatchMessage({ type: 'deleteMessage', id });
- },
});
diff --git a/src/store/session/slices/chat/initialState.ts b/src/store/session/slices/chat/initialState.ts
index 5cc47db685ff..1f4ded46269e 100644
--- a/src/store/session/slices/chat/initialState.ts
+++ b/src/store/session/slices/chat/initialState.ts
@@ -1,6 +1,6 @@
export interface ChatState {
- editingMessageId?: string;
chatLoading: boolean;
+ editingMessageId?: string;
}
export const initialChatState: ChatState = {
diff --git a/src/store/session/slices/chat/messageReducer.ts b/src/store/session/slices/chat/messageReducer.ts
index 16cc7c840b9c..c3ba688756ea 100644
--- a/src/store/session/slices/chat/messageReducer.ts
+++ b/src/store/session/slices/chat/messageReducer.ts
@@ -6,18 +6,18 @@ import { MetaData } from '@/types/meta';
import { nanoid } from '@/utils/uuid';
interface AddMessage {
- type: 'addMessage';
- message: string;
- role: LLMRoleType;
id?: string;
- quotaId?: string;
- parentId?: string;
+ message: string;
meta?: MetaData;
+ parentId?: string;
+ quotaId?: string;
+ role: LLMRoleType;
+ type: 'addMessage';
}
interface DeleteMessage {
- type: 'deleteMessage';
id: string;
+ type: 'deleteMessage';
}
interface ResetMessages {
@@ -25,38 +25,43 @@ interface ResetMessages {
}
interface UpdateMessage {
- type: 'updateMessage';
id: string;
key: keyof ChatMessage;
+ type: 'updateMessage';
value: ChatMessage[keyof ChatMessage];
}
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) {
- case 'addMessage':
+ case 'addMessage': {
return produce(state, (draftState) => {
const mid = payload.id || nanoid();
draftState[mid] = {
- id: mid,
- role: payload.role,
content: payload.message,
+ createAt: Date.now(),
+ id: mid,
meta: payload.meta || {},
- quotaId: payload.quotaId,
parentId: payload.parentId,
+ quotaId: payload.quotaId,
+ role: payload.role,
updateAt: Date.now(),
- createAt: Date.now(),
};
});
+ }
- case 'deleteMessage':
+ case 'deleteMessage': {
return produce(state, (draftState) => {
delete draftState[payload.id];
});
+ }
- case 'updateMessage':
+ case 'updateMessage': {
return produce(state, (draftState) => {
const { id, key, value } = payload;
const message = draftState[id];
@@ -66,11 +71,14 @@ export const messagesReducer = (state: ChatMessageMap, payload: MessageDispatch)
message[key] = value;
message.updateAt = Date.now();
});
+ }
- case 'resetMessages':
+ case 'resetMessages': {
return {};
+ }
- default:
+ default: {
throw new Error('暂未实现的 type,请检查 reducer');
+ }
}
};
diff --git a/src/store/session/slices/chat/selectors.ts b/src/store/session/slices/chat/selectors.ts
index b1fdc851ff76..948f86a8e8dd 100644
--- a/src/store/session/slices/chat/selectors.ts
+++ b/src/store/session/slices/chat/selectors.ts
@@ -1,6 +1,7 @@
-import { ChatMessage } from '@/types/chatMessage';
import { encode } from 'gpt-tokenizer';
+import { ChatMessage } from '@/types/chatMessage';
+
import type { SessionStore } from '../../store';
import { sessionSelectors } from '../session';
@@ -40,8 +41,8 @@ const systemRoleTokenCount = (s: SessionStore) => systemRoleTokens(s).length;
export const chatSelectors = {
currentChats: currentChatsSel,
systemRole: systemRoleSel,
- totalTokens,
- totalTokenCount,
- systemRoleTokens,
systemRoleTokenCount,
+ systemRoleTokens,
+ totalTokenCount,
+ totalTokens,
};
diff --git a/src/store/session/slices/session/action.ts b/src/store/session/slices/session/action.ts
index 5081a14972ba..12dd3b238ffc 100644
--- a/src/store/session/slices/session/action.ts
+++ b/src/store/session/slices/session/action.ts
@@ -15,6 +15,12 @@ export interface SessionAction {
* @returns void
*/
createSession: () => Promise;
+ /**
+ * 分发聊天记录
+ * @param payload - 聊天记录
+ */
+ dispatchSession: (payload: SessionDispatch) => void;
+
/**
* @title 删除会话
* @param index - 会话索引
@@ -29,12 +35,6 @@ export interface SessionAction {
*/
switchSession: (sessionId?: string | 'new') => void;
- /**
- * 分发聊天记录
- * @param payload - 聊天记录
- */
- dispatchSession: (payload: SessionDispatch) => void;
-
/**
* 生成压缩后的消息
* @returns 压缩后的消息
@@ -42,48 +42,50 @@ export interface SessionAction {
// genShareUrl: () => string;
}
-export const createSessionSlice: StateCreator = (
- set,
- get,
-) => ({
- dispatchSession: (payload) => {
- const { type, ...res } = payload;
- set({ sessions: sessionsReducer(get().sessions, payload) }, false, {
- type: `dispatchChat/${type}`,
- payload: res,
- });
- },
-
+export const createSessionSlice: StateCreator<
+ SessionStore,
+ [['zustand/devtools', never]],
+ [],
+ SessionAction
+> = (set, get) => ({
createSession: async () => {
const { dispatchSession, switchSession } = get();
const timestamp = Date.now();
const newSession: LobeAgentSession = {
- id: uuid(),
- createAt: timestamp,
- updateAt: timestamp,
- type: LobeSessionType.Agent,
chats: {},
- meta: {
- title: '默认对话',
- },
config: {
model: LanguageModel.GPT3_5,
- systemRole: '',
params: {
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);
},
+ dispatchSession: (payload) => {
+ const { type, ...res } = payload;
+ set({ sessions: sessionsReducer(get().sessions, payload) }, false, {
+ payload: res,
+ type: `dispatchChat/${type}`,
+ });
+ },
+
removeSession: (sessionId) => {
- get().dispatchSession({ type: 'removeSession', id: sessionId });
+ get().dispatchSession({ id: sessionId, type: 'removeSession' });
Router.push('/');
},
diff --git a/src/store/session/slices/session/reducers/session.ts b/src/store/session/slices/session/reducers/session.ts
index 6c4d6b94999d..a53557013011 100644
--- a/src/store/session/slices/session/reducers/session.ts
+++ b/src/store/session/slices/session/reducers/session.ts
@@ -1,52 +1,53 @@
+import { produce } from 'immer';
+
import { ChatMessageMap } from '@/types/chatMessage';
import { MetaData } from '@/types/meta';
import { LobeAgentConfig, LobeAgentSession, LobeSessions } from '@/types/session';
-import { produce } from 'immer';
/**
* @title 添加会话
*/
interface AddSession {
+ /**
+ * @param session - 会话信息
+ */
+ session: LobeAgentSession;
/**
* @param type - 操作类型
* @default 'addChat'
*/
type: 'addSession';
- /**
- * @param session - 会话信息
- */
- session: LobeAgentSession;
}
interface RemoveSession {
- type: 'removeSession';
id: string;
+ type: 'removeSession';
}
/**
* @title 更新会话聊天上下文
*/
interface UpdateSessionChat {
- type: 'updateSessionChat';
+ chats: ChatMessageMap;
/**
* 会话 ID
*/
id: string;
- chats: ChatMessageMap;
+ type: 'updateSessionChat';
}
interface UpdateSessionMeta {
- type: 'updateSessionMeta';
id: string;
key: keyof MetaData;
+ type: 'updateSessionMeta';
value: any;
}
interface UpdateSessionAgentConfig {
- type: 'updateSessionConfig';
- id: string;
config: Partial;
+ id: string;
+ type: 'updateSessionConfig';
}
export type SessionDispatch =
@@ -58,17 +59,19 @@ export type SessionDispatch =
export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch): LobeSessions => {
switch (payload.type) {
- case 'addSession':
+ case 'addSession': {
return produce(state, (draft) => {
draft[payload.session.id] = payload.session;
});
+ }
- case 'removeSession':
+ case 'removeSession': {
return produce(state, (draft) => {
delete draft[payload.id];
});
+ }
- case 'updateSessionMeta':
+ case 'updateSessionMeta': {
return produce(state, (draft) => {
const chat = draft[payload.id];
if (!chat) return;
@@ -77,16 +80,18 @@ export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch):
chat.meta[key] = value;
});
+ }
- case 'updateSessionChat':
+ case 'updateSessionChat': {
return produce(state, (draft) => {
const chat = draft[payload.id];
if (!chat) return;
chat.chats = payload.chats;
});
+ }
- case 'updateSessionConfig':
+ case 'updateSessionConfig': {
return produce(state, (draft) => {
const { id, config } = payload;
const chat = draft[id];
@@ -94,8 +99,10 @@ export const sessionsReducer = (state: LobeSessions, payload: SessionDispatch):
chat.config = { ...chat.config, ...config };
});
+ }
- default:
+ default: {
return produce(state, () => {});
+ }
}
};
diff --git a/src/store/session/slices/session/selectors/index.ts b/src/store/session/slices/session/selectors/index.ts
index eeec4b8e6484..e9a885573c9e 100644
--- a/src/store/session/slices/session/selectors/index.ts
+++ b/src/store/session/slices/session/selectors/index.ts
@@ -1,12 +1,20 @@
import { getAgentAvatar } from './chat';
-import { chatListSel, currentSessionSafe, currentSessionSel, getSessionById, getSessionMetaById } from './list';
+import {
+ chatListSel,
+ currentSessionSafe,
+ currentSessionSel,
+ getSessionById,
+ getSessionMetaById,
+} from './list';
export const sessionSelectors = {
+ chatList: chatListSel,
currentSession: currentSessionSel,
currentSessionSafe,
- chatList: chatListSel,
+
+ getAgentAvatar,
+
+ getSessionById,
// sessionTree: sessionTreeSel,
getSessionMetaById,
- getSessionById,
- getAgentAvatar,
};
diff --git a/src/store/session/slices/session/selectors/list.ts b/src/store/session/slices/session/selectors/list.ts
index 5925541aefbf..68d957216248 100644
--- a/src/store/session/slices/session/selectors/list.ts
+++ b/src/store/session/slices/session/selectors/list.ts
@@ -1,7 +1,6 @@
import { SessionStore } from '@/store/session';
-import { LobeAgentSession } from '@/types/session';
-
import { MetaData } from '@/types/meta';
+import { LobeAgentSession } from '@/types/session';
import { filterWithKeywords } from '@/utils/filter';
import { initLobeSession } from '../initialState';
diff --git a/src/store/settings.ts b/src/store/settings.ts
index 94679bdf6361..8316684cf454 100644
--- a/src/store/settings.ts
+++ b/src/store/settings.ts
@@ -7,26 +7,26 @@ import { ConfigSettings } from '@/types/exportConfig';
export type SidebarTabKey = 'chat' | 'market';
interface SettingsStore {
- sessionsWidth: number;
- inputHeight: number;
avatar?: string;
+ importSettings: (settings: ConfigSettings) => void;
+ inputHeight: number;
sessionExpandable?: boolean;
+ sessionsWidth: number;
sidebarKey: SidebarTabKey;
- themeMode?: ThemeMode;
- importSettings: (settings: ConfigSettings) => void;
switchSideBar: (key: SidebarTabKey) => void;
+ themeMode?: ThemeMode;
}
export const useSettings = create()(
persist(
(set) => ({
- sessionsWidth: 320,
- inputHeight: 200,
- sessionExpandable: true,
- sidebarKey: 'chat',
importSettings: (settings) => {
set({ ...settings });
},
+ inputHeight: 200,
+ sessionExpandable: true,
+ sessionsWidth: 320,
+ sidebarKey: 'chat',
switchSideBar: (key) => {
set({ sidebarKey: key });
},
diff --git a/src/types/chatMessage.ts b/src/types/chatMessage.ts
index 49883dbe1c70..a19de5eec6ed 100644
--- a/src/types/chatMessage.ts
+++ b/src/types/chatMessage.ts
@@ -14,11 +14,7 @@ export interface ChatMessageError {
}
export interface ChatMessage extends BaseDataModel {
- /**
- * 角色
- * @description 消息发送者的角色
- */
- role: LLMRoleType;
+ archive?: boolean;
/**
* @title 内容
@@ -27,20 +23,24 @@ export interface ChatMessage extends BaseDataModel {
content: string;
error?: any;
- archive?: boolean;
-
- parentId?: string;
- // 引用
- quotaId?: string;
// 扩展字段
extra?: {
// 翻译
translate: {
- to: string;
target: string;
+ to: string;
};
// 语音
} & Record;
+
+ parentId?: string;
+ // 引用
+ quotaId?: string;
+ /**
+ * 角色
+ * @description 消息发送者的角色
+ */
+ role: LLMRoleType;
}
export type ChatMessageMap = Record;
diff --git a/src/types/langchain.ts b/src/types/langchain.ts
index 95aa5c5a5090..2f29bdf44111 100644
--- a/src/types/langchain.ts
+++ b/src/types/langchain.ts
@@ -2,28 +2,28 @@ import { ChatMessage } from '@lobehub/ui';
export interface LangChainParams {
llm: {
- model: string;
- /**
- * 生成文本的随机度量,用于控制文本的创造性和多样性
- * @default 0.6
- */
- temperature: number;
- /**
- * 控制生成文本中最高概率的单个令牌
- */
- top_p?: number;
/**
* 控制生成文本中的惩罚系数,用于减少重复性
*/
frequency_penalty?: number;
+ /**
+ * 生成文本的最大长度
+ */
+ max_tokens?: number;
+ model: string;
/**
* 控制生成文本中的惩罚系数,用于减少主题的变化
*/
presence_penalty?: number;
/**
- * 生成文本的最大长度
+ * 生成文本的随机度量,用于控制文本的创造性和多样性
+ * @default 0.6
*/
- max_tokens?: number;
+ temperature: number;
+ /**
+ * 控制生成文本中最高概率的单个令牌
+ */
+ top_p?: number;
};
/**
diff --git a/src/types/meta.ts b/src/types/meta.ts
index dc4634be01ab..77abe8b173fd 100644
--- a/src/types/meta.ts
+++ b/src/types/meta.ts
@@ -1,11 +1,4 @@
export interface MetaData {
- /**
- * 名称
- * @description 可选参数,如果不传则使用默认名称
- */
- title?: string;
- description?: string;
- tag?: string[];
/**
* 角色头像
* @description 可选参数,如果不传则使用默认头像
@@ -16,11 +9,18 @@ export interface MetaData {
* @description 可选参数,如果不传则使用默认背景色
*/
backgroundColor?: string;
+ description?: string;
+ tag?: string[];
+ /**
+ * 名称
+ * @description 可选参数,如果不传则使用默认名称
+ */
+ title?: string;
}
export interface BaseDataModel {
+ createAt: number;
id: string;
meta: MetaData;
updateAt: number;
- createAt: number;
}
diff --git a/src/types/session.ts b/src/types/session.ts
index b0e8d665858e..4f9b53676208 100644
--- a/src/types/session.ts
+++ b/src/types/session.ts
@@ -15,17 +15,21 @@ export enum LobeSessionType {
interface LobeSessionBase extends BaseDataModel {
/**
- * 每个会话的类别
+ * 聊天记录
*/
- type: LobeSessionType;
+ chats: ChatMessageMap;
/**
- * 聊天记录
+ * 每个会话的类别
*/
- chats: ChatMessageMap;
+ type: LobeSessionType;
}
export interface LobeAgentConfig {
+ /**
+ * 语言模型示例
+ */
+ example?: LLMExample;
/**
* 角色所使用的语言模型
* @default gpt-3.5-turbo
@@ -39,21 +43,17 @@ export interface LobeAgentConfig {
* 系统角色
*/
systemRole: string;
- /**
- * 语言模型示例
- */
- example?: LLMExample;
}
/**
* Lobe Agent会话
*/
export interface LobeAgentSession extends LobeSessionBase {
- type: LobeSessionType.Agent;
/**
* 语言模型角色设定
*/
config: LobeAgentConfig;
+ type: LobeSessionType.Agent;
}
export type LobeSessions = Record;
diff --git a/src/utils/VersionController.ts b/src/utils/VersionController.ts
index 43c1791b9d17..ff1395b3f179 100644
--- a/src/utils/VersionController.ts
+++ b/src/utils/VersionController.ts
@@ -3,16 +3,16 @@
* @template T - 状态类型
*/
export interface Migration {
- /**
- * 迁移版本号
- */
- version: number;
/**
* 迁移数据
* @param data - 迁移数据
* @returns 迁移后的数据
*/
migrate(data: MigrationData): MigrationData;
+ /**
+ * 迁移版本号
+ */
+ version: number;
}
/**
diff --git a/src/utils/compass.ts b/src/utils/compass.ts
index 9bbf877646fe..6b3ad6778673 100644
--- a/src/utils/compass.ts
+++ b/src/utils/compass.ts
@@ -8,8 +8,8 @@ export class StrCompressor {
* @ignore
*/
private instance!: {
- decompress(buf: Uint8Array): Uint8Array;
compress(buf: Uint8Array, options?: any): Uint8Array;
+ decompress(buf: Uint8Array): Uint8Array;
};
async init(): Promise {
diff --git a/src/utils/fetch.ts b/src/utils/fetch.ts
index 8968752eaf2c..15dd9e7f479d 100644
--- a/src/utils/fetch.ts
+++ b/src/utils/fetch.ts
@@ -21,8 +21,8 @@ const codeMessage: Record = {
};
export interface FetchSSEOptions {
- onMessageHandle?: (text: string) => void;
onErrorHandle?: (error: ChatMessageError) => void;
+ onMessageHandle?: (text: string) => void;
}
/**
@@ -68,15 +68,7 @@ export const fetchSSE = async (fetchFn: () => Promise, options: FetchS
};
interface FetchAITaskResultParams {
- /**
- * 请求对象
- */
- params: T;
- /**
- * 消息处理函数
- * @param text - 消息内容
- */
- onMessageHandle?: (text: string) => void;
+ abortController?: AbortController;
/**
* 错误处理函数
*/
@@ -86,13 +78,27 @@ interface FetchAITaskResultParams {
* @param loading - 是否处于加载状态
*/
onLoadingChange?: (loading: boolean) => void;
+ /**
+ * 消息处理函数
+ * @param text - 消息内容
+ */
+ onMessageHandle?: (text: string) => void;
- abortController?: AbortController;
+ /**
+ * 请求对象
+ */
+ params: T;
}
export const fetchAIFactory =
(fetcher: (params: T, signal?: AbortSignal) => Promise) =>
- async ({ params, onMessageHandle, onError, onLoadingChange, abortController }: FetchAITaskResultParams) => {
+ async ({
+ params,
+ onMessageHandle,
+ onError,
+ onLoadingChange,
+ abortController,
+ }: FetchAITaskResultParams) => {
const errorHandle = (error: Error) => {
onLoadingChange?.(false);
if (abortController?.signal.aborted) {
@@ -112,10 +118,10 @@ export const fetchAIFactory =
onLoadingChange?.(true);
const data = await fetchSSE(() => fetcher(params, abortController?.signal), {
- onMessageHandle,
onErrorHandle: (error) => {
errorHandle(new Error(error.message));
},
+ onMessageHandle,
}).catch(errorHandle);
onLoadingChange?.(false);
diff --git a/src/utils/uuid.ts b/src/utils/uuid.ts
index a25793ff90b7..3c9d63e6cd59 100644
--- a/src/utils/uuid.ts
+++ b/src/utils/uuid.ts
@@ -1,8 +1,4 @@
-
-
-
// generate('1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', 16); //=> "4f90d13a42"
-
import { customAlphabet } from 'nanoid/non-secure';
export const nanoid = customAlphabet(
@@ -10,4 +6,4 @@ export const nanoid = customAlphabet(
8,
);
-export {v4 as uuid} from 'uuid';
\ No newline at end of file
+export { v4 as uuid } from 'uuid';
diff --git a/tsconfig.json b/tsconfig.json
index a5a8108b21cd..3ccd653b687f 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -20,6 +20,6 @@
"@/*": ["src/*"]
}
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
- "exclude": ["node_modules"]
+ "exclude": ["node_modules"],
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"]
}