@lobehub/chat 1.96.6 → 1.96.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,56 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.96.8](https://github.com/lobehub/lobe-chat/compare/v1.96.7...v1.96.8)
6
+
7
+ <sup>Released on **2025-06-23**</sup>
8
+
9
+ #### 💄 Styles
10
+
11
+ - **misc**: Optimized Gemini thinkingBudget configuration.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### Styles
19
+
20
+ - **misc**: Optimized Gemini thinkingBudget configuration, closes [#8224](https://github.com/lobehub/lobe-chat/issues/8224) ([03625e8](https://github.com/lobehub/lobe-chat/commit/03625e8))
21
+
22
+ </details>
23
+
24
+ <div align="right">
25
+
26
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
27
+
28
+ </div>
29
+
30
+ ### [Version 1.96.7](https://github.com/lobehub/lobe-chat/compare/v1.96.6...v1.96.7)
31
+
32
+ <sup>Released on **2025-06-23**</sup>
33
+
34
+ #### 💄 Styles
35
+
36
+ - **misc**: Add `blockAds` & `stealth` params for Browserless.
37
+
38
+ <br/>
39
+
40
+ <details>
41
+ <summary><kbd>Improvements and Fixes</kbd></summary>
42
+
43
+ #### Styles
44
+
45
+ - **misc**: Add `blockAds` & `stealth` params for Browserless, closes [#8255](https://github.com/lobehub/lobe-chat/issues/8255) ([2ff3efa](https://github.com/lobehub/lobe-chat/commit/2ff3efa))
46
+
47
+ </details>
48
+
49
+ <div align="right">
50
+
51
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
52
+
53
+ </div>
54
+
5
55
  ### [Version 1.96.6](https://github.com/lobehub/lobe-chat/compare/v1.96.5...v1.96.6)
6
56
 
7
57
  <sup>Released on **2025-06-23**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "improvements": [
5
+ "Optimized Gemini thinkingBudget configuration."
6
+ ]
7
+ },
8
+ "date": "2025-06-23",
9
+ "version": "1.96.8"
10
+ },
11
+ {
12
+ "children": {
13
+ "improvements": [
14
+ "Add blockAds & stealth params for Browserless."
15
+ ]
16
+ },
17
+ "date": "2025-06-23",
18
+ "version": "1.96.7"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "improvements": [
@@ -88,6 +88,40 @@ BROWSERLESS_URL=https://chrome.browserless.io
88
88
 
89
89
  ---
90
90
 
91
+ ## `BROWSERLESS_BLOCK_ADS`
92
+
93
+ Enables ad blocking functionality. When using [Browserless](https://www.browserless.io/) for web scraping, it automatically blocks common ad resources (such as scripts, images, trackers, etc.), improving scraping speed and page clarity.
94
+
95
+ ```env
96
+ BROWSERLESS_BLOCK_ADS=1
97
+ ```
98
+
99
+ > 📌 Supported values:
100
+ >
101
+ > * `1`: Enable ad blocking (recommended);
102
+ > * `0`: Disable ad blocking (default).
103
+
104
+ > ✅ It is recommended to use with `BROWSERLESS_STEALTH_MODE=1` to enhance stealth and scraping success rate.
105
+
106
+ ---
107
+
108
+ ## `BROWSERLESS_STEALTH_MODE`
109
+
110
+ Enables stealth mode. When using [Browserless](https://www.browserless.io/) for web scraping, it applies various anti-detection techniques (such as modifying the user agent, removing webdriver traits, simulating user interactions) to bypass anti-bot mechanisms.
111
+
112
+ ```env
113
+ BROWSERLESS_STEALTH_MODE=1
114
+ ```
115
+
116
+ > 📌 Supported values:
117
+ >
118
+ > * `1`: Enable stealth mode (recommended);
119
+ > * `0`: Disable stealth mode (default).
120
+
121
+ > ⚠️ Some websites use advanced anti-scraping techniques. Enabling stealth mode can significantly improve scraping success rate.
122
+
123
+ ---
124
+
91
125
  ## `GOOGLE_PSE_ENGINE_ID`
92
126
 
93
127
  Configure the Search Engine ID for Google Programmable Search Engine (Google PSE), used to restrict the search scope. Must be used alongside `GOOGLE_PSE_API_KEY`.
@@ -84,6 +84,40 @@ BROWSERLESS_URL=https://chrome.browserless.io
84
84
 
85
85
  ---
86
86
 
87
+ ## `BROWSERLESS_BLOCK_ADS`
88
+
89
+ 启用广告拦截功能,在使用 [Browserless](https://www.browserless.io/) 进行网页抓取时自动屏蔽常见广告资源(如脚本、图片、追踪器等),提高抓取速度与页面清晰度。
90
+
91
+ ```env
92
+ BROWSERLESS_BLOCK_ADS=1
93
+ ```
94
+
95
+ > 📌 支持的值:
96
+ >
97
+ > * `1`:启用广告拦截(推荐);
98
+ > * `0`:禁用广告拦截(默认)。
99
+
100
+ > ✅ 建议与 `BROWSERLESS_STEALTH_MODE=1` 一起使用,提高爬虫的隐蔽性和成功率。
101
+
102
+ ---
103
+
104
+ ## `BROWSERLESS_STEALTH_MODE`
105
+
106
+ 启用隐身模式,在使用 [Browserless](https://www.browserless.io/) 抓取网页时,通过一系列防检测手段(如修改 UA、移除 webdriver 特征、模拟用户操作)来规避反爬虫机制。
107
+
108
+ ```env
109
+ BROWSERLESS_STEALTH_MODE=1
110
+ ```
111
+
112
+ > 📌 支持的值:
113
+ >
114
+ > * `1`:启用隐身模式(推荐);
115
+ > * `0`:禁用隐身模式(默认)。
116
+
117
+ > ⚠️ 某些网站存在高级反爬机制,启用隐身模式可以显著提升抓取成功率。
118
+
119
+ ---
120
+
87
121
  ## `GOOGLE_PSE_ENGINE_ID`
88
122
 
89
123
  配置 Google Programmable Search Engine(Google PSE)的搜索引擎 ID,用于限定搜索范围。需配合 `GOOGLE_PSE_API_KEY` 一起使用。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.96.6",
3
+ "version": "1.96.8",
4
4
  "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.",
5
5
  "keywords": [
6
6
  "framework",
@@ -10,6 +10,9 @@ const REJECT_REQUEST_PATTERN =
10
10
  '.*\\.(?!(html|css|js|json|xml|webmanifest|txt|md)(\\?|#|$))[\\w-]+(?:[\\?#].*)?$';
11
11
  const BROWSERLESS_TOKEN = process.env.BROWSERLESS_TOKEN;
12
12
 
13
+ const BROWSERLESS_BLOCK_ADS = process.env.BROWSERLESS_BLOCK_ADS === '1';
14
+ const BROWSERLESS_STEALTH_MODE = process.env.BROWSERLESS_STEALTH_MODE === '1';
15
+
13
16
  class BrowserlessInitError extends Error {
14
17
  constructor() {
15
18
  super('`BROWSERLESS_URL` or `BROWSERLESS_TOKEN` are required');
@@ -30,7 +33,14 @@ export const browserless: CrawlImpl = async (url, { filterOptions }) => {
30
33
 
31
34
  try {
32
35
  const res = await fetch(
33
- qs.stringifyUrl({ query: { token: BROWSERLESS_TOKEN }, url: urlJoin(BASE_URL, '/content') }),
36
+ qs.stringifyUrl({
37
+ query: {
38
+ blockAds: BROWSERLESS_BLOCK_ADS,
39
+ launch: JSON.stringify({ stealth: BROWSERLESS_STEALTH_MODE }),
40
+ token: BROWSERLESS_TOKEN,
41
+ },
42
+ url: urlJoin(BASE_URL, '/content'),
43
+ }),
34
44
  {
35
45
  body: JSON.stringify(input),
36
46
  headers: {
@@ -20,7 +20,7 @@ const googleChatModels: AIChatModelCard[] = [
20
20
  },
21
21
  releasedAt: '2025-06-17',
22
22
  settings: {
23
- extendParams: ['enableReasoning', 'reasoningBudgetToken'],
23
+ extendParams: ['thinkingBudget'],
24
24
  searchImpl: 'params',
25
25
  searchProvider: 'google',
26
26
  },
@@ -45,7 +45,7 @@ const googleChatModels: AIChatModelCard[] = [
45
45
  },
46
46
  releasedAt: '2025-06-05',
47
47
  settings: {
48
- extendParams: ['enableReasoning', 'reasoningBudgetToken'],
48
+ extendParams: ['thinkingBudget'],
49
49
  searchImpl: 'params',
50
50
  searchProvider: 'google',
51
51
  },
@@ -119,7 +119,7 @@ const googleChatModels: AIChatModelCard[] = [
119
119
  },
120
120
  releasedAt: '2025-06-17',
121
121
  settings: {
122
- extendParams: ['enableReasoning', 'reasoningBudgetToken'],
122
+ extendParams: ['thinkingBudget'],
123
123
  searchImpl: 'params',
124
124
  searchProvider: 'google',
125
125
  },
@@ -143,7 +143,7 @@ const googleChatModels: AIChatModelCard[] = [
143
143
  },
144
144
  releasedAt: '2025-05-20',
145
145
  settings: {
146
- extendParams: ['enableReasoning', 'reasoningBudgetToken'],
146
+ extendParams: ['thinkingBudget'],
147
147
  searchImpl: 'params',
148
148
  searchProvider: 'google',
149
149
  },
@@ -167,7 +167,7 @@ const googleChatModels: AIChatModelCard[] = [
167
167
  },
168
168
  releasedAt: '2025-04-17',
169
169
  settings: {
170
- extendParams: ['enableReasoning', 'reasoningBudgetToken'],
170
+ extendParams: ['thinkingBudget'],
171
171
  searchImpl: 'params',
172
172
  searchProvider: 'google',
173
173
  },
@@ -215,7 +215,7 @@ const googleChatModels: AIChatModelCard[] = [
215
215
  },
216
216
  releasedAt: '2025-06-11',
217
217
  settings: {
218
- extendParams: ['enableReasoning', 'reasoningBudgetToken'],
218
+ extendParams: ['thinkingBudget'],
219
219
  searchImpl: 'params',
220
220
  searchProvider: 'google',
221
221
  },
@@ -13,6 +13,7 @@ import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
13
13
  import ContextCachingSwitch from './ContextCachingSwitch';
14
14
  import ReasoningEffortSlider from './ReasoningEffortSlider';
15
15
  import ReasoningTokenSlider from './ReasoningTokenSlider';
16
+ import ThinkingBudgetSlider from './ThinkingBudgetSlider';
16
17
 
17
18
  const ControlsForm = memo(() => {
18
19
  const { t } = useTranslation('chat');
@@ -93,6 +94,17 @@ const ControlsForm = memo(() => {
93
94
  paddingBottom: 0,
94
95
  },
95
96
  },
97
+ {
98
+ children: <ThinkingBudgetSlider />,
99
+ label: t('extendParams.reasoningBudgetToken.title'),
100
+ layout: 'vertical',
101
+ minWidth: 500,
102
+ name: 'thinkingBudget',
103
+ style: {
104
+ paddingBottom: 0,
105
+ },
106
+ tag: 'thinkingBudget',
107
+ },
96
108
  ].filter(Boolean) as FormItemProps[];
97
109
 
98
110
  return (
@@ -0,0 +1,133 @@
1
+ import { InputNumber } from '@lobehub/ui';
2
+ import { Slider } from 'antd';
3
+ import { memo, useMemo } from 'react';
4
+ import { Flexbox } from 'react-layout-kit';
5
+ import useMergeState from 'use-merge-value';
6
+
7
+ // 定义特殊值映射
8
+ const SPECIAL_VALUES = {
9
+ AUTO: -1,
10
+ OFF: 0,
11
+ };
12
+
13
+ // 定义滑块位置到实际值的映射
14
+ const SLIDER_TO_VALUE_MAP = [
15
+ SPECIAL_VALUES.AUTO, // 位置 0 -> -1 (Auto)
16
+ SPECIAL_VALUES.OFF, // 位置 1 -> 0 (OFF)
17
+ 128, // 位置 2 -> 128
18
+ 512, // 位置 3 -> 512
19
+ 1024, // 位置 4 -> 1024
20
+ 2048, // 位置 5 -> 2048
21
+ 4096, // 位置 6 -> 4096
22
+ 8192, // 位置 7 -> 8192
23
+ 16_384, // 位置 8 -> 16384
24
+ 24_576, // 位置 9 -> 24576
25
+ 32_768, // 位置 10 -> 32768
26
+ ];
27
+
28
+ // 从实际值获取滑块位置
29
+ const getSliderPosition = (value: number): number => {
30
+ const index = SLIDER_TO_VALUE_MAP.indexOf(value);
31
+ return index === -1 ? 0 : index;
32
+ };
33
+
34
+ // 从滑块位置获取实际值(修复:0 不再被当作 falsy)
35
+ const getValueFromPosition = (position: number): number => {
36
+ const v = SLIDER_TO_VALUE_MAP[position];
37
+ return v === undefined ? SPECIAL_VALUES.AUTO : v;
38
+ };
39
+
40
+ interface ThinkingBudgetSliderProps {
41
+ defaultValue?: number;
42
+ onChange?: (value: number) => void;
43
+ value?: number;
44
+ }
45
+
46
+ const ThinkingBudgetSlider = memo<ThinkingBudgetSliderProps>(
47
+ ({ value, onChange, defaultValue }) => {
48
+ // 首先确定初始的 budget 值
49
+ const initialBudget = value ?? defaultValue ?? SPECIAL_VALUES.AUTO;
50
+
51
+ const [budget, setBudget] = useMergeState(initialBudget, {
52
+ defaultValue,
53
+ onChange,
54
+ value,
55
+ });
56
+
57
+ const sliderPosition = getSliderPosition(budget);
58
+
59
+ const updateWithSliderPosition = (position: number) => {
60
+ const newValue = getValueFromPosition(position);
61
+ setBudget(newValue);
62
+ };
63
+
64
+ const updateWithRealValue = (value: number) => {
65
+ setBudget(value);
66
+ };
67
+
68
+ const marks = useMemo(() => {
69
+ return {
70
+ 0: 'Auto',
71
+ 1: 'OFF',
72
+ 2: '128',
73
+ 3: '512',
74
+ 4: '1K',
75
+ 5: '2K',
76
+ 6: '4K',
77
+ 7: '8K',
78
+ 8: '16K',
79
+ 9: '24K',
80
+ // eslint-disable-next-line sort-keys-fix/sort-keys-fix
81
+ 10: '32K',
82
+ };
83
+ }, []);
84
+
85
+ return (
86
+ <Flexbox align={'center'} gap={12} horizontal paddingInline={'4px 0'}>
87
+ <Flexbox flex={1}>
88
+ <Slider
89
+ marks={marks}
90
+ max={10}
91
+ min={0}
92
+ onChange={updateWithSliderPosition}
93
+ step={null}
94
+ tooltip={{ open: false }}
95
+ value={sliderPosition}
96
+ />
97
+ </Flexbox>
98
+ <div>
99
+ <InputNumber
100
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
101
+ formatter={(value, _info) => {
102
+ if (value === SPECIAL_VALUES.AUTO) return 'Auto';
103
+ if (value === SPECIAL_VALUES.OFF) return 'OFF';
104
+ return `${value}`;
105
+ }}
106
+ max={32_768}
107
+ min={-1}
108
+ onChange={(e) => {
109
+ if (e === null || e === undefined) return;
110
+ updateWithRealValue(e as number);
111
+ }}
112
+ parser={(value) => {
113
+ if (typeof value === 'string') {
114
+ if (value.toLowerCase() === 'auto') return SPECIAL_VALUES.AUTO;
115
+ if (value.toLowerCase() === 'off') return SPECIAL_VALUES.OFF;
116
+ return parseInt(value.replaceAll(/[^\d-]/g, ''), 10) || 0;
117
+ }
118
+ if (typeof value === 'number') {
119
+ return value;
120
+ }
121
+ return SPECIAL_VALUES.AUTO;
122
+ }}
123
+ step={128}
124
+ style={{ width: 80 }}
125
+ value={budget}
126
+ />
127
+ </div>
128
+ </Flexbox>
129
+ );
130
+ },
131
+ );
132
+
133
+ export default ThinkingBudgetSlider;
@@ -120,28 +120,37 @@ export class LobeGoogleAI implements LobeRuntimeAI {
120
120
  async chat(rawPayload: ChatStreamPayload, options?: ChatMethodOptions) {
121
121
  try {
122
122
  const payload = this.buildPayload(rawPayload);
123
- const { model, thinking } = payload;
123
+ const { model, thinkingBudget } = payload;
124
124
 
125
125
  const thinkingConfig: GoogleAIThinkingConfig = {
126
126
  includeThoughts:
127
- thinking?.type === 'enabled' ||
128
- (!thinking && model && (model.includes('-2.5-') || model.includes('thinking')))
127
+ !!thinkingBudget ||
128
+ (!thinkingBudget && model && (model.includes('-2.5-') || model.includes('thinking')))
129
129
  ? true
130
130
  : undefined,
131
- thinkingBudget:
132
- thinking?.type === 'enabled'
133
- ? (() => {
134
- const budget = thinking.budget_tokens;
135
- if (model.includes('-2.5-flash')) {
136
- return Math.min(budget, 24_576);
137
- } else if (model.includes('-2.5-pro')) {
138
- return Math.max(128, Math.min(budget, 32_768));
139
- }
140
- return Math.min(budget, 24_576);
141
- })()
142
- : thinking?.type === 'disabled'
143
- ? model.includes('-2.5-pro') ? 128 : 0
144
- : undefined,
131
+ // https://ai.google.dev/gemini-api/docs/thinking#set-budget
132
+ thinkingBudget: (() => {
133
+ if (thinkingBudget !== undefined && thinkingBudget !== null) {
134
+ if (model.includes('-2.5-flash-lite')) {
135
+ if (thinkingBudget === 0 || thinkingBudget === -1) {
136
+ return thinkingBudget;
137
+ }
138
+ return Math.max(512, Math.min(thinkingBudget, 24_576));
139
+ } else if (model.includes('-2.5-flash')) {
140
+ return Math.min(thinkingBudget, 24_576);
141
+ } else if (model.includes('-2.5-pro')) {
142
+ return Math.max(128, Math.min(thinkingBudget, 32_768));
143
+ }
144
+ return Math.min(thinkingBudget, 24_576);
145
+ }
146
+
147
+ if (model.includes('-2.5-pro') || model.includes('-2.5-flash')) {
148
+ return -1;
149
+ } else if (model.includes('-2.5-flash-lite')) {
150
+ return 0;
151
+ }
152
+ return undefined;
153
+ })(),
145
154
  };
146
155
 
147
156
  const contents = await this.buildGoogleMessages(payload.messages);
@@ -126,6 +126,7 @@ export interface ChatStreamPayload {
126
126
  budget_tokens: number;
127
127
  type: 'enabled' | 'disabled';
128
128
  };
129
+ thinkingBudget?: number;
129
130
  tool_choice?: string;
130
131
  tools?: ChatCompletionTool[];
131
132
  /**
@@ -263,6 +263,13 @@ class ChatService {
263
263
  if (modelExtendParams!.includes('reasoningEffort') && chatConfig.reasoningEffort) {
264
264
  extendParams.reasoning_effort = chatConfig.reasoningEffort;
265
265
  }
266
+
267
+ if (
268
+ modelExtendParams!.includes('thinkingBudget') &&
269
+ chatConfig.thinkingBudget !== undefined
270
+ ) {
271
+ extendParams.thinkingBudget = chatConfig.thinkingBudget;
272
+ }
266
273
  }
267
274
 
268
275
  return this.getChatCompletion(
@@ -26,7 +26,7 @@ export interface LobeAgentChatConfig {
26
26
  enableReasoningEffort?: boolean;
27
27
  reasoningBudgetToken?: number;
28
28
  reasoningEffort?: 'low' | 'medium' | 'high';
29
-
29
+ thinkingBudget?: number;
30
30
  /**
31
31
  * 禁用上下文缓存
32
32
  */
@@ -147,7 +147,8 @@ export type ExtendParamsType =
147
147
  | 'reasoningBudgetToken'
148
148
  | 'enableReasoning'
149
149
  | 'disableContextCaching'
150
- | 'reasoningEffort';
150
+ | 'reasoningEffort'
151
+ | 'thinkingBudget';
151
152
 
152
153
  export interface AiModelSettings {
153
154
  extendParams?: ExtendParamsType[];