@smythos/sre 1.5.37 → 1.5.40

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 (82) hide show
  1. package/dist/index.js +65 -45
  2. package/dist/index.js.map +1 -1
  3. package/dist/types/Components/ECMASandbox.class.d.ts +14 -0
  4. package/dist/types/Components/MemoryDeleteKeyVal.class.d.ts +19 -0
  5. package/dist/types/Components/MemoryReadKeyVal.class.d.ts +17 -0
  6. package/dist/types/Components/MemoryWriteKeyVal.class.d.ts +17 -0
  7. package/dist/types/Components/MemoryWriteObject.class.d.ts +19 -0
  8. package/dist/types/Components/index.d.ts +10 -0
  9. package/dist/types/Core/ConnectorsService.d.ts +2 -1
  10. package/dist/types/helpers/ECMASandbox.helper.d.ts +3 -0
  11. package/dist/types/helpers/Log.helper.d.ts +1 -1
  12. package/dist/types/index.d.ts +8 -1
  13. package/dist/types/subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class.d.ts +19 -0
  14. package/dist/types/subsystems/LLMManager/LLM.helper.d.ts +21 -10
  15. package/dist/types/subsystems/LLMManager/LLM.service/LLMConnector.d.ts +5 -5
  16. package/dist/types/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.d.ts +2 -3
  17. package/dist/types/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.d.ts +2 -3
  18. package/dist/types/subsystems/LLMManager/LLM.service/connectors/Echo.class.d.ts +2 -3
  19. package/dist/types/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.d.ts +2 -3
  20. package/dist/types/subsystems/LLMManager/LLM.service/connectors/Groq.class.d.ts +2 -3
  21. package/dist/types/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.d.ts +3 -4
  22. package/dist/types/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.d.ts +19 -14
  23. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.d.ts +95 -0
  24. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.d.ts +87 -0
  25. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterface.d.ts +85 -0
  26. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterfaceFactory.d.ts +49 -0
  27. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.d.ts +146 -0
  28. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.d.ts +10 -0
  29. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/index.d.ts +4 -0
  30. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/types.d.ts +38 -0
  31. package/dist/types/subsystems/LLMManager/LLM.service/connectors/xAI.class.d.ts +1 -2
  32. package/dist/types/subsystems/Security/Vault.service/connectors/JSONFileVault.class.d.ts +5 -0
  33. package/dist/types/types/LLM.types.d.ts +82 -37
  34. package/dist/types/utils/data.utils.d.ts +2 -1
  35. package/package.json +4 -3
  36. package/src/Components/APICall/APICall.class.ts +2 -1
  37. package/src/Components/Component.class.ts +1 -1
  38. package/src/Components/ECMASandbox.class.ts +71 -0
  39. package/src/Components/GenAILLM.class.ts +1 -1
  40. package/src/Components/MemoryDeleteKeyVal.class.ts +70 -0
  41. package/src/Components/MemoryReadKeyVal.class.ts +66 -0
  42. package/src/Components/MemoryWriteKeyVal.class.ts +62 -0
  43. package/src/Components/MemoryWriteObject.class.ts +97 -0
  44. package/src/Components/index.ts +10 -0
  45. package/src/Core/ConnectorsService.ts +3 -3
  46. package/src/helpers/Conversation.helper.ts +11 -3
  47. package/src/helpers/ECMASandbox.helper.ts +54 -0
  48. package/src/helpers/Log.helper.ts +57 -17
  49. package/src/index.ts +192 -185
  50. package/src/index.ts.bak +192 -185
  51. package/src/subsystems/AgentManager/Agent.class.ts +11 -6
  52. package/src/subsystems/AgentManager/AgentRuntime.class.ts +13 -13
  53. package/src/subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class.ts +131 -0
  54. package/src/subsystems/ComputeManager/Code.service/index.ts +2 -0
  55. package/src/subsystems/LLMManager/LLM.helper.ts +57 -27
  56. package/src/subsystems/LLMManager/LLM.inference.ts +4 -0
  57. package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +125 -28
  58. package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +18 -17
  59. package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +2 -7
  60. package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +2 -6
  61. package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +26 -17
  62. package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +2 -7
  63. package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +2 -7
  64. package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +314 -84
  65. package/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts +455 -0
  66. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +528 -0
  67. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterface.ts +100 -0
  68. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterfaceFactory.ts +81 -0
  69. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +853 -0
  70. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.ts +37 -0
  71. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/index.ts +4 -0
  72. package/src/subsystems/LLMManager/LLM.service/connectors/openai/types.ts +37 -0
  73. package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +0 -5
  74. package/src/subsystems/LLMManager/LLM.service/index.ts +1 -1
  75. package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +2 -2
  76. package/src/subsystems/LLMManager/models.ts +1 -1
  77. package/src/subsystems/MemoryManager/Cache.service/connectors/RedisCache.class.ts +18 -0
  78. package/src/subsystems/MemoryManager/RuntimeContext.ts +33 -16
  79. package/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts +68 -42
  80. package/src/types/LLM.types.ts +91 -43
  81. package/src/utils/data.utils.ts +3 -2
  82. package/src/subsystems/LLMManager/LLM.service/connectors/OpenAI.class.ts +0 -848
