@smythos/sre 1.7.40 → 1.7.42

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 (40) hide show
  1. package/dist/index.js +49 -42
  2. package/dist/index.js.map +1 -1
  3. package/dist/types/Components/AgentPlugin.class.d.ts +1 -1
  4. package/dist/types/Components/RAG/DataSourceCleaner.class.d.ts +4 -4
  5. package/dist/types/Components/RAG/DataSourceComponent.class.d.ts +5 -1
  6. package/dist/types/config.d.ts +1 -0
  7. package/dist/types/helpers/Conversation.helper.d.ts +10 -13
  8. package/dist/types/helpers/TemplateString.helper.d.ts +1 -1
  9. package/dist/types/index.d.ts +1 -0
  10. package/dist/types/subsystems/IO/VectorDB.service/VectorDBConnector.d.ts +1 -0
  11. package/dist/types/subsystems/LLMManager/LLM.helper.d.ts +19 -0
  12. package/dist/types/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.d.ts +15 -10
  13. package/dist/types/types/LLM.types.d.ts +23 -0
  14. package/package.json +1 -1
  15. package/src/Components/AgentPlugin.class.ts +20 -3
  16. package/src/Components/Classifier.class.ts +79 -16
  17. package/src/Components/ForEach.class.ts +34 -6
  18. package/src/Components/GenAILLM.class.ts +54 -23
  19. package/src/Components/LLMAssistant.class.ts +56 -21
  20. package/src/Components/RAG/DataSourceCleaner.class.ts +13 -11
  21. package/src/Components/RAG/DataSourceComponent.class.ts +39 -13
  22. package/src/Components/RAG/DataSourceIndexer.class.ts +18 -12
  23. package/src/Components/RAG/DataSourceLookup.class.ts +14 -10
  24. package/src/Components/ScrapflyWebScrape.class.ts +7 -0
  25. package/src/config.ts +1 -0
  26. package/src/helpers/Conversation.helper.ts +112 -26
  27. package/src/helpers/TemplateString.helper.ts +6 -5
  28. package/src/index.ts +213 -212
  29. package/src/index.ts.bak +213 -212
  30. package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +1 -0
  31. package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +11 -0
  32. package/src/subsystems/IO/VectorDB.service/embed/index.ts +9 -11
  33. package/src/subsystems/LLMManager/LLM.helper.ts +25 -0
  34. package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +1 -1
  35. package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +190 -146
  36. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/utils.ts +1 -1
  37. package/src/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.class.ts +402 -66
  38. package/src/types/LLM.types.ts +24 -0
  39. package/src/utils/data.utils.ts +6 -4
  40. package/src/Components/DataSourceIndexer.class.ts +0 -295
@@ -9,7 +9,7 @@ export declare class AgentPlugin extends Component {
9
9
  _debug: string;
10
10
  Response?: undefined;
11
11
  } | {
12
- Response: string;
12
+ Response: any;
13
13
  _debug: string;
14
14
  _error?: undefined;
15
15
  }>;
