@minded-ai/mindedjs 2.0.13 → 2.0.14-beta-1

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.
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const messages_1 = require("@langchain/core/messages");
4
+ const logger_1 = require("./logger");
5
+ const extractToolStateResponse = (toolMessage) => {
6
+ try {
7
+ const parsed = JSON.parse(toolMessage.content);
8
+ if (typeof parsed === 'object' && parsed !== null && 'state' in parsed) {
9
+ if (parsed.state.messages) {
10
+ parsed.state.messages = constructMessagesFromToolMessage(parsed.state.messages);
11
+ }
12
+ return parsed.state;
13
+ }
14
+ }
15
+ catch (err) {
16
+ logger_1.logger.error({ message: 'Error parsing tool state response', err });
17
+ }
18
+ return {};
19
+ };
20
+ const constructMessagesFromToolMessage = (messages) => {
21
+ if (!Array.isArray(messages)) {
22
+ logger_1.logger.error({ msg: 'Expected messages to be an array', messages });
23
+ return [];
24
+ }
25
+ return messages
26
+ .map((messageObj) => {
27
+ try {
28
+ // Handle case where message is already a BaseMessage instance
29
+ if (messageObj instanceof messages_1.BaseMessage) {
30
+ return messageObj;
31
+ }
32
+ // Handle case where message is a plain object that needs reconstruction
33
+ const messageType = messageObj.id[2];
34
+ const content = messageObj.kwargs.content || '';
35
+ const id = messageObj.kwargs.id;
36
+ const additionalKwargs = messageObj.additional_kwargs || {};
37
+ switch (messageType) {
38
+ case 'AIMessage':
39
+ return new messages_1.AIMessage({
40
+ content,
41
+ id,
42
+ additional_kwargs: additionalKwargs,
43
+ tool_calls: messageObj.tool_calls || [],
44
+ invalid_tool_calls: messageObj.invalid_tool_calls || [],
45
+ });
46
+ case 'HumanMessage':
47
+ return new messages_1.HumanMessage({
48
+ content,
49
+ id,
50
+ additional_kwargs: additionalKwargs,
51
+ });
52
+ case 'SystemMessage':
53
+ return new messages_1.SystemMessage({
54
+ content,
55
+ id,
56
+ additional_kwargs: additionalKwargs,
57
+ });
58
+ case 'ToolMessage':
59
+ return new messages_1.ToolMessage({
60
+ content,
61
+ id,
62
+ additional_kwargs: additionalKwargs,
63
+ tool_call_id: messageObj.tool_call_id || '',
64
+ });
65
+ default:
66
+ // Default to HumanMessage for unknown types
67
+ logger_1.logger.warn({
68
+ msg: 'Unknown message type, defaulting to HumanMessage',
69
+ messageType,
70
+ messageObj,
71
+ });
72
+ return null;
73
+ }
74
+ }
75
+ catch (err) {
76
+ logger_1.logger.error({
77
+ msg: 'Error reconstructing message',
78
+ err,
79
+ messageObj,
80
+ });
81
+ // Return a fallback HumanMessage in case of error
82
+ return new messages_1.HumanMessage({
83
+ content: messageObj.content || 'Error reconstructing message',
84
+ id: messageObj.id,
85
+ });
86
+ }
87
+ })
88
+ .filter(Boolean);
89
+ };
90
+ exports.default = extractToolStateResponse;
91
+ //# sourceMappingURL=extractStateMemoryResponse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractStateMemoryResponse.js","sourceRoot":"","sources":["../../src/utils/extractStateMemoryResponse.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minded-ai/mindedjs",
3
- "version": "2.0.13",
3
+ "version": "2.0.14-beta-1",
4
4
  "description": "MindedJS is a TypeScript library for building agents.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -70,4 +70,4 @@
70
70
  "peerDependencies": {
71
71
  "playwright": "^1.55.0"
72
72
  }
73
- }
73
+ }
@@ -35,6 +35,34 @@ const DEFAULT_CONFIG: Partial<ClassifierConfig> = {
35
35
  defaultReason: 'Unable to determine classification',
36
36
  };
37
37
 
38
+ /**
39
+ * Validates the classification result
40
+ * @param result The classification result to validate
41
+ * @param config The classifier configuration
42
+ * @returns True if the result is valid, false otherwise
43
+ */
44
+ function validateClassificationResult(result: ClassificationResult, config: ClassifierConfig): boolean {
45
+ // Check if result exists and has a class property
46
+ if (!result || !result.class) {
47
+ return false;
48
+ }
49
+
50
+ // Check if the class is one of the configured classes
51
+ const validClasses = config.classes.map((c) => c.name);
52
+ if (!validClasses.includes(result.class)) {
53
+ return false;
54
+ }
55
+
56
+ // If we expect JSON format with confidence, validate it
57
+ if (config.outputFormat === 'json' && result.confidence !== undefined) {
58
+ if (typeof result.confidence !== 'number' || result.confidence < 0 || result.confidence > 1) {
59
+ return false;
60
+ }
61
+ }
62
+
63
+ return true;
64
+ }
65
+
38
66
  /**
39
67
  * Generic classifier utility that can be used standalone
40
68
  * @param content The content to classify
@@ -44,48 +72,98 @@ const DEFAULT_CONFIG: Partial<ClassifierConfig> = {
44
72
  */
