@minded-ai/mindedjs 1.0.115 → 1.0.117
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/dist/agent.d.ts +31 -158
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +39 -160
- package/dist/agent.js.map +1 -1
- package/dist/events/AgentEvents.d.ts +1 -0
- package/dist/events/AgentEvents.d.ts.map +1 -1
- package/dist/platform/mindedConnection.d.ts.map +1 -1
- package/dist/platform/mindedConnection.js +34 -0
- package/dist/platform/mindedConnection.js.map +1 -1
- package/dist/platform/mindedConnectionTypes.d.ts +8 -1
- package/dist/platform/mindedConnectionTypes.d.ts.map +1 -1
- package/dist/platform/mindedConnectionTypes.js +1 -0
- package/dist/platform/mindedConnectionTypes.js.map +1 -1
- package/dist/toolsLibrary/classifier.d.ts +82 -0
- package/dist/toolsLibrary/classifier.d.ts.map +1 -0
- package/dist/toolsLibrary/classifier.js +223 -0
- package/dist/toolsLibrary/classifier.js.map +1 -0
- package/dist/toolsLibrary/index.d.ts +2 -0
- package/dist/toolsLibrary/index.d.ts.map +1 -1
- package/dist/toolsLibrary/index.js +2 -0
- package/dist/toolsLibrary/index.js.map +1 -1
- package/dist/types/Flows.types.d.ts +6 -0
- package/dist/types/Flows.types.d.ts.map +1 -1
- package/dist/types/Flows.types.js.map +1 -1
- package/docs/SUMMARY.md +1 -0
- package/docs/tooling/classifier.md +217 -0
- package/package.json +2 -2
- package/src/agent.ts +42 -160
- package/src/events/AgentEvents.ts +1 -0
- package/src/platform/mindedConnection.ts +14 -13
- package/src/platform/mindedConnectionTypes.ts +8 -2
- package/src/toolsLibrary/classifier.ts +273 -0
- package/src/toolsLibrary/index.ts +2 -0
- package/src/types/Flows.types.ts +6 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { Tool } from '../types/Tools.types';
|
|
3
|
+
import { logger } from '../utils/logger';
|
|
4
|
+
import { JsonOutputParser } from '@langchain/core/output_parsers';
|
|
5
|
+
import { SystemMessage } from '@langchain/core/messages';
|
|
6
|
+
import { BaseLanguageModel } from '@langchain/core/language_models/base';
|
|
7
|
+
|
|
8
|
+
// Type definitions for classifier configuration
|
|
9
|
+
export interface ClassDefinition {
|
|
10
|
+
name: string;
|
|
11
|
+
description: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface ClassifierConfig {
|
|
15
|
+
classes: ClassDefinition[];
|
|
16
|
+
systemPrompt?: string;
|
|
17
|
+
outputFormat?: 'json' | 'text';
|
|
18
|
+
includeReason?: boolean;
|
|
19
|
+
defaultClass?: string;
|
|
20
|
+
defaultReason?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ClassificationResult {
|
|
24
|
+
class: string;
|
|
25
|
+
reason?: string;
|
|
26
|
+
confidence?: number;
|
|
27
|
+
[key: string]: any; // Allow additional fields
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Default configuration
|
|
31
|
+
const DEFAULT_CONFIG: Partial<ClassifierConfig> = {
|
|
32
|
+
outputFormat: 'json',
|
|
33
|
+
includeReason: true,
|
|
34
|
+
defaultClass: 'unknown',
|
|
35
|
+
defaultReason: 'Unable to determine classification',
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generic classifier utility that can be used standalone
|
|
40
|
+
* @param content The content to classify
|
|
41
|
+
* @param config The classifier configuration
|
|
42
|
+
* @param llm The language model to use for classification
|
|
43
|
+
* @returns The classification result
|
|
44
|
+
*/
|
|
45
|
+
export async function classify(content: string, config: ClassifierConfig, llm: BaseLanguageModel): Promise<ClassificationResult> {
|
|
46
|
+
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
// Build the classification prompt
|
|
50
|
+
const classesDescription = mergedConfig.classes.map((c) => `${c.name}: ${c.description}`).join('\n');
|
|
51
|
+
|
|
52
|
+
const basePrompt =
|
|
53
|
+
mergedConfig.systemPrompt || 'You are a classifier. Your task is to classify the given content into one of the following categories:';
|
|
54
|
+
|
|
55
|
+
let prompt = `${basePrompt}\n\n${classesDescription}\n\n`;
|
|
56
|
+
|
|
57
|
+
if (mergedConfig.outputFormat === 'json') {
|
|
58
|
+
prompt += `You should output the result in the following JSON format: {
|
|
59
|
+
"class": "<selected class name>",
|
|
60
|
+
${mergedConfig.includeReason ? '"reason": "<explanation for the classification>",' : ''}
|
|
61
|
+
"confidence": <confidence score between 0 and 1>
|
|
62
|
+
}.\nReturn JSON and nothing more.`;
|
|
63
|
+
} else {
|
|
64
|
+
prompt += 'Return only the class name.';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
prompt += `\n\nContent to classify:\n${content}`;
|
|
68
|
+
|
|
69
|
+
// Make the classification request
|
|
70
|
+
if (mergedConfig.outputFormat === 'json') {
|
|
71
|
+
const parser = new JsonOutputParser();
|
|
72
|
+
const result = await llm.pipe(parser).invoke([new SystemMessage(prompt)]);
|
|
73
|
+
return result as ClassificationResult;
|
|
74
|
+
} else {
|
|
75
|
+
const result = await llm.invoke([new SystemMessage(prompt)]);
|
|
76
|
+
const classText = typeof result.content === 'string' ? result.content.trim() : '';
|
|
77
|
+
return { class: classText };
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
81
|
+
logger.error({ message: 'Classification failed, using default', error: errorMessage });
|
|
82
|
+
|
|
83
|
+
// Return default classification on error
|
|
84
|
+
return {
|
|
85
|
+
class: mergedConfig.defaultClass || 'unknown',
|
|
86
|
+
reason: mergedConfig.defaultReason || errorMessage,
|
|
87
|
+
confidence: 0,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Create a classifier from a simple class list
|
|
94
|
+
* @param classes Array of class names or [name, description] tuples
|
|
95
|
+
* @param options Additional configuration options
|
|
96
|
+
* @returns A configured classify function
|
|
97
|
+
*/
|
|
98
|
+
export function createClassifier(classes: string[] | [string, string][], options?: Partial<ClassifierConfig>) {
|
|
99
|
+
const classDefinitions: ClassDefinition[] = classes.map((c) => {
|
|
100
|
+
if (typeof c === 'string') {
|
|
101
|
+
return { name: c, description: '' };
|
|
102
|
+
}
|
|
103
|
+
return { name: c[0], description: c[1] };
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const config: ClassifierConfig = {
|
|
107
|
+
...options,
|
|
108
|
+
classes: classDefinitions,
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return (content: string, llm: BaseLanguageModel) => classify(content, config, llm);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Schema for the classifier tool
|
|
115
|
+
export const schema = z.object({
|
|
116
|
+
content: z.string().describe('The content to classify'),
|
|
117
|
+
classes: z
|
|
118
|
+
.array(
|
|
119
|
+
z.union([
|
|
120
|
+
z.string(),
|
|
121
|
+
z.tuple([z.string(), z.string()]),
|
|
122
|
+
z.object({
|
|
123
|
+
name: z.string(),
|
|
124
|
+
description: z.string(),
|
|
125
|
+
}),
|
|
126
|
+
]),
|
|
127
|
+
)
|
|
128
|
+
.optional()
|
|
129
|
+
.describe('Classes to classify into. Can be strings, [name, description] tuples, or objects with name and description'),
|
|
130
|
+
systemPrompt: z.string().optional().describe('Custom system prompt for classification'),
|
|
131
|
+
includeReason: z.boolean().optional().default(true).describe('Whether to include reasoning in the response'),
|
|
132
|
+
outputFormat: z.enum(['json', 'text']).optional().default('json').describe('Output format for the classification'),
|
|
133
|
+
defaultClass: z.string().optional().describe('Default class to use if classification fails'),
|
|
134
|
+
defaultReason: z.string().optional().describe('Default reason to use if classification fails'),
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const classifierTool: Tool<typeof schema, any> = {
|
|
138
|
+
name: 'minded-classifier',
|
|
139
|
+
description:
|
|
140
|
+
'Classify content into predefined categories using AI. Supports custom classes, system prompts, and various output formats. Can be configured with default fallback values.',
|
|
141
|
+
input: schema,
|
|
142
|
+
isGlobal: false,
|
|
143
|
+
execute: async ({ input, state, agent }) => {
|
|
144
|
+
const { content, classes, systemPrompt, includeReason, outputFormat, defaultClass, defaultReason } = input;
|
|
145
|
+
|
|
146
|
+
logger.info({
|
|
147
|
+
message: 'Classifying content',
|
|
148
|
+
sessionId: state.sessionId,
|
|
149
|
+
contentLength: content.length,
|
|
150
|
+
classCount: classes?.length,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
// Convert input classes to ClassDefinition format
|
|
154
|
+
let classDefinitions: ClassDefinition[] = [];
|
|
155
|
+
if (classes) {
|
|
156
|
+
classDefinitions = classes.map((c) => {
|
|
157
|
+
if (typeof c === 'string') {
|
|
158
|
+
return { name: c, description: '' };
|
|
159
|
+
} else if (Array.isArray(c)) {
|
|
160
|
+
return { name: c[0], description: c[1] };
|
|
161
|
+
} else {
|
|
162
|
+
return c as ClassDefinition;
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// If no classes provided, try to get from state memory
|
|
168
|
+
if (classDefinitions.length === 0 && state.memory?.classifierConfig?.classes) {
|
|
169
|
+
classDefinitions = state.memory.classifierConfig.classes;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (classDefinitions.length === 0) {
|
|
173
|
+
throw new Error('No classes provided for classification');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const config: ClassifierConfig = {
|
|
177
|
+
classes: classDefinitions,
|
|
178
|
+
systemPrompt,
|
|
179
|
+
includeReason,
|
|
180
|
+
outputFormat,
|
|
181
|
+
defaultClass,
|
|
182
|
+
defaultReason,
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
const result = await classify(content, config, agent.llm);
|
|
187
|
+
|
|
188
|
+
logger.info({
|
|
189
|
+
message: 'Classification completed',
|
|
190
|
+
sessionId: state.sessionId,
|
|
191
|
+
class: result.class,
|
|
192
|
+
confidence: result.confidence,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
result,
|
|
197
|
+
state: {
|
|
198
|
+
memory: {
|
|
199
|
+
lastClassification: {
|
|
200
|
+
content: content.substring(0, 100) + (content.length > 100 ? '...' : ''),
|
|
201
|
+
result,
|
|
202
|
+
timestamp: new Date().toISOString(),
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
};
|
|
207
|
+
} catch (error) {
|
|
208
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
209
|
+
logger.error({
|
|
210
|
+
message: 'Classification failed',
|
|
211
|
+
sessionId: state.sessionId,
|
|
212
|
+
error: errorMessage,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
export default classifierTool;
|
|
221
|
+
|
|
222
|
+
// Export utility for multi-label classification
|
|
223
|
+
export async function multiClassify(
|
|
224
|
+
content: string,
|
|
225
|
+
config: ClassifierConfig & { maxClasses?: number },
|
|
226
|
+
llm: BaseLanguageModel,
|
|
227
|
+
): Promise<ClassificationResult[]> {
|
|
228
|
+
const mergedConfig = { ...DEFAULT_CONFIG, ...config };
|
|
229
|
+
const maxClasses = mergedConfig.maxClasses || 3;
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
const classesDescription = mergedConfig.classes.map((c) => `${c.name}: ${c.description}`).join('\n');
|
|
233
|
+
|
|
234
|
+
const basePrompt =
|
|
235
|
+
mergedConfig.systemPrompt || 'You are a multi-label classifier. Select all applicable categories for the given content:';
|
|
236
|
+
|
|
237
|
+
const prompt = `${basePrompt}\n\n${classesDescription}\n\n
|
|
238
|
+
You should output the result as a JSON array of up to ${maxClasses} classifications, ordered by relevance:
|
|
239
|
+
[
|
|
240
|
+
{
|
|
241
|
+
"class": "<class name>",
|
|
242
|
+
${mergedConfig.includeReason ? '"reason": "<explanation>",' : ''}
|
|
243
|
+
"confidence": <confidence score between 0 and 1>
|
|
244
|
+
},
|
|
245
|
+
...
|
|
246
|
+
]
|
|
247
|
+
Return JSON and nothing more.
|
|
248
|
+
|
|
249
|
+
Content to classify:
|
|
250
|
+
${content}`;
|
|
251
|
+
|
|
252
|
+
const parser = new JsonOutputParser();
|
|
253
|
+
const result = await llm.pipe(parser).invoke([new SystemMessage(prompt)]);
|
|
254
|
+
|
|
255
|
+
if (Array.isArray(result)) {
|
|
256
|
+
return result as ClassificationResult[];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// If single result returned, wrap in array
|
|
260
|
+
return [result as ClassificationResult];
|
|
261
|
+
} catch (error) {
|
|
262
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
263
|
+
logger.error({ message: 'Multi-classification failed', error: errorMessage });
|
|
264
|
+
|
|
265
|
+
return [
|
|
266
|
+
{
|
|
267
|
+
class: mergedConfig.defaultClass || 'unknown',
|
|
268
|
+
reason: mergedConfig.defaultReason || errorMessage,
|
|
269
|
+
confidence: 0,
|
|
270
|
+
},
|
|
271
|
+
];
|
|
272
|
+
}
|
|
273
|
+
}
|
package/src/types/Flows.types.ts
CHANGED
|
@@ -67,6 +67,10 @@ export interface WebhookTriggerNode extends BaseTriggerNode {
|
|
|
67
67
|
export interface InterfaceTriggerNode extends BaseTriggerNode {
|
|
68
68
|
triggerType: TriggerType.INTERFACE;
|
|
69
69
|
parameters: Record<string, any>;
|
|
70
|
+
appName: string;
|
|
71
|
+
appImgSrc: string;
|
|
72
|
+
actionName: string;
|
|
73
|
+
actionKey: string;
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
export interface VoiceTriggerNode extends BaseTriggerNode {
|
|
@@ -106,6 +110,8 @@ export interface BrowserTaskNode extends BaseNode {
|
|
|
106
110
|
description?: string;
|
|
107
111
|
required?: boolean;
|
|
108
112
|
}[];
|
|
113
|
+
autoTrigger?: boolean;
|
|
114
|
+
defaultPayload?: string;
|
|
109
115
|
}
|
|
110
116
|
|
|
111
117
|
export type TriggerNode = AppTriggerNode | WebhookTriggerNode | ManualTriggerNode | VoiceTriggerNode | InterfaceTriggerNode;
|