@smythos/sre 1.6.14 → 1.7.5
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/CHANGELOG +15 -0
- package/dist/index.js +66 -58
- package/dist/index.js.map +1 -1
- package/dist/types/Components/APIEndpoint.class.d.ts +2 -8
- package/dist/types/Components/Component.class.d.ts +9 -0
- package/dist/types/Components/Triggers/Gmail.trigger.d.ts +0 -17
- package/dist/types/Components/Triggers/JobScheduler.trigger.d.ts +10 -0
- package/dist/types/Components/Triggers/Trigger.class.d.ts +11 -0
- package/dist/types/Components/index.d.ts +6 -0
- package/dist/types/Core/Connector.class.d.ts +1 -0
- package/dist/types/Core/ConnectorsService.d.ts +2 -0
- package/dist/types/Core/HookService.d.ts +1 -1
- package/dist/types/helpers/BinaryInput.helper.d.ts +1 -1
- package/dist/types/helpers/Conversation.helper.d.ts +2 -0
- package/dist/types/helpers/Crypto.helper.d.ts +8 -0
- package/dist/types/helpers/LocalCache.helper.d.ts +18 -0
- package/dist/types/helpers/TemplateString.helper.d.ts +2 -1
- package/dist/types/index.d.ts +13 -0
- package/dist/types/subsystems/AgentManager/Agent.class.d.ts +4 -2
- package/dist/types/subsystems/AgentManager/AgentData.service/AgentDataConnector.d.ts +13 -0
- package/dist/types/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.d.ts +1 -4
- package/dist/types/subsystems/AgentManager/Scheduler.service/Job.class.d.ts +29 -6
- package/dist/types/subsystems/AgentManager/Scheduler.service/SchedulerConnector.d.ts +11 -3
- package/dist/types/subsystems/AgentManager/Scheduler.service/connectors/LocalScheduler.class.d.ts +31 -7
- package/dist/types/subsystems/IO/VectorDB.service/VectorDBConnector.d.ts +4 -4
- package/dist/types/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.d.ts +2 -2
- package/dist/types/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.d.ts +2 -2
- package/dist/types/subsystems/IO/VectorDB.service/connectors/RAMVecrtorDB.class.d.ts +2 -2
- package/dist/types/subsystems/IO/VectorDB.service/embed/BaseEmbedding.d.ts +16 -9
- package/dist/types/subsystems/IO/VectorDB.service/embed/index.d.ts +4 -1
- package/dist/types/subsystems/LLMManager/LLM.inference.d.ts +36 -2
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.d.ts +2 -5
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.d.ts +3 -6
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.d.ts +7 -0
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/xAI.class.d.ts +2 -5
- package/dist/types/types/Agent.types.d.ts +1 -0
- package/dist/types/types/LLM.types.d.ts +56 -36
- package/dist/types/types/SRE.types.d.ts +4 -1
- package/dist/types/types/VectorDB.types.d.ts +6 -3
- package/dist/types/utils/string.utils.d.ts +0 -4
- package/package.json +6 -2
- package/src/Components/APICall/OAuth.helper.ts +30 -35
- package/src/Components/APIEndpoint.class.ts +25 -6
- package/src/Components/Classifier.class.ts +8 -2
- package/src/Components/Component.class.ts +11 -0
- package/src/Components/GenAILLM.class.ts +11 -7
- package/src/Components/LLMAssistant.class.ts +12 -3
- package/src/Components/ScrapflyWebScrape.class.ts +8 -1
- package/src/Components/TavilyWebSearch.class.ts +4 -1
- package/src/Components/Triggers/Gmail.trigger.ts +282 -0
- package/src/Components/Triggers/JobScheduler.trigger.ts +45 -0
- package/src/Components/Triggers/README.md +3 -0
- package/src/Components/Triggers/Trigger.class.ts +101 -0
- package/src/Components/Triggers/WhatsApp.trigger.ts +219 -0
- package/src/Components/index.ts +8 -0
- package/src/Core/AgentProcess.helper.ts +4 -6
- package/src/Core/Connector.class.ts +11 -3
- package/src/Core/ConnectorsService.ts +5 -0
- package/src/Core/ExternalEventsReceiver.ts +317 -0
- package/src/Core/HookService.ts +20 -6
- package/src/Core/SmythRuntime.class.ts +20 -2
- package/src/Core/SystemEvents.ts +17 -0
- package/src/Core/boot.ts +2 -0
- package/src/helpers/BinaryInput.helper.ts +8 -8
- package/src/helpers/Conversation.helper.ts +46 -12
- package/src/helpers/Crypto.helper.ts +28 -0
- package/src/helpers/LocalCache.helper.ts +18 -0
- package/src/helpers/TemplateString.helper.ts +20 -9
- package/src/index.ts +13 -0
- package/src/index.ts.bak +13 -0
- package/src/subsystems/AGENTS.md +594 -0
- package/src/subsystems/AgentManager/Agent.class.ts +73 -21
- package/src/subsystems/AgentManager/AgentData.service/AgentDataConnector.ts +30 -6
- package/src/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.ts +2 -2
- package/src/subsystems/AgentManager/AgentLogger.class.ts +1 -1
- package/src/subsystems/AgentManager/AgentRuntime.class.ts +34 -5
- package/src/subsystems/AgentManager/Scheduler.service/Job.class.ts +414 -0
- package/src/subsystems/AgentManager/Scheduler.service/Schedule.class.ts +200 -0
- package/src/subsystems/AgentManager/Scheduler.service/SchedulerConnector.ts +200 -0
- package/src/subsystems/AgentManager/Scheduler.service/connectors/LocalScheduler.class.ts +767 -0
- package/src/subsystems/AgentManager/Scheduler.service/index.ts +11 -0
- package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +15 -4
- package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +32 -11
- package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +27 -10
- package/src/subsystems/IO/VectorDB.service/connectors/RAMVecrtorDB.class.ts +25 -9
- package/src/subsystems/IO/VectorDB.service/embed/BaseEmbedding.ts +182 -12
- package/src/subsystems/IO/VectorDB.service/embed/GoogleEmbedding.ts +1 -1
- package/src/subsystems/IO/VectorDB.service/embed/OpenAIEmbedding.ts +1 -1
- package/src/subsystems/IO/VectorDB.service/embed/index.ts +12 -2
- package/src/subsystems/LLMManager/LLM.inference.ts +76 -17
- package/src/subsystems/LLMManager/LLM.service/LLMCredentials.helper.ts +61 -2
- package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +3 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +3 -1
- package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +5 -1
- package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +247 -56
- package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +3 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +28 -21
- package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +3 -0
- package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +121 -33
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts +38 -27
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +3 -2
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +117 -20
- package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +3 -0
- package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +3 -8
- package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts +4 -1
- package/src/subsystems/MemoryManager/Cache.service/connectors/RedisCache.class.ts +12 -0
- package/src/subsystems/MemoryManager/LLMContext.ts +3 -8
- package/src/subsystems/MemoryManager/RuntimeContext.ts +10 -9
- package/src/subsystems/Security/Credentials/Credentials.class.ts +1 -0
- package/src/subsystems/Security/Credentials/ManagedOAuth2Credentials.class.ts +106 -0
- package/src/types/Agent.types.ts +1 -0
- package/src/types/LLM.types.ts +68 -40
- package/src/types/SRE.types.ts +3 -0
- package/src/types/VectorDB.types.ts +7 -3
- package/src/utils/string.utils.ts +193 -191
|
@@ -24,6 +24,7 @@ import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.cla
|
|
|
24
24
|
import { LLMConnector } from '../LLMConnector';
|
|
25
25
|
import { SystemEvents } from '@sre/Core/SystemEvents';
|
|
26
26
|
import { Logger } from '@sre/helpers/Log.helper';
|
|
27
|
+
import { hookAsync } from '@sre/Core/HookService';
|
|
27
28
|
|
|
28
29
|
const logger = Logger('VertexAIConnector');
|
|
29
30
|
|
|
@@ -49,6 +50,7 @@ export class VertexAIConnector extends LLMConnector {
|
|
|
49
50
|
});
|
|
50
51
|
}
|
|
51
52
|
|
|
53
|
+
@hookAsync('LLMConnector.request')
|
|
52
54
|
protected async request({ acRequest, body, context }: ILLMRequestFuncParams): Promise<TLLMChatResponse> {
|
|
53
55
|
try {
|
|
54
56
|
logger.debug(`request ${this.name}`, acRequest.candidate);
|
|
@@ -110,6 +112,7 @@ export class VertexAIConnector extends LLMConnector {
|
|
|
110
112
|
}
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
@hookAsync('LLMConnector.streamRequest')
|
|
113
116
|
protected async streamRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter> {
|
|
114
117
|
const emitter = new EventEmitter();
|
|
115
118
|
|
|
@@ -380,51 +383,136 @@ export class VertexAIConnector extends LLMConnector {
|
|
|
380
383
|
}): TLLMToolResultMessageBlock[] {
|
|
381
384
|
const messageBlocks: TLLMToolResultMessageBlock[] = [];
|
|
382
385
|
|
|
386
|
+
const parseFunctionArgs = (args: unknown) => {
|
|
387
|
+
if (typeof args === 'string') {
|
|
388
|
+
try {
|
|
389
|
+
return JSON.parse(args);
|
|
390
|
+
} catch {
|
|
391
|
+
return args;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
return args ?? {};
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
const parseFunctionResponse = (response: unknown): any => {
|
|
398
|
+
if (typeof response === 'string') {
|
|
399
|
+
try {
|
|
400
|
+
const parsed = JSON.parse(response);
|
|
401
|
+
if (typeof parsed === 'string' && parsed !== response) {
|
|
402
|
+
return parseFunctionResponse(parsed);
|
|
403
|
+
}
|
|
404
|
+
return parsed;
|
|
405
|
+
} catch {
|
|
406
|
+
return response;
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
return response ?? {};
|
|
410
|
+
};
|
|
411
|
+
|
|
383
412
|
if (messageBlock) {
|
|
384
|
-
const parts = [];
|
|
413
|
+
const parts: any[] = [];
|
|
385
414
|
|
|
386
|
-
if (
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
415
|
+
if (Array.isArray(messageBlock.parts) && messageBlock.parts.length > 0) {
|
|
416
|
+
for (const part of messageBlock.parts) {
|
|
417
|
+
if (!part) continue;
|
|
418
|
+
|
|
419
|
+
if (typeof part.text === 'string' && part.text.trim()) {
|
|
420
|
+
parts.push({ text: part.text.trim() });
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (part.functionCall) {
|
|
425
|
+
parts.push({
|
|
426
|
+
functionCall: {
|
|
427
|
+
name: part.functionCall.name,
|
|
428
|
+
args: parseFunctionArgs(part.functionCall.args),
|
|
429
|
+
},
|
|
430
|
+
});
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (part.functionResponse) {
|
|
435
|
+
parts.push({
|
|
436
|
+
functionResponse: {
|
|
437
|
+
name: part.functionResponse.name,
|
|
438
|
+
response: parseFunctionResponse(part.functionResponse.response),
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if ((part as any).inlineData) {
|
|
445
|
+
parts.push({ inlineData: (part as any).inlineData });
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
} else {
|
|
449
|
+
if (typeof messageBlock.content === 'string' && messageBlock.content.trim()) {
|
|
450
|
+
parts.push({ text: messageBlock.content.trim() });
|
|
451
|
+
} else if (Array.isArray(messageBlock.content) && messageBlock.content.length > 0) {
|
|
452
|
+
parts.push(...messageBlock.content);
|
|
453
|
+
}
|
|
390
454
|
}
|
|
391
455
|
|
|
392
|
-
if (messageBlock.tool_calls) {
|
|
393
|
-
const functionCalls = messageBlock.tool_calls
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
: toolCall
|
|
400
|
-
|
|
401
|
-
|
|
456
|
+
if (Array.isArray(messageBlock.tool_calls) && messageBlock.tool_calls.length > 0) {
|
|
457
|
+
const functionCalls = messageBlock.tool_calls
|
|
458
|
+
.map((toolCall: any) => {
|
|
459
|
+
if (!toolCall?.function?.name) return undefined;
|
|
460
|
+
return {
|
|
461
|
+
functionCall: {
|
|
462
|
+
name: toolCall.function.name,
|
|
463
|
+
args: parseFunctionArgs(toolCall.function.arguments),
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
})
|
|
467
|
+
.filter(Boolean);
|
|
468
|
+
|
|
402
469
|
parts.push(...functionCalls);
|
|
403
470
|
}
|
|
404
471
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
472
|
+
const hasFunctionCall = parts.some((part) => part.functionCall);
|
|
473
|
+
if (!hasFunctionCall && toolsData.length > 0) {
|
|
474
|
+
toolsData.forEach((toolCall) => {
|
|
475
|
+
parts.push({
|
|
476
|
+
functionCall: {
|
|
477
|
+
name: toolCall.name,
|
|
478
|
+
args: parseFunctionArgs(toolCall.arguments),
|
|
479
|
+
},
|
|
480
|
+
});
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
if (parts.length > 0) {
|
|
485
|
+
let role = messageBlock.role;
|
|
486
|
+
if (role === TLLMMessageRole.Assistant) {
|
|
487
|
+
role = TLLMMessageRole.Model;
|
|
488
|
+
} else if (role === TLLMMessageRole.Tool) {
|
|
489
|
+
role = TLLMMessageRole.Function;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
messageBlocks.push({
|
|
493
|
+
role,
|
|
494
|
+
parts,
|
|
495
|
+
});
|
|
496
|
+
}
|
|
409
497
|
}
|
|
410
498
|
|
|
411
499
|
// Transform tool results
|
|
412
|
-
const
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
{
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
response: {
|
|
419
|
-
name: toolData.name,
|
|
420
|
-
content: toolData.result,
|
|
421
|
-
},
|
|
422
|
-
},
|
|
500
|
+
const functionResponseParts = toolsData
|
|
501
|
+
.filter((toolData) => toolData.result !== undefined)
|
|
502
|
+
.map((toolData) => ({
|
|
503
|
+
functionResponse: {
|
|
504
|
+
name: toolData.name,
|
|
505
|
+
response: parseFunctionResponse(toolData.result),
|
|
423
506
|
},
|
|
424
|
-
|
|
425
|
-
|
|
507
|
+
}));
|
|
508
|
+
|
|
509
|
+
if (functionResponseParts.length > 0) {
|
|
510
|
+
messageBlocks.push({
|
|
511
|
+
role: TLLMMessageRole.Function,
|
|
512
|
+
parts: functionResponseParts,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
426
515
|
|
|
427
|
-
messageBlocks.push(...toolResults);
|
|
428
516
|
return messageBlocks;
|
|
429
517
|
}
|
|
430
518
|
}
|
|
@@ -28,6 +28,7 @@ import { Logger } from '@sre/helpers/Log.helper';
|
|
|
28
28
|
import { LLMConnector } from '../../LLMConnector';
|
|
29
29
|
import { OpenAIApiInterface, OpenAIApiInterfaceFactory } from './apiInterfaces';
|
|
30
30
|
import { HandlerDependencies } from './types';
|
|
31
|
+
import { hookAsync } from '@sre/Core/HookService';
|
|
31
32
|
|
|
32
33
|
const logger = Logger('OpenAIConnector');
|
|
33
34
|
|
|
@@ -69,31 +70,38 @@ export class OpenAIConnector extends LLMConnector {
|
|
|
69
70
|
return responseInterface;
|
|
70
71
|
}
|
|
71
72
|
|
|
72
|
-
protected async getClient(
|
|
73
|
-
const apiKey = (
|
|
74
|
-
const baseURL =
|
|
73
|
+
protected async getClient(context: ILLMRequestContext): Promise<OpenAI> {
|
|
74
|
+
const apiKey = (context.credentials as BasicCredentials)?.apiKey || '';
|
|
75
|
+
const baseURL = context?.modelInfo?.baseURL;
|
|
75
76
|
|
|
76
|
-
|
|
77
|
+
try {
|
|
78
|
+
const openai = new OpenAI({ baseURL, apiKey });
|
|
77
79
|
|
|
78
|
-
|
|
80
|
+
return openai;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
console.error('Error: on OpenAI client initialization', error);
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
79
85
|
}
|
|
80
86
|
|
|
87
|
+
@hookAsync('LLMConnector.request')
|
|
81
88
|
protected async request({ acRequest, body, context }: ILLMRequestFuncParams): Promise<TLLMChatResponse> {
|
|
82
89
|
try {
|
|
83
90
|
logger.debug(`request ${this.name}`, acRequest.candidate);
|
|
84
91
|
const _body = body as OpenAI.ChatCompletionCreateParams;
|
|
85
92
|
|
|
86
|
-
// #region Validate token limit
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
93
|
+
// #region Validate the token limit only if it's a legacy model.
|
|
94
|
+
if (context?.modelEntryName?.startsWith('legacy/')) {
|
|
95
|
+
const messages = _body?.messages || [];
|
|
96
|
+
const promptTokens = await this.computePromptTokens(messages, context);
|
|
97
|
+
|
|
98
|
+
await this.validateTokenLimit({
|
|
99
|
+
acRequest,
|
|
100
|
+
promptTokens,
|
|
101
|
+
context,
|
|
102
|
+
maxTokens: _body.max_completion_tokens,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
97
105
|
// #endregion Validate token limit
|
|
98
106
|
|
|
99
107
|
const responseInterface = this.getInterfaceType(context);
|
|
@@ -143,20 +151,23 @@ export class OpenAIConnector extends LLMConnector {
|
|
|
143
151
|
}
|
|
144
152
|
}
|
|
145
153
|
|
|
154
|
+
@hookAsync('LLMConnector.streamRequest')
|
|
146
155
|
protected async streamRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter> {
|
|
147
156
|
try {
|
|
148
157
|
logger.debug(`streamRequest ${this.name}`, acRequest.candidate);
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
|
|
159
|
+
// #region Validate the token limit only if it's a legacy model.
|
|
160
|
+
if (context?.modelEntryName?.startsWith('legacy/')) {
|
|
161
|
+
const messages = body?.messages || body?.input || [];
|
|
162
|
+
const promptTokens = await this.computePromptTokens(messages, context);
|
|
163
|
+
|
|
164
|
+
await this.validateTokenLimit({
|
|
165
|
+
acRequest,
|
|
166
|
+
promptTokens,
|
|
167
|
+
context,
|
|
168
|
+
maxTokens: body.max_completion_tokens,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
160
171
|
// #endregion Validate token limit
|
|
161
172
|
|
|
162
173
|
const responseInterface = this.getInterfaceType(context);
|
|
@@ -389,9 +389,10 @@ export class ChatCompletionsApiInterface extends OpenAIApiInterface {
|
|
|
389
389
|
* Upload files to storage
|
|
390
390
|
*/
|
|
391
391
|
private async uploadFiles(files: BinaryInput[], agentId: string): Promise<BinaryInput[]> {
|
|
392
|
+
const candidate = AccessCandidate.agent(agentId);
|
|
392
393
|
const promises = files.map((file) => {
|
|
393
|
-
const binaryInput = BinaryInput.from(file);
|
|
394
|
-
return binaryInput.upload(
|
|
394
|
+
const binaryInput = BinaryInput.from(file, null, null, candidate);
|
|
395
|
+
return binaryInput.upload(candidate).then(() => binaryInput);
|
|
395
396
|
});
|
|
396
397
|
|
|
397
398
|
return Promise.all(promises);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import EventEmitter from 'events';
|
|
2
2
|
import OpenAI from 'openai';
|
|
3
3
|
import type { Stream } from 'openai/streaming';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import fs from 'fs';
|
|
4
7
|
|
|
5
8
|
import { BinaryInput } from '@sre/helpers/BinaryInput.helper';
|
|
6
9
|
import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
|
|
@@ -10,6 +13,7 @@ import { HandlerDependencies, TToolType } from '../types';
|
|
|
10
13
|
import { SUPPORTED_MIME_TYPES_MAP } from '@sre/constants';
|
|
11
14
|
import { SEARCH_TOOL_COSTS } from './constants';
|
|
12
15
|
import { isValidOpenAIReasoningEffort } from './utils';
|
|
16
|
+
import { uid } from '@sre/utils';
|
|
13
17
|
|
|
14
18
|
// File size limits in bytes
|
|
15
19
|
const MAX_IMAGE_SIZE = 20 * 1024 * 1024; // 20MB
|
|
@@ -30,6 +34,7 @@ const EVENT_TYPES = {
|
|
|
30
34
|
FUNCTION_CALL_ARGUMENTS_DELTA: 'response.function_call_arguments.delta',
|
|
31
35
|
FUNCTION_CALL_ARGUMENTS_DONE: 'response.function_call_arguments.done',
|
|
32
36
|
OUTPUT_ITEM_DONE: 'response.output_item.done',
|
|
37
|
+
INCOMPLETE: 'response.incomplete',
|
|
33
38
|
} as const;
|
|
34
39
|
|
|
35
40
|
// Type definitions for web search events (augmenting SDK types locally)
|
|
@@ -176,6 +181,14 @@ export class ResponsesApiInterface extends OpenAIApiInterface {
|
|
|
176
181
|
break;
|
|
177
182
|
}
|
|
178
183
|
|
|
184
|
+
case EVENT_TYPES.INCOMPLETE:
|
|
185
|
+
finishReason = 'incomplete';
|
|
186
|
+
const responseData = (part as any)?.response;
|
|
187
|
+
if (responseData?.usage) {
|
|
188
|
+
usageData.push(responseData.usage);
|
|
189
|
+
}
|
|
190
|
+
break;
|
|
191
|
+
|
|
179
192
|
default: {
|
|
180
193
|
const eventType = String(part.type);
|
|
181
194
|
// Handle legacy started event if ever emitted
|
|
@@ -257,12 +270,12 @@ export class ResponsesApiInterface extends OpenAIApiInterface {
|
|
|
257
270
|
|
|
258
271
|
// Emit interrupted event if finishReason is not 'stop'
|
|
259
272
|
if (finishReason !== 'stop') {
|
|
260
|
-
emitter.emit(
|
|
273
|
+
emitter.emit(TLLMEvent.Interrupted, finishReason);
|
|
261
274
|
}
|
|
262
275
|
|
|
263
276
|
// Emit end event with setImmediate to ensure proper event ordering
|
|
264
277
|
setImmediate(() => {
|
|
265
|
-
emitter.emit(
|
|
278
|
+
emitter.emit(TLLMEvent.End, toolsData, reportedUsage, finishReason);
|
|
266
279
|
});
|
|
267
280
|
}
|
|
268
281
|
|
|
@@ -790,8 +803,62 @@ export class ResponsesApiInterface extends OpenAIApiInterface {
|
|
|
790
803
|
return Promise.all(promises);
|
|
791
804
|
}
|
|
792
805
|
|
|
806
|
+
/**
|
|
807
|
+
* Upload file to OpenAI Files API
|
|
808
|
+
* Similar to GoogleAI's uploadFile implementation
|
|
809
|
+
*/
|
|
810
|
+
private async uploadFile({
|
|
811
|
+
file,
|
|
812
|
+
agentId,
|
|
813
|
+
purpose = 'user_data',
|
|
814
|
+
}: {
|
|
815
|
+
file: BinaryInput;
|
|
816
|
+
agentId: string;
|
|
817
|
+
purpose?: 'user_data' | 'assistants' | 'batch' | 'fine-tune' | 'vision';
|
|
818
|
+
}): Promise<{ fileId: string; filename: string }> {
|
|
819
|
+
try {
|
|
820
|
+
if (!file?.mimetype) {
|
|
821
|
+
throw new Error('Missing required parameters to upload file to OpenAI!');
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const tempDir = os.tmpdir();
|
|
825
|
+
const fileName = await file.getName();
|
|
826
|
+
const tempFilePath = path.join(tempDir, `${uid()}_${fileName}`);
|
|
827
|
+
|
|
828
|
+
// Write file to temporary location
|
|
829
|
+
const bufferData = await file.readData(AccessCandidate.agent(agentId));
|
|
830
|
+
await fs.promises.writeFile(tempFilePath, new Uint8Array(bufferData));
|
|
831
|
+
|
|
832
|
+
const openai = await this.deps.getClient(this.context);
|
|
833
|
+
|
|
834
|
+
// Upload file to OpenAI Files API
|
|
835
|
+
const uploadResponse = await openai.files.create({
|
|
836
|
+
file: fs.createReadStream(tempFilePath),
|
|
837
|
+
purpose: purpose,
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
const fileId = uploadResponse.id;
|
|
841
|
+
if (!fileId) {
|
|
842
|
+
throw new Error('File upload did not return a file ID.');
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Clean up temporary file
|
|
846
|
+
await fs.promises.unlink(tempFilePath).catch(() => {
|
|
847
|
+
// Ignore cleanup errors
|
|
848
|
+
});
|
|
849
|
+
|
|
850
|
+
return {
|
|
851
|
+
fileId,
|
|
852
|
+
filename: fileName,
|
|
853
|
+
};
|
|
854
|
+
} catch (error: any) {
|
|
855
|
+
throw new Error(`Error uploading file to OpenAI: ${error.message}`);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
793
859
|
/**
|
|
794
860
|
* Process image files with Responses API specific formatting
|
|
861
|
+
* Uses OpenAI Files API for uploading images
|
|
795
862
|
*/
|
|
796
863
|
private async processImageData(files: BinaryInput[], agentId: string): Promise<any[]> {
|
|
797
864
|
if (files.length === 0) return [];
|
|
@@ -800,14 +867,30 @@ export class ResponsesApiInterface extends OpenAIApiInterface {
|
|
|
800
867
|
for (const file of files) {
|
|
801
868
|
await this.validateFileSize(file, MAX_IMAGE_SIZE, 'Image');
|
|
802
869
|
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
870
|
+
try {
|
|
871
|
+
// Upload file to OpenAI Files API with 'vision' purpose
|
|
872
|
+
const { fileId } = await this.uploadFile({
|
|
873
|
+
file,
|
|
874
|
+
agentId,
|
|
875
|
+
purpose: 'vision',
|
|
876
|
+
});
|
|
806
877
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
878
|
+
imageData.push({
|
|
879
|
+
type: 'input_image',
|
|
880
|
+
file_id: fileId,
|
|
881
|
+
});
|
|
882
|
+
} catch (error) {
|
|
883
|
+
// If Files API upload fails, fall back to base64 inline data
|
|
884
|
+
console.warn('Failed to upload image via Files API, falling back to base64:', error);
|
|
885
|
+
const bufferData = await file.readData(AccessCandidate.agent(agentId));
|
|
886
|
+
const base64Data = bufferData.toString('base64');
|
|
887
|
+
const url = `data:${file.mimetype};base64,${base64Data}`;
|
|
888
|
+
|
|
889
|
+
imageData.push({
|
|
890
|
+
type: 'input_image',
|
|
891
|
+
image_url: url,
|
|
892
|
+
});
|
|
893
|
+
}
|
|
811
894
|
}
|
|
812
895
|
|
|
813
896
|
return imageData;
|
|
@@ -815,6 +898,7 @@ export class ResponsesApiInterface extends OpenAIApiInterface {
|
|
|
815
898
|
|
|
816
899
|
/**
|
|
817
900
|
* Process document files with Responses API specific formatting
|
|
901
|
+
* Uses OpenAI Files API for uploading documents
|
|
818
902
|
*/
|
|
819
903
|
private async processDocumentData(files: BinaryInput[], agentId: string): Promise<any[]> {
|
|
820
904
|
if (files.length === 0) return [];
|
|
@@ -823,18 +907,31 @@ export class ResponsesApiInterface extends OpenAIApiInterface {
|
|
|
823
907
|
for (const file of files) {
|
|
824
908
|
await this.validateFileSize(file, MAX_DOCUMENT_SIZE, 'Document');
|
|
825
909
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
910
|
+
try {
|
|
911
|
+
// Upload file to OpenAI Files API with 'user_data' purpose
|
|
912
|
+
const { fileId, filename } = await this.uploadFile({
|
|
913
|
+
file,
|
|
914
|
+
agentId,
|
|
915
|
+
purpose: 'user_data',
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
documentData.push({
|
|
919
|
+
type: 'input_file',
|
|
920
|
+
file_id: fileId,
|
|
921
|
+
});
|
|
922
|
+
} catch (error) {
|
|
923
|
+
// If Files API upload fails, fall back to base64 inline data
|
|
924
|
+
console.warn('Failed to upload document via Files API, falling back to base64:', error);
|
|
925
|
+
const bufferData = await file.readData(AccessCandidate.agent(agentId));
|
|
926
|
+
const base64Data = bufferData.toString('base64');
|
|
927
|
+
const filename = await file.getName();
|
|
928
|
+
|
|
929
|
+
documentData.push({
|
|
930
|
+
type: 'input_file',
|
|
835
931
|
filename,
|
|
836
|
-
|
|
837
|
-
|
|
932
|
+
file_data: `data:${file.mimetype};base64,${base64Data}`,
|
|
933
|
+
});
|
|
934
|
+
}
|
|
838
935
|
}
|
|
839
936
|
|
|
840
937
|
return documentData;
|
|
@@ -20,6 +20,7 @@ import { LLMHelper } from '@sre/LLMManager/LLM.helper';
|
|
|
20
20
|
import { LLMConnector } from '../LLMConnector';
|
|
21
21
|
import { SystemEvents } from '@sre/Core/SystemEvents';
|
|
22
22
|
import { Logger } from '@sre/helpers/Log.helper';
|
|
23
|
+
import { hookAsync } from '@sre/Core/HookService';
|
|
23
24
|
|
|
24
25
|
const logger = Logger('xAIConnector');
|
|
25
26
|
|
|
@@ -97,6 +98,7 @@ export class xAIConnector extends LLMConnector {
|
|
|
97
98
|
});
|
|
98
99
|
}
|
|
99
100
|
|
|
101
|
+
@hookAsync('LLMConnector.request')
|
|
100
102
|
protected async request({ acRequest, body, context }: ILLMRequestFuncParams): Promise<TLLMChatResponse> {
|
|
101
103
|
try {
|
|
102
104
|
logger.debug(`request ${this.name}`, acRequest.candidate);
|
|
@@ -153,6 +155,7 @@ export class xAIConnector extends LLMConnector {
|
|
|
153
155
|
}
|
|
154
156
|
}
|
|
155
157
|
|
|
158
|
+
@hookAsync('LLMConnector.streamRequest')
|
|
156
159
|
protected async streamRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter> {
|
|
157
160
|
const emitter = new EventEmitter();
|
|
158
161
|
|
|
@@ -193,11 +193,11 @@ export abstract class ModelsProviderConnector extends SecureConnector {
|
|
|
193
193
|
modelInfo = models?.[model as string];
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
// TODO: We will clean up `keyOptions` in the future but keep it for legacy users.
|
|
196
197
|
const aliasKeyOptions = aliasModelInfo && hasAPIKey ? aliasModelInfo?.keyOptions : null;
|
|
197
|
-
|
|
198
198
|
const modelKeyOptions = modelInfo?.keyOptions || aliasKeyOptions;
|
|
199
199
|
|
|
200
|
-
return { ...
|
|
200
|
+
return { ...modelInfo, ...aliasModelInfo, ...aliasKeyOptions, ...modelKeyOptions, modelId };
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
protected async getModelId(acRequest: AccessRequest, models: TLLMModelsList, model: string | TLLMModel | TCustomLLMModel): Promise<string> {
|
|
@@ -394,12 +394,7 @@ export abstract class ModelsProviderConnector extends SecureConnector {
|
|
|
394
394
|
baseURL: entry.baseURL,
|
|
395
395
|
fallbackLLM: entry.fallbackLLM,
|
|
396
396
|
isUserCustomLLM: true,
|
|
397
|
-
|
|
398
|
-
// TODO: Credentials will usually look like { apiKey: 'api-key-goes-here' }.
|
|
399
|
-
// However, for fallback models we also need to handle ['vault', 'internal']
|
|
400
|
-
// using the same credentials format, since fallback models can be either
|
|
401
|
-
// personal or built-in.
|
|
402
|
-
credentials: entry?.credentials || ['vault', 'internal'],
|
|
397
|
+
credentials: entry?.credentials || {},
|
|
403
398
|
};
|
|
404
399
|
}
|
|
405
400
|
|
package/src/subsystems/LLMManager/ModelsProvider.service/connectors/JSONModelsProvider.class.ts
CHANGED
|
@@ -170,11 +170,13 @@ export class JSONModelsProvider extends ModelsProviderConnector {
|
|
|
170
170
|
}
|
|
171
171
|
} else if (typeof modelData === 'object' && !Array.isArray(modelData)) {
|
|
172
172
|
// Object of models case
|
|
173
|
+
let models = '';
|
|
173
174
|
for (const [modelId, model] of Object.entries(modelData)) {
|
|
174
175
|
try {
|
|
175
176
|
if (this.isValidSingleModel(model)) {
|
|
176
177
|
validModels[modelId] = model as TLLMModel;
|
|
177
|
-
console.debug(`Loaded model: ${modelId}`);
|
|
178
|
+
//console.debug(`Loaded model: ${modelId}`);
|
|
179
|
+
models += `${modelId} `;
|
|
178
180
|
} else {
|
|
179
181
|
console.warn(`Invalid model format for model "${modelId}"`);
|
|
180
182
|
}
|
|
@@ -183,6 +185,7 @@ export class JSONModelsProvider extends ModelsProviderConnector {
|
|
|
183
185
|
// Continue processing other models instead of failing the whole file
|
|
184
186
|
}
|
|
185
187
|
}
|
|
188
|
+
console.debug(`Loaded models: ${models}`);
|
|
186
189
|
} else {
|
|
187
190
|
console.warn(`Invalid format (not a model or object of models)`);
|
|
188
191
|
}
|
|
@@ -111,6 +111,18 @@ export class RedisCache extends CacheConnector {
|
|
|
111
111
|
|
|
112
112
|
@SecureConnector.AccessControl
|
|
113
113
|
public async setMetadata(acRequest: AccessRequest, key: string, metadata: CacheMetadata): Promise<void> {
|
|
114
|
+
if (metadata.acl) {
|
|
115
|
+
//preserve the ownership of the metadata
|
|
116
|
+
const newACL = ACL.from(metadata.acl).addAccess(acRequest.candidate.role, acRequest.candidate.id, TAccessLevel.Owner).ACL;
|
|
117
|
+
metadata.acl = newACL;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
//no ACL present ==> preserve the existing ACL
|
|
121
|
+
if (!metadata.acl) {
|
|
122
|
+
const curACL = await this.getACL(acRequest, key);
|
|
123
|
+
metadata.acl = curACL;
|
|
124
|
+
}
|
|
125
|
+
|
|
114
126
|
await this.setMetadataWithTTL(acRequest, key, metadata);
|
|
115
127
|
}
|
|
116
128
|
private async setMetadataWithTTL(acRequest: AccessRequest, key: string, metadata: CacheMetadata, ttl?: number): Promise<void> {
|
|
@@ -25,7 +25,7 @@ export class LLMContext {
|
|
|
25
25
|
public contextLength: number;
|
|
26
26
|
|
|
27
27
|
private _messages: any[] = [];
|
|
28
|
-
public get messages() {
|
|
28
|
+
public get messages() {
|
|
29
29
|
return this._messages;
|
|
30
30
|
}
|
|
31
31
|
|
|
@@ -36,16 +36,11 @@ export class LLMContext {
|
|
|
36
36
|
*
|
|
37
37
|
* @param source a messages[] object, or smyth file system uri (smythfs://...)
|
|
38
38
|
*/
|
|
39
|
-
constructor(
|
|
40
|
-
private llmInference,
|
|
41
|
-
_systemPrompt: string = '',
|
|
42
|
-
llmContextStore?: ILLMContextStore,
|
|
43
|
-
) {
|
|
39
|
+
constructor(private llmInference, _systemPrompt: string = '', llmContextStore?: ILLMContextStore) {
|
|
44
40
|
this._llmCache = new LLMCache(AccessCandidate.team(this.llmInference.teamId));
|
|
45
41
|
//this._systemPrompt = _systemPrompt;
|
|
46
42
|
this.systemPrompt = _systemPrompt;
|
|
47
43
|
|
|
48
|
-
|
|
49
44
|
if (llmContextStore) {
|
|
50
45
|
this._llmContextStore = llmContextStore;
|
|
51
46
|
this._llmContextStore.load().then((messages) => {
|
|
@@ -57,7 +52,7 @@ export class LLMContext {
|
|
|
57
52
|
|
|
58
53
|
private push(...message: any[]) {
|
|
59
54
|
this._messages.push(...message);
|
|
60
|
-
|
|
55
|
+
|
|
61
56
|
if (this._llmContextStore) {
|
|
62
57
|
this._llmContextStore.save(this._messages);
|
|
63
58
|
}
|