@@ -0,0 +1,131 @@
1
+ import { IAccessCandidate, TAccessLevel } from '@sre/types/ACL.types';
2
+ import { ACL } from '@sre/Security/AccessControl/ACL.class';
3
+ import { CodeConfig, CodePreparationResult, CodeConnector, CodeInput, CodeDeployment, CodeExecutionResult } from '../CodeConnector';
4
+ import { AccessRequest } from '@sre/Security/AccessControl/AccessRequest.class';
5
+ import { Logger } from '@sre/helpers/Log.helper';
6
+ import axios from 'axios';
7
+ import { generateExecutableCode, runJs } from '@sre/helpers/ECMASandbox.helper';
8
+ import { validateAsyncMainFunction } from '@sre/helpers/AWSLambdaCode.helper';
9
+
10
+ const console = Logger('ECMASandbox');
11
+ export class ECMASandbox extends CodeConnector {
12
+ public name = 'ECMASandbox';
13
+ private sandboxUrl: string;
14
+
15
+ constructor(config: { sandboxUrl: string }) {
16
+ super(config);
17
+ this.sandboxUrl = config.sandboxUrl;
18
+ }
19
+ public async prepare(acRequest: AccessRequest, codeUID: string, input: CodeInput, config: CodeConfig): Promise<CodePreparationResult> {
20
+ return {
21
+ prepared: true,
22
+ errors: [],
23
+ warnings: [],
24
+ };
25
+ }
26
+
27
+ public async deploy(acRequest: AccessRequest, codeUID: string, input: CodeInput, config: CodeConfig): Promise<CodeDeployment> {
28
+ return {
29
+ id: codeUID,
30
+ runtime: config.runtime,
31
+ createdAt: new Date(),
32
+ status: 'Deployed',
33
+ };
34
+ }
35
+
36
+ public async execute(acRequest: AccessRequest, codeUID: string, inputs: Record<string, any>, config: CodeConfig): Promise<CodeExecutionResult> {
37
+ try {
38
+ const { isValid, error, parameters } = validateAsyncMainFunction(inputs.code);
39
+ if (!isValid) {
40
+ return {
41
+ output: undefined,
42
+ executionTime: 0,
43
+ success: false,
44
+ errors: [error],
45
+ };
46
+ }
47
+ const executableCode = generateExecutableCode(inputs.code, parameters, inputs.inputs);
48
+ if (!this.sandboxUrl) {
49
+ //Temporarily disable the builtin ECMASandbox
50
+
51
+ // run js code in isolated vm
52
+
53
+ console.debug('Running code in isolated vm');
54
+ const executionStartTime = Date.now();
55
+ const result = await runJs(executableCode);
56
+ const executionTime = Date.now() - executionStartTime;
57
+ console.debug(`Code result: ${result}`);
58
+ return {
59
+ output: result,
60
+ executionTime,
61
+ success: true,
62
+ errors: [],
63
+
64
+ };
65
+ } else {
66
+ console.debug('Running code in remote sandbox');
67
+ const executionStartTime = Date.now();
68
+ const result: any = await axios.post(this.sandboxUrl, { code: executableCode }).catch((error) => ({ error }));
69
+ const executionTime = Date.now() - executionStartTime;
70
+ if (result.error) {
71
+ const error = result.error?.response?.data || result.error?.message || result.error.toString() || 'Unknown error';
72
+ console.error(`Error running code: ${JSON.stringify(error, null, 2)}`);
73
+ return {
74
+ output: undefined,
75
+ executionTime,
76
+ success: false,
77
+ errors: [error],
78
+ };
79
+ } else {
80
+ console.debug(`Code result: ${result?.data?.Output}`);
81
+ return {
82
+ output: result.data?.Output,
83
+ executionTime,
84
+ success: true,
85
+ errors: [],
86
+ };
87
+ }
88
+ }
89
+ } catch (error) {
90
+ console.error(`Error running code: ${error}`);
91
+ return {
92
+ output: undefined,
93
+ executionTime: 0,
94
+ success: false,
95
+ errors: [error],
96
+ };
97
+ }
98
+ }
99
+ public async executeDeployment(
100
+ acRequest: AccessRequest,
101
+ codeUID: string,
102
+ deploymentId: string,
103
+ inputs: Record<string, any>,
104
+ config: CodeConfig
105
+ ): Promise<CodeExecutionResult> {
106
+ const result = await this.execute(acRequest, codeUID, inputs, config);
107
+ return result;
108
+ }
109
+
110
+ public async listDeployments(acRequest: AccessRequest, codeUID: string, config: CodeConfig): Promise<CodeDeployment[]> {
111
+ return [];
112
+ }
113
+
114
+ public async getDeployment(acRequest: AccessRequest, codeUID: string, deploymentId: string, config: CodeConfig): Promise<CodeDeployment | null> {
115
+ return null;
116
+ }
117
+
118
+ public async deleteDeployment(acRequest: AccessRequest, codeUID: string, deploymentId: string): Promise<void> {
119
+ return;
120
+ }
121
+
122
+ public async getResourceACL(resourceId: string, candidate: IAccessCandidate): Promise<ACL> {
123
+ const acl = new ACL();
124
+
125
+ //give Read access everytime
126
+ //FIXME: !!!!!! IMPORTANT !!!!!! this implementation have to be changed in order to reflect the security model of AWS Lambda
127
+ acl.addAccess(candidate.role, candidate.id, TAccessLevel.Read);
128
+
129
+ return acl;
130
+ }
131
+ }
@@ -3,9 +3,11 @@
3
3
  import { ConnectorService, ConnectorServiceProvider } from '@sre/Core/ConnectorsService';
