@lobehub/chat 1.137.4 → 1.137.6

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.
Files changed (27) hide show
  1. package/.github/scripts/auto-close-duplicates.ts +260 -0
  2. package/.github/workflows/claude-dedupe-issues.yml +33 -0
  3. package/.github/workflows/claude-issue-triage.yml +199 -0
  4. package/.github/workflows/desktop-pr-build.yml +3 -3
  5. package/.github/workflows/docker-database.yml +1 -1
  6. package/.github/workflows/docker-pglite.yml +1 -1
  7. package/.github/workflows/docker.yml +1 -1
  8. package/.github/workflows/issue-auto-close-duplicates.yml +30 -0
  9. package/.github/workflows/release-desktop-beta.yml +1 -1
  10. package/CHANGELOG.md +51 -0
  11. package/changelog/v1.json +18 -0
  12. package/package.json +1 -1
  13. package/packages/context-engine/src/processors/MessageContent.ts +4 -2
  14. package/packages/context-engine/src/processors/__tests__/MessageContent.test.ts +11 -14
  15. package/packages/model-bank/src/aiModels/google.ts +7 -7
  16. package/packages/model-bank/src/aiModels/vertexai.ts +63 -2
  17. package/packages/utils/package.json +2 -1
  18. package/packages/utils/src/url.test.ts +19 -19
  19. package/packages/utils/src/url.ts +7 -11
  20. package/src/server/modules/ModelRuntime/index.test.ts +31 -0
  21. package/src/server/modules/ModelRuntime/index.ts +43 -0
  22. package/src/services/chat/chat.test.ts +38 -17
  23. package/src/services/chat/clientModelRuntime.test.ts +3 -3
  24. package/src/store/chat/slices/aiChat/actions/__tests__/fixtures.ts +66 -0
  25. package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +893 -650
  26. package/src/store/chat/slices/aiChat/actions/__tests__/helpers.ts +106 -0
  27. package/src/app/(backend)/webapi/chat/vertexai/route.ts +0 -38
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env bun
2
+
3
+ declare global {
4
+ // @ts-ignore
5
+ // eslint-disable-next-line no-var
6
+ var process: {
7
+ env: Record<string, string | undefined>;
8
+ };
9
+ }
10
+
11
+ interface GitHubIssue {
12
+ created_at: string;
13
+ number: number;
14
+ title: string;
15
+ user: { id: number };
16
+ }
17
+
18
+ interface GitHubComment {
19
+ body: string;
20
+ created_at: string;
21
+ id: number;
22
+ user: { id: number; type: string };
23
+ }
24
+
25
+ interface GitHubReaction {
26
+ content: string;
27
+ user: { id: number };
28
+ }
29
+
30
+ async function githubRequest<T>(
31
+ endpoint: string,
32
+ token: string,
33
+ method: string = 'GET',
34
+ body?: any,
35
+ ): Promise<T> {
36
+ const response = await fetch(`https://api.github.com${endpoint}`, {
37
+ headers: {
38
+ 'Accept': 'application/vnd.github.v3+json',
39
+ 'Authorization': `Bearer ${token}`,
40
+ 'User-Agent': 'auto-close-duplicates-script',
41
+ ...(body && { 'Content-Type': 'application/json' }),
42
+ },
43
+ method,
44
+ ...(body && { body: JSON.stringify(body) }),
45
+ });
46
+
47
+ if (!response.ok) {
48
+ throw new Error(`GitHub API request failed: ${response.status} ${response.statusText}`);
49
+ }
50
+
51
+ return response.json();
52
+ }
53
+
54
+ function extractDuplicateIssueNumber(commentBody: string): number | null {
55
+ // Try to match #123 format first
56
+ let match = commentBody.match(/#(\d+)/);
57
+ if (match) {
58
+ return parseInt(match[1], 10);
59
+ }
60
+
61
+ // Try to match GitHub issue URL format: https://github.com/owner/repo/issues/123
62
+ match = commentBody.match(/github\.com\/[^/]+\/[^/]+\/issues\/(\d+)/);
63
+ if (match) {
64
+ return parseInt(match[1], 10);
65
+ }
66
+
67
+ return null;
68
+ }
69
+
70
+ async function closeIssueAsDuplicate(
71
+ owner: string,
72
+ repo: string,
73
+ issueNumber: number,
74
+ duplicateOfNumber: number,
75
+ token: string,
76
+ ): Promise<void> {
77
+ await githubRequest(`/repos/${owner}/${repo}/issues/${issueNumber}`, token, 'PATCH', {
78
+ labels: ['duplicate'],
79
+ state: 'closed',
80
+ state_reason: 'duplicate',
81
+ });
82
+
83
+ await githubRequest(`/repos/${owner}/${repo}/issues/${issueNumber}/comments`, token, 'POST', {
84
+ body: `This issue has been automatically closed as a duplicate of #${duplicateOfNumber}.
85
+
86
+ If this is incorrect, please re-open this issue or create a new one.
87
+
88
+ 🤖 Generated with [Claude Code](https://claude.ai/code)`,
89
+ });
90
+ }
91
+
92
+ async function autoCloseDuplicates(): Promise<void> {
93
+ console.log('[DEBUG] Starting auto-close duplicates script');
94
+
95
+ const token = process.env.GITHUB_TOKEN;
96
+ if (!token) {
97
+ throw new Error('GITHUB_TOKEN environment variable is required');
98
+ }
99
+ console.log('[DEBUG] GitHub token found');
100
+
101
+ const owner = process.env.GITHUB_REPOSITORY_OWNER || 'lobehub';
102
+ const repo = process.env.GITHUB_REPOSITORY_NAME || 'lobe-chat';
103
+ console.log(`[DEBUG] Repository: ${owner}/${repo}`);
104
+
105
+ const threeDaysAgo = new Date();
106
+ threeDaysAgo.setDate(threeDaysAgo.getDate() - 3);
107
+ console.log(`[DEBUG] Checking for duplicate comments older than: ${threeDaysAgo.toISOString()}`);
108
+
109
+ console.log('[DEBUG] Fetching open issues created more than 3 days ago...');
110
+ const allIssues: GitHubIssue[] = [];
111
+ let page = 1;
112
+ const perPage = 100;
113
+
114
+ // eslint-disable-next-line no-constant-condition
115
+ while (true) {
116
+ const pageIssues: GitHubIssue[] = await githubRequest(
117
+ `/repos/${owner}/${repo}/issues?state=open&per_page=${perPage}&page=${page}`,
118
+ token,
119
+ );
120
+
121
+ if (pageIssues.length === 0) break;
122
+
123
+ // Filter for issues created more than 3 days ago
124
+ const oldEnoughIssues = pageIssues.filter(
125
+ (issue) => new Date(issue.created_at) <= threeDaysAgo,
126
+ );
127
+
128
+ allIssues.push(...oldEnoughIssues);
129
+ page++;
130
+
131
+ // Safety limit to avoid infinite loops
132
+ if (page > 20) break;
133
+ }
134
+
135
+ const issues = allIssues;
136
+ console.log(`[DEBUG] Found ${issues.length} open issues`);
137
+
138
+ let processedCount = 0;
139
+ let candidateCount = 0;
140
+
141
+ for (const issue of issues) {
142
+ processedCount++;
143
+ console.log(
144
+ `[DEBUG] Processing issue #${issue.number} (${processedCount}/${issues.length}): ${issue.title}`,
145
+ );
146
+
147
+ console.log(`[DEBUG] Fetching comments for issue #${issue.number}...`);
148
+ const comments: GitHubComment[] = await githubRequest(
149
+ `/repos/${owner}/${repo}/issues/${issue.number}/comments`,
150
+ token,
151
+ );
152
+ console.log(`[DEBUG] Issue #${issue.number} has ${comments.length} comments`);
153
+
154
+ const dupeComments = comments.filter(
155
+ (comment) =>
156
+ comment.body.includes('Found') &&
157
+ comment.body.includes('possible duplicate') &&
158
+ comment.user.type === 'Bot',
159
+ );
160
+ console.log(
161
+ `[DEBUG] Issue #${issue.number} has ${dupeComments.length} duplicate detection comments`,
162
+ );
163
+
164
+ if (dupeComments.length === 0) {
165
+ console.log(`[DEBUG] Issue #${issue.number} - no duplicate comments found, skipping`);
166
+ continue;
167
+ }
168
+
169
+ const lastDupeComment = dupeComments.at(-1);
170
+ // @ts-ignore
171
+ const dupeCommentDate = new Date(lastDupeComment.created_at);
172
+ console.log(
173
+ `[DEBUG] Issue #${
174
+ issue.number
175
+ } - most recent duplicate comment from: ${dupeCommentDate.toISOString()}`,
176
+ );
177
+
178
+ if (dupeCommentDate > threeDaysAgo) {
179
+ console.log(`[DEBUG] Issue #${issue.number} - duplicate comment is too recent, skipping`);
180
+ continue;
181
+ }
182
+ console.log(
183
+ `[DEBUG] Issue #${issue.number} - duplicate comment is old enough (${Math.floor(
184
+ (Date.now() - dupeCommentDate.getTime()) / (1000 * 60 * 60 * 24),
185
+ )} days)`,
186
+ );
187
+
188
+ const commentsAfterDupe = comments.filter(
189
+ (comment) => new Date(comment.created_at) > dupeCommentDate,
190
+ );
191
+ console.log(
192
+ `[DEBUG] Issue #${issue.number} - ${commentsAfterDupe.length} comments after duplicate detection`,
193
+ );
194
+
195
+ if (commentsAfterDupe.length > 0) {
196
+ console.log(
197
+ `[DEBUG] Issue #${issue.number} - has activity after duplicate comment, skipping`,
198
+ );
199
+ continue;
200
+ }
201
+
202
+ console.log(`[DEBUG] Issue #${issue.number} - checking reactions on duplicate comment...`);
203
+ const reactions: GitHubReaction[] = await githubRequest(
204
+ // @ts-ignore
205
+ `/repos/${owner}/${repo}/issues/comments/${lastDupeComment.id}/reactions`,
206
+ token,
207
+ );
208
+ console.log(
209
+ `[DEBUG] Issue #${issue.number} - duplicate comment has ${reactions.length} reactions`,
210
+ );
211
+
212
+ const authorThumbsDown = reactions.some(
213
+ (reaction) => reaction.user.id === issue.user.id && reaction.content === '-1',
214
+ );
215
+ console.log(
216
+ `[DEBUG] Issue #${issue.number} - author thumbs down reaction: ${authorThumbsDown}`,
217
+ );
218
+
219
+ if (authorThumbsDown) {
220
+ console.log(
221
+ `[DEBUG] Issue #${issue.number} - author disagreed with duplicate detection, skipping`,
222
+ );
223
+ continue;
224
+ }
225
+
226
+ // @ts-ignore
227
+ const duplicateIssueNumber = extractDuplicateIssueNumber(lastDupeComment.body);
228
+ if (!duplicateIssueNumber) {
229
+ console.log(
230
+ `[DEBUG] Issue #${issue.number} - could not extract duplicate issue number from comment, skipping`,
231
+ );
232
+ continue;
233
+ }
234
+
235
+ candidateCount++;
236
+ const issueUrl = `https://github.com/${owner}/${repo}/issues/${issue.number}`;
237
+
238
+ try {
239
+ console.log(
240
+ `[INFO] Auto-closing issue #${issue.number} as duplicate of #${duplicateIssueNumber}: ${issueUrl}`,
241
+ );
242
+ await closeIssueAsDuplicate(owner, repo, issue.number, duplicateIssueNumber, token);
243
+ console.log(
244
+ `[SUCCESS] Successfully closed issue #${issue.number} as duplicate of #${duplicateIssueNumber}`,
245
+ );
246
+ } catch (error) {
247
+ console.error(`[ERROR] Failed to close issue #${issue.number} as duplicate: ${error}`);
248
+ }
249
+ }
250
+
251
+ console.log(
252
+ `[DEBUG] Script completed. Processed ${processedCount} issues, found ${candidateCount} candidates for auto-close`,
253
+ );
254
+ }
255
+
256
+ // eslint-disable-next-line unicorn/prefer-top-level-await
257
+ autoCloseDuplicates().catch(console.error);
258
+
259
+ // Make it a module
260
+ export {};
@@ -0,0 +1,33 @@
1
+ name: Claude Issue Dedupe
2
+ description: Automatically dedupe GitHub issues using Claude Code
3
+ on:
4
+ issues:
5
+ types: [opened]
6
+ workflow_dispatch:
7
+ inputs:
8
+ issue_number:
9
+ description: 'Issue number to process for duplicate detection'
10
+ required: true
11
+ type: string
12
+
13
+ jobs:
14
+ claude-dedupe-issues:
15
+ runs-on: ubuntu-latest
16
+ timeout-minutes: 10
17
+ permissions:
18
+ contents: read
19
+ issues: write
20
+
21
+ steps:
22
+ - name: Checkout repository
23
+ uses: actions/checkout@v5
24
+ with:
25
+ fetch-depth: 1
26
+
27
+ - name: Run Claude Code slash command
28
+ uses: anthropics/claude-code-base-action@beta
29
+ with:
30
+ prompt: '/dedupe ${{ github.repository }}/issues/${{ github.event.issue.number || inputs.issue_number }}'
31
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
32
+ claude_env: |
33
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,199 @@
1
+ name: Claude Issue Triage
2
+ description: Automatically triage GitHub issues using Claude Code
3
+ on:
4
+ issues:
5
+ types: [opened, labeled]
6
+
7
+ jobs:
8
+ triage-issue:
9
+ runs-on: ubuntu-latest
10
+ timeout-minutes: 10
11
+ # Only run on issue opened, or when "trigger:triage" label is added
12
+ if: github.event.action == 'opened' || (github.event.action == 'labeled' && github.event.label.name == 'trigger:triage')
13
+ permissions:
14
+ contents: read
15
+ issues: write
16
+
17
+ steps:
18
+ - name: Checkout repository
19
+ uses: actions/checkout@v5
20
+
21
+ - name: Copy team assignment guide
22
+ run: |
23
+ mkdir -p /tmp/claude-prompts
24
+ cp .claude/prompts/team-assignment.md /tmp/claude-prompts/
25
+
26
+ - name: Create triage prompt
27
+ run: |
28
+ mkdir -p /tmp/claude-prompts
29
+ cat > /tmp/claude-prompts/triage-prompt.md << 'EOF'
30
+ You're an issue triage assistant for GitHub issues. Your task is to analyze issues, apply appropriate labels, and mention the responsible team member.
31
+
32
+ REPOSITORY: ${{ github.repository }}
33
+ ISSUE_NUMBER: ${{ github.event.issue.number }}
34
+
35
+ ## WORKFLOW
36
+
37
+ ### Step 1: Get Available Labels
38
+ ```bash
39
+ gh label list --json name,description --limit 300
40
+ ```
41
+
42
+ ### Step 2: Get Issue Details
43
+ ```bash
44
+ gh issue view ${{ github.event.issue.number }} --json number,title,body,labels,comments
45
+ ```
46
+
47
+ ### Step 3: Read Team Assignment Guide
48
+ ```bash
49
+ cat /tmp/claude-prompts/team-assignment.md
50
+ ```
51
+
52
+ ### Step 4: Analyze and Select Labels
53
+
54
+ Extract information from the issue template:
55
+ - 📦 Platform field → platform:web/desktop/mobile
56
+ - 💻 Operating System → os:windows/macos/linux/ios
57
+ - 🌐 Browser → device:pc/mobile
58
+ - 📦 Deployment mode → deployment:server/client/pglite
59
+ - Platform (hosting) → hosting:cloud/self-host/vercel/zeabur/railway
60
+
61
+ **LABEL CATEGORIES:**
62
+
63
+ **a) Issue Type (REQUIRED - select ONE):**
64
+ - 🐛 Bug, 🌠 Feature Request, 💄 Design, 📝 Documentation, ⚡️ Performance
65
+
66
+ **b) Priority (select ONE if applicable):**
67
+ - priority:high - Critical, maintainer mentions "urgent"/"serious"/"critical"
68
+ - priority:medium - Important, affects multiple users
69
+ - priority:low - Nice to have, minor issues
70
+
71
+ **c) Platform (select ALL applicable):**
72
+ - platform:web, platform:desktop, platform:mobile
73
+
74
+ **d) Device (for platform:web, select ONE):**
75
+ - device:pc, device:mobile
76
+
77
+ **e) Operating System (select ALL applicable):**
78
+ - os:windows, os:macos, os:linux, os:ios
79
+
80
+ **f) Hosting Platform (select ONE):**
81
+ - hosting:cloud, hosting:self-host, hosting:vercel, hosting:zeabur, hosting:railway
82
+
83
+ **g) Deployment Mode (select ONE if mentioned):**
84
+ - deployment:server, deployment:client, deployment:pglite
85
+
86
+ **h) Model Provider (select ALL applicable):**
87
+ - provider:openai, provider:gemini, provider:claude, provider:deepseek
88
+ - provider:google, provider:ollama, provider:azure, provider:bedrock, provider:vertex
89
+
90
+ **i) Feature/Component (select ALL applicable):**
91
+ - feature:settings, feature:agent, feature:topic, feature:marketplace
92
+ - feature:streaming, feature:tool, feature:sync, feature:export
93
+ - feature:search, feature:auth, feature:files, feature:knowledge-base
94
+ - feature:tts, feature:vision, feature:mcp, feature:editor, feature:thread
95
+ - feature:image, feature:api, feature:dalle, feature:plugin, feature:markdown
96
+ - feature:group-chat, feature:memory, feature:team-workspace
97
+
98
+ **j) Workflow/Status:**
99
+ - Duplicate - Only if duplicate of OPEN issue
100
+ - needs-reproduction, good-first-issue, 🤔 Need Reproduce
101
+
102
+ **IMPORTANT RULES:**
103
+ - Read issue template fields carefully
104
+ - Check maintainer comments for priority/status
105
+ - Use ALL applicable labels from different categories
106
+ - Always use prefixes (feature:, provider:, os:, platform:, etc.)
107
+
108
+ ### Step 5: Apply Labels
109
+
110
+ Add labels:
111
+ ```bash
112
+ gh issue edit ${{ github.event.issue.number }} --add-label "label1,label2,label3"
113
+ ```
114
+
115
+ Remove "unconfirm" if adding other labels:
116
+ ```bash
117
+ gh issue edit ${{ github.event.issue.number }} --remove-label "unconfirm"
118
+ ```
119
+
120
+ **Execute the commands now.**
121
+
122
+ ### Step 6: Determine Team Member
123
+
124
+ Based on the team assignment guide and applied labels, determine who to mention.
125
+
126
+ **Priority Order:**
127
+ 1. Specific feature owner (feature:knowledge-base → @RiverTwilight)
128
+ 2. Platform owner (platform:mobile → @sudongyuer)
129
+ 3. Provider owner (provider:* → @sxjeru)
130
+ 4. Component owner (💄 Design → @canisminor1990)
131
+ 5. Infrastructure owner (deployment:* → @nekomeowww)
132
+ 6. General maintainer (@ONLY-yours)
133
+ 7. Last resort (@arvinxx)
134
+
135
+ **Special Cases:**
136
+ - Multiple owners: Mention primary + secondary
137
+ - priority:high: Mention owner + @arvinxx
138
+
139
+ ### Step 7: Post Comment
140
+
141
+ Format the comment (1-2 sentences):
142
+
143
+ **Single owner:**
144
+ ```
145
+ @username - This is a [feature/component] issue. Please take a look.
146
+ ```
147
+
148
+ **Multiple owners:**
149
+ ```
150
+ @user1 @user2 - This involves [features]. Please coordinate.
151
+ ```
152
+
153
+ **High priority:**
154
+ ```
155
+ @owner @arvinxx - High priority [feature] issue.
156
+ ```
157
+
158
+ Post the comment:
159
+ ```bash
160
+ gh issue comment ${{ github.event.issue.number }} --body "@username - [message]"
161
+ ```
162
+
163
+ **Execute the command now.**
164
+
165
+ ### Step 8: Output Summary
166
+
167
+ Log your reasoning (2-4 sentences):
168
+ - Labels applied and why
169
+ - Team member(s) mentioned and reason
170
+ - Key factors from issue template/comments
171
+
172
+ ## GUIDELINES
173
+
174
+ - Be thorough in analysis
175
+ - Use only labels from the provided list
176
+ - Extract info from issue template fields
177
+ - ALWAYS post a mention comment (unless no clear owner)
178
+ - Keep comments professional and brief
179
+ - Output reasoning to logs
180
+
181
+ **Start the triage process now.**
182
+ EOF
183
+
184
+ - name: Run Claude Code for Issue Triage
185
+ uses: anthropics/claude-code-base-action@beta
186
+ with:
187
+ prompt_file: /tmp/claude-prompts/triage-prompt.md
188
+ allowed_tools: "Bash(gh *),Read"
189
+ timeout_minutes: "5"
190
+ claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
191
+ claude_env: |
192
+ GH_TOKEN: ${{ secrets.GH_TOKEN }}
193
+
194
+ - name: Remove trigger label
195
+ if: github.event.action == 'labeled' && github.event.label.name == 'trigger:triage'
196
+ run: |
197
+ gh issue edit ${{ github.event.issue.number }} --remove-label "trigger:triage"
198
+ env:
199
+ GH_TOKEN: ${{ secrets.GH_TOKEN }}
@@ -18,8 +18,8 @@ env:
18
18
  jobs:
