@smythos/sre 1.7.20 → 1.7.41

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.
Files changed (78) hide show
  1. package/dist/index.js +134 -89
  2. package/dist/index.js.map +1 -1
  3. package/dist/types/Components/AgentPlugin.class.d.ts +1 -1
  4. package/dist/types/Components/DataSourceIndexer.class.d.ts +4 -12
  5. package/dist/types/Components/GenAILLM.class.d.ts +5 -5
  6. package/dist/types/Components/RAG/DataSourceCleaner.class.d.ts +4 -4
  7. package/dist/types/Components/RAG/DataSourceComponent.class.d.ts +5 -1
  8. package/dist/types/Components/index.d.ts +3 -3
  9. package/dist/types/config.d.ts +1 -0
  10. package/dist/types/helpers/Conversation.helper.d.ts +10 -13
  11. package/dist/types/helpers/TemplateString.helper.d.ts +1 -1
  12. package/dist/types/index.d.ts +4 -3
  13. package/dist/types/subsystems/IO/VectorDB.service/VectorDBConnector.d.ts +1 -0
  14. package/dist/types/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.d.ts +1 -0
  15. package/dist/types/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.d.ts +11 -4
  16. package/dist/types/subsystems/IO/VectorDB.service/embed/index.d.ts +5 -0
  17. package/dist/types/subsystems/LLMManager/LLM.helper.d.ts +19 -0
  18. package/dist/types/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.d.ts +15 -10
  19. package/dist/types/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.d.ts +35 -0
  20. package/dist/types/subsystems/Security/Account.service/AccountConnector.d.ts +2 -2
  21. package/dist/types/subsystems/Security/Vault.service/connectors/SecretsManager.class.d.ts +2 -3
  22. package/dist/types/types/LLM.types.d.ts +23 -0
  23. package/dist/types/types/VectorDB.types.d.ts +4 -0
  24. package/dist/types/utils/string.utils.d.ts +1 -0
  25. package/package.json +3 -3
  26. package/src/Components/APIEndpoint.class.ts +1 -6
  27. package/src/Components/AgentPlugin.class.ts +20 -3
  28. package/src/Components/Classifier.class.ts +79 -16
  29. package/src/Components/Component.class.ts +14 -1
  30. package/src/Components/ForEach.class.ts +34 -6
  31. package/src/Components/GenAILLM.class.ts +75 -34
  32. package/src/Components/LLMAssistant.class.ts +56 -21
  33. package/src/Components/RAG/DataSourceCleaner.class.ts +180 -0
  34. package/src/Components/RAG/DataSourceComponent.class.ts +137 -0
  35. package/src/Components/RAG/DataSourceIndexer.class.ts +260 -0
  36. package/src/Components/{DataSourceLookup.class.ts → RAG/DataSourceLookup.class.ts} +96 -3
  37. package/src/Components/ScrapflyWebScrape.class.ts +7 -0
  38. package/src/Components/ServerlessCode.class.ts +1 -4
  39. package/src/Components/index.ts +3 -3
  40. package/src/config.ts +1 -0
  41. package/src/helpers/Conversation.helper.ts +112 -26
  42. package/src/helpers/S3Cache.helper.ts +2 -1
  43. package/src/helpers/TemplateString.helper.ts +6 -5
  44. package/src/index.ts +213 -212
  45. package/src/index.ts.bak +213 -212
  46. package/src/subsystems/IO/NKV.service/connectors/NKVRedis.class.ts +3 -1
  47. package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +1 -0
  48. package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +145 -19
  49. package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +67 -22
  50. package/src/subsystems/IO/VectorDB.service/embed/GoogleEmbedding.ts +1 -0
  51. package/src/subsystems/IO/VectorDB.service/embed/OpenAIEmbedding.ts +2 -1
  52. package/src/subsystems/IO/VectorDB.service/embed/index.ts +16 -0
  53. package/src/subsystems/LLMManager/LLM.helper.ts +25 -0
  54. package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +1 -1
  55. package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +35 -10
  56. package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +12 -4
  57. package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +4 -4
  58. package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +192 -139
  59. package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +17 -5
  60. package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +18 -3
  61. package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +14 -5
  62. package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +6 -4
  63. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +5 -5
  64. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +8 -3
  65. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/utils.ts +1 -1
  66. package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +9 -8
  67. package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +92 -1
  68. package/src/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.class.ts +260 -17
  69. package/src/subsystems/Security/Account.service/AccountConnector.ts +3 -3
  70. package/src/subsystems/Security/Vault.service/connectors/SecretsManager.class.ts +8 -63
  71. package/src/types/LLM.types.ts +24 -0
  72. package/src/types/VectorDB.types.ts +4 -0
  73. package/src/utils/array.utils.ts +11 -0
  74. package/src/utils/base64.utils.ts +1 -1
  75. package/src/utils/data.utils.ts +6 -4
  76. package/src/utils/string.utils.ts +3 -192
  77. package/src/Components/DataSourceCleaner.class.ts +0 -92
  78. package/src/Components/DataSourceIndexer.class.ts +0 -181