@@ -6,13 +6,13 @@ export declare class DataSourceCleaner extends DataSourceComponent {
6
6
  constructor();
7
7
  init(): void;
8
8
  process(input: any, config: any, agent: Agent): Promise<{
9
- _debug: string;
10
- Success: boolean;
11
- _error?: undefined;
12
- } | {
13
9
  _debug: string;
14
10
  _error: any;
15
11
  Success?: undefined;
12
+ } | {
13
+ _debug: string;
14
+ Success: boolean;
15
+ _error?: undefined;
16
16
  }>;
17
17
  processV1(input: any, config: any, agent: Agent): Promise<{
18
18
  _debug: string;
@@ -21,7 +21,11 @@ export declare class DataSourceCompError extends Error {
21
21
  }
22
22
  export declare class DataSourceComponent extends Component {
23
23
  constructor();
24
- resolveVectorDbConnector(namespace: string | NsRecord, teamId: string): Promise<VectorDBConnector>;
24
+ resolveVectorDbConnector(namespace: string | NsRecord, teamId: string): Promise<{
25
+ vecDbConnector: VectorDBConnector;
26
+ namespaceRecord: NsRecord;
27
+ }>;
28
+ private resolveNamespaceRecord;
25
29
  buildEmbeddingConfig(embedding: {
26
30
  dimensions: string;
27
31
  modelId: string;
@@ -3,6 +3,7 @@ declare const config: {
3
3
  LOG_LEVEL: string;
4
4
  LOG_FILTER: string;
5
5
  NODE_ENV: string;
6
+ ROLLOUT_RAG_V2: string;
6
7
  };
7
8
  agent: {
8
9
  ENDPOINT_PREFIX: string;
@@ -1,6 +1,6 @@
1
1
  import { LLMInference } from '@sre/LLMManager/LLM.inference';
2
2
  import { LLMContext } from '@sre/MemoryManager/LLMContext';
3
- import { ILLMContextStore, TLLMModel } from '@sre/types/LLM.types';
3
+ import { IConversationSettings, TLLMModel } from '@sre/types/LLM.types';
4
4
  import EventEmitter from 'events';
5
5
  export declare class Conversation extends EventEmitter {
6
6
  private _model;
@@ -30,7 +30,15 @@ export declare class Conversation extends EventEmitter {
30
30
  agentData: any;
31
31
  private _id;
32
32
  get id(): string;
33
+ private _toolCallCount;
34
+ private _maxToolCallsPerSession;
35
+ private _disableToolsForNextCall;
33
36
  get context(): LLMContext;
37
+ get storeId(): string;
38
+ /**
39
+ * Headers to be added to all tool call requests
40
+ */
41
+ headers: Record<string, string>;
34
42
  private _lastError;
35
43
  private _spec;
36
44
  private _customToolsDeclarations;
@@ -41,18 +49,7 @@ export declare class Conversation extends EventEmitter {
41
49
  get model(): string | TLLMModel;
42
50
  private _llmInference;
43
51
  get llmInference(): LLMInference;
44
- constructor(_model: string | TLLMModel, _specSource?: string | Record<string, any>, _settings?: {
45
- maxContextSize?: number;
46
- maxOutputTokens?: number;
47
- systemPrompt?: string;
48
- toolChoice?: string;
49
- store?: ILLMContextStore;
50
- experimentalCache?: boolean;
51
- toolsStrategy?: (toolsConfig: any) => any;
52
- agentId?: string;
53
- agentVersion?: string;
54
- baseUrl?: string;
55
- });
52
+ constructor(_model: string | TLLMModel, _specSource?: string | Record<string, any>, _settings?: IConversationSettings);
56
53
  get ready(): any;
57
54
  prompt(message?: string | any, toolHeaders?: {}, concurrentToolCalls?: number, abortSignal?: AbortSignal): Promise<string>;
58
55
  streamPrompt(message?: string | any, toolHeaders?: {}, concurrentToolCalls?: number, abortSignal?: AbortSignal): Promise<string>;
@@ -33,7 +33,7 @@ export declare class TemplateStringHelper {
33
33
  * unmatched placeholders will be left as is
34
34
  * Recursively resolves nested template variables until no more variables are found
35
35
  */
36
- parse(data: Record<string, string>, regex?: TemplateStringMatch, maxDepth?: number): this;
36
+ parse(data: Record<string, unknown>, regex?: TemplateStringMatch, maxDepth?: number): this;
37
37
  /**
38
38
  * Parses a template string by replacing placeholders with values from the provided data object, keeping the original raw values intact. This is particularly important for BinaryInput instances, as they include buffer data.
39
39
  * unmatched placeholders will be left as is
@@ -87,6 +87,7 @@ export * from './Components/APICall/parseUrl';
87
87
  export * from './Components/Image/imageSettings.config';
88
88
  export * from './Components/RAG/DataSourceCleaner.class';
89
89
  export * from './Components/RAG/DataSourceComponent.class';
90
+ export * from './Components/RAG/DataSourceIndexer.class';
90
91
  export * from './Components/RAG/DataSourceLookup.class';
91
92
  export * from './Components/Triggers/Gmail.trigger';
92
93
  export * from './Components/Triggers/JobScheduler.trigger';
@@ -22,6 +22,7 @@ export interface IVectorDBRequest {
22
22
  }
23
23
  export declare abstract class VectorDBConnector extends SecureConnector<IVectorDBRequest> {
24
24
  protected readonly USER_METADATA_KEY = "user_metadata";
25
+ protected readonly LEGACY_USER_METADATA_KEY = "metadata";
25
26
  abstract id: string;
26
27
  abstract getResourceACL(resourceId: string, candidate: IAccessCandidate): Promise<ACL>;
27
28
  requester(candidate: AccessCandidate): IVectorDBRequest;
@@ -133,4 +133,23 @@ export declare class LLMHelper {
133
133
  * @returns {TLLMMessageBlock[]} The modified array of message objects.
134
134
  */
135
135
  static removeDuplicateUserMessages(messages: TLLMMessageBlock[]): TLLMMessageBlock[];
136
+ /**
137
+ * Checks if the given model is part of the Claude 4 family.
138
+ *
139
+ * @param {string} modelId - The model identifier to check.
140
+ * @returns {boolean} True if the model is Claude 4 family, false otherwise.
141
+ *
142
+ * @example
143
+ * const isClaude4 = LLMHelper.isClaude4Family('claude-sonnet-4-20250514');
144
+ * console.log(isClaude4); // true
145
+ *
146
+ * @example
147
+ * const isClaude4 = LLMHelper.isClaude4Family('claude-opus-4-5');
148
+ * console.log(isClaude4); // true
149
+ *
150
+ * @example
151
+ * const isClaude4 = LLMHelper.isClaude4Family('gpt-4-turbo');
152
+ * console.log(isClaude4); // false
153
+ */
154
+ static isClaude4Family(modelId: string): boolean;
136
155
  }
@@ -1,6 +1,6 @@
1
1
  import EventEmitter from 'events';
2
2
  import { type GenerateContentResponseUsageMetadata } from '@google/genai/node';
3
- import { TLLMMessageBlock, ToolData, TLLMToolResultMessageBlock, APIKeySource, ILLMRequestFuncParams, TLLMChatResponse, TGoogleAIRequestBody, ILLMRequestContext, TLLMPreparedParams } from '@sre/types/LLM.types';
3
+ import { TLLMMessageBlock, ToolData, TLLMToolResultMessageBlock, APIKeySource, ILLMRequestFuncParams, TLLMChatResponse, TGoogleAIRequestBody, TLLMPreparedParams } from '@sre/types/LLM.types';
4
4
  import { LLMConnector } from '../LLMConnector';
5
5
  type UsageMetadataWithThoughtsToken = GenerateContentResponseUsageMetadata & {
6
6
  thoughtsTokenCount?: number;
@@ -26,6 +26,7 @@ export declare class GoogleAIConnector extends LLMConnector {
26
26
  sourceId: string;
27
27
  input_tokens: number;
28
28
  output_tokens: number;
29
+ output_tokens_image: number;
29
30
  input_tokens_audio: number;
30
31
  input_tokens_cache_read: number;
31
32
  input_tokens_cache_read_audio: number;
@@ -33,22 +34,26 @@ export declare class GoogleAIConnector extends LLMConnector {
33
34
  keySource: APIKeySource;
34
35
  agentId: string;
35
36
  teamId: string;
36
- inTier: string;
37
- outTier: string;
38
- crTier: string;
37
+ tier: string;
39
38
  };
40
39
  /**
41
40
  * Extract text and image tokens from Google AI usage metadata
42
41
  */
43
42
  private extractTokenCounts;
44
- protected reportImageUsage({ usage, context, numberOfImages, }: {
45
- usage: {
46
- cost?: number;
47
- usageMetadata?: UsageMetadataWithThoughtsToken;
48
- };
49
- context: ILLMRequestContext;
43
+ protected reportImageCost({ cost, context, numberOfImages }: {
44
+ cost: any;
45
+ context: any;
50
46
  numberOfImages?: number;
51
47
  }): void;
48
+ /**
49
+ * Normalizes function response values to ensure they conform to Google AI's STRUCT requirement.
50
+ * Gemini expects functionResponse.response to be a STRUCT (JSON object format), not a list or scalar.
51
+ */
52
+ private normalizeFunctionResponse;
53
+ /**
54
+ * Parses and normalizes function response values, handling string JSON and various data types.
55
+ */
56
+ private parseFunctionResponse;
52
57
  formatToolsConfig({ toolDefinitions, toolChoice }: {
53
58
  toolDefinitions: any;
54
59
  toolChoice?: string;
@@ -196,6 +196,7 @@ export type TLLMModel = {
196
196
  isCustomLLM?: boolean;
197
197
  isUserCustomLLM?: boolean;
198
198
  modelId?: string;
199
+ modelEntryName?: string;
199
200
  tokens?: number;
200
201
  completionTokens?: number;
201
202
  components?: string[];
@@ -394,10 +395,32 @@ export type TLLMInputMessage = {
394
395
  }[];
395
396
  };
396
397
  export interface ILLMContextStore {
398
+ id: string;
397
399
  save(messages: any[]): Promise<void>;
398
400
  load(count?: number): Promise<any[]>;
399
401
  getMessage(message_id: string): Promise<any[]>;
400
402
  }
403
+ /**
404
+ * Configuration options for Conversation helper
405
+ */
406
+ export interface IConversationSettings {
407
+ maxContextSize?: number;
408
+ maxOutputTokens?: number;
409
+ systemPrompt?: string;
410
+ toolChoice?: string;
411
+ store?: ILLMContextStore;
412
+ experimentalCache?: boolean;
413
+ toolsStrategy?: (toolsConfig: any) => any;
414
+ agentId?: string;
415
+ agentVersion?: string;
416
+ baseUrl?: string;
417
+ /**
418
+ * Maximum number of tool calls allowed in a single conversation session.
419
+ * Prevents infinite loops in tool calling scenarios.
420
+ * @default 100
421
+ */
422
+ maxToolCalls?: number;
423
+ }
401
424
  export declare enum APIKeySource {
402
425
  Smyth = "smyth-managed",
403
426
  User = "user-managed",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smythos/sre",
3
- "version": "1.7.40",
3
+ "version": "1.7.42",
4
4
  "description": "Smyth Runtime Environment",
5
5
  "author": "Alaa-eddine KADDOURI",
6
6
  "license": "MIT",
@@ -6,6 +6,8 @@ import { TemplateString } from '@sre/helpers/TemplateString.helper';
6
6
  import { IAgent as Agent } from '@sre/types/Agent.types';
7
7
  import { Conversation } from '@sre/helpers/Conversation.helper';
8
8
  import { Component } from './Component.class';
9
+ import { LLMInference } from '@sre/LLMManager/LLM.inference';
10
+ import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
9
11
 
10
12
  export class AgentPlugin extends Component {
11
13
  protected configSchema = Joi.object({
@@ -84,16 +86,31 @@ export class AgentPlugin extends Component {
84
86
 
85
87
  const conv = new Conversation(model, subAgentId, { systemPrompt: descForModel, agentVersion: version });
86
88
 
87
- const result = await conv.prompt(prompt, {
89
+ // # Region: enhance the prompt to produce a JSON output format based on the available component outputs.
90
+ const llmInference: LLMInference = await LLMInference.getInstance(model, AccessCandidate.agent(agent.id));
91
+
92
+ // if the llm is undefined, then it means we removed the model from our system
93
+ if (!llmInference.connector) {
94
+ return {
95
+ _error: `The model '${model}' is not available. Please try a different one.`,
96
+ _debug: logger.output,
97
+ };
98
+ }
99
+ const enhancedPrompt = llmInference.connector.enhancePrompt(prompt, config);
100
+ // # End Region: prompt enhancement
101
+
102
+ const result = await conv.prompt(enhancedPrompt, {
88
103
  'X-AGENT-ID': subAgentId,
89
104
  'X-AGENT-VERSION': version,
90
105
  'X-REQUEST-TAG': reqTag, //request Tag identifies the request and tells the called agent that the call comes from internal agent
91
106
  'x-caller-session-id': agent.callerSessionId,
92
107
  });
93
108
 
94
- logger.debug(`Response:\n`, result, '\n');
109
+ const processedResponse = llmInference.connector.postProcess(result);
110
+
111
+ logger.debug(`Response:\n`, processedResponse, '\n');
95
112
 
96
- return { Response: result, _debug: logger.output };
113
+ return { Response: processedResponse, _debug: logger.output };
97
114
  } catch (error: any) {
98
115
  console.error('Error on running Agent Component: ', error);
99
116
  return { _error: `Error on running Agent Component!\n${error?.message || JSON.stringify(error)}`, _debug: logger.output };
@@ -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);
@@ -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) => {