4
4
  import { TConnectorService } from '@sre/types/SRE.types';
5
5
  import { AWSLambdaCode } from './connectors/AWSLambdaCode.class';
6
+ import { ECMASandbox } from './connectors/ECMASandbox.class';
6
7
 
7
8
  export class CodeService extends ConnectorServiceProvider {
8
9
  public register() {
9
10
  ConnectorService.register(TConnectorService.Code, 'AWSLambda', AWSLambdaCode);
11
+ ConnectorService.register(TConnectorService.Code, 'ECMASandbox', ECMASandbox);
10
12
  }
11
13
  }
@@ -65,25 +65,36 @@ export class LLMHelper {
65
65
  * 2. Calculating tokens for each image in the prompt based on its dimensions.
66
66
  * 3. Summing up text and image tokens to get the total token count.
67
67
  *
68
+ * IMPORTANT: This returns the base token calculation for rate limiting and quota management.
69
+ * The actual tokens charged by OpenAI may differ significantly:
70
+ * - GPT-4o: Uses base calculation (matches this result)
71
+ * - GPT-4o-mini: Intentionally inflates image tokens by ~33x (e.g., 431 → 14,180 tokens)
72
+ * - GPT-4.1 series: Uses different patch-based calculations with various multipliers
73
+ *
74
+ * For consistent user limits regardless of model choice, use this base calculation.
75
+ * For billing estimates, refer to OpenAI's pricing calculator or API response.
76
+ *
77
+ * @see https://platform.openai.com/docs/guides/images-vision?api-mode=responses#calculating-costs
78
+ *
68
79
  * @example
69
80
  * const prompt = [
70
81
  * { type: 'text', text: 'Describe this image:' },
71
82
  * { type: 'image_url', image_url: { url: 'https://example.com/image.jpg' } }
72
83
  * ];
73
84
  * const tokenCount = await countVisionPromptTokens(prompt);
74
- * console.log(tokenCount); // e.g., 150
85
+ * console.log(tokenCount); // e.g., 150 (base calculation for rate limiting)
75
86
  */
76
87
  public static async countVisionPromptTokens(prompt: any): Promise<number> {
77
88
  let tokens = 0;
78
89
 
79
- const textObj = prompt?.filter((item) => item.type === 'text');
90
+ const textObj = prompt?.filter((item) => ['text', 'input_text'].includes(item.type));
80
91
  const textTokens = encode(textObj?.[0]?.text).length;
81
92
 
82
- const images = prompt?.filter((item) => item.type === 'image_url');
93
+ const images = prompt?.filter((item) => ['image_url', 'input_image'].includes(item.type));
83
94
  let imageTokens = 0;
84
95
 
85
96
  for (const image of images) {
86
- const imageUrl = image?.image_url?.url;
97
+ const imageUrl = image?.image_url?.url || image?.image_url; // image?.image_url?.url for 'chat.completions', image?.image_url for 'responses' interface
87
98
  const { width, height } = await this.getImageDimensions(imageUrl);
88
99
  const tokens = this.countImageTokens(width, height);
89
100
  imageTokens += tokens;
@@ -124,7 +135,7 @@ export class LLMHelper {
124
135
  throw new Error('Please provide a valid image url!');
125
136
  }
126
137
 
127
- const dimensions = imageSize(buffer);
138
+ const dimensions = imageSize(buffer);
128
139
 
129
140
  return {
130
141
  width: dimensions?.width || 0,
@@ -145,40 +156,59 @@ export class LLMHelper {
145
156
  * @returns {number} The number of tokens required to process the image.
146
157
  *
147
158
  * @description
148
- * This method estimates the token count for image processing based on the image dimensions and detail mode.
149
- * It uses a tiling approach to calculate the token count, scaling the image if necessary.
159
+ * This method calculates the token count for image processing based on OpenAI's official documentation:
150
160
  *
151
- * - If detailMode is 'low', it returns a fixed token count of 85.
152
- * - For other modes, it calculates based on the image dimensions:
153
- * - Scales down images larger than 2048 pixels in any dimension.
154
- * - Adjusts the scaled dimension to fit within a 768x1024 aspect ratio.
155
- * - Calculates the number of 512x512 tiles needed to cover the image.
156
- * - Returns the total token count based on the number of tiles.
161
+ * For 'low' detail mode: Returns 85 tokens regardless of image size.
162
+ *
163
+ * For 'high' detail mode (default):
164
+ * 1. Scale image to fit within 2048x2048 square (maintaining aspect ratio)
165
+ * 2. Scale image so shortest side is 768px (if both dimensions > 768px)
166
+ * 3. Calculate number of 512x512 tiles needed
167
+ * 4. Return 85 + (170 * number_of_tiles)
157
168
  *
158
169
  * @example
159
170
  * const tokenCount = countImageTokens(1024, 768);
160
171
  * console.log(tokenCount); // Outputs the calculated token count
161
172
  */
162
173
  public static countImageTokens(width: number, height: number, detailMode: string = 'auto'): number {
163
- if (detailMode === 'low') return 85;
164
-
165
- const maxDimension = Math.max(width, height);
166
- const minDimension = Math.min(width, height);
167
- let scaledMinDimension = minDimension;
168
-
169
- if (maxDimension > 2048) {
170
- scaledMinDimension = (2048 / maxDimension) * minDimension;
174
+ // For low detail mode, always return 85 tokens
175
+ if (detailMode === 'low') {
176
+ return 85;
171
177
  }
172
- scaledMinDimension = Math.floor((768 / 1024) * scaledMinDimension);
173
178
 
174
- let tileSize = 512;
175
- let tiles = Math.ceil(scaledMinDimension / tileSize);
179
+ // Step 1: Scale to fit within 2048x2048 square (maintaining aspect ratio)
180
+ if (width > 2048 || height > 2048) {
181
+ const aspectRatio = width / height;
182
+ if (aspectRatio > 1) {
183
+ width = 2048;
184
+ height = Math.floor(2048 / aspectRatio);
185
+ } else {
186
+ height = 2048;
187
+ width = Math.floor(2048 * aspectRatio);
188
+ }
189
+ }
176
190
 
177
- if (minDimension !== scaledMinDimension) {
178
- tiles *= Math.ceil((scaledMinDimension * (maxDimension / minDimension)) / tileSize);
191
+ // Step 2: Scale such that shortest side is 768px (if both dimensions > 768px)
192
+ if (width > 768 && height > 768) {
193
+ const aspectRatio = width / height;
194
+ if (aspectRatio > 1) {
195
+ // height is shorter, scale to 768px
196
+ height = 768;
197
+ width = Math.floor(768 * aspectRatio);
198
+ } else {
199
+ // width is shorter, scale to 768px
200
+ width = 768;
201
+ height = Math.floor(768 / aspectRatio);
202
+ }
179
203
  }
180
204
 
181
- return tiles * 170 + 85;
205
+ // Step 3: Calculate number of 512x512 tiles needed
206
+ const tilesWidth = Math.ceil(width / 512);
207
+ const tilesHeight = Math.ceil(height / 512);
208
+ const totalTiles = tilesWidth * tilesHeight;
209
+
210
+ // Step 4: Calculate total tokens (85 base + 170 per tile)
211
+ return 85 + 170 * totalTiles;
182
212
  }
183
213
 
184
214
  /**
@@ -261,6 +261,10 @@ export class LLMInference {
261
261
  maxInputContext -= maxInputContext + maxOutputContext - maxModelContext;
262
262
  }
263
263
 
264
+ if (maxInputContext <= 0) {
265
+ console.warn('Max input context is 0, returning empty context window, This usually indicates a wrong model configuration');
266
+ }
267
+
264
268
  const systemMessage = { role: 'system', content: systemPrompt };
265
269
 
266
270
  let smythContextWindow = [];
@@ -3,20 +3,20 @@ import { ConnectorService } from '@sre/Core/ConnectorsService';
3
3
  import { Logger } from '@sre/helpers/Log.helper';
4
4
  import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
5
5
  import { JSONContent } from '@sre/helpers/JsonContent.helper';
6
- import {
7
- TLLMParams,
6
+ import type {
8
7
  TLLMConnectorParams,
9
8
  TLLMMessageBlock,
10
9
  TLLMToolResultMessageBlock,
11
10
  ToolData,
12
11
  APIKeySource,
13
12
  TLLMModel,
14
- TLLMCredentials,
15
- TBedrockSettings,
16
- TVertexAISettings,
17
13
  ILLMRequestFuncParams,
18
14
  TLLMChatResponse,
19
15
  TLLMRequestBody,
16
+ TOpenAIToolsInfo,
17
+ TxAIToolsInfo,
18
+ TLLMPreparedParams,
19
+ TToolsInfo,
20
20
  } from '@sre/types/LLM.types';
21
21
  import EventEmitter from 'events';
22
22
  import { Readable } from 'stream';
@@ -44,13 +44,10 @@ export interface ILLMConnectorRequest {
44
44
 
45
45
  export class LLMStream extends Readable {
46
46
  private dataQueue: any[];
47
- private toolsData: any[];
48
- private hasData: boolean;
49
47
  isReading: boolean;
50
48
  constructor(options?) {
51
49
  super(options);
52
50
  this.dataQueue = [];
53
- this.toolsData = [];
54
51
  this.isReading = true;
55
52
  }
56
53
 
@@ -83,7 +80,6 @@ export abstract class LLMConnector extends Connector {
83
80
 
84
81
  protected abstract request({ acRequest, body, context }: ILLMRequestFuncParams): Promise<TLLMChatResponse>;
85
82
  protected abstract streamRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter>;
86
- protected abstract webSearchRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter>;
87
83
 
88
84
  protected abstract reqBodyAdapter(params: TLLMConnectorParams): Promise<TLLMRequestBody>;
89
85
  protected abstract reportUsage(usage: any, metadata: { modelEntryName: string; keySource: APIKeySource; agentId: string; teamId: string }): any;
@@ -111,6 +107,8 @@ export abstract class LLMConnector extends Connector {
111
107
  request: async (params: TLLMConnectorParams) => {
112
108
  const preparedParams = await this.prepareParams(candidate, params);
113
109
 
110
+ const provider = preparedParams.modelInfo.provider;
111
+
114
112
  const response = await this.request({
115
113
  acRequest: candidate.readRequest,
116
114
  body: preparedParams.body,
@@ -122,6 +120,9 @@ export abstract class LLMConnector extends Connector {
122
120
  hasFiles: preparedParams.files?.length > 0,
123
121
  modelInfo: preparedParams.modelInfo,
124
122
  credentials: preparedParams.credentials,
123
+ toolsInfo: {
124
+ [provider]: preparedParams.toolsInfo[provider],
125
+ } as TToolsInfo,
125
126
  },
126
127
  });
127
128
 
@@ -130,6 +131,8 @@ export abstract class LLMConnector extends Connector {
130
131
  streamRequest: async (params: TLLMConnectorParams) => {
131
132
  const preparedParams = await this.prepareParams(candidate, params);
132
133
 
134
+ const provider = preparedParams.modelInfo.provider?.toLowerCase();
135
+
133
136
  const requestParams = {
134
137
  acRequest: candidate.readRequest,
135
138
  body: preparedParams.body,
@@ -141,21 +144,13 @@ export abstract class LLMConnector extends Connector {
141
144
  hasFiles: preparedParams.files?.length > 0,
142
145
  modelInfo: preparedParams.modelInfo,
143
146
  credentials: preparedParams.credentials,
147
+ toolsInfo: {
148
+ [provider]: preparedParams.toolsInfo[provider],
149
+ } as TToolsInfo,
144
150
  },
145
151
  };
146
152
 
147
- let response;
148
-
149
- if (
150
- preparedParams.capabilities?.search === true &&
151
- preparedParams.useWebSearch === true &&
152
- preparedParams.modelInfo.provider === 'OpenAI'
153
- ) {
154
- // ! webSearchRequest will be removed in next update
155
- response = await this.webSearchRequest(requestParams);
156
- } else {
157
- response = await this.streamRequest(requestParams);
158
- }
153
+ const response = await this.streamRequest(requestParams);
159
154
 
160
155
  return response;
161
156
  },
@@ -244,7 +239,7 @@ export abstract class LLMConnector extends Connector {
244
239
  };
245
240
  }
246
241
  }
247
- public formatToolsConfig({ type = 'function', toolDefinitions, toolChoice = 'auto' }) {
242
+ public formatToolsConfig({ type = 'function', toolDefinitions, toolChoice = 'auto', modelInfo = null }) {
248
243
  throw new Error('This model does not support tools');
249
244
  }
250
245
 
@@ -262,7 +257,7 @@ export abstract class LLMConnector extends Connector {
262
257
  return messages; // if a LLM connector does not implement this method, the messages will not be modified
263
258
  }
264
259
 
265
- private async prepareParams(candidate: AccessCandidate, params: TLLMConnectorParams): Promise<TLLMConnectorParams & { body: any }> {
260
+ private async prepareParams(candidate: AccessCandidate, params: TLLMConnectorParams): Promise<TLLMPreparedParams> {
266
261
  const modelsProvider: ModelsProviderConnector = ConnectorService.getModelsProviderConnector();
267
262
  // Assign file from the original parameters to avoid overwriting the original constructor
268
263
  const files = params?.files;
@@ -271,7 +266,7 @@ export abstract class LLMConnector extends Connector {
271
266
  const clonedParams = JSON.parse(JSON.stringify(params)); // Avoid mutation of the original params
272
267
 
273
268
  // Format the parameters to ensure proper type of values
274
- const _params: TLLMConnectorParams = this.formatParamValues(clonedParams);
269
+ const _params: TLLMPreparedParams = this.formatParamValues(clonedParams);
275
270
 
276
271
  const model = _params.model;
277
272
  const teamId = await this.getTeamId(candidate);
@@ -292,10 +287,6 @@ export abstract class LLMConnector extends Connector {
292
287
  }
293
288
  }
294
289
 
295
- const isStandardLLM = await modelProviderCandidate.isStandardLLM(model);
296
-
297
- const llmProvider = await modelProviderCandidate.getProvider(model);
298
-
299
290
  _params.credentials = await getLLMCredentials(candidate, modelInfo);
300
291
 
301
292
  //_params.model = (await modelProviderCandidate.getModelId(model)) || model;
@@ -324,6 +315,15 @@ export abstract class LLMConnector extends Connector {
324
315
  imageGeneration: features.includes('image-generation'),
325
316
  };
326
317
 
318
+ // We're using an object with providers instead of setting toolsInfo directly based on the provider,
319
+ // so the code stays clean and easy to read in connectors like toolsInfo.openai.webSearch or toolsInfo.xai.search.
320
+ // it helps prevent errors such as mistakenly using toolsInfo.search in the OpenAI connector and similar cases.
321
+ // This also helps enable autocomplete, so when typing toolsInfo.openai, it shows suggestions like webSearch.
322
+ _params.toolsInfo = {
323
+ openai: await this.prepareOpenAIToolsInfo(_params),
324
+ xai: await this.prepareXAIToolsInfo(_params),
325
+ };
326
+
327
327
  // The input adapter transforms the standardized parameters into the specific format required by the target LLM provider
328
328
  _params.agentId = candidate.id;
329
329
  const body = await this.reqBodyAdapter(_params);
@@ -331,6 +331,103 @@ export abstract class LLMConnector extends Connector {
331
331
  return { ..._params, body };
332
332
  }
333
333
 
334
+ private async prepareOpenAIToolsInfo(params: TLLMPreparedParams) {
335
+ const openAIToolsInfo: TOpenAIToolsInfo = {
336
+ webSearch: {
337
+ enabled: params?.useWebSearch && params.capabilities.search === true,
338
+ contextSize: params?.webSearchContextSize || 'medium',
339
+ },
340
+ };
341
+
342
+ if (params?.webSearchCity) {
343
+ openAIToolsInfo.webSearch.city = params?.webSearchCity;
344
+ }
345
+
346
+ if (params?.webSearchCountry) {
347
+ openAIToolsInfo.webSearch.country = params?.webSearchCountry;
348
+ }
349
+
350
+ if (params?.webSearchRegion) {
351
+ openAIToolsInfo.webSearch.region = params?.webSearchRegion;
352
+ }
353
+
354
+ if (params?.webSearchTimezone) {
355
+ openAIToolsInfo.webSearch.timezone = params?.webSearchTimezone;
356
+ }
357
+
358
+ return openAIToolsInfo;
359
+ }
360
+
361
+ private async prepareXAIToolsInfo(params: TLLMPreparedParams) {
362
+ const xaiToolsInfo: TxAIToolsInfo = {
363
+ search: {
364
+ enabled: params?.useSearch === true && params.capabilities.search === true,
365
+ },
366
+ };
367
+
368
+ if (params?.searchMode) {
369
+ xaiToolsInfo.search.mode = params?.searchMode;
370
+ }
371
+
372
+ if (params?.returnCitations) {
373
+ xaiToolsInfo.search.returnCitations = params?.returnCitations;
374
+ }
375
+
376
+ if (params?.maxSearchResults) {
377
+ xaiToolsInfo.search.maxResults = params?.maxSearchResults;
378
+ }
379
+
380
+ if (params?.searchDataSources) {
381
+ xaiToolsInfo.search.dataSources = params?.searchDataSources;
382
+ }
383
+
384
+ if (params?.searchCountry) {
385
+ xaiToolsInfo.search.country = params?.searchCountry;
386
+ }
387
+
388
+ if (params?.excludedWebsites) {
389
+ xaiToolsInfo.search.excludedWebsites = params?.excludedWebsites;
390
+ }
391
+
392
+ if (params?.allowedWebsites) {
393
+ xaiToolsInfo.search.allowedWebsites = params?.allowedWebsites;
394
+ }
395
+
396
+ if (params?.includedXHandles) {
397
+ xaiToolsInfo.search.includedXHandles = params?.includedXHandles;
398
+ }
399
+
400
+ if (params?.excludedXHandles) {
401
+ xaiToolsInfo.search.excludedXHandles = params?.excludedXHandles;
402
+ }
403
+
404
+ if (params?.postFavoriteCount) {
405
+ xaiToolsInfo.search.postFavoriteCount = params?.postFavoriteCount;
406
+ }
407
+
408
+ if (params?.postViewCount) {
409
+ xaiToolsInfo.search.postViewCount = params?.postViewCount;
410
+ }
411
+
412
+ if (params?.rssLinks) {
413
+ xaiToolsInfo.search.rssLinks = params?.rssLinks;
414
+ }
415
+
416
+ if (params?.safeSearch) {
417
+ xaiToolsInfo.search.safeSearch = params?.safeSearch;
418
+ }
419
+
420
+ if (params?.fromDate) {
421
+ xaiToolsInfo.search.fromDate = params?.fromDate;
422
+ }
423
+
424
+ if (params?.toDate) {
425
+ xaiToolsInfo.search.toDate = params?.toDate;
426
+ }
427
+
428
+ return xaiToolsInfo;
429
+ }
430
+
334
431
  // TODO [Forhad]: apply proper typing for _value and return value
335
432
  private formatParamValues(params: Record<string, string | number | string[] | TLLMMessageBlock[]>): any {
336
433
  let _params = {};
@@ -5,7 +5,6 @@ import { JSON_RESPONSE_INSTRUCTION, BUILT_IN_MODEL_PREFIX } from '@sre/constants
5
5
  import { BinaryInput } from '@sre/helpers/BinaryInput.helper';
6
6
  import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
7
7
  import {
8
- TLLMParams,
9
8
  ToolData,
10
9
  TLLMMessageBlock,
11
10
  TLLMToolResultMessageBlock,
@@ -17,6 +16,7 @@ import {
17
16
  BasicCredentials,
18
17
  TAnthropicRequestBody,
19
18
  ILLMRequestContext,
19
+ TLLMPreparedParams,
20
20
  } from '@sre/types/LLM.types';
21
21
 
22
22
  import { LLMHelper } from '@sre/LLMManager/LLM.helper';
@@ -203,11 +203,7 @@ export class AnthropicConnector extends LLMConnector {
203
203
  }
204
204
  }
205
205
 
206
- protected async webSearchRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter> {
207
- throw new Error('Web search requests are not supported by Anthropic');
208
- }
209
-
210
- protected async reqBodyAdapter(params: TLLMParams): Promise<TAnthropicRequestBody> {
206
+ protected async reqBodyAdapter(params: TLLMPreparedParams): Promise<TAnthropicRequestBody> {
211
207
  const body = await this.prepareBody(params);
212
208
 
213
209
  const shouldUseThinking = await this.shouldUseThinkingMode(params);
@@ -224,7 +220,7 @@ export class AnthropicConnector extends LLMConnector {
224
220
 
225
221
  protected reportUsage(
226
222
  usage: Anthropic.Messages.Usage & { cache_creation_input_tokens?: number; cache_read_input_tokens?: number },
227
- metadata: { modelEntryName: string; keySource: APIKeySource; agentId: string; teamId: string },
223
+ metadata: { modelEntryName: string; keySource: APIKeySource; agentId: string; teamId: string }
228
224
  ) {
229
225
  // SmythOS (built-in) models have a prefix, so we need to remove it to get the model name
230
226
  const modelName = metadata.modelEntryName.replace(BUILT_IN_MODEL_PREFIX, '');
@@ -355,7 +351,7 @@ export class AnthropicConnector extends LLMConnector {
355
351
  } else if (Array.isArray(message?.content)) {
356
352
  if (Array.isArray(message.content)) {
357
353
  const toolBlocks = message.content.filter(
358
- (item) => typeof item === 'object' && 'type' in item && (item.type === 'tool_use' || item.type === 'tool_result'),
354
+ (item) => typeof item === 'object' && 'type' in item && (item.type === 'tool_use' || item.type === 'tool_result')
359
355
  );
360
356
 
361
357
  if (toolBlocks?.length > 0) {
@@ -407,7 +403,7 @@ export class AnthropicConnector extends LLMConnector {
407
403
  return _messages;
408
404
  }
409
405
 
410
- private async prepareBody(params: TLLMParams): Promise<Anthropic.MessageCreateParamsNonStreaming> {
406
+ private async prepareBody(params: TLLMPreparedParams): Promise<Anthropic.MessageCreateParamsNonStreaming> {
411
407
  let messages = await this.prepareMessages(params);
412
408
 
413
409
  let body: Anthropic.MessageCreateParamsNonStreaming = {
@@ -444,9 +440,11 @@ export class AnthropicConnector extends LLMConnector {
444
440
  }
445
441
  //#endregion Prepare system message and add JSON response instruction if needed
446
442
 
447
- if (params?.temperature !== undefined) body.temperature = params.temperature;
448
- if (params?.topP !== undefined) body.top_p = params.topP;
449
- if (params?.topK !== undefined) body.top_k = params.topK;
443
+ const isReasoningModel = params?.capabilities?.reasoning;
444
+
445
+ if (params?.temperature !== undefined && !isReasoningModel) body.temperature = params.temperature;
446
+ if (params?.topP !== undefined && !isReasoningModel) body.top_p = params.topP;
447
+ if (params?.topK !== undefined && !isReasoningModel) body.top_k = params.topK;
450
448
  if (params?.stopSequences?.length) body.stop_sequences = params.stopSequences;
451
449
 
452
450
  // #region Tools
@@ -479,7 +477,7 @@ export class AnthropicConnector extends LLMConnector {
479
477
  }): Promise<Anthropic.MessageCreateParamsNonStreaming> {
480
478
  // Remove the assistant message with the prefill text for JSON response, it's not supported with thinking
481
479
  let messages = body.messages.filter(
482
- (message) => message?.role !== TLLMMessageRole.Assistant && message?.content !== PREFILL_TEXT_FOR_JSON_RESPONSE,
480
+ (message) => !(message?.role === TLLMMessageRole.Assistant && message?.content === PREFILL_TEXT_FOR_JSON_RESPONSE)
483
481
  );
484
482
 
485
483
  let budget_tokens = Math.min(maxThinkingTokens, body.max_tokens);
@@ -523,7 +521,7 @@ export class AnthropicConnector extends LLMConnector {
523
521
  return thinkingBody;
524
522
  }
525
523
 
526
- private async prepareMessages(params: TLLMParams) {
524
+ private async prepareMessages(params: TLLMPreparedParams) {
527
525
  const messages = params?.messages || [];
528
526
 
529
527
  const files: BinaryInput[] = params?.files || [];
@@ -556,7 +554,10 @@ export class AnthropicConnector extends LLMConnector {
556
554
  return messages;
557
555
  }
558
556
 
559
- private async prepareSystemPrompt(systemMessage: TLLMMessageBlock, params: TLLMParams): Promise<string | Array<Anthropic.TextBlockParam>> {
557
+ private async prepareSystemPrompt(
558
+ systemMessage: TLLMMessageBlock,
559
+ params: TLLMPreparedParams
560
+ ): Promise<string | Array<Anthropic.TextBlockParam>> {
560
561
  let systemPrompt = systemMessage?.content;
561
562
 
562
563
  if (typeof systemPrompt === 'string') {
@@ -584,7 +585,7 @@ export class AnthropicConnector extends LLMConnector {
584
585
  /**
585
586
  * Determines if thinking mode should be used based on model capabilities and parameters.
586
587
  */
587
- private async shouldUseThinkingMode(params: TLLMParams): Promise<boolean> {
588
+ private async shouldUseThinkingMode(params: TLLMPreparedParams): Promise<boolean> {
588
589
  // Legacy thinking models always use thinking mode
589
590
  if (LEGACY_THINKING_MODELS.includes(params.modelEntryName)) {
590
591
  return true;
@@ -614,7 +615,7 @@ export class AnthropicConnector extends LLMConnector {
614
615
 
615
616
  private async getImageData(
616
617
  files: BinaryInput[],
617
- agentId: string,
618
+ agentId: string
618
619
  ): Promise<
619
620
  {
620
621
  type: string;