@@ -5,7 +5,9 @@ import { Component } from './Component.class';
5
5
  import { IAgent as Agent } from '@sre/types/Agent.types';
6
6
  import { TemplateString } from '@sre/helpers/TemplateString.helper';
7
7
  import { LLMInference } from '@sre/LLMManager/LLM.inference';
8
+ import { LLMHelper } from '@sre/LLMManager/LLM.helper';
8
9
  import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
10
+ import { TLLMEvent } from '@sre/types/LLM.types';
9
11
 
10
12
  export class Classifier extends Component {
11
13
  protected schema = {
@@ -97,7 +99,7 @@ ${JSON.stringify(categories, null, 2)}`;
97
99
  prompt = TemplateString(prompt).parse(input).result;
98
100
  }
99
101
 
100
- logger.log(` Enhanced prompt \n${prompt}\n`);
102
+ logger.log(` Enhanced prompt \n${prompt}`);
101
103
 
102
104
  if (!prompt) {
103
105
  logger.error(` Missing information, Cannot run classifier`);
@@ -114,21 +116,82 @@ ${JSON.stringify(categories, null, 2)}`;
114
116
  }
115
117
 
116
118
  try {
117
- let response = await llmInference
118
- .prompt({
119
- query: prompt,
120
- params: { ...config, agentId: agent.id },
121
- onFallback: (data) => {
122
- logger.debug(`\n ↩️ Using Fallback Model: ${data.model}`);
123
- },
124
- })
125
- .catch((error) => ({ error: error }));
126
-
127
- if (response?.error) {
128
- const error = response?.error + ' ' + (response?.details || '');
129
- logger.error(` LLM Error=`, error);
130
-
131
- return { _error: error, _debug: logger.output };
119
+ let response;
120
+
121
+ // Use streaming for Claude 4 family models
122
+ if (LLMHelper.isClaude4Family(modelId || model)) {
123
+ logger.debug(`\n Using streaming for Claude 4 family model`);
124
+
125
+ const contentPromise = new Promise(async (resolve, reject) => {
126
+ let _content = '';
127
+ let eventEmitter;
128
+
129
+ eventEmitter = await llmInference
130
+ .promptStream({
131
+ contextWindow: [{ role: 'user', content: prompt }],
132
+ files: [],
133
+ params: {
134
+ ...config.data,
135
+ agentId: agent.id,
136
+ responseFormat: 'json',
137
+ },
138
+ onFallback: (fallbackInfo) => {
139
+ logger.debug(`\n ↩️ Using fallback model: ${fallbackInfo.model}`);
140
+ },
141
+ })
142
+ .catch((error) => {
143
+ console.error('Error on promptStream: ', error);
144
+ reject(error);
145
+ });
146
+
147
+ eventEmitter.on(TLLMEvent.Content, (content) => {
148
+ _content += content;
149
+ });
150
+
151
+ eventEmitter.on(TLLMEvent.End, () => {
152
+ resolve(_content);
153
+ });
154
+
155
+ eventEmitter.on(TLLMEvent.Error, (error) => {
156
+ reject(error);
157
+ });
158
+ });
159
+
160
+ response = await contentPromise.catch((error) => {
161
+ return { error: error.message || error };
162
+ });
163
+
164
+ // Handle streaming errors
165
+ if (response?.error) {
166
+ const error = response?.error + ' ' + (response?.details || '');
167
+ logger.error(` LLM Error=`, error);
168
+ return { _error: error, _debug: logger.output };
169
+ }
170
+
171
+ // Post-process the streaming response
172
+ const postProcessed = llmInference.connector.postProcess(response);
173
+ if (postProcessed.error) {
174
+ logger.error(` LLM Post-process Error=`, postProcessed.error);
175
+ return { _error: postProcessed.error, _debug: logger.output };
176
+ }
177
+ response = postProcessed;
178
+ } else {
179
+ // Use regular prompt for non-Claude 4 models
180
+ response = await llmInference
181
+ .prompt({
182
+ query: prompt,
183
+ params: { ...config, agentId: agent.id },
184
+ onFallback: (data) => {
185
+ logger.debug(`\n ↩️ Using Fallback Model: ${data.model}`);
186
+ },
187
+ })
188
+ .catch((error) => ({ error: error }));
189
+
190
+ if (response?.error) {
191
+ const error = response?.error + ' ' + (response?.details || '');
192
+ logger.error(` LLM Error=`, error);
193
+ return { _error: error, _debug: logger.output };
194
+ }
132
195
  }
133
196
 
134
197
  // let parsed = parseJson(response);
@@ -3,6 +3,7 @@ import { IAgent as Agent } from '@sre/types/Agent.types';
3
3
  import { Logger } from '@sre/helpers/Log.helper';
4
4
  import { performTypeInference } from '@sre/helpers/TypeChecker.helper';
5
5
  import { hookableClass, hookAsync } from '@sre/Core/HookService';
6
+ import { TemplateString } from '@sre/helpers/TemplateString.helper';
6
7
 
7
8
  export type TComponentSchema = {
8
9
  name: string;
@@ -123,7 +124,19 @@ export class Component {
123
124
  if (agent.isKilled()) {
124
125
  throw new Error('Agent killed');
125
126
  }
126
- const _input = await performTypeInference(input, config?.inputs, agent);
127
+
128
+ let _input = {};
129
+
130
+ // #region Resolve agent variables so that:
131
+ // - type inference works correctly
132
+ // - we don’t need a separate resolution step when the variable name
133
+ // matches the component input name
134
+ for (let [key, value] of Object.entries(input)) {
135
+ _input[key] = TemplateString(value as string).parse(agent.agentVariables).result;
136
+ }
137
+ // #endregion
138
+
139
+ _input = await performTypeInference(_input, config?.inputs, agent);
127
140
 
128
141
  // modify the input object for component's process method
129
142
  for (const [key, value] of Object.entries(_input)) {
@@ -18,6 +18,8 @@ export class ForEach extends Component {
18
18
  let _in_progress = true;
19
19
  const logger = this.createComponentLogger(agent, config);
20
20
  try {
21
+ logger.debug(`=== ForEach Log ===`);
22
+
21
23
  const inputObject = input.Input;
22
24
  let inputArray;
23
25
 
@@ -38,18 +40,30 @@ export class ForEach extends Component {
38
40
  const runtimeData = agent.agentRuntime.getRuntimeData(config.id);
39
41
  const _ForEachData = runtimeData._LoopData || { parentId: config.id, loopIndex: 0, loopLength: inputArray.length };
40
42
 
41
- logger.debug(`Loop: ${_ForEachData.loopIndex} / ${_ForEachData.loopLength}`);
42
43
  delete _ForEachData.branches; //reset branches (the number of branches is calculated in CallComponent@Agent.class.ts )
43
44
 
44
45
  if (_ForEachData.result) {
45
46
  _temp_result = _ForEachData.result;
46
- logger.debug(` => Loop Result : ${JSON.stringify(Loop, null, 2)}`);
47
- logger.debug(`---------------------------------------------------`);
48
47
  }
49
48
 
50
49
  Loop = inputArray[_ForEachData.loopIndex];
51
50
 
52
- logger.debug(` => Loop Data : ${JSON.stringify(Loop, null, 2)}`);
51
+ // Log each iteration: show iteration number, input, and previous iteration details (if available)
52
+ if (Loop !== undefined) {
53
+ logger.debug(` Iteration ${_ForEachData.loopIndex + 1}/${_ForEachData.loopLength}`);
54
+ logger.debug(` In Progress: ${_in_progress}`);
55
+ logger.debug(` Input: \n${JSON.stringify(Loop, null, 2)}`);
56
+
57
+ // Show previous iteration's input and result if available
58
+ if (_ForEachData.loopIndex > 0 && _temp_result && _temp_result[_ForEachData.loopIndex - 1]) {
59
+ const prevInput = inputArray[_ForEachData.loopIndex - 1];
60
+ const prevItem = _temp_result[_ForEachData.loopIndex - 1];
61
+ logger.debug(''); // empty line
62
+ logger.debug(` Previous Input: \n${JSON.stringify(prevInput, null, 2)}`);
63
+ logger.debug(''); // empty line
64
+ logger.debug(` Previous Result: \n${JSON.stringify(prevItem.result || prevItem, null, 2)}`);
65
+ }
66
+ }
53
67
 
54
68
  _in_progress = Loop !== undefined;
55
69
  if (_in_progress) {
@@ -67,12 +81,26 @@ export class ForEach extends Component {
67
81
 
68
82
  switch (config?.data?.format) {
69
83
  case 'minimal':
70
- Result = Result.map((item) => cleanupResult(item.result));
84
+ Result = Result.map((item) => cleanupResult(item.result || item));
71
85
  break;
72
86
  case 'results-array':
73
- Result = Result.map((item) => Object.values(cleanupResult(item.result))).flat(Infinity);
87
+ Result = Result.map((item) => Object.values(cleanupResult(item.result || item))).flat(Infinity);
74
88
  break;
75
89
  }
90
+
91
+ // Final summary: Input array, total iterations, and result
92
+ logger.debug(` Total Iterations: ${Result.length}`);
93
+ logger.debug(` In Progress: ${_in_progress}`);
94
+ logger.debug(''); // empty line
95
+ logger.debug(` Input: \n${JSON.stringify(input.Input, null, 2)}`);
96
+ logger.debug(''); // empty line
97
+ logger.debug(
98
+ ` Result: \n${JSON.stringify(
99
+ Result.map((item) => item.result || item),
100
+ null,
101
+ 2
102
+ )}`
103
+ );
76
104
  }
77
105
 
78
106
  return { Loop, Result, _temp_result, _error, _in_progress, _debug: logger.output };
@@ -107,25 +107,25 @@ export class GenAILLM extends Component {
107
107
  },
108
108
  webSearchCity: {
109
109
  type: 'string',
110
- max: 100,
110
+ max: 255,
111
111
  label: 'Web Search City',
112
112
  allowEmpty: true,
113
113
  },
114
114
  webSearchCountry: {
115
115
  type: 'string',
116
- max: 2,
116
+ max: 255,
117
117
  label: 'Web Search Country',
118
118
  allowEmpty: true,
119
119
  },
120
120
  webSearchRegion: {
121
121
  type: 'string',
122
- max: 100,
122
+ max: 255,
123
123
  label: 'Web Search Region',
124
124
  allowEmpty: true,
125
125
  },
126
126
  webSearchTimezone: {
127
127
  type: 'string',
128
- max: 100,
128
+ max: 255,
129
129
  label: 'Web Search Timezone',
130
130
  allowEmpty: true,
131
131
  },
@@ -165,7 +165,7 @@ export class GenAILLM extends Component {
165
165
  },
166
166
  searchCountry: {
167
167
  type: 'string',
168
- max: 2,
168
+ max: 255,
169
169
  label: 'Search Country',
170
170
  allowEmpty: true,
171
171
  },
@@ -247,7 +247,7 @@ export class GenAILLM extends Component {
247
247
  },
248
248
  reasoningEffort: {
249
249
  type: 'string',
250
- valid: ['none', 'default', 'low', 'medium', 'high'],
250
+ valid: ['none', 'default', 'low', 'medium', 'high', 'xhigh'],
251
251
  description: 'Controls the level of effort the model will put into reasoning',
252
252
  label: 'Reasoning Effort',
253
253
  },
@@ -295,10 +295,10 @@ export class GenAILLM extends Component {
295
295
  // #region Web Search
296
296
  useWebSearch: Joi.boolean().optional().label('Use Web Search'),
297
297
  webSearchContextSize: Joi.string().valid('high', 'medium', 'low').optional().label('Web Search Context Size'),
298
- webSearchCity: Joi.string().max(100).optional().allow('').label('Web Search City'),
299
- webSearchCountry: Joi.string().max(2).optional().allow('').label('Web Search Country'),
300
- webSearchRegion: Joi.string().max(100).optional().allow('').label('Web Search Region'),
301
- webSearchTimezone: Joi.string().max(100).optional().allow('').label('Web Search Timezone'),
298
+ webSearchCity: Joi.string().max(255).optional().allow('').label('Web Search City'),
299
+ webSearchCountry: Joi.string().max(255).optional().allow('').label('Web Search Country'),
300
+ webSearchRegion: Joi.string().max(255).optional().allow('').label('Web Search Region'),
301
+ webSearchTimezone: Joi.string().max(255).optional().allow('').label('Web Search Timezone'),
302
302
  // #endregion
303
303
 
304
304
  // #region xAI Search
@@ -307,7 +307,7 @@ export class GenAILLM extends Component {
307
307
  returnCitations: Joi.boolean().optional().allow('').label('Return Citations'),
308
308
  maxSearchResults: Joi.number().min(1).max(100).optional().allow('').label('Max Search Results'),
309
309
  searchDataSources: Joi.array().items(Joi.string().valid('web', 'x', 'news', 'rss')).max(4).optional().allow('').label('Search Data Sources'),
310
- searchCountry: Joi.string().length(2).optional().allow('').label('Search Country'),
310
+ searchCountry: Joi.string().max(255).optional().allow('').label('Search Country'),
311
311
  excludedWebsites: Joi.string().max(10000).optional().allow('').label('Excluded Websites'),
312
312
  allowedWebsites: Joi.string().max(10000).optional().allow('').label('Allowed Websites'),
313
313
  includedXHandles: Joi.string().max(1000).optional().allow('').label('Included X Handles'),
@@ -330,7 +330,11 @@ export class GenAILLM extends Component {
330
330
 
331
331
  // #region Reasoning
332
332
  useReasoning: Joi.boolean().optional().label('Use Reasoning'),
333
- reasoningEffort: Joi.string().valid('none', 'default', 'minimal', 'low', 'medium', 'high').optional().allow('').label('Reasoning Effort'),
333
+ reasoningEffort: Joi.string()
334
+ .valid('none', 'default', 'minimal', 'low', 'medium', 'high', 'xhigh')
335
+ .optional()
336
+ .allow('')
337
+ .label('Reasoning Effort'),
334
338
  maxThinkingTokens: Joi.number().min(1).optional().label('Maximum Thinking Tokens'),
335
339
  // #endregion
336
340
  });
@@ -348,13 +352,24 @@ export class GenAILLM extends Component {
348
352
  logger.debug(`=== GenAILLM Log ===`);
349
353
  let teamId = agent?.teamId;
350
354
 
351
- const passThrough: boolean = config.data.passthrough || false;
352
- const useContextWindow: boolean = config.data.useContextWindow || false;
353
- const useSystemPrompt: boolean = config.data.useSystemPrompt || false;
354
- const useWebSearch: boolean = config.data.useWebSearch || false;
355
- const maxTokens: number = parseInt(config.data.maxTokens) || 1024;
356
- const maxContextWindowLength: number = parseInt(config.data.maxContextWindowLength) || 1024;
357
- const model: string = config.data.model || 'echo';
355
+ // Resolve template variables in config.data without mutating original config
356
+ const resolvedConfigData = {
357
+ ...config.data,
358
+ prompt: config.data.prompt && TemplateString(config.data.prompt).parse(input).result,
359
+ webSearchCity: config.data.webSearchCity && TemplateString(config.data.webSearchCity).parse(input).result,
360
+ webSearchCountry: config.data.webSearchCountry && TemplateString(config.data.webSearchCountry).parse(input).result,
361
+ webSearchRegion: config.data.webSearchRegion && TemplateString(config.data.webSearchRegion).parse(input).result,
362
+ webSearchTimezone: config.data.webSearchTimezone && TemplateString(config.data.webSearchTimezone).parse(input).result,
363
+
364
+ searchCountry: config.data.searchCountry && TemplateString(config.data.searchCountry).parse(input).result,
365
+ };
366
+
367
+ const passThrough: boolean = resolvedConfigData.passthrough || false;
368
+ const useContextWindow: boolean = resolvedConfigData.useContextWindow || false;
369
+ const useSystemPrompt: boolean = resolvedConfigData.useSystemPrompt || false;
370
+ const maxTokens: number = parseInt(resolvedConfigData.maxTokens) || 1024;
371
+ const maxContextWindowLength: number = parseInt(resolvedConfigData.maxContextWindowLength) || 1024;
372
+ const model: string = resolvedConfigData.model || 'echo';
358
373
  const llmInference: LLMInference = await LLMInference.getInstance(model, AccessCandidate.agent(agent.id));
359
374
 
360
375
  // if the llm is undefined, then it means we removed the model from our system
@@ -371,7 +386,7 @@ export class GenAILLM extends Component {
371
386
 
372
387
  logger.debug(` Model : ${modelId || model}`);
373
388
 
374
- let prompt: any = TemplateString(config.data.prompt).parse(input).result;
389
+ const prompt: any = resolvedConfigData.prompt;
375
390
 
376
391
  let files: any[] = parseFiles(input, config);
377
392
  let isMultimodalRequest = false;
@@ -403,8 +418,24 @@ export class GenAILLM extends Component {
403
418
  files = validFiles.filter(Boolean);
404
419
 
405
420
  if (files.length === 0) {
421
+ // No valid files after filtering - determine the cause
422
+ const hasDetectedMimeTypes = fileTypes.size > 0;
423
+
424
+ if (!hasDetectedMimeTypes) {
425
+ // Case 1: No mime types detected - files are corrupted/invalid
426
+ return {
427
+ _error: `Unable to process the provided file(s). Please ensure file(s) are not corrupted, have valid formats, and contain readable data.`,
428
+ _debug: logger.output,
429
+ };
430
+ }
431
+
432
+ // Case 2: Files detected but model doesn't support those types
433
+ const detectedTypes = Array.from(fileTypes).join(', ');
434
+ const supportedTypes = new Set(Object.values(supportedFileTypes).flat());
435
+ const supportedTypesText = supportedTypes.size > 0 ? `\nSupported types: ${Array.from(supportedTypes).join(', ')}` : '';
436
+
406
437
  return {
407
- _error: `Model does not support ${fileTypes?.size > 0 ? Array.from(fileTypes).join(', ') : 'File(s)'}`,
438
+ _error: `Model '${model}' does not support the provided file type(s): ${detectedTypes}.${supportedTypesText}`,
408
439
  _debug: logger.output,
409
440
  };
410
441
  }
@@ -420,7 +451,7 @@ export class GenAILLM extends Component {
420
451
 
421
452
  // default to json response format
422
453
  const hasCustomOutputs = config?.outputs?.some((output) => !output.default);
423
- config.data.responseFormat = config.data?.responseFormat || (hasCustomOutputs ? 'json' : '');
454
+ resolvedConfigData.responseFormat = resolvedConfigData?.responseFormat || (hasCustomOutputs ? 'json' : '');
424
455
 
425
456
  // request to LLM
426
457
  let response: any;
@@ -478,7 +509,7 @@ export class GenAILLM extends Component {
478
509
  contextWindow: messages,
479
510
  files,
480
511
  params: {
481
- ...config.data,
512
+ ...resolvedConfigData,
482
513
  agentId: agent.id,
483
514
  },
484
515
  onFallback: (fallbackInfo) => {
@@ -528,26 +559,36 @@ export class GenAILLM extends Component {
528
559
  response = await contentPromise.catch((error) => {
529
560
  return { error: error.message || error };
530
561
  });
531
- // If the model stopped before completing the response, this is usually due to output token limit reached.
562
+
563
+ // #region Handle Response Errors
564
+ if (response?.error) {
565
+ const error = response?.error + ' ' + (response?.details || '');
566
+ logger.error(` LLM Error=`, error);
567
+
568
+ return { Output: response?.data, _error: error, _debug: logger.output };
569
+ }
570
+
571
+ const emptyResponseErrorMsg =
572
+ "Empty response. This is usually due to output token limit reached. Please try again with a higher 'Maximum Output Tokens'.";
573
+
574
+ // If the finish reason is not "stop", it means the model stopped before completing the response.
532
575
  if (finishReason !== 'stop') {
576
+ let errMsg = `The model stopped before completing the response.
577
+ \nReason: ${finishReason}.
578
+ \n${!response ? emptyResponseErrorMsg : ''}`;
579
+
533
580
  return {
534
581
  Reply: response,
535
- _error: 'The model stopped before completing the response, this is usually due to output token limit reached.',
582
+ _error: errMsg,
536
583
  _debug: logger.output,
537
584
  };
538
585
  }
539
586
 
540
- // in case we have the response but it's empty string, undefined or null
587
+ // If the finish reason is "stop" but there is still no response, it is usually caused by reaching the output token limit.
541
588
  if (!response) {
542
- return { _error: ' LLM Error = Empty Response!', _debug: logger.output };
543
- }
544
-
545
- if (response?.error) {
546
- const error = response?.error + ' ' + (response?.details || '');
547
- logger.error(` LLM Error=`, error);
548
-
549
- return { Output: response?.data, _error: error, _debug: logger.output };
589
+ return { _error: emptyResponseErrorMsg, _debug: logger.output };
550
590
  }
591
+ // #endregion
551
592
 
552
593
  const Reply = llmInference.connector.postProcess(response);
553
594
  if (Reply.error) {
@@ -10,7 +10,8 @@ import { encode } from 'gpt-tokenizer';
10
10
  import { Component } from './Component.class';
11
11
  import { JSONContent } from '@sre/helpers/JsonContent.helper';
12
12
  import { LLMInference } from '@sre/LLMManager/LLM.inference';
13
- import { TLLMMessageRole } from '@sre/types/LLM.types';
13
+ import { LLMHelper } from '@sre/LLMManager/LLM.helper';
14
+ import { TLLMMessageRole, TLLMEvent } from '@sre/types/LLM.types';
14
15
  import { VaultHelper } from '@sre/Security/Vault.service/Vault.helper';
15
16
  import path from 'path';
16
17
  import config from '@sre/config';
@@ -113,7 +114,7 @@ export class LLMAssistant extends Component {
113
114
  const conversationId = input.ConversationId;
114
115
 
115
116
  let behavior = TemplateString(config.data.behavior).parse(input).result;
116
- logger.debug(`[Parsed Behavior] \n${behavior}`);
117
+ logger.debug(` [Parsed Behavior] \n${behavior}`);
117
118
 
118
119
  //#region get max tokens
119
120
  let maxTokens = 2048;
@@ -147,7 +148,14 @@ export class LLMAssistant extends Component {
147
148
  };
148
149
 
149
150
  let response: any;
150
- if (passThrough) {
151
+ const isClaude4 = LLMHelper.isClaude4Family(modelId || model);
152
+
153
+ // Use streaming for passthrough OR Claude 4 family models
154
+ if (passThrough || isClaude4) {
155
+ if (isClaude4) {
156
+ logger.debug(`\n ⚡ Using streaming for Claude 4 family model`);
157
+ }
158
+
151
159
  const contentPromise = new Promise(async (resolve, reject) => {
152
160
  let _content = '';
153
161
  const eventEmitter: any = await llmInference
@@ -162,25 +170,53 @@ export class LLMAssistant extends Component {
162
170
  console.error('Error on promptStream: ', error);
163
171
  reject(error);
164
172
  });
165
- eventEmitter.on('content', (content) => {
166
- if (typeof agent.callback === 'function') {
167
- agent.callback({ content });
173
+
174
+ eventEmitter.on(TLLMEvent.Content, (content) => {
175
+ if (passThrough) {
176
+ if (typeof agent.callback === 'function') {
177
+ agent.callback({ content });
178
+ }
179
+ agent.sse.send('llm/passthrough/content', content);
168
180
  }
169
- agent.sse.send('llm/passthrough/content', content);
170
181
  _content += content;
171
182
  });
172
- eventEmitter.on('thinking', (thinking) => {
173
- if (typeof agent.callback === 'function') {
174
- agent.callback({ thinking });
183
+
184
+ eventEmitter.on(TLLMEvent.Thinking, (thinking) => {
185
+ if (passThrough) {
186
+ if (typeof agent.callback === 'function') {
187
+ agent.callback({ thinking });
188
+ }
189
+ agent.sse.send('llm/passthrough/thinking', thinking);
175
190
  }
176
- agent.sse.send('llm/passthrough/thinking', thinking);
177
191
  });
178
- eventEmitter.on('end', () => {
179
- console.log('end');
192
+
193
+ eventEmitter.on(TLLMEvent.End, () => {
180
194
  resolve(_content);
181
195
  });
196
+
197
+ eventEmitter.on(TLLMEvent.Error, (error) => {
198
+ reject(error);
199
+ });
182
200
  });
183
- response = await contentPromise;
201
+
202
+ response = await contentPromise.catch((error) => {
203
+ return { error: error.message || error };
204
+ });
205
+
206
+ // Handle streaming errors
207
+ if (response?.error) {
208
+ const error = response?.error + ' ' + (response?.details || '');
209
+ logger.error(` LLM Error=`, error);
210
+ return { Response: response?.data, _error: error, _debug: logger.output };
211
+ }
212
+
213
+ // Post-process the streaming response
214
+ const postProcessed = llmInference.connector.postProcess(response);
215
+ if (postProcessed.error) {
216
+ logger.error(` LLM Post-process Error=`, postProcessed.error);
217
+ return { _error: postProcessed.error, _debug: logger.output };
218
+ }
219
+ response = postProcessed;
184
220
  } else {
185
221
  response = await llmInference
186
222
  .prompt({
@@ -191,6 +227,12 @@ export class LLMAssistant extends Component {
191
227
  },
192
228
  })
193
229
  .catch((error) => ({ error: error }));
230
+
231
+ if (response?.error) {
232
+ const error = response?.error + ' ' + (response?.details || '');
233
+ logger.error(` LLM Error=`, error);
234
+ return { Response: response?.data, _error: error, _debug: logger.output };
235
+ }
194
236
  }
195
237
 
196
238
  // in case we have the response but it's empty string, undefined or null
@@ -198,13 +240,6 @@ export class LLMAssistant extends Component {
198
240
  return { _error: ' LLM Error = Empty Response!', _debug: logger.output };
199
241
  }
200
242
 
201
- if (response?.error) {
202
- const error = response?.error + ' ' + (response?.details || '');
203
- logger.error(` LLM Error=`, error);
204
-
205
- return { Response: response?.data, _error: error, _debug: logger.output };
206
- }
207
-
208
243
  messages.push({ role: 'assistant', content: response });
209
244
  saveMessagesToSession(agent.id, userId, conversationId, messages, ttl);
210
245