19
19
  test:
20
20
  name: Code quality check
21
- # 添加 PR label 触发条件,只有添加了 Build Desktop 标签的 PR 才会触发构建
22
- if: contains(github.event.pull_request.labels.*.name, 'Build Desktop')
21
+ # 添加 PR label 触发条件,只有添加了 trigger:build-desktop 标签的 PR 才会触发构建
22
+ if: contains(github.event.pull_request.labels.*.name, 'trigger:build-desktop')
23
23
  runs-on: ubuntu-latest # 只在 ubuntu 上运行一次检查
24
24
  steps:
25
25
  - name: Checkout base
@@ -51,7 +51,7 @@ jobs:
51
51
  version:
52
52
  name: Determine version
53
53
  # 与 test job 相同的触发条件
54
- if: contains(github.event.pull_request.labels.*.name, 'Build Desktop')
54
+ if: contains(github.event.pull_request.labels.*.name, 'trigger:build-desktop')
55
55
  runs-on: ubuntu-latest
56
56
  outputs:
57
57
  # 输出版本信息,供后续 job 使用
@@ -22,7 +22,7 @@ jobs:
22
22
  # 添加 PR label 触发条件
23
23
  if: |
24
24
  (github.event_name == 'pull_request' &&
25
- contains(github.event.pull_request.labels.*.name, 'Build Docker')) ||
25
+ contains(github.event.pull_request.labels.*.name, 'trigger:build-docker')) ||
26
26
  github.event_name != 'pull_request'