45
73
  export async function classify(content: string, config: ClassifierConfig, llm: BaseLanguageModel): Promise<ClassificationResult> {
46
74
  const mergedConfig = { ...DEFAULT_CONFIG, ...config };
75
+ const MAX_RETRIES = 3;
76
+ let lastError: Error | null = null;
77
+ let lastInvalidResult: ClassificationResult | null = null;
47
78
 
48
- try {
49
- // Build the classification prompt
50
- const classesDescription = mergedConfig.classes.map((c) => `${c.name}: ${c.description}`).join('\n');
79
+ for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
80
+ try {
81
+ // Build the classification prompt
82
+ const classesDescription = mergedConfig.classes.map((c) => `${c.name}: ${c.description}`).join('\n');
83
+ const validClassNames = mergedConfig.classes.map((c) => c.name);
84
+
85
+ const basePrompt =
86
+ mergedConfig.systemPrompt ||
87
+ 'You are a classifier. Your task is to classify the given content into one of the following categories:';
88
+
89
+ let prompt = `${basePrompt}\n\n${classesDescription}\n\n`;
90
+
91
+ // Add retry feedback if this is not the first attempt
92
+ if (attempt > 1 && lastInvalidResult) {
93
+ prompt += `\n⚠️ IMPORTANT: Your previous classification attempt was INVALID. `;
94
+ if (lastInvalidResult.class && !validClassNames.includes(lastInvalidResult.class)) {
95
+ prompt += `You selected "${lastInvalidResult.class}" which is NOT in the list of valid classes. `;
96
+ } else if (!lastInvalidResult.class) {
97
+ prompt += `You did not provide a class value. `;
98
+ }
99
+ prompt += `You MUST select ONLY from these valid classes:\n${validClassNames.join(', ')}\n\n`;
100
+ }
101
+
102
+ if (mergedConfig.outputFormat === 'json') {
103
+ prompt += `You should output the result in the following JSON format: {
104
+ "class": "<selected class name>",
105
+ ${mergedConfig.includeReason ? '"reason": "<explanation for the classification>",' : ''}
106
+ "confidence": <confidence score between 0 and 1>
107
+ }.\nReturn JSON and nothing more.`;
108
+ } else {
109
+ prompt += 'Return only the class name.';
110
+ }
111
+
112
+ prompt += `\n\nContent to classify:\n${content}`;
113
+
114
+ // Make the classification request
115
+ let result: ClassificationResult;
116
+ if (mergedConfig.outputFormat === 'json') {
117
+ const parser = new JsonOutputParser();
118
+ const parsedResult = await llm.pipe(parser).invoke([new SystemMessage(prompt)]);
119
+ result = parsedResult as ClassificationResult;
120
+ } else {
121
+ const llmResult = await llm.invoke([new SystemMessage(prompt)]);
122
+ const classText = typeof llmResult.content === 'string' ? llmResult.content.trim() : '';
123
+ result = { class: classText };
124
+ }
125
+
126
+ // Validate the result
127
+ if (!validateClassificationResult(result, mergedConfig)) {
128
+ lastInvalidResult = result;
129
+ throw new Error(`Invalid classification result: ${JSON.stringify(result)}`);
130
+ }
131
+
132
+ // Success - return the valid result
133
+ return result;
134
+ } catch (err) {
135
+ lastError = err as Error;
136
+ logger.warn({
137
+ message: `Classification attempt ${attempt} failed`,
138
+ attempt,
139
+ maxRetries: MAX_RETRIES,
140
+ error: lastError.message,
141
+ invalidResult: lastInvalidResult,
142
+ });
51
143
 
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.';
144
+ // If this is not the last attempt, continue to retry
145
+ if (attempt < MAX_RETRIES) {
146
+ continue;
147
+ }
65
148
  }
149
+ }
66
150
 
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 (err) {
80
- logger.error({ message: 'Classification failed, using default', err });
151
+ // All retries exhausted - fallback to default
152
+ logger.warn({
153
+ message: 'Classification failed after max retries, using default class',
154
+ maxRetries: MAX_RETRIES,
155
+ lastError: lastError?.message,
156
+ lastInvalidResult,
157
+ defaultClass: mergedConfig.defaultClass,
158
+ defaultReason: mergedConfig.defaultReason,
159
+ });
81
160
 
82
- // Return default classification on error
83
- return {
84
- class: mergedConfig.defaultClass || 'unknown',
85
- reason: mergedConfig.defaultReason || (err as Error).message,
86
- confidence: 0,
87
- };
88
- }
161
+ // Return default classification
162
+ return {
163
+ class: mergedConfig.defaultClass || 'unknown',
164
+ reason: mergedConfig.defaultReason || `Classification failed after ${MAX_RETRIES} attempts: ${lastError?.message}`,
165
+ confidence: 0,
166
+ };
89
167
  }
90
168
 
91
169
  /**