mirror of
https://github.com/lobehub/lobehub.git
synced 2026-03-26 13:19:34 +07:00
💄 style: support default config for system agent and pre-merge some desktop code (#7296)
* refactor * update * improve scripts * fix changelog issue * improve system agent config * fix tests * update scripts * update ollama models * Update ollama.ts
This commit is contained in:
151
.github/scripts/pr-comment.js
vendored
151
.github/scripts/pr-comment.js
vendored
@@ -1,59 +1,69 @@
|
||||
/**
|
||||
* Generate PR comment with download links for desktop builds
|
||||
* and handle comment creation/update logic
|
||||
*/
|
||||
module.exports = async ({ github, context, releaseUrl, version, tag }) => {
|
||||
try {
|
||||
// Get release assets to create download links
|
||||
const release = await github.rest.repos.getReleaseByTag({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
tag,
|
||||
});
|
||||
// 用于识别构建评论的标识符
|
||||
const COMMENT_IDENTIFIER = '<!-- DESKTOP-BUILD-COMMENT -->';
|
||||
|
||||
// Organize assets by platform
|
||||
const macAssets = release.data.assets.filter(
|
||||
(asset) =>
|
||||
((asset.name.includes('.dmg') || asset.name.includes('.zip')) &&
|
||||
!asset.name.includes('.blockmap')) ||
|
||||
(asset.name.includes('latest-mac') && asset.name.endsWith('.yml')),
|
||||
);
|
||||
/**
|
||||
* 生成评论内容
|
||||
*/
|
||||
const generateCommentBody = async () => {
|
||||
try {
|
||||
// Get release assets to create download links
|
||||
const release = await github.rest.repos.getReleaseByTag({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
tag,
|
||||
});
|
||||
|
||||
const winAssets = release.data.assets.filter(
|
||||
(asset) =>
|
||||
(asset.name.includes('.exe') && !asset.name.includes('.blockmap')) ||
|
||||
(asset.name.includes('latest-win') && asset.name.endsWith('.yml')),
|
||||
);
|
||||
// Organize assets by platform
|
||||
const macAssets = release.data.assets.filter(
|
||||
(asset) =>
|
||||
((asset.name.includes('.dmg') || asset.name.includes('.zip')) &&
|
||||
!asset.name.includes('.blockmap')) ||
|
||||
(asset.name.includes('latest-mac') && asset.name.endsWith('.yml')),
|
||||
);
|
||||
|
||||
const linuxAssets = release.data.assets.filter(
|
||||
(asset) =>
|
||||
(asset.name.includes('.AppImage') && !asset.name.includes('.blockmap')) ||
|
||||
(asset.name.includes('latest-linux') && asset.name.endsWith('.yml')),
|
||||
);
|
||||
const winAssets = release.data.assets.filter(
|
||||
(asset) =>
|
||||
(asset.name.includes('.exe') && !asset.name.includes('.blockmap')) ||
|
||||
(asset.name.includes('latest-win') && asset.name.endsWith('.yml')),
|
||||
);
|
||||
|
||||
// Generate combined download table
|
||||
let assetTable = '| Platform | File | Size |\n| --- | --- | --- |\n';
|
||||
const linuxAssets = release.data.assets.filter(
|
||||
(asset) =>
|
||||
(asset.name.includes('.AppImage') && !asset.name.includes('.blockmap')) ||
|
||||
(asset.name.includes('latest-linux') && asset.name.endsWith('.yml')),
|
||||
);
|
||||
|
||||
// Add macOS assets
|
||||
macAssets.forEach((asset) => {
|
||||
const sizeInMB = (asset.size / (1024 * 1024)).toFixed(2);
|
||||
assetTable += `| macOS | [${asset.name}](${asset.browser_download_url}) | ${sizeInMB} MB |\n`;
|
||||
});
|
||||
// Generate combined download table
|
||||
let assetTable = '| Platform | File | Size |\n| --- | --- | --- |\n';
|
||||
|
||||
// Add Windows assets
|
||||
winAssets.forEach((asset) => {
|
||||
const sizeInMB = (asset.size / (1024 * 1024)).toFixed(2);
|
||||
assetTable += `| Windows | [${asset.name}](${asset.browser_download_url}) | ${sizeInMB} MB |\n`;
|
||||
});
|
||||
// Add macOS assets
|
||||
macAssets.forEach((asset) => {
|
||||
const sizeInMB = (asset.size / (1024 * 1024)).toFixed(2);
|
||||
assetTable += `| macOS | [${asset.name}](${asset.browser_download_url}) | ${sizeInMB} MB |\n`;
|
||||
});
|
||||
|
||||
// Add Linux assets
|
||||
linuxAssets.forEach((asset) => {
|
||||
const sizeInMB = (asset.size / (1024 * 1024)).toFixed(2);
|
||||
assetTable += `| Linux | [${asset.name}](${asset.browser_download_url}) | ${sizeInMB} MB |\n`;
|
||||
});
|
||||
// Add Windows assets
|
||||
winAssets.forEach((asset) => {
|
||||
const sizeInMB = (asset.size / (1024 * 1024)).toFixed(2);
|
||||
assetTable += `| Windows | [${asset.name}](${asset.browser_download_url}) | ${sizeInMB} MB |\n`;
|
||||
});
|
||||
|
||||
return `### 🚀 Desktop App Build Completed!
|
||||
// Add Linux assets
|
||||
linuxAssets.forEach((asset) => {
|
||||
const sizeInMB = (asset.size / (1024 * 1024)).toFixed(2);
|
||||
assetTable += `| Linux | [${asset.name}](${asset.browser_download_url}) | ${sizeInMB} MB |\n`;
|
||||
});
|
||||
|
||||
return `${COMMENT_IDENTIFIER}
|
||||
### 🚀 Desktop App Build Completed!
|
||||
|
||||
**Version**: \`${version}\`
|
||||
**Build Time**: \`${new Date().toISOString()}\`
|
||||
|
||||
📦 [View All Build Artifacts](${releaseUrl})
|
||||
|
||||
@@ -65,17 +75,62 @@ ${assetTable}
|
||||
> [!Warning]
|
||||
>
|
||||
> Note: This is a temporary build for testing purposes only.`;
|
||||
} catch (error) {
|
||||
console.error('Error generating PR comment:', error);
|
||||
// Fallback to a simple comment if error occurs
|
||||
return `
|
||||
} catch (error) {
|
||||
console.error('Error generating PR comment:', error);
|
||||
// Fallback to a simple comment if error occurs
|
||||
return `${COMMENT_IDENTIFIER}
|
||||
### 🚀 Desktop App Build Completed!
|
||||
|
||||
**Version**: \`${version}\`
|
||||
**Build Time**: \`${new Date().toISOString()}\`
|
||||
|
||||
## 📦 [View All Build Artifacts](${releaseUrl})
|
||||
|
||||
> Note: This is a temporary build for testing purposes only.
|
||||
`;
|
||||
}
|
||||
`;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 查找并更新或创建PR评论
|
||||
*/
|
||||
const updateOrCreateComment = async () => {
|
||||
// 生成评论内容
|
||||
const body = await generateCommentBody();
|
||||
|
||||
// 查找我们之前可能创建的评论
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
});
|
||||
|
||||
// 查找包含我们标识符的评论
|
||||
const buildComment = comments.find((comment) => comment.body.includes(COMMENT_IDENTIFIER));
|
||||
|
||||
if (buildComment) {
|
||||
// 如果找到现有评论,则更新它
|
||||
await github.rest.issues.updateComment({
|
||||
comment_id: buildComment.id,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: body,
|
||||
});
|
||||
console.log(`已更新现有评论 ID: ${buildComment.id}`);
|
||||
return { updated: true, id: buildComment.id };
|
||||
} else {
|
||||
// 如果没有找到现有评论,则创建新评论
|
||||
const result = await github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: body,
|
||||
});
|
||||
console.log(`已创建新评论 ID: ${result.data.id}`);
|
||||
return { updated: false, id: result.data.id };
|
||||
}
|
||||
};
|
||||
|
||||
// 执行评论更新或创建
|
||||
return await updateOrCreateComment();
|
||||
};
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -68,4 +68,4 @@ public/swe-worker*
|
||||
*.patch
|
||||
*.pdf
|
||||
vertex-ai-key.json
|
||||
.pnpm-store
|
||||
.pnpm-store
|
||||
|
||||
@@ -71,6 +71,37 @@ Further reading:
|
||||
|
||||
- [\[RFC\] 022 - Default Assistant Parameters Configuration via Environment Variables](https://github.com/lobehub/lobe-chat/discussions/913)
|
||||
|
||||
### `SYSTEM_AGENT`
|
||||
|
||||
- Type: Optional
|
||||
- Description: Used to configure models and providers for LobeChat system agents (such as topic generation, translation, etc.).
|
||||
- Default value: `-`
|
||||
- Example: `default=ollama/deepseek-v3` or `topic=openai/gpt-4,translation=anthropic/claude-1`
|
||||
|
||||
The `SYSTEM_AGENT` environment variable supports two configuration methods:
|
||||
|
||||
1. Use `default=provider/model` to set the same default configuration for all system agents
|
||||
2. Configure specific system agents individually using the format `agent-name=provider/model`
|
||||
|
||||
Configuration details:
|
||||
|
||||
| Config Type | Format | Explanation |
|
||||
| ------------------- | ----------------------------------------------- | ---------------------------------------------------------------------- |
|
||||
| Default setting | `default=ollama/deepseek-v3` | Set deepseek-v3 from ollama as the default model for all system agents |
|
||||
| Specific setting | `topic=openai/gpt-4` | Set a specific provider and model for topic generation |
|
||||
| Mixed configuration | `default=ollama/deepseek-v3,topic=openai/gpt-4` | First set default values for all agents, then override specific agents |
|
||||
|
||||
Available system agents and their functions:
|
||||
|
||||
| System Agent | Key Name | Function Description |
|
||||
| ------------------- | ----------------- | --------------------------------------------------------------------------------------------------- |
|
||||
| Topic Generation | `topic` | Automatically generates topic names and summaries based on chat content |
|
||||
| Translation | `translation` | Handles text translation between multiple languages |
|
||||
| Metadata Generation | `agentMeta` | Generates descriptive information and metadata for assistants |
|
||||
| History Compression | `historyCompress` | Compresses and organizes history for long conversations, optimizing context management |
|
||||
| Query Rewrite | `queryRewrite` | Rewrites follow-up questions as standalone questions with context, improving conversation coherence |
|
||||
| Thread Management | `thread` | Handles the creation and management of conversation threads |
|
||||
|
||||
### `FEATURE_FLAGS`
|
||||
|
||||
- Type: Optional
|
||||
|
||||
@@ -67,6 +67,37 @@ LobeChat 在部署时提供了一些额外的配置项,你可以使用环境
|
||||
|
||||
- [\[RFC\] 022 - 环境变量配置默认助手参数](https://github.com/lobehub/lobe-chat/discussions/913)
|
||||
|
||||
### `SYSTEM_AGENT`
|
||||
|
||||
- 类型:可选
|
||||
- 描述:用于配置 LobeChat 系统助手(如主题生成、翻译等功能)的模型和供应商。
|
||||
- 默认值:`-`
|
||||
- 示例:`default=ollama/deepseek-v3` 或 `topic=openai/gpt-4,translation=anthropic/claude-1`
|
||||
|
||||
`SYSTEM_AGENT` 环境变量支持两种配置方式:
|
||||
|
||||
1. 使用 `default=供应商/模型` 为所有系统助手设置相同的默认配置
|
||||
2. 针对特定的系统助手进行单独配置,格式为 `助手名称=供应商/模型`
|
||||
|
||||
配置项说明:
|
||||
|
||||
| 配置项 | 格式 | 解释 |
|
||||
| ---- | ----------------------------------------------- | ----------------------------------- |
|
||||
| 默认设置 | `default=ollama/deepseek-v3` | 为所有系统助手设置默认模型为 ollama 的 deepseek-v3 |
|
||||
| 特定设置 | `topic=openai/gpt-4` | 为主题生成设置特定的供应商和模型 |
|
||||
| 混合配置 | `default=ollama/deepseek-v3,topic=openai/gpt-4` | 先为所有助手设置默认值,然后针对特定助手进行覆盖 |
|
||||
|
||||
可配置的系统助手及其作用:
|
||||
|
||||
| 系统助手 | 键名 | 作用描述 |
|
||||
| ------- | ----------------- | --------------------------- |
|
||||
| 主题生成 | `topic` | 根据聊天内容自动生成主题名称和摘要 |
|
||||
| 翻译 | `translation` | 文本翻译使用的助手 |
|
||||
| 元数据生成 | `agentMeta` | 为助手生成描述性信息和元数据 |
|
||||
| 历史记录压缩 | `historyCompress` | 压缩和整理长对话的历史记录,优化上下文管理 |
|
||||
| 知识库查询重写 | `queryRewrite` | 将后续问题改写为包含上下文的独立问题,提升对话的连贯性 |
|
||||
| 分支对话 | `thread` | 自定生成分支对话的标题 |
|
||||
|
||||
### `FEATURE_FLAGS`
|
||||
|
||||
- 类型:可选
|
||||
|
||||
@@ -12,6 +12,7 @@ dotenv.config();
|
||||
|
||||
const migrationsFolder = join(__dirname, '../../src/database/migrations');
|
||||
|
||||
const isDesktop = process.env.NEXT_PUBLIC_IS_DESKTOP_APP === '1';
|
||||
const runMigrations = async () => {
|
||||
if (process.env.DATABASE_DRIVER === 'node') {
|
||||
await nodeMigrate(serverDB, { migrationsFolder });
|
||||
@@ -27,7 +28,7 @@ const runMigrations = async () => {
|
||||
let connectionString = process.env.DATABASE_URL;
|
||||
|
||||
// only migrate database if the connection string is available
|
||||
if (connectionString) {
|
||||
if (!isDesktop && connectionString) {
|
||||
// eslint-disable-next-line unicorn/prefer-top-level-await
|
||||
runMigrations().catch((err) => {
|
||||
console.error('❌ Database migrate failed:', err);
|
||||
@@ -44,5 +45,5 @@ if (connectionString) {
|
||||
process.exit(1);
|
||||
});
|
||||
} else {
|
||||
console.log('🟢 not find database env, migration skipped');
|
||||
console.log('🟢 not find database env or in desktop mode, migration skipped');
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { z } from 'zod';
|
||||
import { FormInput, FormPassword } from '@/components/FormInput';
|
||||
import { FORM_STYLE } from '@/const/layoutTokens';
|
||||
import { AES_GCM_URL, BASE_PROVIDER_DOC_URL } from '@/const/url';
|
||||
import { isServerMode } from '@/const/version';
|
||||
import { isDesktop, isServerMode } from '@/const/version';
|
||||
import { aiProviderSelectors, useAiInfraStore } from '@/store/aiInfra';
|
||||
import {
|
||||
AiProviderDetailItem,
|
||||
@@ -244,12 +244,14 @@ const ProviderConfig = memo<ProviderConfigProps>(
|
||||
|
||||
/*
|
||||
* Conditions to show Client Fetch Switch
|
||||
* 0. is not desktop app
|
||||
* 1. provider is not disabled browser request
|
||||
* 2. provider show browser request by default
|
||||
* 3. Provider allow to edit endpoint and the value of endpoint is not empty
|
||||
* 4. There is an apikey provided by user
|
||||
*/
|
||||
const showClientFetch =
|
||||
!isDesktop &&
|
||||
!disableBrowserRequest &&
|
||||
(defaultShowBrowserRequest ||
|
||||
(showEndpoint && isProviderEndpointNotEmpty) ||
|
||||
|
||||
@@ -30,44 +30,43 @@ const DevTools = memo(() => {
|
||||
const [tab, setTab] = useState<string>(items[0].key);
|
||||
|
||||
return (
|
||||
<Flexbox
|
||||
height={'100%'}
|
||||
horizontal
|
||||
style={{ background: theme.colorBgLayout, overflow: 'hidden', position: 'relative' }}
|
||||
width={'100%'}
|
||||
>
|
||||
<SideNav
|
||||
bottomActions={[]}
|
||||
style={{
|
||||
background: 'transparent',
|
||||
paddingBlock: 32,
|
||||
width: 48,
|
||||
}}
|
||||
topActions={items.map((item) => (
|
||||
<ActionIcon
|
||||
active={tab === item.key}
|
||||
key={item.key}
|
||||
onClick={() => setTab(item.key)}
|
||||
placement={'right'}
|
||||
title={item.key}
|
||||
>
|
||||
{item.icon}
|
||||
</ActionIcon>
|
||||
))}
|
||||
/>
|
||||
<Flexbox height={'100%'} style={{ overflow: 'hidden', position: 'relative' }} width={'100%'}>
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
className={cx(`panel-drag-handle`, styles.header, electronStylish.draggable)}
|
||||
horizontal
|
||||
justify={'center'}
|
||||
>
|
||||
<Flexbox align={'baseline'} gap={6} horizontal>
|
||||
<b>{BRANDING_NAME} Dev Tools</b>
|
||||
<span style={{ color: theme.colorTextDescription }}>/</span>
|
||||
<span style={{ color: theme.colorTextDescription }}>{tab}</span>
|
||||
</Flexbox>
|
||||
<Flexbox height={'100%'} style={{ overflow: 'hidden', position: 'relative' }} width={'100%'}>
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
className={cx(`panel-drag-handle`, styles.header, electronStylish.draggable)}
|
||||
horizontal
|
||||
justify={'center'}
|
||||
>
|
||||
<Flexbox align={'baseline'} gap={6} horizontal>
|
||||
<b>{BRANDING_NAME} Dev Tools</b>
|
||||
<span style={{ color: theme.colorTextDescription }}>/</span>
|
||||
<span style={{ color: theme.colorTextDescription }}>{tab}</span>
|
||||
</Flexbox>
|
||||
</Flexbox>
|
||||
<Flexbox
|
||||
height={'100%'}
|
||||
horizontal
|
||||
style={{ background: theme.colorBgLayout, overflow: 'hidden', position: 'relative' }}
|
||||
width={'100%'}
|
||||
>
|
||||
<SideNav
|
||||
bottomActions={[]}
|
||||
style={{
|
||||
background: 'transparent',
|
||||
width: 48,
|
||||
}}
|
||||
topActions={items.map((item) => (
|
||||
<ActionIcon
|
||||
active={tab === item.key}
|
||||
key={item.key}
|
||||
onClick={() => setTab(item.key)}
|
||||
placement={'right'}
|
||||
title={item.key}
|
||||
>
|
||||
{item.icon}
|
||||
</ActionIcon>
|
||||
))}
|
||||
/>
|
||||
{items.map((item) => (
|
||||
<Flexbox
|
||||
flex={1}
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import { AIChatModelCard } from '@/types/aiModel';
|
||||
|
||||
const ollamaChatModels: AIChatModelCard[] = [
|
||||
{
|
||||
contextWindowTokens: 65_536,
|
||||
description:
|
||||
'DeepSeek-V3 是一个强大的专家混合(MoE)语言模型,总参数量为 671B,每个 Token 激活 37B 参数。该模型采用多头潜在注意力(MLA)和 DeepSeekMoE 架构,实现了高效推理和经济训练,并在前代 DeepSeek-V3 的基础上显著提升了性能。',
|
||||
displayName: 'DeepSeek V3',
|
||||
enabled: true,
|
||||
id: 'deepseek-v3',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
reasoning: true,
|
||||
@@ -22,6 +13,14 @@ const ollamaChatModels: AIChatModelCard[] = [
|
||||
id: 'deepseek-r1',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
contextWindowTokens: 65_536,
|
||||
description:
|
||||
'DeepSeek-V3 是一个强大的专家混合(MoE)语言模型,总参数量为 671B,每个 Token 激活 37B 参数。该模型采用多头潜在注意力(MLA)和 DeepSeekMoE 架构,实现了高效推理和经济训练,并在前代 DeepSeek-V3 的基础上显著提升了性能。',
|
||||
displayName: 'DeepSeek V3 671B',
|
||||
id: 'deepseek-v3',
|
||||
type: 'chat',
|
||||
},
|
||||
{
|
||||
abilities: {
|
||||
functionCall: true,
|
||||
@@ -87,8 +86,10 @@ const ollamaChatModels: AIChatModelCard[] = [
|
||||
reasoning: true,
|
||||
},
|
||||
contextWindowTokens: 128_000,
|
||||
description: 'QwQ 是一个实验研究模型,专注于提高 AI 推理能力。',
|
||||
description:
|
||||
'QwQ 是 Qwen 系列的推理模型。与传统的指令调优模型相比,QwQ 具备思考和推理的能力,能够在下游任务中,尤其是困难问题上,显著提升性能。QwQ-32B 是中型推理模型,能够在与最先进的推理模型(如 DeepSeek-R1、o1-mini)竞争时取得可观的表现。',
|
||||
displayName: 'QwQ 32B',
|
||||
enabled: true,
|
||||
id: 'qwq',
|
||||
releasedAt: '2024-11-28',
|
||||
type: 'chat',
|
||||
@@ -114,6 +115,7 @@ const ollamaChatModels: AIChatModelCard[] = [
|
||||
contextWindowTokens: 128_000,
|
||||
description: 'Qwen2.5 是阿里巴巴的新一代大规模语言模型,以优异的性能支持多元化的应用需求。',
|
||||
displayName: 'Qwen2.5 7B',
|
||||
enabled: true,
|
||||
id: 'qwen2.5',
|
||||
type: 'chat',
|
||||
},
|
||||
|
||||
@@ -129,7 +129,7 @@ const SchemaPanel = ({ onTableSelect, selectedTable }: SchemaPanelProps) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<DraggablePanel placement={'left'}>
|
||||
<DraggablePanel minWidth={264} placement={'left'}>
|
||||
<Flexbox height={'100%'} style={{ overflow: 'hidden', position: 'relative' }}>
|
||||
<Flexbox
|
||||
align={'center'}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useNewVersion } from './useNewVersion';
|
||||
const useStyles = createStyles(({ css }) => {
|
||||
return {
|
||||
popover: css`
|
||||
inset-block-start: ${isDesktop ? 24 : 8}px !important;
|
||||
inset-block-start: ${isDesktop ? 32 : 8}px !important;
|
||||
inset-inline-start: 8px !important;
|
||||
`,
|
||||
};
|
||||
|
||||
@@ -63,6 +63,7 @@ vi.mock('../DataStatistics', () => ({
|
||||
|
||||
vi.mock('@/const/version', () => ({
|
||||
isDeprecatedEdition: false,
|
||||
isDesktop: false,
|
||||
}));
|
||||
|
||||
// 定义一个变量来存储 enableAuth 的值
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
* @link https://trpc.io/docs/v11/router
|
||||
* @link https://trpc.io/docs/v11/procedures
|
||||
*/
|
||||
import { DESKTOP_USER_ID } from '@/const/desktop';
|
||||
import { isDesktop } from '@/const/version';
|
||||
|
||||
import { trpc } from './init';
|
||||
import { jwtPayloadChecker } from './middleware/jwtPayload';
|
||||
import { userAuth } from './middleware/userAuth';
|
||||
@@ -21,7 +24,11 @@ export const router = trpc.router;
|
||||
* Create an unprotected procedure
|
||||
* @link https://trpc.io/docs/v11/procedures
|
||||
**/
|
||||
export const publicProcedure = trpc.procedure;
|
||||
export const publicProcedure = trpc.procedure.use(({ next }) => {
|
||||
return next({
|
||||
ctx: { userId: isDesktop ? DESKTOP_USER_ID : null },
|
||||
});
|
||||
});
|
||||
|
||||
// procedure that asserts that the user is logged in
|
||||
export const authedProcedure = trpc.procedure.use(userAuth);
|
||||
|
||||
@@ -9,11 +9,10 @@ import { trpc } from '../init';
|
||||
export const userAuth = trpc.middleware(async (opts) => {
|
||||
const { ctx } = opts;
|
||||
|
||||
// 桌面端模式下,跳过默认鉴权逻辑
|
||||
if (isDesktop) {
|
||||
return opts.next({
|
||||
ctx: {
|
||||
userId: DESKTOP_USER_ID,
|
||||
},
|
||||
ctx: { userId: DESKTOP_USER_ID },
|
||||
});
|
||||
}
|
||||
// `ctx.user` is nullable
|
||||
|
||||
@@ -92,4 +92,60 @@ describe('parseSystemAgent', () => {
|
||||
|
||||
expect(parseSystemAgent(envValue)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should apply default setting to all system agents when default is specified', () => {
|
||||
const envValue = 'default=ollama/deepseek-v3';
|
||||
|
||||
const result = parseSystemAgent(envValue);
|
||||
|
||||
expect(result.topic).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.translation).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.agentMeta).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.historyCompress).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.thread).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.queryRewrite).toEqual({
|
||||
provider: 'ollama',
|
||||
model: 'deepseek-v3',
|
||||
enabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should override default setting with specific settings', () => {
|
||||
const envValue = 'default=ollama/deepseek-v3,topic=openai/gpt-4';
|
||||
|
||||
const result = parseSystemAgent(envValue);
|
||||
|
||||
expect(result.topic).toEqual({ provider: 'openai', model: 'gpt-4' });
|
||||
|
||||
expect(result.translation).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.agentMeta).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.historyCompress).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.thread).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.queryRewrite).toEqual({
|
||||
provider: 'ollama',
|
||||
model: 'deepseek-v3',
|
||||
enabled: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should properly handle priority when topic appears before default in the string', () => {
|
||||
// 即使 topic 在 default 之前出现,topic 的设置仍然应该优先
|
||||
const envValue = 'topic=openai/gpt-4,default=ollama/deepseek-v3';
|
||||
|
||||
const result = parseSystemAgent(envValue);
|
||||
|
||||
// topic 应该保持自己的设置而不被 default 覆盖
|
||||
expect(result.topic).toEqual({ provider: 'openai', model: 'gpt-4' });
|
||||
|
||||
// 其他系统智能体应该使用默认配置
|
||||
expect(result.translation).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.agentMeta).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.historyCompress).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.thread).toEqual({ provider: 'ollama', model: 'deepseek-v3' });
|
||||
expect(result.queryRewrite).toEqual({
|
||||
provider: 'ollama',
|
||||
model: 'deepseek-v3',
|
||||
enabled: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,6 +13,9 @@ export const parseSystemAgent = (envString: string = ''): Partial<UserSystemAgen
|
||||
|
||||
const pairs = envValue.split(',');
|
||||
|
||||
// 用于存储默认设置,如果有 default=provider/model 的情况
|
||||
let defaultSetting: { model: string; provider: string } | undefined;
|
||||
|
||||
for (const pair of pairs) {
|
||||
const [key, value] = pair.split('=').map((s) => s.trim());
|
||||
|
||||
@@ -24,6 +27,15 @@ export const parseSystemAgent = (envString: string = ''): Partial<UserSystemAgen
|
||||
throw new Error('Missing model or provider value');
|
||||
}
|
||||
|
||||
// 如果是 default 键,保存默认设置
|
||||
if (key === 'default') {
|
||||
defaultSetting = {
|
||||
model: model.trim(),
|
||||
provider: provider.trim(),
|
||||
};
|
||||
continue;
|
||||
}
|
||||
|
||||
if (protectedKeys.includes(key)) {
|
||||
config[key as keyof UserSystemAgentConfig] = {
|
||||
enabled: key === 'queryRewrite' ? true : undefined,
|
||||
@@ -36,5 +48,18 @@ export const parseSystemAgent = (envString: string = ''): Partial<UserSystemAgen
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有默认设置,应用到所有未设置的系统智能体
|
||||
if (defaultSetting) {
|
||||
for (const key of protectedKeys) {
|
||||
if (!config[key as keyof UserSystemAgentConfig]) {
|
||||
config[key as keyof UserSystemAgentConfig] = {
|
||||
enabled: key === 'queryRewrite' ? true : undefined,
|
||||
model: defaultSetting.model,
|
||||
provider: defaultSetting.provider,
|
||||
} as any;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
|
||||
@@ -79,6 +79,7 @@ describe('ChangelogService', () => {
|
||||
describe('getChangelogIndex', () => {
|
||||
it('should fetch and merge changelog data', async () => {
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
json: vi.fn().mockResolvedValue({
|
||||
cloud: [{ id: 'cloud1', date: '2023-01-01', versionRange: ['1.0.0'] }],
|
||||
community: [{ id: 'community1', date: '2023-01-02', versionRange: ['1.1.0'] }],
|
||||
@@ -104,6 +105,7 @@ describe('ChangelogService', () => {
|
||||
it('should return only community items when config type is community', async () => {
|
||||
service.config.type = 'community';
|
||||
const mockResponse = {
|
||||
ok: true,
|
||||
json: vi.fn().mockResolvedValue({
|
||||
cloud: [{ id: 'cloud1', date: '2023-01-01', versionRange: ['1.0.0'] }],
|
||||
community: [{ id: 'community1', date: '2023-01-02', versionRange: ['1.1.0'] }],
|
||||
|
||||
@@ -55,9 +55,13 @@ export class ChangelogService {
|
||||
next: { revalidate: 3600, tags: [FetchCacheTag.Changelog] },
|
||||
});
|
||||
|
||||
const data = await res.json();
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
|
||||
return this.mergeChangelogs(data.cloud, data.community).slice(0, 5);
|
||||
return this.mergeChangelogs(data.cloud, data.community).slice(0, 5);
|
||||
}
|
||||
|
||||
return [];
|
||||
} catch (e) {
|
||||
const cause = (e as Error).cause as { code: string };
|
||||
if (cause?.code.includes('ETIMEDOUT')) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { dispatch } from '@/utils/electron/dispatch';
|
||||
import { dispatch } from '@lobechat/electron-client-ipc';
|
||||
|
||||
class DevtoolsService {
|
||||
async openDevtools(): Promise<void> {
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { DispatchInvoke } from '@lobechat/electron-client-ipc';
|
||||
|
||||
/**
|
||||
* client 端请求 sketch 端 event 数据的方法
|
||||
*/
|
||||
export const dispatch: DispatchInvoke = async (event, ...data) => {
|
||||
if (!window.electronAPI) throw new Error('electronAPI not found');
|
||||
|
||||
return window.electronAPI.invoke(event, ...data);
|
||||
};
|
||||
Reference in New Issue
Block a user