27
27
 
28
28
  strategy:
@@ -22,7 +22,7 @@ jobs:
22
22
  # 添加 PR label 触发条件
23
23
  if: |
24
24
  (github.event_name == 'pull_request' &&
25
- contains(github.event.pull_request.labels.*.name, 'Build Docker')) ||
25
+ contains(github.event.pull_request.labels.*.name, 'trigger:build-docker')) ||
26
26
  github.event_name != 'pull_request'
27
27
 
28
28
  strategy:
@@ -22,7 +22,7 @@ jobs:
22
22
  # 添加 PR label 触发条件
23
23
  if: |
24
24
  (github.event_name == 'pull_request' &&
25
- contains(github.event.pull_request.labels.*.name, 'Build Docker')) ||
25
+ contains(github.event.pull_request.labels.*.name, 'trigger:build-docker')) ||
26
26
  github.event_name != 'pull_request'
27
27
 
28
28
  strategy:
@@ -0,0 +1,30 @@
1
+ name: Auto-close duplicate issues
2
+ description: Auto-closes issues that are duplicates of existing issues
3
+ on:
4
+ schedule:
5
+ - cron: "0 2 * * *"
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ auto-close-duplicates:
10
+ runs-on: ubuntu-latest
11
+ timeout-minutes: 10
12
+ permissions:
13
+ contents: read
14
+ issues: write
15
+
16
+ steps:
17
+ - name: Checkout repository
18
+ uses: actions/checkout@v5
19
+
20
+ - name: Setup Bun
21
+ uses: oven-sh/setup-bun@v2
22
+ with:
23
+ bun-version: latest
24
+
25
+ - name: Auto-close duplicate issues
26
+ run: bun run .github/scripts/auto-close-duplicates.ts
27
+ env:
28
+ GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
29
+ GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
30
+ GITHUB_REPOSITORY_NAME: ${{ github.event.repository.name }}
@@ -15,7 +15,7 @@ permissions: read-all
15
15
  jobs:
