@smythos/sre 1.6.14 → 1.7.1

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 (81) hide show
  1. package/CHANGELOG +15 -0
  2. package/dist/index.js +52 -46
  3. package/dist/index.js.map +1 -1
  4. package/dist/types/Components/APIEndpoint.class.d.ts +2 -8
  5. package/dist/types/Components/Component.class.d.ts +9 -0
  6. package/dist/types/Components/Triggers/Gmail.trigger.d.ts +0 -17
  7. package/dist/types/Components/Triggers/JobScheduler.trigger.d.ts +10 -0
  8. package/dist/types/Components/Triggers/Trigger.class.d.ts +11 -0
  9. package/dist/types/Components/index.d.ts +6 -0
  10. package/dist/types/Core/Connector.class.d.ts +1 -0
  11. package/dist/types/Core/ConnectorsService.d.ts +2 -0
  12. package/dist/types/Core/HookService.d.ts +1 -1
  13. package/dist/types/helpers/Conversation.helper.d.ts +2 -0
  14. package/dist/types/helpers/Crypto.helper.d.ts +8 -0
  15. package/dist/types/index.d.ts +13 -0
  16. package/dist/types/subsystems/AgentManager/Agent.class.d.ts +4 -2
  17. package/dist/types/subsystems/AgentManager/AgentData.service/AgentDataConnector.d.ts +13 -0
  18. package/dist/types/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.d.ts +1 -4
  19. package/dist/types/subsystems/AgentManager/Scheduler.service/Job.class.d.ts +29 -6
  20. package/dist/types/subsystems/AgentManager/Scheduler.service/SchedulerConnector.d.ts +11 -3
  21. package/dist/types/subsystems/AgentManager/Scheduler.service/connectors/LocalScheduler.class.d.ts +31 -7
  22. package/dist/types/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.d.ts +2 -5
  23. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.d.ts +3 -6
  24. package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.d.ts +7 -0
  25. package/dist/types/subsystems/LLMManager/LLM.service/connectors/xAI.class.d.ts +2 -5
  26. package/dist/types/types/Agent.types.d.ts +1 -0
  27. package/dist/types/types/LLM.types.d.ts +2 -5
  28. package/dist/types/types/SRE.types.d.ts +4 -1
  29. package/package.json +6 -2
  30. package/src/Components/APICall/OAuth.helper.ts +30 -35
  31. package/src/Components/APIEndpoint.class.ts +25 -6
  32. package/src/Components/Component.class.ts +11 -0
  33. package/src/Components/Triggers/Gmail.trigger.ts +282 -0
  34. package/src/Components/Triggers/JobScheduler.trigger.ts +45 -0
  35. package/src/Components/Triggers/README.md +3 -0
  36. package/src/Components/Triggers/Trigger.class.ts +101 -0
  37. package/src/Components/Triggers/WhatsApp.trigger.ts +219 -0
  38. package/src/Components/index.ts +8 -0
  39. package/src/Core/AgentProcess.helper.ts +4 -6
  40. package/src/Core/Connector.class.ts +11 -3
  41. package/src/Core/ConnectorsService.ts +5 -0
  42. package/src/Core/ExternalEventsReceiver.ts +317 -0
  43. package/src/Core/HookService.ts +20 -6
  44. package/src/Core/SmythRuntime.class.ts +7 -0
  45. package/src/Core/SystemEvents.ts +17 -0
  46. package/src/Core/boot.ts +2 -0
  47. package/src/helpers/Conversation.helper.ts +35 -11
  48. package/src/helpers/Crypto.helper.ts +28 -0
  49. package/src/index.ts +208 -195
  50. package/src/index.ts.bak +208 -195
  51. package/src/subsystems/AGENTS.md +594 -0
  52. package/src/subsystems/AgentManager/Agent.class.ts +71 -21
  53. package/src/subsystems/AgentManager/AgentData.service/AgentDataConnector.ts +24 -1
  54. package/src/subsystems/AgentManager/AgentData.service/connectors/NullAgentData.class.ts +2 -2
  55. package/src/subsystems/AgentManager/AgentRuntime.class.ts +34 -5
  56. package/src/subsystems/AgentManager/Scheduler.service/Job.class.ts +414 -0
  57. package/src/subsystems/AgentManager/Scheduler.service/Schedule.class.ts +200 -0
  58. package/src/subsystems/AgentManager/Scheduler.service/SchedulerConnector.ts +200 -0
  59. package/src/subsystems/AgentManager/Scheduler.service/connectors/LocalScheduler.class.ts +767 -0
  60. package/src/subsystems/AgentManager/Scheduler.service/index.ts +11 -0
  61. package/src/subsystems/IO/VectorDB.service/connectors/MilvusVectorDB.class.ts +1 -1
  62. package/src/subsystems/LLMManager/LLM.service/LLMCredentials.helper.ts +61 -2
  63. package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +3 -0
  64. package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +3 -1
  65. package/src/subsystems/LLMManager/LLM.service/connectors/Echo.class.ts +5 -1
  66. package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +247 -56
  67. package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +3 -0
  68. package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +28 -21
  69. package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +3 -0
  70. package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +121 -33
  71. package/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts +38 -27
  72. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +115 -18
  73. package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +3 -0
  74. package/src/subsystems/LLMManager/ModelsProvider.service/ModelsProviderConnector.ts +1 -6
  75. package/src/subsystems/MemoryManager/LLMContext.ts +3 -8
  76. package/src/subsystems/MemoryManager/RuntimeContext.ts +10 -9
  77. package/src/subsystems/Security/Credentials/Credentials.class.ts +1 -0
  78. package/src/subsystems/Security/Credentials/ManagedOAuth2Credentials.class.ts +106 -0
  79. package/src/types/Agent.types.ts +1 -0
  80. package/src/types/LLM.types.ts +2 -2
  81. package/src/types/SRE.types.ts +3 -0
