mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-14 03:30:19 +00:00
Compare commits
100 Commits
improve-task
...
v2.1.23
| Author | SHA1 | Date | |
|---|---|---|---|
| b06b0e5662 | |||
| 31145c9a1f | |||
| ce8c0c3eaf | |||
| c12d0221ac | |||
| 8877bc12d7 | |||
| dd7d590bdd | |||
| bbc1dfcc23 | |||
| 0dbd8d6abf | |||
| 40edeb4025 | |||
| ee7ae5b1d2 | |||
| 044e290ab0 | |||
| 6ed5e7bba1 | |||
| be6da39437 | |||
| 0e688d08b8 | |||
| f55b8344c6 | |||
| e1599d2e5e | |||
| 72a85ac151 | |||
| bd4e253d1b | |||
| 9fa6253406 | |||
| 35b6d8f9ca | |||
| e14cfae266 | |||
| 9740abd1d3 | |||
| 3d99e7ed77 | |||
| 83173d9380 | |||
| 16b1904088 | |||
| 37814db6df | |||
| b6ada85d7f | |||
| ffeec13c28 | |||
| 7e919b124c | |||
| 42bccd2307 | |||
| ea81cd42aa | |||
| 882de51b8a | |||
| 3e3ef507ca | |||
| e1cf2dee9d | |||
| 2c7ecdafdf | |||
| f26d0df93d | |||
| 463d6c8762 | |||
| 5cae71eda7 | |||
| 521ecdd047 | |||
| 265491cfde | |||
| deb28e5c12 | |||
| 90a75af669 | |||
| f628564acf | |||
| b0a8e0f51c | |||
| b3e5b5cc36 | |||
| 41dcaad567 | |||
| f290164091 | |||
| 193c96f9d9 | |||
| 87a6ed06f8 | |||
| 7ba15cceba | |||
| 9e7825bef1 | |||
| 20029a7820 | |||
| a1decbb46f | |||
| bb31a10f45 | |||
| e402c51bec | |||
| 709b24ec6d | |||
| ce35fac930 | |||
| e036ccee86 | |||
| 3f1a1983e3 | |||
| 151261d0c5 | |||
| 0f63781876 | |||
| 71064fdede | |||
| 20928ac466 | |||
| 347d0ce70f | |||
| 28887c77e8 | |||
| 1cc9034c7c | |||
| 37609e42d6 | |||
| 3063ceef8c | |||
| f61ab26081 | |||
| 79712bd38c | |||
| 66caf30e7e | |||
| 0c855e44fc | |||
| e18b7a92c7 | |||
| 3f1fd102c5 | |||
| ccfaec2fdb | |||
| 8aba59bffd | |||
| 13e0652c59 | |||
| 4a6be92604 | |||
| c576a13a43 | |||
| 63a0464a83 | |||
| b1c6bdb192 | |||
| 74b9bd0bed | |||
| 6977c570e6 | |||
| 4efe60e9f7 | |||
| 336d10663c | |||
| 5f21aaf048 | |||
| e2fd28eece | |||
| a6a1fecae0 | |||
| fdee6b9aac | |||
| 98f93ef2f0 | |||
| df7e2800a7 | |||
| 4aac694364 | |||
| c7a06a4b62 | |||
| 2f21c15172 | |||
| de0ce799c7 | |||
| 3423ad1b15 | |||
| 5db07efe6b | |||
| f5d67a7385 | |||
| 99d4c02b9d | |||
| 182030f404 |
@@ -5,10 +5,11 @@ You are a support assistant for LobeChat authentication migration issues. Your j
|
||||
**IMPORTANT**: The official documentation website is `https://lobehub.com`. When providing documentation links, always use `https://lobehub.com/docs/...` format. Never use `lobechat.com` - that domain is incorrect.
|
||||
|
||||
Examples of correct documentation URLs:
|
||||
- `https://lobehub.com/docs/self-hosting/advanced/auth/nextauth-to-betterauth`
|
||||
- `https://lobehub.com/docs/self-hosting/advanced/auth/clerk-to-betterauth`
|
||||
- `https://lobehub.com/docs/self-hosting/advanced/auth`
|
||||
- `https://lobehub.com/docs/self-hosting/advanced/auth/providers/casdoor`
|
||||
|
||||
- `https://lobehub.com/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth`
|
||||
- `https://lobehub.com/docs/self-hosting/migration/v2/auth/clerk-to-betterauth`
|
||||
- `https://lobehub.com/docs/self-hosting/auth`
|
||||
- `https://lobehub.com/docs/self-hosting/auth/providers/casdoor`
|
||||
|
||||
## Target Issues
|
||||
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../.agents/skills
|
||||
+28
-94
@@ -1,114 +1,48 @@
|
||||
# LobeChat Development Server Configuration
|
||||
# This file contains environment variables for both LobeChat server mode and Docker compose setup
|
||||
# LobeChat Development Environment Configuration
|
||||
# ⚠️ DO NOT USE THESE VALUES IN PRODUCTION!
|
||||
|
||||
COMPOSE_FILE="docker-compose.development.yml"
|
||||
# Application
|
||||
APP_URL=http://localhost:3010
|
||||
|
||||
# ⚠️⚠️⚠️ DO NOT USE THE SECRETS BELOW IN PRODUCTION!
|
||||
UNSAFE_SECRET="ww+0igxjGRAAR/eTNFQ55VmhQB5KE5trFZseuntThJs="
|
||||
UNSAFE_PASSWORD="CHANGE_THIS_PASSWORD_IN_PRODUCTION"
|
||||
# Allow access to private IP addresses (localhost services) in development
|
||||
# https://lobehub.com/docs/self-hosting/environment-variables/basic#ssrf-allow-private-ip-address
|
||||
SSRF_ALLOW_PRIVATE_IP_ADDRESS=1
|
||||
|
||||
# Core Server Configuration
|
||||
# Secrets (pre-generated for development only)
|
||||
KEY_VAULTS_SECRET=ww+0igxjGRAAR/eTNFQ55VmhQB5KE5trFZseuntThJs=
|
||||
AUTH_SECRET=ww+0igxjGRAAR/eTNFQ55VmhQB5KE5trFZseuntThJs=
|
||||
|
||||
# Service Ports Configuration
|
||||
LOBE_PORT=3010
|
||||
|
||||
# Application URL - the base URL where LobeChat will be accessible
|
||||
APP_URL=http://localhost:${LOBE_PORT}
|
||||
|
||||
# Secret key for encrypting vault data (generate with: openssl rand -base64 32)
|
||||
KEY_VAULTS_SECRET=${UNSAFE_SECRET}
|
||||
|
||||
# Database Configuration
|
||||
# Database name for LobeChat
|
||||
LOBE_DB_NAME=lobechat
|
||||
|
||||
# PostgreSQL password
|
||||
POSTGRES_PASSWORD=${UNSAFE_PASSWORD}
|
||||
|
||||
# PostgreSQL database connection URL
|
||||
DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@localhost:5432/${LOBE_DB_NAME}
|
||||
|
||||
# Database driver type
|
||||
# Database (PostgreSQL)
|
||||
DATABASE_URL=postgresql://postgres:change_this_password_on_production@localhost:5432/lobechat
|
||||
DATABASE_DRIVER=node
|
||||
|
||||
# Redis Cache/Queue Configuration
|
||||
# Redis
|
||||
REDIS_URL=redis://localhost:6379
|
||||
REDIS_PREFIX=lobechat
|
||||
REDIS_TLS=0
|
||||
|
||||
# Authentication Configuration
|
||||
# Auth secret for JWT signing (generate with: openssl rand -base64 32)
|
||||
AUTH_SECRET=${UNSAFE_SECRET}
|
||||
|
||||
# SSO providers configuration - using Casdoor for development
|
||||
AUTH_SSO_PROVIDERS=casdoor
|
||||
|
||||
# Casdoor Configuration
|
||||
# Casdoor service port
|
||||
CASDOOR_PORT=8000
|
||||
|
||||
# Casdoor OIDC issuer URL
|
||||
AUTH_CASDOOR_ISSUER=http://localhost:${CASDOOR_PORT}
|
||||
|
||||
# Casdoor application client ID
|
||||
AUTH_CASDOOR_ID=a387a4892ee19b1a2249 # DO NOT USE IN PROD
|
||||
|
||||
# Casdoor application client secret
|
||||
AUTH_CASDOOR_SECRET=dbf205949d704de81b0b5b3603174e23fbecc354 # DO NOT USE IN PROD
|
||||
|
||||
# Origin URL for Casdoor internal configuration
|
||||
origin=http://localhost:${CASDOOR_PORT}
|
||||
|
||||
# MinIO Storage Configuration
|
||||
# MinIO service port
|
||||
MINIO_PORT=9000
|
||||
|
||||
# MinIO root user (admin username)
|
||||
MINIO_ROOT_USER=admin
|
||||
|
||||
# MinIO root password
|
||||
MINIO_ROOT_PASSWORD=${UNSAFE_PASSWORD}
|
||||
|
||||
# MinIO bucket for LobeChat files
|
||||
MINIO_LOBE_BUCKET=lobe
|
||||
|
||||
# S3/MinIO Configuration for LobeChat
|
||||
# S3/MinIO access key ID
|
||||
S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}
|
||||
|
||||
# S3/MinIO secret access key
|
||||
S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
|
||||
|
||||
# S3/MinIO endpoint URL
|
||||
S3_ENDPOINT=http://localhost:${MINIO_PORT}
|
||||
|
||||
# S3 bucket name for storing files
|
||||
S3_BUCKET=${MINIO_LOBE_BUCKET}
|
||||
|
||||
# Enable path-style S3 requests (required for MinIO)
|
||||
# S3 Storage (RustFS)
|
||||
S3_ACCESS_KEY_ID=admin
|
||||
S3_SECRET_ACCESS_KEY=change_this_password_on_production
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
S3_BUCKET=lobe
|
||||
S3_ENABLE_PATH_STYLE=1
|
||||
|
||||
# Disable S3 ACL setting (for MinIO compatibility)
|
||||
S3_SET_ACL=0
|
||||
|
||||
# Use base64 encoding for LLM vision images
|
||||
# LLM vision uses base64 to avoid S3 presigned URL issues in development
|
||||
LLM_VISION_IMAGE_USE_BASE64=1
|
||||
|
||||
# Search Service Configuration
|
||||
# SearXNG search engine URL
|
||||
SEARXNG_URL=http://searxng:8080
|
||||
# Search (SearXNG)
|
||||
SEARXNG_URL=http://localhost:8180
|
||||
|
||||
# Development Options
|
||||
# Uncomment to skip authentication during development
|
||||
|
||||
# Proxy Configuration (Optional)
|
||||
# Uncomment if you need proxy support (e.g., for GitHub auth or API access)
|
||||
# Proxy (Optional)
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
# AI Model Configuration (Optional)
|
||||
# Add your AI model API keys and configurations here
|
||||
# ⚠️ WARNING: Never commit real API keys to version control!
|
||||
# OPENAI_API_KEY=sk-NEVER_USE_REAL_API_KEYS_IN_CONFIG_FILES
|
||||
# AI Model API Keys (Required for chat functionality)
|
||||
# ANTHROPIC_API_KEY=sk-ant-xxx
|
||||
# ANTHROPIC_PROXY_URL=https://api.anthropic.com
|
||||
|
||||
# OPENAI_API_KEY=sk-xxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ config.rules['unicorn/no-array-for-each'] = 0;
|
||||
config.rules['unicorn/prefer-number-properties'] = 0;
|
||||
config.rules['unicorn/prefer-query-selector'] = 0;
|
||||
config.rules['unicorn/no-array-callback-reference'] = 0;
|
||||
config.rules['unicorn/text-encoding-identifier-case'] = 0;
|
||||
config.rules['@typescript-eslint/no-use-before-define'] = 0;
|
||||
// FIXME: Linting error in src/app/[variants]/(main)/chat/features/Migration/DBReader.ts, the fundamental solution should be upgrading typescript-eslint
|
||||
config.rules['@typescript-eslint/no-useless-constructor'] = 0;
|
||||
|
||||
@@ -24,6 +24,17 @@ runs:
|
||||
shell: bash
|
||||
run: pnpm install --node-linker=hoisted
|
||||
|
||||
# 移除国内 electron 镜像配置,GitHub Actions 使用官方源更快
|
||||
- name: Remove China electron mirror from .npmrc
|
||||
shell: bash
|
||||
run: |
|
||||
NPMRC_FILE="./apps/desktop/.npmrc"
|
||||
if [ -f "$NPMRC_FILE" ]; then
|
||||
sed -i.bak '/^electron_mirror=/d; /^electron_builder_binaries_mirror=/d' "$NPMRC_FILE"
|
||||
rm -f "${NPMRC_FILE}.bak"
|
||||
echo "✅ Removed electron mirror config from .npmrc"
|
||||
fi
|
||||
|
||||
- name: Install deps on Desktop
|
||||
shell: bash
|
||||
run: npm run install-isolated --prefix=./apps/desktop
|
||||
|
||||
@@ -70,12 +70,12 @@ jobs:
|
||||
```
|
||||
|
||||
2. Read the latest migration documentation based on the issue:
|
||||
- If issue #11757 (NextAuth): `cat docs/self-hosting/advanced/auth/nextauth-to-betterauth.mdx`
|
||||
- If issue #11707 (Clerk): `cat docs/self-hosting/advanced/auth/clerk-to-betterauth.mdx`
|
||||
- If issue #11757 (NextAuth): `cat docs/self-hosting/migration/v2/auth/nextauth-to-betterauth.mdx`
|
||||
- If issue #11707 (Clerk): `cat docs/self-hosting/migration/v2/auth/clerk-to-betterauth.mdx`
|
||||
|
||||
3. Read additional reference files:
|
||||
- Main auth documentation: `cat docs/self-hosting/advanced/auth.mdx`
|
||||
- Migration internals: `cat docs/self-hosting/advanced/auth/migration-internals.mdx`
|
||||
- Main auth documentation: `cat docs/self-hosting/auth.mdx`
|
||||
- Migration internals: `cat docs/self-hosting/migration/v2/auth/migration-internals.mdx`
|
||||
- Deprecated env vars checker: `cat scripts/_shared/checkDeprecatedAuth.js`
|
||||
|
||||
4. Analyze the user's comment and determine:
|
||||
|
||||
@@ -109,6 +109,17 @@ jobs:
|
||||
- name: Install dependencies
|
||||
run: pnpm install --node-linker=hoisted
|
||||
|
||||
# 移除国内 electron 镜像配置,GitHub Actions 使用官方源更快
|
||||
- name: Remove China electron mirror from .npmrc
|
||||
shell: bash
|
||||
run: |
|
||||
NPMRC_FILE="./apps/desktop/.npmrc"
|
||||
if [ -f "$NPMRC_FILE" ]; then
|
||||
sed -i.bak '/^electron_mirror=/d; /^electron_builder_binaries_mirror=/d' "$NPMRC_FILE"
|
||||
rm -f "${NPMRC_FILE}.bak"
|
||||
echo "✅ Removed electron mirror config from .npmrc"
|
||||
fi
|
||||
|
||||
- name: Install deps on Desktop
|
||||
run: npm run install-isolated --prefix=./apps/desktop
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ concurrency:
|
||||
jobs:
|
||||
release:
|
||||
name: Release
|
||||
if: github.repository == 'lobehub/lobehub'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
name: 🔄 Branch Synchronization
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
sync-branches:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Git
|
||||
run: |
|
||||
git config --global user.name 'GitHub Actions'
|
||||
git config --global user.email 'actions@github.com'
|
||||
|
||||
- name: Prepare sync branch
|
||||
id: branch
|
||||
run: |
|
||||
echo "SYNC_BRANCH_MAIN_DEV=sync/main-to-dev-$(date +'%Y%m%d')" >> $GITHUB_ENV
|
||||
|
||||
- name: Sync main to dev
|
||||
if: github.ref == 'refs/heads/main'
|
||||
run: |
|
||||
# Sync main to dev
|
||||
git checkout main
|
||||
SYNC_BRANCH_DEV=${{ env.SYNC_BRANCH_MAIN_DEV }}
|
||||
git checkout -B $SYNC_BRANCH_DEV
|
||||
DIFF=$(git diff origin/dev...)
|
||||
if [ -z "$DIFF" ]; then
|
||||
echo "No changes to sync"
|
||||
exit 0
|
||||
fi
|
||||
git push origin $SYNC_BRANCH_DEV -f
|
||||
gh pr create --base dev --head $SYNC_BRANCH_DEV --title "Sync main branch to dev branch" --body "Automatic sync" || exit 0
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
@@ -108,6 +108,9 @@ CLAUDE.local.md
|
||||
# MCP tools
|
||||
.serena/**
|
||||
|
||||
# Docker development data
|
||||
docker-compose/dev/data/
|
||||
|
||||
# Migration scripts data
|
||||
scripts/clerk-to-betterauth/test/*.csv
|
||||
scripts/clerk-to-betterauth/test/*.json
|
||||
|
||||
Vendored
+2
@@ -24,6 +24,7 @@
|
||||
// support mdx
|
||||
"mdx"
|
||||
],
|
||||
"mdx.server.enable": false,
|
||||
"npm.packageManager": "pnpm",
|
||||
"search.exclude": {
|
||||
"**/node_modules": true,
|
||||
@@ -51,6 +52,7 @@
|
||||
// make stylelint work with tsx antd-style css template string
|
||||
"typescriptreact"
|
||||
],
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"vitest.maximumConfigs": 20,
|
||||
"workbench.editor.customLabels.patterns": {
|
||||
"**/app/**/[[]*[]]/[[]*[]]/page.tsx": "${dirname(2)}/${dirname(1)}/${dirname} • page component",
|
||||
|
||||
+816
@@ -2,6 +2,822 @@
|
||||
|
||||
# Changelog
|
||||
|
||||
### [Version 2.1.23](https://github.com/lobehub/lobe-chat/compare/v2.1.22...v2.1.23)
|
||||
|
||||
<sup>Released on **2026-02-09**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **swr**: Prevent useActionSWR isValidating from getting stuck.
|
||||
- **misc**: Fix editor content missing when send error, use custom avatar for group chat in sidebar.
|
||||
|
||||
#### 💄 Styles
|
||||
|
||||
- **misc**: Update i18n.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **swr**: Prevent useActionSWR isValidating from getting stuck, closes [#12059](https://github.com/lobehub/lobe-chat/issues/12059) ([8877bc1](https://github.com/lobehub/lobe-chat/commit/8877bc1))
|
||||
- **misc**: Fix editor content missing when send error, closes [#12205](https://github.com/lobehub/lobe-chat/issues/12205) ([ee7ae5b](https://github.com/lobehub/lobe-chat/commit/ee7ae5b))
|
||||
- **misc**: Use custom avatar for group chat in sidebar, closes [#12208](https://github.com/lobehub/lobe-chat/issues/12208) ([31145c9](https://github.com/lobehub/lobe-chat/commit/31145c9))
|
||||
|
||||
#### Styles
|
||||
|
||||
- **misc**: Update i18n, closes [#12025](https://github.com/lobehub/lobe-chat/issues/12025) ([c12d022](https://github.com/lobehub/lobe-chat/commit/c12d022))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.23](https://github.com/lobehub/lobe-chat/compare/v2.1.22...v2.1.23)
|
||||
|
||||
<sup>Released on **2026-02-08**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Fix editor content missing when send error.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Fix editor content missing when send error, closes [#12205](https://github.com/lobehub/lobe-chat/issues/12205) ([ee7ae5b](https://github.com/lobehub/lobe-chat/commit/ee7ae5b))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.23](https://github.com/lobehub/lobe-chat/compare/v2.1.22...v2.1.23)
|
||||
|
||||
<sup>Released on **2026-02-08**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Fix editor content missing when send error.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Fix editor content missing when send error, closes [#12205](https://github.com/lobehub/lobe-chat/issues/12205) ([ee7ae5b](https://github.com/lobehub/lobe-chat/commit/ee7ae5b))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.22](https://github.com/lobehub/lobe-chat/compare/v2.1.21...v2.1.22)
|
||||
|
||||
<sup>Released on **2026-02-08**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Register Notebook tool in server runtime.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Register Notebook tool in server runtime, closes [#12203](https://github.com/lobehub/lobe-chat/issues/12203) ([be6da39](https://github.com/lobehub/lobe-chat/commit/be6da39))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.21](https://github.com/lobehub/lobe-chat/compare/v2.1.20...v2.1.21)
|
||||
|
||||
<sup>Released on **2026-02-08**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Add end-user info on OpenAI Responses API call, enable vertical scrolling for topic list on mobile.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Add end-user info on OpenAI Responses API call, closes [#12134](https://github.com/lobehub/lobe-chat/issues/12134) ([72a85ac](https://github.com/lobehub/lobe-chat/commit/72a85ac))
|
||||
- **misc**: Enable vertical scrolling for topic list on mobile, closes [#12157](https://github.com/lobehub/lobe-chat/issues/12157) [lobehub/lobe-chat#12029](https://github.com/lobehub/lobe-chat/issues/12029) ([bd4e253](https://github.com/lobehub/lobe-chat/commit/bd4e253))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.21](https://github.com/lobehub/lobe-chat/compare/v2.1.20...v2.1.21)
|
||||
|
||||
<sup>Released on **2026-02-08**</sup>
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.21](https://github.com/lobehub/lobe-chat/compare/v2.1.20...v2.1.21)
|
||||
|
||||
<sup>Released on **2026-02-08**</sup>
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.20](https://github.com/lobehub/lobe-chat/compare/v2.1.19...v2.1.20)
|
||||
|
||||
<sup>Released on **2026-02-08**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Add api/version and api/desktop to public routes, show notification when file upload fails due to storage plan limit.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Add api/version and api/desktop to public routes, closes [#12194](https://github.com/lobehub/lobe-chat/issues/12194) ([ea81cd4](https://github.com/lobehub/lobe-chat/commit/ea81cd4))
|
||||
- **misc**: Show notification when file upload fails due to storage plan limit, closes [#12176](https://github.com/lobehub/lobe-chat/issues/12176) ([f26d0df](https://github.com/lobehub/lobe-chat/commit/f26d0df))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.20](https://github.com/lobehub/lobe-chat/compare/v2.1.19...v2.1.20)
|
||||
|
||||
<sup>Released on **2026-02-08**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Add api/version and api/desktop to public routes, show notification when file upload fails due to storage plan limit.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Add api/version and api/desktop to public routes, closes [#12194](https://github.com/lobehub/lobe-chat/issues/12194) ([ea81cd4](https://github.com/lobehub/lobe-chat/commit/ea81cd4))
|
||||
- **misc**: Show notification when file upload fails due to storage plan limit, closes [#12176](https://github.com/lobehub/lobe-chat/issues/12176) ([f26d0df](https://github.com/lobehub/lobe-chat/commit/f26d0df))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.20](https://github.com/lobehub/lobe-chat/compare/v2.1.19...v2.1.20)
|
||||
|
||||
<sup>Released on **2026-02-07**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Show notification when file upload fails due to storage plan limit.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Show notification when file upload fails due to storage plan limit, closes [#12176](https://github.com/lobehub/lobe-chat/issues/12176) ([f26d0df](https://github.com/lobehub/lobe-chat/commit/f26d0df))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.20](https://github.com/lobehub/lobe-chat/compare/v2.1.19...v2.1.20)
|
||||
|
||||
<sup>Released on **2026-02-07**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Show notification when file upload fails due to storage plan limit.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Show notification when file upload fails due to storage plan limit, closes [#12176](https://github.com/lobehub/lobe-chat/issues/12176) ([f26d0df](https://github.com/lobehub/lobe-chat/commit/f26d0df))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.19](https://github.com/lobehub/lobe-chat/compare/v2.1.18...v2.1.19)
|
||||
|
||||
<sup>Released on **2026-02-06**</sup>
|
||||
|
||||
#### ♻ Code Refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment.
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button.
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Fixed in community pluings tab the lobehub skills not display.
|
||||
|
||||
#### 💄 Styles
|
||||
|
||||
- **model-runtime**: Add Claude Opus 4.6 support for Bedrock runtime.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### Code refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment, closes [#12132](https://github.com/lobehub/lobe-chat/issues/12132) ([7ba15cc](https://github.com/lobehub/lobe-chat/commit/7ba15cc))
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button, closes [#12127](https://github.com/lobehub/lobe-chat/issues/12127) ([e402c51](https://github.com/lobehub/lobe-chat/commit/e402c51))
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Fixed in community pluings tab the lobehub skills not display, closes [#12141](https://github.com/lobehub/lobe-chat/issues/12141) ([193c96f](https://github.com/lobehub/lobe-chat/commit/193c96f))
|
||||
|
||||
#### Styles
|
||||
|
||||
- **model-runtime**: Add Claude Opus 4.6 support for Bedrock runtime, closes [#12155](https://github.com/lobehub/lobe-chat/issues/12155) ([90a75af](https://github.com/lobehub/lobe-chat/commit/90a75af))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.19](https://github.com/lobehub/lobe-chat/compare/v2.1.18...v2.1.19)
|
||||
|
||||
<sup>Released on **2026-02-06**</sup>
|
||||
|
||||
#### ♻ Code Refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment.
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button.
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Fixed in community pluings tab the lobehub skills not display.
|
||||
|
||||
#### 💄 Styles
|
||||
|
||||
- **model-runtime**: Add Claude Opus 4.6 support for Bedrock runtime.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### Code refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment, closes [#12132](https://github.com/lobehub/lobe-chat/issues/12132) ([7ba15cc](https://github.com/lobehub/lobe-chat/commit/7ba15cc))
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button, closes [#12127](https://github.com/lobehub/lobe-chat/issues/12127) ([e402c51](https://github.com/lobehub/lobe-chat/commit/e402c51))
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Fixed in community pluings tab the lobehub skills not display, closes [#12141](https://github.com/lobehub/lobe-chat/issues/12141) ([193c96f](https://github.com/lobehub/lobe-chat/commit/193c96f))
|
||||
|
||||
#### Styles
|
||||
|
||||
- **model-runtime**: Add Claude Opus 4.6 support for Bedrock runtime, closes [#12155](https://github.com/lobehub/lobe-chat/issues/12155) ([90a75af](https://github.com/lobehub/lobe-chat/commit/90a75af))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.19](https://github.com/lobehub/lobe-chat/compare/v2.1.18...v2.1.19)
|
||||
|
||||
<sup>Released on **2026-02-06**</sup>
|
||||
|
||||
#### ♻ Code Refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment.
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button.
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Fixed in community pluings tab the lobehub skills not display.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### Code refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment, closes [#12132](https://github.com/lobehub/lobe-chat/issues/12132) ([7ba15cc](https://github.com/lobehub/lobe-chat/commit/7ba15cc))
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button, closes [#12127](https://github.com/lobehub/lobe-chat/issues/12127) ([e402c51](https://github.com/lobehub/lobe-chat/commit/e402c51))
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Fixed in community pluings tab the lobehub skills not display, closes [#12141](https://github.com/lobehub/lobe-chat/issues/12141) ([193c96f](https://github.com/lobehub/lobe-chat/commit/193c96f))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.19](https://github.com/lobehub/lobe-chat/compare/v2.1.18...v2.1.19)
|
||||
|
||||
<sup>Released on **2026-02-06**</sup>
|
||||
|
||||
#### ♻ Code Refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment.
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button.
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Fixed in community pluings tab the lobehub skills not display.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### Code refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment, closes [#12132](https://github.com/lobehub/lobe-chat/issues/12132) ([7ba15cc](https://github.com/lobehub/lobe-chat/commit/7ba15cc))
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button, closes [#12127](https://github.com/lobehub/lobe-chat/issues/12127) ([e402c51](https://github.com/lobehub/lobe-chat/commit/e402c51))
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Fixed in community pluings tab the lobehub skills not display, closes [#12141](https://github.com/lobehub/lobe-chat/issues/12141) ([193c96f](https://github.com/lobehub/lobe-chat/commit/193c96f))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.19](https://github.com/lobehub/lobe-chat/compare/v2.1.18...v2.1.19)
|
||||
|
||||
<sup>Released on **2026-02-06**</sup>
|
||||
|
||||
#### ♻ Code Refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment.
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button.
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Fixed in community pluings tab the lobehub skills not display.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### Code refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment, closes [#12132](https://github.com/lobehub/lobe-chat/issues/12132) ([7ba15cc](https://github.com/lobehub/lobe-chat/commit/7ba15cc))
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button, closes [#12127](https://github.com/lobehub/lobe-chat/issues/12127) ([e402c51](https://github.com/lobehub/lobe-chat/commit/e402c51))
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Fixed in community pluings tab the lobehub skills not display, closes [#12141](https://github.com/lobehub/lobe-chat/issues/12141) ([193c96f](https://github.com/lobehub/lobe-chat/commit/193c96f))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.19](https://github.com/lobehub/lobe-chat/compare/v2.1.18...v2.1.19)
|
||||
|
||||
<sup>Released on **2026-02-06**</sup>
|
||||
|
||||
#### ♻ Code Refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment.
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### Code refactoring
|
||||
|
||||
- **docker-compose**: Restructure dev environment, closes [#12132](https://github.com/lobehub/lobe-chat/issues/12132) ([7ba15cc](https://github.com/lobehub/lobe-chat/commit/7ba15cc))
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button, closes [#12127](https://github.com/lobehub/lobe-chat/issues/12127) ([e402c51](https://github.com/lobehub/lobe-chat/commit/e402c51))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.19](https://github.com/lobehub/lobe-chat/compare/v2.1.18...v2.1.19)
|
||||
|
||||
<sup>Released on **2026-02-05**</sup>
|
||||
|
||||
#### ♻ Code Refactoring
|
||||
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### Code refactoring
|
||||
|
||||
- **misc**: Upgrade agents/group detail pages tabs、hidden like button, closes [#12127](https://github.com/lobehub/lobe-chat/issues/12127) ([e402c51](https://github.com/lobehub/lobe-chat/commit/e402c51))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.18](https://github.com/lobehub/lobe-chat/compare/v2.1.17...v2.1.18)
|
||||
|
||||
<sup>Released on **2026-02-04**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **model-runtime**: Fix moonshot interleaved thinking and circular dependency.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **model-runtime**: Fix moonshot interleaved thinking and circular dependency, closes [#12112](https://github.com/lobehub/lobe-chat/issues/12112) ([3f1a198](https://github.com/lobehub/lobe-chat/commit/3f1a198))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.17](https://github.com/lobehub/lobe-chat/compare/v2.1.16...v2.1.17)
|
||||
|
||||
<sup>Released on **2026-02-04**</sup>
|
||||
|
||||
#### ♻ Code Refactoring
|
||||
|
||||
- **model-runtime**: Extract Anthropic factory and convert Moonshot to RouterRuntime.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### Code refactoring
|
||||
|
||||
- **model-runtime**: Extract Anthropic factory and convert Moonshot to RouterRuntime, closes [#12109](https://github.com/lobehub/lobe-chat/issues/12109) ([71064fd](https://github.com/lobehub/lobe-chat/commit/71064fd))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.16](https://github.com/lobehub/lobe-chat/compare/v2.1.15...v2.1.16)
|
||||
|
||||
<sup>Released on **2026-02-04**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Add the preview publish to market button preview check.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Add the preview publish to market button preview check, closes [#12105](https://github.com/lobehub/lobe-chat/issues/12105) ([28887c7](https://github.com/lobehub/lobe-chat/commit/28887c7))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.15](https://github.com/lobehub/lobe-chat/compare/v2.1.14...v2.1.15)
|
||||
|
||||
<sup>Released on **2026-02-04**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Fixed the agents list the show updateAt time error.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Fixed the agents list the show updateAt time error, closes [#12103](https://github.com/lobehub/lobe-chat/issues/12103) ([3063cee](https://github.com/lobehub/lobe-chat/commit/3063cee))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.14](https://github.com/lobehub/lobe-chat/compare/v2.1.13...v2.1.14)
|
||||
|
||||
<sup>Released on **2026-02-04**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Fix cannot uncompressed messages.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Fix cannot uncompressed messages, closes [#12086](https://github.com/lobehub/lobe-chat/issues/12086) ([ccfaec2](https://github.com/lobehub/lobe-chat/commit/ccfaec2))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.13](https://github.com/lobehub/lobe-chat/compare/v2.1.12...v2.1.13)
|
||||
|
||||
<sup>Released on **2026-02-03**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **docker**: Add librt.so.1 to fix PDF parsing.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **docker**: Add librt.so.1 to fix PDF parsing, closes [#12039](https://github.com/lobehub/lobe-chat/issues/12039) ([4a6be92](https://github.com/lobehub/lobe-chat/commit/4a6be92))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.12](https://github.com/lobehub/lobe-chat/compare/v2.1.11...v2.1.12)
|
||||
|
||||
<sup>Released on **2026-02-03**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **changelog**: Normalize versionRange to valid semver.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **changelog**: Normalize versionRange to valid semver, closes [#12049](https://github.com/lobehub/lobe-chat/issues/12049) ([74b9bd0](https://github.com/lobehub/lobe-chat/commit/74b9bd0))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.11](https://github.com/lobehub/lobe-chat/compare/v2.1.10...v2.1.11)
|
||||
|
||||
<sup>Released on **2026-02-02**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Hide password features when AUTH_DISABLE_EMAIL_PASSWORD is set.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Hide password features when AUTH_DISABLE_EMAIL_PASSWORD is set, closes [#12023](https://github.com/lobehub/lobe-chat/issues/12023) ([e2fd28e](https://github.com/lobehub/lobe-chat/commit/e2fd28e))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.10](https://github.com/lobehub/lobe-chat/compare/v2.1.9...v2.1.10)
|
||||
|
||||
<sup>Released on **2026-02-02**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **auth**: Revert authority URL and tenant ID for Microsoft authentication..
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **auth**: Revert authority URL and tenant ID for Microsoft authentication., closes [#11930](https://github.com/lobehub/lobe-chat/issues/11930) ([98f93ef](https://github.com/lobehub/lobe-chat/commit/98f93ef))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.9](https://github.com/lobehub/lobe-chat/compare/v2.1.8...v2.1.9)
|
||||
|
||||
<sup>Released on **2026-02-02**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Use oauth2.link for generic OIDC provider account linking.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Use oauth2.link for generic OIDC provider account linking, closes [#12024](https://github.com/lobehub/lobe-chat/issues/12024) ([c7a06a4](https://github.com/lobehub/lobe-chat/commit/c7a06a4))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.8](https://github.com/lobehub/lobe-chat/compare/v2.1.7...v2.1.8)
|
||||
|
||||
<sup>Released on **2026-02-01**</sup>
|
||||
|
||||
#### 💄 Styles
|
||||
|
||||
- **misc**: Improve tasks display.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### Styles
|
||||
|
||||
- **misc**: Improve tasks display, closes [#12032](https://github.com/lobehub/lobe-chat/issues/12032) ([3423ad1](https://github.com/lobehub/lobe-chat/commit/3423ad1))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.7](https://github.com/lobehub/lobe-chat/compare/v2.1.6...v2.1.7)
|
||||
|
||||
<sup>Released on **2026-02-01**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Add missing description parameter docs in Notebook system prompt.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Add missing description parameter docs in Notebook system prompt, closes [#12015](https://github.com/lobehub/lobe-chat/issues/12015) [#11391](https://github.com/lobehub/lobe-chat/issues/11391) ([182030f](https://github.com/lobehub/lobe-chat/commit/182030f))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
### [Version 2.1.6](https://github.com/lobehub/lobe-chat/compare/v2.1.5...v2.1.6)
|
||||
|
||||
<sup>Released on **2026-02-01**</sup>
|
||||
|
||||
+4
-1
@@ -21,6 +21,7 @@ RUN set -e && \
|
||||
cp /etc/proxychains4.conf /distroless/etc/proxychains4.conf && \
|
||||
cp /usr/lib/$(arch)-linux-gnu/libstdc++.so.6 /distroless/lib/libstdc++.so.6 && \
|
||||
cp /usr/lib/$(arch)-linux-gnu/libgcc_s.so.1 /distroless/lib/libgcc_s.so.1 && \
|
||||
cp /usr/lib/$(arch)-linux-gnu/librt.so.1 /distroless/lib/librt.so.1 && \
|
||||
cp /usr/local/bin/node /distroless/bin/node && \
|
||||
cp /etc/ssl/certs/ca-certificates.crt /distroless/etc/ssl/certs/ca-certificates.crt && \
|
||||
rm -rf /tmp/* /var/lib/apt/lists/* /var/tmp/*
|
||||
@@ -186,7 +187,9 @@ ENV AUTH_SECRET="" \
|
||||
AUTH_GITHUB_SECRET="" \
|
||||
# Microsoft
|
||||
AUTH_MICROSOFT_ID="" \
|
||||
AUTH_MICROSOFT_SECRET=""
|
||||
AUTH_MICROSOFT_SECRET="" \
|
||||
AUTH_MICROSOFT_AUTHORITY_URL="" \
|
||||
AUTH_MICROSOFT_TENANT_ID=""
|
||||
|
||||
# Redis
|
||||
ENV REDIS_URL="" \
|
||||
|
||||
@@ -16,15 +16,26 @@ import { ControllerModule, IpcMethod } from './index';
|
||||
const logger = createLogger('controllers:ShellCommandCtr');
|
||||
|
||||
// Maximum output length to prevent context explosion
|
||||
const MAX_OUTPUT_LENGTH = 10_000;
|
||||
const MAX_OUTPUT_LENGTH = 80_000;
|
||||
|
||||
/**
|
||||
* Strip ANSI escape codes from terminal output
|
||||
*/
|
||||
// eslint-disable-next-line no-control-regex
|
||||
const ANSI_REGEX = /\u001B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g;
|
||||
const stripAnsi = (str: string): string => str.replaceAll(ANSI_REGEX, '');
|
||||
|
||||
/**
|
||||
* Truncate string to max length with ellipsis indicator
|
||||
*/
|
||||
const truncateOutput = (str: string, maxLength: number = MAX_OUTPUT_LENGTH): string => {
|
||||
if (str.length <= maxLength) return str;
|
||||
const cleaned = stripAnsi(str);
|
||||
if (cleaned.length <= maxLength) return cleaned;
|
||||
return (
|
||||
str.slice(0, maxLength) + '\n... [truncated, ' + (str.length - maxLength) + ' more characters]'
|
||||
cleaned.slice(0, maxLength) +
|
||||
'\n... [truncated, ' +
|
||||
(cleaned.length - maxLength) +
|
||||
' more characters]'
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -193,6 +193,62 @@ describe('ShellCommandCtr', () => {
|
||||
expect(result.stderr).toBe('error message\n');
|
||||
});
|
||||
|
||||
it('should strip ANSI escape codes from output', async () => {
|
||||
let exitCallback: (code: number) => void;
|
||||
let stdoutCallback: (data: Buffer) => void;
|
||||
let stderrCallback: (data: Buffer) => void;
|
||||
|
||||
mockChildProcess.on.mockImplementation((event: string, callback: any) => {
|
||||
if (event === 'exit') {
|
||||
exitCallback = callback;
|
||||
setTimeout(() => exitCallback(0), 10);
|
||||
}
|
||||
return mockChildProcess;
|
||||
});
|
||||
|
||||
mockChildProcess.stdout.on.mockImplementation((event: string, callback: any) => {
|
||||
if (event === 'data') {
|
||||
stdoutCallback = callback;
|
||||
// Simulate output with ANSI color codes
|
||||
setTimeout(
|
||||
() =>
|
||||
stdoutCallback(
|
||||
Buffer.from(
|
||||
'\x1b[38;5;250m███████╗\x1b[0m\n\x1b[1;32mSuccess\x1b[0m\n\x1b[31mError\x1b[0m',
|
||||
),
|
||||
),
|
||||
5,
|
||||
);
|
||||
}
|
||||
return mockChildProcess.stdout;
|
||||
});
|
||||
|
||||
mockChildProcess.stderr.on.mockImplementation((event: string, callback: any) => {
|
||||
if (event === 'data') {
|
||||
stderrCallback = callback;
|
||||
setTimeout(
|
||||
() => stderrCallback(Buffer.from('\x1b[33mwarning:\x1b[0m something happened')),
|
||||
5,
|
||||
);
|
||||
}
|
||||
return mockChildProcess.stderr;
|
||||
});
|
||||
|
||||
const result = await shellCommandCtr.handleRunCommand({
|
||||
command: 'npx skills find react',
|
||||
description: 'search skills',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
// ANSI codes should be stripped
|
||||
expect(result.stdout).not.toContain('\x1b[');
|
||||
expect(result.stdout).toContain('███████╗');
|
||||
expect(result.stdout).toContain('Success');
|
||||
expect(result.stdout).toContain('Error');
|
||||
expect(result.stderr).not.toContain('\x1b[');
|
||||
expect(result.stderr).toContain('warning: something happened');
|
||||
});
|
||||
|
||||
it('should truncate long output to prevent context explosion', async () => {
|
||||
let exitCallback: (code: number) => void;
|
||||
let stdoutCallback: (data: Buffer) => void;
|
||||
@@ -208,8 +264,8 @@ describe('ShellCommandCtr', () => {
|
||||
mockChildProcess.stdout.on.mockImplementation((event: string, callback: any) => {
|
||||
if (event === 'data') {
|
||||
stdoutCallback = callback;
|
||||
// Simulate very long output (15k characters)
|
||||
const longOutput = 'x'.repeat(15_000);
|
||||
// Simulate very long output (100k characters, exceeding 80k MAX_OUTPUT_LENGTH)
|
||||
const longOutput = 'x'.repeat(100_000);
|
||||
setTimeout(() => stdoutCallback(Buffer.from(longOutput)), 5);
|
||||
}
|
||||
return mockChildProcess.stdout;
|
||||
@@ -223,8 +279,8 @@ describe('ShellCommandCtr', () => {
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
// Output should be truncated to ~10k + truncation message
|
||||
expect(result.stdout!.length).toBeLessThan(15_000);
|
||||
// Output should be truncated to 80k + truncation message
|
||||
expect(result.stdout!.length).toBeLessThan(100_000);
|
||||
expect(result.stdout).toContain('truncated');
|
||||
expect(result.stdout).toContain('more characters');
|
||||
});
|
||||
|
||||
@@ -45,7 +45,7 @@ export class LinuxMenu extends BaseMenuPlatform implements IMenuPlatform {
|
||||
this.buildAndSetAppMenu(options);
|
||||
}
|
||||
|
||||
// --- 私有方法:定义菜单模板和逻辑 ---
|
||||
// --- Private methods: define menu templates and logic ---
|
||||
|
||||
private getAppMenuTemplate(options?: MenuOptions): MenuItemConstructorOptions[] {
|
||||
const showDev = isDev || options?.showDevItems;
|
||||
|
||||
@@ -48,23 +48,23 @@ export class MacOSMenu extends BaseMenuPlatform implements IMenuPlatform {
|
||||
}
|
||||
|
||||
refresh(options?: MenuOptions): void {
|
||||
// 重建Application menu
|
||||
// Rebuild Application menu
|
||||
this.buildAndSetAppMenu(options);
|
||||
// 如果托盘菜单存在,也重建它(如果需要动态更新)
|
||||
// If tray menu exists, rebuild it as well (if dynamic update is needed)
|
||||
// this.trayMenu = this.buildTrayMenu();
|
||||
// 需要考虑如何更新现有托盘图标的菜单
|
||||
// Need to consider how to update the menu for existing tray icons
|
||||
}
|
||||
|
||||
// --- 私有方法:定义菜单模板和逻辑 ---
|
||||
// --- Private methods: define menu templates and logic ---
|
||||
|
||||
private getAppMenuTemplate(options?: MenuOptions): MenuItemConstructorOptions[] {
|
||||
const appName = app.getName();
|
||||
const showDev = isDev || options?.showDevItems;
|
||||
// 创建命名空间翻译函数
|
||||
// Create namespaced translation function
|
||||
const t = this.app.i18n.ns('menu');
|
||||
|
||||
// 添加调试日志
|
||||
// console.log('[MacOSMenu] 菜单渲染, i18n实例:', !!this.app.i18n);
|
||||
// Add debug logging
|
||||
// console.log('[MacOSMenu] Menu rendering, i18n instance:', !!this.app.i18n);
|
||||
|
||||
const template: MenuItemConstructorOptions[] = [
|
||||
{
|
||||
@@ -324,7 +324,7 @@ export class MacOSMenu extends BaseMenuPlatform implements IMenuPlatform {
|
||||
},
|
||||
{
|
||||
click: () => {
|
||||
// @ts-expect-error cache 目录好像暂时不在类型定义里
|
||||
// @ts-expect-error cache directory seems to be temporarily missing from type definitions
|
||||
const cachePath = app.getPath('cache');
|
||||
|
||||
const updaterCachePath = path.join(cachePath, `${app.getName()}-updater`);
|
||||
|
||||
@@ -43,7 +43,7 @@ export class WindowsMenu extends BaseMenuPlatform implements IMenuPlatform {
|
||||
|
||||
refresh(options?: MenuOptions): void {
|
||||
this.buildAndSetAppMenu(options);
|
||||
// 如果有必要更新托盘菜单,可以在这里添加逻辑
|
||||
// If it's necessary to update tray menu, logic can be added here
|
||||
}
|
||||
|
||||
private getAppMenuTemplate(options?: MenuOptions): MenuItemConstructorOptions[] {
|
||||
|
||||
+31289
-10371
File diff suppressed because it is too large
Load Diff
+1917
-639
File diff suppressed because it is too large
Load Diff
+3472
-3796
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,110 @@
|
||||
[
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Register Notebook tool in server runtime."]
|
||||
},
|
||||
"date": "2026-02-08",
|
||||
"version": "2.1.22"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": [
|
||||
"Add end-user info on OpenAI Responses API call, enable vertical scrolling for topic list on mobile."
|
||||
]
|
||||
},
|
||||
"date": "2026-02-08",
|
||||
"version": "2.1.21"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": [
|
||||
"Add api/version and api/desktop to public routes, show notification when file upload fails due to storage plan limit."
|
||||
]
|
||||
},
|
||||
"date": "2026-02-08",
|
||||
"version": "2.1.20"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fixed in community pluings tab the lobehub skills not display."]
|
||||
},
|
||||
"date": "2026-02-06",
|
||||
"version": "2.1.19"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2026-02-04",
|
||||
"version": "2.1.18"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2026-02-04",
|
||||
"version": "2.1.17"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Add the preview publish to market button preview check."]
|
||||
},
|
||||
"date": "2026-02-04",
|
||||
"version": "2.1.16"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fixed the agents list the show updateAt time error."]
|
||||
},
|
||||
"date": "2026-02-04",
|
||||
"version": "2.1.15"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Fix cannot uncompressed messages."]
|
||||
},
|
||||
"date": "2026-02-04",
|
||||
"version": "2.1.14"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2026-02-03",
|
||||
"version": "2.1.13"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2026-02-03",
|
||||
"version": "2.1.12"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Hide password features when AUTH_DISABLE_EMAIL_PASSWORD is set."]
|
||||
},
|
||||
"date": "2026-02-02",
|
||||
"version": "2.1.11"
|
||||
},
|
||||
{
|
||||
"children": {},
|
||||
"date": "2026-02-02",
|
||||
"version": "2.1.10"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Use oauth2.link for generic OIDC provider account linking."]
|
||||
},
|
||||
"date": "2026-02-02",
|
||||
"version": "2.1.9"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Improve tasks display."]
|
||||
},
|
||||
"date": "2026-02-01",
|
||||
"version": "2.1.8"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"fixes": ["Add missing description parameter docs in Notebook system prompt."]
|
||||
},
|
||||
"date": "2026-02-01",
|
||||
"version": "2.1.7"
|
||||
},
|
||||
{
|
||||
"children": {
|
||||
"improvements": ["Improve local-system tool implement."]
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
## chore(i18n): Adjust Latin language locales for terminology consistency
|
||||
|
||||
This comprehensive update ensures all Latin language locales (de-DE, fr-FR, es-ES, it-IT, pt-BR, nl-NL, pl-PL) follow the microcopy style guide's terminology requirements.
|
||||
|
||||
### Changes Made
|
||||
|
||||
**Total: 557 changes across 7 Latin locales**
|
||||
|
||||
#### Primary Terminology Updates
|
||||
1. **"Plugin" → "Skill"**
|
||||
- Fixed terminology inconsistency across all Latin languages
|
||||
- UI elements now consistently use "Skill" instead of localized equivalents
|
||||
- Includes both singular and plural forms: `plugin/Skill`, `plugins/Skills`
|
||||
|
||||
2. **"LobeChat" → "LobeHub"**
|
||||
- Updated brand name references to current branding
|
||||
|
||||
3. **"Agent" Terminology Consistency**
|
||||
- French: Fixed inconsistent "Assistant" → "Agent" usage in UI elements
|
||||
- Ensured consistent terminology across all languages
|
||||
|
||||
#### Per-Locale Breakdown
|
||||
- **de-DE (German)**: 267 changes
|
||||
- **fr-FR (French)**: 94 changes (including 7 Agent→Assistant fixes)
|
||||
- **es-ES (Spanish)**: 39 changes
|
||||
- **it-IT (Italian)**: 59 changes (including 18 plugin→skill fixes)
|
||||
- **pt-BR (Portuguese)**: 58 changes
|
||||
- **nl-NL (Dutch)**: 62 changes
|
||||
- **pl-PL (Polish)**: 28 changes
|
||||
|
||||
#### Files Modified
|
||||
- All 37 locale JSON files for each language (259 total files)
|
||||
- Includes: auth.json, chat.json, common.json, discover.json, plugin.json, setting.json, etc.
|
||||
|
||||
#### Key Improvements
|
||||
1. **Fixed Terminology**: Following microcopy guide's fixed terminology rules
|
||||
2. **Brand Consistency**: Changed all brand references to "LobeHub"
|
||||
3. **Natural Localization**: Maintained natural language patterns while ensuring consistency
|
||||
4. **User Experience**: Improved consistency across all Latin language interfaces
|
||||
|
||||
### Scripts Created
|
||||
Two utility scripts for future locale maintenance:
|
||||
- `scripts/adjust-latin-locales.py` - For common.json specific adjustments
|
||||
- `scripts/adjust-latin-locales-full.py` - For comprehensive adjustments across all files
|
||||
|
||||
### Review Notes
|
||||
- All changes maintain backward compatibility
|
||||
- No breaking changes to functionality
|
||||
- JSON files validated and remain syntactically correct
|
||||
- Changes reviewed against English base for consistency
|
||||
@@ -1,90 +0,0 @@
|
||||
name: lobe-chat-development
|
||||
services:
|
||||
network-service:
|
||||
image: alpine
|
||||
container_name: lobe-network
|
||||
restart: always
|
||||
ports:
|
||||
- '${MINIO_PORT}:${MINIO_PORT}' # MinIO API
|
||||
- '9001:9001' # MinIO Console
|
||||
- '${CASDOOR_PORT}:${CASDOOR_PORT}' # Casdoor
|
||||
- '3000:3000' # Grafana
|
||||
- '4318:4318' # otel-collector HTTP
|
||||
- '4317:4317' # otel-collector gRPC
|
||||
command: tail -f /dev/null
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
postgresql:
|
||||
extends:
|
||||
file: docker-compose/local/docker-compose.yml
|
||||
service: postgresql
|
||||
redis:
|
||||
extends:
|
||||
file: docker-compose/local/docker-compose.yml
|
||||
service: redis
|
||||
minio:
|
||||
extends:
|
||||
file: docker-compose/local/docker-compose.yml
|
||||
service: minio
|
||||
casdoor:
|
||||
extends:
|
||||
file: docker-compose/local/docker-compose.yml
|
||||
service: casdoor
|
||||
searxng:
|
||||
extends:
|
||||
file: docker-compose/local/docker-compose.yml
|
||||
service: searxng
|
||||
|
||||
grafana:
|
||||
profiles:
|
||||
- otel
|
||||
extends:
|
||||
file: docker-compose/local/grafana/docker-compose.yml
|
||||
service: grafana
|
||||
|
||||
tempo:
|
||||
profiles:
|
||||
- otel
|
||||
extends:
|
||||
file: docker-compose/local/grafana/docker-compose.yml
|
||||
service: tempo
|
||||
|
||||
prometheus:
|
||||
profiles:
|
||||
- otel
|
||||
extends:
|
||||
file: docker-compose/local/grafana/docker-compose.yml
|
||||
service: prometheus
|
||||
|
||||
otel-collector:
|
||||
profiles:
|
||||
- otel
|
||||
extends:
|
||||
file: docker-compose/local/grafana/docker-compose.yml
|
||||
service: otel-collector
|
||||
|
||||
otel-tracing-test:
|
||||
profiles:
|
||||
- otel-test
|
||||
extends:
|
||||
file: docker-compose/local/grafana/docker-compose.yml
|
||||
service: otel-tracing-test
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
s3_data:
|
||||
driver: local
|
||||
grafana_data:
|
||||
driver: local
|
||||
tempo_data:
|
||||
driver: local
|
||||
prometheus_data:
|
||||
driver: local
|
||||
redis_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
lobe-network:
|
||||
driver: bridge
|
||||
@@ -22,6 +22,10 @@ APP_URL=http://localhost:3210
|
||||
# to bypass CDN/proxy. If not set, defaults to APP_URL.
|
||||
# Example: INTERNAL_APP_URL=http://localhost:3210
|
||||
|
||||
# Secrets (auto-generated by setup.sh)
|
||||
KEY_VAULTS_SECRET=YOUR_KEY_VAULTS_SECRET
|
||||
AUTH_SECRET=YOUR_AUTH_SECRET
|
||||
|
||||
# Postgres related, which are the necessary environment variables for DB
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
|
||||
@@ -19,8 +19,12 @@ LOBE_PORT=3210
|
||||
RUSTFS_PORT=9000
|
||||
APP_URL=http://localhost:3210
|
||||
|
||||
# 密钥配置(由 setup.sh 自动生成)
|
||||
KEY_VAULTS_SECRET=YOUR_KEY_VAULTS_SECRET
|
||||
AUTH_SECRET=YOUR_AUTH_SECRET
|
||||
|
||||
# Postgres 相关,也即 DB 必须的环境变量
|
||||
LOBE_DB_NAME=lobehub
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
|
||||
# RustFS S3 配置
|
||||
|
||||
@@ -117,8 +117,8 @@ services:
|
||||
redis:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- 'KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ='
|
||||
- 'AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg'
|
||||
- 'KEY_VAULTS_SECRET=${KEY_VAULTS_SECRET}'
|
||||
- 'AUTH_SECRET=${AUTH_SECRET}'
|
||||
- 'DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgresql:5432/${LOBE_DB_NAME}'
|
||||
- 'S3_BUCKET=${RUSTFS_LOBE_BUCKET}'
|
||||
- 'S3_ENABLE_PATH_STYLE=1'
|
||||
|
||||
@@ -1,17 +1,3 @@
|
||||
# Logto secret
|
||||
AUTH_LOGTO_ID=
|
||||
AUTH_LOGTO_SECRET=
|
||||
AUTH_LOGTO_ISSUER=
|
||||
|
||||
# MinIO S3 configuration
|
||||
MINIO_ROOT_USER=YOUR_MINIO_USER
|
||||
MINIO_ROOT_PASSWORD=YOUR_MINIO_PASSWORD
|
||||
|
||||
# Configure the bucket information of MinIO
|
||||
MINIO_LOBE_BUCKET=lobe
|
||||
S3_ACCESS_KEY_ID=
|
||||
S3_SECRET_ACCESS_KEY=
|
||||
|
||||
# Proxy, if you need it
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
@@ -23,12 +9,21 @@ S3_SECRET_ACCESS_KEY=
|
||||
# OPENAI_MODEL_LIST=...
|
||||
|
||||
|
||||
# ----- Other config -----
|
||||
# ===========================
|
||||
# ====== Preset config ======
|
||||
# ===========================
|
||||
# if no special requirements, no need to change
|
||||
LOBE_PORT=3210
|
||||
LOGTO_PORT=3001
|
||||
MINIO_PORT=9000
|
||||
RUSTFS_PORT=9000
|
||||
|
||||
# Postgres related, which are the necessary environment variables for DB
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
POSTGRES_PASSWORD=change_this_password_on_production
|
||||
|
||||
# RUSTFS S3 configuration
|
||||
RUSTFS_ACCESS_KEY=admin
|
||||
RUSTFS_SECRET_KEY=change_this_password_on_production
|
||||
|
||||
# Configure the bucket information of RUSTFS
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
RUSTFS_LOBE_BUCKET=lobe
|
||||
+14
-19
@@ -1,34 +1,29 @@
|
||||
# Logto 鉴权相关
|
||||
AUTH_LOGTO_ID=
|
||||
AUTH_LOGTO_SECRET=
|
||||
AUTH_LOGTO_ISSUER=
|
||||
|
||||
# MinIO S3 配置
|
||||
MINIO_ROOT_USER=YOUR_MINIO_USER
|
||||
MINIO_ROOT_PASSWORD=YOUR_MINIO_PASSWORD
|
||||
|
||||
# 在下方配置 minio 中添加的桶
|
||||
MINIO_LOBE_BUCKET=lobe
|
||||
S3_ACCESS_KEY_ID=
|
||||
S3_SECRET_ACCESS_KEY=
|
||||
|
||||
|
||||
# Proxy,如果你需要的话(比如你使用 GitHub 作为鉴权服务提供商)
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
|
||||
# 其他环境变量,视需求而定,可以参照客户端版本的环境变量配置,注意不要有 ACCESS_CODE
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
|
||||
|
||||
# ----- 相关配置 start -----
|
||||
# ===================
|
||||
# ===== 预设配置 =====
|
||||
# ===================
|
||||
# 如没有特殊需要不用更改
|
||||
LOBE_PORT=3210
|
||||
LOGTO_PORT=3001
|
||||
MINIO_PORT=9000
|
||||
RUSTFS_PORT=9000
|
||||
|
||||
# Postgres 相关,也即 DB 必须的环境变量
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
POSTGRES_PASSWORD=change_this_password_on_production
|
||||
|
||||
# RustFS S3 配置
|
||||
RUSTFS_ACCESS_KEY=admin
|
||||
RUSTFS_SECRET_KEY=change_this_password_on_production
|
||||
|
||||
# RustFS bucket 配置
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
RUSTFS_LOBE_BUCKET=lobe
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"ID": "",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"AWS": ["*"]
|
||||
},
|
||||
"Action": ["s3:GetObject"],
|
||||
"NotAction": [],
|
||||
"Resource": ["arn:aws:s3:::lobe/*"],
|
||||
"NotResource": [],
|
||||
"Condition": {}
|
||||
}
|
||||
],
|
||||
"Version": "2012-10-17"
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
name: lobehub
|
||||
services:
|
||||
network-service:
|
||||
image: alpine
|
||||
container_name: lobe-network
|
||||
restart: always
|
||||
ports:
|
||||
- '${RUSTFS_PORT}:9000' # RustFS API
|
||||
- '9001:9001' # RustFS Console
|
||||
- '${LOBE_PORT}:3210' # LobeChat
|
||||
command: tail -f /dev/null
|
||||
networks:
|
||||
- lobe-network
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
postgresql:
|
||||
image: pgvector/pgvector:pg17
|
||||
container_name: lobe-postgres
|
||||
ports:
|
||||
- '5432:5432'
|
||||
volumes:
|
||||
- './data:/var/lib/postgresql/data'
|
||||
environment:
|
||||
- 'POSTGRES_DB=${LOBE_DB_NAME}'
|
||||
- 'POSTGRES_PASSWORD=${POSTGRES_PASSWORD}'
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: lobe-redis
|
||||
ports:
|
||||
- '6379:6379'
|
||||
command: redis-server --save 60 1000 --appendonly yes
|
||||
volumes:
|
||||
- 'redis_data:/data'
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', 'ping']
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
rustfs:
|
||||
image: rustfs/rustfs:latest
|
||||
container_name: lobe-rustfs
|
||||
network_mode: 'service:network-service'
|
||||
environment:
|
||||
- RUSTFS_CONSOLE_ENABLE=true
|
||||
- RUSTFS_ACCESS_KEY=${RUSTFS_ACCESS_KEY}
|
||||
- RUSTFS_SECRET_KEY=${RUSTFS_SECRET_KEY}
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:9000/health >/dev/null 2>&1 || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 30
|
||||
command: ["--access-key","${RUSTFS_ACCESS_KEY}","--secret-key","${RUSTFS_SECRET_KEY}","/data"]
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
rustfs-init:
|
||||
image: minio/mc:latest
|
||||
container_name: lobe-rustfs-init
|
||||
depends_on:
|
||||
rustfs:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./bucket.config.json:/bucket.config.json:ro
|
||||
entrypoint: /bin/sh
|
||||
command: -c '
|
||||
set -eux;
|
||||
echo "S3_ACCESS_KEY=${RUSTFS_ACCESS_KEY}, S3_SECRET_KEY=${RUSTFS_SECRET_KEY}";
|
||||
mc --version;
|
||||
mc alias set rustfs "http://network-service:9000" "${RUSTFS_ACCESS_KEY}" "${RUSTFS_SECRET_KEY}";
|
||||
mc ls rustfs || true;
|
||||
mc mb "rustfs/lobe" --ignore-existing;
|
||||
mc admin info rustfs || true;
|
||||
mc anonymous set-json "/bucket.config.json" "rustfs/lobe";
|
||||
'
|
||||
restart: "no"
|
||||
networks:
|
||||
- lobe-network
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
searxng:
|
||||
image: searxng/searxng
|
||||
container_name: lobe-searxng
|
||||
ports:
|
||||
- '8180:8080'
|
||||
volumes:
|
||||
- './searxng-settings.yml:/etc/searxng/settings.yml'
|
||||
environment:
|
||||
- 'SEARXNG_SETTINGS_FILE=/etc/searxng/settings.yml'
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
redis_data:
|
||||
driver: local
|
||||
rustfs-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
lobe-network:
|
||||
driver: bridge
|
||||
@@ -1,46 +0,0 @@
|
||||
# Proxy, if you need it
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
|
||||
# Other environment variables, as needed. You can refer to the environment variables configuration for the client version, making sure not to have ACCESS_CODE.
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
|
||||
|
||||
# ===========================
|
||||
# ====== Preset config ======
|
||||
# ===========================
|
||||
# if no special requirements, no need to change
|
||||
LOBE_PORT=3210
|
||||
CASDOOR_PORT=8000
|
||||
RUSTFS_PORT=9000
|
||||
APP_URL=http://localhost:3210
|
||||
# INTERNAL_APP_URL is optional, used for server-to-server calls
|
||||
# to bypass CDN/proxy. If not set, defaults to APP_URL.
|
||||
# Example: INTERNAL_APP_URL=http://localhost:3210
|
||||
|
||||
# Postgres related, which are the necessary environment variables for DB
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
|
||||
AUTH_SSO_PROVIDERS=casdoor
|
||||
AUTH_CASDOOR_ISSUER=http://localhost:8000
|
||||
# Casdoor secret
|
||||
AUTH_CASDOOR_ID=a387a4892ee19b1a2249
|
||||
AUTH_CASDOOR_SECRET=dbf205949d704de81b0b5b3603174e23fbecc354
|
||||
CASDOOR_WEBHOOK_SECRET=casdoor-secret
|
||||
|
||||
# RUSTFS S3 configuration
|
||||
RUSTFS_ACCESS_KEY=admin
|
||||
RUSTFS_SECRET_KEY=YOUR_RUSTFS_PASSWORD
|
||||
|
||||
# Configure the bucket information of RUSTFS
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
RUSTFS_LOBE_BUCKET=lobe
|
||||
|
||||
# Configure for casdoor
|
||||
origin=http://localhost:8000
|
||||
|
||||
JWKS_KEY={"keys":[{"d":"PVoFyqyrGstB8wU52S7gqqQQdZLtin_thcEM0nrNtqp9U-NlKLlhgEcWp5t89ycgvhsAzmrRbezGj4JBTr3jn7eWdwQpPJNYiipnsgeJn0pwsB0H2dMqtavxinoPVXkMTOuGHMTFhhyguFBw2JbIL0PTQUcUlXjv40OoJpYHZeggSxgfV-TuxjwW8Ll4-n84M5IOi6A53RvioE-Hm1iyIc2XLBCfyOu-SbAQYi8HzrA64kCxobAB0peLQMiAzfZmwPKiGOhnhKrAlYmG02qFnbUYiJu_-AXwsAyGv9S9i6dwK7QXaGGWYyis8LlPpd_JmPrBnrWomwDlI045NUMWZQ","dp":"OSXI2NBBZl2r0Dpf4-1z44A_jC5lOyXtJhXQYnSXy5eIuxTJcEtkUYagGEwnREO4Q3t-4J-lT_6Y71M1ZlgKG1upwfw1O4aE3vGpHOik9iZYYCjA8fe5uBfOpX1ELmOtHNoHRhMtyjuPxSFXLlSp3bgcF1f3F40ClukdvXCx0Mc","dq":"m6hNdfj-F8E_7nUlX2nG95OffkFrhHTo67ML9aPgpvFwBlzg-hk5LwtxMfUzngqWF78TMl0JDm7vS1bz0xlWqXqu8pFPoTUnUoWgYfvuyHLBwR5TgccQkfoKbkSMzYNy8VJPXZeyIjVXsW98tZvj-NZF-M9Pke_EWJm-jjXCu_8","e":"AQAB","kty":"RSA","n":"piffosMS0HOSgsSr_zQkXYaQt1kOCD73VR0b2XJD6UdQCKPbnBOzTIuA_xowX61QVsl5pCZLTw8ERC3r2Nlxj5Rp_H6RuOT7ioUqlbnxSGnfuAn8dFupY3A-sf9HVDOvtJdlS-nO9yA4wWU-A50zZ1Mf0pPZlUZE6dUQfsJFi5yXaNAybyk3U4VpMO_SXAilWEHVhiO0F0ccpJMCkT47AeXmYH9MlWwIGcay0UiAsdrs8J-q1arZ7Mbq0oxHmUXJG0vwRvAL8KnCEi8cJ3e2kKCRcr-BQCujsHUyUl6f_ATwSVuTHdAR1IzIcW37v27h3WQK_v0ffQM1NstamDX5vQ","p":"4myVm2M5cZGvVXsOmWUTUG87VC1GlQcL5tmMNSGSpQCL8yWZ1vANkmCxSMptrKB4dU9DAB3On6_oMhW1pJ3uYNGSW49BcmJoLkiWKeg5zWFnKPQNuThQmY1sCCubtKhBQgaYUr7TVzN9smrDV3zCu9MlRl-XPwnEmWaDII3g-f8","q":"u9v4IOEsb4l2Y3eWKE2bwJh5fJRR4vivaYA7U-1-OpvDwB3A48Rey9IL1ucXqE5G1Du8BtijPm5oSAar5uzrjtg1bZ9gevif6DnBGaIRE7LnSrUsTPfZwzntJ1rTaGiVe_pAdnTKXXaH6DxygXxH4wvGgA44V3TTfBXQUcjzdEM","qi":"lDBnSPKkRnYqQvbqVD1LxzqBPEeqEA3GyCqMj6fIZNgoEaBSLi0TSsUyGZ5mahX3KO35vKAZa5jvGjhvUGUiXycq8KvRZdeGK45vJdwZT2TiXiDwo9IQgJcbFMpxaB9DhjX2x0yqxgUY5ca75jLqbMuKBKBN0PVqIr9jlHkR8_s","use":"sig","kid":"6823046760c5d460","alg":"RS256"}]}
|
||||
@@ -1,43 +0,0 @@
|
||||
# Proxy,如果你需要的话(比如你使用 GitHub 作为鉴权服务提供商)
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
|
||||
# 其他环境变量,视需求而定,可以参照客户端版本的环境变量配置,注意不要有 ACCESS_CODE
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
|
||||
|
||||
# ===================
|
||||
# ===== 预设配置 =====
|
||||
# ===================
|
||||
# 如没有特殊需要不用更改
|
||||
LOBE_PORT=3210
|
||||
CASDOOR_PORT=8000
|
||||
RUSTFS_PORT=9000
|
||||
APP_URL=http://localhost:3210
|
||||
|
||||
# Postgres 相关,也即 DB 必须的环境变量
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
|
||||
AUTH_SSO_PROVIDERS=casdoor
|
||||
AUTH_CASDOOR_ISSUER=http://localhost:8000
|
||||
# Casdoor secret
|
||||
AUTH_CASDOOR_ID=a387a4892ee19b1a2249
|
||||
AUTH_CASDOOR_SECRET=dbf205949d704de81b0b5b3603174e23fbecc354
|
||||
CASDOOR_WEBHOOK_SECRET=casdoor-secret
|
||||
|
||||
# RustFS S3 配置
|
||||
RUSTFS_ACCESS_KEY=admin
|
||||
RUSTFS_SECRET_KEY=YOUR_RUSTFS_PASSWORD
|
||||
|
||||
# 在下方配置 rustfs 中添加的桶
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
RUSTFS_LOBE_BUCKET=lobe
|
||||
|
||||
# 为 casdoor 配置
|
||||
origin=http://localhost:8000
|
||||
|
||||
JWKS_KEY={"keys":[{"d":"PVoFyqyrGstB8wU52S7gqqQQdZLtin_thcEM0nrNtqp9U-NlKLlhgEcWp5t89ycgvhsAzmrRbezGj4JBTr3jn7eWdwQpPJNYiipnsgeJn0pwsB0H2dMqtavxinoPVXkMTOuGHMTFhhyguFBw2JbIL0PTQUcUlXjv40OoJpYHZeggSxgfV-TuxjwW8Ll4-n84M5IOi6A53RvioE-Hm1iyIc2XLBCfyOu-SbAQYi8HzrA64kCxobAB0peLQMiAzfZmwPKiGOhnhKrAlYmG02qFnbUYiJu_-AXwsAyGv9S9i6dwK7QXaGGWYyis8LlPpd_JmPrBnrWomwDlI045NUMWZQ","dp":"OSXI2NBBZl2r0Dpf4-1z44A_jC5lOyXtJhXQYnSXy5eIuxTJcEtkUYagGEwnREO4Q3t-4J-lT_6Y71M1ZlgKG1upwfw1O4aE3vGpHOik9iZYYCjA8fe5uBfOpX1ELmOtHNoHRhMtyjuPxSFXLlSp3bgcF1f3F40ClukdvXCx0Mc","dq":"m6hNdfj-F8E_7nUlX2nG95OffkFrhHTo67ML9aPgpvFwBlzg-hk5LwtxMfUzngqWF78TMl0JDm7vS1bz0xlWqXqu8pFPoTUnUoWgYfvuyHLBwR5TgccQkfoKbkSMzYNy8VJPXZeyIjVXsW98tZvj-NZF-M9Pke_EWJm-jjXCu_8","e":"AQAB","kty":"RSA","n":"piffosMS0HOSgsSr_zQkXYaQt1kOCD73VR0b2XJD6UdQCKPbnBOzTIuA_xowX61QVsl5pCZLTw8ERC3r2Nlxj5Rp_H6RuOT7ioUqlbnxSGnfuAn8dFupY3A-sf9HVDOvtJdlS-nO9yA4wWU-A50zZ1Mf0pPZlUZE6dUQfsJFi5yXaNAybyk3U4VpMO_SXAilWEHVhiO0F0ccpJMCkT47AeXmYH9MlWwIGcay0UiAsdrs8J-q1arZ7Mbq0oxHmUXJG0vwRvAL8KnCEi8cJ3e2kKCRcr-BQCujsHUyUl6f_ATwSVuTHdAR1IzIcW37v27h3WQK_v0ffQM1NstamDX5vQ","p":"4myVm2M5cZGvVXsOmWUTUG87VC1GlQcL5tmMNSGSpQCL8yWZ1vANkmCxSMptrKB4dU9DAB3On6_oMhW1pJ3uYNGSW49BcmJoLkiWKeg5zWFnKPQNuThQmY1sCCubtKhBQgaYUr7TVzN9smrDV3zCu9MlRl-XPwnEmWaDII3g-f8","q":"u9v4IOEsb4l2Y3eWKE2bwJh5fJRR4vivaYA7U-1-OpvDwB3A48Rey9IL1ucXqE5G1Du8BtijPm5oSAar5uzrjtg1bZ9gevif6DnBGaIRE7LnSrUsTPfZwzntJ1rTaGiVe_pAdnTKXXaH6DxygXxH4wvGgA44V3TTfBXQUcjzdEM","qi":"lDBnSPKkRnYqQvbqVD1LxzqBPEeqEA3GyCqMj6fIZNgoEaBSLi0TSsUyGZ5mahX3KO35vKAZa5jvGjhvUGUiXycq8KvRZdeGK45vJdwZT2TiXiDwo9IQgJcbFMpxaB9DhjX2x0yqxgUY5ca75jLqbMuKBKBN0PVqIr9jlHkR8_s","use":"sig","kid":"6823046760c5d460","alg":"RS256"}]}
|
||||
@@ -1,24 +0,0 @@
|
||||
{
|
||||
"ID": "",
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "",
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"AWS": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
"Action": [
|
||||
"s3:GetObject"
|
||||
],
|
||||
"NotAction": [],
|
||||
"Resource": [
|
||||
"arn:aws:s3:::lobe/*"
|
||||
],
|
||||
"NotResource": [],
|
||||
"Condition": {}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,297 +0,0 @@
|
||||
name: lobehub
|
||||
services:
|
||||
network-service:
|
||||
image: alpine
|
||||
container_name: lobe-network
|
||||
restart: always
|
||||
ports:
|
||||
- '${RUSTFS_PORT}:9000' # RustFS API
|
||||
- '9001:9001' # RustFS Console
|
||||
- '${CASDOOR_PORT}:${CASDOOR_PORT}' # Casdoor
|
||||
- '${LOBE_PORT}:3210' # LobeChat
|
||||
- '3000:3000' # Grafana
|
||||
- '4318:4318' # otel-collector HTTP
|
||||
- '4317:4317' # otel-collector gRPC
|
||||
command: tail -f /dev/null
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
postgresql:
|
||||
image: pgvector/pgvector:pg17
|
||||
container_name: lobe-postgres
|
||||
ports:
|
||||
- '5432:5432'
|
||||
volumes:
|
||||
- './data:/var/lib/postgresql/data'
|
||||
environment:
|
||||
- 'POSTGRES_DB=${LOBE_DB_NAME}'
|
||||
- 'POSTGRES_PASSWORD=${POSTGRES_PASSWORD}'
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: lobe-redis
|
||||
ports:
|
||||
- '6379:6379'
|
||||
command: redis-server --save 60 1000 --appendonly yes
|
||||
volumes:
|
||||
- 'redis_data:/data'
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', 'ping']
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
|
||||
rustfs:
|
||||
image: rustfs/rustfs:latest
|
||||
container_name: lobe-rustfs
|
||||
network_mode: 'service:network-service'
|
||||
environment:
|
||||
- RUSTFS_CONSOLE_ENABLE=true
|
||||
- RUSTFS_ACCESS_KEY=${RUSTFS_ACCESS_KEY}
|
||||
- RUSTFS_SECRET_KEY=${RUSTFS_SECRET_KEY}
|
||||
volumes:
|
||||
- rustfs-data:/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget -qO- http://localhost:9000/health >/dev/null 2>&1 || exit 1"]
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 30
|
||||
command: ["--access-key","${RUSTFS_ACCESS_KEY}","--secret-key","${RUSTFS_SECRET_KEY}","/data"]
|
||||
|
||||
rustfs-init:
|
||||
image: minio/mc:latest
|
||||
container_name: lobe-rustfs-init
|
||||
depends_on:
|
||||
rustfs:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./bucket.config.json:/bucket.config.json:ro
|
||||
entrypoint: /bin/sh
|
||||
command: -c '
|
||||
set -eux;
|
||||
echo "S3_ACCESS_KEY=${RUSTFS_ACCESS_KEY}, S3_SECRET_KEY=${RUSTFS_SECRET_KEY}";
|
||||
mc --version;
|
||||
mc alias set rustfs "http://network-service:9000" "${RUSTFS_ACCESS_KEY}" "${RUSTFS_SECRET_KEY}";
|
||||
mc ls rustfs || true;
|
||||
mc mb "rustfs/lobe" --ignore-existing;
|
||||
mc admin info rustfs || true;
|
||||
mc anonymous set-json "/bucket.config.json" "rustfs/lobe";
|
||||
'
|
||||
restart: "no"
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
# version lock ref: https://github.com/lobehub/lobe-chat/pull/7331
|
||||
casdoor:
|
||||
image: casbin/casdoor:v2.13.0
|
||||
container_name: lobe-casdoor
|
||||
entrypoint: /bin/sh -c './server --createDatabase=true'
|
||||
network_mode: 'service:network-service'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
httpport: ${CASDOOR_PORT}
|
||||
RUNNING_IN_DOCKER: 'true'
|
||||
driverName: 'postgres'
|
||||
dataSourceName: 'user=postgres password=${POSTGRES_PASSWORD} host=postgresql port=5432 sslmode=disable dbname=casdoor'
|
||||
runmode: 'dev'
|
||||
volumes:
|
||||
- ./init_data.json:/init_data.json
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
searxng:
|
||||
image: searxng/searxng
|
||||
container_name: lobe-searxng
|
||||
volumes:
|
||||
- './searxng-settings.yml:/etc/searxng/settings.yml'
|
||||
environment:
|
||||
- 'SEARXNG_SETTINGS_FILE=/etc/searxng/settings.yml'
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
lobe:
|
||||
image: lobehub/lobehub
|
||||
container_name: lobehub
|
||||
network_mode: 'service:network-service'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
network-service:
|
||||
condition: service_started
|
||||
rustfs:
|
||||
condition: service_healthy
|
||||
rustfs-init:
|
||||
condition: service_completed_successfully
|
||||
casdoor:
|
||||
condition: service_started
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
environment:
|
||||
- 'AUTH_SSO_PROVIDERS=casdoor'
|
||||
- 'KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ='
|
||||
- 'AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg'
|
||||
- 'DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgresql:5432/${LOBE_DB_NAME}'
|
||||
- 'S3_BUCKET=${RUSTFS_LOBE_BUCKET}'
|
||||
- 'S3_ENABLE_PATH_STYLE=1'
|
||||
- 'S3_ACCESS_KEY=${RUSTFS_ACCESS_KEY}'
|
||||
- 'S3_ACCESS_KEY_ID=${RUSTFS_ACCESS_KEY}'
|
||||
- 'S3_SECRET_ACCESS_KEY=${RUSTFS_SECRET_KEY}'
|
||||
- 'LLM_VISION_IMAGE_USE_BASE64=1'
|
||||
- 'S3_SET_ACL=0'
|
||||
- 'SEARXNG_URL=http://searxng:8080'
|
||||
- 'REDIS_URL=redis://redis:6379'
|
||||
- 'REDIS_PREFIX=lobechat'
|
||||
- 'REDIS_TLS=0'
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
/bin/node /app/startServer.js &
|
||||
LOBE_PID=\$!
|
||||
sleep 3
|
||||
if [ $(wget --timeout=5 --spider --server-response ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
|
||||
echo '⚠️Warning: Unable to fetch OIDC configuration from Casdoor'
|
||||
echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
echo '⚠️注意:无法从 Casdoor 获取 OIDC 配置'
|
||||
echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
else
|
||||
if ! wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep 'issuer' | grep ${AUTH_CASDOOR_ISSUER}; then
|
||||
printf '❌Error: The Auth issuer is conflict, Issuer in OIDC configuration is: %s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
|
||||
echo ' , but the issuer in .env file is: ${AUTH_CASDOOR_ISSUER} '
|
||||
echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
printf '❌错误:Auth 的 issuer 冲突,OIDC 配置中的 issuer 是:%s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
|
||||
echo ' , 但 .env 文件中的 issuer 是:${AUTH_CASDOOR_ISSUER} '
|
||||
echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
fi
|
||||
fi
|
||||
if [ $(wget --timeout=5 --spider --server-response ${S3_ENDPOINT}/health 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
|
||||
echo '⚠️Warning: Unable to fetch RustFS health status'
|
||||
echo 'Request URL: ${S3_ENDPOINT}/health'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
echo '⚠️注意:无法获取 RustFS 健康状态'
|
||||
echo '请求 URL: ${S3_ENDPOINT}/health'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
fi
|
||||
wait \$LOBE_PID
|
||||
"
|
||||
|
||||
grafana:
|
||||
profiles:
|
||||
- otel
|
||||
image: grafana/grafana:12.2.0-17419259409
|
||||
container_name: lobe-grafana
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
environment:
|
||||
- GF_AUTH_ANONYMOUS_ENABLED=true
|
||||
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
|
||||
- GF_AUTH_DISABLE_LOGIN_FORM=true
|
||||
- GF_FEATURE_TOGGLES_ENABLE=traceqlEditor
|
||||
volumes:
|
||||
- grafana_data:/var/lib/grafana
|
||||
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
|
||||
- ./grafana/datasources:/etc/grafana/provisioning/datasources
|
||||
depends_on:
|
||||
- tempo
|
||||
- prometheus
|
||||
|
||||
tempo:
|
||||
profiles:
|
||||
- otel
|
||||
image: grafana/tempo:latest
|
||||
container_name: lobe-tempo
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./tempo/tempo.yaml:/etc/tempo.yaml
|
||||
- tempo_data:/var/tempo
|
||||
command: ['-config.file=/etc/tempo.yaml']
|
||||
|
||||
prometheus:
|
||||
profiles:
|
||||
- otel
|
||||
image: prom/prometheus
|
||||
container_name: lobe-prometheus
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus_data:/prometheus
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--web.enable-otlp-receiver'
|
||||
- '--web.enable-remote-write-receiver'
|
||||
- '--enable-feature=exemplar-storage'
|
||||
|
||||
otel-collector:
|
||||
profiles:
|
||||
- otel
|
||||
image: otel/opentelemetry-collector
|
||||
container_name: lobe-otel-collector
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./otel-collector/collector-config.yaml:/etc/otelcol/config.yaml
|
||||
command: ['--config', '/etc/otelcol/config.yaml']
|
||||
depends_on:
|
||||
- tempo
|
||||
- prometheus
|
||||
|
||||
otel-tracing-test:
|
||||
profiles:
|
||||
- otel-test
|
||||
image: ghcr.io/grafana/xk6-client-tracing:v0.0.9
|
||||
container_name: lobe-otel-tracing-test
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
environment:
|
||||
- ENDPOINT=127.0.0.1:4317
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
s3_data:
|
||||
driver: local
|
||||
grafana_data:
|
||||
driver: local
|
||||
tempo_data:
|
||||
driver: local
|
||||
prometheus_data:
|
||||
driver: local
|
||||
redis_data:
|
||||
driver: local
|
||||
rustfs-data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
lobe-network:
|
||||
driver: bridge
|
||||
@@ -1,41 +0,0 @@
|
||||
# Proxy, if you need it
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
|
||||
# Other environment variables, as needed. You can refer to the environment variables configuration for the client version, making sure not to have ACCESS_CODE.
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
|
||||
|
||||
# ===========================
|
||||
# ====== Preset config ======
|
||||
# ===========================
|
||||
# if no special requirements, no need to change
|
||||
LOBE_PORT=3210
|
||||
CASDOOR_PORT=8000
|
||||
MINIO_PORT=9000
|
||||
APP_URL=http://localhost:3210
|
||||
AUTH_URL=http://localhost:3210/api/auth
|
||||
|
||||
# Postgres related, which are the necessary environment variables for DB
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
|
||||
AUTH_CASDOOR_ISSUER=http://localhost:8000
|
||||
# Casdoor secret
|
||||
AUTH_CASDOOR_ID=a387a4892ee19b1a2249
|
||||
AUTH_CASDOOR_SECRET=dbf205949d704de81b0b5b3603174e23fbecc354
|
||||
CASDOOR_WEBHOOK_SECRET=casdoor-secret
|
||||
|
||||
# MinIO S3 configuration
|
||||
MINIO_ROOT_USER=admin
|
||||
MINIO_ROOT_PASSWORD=YOUR_MINIO_PASSWORD
|
||||
|
||||
# Configure the bucket information of MinIO
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
MINIO_LOBE_BUCKET=lobe
|
||||
|
||||
# Configure for casdoor
|
||||
origin=http://localhost:8000
|
||||
@@ -1,41 +0,0 @@
|
||||
# Proxy,如果你需要的话(比如你使用 GitHub 作为鉴权服务提供商)
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
|
||||
# 其他环境变量,视需求而定,可以参照客户端版本的环境变量配置,注意不要有 ACCESS_CODE
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
|
||||
|
||||
# ===================
|
||||
# ===== 预设配置 =====
|
||||
# ===================
|
||||
# 如没有特殊需要不用更改
|
||||
LOBE_PORT=3210
|
||||
CASDOOR_PORT=8000
|
||||
MINIO_PORT=9000
|
||||
APP_URL=http://localhost:3210
|
||||
AUTH_URL=http://localhost:3210/api/auth
|
||||
|
||||
# Postgres 相关,也即 DB 必须的环境变量
|
||||
LOBE_DB_NAME=lobechat
|
||||
POSTGRES_PASSWORD=uWNZugjBqixf8dxC
|
||||
|
||||
AUTH_CASDOOR_ISSUER=http://localhost:8000
|
||||
# Casdoor secret
|
||||
AUTH_CASDOOR_ID=a387a4892ee19b1a2249
|
||||
AUTH_CASDOOR_SECRET=dbf205949d704de81b0b5b3603174e23fbecc354
|
||||
CASDOOR_WEBHOOK_SECRET=casdoor-secret
|
||||
|
||||
# MinIO S3 配置
|
||||
MINIO_ROOT_USER=admin
|
||||
MINIO_ROOT_PASSWORD=YOUR_MINIO_PASSWORD
|
||||
|
||||
# 在下方配置 minio 中添加的桶
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
MINIO_LOBE_BUCKET=lobe
|
||||
|
||||
# 为 casdoor 配置
|
||||
origin=http://localhost:8000
|
||||
@@ -1,251 +0,0 @@
|
||||
name: lobehub
|
||||
services:
|
||||
network-service:
|
||||
image: alpine
|
||||
container_name: lobe-network
|
||||
restart: always
|
||||
ports:
|
||||
- '${MINIO_PORT}:${MINIO_PORT}' # MinIO API
|
||||
- '9001:9001' # MinIO Console
|
||||
- '${CASDOOR_PORT}:${CASDOOR_PORT}' # Casdoor
|
||||
- '${LOBE_PORT}:3210' # LobeChat
|
||||
- '3000:3000' # Grafana
|
||||
- '4318:4318' # otel-collector HTTP
|
||||
- '4317:4317' # otel-collector gRPC
|
||||
command: tail -f /dev/null
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
postgresql:
|
||||
image: pgvector/pgvector:pg17
|
||||
container_name: lobe-postgres
|
||||
ports:
|
||||
- '5432:5432'
|
||||
volumes:
|
||||
- './data:/var/lib/postgresql/data'
|
||||
environment:
|
||||
- 'POSTGRES_DB=${LOBE_DB_NAME}'
|
||||
- 'POSTGRES_PASSWORD=${POSTGRES_PASSWORD}'
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
network_mode: 'service:network-service'
|
||||
volumes:
|
||||
- './s3_data:/etc/minio/data'
|
||||
environment:
|
||||
- 'MINIO_API_CORS_ALLOW_ORIGIN=*'
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
minio server /etc/minio/data --address ':${MINIO_PORT}' --console-address ':9001' &
|
||||
MINIO_PID=\$!
|
||||
while ! curl -s http://localhost:${MINIO_PORT}/minio/health/live; do
|
||||
echo 'Waiting for MinIO to start...'
|
||||
sleep 1
|
||||
done
|
||||
sleep 5
|
||||
mc alias set myminio http://localhost:${MINIO_PORT} ${MINIO_ROOT_USER} ${MINIO_ROOT_PASSWORD}
|
||||
echo 'Creating bucket ${MINIO_LOBE_BUCKET}'
|
||||
mc mb myminio/${MINIO_LOBE_BUCKET}
|
||||
wait \$MINIO_PID
|
||||
"
|
||||
|
||||
# version lock ref: https://github.com/lobehub/lobe-chat/pull/7331
|
||||
casdoor:
|
||||
image: casbin/casdoor:v2.13.0
|
||||
container_name: lobe-casdoor
|
||||
entrypoint: /bin/sh -c './server --createDatabase=true'
|
||||
network_mode: 'service:network-service'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
httpport: ${CASDOOR_PORT}
|
||||
RUNNING_IN_DOCKER: 'true'
|
||||
driverName: 'postgres'
|
||||
dataSourceName: 'user=postgres password=${POSTGRES_PASSWORD} host=postgresql port=5432 sslmode=disable dbname=casdoor'
|
||||
runmode: 'dev'
|
||||
volumes:
|
||||
- ./init_data.json:/init_data.json
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
searxng:
|
||||
image: searxng/searxng
|
||||
container_name: lobe-searxng
|
||||
volumes:
|
||||
- './searxng-settings.yml:/etc/searxng/settings.yml'
|
||||
environment:
|
||||
- 'SEARXNG_SETTINGS_FILE=/etc/searxng/settings.yml'
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
env_file:
|
||||
- .env
|
||||
|
||||
grafana:
|
||||
image: grafana/grafana:12.2.0-17419259409
|
||||
container_name: lobe-grafana
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
environment:
|
||||
- GF_AUTH_ANONYMOUS_ENABLED=true
|
||||
- GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
|
||||
- GF_AUTH_DISABLE_LOGIN_FORM=true
|
||||
- GF_FEATURE_TOGGLES_ENABLE=traceqlEditor
|
||||
volumes:
|
||||
- grafana_data:/var/lib/grafana
|
||||
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
|
||||
- ./grafana/datasources:/etc/grafana/provisioning/datasources
|
||||
depends_on:
|
||||
- tempo
|
||||
- prometheus
|
||||
|
||||
tempo:
|
||||
image: grafana/tempo:latest
|
||||
container_name: lobe-tempo
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./tempo/tempo.yaml:/etc/tempo.yaml
|
||||
- tempo_data:/var/tempo
|
||||
command: ['-config.file=/etc/tempo.yaml']
|
||||
|
||||
prometheus:
|
||||
image: prom/prometheus
|
||||
container_name: lobe-prometheus
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./prometheus/prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- prometheus_data:/prometheus
|
||||
command:
|
||||
- '--config.file=/etc/prometheus/prometheus.yml'
|
||||
- '--web.enable-otlp-receiver'
|
||||
- '--web.enable-remote-write-receiver'
|
||||
- '--enable-feature=exemplar-storage'
|
||||
|
||||
otel-collector:
|
||||
image: otel/opentelemetry-collector
|
||||
container_name: lobe-otel-collector
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
volumes:
|
||||
- ./otel-collector/collector-config.yaml:/etc/otelcol/config.yaml
|
||||
command: ['--config', '/etc/otelcol/config.yaml']
|
||||
depends_on:
|
||||
- tempo
|
||||
- prometheus
|
||||
|
||||
otel-tracing-test:
|
||||
profiles:
|
||||
- otel-test
|
||||
image: ghcr.io/grafana/xk6-client-tracing:v0.0.9
|
||||
container_name: lobe-otel-tracing-test
|
||||
network_mode: 'service:network-service'
|
||||
restart: always
|
||||
environment:
|
||||
- ENDPOINT=127.0.0.1:4317
|
||||
|
||||
lobe:
|
||||
image: lobehub/lobehub
|
||||
container_name: lobehub
|
||||
network_mode: 'service:network-service'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
network-service:
|
||||
condition: service_started
|
||||
minio:
|
||||
condition: service_started
|
||||
casdoor:
|
||||
condition: service_started
|
||||
|
||||
environment:
|
||||
- 'AUTH_SSO_PROVIDERS=casdoor'
|
||||
- 'KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ='
|
||||
- 'AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg'
|
||||
- 'DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgresql:5432/${LOBE_DB_NAME}'
|
||||
- 'S3_BUCKET=${MINIO_LOBE_BUCKET}'
|
||||
- 'S3_ENABLE_PATH_STYLE=1'
|
||||
- 'S3_ACCESS_KEY=${MINIO_ROOT_USER}'
|
||||
- 'S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}'
|
||||
- 'S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}'
|
||||
- 'LLM_VISION_IMAGE_USE_BASE64=1'
|
||||
- 'S3_SET_ACL=0'
|
||||
- 'SEARXNG_URL=http://searxng:8080'
|
||||
- 'OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=http/protobuf'
|
||||
- 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=http://localhost:4318/v1/metrics'
|
||||
- 'OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf'
|
||||
- 'OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=http://localhost:4318/v1/traces'
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
entrypoint: >
|
||||
/bin/sh -c "
|
||||
/bin/node /app/startServer.js &
|
||||
LOBE_PID=\$!
|
||||
sleep 3
|
||||
if [ $(wget --timeout=5 --spider --server-response ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
|
||||
echo '⚠️Warning: Unable to fetch OIDC configuration from Casdoor'
|
||||
echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
echo '⚠️注意:无法从 Casdoor 获取 OIDC 配置'
|
||||
echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
else
|
||||
if ! wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep 'issuer' | grep ${AUTH_CASDOOR_ISSUER}; then
|
||||
printf '❌Error: The Auth issuer is conflict, Issuer in OIDC configuration is: %s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
|
||||
echo ' , but the issuer in .env file is: ${AUTH_CASDOOR_ISSUER} '
|
||||
echo 'Request URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
printf '❌错误:Auth 的 issuer 冲突,OIDC 配置中的 issuer 是:%s' \$(wget -O - --timeout=5 ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration 2>&1 | grep -E 'issuer.*' | awk -F '\"' '{print \$4}')
|
||||
echo ' , 但 .env 文件中的 issuer 是:${AUTH_CASDOOR_ISSUER} '
|
||||
echo '请求 URL: ${AUTH_CASDOOR_ISSUER}/.well-known/openid-configuration'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
fi
|
||||
fi
|
||||
if [ $(wget --timeout=5 --spider --server-response ${S3_ENDPOINT}/minio/health/live 2>&1 | grep -c 'HTTP/1.1 200 OK') -eq 0 ]; then
|
||||
echo '⚠️Warning: Unable to fetch MinIO health status'
|
||||
echo 'Request URL: ${S3_ENDPOINT}/minio/health/live'
|
||||
echo 'Read more at: https://lobehub.com/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
echo '⚠️注意:无法获取 MinIO 健康状态'
|
||||
echo '请求 URL: ${S3_ENDPOINT}/minio/health/live'
|
||||
echo '了解更多:https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose#necessary-configuration'
|
||||
echo ''
|
||||
fi
|
||||
wait \$LOBE_PID
|
||||
"
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
s3_data:
|
||||
driver: local
|
||||
grafana_data:
|
||||
driver: local
|
||||
tempo_data:
|
||||
driver: local
|
||||
prometheus_data:
|
||||
driver: local
|
||||
|
||||
|
||||
networks:
|
||||
lobe-network:
|
||||
driver: bridge
|
||||
@@ -1,15 +0,0 @@
|
||||
apiVersion: 1
|
||||
|
||||
prune: true
|
||||
|
||||
datasources:
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
uid: prometheus
|
||||
access: proxy
|
||||
orgId: 1
|
||||
url: http://127.0.0.1:9090
|
||||
basicAuth: false
|
||||
isDefault: false
|
||||
version: 1
|
||||
editable: false
|
||||
@@ -1,16 +0,0 @@
|
||||
apiVersion: 1
|
||||
|
||||
prune: true
|
||||
|
||||
datasources:
|
||||
- name: Tempo
|
||||
type: tempo
|
||||
access: proxy
|
||||
orgId: 1
|
||||
url: http://127.0.0.1:3200
|
||||
basicAuth: false
|
||||
isDefault: true
|
||||
version: 1
|
||||
editable: false
|
||||
apiVersion: 1
|
||||
uid: tempo
|
||||
@@ -1,45 +0,0 @@
|
||||
extensions:
|
||||
health_check:
|
||||
endpoint: 127.0.0.1:13133
|
||||
|
||||
receivers:
|
||||
prometheus:
|
||||
config:
|
||||
scrape_configs:
|
||||
- job_name: otel-collector-metrics
|
||||
scrape_interval: 10s
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:8888"]
|
||||
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 0.0.0.0:4317
|
||||
http:
|
||||
endpoint: 0.0.0.0:4318
|
||||
|
||||
exporters:
|
||||
prometheusremotewrite:
|
||||
endpoint: http://127.0.0.1:9090/api/v1/write
|
||||
tls:
|
||||
insecure: true
|
||||
|
||||
otlp:
|
||||
endpoint: 127.0.0.1:14317
|
||||
tls:
|
||||
insecure: true
|
||||
|
||||
debug:
|
||||
verbosity: detailed
|
||||
|
||||
service:
|
||||
pipelines:
|
||||
metrics:
|
||||
receivers: [prometheus, otlp]
|
||||
exporters: [prometheusremotewrite]
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
exporters: [otlp]
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
exporters: [debug]
|
||||
@@ -1,11 +0,0 @@
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
evaluation_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: "prometheus"
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:9090"]
|
||||
- job_name: "tempo"
|
||||
static_configs:
|
||||
- targets: ["127.0.0.1:3200"]
|
||||
@@ -1,58 +0,0 @@
|
||||
stream_over_http_enabled: true
|
||||
server:
|
||||
http_listen_port: 3200
|
||||
log_level: info
|
||||
|
||||
query_frontend:
|
||||
search:
|
||||
duration_slo: 5s
|
||||
throughput_bytes_slo: 1.073741824e+09
|
||||
metadata_slo:
|
||||
duration_slo: 5s
|
||||
throughput_bytes_slo: 1.073741824e+09
|
||||
trace_by_id:
|
||||
duration_slo: 5s
|
||||
|
||||
distributor:
|
||||
max_attribute_bytes: 10485760
|
||||
receivers:
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: 127.0.0.1:14317
|
||||
http:
|
||||
endpoint: 127.0.0.1:14318
|
||||
|
||||
ingester:
|
||||
max_block_duration: 5m # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally
|
||||
|
||||
compactor:
|
||||
compaction:
|
||||
block_retention: 1h # overall Tempo trace retention. set for demo purposes
|
||||
|
||||
metrics_generator:
|
||||
registry:
|
||||
external_labels:
|
||||
source: tempo
|
||||
cluster: docker-compose
|
||||
storage:
|
||||
path: /var/tempo/generator/wal
|
||||
remote_write:
|
||||
- url: http://127.0.0.1:9090/api/v1/write
|
||||
send_exemplars: true
|
||||
traces_storage:
|
||||
path: /var/tempo/generator/traces
|
||||
|
||||
storage:
|
||||
trace:
|
||||
backend: local # backend configuration to use
|
||||
wal:
|
||||
path: /var/tempo/wal # where to store the wal locally
|
||||
local:
|
||||
path: /var/tempo/blocks
|
||||
|
||||
overrides:
|
||||
defaults:
|
||||
metrics_generator:
|
||||
processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator
|
||||
generate_native_histograms: both
|
||||
@@ -1,124 +0,0 @@
|
||||
name: lobehub
|
||||
services:
|
||||
network-service:
|
||||
image: alpine
|
||||
container_name: lobe-network
|
||||
ports:
|
||||
- '${MINIO_PORT}:${MINIO_PORT}' # MinIO API
|
||||
- '9001:9001' # MinIO Console
|
||||
- '${LOGTO_PORT}:${LOGTO_PORT}' # Logto
|
||||
- '3002:3002' # Logto Admin
|
||||
- '${LOBE_PORT}:3210' # LobeChat
|
||||
command: tail -f /dev/null
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
postgresql:
|
||||
image: pgvector/pgvector:pg16
|
||||
container_name: lobe-postgres
|
||||
ports:
|
||||
- '5432:5432'
|
||||
volumes:
|
||||
- './data:/var/lib/postgresql/data'
|
||||
environment:
|
||||
- 'POSTGRES_DB=${LOBE_DB_NAME}'
|
||||
- 'POSTGRES_PASSWORD=${POSTGRES_PASSWORD}'
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
container_name: lobe-redis
|
||||
ports:
|
||||
- '6379:6379'
|
||||
command: redis-server --save 60 1000 --appendonly yes
|
||||
volumes:
|
||||
- 'redis_data:/data'
|
||||
healthcheck:
|
||||
test: ['CMD', 'redis-cli', 'ping']
|
||||
interval: 5s
|
||||
timeout: 3s
|
||||
retries: 5
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
network_mode: "service:network-service"
|
||||
volumes:
|
||||
- './s3_data:/etc/minio/data'
|
||||
environment:
|
||||
- 'MINIO_ROOT_USER=${MINIO_ROOT_USER}'
|
||||
- 'MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}'
|
||||
- 'MINIO_API_CORS_ALLOW_ORIGIN=http://localhost:${LOBE_PORT}'
|
||||
restart: always
|
||||
command: >
|
||||
server /etc/minio/data --address ":${MINIO_PORT}" --console-address ":9001"
|
||||
|
||||
logto:
|
||||
image: svhd/logto
|
||||
container_name: lobe-logto
|
||||
network_mode: 'service:network-service'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- 'TRUST_PROXY_HEADER=1'
|
||||
- 'PORT=${LOGTO_PORT}'
|
||||
- 'DB_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgresql:5432/logto'
|
||||
- 'ENDPOINT=http://localhost:${LOGTO_PORT}'
|
||||
- 'ADMIN_ENDPOINT=http://localhost:3002'
|
||||
entrypoint: ['sh', '-c', 'npm run cli db seed -- --swe && npm start']
|
||||
|
||||
lobe:
|
||||
image: lobehub/lobehub
|
||||
container_name: lobehub
|
||||
network_mode: 'service:network-service'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
network-service:
|
||||
condition: service_started
|
||||
minio:
|
||||
condition: service_started
|
||||
logto:
|
||||
condition: service_started
|
||||
redis:
|
||||
condition: service_healthy
|
||||
|
||||
environment:
|
||||
- 'APP_URL=http://localhost:3210'
|
||||
- 'AUTH_SSO_PROVIDERS=logto'
|
||||
- 'KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ='
|
||||
- 'AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg'
|
||||
- 'AUTH_LOGTO_ISSUER=http://localhost:${LOGTO_PORT}/oidc'
|
||||
- 'DATABASE_URL=postgresql://postgres:${POSTGRES_PASSWORD}@postgresql:5432/${LOBE_DB_NAME}'
|
||||
- 'S3_ENDPOINT=http://localhost:${MINIO_PORT}'
|
||||
- 'S3_BUCKET=${MINIO_LOBE_BUCKET}'
|
||||
- 'S3_ENABLE_PATH_STYLE=1'
|
||||
- 'REDIS_URL=redis://redis:6379'
|
||||
- 'REDIS_PREFIX=lobechat'
|
||||
- 'REDIS_TLS=0'
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
s3_data:
|
||||
driver: local
|
||||
redis_data:
|
||||
driver: local
|
||||
|
||||
networks:
|
||||
lobe-network:
|
||||
driver: bridge
|
||||
@@ -1,31 +0,0 @@
|
||||
# Required: LobeChat domain for tRPC calls
|
||||
# Ensure this domain is whitelisted in your SSO providers and S3 service CORS settings
|
||||
APP_URL=http://localhost:3210
|
||||
|
||||
# Postgres related environment variables
|
||||
# Required: Secret key for encrypting sensitive information. Generate with: openssl rand -base64 32
|
||||
KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ=
|
||||
# Required: Postgres database connection string
|
||||
DATABASE_URL=postgresql://postgres:uWNZugjBqixf8dxC@postgresql:5432/lobechat
|
||||
|
||||
# Authentication related environment variables
|
||||
AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
|
||||
AUTH_SSO_PROVIDERS=zitadel
|
||||
# ZiTADEL provider configuration
|
||||
# Please refer to:https://lobehub.com/zh/docs/self-hosting/advanced/auth/providers/zitadel
|
||||
AUTH_ZITADEL_ID=285945938244075523
|
||||
AUTH_ZITADEL_SECRET=hkbtzHLaCEIeHeFThym14UcydpmQiEB5JtAX08HSqSoJxhAlVVkyovTuNUZ5TNrT
|
||||
AUTH_ZITADEL_ISSUER=http://localhost:8080
|
||||
|
||||
# MinIO S3 configuration
|
||||
S3_ACCESS_KEY_ID=
|
||||
S3_SECRET_ACCESS_KEY=
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
S3_BUCKET=lobe
|
||||
S3_ENABLE_PATH_STYLE=1
|
||||
LLM_VISION_IMAGE_USE_BASE64=1
|
||||
|
||||
# Other environment variables, as needed. You can refer to the environment variables configuration for the client version, making sure not to have ACCESS_CODE.
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
@@ -1,30 +0,0 @@
|
||||
# LobeChat 域名
|
||||
APP_URL=http://localhost:3210
|
||||
|
||||
# Postgres 相关,也即 DB 必须的环境变量
|
||||
# 用于加密敏感信息的密钥,可以使用 openssl rand -base64 32 生成
|
||||
KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ=
|
||||
# Postgres 数据库连接字符串
|
||||
DATABASE_URL=postgresql://postgres:uWNZugjBqixf8dxC@postgresql:5432/lobechat
|
||||
|
||||
# 鉴权相关
|
||||
AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
|
||||
AUTH_SSO_PROVIDERS=zitadel
|
||||
# ZiTADEL 鉴权服务提供商部分
|
||||
# 请参考:https://lobehub.com/zh/docs/self-hosting/advanced/auth/next-auth/zitadel
|
||||
AUTH_ZITADEL_ID=285945938244075523
|
||||
AUTH_ZITADEL_SECRET=hkbtzHLaCEIeHeFThym14UcydpmQiEB5JtAX08HSqSoJxhAlVVkyovTuNUZ5TNrT
|
||||
AUTH_ZITADEL_ISSUER=http://localhost:8080
|
||||
|
||||
# MinIO S3 配置
|
||||
S3_ACCESS_KEY_ID=
|
||||
S3_SECRET_ACCESS_KEY=
|
||||
S3_ENDPOINT=http://localhost:9000
|
||||
S3_BUCKET=lobe
|
||||
S3_ENABLE_PATH_STYLE=1
|
||||
LLM_VISION_IMAGE_USE_BASE64=1
|
||||
|
||||
# 其他环境变量,视需求而定,可以参照客户端版本的环境变量配置,注意不要有 ACCESS_CODE
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
@@ -1,86 +0,0 @@
|
||||
name: lobehub
|
||||
services:
|
||||
network-service:
|
||||
image: alpine
|
||||
container_name: lobe-network
|
||||
ports:
|
||||
- '9000:9000' # MinIO API
|
||||
- '9001:9001' # MinIO Console
|
||||
- '8080:8080' # Zitadel Console
|
||||
- '3210:3210' # LobeChat
|
||||
command: tail -f /dev/null
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
postgresql:
|
||||
image: pgvector/pgvector:pg16
|
||||
container_name: lobe-postgres
|
||||
ports:
|
||||
- '5432:5432'
|
||||
volumes:
|
||||
- './data:/var/lib/postgresql/data'
|
||||
environment:
|
||||
- 'POSTGRES_DB=lobechat'
|
||||
- 'POSTGRES_PASSWORD=uWNZugjBqixf8dxC'
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: always
|
||||
networks:
|
||||
- lobe-network
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
network_mode: 'service:network-service'
|
||||
volumes:
|
||||
- './s3_data:/etc/minio/data'
|
||||
environment:
|
||||
- 'MINIO_ROOT_USER=YOUR_MINIO_USER'
|
||||
- 'MINIO_ROOT_PASSWORD=YOUR_MINIO_PASSWORD'
|
||||
- 'MINIO_API_CORS_ALLOW_ORIGIN=http://localhost:3210'
|
||||
restart: always
|
||||
command: >
|
||||
server /etc/minio/data --address ":9000" --console-address ":9001"
|
||||
|
||||
|
||||
zitadel:
|
||||
restart: 'always'
|
||||
image: 'ghcr.io/zitadel/zitadel:latest'
|
||||
container_name: lobe-zitadel
|
||||
network_mode: 'service:network-service'
|
||||
command: start-from-init --config /zitadel-config.yaml --steps /zitadel-init-steps.yaml --masterkey "cft3Tekr/rQBOqwoQSCPoncA9BHbn7QJ" --tlsMode disabled #MasterkeyNeedsToHave32Characters
|
||||
volumes:
|
||||
- ./zitadel-config.yaml:/zitadel-config.yaml:ro
|
||||
- ./zitadel-init-steps.yaml:/zitadel-init-steps.yaml:ro
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
|
||||
lobe:
|
||||
image: lobehub/lobehub
|
||||
container_name: lobehub
|
||||
network_mode: 'service:network-service'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
network-service:
|
||||
condition: service_started
|
||||
minio:
|
||||
condition: service_started
|
||||
zitadel:
|
||||
condition: service_started
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
s3_data:
|
||||
driver: local
|
||||
networks:
|
||||
lobe-network:
|
||||
driver: bridge
|
||||
@@ -1,26 +0,0 @@
|
||||
Log:
|
||||
Level: 'info'
|
||||
|
||||
Port: 8080
|
||||
ExternalPort: 8080
|
||||
ExternalDomain: localhost
|
||||
ExternalSecure: false
|
||||
TLS:
|
||||
Enabled: false
|
||||
|
||||
# If not using the docker compose example, adjust these values for connecting ZITADEL to your PostgreSQL
|
||||
Database:
|
||||
postgres:
|
||||
Host: postgresql
|
||||
Port: 5432
|
||||
Database: zitadel
|
||||
User:
|
||||
Username: 'zitadel'
|
||||
Password: 'zitadel'
|
||||
SSL:
|
||||
Mode: 'disable'
|
||||
Admin:
|
||||
Username: 'postgres'
|
||||
Password: 'uWNZugjBqixf8dxC' #postgres password
|
||||
SSL:
|
||||
Mode: 'disable'
|
||||
@@ -1,11 +0,0 @@
|
||||
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/setup/steps.yaml
|
||||
FirstInstance:
|
||||
Org:
|
||||
Human:
|
||||
# use the loginname root@zitadel.localhost
|
||||
Username: 'root'
|
||||
# The password must be 8 characters or more and must contain uppercase letters, lowercase letters, symbols, and numbers. The first login will require a password change.
|
||||
Password: 'Password1!'
|
||||
Email:
|
||||
# Optional, if set, can be used to log in with email.
|
||||
Address: 'example@zitadel.com' # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_ADDRESS
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"Statement": [
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"AWS": ["*"]
|
||||
},
|
||||
"Action": ["s3:GetBucketLocation"],
|
||||
"Resource": ["arn:aws:s3:::lobe"]
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"AWS": ["*"]
|
||||
},
|
||||
"Action": ["s3:ListBucket"],
|
||||
"Resource": ["arn:aws:s3:::lobe"],
|
||||
"Condition": {
|
||||
"StringEquals": {
|
||||
"s3:prefix": ["files/*"]
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"Effect": "Allow",
|
||||
"Principal": {
|
||||
"AWS": ["*"]
|
||||
},
|
||||
"Action": ["s3:PutObject", "s3:DeleteObject", "s3:GetObject"],
|
||||
"Resource": ["arn:aws:s3:::lobe/files/**"]
|
||||
}
|
||||
],
|
||||
"Version": "2012-10-17"
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,52 +0,0 @@
|
||||
# Required: LobeChat domain for tRPC calls
|
||||
# Ensure this domain is whitelisted in your SSO providers and S3 service CORS settings
|
||||
APP_URL=https://lobe.example.com/
|
||||
|
||||
# Postgres related environment variables
|
||||
# Required: Secret key for encrypting sensitive information. Generate with: openssl rand -base64 32
|
||||
KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ=
|
||||
# Required: Postgres database connection string
|
||||
# Format: postgresql://username:password@host:port/dbname
|
||||
# If using Docker, you can use the container name as the host
|
||||
DATABASE_URL=postgresql://postgres:uWNZugjBqixf8dxC@postgresql:5432/lobe
|
||||
|
||||
# Authentication related environment variables
|
||||
# Supports Auth0, Azure AD, GitHub, Authentik, Zitadel, Logto, etc.
|
||||
# For supported providers, see: https://lobehub.com/docs/self-hosting/advanced/auth
|
||||
# If you have ACCESS_CODE, please remove it. We use Better Auth as the sole authentication source
|
||||
# Required: Auth secret key. Generate with: openssl rand -base64 32
|
||||
AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
|
||||
# Required: Specify the authentication provider (e.g., Logto)
|
||||
AUTH_SSO_PROVIDERS=logto
|
||||
|
||||
# SSO providers configuration (example using Logto)
|
||||
# For other providers, see: https://lobehub.com/docs/self-hosting/environment-variables/auth
|
||||
AUTH_LOGTO_ID=YOUR_LOGTO_ID
|
||||
AUTH_LOGTO_SECRET=YOUR_LOGTO_SECRET
|
||||
AUTH_LOGTO_ISSUER=https://lobe-auth-api.example.com/oidc
|
||||
|
||||
# Proxy settings (if needed, e.g., when using GitHub as an auth provider)
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
# S3 related environment variables (example using MinIO)
|
||||
# Required: S3 Access Key ID (for MinIO, invalid until manually created in MinIO UI)
|
||||
S3_ACCESS_KEY_ID=YOUR_S3_ACCESS_KEY_ID
|
||||
# Required: S3 Secret Access Key (for MinIO, invalid until manually created in MinIO UI)
|
||||
S3_SECRET_ACCESS_KEY=YOUR_S3_SECRET_ACCESS_KEY
|
||||
# Required: S3 Endpoint for server/client connections to S3 API
|
||||
S3_ENDPOINT=https://lobe-s3-api.example.com
|
||||
# Required: S3 Bucket (invalid until manually created in MinIO UI)
|
||||
S3_BUCKET=lobe
|
||||
# Optional: S3 Enable Path Style
|
||||
# Use 0 for mainstream S3 cloud providers; use 1 for self-hosted MinIO
|
||||
# See: https://lobehub.com/docs/self-hosting/advanced/s3#s-3-enable-path-style
|
||||
S3_ENABLE_PATH_STYLE=1
|
||||
|
||||
# Other basic environment variables (as needed)
|
||||
# See: https://lobehub.com/docs/self-hosting/environment-variables/basic
|
||||
# Note: For server versions, the API must support embedding models (OpenAI text-embedding-3-small) for file processing
|
||||
# You don't need to specify this model in OPENAI_MODEL_LIST
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
@@ -1,51 +0,0 @@
|
||||
# 必填,LobeChat 域名,用于 tRPC 调用
|
||||
# 请保证此域名在你的 SSO 鉴权服务提供商、S3 服务商的 CORS 白名单中
|
||||
APP_URL=https://lobe.example.com/
|
||||
|
||||
# Postgres 相关,也即 DB 必需的环境变量
|
||||
# 必填,用于加密敏感信息的密钥,可以使用 openssl rand -base64 32 生成
|
||||
KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ=
|
||||
# 必填,Postgres 数据库连接字符串,用于连接到数据库
|
||||
# 格式:postgresql://username:password@host:port/dbname,如果你的 pg 实例为 Docker 容器且位于同一 docker-compose 文件中,亦可使用容器名作为 host
|
||||
DATABASE_URL=postgresql://postgres:uWNZugjBqixf8dxC@postgresql:5432/lobe
|
||||
|
||||
# 鉴权服务必需的环境变量
|
||||
# 可以使用 Auth0、Azure AD、GitHub、Authentik、Zitadel、Logto 等,如有其他接入诉求欢迎提 PR
|
||||
# 目前支持的鉴权服务提供商请参考:https://lobehub.com/zh/docs/self-hosting/advanced/auth
|
||||
# 如果你有 ACCESS_CODE,请务必清空,我们以 Better Auth 作为唯一鉴权来源
|
||||
# 必填,用于鉴权的密钥,可以使用 openssl rand -base64 32 生成
|
||||
AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
|
||||
# 必填,指定鉴权服务提供商,这里以 Logto 为例
|
||||
AUTH_SSO_PROVIDERS=logto
|
||||
|
||||
# SSO 鉴权服务提供商部分,以 Logto 为例
|
||||
# 其他鉴权服务提供商所需的环境变量,请参考:https://lobehub.com/zh/docs/self-hosting/environment-variables/auth
|
||||
AUTH_LOGTO_ID=YOUR_LOGTO_ID
|
||||
AUTH_LOGTO_SECRET=YOUR_LOGTO_SECRET
|
||||
AUTH_LOGTO_ISSUER=https://lobe-auth-api.example.com/oidc
|
||||
|
||||
# 代理相关,如果你需要的话(比如你使用 GitHub 作为鉴权服务提供商)
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
# S3 相关,也即非结构化数据(文件、图片等)存储必需的环境变量
|
||||
# 这里以 MinIO 为例
|
||||
# 必填,S3 的 Access Key ID,对于 MinIO 来说,直到在 MinIO UI 中手动创建之前都是无效的
|
||||
S3_ACCESS_KEY_ID=YOUR_S3_ACCESS_KEY_ID
|
||||
# 必填,S3 的 Secret Access Key,对于 MinIO 来说,直到在 MinIO UI 中手动创建之前都是无效的
|
||||
S3_SECRET_ACCESS_KEY=YOUR_S3_SECRET_ACCESS_KEY
|
||||
# 必填,S3 的 Endpoint,用于服务端/客户端连接到 S3 API
|
||||
S3_ENDPOINT=https://lobe-s3-api.example.com
|
||||
# 必填,S3 的 Bucket,直到在 MinIO UI 中手动创建之前都是无效的
|
||||
S3_BUCKET=lobe
|
||||
# 选填,S3 的 Enable Path Style
|
||||
# 对于主流 S3 Cloud 服务商,一般填 0 即可;对于自部署的 MinIO,请填 1
|
||||
# 请参考:https://lobehub.com/zh/docs/self-hosting/advanced/s3#s-3-enable-path-style
|
||||
S3_ENABLE_PATH_STYLE=1
|
||||
|
||||
# 其他基础环境变量,视需求而定。注意不要有 ACCESS_CODE
|
||||
# 请参考:https://lobehub.com/zh/docs/self-hosting/environment-variables/basic
|
||||
# 请注意,对于服务端版本,其 API 必须支持嵌入(即 OpenAI text-embedding-3-small)模型,否则无法对上传文件进行处理,但你无需在 OPENAI_MODEL_LIST 中指定此模型
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
@@ -1,71 +0,0 @@
|
||||
name: lobehub
|
||||
services:
|
||||
postgresql:
|
||||
image: pgvector/pgvector:pg16
|
||||
container_name: lobe-postgres
|
||||
ports:
|
||||
- '5432:5432'
|
||||
volumes:
|
||||
- './data:/var/lib/postgresql/data'
|
||||
environment:
|
||||
- 'POSTGRES_DB=lobe'
|
||||
- 'POSTGRES_PASSWORD=uWNZugjBqixf8dxC'
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: always
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
ports:
|
||||
- '9000:9000'
|
||||
- '9001:9001'
|
||||
volumes:
|
||||
- './s3_data:/etc/minio/data'
|
||||
environment:
|
||||
- 'MINIO_ROOT_USER=YOUR_MINIO_USER'
|
||||
- 'MINIO_ROOT_PASSWORD=YOUR_MINIO_PASSWORD'
|
||||
- 'MINIO_DOMAIN=lobe-s3-api.example.com'
|
||||
- 'MINIO_API_CORS_ALLOW_ORIGIN=https://lobe.example.com' # Your LobeChat's domain name.
|
||||
restart: always
|
||||
command: >
|
||||
server /etc/minio/data --address ":9000" --console-address ":9001"
|
||||
|
||||
|
||||
logto:
|
||||
image: svhd/logto
|
||||
container_name: lobe-logto
|
||||
ports:
|
||||
- '3001:3001'
|
||||
- '3002:3002'
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- 'TRUST_PROXY_HEADER=1'
|
||||
- 'DB_URL=postgresql://postgres:uWNZugjBqixf8dxC@postgresql:5432/logto'
|
||||
- 'ENDPOINT=https://lobe-auth-api.example.com'
|
||||
- 'ADMIN_ENDPOINT=https://lobe-auth-ui.example.com'
|
||||
entrypoint: ['sh', '-c', 'npm run cli db seed -- --swe && npm start']
|
||||
|
||||
lobe:
|
||||
image: lobehub/lobehub
|
||||
container_name: lobehub
|
||||
ports:
|
||||
- '3210:3210'
|
||||
depends_on:
|
||||
- postgresql
|
||||
- minio
|
||||
- logto
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
s3_data:
|
||||
driver: local
|
||||
@@ -1,49 +0,0 @@
|
||||
# Required: LobeChat domain for tRPC calls
|
||||
# Ensure this domain is whitelisted in your SSO providers and S3 service CORS settings
|
||||
APP_URL=https://lobe.example.com/
|
||||
|
||||
# Postgres related environment variables
|
||||
# Required: Secret key for encrypting sensitive information. Generate with: openssl rand -base64 32
|
||||
KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ=
|
||||
# Required: Postgres database connection string
|
||||
# Format: postgresql://username:password@host:port/dbname
|
||||
# If using Docker, you can use the container name as the host
|
||||
DATABASE_URL=postgresql://postgres:uWNZugjBqixf8dxC@postgresql:5432/lobe
|
||||
|
||||
# Authentication related environment variables
|
||||
# Required: Auth secret key. Generate with: openssl rand -base64 32
|
||||
AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
|
||||
# Required: Specify the authentication provider
|
||||
AUTH_SSO_PROVIDERS=zitadel
|
||||
|
||||
# ZiTADEL provider configuration
|
||||
# Please refer to:https://lobehub.com/zh/docs/self-hosting/advanced/auth/providers/zitadel
|
||||
AUTH_ZITADEL_ID=285934220675723622
|
||||
AUTH_ZITADEL_SECRET=pe7Nh3lopXkZkfqh5YEDYI2xsbIz08eZKqInOUZxssd3refRia518Apbv3DZ
|
||||
AUTH_ZITADEL_ISSUER=https://zitadel.example.com
|
||||
|
||||
# Proxy settings (if needed, e.g., when using GitHub as an auth provider)
|
||||
# HTTP_PROXY=http://localhost:7890
|
||||
# HTTPS_PROXY=http://localhost:7890
|
||||
|
||||
# S3 related environment variables (example using MinIO)
|
||||
# Required: S3 Access Key ID (for MinIO, invalid until manually created in MinIO UI)
|
||||
S3_ACCESS_KEY_ID=YOUR_S3_ACCESS_KEY_ID
|
||||
# Required: S3 Secret Access Key (for MinIO, invalid until manually created in MinIO UI)
|
||||
S3_SECRET_ACCESS_KEY=YOUR_S3_SECRET_ACCESS_KEY
|
||||
# Required: S3 Endpoint for server/client connections to S3 API
|
||||
S3_ENDPOINT=https://lobe-s3-api.example.com
|
||||
# Required: S3 Bucket (invalid until manually created in MinIO UI)
|
||||
S3_BUCKET=lobe
|
||||
# Optional: S3 Enable Path Style
|
||||
# Use 0 for mainstream S3 cloud providers; use 1 for self-hosted MinIO
|
||||
# See: https://lobehub.com/docs/self-hosting/advanced/s3#s-3-enable-path-style
|
||||
S3_ENABLE_PATH_STYLE=1
|
||||
|
||||
# Other basic environment variables (as needed)
|
||||
# See: https://lobehub.com/docs/self-hosting/environment-variables/basic
|
||||
# Note: For server versions, the API must support embedding models (OpenAI text-embedding-3-small) for file processing
|
||||
# You don't need to specify this model in OPENAI_MODEL_LIST
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
@@ -1,44 +0,0 @@
|
||||
# 必填,LobeChat 域名,用于 tRPC 调用
|
||||
# 请保证此域名在你的 SSO 鉴权服务提供商、S3 服务商的 CORS 白名单中
|
||||
APP_URL=https://lobe.example.com/
|
||||
|
||||
# Postgres 相关,也即 DB 必需的环境变量
|
||||
# 必填,用于加密敏感信息的密钥,可以使用 openssl rand -base64 32 生成
|
||||
KEY_VAULTS_SECRET=Kix2wcUONd4CX51E/ZPAd36BqM4wzJgKjPtz2sGztqQ=
|
||||
# 必填,Postgres 数据库连接字符串,用于连接到数据库
|
||||
# 格式:postgresql://username:password@host:port/dbname,如果你的 pg 实例为 Docker 容器且位于同一 docker-compose 文件中,亦可使用容器名作为 host
|
||||
DATABASE_URL=postgresql://postgres:uWNZugjBqixf8dxC@postgresql:5432/lobe
|
||||
|
||||
# 鉴权服务必需的环境变量
|
||||
# 必填,用于鉴权的密钥,可以使用 openssl rand -base64 32 生成
|
||||
AUTH_SECRET=NX2kaPE923dt6BL2U8e9oSre5RfoT7hg
|
||||
# 必填,指定鉴权服务提供商
|
||||
AUTH_SSO_PROVIDERS=zitadel
|
||||
|
||||
# ZiTADEL 鉴权服务提供商部分
|
||||
# 请参考:https://lobehub.com/zh/docs/self-hosting/advanced/auth/next-auth/zitadel
|
||||
AUTH_ZITADEL_ID=285934220675723622
|
||||
AUTH_ZITADEL_SECRET=pe7Nh3lopXkZkfqh5YEDYI2xsbIz08eZKqInOUZxssd3refRia518Apbv3DZ
|
||||
AUTH_ZITADEL_ISSUER=https://zitadel.example.com
|
||||
|
||||
# S3 相关,也即非结构化数据(文件、图片等)存储必需的环境变量
|
||||
# 这里以 MinIO 为例
|
||||
# 必填,S3 的 Access Key ID,对于 MinIO 来说,直到在 MinIO UI 中手动创建之前都是无效的
|
||||
S3_ACCESS_KEY_ID=YOUR_S3_ACCESS_KEY_ID
|
||||
# 必填,S3 的 Secret Access Key,对于 MinIO 来说,直到在 MinIO UI 中手动创建之前都是无效的
|
||||
S3_SECRET_ACCESS_KEY=YOUR_S3_SECRET_ACCESS_KEY
|
||||
# 必填,S3 的 Endpoint,用于服务端/客户端连接到 S3 API
|
||||
S3_ENDPOINT=https://lobe-s3-api.example.com
|
||||
# 必填,S3 的 Bucket,直到在 MinIO UI 中手动创建之前都是无效的
|
||||
S3_BUCKET=lobe
|
||||
# 选填,S3 的 Enable Path Style
|
||||
# 对于主流 S3 Cloud 服务商,一般填 0 即可;对于自部署的 MinIO,请填 1
|
||||
# 请参考:https://lobehub.com/zh/docs/self-hosting/advanced/s3#s-3-enable-path-style
|
||||
S3_ENABLE_PATH_STYLE=1
|
||||
|
||||
# 其他基础环境变量,视需求而定。注意不要有 ACCESS_CODE
|
||||
# 请参考:https://lobehub.com/zh/docs/self-hosting/environment-variables/basic
|
||||
# 请注意,对于服务端版本,其 API 必须支持嵌入(即 OpenAI text-embedding-3-small)模型,否则无法对上传文件进行处理,但你无需在 OPENAI_MODEL_LIST 中指定此模型
|
||||
# OPENAI_API_KEY=sk-xxxx
|
||||
# OPENAI_PROXY_URL=https://api.openai.com/v1
|
||||
# OPENAI_MODEL_LIST=...
|
||||
@@ -1,69 +0,0 @@
|
||||
name: lobehub
|
||||
services:
|
||||
postgresql:
|
||||
image: pgvector/pgvector:pg16
|
||||
container_name: lobe-postgres
|
||||
ports:
|
||||
- '5432:5432'
|
||||
volumes:
|
||||
- './data:/var/lib/postgresql/data'
|
||||
environment:
|
||||
- 'POSTGRES_DB=lobe'
|
||||
- 'POSTGRES_PASSWORD=uWNZugjBqixf8dxC'
|
||||
healthcheck:
|
||||
test: ['CMD-SHELL', 'pg_isready -U postgres']
|
||||
interval: 5s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
restart: always
|
||||
|
||||
minio:
|
||||
image: minio/minio:RELEASE.2025-04-22T22-12-26Z
|
||||
container_name: lobe-minio
|
||||
ports:
|
||||
- '9000:9000'
|
||||
- '9001:9001'
|
||||
volumes:
|
||||
- './s3_data:/etc/minio/data'
|
||||
environment:
|
||||
- 'MINIO_ROOT_USER=YOUR_MINIO_USER'
|
||||
- 'MINIO_ROOT_PASSWORD=YOUR_MINIO_PASSWORD'
|
||||
- 'MINIO_DOMAIN=lobe-s3-api.example.com'
|
||||
- 'MINIO_API_CORS_ALLOW_ORIGIN=https://lobe.example.com' # Your LobeChat's domain name.
|
||||
restart: always
|
||||
command: >
|
||||
server /etc/minio/data --address ":9000" --console-address ":9001"
|
||||
|
||||
|
||||
zitadel:
|
||||
restart: always
|
||||
image: ghcr.io/zitadel/zitadel:latest
|
||||
container_name: lobe-zitadel
|
||||
command: start-from-init --config /zitadel-config.yaml --steps /zitadel-init-steps.yaml --masterkey "cft3Tekr/rQBOqwoQSCPoncA9BHbn7QJ" --tlsMode external #MasterkeyNeedsToHave32Characters
|
||||
ports:
|
||||
- 8080:8080
|
||||
volumes:
|
||||
- ./zitadel-config.yaml:/zitadel-config.yaml:ro
|
||||
- ./zitadel-init-steps.yaml:/zitadel-init-steps.yaml:ro
|
||||
depends_on:
|
||||
postgresql:
|
||||
condition: service_healthy
|
||||
|
||||
lobe:
|
||||
image: lobehub/lobehub
|
||||
container_name: lobehub
|
||||
ports:
|
||||
- '3210:3210'
|
||||
depends_on:
|
||||
- postgresql
|
||||
- minio
|
||||
- zitadel
|
||||
env_file:
|
||||
- .env
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
data:
|
||||
driver: local
|
||||
s3_data:
|
||||
driver: local
|
||||
@@ -1,25 +0,0 @@
|
||||
Log:
|
||||
Level: 'info'
|
||||
|
||||
ExternalPort: 443
|
||||
ExternalDomain: example.com #Your zitadel's domain name
|
||||
ExternalSecure: true
|
||||
TLS:
|
||||
Enabled: false # ZITADEL_TLS_ENABLED
|
||||
|
||||
# If not using the docker compose example, adjust these values for connecting ZITADEL to your PostgreSQL
|
||||
Database:
|
||||
postgres:
|
||||
Host: postgresql
|
||||
Port: 5432
|
||||
Database: zitadel
|
||||
User:
|
||||
Username: 'zitadel'
|
||||
Password: 'zitadel'
|
||||
SSL:
|
||||
Mode: 'disable'
|
||||
Admin:
|
||||
Username: 'postgres'
|
||||
Password: 'uWNZugjBqixf8dxC' #postgres password
|
||||
SSL:
|
||||
Mode: 'disable'
|
||||
@@ -1,11 +0,0 @@
|
||||
# All possible options and their defaults: https://github.com/zitadel/zitadel/blob/main/cmd/setup/steps.yaml
|
||||
FirstInstance:
|
||||
Org:
|
||||
Human:
|
||||
# use the loginname root@zitadel.localhost, replace localhost with your configured external domain
|
||||
Username: 'root'
|
||||
# The password must be 8 characters or more and must contain uppercase letters, lowercase letters, symbols, and numbers. The first login will require a password change.
|
||||
Password: 'Password1!'
|
||||
Email:
|
||||
# Optional, if set, can be used to log in with email.
|
||||
Address: 'example@zitadel.com' # ZITADEL_FIRSTINSTANCE_ORG_HUMAN_EMAIL_ADDRESS
|
||||
+22
-1
@@ -668,12 +668,33 @@ section_regenerate_secrets() {
|
||||
echo $(show_message "security_secrect_regenerate_failed") "RUSTFS_SECRET_KEY"
|
||||
RUSTFS_SECRET_KEY="YOUR_RUSTFS_PASSWORD"
|
||||
else
|
||||
# Search and replace the value of S3_SECRET_ACCESS_KEY in .env
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#^RUSTFS_SECRET_KEY=.*#RUSTFS_SECRET_KEY=${RUSTFS_SECRET_KEY}#" .env
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $(show_message "security_secrect_regenerate_failed") "RUSTFS_SECRET_KEY in \`.env\`"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Generate KEY_VAULTS_SECRET (base64 encoded 32 bytes)
|
||||
KEY_VAULTS_SECRET=$(openssl rand -base64 32)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $(show_message "security_secrect_regenerate_failed") "KEY_VAULTS_SECRET"
|
||||
else
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#^KEY_VAULTS_SECRET=.*#KEY_VAULTS_SECRET=${KEY_VAULTS_SECRET}#" .env
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $(show_message "security_secrect_regenerate_failed") "KEY_VAULTS_SECRET in \`.env\`"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Generate AUTH_SECRET (base64 encoded 32 bytes)
|
||||
AUTH_SECRET=$(openssl rand -base64 32)
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $(show_message "security_secrect_regenerate_failed") "AUTH_SECRET"
|
||||
else
|
||||
sed "${SED_INPLACE_ARGS[@]}" "s#^AUTH_SECRET=.*#AUTH_SECRET=${AUTH_SECRET}#" .env
|
||||
if [ $? -ne 0 ]; then
|
||||
echo $(show_message "security_secrect_regenerate_failed") "AUTH_SECRET in \`.env\`"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
show_message "ask_regenerate_secrets"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"image": "/blog/assets7f3b38c1d76cceb91edb29d6b1eb60db.webp",
|
||||
"id": "2025-12-20-mcp",
|
||||
"date": "2025-12-20",
|
||||
"versionRange": ["1.142.8", "1.143"]
|
||||
"versionRange": ["1.142.8", "1.143.0"]
|
||||
},
|
||||
{
|
||||
"image": "/blog/assets3a7f0b29839603336e39e923b423409b.webp",
|
||||
|
||||
@@ -114,7 +114,7 @@ AUTH_OKTA_ISSUER: process.env.AUTH_OKTA_ISSUER,
|
||||
|
||||
### Step 4: Update Documentation (Optional)
|
||||
|
||||
Add provider documentation in `docs/self-hosting/advanced/auth.mdx` and `docs/self-hosting/advanced/auth.zh-CN.mdx`.
|
||||
Add provider documentation in `docs/self-hosting/auth.mdx` and `docs/self-hosting/auth.zh-CN.mdx`.
|
||||
|
||||
## Adding a Built-in Provider
|
||||
|
||||
|
||||
@@ -115,7 +115,7 @@ AUTH_OKTA_ISSUER: process.env.AUTH_OKTA_ISSUER,
|
||||
|
||||
### 步骤 4: 更新文档(可选)
|
||||
|
||||
在 `docs/self-hosting/advanced/auth.mdx` 和 `docs/self-hosting/advanced/auth.zh-CN.mdx` 中添加提供商文档。
|
||||
在 `docs/self-hosting/auth.mdx` 和 `docs/self-hosting/auth.zh-CN.mdx` 中添加提供商文档。
|
||||
|
||||
## 添加内置提供商
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ tags:
|
||||
|
||||
## Parameter Standardization
|
||||
|
||||
All image generation models must use the standard parameters defined in `src/libs/standard-parameters/index.ts`. This ensures parameter consistency across different Providers, creating a more unified user experience.
|
||||
All image generation models must use the standard parameters defined in `packages/model-bank/src/standard-parameters/index.ts`. This ensures parameter consistency across different Providers, creating a more unified user experience.
|
||||
|
||||
**Supported Standard Parameters**:
|
||||
|
||||
@@ -32,7 +32,7 @@ All image generation models must use the standard parameters defined in `src/lib
|
||||
|
||||
These models can be requested using the OpenAI SDK, with request parameters and return values consistent with DALL-E and GPT-Image-X series.
|
||||
|
||||
Taking Zhipu's CogView-4 as an example, which is an OpenAI-compatible model, you can add it by adding the model configuration in the corresponding AI models file `src/config/aiModels/zhipu.ts`:
|
||||
Taking Zhipu's CogView-4 as an example, which is an OpenAI-compatible model, you can add it by adding the model configuration in the corresponding AI models file `packages/model-bank/src/aiModels/zhipu.ts`:
|
||||
|
||||
```ts
|
||||
const zhipuImageModels: AIImageModelCard[] = [
|
||||
@@ -71,7 +71,7 @@ Most Providers use `openaiCompatibleFactory` for OpenAI compatibility. You can p
|
||||
|
||||
1. **Read Provider documentation and standard parameter definitions**
|
||||
- Review the Provider's image generation API documentation to understand request and response formats
|
||||
- Read `src/libs/standard-parameters/index.ts` to understand supported parameters
|
||||
- Read `packages/model-bank/src/standard-parameters/index.ts` to understand supported parameters
|
||||
- Add image model configuration in the corresponding AI models file
|
||||
|
||||
2. **Implement custom createImage method**
|
||||
@@ -87,7 +87,7 @@ Most Providers use `openaiCompatibleFactory` for OpenAI compatibility. You can p
|
||||
**Code Example**:
|
||||
|
||||
```ts
|
||||
// src/libs/model-runtime/provider-name/createImage.ts
|
||||
// packages/model-runtime/src/providers/<provider-name>/createImage.ts
|
||||
export const createProviderImage = async (
|
||||
payload: ImageGenerationPayload,
|
||||
options: any,
|
||||
@@ -112,7 +112,7 @@ export const createProviderImage = async (
|
||||
```
|
||||
|
||||
```ts
|
||||
// src/libs/model-runtime/provider-name/index.ts
|
||||
// packages/model-runtime/src/providers/<provider-name>/index.ts
|
||||
export const LobeProviderAI = openaiCompatibleFactory({
|
||||
constructorOptions: {
|
||||
// ... other configurations
|
||||
@@ -130,7 +130,7 @@ If your Provider has an independent class implementation, you can directly add t
|
||||
|
||||
1. **Read Provider documentation and standard parameter definitions**
|
||||
- Review the Provider's image generation API documentation
|
||||
- Read `src/libs/standard-parameters/index.ts`
|
||||
- Read `packages/model-bank/src/standard-parameters/index.ts`
|
||||
- Add image model configuration in the corresponding AI models file
|
||||
|
||||
2. **Implement createImage method in Provider class**
|
||||
@@ -144,7 +144,7 @@ If your Provider has an independent class implementation, you can directly add t
|
||||
**Code Example**:
|
||||
|
||||
```ts
|
||||
// src/libs/model-runtime/provider-name/index.ts
|
||||
// packages/model-runtime/src/providers/<provider-name>/index.ts
|
||||
export class LobeProviderAI {
|
||||
async createImage(
|
||||
payload: ImageGenerationPayload,
|
||||
|
||||
@@ -13,7 +13,7 @@ tags:
|
||||
|
||||
## 参数标准化
|
||||
|
||||
所有图像生成模型都必须使用 `src/libs/standard-parameters/index.ts` 中定义的标准参数。这确保了不同 Provider 之间的参数一致性,让用户体验更加统一。
|
||||
所有图像生成模型都必须使用 `packages/model-bank/src/standard-parameters/index.ts` 中定义的标准参数。这确保了不同 Provider 之间的参数一致性,让用户体验更加统一。
|
||||
|
||||
**支持的标准参数**:
|
||||
|
||||
@@ -30,7 +30,7 @@ tags:
|
||||
|
||||
指的是可以使用 openai SDK 进行请求,并且请求参数和和返回值和 dall-e 以及 gpt-image-x 系列一致。
|
||||
|
||||
以智谱的 CogView-4 为例,它是一个兼容 openai 请求格式的模型。你只需要在对应的 ai models 文件 `src/config/aiModels/zhipu.ts` 中,添加模型配置,例如:
|
||||
以智谱的 CogView-4 为例,它是一个兼容 openai 请求格式的模型。你只需要在对应的 ai models 文件 `packages/model-bank/src/aiModels/zhipu.ts` 中,添加模型配置,例如:
|
||||
|
||||
```ts
|
||||
const zhipuImageModels: AIImageModelCard[] = [
|
||||
@@ -69,7 +69,7 @@ const zhipuImageModels: AIImageModelCard[] = [
|
||||
|
||||
1. **阅读 Provider 官方文档和标准参数定义**
|
||||
- 查看 Provider 的图像生成 API 文档,了解请求格式和响应格式
|
||||
- 阅读 `src/libs/standard-parameters/index.ts`,了解支持的参数
|
||||
- 阅读 `packages/model-bank/src/standard-parameters/index.ts`,了解支持的参数
|
||||
- 在对应的 ai models 文件中增加 image model 配置
|
||||
|
||||
2. **实现自定义的 createImage 方法**
|
||||
@@ -85,7 +85,7 @@ const zhipuImageModels: AIImageModelCard[] = [
|
||||
**代码示例**:
|
||||
|
||||
```ts
|
||||
// src/libs/model-runtime/provider-name/createImage.ts
|
||||
// packages/model-runtime/src/providers/<provider-name>/createImage.ts
|
||||
export const createProviderImage = async (
|
||||
payload: ImageGenerationPayload,
|
||||
options: any,
|
||||
@@ -110,7 +110,7 @@ export const createProviderImage = async (
|
||||
```
|
||||
|
||||
```ts
|
||||
// src/libs/model-runtime/provider-name/index.ts
|
||||
// packages/model-runtime/src/providers/<provider-name>/index.ts
|
||||
export const LobeProviderAI = openaiCompatibleFactory({
|
||||
constructorOptions: {
|
||||
// ... 其他配置
|
||||
@@ -128,7 +128,7 @@ export const LobeProviderAI = openaiCompatibleFactory({
|
||||
|
||||
1. **阅读 Provider 官方文档和标准参数定义**
|
||||
- 查看 Provider 的图像生成 API 文档
|
||||
- 阅读 `src/libs/standard-parameters/index.ts`
|
||||
- 阅读 `packages/model-bank/src/standard-parameters/index.ts`
|
||||
- 在对应的 ai models 文件中增加 image model 配置
|
||||
|
||||
2. **在 Provider 类中实现 createImage 方法**
|
||||
@@ -142,7 +142,7 @@ export const LobeProviderAI = openaiCompatibleFactory({
|
||||
**代码示例**:
|
||||
|
||||
```ts
|
||||
// src/libs/model-runtime/provider-name/index.ts
|
||||
// packages/model-runtime/src/providers/<provider-name>/index.ts
|
||||
export class LobeProviderAI {
|
||||
async createImage(
|
||||
payload: ImageGenerationPayload,
|
||||
|
||||
@@ -1,50 +1,114 @@
|
||||
---
|
||||
title: Architecture Design
|
||||
description: >-
|
||||
Explore the architecture of LobeHub, an AI chat app built on Next.js,
|
||||
featuring frontend, APIs, and markets.
|
||||
Explore the architecture of LobeHub, an open-source AI Agent platform
|
||||
built on Next.js, covering frontend, backend, runtime, and data storage.
|
||||
tags:
|
||||
- LobeHub
|
||||
- AI Chat Application
|
||||
- Next.js
|
||||
- Architecture Design
|
||||
- Frontend Development
|
||||
- Agent Platform
|
||||
- Next.js
|
||||
---
|
||||
|
||||
# Architecture Design
|
||||
|
||||
LobeHub is an AI chat application built on the Next.js framework, aiming to provide an AI productivity platform that enables users to interact with AI through natural language. The following is an overview of the architecture design of LobeHub:
|
||||
LobeHub is an open-source AI Agent platform built on Next.js, enabling users to interact with AI through natural language, use tools, manage knowledge bases, and more. The following is an overview of LobeHub's architecture design.
|
||||
|
||||
## Application Architecture Overview
|
||||
|
||||
The overall architecture of LobeHub consists of the frontend, EdgeRuntime API, Agents Market, Plugin Market, and independent plugins. These components collaborate to provide a complete AI experience.
|
||||
The overall architecture of LobeHub consists of the following core layers:
|
||||
|
||||
```plaintext
|
||||
+---------------------+--------------------------------------------------+
|
||||
| Layer | Description |
|
||||
+---------------------+--------------------------------------------------+
|
||||
| Frontend | Next.js RSC + React Router DOM hybrid SPA |
|
||||
| Backend API | RESTful WebAPI + tRPC Routers |
|
||||
| Runtime | Model Runtime + Agent Runtime |
|
||||
| Auth | Better Auth (email/password + SSO) |
|
||||
| Data Storage | PostgreSQL + Redis + S3 |
|
||||
| Marketplace | Agent Market + MCP Tool Market |
|
||||
+---------------------+--------------------------------------------------+
|
||||
```
|
||||
|
||||
## Frontend Architecture
|
||||
|
||||
The frontend of LobeHub adopts the Next.js framework, leveraging its powerful server-side rendering (SSR) capability and routing functionality. The frontend utilizes a stack of technologies, including the antd component library, lobe-ui AIGC component library, zustand state management, swr request library, i18next internationalization library, and more. These technologies collectively support the functionality and features of LobeHub.
|
||||
The frontend uses the Next.js framework with a **Next.js RSC + React Router DOM hybrid routing** approach: Next.js App Router handles server-rendered pages (e.g., auth pages), while React Router DOM powers the main SPA.
|
||||
|
||||
The components in the frontend architecture include app, components, config, const, features, helpers, hooks, layout, locales, migrations, prompts, services, store, styles, types, and utils. Each component has specific responsibilities and collaborates with others to achieve different functionalities.
|
||||
Key tech stack:
|
||||
|
||||
## Edge Runtime API
|
||||
- **UI Components**: `@lobehub/ui`, antd
|
||||
- **CSS-in-JS**: antd-style
|
||||
- **State Management**: zustand (slice pattern)
|
||||
- **Data Fetching**: SWR + tRPC
|
||||
- **i18n**: react-i18next
|
||||
|
||||
The Edge Runtime API is one of the core components of LobeHub, responsible for handling the core logic of AI conversations. It provides interaction interfaces with the AI engine, including natural language processing, intent recognition, and response generation. The EdgeRuntime API communicates with the frontend, receiving user input and returning corresponding responses.
|
||||
Frontend code is organized by responsibility under `src/`. See [Directory Structure](/docs/development/basic/folder-structure) for details.
|
||||
|
||||
## Agents Market
|
||||
## Backend API
|
||||
|
||||
The Agents Market is a crucial part of LobeHub, providing various AI agents for different scenarios to handle specific tasks and domains. The Agents Market also offers functionality for discovering and uploading agents, allowing users to find agents created by others and easily share their own agents in the market.
|
||||
The backend provides two API styles:
|
||||
|
||||
## Plugin Market
|
||||
- **RESTful WebAPI** (`src/app/(backend)/webapi/`): Handles endpoints requiring special processing such as chat streaming, TTS, and file serving
|
||||
- **tRPC Routers** (`src/server/routers/`): Type-safe main business routes, grouped by runtime:
|
||||
- `lambda/` — Main business (agent, session, message, topic, file, knowledge, settings, etc.)
|
||||
- `async/` — Long-running async operations (file processing, image generation, RAG evaluation)
|
||||
- `tools/` — Tool invocations (search, MCP, market)
|
||||
- `mobile/` — Mobile-specific routes
|
||||
|
||||
The Plugin Market is another key component of LobeHub, offering various plugins to extend the functionality and features of LobeHub. Plugins can be independent functional modules or integrated with agents from the Agents Market. During conversations, the assistant automatically identifies user input, recognizes suitable plugins, and passes them to the corresponding plugins for processing and returns the results.
|
||||
## Runtime
|
||||
|
||||
## Security and Performance Optimization
|
||||
### Model Runtime
|
||||
|
||||
LobeHub's security strategy includes authentication and permission management. Users need to authenticate before using LobeHub, and operations are restricted based on the user's permissions.
|
||||
`@lobechat/model-runtime` (`packages/model-runtime/`) is the LLM API adapter layer that normalizes API differences across 30+ AI providers (OpenAI, Anthropic, Google, Bedrock, Ollama, etc.), providing a unified calling interface. Each provider has its own adapter implementation. It is stateless — each call is independent.
|
||||
|
||||
To optimize performance, LobeHub utilizes Next.js SSR functionality to achieve fast page loading and response times. Additionally, a series of performance optimization measures are implemented, including code splitting, caching, and resource compression.
|
||||
### Agent Runtime
|
||||
|
||||
## Development and Deployment Process
|
||||
`@lobechat/agent-runtime` (`packages/agent-runtime/`) is the agent orchestration engine that sits above Model Runtime, driving the full lifecycle of multi-step AI agent behavior:
|
||||
|
||||
LobeHub's development process includes version control, testing, continuous integration, and continuous deployment. The development team uses version control systems for code management and conducts unit and integration testing to ensure code quality. Continuous integration and deployment processes ensure rapid delivery and deployment of code.
|
||||
- **Plan-Execute Loop**: Core state machine cycling through LLM calls → tool execution → result processing
|
||||
- **Tool Invocation & Batch Execution**: Supports single and batch tool calls
|
||||
- **Human-in-the-Loop**: Security checks and human approval flows
|
||||
- **Context Compression**: Manages context window limits
|
||||
- **Usage & Cost Tracking**: Accumulates token usage and monetary costs
|
||||
- **Multi-Agent Orchestration**: `GroupOrchestrationRuntime` supports Supervisor + Executor pattern for multi-agent collaboration
|
||||
|
||||
The above is a brief introduction to the architecture design of LobeHub, detailing the responsibilities and collaboration of each component, as well as the impact of design decisions on application functionality and performance.
|
||||
In short: Model Runtime handles "how to communicate with an LLM provider"; Agent Runtime handles "how to run a complete agent using LLMs, tools, and human approvals."
|
||||
|
||||
## Authentication
|
||||
|
||||
LobeHub uses [Better Auth](https://www.better-auth.com/) as the authentication framework, supporting:
|
||||
|
||||
- Email + password registration and login
|
||||
- SSO single sign-on (GitHub, Google, and various OAuth providers)
|
||||
|
||||
Auth configuration is in `src/auth.ts`, with related routes under `src/app/(backend)/api/`.
|
||||
|
||||
## Data Storage
|
||||
|
||||
```plaintext
|
||||
+---------------+----------------------------------------------+
|
||||
| Storage | Usage |
|
||||
+---------------+----------------------------------------------+
|
||||
| PostgreSQL | Primary database for users, sessions, |
|
||||
| | messages, agent configs, etc. |
|
||||
| Redis | Caching, session state, rate limiting |
|
||||
| S3 | File storage (uploads, images, knowledge |
|
||||
| | base files, etc.) |
|
||||
+---------------+----------------------------------------------+
|
||||
```
|
||||
|
||||
Database operations use Drizzle ORM, with schemas defined in `packages/database/src/schemas/`.
|
||||
|
||||
## Marketplace
|
||||
|
||||
- **Agent Market**: Provides AI agents for various scenarios; users can discover, use, and share agents
|
||||
- **MCP Tool Market**: Discover and integrate MCP tools to extend agent capabilities
|
||||
|
||||
## Development and Deployment
|
||||
|
||||
- **Version Control**: Git + GitHub, gitmoji commit conventions
|
||||
- **Code Quality**: ESLint, Stylelint, TypeScript type checking, circular dependency detection (dpdm), dead code detection (knip)
|
||||
- **Testing**: Vitest unit tests + Cucumber/Playwright E2E tests
|
||||
- **CI/CD**: GitHub Actions for automated testing, building, and releasing
|
||||
- **Deployment**: Supports Vercel, Docker, and self-hosting on major cloud platforms
|
||||
|
||||
@@ -1,48 +1,110 @@
|
||||
---
|
||||
title: 架构设计
|
||||
description: 深入了解 LobeHub 的架构设计,包括前端、API 和市场组件。
|
||||
description: 深入了解 LobeHub 的架构设计,包括前端、后端、运行时和数据存储。
|
||||
tags:
|
||||
- LobeHub
|
||||
- 架构设计
|
||||
- AI 聊天应用
|
||||
- Agent 平台
|
||||
- Next.js
|
||||
- Edge Runtime API
|
||||
---
|
||||
|
||||
# 架构设计
|
||||
|
||||
LobeHub 是一个基于 Next.js 框架构建的 AI 聊天应用,旨在提供一个 AI 生产力平台,使用户能够与 AI 进行自然语言交互。以下是 LobeHub 的架构设计介稿:
|
||||
LobeHub 是一个基于 Next.js 构建的开源 AI Agent 平台,使用户能够与 AI 进行自然语言交互、使用工具、管理知识库等。以下是 LobeHub 的架构设计概览。
|
||||
|
||||
## 应用架构概览
|
||||
|
||||
LobeHub 的整体架构由前端、EdgeRuntime API、Agents 市场、插件市场和独立插件组成。这些组件相互协作,以提供完整的 AI 体验。
|
||||
LobeHub 的整体架构由以下核心层组成:
|
||||
|
||||
```plaintext
|
||||
+---------------------+--------------------------------------------------+
|
||||
| Layer | Description |
|
||||
+---------------------+--------------------------------------------------+
|
||||
| Frontend | Next.js RSC + React Router DOM 混合路由 SPA |
|
||||
| Backend API | RESTful WebAPI + tRPC Routers |
|
||||
| Runtime | Model Runtime + Agent Runtime |
|
||||
| Auth | Better Auth(邮箱密码 + SSO) |
|
||||
| Data Storage | PostgreSQL + Redis + S3 |
|
||||
| Marketplace | Agent 市场 + MCP 工具市场 |
|
||||
+---------------------+--------------------------------------------------+
|
||||
```
|
||||
|
||||
## 前端架构
|
||||
|
||||
LobeHub 的前端采用 Next.js 框架,利用其强大的 SSR(服务器端渲染)能力和路由功能。前端使用了一系列技术栈,包括 antd 组件库和 lobe-ui AIGC 组件库、zustand 状态管理、swr 请求库、i18next 国际化库等。这些技术栈共同支持了 LobeHub 的功能和特性。
|
||||
前端采用 Next.js 框架,使用 **Next.js RSC + React Router DOM 混合路由**方案:Next.js App Router 处理服务端渲染页面(如认证页),React Router DOM 承载主应用 SPA。
|
||||
|
||||
前端架构中的组件包括 app、components、config、const、features、helpers、hooks、layout、locales、migrations、prompts、services、store、styles、types 和 utils。每个组件都有特定的职责,并与其他组件协同工作,以实现不同的功能。
|
||||
主要技术栈:
|
||||
|
||||
## Edge Runtime API
|
||||
- **UI 组件**:`@lobehub/ui`、antd
|
||||
- **CSS-in-JS**:antd-style
|
||||
- **状态管理**:zustand(slice 模式)
|
||||
- **数据请求**:SWR + tRPC
|
||||
- **国际化**:react-i18next
|
||||
|
||||
Edge Runtime API 是 LobeHub 的核心组件之一,负责处理 AI 会话的核心逻辑。它提供了与 AI 引擎的交互接口,包括自然语言处理、意图识别和回复生成等。EdgeRuntime API 与前端进行通信,接收用户的输入并返回相应的回复。
|
||||
前端代码按职责分层在 `src/` 目录下,详见 [目录架构](/zh/docs/development/basic/folder-structure)。
|
||||
|
||||
## Agents 市场
|
||||
## 后端 API
|
||||
|
||||
Agents 市场是 LobeHub 的一个重要组成部分,它提供了各种不同场景的 AI Agent,用于处理特定的任务和领域。Agents 市场还提供了使用和上传 Agent 的功能,使用户能够发现其他人制作的 Agent ,也可以一键分享自己的 Agent 到市场上。
|
||||
后端提供两种 API 形式:
|
||||
|
||||
## 插件市场
|
||||
- **RESTful WebAPI**(`src/app/(backend)/webapi/`):处理 chat 流式响应、TTS、文件服务等需要特殊处理的端点
|
||||
- **tRPC Routers**(`src/server/routers/`):类型安全的主要业务路由,按运行时分组:
|
||||
- `lambda/` — 主业务(agent、session、message、topic、file、knowledge、settings 等)
|
||||
- `async/` — 耗时异步操作(文件处理、图像生成、RAG 评估)
|
||||
- `tools/` — 工具调用(search、MCP、market)
|
||||
- `mobile/` — 移动端专用
|
||||
|
||||
插件市场是 LobeHub 的另一个关键组件,它提供了各种插件,用于扩展 LobeHub 的功能和特性。插件可以是独立的功能模块,也可以与 Agents 市场的 Agent 进行集成。在会话中,助手将自动识别用户的输入,并识别适合的插件并传递给相应的插件进行处理,并返回处理结果。
|
||||
## Runtime
|
||||
|
||||
## 安全性和性能优化
|
||||
### Model Runtime
|
||||
|
||||
LobeHub 的安全性策略包括身份验证和权限管理。用户需要进行身份验证后才能使用 LobeHub,同时根据用户的权限进行相应的操作限制。
|
||||
`@lobechat/model-runtime`(`packages/model-runtime/`)是 LLM API 适配层,抹平了 30+ 不同 AI Provider 之间的 API 差异(OpenAI、Anthropic、Google、Bedrock、Ollama 等),提供统一的调用接口。每个 Provider 有独立的适配器实现。它是无状态的,每次调用独立。
|
||||
|
||||
为了优化性能,LobeHub 使用了 Next.js 的 SSR 功能,实现了快速的页面加载和响应时间。此外,还采用了一系列的性能优化措施,包括代码分割、缓存和资源压缩等。
|
||||
### Agent Runtime
|
||||
|
||||
## 开发和部署流程
|
||||
`@lobechat/agent-runtime`(`packages/agent-runtime/`)是 Agent 编排引擎,位于 Model Runtime 之上,负责驱动多步 AI Agent 行为的完整生命周期:
|
||||
|
||||
LobeHub 的开发流程包括版本控制、测试、持续集成和持续部署。开发团队使用版本控制系统进行代码管理,并进行单元测试和集成测试以确保代码质量。持续集成和持续部署流程确保了代码的快速交付和部署。
|
||||
- **Plan-Execute 循环**:核心状态机,循环执行 LLM 调用 → 工具执行 → 结果处理
|
||||
- **工具调用与批量执行**:支持单工具和批量工具调用
|
||||
- **Human-in-the-Loop**:安全检查、人工审批流程
|
||||
- **上下文压缩**:管理上下文窗口
|
||||
- **用量与成本追踪**:累计 token 用量和费用
|
||||
- **多 Agent 协作**:`GroupOrchestrationRuntime` 支持 Supervisor + Executor 模式的多 Agent 编排
|
||||
|
||||
以上是 LobeHub 的架构设计介绍简介,详细解释了各个组件的职责和协作方式,以及设计决策对应用功能和性能的影响。
|
||||
简言之:Model Runtime 解决 "如何与 LLM Provider 通信",Agent Runtime 解决 "如何运行一个使用 LLM、工具、人工审批的完整 Agent"。
|
||||
|
||||
## 认证鉴权
|
||||
|
||||
LobeHub 使用 [Better Auth](https://www.better-auth.com/) 作为认证框架,支持:
|
||||
|
||||
- 邮箱 + 密码注册登录
|
||||
- SSO 单点登录(GitHub、Google 等多种 OAuth Provider)
|
||||
|
||||
认证配置位于 `src/auth.ts`,相关路由在 `src/app/(backend)/api/` 下。
|
||||
|
||||
## 数据存储
|
||||
|
||||
```plaintext
|
||||
+---------------+----------------------------------------------+
|
||||
| Storage | Usage |
|
||||
+---------------+----------------------------------------------+
|
||||
| PostgreSQL | 主数据库,存储用户、会话、消息、Agent 配置等 |
|
||||
| Redis | 缓存、会话状态、速率限制 |
|
||||
| S3 | 文件存储(用户上传、图片、知识库文件等) |
|
||||
+---------------+----------------------------------------------+
|
||||
```
|
||||
|
||||
数据库使用 Drizzle ORM 操作,schema 定义在 `packages/database/src/schemas/`。
|
||||
|
||||
## 市场
|
||||
|
||||
- **Agent 市场**:提供各种场景的 AI Agent,用户可以发现、使用和分享 Agent
|
||||
- **MCP 工具市场**:发现和集成 MCP 工具,扩展 Agent 的能力
|
||||
|
||||
## 开发和部署
|
||||
|
||||
- **版本控制**:Git + GitHub,gitmoji commit 规范
|
||||
- **代码质量**:ESLint、Stylelint、TypeScript 类型检查、循环依赖检测 (dpdm)、死代码检测 (knip)
|
||||
- **测试**:Vitest 单元测试 + Cucumber/Playwright E2E 测试
|
||||
- **CI/CD**:GitHub Actions 自动化测试、构建和发布
|
||||
- **部署**:支持 Vercel、Docker、各大云平台自托管
|
||||
|
||||
+339
-276
@@ -1,357 +1,420 @@
|
||||
---
|
||||
title: LobeHub API Client-Server Interaction Logic
|
||||
title: Chat API Client-Server Interaction Logic
|
||||
description: >-
|
||||
Explore the client-server interaction logic of LobeHub API, including event
|
||||
sequences.
|
||||
Explore the client-server interaction logic of LobeChat
|
||||
Chat API, including event sequences and core components.
|
||||
tags:
|
||||
- LobeHub API
|
||||
- Chat API
|
||||
- Client-Server Interaction
|
||||
- Event Sequences
|
||||
- API Logic
|
||||
- Model Runtime
|
||||
- Agent Runtime
|
||||
- MCP
|
||||
---
|
||||
|
||||
# LobeHub API Client-Server Interaction Logic
|
||||
# Chat API Client-Server Interaction Logic
|
||||
|
||||
This document explains the implementation logic of LobeHub API in client-server interactions, including event sequences and core components involved.
|
||||
This document explains the implementation logic of
|
||||
LobeChat Chat API in client-server interactions,
|
||||
including event sequences and core components involved.
|
||||
|
||||
## Interaction Sequence Diagram
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as Frontend Client
|
||||
participant ChatService as Frontend ChatService
|
||||
participant AgentLoop as Agent Runtime Loop
|
||||
participant ChatService as ChatService
|
||||
participant ChatAPI as Backend Chat API
|
||||
participant AgentRuntime as AgentRuntime
|
||||
participant ModelRuntime as Model Runtime
|
||||
participant ModelProvider as Model Provider API
|
||||
participant PluginGateway as Plugin Gateway
|
||||
participant ToolExecution as Tool Execution Layer
|
||||
|
||||
Client->>ChatService: Call createAssistantMessage
|
||||
Note over ChatService: Process messages, tools, and parameters
|
||||
Client->>AgentLoop: sendMessage()
|
||||
Note over AgentLoop: Create GeneralChatAgent + AgentRuntime
|
||||
|
||||
ChatService->>ChatService: Call getChatCompletion
|
||||
Note over ChatService: Prepare request parameters
|
||||
loop Agent Plan-Execute Loop
|
||||
AgentLoop->>AgentLoop: Agent decides next instruction
|
||||
|
||||
ChatService->>ChatAPI: Send POST request to /webapi/chat/[provider]
|
||||
alt call_llm instruction
|
||||
AgentLoop->>ChatService: getChatCompletion
|
||||
Note over ChatService: Context engineering
|
||||
ChatService->>ChatAPI: POST /webapi/chat/[provider]
|
||||
ChatAPI->>ModelRuntime: Initialize ModelRuntime
|
||||
ModelRuntime->>ModelProvider: Chat completion request
|
||||
ModelProvider-->>ChatService: Stream back SSE response
|
||||
ChatService-->>Client: onMessageHandle callback
|
||||
|
||||
ChatAPI->>AgentRuntime: Initialize AgentRuntime
|
||||
Note over AgentRuntime: Create runtime with provider and user config
|
||||
else call_tool instruction
|
||||
AgentLoop->>ToolExecution: Execute tool
|
||||
Note over ToolExecution: Builtin / MCP / Plugin
|
||||
ToolExecution-->>AgentLoop: Return tool result
|
||||
|
||||
ChatAPI->>AgentRuntime: Call chat method
|
||||
AgentRuntime->>ModelProvider: Send chat completion request
|
||||
else request_human_* instruction
|
||||
AgentLoop-->>Client: Request user intervention
|
||||
Client->>AgentLoop: User feedback
|
||||
|
||||
ModelProvider-->>AgentRuntime: Return streaming response
|
||||
AgentRuntime-->>ChatAPI: Process response and return stream
|
||||
|
||||
ChatAPI-->>ChatService: Stream back SSE response
|
||||
|
||||
ChatService->>ChatService: Handle streaming response with fetchSSE
|
||||
Note over ChatService: Process event stream with fetchEventSource
|
||||
|
||||
loop For each data chunk
|
||||
ChatService->>ChatService: Handle different event types (text, tool_calls, reasoning, etc.)
|
||||
ChatService-->>Client: Return current chunk via onMessageHandle callback
|
||||
else finish instruction
|
||||
AgentLoop-->>Client: onFinish callback
|
||||
end
|
||||
end
|
||||
|
||||
ChatService-->>Client: Return complete result via onFinish callback
|
||||
|
||||
Note over ChatService,ModelProvider: Plugin calling scenario
|
||||
ModelProvider-->>ChatService: Return response with tool_calls
|
||||
ChatService->>ChatService: Parse tool calls
|
||||
ChatService->>ChatService: Call runPluginApi
|
||||
ChatService->>PluginGateway: Send plugin request to gateway
|
||||
PluginGateway-->>ChatService: Return plugin execution result
|
||||
ChatService->>ModelProvider: Return plugin result to model
|
||||
ModelProvider-->>ChatService: Generate final response based on plugin result
|
||||
|
||||
Note over ChatService,ModelProvider: Preset task scenario
|
||||
Client->>ChatService: Trigger preset task (e.g., translation, search)
|
||||
ChatService->>ChatService: Call fetchPresetTaskResult
|
||||
Note over Client,ModelProvider: Preset task scenario (bypasses Agent loop)
|
||||
Client->>ChatService: fetchPresetTaskResult
|
||||
ChatService->>ChatAPI: Send preset task request
|
||||
ChatAPI-->>ChatService: Return task result
|
||||
ChatService-->>Client: Return result via callback function
|
||||
ChatService-->>Client: Return result via callback
|
||||
```
|
||||
|
||||
## Main Process Steps
|
||||
|
||||
1. **Client Initiates Request**: The client calls the createAssistantMessage method of the frontend ChatService.
|
||||
### 1. Client Initiates Request
|
||||
|
||||
2. **Frontend Processes Request**:
|
||||
After the user sends a message, `sendMessage()`
|
||||
(`src/store/chat/slices/aiChat/actions/conversationLifecycle.ts`)
|
||||
creates the user message and assistant message placeholder,
|
||||
then calls `internal_execAgentRuntime()`.
|
||||
|
||||
- `src/services/chat.ts` preprocesses messages, tools, and parameters
|
||||
- Calls getChatCompletion to prepare request parameters
|
||||
- Uses `src/utils/fetch/fetchSSE.ts` to send request to backend API
|
||||
### 2. Agent Runtime Drives the Loop
|
||||
|
||||
3. **Backend Processes Request**:
|
||||
Agent Runtime is the **core execution engine**
|
||||
of the entire chat flow. Every chat interaction
|
||||
(from simple Q\&A to complex multi-step tool calling)
|
||||
is driven by the `AgentRuntime.step()` loop.
|
||||
|
||||
- `src/app/(backend)/webapi/chat/[provider]/route.ts` receives the request
|
||||
- Initializes AgentRuntime
|
||||
- Creates the appropriate model instance based on user configuration and provider
|
||||
**Initialization**
|
||||
(`src/store/chat/slices/aiChat/actions/streamingExecutor.ts`):
|
||||
|
||||
4. **Model Call**:
|
||||
1. Resolve agent config (model, provider, plugin list, etc.)
|
||||
2. Create the tool registry via `createAgentToolsEngine()`
|
||||
3. Create `GeneralChatAgent` (the "brain" that decides
|
||||
what to do next) and `AgentRuntime` (the "engine"
|
||||
that executes instructions)
|
||||
4. Inject custom executors via `createAgentExecutors()`
|
||||
|
||||
- `src/libs/agent-runtime/AgentRuntime.ts` calls the respective model provider's API
|
||||
- Returns streaming response
|
||||
**Execution Loop**:
|
||||
|
||||
5. **Process Response**:
|
||||
```ts
|
||||
while (state.status !== 'done' && state.status !== 'error') {
|
||||
result = await runtime.step(state, nextContext);
|
||||
// GeneralChatAgent decides: call_llm → call_tool → call_llm → finish
|
||||
}
|
||||
```
|
||||
|
||||
- Backend converts model response to Stream and returns it
|
||||
- Frontend processes streaming response via fetchSSE and [fetchEventSource](https://github.com/Azure/fetch-event-source)
|
||||
- Handles different types of events (text, tool calls, reasoning, etc.)
|
||||
- Passes results back to client through callback functions
|
||||
At each step, `GeneralChatAgent` returns an
|
||||
`AgentInstruction` based on current state,
|
||||
and `AgentRuntime` executes it via the corresponding
|
||||
executor:
|
||||
|
||||
6. **Plugin Calling Scenario**:
|
||||
- `call_llm`: Call the LLM (see steps 3-5 below)
|
||||
- `call_tool`: Execute tool calls (see step 6 below)
|
||||
- `finish`: End the loop
|
||||
- `compress_context`: Context compression
|
||||
- `request_human_approve` / `request_human_prompt` /
|
||||
`request_human_select`: Request user intervention
|
||||
|
||||
When the AI model returns a `tool_calls` field in its response, it triggers the plugin calling process:
|
||||
### 3. Frontend Processes LLM Request
|
||||
|
||||
- AI model returns response containing `tool_calls`, indicating a need to call tools
|
||||
- Frontend handles tool calls via the `internal_callPluginApi` method
|
||||
- Calls `runPluginApi` method to execute plugin functionality, including retrieving plugin settings and manifest, creating authentication headers, and sending requests to the plugin gateway
|
||||
- After plugin execution completes, the result is returned to the AI model, which generates the final response based on the result
|
||||
When the Agent issues a `call_llm` instruction,
|
||||
the executor calls ChatService:
|
||||
|
||||
**Real-world Examples**:
|
||||
- `src/services/chat/index.ts` preprocesses messages,
|
||||
tools, and parameters
|
||||
- Modules under `src/services/chat/mecha/` perform
|
||||
context engineering, including agent config resolution,
|
||||
model parameter resolution, MCP context injection, etc.
|
||||
- Calls `getChatCompletion` to prepare request parameters
|
||||
- Uses `fetchSSE` from the `@lobechat/fetch-sse` package
|
||||
to send the request to the backend API
|
||||
|
||||
- **Search Plugin**: When a user needs real-time information, the AI calls a web search plugin to retrieve the latest data
|
||||
- **DALL-E Plugin**: When a user requests image generation, the AI calls the DALL-E plugin to create images
|
||||
- **Midjourney Plugin**: Provides higher quality image generation capabilities by calling the Midjourney service via API
|
||||
### 4. Backend Processes Request
|
||||
|
||||
7. **Preset Task Processing**:
|
||||
- `src/app/(backend)/webapi/chat/[provider]/route.ts`
|
||||
receives the request
|
||||
- Calls `initModelRuntimeFromDB` to read user's provider
|
||||
config from the database and initialize ModelRuntime
|
||||
- A tRPC route `src/server/routers/lambda/aiChat.ts`
|
||||
also exists for server-side message sending
|
||||
and structured output scenarios
|
||||
|
||||
Preset tasks are specific predefined functions that are typically triggered when users perform specific actions (rather than being part of the regular chat flow). These tasks use the `fetchPresetTaskResult` method, which is similar to the normal chat flow but uses specially designed prompt chains.
|
||||
### 5. Model Call and Response Processing
|
||||
|
||||
**Execution Timing**: Preset tasks are mainly triggered in the following scenarios:
|
||||
- `ModelRuntime`
|
||||
(`packages/model-runtime/src/core/ModelRuntime.ts`)
|
||||
calls the respective model provider's API
|
||||
and returns a streaming response
|
||||
- Frontend processes the streaming response via
|
||||
`fetchSSE` and
|
||||
[fetchEventSource](https://github.com/Azure/fetch-event-source)
|
||||
- Handles different types of events
|
||||
(text, tool calls, reasoning, etc.)
|
||||
- Passes results back to client through callback functions
|
||||
|
||||
1. **Agent Information Auto-generation**: Triggered when users create or edit an agent
|
||||
### 6. Tool Calling Scenario
|
||||
|
||||
- Agent avatar generation (via `autoPickEmoji` method)
|
||||
- Agent description generation (via `autocompleteAgentDescription` method)
|
||||
- Agent tag generation (via `autocompleteAgentTags` method)
|
||||
- Agent title generation (via `autocompleteAgentTitle` method)
|
||||
When the AI model returns a `tool_calls` field in its
|
||||
response, the Agent issues a `call_tool` instruction.
|
||||
LobeChat supports three types of tools:
|
||||
|
||||
2. **Message Translation**: Triggered when users manually click the translate button (via `translateMessage` method)
|
||||
**Builtin Tools**: Tools built into the application,
|
||||
executed directly via local executors.
|
||||
|
||||
3. **Web Search**: When search is enabled but the model doesn't support tool calling, search functionality is implemented via `fetchPresetTaskResult`
|
||||
- Frontend executes them directly on the client
|
||||
via the `invokeBuiltinTool` method
|
||||
- Includes built-in features like search,
|
||||
DALL-E image generation, etc.
|
||||
|
||||
**Code Examples**:
|
||||
**MCP Tools**: External tools connected via
|
||||
[Model Context Protocol](https://modelcontextprotocol.io/).
|
||||
|
||||
Agent avatar auto-generation implementation:
|
||||
- Frontend calls `MCPService` (`src/services/mcp.ts`)
|
||||
via the `invokeMCPTypePlugin` method
|
||||
- Supports three connection modes:
|
||||
stdio, HTTP (streamable-http/SSE), and cloud (Klavis)
|
||||
- MCP tool registration and discovery is managed
|
||||
through MCP server configuration
|
||||
|
||||
```ts
|
||||
// src/features/AgentSetting/store/action.ts
|
||||
autoPickEmoji: async () => {
|
||||
const { config, meta, dispatchMeta } = get();
|
||||
const systemRole = config.systemRole;
|
||||
**Plugin Tools**: Legacy plugin system,
|
||||
invoked via API gateway.
|
||||
This system is expected to be gradually deprecated
|
||||
in favor of the MCP tool system.
|
||||
|
||||
chatService.fetchPresetTaskResult({
|
||||
onFinish: async (emoji) => {
|
||||
dispatchMeta({ type: 'update', value: { avatar: emoji } });
|
||||
},
|
||||
onLoadingChange: (loading) => {
|
||||
get().updateLoadingState('avatar', loading);
|
||||
},
|
||||
params: merge(
|
||||
get().internal_getSystemAgentForMeta(),
|
||||
chainPickEmoji([meta.title, meta.description, systemRole].filter(Boolean).join(',')),
|
||||
),
|
||||
trace: get().getCurrentTracePayload({ traceName: TraceNameMap.EmojiPicker }),
|
||||
});
|
||||
};
|
||||
```
|
||||
- Frontend calls them via the
|
||||
`invokeDefaultTypePlugin` method
|
||||
- Retrieves plugin settings and manifest,
|
||||
creates authentication headers,
|
||||
and sends requests to the plugin gateway
|
||||
|
||||
Translation feature implementation:
|
||||
After tool execution completes, results are written to
|
||||
the message and returned to the Agent loop.
|
||||
The Agent then calls the LLM again to generate
|
||||
the final response based on tool results.
|
||||
The tool dispatch logic is in
|
||||
`src/store/chat/slices/plugin/actions/pluginTypes.ts`.
|
||||
|
||||
```ts
|
||||
// src/store/chat/slices/translate/action.ts
|
||||
translateMessage: async (id, targetLang) => {
|
||||
// ...omitted code...
|
||||
### 7. Preset Task Processing
|
||||
|
||||
// Detect language
|
||||
chatService.fetchPresetTaskResult({
|
||||
onFinish: async (data) => {
|
||||
if (data && supportLocales.includes(data)) from = data;
|
||||
await updateMessageTranslate(id, { content, from, to: targetLang });
|
||||
},
|
||||
params: merge(translationSetting, chainLangDetect(message.content)),
|
||||
trace: get().getCurrentTracePayload({ traceName: TraceNameMap.LanguageDetect }),
|
||||
});
|
||||
Preset tasks are predefined functions typically triggered
|
||||
when users perform specific actions
|
||||
(bypassing the Agent Runtime loop, calling LLM directly).
|
||||
These tasks use the `fetchPresetTaskResult` method,
|
||||
which is similar to the normal chat flow
|
||||
but uses specially designed prompt chains.
|
||||
|
||||
// Perform translation
|
||||
chatService.fetchPresetTaskResult({
|
||||
onMessageHandle: (chunk) => {
|
||||
if (chunk.type === 'text') {
|
||||
content = chunk.text;
|
||||
internal_dispatchMessage({
|
||||
id,
|
||||
type: 'updateMessageTranslate',
|
||||
value: { content, from, to: targetLang },
|
||||
});
|
||||
}
|
||||
},
|
||||
onFinish: async () => {
|
||||
await updateMessageTranslate(id, { content, from, to: targetLang });
|
||||
internal_toggleChatLoading(false, id, n('translateMessage(end)', { id }) as string);
|
||||
},
|
||||
params: merge(translationSetting, chainTranslate(message.content, targetLang)),
|
||||
trace: get().getCurrentTracePayload({ traceName: TraceNameMap.Translation }),
|
||||
});
|
||||
};
|
||||
```
|
||||
**Execution Timing**:
|
||||
|
||||
8. **Completion**:
|
||||
- When the stream ends, the onFinish callback is called, providing the complete response result
|
||||
1. **Agent Information Auto-generation**: Triggered when
|
||||
users create or edit an agent
|
||||
- Agent avatar generation (via `autoPickEmoji` method)
|
||||
- Agent description generation
|
||||
(via `autocompleteAgentDescription` method)
|
||||
- Agent tag generation
|
||||
(via `autocompleteAgentTags` method)
|
||||
- Agent title generation
|
||||
(via `autocompleteAgentTitle` method)
|
||||
2. **Message Translation**: Triggered when users manually
|
||||
click the translate button
|
||||
(via `translateMessage` method)
|
||||
3. **Web Search**: When search is enabled but the model
|
||||
doesn't support tool calling, search functionality
|
||||
is implemented via `fetchPresetTaskResult`
|
||||
|
||||
## AgentRuntime Overview
|
||||
**Code Examples**:
|
||||
|
||||
AgentRuntime is a core abstraction layer in LobeHub that encapsulates a unified interface for interacting with different AI model providers. Its main responsibilities and features include:
|
||||
Agent avatar auto-generation implementation:
|
||||
|
||||
1. **Unified Abstraction Layer**: AgentRuntime provides a unified interface that hides the implementation details and differences between various AI provider APIs (such as OpenAI, Anthropic, Bedrock, etc.).
|
||||
```ts
|
||||
// src/features/AgentSetting/store/action.ts
|
||||
autoPickEmoji: async () => {
|
||||
const { config, meta, dispatchMeta } = get();
|
||||
const systemRole = config.systemRole;
|
||||
|
||||
2. **Model Initialization**: Through the static `initializeWithProvider` method, it initializes the corresponding runtime instance based on the specified provider and configuration parameters.
|
||||
chatService.fetchPresetTaskResult({
|
||||
onFinish: async (emoji) => {
|
||||
dispatchMeta({ type: 'update', value: { avatar: emoji } });
|
||||
},
|
||||
onLoadingChange: (loading) => {
|
||||
get().updateLoadingState('avatar', loading);
|
||||
},
|
||||
params: merge(
|
||||
get().internal_getSystemAgentForMeta(),
|
||||
chainPickEmoji(
|
||||
[meta.title, meta.description, systemRole]
|
||||
.filter(Boolean)
|
||||
.join(','),
|
||||
),
|
||||
),
|
||||
trace: get().getCurrentTracePayload({
|
||||
traceName: TraceNameMap.EmojiPicker,
|
||||
}),
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
3. **Capability Encapsulation**:
|
||||
Translation feature implementation:
|
||||
|
||||
- `chat` method: Handles chat streaming requests
|
||||
- `models` method: Retrieves model lists
|
||||
- Supports text embedding, text-to-image, text-to-speech, and other functionalities (if supported by the model provider)
|
||||
```ts
|
||||
// src/store/chat/slices/translate/action.ts
|
||||
translateMessage: async (id, targetLang) => {
|
||||
// ...omitted code...
|
||||
|
||||
4. **Plugin Architecture**: Through the `src/libs/agent-runtime/runtimeMap.ts` mapping table, it implements an extensible plugin architecture, making it easy to add new model providers. Currently, it supports over 40 different model providers:
|
||||
// Detect language
|
||||
chatService.fetchPresetTaskResult({
|
||||
onFinish: async (data) => {
|
||||
if (data && supportLocales.includes(data)) from = data;
|
||||
await updateMessageTranslate(id, {
|
||||
content,
|
||||
from,
|
||||
to: targetLang,
|
||||
});
|
||||
},
|
||||
params: merge(
|
||||
translationSetting,
|
||||
chainLangDetect(message.content),
|
||||
),
|
||||
trace: get().getCurrentTracePayload({
|
||||
traceName: TraceNameMap.LanguageDetect,
|
||||
}),
|
||||
});
|
||||
|
||||
```ts
|
||||
export const providerRuntimeMap = {
|
||||
openai: LobeOpenAI,
|
||||
anthropic: LobeAnthropicAI,
|
||||
google: LobeGoogleAI,
|
||||
azure: LobeAzureOpenAI,
|
||||
bedrock: LobeBedrockAI,
|
||||
ollama: LobeOllamaAI,
|
||||
// ...over 40 other model providers
|
||||
};
|
||||
```
|
||||
|
||||
5. **Adapter Pattern**: Internally, it uses the adapter pattern to adapt different provider APIs to the unified `src/libs/agent-runtime/BaseAI.ts` interface:
|
||||
|
||||
```ts
|
||||
export interface LobeRuntimeAI {
|
||||
baseURL?: string;
|
||||
chat(payload: ChatStreamPayload, options?: ChatCompetitionOptions): Promise<Response>;
|
||||
embeddings?(payload: EmbeddingsPayload, options?: EmbeddingsOptions): Promise<Embeddings[]>;
|
||||
models?(): Promise<any>;
|
||||
textToSpeech?: (
|
||||
payload: TextToSpeechPayload,
|
||||
options?: TextToSpeechOptions,
|
||||
) => Promise<ArrayBuffer>;
|
||||
}
|
||||
```
|
||||
|
||||
**Adapter Implementation Examples**:
|
||||
|
||||
1. **OpenRouter Adapter**: OpenRouter is a unified API that allows access to AI models from multiple providers. LobeHub implements support for OpenRouter through an adapter:
|
||||
|
||||
```ts
|
||||
// OpenRouter adapter implementation
|
||||
class LobeOpenRouterAI implements LobeRuntimeAI {
|
||||
client: OpenAI;
|
||||
baseURL: string;
|
||||
|
||||
constructor(options: OpenAICompatibleOptions) {
|
||||
// Initialize OpenRouter client using OpenAI-compatible API
|
||||
this.client = new OpenAI({
|
||||
apiKey: options.apiKey,
|
||||
baseURL: OPENROUTER_BASE_URL,
|
||||
defaultHeaders: {
|
||||
'HTTP-Referer': 'https://github.com/lobehub/lobehub',
|
||||
'X-Title': 'LobeHub',
|
||||
},
|
||||
});
|
||||
this.baseURL = OPENROUTER_BASE_URL;
|
||||
}
|
||||
|
||||
// Implement chat functionality
|
||||
async chat(payload: ChatCompletionCreateParamsBase, options?: RequestOptions) {
|
||||
// Convert LobeHub request format to OpenRouter format
|
||||
// Handle model mapping, message format, etc.
|
||||
return this.client.chat.completions.create(
|
||||
{
|
||||
...payload,
|
||||
model: payload.model || 'openai/gpt-4-turbo', // Default model
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
// Implement other LobeRuntimeAI interface methods
|
||||
// Perform translation
|
||||
chatService.fetchPresetTaskResult({
|
||||
onMessageHandle: (chunk) => {
|
||||
if (chunk.type === 'text') {
|
||||
content = chunk.text;
|
||||
internal_dispatchMessage({
|
||||
id,
|
||||
type: 'updateMessageTranslate',
|
||||
value: { content, from, to: targetLang },
|
||||
});
|
||||
}
|
||||
```
|
||||
},
|
||||
onFinish: async () => {
|
||||
await updateMessageTranslate(id, {
|
||||
content,
|
||||
from,
|
||||
to: targetLang,
|
||||
});
|
||||
internal_toggleChatLoading(
|
||||
false,
|
||||
id,
|
||||
n('translateMessage(end)', { id }) as string,
|
||||
);
|
||||
},
|
||||
params: merge(
|
||||
translationSetting,
|
||||
chainTranslate(message.content, targetLang),
|
||||
),
|
||||
trace: get().getCurrentTracePayload({
|
||||
traceName: TraceNameMap.Translation,
|
||||
}),
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
2. **Google Gemini Adapter**: Gemini is Google's large language model. LobeHub supports Gemini series models through a dedicated adapter:
|
||||
### 8. Completion
|
||||
|
||||
```ts
|
||||
import { GoogleGenerativeAI } from '@google/generative-ai';
|
||||
When the Agent issues a `finish` instruction,
|
||||
the loop ends, and the `onFinish` callback is called
|
||||
with the complete response result.
|
||||
|
||||
// Gemini adapter implementation
|
||||
class LobeGoogleAI implements LobeRuntimeAI {
|
||||
client: GoogleGenerativeAI;
|
||||
baseURL: string;
|
||||
apiKey: string;
|
||||
## Client-Side vs Server-Side Execution
|
||||
|
||||
constructor(options: GoogleAIOptions) {
|
||||
// Initialize Google Generative AI client
|
||||
this.client = new GoogleGenerativeAI(options.apiKey);
|
||||
this.apiKey = options.apiKey;
|
||||
this.baseURL = options.baseURL || GOOGLE_AI_BASE_URL;
|
||||
}
|
||||
The Agent Runtime loop execution location
|
||||
depends on the scenario:
|
||||
|
||||
// Implement chat functionality
|
||||
async chat(payload: ChatCompletionCreateParamsBase, options?: RequestOptions) {
|
||||
// Select appropriate model (supports Gemini Pro, Gemini Flash, etc.)
|
||||
const modelName = payload.model || 'gemini-pro';
|
||||
const model = this.client.getGenerativeModel({ model: modelName });
|
||||
- **Client-side loop** (browser): Regular 1:1 chat,
|
||||
continue generation, group orchestration decisions.
|
||||
The loop runs in the browser, entry point is
|
||||
`internal_execAgentRuntime()`
|
||||
(`src/store/chat/slices/aiChat/actions/streamingExecutor.ts`)
|
||||
- **Server-side loop** (queue/local):
|
||||
Group chat supervisor agent, sub-agent tasks,
|
||||
API/Cron triggers. The loop runs on the server,
|
||||
streaming events to the client via SSE, entry point is
|
||||
`AgentRuntimeService.executeStep()`
|
||||
(`src/server/services/agentRuntime/AgentRuntimeService.ts`),
|
||||
tRPC route at `src/server/routers/lambda/aiAgent.ts`
|
||||
|
||||
// Process multimodal inputs (e.g., images)
|
||||
const contents = this.processMessages(payload.messages);
|
||||
## Model Runtime
|
||||
|
||||
// Set generation parameters
|
||||
const generationConfig = {
|
||||
temperature: payload.temperature,
|
||||
topK: payload.top_k,
|
||||
topP: payload.top_p,
|
||||
maxOutputTokens: payload.max_tokens,
|
||||
};
|
||||
Model Runtime (`packages/model-runtime/`) is the core
|
||||
abstraction layer in LobeChat for interacting with
|
||||
LLM model providers, adapting different provider APIs
|
||||
into a unified interface.
|
||||
|
||||
// Create chat session and get response
|
||||
const chat = model.startChat({
|
||||
generationConfig,
|
||||
history: contents.slice(0, -1),
|
||||
safetySettings: this.getSafetySettings(payload),
|
||||
});
|
||||
**Core Responsibilities**:
|
||||
|
||||
// Handle streaming response
|
||||
return this.handleStreamResponse(chat, contents, options?.signal);
|
||||
}
|
||||
- **Unified Abstraction Layer**: Hides differences
|
||||
between AI provider APIs through the `LobeRuntimeAI`
|
||||
interface
|
||||
(`packages/model-runtime/src/core/BaseAI.ts`)
|
||||
- **Model Initialization**: Initializes the corresponding
|
||||
runtime instance through the provider mapping table
|
||||
(`packages/model-runtime/src/runtimeMap.ts`)
|
||||
- **Capability Encapsulation**: `chat` (streaming chat),
|
||||
`models` (model listing), `embeddings` (text embeddings),
|
||||
`createImage` (image generation),
|
||||
`textToSpeech` (speech synthesis),
|
||||
`generateObject` (structured output)
|
||||
|
||||
// Implement other processing methods
|
||||
private processMessages(messages) {
|
||||
/* ... */
|
||||
}
|
||||
private getSafetySettings(payload) {
|
||||
/* ... */
|
||||
}
|
||||
private handleStreamResponse(chat, contents, signal) {
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
```
|
||||
**Core Interface**:
|
||||
|
||||
**Different Model Implementations**:
|
||||
```ts
|
||||
// packages/model-runtime/src/core/BaseAI.ts
|
||||
export interface LobeRuntimeAI {
|
||||
baseURL?: string;
|
||||
chat?(
|
||||
payload: ChatStreamPayload,
|
||||
options?: ChatMethodOptions,
|
||||
): Promise<Response>;
|
||||
generateObject?(
|
||||
payload: GenerateObjectPayload,
|
||||
options?: GenerateObjectOptions,
|
||||
): Promise<any>;
|
||||
embeddings?(
|
||||
payload: EmbeddingsPayload,
|
||||
options?: EmbeddingsOptions,
|
||||
): Promise<Embeddings[]>;
|
||||
models?(): Promise<any>;
|
||||
createImage?: (
|
||||
payload: CreateImagePayload,
|
||||
) => Promise<CreateImageResponse>;
|
||||
textToSpeech?: (
|
||||
payload: TextToSpeechPayload,
|
||||
options?: TextToSpeechOptions,
|
||||
) => Promise<ArrayBuffer>;
|
||||
}
|
||||
```
|
||||
|
||||
- `src/libs/agent-runtime/openai/index.ts` - OpenAI implementation
|
||||
- `src/libs/agent-runtime/anthropic/index.ts` - Anthropic implementation
|
||||
- `src/libs/agent-runtime/google/index.ts` - Google implementation
|
||||
- `src/libs/agent-runtime/openrouter/index.ts` - OpenRouter implementation
|
||||
**Adapter Architecture**: Through two factory functions —
|
||||
`openaiCompatibleFactory` and
|
||||
`anthropicCompatibleFactory` — most providers can be
|
||||
integrated with minimal configuration. Currently supports
|
||||
over 40 model providers (OpenAI, Anthropic, Google,
|
||||
Azure, Bedrock, Ollama, etc.), with implementations
|
||||
in `packages/model-runtime/src/providers/`.
|
||||
|
||||
For detailed implementation, see:
|
||||
## Agent Runtime
|
||||
|
||||
- `src/libs/agent-runtime/AgentRuntime.ts` - Core runtime class
|
||||
- `src/libs/agent-runtime/BaseAI.ts` - Define base interface
|
||||
- `src/libs/agent-runtime/runtimeMap.ts` - Provider mapping table
|
||||
- `src/libs/agent-runtime/UniformRuntime/index.ts` - Handle multi-model unified runtime
|
||||
- `src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts` - OpenAI compatible adapter factory
|
||||
Agent Runtime (`packages/agent-runtime/`) is LobeChat's
|
||||
agent orchestration engine. As described above, it is the
|
||||
core execution engine that drives the entire chat flow.
|
||||
|
||||
**Core Components**:
|
||||
|
||||
- **`AgentRuntime`**
|
||||
(`packages/agent-runtime/src/core/runtime.ts`):
|
||||
The "engine" that executes the Agent instruction loop,
|
||||
supporting `call_llm`, `call_tool`, `finish`,
|
||||
`compress_context`, `request_human_*`, etc.
|
||||
- **`GeneralChatAgent`**
|
||||
(`packages/agent-runtime/src/agents/GeneralChatAgent.ts`):
|
||||
The "brain" that decides which instruction to execute
|
||||
next based on current state
|
||||
- **`GroupOrchestrationRuntime`**
|
||||
(`packages/agent-runtime/src/groupOrchestration/`):
|
||||
Multi-agent orchestration supporting speak / broadcast /
|
||||
delegate / executeTask collaboration modes
|
||||
- **`UsageCounter`**: Token usage and cost tracking
|
||||
- **`InterventionChecker`**:
|
||||
Security blacklist for managing agent behavior boundaries
|
||||
|
||||
@@ -1,69 +1,62 @@
|
||||
---
|
||||
title: LobeHub API 前后端交互逻辑
|
||||
description: 深入了解 LobeHub API 的前后端交互实现逻辑和核心组件。
|
||||
title: Chat API 前后端交互逻辑
|
||||
description: 深入了解 LobeChat Chat API 的前后端交互实现逻辑和核心组件。
|
||||
tags:
|
||||
- LobeHub API
|
||||
- Chat API
|
||||
- 前后端交互
|
||||
- 事件序列
|
||||
- 核心组件
|
||||
- Model Runtime
|
||||
- Agent Runtime
|
||||
- MCP
|
||||
---
|
||||
|
||||
# LobeHub API 前后端交互逻辑
|
||||
# Chat API 前后端交互逻辑
|
||||
|
||||
本文档说明了 LobeHub API 在前后端交互中的实现逻辑,包括事件序列和涉及的核心组件。
|
||||
本文档说明了 LobeChat Chat API 在前后端交互中的实现逻辑,
|
||||
包括事件序列和涉及的核心组件。
|
||||
|
||||
## 交互时序图
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as 前端客户端
|
||||
participant ChatService as 前端 ChatService
|
||||
participant AgentLoop as Agent Runtime 循环
|
||||
participant ChatService as ChatService
|
||||
participant ChatAPI as 后端 Chat API
|
||||
participant AgentRuntime as AgentRuntime
|
||||
participant ModelRuntime as Model Runtime
|
||||
participant ModelProvider as 模型提供商 API
|
||||
participant PluginGateway as 插件网关
|
||||
participant ToolExecution as 工具执行层
|
||||
|
||||
Client->>ChatService: 调用 createAssistantMessage
|
||||
Note over ChatService: 处理消息、工具和参数
|
||||
Client->>AgentLoop: sendMessage()
|
||||
Note over AgentLoop: 创建 GeneralChatAgent + AgentRuntime
|
||||
|
||||
ChatService->>ChatService: 调用 getChatCompletion
|
||||
Note over ChatService: 准备请求参数
|
||||
loop Agent Plan-Execute 循环
|
||||
AgentLoop->>AgentLoop: Agent 决定下一步指令
|
||||
|
||||
ChatService->>ChatAPI: 发送 POST 请求到 /webapi/chat/[provider]
|
||||
alt call_llm 指令
|
||||
AgentLoop->>ChatService: getChatCompletion
|
||||
Note over ChatService: 上下文工程 (context engineering)
|
||||
ChatService->>ChatAPI: POST /webapi/chat/[provider]
|
||||
ChatAPI->>ModelRuntime: 初始化 ModelRuntime
|
||||
ModelRuntime->>ModelProvider: chat completion 请求
|
||||
ModelProvider-->>ChatService: 流式返回 SSE 响应
|
||||
ChatService-->>Client: onMessageHandle 回调
|
||||
|
||||
ChatAPI->>AgentRuntime: 初始化 AgentRuntime
|
||||
Note over AgentRuntime: 通过 provider 和 用户配置创建运行时
|
||||
else call_tool 指令
|
||||
AgentLoop->>ToolExecution: 执行工具
|
||||
Note over ToolExecution: Builtin / MCP / Plugin
|
||||
ToolExecution-->>AgentLoop: 返回工具结果
|
||||
|
||||
ChatAPI->>AgentRuntime: 调用 chat 方法
|
||||
AgentRuntime->>ModelProvider: 发送 chat completion 请求
|
||||
else request_human_* 指令
|
||||
AgentLoop-->>Client: 请求用户介入
|
||||
Client->>AgentLoop: 用户反馈
|
||||
|
||||
ModelProvider-->>AgentRuntime: 返回流式响应
|
||||
AgentRuntime-->>ChatAPI: 处理响应并返回 stream
|
||||
|
||||
ChatAPI-->>ChatService: 流式返回 SSE 响应
|
||||
|
||||
ChatService->>ChatService: 使用 fetchSSE 处理流式响应
|
||||
Note over ChatService: 通过 fetchEventSource 处理事件流
|
||||
|
||||
loop 对于每个数据块
|
||||
ChatService->>ChatService: 处理不同类型的事件 (text, tool_calls, reasoning 等)
|
||||
ChatService-->>Client: 通过 onMessageHandle 回调返回当前块
|
||||
else finish 指令
|
||||
AgentLoop-->>Client: onFinish 回调
|
||||
end
|
||||
end
|
||||
|
||||
ChatService-->>Client: 通过 onFinish 回调返回完整结果
|
||||
|
||||
Note over ChatService,ModelProvider: 插件调用场景
|
||||
ModelProvider-->>ChatService: 返回包含 tool_calls 的响应
|
||||
ChatService->>ChatService: 解析工具调用
|
||||
ChatService->>ChatService: 调用 runPluginApi
|
||||
ChatService->>PluginGateway: 发送插件请求到网关
|
||||
PluginGateway-->>ChatService: 返回插件执行结果
|
||||
ChatService->>ModelProvider: 将插件结果返回给模型
|
||||
ModelProvider-->>ChatService: 基于插件结果生成最终响应
|
||||
|
||||
Note over ChatService,ModelProvider: 预设任务场景
|
||||
Client->>ChatService: 触发预设任务(如自动翻译、搜索等)
|
||||
ChatService->>ChatService: 调用 fetchPresetTaskResult
|
||||
Note over Client,ModelProvider: 预设任务场景 (不经过 Agent 循环)
|
||||
Client->>ChatService: fetchPresetTaskResult
|
||||
ChatService->>ChatAPI: 发送预设任务请求
|
||||
ChatAPI-->>ChatService: 返回任务结果
|
||||
ChatService-->>Client: 通过回调函数返回结果
|
||||
@@ -71,285 +64,319 @@ sequenceDiagram
|
||||
|
||||
## 主要步骤说明
|
||||
|
||||
1. **客户端发起请求**:客户端调用前端 ChatService 的 createAssistantMessage 方法。
|
||||
### 1. 客户端发起请求
|
||||
|
||||
2. **前端处理请求**:
|
||||
用户发送消息后,`sendMessage()`
|
||||
(`src/store/chat/slices/aiChat/actions/conversationLifecycle.ts`)
|
||||
创建用户消息和助手消息占位,然后调用 `internal_execAgentRuntime()`。
|
||||
|
||||
- `src/services/chat.ts` 对消息、工具和参数进行预处理
|
||||
- 调用 getChatCompletion 准备请求参数
|
||||
- 使用 `src/utils/fetch/fetchSSE.ts` 发送请求到后端 API
|
||||
### 2. Agent Runtime 驱动循环
|
||||
|
||||
3. **后端处理请求**:
|
||||
Agent Runtime 是整个 chat 流程的**核心执行引擎**。
|
||||
每次聊天交互(从简单问答到复杂多步工具调用)都通过
|
||||
`AgentRuntime.step()` 循环驱动。
|
||||
|
||||
- `src/app/(backend)/webapi/chat/[provider]/route.ts` 接收请求
|
||||
- 初始化 AgentRuntime
|
||||
- 根据用户配置和提供商创建相应的模型实例
|
||||
**初始化**
|
||||
(`src/store/chat/slices/aiChat/actions/streamingExecutor.ts`):
|
||||
|
||||
4. **模型调用**:
|
||||
1. 解析 agent 配置(模型、provider、插件列表等)
|
||||
2. 通过 `createAgentToolsEngine()` 创建工具注册表
|
||||
3. 创建 `GeneralChatAgent`("大脑",决定下一步做什么)
|
||||
和 `AgentRuntime`("引擎",执行指令)
|
||||
4. 通过 `createAgentExecutors()` 注入自定义执行器
|
||||
|
||||
- `src/libs/agent-runtime/AgentRuntime.ts` 调用相应模型提供商的 API
|
||||
- 返回流式响应
|
||||
**执行循环**:
|
||||
|
||||
5. **处理响应**:
|
||||
```ts
|
||||
while (state.status !== 'done' && state.status !== 'error') {
|
||||
result = await runtime.step(state, nextContext);
|
||||
// GeneralChatAgent 决定: call_llm → call_tool → call_llm → finish
|
||||
}
|
||||
```
|
||||
|
||||
- 后端将模型响应转换为 Stream 返回
|
||||
- 前端通过 fetchSSE 和 [fetchEventSource](https://github.com/Azure/fetch-event-source) 处理流式响应
|
||||
- 对不同类型的事件(文本、工具调用、推理等)进行处理
|
||||
- 通过回调函数将结果传递回客户端
|
||||
每一步中,`GeneralChatAgent` 根据当前状态返回一条
|
||||
`AgentInstruction`,`AgentRuntime` 通过对应的 executor 执行:
|
||||
|
||||
6. **插件调用场景**:
|
||||
- `call_llm`:调用 LLM(见下方步骤 3-5)
|
||||
- `call_tool`:执行工具调用(见下方步骤 6)
|
||||
- `finish`:结束循环
|
||||
- `compress_context`:上下文压缩
|
||||
- `request_human_approve` / `request_human_prompt` /
|
||||
`request_human_select`:请求用户介入
|
||||
|
||||
当 AI 模型在响应中返回 `tool_calls` 字段时,会触发插件调用流程:
|
||||
### 3. 前端处理 LLM 请求
|
||||
|
||||
- AI 模型返回包含 `tool_calls` 的响应,表明需要调用工具
|
||||
- 前端通过 `internal_callPluginApi` 方法处理工具调用
|
||||
- 调用 `runPluginApi` 方法执行插件功能,包括获取插件设置和清单、创建认证请求头、发送请求到插件网关
|
||||
- 插件执行完成后,结果返回给 AI 模型,模型基于结果生成最终响应
|
||||
当 Agent 发出 `call_llm` 指令时,executor 调用 ChatService:
|
||||
|
||||
**实际应用示例**:
|
||||
- `src/services/chat/index.ts` 对消息、工具和参数进行预处理
|
||||
- `src/services/chat/mecha/` 下的模块完成上下文工程
|
||||
(context engineering),包括 agent 配置解析、
|
||||
模型参数解析、MCP 上下文注入等
|
||||
- 调用 `getChatCompletion` 准备请求参数
|
||||
- 使用 `@lobechat/fetch-sse` 包中的 `fetchSSE`
|
||||
发送请求到后端 API
|
||||
|
||||
- **搜索插件**:当用户需要获取实时信息时,AI 会调用网页搜索插件来获取最新数据
|
||||
- **DALL-E 插件**:用户要求生成图片时,AI 调用 DALL-E 插件创建图像
|
||||
- **Midjourney 插件**:提供更高质量的图像生成能力,通过 API 调用 Midjourney 服务
|
||||
### 4. 后端处理请求
|
||||
|
||||
7. **预设任务处理**:
|
||||
- `src/app/(backend)/webapi/chat/[provider]/route.ts` 接收请求
|
||||
- 调用 `initModelRuntimeFromDB` 从数据库读取用户的
|
||||
provider 配置,初始化 ModelRuntime
|
||||
- 同时存在 tRPC 路由
|
||||
`src/server/routers/lambda/aiChat.ts`,
|
||||
用于服务端消息发送和结构化输出等场景
|
||||
|
||||
预设任务是指系统预定义的特定功能任务,通常在用户执行特定操作时触发(而非常规聊天流程的一部分)。这些任务使用 `fetchPresetTaskResult` 方法执行,该方法与正常聊天流程类似,但会使用专门设计的提示词(prompt chain)。
|
||||
### 5. 模型调用与响应处理
|
||||
|
||||
**执行时机**:预设任务主要在以下场景被触发:
|
||||
- `ModelRuntime`
|
||||
(`packages/model-runtime/src/core/ModelRuntime.ts`)
|
||||
调用相应模型提供商的 API,返回流式响应
|
||||
- 前端通过 `fetchSSE` 和
|
||||
[fetchEventSource](https://github.com/Azure/fetch-event-source)
|
||||
处理流式响应
|
||||
- 对不同类型的事件(文本、工具调用、推理等)进行处理
|
||||
- 通过回调函数将结果传递回客户端
|
||||
|
||||
1. **角色信息自动生成**:当用户创建或编辑角色时触发
|
||||
### 6. 工具调用场景
|
||||
|
||||
- 角色头像生成(通过 `autoPickEmoji` 方法)
|
||||
- 角色描述生成(通过 `autocompleteAgentDescription` 方法)
|
||||
- 角色标签生成(通过 `autocompleteAgentTags` 方法)
|
||||
- 角色标题生成(通过 `autocompleteAgentTitle` 方法)
|
||||
当 AI 模型在响应中返回 `tool_calls` 字段时,Agent 会发出
|
||||
`call_tool` 指令。LobeChat 支持三种工具类型:
|
||||
|
||||
2. **消息翻译**:用户手动点击翻译按钮时触发(通过 `translateMessage` 方法)
|
||||
**Builtin 工具**:内置在应用中的工具,通过本地执行器直接运行。
|
||||
|
||||
3. **网页搜索**:当启用搜索但模型不支持工具调用时,通过 `fetchPresetTaskResult` 实现搜索功能
|
||||
- 前端通过 `invokeBuiltinTool` 方法在客户端直接执行
|
||||
- 包括搜索、DALL-E 图像生成等内置功能
|
||||
|
||||
**实际代码示例**:
|
||||
**MCP 工具**:通过
|
||||
[Model Context Protocol](https://modelcontextprotocol.io/)
|
||||
连接的外部工具。
|
||||
|
||||
角色头像自动生成实现:
|
||||
- 前端通过 `invokeMCPTypePlugin` 方法调用
|
||||
`MCPService`(`src/services/mcp.ts`)
|
||||
- 支持 stdio、HTTP(streamable-http/SSE)
|
||||
和云端(Klavis)三种连接方式
|
||||
- MCP 工具的注册和发现通过 MCP 服务器配置管理
|
||||
|
||||
```ts
|
||||
// src/features/AgentSetting/store/action.ts
|
||||
autoPickEmoji: async () => {
|
||||
const { config, meta, dispatchMeta } = get();
|
||||
const systemRole = config.systemRole;
|
||||
**Plugin 工具**:传统插件体系,通过 API 网关调用。
|
||||
该体系预期将逐步废弃,由 MCP 工具体系替代。
|
||||
|
||||
chatService.fetchPresetTaskResult({
|
||||
onFinish: async (emoji) => {
|
||||
dispatchMeta({ type: 'update', value: { avatar: emoji } });
|
||||
},
|
||||
onLoadingChange: (loading) => {
|
||||
get().updateLoadingState('avatar', loading);
|
||||
},
|
||||
params: merge(
|
||||
get().internal_getSystemAgentForMeta(),
|
||||
chainPickEmoji([meta.title, meta.description, systemRole].filter(Boolean).join(',')),
|
||||
),
|
||||
trace: get().getCurrentTracePayload({ traceName: TraceNameMap.EmojiPicker }),
|
||||
});
|
||||
};
|
||||
```
|
||||
- 前端通过 `invokeDefaultTypePlugin` 方法调用
|
||||
- 获取插件设置和清单、创建认证请求头、
|
||||
发送请求到插件网关
|
||||
|
||||
翻译功能实现:
|
||||
工具执行完成后,结果写入消息并返回给 Agent 循环,
|
||||
Agent 会再次调用 LLM 基于工具结果生成最终响应。
|
||||
工具分发逻辑位于
|
||||
`src/store/chat/slices/plugin/actions/pluginTypes.ts`。
|
||||
|
||||
```ts
|
||||
// src/store/chat/slices/translate/action.ts
|
||||
translateMessage: async (id, targetLang) => {
|
||||
// ...省略部分代码...
|
||||
### 7. 预设任务处理
|
||||
|
||||
// 检测语言
|
||||
chatService.fetchPresetTaskResult({
|
||||
onFinish: async (data) => {
|
||||
if (data && supportLocales.includes(data)) from = data;
|
||||
await updateMessageTranslate(id, { content, from, to: targetLang });
|
||||
},
|
||||
params: merge(translationSetting, chainLangDetect(message.content)),
|
||||
trace: get().getCurrentTracePayload({ traceName: TraceNameMap.LanguageDetect }),
|
||||
});
|
||||
预设任务是系统预定义的特定功能任务,
|
||||
通常在用户执行特定操作时触发
|
||||
(不经过 Agent Runtime 循环,直接调用 LLM)。
|
||||
这些任务使用 `fetchPresetTaskResult` 方法执行,
|
||||
该方法与正常聊天流程类似,
|
||||
但会使用专门设计的提示词(prompt chain)。
|
||||
|
||||
// 执行翻译
|
||||
chatService.fetchPresetTaskResult({
|
||||
onMessageHandle: (chunk) => {
|
||||
if (chunk.type === 'text') {
|
||||
content = chunk.text;
|
||||
internal_dispatchMessage({
|
||||
id,
|
||||
type: 'updateMessageTranslate',
|
||||
value: { content, from, to: targetLang },
|
||||
});
|
||||
}
|
||||
},
|
||||
onFinish: async () => {
|
||||
await updateMessageTranslate(id, { content, from, to: targetLang });
|
||||
internal_toggleChatLoading(false, id, n('translateMessage(end)', { id }) as string);
|
||||
},
|
||||
params: merge(translationSetting, chainTranslate(message.content, targetLang)),
|
||||
trace: get().getCurrentTracePayload({ traceName: TraceNameMap.Translation }),
|
||||
});
|
||||
};
|
||||
```
|
||||
**执行时机**:
|
||||
|
||||
8. **完成**:
|
||||
- 当流结束时,调用 onFinish 回调,提供完整的响应结果
|
||||
1. **角色信息自动生成**:当用户创建或编辑角色时触发
|
||||
- 角色头像生成(通过 `autoPickEmoji` 方法)
|
||||
- 角色描述生成(通过 `autocompleteAgentDescription` 方法)
|
||||
- 角色标签生成(通过 `autocompleteAgentTags` 方法)
|
||||
- 角色标题生成(通过 `autocompleteAgentTitle` 方法)
|
||||
2. **消息翻译**:用户手动点击翻译按钮时触发
|
||||
(通过 `translateMessage` 方法)
|
||||
3. **网页搜索**:当启用搜索但模型不支持工具调用时,
|
||||
通过 `fetchPresetTaskResult` 实现搜索功能
|
||||
|
||||
## AgentRuntime 说明
|
||||
**实际代码示例**:
|
||||
|
||||
AgentRuntime 是 LobeHub 中的一个核心抽象层,它封装了与不同 AI 模型提供商交互的统一接口。其主要职责和特点包括:
|
||||
角色头像自动生成实现:
|
||||
|
||||
1. **统一抽象层**:AgentRuntime 提供了一个统一的接口,隐藏了不同 AI 提供商 API 的实现细节差异(如 OpenAI、Anthropic、Bedrock 等)。
|
||||
```ts
|
||||
// src/features/AgentSetting/store/action.ts
|
||||
autoPickEmoji: async () => {
|
||||
const { config, meta, dispatchMeta } = get();
|
||||
const systemRole = config.systemRole;
|
||||
|
||||
2. **模型初始化**:通过 `initializeWithProvider` 静态方法,根据指定的提供商和配置参数初始化对应的运行时实例。
|
||||
chatService.fetchPresetTaskResult({
|
||||
onFinish: async (emoji) => {
|
||||
dispatchMeta({ type: 'update', value: { avatar: emoji } });
|
||||
},
|
||||
onLoadingChange: (loading) => {
|
||||
get().updateLoadingState('avatar', loading);
|
||||
},
|
||||
params: merge(
|
||||
get().internal_getSystemAgentForMeta(),
|
||||
chainPickEmoji(
|
||||
[meta.title, meta.description, systemRole]
|
||||
.filter(Boolean)
|
||||
.join(','),
|
||||
),
|
||||
),
|
||||
trace: get().getCurrentTracePayload({
|
||||
traceName: TraceNameMap.EmojiPicker,
|
||||
}),
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
3. **能力封装**:
|
||||
翻译功能实现:
|
||||
|
||||
- `chat` 方法:处理聊天流式请求
|
||||
- `models` 方法:获取模型列表
|
||||
- 支持文本嵌入、文本到图像、文本到语音等功能(如果模型提供商支持)
|
||||
```ts
|
||||
// src/store/chat/slices/translate/action.ts
|
||||
translateMessage: async (id, targetLang) => {
|
||||
// ...省略部分代码...
|
||||
|
||||
4. **插件化架构**:通过 `src/libs/agent-runtime/runtimeMap.ts` 映射表,实现了可扩展的插件化架构,方便添加新的模型提供商。目前支持超过 40 个不同的模型提供商:
|
||||
// 检测语言
|
||||
chatService.fetchPresetTaskResult({
|
||||
onFinish: async (data) => {
|
||||
if (data && supportLocales.includes(data)) from = data;
|
||||
await updateMessageTranslate(id, {
|
||||
content,
|
||||
from,
|
||||
to: targetLang,
|
||||
});
|
||||
},
|
||||
params: merge(
|
||||
translationSetting,
|
||||
chainLangDetect(message.content),
|
||||
),
|
||||
trace: get().getCurrentTracePayload({
|
||||
traceName: TraceNameMap.LanguageDetect,
|
||||
}),
|
||||
});
|
||||
|
||||
```ts
|
||||
export const providerRuntimeMap = {
|
||||
openai: LobeOpenAI,
|
||||
anthropic: LobeAnthropicAI,
|
||||
google: LobeGoogleAI,
|
||||
azure: LobeAzureOpenAI,
|
||||
bedrock: LobeBedrockAI,
|
||||
ollama: LobeOllamaAI,
|
||||
// ...其他40多个模型提供商
|
||||
};
|
||||
```
|
||||
|
||||
5. **适配器模式**:在内部使用适配器模式,将不同提供商的 API 适配到统一的 `src/libs/agent-runtime/BaseAI.ts` 接口:
|
||||
|
||||
```ts
|
||||
export interface LobeRuntimeAI {
|
||||
baseURL?: string;
|
||||
chat(payload: ChatStreamPayload, options?: ChatCompetitionOptions): Promise<Response>;
|
||||
embeddings?(payload: EmbeddingsPayload, options?: EmbeddingsOptions): Promise<Embeddings[]>;
|
||||
models?(): Promise<any>;
|
||||
textToSpeech?: (
|
||||
payload: TextToSpeechPayload,
|
||||
options?: TextToSpeechOptions,
|
||||
) => Promise<ArrayBuffer>;
|
||||
}
|
||||
```
|
||||
|
||||
**适配器实现示例**:
|
||||
|
||||
1. **OpenRouter 适配器**:OpenRouter 是一个统一 API,可以通过它访问多个模型提供商的 AI 模型。LobeHub 通过适配器实现对 OpenRouter 的支持:
|
||||
|
||||
```ts
|
||||
// OpenRouter 适配器实现
|
||||
class LobeOpenRouterAI implements LobeRuntimeAI {
|
||||
client: OpenAI;
|
||||
baseURL: string;
|
||||
|
||||
constructor(options: OpenAICompatibleOptions) {
|
||||
// 初始化 OpenRouter 客户端,使用 OpenAI 兼容的 API
|
||||
this.client = new OpenAI({
|
||||
apiKey: options.apiKey,
|
||||
baseURL: OPENROUTER_BASE_URL,
|
||||
defaultHeaders: {
|
||||
'HTTP-Referer': 'https://github.com/lobehub/lobehub',
|
||||
'X-Title': 'LobeHub',
|
||||
},
|
||||
});
|
||||
this.baseURL = OPENROUTER_BASE_URL;
|
||||
}
|
||||
|
||||
// 实现聊天功能
|
||||
async chat(payload: ChatCompletionCreateParamsBase, options?: RequestOptions) {
|
||||
// 将 LobeHub 的请求格式转换为 OpenRouter 格式
|
||||
// 处理模型映射、消息格式等
|
||||
return this.client.chat.completions.create(
|
||||
{
|
||||
...payload,
|
||||
model: payload.model || 'openai/gpt-4-turbo', // 默认模型
|
||||
},
|
||||
options,
|
||||
);
|
||||
}
|
||||
|
||||
// 实现其他 LobeRuntimeAI 接口方法
|
||||
// 执行翻译
|
||||
chatService.fetchPresetTaskResult({
|
||||
onMessageHandle: (chunk) => {
|
||||
if (chunk.type === 'text') {
|
||||
content = chunk.text;
|
||||
internal_dispatchMessage({
|
||||
id,
|
||||
type: 'updateMessageTranslate',
|
||||
value: { content, from, to: targetLang },
|
||||
});
|
||||
}
|
||||
```
|
||||
},
|
||||
onFinish: async () => {
|
||||
await updateMessageTranslate(id, {
|
||||
content,
|
||||
from,
|
||||
to: targetLang,
|
||||
});
|
||||
internal_toggleChatLoading(
|
||||
false,
|
||||
id,
|
||||
n('translateMessage(end)', { id }) as string,
|
||||
);
|
||||
},
|
||||
params: merge(
|
||||
translationSetting,
|
||||
chainTranslate(message.content, targetLang),
|
||||
),
|
||||
trace: get().getCurrentTracePayload({
|
||||
traceName: TraceNameMap.Translation,
|
||||
}),
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
2. **Google Gemini 适配器**:Gemini 是 Google 的大语言模型,LobeHub 通过专门的适配器支持 Gemini 系列模型:
|
||||
### 8. 完成
|
||||
|
||||
```ts
|
||||
import { GoogleGenerativeAI } from '@google/generative-ai';
|
||||
当 Agent 发出 `finish` 指令时,循环结束,
|
||||
调用 `onFinish` 回调,提供完整的响应结果。
|
||||
|
||||
// Gemini 适配器实现
|
||||
class LobeGoogleAI implements LobeRuntimeAI {
|
||||
client: GoogleGenerativeAI;
|
||||
baseURL: string;
|
||||
apiKey: string;
|
||||
## 客户端 vs 服务端执行
|
||||
|
||||
constructor(options: GoogleAIOptions) {
|
||||
// 初始化 Google Generative AI 客户端
|
||||
this.client = new GoogleGenerativeAI(options.apiKey);
|
||||
this.apiKey = options.apiKey;
|
||||
this.baseURL = options.baseURL || GOOGLE_AI_BASE_URL;
|
||||
}
|
||||
Agent Runtime 循环的执行位置取决于场景:
|
||||
|
||||
// 实现聊天功能
|
||||
async chat(payload: ChatCompletionCreateParamsBase, options?: RequestOptions) {
|
||||
// 选择合适的模型(支持 Gemini Pro、Gemini Flash 等)
|
||||
const modelName = payload.model || 'gemini-pro';
|
||||
const model = this.client.getGenerativeModel({ model: modelName });
|
||||
- **客户端循环**(浏览器):常规 1:1 对话、继续生成、
|
||||
群组编排决策。循环在浏览器中运行,
|
||||
入口为 `internal_execAgentRuntime()`
|
||||
(`src/store/chat/slices/aiChat/actions/streamingExecutor.ts`)
|
||||
- **服务端循环**(队列 / 本地):群聊 supervisor agent、
|
||||
子 agent 任务、API/Cron 触发。循环在服务端运行,
|
||||
通过 SSE 流式推送事件到客户端,
|
||||
入口为 `AgentRuntimeService.executeStep()`
|
||||
(`src/server/services/agentRuntime/AgentRuntimeService.ts`),
|
||||
tRPC 路由为 `src/server/routers/lambda/aiAgent.ts`
|
||||
|
||||
// 处理多模态输入(如图像)
|
||||
const contents = this.processMessages(payload.messages);
|
||||
## Model Runtime
|
||||
|
||||
// 设置生成参数
|
||||
const generationConfig = {
|
||||
temperature: payload.temperature,
|
||||
topK: payload.top_k,
|
||||
topP: payload.top_p,
|
||||
maxOutputTokens: payload.max_tokens,
|
||||
};
|
||||
Model Runtime(`packages/model-runtime/`)是 LobeChat 中
|
||||
与 LLM 模型提供商交互的核心抽象层,
|
||||
负责将不同提供商的 API 适配为统一接口。
|
||||
|
||||
// 创建聊天会话并获取响应
|
||||
const chat = model.startChat({
|
||||
generationConfig,
|
||||
history: contents.slice(0, -1),
|
||||
safetySettings: this.getSafetySettings(payload),
|
||||
});
|
||||
**核心职责**:
|
||||
|
||||
// 处理流式响应
|
||||
return this.handleStreamResponse(chat, contents, options?.signal);
|
||||
}
|
||||
- **统一抽象层**:通过 `LobeRuntimeAI` 接口
|
||||
(`packages/model-runtime/src/core/BaseAI.ts`)
|
||||
隐藏不同 AI 提供商 API 的差异
|
||||
- **模型初始化**:通过 provider 映射表
|
||||
(`packages/model-runtime/src/runtimeMap.ts`)
|
||||
初始化对应的运行时实例
|
||||
- **能力封装**:`chat`(聊天流式请求)、
|
||||
`models`(模型列表)、`embeddings`(文本嵌入)、
|
||||
`createImage`(图像生成)、`textToSpeech`(语音合成)、
|
||||
`generateObject`(结构化输出)
|
||||
|
||||
// 实现其他处理方法
|
||||
private processMessages(messages) {
|
||||
/* ... */
|
||||
}
|
||||
private getSafetySettings(payload) {
|
||||
/* ... */
|
||||
}
|
||||
private handleStreamResponse(chat, contents, signal) {
|
||||
/* ... */
|
||||
}
|
||||
}
|
||||
```
|
||||
**核心接口**:
|
||||
|
||||
**不同模型的适配实现**:
|
||||
```ts
|
||||
// packages/model-runtime/src/core/BaseAI.ts
|
||||
export interface LobeRuntimeAI {
|
||||
baseURL?: string;
|
||||
chat?(
|
||||
payload: ChatStreamPayload,
|
||||
options?: ChatMethodOptions,
|
||||
): Promise<Response>;
|
||||
generateObject?(
|
||||
payload: GenerateObjectPayload,
|
||||
options?: GenerateObjectOptions,
|
||||
): Promise<any>;
|
||||
embeddings?(
|
||||
payload: EmbeddingsPayload,
|
||||
options?: EmbeddingsOptions,
|
||||
): Promise<Embeddings[]>;
|
||||
models?(): Promise<any>;
|
||||
createImage?: (
|
||||
payload: CreateImagePayload,
|
||||
) => Promise<CreateImageResponse>;
|
||||
textToSpeech?: (
|
||||
payload: TextToSpeechPayload,
|
||||
options?: TextToSpeechOptions,
|
||||
) => Promise<ArrayBuffer>;
|
||||
}
|
||||
```
|
||||
|
||||
- `src/libs/agent-runtime/openai/index.ts` - OpenAI 实现
|
||||
- `src/libs/agent-runtime/anthropic/index.ts` - Anthropic 实现
|
||||
- `src/libs/agent-runtime/google/index.ts` - Google 实现
|
||||
- `src/libs/agent-runtime/openrouter/index.ts` - OpenRouter 实现
|
||||
**适配器架构**:通过 `openaiCompatibleFactory` 和
|
||||
`anthropicCompatibleFactory` 两种工厂函数,
|
||||
大多数提供商只需少量配置即可接入。
|
||||
目前支持超过 40 个模型提供商
|
||||
(OpenAI、Anthropic、Google、Azure、Bedrock、Ollama 等),
|
||||
各实现位于 `packages/model-runtime/src/providers/`。
|
||||
|
||||
详细实现可以查看:
|
||||
## Agent Runtime
|
||||
|
||||
- `src/libs/agent-runtime/AgentRuntime.ts` - 核心运行时类
|
||||
- `src/libs/agent-runtime/BaseAI.ts` - 定义基础接口
|
||||
- `src/libs/agent-runtime/runtimeMap.ts` - 提供商映射表
|
||||
- `src/libs/agent-runtime/UniformRuntime/index.ts` - 处理多模型统一运行时
|
||||
- `src/libs/agent-runtime/utils/openaiCompatibleFactory/index.ts` - OpenAI 兼容适配器工厂
|
||||
Agent Runtime(`packages/agent-runtime/`)是
|
||||
LobeChat 的 Agent 编排引擎。
|
||||
如上文所述,它是驱动整个 chat 流程的核心执行引擎。
|
||||
|
||||
**核心组件**:
|
||||
|
||||
- **`AgentRuntime`**
|
||||
(`packages/agent-runtime/src/core/runtime.ts`):
|
||||
"引擎",执行 Agent 指令循环,
|
||||
支持 `call_llm`、`call_tool`、`finish`、
|
||||
`compress_context`、`request_human_*` 等指令类型
|
||||
- **`GeneralChatAgent`**
|
||||
(`packages/agent-runtime/src/agents/GeneralChatAgent.ts`):
|
||||
"大脑",根据当前状态决定下一步执行什么指令
|
||||
- **`GroupOrchestrationRuntime`**
|
||||
(`packages/agent-runtime/src/groupOrchestration/`):
|
||||
多 Agent 编排,支持 speak /broadcast/
|
||||
delegate /executeTask 等协作模式
|
||||
- **`UsageCounter`**:token 使用和费用追踪
|
||||
- **`InterventionChecker`**:
|
||||
安全黑名单管理 Agent 行为边界
|
||||
|
||||
@@ -65,7 +65,10 @@ In this example, the 📝 emoji represents a documentation update. The commit me
|
||||
|
||||
### Semantic Release
|
||||
|
||||
We use semantic release to automate version control and release processes. Ensure that your commit messages adhere to the semantic release specifications so that when the code is merged into the main branch, the system can automatically create a new version and release it.
|
||||
We use semantic release to automate version control and release processes. When a PR is merged into the main branch, the system automatically determines whether to publish a new version based on the gitmoji prefix in commit messages:
|
||||
|
||||
- Commits with `✨ feat` or `🐛 fix` prefixes will **trigger a new release**
|
||||
- For minor changes that don't need a release, use prefixes like `💄 style` or `🔨 chore`
|
||||
|
||||
### Commitlint
|
||||
|
||||
@@ -73,13 +76,27 @@ To ensure consistency in commit messages, we use `commitlint` to check the forma
|
||||
|
||||
Before committing your code, ensure that your commit messages adhere to our standards.
|
||||
|
||||
### Testing
|
||||
|
||||
LobeHub has comprehensive unit tests (Vitest) and E2E tests (Cucumber + Playwright), which run automatically via GitHub Actions CI on every PR. Before submitting a PR or requesting a merge, make sure all tests pass.
|
||||
|
||||
You can run specific test files locally to verify:
|
||||
|
||||
```bash
|
||||
# Run specific test (never run bun run test — full suite is very slow)
|
||||
bunx vitest run --silent='passed-only' '[file-path]'
|
||||
```
|
||||
|
||||
For more testing details, see the [Testing Guide](/docs/development/basic/test).
|
||||
|
||||
### How to Contribute
|
||||
|
||||
1. Fork the project to your account.
|
||||
2. Create a new branch for development.
|
||||
3. After completing the development, ensure that your code passes the aforementioned code style checks.
|
||||
3. After completing development, ensure your code passes code style checks and tests.
|
||||
4. Commit your changes and use appropriate gitmoji to label your commit message.
|
||||
5. Create a Pull Request to the main branch of the original project.
|
||||
6. Await code review and make necessary modifications based on feedback.
|
||||
6. Ensure all GitHub Actions CI checks pass.
|
||||
7. Await code review and make necessary modifications based on feedback.
|
||||
|
||||
Thank you for following these guidelines, as they help us maintain the quality and consistency of the project. We look forward to your contributions!
|
||||
|
||||
@@ -63,7 +63,10 @@ Gitmoji commit messages 使用特定的 emoji 来表示提交的类型或意图
|
||||
|
||||
### Semantic Release
|
||||
|
||||
我们使用 semantic release 来自动化版本控制和发布流程。请确保您的提交信息遵循 semantic release 的规范,这样当代码合并到主分支后,系统就可以自动创建新的版本并发布。
|
||||
我们使用 semantic release 来自动化版本控制和发布流程。当 PR 合并到主分支后,系统会根据提交信息中的 gitmoji 前缀自动判断是否需要发布新版本:
|
||||
|
||||
- `✨ feat` 和 `🐛 fix` 前缀的提交会**触发新版本发布**
|
||||
- 如果只是小修改不想发新版本,可以使用 `💄 style` 或 `🔨 chore` 等前缀
|
||||
|
||||
### Commitlint
|
||||
|
||||
@@ -71,13 +74,27 @@ Gitmoji commit messages 使用特定的 emoji 来表示提交的类型或意图
|
||||
|
||||
在您提交代码之前,请确保您的提交信息遵循我们的规范。
|
||||
|
||||
### 测试
|
||||
|
||||
LobeHub 配置了完善的单元测试(Vitest)和 E2E 测试(Cucumber + Playwright),并通过 GitHub Actions CI 在每次 PR 提交时自动运行。提交 PR 或请求合并前,请务必确保所有测试通过。
|
||||
|
||||
你可以在本地运行指定测试文件来验证:
|
||||
|
||||
```bash
|
||||
# 运行指定测试(不要运行 bun run test,全量测试耗时很长)
|
||||
bunx vitest run --silent='passed-only' '[file-path]'
|
||||
```
|
||||
|
||||
更多测试相关信息请参阅 [测试指南](/zh/docs/development/basic/test)。
|
||||
|
||||
### 如何贡献
|
||||
|
||||
1. Fork 项目到您的账户。
|
||||
2. 创建一个新的分支进行开发。
|
||||
3. 开发完成后,确保您的代码通过了上述的代码风格检查。
|
||||
3. 开发完成后,确保您的代码通过了代码风格检查和测试。
|
||||
4. 提交您的更改,并使用合适的 gitmoji 标注您的提交信息。
|
||||
5. 创建一个 Pull Request 到原项目的主分支。
|
||||
6. 等待代码审查,并根据反馈进行必要的修改。
|
||||
6. 确保 GitHub Actions CI 全部通过。
|
||||
7. 等待代码审查,并根据反馈进行必要的修改。
|
||||
|
||||
感谢您遵循这些指导原则,它们有助于我们维护项目的质量和一致性。我们期待您的贡献!
|
||||
|
||||
@@ -1,144 +0,0 @@
|
||||
---
|
||||
title: How to Develop a New Feature
|
||||
description: >-
|
||||
Learn how to implement the Chat Messages feature in LobeHub using Next.js and
|
||||
TypeScript.
|
||||
tags:
|
||||
- LobeHub
|
||||
- Next.js
|
||||
- TypeScript
|
||||
- Chat Feature
|
||||
- Zustand
|
||||
---
|
||||
|
||||
# How to Develop a New Feature
|
||||
|
||||
LobeHub is built on the Next.js framework and uses TypeScript as the primary development language. When developing a new feature, we need to follow a certain development process to ensure the quality and stability of the code. The general process can be divided into the following five steps:
|
||||
|
||||
1. Routing: Define routes (`src/app`).
|
||||
2. Data Structure: Define data structures (`src/types`).
|
||||
3. Business Logic Implementation: Zustand store (`src/store`).
|
||||
4. Page Display: Write static components/pages. Create features in:
|
||||
- `src/features/<feature-name>/` for **shared global features** (used across multiple pages)
|
||||
- `src/app/<new-page>/features/<feature-name>/` for **page-specific features** (only used in this page)
|
||||
5. Function Binding: Bind the store with page triggers (`const [state, function] = useNewStore(s => [s.state, s.function])`).
|
||||
|
||||
Taking the "Chat Messages" feature as an example, here are the brief steps to implement this feature:
|
||||
|
||||
## 1. Define Routes
|
||||
|
||||
In the `src/app` directory, we need to define a new route to host the "Chat Messages" page. Generally, we would create a new folder under `src/app`, for example, `chat`, and create a `page.tsx` file within this folder to export a React component as the main body of the page.
|
||||
|
||||
```tsx
|
||||
// src/app/chat/page.tsx
|
||||
import ChatPage from './features/chat';
|
||||
|
||||
export default ChatPage;
|
||||
```
|
||||
|
||||
## 2. Define Data Structure
|
||||
|
||||
In the `src/types` directory, we need to define the data structure for "Chat Messages". For example, we create a `chat.ts` file and define the `ChatMessage` type within it:
|
||||
|
||||
```ts
|
||||
// src/types/chat.ts
|
||||
|
||||
export type ChatMessage = {
|
||||
id: string;
|
||||
content: string;
|
||||
timestamp: number;
|
||||
sender: 'user' | 'bot';
|
||||
};
|
||||
```
|
||||
|
||||
## 3. Create Zustand Store
|
||||
|
||||
In the `src/store` directory, we need to create a new Zustand Store to manage the state of "Chat Messages". For example, we create a `chatStore.ts` file and define a Zustand Store within it:
|
||||
|
||||
```ts
|
||||
// src/store/chatStore.ts
|
||||
import create from 'zustand';
|
||||
|
||||
type ChatState = {
|
||||
messages: ChatMessage[];
|
||||
addMessage: (message: ChatMessage) => void;
|
||||
};
|
||||
|
||||
export const useChatStore = create<ChatState>((set) => ({
|
||||
messages: [],
|
||||
addMessage: (message) => set((state) => ({ messages: [...state.messages, message] })),
|
||||
}));
|
||||
```
|
||||
|
||||
## 4. Create Page and Components
|
||||
|
||||
In `src/app/<new-page>/features/<new-feature>.tsx`, we need to create a new page or component to display "Chat Messages". In this file, we can use the Zustand Store created earlier and Ant Design components to build the UI:
|
||||
|
||||
```jsx
|
||||
// src/app/chat/features/ChatPage/index.tsx
|
||||
// Note: Use src/app/<page>/features/ for page-specific components
|
||||
import { List, Typography } from 'antd';
|
||||
import { useChatStore } from 'src/store/chatStore';
|
||||
|
||||
const ChatPage = () => {
|
||||
const messages = useChatStore((state) => state.messages);
|
||||
|
||||
return (
|
||||
<List
|
||||
dataSource={messages}
|
||||
renderItem={(message) => (
|
||||
<List.Item>
|
||||
<Typography.Text>{message.content}</Typography.Text>
|
||||
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatPage;
|
||||
```
|
||||
|
||||
> **Note on Feature Organization**: LobeHub uses two patterns for organizing features:
|
||||
>
|
||||
> - **Global features** (`src/features/`): Shared components like `ChatInput`, `Conversation` used across the app
|
||||
> - **Page-specific features** (`src/app/<page>/features/`): Components used only within a specific page route
|
||||
>
|
||||
> Choose based on reusability. If unsure, start with page-specific and refactor to global if needed elsewhere.
|
||||
|
||||
## 5. Function Binding
|
||||
|
||||
In a page or component, we need to bind the Zustand Store's state and methods to the UI. In the example above, we have already bound the `messages` state to the `dataSource` property of the list. Now, we also need a method to add new messages. We can define this method in the Zustand Store and then use it in the page or component:
|
||||
|
||||
```jsx
|
||||
import { Button } from 'antd';
|
||||
|
||||
const ChatPage = () => {
|
||||
const messages = useChatStore((state) => state.messages);
|
||||
const addMessage = useChatStore((state) => state.addMessage);
|
||||
|
||||
const handleSend = () => {
|
||||
addMessage({ id: '1', content: 'Hello, world!', timestamp: Date.now(), sender: 'user' });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<List
|
||||
dataSource={messages}
|
||||
renderItem={(message) => (
|
||||
<List.Item>
|
||||
<Typography.Text>{message.content}</Typography.Text>
|
||||
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
<Button onClick={handleSend}>Send</Button>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatPage;
|
||||
```
|
||||
|
||||
The above is the step to implement the "chat message" feature in LobeHub. Of course, in the actual development of LobeHub, the business requirements and scenarios faced in real situations are far more complex than the above demo. Please develop according to the actual situation.
|
||||
@@ -1,142 +0,0 @@
|
||||
---
|
||||
title: 如何开发一个新功能:前端实现
|
||||
description: 学习如何在 LobeHub 中实现会话消息功能,使用 Next.js 和 TypeScript。
|
||||
tags:
|
||||
- 前端开发
|
||||
- Next.js
|
||||
- TypeScript
|
||||
- Zustand
|
||||
- 功能实现
|
||||
---
|
||||
|
||||
# 如何开发一个新功能:前端实现
|
||||
|
||||
LobeHub 基于 Next.js 框架构建,使用 TypeScript 作为主要开发语言。在开发新功能时,我们需要遵循一定的开发流程,以确保代码的质量和稳定性。大致的流程分为以下五步:
|
||||
|
||||
1. 路由:定义路由 (`src/app`)
|
||||
2. 数据结构: 定义数据结构 ( `src/types` )
|
||||
3. 业务功能实现: zustand store (`src/store`)
|
||||
4. 页面展示:书写静态组件 / 页面。根据以下方式创建功能组件:
|
||||
- `src/features/<feature-name>/` 用于 **全局共享功能**(跨多个页面使用)
|
||||
- `src/app/<new-page>/features/<feature-name>/` 用于 **页面专属功能**(仅在当前页面使用)
|
||||
5. 功能绑定:绑定 store 与页面的触发 (`const [state,function]= useNewStore(s=>[s.state,s.function])`)
|
||||
|
||||
我们以 "会话消息" 功能为例,以下是实现这个功能的简要步骤:
|
||||
|
||||
## 1. 定义路由
|
||||
|
||||
在 `src/app` 目录下,我们需要定义一个新的路由来承载 "会话消息" 页面。一般来说,我们会在 `src/app` 下创建一个新的文件夹,例如 `chat`,并且在这个文件夹中创建 `page.tsx`文件,在该文件中导出 React 组件作为页面的主体。
|
||||
|
||||
```tsx
|
||||
// src/app/chat/page.tsx
|
||||
import ChatPage from './features/chat';
|
||||
|
||||
export default ChatPage;
|
||||
```
|
||||
|
||||
## 2. 定义数据结构
|
||||
|
||||
在 `src/types` 目录下,我们需要定义 "会话消息" 的数据结构。例如,我们创建一个 `chat.ts` 文件,并在其中定义 `ChatMessage` 类型:
|
||||
|
||||
```ts
|
||||
// src/types/chat.ts
|
||||
|
||||
export type ChatMessage = {
|
||||
id: string;
|
||||
content: string;
|
||||
timestamp: number;
|
||||
sender: 'user' | 'bot';
|
||||
};
|
||||
```
|
||||
|
||||
## 3. 创建 Zustand Store
|
||||
|
||||
在 `src/store` 目录下,我们需要创建一个新的 Zustand Store 来管理 "会话消息" 的状态。例如,我们创建一个 `chatStore.ts` 文件,并在其中定义一个 Zustand Store:
|
||||
|
||||
```ts
|
||||
// src/store/chatStore.ts
|
||||
import { create } from 'zustand';
|
||||
|
||||
type ChatState = {
|
||||
messages: ChatMessage[];
|
||||
addMessage: (message: ChatMessage) => void;
|
||||
};
|
||||
|
||||
export const useChatStore = create<ChatState>((set) => ({
|
||||
messages: [],
|
||||
addMessage: (message) => set((state) => ({ messages: [...state.messages, message] })),
|
||||
}));
|
||||
```
|
||||
|
||||
## 4. 创建页面与组件
|
||||
|
||||
在 `src/app/<new-page>/features/<new-feature>.tsx` 中,我们需要创建一个新的页面或组件来显示 "会话消息"。在这个文件中,我们可以使用上面创建的 Zustand Store,以及 Ant Design 的组件来构建 UI:
|
||||
|
||||
```jsx
|
||||
// src/app/chat/features/ChatPage/index.tsx
|
||||
// 注意:使用 src/app/<page>/features/ 放置页面专属组件
|
||||
import { List, Typography } from 'antd';
|
||||
import { useChatStore } from 'src/store/chatStore';
|
||||
|
||||
const ChatPage = () => {
|
||||
const messages = useChatStore((state) => state.messages);
|
||||
|
||||
return (
|
||||
<List
|
||||
dataSource={messages}
|
||||
renderItem={(message) => (
|
||||
<List.Item>
|
||||
<Typography.Text>{message.content}</Typography.Text>
|
||||
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatPage;
|
||||
```
|
||||
|
||||
> **关于功能组件组织方式的说明**:LobeHub 使用两种模式来组织功能组件:
|
||||
>
|
||||
> - **全局功能**(`src/features/`):跨应用共享的组件,如 `ChatInput`、`Conversation` 等
|
||||
> - **页面专属功能**(`src/app/<page>/features/`):仅在特定页面路由中使用的组件
|
||||
>
|
||||
> 根据可复用性选择合适的方式。如果不确定,可以先放在页面专属位置,需要时再重构为全局共享。
|
||||
|
||||
## 5. 功能绑定
|
||||
|
||||
在页面或组件中,我们需要将 Zustand Store 的状态和方法绑定到 UI 上。在上面的示例中,我们已经将 `messages` 状态绑定到了列表的 `dataSource` 属性上。现在,我们还需要一个方法来添加新的消息。我们可以在 Zustand Store 中定义这个方法,然后在页面或组件中使用它:
|
||||
|
||||
```jsx
|
||||
import { Button } from 'antd';
|
||||
|
||||
const ChatPage = () => {
|
||||
const messages = useChatStore((state) => state.messages);
|
||||
const addMessage = useChatStore((state) => state.addMessage);
|
||||
|
||||
const handleSend = () => {
|
||||
addMessage({ id: '1', content: 'Hello, world!', timestamp: Date.now(), sender: 'user' });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<List
|
||||
dataSource={messages}
|
||||
renderItem={(message) => (
|
||||
<List.Item>
|
||||
<Typography.Text>{message.content}</Typography.Text>
|
||||
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
<Button onClick={handleSend}>Send</Button>
|
||||
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ChatPage;
|
||||
```
|
||||
|
||||
以上就是在 LobeHub 中实现 "会话消息" 功能的步骤。当然,在 LobeHub 的实际开发中,真实场景所面临的业务诉求和场景远比上述 demo 复杂,请根据实际情况进行开发。
|
||||
@@ -17,14 +17,12 @@ We will use [RFC 021 - Custom Assistant Opening Guidance](https://github.com/lob
|
||||
|
||||
## 1. Update Schema
|
||||
|
||||
lobehub uses a postgres database, with the browser-side local database using [pglite](https://pglite.dev/) (wasm version of postgres). The project also uses [drizzle](https://orm.drizzle.team/) ORM to operate the database.
|
||||
LobeHub uses a PostgreSQL database, with [Drizzle ORM](https://orm.drizzle.team/) to operate the database.
|
||||
|
||||
Compared to the old solution where the browser side used indexDB, having both the browser side and server side use postgres has the advantage that the model layer code can be completely reused.
|
||||
|
||||
All schemas are uniformly placed in `src/database/schemas`. We need to adjust the `agents` table to add two fields corresponding to the configuration items:
|
||||
All schemas are located in `packages/database/src/schemas/`. We need to adjust the `agents` table to add two fields corresponding to the configuration items:
|
||||
|
||||
```diff
|
||||
// src/database/schemas/agent.ts
|
||||
// packages/database/src/schemas/agent.ts
|
||||
export const agents = pgTable(
|
||||
'agents',
|
||||
{
|
||||
@@ -53,22 +51,15 @@ export const agents = pgTable(
|
||||
|
||||
Note that sometimes we may also need to update the index, but for this feature, we don't have any related performance bottleneck issues, so we don't need to update the index.
|
||||
|
||||
After adjusting the schema, we need to run `npm run db:generate` to use drizzle-kit's built-in database migration capability to generate the corresponding SQL code for migrating to the latest schema. After execution, four files will be generated:
|
||||
### Database Migration
|
||||
|
||||
- src/database/migrations/meta/\_journal.json: Saves information about each migration
|
||||
- src/database/migrations/0021\_add\_agent\_opening\_settings.sql: SQL commands for this migration
|
||||
- src/database/client/migrations.json: SQL commands for this migration used by pglite
|
||||
- src/database/migrations/meta/0021\_snapshot.json: The current latest complete database snapshot
|
||||
|
||||
Note that the migration SQL filename generated by the script by default is not semantically clear like `0021_add_agent_opening_settings.sql`. You need to manually rename it and update `src/database/migrations/meta/_journal.json`.
|
||||
|
||||
Previously, client-side storage using indexDB made database migration relatively complicated. Now with pglite on the local side, database migration is a simple command, very quick and easy. You can also check if there's any room for optimization in the generated migration SQL and make manual adjustments.
|
||||
After adjusting the schema, you need to generate and optimize migration files. See the [Database Migration Guide](https://github.com/lobehub/lobe-chat/blob/main/.agents/skills/drizzle/references/db-migrations.md) for detailed steps.
|
||||
|
||||
## 2. Update Data Model
|
||||
|
||||
Data models used in our project are defined in `src/types`. We don't directly use the types exported from the drizzle schema, such as `export type NewAgent = typeof agents.$inferInsert;`, but instead define corresponding data models based on frontend requirements and data types of the corresponding fields in the db schema definition.
|
||||
Data models are defined in `packages/types/src/`. We don't directly use the types exported from the Drizzle schema (e.g., `typeof agents.$inferInsert`), but instead define independent data models based on frontend requirements.
|
||||
|
||||
Data model definitions are placed in the `src/types` folder. Update the `LobeAgentConfig` type in `src/types/agent/index.ts`:
|
||||
Update the `LobeAgentConfig` type in `packages/types/src/agent/index.ts`:
|
||||
|
||||
```diff
|
||||
export interface LobeAgentConfig {
|
||||
@@ -93,51 +84,42 @@ export interface LobeAgentConfig {
|
||||
* Language model parameters
|
||||
*/
|
||||
params: LLMParams;
|
||||
/**
|
||||
* Enabled plugins
|
||||
*/
|
||||
plugins?: string[];
|
||||
|
||||
/**
|
||||
* Model provider
|
||||
*/
|
||||
provider?: string;
|
||||
|
||||
/**
|
||||
* System role
|
||||
*/
|
||||
systemRole: string;
|
||||
|
||||
/**
|
||||
* Text-to-speech service
|
||||
*/
|
||||
tts: LobeAgentTTSConfig;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Service Implementation / Model Implementation
|
||||
## 3. Service / Model Layer Implementation
|
||||
|
||||
- The `model` layer encapsulates reusable operations on the DB
|
||||
- The `service` layer implements application business logic
|
||||
The project is divided into multiple frontend and backend layers by responsibility:
|
||||
|
||||
Both have corresponding top-level folders in the `src` directory.
|
||||
```plaintext
|
||||
+-------------------+--------------------------------------+------------------------------------------------------+
|
||||
| Layer | Location | Responsibility |
|
||||
+-------------------+--------------------------------------+------------------------------------------------------+
|
||||
| Client Service | src/services/ | Reusable frontend business logic, often multiple tRPC |
|
||||
| WebAPI | src/app/(backend)/webapi/ | REST API endpoints |
|
||||
| tRPC Router | src/server/routers/ | tRPC entry, validates input, routes to service |
|
||||
| Server Service | src/server/services/ | Server-side business logic, with DB access |
|
||||
| Server Module | src/server/modules/ | Server-side modules, no direct DB access |
|
||||
| Repository | packages/database/src/repositories/ | Complex queries, cross-table operations |
|
||||
| DB Model | packages/database/src/models/ | Single-table CRUD operations |
|
||||
+-------------------+--------------------------------------+------------------------------------------------------+
|
||||
```
|
||||
|
||||
We need to check if we need to update their implementation. Agent configuration in the frontend is abstracted as session configuration. In `src/services/session/server.ts` we can see a service function `updateSessionConfig`:
|
||||
**Client Service** is frontend code that encapsulates reusable business logic, calling the backend via tRPC client. For example, `src/services/session/index.ts`:
|
||||
|
||||
```typescript
|
||||
export class ServerService implements ISessionService {
|
||||
// ...
|
||||
updateSessionConfig: ISessionService['updateSessionConfig'] = (id, config, signal) => {
|
||||
export class SessionService {
|
||||
updateSessionConfig = (id: string, config: PartialDeep<LobeAgentConfig>, signal?: AbortSignal) => {
|
||||
return lambdaClient.session.updateSessionConfig.mutate({ id, value: config }, { signal });
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Jumping to the implementation of `lambdaClient.session.updateSessionConfig`, we find that it simply **merges** the new config with the old config.
|
||||
**tRPC Router** is the backend entry point (`src/server/routers/lambda/`), validates input and calls Server Service for business logic:
|
||||
|
||||
```typescript
|
||||
export const sessionRouter = router({
|
||||
// ..
|
||||
updateSessionConfig: sessionProcedure
|
||||
.input(
|
||||
z.object({
|
||||
@@ -154,13 +136,13 @@ export const sessionRouter = router({
|
||||
});
|
||||
```
|
||||
|
||||
Foreseeably, the frontend will add two inputs, calling updateSessionConfig upon user modification. As the current implementation lacks field-level granularity for the config update, the service and model layers remain unaffected.
|
||||
For this feature, `updateSessionConfig` simply merges config without field-level granularity, so none of the layers need modification.
|
||||
|
||||
## 4. Frontend Implementation
|
||||
|
||||
### Data Flow Store Implementation
|
||||
|
||||
lobehub uses [zustand](https://zustand.docs.pmnd.rs/getting-started/introduction) as the global state management framework. For detailed practices on state management, you can refer to [📘 State Management Best Practices](/docs/development/state-management/state-management-intro).
|
||||
LobeHub uses [zustand](https://zustand.docs.pmnd.rs/getting-started/introduction) as the global state management framework. For detailed practices on state management, refer to [State Management Best Practices](/docs/development/state-management/state-management-intro).
|
||||
|
||||
There are two stores related to the agent:
|
||||
|
||||
@@ -193,17 +175,9 @@ export const DEFAULT_AGENT_CONFIG: LobeAgentConfig = {
|
||||
|
||||
Actually, you don't even need to update this since the `openingQuestions` type is already optional. I'm not updating `openingMessage` here.
|
||||
|
||||
Because we've added two new fields, to facilitate access by components in the `src/features/AgentSetting/AgentOpening` folder and for performance optimization, we add related selectors in `src/features/AgentSetting/store/selectors.ts`:
|
||||
Because we've added two new fields, to facilitate component access in `src/features/AgentSetting/AgentOpening` and for performance optimization, we add related selectors in `src/features/AgentSetting/store/selectors.ts`:
|
||||
|
||||
```diff
|
||||
import { DEFAULT_AGENT_CHAT_CONFIG } from '@/const/settings';
|
||||
import { LobeAgentChatConfig } from '@/types/agent';
|
||||
|
||||
import { Store } from './action';
|
||||
|
||||
const chatConfig = (s: Store): LobeAgentChatConfig =>
|
||||
s.config.chatConfig || DEFAULT_AGENT_CHAT_CONFIG;
|
||||
|
||||
+export const DEFAULT_OPENING_QUESTIONS: string[] = [];
|
||||
export const selectors = {
|
||||
chatConfig,
|
||||
@@ -212,7 +186,7 @@ export const selectors = {
|
||||
};
|
||||
```
|
||||
|
||||
We won't add additional actions to update the agent config here, as I've observed that other existing code also directly uses the unified `setChatConfig` in the existing code:
|
||||
We won't add additional actions to update the agent config here, as existing code also directly uses the unified `setAgentConfig`:
|
||||
|
||||
```typescript
|
||||
export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, get) => ({
|
||||
@@ -224,14 +198,11 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
||||
|
||||
#### Update store/agent
|
||||
|
||||
In the component `src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx`, we use this store to get the current agent configuration to render user-customized opening messages and guiding questions.
|
||||
|
||||
Since we only need to read two configuration items, we'll simply add two selectors:
|
||||
In the display component we use `src/store/agent` to get the current agent configuration. Simply add two selectors:
|
||||
|
||||
Update `src/store/agent/slices/chat/selectors/agent.ts`:
|
||||
|
||||
```diff
|
||||
// ...
|
||||
+const openingQuestions = (s: AgentStoreState) =>
|
||||
+ currentAgentConfig(s).openingQuestions || DEFAULT_OPENING_QUESTIONS;
|
||||
+const openingMessage = (s: AgentStoreState) => currentAgentConfig(s).openingMessage || '';
|
||||
@@ -244,63 +215,48 @@ export const agentSelectors = {
|
||||
};
|
||||
```
|
||||
|
||||
### i18n Handling
|
||||
|
||||
LobeHub is an internationalized project using [react-i18next](https://github.com/i18next/react-i18next). Newly added UI text needs to:
|
||||
|
||||
1. Add keys to the corresponding namespace file in `src/locales/default/` (default language is English):
|
||||
|
||||
```typescript
|
||||
// src/locales/default/setting.ts
|
||||
export default {
|
||||
// ...
|
||||
'settingOpening.title': 'Opening Settings',
|
||||
'settingOpening.openingMessage.title': 'Opening Message',
|
||||
'settingOpening.openingMessage.placeholder': 'Enter a custom opening message...',
|
||||
'settingOpening.openingQuestions.title': 'Opening Questions',
|
||||
'settingOpening.openingQuestions.placeholder': 'Enter a guiding question',
|
||||
'settingOpening.openingQuestions.empty': 'No opening questions yet',
|
||||
'settingOpening.openingQuestions.repeat': 'Question already exists',
|
||||
};
|
||||
```
|
||||
|
||||
2. If a new namespace is added, export it in `src/locales/default/index.ts`
|
||||
3. For dev preview: manually translate the corresponding JSON files in `locales/zh-CN/` and `locales/en-US/`
|
||||
4. CI will automatically run `pnpm i18n` to generate translations for other languages
|
||||
|
||||
Key naming convention uses flat dot notation: `{feature}.{context}.{action|status}`.
|
||||
|
||||
### UI Implementation and Action Binding
|
||||
|
||||
We're adding a new category of settings this time. In `src/features/AgentSetting`, various UI components for agent settings are defined. This time we're adding a setting type: opening settings. We'll add a folder `AgentOpening` to store opening settings-related components. The project uses:
|
||||
We're adding a new category of settings. In `src/features/AgentSetting`, various UI components for agent settings are defined. We'll add an `AgentOpening` folder for opening settings components.
|
||||
|
||||
- [ant-design](https://ant.design/) and [lobe-ui](https://github.com/lobehub/lobe-ui): component libraries
|
||||
- [antd-style](https://ant-design.github.io/antd-style): css-in-js solution
|
||||
- [@lobehub/ui](https://ui.lobehub.com/): UI component library (includes Flexbox and Center for responsive layouts)
|
||||
- [@ant-design/icons](https://ant.design/components/icon-cn) and [lucide](https://lucide.dev/icons/): icon libraries
|
||||
- [react-i18next](https://github.com/i18next/react-i18next) and [lobe-i18n](https://github.com/lobehub/lobe-cli-toolbox/tree/master/packages/lobe-i18n): i18n framework and multi-language automatic translation tool
|
||||
|
||||
lobehub is an internationalized project, so newly added text needs to update the default `locale` file: `src/locales/default/setting.ts`.
|
||||
|
||||
Let's take the subcomponent `OpeningQuestion.tsx` as an example. Component implementation:
|
||||
Taking the subcomponent `OpeningQuestions.tsx` as an example, here's the key logic (style code omitted):
|
||||
|
||||
```typescript
|
||||
// src/features/AgentSetting/AgentOpening/OpeningQuestions.tsx
|
||||
'use client';
|
||||
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Flexbox, SortableList } from '@lobehub/ui';
|
||||
import { Button, Empty, Input } from 'antd';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useStore } from '../store';
|
||||
import { selectors } from '../store/selectors';
|
||||
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
empty: css`
|
||||
margin-block: 24px;
|
||||
margin-inline: 0;
|
||||
`,
|
||||
questionItemContainer: css`
|
||||
margin-block-end: 8px;
|
||||
padding-block: 2px;
|
||||
padding-inline: 10px 0;
|
||||
background: ${cssVar.colorBgContainer};
|
||||
`,
|
||||
questionItemContent: css`
|
||||
flex: 1;
|
||||
`,
|
||||
questionsList: css`
|
||||
width: 100%;
|
||||
margin-block-start: 16px;
|
||||
`,
|
||||
repeatError: css`
|
||||
margin: 0;
|
||||
color: ${cssVar.colorErrorText};
|
||||
`,
|
||||
}));
|
||||
|
||||
interface QuestionItem {
|
||||
content: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
const OpeningQuestions = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const [questionInput, setQuestionInput] = useState('');
|
||||
@@ -318,7 +274,6 @@ const OpeningQuestions = memo(() => {
|
||||
|
||||
const addQuestion = useCallback(() => {
|
||||
if (!questionInput.trim()) return;
|
||||
|
||||
setQuestions([...openingQuestions, questionInput.trim()]);
|
||||
setQuestionInput('');
|
||||
}, [openingQuestions, questionInput, setQuestions]);
|
||||
@@ -333,106 +288,27 @@ const OpeningQuestions = memo(() => {
|
||||
[openingQuestions, setQuestions],
|
||||
);
|
||||
|
||||
// Handle logic after drag and drop sorting
|
||||
const handleSortEnd = useCallback(
|
||||
(items: QuestionItem[]) => {
|
||||
setQuestions(items.map((item) => item.content));
|
||||
},
|
||||
[setQuestions],
|
||||
);
|
||||
|
||||
const items: QuestionItem[] = useMemo(() => {
|
||||
return openingQuestions.map((item, index) => ({
|
||||
content: item,
|
||||
id: index,
|
||||
}));
|
||||
}, [openingQuestions]);
|
||||
|
||||
const isRepeat = openingQuestions.includes(questionInput.trim());
|
||||
|
||||
return (
|
||||
<Flexbox gap={8}>
|
||||
<Flexbox gap={4}>
|
||||
<Input
|
||||
addonAfter={
|
||||
<Button
|
||||
// don't allow repeat
|
||||
disabled={openingQuestions.includes(questionInput.trim())}
|
||||
icon={<PlusOutlined />}
|
||||
onClick={addQuestion}
|
||||
size="small"
|
||||
type="text"
|
||||
/>
|
||||
}
|
||||
onChange={(e) => setQuestionInput(e.target.value)}
|
||||
onPressEnter={addQuestion}
|
||||
placeholder={t('settingOpening.openingQuestions.placeholder')}
|
||||
value={questionInput}
|
||||
/>
|
||||
|
||||
{isRepeat && (
|
||||
<p className={styles.repeatError}>{t('settingOpening.openingQuestions.repeat')}</p>
|
||||
)}
|
||||
|
||||
</Flexbox>
|
||||
|
||||
<div className={styles.questionsList}>
|
||||
{openingQuestions.length > 0 ? (
|
||||
<SortableList
|
||||
items={items}
|
||||
onChange={handleSortEnd}
|
||||
renderItem={(item) => (
|
||||
<SortableList.Item className={styles.questionItemContainer} id={item.id}>
|
||||
<SortableList.DragHandle />
|
||||
<div className={styles.questionItemContent}>{item.content}</div>
|
||||
<Button
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => removeQuestion(item.content)}
|
||||
type="text"
|
||||
/>
|
||||
|
||||
</SortableList.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty
|
||||
className={styles.empty}
|
||||
description={t('settingOpening.openingQuestions.empty')}
|
||||
/>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
</Flexbox>
|
||||
);
|
||||
// Render Input + SortableList, see component library docs for UI details
|
||||
// ...
|
||||
});
|
||||
|
||||
export default OpeningQuestions;
|
||||
```
|
||||
|
||||
At the same time, we need to display the opening configuration set by the user, which is on the chat page. The corresponding component is in `src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx`:
|
||||
Key points:
|
||||
- Read store config via `selectors`
|
||||
- Update config via the `setAgentConfig` action
|
||||
- Use `useTranslation('setting')` for i18n text
|
||||
|
||||
We also need to display the opening configuration set by the user on the chat page. The corresponding component is in `src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx`:
|
||||
|
||||
```typescript
|
||||
const WelcomeMessage = () => {
|
||||
const { t } = useTranslation('chat');
|
||||
|
||||
// Get current opening configuration
|
||||
// Get current opening configuration from store/agent
|
||||
const openingMessage = useAgentStore(agentSelectors.openingMessage);
|
||||
const openingQuestions = useAgentStore(agentSelectors.openingQuestions);
|
||||
|
||||
const meta = useSessionStore(sessionMetaSelectors.currentAgentMeta, isEqual);
|
||||
const { isAgentEditable } = useServerConfigStore(featureFlagsSelectors);
|
||||
const activeId = useChatStore((s) => s.activeId);
|
||||
|
||||
const agentSystemRoleMsg = t('agentDefaultMessageWithSystemRole', {
|
||||
name: meta.title || t('defaultAgent'),
|
||||
systemRole: meta.description,
|
||||
});
|
||||
|
||||
const agentMsg = t(isAgentEditable ? 'agentDefaultMessage' : 'agentDefaultMessageWithoutEdit', {
|
||||
name: meta.title || t('defaultAgent'),
|
||||
url: `/chat/settings?session=${activeId}`,
|
||||
});
|
||||
|
||||
const message = useMemo(() => {
|
||||
// Use user-set message if available
|
||||
@@ -440,38 +316,50 @@ const WelcomeMessage = () => {
|
||||
return !!meta.description ? agentSystemRoleMsg : agentMsg;
|
||||
}, [openingMessage, agentSystemRoleMsg, agentMsg, meta.description]);
|
||||
|
||||
const chatItem = (
|
||||
<ChatItem
|
||||
avatar={meta}
|
||||
editing={false}
|
||||
message={message}
|
||||
placement={'left'}
|
||||
type={type === 'chat' ? 'block' : 'pure'}
|
||||
/>
|
||||
);
|
||||
|
||||
return openingQuestions.length > 0 ? (
|
||||
<Flexbox>
|
||||
{chatItem}
|
||||
<ChatItem avatar={meta} message={message} placement="left" />
|
||||
{/* Render guiding questions */}
|
||||
<OpeningQuestions mobile={mobile} questions={openingQuestions} />
|
||||
|
||||
</Flexbox>
|
||||
<OpeningQuestions questions={openingQuestions} />
|
||||
</Flexbox>
|
||||
) : (
|
||||
chatItem
|
||||
<ChatItem avatar={meta} message={message} placement="left" />
|
||||
);
|
||||
};
|
||||
export default WelcomeMessage;
|
||||
```
|
||||
|
||||
## 5. Testing
|
||||
|
||||
The project uses vitest for unit testing.
|
||||
The project uses Vitest for unit testing. See the [Testing Skill Guide](https://github.com/lobehub/lobe-chat/blob/main/.agents/skills/testing/SKILL.md) for details.
|
||||
|
||||
Since our two new configuration fields are both optional, theoretically you could pass the tests without updating them. However, since we added the `openingQuestions` field to the `DEFAULT_AGENT_CONFIG` mentioned earlier, this causes many tests to calculate configurations that include this field, so we still need to update some test snapshots.
|
||||
**Running tests:**
|
||||
|
||||
For the current scenario, I recommend running the tests locally to see which tests fail, and then update them as needed. For example, for the test file `src/store/agent/slices/chat/selectors/agent.test.ts`, you need to run `bunx vitest -u src/store/agent/slices/chat/selectors/agent.test.ts` to update the snapshot.
|
||||
```bash
|
||||
# Run specific test file (never run bun run test — full suite is very slow)
|
||||
bunx vitest run --silent='passed-only' '[file-path]'
|
||||
|
||||
# Database package tests
|
||||
cd packages/database && bunx vitest run --silent='passed-only' '[file]'
|
||||
```
|
||||
|
||||
**Testing suggestions for new features:**
|
||||
|
||||
Since our two new configuration fields are both optional, theoretically tests would pass without updates. However, if you modified default config (e.g., added `openingQuestions` to `DEFAULT_AGENT_CONFIG`), some test snapshots may become stale and need updating.
|
||||
|
||||
It's recommended to run related tests locally first to see which fail, then update as needed. For example:
|
||||
|
||||
```bash
|
||||
bunx vitest run --silent='passed-only' 'src/store/agent/slices/chat/selectors/agent.test.ts'
|
||||
```
|
||||
|
||||
If you just want to check whether existing tests pass without running locally, you can also check the GitHub Actions test results directly.
|
||||
|
||||
**More testing scenario guides:**
|
||||
|
||||
- DB Model testing: `.agents/skills/testing/references/db-model-test.md`
|
||||
- Zustand Store Action testing: `.agents/skills/testing/references/zustand-store-action-test.md`
|
||||
- Electron IPC testing: `.agents/skills/testing/references/electron-ipc-test.md`
|
||||
|
||||
## Summary
|
||||
|
||||
The above is the complete implementation process for the LobeHub opening settings feature. Developers can refer to this document for the development and testing of related features.
|
||||
The above is the complete implementation process for the LobeHub opening settings feature, covering the full chain from database schema → data model → Service/Model → Store → i18n → UI → testing. Developers can refer to this document for developing related features.
|
||||
|
||||
@@ -14,16 +14,14 @@ tags:
|
||||
|
||||
我们将以 [RFC 021 - 自定义助手开场引导](https://github.com/lobehub/lobehub/discussions/891) 为例,阐述完整的实现流程。
|
||||
|
||||
## 一、更新 schema
|
||||
## 一、更新 Schema
|
||||
|
||||
lobehub 使用 postgres 数据库,浏览器端本地数据库使用 [pglite](https://pglite.dev/)(wasm 版本 postgres)。项目还使用了 [drizzle](https://orm.drizzle.team/) ORM 用来操作数据库。
|
||||
LobeHub 使用 PostgreSQL 数据库,项目使用 [Drizzle ORM](https://orm.drizzle.team/) 来操作数据库。
|
||||
|
||||
相比旧方案浏览器端使用 indexDB 来说,浏览器端和 server 端都使用 postgres 好处在于 model 层代码可以完全复用。
|
||||
|
||||
schemas 都统一放在 `src/database/schemas`,我们需要调整 `agents` 表增加两个配置项对应的字段:
|
||||
Schemas 统一放在 `packages/database/src/schemas/` 下,我们需要调整 `agents` 表增加两个配置项对应的字段:
|
||||
|
||||
```diff
|
||||
// src/database/schemas/agent.ts
|
||||
// packages/database/src/schemas/agent.ts
|
||||
export const agents = pgTable(
|
||||
'agents',
|
||||
{
|
||||
@@ -52,22 +50,15 @@ export const agents = pgTable(
|
||||
|
||||
需要注意的是,有些时候我们可能还需要更新索引,但对于这个需求我们没有相关的性能瓶颈问题,所以不需要更新索引。
|
||||
|
||||
调整完 schema 后我们需要运行 `npm run db:generate,` 使用 drizzle-kit 自带的数据库迁移能力生成对应的用于迁移到最新 schema 的 sql 代码。执行后会产生四个文件:
|
||||
### 数据库迁移
|
||||
|
||||
- src/database/migrations/meta/\_journal.json:保存每次迁移的相关信息
|
||||
- src/database/migrations/0021\_add\_agent\_opening\_settings.sql:此次迁移的 sql 命令
|
||||
- src/database/client/migrations.json:pglite 使用的此次迁移的 sql 命令
|
||||
- src/database/migrations/meta/0021\_snapshot.json:当前最新的完整数据库快照
|
||||
|
||||
注意脚本默认生成的迁移 sql 文件名不会像 `0021_add_agent_opening_settings.sql` 这样语义清晰,你需要自己手动对它重命名并且更新 `src/database/migrations/meta/_journal.json`。
|
||||
|
||||
以前客户端存储使用 indexDB 数据迁移相对麻烦,现在本地端使用 pglite 之后数据库迁移就是一条命令的事,非常简单快捷,你也可以检查生成的迁移 sql 是否有什么优化空间,手动调整。
|
||||
调整完 schema 后需要生成并优化迁移文件,详细步骤请参阅 [数据库迁移指南](https://github.com/lobehub/lobe-chat/blob/main/.agents/skills/drizzle/references/db-migrations.md)。
|
||||
|
||||
## 二、更新数据模型
|
||||
|
||||
在 `src/types` 下定义了我们项目中使用到的各种数据模型,我们并没有直接使用 drizzle schema 导出的类型,例如 `export type NewAgent = typeof agents.$inferInsert;`,而是根据前端需求和 db schema 定义中对应字段数据类型定义了对应的数据模型。
|
||||
数据模型定义在 `packages/types/src/` 下,我们并没有直接使用 Drizzle schema 导出的类型(例如 `typeof agents.$inferInsert`),而是根据前端需求定义了独立的数据模型。
|
||||
|
||||
数据模型定义都放在 `src/types` 文件夹下,更新 `src/types/agent/index.ts` 中 `LobeAgentConfig` 类型:
|
||||
更新 `packages/types/src/agent/index.ts` 中 `LobeAgentConfig` 类型:
|
||||
|
||||
```diff
|
||||
export interface LobeAgentConfig {
|
||||
@@ -92,51 +83,42 @@ export interface LobeAgentConfig {
|
||||
* 语言模型参数
|
||||
*/
|
||||
params: LLMParams;
|
||||
/**
|
||||
* 启用的插件
|
||||
*/
|
||||
plugins?: string[];
|
||||
|
||||
/**
|
||||
* 模型供应商
|
||||
*/
|
||||
provider?: string;
|
||||
|
||||
/**
|
||||
* 系统角色
|
||||
*/
|
||||
systemRole: string;
|
||||
|
||||
/**
|
||||
* 语音服务
|
||||
*/
|
||||
tts: LobeAgentTTSConfig;
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## 三、Service 实现 / Model 实现
|
||||
## 三、Service / Model 各层实现
|
||||
|
||||
- `model` 层封装对 DB 的可复用操作
|
||||
- `service` 层实现应用业务逻辑
|
||||
项目按职责分为前端和后端多层,完整的分层如下:
|
||||
|
||||
在 `src` 目录下都有对应的顶层文件夹。
|
||||
```plaintext
|
||||
+-------------------+--------------------------------------+------------------------------------------------------+
|
||||
| Layer | Location | Responsibility |
|
||||
+-------------------+--------------------------------------+------------------------------------------------------+
|
||||
| Client Service | src/services/ | 封装前端可复用的业务逻辑,一般涉及多个后端请求(tRPC) |
|
||||
| WebAPI | src/app/(backend)/webapi/ | REST API 端点 |
|
||||
| tRPC Router | src/server/routers/ | tRPC 入口,校验输入,路由到 service |
|
||||
| Server Service | src/server/services/ | 服务端业务逻辑,可访问数据库 |
|
||||
| Server Module | src/server/modules/ | 服务端模块,不直接访问数据库 |
|
||||
| Repository | packages/database/src/repositories/ | 封装复杂查询、跨表操作 |
|
||||
| DB Model | packages/database/src/models/ | 封装单表的 CRUD 操作 |
|
||||
+-------------------+--------------------------------------+------------------------------------------------------+
|
||||
```
|
||||
|
||||
我们需要查看是否需要更新其实现,agent 配置在前端被抽象成 session 的配置,在 `src/services/session/server.ts` 中可以看到有个 service 函数 `updateSessionConfig`:
|
||||
**Client Service** 是前端代码,封装可复用的业务逻辑,通过 tRPC 客户端调用后端。例如 `src/services/session/index.ts`:
|
||||
|
||||
```typescript
|
||||
export class ServerService implements ISessionService {
|
||||
// ...
|
||||
updateSessionConfig: ISessionService['updateSessionConfig'] = (id, config, signal) => {
|
||||
export class SessionService {
|
||||
updateSessionConfig = (id: string, config: PartialDeep<LobeAgentConfig>, signal?: AbortSignal) => {
|
||||
return lambdaClient.session.updateSessionConfig.mutate({ id, value: config }, { signal });
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
跳转 `lambdaClient.session.updateSessionConfig` 实现,发现它只是简单的 **merge** 了新的 config 和旧的 config。
|
||||
**tRPC Router** 是后端入口(`src/server/routers/lambda/`),校验输入后调用 Server Service 处理业务逻辑:
|
||||
|
||||
```typescript
|
||||
export const sessionRouter = router({
|
||||
// ..
|
||||
updateSessionConfig: sessionProcedure
|
||||
.input(
|
||||
z.object({
|
||||
@@ -153,13 +135,13 @@ export const sessionRouter = router({
|
||||
});
|
||||
```
|
||||
|
||||
可以预想的到,我们前端会增加两个输入,用户修改的时候去调用这个 `updateSessionConfig`,而目前的时候都没细粒度到 config 中的具体字段,因此,service 层和 model 层不需要修改。
|
||||
对于本次需求,`updateSessionConfig` 只是简单 merge config,并没有细粒度到具体字段,因此各层都不需要修改。
|
||||
|
||||
## 四、前端实现
|
||||
|
||||
### 数据流 store 实现
|
||||
### 数据流 Store 实现
|
||||
|
||||
lobehub 使用 [zustand](https://zustand.docs.pmnd.rs/getting-started/introduction) 作为全局状态管理框架,对于状态管理的详细实践介绍,可以查阅 [📘 状态管理最佳实践](/zh/docs/development/state-management/state-management-intro)。
|
||||
LobeHub 使用 [zustand](https://zustand.docs.pmnd.rs/getting-started/introduction) 作为全局状态管理框架,对于状态管理的详细实践介绍,可以查阅 [📘 状态管理最佳实践](/zh/docs/development/state-management/state-management-intro)。
|
||||
|
||||
和 agent 相关的 store 有两个:
|
||||
|
||||
@@ -195,14 +177,6 @@ export const DEFAULT_AGENT_CONFIG: LobeAgentConfig = {
|
||||
因为我们增加了两个新字段,为了方便在 `src/features/AgentSetting/AgentOpening` 文件夹中组件访问和性能优化,我们在 `src/features/AgentSetting/store/selectors.ts` 增加相关的 selectors:
|
||||
|
||||
```diff
|
||||
import { DEFAULT_AGENT_CHAT_CONFIG } from '@/const/settings';
|
||||
import { LobeAgentChatConfig } from '@/types/agent';
|
||||
|
||||
import { Store } from './action';
|
||||
|
||||
const chatConfig = (s: Store): LobeAgentChatConfig =>
|
||||
s.config.chatConfig || DEFAULT_AGENT_CHAT_CONFIG;
|
||||
|
||||
+export const DEFAULT_OPENING_QUESTIONS: string[] = [];
|
||||
export const selectors = {
|
||||
chatConfig,
|
||||
@@ -211,7 +185,7 @@ export const selectors = {
|
||||
};
|
||||
```
|
||||
|
||||
这里我们就不增加额外的 action 用于更新 agent config 了,因为我观察到已有的其它代码也是直接使用现有代码中统一的 `setChatConfig`:
|
||||
这里我们就不增加额外的 action 用于更新 agent config 了,因为已有的代码也是直接使用统一的 `setAgentConfig`:
|
||||
|
||||
```typescript
|
||||
export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, get) => ({
|
||||
@@ -223,14 +197,11 @@ export const store: StateCreator<Store, [['zustand/devtools', never]]> = (set, g
|
||||
|
||||
#### 更新 store/agent
|
||||
|
||||
在组件 `src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx` 我们使用了这个 store 用于获取当前 agent 配置,用来渲染用户自定义的开场消息和引导性问题。
|
||||
|
||||
这里因为只需要读取两个配置项,我们就简单的加两个 selectors 就好了:
|
||||
在展示组件中我们使用 `src/store/agent` 获取当前 agent 配置,简单加两个 selectors:
|
||||
|
||||
更新 `src/store/agent/slices/chat/selectors/agent.ts`:
|
||||
|
||||
```diff
|
||||
// ...
|
||||
+const openingQuestions = (s: AgentStoreState) =>
|
||||
+ currentAgentConfig(s).openingQuestions || DEFAULT_OPENING_QUESTIONS;
|
||||
+const openingMessage = (s: AgentStoreState) => currentAgentConfig(s).openingMessage || '';
|
||||
@@ -243,63 +214,48 @@ export const agentSelectors = {
|
||||
};
|
||||
```
|
||||
|
||||
### i18n 处理
|
||||
|
||||
LobeHub 是国际化项目,使用 [react-i18next](https://github.com/i18next/react-i18next) 作为 i18n 框架。新增的 UI 文案需要:
|
||||
|
||||
1. 在 `src/locales/default/` 对应的 namespace 文件中添加 key(默认语言为英文):
|
||||
|
||||
```typescript
|
||||
// src/locales/default/setting.ts
|
||||
export default {
|
||||
// ...
|
||||
'settingOpening.title': 'Opening Settings',
|
||||
'settingOpening.openingMessage.title': 'Opening Message',
|
||||
'settingOpening.openingMessage.placeholder': 'Enter a custom opening message...',
|
||||
'settingOpening.openingQuestions.title': 'Opening Questions',
|
||||
'settingOpening.openingQuestions.placeholder': 'Enter a guiding question',
|
||||
'settingOpening.openingQuestions.empty': 'No opening questions yet',
|
||||
'settingOpening.openingQuestions.repeat': 'Question already exists',
|
||||
};
|
||||
```
|
||||
|
||||
2. 如果新增了 namespace,需要在 `src/locales/default/index.ts` 中导出
|
||||
3. 开发预览时手动翻译 `locales/zh-CN/` 和 `locales/en-US/` 对应的 JSON 文件
|
||||
4. CI 会自动运行 `pnpm i18n` 生成其他语言的翻译
|
||||
|
||||
key 的命名规范为扁平的 dot notation:`{feature}.{context}.{action|status}`。
|
||||
|
||||
### UI 实现和 action 绑定
|
||||
|
||||
我们这次要新增一个类别的设置, 在 `src/features/AgentSetting` 中定义了 agent 的各种设置的 UI 组件,这次我们要增加一个设置类型:开场设置。增加一个文件夹 `AgentOpening` 存放开场设置相关的组件。项目使用了:
|
||||
我们这次要新增一个类别的设置。在 `src/features/AgentSetting` 中定义了 agent 的各种设置 UI 组件,增加一个文件夹 `AgentOpening` 存放开场设置相关的组件。
|
||||
|
||||
- [ant-design](https://ant.design/) 和 [lobe-ui:](https://github.com/lobehub/lobe-ui)组件库
|
||||
- [antd-style](https://ant-design.github.io/antd-style) : css-in-js 方案
|
||||
- [@lobehub/ui](https://ui.lobehub.com/):UI 组件库(包含 Flexbox 和 Center 用于响应式布局)
|
||||
- [@ant-design/icons](https://ant.design/components/icon-cn) 和 [lucide](https://lucide.dev/icons/): 图标库
|
||||
- [react-i18next](https://github.com/i18next/react-i18next) 和 [lobe-i18n](https://github.com/lobehub/lobe-cli-toolbox/tree/master/packages/lobe-i18n) :i18n 框架和多语言自动翻译工具
|
||||
|
||||
lobehub 是个国际化项目,新加的文案需要更新默认的 `locale` 文件: `src/locales/default/setting.ts` 。
|
||||
|
||||
我们以子组件 `OpeningQuestion.tsx` 为例,组件实现:
|
||||
以子组件 `OpeningQuestions.tsx` 为例,展示关键逻辑(省略样式代码):
|
||||
|
||||
```typescript
|
||||
// src/features/AgentSetting/AgentOpening/OpeningQuestions.tsx
|
||||
'use client';
|
||||
|
||||
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import { Flexbox, SortableList } from '@lobehub/ui';
|
||||
import { Button, Empty, Input } from 'antd';
|
||||
import { createStaticStyles } from 'antd-style';
|
||||
import { memo, useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useStore } from '../store';
|
||||
import { selectors } from '../store/selectors';
|
||||
|
||||
const styles = createStaticStyles(({ css, cssVar }) => ({
|
||||
empty: css`
|
||||
margin-block: 24px;
|
||||
margin-inline: 0;
|
||||
`,
|
||||
questionItemContainer: css`
|
||||
margin-block-end: 8px;
|
||||
padding-block: 2px;
|
||||
padding-inline: 10px 0;
|
||||
background: ${cssVar.colorBgContainer};
|
||||
`,
|
||||
questionItemContent: css`
|
||||
flex: 1;
|
||||
`,
|
||||
questionsList: css`
|
||||
width: 100%;
|
||||
margin-block-start: 16px;
|
||||
`,
|
||||
repeatError: css`
|
||||
margin: 0;
|
||||
color: ${cssVar.colorErrorText};
|
||||
`,
|
||||
}));
|
||||
|
||||
interface QuestionItem {
|
||||
content: string;
|
||||
id: number;
|
||||
}
|
||||
|
||||
const OpeningQuestions = memo(() => {
|
||||
const { t } = useTranslation('setting');
|
||||
const [questionInput, setQuestionInput] = useState('');
|
||||
@@ -317,7 +273,6 @@ const OpeningQuestions = memo(() => {
|
||||
|
||||
const addQuestion = useCallback(() => {
|
||||
if (!questionInput.trim()) return;
|
||||
|
||||
setQuestions([...openingQuestions, questionInput.trim()]);
|
||||
setQuestionInput('');
|
||||
}, [openingQuestions, questionInput, setQuestions]);
|
||||
@@ -332,106 +287,27 @@ const OpeningQuestions = memo(() => {
|
||||
[openingQuestions, setQuestions],
|
||||
);
|
||||
|
||||
// 处理拖拽排序后的逻辑
|
||||
const handleSortEnd = useCallback(
|
||||
(items: QuestionItem[]) => {
|
||||
setQuestions(items.map((item) => item.content));
|
||||
},
|
||||
[setQuestions],
|
||||
);
|
||||
|
||||
const items: QuestionItem[] = useMemo(() => {
|
||||
return openingQuestions.map((item, index) => ({
|
||||
content: item,
|
||||
id: index,
|
||||
}));
|
||||
}, [openingQuestions]);
|
||||
|
||||
const isRepeat = openingQuestions.includes(questionInput.trim());
|
||||
|
||||
return (
|
||||
<Flexbox gap={8}>
|
||||
<Flexbox gap={4}>
|
||||
<Input
|
||||
addonAfter={
|
||||
<Button
|
||||
// don't allow repeat
|
||||
disabled={openingQuestions.includes(questionInput.trim())}
|
||||
icon={<PlusOutlined />}
|
||||
onClick={addQuestion}
|
||||
size="small"
|
||||
type="text"
|
||||
/>
|
||||
}
|
||||
onChange={(e) => setQuestionInput(e.target.value)}
|
||||
onPressEnter={addQuestion}
|
||||
placeholder={t('settingOpening.openingQuestions.placeholder')}
|
||||
value={questionInput}
|
||||
/>
|
||||
|
||||
{isRepeat && (
|
||||
<p className={styles.repeatError}>{t('settingOpening.openingQuestions.repeat')}</p>
|
||||
)}
|
||||
|
||||
</Flexbox>
|
||||
|
||||
<div className={styles.questionsList}>
|
||||
{openingQuestions.length > 0 ? (
|
||||
<SortableList
|
||||
items={items}
|
||||
onChange={handleSortEnd}
|
||||
renderItem={(item) => (
|
||||
<SortableList.Item className={styles.questionItemContainer} id={item.id}>
|
||||
<SortableList.DragHandle />
|
||||
<div className={styles.questionItemContent}>{item.content}</div>
|
||||
<Button
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => removeQuestion(item.content)}
|
||||
type="text"
|
||||
/>
|
||||
|
||||
</SortableList.Item>
|
||||
)}
|
||||
/>
|
||||
) : (
|
||||
<Empty
|
||||
className={styles.empty}
|
||||
description={t('settingOpening.openingQuestions.empty')}
|
||||
/>
|
||||
)}
|
||||
|
||||
</div>
|
||||
|
||||
</Flexbox>
|
||||
);
|
||||
// 渲染 Input + SortableList,具体 UI 参考组件库文档
|
||||
// ...
|
||||
});
|
||||
|
||||
export default OpeningQuestions;
|
||||
```
|
||||
|
||||
关键点:
|
||||
- 通过 `selectors` 读取 store 中的配置
|
||||
- 通过 `setAgentConfig` action 更新配置
|
||||
- 使用 `useTranslation('setting')` 获取 i18n 文案
|
||||
|
||||
同时我们需要将用户设置的开场配置展示出来,这个是在 chat 页面,对应组件在 `src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/WelcomeMessage.tsx`:
|
||||
|
||||
```typescript
|
||||
const WelcomeMessage = () => {
|
||||
const { t } = useTranslation('chat');
|
||||
|
||||
// 获取当前开场配置
|
||||
// 从 store/agent 获取当前开场配置
|
||||
const openingMessage = useAgentStore(agentSelectors.openingMessage);
|
||||
const openingQuestions = useAgentStore(agentSelectors.openingQuestions);
|
||||
|
||||
const meta = useSessionStore(sessionMetaSelectors.currentAgentMeta, isEqual);
|
||||
const { isAgentEditable } = useServerConfigStore(featureFlagsSelectors);
|
||||
const activeId = useChatStore((s) => s.activeId);
|
||||
|
||||
const agentSystemRoleMsg = t('agentDefaultMessageWithSystemRole', {
|
||||
name: meta.title || t('defaultAgent'),
|
||||
systemRole: meta.description,
|
||||
});
|
||||
|
||||
const agentMsg = t(isAgentEditable ? 'agentDefaultMessage' : 'agentDefaultMessageWithoutEdit', {
|
||||
name: meta.title || t('defaultAgent'),
|
||||
url: `/chat/settings?session=${activeId}`,
|
||||
});
|
||||
|
||||
const message = useMemo(() => {
|
||||
// 用户设置了就用用户设置的
|
||||
@@ -439,38 +315,50 @@ const WelcomeMessage = () => {
|
||||
return !!meta.description ? agentSystemRoleMsg : agentMsg;
|
||||
}, [openingMessage, agentSystemRoleMsg, agentMsg, meta.description]);
|
||||
|
||||
const chatItem = (
|
||||
<ChatItem
|
||||
avatar={meta}
|
||||
editing={false}
|
||||
message={message}
|
||||
placement={'left'}
|
||||
type={type === 'chat' ? 'block' : 'pure'}
|
||||
/>
|
||||
);
|
||||
|
||||
return openingQuestions.length > 0 ? (
|
||||
<Flexbox>
|
||||
{chatItem}
|
||||
<ChatItem avatar={meta} message={message} placement="left" />
|
||||
{/* 渲染引导性问题 */}
|
||||
<OpeningQuestions mobile={mobile} questions={openingQuestions} />
|
||||
|
||||
</Flexbox>
|
||||
<OpeningQuestions questions={openingQuestions} />
|
||||
</Flexbox>
|
||||
) : (
|
||||
chatItem
|
||||
<ChatItem avatar={meta} message={message} placement="left" />
|
||||
);
|
||||
};
|
||||
export default WelcomeMessage;
|
||||
```
|
||||
|
||||
## 五、测试
|
||||
|
||||
项目使用 vitest 进行单元测试。
|
||||
项目使用 Vitest 进行单元测试,相关指南详见 [测试技能文档](https://github.com/lobehub/lobe-chat/blob/main/.agents/skills/testing/SKILL.md)。
|
||||
|
||||
由于我们目前两个新的配置字段都是可选的,所以理论上你不更新测试也能跑通,不过由于我们把前面提到的默认配置 `DEFAULT_AGENT_CONFIG` 增加了 `openingQuestions` 字段,这导致很多测试计算出的配置都是有这个字段的,因此我们还是需要更新一部分测试的快照。
|
||||
**运行测试:**
|
||||
|
||||
对于当前这个场景,我建议是本地直接跑下测试,看哪些测试失败了,针对需要更新,例如测试文件 `src/store/agent/slices/chat/selectors/agent.test.ts` 需要执行一下 `bunx vitest -u src/store/agent/slices/chat/selectors/agent.test.ts` 更新快照。
|
||||
```bash
|
||||
# 运行指定测试文件(不要运行 bun run test,全量测试耗时很长)
|
||||
bunx vitest run --silent='passed-only' '[file-path]'
|
||||
|
||||
# database 包的测试
|
||||
cd packages/database && bunx vitest run --silent='passed-only' '[file]'
|
||||
```
|
||||
|
||||
**添加新功能的测试建议:**
|
||||
|
||||
由于我们目前两个新的配置字段都是可选的,理论上不更新测试也能跑通。但如果修改了默认配置(如 `DEFAULT_AGENT_CONFIG` 增加了 `openingQuestions` 字段),可能导致一些测试快照不匹配,需要更新。
|
||||
|
||||
建议先本地跑下相关测试,看哪些失败了再针对性更新。例如:
|
||||
|
||||
```bash
|
||||
bunx vitest run --silent='passed-only' 'src/store/agent/slices/chat/selectors/agent.test.ts'
|
||||
```
|
||||
|
||||
如果只是想确认现有测试是否通过而不想本地跑,也可以直接查看 GitHub Actions 的测试结果。
|
||||
|
||||
**更多测试场景指南:**
|
||||
|
||||
- DB Model 测试:`.agents/skills/testing/references/db-model-test.md`
|
||||
- Zustand Store Action 测试:`.agents/skills/testing/references/zustand-store-action-test.md`
|
||||
- Electron IPC 测试:`.agents/skills/testing/references/electron-ipc-test.md`
|
||||
|
||||
## 总结
|
||||
|
||||
以上就是 LobeHub 开场设置功能的完整实现流程。开发者可以参考本文档进行相关功能的开发和测试。
|
||||
以上就是 LobeHub 开场设置功能的完整实现流程,涵盖了从数据库 schema → 数据模型 → Service/Model → Store → i18n → UI → 测试的全链路。开发者可以参考本文档进行相关功能的开发。
|
||||
|
||||
@@ -13,103 +13,212 @@ tags:
|
||||
|
||||
# Directory Structure
|
||||
|
||||
The directory structure of LobeHub is as follows:
|
||||
LobeHub uses a Monorepo architecture (`@lobechat/` namespace).
|
||||
The top-level directory structure is as follows:
|
||||
|
||||
```bash
|
||||
src
|
||||
├── app # Next.js App Router implementation with route groups and API routes
|
||||
├── business # Business logic modules (client and server)
|
||||
├── components # Reusable UI components
|
||||
├── config # Application configuration files, including client-side and server-side environment variables
|
||||
├── const # Application constants and enums
|
||||
├── envs # Environment variable definitions and validation (analytics, auth, llm, etc.)
|
||||
├── features # Function modules related to business functions, such as agent settings, plugin development pop-ups, etc.
|
||||
├── helpers # Utility helpers for tool engineering, placeholder parsing, etc.
|
||||
├── hooks # Custom utility hooks reused throughout the application
|
||||
├── layout # Application layout components, such as navigation bars, sidebars, etc.
|
||||
├── libs # Third-party integrations (analytics, OIDC, etc.)
|
||||
├── locales # Internationalization language files
|
||||
├── server # Server-side modules and services
|
||||
├── services # Encapsulated backend service interfaces, such as HTTP requests
|
||||
├── store # Zustand store for state management
|
||||
├── styles # Global styles and CSS-in-JS configurations
|
||||
├── tools # Built-in tools (artifacts, inspectors, interventions, etc.)
|
||||
├── types # TypeScript type definition files
|
||||
├── utils # Common utility functions
|
||||
├── auth.ts # Authentication configuration (Better Auth)
|
||||
├── instrumentation.ts # Application monitoring and telemetry setup
|
||||
├── instrumentation.node.ts # Node.js-specific instrumentation
|
||||
└── proxy.ts # Next.js middleware proxy configuration
|
||||
lobe-chat/
|
||||
├── apps/
|
||||
│ └── desktop/ # Electron desktop app
|
||||
├── packages/ # Shared packages (@lobechat/*)
|
||||
│ ├── agent-runtime/ # Agent runtime
|
||||
│ ├── database/ # Database schemas, models, repositories
|
||||
│ ├── model-runtime/ # Model runtime (AI provider adapters)
|
||||
│ ├── builtin-tool-*/ # Built-in tool packages
|
||||
│ ├── business/ # Cloud business slot packages
|
||||
│ ├── context-engine/ # Context engine
|
||||
│ ├── conversation-flow/ # Conversation flow
|
||||
│ ├── editor-runtime/ # Editor runtime
|
||||
│ ├── file-loaders/ # File loaders
|
||||
│ ├── prompts/ # Prompt templates
|
||||
│ └── ... # More shared packages
|
||||
├── src/ # Main app source code (see below)
|
||||
├── locales/ # i18n translation files (zh-CN, en-US, etc.)
|
||||
├── e2e/ # E2E tests (Cucumber + Playwright)
|
||||
└── docs/ # Documentation
|
||||
```
|
||||
|
||||
## app
|
||||
|
||||
The `app` directory follows Next.js 13+ App Router conventions with a sophisticated architecture using [Route Groups](https://nextjs.org/docs/app/building-your-application/routing/route-groups) to organize backend services, platform variants, and application routes:
|
||||
## src Directory
|
||||
|
||||
```bash
|
||||
app
|
||||
├── (backend)/ # Backend API routes and services
|
||||
│ ├── api/ # REST API endpoints
|
||||
│ │ ├── auth/ # Authentication routes
|
||||
│ │ └── webhooks/ # Webhook handlers
|
||||
│ ├── middleware/ # Request middleware
|
||||
│ ├── oidc/ # OpenID Connect routes
|
||||
│ ├── trpc/ # tRPC API endpoints
|
||||
│ │ ├── async/ # Async tRPC routes
|
||||
│ │ ├── desktop/ # Desktop-specific tRPC routes
|
||||
│ │ ├── edge/ # Edge runtime tRPC routes
|
||||
│ │ ├── lambda/ # Lambda tRPC routes
|
||||
│ │ └── tools/ # Tools tRPC routes
|
||||
│ └── webapi/ # Web API endpoints
|
||||
│ ├── chat/ # Chat-related APIs
|
||||
│ ├── models/ # Model management APIs
|
||||
│ ├── tts/ # Text-to-speech APIs
|
||||
│ └── ...
|
||||
├── [variants]/ # Platform and device variants
|
||||
│ ├── (auth)/ # Authentication pages
|
||||
│ │ ├── login/
|
||||
│ │ └── signup/
|
||||
│ ├── (main)/ # Main application routes
|
||||
│ │ ├── (mobile)/ # Mobile-specific routes
|
||||
│ │ │ └── me/ # Mobile profile pages
|
||||
│ │ ├── _layout/ # Layout components
|
||||
│ │ ├── chat/ # Chat interface
|
||||
│ │ ├── discover/ # Discovery pages
|
||||
│ │ ├── files/ # File management
|
||||
│ │ ├── image/ # Image generation
|
||||
│ │ ├── profile/ # User profile
|
||||
│ │ ├── repos/ # Repository management
|
||||
│ │ └── settings/ # Application settings
|
||||
│ └── @modal/ # Parallel modal routes
|
||||
│ ├── (.)changelog/
|
||||
│ └── _layout/
|
||||
├── desktop/ # Desktop-specific routes
|
||||
│ └── devtools/
|
||||
├── manifest.ts # PWA manifest
|
||||
├── robots.tsx # Robots.txt generation
|
||||
├── sitemap.tsx # Sitemap generation
|
||||
└── sw.ts # Service worker
|
||||
src/
|
||||
├── app/ # Next.js App Router (route groups and API routes)
|
||||
├── business/ # Cloud-only business logic (client/server)
|
||||
├── components/ # Reusable UI components
|
||||
├── config/ # App configuration (client and server env vars)
|
||||
├── const/ # Application constants and enums
|
||||
├── envs/ # Environment variable definitions and validation
|
||||
├── features/ # Business feature modules (Agent settings, plugin dev, etc.)
|
||||
├── helpers/ # Utility helper functions
|
||||
├── hooks/ # Reusable custom Hooks
|
||||
├── layout/ # Global layout components (AuthProvider, GlobalProvider)
|
||||
├── libs/ # Third-party integrations (better-auth, OIDC, tRPC, MCP, etc.)
|
||||
├── locales/ # i18n default language files (English)
|
||||
├── server/ # Server-side modules
|
||||
│ ├── featureFlags/ # Feature flags
|
||||
│ ├── globalConfig/ # Global configuration
|
||||
│ ├── modules/ # Server modules (no DB access)
|
||||
│ ├── routers/ # tRPC routers (async, lambda, mobile, tools)
|
||||
│ └── services/ # Server services (with DB access)
|
||||
├── services/ # Client-side service interfaces
|
||||
├── store/ # Zustand state management
|
||||
├── styles/ # Global styles and CSS-in-JS configurations
|
||||
├── tools/ # Built-in tools (artifacts, inspectors, etc.)
|
||||
├── types/ # TypeScript type definitions
|
||||
├── utils/ # General utility functions
|
||||
├── auth.ts # Authentication configuration (Better Auth)
|
||||
├── instrumentation.ts # App monitoring and telemetry setup
|
||||
└── proxy.ts # Next.js middleware proxy configuration
|
||||
```
|
||||
|
||||
## app Directory
|
||||
|
||||
The `app` directory follows Next.js App Router conventions,
|
||||
using [Route Groups](https://nextjs.org/docs/app/building-your-application/routing/route-groups)
|
||||
to organize backend services, platform variants,
|
||||
and application routes:
|
||||
|
||||
```bash
|
||||
app/
|
||||
├── (backend)/ # Backend API routes and services
|
||||
│ ├── api/ # REST API endpoints (auth, webhooks)
|
||||
│ ├── f/ # File service
|
||||
│ ├── market/ # Market service
|
||||
│ ├── middleware/ # Request middleware
|
||||
│ ├── oidc/ # OpenID Connect routes
|
||||
│ ├── trpc/ # tRPC API endpoints
|
||||
│ │ ├── async/ # Async tRPC routes
|
||||
│ │ ├── desktop/ # Desktop tRPC routes
|
||||
│ │ ├── lambda/ # Lambda tRPC routes
|
||||
│ │ └── tools/ # Tools tRPC routes
|
||||
│ └── webapi/ # Web API endpoints (chat, models, tts, etc.)
|
||||
├── [variants]/ # Platform and device variants
|
||||
│ ├── (auth)/ # Auth pages (login, signup)
|
||||
│ ├── (desktop)/ # Desktop-specific routes
|
||||
│ ├── (main)/ # Main application routes (SPA)
|
||||
│ │ ├── _layout/ # Layout components
|
||||
│ │ ├── agent/ # Agent pages
|
||||
│ │ ├── home/ # Home page
|
||||
│ │ ├── image/ # Image generation
|
||||
│ │ ├── memory/ # Memory management
|
||||
│ │ ├── resource/ # Resource management
|
||||
│ │ └── settings/ # Application settings
|
||||
│ ├── (mobile)/ # Mobile-specific routes
|
||||
│ ├── onboarding/ # Onboarding flow
|
||||
│ ├── router/ # SPA router configuration
|
||||
│ └── share/ # Share pages
|
||||
├── manifest.ts # PWA manifest
|
||||
├── robots.tsx # Robots.txt generation
|
||||
├── sitemap.tsx # Sitemap generation
|
||||
└── sw.ts # Service Worker
|
||||
```
|
||||
|
||||
### Architecture Explanation
|
||||
|
||||
**Route Groups:**
|
||||
|
||||
- `(backend)` - Contains all server-side API routes, middleware, and backend services
|
||||
- `[variants]` - Dynamic route group handling different platform variants and main application pages
|
||||
- `@modal` - Parallel routes for modal dialogs using Next.js parallel routing
|
||||
- `(backend)` — All server-side API routes, middleware, and backend services
|
||||
- `[variants]` — Dynamic route group for platform variants
|
||||
- `(main)` — Main SPA using React Router DOM
|
||||
|
||||
**Platform Organization:**
|
||||
|
||||
- The architecture supports multiple platforms (web, desktop, mobile) through route organization
|
||||
- Desktop-specific routes are in the `desktop/` directory
|
||||
- Mobile-specific routes are organized under `(main)/(mobile)/`
|
||||
- Shared layouts and components are in `_layout/` directories
|
||||
- Supports multiple platforms (web, desktop, mobile) through route organization
|
||||
- Desktop-specific routes under `(desktop)/`
|
||||
- Mobile-specific routes under `(mobile)/`
|
||||
- Shared layout components in `_layout/` directories
|
||||
|
||||
**API Architecture:**
|
||||
|
||||
- REST APIs in `(backend)/api/` and `(backend)/webapi/`
|
||||
- tRPC endpoints organized by runtime environment (edge, lambda, async, desktop)
|
||||
- Authentication and OIDC handling in dedicated route groups
|
||||
- REST APIs: `(backend)/api/` and `(backend)/webapi/`
|
||||
- tRPC endpoints (`src/server/routers/`): grouped by runtime
|
||||
- `lambda/` — Main business routers (agent, session, message,
|
||||
topic, file, knowledge, settings, etc.)
|
||||
- `async/` — Long-running async operations
|
||||
(file processing, image generation, RAG evaluation)
|
||||
- `tools/` — Tool invocations (search, MCP, market, klavis)
|
||||
- `mobile/` — Mobile-specific routers
|
||||
|
||||
This architecture provides clear separation of concerns while maintaining flexibility for different deployment targets and runtime environments.
|
||||
**Data Flow:**
|
||||
|
||||
Using a typical user action (e.g., updating Agent config) as an example, here's how data flows through each layer:
|
||||
|
||||
```plaintext
|
||||
React UI (src/features/, src/app/)
|
||||
│ User interaction triggers event
|
||||
▼
|
||||
Store Actions (src/store/)
|
||||
│ Zustand action updates local state, calls service
|
||||
▼
|
||||
Client Service (src/services/)
|
||||
│ Wraps tRPC client call, prepares request params
|
||||
▼
|
||||
tRPC Router (src/server/routers/lambda/)
|
||||
│ Validates input (zod), routes to service
|
||||
▼
|
||||
Server Service (src/server/services/)
|
||||
│ Executes business logic, calls DB model
|
||||
▼
|
||||
DB Model (packages/database/src/models/)
|
||||
│ Wraps Drizzle ORM queries
|
||||
▼
|
||||
PostgreSQL
|
||||
```
|
||||
|
||||
For data reads, the flow is reversed: UI consumes data via store selectors, and stores fetch from the backend via SWR + tRPC queries.
|
||||
|
||||
### Routing Architecture
|
||||
|
||||
The project uses hybrid routing:
|
||||
Next.js App Router handles static pages (e.g., auth pages),
|
||||
while React Router DOM powers the main SPA.
|
||||
|
||||
**Entry**: `src/app/[variants]/page.tsx`
|
||||
dispatches to desktop or mobile router based on device type.
|
||||
|
||||
**Key configuration files:**
|
||||
|
||||
- Desktop router: `src/app/[variants]/router/desktopRouter.config.tsx`
|
||||
- Mobile router: `src/app/[variants]/(mobile)/router/mobileRouter.config.tsx`
|
||||
- Router utilities: `src/utils/router.tsx`
|
||||
|
||||
**Desktop SPA Routes (React Router DOM):**
|
||||
|
||||
```bash
|
||||
/ # Home
|
||||
/agent/:aid # Agent conversation
|
||||
/agent/:aid/profile # Agent profile
|
||||
/agent/:aid/cron/:cronId # Cron task detail
|
||||
/group/:gid # Group conversation
|
||||
/group/:gid/profile # Group profile
|
||||
/community # Community discovery (agent, model, provider, mcp)
|
||||
/community/agent/:slug # Agent detail page
|
||||
/community/model/:slug # Model detail page
|
||||
/community/provider/:slug # Provider detail page
|
||||
/community/mcp/:slug # MCP detail page
|
||||
/resource # Resource management
|
||||
/resource/library/:id # Knowledge base detail
|
||||
/settings/:tab # Settings (profile, provider, etc.)
|
||||
/settings/provider/:id # Model provider configuration
|
||||
/memory # Memory management
|
||||
/image # Image generation
|
||||
/page/:id # Page detail
|
||||
/share/t/:id # Share topic
|
||||
/onboarding # Onboarding flow
|
||||
```
|
||||
|
||||
**Mobile SPA Routes (React Router DOM):**
|
||||
|
||||
```bash
|
||||
/ # Home
|
||||
/agent/:aid # Agent conversation
|
||||
/community # Community discovery
|
||||
/settings # Settings home
|
||||
/settings/:tab # Settings detail
|
||||
/settings/provider/:id # Model provider configuration
|
||||
/me # Personal center
|
||||
/me/profile # Profile
|
||||
/me/settings # Personal settings
|
||||
/share/t/:id # Share topic
|
||||
/onboarding # Onboarding flow
|
||||
```
|
||||
|
||||
@@ -11,103 +11,210 @@ tags:
|
||||
|
||||
# 目录架构
|
||||
|
||||
LobeHub 的文件夹目录架构如下:
|
||||
LobeHub 采用 Monorepo 架构(`@lobechat/` 命名空间),
|
||||
顶层目录结构如下:
|
||||
|
||||
```bash
|
||||
src
|
||||
├── app # Next.js App Router 实现,包含路由组和 API 路由
|
||||
├── business # 业务逻辑模块(客户端和服务端)
|
||||
├── components # 可复用的 UI 组件
|
||||
├── config # 应用的配置文件,包含客户端环境变量与服务端环境变量
|
||||
├── const # 应用常量和枚举
|
||||
├── envs # 环境变量定义和校验(分析、认证、LLM 等)
|
||||
├── features # 与业务功能相关的功能模块,如 Agent 设置、插件开发弹窗等
|
||||
├── helpers # 工具辅助函数,用于工具工程、占位符解析等
|
||||
├── hooks # 全应用复用自定义的工具 Hooks
|
||||
├── layout # 应用的布局组件,如导航栏、侧边栏等
|
||||
├── libs # 第三方集成(分析、OIDC 等)
|
||||
├── locales # 国际化的语言文件
|
||||
├── server # 服务端模块和服务
|
||||
├── services # 封装的后端服务接口,如 HTTP 请求
|
||||
├── store # 用于状态管理的 zustand store
|
||||
├── styles # 全局样式和 CSS-in-JS 配置
|
||||
├── tools # 内置工具(artifacts、inspectors、interventions 等)
|
||||
├── types # TypeScript 的类型定义文件
|
||||
├── utils # 通用的工具函数
|
||||
├── auth.ts # 认证配置(Better Auth)
|
||||
├── instrumentation.ts # 应用监控和遥测设置
|
||||
├── instrumentation.node.ts # Node.js 专用的 instrumentation
|
||||
└── proxy.ts # Next.js 中间件代理配置
|
||||
lobe-chat/
|
||||
├── apps/
|
||||
│ └── desktop/ # Electron 桌面应用
|
||||
├── packages/ # 共享包(@lobechat/*)
|
||||
│ ├── agent-runtime/ # Agent 运行时
|
||||
│ ├── database/ # 数据库 schemas、models、repositories
|
||||
│ ├── model-runtime/ # 模型运行时(各 AI 提供商适配)
|
||||
│ ├── builtin-tool-*/ # 内置工具包
|
||||
│ ├── business/ # Cloud 业务插槽 packages
|
||||
│ ├── context-engine/ # 上下文引擎
|
||||
│ ├── conversation-flow/ # 会话流程
|
||||
│ ├── editor-runtime/ # 编辑器运行时
|
||||
│ ├── file-loaders/ # 文件加载器
|
||||
│ ├── prompts/ # Prompt 模板
|
||||
│ └── ... # 更多共享包
|
||||
├── src/ # 主应用源码(见下方详细说明)
|
||||
├── locales/ # i18n 翻译文件(zh-CN、en-US 等)
|
||||
├── e2e/ # E2E 测试(Cucumber + Playwright)
|
||||
└── docs/ # 文档
|
||||
```
|
||||
|
||||
## app
|
||||
|
||||
`app` 目录遵循 Next.js 13+ App Router 约定,采用复杂的架构,使用 [路由组](https://nextjs.org/docs/app/building-your-application/routing/route-groups) 来组织后端服务、平台变体和应用路由:
|
||||
## src 目录
|
||||
|
||||
```bash
|
||||
app
|
||||
├── (backend)/ # 后端 API 路由和服务
|
||||
│ ├── api/ # REST API 端点
|
||||
│ │ ├── auth/ # 身份验证路由
|
||||
│ │ └── webhooks/ # Webhook 处理器
|
||||
│ ├── middleware/ # 请求中间件
|
||||
│ ├── oidc/ # OpenID Connect 路由
|
||||
│ ├── trpc/ # tRPC API 端点
|
||||
│ │ ├── async/ # 异步 tRPC 路由
|
||||
│ │ ├── desktop/ # 桌面端专用 tRPC 路由
|
||||
│ │ ├── edge/ # Edge 运行时 tRPC 路由
|
||||
│ │ ├── lambda/ # Lambda tRPC 路由
|
||||
│ │ └── tools/ # 工具 tRPC 路由
|
||||
│ └── webapi/ # Web API 端点
|
||||
│ ├── chat/ # 聊天相关 API
|
||||
│ ├── models/ # 模型管理 API
|
||||
│ ├── tts/ # 文本转语音 API
|
||||
│ └── ...
|
||||
├── [variants]/ # 平台和设备变体
|
||||
│ ├── (auth)/ # 身份验证页面
|
||||
│ │ ├── login/
|
||||
│ │ └── signup/
|
||||
│ ├── (main)/ # 主应用路由
|
||||
│ │ ├── (mobile)/ # 移动端专用路由
|
||||
│ │ │ └── me/ # 移动端个人资料页面
|
||||
│ │ ├── _layout/ # 布局组件
|
||||
│ │ ├── chat/ # 聊天界面
|
||||
│ │ ├── discover/ # 发现页面
|
||||
│ │ ├── files/ # 文件管理
|
||||
│ │ ├── image/ # 图像生成
|
||||
│ │ ├── profile/ # 用户资料
|
||||
│ │ ├── repos/ # 仓库管理
|
||||
│ │ └── settings/ # 应用设置
|
||||
│ └── @modal/ # 并行模态框路由
|
||||
│ ├── (.)changelog/
|
||||
│ └── _layout/
|
||||
├── desktop/ # 桌面端专用路由
|
||||
│ └── devtools/
|
||||
├── manifest.ts # PWA 清单
|
||||
├── robots.tsx # Robots.txt 生成
|
||||
├── sitemap.tsx # 站点地图生成
|
||||
└── sw.ts # Service Worker
|
||||
src/
|
||||
├── app/ # Next.js App Router(路由组和 API 路由)
|
||||
├── business/ # Cloud 版专用业务逻辑(客户端/服务端)
|
||||
├── components/ # 可复用的 UI 组件
|
||||
├── config/ # 应用配置(客户端与服务端环境变量)
|
||||
├── const/ # 应用常量和枚举
|
||||
├── envs/ # 环境变量定义和校验
|
||||
├── features/ # 业务功能模块(Agent 设置、插件开发弹窗等)
|
||||
├── helpers/ # 工具辅助函数
|
||||
├── hooks/ # 全应用复用的自定义 Hooks
|
||||
├── layout/ # 全局布局组件(AuthProvider、GlobalProvider)
|
||||
├── libs/ # 第三方集成(better-auth、OIDC、tRPC、MCP 等)
|
||||
├── locales/ # 国际化默认语言文件(英文)
|
||||
├── server/ # 服务端模块
|
||||
│ ├── featureFlags/ # Feature Flags
|
||||
│ ├── globalConfig/ # 全局配置
|
||||
│ ├── modules/ # 服务端模块(不访问数据库)
|
||||
│ ├── routers/ # tRPC 路由(async、lambda、mobile、tools)
|
||||
│ └── services/ # 服务端服务(可访问数据库)
|
||||
├── services/ # 客户端服务接口
|
||||
├── store/ # zustand 状态管理
|
||||
├── styles/ # 全局样式和 CSS-in-JS 配置
|
||||
├── tools/ # 内置工具(artifacts、inspectors 等)
|
||||
├── types/ # TypeScript 类型定义
|
||||
├── utils/ # 通用工具函数
|
||||
├── auth.ts # 认证配置(Better Auth)
|
||||
├── instrumentation.ts # 应用监控和遥测设置
|
||||
└── proxy.ts # Next.js 中间件代理配置
|
||||
```
|
||||
|
||||
## app 目录
|
||||
|
||||
`app` 目录遵循 Next.js App Router 约定,
|
||||
使用[路由组](https://nextjs.org/docs/app/building-your-application/routing/route-groups)
|
||||
来组织后端服务、平台变体和应用路由:
|
||||
|
||||
```bash
|
||||
app/
|
||||
├── (backend)/ # 后端 API 路由和服务
|
||||
│ ├── api/ # REST API 端点(auth、webhooks)
|
||||
│ ├── f/ # 文件服务
|
||||
│ ├── market/ # 市场服务
|
||||
│ ├── middleware/ # 请求中间件
|
||||
│ ├── oidc/ # OpenID Connect 路由
|
||||
│ ├── trpc/ # tRPC API 端点
|
||||
│ │ ├── async/ # 异步 tRPC 路由
|
||||
│ │ ├── desktop/ # 桌面端 tRPC 路由
|
||||
│ │ ├── lambda/ # Lambda tRPC 路由
|
||||
│ │ └── tools/ # 工具 tRPC 路由
|
||||
│ └── webapi/ # Web API 端点(chat、models、tts 等)
|
||||
├── [variants]/ # 平台和设备变体
|
||||
│ ├── (auth)/ # 身份验证页面(login、signup)
|
||||
│ ├── (desktop)/ # 桌面端专用路由
|
||||
│ ├── (main)/ # 主应用路由(SPA)
|
||||
│ │ ├── _layout/ # 布局组件
|
||||
│ │ ├── agent/ # Agent 页面
|
||||
│ │ ├── home/ # 首页
|
||||
│ │ ├── image/ # 图像生成
|
||||
│ │ ├── memory/ # 记忆管理
|
||||
│ │ ├── resource/ # 资源管理
|
||||
│ │ └── settings/ # 应用设置
|
||||
│ ├── (mobile)/ # 移动端专用路由
|
||||
│ ├── onboarding/ # 新用户引导
|
||||
│ ├── router/ # SPA 路由配置
|
||||
│ └── share/ # 分享页面
|
||||
├── manifest.ts # PWA 清单
|
||||
├── robots.tsx # Robots.txt 生成
|
||||
├── sitemap.tsx # 站点地图生成
|
||||
└── sw.ts # Service Worker
|
||||
```
|
||||
|
||||
### 架构说明
|
||||
|
||||
**路由组:**
|
||||
|
||||
- `(backend)` - 包含所有服务端 API 路由、中间件和后端服务
|
||||
- `[variants]` - 处理不同平台变体和主应用页面的动态路由组
|
||||
- `@modal` - 使用 Next.js 并行路由的模态框对话框并行路由
|
||||
- `(backend)` — 所有服务端 API 路由、中间件和后端服务
|
||||
- `[variants]` — 处理不同平台变体的动态路由组
|
||||
- `(main)` — 主应用 SPA,使用 React Router DOM
|
||||
|
||||
**平台组织:**
|
||||
|
||||
- 架构通过路由组织支持多个平台(Web、桌面端、移动端)
|
||||
- 桌面端专用路由位于 `desktop/` 目录中
|
||||
- 移动端专用路由组织在 `(main)/(mobile)/` 下
|
||||
- 共享布局和组件位于 `_layout/` 目录中
|
||||
- 通过路由组织支持多平台(Web、桌面端、移动端)
|
||||
- 桌面端专用路由在 `(desktop)/` 下
|
||||
- 移动端专用路由在 `(mobile)/` 下
|
||||
- 共享布局组件在 `_layout/` 目录中
|
||||
|
||||
**API 架构:**
|
||||
|
||||
- `(backend)/api/` 和 `(backend)/webapi/` 中的 REST API
|
||||
- 按运行时环境组织的 tRPC 端点(edge、lambda、async、desktop)
|
||||
- 专用路由组中的身份验证和 OIDC 处理
|
||||
- REST API:`(backend)/api/` 和 `(backend)/webapi/`
|
||||
- tRPC 端点(`src/server/routers/`):按运行时分组
|
||||
- `lambda/` — 主要业务路由(agent、session、message、
|
||||
topic、file、knowledge、settings 等)
|
||||
- `async/` — 耗时异步操作(文件处理、图像生成、RAG 评估)
|
||||
- `tools/` — 工具调用(search、MCP、market、klavis)
|
||||
- `mobile/` — 移动端专用路由
|
||||
|
||||
这种架构在保持不同部署目标和运行时环境灵活性的同时,提供了清晰的关注点分离。
|
||||
**数据流:**
|
||||
|
||||
以一个典型的用户操作(如更新 Agent 配置)为例,数据在各层之间的流转:
|
||||
|
||||
```plaintext
|
||||
React UI (src/features/, src/app/)
|
||||
│ 用户交互触发事件
|
||||
▼
|
||||
Store Actions (src/store/)
|
||||
│ zustand action 更新本地状态,调用 service
|
||||
▼
|
||||
Client Service (src/services/)
|
||||
│ 封装 tRPC 客户端调用,处理请求参数
|
||||
▼
|
||||
tRPC Router (src/server/routers/lambda/)
|
||||
│ 校验输入(zod),路由到对应 service
|
||||
▼
|
||||
Server Service (src/server/services/)
|
||||
│ 执行业务逻辑,调用 DB model
|
||||
▼
|
||||
DB Model (packages/database/src/models/)
|
||||
│ 封装 Drizzle ORM 查询
|
||||
▼
|
||||
PostgreSQL
|
||||
```
|
||||
|
||||
读取数据的流程方向相反:UI 通过 store selector 消费数据,store 通过 SWR + tRPC query 从后端拉取。
|
||||
|
||||
### 路由架构
|
||||
|
||||
项目采用混合路由:
|
||||
Next.js App Router 处理静态页面(如认证页),
|
||||
React Router DOM 承载主应用 SPA。
|
||||
|
||||
**入口**:`src/app/[variants]/page.tsx`
|
||||
根据设备类型分发到桌面端或移动端路由。
|
||||
|
||||
**关键配置文件:**
|
||||
|
||||
- 桌面端路由:`src/app/[variants]/router/desktopRouter.config.tsx`
|
||||
- 移动端路由:`src/app/[variants]/(mobile)/router/mobileRouter.config.tsx`
|
||||
- 路由工具:`src/utils/router.tsx`
|
||||
|
||||
**桌面端 SPA 路由(React Router DOM):**
|
||||
|
||||
```bash
|
||||
/ # 首页
|
||||
/agent/:aid # Agent 会话
|
||||
/agent/:aid/profile # Agent 详情
|
||||
/agent/:aid/cron/:cronId # 定时任务详情
|
||||
/group/:gid # 群组会话
|
||||
/group/:gid/profile # 群组详情
|
||||
/community # 社区发现(agent、model、provider、mcp)
|
||||
/community/agent/:slug # Agent 详情页
|
||||
/community/model/:slug # 模型详情页
|
||||
/community/provider/:slug # 提供商详情页
|
||||
/community/mcp/:slug # MCP 详情页
|
||||
/resource # 资源管理
|
||||
/resource/library/:id # 知识库详情
|
||||
/settings/:tab # 设置(profile、provider 等)
|
||||
/settings/provider/:id # 模型提供商配置
|
||||
/memory # 记忆管理
|
||||
/image # 图像生成
|
||||
/page/:id # 页面详情
|
||||
/share/t/:id # 分享话题
|
||||
/onboarding # 新用户引导
|
||||
```
|
||||
|
||||
**移动端 SPA 路由(React Router DOM):**
|
||||
|
||||
```bash
|
||||
/ # 首页
|
||||
/agent/:aid # Agent 会话
|
||||
/community # 社区发现
|
||||
/settings # 设置首页
|
||||
/settings/:tab # 设置详情
|
||||
/settings/provider/:id # 模型提供商配置
|
||||
/me # 个人中心
|
||||
/me/profile # 个人资料
|
||||
/me/settings # 个人设置
|
||||
/share/t/:id # 分享话题
|
||||
/onboarding # 新用户引导
|
||||
```
|
||||
|
||||
@@ -23,4 +23,23 @@ The design and development of LobeHub would not have been possible without the e
|
||||
6. **Next.js Documentation**: Our project is built on Next.js, and you can refer to the [Next.js Documentation](https://nextjs.org/docs) for more information about Next.js.
|
||||
7. **FlowGPT**: FlowGPT is currently the world's largest Prompt community, and some of the agents in LobeHub come from active authors in FlowGPT. You can visit [FlowGPT](https://flowgpt.com/) to learn more about it.
|
||||
|
||||
## LobeHub Official Ecosystem
|
||||
|
||||
- [🍭 LobeUI](https://github.com/lobehub/lobe-ui) (`@lobehub/ui`): LobeHub UI component library
|
||||
- [🥨 LobeIcons](https://github.com/lobehub/lobe-icons) (`@lobehub/icons`): AI / LLM brand SVG icon library
|
||||
- [📊 LobeCharts](https://github.com/lobehub/lobe-charts) (`@lobehub/charts`): Chart component library
|
||||
- [✒️ LobeEditor](https://github.com/lobehub/lobe-editor) (`@lobehub/editor`): Editor components
|
||||
- [🎤 LobeTTS](https://github.com/lobehub/lobe-tts) (`@lobehub/tts`): TTS / STT voice processing library
|
||||
- [📐 LobeLint](https://github.com/lobehub/lobe-lint) (`@lobehub/lint`): ESLint / Prettier / Commitlint config presets
|
||||
- [🌐 Lobe i18n](https://github.com/lobehub/lobe-cli-toolbox/tree/master/packages/lobe-i18n): AI-powered i18n auto-translation CLI tool
|
||||
- [🔌 MCP Mark](https://mcpmark.ai/): MCP tool discovery and evaluation platform
|
||||
|
||||
## LobeHub Community & Platforms
|
||||
|
||||
- [🤖 Agent Market](https://lobehub.com/agent): Discover and share AI Agents
|
||||
- [🔌 MCP Market](https://lobehub.com/mcp): Discover and share MCP tools
|
||||
- [🎬 YouTube](https://www.youtube.com/@lobehub): Official video tutorials and demos
|
||||
- [🐦 X (Twitter)](https://x.com/lobehub): Project updates and announcements
|
||||
- [💬 Discord](https://discord.com/invite/AYFPHvv2jT): Community discussion and support
|
||||
|
||||
We will continue to update and supplement this list to provide developers with more reference resources.
|
||||
|
||||
@@ -21,4 +21,23 @@ LobeHub 的设计和开发离不开社区和生态中的优秀项目。我们在
|
||||
6. **Next.js 文档**:我们的项目是基于 Next.js 构建的,你可以查看 [Next.js 文档](https://nextjs.org/docs) 来了解更多关于 Next.js 的信息。
|
||||
7. **FlowGPT**:FlowGPT 是目前全球最大的 Prompt 社区,LobeHub 中的一些 Agent 来自 FlowGPT 的活跃作者。你可以访问 [FlowGPT](https://flowgpt.com/) 来了解更多关于它的信息。
|
||||
|
||||
## LobeHub 官方生态
|
||||
|
||||
- [🍭 LobeUI](https://github.com/lobehub/lobe-ui) (`@lobehub/ui`):LobeHub UI 组件库
|
||||
- [🥨 LobeIcons](https://github.com/lobehub/lobe-icons) (`@lobehub/icons`):AI / LLM 品牌 SVG 图标库
|
||||
- [📊 LobeCharts](https://github.com/lobehub/lobe-charts) (`@lobehub/charts`):图表组件库
|
||||
- [✒️ LobeEditor](https://github.com/lobehub/lobe-editor) (`@lobehub/editor`):编辑器组件
|
||||
- [🎤 LobeTTS](https://github.com/lobehub/lobe-tts) (`@lobehub/tts`):TTS / STT 语音处理库
|
||||
- [📐 LobeLint](https://github.com/lobehub/lobe-lint) (`@lobehub/lint`):ESLint / Prettier / Commitlint 等配置预设
|
||||
- [🌐 Lobe i18n](https://github.com/lobehub/lobe-cli-toolbox/tree/master/packages/lobe-i18n):AI 驱动的 i18n 自动翻译 CLI 工具
|
||||
- [🔌 MCP Mark](https://mcpmark.ai/):MCP 工具发现与评测平台
|
||||
|
||||
## LobeHub 社区与平台
|
||||
|
||||
- [🤖 Agent 市场](https://lobehub.com/zh/agent):发现和分享 AI Agent
|
||||
- [🔌 MCP 市场](https://lobehub.com/zh/mcp):发现和分享 MCP 工具
|
||||
- [🎬 YouTube](https://www.youtube.com/@lobehub):官方视频教程与演示
|
||||
- [🐦 X (Twitter)](https://x.com/lobehub):项目动态与公告
|
||||
- [💬 Discord](https://discord.com/invite/AYFPHvv2jT):社区讨论与技术支持
|
||||
|
||||
我们会持续更新和补充这个列表,为开发者提供更多的参考资源。
|
||||
|
||||
@@ -10,7 +10,8 @@ tags:
|
||||
- PNPM
|
||||
- Bun
|
||||
- Git
|
||||
- VSCode
|
||||
- Docker
|
||||
- PostgreSQL
|
||||
---
|
||||
|
||||
# Environment Setup Guide
|
||||
@@ -35,6 +36,7 @@ First, you need to install the following software:
|
||||
- PNPM: We use PNPM as the preferred package manager. You can download and install it from the [PNPM official website](https://pnpm.io/installation).
|
||||
- Bun: We use Bun as the npm scripts runner. You can download and install it from the [Bun official website](https://bun.com/docs/installation).
|
||||
- Git: We use Git for version control. You can download and install it from the Git official website.
|
||||
- Docker: Required for running PostgreSQL, RustFS, and other services. You can download and install it from the [Docker official website](https://www.docker.com/get-started).
|
||||
- IDE: You can choose your preferred integrated development environment (IDE). We recommend using WebStorm/VSCode.
|
||||
|
||||
### VSCode Users
|
||||
@@ -45,20 +47,74 @@ We recommend installing the extensions listed in [.vscode/extensions.json](https
|
||||
|
||||
After installing the above software, you can start setting up the LobeHub project.
|
||||
|
||||
1. **Get the code**: First, you need to clone the LobeHub codebase from GitHub. Run the following command in the terminal:
|
||||
#### 1. Get the Code
|
||||
|
||||
First, you need to clone the LobeHub codebase from GitHub. Run the following command in the terminal:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/lobehub/lobehub.git
|
||||
cd lobehub
|
||||
```
|
||||
|
||||
2. **Install dependencies**: Then, navigate to the project directory and use PNPM to install the project's dependencies:
|
||||
#### 2. Install Dependencies
|
||||
|
||||
Use PNPM to install the project's dependencies:
|
||||
|
||||
```bash
|
||||
cd lobehub
|
||||
pnpm i
|
||||
```
|
||||
|
||||
3. **Start the development server**: After installing the dependencies, you can start the development server:
|
||||
#### 3. Configure Environment
|
||||
|
||||
Copy the example environment files:
|
||||
|
||||
```bash
|
||||
# Docker services configuration
|
||||
cp docker-compose/dev/.env.example docker-compose/dev/.env
|
||||
|
||||
# Next.js development server configuration
|
||||
cp .env.example.development .env
|
||||
```
|
||||
|
||||
Edit these files as needed:
|
||||
|
||||
- `docker-compose/dev/.env` - Docker services (PostgreSQL, Redis, RustFS, SearXNG)
|
||||
- `.env` - Next.js dev server (database connection, S3 storage, auth, etc.)
|
||||
|
||||
#### 4. Start Docker Services
|
||||
|
||||
Start all required services using Docker Compose:
|
||||
|
||||
```bash
|
||||
bun run dev:docker
|
||||
```
|
||||
|
||||
This will start the following services:
|
||||
|
||||
- PostgreSQL database (port 5432)
|
||||
- Redis cache (port 6379)
|
||||
- RustFS storage (API port 9000, Console port 9001)
|
||||
- SearXNG search (port 8180)
|
||||
|
||||
You can check all Docker services are running by running:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose/dev/docker-compose.yml ps
|
||||
```
|
||||
|
||||
#### 5. Run Database Migrations
|
||||
|
||||
Execute the database migration script to create all necessary tables:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
You should see: `✅ database migration pass.`
|
||||
|
||||
#### 6. Start Development Server
|
||||
|
||||
Launch the LobeHub development server:
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
@@ -68,17 +124,50 @@ Now, you can open `http://localhost:3010` in your browser, and you should see th
|
||||
|
||||

|
||||
|
||||
## Working with Server-Side Features
|
||||
## Service URLs
|
||||
|
||||
The basic setup above uses LobeHub's client-side database mode. If you need to work with server-side features such as:
|
||||
When running with Docker Compose development setup:
|
||||
|
||||
- Database persistence
|
||||
- File uploads and storage
|
||||
- Image generation
|
||||
- Multi-user authentication
|
||||
- Advanced server-side integrations
|
||||
- **PostgreSQL**: `postgres://postgres@localhost:5432/lobechat`
|
||||
- **Redis**: `redis://localhost:6379`
|
||||
- **RustFS API**: `http://localhost:9000`
|
||||
- **RustFS Console**: `http://localhost:9001`
|
||||
- **SearXNG**: `http://localhost:8180`
|
||||
- **Application**: `http://localhost:3010`
|
||||
|
||||
Please refer to the [Work with Server-Side Database](/docs/development/basic/work-with-server-side-database) guide for complete setup instructions.
|
||||
## Troubleshooting
|
||||
|
||||
### Reset Services
|
||||
|
||||
If you encounter issues, you can reset the entire stack:
|
||||
|
||||
```bash
|
||||
# Completely reset Docker environment (delete all data and restart)
|
||||
bun run dev:docker:reset
|
||||
```
|
||||
|
||||
### Port Conflicts
|
||||
|
||||
If ports are already in use:
|
||||
|
||||
```bash
|
||||
# Check what's using the ports
|
||||
lsof -i :5432 # PostgreSQL
|
||||
lsof -i :6379 # Redis
|
||||
lsof -i :9000 # RustFS API
|
||||
lsof -i :9001 # RustFS Console
|
||||
lsof -i :8180 # SearXNG
|
||||
```
|
||||
|
||||
### Database Migrations
|
||||
|
||||
Run migrations manually when needed:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
During the development process, if you encounter any issues with environment setup or have any questions about LobeHub development, feel free to ask us at any time. We look forward to seeing your contributions!
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ tags:
|
||||
- Node.js
|
||||
- PNPM
|
||||
- Git
|
||||
- Docker
|
||||
- PostgreSQL
|
||||
---
|
||||
|
||||
# 环境设置指南
|
||||
@@ -29,8 +31,9 @@ tags:
|
||||
|
||||
- Node.js:LobeHub 是基于 Node.js 构建的,因此你需要安装 Node.js。我们建议安装最新的稳定版。
|
||||
- PNPM:我们使用 PNPM 作为管理器。你可以从 [pnpm 的官方网站](https://pnpm.io/installation) 上下载并安装。
|
||||
- Bun:我们使用 Bun 作为 npm scripts runner, 你可以从 [Bun 的官方网站](https://bun.com/docs/installation) 上下载并安装。
|
||||
- Bun:我们使用 Bun 作为 npm scripts runner,你可以从 [Bun 的官方网站](https://bun.com/docs/installation) 上下载并安装。
|
||||
- Git:我们使用 Git 进行版本控制。你可以从 Git 的官方网站上下载并安装。
|
||||
- Docker:用于运行 PostgreSQL、RustFS 等服务。你可以从 [Docker 官方网站](https://www.docker.com/get-started) 下载并安装。
|
||||
- IDE:你可以选择你喜欢的集成开发环境(IDE),我们推荐使用 WebStorm/VSCode。
|
||||
|
||||
### VSCode 用户
|
||||
@@ -41,20 +44,74 @@ tags:
|
||||
|
||||
完成上述软件的安装后,你可以开始设置 LobeHub 项目了。
|
||||
|
||||
1. **获取代码**:首先,你需要从 GitHub 上克隆 LobeHub 的代码库。在终端中运行以下命令:
|
||||
#### 1. 获取代码
|
||||
|
||||
首先,你需要从 GitHub 上克隆 LobeHub 的代码库。在终端中运行以下命令:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/lobehub/lobehub.git
|
||||
cd lobehub
|
||||
```
|
||||
|
||||
2. **安装依赖**:然后,进入项目目录,并使用 `pnpm` 安装项目的依赖包:
|
||||
#### 2. 安装依赖
|
||||
|
||||
使用 PNPM 安装项目的依赖包:
|
||||
|
||||
```bash
|
||||
cd lobehub
|
||||
pnpm i
|
||||
```
|
||||
|
||||
3. **启动开发服务器**:安装完依赖后,你可以启动开发服务器:
|
||||
#### 3. 配置环境
|
||||
|
||||
复制示例环境文件:
|
||||
|
||||
```bash
|
||||
# Docker 服务配置
|
||||
cp docker-compose/dev/.env.example docker-compose/dev/.env
|
||||
|
||||
# Next.js 开发服务器配置
|
||||
cp .env.example.development .env
|
||||
```
|
||||
|
||||
根据需要编辑这些文件:
|
||||
|
||||
- `docker-compose/dev/.env` - Docker 服务配置(PostgreSQL、Redis、RustFS、SearXNG)
|
||||
- `.env` - Next.js 开发服务器配置(数据库连接、S3 存储、认证等)
|
||||
|
||||
#### 4. 启动 Docker 服务
|
||||
|
||||
使用 Docker Compose 启动所有必需的服务:
|
||||
|
||||
```bash
|
||||
bun run dev:docker
|
||||
```
|
||||
|
||||
这将启动以下服务:
|
||||
|
||||
- PostgreSQL 数据库(端口 5432)
|
||||
- Redis 缓存(端口 6379)
|
||||
- RustFS 存储(API 端口 9000,控制台端口 9001)
|
||||
- SearXNG 搜索(端口 8180)
|
||||
|
||||
可以通过运行以下命令检查所有 Docker 服务运行状态:
|
||||
|
||||
```bash
|
||||
docker compose -f docker-compose/dev/docker-compose.yml ps
|
||||
```
|
||||
|
||||
#### 5. 运行数据库迁移
|
||||
|
||||
执行数据库迁移脚本以创建所有必要的表:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
预期输出:`✅ database migration pass.`
|
||||
|
||||
#### 6. 启动开发服务器
|
||||
|
||||
启动 LobeHub 开发服务器:
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
@@ -64,17 +121,50 @@ bun run dev
|
||||
|
||||

|
||||
|
||||
## 使用服务端功能
|
||||
## 服务地址
|
||||
|
||||
上述基础设置使用 LobeHub 的客户端数据库模式。如果你需要开发服务端功能,如:
|
||||
运行 Docker Compose 开发环境时:
|
||||
|
||||
- 数据库持久化
|
||||
- 文件上传和存储
|
||||
- 图像生成
|
||||
- 多用户身份验证
|
||||
- 高级服务端集成
|
||||
- **PostgreSQL**:`postgres://postgres@localhost:5432/lobechat`
|
||||
- **Redis**:`redis://localhost:6379`
|
||||
- **RustFS API**:`http://localhost:9000`
|
||||
- **RustFS 控制台**:`http://localhost:9001`
|
||||
- **SearXNG**:`http://localhost:8180`
|
||||
- **应用程序**:`http://localhost:3010`
|
||||
|
||||
请参考[使用服务端数据库](/docs/development/basic/work-with-server-side-database)指南获得完整的设置说明。
|
||||
## 故障排除
|
||||
|
||||
### 重置服务
|
||||
|
||||
如遇到问题,可以重置整个服务堆栈:
|
||||
|
||||
```bash
|
||||
# 完全重置 Docker 环境(删除所有数据并重新启动)
|
||||
bun run dev:docker:reset
|
||||
```
|
||||
|
||||
### 端口冲突
|
||||
|
||||
如果端口已被占用:
|
||||
|
||||
```bash
|
||||
# 检查端口使用情况
|
||||
lsof -i :5432 # PostgreSQL
|
||||
lsof -i :6379 # Redis
|
||||
lsof -i :9000 # RustFS API
|
||||
lsof -i :9001 # RustFS 控制台
|
||||
lsof -i :8180 # SearXNG
|
||||
```
|
||||
|
||||
### 数据库迁移
|
||||
|
||||
如需手动运行迁移:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
在开发过程中,如果你在环境设置上遇到任何问题,或者有任何关于 LobeHub 开发的问题,欢迎随时向我们提问。我们期待看到你的贡献!
|
||||
|
||||
|
||||
@@ -1,195 +0,0 @@
|
||||
---
|
||||
title: Work with Server-Side Database
|
||||
description: Learn how to set up a server-side database for LobeHub with Docker.
|
||||
tags:
|
||||
- LobeHub
|
||||
- Server-Side Database
|
||||
- Docker
|
||||
- PostgreSQL
|
||||
- MinIO
|
||||
---
|
||||
|
||||
# Work with Server-Side Database
|
||||
|
||||
LobeHub provides a battery-included experience with its client-side database.
|
||||
While some features you really care about is only available at a server-side development.
|
||||
|
||||
In order to work with the aspect of server-side database,
|
||||
you can setup all the prerequisites by following the [Deploying Server-Side Database](https://lobehub.com/docs/self-hosting/server-database) story.
|
||||
But here is the easier approach that can reduce your pain.
|
||||
|
||||
## Quick Setup
|
||||
|
||||
### Environment Configuration
|
||||
|
||||
First, copy the example environment file to create your Docker Compose configuration:
|
||||
|
||||
```bash
|
||||
cp docker-compose/local/.env.example docker-compose/local/.env
|
||||
```
|
||||
|
||||
Edit `docker-compose/local/.env` as needed for your development setup. This file contains all necessary environment variables for the Docker services and configures:
|
||||
|
||||
- **Database**: PostgreSQL with connection string
|
||||
- **Authentication**: NextAuth with Casdoor SSO
|
||||
- **Storage**: MinIO S3-compatible storage
|
||||
- **Search**: SearXNG search engine
|
||||
|
||||
### Start Docker Services
|
||||
|
||||
Start all required services using Docker Compose:
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.development.yml up -d
|
||||
```
|
||||
|
||||
This will start the following services:
|
||||
|
||||
- PostgreSQL database (port 5432)
|
||||
- MinIO storage (port 9000)
|
||||
- Casdoor authentication (port 8000)
|
||||
- SearXNG search (port 8080)
|
||||
|
||||
### Run Database Migrations
|
||||
|
||||
Execute the database migration script to create all necessary tables:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
You should see: `✅ database migration pass.`
|
||||
|
||||
### Start Development Server
|
||||
|
||||
Launch the LobeHub development server:
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
The server will start on `http://localhost:3010`
|
||||
|
||||
And you can check all Docker services are running by running:
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.development.yml ps
|
||||
```
|
||||
|
||||
## Image Generation Development
|
||||
|
||||
When working with image generation features (text-to-image, image-to-image), the Docker Compose setup already includes all necessary storage services for handling generated images and user uploads.
|
||||
|
||||
### Image Generation Configuration
|
||||
|
||||
The existing Docker Compose configuration already includes MinIO storage service and all necessary environment variables in `docker-compose/local/.env.example`. No additional setup is required.
|
||||
|
||||
### Image Generation Architecture
|
||||
|
||||
The image generation feature requires:
|
||||
|
||||
- **PostgreSQL**: Stores metadata about generated images
|
||||
- **MinIO/S3**: Stores the actual image files
|
||||
|
||||
### Storage Configuration
|
||||
|
||||
The `docker-compose/local/.env.example` file includes all necessary S3 environment variables:
|
||||
|
||||
```bash
|
||||
# S3 Storage Configuration (MinIO for local development)
|
||||
S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}
|
||||
S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
|
||||
S3_ENDPOINT=http://localhost:${MINIO_PORT}
|
||||
S3_BUCKET=${MINIO_LOBE_BUCKET}
|
||||
S3_ENABLE_PATH_STYLE=1 # Required for MinIO
|
||||
S3_SET_ACL=0 # MinIO compatibility
|
||||
```
|
||||
|
||||
### File Storage Structure
|
||||
|
||||
Generated images and user uploads are organized in the MinIO bucket:
|
||||
|
||||
```
|
||||
lobe/ # S3 Bucket (MINIO_LOBE_BUCKET)
|
||||
├── generated/ # Generated images
|
||||
│ └── {userId}/
|
||||
│ └── {sessionId}/
|
||||
│ └── {imageId}.png
|
||||
└── uploads/ # User uploads for image-to-image
|
||||
└── {userId}/
|
||||
└── {fileId}.{ext}
|
||||
```
|
||||
|
||||
### Development Workflow for Images
|
||||
|
||||
When developing image generation features, generated images will be:
|
||||
|
||||
1. Created by the AI model
|
||||
2. Uploaded to S3/MinIO via presigned URLs
|
||||
3. Metadata stored in PostgreSQL
|
||||
4. Served via the public S3 URL
|
||||
|
||||
Example code for testing image upload:
|
||||
|
||||
```typescript
|
||||
// Example: Upload generated image
|
||||
const uploadUrl = await trpc.upload.createPresignedUrl.mutate({
|
||||
filename: 'generated-image.png',
|
||||
contentType: 'image/png',
|
||||
});
|
||||
|
||||
// Upload to S3
|
||||
await fetch(uploadUrl, {
|
||||
method: 'PUT',
|
||||
body: imageBlob,
|
||||
headers: { 'Content-Type': 'image/png' },
|
||||
});
|
||||
```
|
||||
|
||||
### Service URLs
|
||||
|
||||
When running with Docker Compose development setup:
|
||||
|
||||
- **PostgreSQL**: `postgres://postgres@localhost:5432/LobeHub`
|
||||
- **MinIO API**: `http://localhost:9000`
|
||||
- **MinIO Console**: `http://localhost:9001` (admin/CHANGE\_THIS\_PASSWORD\_IN\_PRODUCTION)
|
||||
- **Application**: `http://localhost:3010`
|
||||
|
||||
### Reset Services
|
||||
|
||||
If you encounter issues, you can reset the entire stack:
|
||||
|
||||
```bash
|
||||
# Stop and remove all containers
|
||||
docker-compose -f docker-compose.development.yml down
|
||||
|
||||
# Remove volumes (this will delete all data)
|
||||
docker-compose -f docker-compose.development.yml down -v
|
||||
|
||||
# Start fresh
|
||||
docker-compose -f docker-compose.development.yml up -d
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
#### Port Conflicts
|
||||
|
||||
If ports are already in use:
|
||||
|
||||
```bash
|
||||
# Check what's using the ports
|
||||
lsof -i :5432 # PostgreSQL
|
||||
lsof -i :9000 # MinIO API
|
||||
lsof -i :9001 # MinIO Console
|
||||
```
|
||||
|
||||
#### Database Migrations
|
||||
|
||||
The setup script runs migrations automatically. If you need to run them manually:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
Note: In development mode with `pnpm dev:desktop`, migrations also run automatically on startup.
|
||||
@@ -1,195 +0,0 @@
|
||||
---
|
||||
title: 使用服务端数据库
|
||||
description: 快速设置 LobeHub 服务端数据库,支持 Docker 和图像生成。
|
||||
tags:
|
||||
- 服务端数据库
|
||||
- LobeHub
|
||||
- Docker
|
||||
- 图像生成
|
||||
- PostgreSQL
|
||||
---
|
||||
|
||||
# 使用服务端数据库
|
||||
|
||||
LobeHub 提供了内置的客户端数据库体验。
|
||||
但某些重要功能仅在服务端开发中可用。
|
||||
|
||||
为了使用服务端数据库功能,
|
||||
需要参考 [部署服务端数据库](https://lobehub.com/docs/self-hosting/server-database) 的说明来配置所有前置条件。
|
||||
本文档提供了一个更简化的配置方法,能够在本地开发时快速启动简化的服务端环境。
|
||||
|
||||
## 快速设置
|
||||
|
||||
### 环境配置
|
||||
|
||||
首先,复制示例环境文件来创建你的 Docker Compose 配置:
|
||||
|
||||
```bash
|
||||
cp docker-compose/local/.env.example docker-compose/local/.env
|
||||
```
|
||||
|
||||
根据需要编辑 `docker-compose/local/.env` 文件以适应你的开发设置。此文件包含 Docker 服务所需的所有环境变量,配置了:
|
||||
|
||||
- **数据库**: 带连接字符串的 PostgreSQL
|
||||
- **身份验证**: 带 Casdoor SSO 的 NextAuth
|
||||
- **存储**: MinIO S3 兼容存储
|
||||
- **搜索**: SearXNG 搜索引擎
|
||||
|
||||
### 启动 Docker 服务
|
||||
|
||||
使用 Docker Compose 启动所有必需的服务:
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.development.yml up -d
|
||||
```
|
||||
|
||||
这将启动以下服务:
|
||||
|
||||
- PostgreSQL 数据库(端口 5432)
|
||||
- MinIO 存储(端口 9000)
|
||||
- Casdoor 身份验证(端口 8000)
|
||||
- SearXNG 搜索(端口 8080)
|
||||
|
||||
### 运行数据库迁移
|
||||
|
||||
执行数据库迁移脚本以创建所有必要的表:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
预期输出:`✅ database migration pass.`
|
||||
|
||||
### 启动开发服务器
|
||||
|
||||
启动 LobeHub 开发服务器:
|
||||
|
||||
```bash
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
服务器将在 `http://localhost:3010` 上启动
|
||||
|
||||
可以通过运行以下命令检查所有 Docker 服务运行状态:
|
||||
|
||||
```bash
|
||||
docker-compose -f docker-compose.development.yml ps
|
||||
```
|
||||
|
||||
## 图像生成开发
|
||||
|
||||
在开发图像生成功能(文生图、图生图)时,Docker Compose 配置已经包含了处理生成图像和用户上传所需的所有存储服务。
|
||||
|
||||
### 图像生成配置
|
||||
|
||||
现有的 Docker Compose 配置已经包含了 MinIO 存储服务以及 `docker-compose/local/.env.example` 中的所有必要环境变量。无需额外配置。
|
||||
|
||||
### 图像生成架构
|
||||
|
||||
图像生成功能需要:
|
||||
|
||||
- **PostgreSQL**:存储生成图像的元数据
|
||||
- **MinIO/S3**:存储实际的图像文件
|
||||
|
||||
### 存储配置
|
||||
|
||||
`docker-compose/local/.env.example` 文件包含所有必要的 S3 环境变量:
|
||||
|
||||
```bash
|
||||
# S3 存储配置(本地开发使用 MinIO)
|
||||
S3_ACCESS_KEY_ID=${MINIO_ROOT_USER}
|
||||
S3_SECRET_ACCESS_KEY=${MINIO_ROOT_PASSWORD}
|
||||
S3_ENDPOINT=http://localhost:${MINIO_PORT}
|
||||
S3_BUCKET=${MINIO_LOBE_BUCKET}
|
||||
S3_ENABLE_PATH_STYLE=1 # MinIO 必需
|
||||
S3_SET_ACL=0 # MinIO 兼容性
|
||||
```
|
||||
|
||||
### 文件存储结构
|
||||
|
||||
生成的图像和用户上传在 MinIO 存储桶中按以下方式组织:
|
||||
|
||||
```
|
||||
lobe/ # S3 存储桶 (MINIO_LOBE_BUCKET)
|
||||
├── generated/ # 生成的图像
|
||||
│ └── {userId}/
|
||||
│ └── {sessionId}/
|
||||
│ └── {imageId}.png
|
||||
└── uploads/ # 用户上传的图像处理文件
|
||||
└── {userId}/
|
||||
└── {fileId}.{ext}
|
||||
```
|
||||
|
||||
### 图像开发工作流
|
||||
|
||||
在开发图像生成功能时,生成的图像将:
|
||||
|
||||
1. 由 AI 模型创建
|
||||
2. 通过预签名 URL 上传到 S3/MinIO
|
||||
3. 元数据存储在 PostgreSQL 中
|
||||
4. 通过公共 S3 URL 提供服务
|
||||
|
||||
测试图像上传的示例代码:
|
||||
|
||||
```typescript
|
||||
// 示例:上传生成的图像
|
||||
const uploadUrl = await trpc.upload.createPresignedUrl.mutate({
|
||||
filename: 'generated-image.png',
|
||||
contentType: 'image/png',
|
||||
});
|
||||
|
||||
// 上传到 S3
|
||||
await fetch(uploadUrl, {
|
||||
method: 'PUT',
|
||||
body: imageBlob,
|
||||
headers: { 'Content-Type': 'image/png' },
|
||||
});
|
||||
```
|
||||
|
||||
### 服务地址
|
||||
|
||||
运行 Docker Compose 开发环境时:
|
||||
|
||||
- **PostgreSQL**:`postgres://postgres@localhost:5432/LobeHub`
|
||||
- **MinIO API**:`http://localhost:9000`
|
||||
- **MinIO 控制台**:`http://localhost:9001` (admin/CHANGE\_THIS\_PASSWORD\_IN\_PRODUCTION)
|
||||
- **应用程序**:`http://localhost:3010`
|
||||
|
||||
### 重置服务
|
||||
|
||||
如遇到问题,可以重置整个服务堆栈:
|
||||
|
||||
```bash
|
||||
# 停止并删除所有容器
|
||||
docker-compose -f docker-compose.development.yml down
|
||||
|
||||
# 删除卷(这将删除所有数据)
|
||||
docker-compose -f docker-compose.development.yml down -v
|
||||
|
||||
# 重新启动
|
||||
docker-compose -f docker-compose.development.yml up -d
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
### 故障排除
|
||||
|
||||
#### 端口冲突
|
||||
|
||||
如果端口已被占用:
|
||||
|
||||
```bash
|
||||
# 检查端口使用情况
|
||||
lsof -i :5432 # PostgreSQL
|
||||
lsof -i :9000 # MinIO API
|
||||
lsof -i :9001 # MinIO 控制台
|
||||
```
|
||||
|
||||
#### 数据库迁移
|
||||
|
||||
配置脚本会自动运行迁移。如需手动运行:
|
||||
|
||||
```bash
|
||||
pnpm db:migrate
|
||||
```
|
||||
|
||||
注意:在使用 `pnpm dev:desktop` 的开发模式下,迁移也会在启动时自动运行。
|
||||
@@ -643,6 +643,7 @@ table messages {
|
||||
thread_id [name: 'messages_thread_id_idx']
|
||||
agent_id [name: 'messages_agent_id_idx']
|
||||
group_id [name: 'messages_group_id_idx']
|
||||
message_group_id [name: 'messages_message_group_id_idx']
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+53
-63
@@ -19,78 +19,63 @@ Welcome to the LobeHub Technical Development Getting Started Guide. LobeHub is a
|
||||
|
||||
The core technology stack of LobeHub is as follows:
|
||||
|
||||
- **Framework**: We chose [Next.js](https://nextjs.org/), a powerful React framework that provides key features such as server-side rendering, routing framework, and Router Handler.
|
||||
- **Component Library**: We use [Ant Design (antd)](https://ant.design/) as the basic component library, along with [lobe-ui](https://github.com/lobehub/lobe-ui) as our business component library.
|
||||
- **State Management**: We selected [zustand](https://github.com/pmndrs/zustand), a lightweight and easy-to-use state management library.
|
||||
- **Network Requests**: We use [swr](https://swr.vercel.app/), a React Hooks library for data fetching.
|
||||
- **Routing**: For routing management, we directly use the solution provided by [Next.js](https://nextjs.org/).
|
||||
- **Internationalization**: We use [i18next](https://www.i18next.com/) to support multiple languages in the application.
|
||||
- **Styling**: We use [antd-style](https://github.com/ant-design/antd-style), a CSS-in-JS library that complements Ant Design.
|
||||
- **Unit Testing**: We use [vitest](https://github.com/vitest-dev/vitest) for unit testing.
|
||||
- **Framework**: [Next.js](https://nextjs.org/) 16 + [React](https://react.dev/) 19, providing server-side rendering, Router Handler, and other key features.
|
||||
- **Component Library**: [Ant Design (antd)](https://ant.design/) as the base component library, [@lobehub/ui](https://github.com/lobehub/lobe-ui) as the business component library.
|
||||
- **State Management**: [zustand](https://github.com/pmndrs/zustand), a lightweight and easy-to-use state management library.
|
||||
- **Data Fetching**: [SWR](https://swr.vercel.app/) for client-side data fetching.
|
||||
- **Routing**: Hybrid routing architecture — [Next.js App Router](https://nextjs.org/) for static pages (e.g., auth pages), [React Router DOM](https://reactrouter.com/) for the main SPA.
|
||||
- **API**: [tRPC](https://trpc.io/) for end-to-end type-safe API communication.
|
||||
- **Database**: [Drizzle ORM](https://orm.drizzle.team/) + PostgreSQL.
|
||||
- **Internationalization**: [react-i18next](https://react.i18next.com/) for multilingual support.
|
||||
- **Styling**: [antd-style](https://github.com/ant-design/antd-style), a CSS-in-JS library that complements Ant Design.
|
||||
- **Unit Testing**: [Vitest](https://github.com/vitest-dev/vitest) for unit testing.
|
||||
|
||||
## Folder Directory Structure
|
||||
|
||||
The folder directory structure of LobeHub is as follows:
|
||||
LobeHub uses a Monorepo architecture
|
||||
(`@lobechat/` namespace).
|
||||
The top-level directory structure is as follows:
|
||||
|
||||
```bash
|
||||
src
|
||||
├── app # Next.js App Router implementation with route groups and API routes
|
||||
├── business # Business logic modules (client and server)
|
||||
├── components # Reusable UI components
|
||||
├── config # Application configuration files, including client and server environment variables
|
||||
├── const # Application constants and enums
|
||||
├── envs # Environment variable definitions and validation (analytics, auth, llm, etc.)
|
||||
├── features # Business-related feature modules, such as Agent settings, plugin development pop-ups, etc.
|
||||
├── helpers # Utility helpers for tool engineering, placeholder parsing, etc.
|
||||
├── hooks # Custom utility Hooks reusable across the application
|
||||
├── layout # Application layout components, such as navigation bars, sidebars, etc.
|
||||
├── libs # Third-party integrations (analytics, OIDC, etc.)
|
||||
├── locales # Language files for internationalization
|
||||
├── server # Server-side modules and services
|
||||
├── services # Encapsulated backend service interfaces, such as HTTP requests
|
||||
├── store # Zustand store for state management
|
||||
├── styles # Global styles and CSS-in-JS configurations
|
||||
├── tools # Built-in tools (artifacts, inspectors, interventions, etc.)
|
||||
├── types # TypeScript type definition files
|
||||
└── utils # General utility functions
|
||||
lobe-chat/
|
||||
├── apps/desktop/ # Electron desktop app
|
||||
├── packages/ # Shared packages (@lobechat/*)
|
||||
│ ├── database/ # Database schemas, models, repositories
|
||||
│ ├── agent-runtime/ # Agent runtime
|
||||
│ ├── model-runtime/ # Model runtime
|
||||
│ └── ... # More shared packages
|
||||
├── src/ # Main application source code
|
||||
│ ├── app/ # Next.js App Router with route groups and API routes
|
||||
│ ├── components/ # Reusable UI components
|
||||
│ ├── config/ # App configuration, client and server env vars
|
||||
│ ├── const/ # Application constants and enums
|
||||
│ ├── envs/ # Env var definitions and validation (analytics, auth, LLM, etc.)
|
||||
│ ├── features/ # Business feature modules (Agent settings, plugin dev, etc.)
|
||||
│ ├── helpers/ # Utility helper functions
|
||||
│ ├── hooks/ # Reusable custom Hooks
|
||||
│ ├── layout/ # Layout components (AuthProvider, GlobalProvider, etc.)
|
||||
│ ├── libs/ # Third-party integrations (better-auth, OIDC, tRPC, etc.)
|
||||
│ ├── locales/ # Internationalization language files
|
||||
│ ├── server/ # Server-side modules, routers, and services
|
||||
│ ├── services/ # Client-side service interfaces
|
||||
│ ├── store/ # Zustand state management
|
||||
│ ├── styles/ # Global styles and CSS-in-JS configurations
|
||||
│ ├── tools/ # Built-in tools (artifacts, inspectors, interventions, etc.)
|
||||
│ ├── types/ # TypeScript type definitions
|
||||
│ └── utils/ # General utility functions
|
||||
├── locales/ # i18n translation files (zh-CN, en-US, etc.)
|
||||
└── e2e/ # E2E tests (Cucumber + Playwright)
|
||||
```
|
||||
|
||||
For a detailed introduction to the directory structure, see: [Folder Directory Structure](/docs/development/basic/folder-structure)
|
||||
|
||||
## Local Development Environment Setup
|
||||
|
||||
This section outlines setting up the development environment and local development. Before starting, please ensure that Node.js, Git, and your chosen package manager (Bun or PNPM) are installed in your local environment.
|
||||
|
||||
We recommend using WebStorm as your integrated development environment (IDE).
|
||||
|
||||
1. **Get the code**: Clone the LobeHub code repository locally:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/lobehub/lobehub.git
|
||||
```
|
||||
|
||||
2. **Install dependencies**: Enter the project directory and install the required dependencies:
|
||||
|
||||
```bash
|
||||
cd lobehub
|
||||
# If you use Bun
|
||||
bun install
|
||||
# If you use PNPM
|
||||
pnpm install
|
||||
```
|
||||
|
||||
3. **Run and debug**: Start the local development server and begin your development journey:
|
||||
|
||||
```bash
|
||||
# Start the development server with Bun
|
||||
bun run dev
|
||||
# Visit http://localhost:3010 to view the application
|
||||
```
|
||||
|
||||
> \[!IMPORTANT]\
|
||||
> If you encounter the error "Could not find 'stylelint-config-recommended'" when installing dependencies with `npm`, please reinstall the dependencies using `pnpm` or `bun`.
|
||||
|
||||
Now, you should be able to see the welcome page of LobeHub in your browser. For a detailed environment setup guide, please refer to [Development Environment Setup Guide](/docs/development/basic/setup-development).
|
||||
Please refer to the
|
||||
[Environment Setup Guide](/docs/development/basic/setup-development)
|
||||
for the complete setup process,
|
||||
including software installation, project configuration,
|
||||
Docker service startup, and database migrations.
|
||||
|
||||
## Code Style and Contribution Guide
|
||||
|
||||
@@ -105,9 +90,14 @@ For detailed code style and contribution guidelines, please refer to [Code Style
|
||||
|
||||
## Internationalization Implementation Guide
|
||||
|
||||
LobeHub uses `i18next` and `lobe-i18n` to implement multilingual support, ensuring a global user experience.
|
||||
LobeHub uses `react-i18next` for multilingual support,
|
||||
ensuring a global user experience.
|
||||
|
||||
Internationalization files are located in `src/locales`, containing the default language (Chinese). We generate other language JSON files automatically through `lobe-i18n`.
|
||||
Default language files are located in `src/locales/default/`
|
||||
(English). Translation files are in the `locales/` directory.
|
||||
During development, you only need to edit keys in
|
||||
`src/locales/default/` — CI automatically generates
|
||||
translation files for other languages.
|
||||
|
||||
If you want to add a new language, follow specific steps detailed in [New Language Addition Guide](/docs/development/internationalization/add-new-locale). We encourage you to participate in our internationalization efforts to provide better services to global users.
|
||||
|
||||
|
||||
@@ -17,78 +17,60 @@ tags:
|
||||
|
||||
LobeHub 的核心技术栈如下:
|
||||
|
||||
- **框架**:我们选择了 [Next.js](https://nextjs.org/),这是一款强大的 React 框架,为我们的项目提供了服务端渲染、路由框架、Router Handler 等关键功能。
|
||||
- **组件库**:我们使用了 [Ant Design (antd)](https://ant.design/) 作为基础组件库,同时引入了 [lobe-ui](https://github.com/lobehub/lobe-ui) 作为我们的业务组件库。
|
||||
- **状态管理**:我们选用了 [zustand](https://github.com/pmndrs/zustand),一款轻量级且易于使用的状态管理库。
|
||||
- **网络请求**:我们采用 [swr](https://swr.vercel.app/),这是一款用于数据获取的 React Hooks 库。
|
||||
- **路由**:路由管理我们直接使用 [Next.js](https://nextjs.org/) 自身提供的解决方案。
|
||||
- **国际化**:我们使用 [i18next](https://www.i18next.com/) 来实现应用的多语言支持。
|
||||
- **样式**:我们使用 [antd-style](https://github.com/ant-design/antd-style),这是一款与 Ant Design 配套的 CSS-in-JS 库。
|
||||
- **单元测试**:我们使用 [vitest](https://github.com/vitest-dev/vitest) 进行单元测试。
|
||||
- **框架**:[Next.js](https://nextjs.org/) 16 + [React](https://react.dev/) 19,为项目提供服务端渲染、Router Handler 等关键功能。
|
||||
- **组件库**:[Ant Design (antd)](https://ant.design/) 作为基础组件库,[@lobehub/ui](https://github.com/lobehub/lobe-ui) 作为业务组件库。
|
||||
- **状态管理**:[zustand](https://github.com/pmndrs/zustand),一款轻量级且易于使用的状态管理库。
|
||||
- **数据获取**:[SWR](https://swr.vercel.app/) 用于客户端数据获取。
|
||||
- **路由**:采用混合路由架构 —— [Next.js App Router](https://nextjs.org/) 处理静态页面(如认证页),[React Router DOM](https://reactrouter.com/) 承载主应用 SPA。
|
||||
- **API**:[tRPC](https://trpc.io/) 实现端到端类型安全的 API 通信。
|
||||
- **数据库**:[Drizzle ORM](https://orm.drizzle.team/) + PostgreSQL。
|
||||
- **国际化**:[react-i18next](https://react.i18next.com/) 实现多语言支持。
|
||||
- **样式**:[antd-style](https://github.com/ant-design/antd-style),与 Ant Design 配套的 CSS-in-JS 库。
|
||||
- **单元测试**:[Vitest](https://github.com/vitest-dev/vitest) 进行单元测试。
|
||||
|
||||
## 文件夹目录架构
|
||||
|
||||
LobeHub 的文件夹目录架构如下:
|
||||
LobeHub 采用 Monorepo 架构(`@lobechat/` 命名空间),顶层目录结构如下:
|
||||
|
||||
```bash
|
||||
src
|
||||
├── app # Next.js App Router 实现,包含路由组和 API 路由
|
||||
├── business # 业务逻辑模块(客户端和服务端)
|
||||
├── components # 可复用的 UI 组件
|
||||
├── config # 应用的配置文件,包含客户端环境变量与服务端环境变量
|
||||
├── const # 应用常量和枚举
|
||||
├── envs # 环境变量定义和校验(分析、认证、LLM 等)
|
||||
├── features # 与业务功能相关的功能模块,如 Agent 设置、插件开发弹窗等
|
||||
├── helpers # 工具辅助函数,用于工具工程、占位符解析等
|
||||
├── hooks # 全应用复用自定义的工具 Hooks
|
||||
├── layout # 应用的布局组件,如导航栏、侧边栏等
|
||||
├── libs # 第三方集成(分析、OIDC 等)
|
||||
├── locales # 国际化的语言文件
|
||||
├── server # 服务端模块和服务
|
||||
├── services # 封装的后端服务接口,如 HTTP 请求
|
||||
├── store # 用于状态管理的 zustand store
|
||||
├── styles # 全局样式和 CSS-in-JS 配置
|
||||
├── tools # 内置工具(artifacts、inspectors、interventions 等)
|
||||
├── types # TypeScript 的类型定义文件
|
||||
└── utils # 通用的工具函数
|
||||
lobe-chat/
|
||||
├── apps/desktop/ # Electron 桌面应用
|
||||
├── packages/ # 共享包(@lobechat/*)
|
||||
│ ├── database/ # 数据库 schemas、models、repositories
|
||||
│ ├── agent-runtime/ # Agent 运行时
|
||||
│ ├── model-runtime/ # 模型运行时
|
||||
│ └── ... # 更多共享包
|
||||
├── src/ # 主应用源码
|
||||
│ ├── app/ # Next.js App Router,包含路由组和 API 路由
|
||||
│ ├── components/ # 可复用的 UI 组件
|
||||
│ ├── config/ # 应用配置文件,包含客户端与服务端环境变量
|
||||
│ ├── const/ # 应用常量和枚举
|
||||
│ ├── envs/ # 环境变量定义和校验(分析、认证、LLM 等)
|
||||
│ ├── features/ # 业务功能模块,如 Agent 设置、插件开发弹窗等
|
||||
│ ├── helpers/ # 工具辅助函数
|
||||
│ ├── hooks/ # 全应用复用的自定义 Hooks
|
||||
│ ├── layout/ # 布局组件(AuthProvider、GlobalProvider 等)
|
||||
│ ├── libs/ # 第三方集成(better-auth、OIDC、tRPC 等)
|
||||
│ ├── locales/ # 国际化的语言文件
|
||||
│ ├── server/ # 服务端模块、路由和服务
|
||||
│ ├── services/ # 客户端服务接口
|
||||
│ ├── store/ # zustand 状态管理
|
||||
│ ├── styles/ # 全局样式和 CSS-in-JS 配置
|
||||
│ ├── tools/ # 内置工具(artifacts、inspectors、interventions 等)
|
||||
│ ├── types/ # TypeScript 类型定义
|
||||
│ └── utils/ # 通用工具函数
|
||||
├── locales/ # 国际化翻译文件(zh-CN、en-US 等)
|
||||
└── e2e/ # E2E 测试(Cucumber + Playwright)
|
||||
```
|
||||
|
||||
有关目录架构的详细介绍,详见: [文件夹目录架构](/zh/docs/development/basic/folder-structure)
|
||||
|
||||
## 本地开发环境设置
|
||||
|
||||
本节将概述搭建开发环境并进行本地开发。 在开始之前,请确保你的本地环境中已安装 Node.js、Git 以及你选择的包管理器(Bun 或 PNPM)。
|
||||
|
||||
我们推荐使用 WebStorm 作为你的集成开发环境(IDE)。
|
||||
|
||||
1. **获取代码**:克隆 LobeHub 的代码库到本地:
|
||||
|
||||
```bash
|
||||
git clone https://github.com/lobehub/lobehub.git
|
||||
```
|
||||
|
||||
2. **安装依赖**:进入项目目录,并安装所需依赖:
|
||||
|
||||
```bash
|
||||
cd lobehub
|
||||
# 如果你使用 Bun
|
||||
bun install
|
||||
# 如果你使用 PNPM
|
||||
pnpm install
|
||||
```
|
||||
|
||||
3. **运行与调试**:启动本地开发服务器,开始你的开发之旅:
|
||||
|
||||
```bash
|
||||
# 使用 Bun 启动开发服务器
|
||||
bun run dev
|
||||
# 访问 http://localhost:3010 查看应用
|
||||
```
|
||||
|
||||
> \[!IMPORTANT]\
|
||||
> 如果使用`npm`安装依赖出现`Could not find "stylelint-config-recommended"`错误,请使用 `pnpm` 或者 `bun` 重新安装依赖。
|
||||
|
||||
现在,你应该可以在浏览器中看到 LobeHub 的欢迎页面。详细的环境配置指南,请参考 [开发环境设置指南](/zh/docs/development/basic/setup-development)。
|
||||
请参考
|
||||
[开发环境设置指南](/zh/docs/development/basic/setup-development)
|
||||
了解完整的环境搭建流程,
|
||||
包括软件安装、项目配置、Docker 服务启动和数据库迁移等步骤。
|
||||
|
||||
## 代码风格与贡献指南
|
||||
|
||||
@@ -103,9 +85,12 @@ bun run dev
|
||||
|
||||
## 国际化实现指南
|
||||
|
||||
LobeHub 采用 `i18next` 和 `lobe-i18n` 实现多语言支持,确保用户全球化体验。
|
||||
LobeHub 采用 `react-i18next` 实现多语言支持,确保用户全球化体验。
|
||||
|
||||
国际化文件位于 `src/locales`,包含默认语言(中文)。 我们会通过 `lobe-i18n` 自动生成其他的语言 JSON 文件。
|
||||
默认语言文件位于 `src/locales/default/`(英文),
|
||||
翻译文件位于 `locales/` 目录。
|
||||
开发时只需编辑 `src/locales/default/` 中的 key,
|
||||
CI 会自动生成其他语言的翻译文件。
|
||||
|
||||
如果要添加新语种,需遵循特定步骤,详见 [新语种添加指南](/zh/docs/development/internationalization/add-new-locale)。 我们鼓励你参与我们的国际化努力,共同为全球用户提供更好的服务。
|
||||
|
||||
|
||||
+19
-19
@@ -42,7 +42,7 @@ To enable Better Auth in LobeHub, set the following environment variables:
|
||||
| --------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| Google | `google` | `AUTH_GOOGLE_ID`, `AUTH_GOOGLE_SECRET` |
|
||||
| GitHub | `github` | `AUTH_GITHUB_ID`, `AUTH_GITHUB_SECRET` |
|
||||
| Microsoft | `microsoft` | `AUTH_MICROSOFT_ID`, `AUTH_MICROSOFT_SECRET` |
|
||||
| Microsoft | `microsoft` | `AUTH_MICROSOFT_ID`, `AUTH_MICROSOFT_SECRET`, `AUTH_MICROSOFT_AUTHORITY_URL`, `AUTH_MICROSOFT_TENANT_ID` |
|
||||
| Apple | `apple` | `AUTH_APPLE_CLIENT_ID`, `AUTH_APPLE_CLIENT_SECRET` |
|
||||
| AWS Cognito | `cognito` | `AUTH_COGNITO_ID`, `AUTH_COGNITO_SECRET`, `AUTH_COGNITO_DOMAIN`, `AUTH_COGNITO_REGION`, `AUTH_COGNITO_USERPOOL_ID` |
|
||||
| Auth0 | `auth0` | `AUTH_AUTH0_ID`, `AUTH_AUTH0_SECRET`, `AUTH_AUTH0_ISSUER` |
|
||||
@@ -61,41 +61,41 @@ To enable Better Auth in LobeHub, set the following environment variables:
|
||||
Click on a provider below for detailed configuration guides:
|
||||
|
||||
<Cards>
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/password'} title={'Email/Password'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/password'} title={'Email/Password'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/github'} title={'GitHub'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/github'} title={'GitHub'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/google'} title={'Google'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/google'} title={'Google'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/microsoft'} title={'Microsoft'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/microsoft'} title={'Microsoft'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/apple'} title={'Apple'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/apple'} title={'Apple'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/cognito'} title={'AWS Cognito'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/cognito'} title={'AWS Cognito'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/auth0'} title={'Auth0'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/auth0'} title={'Auth0'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/authelia'} title={'Authelia'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/authelia'} title={'Authelia'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/authentik'} title={'Authentik'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/authentik'} title={'Authentik'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/casdoor'} title={'Casdoor'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/casdoor'} title={'Casdoor'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/keycloak'} title={'Keycloak'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/keycloak'} title={'Keycloak'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/logto'} title={'Logto'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/logto'} title={'Logto'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/okta'} title={'Okta'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/okta'} title={'Okta'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/zitadel'} title={'ZITADEL'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/zitadel'} title={'ZITADEL'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/generic-oidc'} title={'Generic OIDC'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/generic-oidc'} title={'Generic OIDC'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/feishu'} title={'Feishu'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/feishu'} title={'Feishu'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/providers/wechat'} title={'WeChat'} />
|
||||
<Card href={'/docs/self-hosting/auth/providers/wechat'} title={'WeChat'} />
|
||||
</Cards>
|
||||
|
||||
## Callback URL Format
|
||||
|
||||
@@ -42,7 +42,7 @@ LobeHub 支持使用 Better Auth 配置外部身份验证服务,供企业 /
|
||||
| --------------------- | ----------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| Google | `google` | `AUTH_GOOGLE_ID`, `AUTH_GOOGLE_SECRET` |
|
||||
| GitHub | `github` | `AUTH_GITHUB_ID`, `AUTH_GITHUB_SECRET` |
|
||||
| Microsoft | `microsoft` | `AUTH_MICROSOFT_ID`, `AUTH_MICROSOFT_SECRET` |
|
||||
| Microsoft | `microsoft` | `AUTH_MICROSOFT_ID`, `AUTH_MICROSOFT_SECRET`, `AUTH_MICROSOFT_AUTHORITY_URL`, `AUTH_MICROSOFT_TENANT_ID` |
|
||||
| Apple | `apple` | `AUTH_APPLE_CLIENT_ID`, `AUTH_APPLE_CLIENT_SECRET` |
|
||||
| AWS Cognito | `cognito` | `AUTH_COGNITO_ID`, `AUTH_COGNITO_SECRET`, `AUTH_COGNITO_DOMAIN`, `AUTH_COGNITO_REGION`, `AUTH_COGNITO_USERPOOL_ID` |
|
||||
| Auth0 | `auth0` | `AUTH_AUTH0_ID`, `AUTH_AUTH0_SECRET`, `AUTH_AUTH0_ISSUER` |
|
||||
@@ -61,41 +61,41 @@ LobeHub 支持使用 Better Auth 配置外部身份验证服务,供企业 /
|
||||
点击下方提供商查看详细配置指南:
|
||||
|
||||
<Cards>
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/password'} title={'邮箱密码'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/password'} title={'邮箱密码'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/github'} title={'GitHub'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/github'} title={'GitHub'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/google'} title={'Google'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/google'} title={'Google'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/microsoft'} title={'Microsoft'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/microsoft'} title={'Microsoft'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/apple'} title={'Apple'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/apple'} title={'Apple'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/cognito'} title={'AWS Cognito'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/cognito'} title={'AWS Cognito'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/auth0'} title={'Auth0'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/auth0'} title={'Auth0'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/authelia'} title={'Authelia'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/authelia'} title={'Authelia'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/authentik'} title={'Authentik'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/authentik'} title={'Authentik'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/casdoor'} title={'Casdoor'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/casdoor'} title={'Casdoor'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/keycloak'} title={'Keycloak'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/keycloak'} title={'Keycloak'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/logto'} title={'Logto'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/logto'} title={'Logto'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/okta'} title={'Okta'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/okta'} title={'Okta'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/zitadel'} title={'ZITADEL'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/zitadel'} title={'ZITADEL'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/generic-oidc'} title={'Generic OIDC'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/generic-oidc'} title={'Generic OIDC'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/feishu'} title={'飞书'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/feishu'} title={'飞书'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/providers/wechat'} title={'微信'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/providers/wechat'} title={'微信'} />
|
||||
</Cards>
|
||||
|
||||
## 回调 URL 格式
|
||||
|
||||
@@ -13,7 +13,7 @@ tags:
|
||||
# Legacy Authentication
|
||||
|
||||
<Callout type={'warning'}>
|
||||
**Legacy Notice**: NextAuth and Clerk are legacy authentication methods. For new deployments, we strongly recommend using [Better Auth](/docs/self-hosting/advanced/auth) for its simplicity and flexibility.
|
||||
**Legacy Notice**: NextAuth and Clerk are legacy authentication methods. For new deployments, we strongly recommend using [Better Auth](/docs/self-hosting/auth) for its simplicity and flexibility.
|
||||
</Callout>
|
||||
|
||||
This page documents the legacy authentication methods (NextAuth and Clerk) for users who are still using these services.
|
||||
@@ -27,17 +27,17 @@ LobeHub has deeply integrated with Clerk to provide users with a secure and conv
|
||||
By setting the environment variables `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` and `CLERK_SECRET_KEY` in LobeHub's environment, you can enable and use Clerk.
|
||||
|
||||
<Callout type={'info'}>
|
||||
For detailed Clerk configuration, see [Clerk Configuration Guide](/docs/self-hosting/advanced/auth/clerk).
|
||||
For detailed Clerk configuration, see [Clerk Configuration Guide](/docs/self-hosting/auth/clerk).
|
||||
</Callout>
|
||||
|
||||
<Callout type={'tip'}>
|
||||
To migrate from Clerk to Better Auth, see the [Clerk Migration Guide](/docs/self-hosting/advanced/auth/clerk-to-betterauth).
|
||||
To migrate from Clerk to Better Auth, see the [Clerk Migration Guide](/docs/self-hosting/migration/v2/auth/clerk-to-betterauth).
|
||||
</Callout>
|
||||
|
||||
## Next Auth
|
||||
|
||||
<Callout type={'tip'}>
|
||||
To migrate from NextAuth to Better Auth, see the [NextAuth Migration Guide](/docs/self-hosting/advanced/auth/nextauth-to-betterauth).
|
||||
To migrate from NextAuth to Better Auth, see the [NextAuth Migration Guide](/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth).
|
||||
</Callout>
|
||||
|
||||
Before using NextAuth, please set the following variables in LobeHub's environment variables:
|
||||
@@ -53,27 +53,27 @@ Before using NextAuth, please set the following variables in LobeHub's environme
|
||||
Currently supported identity verification services include:
|
||||
|
||||
<Cards>
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/auth0'} title={'Auth0'} />
|
||||
<Card href={'/docs/self-hosting/auth/next-auth/auth0'} title={'Auth0'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/microsoft-entra-id'} title={'Microsoft Entra ID'} />
|
||||
<Card href={'/docs/self-hosting/auth/next-auth/microsoft-entra-id'} title={'Microsoft Entra ID'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/authentik'} title={'Authentik'} />
|
||||
<Card href={'/docs/self-hosting/auth/next-auth/authentik'} title={'Authentik'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/github'} title={'Github'} />
|
||||
<Card href={'/docs/self-hosting/auth/next-auth/github'} title={'Github'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/zitadel'} title={'ZITADEL'} />
|
||||
<Card href={'/docs/self-hosting/auth/next-auth/zitadel'} title={'ZITADEL'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
|
||||
<Card href={'/docs/self-hosting/auth/next-auth/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/authelia'} title={'Authelia'} />
|
||||
<Card href={'/docs/self-hosting/auth/next-auth/authelia'} title={'Authelia'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/logto'} title={'Logto'} />
|
||||
<Card href={'/docs/self-hosting/auth/next-auth/logto'} title={'Logto'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/keycloak'} title={'Keycloak'} />
|
||||
<Card href={'/docs/self-hosting/auth/next-auth/keycloak'} title={'Keycloak'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/google'} title={'Google'} />
|
||||
<Card href={'/docs/self-hosting/auth/next-auth/google'} title={'Google'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/next-auth/okta'} title={'Okta'} />
|
||||
<Card href={'/docs/self-hosting/auth/next-auth/okta'} title={'Okta'} />
|
||||
</Cards>
|
||||
|
||||
Click on the links to view the corresponding platform's configuration documentation.
|
||||
|
||||
@@ -11,7 +11,7 @@ tags:
|
||||
# 旧版身份验证
|
||||
|
||||
<Callout type={'warning'}>
|
||||
**旧版提示**:NextAuth 和 Clerk 是旧版身份验证方案。对于新部署,我们强烈建议使用 [Better Auth](/zh/docs/self-hosting/advanced/auth),它更简洁、更灵活。
|
||||
**旧版提示**:NextAuth 和 Clerk 是旧版身份验证方案。对于新部署,我们强烈建议使用 [Better Auth](/zh/docs/self-hosting/auth),它更简洁、更灵活。
|
||||
</Callout>
|
||||
|
||||
本页面为仍在使用这些服务的用户提供旧版身份验证方案(NextAuth 和 Clerk)的文档。
|
||||
@@ -25,17 +25,17 @@ LobeHub 与 Clerk 做了深度集成,能够为用户提供安全、便捷的
|
||||
在 LobeHub 的环境变量中设置 `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` 和 `CLERK_SECRET_KEY`,即可开启和使用 Clerk。
|
||||
|
||||
<Callout type={'info'}>
|
||||
详细的 Clerk 配置请参阅 [Clerk 配置指南](/zh/docs/self-hosting/advanced/auth/clerk)。
|
||||
详细的 Clerk 配置请参阅 [Clerk 配置指南](/zh/docs/self-hosting/auth/clerk)。
|
||||
</Callout>
|
||||
|
||||
<Callout type={'tip'}>
|
||||
如需从 Clerk 迁移到 Better Auth,请参阅 [Clerk 迁移指南](/zh/docs/self-hosting/advanced/auth/clerk-to-betterauth)。
|
||||
如需从 Clerk 迁移到 Better Auth,请参阅 [Clerk 迁移指南](/zh/docs/self-hosting/migration/v2/auth/clerk-to-betterauth)。
|
||||
</Callout>
|
||||
|
||||
## Next Auth
|
||||
|
||||
<Callout type={'tip'}>
|
||||
如需从 NextAuth 迁移到 Better Auth,请参阅 [NextAuth 迁移指南](/zh/docs/self-hosting/advanced/auth/nextauth-to-betterauth)。
|
||||
如需从 NextAuth 迁移到 Better Auth,请参阅 [NextAuth 迁移指南](/zh/docs/self-hosting/migration/v2/auth/nextauth-to-betterauth)。
|
||||
</Callout>
|
||||
|
||||
在使用 NextAuth 之前,请先在 LobeHub 的环境变量中设置以下变量:
|
||||
@@ -51,25 +51,25 @@ LobeHub 与 Clerk 做了深度集成,能够为用户提供安全、便捷的
|
||||
目前支持的身份验证服务有:
|
||||
|
||||
<Cards>
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/auth0'} title={'Auth0'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/next-auth/auth0'} title={'Auth0'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/microsoft-entra-id'} title={'Microsoft Entra ID'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/next-auth/microsoft-entra-id'} title={'Microsoft Entra ID'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/authentik'} title={'Authentik'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/next-auth/authentik'} title={'Authentik'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/github'} title={'Github'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/next-auth/github'} title={'Github'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/zitadel'} title={'ZITADEL'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/next-auth/zitadel'} title={'ZITADEL'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/next-auth/cloudflare-zero-trust'} title={'Cloudflare Zero Trust'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/authelia'} title={'Authelia'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/next-auth/authelia'} title={'Authelia'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/logto'} title={'Logto'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/next-auth/logto'} title={'Logto'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/keycloak'} title={'Keycloak'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/next-auth/keycloak'} title={'Keycloak'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/next-auth/okta'} title={'Okta'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/next-auth/okta'} title={'Okta'} />
|
||||
</Cards>
|
||||
|
||||
点击即可查看对应平台的配置文档。
|
||||
|
||||
@@ -70,12 +70,14 @@ tags:
|
||||
|
||||
### Configure Environment Variables
|
||||
|
||||
| Environment Variable | Type | Description |
|
||||
| ----------------------- | -------- | --------------------------------------------------------------- |
|
||||
| `AUTH_SECRET` | Required | Session encryption key, generate with `openssl rand -base64 32` |
|
||||
| `AUTH_SSO_PROVIDERS` | Required | Set to `microsoft` |
|
||||
| `AUTH_MICROSOFT_ID` | Required | Application (client) ID |
|
||||
| `AUTH_MICROSOFT_SECRET` | Required | Client secret value |
|
||||
| Environment Variable | Type | Description |
|
||||
| ------------------------------ | -------- | --------------------------------------------------------------- |
|
||||
| `AUTH_SECRET` | Required | Session encryption key, generate with `openssl rand -base64 32` |
|
||||
| `AUTH_SSO_PROVIDERS` | Required | Set to `microsoft` |
|
||||
| `AUTH_MICROSOFT_ID` | Required | Application (client) ID |
|
||||
| `AUTH_MICROSOFT_SECRET` | Required | Client secret value |
|
||||
| `AUTH_MICROSOFT_AUTHORITY_URL` | Optional | Authority URL for Microsoft Entra ID |
|
||||
| `AUTH_MICROSOFT_TENANT_ID` | Optional | Directory (tenant) ID for single-tenant apps |
|
||||
|
||||
<Callout type={'info'}>
|
||||
**Alternative Environment Variables**: For backward compatibility, these
|
||||
@@ -99,10 +101,6 @@ tags:
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Tenant Configuration
|
||||
|
||||
By default, LobeHub uses `common` tenant which allows both organizational and personal Microsoft accounts. If you need single-tenant configuration, you may need to customize the tenant settings.
|
||||
|
||||
### Client Secret Expiration
|
||||
|
||||
Microsoft client secrets have a maximum validity of 24 months. Remember to rotate secrets before they expire.
|
||||
|
||||
@@ -68,12 +68,14 @@ tags:
|
||||
|
||||
### 配置环境变量
|
||||
|
||||
| 环境变量 | 类型 | 描述 |
|
||||
| ----------------------- | -- | -------------------------------------- |
|
||||
| `AUTH_SECRET` | 必选 | 会话加密密钥,使用 `openssl rand -base64 32` 生成 |
|
||||
| `AUTH_SSO_PROVIDERS` | 必选 | 填写 `microsoft` |
|
||||
| `AUTH_MICROSOFT_ID` | 必选 | Application (client) ID |
|
||||
| `AUTH_MICROSOFT_SECRET` | 必选 | 客户端密钥值 |
|
||||
| 环境变量 | 类型 | 描述 |
|
||||
| ------------------------------ | -- | -------------------------------------- |
|
||||
| `AUTH_SECRET` | 必选 | 会话加密密钥,使用 `openssl rand -base64 32` 生成 |
|
||||
| `AUTH_SSO_PROVIDERS` | 必选 | 填写 `microsoft` |
|
||||
| `AUTH_MICROSOFT_ID` | 必选 | Application (client) ID |
|
||||
| `AUTH_MICROSOFT_SECRET` | 必选 | 客户端密钥值 |
|
||||
| `AUTH_MICROSOFT_AUTHORITY_URL` | 可选 | Microsoft Entra ID 的 Authority URL |
|
||||
| `AUTH_MICROSOFT_TENANT_ID` | 可选 | 单租户应用的 Directory (tenant) ID |
|
||||
|
||||
<Callout type={'info'}>
|
||||
**兼容的环境变量**:为了向后兼容,以下别名也支持:
|
||||
@@ -95,10 +97,6 @@ tags:
|
||||
|
||||
## 常见问题
|
||||
|
||||
### 租户配置
|
||||
|
||||
默认情况下,LobeHub 使用 `common` 租户,允许组织帐户和个人 Microsoft 帐户登录。如果需要单租户配置,可能需要自定义租户设置。
|
||||
|
||||
### 客户端密钥过期
|
||||
|
||||
Microsoft 客户端密钥最长有效期为 24 个月。请记得在过期前轮换密钥。
|
||||
|
||||
@@ -162,6 +162,20 @@ These settings are required for email verification and password reset features.
|
||||
- Default: `-`
|
||||
- Example: `xxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
|
||||
|
||||
#### `AUTH_MICROSOFT_AUTHORITY_URL`
|
||||
|
||||
- Type: Optional
|
||||
- Description: Authority URL for the Microsoft Entra ID. This is used to specify the endpoint for authentication requests.
|
||||
- Default: `https://login.microsoftonline.com`
|
||||
- Example: `https://login.partner.microsoftonline.cn`
|
||||
|
||||
#### `AUTH_MICROSOFT_TENANT_ID`
|
||||
|
||||
- Type: Optional
|
||||
- Description: Directory (tenant) ID for single-tenant Microsoft Entra ID applications.
|
||||
- Default: `common`
|
||||
- Example: `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
|
||||
|
||||
### AWS Cognito
|
||||
|
||||
#### `AUTH_COGNITO_ID`
|
||||
|
||||
@@ -160,6 +160,20 @@ LobeHub 在部署时提供了完善的身份验证服务能力,以下是相关
|
||||
- 默认值:`-`
|
||||
- 示例:`xxxxxxxxxxxxxxxxxxxxxxxxxxxxx`
|
||||
|
||||
#### `AUTH_MICROSOFT_AUTHORITY_URL`
|
||||
|
||||
- 类型:可选
|
||||
- 描述:Microsoft Entra ID 的 Authority URL。
|
||||
- 默认值:`https://login.microsoftonline.com`
|
||||
- 示例:`https://login.partner.microsoftonline.cn`
|
||||
|
||||
#### `AUTH_MICROSOFT_TENANT_ID`
|
||||
|
||||
- 类型:可选
|
||||
- 描述:单租户 Microsoft Entra ID 应用的 Directory (tenant) ID。
|
||||
- 默认值:`common`
|
||||
- 示例:`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`
|
||||
|
||||
### AWS Cognito
|
||||
|
||||
#### `AUTH_COGNITO_ID`
|
||||
|
||||
@@ -57,7 +57,7 @@ For small self-hosted deployments, the simplest approach is to let users reset t
|
||||
|
||||
1. **Configure Email Service**
|
||||
|
||||
Set up email service for password reset functionality. See [Email Service Configuration](/docs/self-hosting/advanced/auth#email-service-configuration).
|
||||
Set up email service for password reset functionality. See [Email Service Configuration](/docs/self-hosting/auth#email-service-configuration).
|
||||
|
||||
2. **Update Environment Variables**
|
||||
|
||||
@@ -80,7 +80,7 @@ For small self-hosted deployments, the simplest approach is to let users reset t
|
||||
```
|
||||
|
||||
<Callout type={'tip'}>
|
||||
See [Authentication Service Configuration](/docs/self-hosting/advanced/auth) for complete environment variables and SSO provider setup.
|
||||
See [Authentication Service Configuration](/docs/self-hosting/auth) for complete environment variables and SSO provider setup.
|
||||
</Callout>
|
||||
|
||||
3. **Redeploy LobeHub**
|
||||
@@ -289,7 +289,7 @@ npx tsx scripts/clerk-to-betterauth/verify.ts
|
||||
After migration is complete, follow [Simple Migration - Step 2](#steps) to configure Better Auth environment variables and redeploy.
|
||||
|
||||
<Callout type={'tip'}>
|
||||
For complete Better Auth configuration, see [Authentication Service Configuration](/docs/self-hosting/advanced/auth).
|
||||
For complete Better Auth configuration, see [Authentication Service Configuration](/docs/self-hosting/auth).
|
||||
</Callout>
|
||||
|
||||
## What Gets Migrated
|
||||
@@ -339,11 +339,11 @@ This error occurs because the database schema is outdated. Run `pnpm db:migrate`
|
||||
## Related Reading
|
||||
|
||||
<Cards>
|
||||
<Card href={'/docs/self-hosting/advanced/auth/migration-internals'} title={'Migration Technical Deep Dive'} />
|
||||
<Card href={'/docs/self-hosting/migration/v2/auth/migration-internals'} title={'Migration Technical Deep Dive'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth'} title={'Authentication Service Configuration'} />
|
||||
<Card href={'/docs/self-hosting/auth'} title={'Authentication Service Configuration'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/environment-variables/auth'} title={'Auth Environment Variables'} />
|
||||
|
||||
<Card href={'/docs/self-hosting/advanced/auth/legacy'} title={'Legacy Authentication (NextAuth & Clerk)'} />
|
||||
<Card href={'/docs/self-hosting/auth/legacy'} title={'Legacy Authentication (NextAuth & Clerk)'} />
|
||||
</Cards>
|
||||
|
||||
@@ -55,7 +55,7 @@ tags:
|
||||
|
||||
1. **配置邮件服务**
|
||||
|
||||
设置邮件服务以支持密码重置功能。参阅 [邮件服务配置](/zh/docs/self-hosting/advanced/auth#邮件服务配置)。
|
||||
设置邮件服务以支持密码重置功能。参阅 [邮件服务配置](/zh/docs/self-hosting/auth#邮件服务配置)。
|
||||
|
||||
2. **更新环境变量**
|
||||
|
||||
@@ -78,7 +78,7 @@ tags:
|
||||
```
|
||||
|
||||
<Callout type={'tip'}>
|
||||
查阅 [身份验证服务配置](/zh/docs/self-hosting/advanced/auth) 了解完整的环境变量和 SSO 提供商配置。
|
||||
查阅 [身份验证服务配置](/zh/docs/self-hosting/auth) 了解完整的环境变量和 SSO 提供商配置。
|
||||
</Callout>
|
||||
|
||||
3. **重新部署 LobeHub**
|
||||
@@ -283,7 +283,7 @@ npx tsx scripts/clerk-to-betterauth/verify.ts
|
||||
迁移完成后,参照 [简单迁移 - 步骤 2](#步骤) 配置 Better Auth 环境变量并重新部署。
|
||||
|
||||
<Callout type={'tip'}>
|
||||
完整的 Better Auth 配置请参阅 [身份验证服务配置](/zh/docs/self-hosting/advanced/auth),包括所有支持的 SSO 提供商和邮件服务配置。
|
||||
完整的 Better Auth 配置请参阅 [身份验证服务配置](/zh/docs/self-hosting/auth),包括所有支持的 SSO 提供商和邮件服务配置。
|
||||
</Callout>
|
||||
|
||||
## 迁移内容对比
|
||||
@@ -333,11 +333,11 @@ npx tsx scripts/clerk-to-betterauth/verify.ts
|
||||
## 相关阅读
|
||||
|
||||
<Cards>
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/migration-internals'} title={'迁移技术原理'} />
|
||||
<Card href={'/zh/docs/self-hosting/migration/v2/auth/migration-internals'} title={'迁移技术原理'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth'} title={'身份验证服务配置'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth'} title={'身份验证服务配置'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/environment-variables/auth'} title={'认证相关环境变量'} />
|
||||
|
||||
<Card href={'/zh/docs/self-hosting/advanced/auth/legacy'} title={'旧版身份验证(NextAuth 和 Clerk)'} />
|
||||
<Card href={'/zh/docs/self-hosting/auth/legacy'} title={'旧版身份验证(NextAuth 和 Clerk)'} />
|
||||
</Cards>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user