@lobehub/lobehub 2.0.0-next.172 → 2.0.0-next.174
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/.cursor/rules/db-migrations.mdc +10 -4
- package/.cursor/rules/drizzle-schema-style-guide.mdc +33 -17
- package/.cursor/rules/hotkey.mdc +161 -0
- package/.cursor/rules/i18n.mdc +2 -5
- package/.cursor/rules/project-introduce.mdc +3 -2
- package/.cursor/rules/project-structure.mdc +33 -37
- package/.cursor/rules/react.mdc +155 -0
- package/.cursor/rules/recent-data-usage.mdc +138 -0
- package/.cursor/rules/rules-index.mdc +1 -1
- package/.cursor/rules/testing-guide/agent-runtime-e2e.mdc +285 -0
- package/.cursor/rules/testing-guide/zustand-store-action-test.mdc +11 -16
- package/.cursor/rules/typescript.mdc +0 -4
- package/.cursor/rules/zustand-action-patterns.mdc +137 -169
- package/.cursor/rules/zustand-slice-organization.mdc +16 -8
- package/.husky/pre-commit +1 -1
- package/.i18nrc.js +3 -1
- package/AGENTS.md +3 -2
- package/CHANGELOG.md +50 -0
- package/CLAUDE.md +10 -3
- package/GEMINI.md +2 -1
- package/README.md +3 -3
- package/README.zh-CN.md +6 -6
- package/changelog/v1.json +18 -0
- package/docs/development/database-schema.dbml +18 -3
- package/docs/development/state-management/state-management-selectors.mdx +1 -1
- package/glossary.json +8 -0
- package/package.json +3 -3
- package/packages/database/migrations/0063_add_columns_for_several_tables.sql +30 -0
- package/packages/database/migrations/meta/0063_snapshot.json +9113 -0
- package/packages/database/migrations/meta/_journal.json +7 -0
- package/packages/database/src/core/migrations.json +29 -0
- package/packages/database/src/schemas/_helpers.ts +5 -1
- package/packages/database/src/schemas/agent.ts +2 -1
- package/packages/database/src/schemas/aiInfra.ts +18 -3
- package/packages/database/src/schemas/message.ts +11 -6
- package/packages/database/src/schemas/topic.ts +17 -2
- package/packages/database/src/schemas/user.ts +5 -2
- package/packages/database/src/schemas/userMemories.ts +9 -8
- package/packages/model-runtime/src/core/contextBuilders/openai.ts +8 -2
- package/packages/model-runtime/src/providers/github/index.test.ts +4 -4
- package/packages/types/src/topic/thread.ts +24 -0
- package/packages/types/src/user/index.ts +1 -0
- package/packages/types/src/user/onboarding.ts +16 -0
- package/src/features/KnowledgeBaseModal/AssignKnowledgeBase/List.tsx +5 -2
- package/src/store/agent/slices/chat/action.ts +3 -3
- package/vitest.config.mts +3 -0
- package/.cursor/rules/group-chat.mdc +0 -35
- package/.cursor/rules/react-component.mdc +0 -173
|
@@ -441,6 +441,13 @@
|
|
|
441
441
|
"when": 1765728439737,
|
|
442
442
|
"tag": "0062_add_more_index",
|
|
443
443
|
"breakpoints": true
|
|
444
|
+
},
|
|
445
|
+
{
|
|
446
|
+
"idx": 63,
|
|
447
|
+
"version": "7",
|
|
448
|
+
"when": 1766157362540,
|
|
449
|
+
"tag": "0063_add_columns_for_several_tables",
|
|
450
|
+
"breakpoints": true
|
|
444
451
|
}
|
|
445
452
|
],
|
|
446
453
|
"version": "6"
|
|
@@ -994,5 +994,34 @@
|
|
|
994
994
|
"bps": true,
|
|
995
995
|
"folderMillis": 1765728439737,
|
|
996
996
|
"hash": "ba6a6beff2ad39419ec4aa7887539c9db0472e13f9242cb8780ff96eec450268"
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
"sql": [
|
|
1000
|
+
"DROP INDEX IF EXISTS \"user_memories_contexts_title_vector_index\";",
|
|
1001
|
+
"\nALTER TABLE \"agents\" ALTER COLUMN \"plugins\" DROP DEFAULT;",
|
|
1002
|
+
"\nALTER TABLE \"agents\" ADD COLUMN IF NOT EXISTS \"pinned\" boolean;",
|
|
1003
|
+
"\nALTER TABLE \"message_groups\" ADD COLUMN IF NOT EXISTS \"type\" text;",
|
|
1004
|
+
"\nALTER TABLE \"message_groups\" ADD COLUMN IF NOT EXISTS \"content\" text;",
|
|
1005
|
+
"\nALTER TABLE \"message_groups\" ADD COLUMN IF NOT EXISTS \"editor_data\" jsonb;",
|
|
1006
|
+
"\nALTER TABLE \"messages\" ADD COLUMN IF NOT EXISTS \"summary\" text;",
|
|
1007
|
+
"\nALTER TABLE \"threads\" ADD COLUMN IF NOT EXISTS \"agent_id\" text;",
|
|
1008
|
+
"\nALTER TABLE \"threads\" ADD COLUMN IF NOT EXISTS \"group_id\" text;",
|
|
1009
|
+
"\nALTER TABLE \"threads\" ADD COLUMN IF NOT EXISTS \"metadata\" jsonb;",
|
|
1010
|
+
"\nALTER TABLE \"user_settings\" ADD COLUMN IF NOT EXISTS \"memory\" jsonb;",
|
|
1011
|
+
"\nALTER TABLE \"users\" ADD COLUMN IF NOT EXISTS \"interests\" varchar(64)[];",
|
|
1012
|
+
"\nALTER TABLE \"users\" ADD COLUMN IF NOT EXISTS \"onboarding\" jsonb;",
|
|
1013
|
+
"\nDO $$ BEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'threads_agent_id_agents_id_fk') THEN\n ALTER TABLE \"threads\" ADD CONSTRAINT \"threads_agent_id_agents_id_fk\" FOREIGN KEY (\"agent_id\") REFERENCES \"public\".\"agents\"(\"id\") ON DELETE cascade ON UPDATE no action;\n END IF;\nEND $$;",
|
|
1014
|
+
"\nDO $$ BEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'threads_group_id_chat_groups_id_fk') THEN\n ALTER TABLE \"threads\" ADD CONSTRAINT \"threads_group_id_chat_groups_id_fk\" FOREIGN KEY (\"group_id\") REFERENCES \"public\".\"chat_groups\"(\"id\") ON DELETE cascade ON UPDATE no action;\n END IF;\nEND $$;",
|
|
1015
|
+
"\nCREATE INDEX IF NOT EXISTS \"ai_models_user_id_idx\" ON \"ai_models\" USING btree (\"user_id\");",
|
|
1016
|
+
"\nCREATE INDEX IF NOT EXISTS \"ai_providers_user_id_idx\" ON \"ai_providers\" USING btree (\"user_id\");",
|
|
1017
|
+
"\nCREATE INDEX IF NOT EXISTS \"message_groups_type_idx\" ON \"message_groups\" USING btree (\"type\");",
|
|
1018
|
+
"\nCREATE INDEX IF NOT EXISTS \"message_plugins_tool_call_id_idx\" ON \"message_plugins\" USING btree (\"tool_call_id\");",
|
|
1019
|
+
"\nCREATE INDEX IF NOT EXISTS \"threads_agent_id_idx\" ON \"threads\" USING btree (\"agent_id\");",
|
|
1020
|
+
"\nCREATE INDEX IF NOT EXISTS \"threads_group_id_idx\" ON \"threads\" USING btree (\"group_id\");",
|
|
1021
|
+
"\nALTER TABLE \"user_memories_contexts\" DROP COLUMN IF EXISTS \"title_vector\";\n"
|
|
1022
|
+
],
|
|
1023
|
+
"bps": true,
|
|
1024
|
+
"folderMillis": 1766157362540,
|
|
1025
|
+
"hash": "7a8ee107778222390e676951173baa81bfa09dd47216a8467575fca54915172c"
|
|
997
1026
|
}
|
|
998
1027
|
]
|
|
@@ -10,7 +10,11 @@ export const updatedAt = () =>
|
|
|
10
10
|
.notNull()
|
|
11
11
|
.defaultNow()
|
|
12
12
|
.$onUpdate(() => new Date());
|
|
13
|
-
export const accessedAt = () =>
|
|
13
|
+
export const accessedAt = () =>
|
|
14
|
+
timestamptz('accessed_at')
|
|
15
|
+
.notNull()
|
|
16
|
+
.defaultNow()
|
|
17
|
+
.$onUpdate(() => new Date());
|
|
14
18
|
|
|
15
19
|
// columns.helpers.ts
|
|
16
20
|
export const timestamps = {
|
|
@@ -37,7 +37,7 @@ export const agents = pgTable(
|
|
|
37
37
|
backgroundColor: text('background_color'),
|
|
38
38
|
marketIdentifier: text('market_identifier'),
|
|
39
39
|
|
|
40
|
-
plugins: jsonb('plugins').$type<string[]>()
|
|
40
|
+
plugins: jsonb('plugins').$type<string[]>(),
|
|
41
41
|
|
|
42
42
|
clientId: text('client_id'),
|
|
43
43
|
|
|
@@ -55,6 +55,7 @@ export const agents = pgTable(
|
|
|
55
55
|
tts: jsonb('tts').$type<LobeAgentTTSConfig>(),
|
|
56
56
|
|
|
57
57
|
virtual: boolean('virtual').default(false),
|
|
58
|
+
pinned: boolean('pinned'),
|
|
58
59
|
|
|
59
60
|
openingMessage: text('opening_message'),
|
|
60
61
|
openingQuestions: text('opening_questions').array().default([]),
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
2
2
|
import type { AiProviderConfig, AiProviderSettings } from '@lobechat/types';
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
boolean,
|
|
5
|
+
index,
|
|
6
|
+
integer,
|
|
7
|
+
jsonb,
|
|
8
|
+
pgTable,
|
|
9
|
+
primaryKey,
|
|
10
|
+
text,
|
|
11
|
+
varchar,
|
|
12
|
+
} from 'drizzle-orm/pg-core';
|
|
4
13
|
import { AiModelSettings } from 'model-bank';
|
|
5
14
|
|
|
6
15
|
import { timestamps } from './_helpers';
|
|
@@ -36,7 +45,10 @@ export const aiProviders = pgTable(
|
|
|
36
45
|
|
|
37
46
|
...timestamps,
|
|
38
47
|
},
|
|
39
|
-
(table) => [
|
|
48
|
+
(table) => [
|
|
49
|
+
primaryKey({ columns: [table.id, table.userId] }),
|
|
50
|
+
index('ai_providers_user_id_idx').on(table.userId),
|
|
51
|
+
],
|
|
40
52
|
);
|
|
41
53
|
|
|
42
54
|
export type NewAiProviderItem = Omit<typeof aiProviders.$inferInsert, 'userId'>;
|
|
@@ -68,7 +80,10 @@ export const aiModels = pgTable(
|
|
|
68
80
|
|
|
69
81
|
...timestamps,
|
|
70
82
|
},
|
|
71
|
-
(table) => [
|
|
83
|
+
(table) => [
|
|
84
|
+
primaryKey({ columns: [table.id, table.providerId, table.userId] }),
|
|
85
|
+
index('ai_models_user_id_idx').on(table.userId),
|
|
86
|
+
],
|
|
72
87
|
);
|
|
73
88
|
|
|
74
89
|
export type NewAiModelItem = Omit<typeof aiModels.$inferInsert, 'userId'>;
|
|
@@ -58,6 +58,11 @@ export const messageGroups = pgTable(
|
|
|
58
58
|
title: varchar255('title'),
|
|
59
59
|
description: text('description'),
|
|
60
60
|
|
|
61
|
+
// Compression fields
|
|
62
|
+
type: text('type', { enum: ['parallel', 'compression'] }),
|
|
63
|
+
content: text('content'), // compression summary (plain text)
|
|
64
|
+
editorData: jsonb('editor_data'), // rich text editor data (future extension)
|
|
65
|
+
|
|
61
66
|
clientId: varchar255('client_id'),
|
|
62
67
|
|
|
63
68
|
...timestamps,
|
|
@@ -65,6 +70,7 @@ export const messageGroups = pgTable(
|
|
|
65
70
|
(t) => [
|
|
66
71
|
uniqueIndex('message_groups_client_id_user_id_unique').on(t.clientId, t.userId),
|
|
67
72
|
index('message_groups_topic_id_idx').on(t.topicId),
|
|
73
|
+
index('message_groups_type_idx').on(t.type),
|
|
68
74
|
],
|
|
69
75
|
);
|
|
70
76
|
|
|
@@ -84,6 +90,7 @@ export const messages = pgTable(
|
|
|
84
90
|
role: varchar255('role').notNull(),
|
|
85
91
|
content: text('content'),
|
|
86
92
|
editorData: jsonb('editor_data'),
|
|
93
|
+
summary: text('summary'),
|
|
87
94
|
reasoning: jsonb('reasoning').$type<ModelReasoning>(),
|
|
88
95
|
search: jsonb('search').$type<GroundingSearch>(),
|
|
89
96
|
metadata: jsonb('metadata'),
|
|
@@ -164,12 +171,10 @@ export const messagePlugins = pgTable(
|
|
|
164
171
|
.references(() => users.id, { onDelete: 'cascade' })
|
|
165
172
|
.notNull(),
|
|
166
173
|
},
|
|
167
|
-
(t) =>
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
),
|
|
172
|
-
}),
|
|
174
|
+
(t) => [
|
|
175
|
+
uniqueIndex('message_plugins_client_id_user_id_unique').on(t.clientId, t.userId),
|
|
176
|
+
index('message_plugins_tool_call_id_idx').on(t.toolCallId),
|
|
177
|
+
],
|
|
173
178
|
);
|
|
174
179
|
|
|
175
180
|
export const messageTTS = pgTable(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
2
|
-
import type { ChatTopicMetadata } from '@lobechat/types';
|
|
2
|
+
import type { ChatTopicMetadata, ThreadMetadata } from '@lobechat/types';
|
|
3
3
|
import { sql } from 'drizzle-orm';
|
|
4
4
|
import { boolean, index, jsonb, pgTable, primaryKey, text, uniqueIndex } from 'drizzle-orm/pg-core';
|
|
5
5
|
import { createInsertSchema } from 'drizzle-zod';
|
|
@@ -63,7 +63,16 @@ export const threads = pgTable(
|
|
|
63
63
|
editor_data: jsonb('editor_data'),
|
|
64
64
|
type: text('type', { enum: ['continuation', 'standalone', 'isolation'] }).notNull(),
|
|
65
65
|
status: text('status', {
|
|
66
|
-
enum: [
|
|
66
|
+
enum: [
|
|
67
|
+
'active',
|
|
68
|
+
'processing',
|
|
69
|
+
'pending',
|
|
70
|
+
'inReview',
|
|
71
|
+
'todo',
|
|
72
|
+
'cancel',
|
|
73
|
+
'completed',
|
|
74
|
+
'failed',
|
|
75
|
+
],
|
|
67
76
|
}),
|
|
68
77
|
|
|
69
78
|
topicId: text('topic_id')
|
|
@@ -74,6 +83,10 @@ export const threads = pgTable(
|
|
|
74
83
|
parentThreadId: text('parent_thread_id').references(() => threads.id, { onDelete: 'set null' }),
|
|
75
84
|
clientId: text('client_id'),
|
|
76
85
|
|
|
86
|
+
agentId: text('agent_id').references(() => agents.id, { onDelete: 'cascade' }),
|
|
87
|
+
groupId: text('group_id').references(() => chatGroups.id, { onDelete: 'cascade' }),
|
|
88
|
+
metadata: jsonb('metadata').$type<ThreadMetadata | undefined>(),
|
|
89
|
+
|
|
77
90
|
userId: text('user_id')
|
|
78
91
|
.references(() => users.id, { onDelete: 'cascade' })
|
|
79
92
|
.notNull(),
|
|
@@ -84,6 +97,8 @@ export const threads = pgTable(
|
|
|
84
97
|
(t) => [
|
|
85
98
|
uniqueIndex('threads_client_id_user_id_unique').on(t.clientId, t.userId),
|
|
86
99
|
index('threads_topic_id_idx').on(t.topicId),
|
|
100
|
+
index('threads_agent_id_idx').on(t.agentId),
|
|
101
|
+
index('threads_group_id_idx').on(t.groupId),
|
|
87
102
|
],
|
|
88
103
|
);
|
|
89
104
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/* eslint-disable sort-keys-fix/sort-keys-fix */
|
|
2
2
|
import { DEFAULT_PREFERENCE } from '@lobechat/const';
|
|
3
|
-
import type { CustomPluginParams } from '@lobechat/types';
|
|
3
|
+
import type { CustomPluginParams, UserOnboarding } from '@lobechat/types';
|
|
4
4
|
import { LobeChatPluginManifest } from '@lobehub/chat-plugin-sdk';
|
|
5
5
|
import { sql } from 'drizzle-orm';
|
|
6
|
-
import { boolean, index, jsonb, pgTable, primaryKey, text } from 'drizzle-orm/pg-core';
|
|
6
|
+
import { boolean, index, jsonb, pgTable, primaryKey, text, varchar } from 'drizzle-orm/pg-core';
|
|
7
7
|
|
|
8
8
|
import { timestamps, timestamptz, varchar255 } from './_helpers';
|
|
9
9
|
|
|
@@ -20,8 +20,10 @@ export const users = pgTable(
|
|
|
20
20
|
firstName: text('first_name'),
|
|
21
21
|
lastName: text('last_name'),
|
|
22
22
|
fullName: text('full_name'),
|
|
23
|
+
interests: varchar('interests', { length: 64 }).array(),
|
|
23
24
|
|
|
24
25
|
isOnboarded: boolean('is_onboarded').default(false),
|
|
26
|
+
onboarding: jsonb('onboarding').$type<UserOnboarding>(),
|
|
25
27
|
// Time user was created in Clerk
|
|
26
28
|
clerkCreatedAt: timestamptz('clerk_created_at'),
|
|
27
29
|
|
|
@@ -77,6 +79,7 @@ export const userSettings = pgTable('user_settings', {
|
|
|
77
79
|
systemAgent: jsonb('system_agent'),
|
|
78
80
|
defaultAgent: jsonb('default_agent'),
|
|
79
81
|
market: jsonb('market'),
|
|
82
|
+
memory: jsonb('memory'),
|
|
80
83
|
tool: jsonb('tool'),
|
|
81
84
|
image: jsonb('image'),
|
|
82
85
|
});
|
|
@@ -55,16 +55,21 @@ export const userMemoriesContexts = pgTable(
|
|
|
55
55
|
.primaryKey(),
|
|
56
56
|
|
|
57
57
|
userId: text('user_id').references(() => users.id, { onDelete: 'cascade' }),
|
|
58
|
-
userMemoryIds: jsonb('user_memory_ids'),
|
|
58
|
+
userMemoryIds: jsonb('user_memory_ids').$type<string[]>(),
|
|
59
59
|
|
|
60
60
|
metadata: jsonb('metadata').$type<Record<string, unknown>>(),
|
|
61
61
|
tags: text('tags').array(),
|
|
62
62
|
|
|
63
|
-
associatedObjects:
|
|
64
|
-
|
|
63
|
+
associatedObjects:
|
|
64
|
+
jsonb('associated_objects').$type<
|
|
65
|
+
{ extra?: Record<string, unknown>; name?: string; type?: string }[]
|
|
66
|
+
>(),
|
|
67
|
+
associatedSubjects:
|
|
68
|
+
jsonb('associated_subjects').$type<
|
|
69
|
+
{ extra?: Record<string, unknown>; name?: string; type?: string }[]
|
|
70
|
+
>(),
|
|
65
71
|
|
|
66
72
|
title: text('title'),
|
|
67
|
-
titleVector: vector('title_vector', { dimensions: 1024 }),
|
|
68
73
|
description: text('description'),
|
|
69
74
|
descriptionVector: vector('description_vector', { dimensions: 1024 }),
|
|
70
75
|
|
|
@@ -79,10 +84,6 @@ export const userMemoriesContexts = pgTable(
|
|
|
79
84
|
...timestamps,
|
|
80
85
|
},
|
|
81
86
|
(table) => [
|
|
82
|
-
index('user_memories_contexts_title_vector_index').using(
|
|
83
|
-
'hnsw',
|
|
84
|
-
table.titleVector.op('vector_cosine_ops'),
|
|
85
|
-
),
|
|
86
87
|
index('user_memories_contexts_description_vector_index').using(
|
|
87
88
|
'hnsw',
|
|
88
89
|
table.descriptionVector.op('vector_cosine_ops'),
|
|
@@ -156,8 +156,14 @@ export const pruneReasoningPayload = (payload: ChatStreamPayload) => {
|
|
|
156
156
|
stream: shouldStream,
|
|
157
157
|
// Only include stream_options when stream is enabled
|
|
158
158
|
...(shouldStream && stream_options && { stream_options }),
|
|
159
|
-
|
|
160
|
-
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* In openai docs: https://platform.openai.com/docs/guides/latest-model#gpt-5-2-parameter-compatibility
|
|
162
|
+
* Fields like `top_p`, `temperature` and `logprobs` only supported to
|
|
163
|
+
* GPT-5 series (e.g. 5-mini 5-nano ) when reasoning effort is none
|
|
164
|
+
*/
|
|
165
|
+
temperature: isEffortNone ? payload.temperature : undefined,
|
|
166
|
+
top_p: isEffortNone ? payload.top_p : undefined,
|
|
161
167
|
};
|
|
162
168
|
};
|
|
163
169
|
|
|
@@ -86,8 +86,8 @@ describe('LobeGithubAI - custom features', () => {
|
|
|
86
86
|
|
|
87
87
|
expect(result.model).toBe('o1-preview');
|
|
88
88
|
expect(result.stream).toBe(false);
|
|
89
|
-
expect(result.temperature).toBe(
|
|
90
|
-
expect(result.top_p).toBe(
|
|
89
|
+
expect(result.temperature).toBe(undefined);
|
|
90
|
+
expect(result.top_p).toBe(undefined);
|
|
91
91
|
expect(result.frequency_penalty).toBe(0);
|
|
92
92
|
expect(result.presence_penalty).toBe(0);
|
|
93
93
|
});
|
|
@@ -117,8 +117,8 @@ describe('LobeGithubAI - custom features', () => {
|
|
|
117
117
|
|
|
118
118
|
expect(result.model).toBe('o3-preview');
|
|
119
119
|
expect(result.stream).toBe(false);
|
|
120
|
-
expect(result.temperature).toBe(
|
|
121
|
-
expect(result.top_p).toBe(
|
|
120
|
+
expect(result.temperature).toBe(undefined);
|
|
121
|
+
expect(result.top_p).toBe(undefined);
|
|
122
122
|
});
|
|
123
123
|
|
|
124
124
|
it('should handle o3-mini models', () => {
|
|
@@ -25,6 +25,30 @@ export interface ThreadItem {
|
|
|
25
25
|
userId: string;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
/**
|
|
29
|
+
* Metadata for Thread, used for agent task execution
|
|
30
|
+
*/
|
|
31
|
+
export interface ThreadMetadata {
|
|
32
|
+
/** Task completion time */
|
|
33
|
+
completedAt?: string;
|
|
34
|
+
/** Execution duration in milliseconds */
|
|
35
|
+
duration?: number;
|
|
36
|
+
/** Error message when task failed */
|
|
37
|
+
error?: string;
|
|
38
|
+
/** Operation ID for tracking */
|
|
39
|
+
operationId?: string;
|
|
40
|
+
/** Task start time, used to calculate duration */
|
|
41
|
+
startedAt?: string;
|
|
42
|
+
/** Total cost in dollars */
|
|
43
|
+
totalCost?: number;
|
|
44
|
+
/** Total messages created during execution */
|
|
45
|
+
totalMessages?: number;
|
|
46
|
+
/** Total tokens consumed */
|
|
47
|
+
totalTokens?: number;
|
|
48
|
+
/** Total tool calls made */
|
|
49
|
+
totalToolCalls?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
28
52
|
export interface CreateThreadParams {
|
|
29
53
|
parentThreadId?: string;
|
|
30
54
|
sourceMessageId?: string;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export interface UserOnboarding {
|
|
4
|
+
/** Current step number (1-based), for resuming onboarding */
|
|
5
|
+
currentStep?: number;
|
|
6
|
+
/** Timestamp when onboarding was completed (ISO 8601) */
|
|
7
|
+
finishedAt?: string;
|
|
8
|
+
/** Onboarding flow version for future upgrades */
|
|
9
|
+
version: number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const UserOnboardingSchema = z.object({
|
|
13
|
+
currentStep: z.number().optional(),
|
|
14
|
+
finishedAt: z.string().optional(),
|
|
15
|
+
version: z.number(),
|
|
16
|
+
});
|
|
@@ -19,9 +19,12 @@ import ViewSwitcher, { ViewMode } from './ViewSwitcher';
|
|
|
19
19
|
export const List = memo(() => {
|
|
20
20
|
const { t } = useTranslation('file');
|
|
21
21
|
|
|
22
|
-
const useFetchFilesAndKnowledgeBases = useAgentStore((s) =>
|
|
22
|
+
const [useFetchFilesAndKnowledgeBases, activeAgentId] = useAgentStore((s) => [
|
|
23
|
+
s.useFetchFilesAndKnowledgeBases,
|
|
24
|
+
s.activeAgentId,
|
|
25
|
+
]);
|
|
23
26
|
|
|
24
|
-
const { isLoading, error, data } = useFetchFilesAndKnowledgeBases();
|
|
27
|
+
const { isLoading, error, data } = useFetchFilesAndKnowledgeBases(activeAgentId);
|
|
25
28
|
|
|
26
29
|
const [columnCount, setColumnCount] = useState(2);
|
|
27
30
|
const [isTransitioning, setIsTransitioning] = useState(false);
|
|
@@ -49,7 +49,7 @@ export interface AgentChatAction {
|
|
|
49
49
|
updateAgentChatConfig: (config: Partial<LobeAgentChatConfig>) => Promise<void>;
|
|
50
50
|
updateAgentConfig: (config: PartialDeep<LobeAgentConfig>) => Promise<void>;
|
|
51
51
|
useFetchAgentConfig: (isLogin: boolean | undefined, id: string) => SWRResponse<LobeAgentConfig>;
|
|
52
|
-
useFetchFilesAndKnowledgeBases: () => SWRResponse<KnowledgeItem[]>;
|
|
52
|
+
useFetchFilesAndKnowledgeBases: (agentId?: string) => SWRResponse<KnowledgeItem[]>;
|
|
53
53
|
useInitInboxAgentStore: (
|
|
54
54
|
isLogin: boolean | undefined,
|
|
55
55
|
defaultAgentConfig?: PartialDeep<LobeAgentConfig>,
|
|
@@ -180,9 +180,9 @@ export const createChatSlice: StateCreator<
|
|
|
180
180
|
},
|
|
181
181
|
},
|
|
182
182
|
),
|
|
183
|
-
useFetchFilesAndKnowledgeBases: () => {
|
|
183
|
+
useFetchFilesAndKnowledgeBases: (agentId) => {
|
|
184
184
|
return useClientDataSWR<KnowledgeItem[]>(
|
|
185
|
-
[FETCH_AGENT_KNOWLEDGE_KEY,
|
|
185
|
+
agentId ? [FETCH_AGENT_KNOWLEDGE_KEY, agentId] : null,
|
|
186
186
|
([, id]: string[]) => agentService.getFilesAndKnowledgeBases(id),
|
|
187
187
|
{
|
|
188
188
|
fallbackData: [],
|
package/vitest.config.mts
CHANGED
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Explain how group chat works in LobeHub (Multi-agent orchestratoin)
|
|
3
|
-
globs:
|
|
4
|
-
alwaysApply: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
This rule explains how group chat (multi-agent orchestration) works. Not confused with session group, which is a organization method to manage session.
|
|
8
|
-
|
|
9
|
-
## Key points
|
|
10
|
-
|
|
11
|
-
- A supervisor will devide who and how will speak next
|
|
12
|
-
- Each agent will speak just like in single chat (if was asked to speak)
|
|
13
|
-
- Not coufused with session group
|
|
14
|
-
|
|
15
|
-
## Related Files
|
|
16
|
-
|
|
17
|
-
- src/store/chat/slices/message/supervisor.ts
|
|
18
|
-
- src/store/chat/slices/aiChat/actions/generateAIGroupChat.ts
|
|
19
|
-
- src/prompts/groupChat/index.ts (All prompts here)
|
|
20
|
-
|
|
21
|
-
## Snippets
|
|
22
|
-
|
|
23
|
-
```tsx
|
|
24
|
-
// Detect whether in group chat
|
|
25
|
-
const isGroupSession = useSessionStore(sessionSelectors.isCurrentSessionGroupSession);
|
|
26
|
-
|
|
27
|
-
// Member actions
|
|
28
|
-
const addAgentsToGroup = useChatGroupStore((s) => s.addAgentsToGroup);
|
|
29
|
-
const removeAgentFromGroup = useChatGroupStore((s) => s.removeAgentFromGroup);
|
|
30
|
-
const persistReorder = useChatGroupStore((s) => s.reorderGroupMembers);
|
|
31
|
-
|
|
32
|
-
// Get group info
|
|
33
|
-
const groupConfig = useChatGroupStore(chatGroupSelectors.currentGroupConfig);
|
|
34
|
-
const currentGroupMemebers = useSessionStore(sessionSelectors.currentGroupAgents);
|
|
35
|
-
```
|
|
@@ -1,173 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description:
|
|
3
|
-
globs: *.tsx
|
|
4
|
-
alwaysApply: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# react component 编写指南
|
|
8
|
-
|
|
9
|
-
- 如果要写复杂样式的话用 antd-style ,简单的话可以用 style 属性直接写内联样式
|
|
10
|
-
- 如果需要 flex 布局或者居中布局应该使用 react-layout-kit 的 Flexbox 和 Center 组件
|
|
11
|
-
- 选择组件时优先顺序应该是 src/components > 安装的组件 package > lobe-ui > antd
|
|
12
|
-
- 使用 selector 访问 zustand store 的数据,而不是直接从 store 获取
|
|
13
|
-
|
|
14
|
-
## antd-style token system
|
|
15
|
-
|
|
16
|
-
### 访问 token system 的两种方式
|
|
17
|
-
|
|
18
|
-
#### 使用 antd-style 的 useTheme hook
|
|
19
|
-
|
|
20
|
-
```tsx
|
|
21
|
-
import { useTheme } from 'antd-style';
|
|
22
|
-
|
|
23
|
-
const MyComponent = () => {
|
|
24
|
-
const theme = useTheme();
|
|
25
|
-
|
|
26
|
-
return (
|
|
27
|
-
<div
|
|
28
|
-
style={{
|
|
29
|
-
color: theme.colorPrimary,
|
|
30
|
-
backgroundColor: theme.colorBgContainer,
|
|
31
|
-
padding: theme.padding,
|
|
32
|
-
borderRadius: theme.borderRadius,
|
|
33
|
-
}}
|
|
34
|
-
>
|
|
35
|
-
使用主题 token 的组件
|
|
36
|
-
</div>
|
|
37
|
-
);
|
|
38
|
-
};
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
#### 使用 antd-style 的 createStyles
|
|
42
|
-
|
|
43
|
-
```tsx
|
|
44
|
-
const useStyles = createStyles(({ css, token }) => {
|
|
45
|
-
return {
|
|
46
|
-
container: css`
|
|
47
|
-
background-color: ${token.colorBgContainer};
|
|
48
|
-
border-radius: ${token.borderRadius}px;
|
|
49
|
-
padding: ${token.padding}px;
|
|
50
|
-
color: ${token.colorText};
|
|
51
|
-
`,
|
|
52
|
-
title: css`
|
|
53
|
-
font-size: ${token.fontSizeLG}px;
|
|
54
|
-
font-weight: ${token.fontWeightStrong};
|
|
55
|
-
margin-bottom: ${token.marginSM}px;
|
|
56
|
-
`,
|
|
57
|
-
content: css`
|
|
58
|
-
font-size: ${token.fontSize}px;
|
|
59
|
-
line-height: ${token.lineHeight};
|
|
60
|
-
`,
|
|
61
|
-
};
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const Card: FC<CardProps> = ({ title, content }) => {
|
|
65
|
-
const { styles } = useStyles();
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<Flexbox className={styles.container}>
|
|
69
|
-
<div className={styles.title}>{title}</div>
|
|
70
|
-
<div className={styles.content}>{content}</div>
|
|
71
|
-
</Flexbox>
|
|
72
|
-
);
|
|
73
|
-
};
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
### 一些你经常会忘记使用的 token
|
|
77
|
-
|
|
78
|
-
请注意使用下面的 token 而不是 css 字面值。可以访问 https://ant.design/docs/react/customize-theme-cn 了解所有 token
|
|
79
|
-
|
|
80
|
-
- 动画类
|
|
81
|
-
- token.motionDurationMid
|
|
82
|
-
- token.motionEaseInOut
|
|
83
|
-
- 包围盒属性
|
|
84
|
-
- token.paddingSM
|
|
85
|
-
- token.marginLG
|
|
86
|
-
|
|
87
|
-
## Lobe UI 包含的组件
|
|
88
|
-
|
|
89
|
-
- 不知道 `@lobehub/ui` 的组件怎么用,有哪些属性,就自己搜下这个项目其它地方怎么用的,不要瞎猜,大部分组件都是在 antd 的基础上扩展了属性
|
|
90
|
-
- 具体用法不懂可以联网搜索,例如 ActionIcon 就爬取 https://ui.lobehub.com/components/action-icon
|
|
91
|
-
- 可以阅读 `node_modules/@lobehub/ui/es/index.js` 了解有哪些组件,每个组件的属性是什么
|
|
92
|
-
|
|
93
|
-
- General
|
|
94
|
-
- ActionIcon
|
|
95
|
-
- ActionIconGroup
|
|
96
|
-
- Block
|
|
97
|
-
- Button
|
|
98
|
-
- DownloadButton
|
|
99
|
-
- Icon
|
|
100
|
-
- Data Display
|
|
101
|
-
- Avatar
|
|
102
|
-
- AvatarGroup
|
|
103
|
-
- GroupAvatar
|
|
104
|
-
- Collapse
|
|
105
|
-
- FileTypeIcon
|
|
106
|
-
- FluentEmoji
|
|
107
|
-
- GuideCard
|
|
108
|
-
- Highlighter
|
|
109
|
-
- Hotkey
|
|
110
|
-
- Image
|
|
111
|
-
- List
|
|
112
|
-
- Markdown
|
|
113
|
-
- SearchResultCards
|
|
114
|
-
- MaterialFileTypeIcon
|
|
115
|
-
- Mermaid
|
|
116
|
-
- Typography
|
|
117
|
-
- Text
|
|
118
|
-
- Segmented
|
|
119
|
-
- Snippet
|
|
120
|
-
- SortableList
|
|
121
|
-
- Tag
|
|
122
|
-
- Tooltip
|
|
123
|
-
- Video
|
|
124
|
-
- Data Entry
|
|
125
|
-
- AutoComplete
|
|
126
|
-
- CodeEditor
|
|
127
|
-
- ColorSwatches
|
|
128
|
-
- CopyButton
|
|
129
|
-
- DatePicker
|
|
130
|
-
- EditableText
|
|
131
|
-
- EmojiPicker
|
|
132
|
-
- Form
|
|
133
|
-
- FormModal
|
|
134
|
-
- HotkeyInput
|
|
135
|
-
- ImageSelect
|
|
136
|
-
- Input
|
|
137
|
-
- SearchBar
|
|
138
|
-
- Select
|
|
139
|
-
- SliderWithInput
|
|
140
|
-
- ThemeSwitch
|
|
141
|
-
- Feedback
|
|
142
|
-
- Alert
|
|
143
|
-
- Drawer
|
|
144
|
-
- Modal
|
|
145
|
-
- Layout
|
|
146
|
-
- DraggablePanel
|
|
147
|
-
- DraggablePanelBody
|
|
148
|
-
- DraggablePanelContainer
|
|
149
|
-
- DraggablePanelFooter
|
|
150
|
-
- DraggablePanelHeader
|
|
151
|
-
- Footer
|
|
152
|
-
- Grid
|
|
153
|
-
- Header
|
|
154
|
-
- Layout
|
|
155
|
-
- LayoutFooter
|
|
156
|
-
- LayoutHeader
|
|
157
|
-
- LayoutMain
|
|
158
|
-
- LayoutSidebar
|
|
159
|
-
- LayoutSidebarInner
|
|
160
|
-
- LayoutToc
|
|
161
|
-
- MaskShadow
|
|
162
|
-
- ScrollShadow
|
|
163
|
-
- Navigation
|
|
164
|
-
- Burger
|
|
165
|
-
- Dropdown
|
|
166
|
-
- Menu
|
|
167
|
-
- SideNav
|
|
168
|
-
- Tabs
|
|
169
|
-
- Toc
|
|
170
|
-
- Theme
|
|
171
|
-
- ConfigProvider
|
|
172
|
-
- FontLoader
|
|
173
|
-
- ThemeProvider
|