@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.
- package/dist/index.js +65 -45
- package/dist/index.js.map +1 -1
- package/dist/types/Components/ECMASandbox.class.d.ts +14 -0
- package/dist/types/Components/MemoryDeleteKeyVal.class.d.ts +19 -0
- package/dist/types/Components/MemoryReadKeyVal.class.d.ts +17 -0
- package/dist/types/Components/MemoryWriteKeyVal.class.d.ts +17 -0
- package/dist/types/Components/MemoryWriteObject.class.d.ts +19 -0
- package/dist/types/Components/index.d.ts +10 -0
- package/dist/types/Core/ConnectorsService.d.ts +2 -1
- package/dist/types/helpers/ECMASandbox.helper.d.ts +3 -0
- package/dist/types/helpers/Log.helper.d.ts +1 -1
- package/dist/types/index.d.ts +8 -1
- package/dist/types/subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class.d.ts +19 -0
- package/dist/types/subsystems/LLMManager/LLM.helper.d.ts +21 -10
- package/dist/types/subsystems/LLMManager/LLM.service/LLMConnector.d.ts +5 -5
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.d.ts +2 -3
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.d.ts +2 -3
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Echo.class.d.ts +2 -3
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.d.ts +2 -3
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Groq.class.d.ts +2 -3
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.d.ts +3 -4
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.d.ts +19 -14
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.d.ts +95 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.d.ts +87 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterface.d.ts +85 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterfaceFactory.d.ts +49 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.d.ts +146 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.d.ts +10 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/index.d.ts +4 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/types.d.ts +38 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/xAI.class.d.ts +1 -2
- package/dist/types/subsystems/Security/Vault.service/connectors/JSONFileVault.class.d.ts +5 -0
- package/dist/types/types/LLM.types.d.ts +82 -37
- package/dist/types/utils/data.utils.d.ts +2 -1
- package/package.json +4 -3
- package/src/Components/APICall/APICall.class.ts +2 -1
- package/src/Components/Component.class.ts +1 -1
- package/src/Components/ECMASandbox.class.ts +71 -0
- package/src/Components/GenAILLM.class.ts +1 -1
- package/src/Components/MemoryDeleteKeyVal.class.ts +70 -0
- package/src/Components/MemoryReadKeyVal.class.ts +66 -0
- package/src/Components/MemoryWriteKeyVal.class.ts +62 -0
- package/src/Components/MemoryWriteObject.class.ts +97 -0
- package/src/Components/index.ts +10 -0
- package/src/Core/ConnectorsService.ts +3 -3
- package/src/helpers/Conversation.helper.ts +11 -3
- package/src/helpers/ECMASandbox.helper.ts +54 -0
- package/src/helpers/Log.helper.ts +57 -17
- package/src/index.ts +192 -185
- package/src/index.ts.bak +192 -185
- package/src/subsystems/AgentManager/Agent.class.ts +11 -6
- package/src/subsystems/AgentManager/AgentRuntime.class.ts +13 -13
- package/src/subsystems/ComputeManager/Code.service/connectors/ECMASandbox.class.ts +131 -0
- package/src/subsystems/ComputeManager/Code.service/index.ts +2 -0
- package/src/subsystems/LLMManager/LLM.helper.ts +57 -27
- package/src/subsystems/LLMManager/LLM.inference.ts +4 -0
- package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +125 -28
- package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +18 -17
- package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +2 -7
- package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +2 -6
- package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +26 -17
- package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +2 -7
- package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +2 -7
- package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +314 -84
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts +455 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +528 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterface.ts +100 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterfaceFactory.ts +81 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +853 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/constants.ts +37 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/index.ts +4 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/types.ts +37 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +0 -5
- package/src/subsystems/LLMManager/LLM.service/index.ts +1 -1
- package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +2 -2
- package/src/subsystems/LLMManager/models.ts +1 -1
- package/src/subsystems/MemoryManager/Cache.service/connectors/RedisCache.class.ts +18 -0
- package/src/subsystems/MemoryManager/RuntimeContext.ts +33 -16
- package/src/subsystems/Security/Vault.service/connectors/JSONFileVault.class.ts +68 -42
- package/src/types/LLM.types.ts +91 -43
- package/src/utils/data.utils.ts +3 -2
- 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
|
|
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
|
|
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 =
|
|
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
|
|
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
|
-
*
|
|
152
|
-
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
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
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
175
|
-
|
|
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
|
-
|
|
178
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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<
|
|
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:
|
|
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
|
|
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:
|
|
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
|
-
|
|
448
|
-
|
|
449
|
-
if (params?.
|
|
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
|
|
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:
|
|
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(
|
|
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:
|
|
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;
|