@lobehub/lobehub 2.0.0-next.4 → 2.0.0-next.5
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 +25 -0
- package/changelog/v1.json +9 -0
- package/docs/development/database-schema.dbml +11 -1
- package/docs/self-hosting/advanced/online-search.mdx +30 -25
- package/docs/self-hosting/advanced/online-search.zh-CN.mdx +25 -23
- package/package.json +1 -1
- package/packages/database/migrations/0041_improve_index.sql +10 -0
- package/packages/database/migrations/meta/0041_snapshot.json +7784 -0
- package/packages/database/migrations/meta/_journal.json +7 -0
- package/packages/database/src/core/migrations.json +17 -0
- package/packages/database/src/models/session.ts +60 -19
- package/packages/database/src/schemas/agent.ts +10 -11
- package/packages/database/src/schemas/message.ts +5 -1
- package/packages/database/src/schemas/relations.ts +6 -4
- package/packages/database/src/schemas/session.ts +2 -0
- package/packages/database/src/schemas/topic.ts +6 -1
- package/packages/model-bank/src/aiModels/anthropic.ts +0 -63
- package/packages/model-bank/src/aiModels/higress.ts +0 -55
- package/packages/model-bank/src/aiModels/infiniai.ts +21 -0
- package/packages/model-bank/src/aiModels/ollamacloud.ts +13 -0
- package/packages/model-bank/src/aiModels/siliconcloud.ts +19 -0
- package/packages/model-runtime/src/core/streams/openai/__snapshots__/responsesStream.test.ts.snap +0 -38
- package/packages/model-runtime/src/providers/minimax/index.ts +5 -5
- package/packages/model-runtime/src/providers/search1api/index.test.ts +2 -2
- package/packages/web-crawler/src/crawImpl/firecrawl.ts +39 -12
- package/scripts/migrateServerDB/index.ts +2 -1
- package/src/config/modelProviders/anthropic.ts +0 -23
- package/src/config/modelProviders/higress.ts +0 -23
- package/src/config/modelProviders/minimax.ts +1 -1
- package/src/config/modelProviders/qiniu.ts +1 -1
- package/src/server/routers/lambda/session.ts +8 -5
- package/src/server/services/search/impls/firecrawl/index.ts +51 -11
- package/src/server/services/search/impls/firecrawl/type.ts +60 -9
- package/src/services/user/client.test.ts +4 -1
|
@@ -287,6 +287,13 @@
|
|
|
287
287
|
"when": 1761563458595,
|
|
288
288
|
"tag": "0040_improve_user_memory_field",
|
|
289
289
|
"breakpoints": true
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"idx": 41,
|
|
293
|
+
"version": "7",
|
|
294
|
+
"when": 1761878697451,
|
|
295
|
+
"tag": "0041_improve_index",
|
|
296
|
+
"breakpoints": true
|
|
290
297
|
}
|
|
291
298
|
],
|
|
292
299
|
"version": "6"
|
|
@@ -729,5 +729,22 @@
|
|
|
729
729
|
"bps": true,
|
|
730
730
|
"folderMillis": 1761563458595,
|
|
731
731
|
"hash": "c483b3ea859f78f687737229bd40f6fef5486aaf05fa20a8ea6961c2a8f0cc64"
|
|
732
|
+
},
|
|
733
|
+
{
|
|
734
|
+
"sql": [
|
|
735
|
+
"CREATE INDEX IF NOT EXISTS \"agents_files_agent_id_idx\" ON \"agents_files\" USING btree (\"agent_id\");",
|
|
736
|
+
"\nCREATE INDEX IF NOT EXISTS \"message_groups_topic_id_idx\" ON \"message_groups\" USING btree (\"topic_id\");",
|
|
737
|
+
"\nCREATE INDEX IF NOT EXISTS \"messages_agent_id_idx\" ON \"messages\" USING btree (\"agent_id\");",
|
|
738
|
+
"\nCREATE INDEX IF NOT EXISTS \"agents_to_sessions_session_id_idx\" ON \"agents_to_sessions\" USING btree (\"session_id\");",
|
|
739
|
+
"\nCREATE INDEX IF NOT EXISTS \"agents_to_sessions_agent_id_idx\" ON \"agents_to_sessions\" USING btree (\"agent_id\");",
|
|
740
|
+
"\nCREATE INDEX IF NOT EXISTS \"sessions_user_id_updated_at_idx\" ON \"sessions\" USING btree (\"user_id\",\"updated_at\");",
|
|
741
|
+
"\nCREATE INDEX IF NOT EXISTS \"sessions_group_id_idx\" ON \"sessions\" USING btree (\"group_id\");",
|
|
742
|
+
"\nCREATE INDEX IF NOT EXISTS \"threads_topic_id_idx\" ON \"threads\" USING btree (\"topic_id\");",
|
|
743
|
+
"\nCREATE INDEX IF NOT EXISTS \"topics_session_id_idx\" ON \"topics\" USING btree (\"session_id\");",
|
|
744
|
+
"\nCREATE INDEX IF NOT EXISTS \"topics_group_id_idx\" ON \"topics\" USING btree (\"group_id\");\n"
|
|
745
|
+
],
|
|
746
|
+
"bps": true,
|
|
747
|
+
"folderMillis": 1761878697451,
|
|
748
|
+
"hash": "2f740719356c4ea28a4f71b6089595871f6e185e056b43288a7d16f7d0aae196"
|
|
732
749
|
}
|
|
733
750
|
]
|
|
@@ -53,13 +53,44 @@ export class SessionModel {
|
|
|
53
53
|
query = async ({ current = 0, pageSize = 9999 } = {}) => {
|
|
54
54
|
const offset = current * pageSize;
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
// Use leftJoin instead of nested with for better performance
|
|
57
|
+
const result = await this.db
|
|
58
|
+
.select({
|
|
59
|
+
// Agent fields (from agentsToSessions join)
|
|
60
|
+
agent: agents,
|
|
61
|
+
// Group fields
|
|
62
|
+
group: sessionGroups,
|
|
63
|
+
// Session fields
|
|
64
|
+
session: sessions,
|
|
65
|
+
})
|
|
66
|
+
.from(sessions)
|
|
67
|
+
.leftJoin(agentsToSessions, eq(sessions.id, agentsToSessions.sessionId))
|
|
68
|
+
.leftJoin(agents, eq(agentsToSessions.agentId, agents.id))
|
|
69
|
+
.leftJoin(sessionGroups, eq(sessions.groupId, sessionGroups.id))
|
|
70
|
+
.where(and(eq(sessions.userId, this.userId), not(eq(sessions.slug, INBOX_SESSION_ID))))
|
|
71
|
+
.orderBy(desc(sessions.updatedAt))
|
|
72
|
+
.limit(pageSize)
|
|
73
|
+
.offset(offset);
|
|
74
|
+
|
|
75
|
+
// Group results by session (since leftJoin can create multiple rows per session)
|
|
76
|
+
// Use Map to preserve order
|
|
77
|
+
const groupedResults = new Map<string, any>();
|
|
78
|
+
|
|
79
|
+
for (const row of result) {
|
|
80
|
+
const sessionId = row.session.id;
|
|
81
|
+
if (!groupedResults.has(sessionId)) {
|
|
82
|
+
groupedResults.set(sessionId, {
|
|
83
|
+
...row.session,
|
|
84
|
+
agentsToSessions: [],
|
|
85
|
+
group: row.group,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (row.agent) {
|
|
89
|
+
groupedResults.get(sessionId)!.agentsToSessions.push({ agent: row.agent });
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return Array.from(groupedResults.values());
|
|
63
94
|
};
|
|
64
95
|
|
|
65
96
|
queryWithGroups = async (): Promise<ChatSessionList> => {
|
|
@@ -71,9 +102,11 @@ export class SessionModel {
|
|
|
71
102
|
where: eq(sessions.userId, this.userId),
|
|
72
103
|
});
|
|
73
104
|
|
|
105
|
+
const mappedSessions = result.map((item) => this.mapSessionItem(item as any));
|
|
106
|
+
|
|
74
107
|
return {
|
|
75
108
|
sessionGroups: groups as unknown as ChatSessionList['sessionGroups'],
|
|
76
|
-
sessions:
|
|
109
|
+
sessions: mappedSessions,
|
|
77
110
|
};
|
|
78
111
|
};
|
|
79
112
|
|
|
@@ -332,7 +365,7 @@ export class SessionModel {
|
|
|
332
365
|
.delete(agentsToSessions)
|
|
333
366
|
.where(and(eq(agentsToSessions.sessionId, id), eq(agentsToSessions.userId, this.userId)));
|
|
334
367
|
|
|
335
|
-
// Delete the session
|
|
368
|
+
// Delete the session (this will cascade delete messages, topics, etc.)
|
|
336
369
|
const result = await trx
|
|
337
370
|
.delete(sessions)
|
|
338
371
|
.where(and(eq(sessions.id, id), eq(sessions.userId, this.userId)));
|
|
@@ -397,17 +430,25 @@ export class SessionModel {
|
|
|
397
430
|
};
|
|
398
431
|
|
|
399
432
|
clearOrphanAgent = async (agentIds: string[], trx: any) => {
|
|
400
|
-
|
|
401
|
-
for (const agentId of agentIds) {
|
|
402
|
-
const remaining = await trx
|
|
403
|
-
.select()
|
|
404
|
-
.from(agentsToSessions)
|
|
405
|
-
.where(eq(agentsToSessions.agentId, agentId))
|
|
406
|
-
.limit(1);
|
|
433
|
+
if (agentIds.length === 0) return;
|
|
407
434
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
}
|
|
435
|
+
// Batch query to find which agents still have sessions
|
|
436
|
+
const remainingLinks = (await trx
|
|
437
|
+
.select({ agentId: agentsToSessions.agentId })
|
|
438
|
+
.from(agentsToSessions)
|
|
439
|
+
.where(inArray(agentsToSessions.agentId, agentIds))) as { agentId: string }[];
|
|
440
|
+
|
|
441
|
+
const linkedAgentIds = new Set(remainingLinks.map((link) => link.agentId));
|
|
442
|
+
|
|
443
|
+
// Find orphaned agents (those not in the linked set)
|
|
444
|
+
const orphanedAgentIds = agentIds.filter((id) => !linkedAgentIds.has(id));
|
|
445
|
+
|
|
446
|
+
// Batch delete orphaned agents (this will cascade to agentsFiles, agentsKnowledgeBases, etc.)
|
|
447
|
+
// and SET NULL on messages.agentId
|
|
448
|
+
if (orphanedAgentIds.length > 0) {
|
|
449
|
+
await trx
|
|
450
|
+
.delete(agents)
|
|
451
|
+
.where(and(inArray(agents.id, orphanedAgentIds), eq(agents.userId, this.userId)));
|
|
411
452
|
}
|
|
412
453
|
};
|
|
413
454
|
|
|
@@ -62,11 +62,11 @@ export const agents = pgTable(
|
|
|
62
62
|
|
|
63
63
|
...timestamps,
|
|
64
64
|
},
|
|
65
|
-
(t) =>
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
(t) => [
|
|
66
|
+
uniqueIndex('client_id_user_id_unique').on(t.clientId, t.userId),
|
|
67
|
+
index('agents_title_idx').on(t.title),
|
|
68
|
+
index('agents_description_idx').on(t.description),
|
|
69
|
+
],
|
|
70
70
|
);
|
|
71
71
|
|
|
72
72
|
export const insertAgentSchema = createInsertSchema(agents);
|
|
@@ -90,9 +90,7 @@ export const agentsKnowledgeBases = pgTable(
|
|
|
90
90
|
|
|
91
91
|
...timestamps,
|
|
92
92
|
},
|
|
93
|
-
(t) => ({
|
|
94
|
-
pk: primaryKey({ columns: [t.agentId, t.knowledgeBaseId] }),
|
|
95
|
-
}),
|
|
93
|
+
(t) => [primaryKey({ columns: [t.agentId, t.knowledgeBaseId] })],
|
|
96
94
|
);
|
|
97
95
|
|
|
98
96
|
export const agentsFiles = pgTable(
|
|
@@ -111,7 +109,8 @@ export const agentsFiles = pgTable(
|
|
|
111
109
|
|
|
112
110
|
...timestamps,
|
|
113
111
|
},
|
|
114
|
-
(t) =>
|
|
115
|
-
|
|
116
|
-
|
|
112
|
+
(t) => [
|
|
113
|
+
primaryKey({ columns: [t.fileId, t.agentId, t.userId] }),
|
|
114
|
+
index('agents_files_agent_id_idx').on(t.agentId),
|
|
115
|
+
],
|
|
117
116
|
);
|
|
@@ -62,7 +62,10 @@ export const messageGroups = pgTable(
|
|
|
62
62
|
|
|
63
63
|
...timestamps,
|
|
64
64
|
},
|
|
65
|
-
(t) => [
|
|
65
|
+
(t) => [
|
|
66
|
+
uniqueIndex('message_groups_client_id_user_id_unique').on(t.clientId, t.userId),
|
|
67
|
+
index('message_groups_topic_id_idx').on(t.topicId),
|
|
68
|
+
],
|
|
66
69
|
);
|
|
67
70
|
|
|
68
71
|
export const insertMessageGroupSchema = createInsertSchema(messageGroups);
|
|
@@ -130,6 +133,7 @@ export const messages = pgTable(
|
|
|
130
133
|
index('messages_user_id_idx').on(table.userId),
|
|
131
134
|
index('messages_session_id_idx').on(table.sessionId),
|
|
132
135
|
index('messages_thread_id_idx').on(table.threadId),
|
|
136
|
+
index('messages_agent_id_idx').on(table.agentId),
|
|
133
137
|
],
|
|
134
138
|
);
|
|
135
139
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
2
2
|
import { relations } from 'drizzle-orm';
|
|
3
|
-
import { pgTable, primaryKey, text, uuid, varchar } from 'drizzle-orm/pg-core';
|
|
3
|
+
import { index, pgTable, primaryKey, text, uuid, varchar } from 'drizzle-orm/pg-core';
|
|
4
4
|
|
|
5
5
|
import { createdAt } from './_helpers';
|
|
6
6
|
import { agents, agentsFiles, agentsKnowledgeBases } from './agent';
|
|
@@ -28,9 +28,11 @@ export const agentsToSessions = pgTable(
|
|
|
28
28
|
.references(() => users.id, { onDelete: 'cascade' })
|
|
29
29
|
.notNull(),
|
|
30
30
|
},
|
|
31
|
-
(t) =>
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
(t) => [
|
|
32
|
+
primaryKey({ columns: [t.agentId, t.sessionId] }),
|
|
33
|
+
index('agents_to_sessions_session_id_idx').on(t.sessionId),
|
|
34
|
+
index('agents_to_sessions_agent_id_idx').on(t.agentId),
|
|
35
|
+
],
|
|
34
36
|
);
|
|
35
37
|
|
|
36
38
|
export const filesToSessions = pgTable(
|
|
@@ -70,6 +70,8 @@ export const sessions = pgTable(
|
|
|
70
70
|
|
|
71
71
|
index('sessions_user_id_idx').on(t.userId),
|
|
72
72
|
index('sessions_id_user_id_idx').on(t.id, t.userId),
|
|
73
|
+
index('sessions_user_id_updated_at_idx').on(t.userId, t.updatedAt),
|
|
74
|
+
index('sessions_group_id_idx').on(t.groupId),
|
|
73
75
|
],
|
|
74
76
|
);
|
|
75
77
|
|
|
@@ -33,6 +33,8 @@ export const topics = pgTable(
|
|
|
33
33
|
uniqueIndex('topics_client_id_user_id_unique').on(t.clientId, t.userId),
|
|
34
34
|
index('topics_user_id_idx').on(t.userId),
|
|
35
35
|
index('topics_id_user_id_idx').on(t.id, t.userId),
|
|
36
|
+
index('topics_session_id_idx').on(t.sessionId),
|
|
37
|
+
index('topics_group_id_idx').on(t.groupId),
|
|
36
38
|
],
|
|
37
39
|
);
|
|
38
40
|
|
|
@@ -65,7 +67,10 @@ export const threads = pgTable(
|
|
|
65
67
|
lastActiveAt: timestamptz('last_active_at').defaultNow(),
|
|
66
68
|
...timestamps,
|
|
67
69
|
},
|
|
68
|
-
(t) => [
|
|
70
|
+
(t) => [
|
|
71
|
+
uniqueIndex('threads_client_id_user_id_unique').on(t.clientId, t.userId),
|
|
72
|
+
index('threads_topic_id_idx').on(t.topicId),
|
|
73
|
+
],
|
|
69
74
|
);
|
|
70
75
|
|
|
71
76
|
export type NewThread = typeof threads.$inferInsert;
|
|
@@ -197,69 +197,6 @@ const anthropicChatModels: AIChatModelCard[] = [
|
|
|
197
197
|
},
|
|
198
198
|
type: 'chat',
|
|
199
199
|
},
|
|
200
|
-
{
|
|
201
|
-
abilities: {
|
|
202
|
-
functionCall: true,
|
|
203
|
-
search: true,
|
|
204
|
-
vision: true,
|
|
205
|
-
},
|
|
206
|
-
contextWindowTokens: 200_000,
|
|
207
|
-
description:
|
|
208
|
-
'Claude 3.5 Sonnet 提供了超越 Opus 的能力和比 Sonnet 更快的速度,同时保持与 Sonnet 相同的价格。Sonnet 特别擅长编程、数据科学、视觉处理、代理任务。',
|
|
209
|
-
displayName: 'Claude 3.5 Sonnet (New)',
|
|
210
|
-
id: 'claude-3-5-sonnet-20241022',
|
|
211
|
-
maxOutput: 8192,
|
|
212
|
-
pricing: {
|
|
213
|
-
units: [
|
|
214
|
-
{ name: 'textInput_cacheRead', rate: 0.3, strategy: 'fixed', unit: 'millionTokens' },
|
|
215
|
-
{ name: 'textInput', rate: 3, strategy: 'fixed', unit: 'millionTokens' },
|
|
216
|
-
{ name: 'textOutput', rate: 15, strategy: 'fixed', unit: 'millionTokens' },
|
|
217
|
-
{
|
|
218
|
-
lookup: { prices: { '1h': 6, '5m': 3.75 }, pricingParams: ['ttl'] },
|
|
219
|
-
name: 'textInput_cacheWrite',
|
|
220
|
-
strategy: 'lookup',
|
|
221
|
-
unit: 'millionTokens',
|
|
222
|
-
},
|
|
223
|
-
],
|
|
224
|
-
},
|
|
225
|
-
releasedAt: '2024-10-22',
|
|
226
|
-
settings: {
|
|
227
|
-
extendParams: ['disableContextCaching'],
|
|
228
|
-
searchImpl: 'params',
|
|
229
|
-
},
|
|
230
|
-
type: 'chat',
|
|
231
|
-
},
|
|
232
|
-
{
|
|
233
|
-
abilities: {
|
|
234
|
-
functionCall: true,
|
|
235
|
-
vision: true,
|
|
236
|
-
},
|
|
237
|
-
contextWindowTokens: 200_000,
|
|
238
|
-
description:
|
|
239
|
-
'Claude 3.5 Sonnet 提供了超越 Opus 的能力和比 Sonnet 更快的速度,同时保持与 Sonnet 相同的价格。Sonnet 特别擅长编程、数据科学、视觉处理、代理任务。',
|
|
240
|
-
displayName: 'Claude 3.5 Sonnet (Old)',
|
|
241
|
-
id: 'claude-3-5-sonnet-20240620',
|
|
242
|
-
maxOutput: 8192,
|
|
243
|
-
pricing: {
|
|
244
|
-
units: [
|
|
245
|
-
{ name: 'textInput_cacheRead', rate: 0.3, strategy: 'fixed', unit: 'millionTokens' },
|
|
246
|
-
{ name: 'textInput', rate: 3, strategy: 'fixed', unit: 'millionTokens' },
|
|
247
|
-
{ name: 'textOutput', rate: 15, strategy: 'fixed', unit: 'millionTokens' },
|
|
248
|
-
{
|
|
249
|
-
lookup: { prices: { '1h': 6, '5m': 3.75 }, pricingParams: ['ttl'] },
|
|
250
|
-
name: 'textInput_cacheWrite',
|
|
251
|
-
strategy: 'lookup',
|
|
252
|
-
unit: 'millionTokens',
|
|
253
|
-
},
|
|
254
|
-
],
|
|
255
|
-
},
|
|
256
|
-
releasedAt: '2024-06-20',
|
|
257
|
-
settings: {
|
|
258
|
-
extendParams: ['disableContextCaching'],
|
|
259
|
-
searchImpl: 'params',
|
|
260
|
-
},
|
|
261
|
-
type: 'chat',
|
|
262
|
-
},
|
|
263
200
|
{
|
|
264
201
|
abilities: {
|
|
265
202
|
functionCall: true,
|
|
@@ -2393,61 +2393,6 @@ const higressChatModels: AIChatModelCard[] = [
|
|
|
2393
2393
|
releasedAt: '2024-11-05',
|
|
2394
2394
|
type: 'chat',
|
|
2395
2395
|
},
|
|
2396
|
-
{
|
|
2397
|
-
abilities: {
|
|
2398
|
-
functionCall: true,
|
|
2399
|
-
vision: true,
|
|
2400
|
-
},
|
|
2401
|
-
contextWindowTokens: 200_000,
|
|
2402
|
-
description:
|
|
2403
|
-
'Claude 3.5 Sonnet 提供了超越 Opus 的能力和比 Sonnet 更快的速度,同时保持与 Sonnet 相同的价格。Sonnet 特别擅长编程、数据科学、视觉处理、代理任务。',
|
|
2404
|
-
displayName: 'Claude 3.5 Sonnet',
|
|
2405
|
-
enabled: true,
|
|
2406
|
-
id: 'claude-3-5-sonnet-20241022',
|
|
2407
|
-
maxOutput: 8192,
|
|
2408
|
-
pricing: {
|
|
2409
|
-
units: [
|
|
2410
|
-
{ name: 'textInput_cacheRead', rate: 0.3, strategy: 'fixed', unit: 'millionTokens' },
|
|
2411
|
-
{ name: 'textInput', rate: 3, strategy: 'fixed', unit: 'millionTokens' },
|
|
2412
|
-
{ name: 'textOutput', rate: 15, strategy: 'fixed', unit: 'millionTokens' },
|
|
2413
|
-
{
|
|
2414
|
-
lookup: { prices: { '5m': 3.75 }, pricingParams: ['ttl'] },
|
|
2415
|
-
name: 'textInput_cacheWrite',
|
|
2416
|
-
strategy: 'lookup',
|
|
2417
|
-
unit: 'millionTokens',
|
|
2418
|
-
},
|
|
2419
|
-
],
|
|
2420
|
-
},
|
|
2421
|
-
releasedAt: '2024-10-22',
|
|
2422
|
-
type: 'chat',
|
|
2423
|
-
},
|
|
2424
|
-
{
|
|
2425
|
-
abilities: {
|
|
2426
|
-
functionCall: true,
|
|
2427
|
-
vision: true,
|
|
2428
|
-
},
|
|
2429
|
-
contextWindowTokens: 200_000,
|
|
2430
|
-
description:
|
|
2431
|
-
'Claude 3.5 Sonnet 提供了超越 Opus 的能力和比 Sonnet 更快的速度,同时保持与 Sonnet 相同的价格。Sonnet 特别擅长编程、数据科学、视觉处理、代理任务。',
|
|
2432
|
-
displayName: 'Claude 3.5 Sonnet 0620',
|
|
2433
|
-
id: 'claude-3-5-sonnet-20240620',
|
|
2434
|
-
maxOutput: 8192,
|
|
2435
|
-
pricing: {
|
|
2436
|
-
units: [
|
|
2437
|
-
{ name: 'textInput_cacheRead', rate: 0.3, strategy: 'fixed', unit: 'millionTokens' },
|
|
2438
|
-
{ name: 'textInput', rate: 3, strategy: 'fixed', unit: 'millionTokens' },
|
|
2439
|
-
{ name: 'textOutput', rate: 15, strategy: 'fixed', unit: 'millionTokens' },
|
|
2440
|
-
{
|
|
2441
|
-
lookup: { prices: { '5m': 3.75 }, pricingParams: ['ttl'] },
|
|
2442
|
-
name: 'textInput_cacheWrite',
|
|
2443
|
-
strategy: 'lookup',
|
|
2444
|
-
unit: 'millionTokens',
|
|
2445
|
-
},
|
|
2446
|
-
],
|
|
2447
|
-
},
|
|
2448
|
-
releasedAt: '2024-06-20',
|
|
2449
|
-
type: 'chat',
|
|
2450
|
-
},
|
|
2451
2396
|
{
|
|
2452
2397
|
abilities: {
|
|
2453
2398
|
functionCall: true,
|
|
@@ -3,6 +3,27 @@ import { AIChatModelCard } from '../types/aiModel';
|
|
|
3
3
|
// https://cloud.infini-ai.com/genstudio/model
|
|
4
4
|
|
|
5
5
|
const infiniaiChatModels: AIChatModelCard[] = [
|
|
6
|
+
{
|
|
7
|
+
abilities: {
|
|
8
|
+
functionCall: true,
|
|
9
|
+
reasoning: true,
|
|
10
|
+
},
|
|
11
|
+
contextWindowTokens: 200_000,
|
|
12
|
+
description:
|
|
13
|
+
'MiniMax-M2 是一款专为编码与智能体工作流优化的专家混合(MoE)语言模型,具有约 230B 总参数与约 10B 活跃参数。它在保持强通用智能的同时,针对多文件编辑、代码-运行-修复闭环、测试校验修复等开发者场景进行深度增强,在终端、IDE 与 CI 等真实环境中表现稳定、高效。',
|
|
14
|
+
displayName: 'MiniMax M2',
|
|
15
|
+
enabled: true,
|
|
16
|
+
id: 'minimax-m2',
|
|
17
|
+
maxOutput: 200_000,
|
|
18
|
+
pricing: {
|
|
19
|
+
currency: 'CNY',
|
|
20
|
+
units: [
|
|
21
|
+
{ name: 'textInput', rate: 2.1, strategy: 'fixed', unit: 'millionTokens' },
|
|
22
|
+
{ name: 'textOutput', rate: 8.4, strategy: 'fixed', unit: 'millionTokens' },
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
|
+
type: 'chat',
|
|
26
|
+
},
|
|
6
27
|
{
|
|
7
28
|
abilities: {
|
|
8
29
|
functionCall: true,
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
import { AIChatModelCard } from '../types/aiModel';
|
|
2
2
|
|
|
3
3
|
const ollamaCloudModels: AIChatModelCard[] = [
|
|
4
|
+
{
|
|
5
|
+
abilities: {
|
|
6
|
+
functionCall: true,
|
|
7
|
+
reasoning: true,
|
|
8
|
+
},
|
|
9
|
+
contextWindowTokens: 200_000,
|
|
10
|
+
description:
|
|
11
|
+
'MiniMax M2 是专为编码和代理工作流程构建的高效大型语言模型。',
|
|
12
|
+
displayName: 'MiniMax M2',
|
|
13
|
+
enabled: true,
|
|
14
|
+
id: 'minimax-m2',
|
|
15
|
+
type: 'chat',
|
|
16
|
+
},
|
|
4
17
|
{
|
|
5
18
|
abilities: {
|
|
6
19
|
functionCall: true,
|
|
@@ -145,6 +145,25 @@ const siliconcloudChatModels: AIChatModelCard[] = [
|
|
|
145
145
|
releasedAt: '2025-09-01',
|
|
146
146
|
type: 'chat',
|
|
147
147
|
},
|
|
148
|
+
{
|
|
149
|
+
abilities: {
|
|
150
|
+
functionCall: true,
|
|
151
|
+
},
|
|
152
|
+
contextWindowTokens: 131_072,
|
|
153
|
+
description:
|
|
154
|
+
'KAT-Dev(32B)是一款专为软件工程任务设计的开源 32B 参数模型。在 SWE-Bench Verified 基准测试中,它取得了 62.4% 的解决率,在所有不同规模的开源模型中排名第五。该模型通过多个阶段进行优化,包括中间训练、监督微调(SFT)与强化学习(RL),旨在为代码补全、缺陷修复、代码评审等复杂编程任务提供强大支持。',
|
|
155
|
+
displayName: 'KAT-Dev 32B',
|
|
156
|
+
id: 'Kwaipilot/KAT-Dev',
|
|
157
|
+
pricing: {
|
|
158
|
+
currency: 'CNY',
|
|
159
|
+
units: [
|
|
160
|
+
{ name: 'textInput', rate: 1, strategy: 'fixed', unit: 'millionTokens' },
|
|
161
|
+
{ name: 'textOutput', rate: 4, strategy: 'fixed', unit: 'millionTokens' },
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
releasedAt: '2025-09-27',
|
|
165
|
+
type: 'chat',
|
|
166
|
+
},
|
|
148
167
|
{
|
|
149
168
|
abilities: {
|
|
150
169
|
functionCall: true,
|
package/packages/model-runtime/src/core/streams/openai/__snapshots__/responsesStream.test.ts.snap
CHANGED
|
@@ -145,25 +145,6 @@ exports[`OpenAIResponsesStream > Reasoning > summary 1`] = `
|
|
|
145
145
|
]
|
|
146
146
|
`;
|
|
147
147
|
|
|
148
|
-
exports[`OpenAIResponsesStream > should handle chunk errors in catch block 1`] = `
|
|
149
|
-
[
|
|
150
|
-
"id: resp_error_catch
|
|
151
|
-
",
|
|
152
|
-
"event: data
|
|
153
|
-
",
|
|
154
|
-
"data: "in_progress"
|
|
155
|
-
|
|
156
|
-
",
|
|
157
|
-
"id: undefined
|
|
158
|
-
",
|
|
159
|
-
"event: reasoning
|
|
160
|
-
",
|
|
161
|
-
"data: undefined
|
|
162
|
-
|
|
163
|
-
",
|
|
164
|
-
]
|
|
165
|
-
`;
|
|
166
|
-
|
|
167
148
|
exports[`OpenAIResponsesStream > should handle chunks with undefined values gracefully 1`] = `
|
|
168
149
|
[
|
|
169
150
|
"id: resp_undefined_vals
|
|
@@ -546,25 +527,6 @@ exports[`OpenAIResponsesStream > should handle response.reasoning_summary_text.d
|
|
|
546
527
|
]
|
|
547
528
|
`;
|
|
548
529
|
|
|
549
|
-
exports[`OpenAIResponsesStream > should handle stream chunk transformation error with null access 1`] = `
|
|
550
|
-
[
|
|
551
|
-
"id: resp_error_test
|
|
552
|
-
",
|
|
553
|
-
"event: data
|
|
554
|
-
",
|
|
555
|
-
"data: "in_progress"
|
|
556
|
-
|
|
557
|
-
",
|
|
558
|
-
"id: null
|
|
559
|
-
",
|
|
560
|
-
"event: text
|
|
561
|
-
",
|
|
562
|
-
"data: "test"
|
|
563
|
-
|
|
564
|
-
",
|
|
565
|
-
]
|
|
566
|
-
`;
|
|
567
|
-
|
|
568
530
|
exports[`OpenAIResponsesStream > should handle unknown chunk type as data 1`] = `
|
|
569
531
|
[
|
|
570
532
|
"id: resp_unknown
|
|
@@ -17,11 +17,11 @@ export const LobeMinimaxAI = createOpenAICompatibleRuntime({
|
|
|
17
17
|
|
|
18
18
|
const minimaxTools = enabledSearch
|
|
19
19
|
? [
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
...(tools || []),
|
|
21
|
+
{
|
|
22
|
+
type: 'web_search',
|
|
23
|
+
},
|
|
24
|
+
]
|
|
25
25
|
: tools;
|
|
26
26
|
|
|
27
27
|
// Resolve parameters with constraints
|
|
@@ -830,9 +830,9 @@ describe('LobeSearch1API - custom features', () => {
|
|
|
830
830
|
it('should handle mix of known and unknown models', async () => {
|
|
831
831
|
mockClient.models.list.mockResolvedValue({
|
|
832
832
|
data: [
|
|
833
|
-
{ id: 'gpt-4o
|
|
833
|
+
{ id: 'gpt-4o' },
|
|
834
834
|
{ id: 'unknown-model-1' },
|
|
835
|
-
{ id: '
|
|
835
|
+
{ id: 'gpt-4o-mini' },
|
|
836
836
|
{ id: 'unknown-model-2' },
|
|
837
837
|
],
|
|
838
838
|
});
|
|
@@ -3,36 +3,55 @@ import { NetworkConnectionError, PageNotFoundError, TimeoutError } from '../util
|
|
|
3
3
|
import { DEFAULT_TIMEOUT, withTimeout } from '../utils/withTimeout';
|
|
4
4
|
|
|
5
5
|
interface FirecrawlMetadata {
|
|
6
|
-
description
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
description?: string;
|
|
7
|
+
error?: string;
|
|
8
|
+
keywords?: string;
|
|
9
|
+
language?: string;
|
|
9
10
|
ogDescription?: string;
|
|
10
11
|
ogImage?: string;
|
|
11
12
|
ogLocaleAlternate?: string[];
|
|
12
13
|
ogSiteName?: string;
|
|
13
14
|
ogTitle?: string;
|
|
14
15
|
ogUrl?: string;
|
|
15
|
-
robots
|
|
16
|
-
statusCode: number;
|
|
16
|
+
robots?: string;
|
|
17
17
|
sourceURL: string;
|
|
18
|
-
|
|
18
|
+
statusCode: number;
|
|
19
|
+
title?: string;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
interface FirecrawlResults {
|
|
23
|
+
actions?: {
|
|
24
|
+
javascriptReturns?: Array<{ type: string; value: any }>;
|
|
25
|
+
pdfs?: string[];
|
|
26
|
+
scrapes?: Array<{ html: string; url: string }>;
|
|
27
|
+
screenshots?: string[];
|
|
28
|
+
};
|
|
29
|
+
changeTracking?: {
|
|
30
|
+
changeStatus?: string;
|
|
31
|
+
diff?: string;
|
|
32
|
+
json?: Record<string, any>;
|
|
33
|
+
previousScrapeAt?: string;
|
|
34
|
+
visibility?: string;
|
|
35
|
+
};
|
|
22
36
|
html?: string;
|
|
37
|
+
links?: string[];
|
|
23
38
|
markdown?: string;
|
|
24
39
|
metadata: FirecrawlMetadata;
|
|
40
|
+
rawHtml?: string;
|
|
41
|
+
screenshot?: string;
|
|
42
|
+
summary?: string;
|
|
43
|
+
warning?: string;
|
|
25
44
|
}
|
|
26
45
|
|
|
27
46
|
interface FirecrawlResponse {
|
|
28
|
-
success: boolean;
|
|
29
47
|
data: FirecrawlResults;
|
|
48
|
+
success: boolean;
|
|
30
49
|
}
|
|
31
50
|
|
|
32
51
|
export const firecrawl: CrawlImpl = async (url) => {
|
|
33
52
|
// Get API key from environment variable
|
|
34
53
|
const apiKey = process.env.FIRECRAWL_API_KEY;
|
|
35
|
-
const baseUrl = process.env.FIRECRAWL_URL || 'https://api.firecrawl.dev/
|
|
54
|
+
const baseUrl = process.env.FIRECRAWL_URL || 'https://api.firecrawl.dev/v2';
|
|
36
55
|
|
|
37
56
|
let res: Response;
|
|
38
57
|
|
|
@@ -40,7 +59,7 @@ export const firecrawl: CrawlImpl = async (url) => {
|
|
|
40
59
|
res = await withTimeout(
|
|
41
60
|
fetch(`${baseUrl}/scrape`, {
|
|
42
61
|
body: JSON.stringify({
|
|
43
|
-
formats: [
|
|
62
|
+
formats: ['markdown'], // ["markdown", "html"]
|
|
44
63
|
url,
|
|
45
64
|
}),
|
|
46
65
|
headers: {
|
|
@@ -75,6 +94,14 @@ export const firecrawl: CrawlImpl = async (url) => {
|
|
|
75
94
|
try {
|
|
76
95
|
const data = (await res.json()) as FirecrawlResponse;
|
|
77
96
|
|
|
97
|
+
if (data.data.warning) {
|
|
98
|
+
console.warn('[Firecrawl] Warning:', data.data.warning);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (data.data.metadata.error) {
|
|
102
|
+
console.error('[Firecrawl] Metadata error:', data.data.metadata.error);
|
|
103
|
+
}
|
|
104
|
+
|
|
78
105
|
// Check if content is empty or too short
|
|
79
106
|
if (!data.data.markdown || data.data.markdown.length < 100) {
|
|
80
107
|
return;
|
|
@@ -83,14 +110,14 @@ export const firecrawl: CrawlImpl = async (url) => {
|
|
|
83
110
|
return {
|
|
84
111
|
content: data.data.markdown,
|
|
85
112
|
contentType: 'text',
|
|
86
|
-
description: data.data.metadata.description,
|
|
113
|
+
description: data.data.metadata.description || '',
|
|
87
114
|
length: data.data.markdown.length,
|
|
88
115
|
siteName: new URL(url).hostname,
|
|
89
|
-
title: data.data.metadata.title,
|
|
116
|
+
title: data.data.metadata.title || '',
|
|
90
117
|
url: url,
|
|
91
118
|
} satisfies CrawlSuccessResult;
|
|
92
119
|
} catch (error) {
|
|
93
|
-
console.error(error);
|
|
120
|
+
console.error('[Firecrawl] Parse error:', error);
|
|
94
121
|
}
|
|
95
122
|
|
|
96
123
|
return;
|
|
@@ -17,13 +17,14 @@ const isDesktop = process.env.NEXT_PUBLIC_IS_DESKTOP_APP === '1';
|
|
|
17
17
|
const runMigrations = async () => {
|
|
18
18
|
const { serverDB } = await import('../../packages/database/src/server');
|
|
19
19
|
|
|
20
|
+
const time = Date.now();
|
|
20
21
|
if (process.env.DATABASE_DRIVER === 'node') {
|
|
21
22
|
await nodeMigrate(serverDB, { migrationsFolder });
|
|
22
23
|
} else {
|
|
23
24
|
await neonMigrate(serverDB, { migrationsFolder });
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
console.log('✅ database migration pass.');
|
|
27
|
+
console.log('✅ database migration pass. use: %s ms', Date.now() - time);
|
|
27
28
|
// eslint-disable-next-line unicorn/no-process-exit
|
|
28
29
|
process.exit(0);
|
|
29
30
|
};
|