@lobehub/chat 1.138.2 → 1.138.4
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 +50 -0
- package/changelog/v1.json +18 -0
- package/docker-compose/local/docker-compose.yml +1 -1
- package/docker-compose/local/grafana/docker-compose.yml +1 -1
- package/docker-compose/production/grafana/docker-compose.yml +1 -1
- package/package.json +1 -1
- package/packages/database/src/models/topic.ts +0 -1
- package/packages/database/src/repositories/aiInfra/index.test.ts +656 -0
- package/packages/database/src/repositories/aiInfra/index.ts +19 -13
- package/packages/model-runtime/src/core/contextBuilders/google.test.ts +585 -0
- package/packages/model-runtime/src/core/contextBuilders/google.ts +201 -0
- package/packages/model-runtime/src/core/openaiCompatibleFactory/index.test.ts +191 -179
- package/packages/model-runtime/src/core/openaiCompatibleFactory/index.ts +305 -47
- package/packages/model-runtime/src/providers/anthropic/generateObject.test.ts +93 -84
- package/packages/model-runtime/src/providers/anthropic/generateObject.ts +3 -3
- package/packages/model-runtime/src/providers/google/generateObject.test.ts +588 -83
- package/packages/model-runtime/src/providers/google/generateObject.ts +104 -6
- package/packages/model-runtime/src/providers/google/index.test.ts +0 -395
- package/packages/model-runtime/src/providers/google/index.ts +28 -194
- package/packages/model-runtime/src/providers/openai/index.test.ts +18 -17
- package/packages/model-runtime/src/types/structureOutput.ts +3 -4
- package/packages/obervability-otel/package.json +4 -4
- package/packages/prompts/CLAUDE.md +289 -43
- package/packages/prompts/package.json +2 -1
- package/packages/prompts/promptfoo/supervisor/productive/eval.yaml +51 -0
- package/packages/prompts/promptfoo/supervisor/productive/prompt.ts +18 -0
- package/packages/prompts/promptfoo/supervisor/productive/tests/basic-case.ts +54 -0
- package/packages/prompts/promptfoo/supervisor/productive/tests/role.ts +58 -0
- package/packages/prompts/promptfoo/supervisor/productive/tools.json +80 -0
- package/packages/prompts/src/contexts/index.ts +1 -0
- package/packages/prompts/src/contexts/supervisor/index.ts +2 -0
- package/packages/prompts/src/contexts/supervisor/makeDecision.ts +68 -0
- package/packages/prompts/src/contexts/supervisor/tools.ts +102 -0
- package/packages/prompts/src/index.ts +1 -0
- package/packages/types/src/aiChat.ts +9 -4
- package/src/server/routers/lambda/aiChat.ts +0 -1
- package/src/server/services/aiChat/index.test.ts +1 -1
- package/src/server/services/aiChat/index.ts +1 -1
- package/src/services/topic/client.ts +1 -1
- package/src/store/chat/slices/message/supervisor.test.ts +12 -5
- package/src/store/chat/slices/message/supervisor.ts +16 -129
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ChatCompletionTool, ChatMessage, ChatStreamPayload } from '@lobechat/types';
|
|
2
|
+
|
|
3
|
+
import { groupChatPrompts, groupSupervisorPrompts } from '../../prompts';
|
|
4
|
+
import { SupervisorToolName, SupervisorTools } from './tools';
|
|
5
|
+
|
|
6
|
+
interface SupervisorTodoItem {
|
|
7
|
+
// optional assigned owner (agent id or name)
|
|
8
|
+
assignee?: string;
|
|
9
|
+
content: string;
|
|
10
|
+
finished: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface AgentItem {
|
|
14
|
+
id: string;
|
|
15
|
+
title?: string | null;
|
|
16
|
+
}
|
|
17
|
+
export interface SupervisorContext {
|
|
18
|
+
allowDM?: boolean;
|
|
19
|
+
availableAgents: AgentItem[];
|
|
20
|
+
messages: ChatMessage[];
|
|
21
|
+
// Group scene controls which tools are exposed (e.g., todos only in 'productive')
|
|
22
|
+
scene?: 'casual' | 'productive';
|
|
23
|
+
systemPrompt?: string;
|
|
24
|
+
todoList?: SupervisorTodoItem[];
|
|
25
|
+
userName?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const contextSupervisorMakeDecision = ({
|
|
29
|
+
allowDM,
|
|
30
|
+
scene,
|
|
31
|
+
systemPrompt,
|
|
32
|
+
availableAgents,
|
|
33
|
+
todoList,
|
|
34
|
+
userName,
|
|
35
|
+
messages,
|
|
36
|
+
}: SupervisorContext) => {
|
|
37
|
+
const conversationHistory = groupSupervisorPrompts(messages);
|
|
38
|
+
const prompt = groupChatPrompts.buildSupervisorPrompt({
|
|
39
|
+
allowDM,
|
|
40
|
+
availableAgents: availableAgents.filter((agent) => agent.id),
|
|
41
|
+
conversationHistory,
|
|
42
|
+
scene,
|
|
43
|
+
systemPrompt,
|
|
44
|
+
todoList,
|
|
45
|
+
userName,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const tools = SupervisorTools.filter((tool) => {
|
|
49
|
+
if (tool.name === SupervisorToolName.trigger_agent_dm) {
|
|
50
|
+
return allowDM;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if ([SupervisorToolName.finish_todo, SupervisorToolName.create_todo].includes(tool.name)) {
|
|
54
|
+
return scene === 'productive';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return true;
|
|
58
|
+
}).map<ChatCompletionTool>((tool) => ({
|
|
59
|
+
function: tool,
|
|
60
|
+
type: 'function',
|
|
61
|
+
}));
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
messages: [{ content: prompt, role: 'user' }],
|
|
65
|
+
temperature: 0.3,
|
|
66
|
+
tools,
|
|
67
|
+
} satisfies Partial<ChatStreamPayload>;
|
|
68
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { LobeUniformTool } from '@lobechat/types';
|
|
2
|
+
|
|
3
|
+
export const SupervisorToolName = {
|
|
4
|
+
create_todo: 'create_todo',
|
|
5
|
+
finish_todo: 'finish_todo',
|
|
6
|
+
trigger_agent: 'trigger_agent',
|
|
7
|
+
trigger_agent_dm: 'trigger_agent_dm',
|
|
8
|
+
wait_for_user_input: 'wait_for_user_input',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const SupervisorTools: LobeUniformTool[] = [
|
|
12
|
+
{
|
|
13
|
+
description: 'Trigger an agent to speak (group message).',
|
|
14
|
+
name: SupervisorToolName.trigger_agent,
|
|
15
|
+
parameters: {
|
|
16
|
+
properties: {
|
|
17
|
+
id: {
|
|
18
|
+
description: 'The agent id to trigger.',
|
|
19
|
+
type: 'string',
|
|
20
|
+
},
|
|
21
|
+
instruction: {
|
|
22
|
+
description:
|
|
23
|
+
'The instruction or message for the agent. No longer than 10 words. Always use English.',
|
|
24
|
+
type: 'string',
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
required: ['id', 'instruction'],
|
|
28
|
+
type: 'object',
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
description:
|
|
33
|
+
'Wait for user input. Use this when the conversation history looks likes fine for now, or agents are waiting for user input.',
|
|
34
|
+
name: SupervisorToolName.wait_for_user_input,
|
|
35
|
+
parameters: {
|
|
36
|
+
properties: {
|
|
37
|
+
reason: {
|
|
38
|
+
description: 'Optional reason for pausing the conversation.',
|
|
39
|
+
type: 'string',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
required: [],
|
|
43
|
+
type: 'object',
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
{
|
|
48
|
+
description: 'Trigger an agent to DM another agent or user.',
|
|
49
|
+
name: SupervisorToolName.trigger_agent_dm,
|
|
50
|
+
parameters: {
|
|
51
|
+
additionalProperties: false,
|
|
52
|
+
properties: {
|
|
53
|
+
id: {
|
|
54
|
+
description: 'The agent id to trigger.',
|
|
55
|
+
type: 'string',
|
|
56
|
+
},
|
|
57
|
+
instruction: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
},
|
|
60
|
+
target: {
|
|
61
|
+
description: 'The target agent id. Only used when need DM.',
|
|
62
|
+
type: 'string',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
required: ['instruction', 'id', 'target'],
|
|
66
|
+
type: 'object',
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
description: 'Create a new todo item',
|
|
71
|
+
name: SupervisorToolName.create_todo,
|
|
72
|
+
parameters: {
|
|
73
|
+
additionalProperties: false,
|
|
74
|
+
properties: {
|
|
75
|
+
assignee: {
|
|
76
|
+
description: 'Who will do the todo. Can be agent id or empty.',
|
|
77
|
+
type: 'string',
|
|
78
|
+
},
|
|
79
|
+
content: {
|
|
80
|
+
description: 'The todo content or description.',
|
|
81
|
+
type: 'string',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
required: ['content', 'assignee'],
|
|
85
|
+
type: 'object',
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
description: 'Finish a todo by index or all todos',
|
|
90
|
+
name: SupervisorToolName.finish_todo,
|
|
91
|
+
parameters: {
|
|
92
|
+
additionalProperties: false,
|
|
93
|
+
properties: {
|
|
94
|
+
index: {
|
|
95
|
+
type: 'number',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
required: ['index'],
|
|
99
|
+
type: 'object',
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
3
|
import { ChatMessage } from './message';
|
|
4
|
+
import { OpenAIChatMessage } from './openai/chat';
|
|
4
5
|
import { LobeUniformTool, LobeUniformToolSchema } from './tool';
|
|
5
6
|
import { ChatTopic } from './topic';
|
|
6
7
|
|
|
@@ -74,8 +75,9 @@ export const StructureOutputSchema = z.object({
|
|
|
74
75
|
model: z.string(),
|
|
75
76
|
provider: z.string(),
|
|
76
77
|
schema: StructureSchema.optional(),
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
tools: z
|
|
79
|
+
.array(z.object({ function: LobeUniformToolSchema, type: z.literal('function') }))
|
|
80
|
+
.optional(),
|
|
79
81
|
});
|
|
80
82
|
|
|
81
83
|
interface IStructureSchema {
|
|
@@ -92,10 +94,13 @@ interface IStructureSchema {
|
|
|
92
94
|
|
|
93
95
|
export interface StructureOutputParams {
|
|
94
96
|
keyVaultsPayload: string;
|
|
95
|
-
messages:
|
|
97
|
+
messages: OpenAIChatMessage[];
|
|
96
98
|
model: string;
|
|
97
99
|
provider: string;
|
|
98
100
|
schema?: IStructureSchema;
|
|
99
101
|
systemRole?: string;
|
|
100
|
-
tools?:
|
|
102
|
+
tools?: {
|
|
103
|
+
function: LobeUniformTool;
|
|
104
|
+
type: 'function';
|
|
105
|
+
}[];
|
|
101
106
|
}
|
|
@@ -32,7 +32,7 @@ describe('AiChatService', () => {
|
|
|
32
32
|
{ includeTopic: true, sessionId: 's1' },
|
|
33
33
|
expect.objectContaining({ postProcessUrl: expect.any(Function) }),
|
|
34
34
|
);
|
|
35
|
-
expect(mockQueryTopics).toHaveBeenCalledWith({
|
|
35
|
+
expect(mockQueryTopics).toHaveBeenCalledWith({ containerId: 's1' });
|
|
36
36
|
expect(res.messages).toEqual([{ id: 'm1' }]);
|
|
37
37
|
expect(res.topics).toEqual([{ id: 't1' }]);
|
|
38
38
|
});
|
|
@@ -29,7 +29,7 @@ export class AiChatService {
|
|
|
29
29
|
this.messageModel.query(params, {
|
|
30
30
|
postProcessUrl: (path) => this.fileService.getFullFileUrl(path),
|
|
31
31
|
}),
|
|
32
|
-
params.includeTopic ? this.topicModel.query({
|
|
32
|
+
params.includeTopic ? this.topicModel.query({ containerId: params.sessionId }) : undefined,
|
|
33
33
|
]);
|
|
34
34
|
|
|
35
35
|
return { messages, topics };
|
|
@@ -38,7 +38,7 @@ export class ClientService extends BaseClientService implements ITopicService {
|
|
|
38
38
|
getTopics: ITopicService['getTopics'] = async (params) => {
|
|
39
39
|
const data = await this.topicModel.query({
|
|
40
40
|
...params,
|
|
41
|
-
|
|
41
|
+
containerId: this.toDbSessionId(params.containerId),
|
|
42
42
|
});
|
|
43
43
|
return data as unknown as Promise<ChatTopic[]>;
|
|
44
44
|
};
|
|
@@ -5,10 +5,17 @@ import { aiChatService } from '@/services/aiChat';
|
|
|
5
5
|
import { GroupChatSupervisor, type SupervisorContext } from './supervisor';
|
|
6
6
|
|
|
7
7
|
vi.mock('@lobechat/prompts', () => ({
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
contextSupervisorMakeDecision: vi.fn(() => ({
|
|
9
|
+
messages: [{ content: 'structured-supervisor-prompt', role: 'user' }],
|
|
10
|
+
temperature: 0.3,
|
|
11
|
+
tools: [
|
|
12
|
+
{ function: { name: 'trigger_agent' }, type: 'function' },
|
|
13
|
+
{ function: { name: 'wait_for_user_input' }, type: 'function' },
|
|
14
|
+
{ function: { name: 'trigger_agent_dm' }, type: 'function' },
|
|
15
|
+
{ function: { name: 'create_todo' }, type: 'function' },
|
|
16
|
+
{ function: { name: 'finish_todo' }, type: 'function' },
|
|
17
|
+
],
|
|
18
|
+
})),
|
|
12
19
|
}));
|
|
13
20
|
|
|
14
21
|
vi.mock('@/services/aiChat', () => ({
|
|
@@ -76,7 +83,7 @@ describe('GroupChatSupervisor', () => {
|
|
|
76
83
|
temperature: 0.3,
|
|
77
84
|
});
|
|
78
85
|
|
|
79
|
-
const toolNames = (payload.tools ?? []).map((tool: any) => tool.name);
|
|
86
|
+
const toolNames = (payload.tools ?? []).map((tool: any) => tool.function.name);
|
|
80
87
|
expect(toolNames).toEqual(
|
|
81
88
|
expect.arrayContaining([
|
|
82
89
|
'trigger_agent',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { contextSupervisorMakeDecision } from '@lobechat/prompts';
|
|
2
2
|
import { ChatMessage, GroupMemberWithAgent } from '@lobechat/types';
|
|
3
3
|
|
|
4
4
|
import { aiChatService } from '@/services/aiChat';
|
|
@@ -61,7 +61,7 @@ export class GroupChatSupervisor {
|
|
|
61
61
|
* Make decision on who should speak next
|
|
62
62
|
*/
|
|
63
63
|
async makeDecision(context: SupervisorContext): Promise<SupervisorDecisionResult> {
|
|
64
|
-
const {
|
|
64
|
+
const { availableAgents } = context;
|
|
65
65
|
|
|
66
66
|
// If no agents available, stop conversation
|
|
67
67
|
if (availableAgents.length === 0) {
|
|
@@ -69,22 +69,7 @@ export class GroupChatSupervisor {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
try {
|
|
72
|
-
|
|
73
|
-
const conversationHistory = groupSupervisorPrompts(messages);
|
|
74
|
-
|
|
75
|
-
const supervisorPrompt = groupChatPrompts.buildSupervisorPrompt({
|
|
76
|
-
allowDM,
|
|
77
|
-
availableAgents: availableAgents
|
|
78
|
-
.filter((agent) => agent.id)
|
|
79
|
-
.map((agent) => ({ id: agent.id!, title: agent.title })),
|
|
80
|
-
conversationHistory,
|
|
81
|
-
scene: context.scene,
|
|
82
|
-
systemPrompt,
|
|
83
|
-
todoList,
|
|
84
|
-
userName,
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const response = await this.callLLMForDecision(supervisorPrompt, context);
|
|
72
|
+
const response = await this.callLLMForDecision(context);
|
|
88
73
|
const result = this.parseSupervisorResponse(response, availableAgents, context);
|
|
89
74
|
|
|
90
75
|
console.log('Supervisor TODO list:', result.todos);
|
|
@@ -102,123 +87,25 @@ export class GroupChatSupervisor {
|
|
|
102
87
|
* Call LLM service to get supervisor decision
|
|
103
88
|
*/
|
|
104
89
|
private async callLLMForDecision(
|
|
105
|
-
prompt: string,
|
|
106
90
|
context: SupervisorContext,
|
|
107
91
|
): Promise<SupervisorToolCall[] | string> {
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
name: 'trigger_agent',
|
|
119
|
-
parameters: {
|
|
120
|
-
properties: {
|
|
121
|
-
id: {
|
|
122
|
-
description: 'The agent id to trigger.',
|
|
123
|
-
type: 'string',
|
|
124
|
-
},
|
|
125
|
-
instruction: {
|
|
126
|
-
description:
|
|
127
|
-
'The instruction or message for the agent. No longer than 10 words. Always use English.',
|
|
128
|
-
type: 'string',
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
required: ['id', 'instruction'],
|
|
132
|
-
type: 'object',
|
|
133
|
-
},
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
description:
|
|
137
|
-
'Wait for user input. Use this when the conversation history looks likes fine for now, or agents are waiting for user input.',
|
|
138
|
-
name: 'wait_for_user_input',
|
|
139
|
-
parameters: {
|
|
140
|
-
properties: {
|
|
141
|
-
reason: {
|
|
142
|
-
description: 'Optional reason for pausing the conversation.',
|
|
143
|
-
type: 'string',
|
|
144
|
-
},
|
|
145
|
-
},
|
|
146
|
-
required: [],
|
|
147
|
-
type: 'object',
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
];
|
|
151
|
-
|
|
152
|
-
// Add DM tool if allowed
|
|
153
|
-
if (context.allowDM) {
|
|
154
|
-
tools.push({
|
|
155
|
-
description: 'Trigger an agent to DM another agent or user.',
|
|
156
|
-
name: 'trigger_agent_dm',
|
|
157
|
-
parameters: {
|
|
158
|
-
properties: {
|
|
159
|
-
id: {
|
|
160
|
-
description: 'The agent id to trigger.',
|
|
161
|
-
type: 'string',
|
|
162
|
-
},
|
|
163
|
-
instruction: {
|
|
164
|
-
description: 'The instruction or message for the agent.',
|
|
165
|
-
type: 'string',
|
|
166
|
-
},
|
|
167
|
-
target: {
|
|
168
|
-
description: 'The target agent id. Only used when need DM.',
|
|
169
|
-
type: 'string',
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
required: ['id', 'instruction', 'target'],
|
|
173
|
-
type: 'object',
|
|
174
|
-
},
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// Add todo tools if in productive scene
|
|
179
|
-
if (context.scene === 'productive') {
|
|
180
|
-
tools.push(
|
|
181
|
-
{
|
|
182
|
-
description: 'Create a new todo item',
|
|
183
|
-
name: 'create_todo',
|
|
184
|
-
parameters: {
|
|
185
|
-
properties: {
|
|
186
|
-
assignee: {
|
|
187
|
-
description: 'Who will do the todo. Can be agent id or empty.',
|
|
188
|
-
type: 'string',
|
|
189
|
-
},
|
|
190
|
-
content: {
|
|
191
|
-
description: 'The todo content or description.',
|
|
192
|
-
type: 'string',
|
|
193
|
-
},
|
|
194
|
-
},
|
|
195
|
-
required: ['content', 'assignee'],
|
|
196
|
-
type: 'object',
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
{
|
|
200
|
-
description: 'Finish a todo by index',
|
|
201
|
-
name: 'finish_todo',
|
|
202
|
-
parameters: {
|
|
203
|
-
properties: {
|
|
204
|
-
index: {
|
|
205
|
-
description: 'The index of the todo to finish.',
|
|
206
|
-
type: 'number',
|
|
207
|
-
},
|
|
208
|
-
},
|
|
209
|
-
required: ['index'],
|
|
210
|
-
type: 'object',
|
|
211
|
-
},
|
|
212
|
-
},
|
|
213
|
-
);
|
|
214
|
-
}
|
|
92
|
+
const contexts = contextSupervisorMakeDecision({
|
|
93
|
+
allowDM: context.allowDM,
|
|
94
|
+
availableAgents: context.availableAgents
|
|
95
|
+
.filter((agent) => agent.id)
|
|
96
|
+
.map((agent) => ({ id: agent.id, title: agent.title })),
|
|
97
|
+
messages: context.messages,
|
|
98
|
+
scene: context.scene,
|
|
99
|
+
todoList: context.todoList,
|
|
100
|
+
userName: context.userName,
|
|
101
|
+
});
|
|
215
102
|
|
|
216
103
|
try {
|
|
217
104
|
const response = await aiChatService.generateJSON(
|
|
218
105
|
{
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
106
|
+
...(contexts as any),
|
|
107
|
+
model: context.model,
|
|
108
|
+
provider: context.provider,
|
|
222
109
|
},
|
|
223
110
|
context.abortController || new AbortController(),
|
|
224
111
|
);
|