16
16
  test:
17
17
  name: Code quality check
18
- # 添加 PR label 触发条件,只有添加了 Build Desktop 标签的 PR 才会触发构建
18
+ # 添加 PR label 触发条件,只有添加了 trigger:build-desktop 标签的 PR 才会触发构建
19
19
  runs-on: ubuntu-latest # 只在 ubuntu 上运行一次检查
20
20
  steps:
21
21
  - name: Checkout base
package/CHANGELOG.md CHANGED
@@ -2,6 +2,57 @@
2
2
 
3
3
  # Changelog
4
4
 
5
+ ### [Version 1.137.6](https://github.com/lobehub/lobe-chat/compare/v1.137.5...v1.137.6)
6
+
7
+ <sup>Released on **2025-10-14**</sup>
8
+
9
+ #### 🐛 Bug Fixes
10
+
11
+ - **misc**: Update Claude workflows to use oauth token, vertext ai create image.
12
+
13
+ <br/>
14
+
15
+ <details>
16
+ <summary><kbd>Improvements and Fixes</kbd></summary>
17
+
18
+ #### What's fixed
19
+
20
+ - **misc**: Update Claude workflows to use oauth token, closes [#9711](https://github.com/lobehub/lobe-chat/issues/9711) ([8dcb00e](https://github.com/lobehub/lobe-chat/commit/8dcb00e))
21
+ - **misc**: Vertext ai create image, closes [#9710](https://github.com/lobehub/lobe-chat/issues/9710) ([790d8fd](https://github.com/lobehub/lobe-chat/commit/790d8fd))
22
+
23
+ </details>
24
+
25
+ <div align="right">
26
+
27
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
28
+
29
+ </div>
30
+
31
+ ### [Version 1.137.5](https://github.com/lobehub/lobe-chat/compare/v1.137.4...v1.137.5)
32
+
33
+ <sup>Released on **2025-10-14**</sup>
34
+
35
+ #### 💄 Styles
36
+
37
+ - **misc**: Add imagen model to vertex ai.
38
+
39
+ <br/>
40
+
41
+ <details>
42
+ <summary><kbd>Improvements and Fixes</kbd></summary>
43
+
44
+ #### Styles
45
+
46
+ - **misc**: Add imagen model to vertex ai, closes [#9699](https://github.com/lobehub/lobe-chat/issues/9699) ([3b2a2c1](https://github.com/lobehub/lobe-chat/commit/3b2a2c1))
47
+
48
+ </details>
49
+
50
+ <div align="right">
51
+
52
+ [![](https://img.shields.io/badge/-BACK_TO_TOP-151515?style=flat-square)](#readme-top)
53
+
54
+ </div>
55
+
5
56
  ### [Version 1.137.4](https://github.com/lobehub/lobe-chat/compare/v1.137.3...v1.137.4)
6
57
 
7
58
  <sup>Released on **2025-10-14**</sup>
package/changelog/v1.json CHANGED
@@ -1,4 +1,22 @@
1
1
  [
2
+ {
3
+ "children": {
4
+ "fixes": [
5
+ "Update Claude workflows to use oauth token, vertext ai create image."
6
+ ]
7
+ },
8
+ "date": "2025-10-14",
9
+ "version": "1.137.6"
10
+ },
11
+ {
12
+ "children": {
13
+ "improvements": [
14
+ "Add imagen model to vertex ai."
15
+ ]
16
+ },
17
+ "date": "2025-10-14",
18
+ "version": "1.137.5"
19
+ },
2
20
  {
3
21
  "children": {
4
22
  "fixes": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lobehub/chat",
3
- "version": "1.137.4",
3
+ "version": "1.137.6",
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",
@@ -1,5 +1,7 @@
1
1
  import { filesPrompts } from '@lobechat/prompts';
2
- import { imageUrlToBase64, isLocalUrl, parseDataUri } from '@lobechat/utils';
2
+ import { imageUrlToBase64 } from '@lobechat/utils/imageToBase64';
3
+ import { parseDataUri } from '@lobechat/utils/uriParser';
4
+ import { isDesktopLocalStaticServerUrl } from '@lobechat/utils/url';
3
5
  import debug from 'debug';
4
6
 
5
7
  import { BaseProcessor } from '../base/BaseProcessor';
@@ -277,7 +279,7 @@ export class MessageContentProcessor extends BaseProcessor {
277
279
  const { type } = parseDataUri(image.url);
278
280
 
279
281
  let processedUrl = image.url;
280
- if (type === 'url' && isLocalUrl(image.url)) {
282
+ if (type === 'url' && isDesktopLocalStaticServerUrl(image.url)) {
281
283
  const { base64, mimeType } = await imageUrlToBase64(image.url);
282
284
  processedUrl = `data:${mimeType};base64,${base64}`;
283
285
  }