Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
579 changes: 579 additions & 0 deletions .cursor/rules/testing-guide/zustand-store-action-test.mdc

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
bun-version: 1.2.23

- name: Install dependencies (bun)
run: bun install
Expand Down
2 changes: 0 additions & 2 deletions .npmrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ resolution-mode=highest
ignore-workspace-root-check=true
enable-pre-post-scripts=true

# Load dotenv files for all the npm scripts
node-options="--require dotenv-expand/config"

public-hoist-pattern[]=*@umijs/lint*
public-hoist-pattern[]=*changelog*
Expand Down
58 changes: 58 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,64 @@

# Changelog

### [Version 1.137.8](https://github.com/lobehub/lobe-chat/compare/v1.137.7...v1.137.8)

<sup>Released on **2025-10-15**</sup>

#### 🐛 Bug Fixes

- **misc**: Fix duplicate tools id issue and fix link dialog issue.

#### 💄 Styles

- **misc**: Add region support for Vertex AI provider.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### What's fixed

- **misc**: Fix duplicate tools id issue and fix link dialog issue, closes [#9731](https://github.com/lobehub/lobe-chat/issues/9731) ([0a8c80d](https://github.com/lobehub/lobe-chat/commit/0a8c80d))

#### Styles

- **misc**: Add region support for Vertex AI provider, closes [#9720](https://github.com/lobehub/lobe-chat/issues/9720) ([d17b50c](https://github.com/lobehub/lobe-chat/commit/d17b50c))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>

### [Version 1.137.7](https://github.com/lobehub/lobe-chat/compare/v1.137.6...v1.137.7)

<sup>Released on **2025-10-15**</sup>

#### 💄 Styles

- **misc**: Use different favicon.ico in dev mode.

<br/>

<details>
<summary><kbd>Improvements and Fixes</kbd></summary>

#### Styles

- **misc**: Use different favicon.ico in dev mode, closes [#9723](https://github.com/lobehub/lobe-chat/issues/9723) ([2f7317b](https://github.com/lobehub/lobe-chat/commit/2f7317b))

</details>

<div align="right">

[![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)

</div>

### [Version 1.137.6](https://github.com/lobehub/lobe-chat/compare/v1.137.5...v1.137.6)

<sup>Released on **2025-10-14**</sup>
Expand Down
15 changes: 15 additions & 0 deletions changelog/v1.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,19 @@
[
{
"children": {
"fixes": ["Fix duplicate tools id issue and fix link dialog issue."],
"improvements": ["Add region support for Vertex AI provider."]
},
"date": "2025-10-15",
"version": "1.137.8"
},
{
"children": {
"improvements": ["Use different favicon.ico in dev mode."]
},
"date": "2025-10-15",
"version": "1.137.7"
},
{
"children": {
"fixes": ["Update Claude workflows to use oauth token, vertext ai create image."]
Expand Down
5 changes: 5 additions & 0 deletions locales/en-US/modelProvider.json
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,11 @@
"desc": "Enter your Vertex AI Keys",
"placeholder": "{ \"type\": \"service_account\", \"project_id\": \"xxx\", \"private_key_id\": ... }",
"title": "Vertex AI Keys"
},
"region": {
"desc": "Select the region for Vertex AI service. Some models like Gemini 2.5 are only available in specific regions (e.g., global)",
"placeholder": "Select region",
"title": "Vertex AI Region"
}
},
"zeroone": {
Expand Down
5 changes: 5 additions & 0 deletions locales/zh-CN/modelProvider.json
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,11 @@
"desc": "填入你的 Vertex Ai Keys",
"placeholder": "{ \"type\": \"service_account\", \"project_id\": \"xxx\", \"private_key_id\": ... }",
"title": "Vertex AI Keys"
},
"region": {
"desc": "选择 Vertex AI 服务的区域。某些模型如 Gemini 2.5 仅在特定区域可用(如 global)",
"placeholder": "选择区域",
"title": "Vertex AI 区域"
}
},
"zeroone": {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@lobehub/chat",
"version": "1.137.6",
"version": "1.137.8",
"description": "Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.",
"keywords": [
"framework",
Expand Down
6 changes: 3 additions & 3 deletions packages/context-engine/src/tools/ToolsEngine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export class ToolsEngine {
const { toolIds = [], model, provider, context } = params;

// Merge user-provided tool IDs with default tool IDs
const allToolIds = [...toolIds, ...this.defaultToolIds];
const allToolIds = [...new Set([...toolIds, ...this.defaultToolIds])];

log(
'Generating tools for model=%s, provider=%s, pluginIds=%o (includes %d default tools)',
Expand Down Expand Up @@ -96,8 +96,8 @@ export class ToolsEngine {
generateToolsDetailed(params: GenerateToolsParams): ToolsGenerationResult {
const { toolIds = [], model, provider, context } = params;

// Merge user-provided tool IDs with default tool IDs
const allToolIds = [...toolIds, ...this.defaultToolIds];
// Merge user-provided tool IDs with default tool IDs and deduplicate
const allToolIds = [...new Set([...toolIds, ...this.defaultToolIds])];

log(
'Generating detailed tools for model=%s, provider=%s, pluginIds=%o (includes %d default tools)',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,9 @@ describe('ToolNameResolver', () => {
it('should handle web browsing tools correctly', () => {
const result = resolver.generate('lobe-web-browsing', 'search', 'builtin');
expect(result).toBe('lobe-web-browsing____search____builtin');

const result2 = resolver.generate('lobe-web-browsing', 'crawlSinglePage', 'builtin');
expect(result2).toBe('lobe-web-browsing____crawlSinglePage____builtin');
});

it('should handle plugin tools correctly', () => {
Expand Down
79 changes: 79 additions & 0 deletions packages/context-engine/src/tools/__tests__/ToolsEngine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -900,4 +900,83 @@ describe('ToolsEngine', () => {
});
});
});

describe('deduplication', () => {
it('should deduplicate tool IDs in toolIds array', () => {
const engine = new ToolsEngine({
manifestSchemas: [mockWebBrowsingManifest, mockDalleManifest],
enableChecker: () => true,
functionCallChecker: () => true,
});

const result = engine.generateTools({
toolIds: ['lobe-web-browsing', 'lobe-web-browsing', 'dalle'],
model: 'gpt-4',
provider: 'openai',
});

// Should only generate 2 tools, not 3
expect(result).toHaveLength(2);
expect(result![0].function.name).toBe('lobe-web-browsing____search____builtin');
expect(result![1].function.name).toBe('dalle____generateImage____builtin');
});

it('should deduplicate between toolIds and defaultToolIds', () => {
const engine = new ToolsEngine({
manifestSchemas: [mockWebBrowsingManifest, mockDalleManifest],
defaultToolIds: ['lobe-web-browsing'],
enableChecker: () => true,
functionCallChecker: () => true,
});

const result = engine.generateTools({
toolIds: ['lobe-web-browsing', 'dalle'],
model: 'gpt-4',
provider: 'openai',
});

// Should only generate 2 tools (lobe-web-browsing should appear once)
expect(result).toHaveLength(2);
expect(result![0].function.name).toBe('lobe-web-browsing____search____builtin');
expect(result![1].function.name).toBe('dalle____generateImage____builtin');
});

it('should deduplicate in generateToolsDetailed', () => {
const engine = new ToolsEngine({
manifestSchemas: [mockWebBrowsingManifest, mockDalleManifest],
defaultToolIds: ['dalle'],
enableChecker: () => true,
functionCallChecker: () => true,
});

const result = engine.generateToolsDetailed({
toolIds: ['lobe-web-browsing', 'dalle', 'dalle'],
model: 'gpt-4',
provider: 'openai',
});

// Should only generate 2 unique tools
expect(result.tools).toHaveLength(2);
expect(result.enabledToolIds).toEqual(['lobe-web-browsing', 'dalle']);
expect(result.filteredTools).toEqual([]);
});

it('should handle complex deduplication scenarios', () => {
const engine = new ToolsEngine({
manifestSchemas: [mockWebBrowsingManifest, mockDalleManifest],
defaultToolIds: ['lobe-web-browsing', 'dalle'],
enableChecker: () => true,
functionCallChecker: () => true,
});

const result = engine.generateTools({
toolIds: ['dalle', 'lobe-web-browsing', 'dalle', 'lobe-web-browsing'],
model: 'gpt-4',
provider: 'openai',
});

// Should only generate 2 unique tools despite multiple duplicates
expect(result).toHaveLength(2);
});
});
});
2 changes: 2 additions & 0 deletions packages/types/src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ export interface ClientSecretPayload {

cloudflareBaseURLOrAccountID?: string;

vertexAIRegion?: string;

/**
* user id
* in client db mode it's a uuid
Expand Down
7 changes: 6 additions & 1 deletion packages/types/src/user/settings/keyVaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ export interface AWSBedrockKeyVault {
sessionToken?: string;
}

export interface VertexAIKeyVault {
apiKey?: string;
region?: string;
}

export interface CloudflareKeyVault {
apiKey?: string;
baseURLOrAccountID?: string;
Expand Down Expand Up @@ -96,7 +101,7 @@ export interface UserKeyVaults extends SearchEngineKeyVaults {
upstage?: OpenAICompatibleKeyVault;
v0?: OpenAICompatibleKeyVault;
vercelaigateway?: OpenAICompatibleKeyVault;
vertexai?: OpenAICompatibleKeyVault;
vertexai?: VertexAIKeyVault;
vllm?: OpenAICompatibleKeyVault;
volcengine?: OpenAICompatibleKeyVault;
wenxin?: OpenAICompatibleKeyVault;
Expand Down
Binary file added public/favicon-32x32-dev.ico
Binary file not shown.
Binary file added public/favicon-dev.ico
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use client';

import { Markdown } from '@lobehub/ui';
import { Markdown, Select } from '@lobehub/ui';
import { createStyles } from 'antd-style';
import { useTranslation } from 'react-i18next';

Expand Down Expand Up @@ -28,6 +28,48 @@ const useStyles = createStyles(({ css, token }) => ({

const providerKey: GlobalLLMProviderKey = 'vertexai';

const VERTEX_AI_REGIONS: string[] = [
'global',
'us-central1',
'us-east1',
'us-east4',
'us-west1',
'us-west2',
'us-west3',
'us-west4',
'us-south1',
'northamerica-northeast1',
'northamerica-northeast2',
'southamerica-east1',
'southamerica-west1',
'europe-central2',
'europe-north1',
'europe-southwest1',
'europe-west1',
'europe-west2',
'europe-west3',
'europe-west4',
'europe-west6',
'europe-west8',
'europe-west9',
'europe-west10',
'europe-west12',
'me-central1',
'me-central2',
'me-west1',
'africa-south1',
'asia-east1',
'asia-east2',
'asia-northeast1',
'asia-northeast2',
'asia-northeast3',
'asia-south1',
'asia-southeast1',
'asia-southeast2',
'australia-southeast1',
'australia-southeast2',
];

// Same as OpenAIProvider, but replace API Key with HuggingFace Access Token
const useProviderCard = (): ProviderItem => {
const { t } = useTranslation('modelProvider');
Expand All @@ -54,6 +96,27 @@ const useProviderCard = (): ProviderItem => {
label: t('vertexai.apiKey.title'),
name: [KeyVaultsConfigKey, LLMProviderApiTokenKey],
},
{
children: isLoading ? (
<SkeletonInput />
) : (
<Select
allowClear
options={VERTEX_AI_REGIONS.map((region) => ({
label: region,
value: region,
}))}
placeholder={t('vertexai.region.placeholder')}
/>
),
desc: (
<Markdown className={styles.markdown} fontSize={12} variant={'chat'}>
{t('vertexai.region.desc')}
</Markdown>
),
label: t('vertexai.region.title'),
name: [KeyVaultsConfigKey, 'region'],
},
],
};
};
Expand Down
6 changes: 4 additions & 2 deletions src/app/[variants]/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import { translation } from '@/server/translation';
import { DynamicLayoutProps } from '@/types/next';
import { RouteVariants } from '@/utils/server/routeVariants';

const isDev = process.env.NODE_ENV === 'development';

export const generateMetadata = async (props: DynamicLayoutProps) => {
const locale = await RouteVariants.getLocale(props);
const { t } = await translation('metadata', locale);
Expand All @@ -23,8 +25,8 @@ export const generateMetadata = async (props: DynamicLayoutProps) => {
? BRANDING_LOGO_URL
: {
apple: '/apple-touch-icon.png?v=1',
icon: '/favicon.ico?v=1',
shortcut: '/favicon-32x32.ico?v=1',
icon: isDev ? '/favicon-dev.ico' : '/favicon.ico?v=1',
shortcut: isDev ? '/favicon-32x32-dev.ico' : '/favicon-32x32.ico?v=1',
},
manifest: '/manifest.json',
metadataBase: new URL(OFFICIAL_URL),
Expand Down
Loading
Loading