@@ -0,0 +1,11 @@
1
+ //==[ SRE: Scheduler ]======================
2
+
3
+ import { ConnectorService, ConnectorServiceProvider } from '@sre/Core/ConnectorsService';
4
+ import { TConnectorService } from '@sre/types/SRE.types';
5
+ import { LocalScheduler } from './connectors/LocalScheduler.class';
6
+
7
+ export class SchedulerService extends ConnectorServiceProvider {
8
+ public register() {
9
+ ConnectorService.register(TConnectorService.Scheduler, 'LocalScheduler', LocalScheduler);
10
+ }
11
+ }
@@ -205,7 +205,7 @@ export class MilvusVectorDB extends VectorDBConnector {
205
205
  }
206
206
 
207
207
  const result = await this.client.search({
208
- vector: _vector as number[],
208
+ data: _vector as number[],
209
209
  collection_name: preparedNs,
210
210
  output_fields: ['id', 'text', this.USER_METADATA_KEY, 'namespaceId', 'datasourceId', 'datasourceLabel', 'vector'],
211
211
  limit: options.topK || 10,
@@ -1,6 +1,7 @@
1
1
  import { ConnectorService } from '@sre/Core/ConnectorsService';
2
2
  import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
3
3
  import { TBedrockSettings, TCustomLLMModel, TLLMCredentials, TLLMModel, TVertexAISettings } from '@sre/types/LLM.types';
4
+ import { TemplateString, Match } from '@sre/helpers/TemplateString.helper';
4
5
 
5
6
  export async function getLLMCredentials(candidate: AccessCandidate, modelInfo: TLLMModel | TCustomLLMModel) {
6
7
  //create a credentials list that we can iterate over
@@ -11,8 +12,16 @@ export async function getLLMCredentials(candidate: AccessCandidate, modelInfo: T
11
12
 
12
13
  for (let credentialsMode of credentialsList) {
13
14
  if (typeof credentialsMode === 'object') {
14
- //credentials passed directly
15
- return credentialsMode;
15
+ // Resolve key templates for each property in the credentials object
16
+ const resolvedCredentials: any = {};
17
+
18
+ for (const [key, value] of Object.entries(credentialsMode)) {
19
+ // Resolve {{KEY(...)}} templates from vault (supports nested objects)
20
+ resolvedCredentials[key] = await resolveKeyTemplate(value, candidate);
21
+ }
22
+
23
+ //credentials passed directly (resolved from vault templates if any)
24
+ return resolvedCredentials;
16
25
  }
17
26
 
18
27
  switch (credentialsMode) {
@@ -169,3 +178,53 @@ async function getVertexAICredentials(candidate: AccessCandidate, modelInfo: TCu
169
178
  if (!credentials) return null;
170
179
  return { ...credentials, isUserKey: true };
171
180
  }
181
+
182
+ /**
183
+ * Creates a vault key processor for the TemplateString helper that works with AccessCandidate
184
+ * @param candidate - The access candidate requesting the credentials
185
+ * @returns A processor function that resolves vault keys using the candidate's permissions
186
+ * @private
187
+ */
188
+ function createVaultKeyProcessor(candidate: AccessCandidate): (token: string) => Promise<string> {
189
+ return async (token: string) => {
190
+ try {
191
+ const vaultConnector = ConnectorService.getVaultConnector();
192
+ return await vaultConnector.requester(candidate).get(token);
193
+ } catch (error) {
194
+ return '';
195
+ }
196
+ };
197
+ }
198
+
199
+ /**
200
+ * Resolves a vault key template by fetching the value from the vault
201
+ * Uses the TemplateString helper to parse {{KEY(...)}} templates
202
+ * Recursively handles nested objects and arrays
203
+ * @param value - The value to resolve (can be a template string, object, array, or primitive)
204
+ * @param candidate - The access candidate requesting the credentials
205
+ * @returns Promise resolving to the vault value or the original value if not a template
206
+ * @private
207
+ */
208
+ async function resolveKeyTemplate(value: any, candidate: AccessCandidate): Promise<any> {
209
+ // Handle strings with templates
210
+ if (typeof value === 'string') {
211
+ return await TemplateString(value).process(createVaultKeyProcessor(candidate), Match.fn('KEY')).asyncResult;
212
+ }
213
+
214
+ // Handle arrays recursively
215
+ if (Array.isArray(value)) {
216
+ return await Promise.all(value.map((item) => resolveKeyTemplate(item, candidate)));
217
+ }
218
+
219
+ // Handle nested objects recursively
220
+ if (typeof value === 'object' && value !== null) {
221
+ const resolvedObject: any = {};
222
+ for (const [key, val] of Object.entries(value)) {
223
+ resolvedObject[key] = await resolveKeyTemplate(val, candidate);
224
+ }
225
+ return resolvedObject;
226
+ }
227
+
228
+ // Return primitives as-is
229
+ return value;
230
+ }
@@ -26,6 +26,7 @@ import { LLMConnector } from '../LLMConnector';
26
26
  import { SystemEvents } from '@sre/Core/SystemEvents';
27
27
  import { SUPPORTED_MIME_TYPES_MAP } from '@sre/constants';
28
28
  import { Logger } from '@sre/helpers/Log.helper';
29
+ import { hookAsync } from '@sre/Core/HookService';
29
30
 
30
31
  const logger = Logger('AnthropicConnector');
31
32
 
@@ -50,6 +51,7 @@ export class AnthropicConnector extends LLMConnector {
50
51
  return new Anthropic({ apiKey });
51
52
  }
52
53
 
54
+ @hookAsync('LLMConnector.request')
53
55
  protected async request({ acRequest, body, context }: ILLMRequestFuncParams): Promise<TLLMChatResponse> {
54
56
  try {
55
57
  logger.debug(`request ${this.name}`, acRequest.candidate);
@@ -113,6 +115,7 @@ export class AnthropicConnector extends LLMConnector {
113
115
  }
114
116
  }
115
117
 
118
+ @hookAsync('LLMConnector.streamRequest')
116
119
  protected async streamRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter> {
117
120
  try {
118
121
  logger.debug(`streamRequest ${this.name}`, acRequest.candidate);
@@ -31,6 +31,7 @@ import { LLMConnector } from '../LLMConnector';
31
31
  import { JSONContent } from '@sre/helpers/JsonContent.helper';
32
32
  import { SystemEvents } from '@sre/Core/SystemEvents';
33
33
  import { Logger } from '@sre/helpers/Log.helper';
34
+ import { hookAsync } from '@sre/Core/HookService';
34
35
 
35
36
  const logger = Logger('BedrockConnector');
36
37
 
@@ -51,6 +52,7 @@ export class BedrockConnector extends LLMConnector {
51
52
  });
52
53
  }
53
54
 
55
+ @hookAsync('LLMConnector.request')
54
56
  protected async request({ acRequest, body, context }: ILLMRequestFuncParams): Promise<TLLMChatResponse> {
55
57
  try {
56
58
  logger.debug(`request ${this.name}`, acRequest.candidate);
@@ -99,7 +101,7 @@ export class BedrockConnector extends LLMConnector {
99
101
  throw error?.error || error;
100
102
  }
101
103
  }
102
-
104
+ @hookAsync('LLMConnector.streamRequest')
103
105
  protected async streamRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter> {
104
106
  const emitter = new EventEmitter();
105
107
 
@@ -3,12 +3,15 @@ import { LLMConnector } from '../LLMConnector';
3
3
  import EventEmitter from 'events';
4
4
  import { APIKeySource, ILLMRequestFuncParams, TLLMChatResponse, TLLMPreparedParams } from '@sre/types/LLM.types';
5
5
  import { Logger } from '@sre/helpers/Log.helper';
6
+ import { delay } from '@sre/utils/index';
7
+ import { hookAsync } from '@sre/Core/HookService';
6
8
 
7
9
  const logger = Logger('EchoConnector');
8
10
 
9
11
  export class EchoConnector extends LLMConnector {
10
12
  public name = 'LLM:Echo';
11
13
 
14
+ @hookAsync('LLMConnector.request')
12
15
  protected async request({ acRequest, body, context }: ILLMRequestFuncParams): Promise<TLLMChatResponse> {
13
16
  try {
14
17
  logger.debug(`request ${this.name}`, acRequest.candidate);
@@ -27,6 +30,7 @@ export class EchoConnector extends LLMConnector {
27
30
  }
28
31
  }
29
32
 
33
+ @hookAsync('LLMConnector.streamRequest')
30
34
  protected async streamRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter> {
31
35
  try {
32
36
  logger.debug(`streamRequest ${this.name}`, acRequest.candidate);
@@ -45,7 +49,7 @@ export class EchoConnector extends LLMConnector {
45
49
 
46
50
  for (let i = 0; i < chunks.length; i++) {
47
51
  // Simulate network delay
48
- await new Promise((resolve) => setTimeout(resolve, 50));
52
+ await delay(10);
49
53
 
50
54
  const isLastChunk = i === chunks.length - 1;
51
55
  // Add space between chunks except for the last one to avoid trailing space in file URLs
@@ -3,12 +3,7 @@ import path from 'path';
3
3
  import EventEmitter from 'events';
4
4
  import fs from 'fs';
5
5
 
6
- import {
7
- GoogleGenAI,
8
- FunctionCallingConfigMode,
9
- FileState,
10
- type GenerateContentResponseUsageMetadata,
11
- } from '@google/genai/node';
6
+ import { GoogleGenAI, FunctionCallingConfigMode, FileState, type GenerateContentResponseUsageMetadata } from '@google/genai/node';
12
7
 
13
8
  import { JSON_RESPONSE_INSTRUCTION, BUILT_IN_MODEL_PREFIX } from '@sre/constants';
14
9
  import { BinaryInput } from '@sre/helpers/BinaryInput.helper';
@@ -40,6 +35,7 @@ import { SUPPORTED_MIME_TYPES_MAP } from '@sre/constants';
40
35
  import { Logger } from '@sre/helpers/Log.helper';
41
36
 
42
37
  import { LLMConnector } from '../LLMConnector';
38
+ import { hookAsync } from '@sre/Core/HookService';
43
39
 
44
40
  const logger = Logger('GoogleAIConnector');
45
41
 
@@ -90,6 +86,7 @@ export class GoogleAIConnector extends LLMConnector {
90
86
  return new GoogleGenAI({ apiKey });
91
87
  }
92
88
 
89
+ @hookAsync('LLMConnector.request')
93
90
  protected async request({ acRequest, body, context }: ILLMRequestFuncParams): Promise<TLLMChatResponse> {
94
91
  try {
95
92
  logger.debug(`request ${this.name}`, acRequest.candidate);
@@ -160,6 +157,7 @@ export class GoogleAIConnector extends LLMConnector {
160
157
  }
161
158
  }
162
159
 
160
+ @hookAsync('LLMConnector.streamRequest')
163
161
  protected async streamRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter> {
164
162
  logger.debug(`streamRequest ${this.name}`, acRequest.candidate);
165
163
  const emitter = new EventEmitter();
@@ -436,9 +434,10 @@ export class GoogleAIConnector extends LLMConnector {
436
434
  return body;
437
435
  }
438
436
 
439
- private normalizePrompt(
440
- prompt: TGoogleAIRequestBody['messages'] | TGoogleAIRequestBody['contents']
441
- ): { contents: any; config?: Record<string, any> } {
437
+ private normalizePrompt(prompt: TGoogleAIRequestBody['messages'] | TGoogleAIRequestBody['contents']): {
438
+ contents: any;
439
+ config?: Record<string, any>;
440
+ } {
442
441
  if (prompt == null) {
443
442
  return { contents: '' };
444
443
  }
@@ -627,87 +626,279 @@ export class GoogleAIConnector extends LLMConnector {
627
626
  }): TLLMToolResultMessageBlock[] {
628
627
  const messageBlocks: TLLMToolResultMessageBlock[] = [];
629
628
 
630
- if (messageBlock) {
631
- const content = [];
632
- if (typeof messageBlock.content === 'string') {
633
- content.push({ text: messageBlock.content });
634
- } else if (Array.isArray(messageBlock.content)) {
635
- content.push(...messageBlock.content);
629
+ const parseFunctionArgs = (args: unknown) => {
630
+ if (typeof args === 'string') {
631
+ try {
632
+ return JSON.parse(args);
633
+ } catch {
634
+ return args;
635
+ }
636
636
  }
637
+ return args ?? {};
638
+ };
639
+
640
+ const parseFunctionResponse = (response: unknown): any => {
641
+ if (typeof response === 'string') {
642
+ try {
643
+ const parsed = JSON.parse(response);
644
+ if (typeof parsed === 'string' && parsed !== response) {
645
+ return parseFunctionResponse(parsed);
646
+ }
647
+ return parsed;
648
+ } catch {
649
+ return response;
650
+ }
651
+ }
652
+ return response ?? {};
653
+ };
637
654
 
638
- if (messageBlock.parts) {
639
- const functionCalls = messageBlock.parts.filter((part) => part.functionCall);
640
- if (functionCalls.length > 0) {
641
- content.push(
642
- ...functionCalls.map((call) => ({
655
+ if (messageBlock) {
656
+ const content: any[] = [];
657
+
658
+ if (Array.isArray(messageBlock.parts) && messageBlock.parts.length > 0) {
659
+ for (const part of messageBlock.parts) {
660
+ if (!part) continue;
661
+
662
+ if (typeof part.text === 'string' && part.text.trim()) {
663
+ content.push({ text: part.text.trim() });
664
+ continue;
665
+ }
666
+
667
+ if (part.functionCall) {
668
+ content.push({
643
669
  functionCall: {
644
- name: call.functionCall.name,
645
- args: JSON.parse(call.functionCall.args),
670
+ name: part.functionCall.name,
671
+ args: parseFunctionArgs(part.functionCall.args),
646
672
  },
647
- }))
648
- );
673
+ });
674
+ continue;
675
+ }
676
+
677
+ if (part.functionResponse) {
678
+ content.push({
679
+ functionResponse: {
680
+ name: part.functionResponse.name,
681
+ response: parseFunctionResponse(part.functionResponse.response),
682
+ },
683
+ });
684
+ continue;
685
+ }
686
+
687
+ if ((part as any).inlineData) {
688
+ content.push({ inlineData: (part as any).inlineData });
689
+ }
690
+ }
691
+ } else {
692
+ if (typeof messageBlock.content === 'string' && messageBlock.content.trim()) {
693
+ content.push({ text: messageBlock.content.trim() });
694
+ } else if (Array.isArray(messageBlock.content) && messageBlock.content.length > 0) {
695
+ content.push(...messageBlock.content);
696
+ }
697
+ }
698
+
699
+ const hasFunctionCall = content.some((part) => part.functionCall);
700
+ if (!hasFunctionCall && toolsData.length > 0) {
701
+ toolsData.forEach((toolCall) => {
702
+ content.push({
703
+ functionCall: {
704
+ name: toolCall.name,
705
+ args: parseFunctionArgs(toolCall.arguments),
706
+ },
707
+ });
708
+ });
709
+ }
710
+
711
+ if (content.length > 0) {
712
+ let role = messageBlock.role;
713
+ if (role === TLLMMessageRole.Assistant) {
714
+ role = TLLMMessageRole.Model;
649
715
  }
716
+
717
+ messageBlocks.push({
718
+ role,
719
+ parts: content,
720
+ });
650
721
  }
722
+ }
723
+
724
+ const functionResponseParts = toolsData
725
+ .filter((toolData) => toolData.result !== undefined)
726
+ .map((toolData) => ({
727
+ functionResponse: {
728
+ name: toolData.name,
729
+ response: parseFunctionResponse(toolData.result),
730
+ },
731
+ }));
651
732
 
733
+ if (functionResponseParts.length > 0) {
652
734
  messageBlocks.push({
653
- role: messageBlock.role,
654
- parts: content,
735
+ role: TLLMMessageRole.Function,
736
+ parts: functionResponseParts,
655
737
  });
656
738
  }
657
739
 
658
- const transformedToolsData = toolsData.map(
659
- (toolData): TLLMToolResultMessageBlock => ({
660
- role: TLLMMessageRole.User,
661
- parts: [
662
- {
663
- functionResponse: {
664
- name: toolData.name,
665
- response: {
666
- name: toolData.name,
667
- content: typeof toolData.result === 'string' ? toolData.result : JSON.stringify(toolData.result),
668
- },
669
- },
670
- },
671
- ],
672
- })
673
- );
674
-
675
- return [...messageBlocks, ...transformedToolsData];
740
+ return messageBlocks;
676
741
  }
677
742
 
678
743
  public getConsistentMessages(messages: TLLMMessageBlock[]): TLLMMessageBlock[] {
679
744
  const _messages = LLMHelper.removeDuplicateUserMessages(messages);
680
745
 
681
746
  return _messages.map((message) => {
682
- const _message = { ...message };
683
- let textContent = '';
747
+ const _message: TLLMMessageBlock = { ...message };
748
+
749
+ const parseFunctionArgs = (args: unknown) => {
750
+ if (typeof args === 'string') {
751
+ try {
752
+ return JSON.parse(args);
753
+ } catch {
754
+ return args;
755
+ }
756
+ }
757
+
758
+ return args ?? {};
759
+ };
760
+
761
+ const parseFunctionResponse = (response: unknown) => {
762
+ if (typeof response === 'string') {
763
+ try {
764
+ return JSON.parse(response);
765
+ } catch {
766
+ return response;
767
+ }
768
+ }
769
+
770
+ return response;
771
+ };
772
+
773
+ const pushTextPart = (parts: any[], text?: string) => {
774
+ const value = typeof text === 'string' && text.trim() ? text : undefined;
775
+ if (value) {
776
+ parts.push({ text: value });
777
+ }
778
+ };
779
+
780
+ const normalizedParts: any[] = [];
684
781
 
685
782
  // Map roles to valid Google AI roles
686
783
  switch (_message.role) {
687
784
  case TLLMMessageRole.Assistant:
688
785
  case TLLMMessageRole.System:
786
+ case TLLMMessageRole.Model:
689
787
  _message.role = TLLMMessageRole.Model;
690
788
  break;
789
+ case TLLMMessageRole.Function:
790
+ case TLLMMessageRole.Tool:
791
+ _message.role = TLLMMessageRole.Function;
792
+ break;
691
793
  case TLLMMessageRole.User:
692
- // User role is already valid
693
794
  break;
694
795
  default:
695
- _message.role = TLLMMessageRole.User; // Default to user for unknown roles
796
+ _message.role = TLLMMessageRole.User;
696
797
  }
697
798
 
698
- // * empty text causes error that's why we added '...'
799
+ if (Array.isArray(message?.parts)) {
800
+ for (const part of message.parts) {
801
+ if (!part) continue;
802
+
803
+ const normalizedPart: any = { ...part };
804
+
805
+ if (typeof normalizedPart.text === 'string') {
806
+ normalizedPart.text = normalizedPart.text.trim() || '...';
807
+ }
808
+
809
+ if (part.functionCall) {
810
+ normalizedPart.functionCall = {
811
+ name: part.functionCall.name,
812
+ args: parseFunctionArgs(part.functionCall.args),
813
+ };
814
+ }
815
+
816
+ if (part.functionResponse) {
817
+ normalizedPart.functionResponse = {
818
+ name: part.functionResponse.name,
819
+ response: parseFunctionResponse(part.functionResponse.response),
820
+ };
821
+ }
822
+
823
+ const hasMeaningfulContent = Object.values(normalizedPart).some((value) => value !== undefined && value !== null && value !== '');
824
+
825
+ if (hasMeaningfulContent) {
826
+ normalizedParts.push(normalizedPart);
827
+ }
828
+ }
829
+ }
830
+
831
+ if (!normalizedParts.length && Array.isArray(message?.content)) {
832
+ for (const contentPart of message.content) {
833
+ if (!contentPart) continue;
834
+
835
+ if (typeof contentPart === 'string') {
836
+ pushTextPart(normalizedParts, contentPart);
837
+ } else if (typeof contentPart === 'object') {
838
+ if ('text' in contentPart && typeof contentPart.text === 'string') {
839
+ pushTextPart(normalizedParts, contentPart.text);
840
+ } else if ('functionCall' in contentPart && (contentPart as any).functionCall) {
841
+ const functionCallPart = (contentPart as any).functionCall;
842
+ normalizedParts.push({
843
+ functionCall: {
844
+ name: functionCallPart.name,
845
+ args: parseFunctionArgs(functionCallPart.args),
846
+ },
847
+ });
848
+ } else if ('functionResponse' in contentPart && (contentPart as any).functionResponse) {
849
+ const functionResponsePart = (contentPart as any).functionResponse;
850
+ normalizedParts.push({
851
+ functionResponse: {
852
+ name: functionResponsePart.name,
853
+ response: parseFunctionResponse(functionResponsePart.response),
854
+ },
855
+ });
856
+ } else {
857
+ const fallbackText = typeof (contentPart as any)?.toString === 'function' ? (contentPart as any).toString() : '';
858
+ if (fallbackText && fallbackText !== '[object Object]') {
859
+ pushTextPart(normalizedParts, fallbackText);
860
+ }
861
+ }
862
+ }
863
+ }
864
+ }
865
+
866
+ if (!normalizedParts.length) {
867
+ if (typeof message?.content === 'string') {
868
+ pushTextPart(normalizedParts, message.content);
869
+ } else if (message?.content && typeof message.content === 'object') {
870
+ if ('text' in (message.content as any)) {
871
+ pushTextPart(normalizedParts, (message.content as any).text);
872
+ } else {
873
+ const fallbackText = typeof (message.content as any)?.toString === 'function' ? (message.content as any).toString() : '';
874
+ if (fallbackText && fallbackText !== '[object Object]') {
875
+ pushTextPart(normalizedParts, fallbackText);
876
+ }
877
+ }
878
+ }
879
+ }
880
+
881
+ if (Array.isArray(message?.tool_calls) && message.tool_calls.length > 0) {
882
+ for (const toolCall of message.tool_calls) {
883
+ if (!toolCall?.function?.name) continue;
884
+
885
+ normalizedParts.push({
886
+ functionCall: {
887
+ name: toolCall.function.name,
888
+ args: parseFunctionArgs(toolCall.function.arguments),
889
+ },
890
+ });
891
+ }
892
+ }
699
893
 
700
- if (_message?.parts) {
701
- textContent = _message.parts.map((textBlock) => textBlock?.text || '...').join(' ');
702
- } else if (Array.isArray(_message?.content)) {
703
- textContent = _message.content.map((textBlock) => textBlock?.text || '...').join(' ');
704
- } else if (_message?.content) {
705
- textContent = (_message.content as string) || '...';
894
+ if (!normalizedParts.length) {
895
+ normalizedParts.push({ text: '...' });
706
896
  }
707
897
 
708
- _message.parts = [{ text: textContent || '...' }];
898
+ _message.parts = normalizedParts as any;
709
899
 
710
900
  delete _message.content; // Remove content to avoid error
901
+ delete (_message as any).tool_calls;
711
902
 
712
903
  return _message;
713
904
  });
@@ -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('GroqConnector');
25
26
 
@@ -50,6 +51,7 @@ export class GroqConnector extends LLMConnector {
50
51
  return new Groq({ apiKey });
51
52
  }
52
53
 
54
+ @hookAsync('LLMConnector.request')
53
55
  protected async request({ acRequest, body, context }: ILLMRequestFuncParams): Promise<TLLMChatResponse> {
54
56
  try {
55
57
  logger.debug(`request ${this.name}`, acRequest.candidate);
@@ -95,6 +97,7 @@ export class GroqConnector extends LLMConnector {
95
97
  }
96
98
  }
97
99
 
100
+ @hookAsync('LLMConnector.streamRequest')
98
101
  protected async streamRequest({ acRequest, body, context }: ILLMRequestFuncParams): Promise<EventEmitter> {
99
102
  try {
100
103
  logger.debug(`streamRequest ${this.name}`, acRequest.candidate);