@smythos/sre 1.7.40 → 1.7.42

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/index.js +49 -42
  2. package/dist/index.js.map +1 -1
  3. package/dist/types/Components/AgentPlugin.class.d.ts +1 -1
  4. package/dist/types/Components/RAG/DataSourceCleaner.class.d.ts +4 -4
  5. package/dist/types/Components/RAG/DataSourceComponent.class.d.ts +5 -1
  6. package/dist/types/config.d.ts +1 -0
  7. package/dist/types/helpers/Conversation.helper.d.ts +10 -13
  8. package/dist/types/helpers/TemplateString.helper.d.ts +1 -1
  9. package/dist/types/index.d.ts +1 -0
  10. package/dist/types/subsystems/IO/VectorDB.service/VectorDBConnector.d.ts +1 -0
  11. package/dist/types/subsystems/LLMManager/LLM.helper.d.ts +19 -0
  12. package/dist/types/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.d.ts +15 -10
  13. package/dist/types/types/LLM.types.d.ts +23 -0
  14. package/package.json +1 -1
  15. package/src/Components/AgentPlugin.class.ts +20 -3
  16. package/src/Components/Classifier.class.ts +79 -16
  17. package/src/Components/ForEach.class.ts +34 -6
  18. package/src/Components/GenAILLM.class.ts +54 -23
  19. package/src/Components/LLMAssistant.class.ts +56 -21
  20. package/src/Components/RAG/DataSourceCleaner.class.ts +13 -11
  21. package/src/Components/RAG/DataSourceComponent.class.ts +39 -13
  22. package/src/Components/RAG/DataSourceIndexer.class.ts +18 -12
  23. package/src/Components/RAG/DataSourceLookup.class.ts +14 -10
  24. package/src/Components/ScrapflyWebScrape.class.ts +7 -0
  25. package/src/config.ts +1 -0
  26. package/src/helpers/Conversation.helper.ts +112 -26
  27. package/src/helpers/TemplateString.helper.ts +6 -5
  28. package/src/index.ts +213 -212
  29. package/src/index.ts.bak +213 -212
  30. package/src/subsystems/IO/VectorDB.service/VectorDBConnector.ts +1 -0
  31. package/src/subsystems/IO/VectorDB.service/connectors/PineconeVectorDB.class.ts +11 -0
  32. package/src/subsystems/IO/VectorDB.service/embed/index.ts +9 -11
  33. package/src/subsystems/LLMManager/LLM.helper.ts +25 -0
  34. package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +1 -1
  35. package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +190 -146
  36. package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/utils.ts +1 -1
  37. package/src/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.class.ts +402 -66
  38. package/src/types/LLM.types.ts +24 -0
  39. package/src/utils/data.utils.ts +6 -4
  40. package/src/Components/DataSourceIndexer.class.ts +0 -295
@@ -10,7 +10,8 @@ import { encode } from 'gpt-tokenizer';
10
10
  import { Component } from './Component.class';
11
11
  import { JSONContent } from '@sre/helpers/JsonContent.helper';
12
12
  import { LLMInference } from '@sre/LLMManager/LLM.inference';
13
- import { TLLMMessageRole } from '@sre/types/LLM.types';
13
+ import { LLMHelper } from '@sre/LLMManager/LLM.helper';
14
+ import { TLLMMessageRole, TLLMEvent } from '@sre/types/LLM.types';
14
15
  import { VaultHelper } from '@sre/Security/Vault.service/Vault.helper';
15
16
  import path from 'path';
16
17
  import config from '@sre/config';
@@ -113,7 +114,7 @@ export class LLMAssistant extends Component {
113
114
  const conversationId = input.ConversationId;
114
115
 
115
116
  let behavior = TemplateString(config.data.behavior).parse(input).result;
116
- logger.debug(`[Parsed Behavior] \n${behavior}`);
117
+ logger.debug(` [Parsed Behavior] \n${behavior}`);
117
118
 
118
119
  //#region get max tokens
119
120
  let maxTokens = 2048;
@@ -147,7 +148,14 @@ export class LLMAssistant extends Component {
147
148
  };
148
149
 
149
150
  let response: any;
150
- if (passThrough) {
151
+ const isClaude4 = LLMHelper.isClaude4Family(modelId || model);
152
+
153
+ // Use streaming for passthrough OR Claude 4 family models
154
+ if (passThrough || isClaude4) {
155
+ if (isClaude4) {
156
+ logger.debug(`\n ⚡ Using streaming for Claude 4 family model`);
157
+ }
158
+
151
159
  const contentPromise = new Promise(async (resolve, reject) => {
152
160
  let _content = '';
153
161
  const eventEmitter: any = await llmInference
@@ -162,25 +170,53 @@ export class LLMAssistant extends Component {
162
170
  console.error('Error on promptStream: ', error);
163
171
  reject(error);
164
172
  });
165
- eventEmitter.on('content', (content) => {
166
- if (typeof agent.callback === 'function') {
167
- agent.callback({ content });
173
+
174
+ eventEmitter.on(TLLMEvent.Content, (content) => {
175
+ if (passThrough) {
176
+ if (typeof agent.callback === 'function') {
177
+ agent.callback({ content });
178
+ }
179
+ agent.sse.send('llm/passthrough/content', content);
168
180
  }
169
- agent.sse.send('llm/passthrough/content', content);
170
181
  _content += content;
171
182
  });
172
- eventEmitter.on('thinking', (thinking) => {
173
- if (typeof agent.callback === 'function') {
174
- agent.callback({ thinking });
183
+
184
+ eventEmitter.on(TLLMEvent.Thinking, (thinking) => {
185
+ if (passThrough) {
186
+ if (typeof agent.callback === 'function') {
187
+ agent.callback({ thinking });
188
+ }
189
+ agent.sse.send('llm/passthrough/thinking', thinking);
175
190
  }
176
- agent.sse.send('llm/passthrough/thinking', thinking);
177
191
  });
178
- eventEmitter.on('end', () => {
179
- console.log('end');
192
+
193
+ eventEmitter.on(TLLMEvent.End, () => {
180
194
  resolve(_content);
181
195
  });
196
+
197
+ eventEmitter.on(TLLMEvent.Error, (error) => {
198
+ reject(error);
199
+ });
182
200
  });
183
- response = await contentPromise;
201
+
202
+ response = await contentPromise.catch((error) => {
203
+ return { error: error.message || error };
204
+ });
205
+
206
+ // Handle streaming errors
207
+ if (response?.error) {
208
+ const error = response?.error + ' ' + (response?.details || '');
209
+ logger.error(` LLM Error=`, error);
210
+ return { Response: response?.data, _error: error, _debug: logger.output };
211
+ }
212
+
213
+ // Post-process the streaming response
214
+ const postProcessed = llmInference.connector.postProcess(response);
215
+ if (postProcessed.error) {
216
+ logger.error(` LLM Post-process Error=`, postProcessed.error);
217
+ return { _error: postProcessed.error, _debug: logger.output };
218
+ }
219
+ response = postProcessed;
184
220
  } else {
185
221
  response = await llmInference
186
222
  .prompt({
@@ -191,6 +227,12 @@ export class LLMAssistant extends Component {
191
227
  },
192
228
  })
193
229
  .catch((error) => ({ error: error }));
230
+
231
+ if (response?.error) {
232
+ const error = response?.error + ' ' + (response?.details || '');
233
+ logger.error(` LLM Error=`, error);
234
+ return { Response: response?.data, _error: error, _debug: logger.output };
235
+ }
194
236
  }
195
237
 
196
238
  // in case we have the response but it's empty string, undefined or null
@@ -198,13 +240,6 @@ export class LLMAssistant extends Component {
198
240
  return { _error: ' LLM Error = Empty Response!', _debug: logger.output };
199
241
  }
200
242
 
201
- if (response?.error) {
202
- const error = response?.error + ' ' + (response?.details || '');
203
- logger.error(` LLM Error=`, error);
204
-
205
- return { Response: response?.data, _error: error, _debug: logger.output };
206
- }
207
-
208
243
  messages.push({ role: 'assistant', content: response });
209
244
  saveMessagesToSession(agent.id, userId, conversationId, messages, ttl);
210
245
 
@@ -7,8 +7,9 @@ import { ConnectorService } from '@sre/Core/ConnectorsService';
7
7
  import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
8
8
 
9
9
  import { DataSourceIndexer } from './DataSourceIndexer.class';
10
- import { DataSourceComponent } from './DataSourceComponent.class';
10
+ import { DataSourceComponent, NsRecord } from './DataSourceComponent.class';
11
11
  import { VectorDBConnector } from '@sre/IO/VectorDB.service/VectorDBConnector';
12
+ import envConfig from '@sre/config';
12
13
 
13
14
  export class DataSourceCleaner extends DataSourceComponent {
14
15
  protected configSchema = Joi.object({
@@ -23,10 +24,10 @@ export class DataSourceCleaner extends DataSourceComponent {
23
24
 
24
25
  async process(input, config, agent: Agent) {
25
26
  await super.process(input, config, agent);
26
- if (!config.data.version || config.data.version === 'v1') {
27
- return await this.processV1(input, config, agent);
28
- } else if (config.data.version === 'v2') {
27
+ if (envConfig.env.ROLLOUT_RAG_V2) {
29
28
  return await this.processV2(input, config, agent);
29
+ } else {
30
+ return await this.processV1(input, config, agent);
30
31
  }
31
32
  }
32
33
  async processV1(input, config, agent: Agent) {
@@ -114,14 +115,15 @@ export class DataSourceCleaner extends DataSourceComponent {
114
115
  }
115
116
 
116
117
  // const namespaceId = configSchema.value.namespaceId.split('_')?.slice(1).join('_') || configSchema.value.namespaceId;
117
- const namespaceLabel = /^c[a-z0-9]{24}.+$/.test(configSchema.value.namespaceId)
118
- ? configSchema.value.namespaceId.split('_').slice(1).join('_')
119
- : configSchema.value.namespaceId;
120
- const namespaceId = configSchema.value.namespaceId;
118
+ const namespaceLabelorId = configSchema.value.namespaceId;
119
+ // const namespaceId = configSchema.value.namespaceId;
121
120
 
122
121
  let vecDbConnector: VectorDBConnector = null;
122
+ let namespaceRecord: NsRecord = null;
123
123
  try {
124
- vecDbConnector = await this.resolveVectorDbConnector(namespaceId, teamId);
124
+ const resolved = await this.resolveVectorDbConnector(namespaceLabelorId, teamId);
125
+ vecDbConnector = resolved.vecDbConnector;
126
+ namespaceRecord = resolved.namespaceRecord;
125
127
  } catch (err: any) {
126
128
  debugOutput += `Error: ${err?.message || "Couldn't get vector database connector"}\n\n`;
127
129
  return {
@@ -142,9 +144,9 @@ export class DataSourceCleaner extends DataSourceComponent {
142
144
  }
143
145
  debugOutput += `Searching for data source with id: ${providedId}\n`;
144
146
 
145
- const dsId = DataSourceComponent.normalizeDsId(providedId, teamId, namespaceLabel);
147
+ const dsId = DataSourceComponent.normalizeDsId(providedId, teamId, namespaceRecord.label);
146
148
 
147
- await vecDbConnector.requester(AccessCandidate.team(teamId)).deleteDatasource(namespaceLabel, dsId);
149
+ await vecDbConnector.requester(AccessCandidate.team(teamId)).deleteDatasource(namespaceRecord.label, dsId);
148
150
 
149
151
  debugOutput += `Deleted data source with id: ${providedId}\n`;
150
152
 
@@ -34,23 +34,16 @@ export class DataSourceComponent extends Component {
34
34
  super();
35
35
  }
36
36
 
37
- public async resolveVectorDbConnector(namespace: string | NsRecord, teamId: string): Promise<VectorDBConnector> {
37
+ public async resolveVectorDbConnector(
38
+ namespace: string | NsRecord,
39
+ teamId: string
40
+ ): Promise<{ vecDbConnector: VectorDBConnector; namespaceRecord: NsRecord }> {
38
41
  // resolve the ns record, if not exist, throw an error (new in v2)
39
42
  // then we also need to resolve the credentials
40
43
  let namespaceRecord = namespace as NsRecord;
41
44
 
42
45
  if (typeof namespace === 'string') {
43
- // if it's a string, we need to get the namespace record from the NKV
44
- const nkvConnector = ConnectorService.getNKVConnector();
45
- const nkvClient = nkvConnector.requester(AccessCandidate.team(teamId));
46
- const rawNsRecord = await nkvClient.get(`vectorDB:namespaces`, namespace);
47
-
48
- if (!rawNsRecord) {
49
- throw new DataSourceCompError(`Namespace ${namespace} does not exist`, TDataSourceCompErrorCodes.NAMESPACE_NOT_FOUND);
50
- }
51
-
52
- // const { credentialId, embeddings: embeddingsOptions } = JSON.parse(rawNsRecord.toString());
53
- namespaceRecord = JSON.parse(rawNsRecord.toString()) as NsRecord;
46
+ namespaceRecord = await this.resolveNamespaceRecord(namespace, teamId);
54
47
  }
55
48
 
56
49
  const accountConnector = ConnectorService.getAccountConnector();
@@ -75,7 +68,40 @@ export class DataSourceComponent extends Component {
75
68
  embeddings: await this.buildEmbeddingConfig(namespaceRecord.embeddings, teamId),
76
69
  });
77
70
 
78
- return vecDbConnector;
71
+ return { vecDbConnector, namespaceRecord };
72
+ }
73
+
74
+ private async resolveNamespaceRecord(namespace: string, teamId: string): Promise<NsRecord> {
75
+ // if it's a string, we need to get the namespace record from the NKV
76
+ // TRY 1) try to get namespace using the label provided from teamSettings
77
+ const nkvConnector = ConnectorService.getNKVConnector();
78
+ const nkvClient = nkvConnector.requester(AccessCandidate.team(teamId));
79
+ const rawNsRecord = await nkvClient.get(`vectorDB:namespaces`, namespace);
80
+
81
+ if (rawNsRecord) {
82
+ return JSON.parse(rawNsRecord.toString()) as NsRecord;
83
+ } else {
84
+ // throw new DataSourceCompError(`Namespace ${namespace} does not exist`, TDataSourceCompErrorCodes.NAMESPACE_NOT_FOUND);
85
+ console.warn(`Namespace ${namespace} does not exist using the label ${namespace}. Trying to get namespace by mapping legacy id to V2 id`);
86
+ }
87
+
88
+ // TRY 2) try to get namespace using the legacy id. list all namespaces and find the one that matches the legacy id
89
+ const namespaces = (await nkvClient.list(`vectorDB:namespaces`))
90
+ .map((namespace) => {
91
+ try {
92
+ return JSON.parse(namespace.data.toString());
93
+ } catch (error) {
94
+ return null;
95
+ }
96
+ })
97
+ .filter((namespace) => namespace !== null);
98
+
99
+ const matchingNamespace = namespaces.find((ns) => ns.__legacy_id === namespace);
100
+ if (!matchingNamespace) {
101
+ console.warn(`Namespace ${namespace} does not exist using the legacy id ${namespace}.`);
102
+ throw new DataSourceCompError(`Namespace ${namespace} does not exist`, TDataSourceCompErrorCodes.NAMESPACE_NOT_FOUND);
103
+ }
104
+ return matchingNamespace;
79
105
  }
80
106
 
81
107
  public async buildEmbeddingConfig(embedding: { dimensions: string; modelId: string }, teamId: string): Promise<TEmbeddings> {
@@ -1,5 +1,5 @@
1
1
  import { IAgent as Agent } from '@sre/types/Agent.types';
2
- import { DataSourceComponent } from './DataSourceComponent.class';
2
+ import { DataSourceComponent, NsRecord } from './DataSourceComponent.class';
3
3
  import Joi from 'joi';
4
4
  import { validateCharacterSet } from '@sre/utils/validation.utils';
5
5
  import { TemplateString } from '@sre/helpers/TemplateString.helper';
@@ -11,6 +11,7 @@ import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.cla
11
11
  import { TEmbeddings } from '@sre/IO/VectorDB.service/embed/BaseEmbedding';
12
12
  import { VectorDBConnector } from '@sre/IO/VectorDB.service/VectorDBConnector';
13
13
  import { JSONContentHelper } from '@sre/helpers/JsonContent.helper';
14
+ import envConfig from '@sre/config';
14
15
 
15
16
  export class DataSourceIndexer extends DataSourceComponent {
16
17
  private MAX_ALLOWED_URLS_PER_INPUT = 20;
@@ -31,10 +32,10 @@ export class DataSourceIndexer extends DataSourceComponent {
31
32
  await super.process(input, config, agent);
32
33
 
33
34
  let response: any = null;
34
- if (!config.data.version || config.data.version === 'v1') {
35
- response = await this.processV1(input, config, agent);
36
- } else if (config.data.version === 'v2') {
35
+ if (envConfig.env.ROLLOUT_RAG_V2) {
37
36
  response = await this.processV2(input, config, agent);
37
+ } else {
38
+ response = await this.processV1(input, config, agent);
38
39
  }
39
40
 
40
41
  return response;
@@ -60,7 +61,7 @@ export class DataSourceIndexer extends DataSourceComponent {
60
61
  }
61
62
 
62
63
  const namespaceId = _config.namespace.split('_').slice(1).join('_') || _config.namespace;
63
- debugOutput += `[Selected namespace id] \n${namespaceId}\n\n`;
64
+ debugOutput += `[Selected data space id] \n${namespaceId}\n\n`;
64
65
 
65
66
  const vectorDbConnector =
66
67
  // (await vectorDBHelper.getTeamConnector(teamId)) ||
@@ -149,13 +150,15 @@ export class DataSourceIndexer extends DataSourceComponent {
149
150
 
150
151
  // we try to get the namespace without the prefix teamId, if not exist, we use the full namespace id
151
152
  // const namespaceLabel = _config.namespace.split('_').slice(1).join('_') || _config.namespace;
152
- const namespaceLabel = /^c[a-z0-9]{24}.+$/.test(_config.namespace) ? _config.namespace.split('_').slice(1).join('_') : _config.namespace;
153
- const namespaceId = _config.namespace;
154
- debugOutput += `[Selected namespace] \n${namespaceLabel}\n\n`;
153
+ const namespaceLabelorId = _config.namespace;
154
+ // const namespaceId = _config.namespace;
155
155
 
156
156
  let vecDbConnector: VectorDBConnector = null;
157
+ let namespaceRecord: NsRecord = null;
157
158
  try {
158
- vecDbConnector = await this.resolveVectorDbConnector(namespaceId, teamId);
159
+ const resolved = await this.resolveVectorDbConnector(namespaceLabelorId, teamId);
160
+ vecDbConnector = resolved.vecDbConnector;
161
+ namespaceRecord = resolved.namespaceRecord;
159
162
  } catch (err: any) {
160
163
  debugOutput += `Error: ${err?.message || "Couldn't get vector database connector"}\n\n`;
161
164
  return {
@@ -163,6 +166,9 @@ export class DataSourceIndexer extends DataSourceComponent {
163
166
  _error: err?.message || "Couldn't get vector database connector",
164
167
  };
165
168
  }
169
+
170
+ debugOutput += `[Selected data space] \n${namespaceRecord.label}\n\n`;
171
+
166
172
  const vecDbClient = vecDbConnector.requester(AccessCandidate.team(teamId));
167
173
 
168
174
  const inputSchema = this.validateInput(input);
@@ -183,10 +189,10 @@ export class DataSourceIndexer extends DataSourceComponent {
183
189
  throw new Error(`Invalid id. Accepted characters: 'a-z', 'A-Z', '0-9', '-', '_', '.'`);
184
190
  }
185
191
 
186
- const dsId = DataSourceIndexer.normalizeDsId(providedId, teamId, namespaceLabel);
192
+ const dsId = DataSourceIndexer.normalizeDsId(providedId, teamId, namespaceRecord.label);
187
193
 
188
194
  // check if the datasource already exists
189
- const dsExists = await vecDbClient.getDatasource(namespaceLabel, dsId);
195
+ const dsExists = await vecDbClient.getDatasource(namespaceRecord.label, dsId);
190
196
  if (dsExists) {
191
197
  debugOutput += `Datasource already exists\n\n`;
192
198
  return {
@@ -197,7 +203,7 @@ export class DataSourceIndexer extends DataSourceComponent {
197
203
 
198
204
  debugOutput += `STEP: Parsing input as text\n\n`;
199
205
 
200
- const response = await vecDbClient.createDatasource(namespaceLabel, {
206
+ const response = await vecDbClient.createDatasource(namespaceRecord.label, {
201
207
  text: inputSchema.value.Source,
202
208
  metadata: JSONContentHelper.create(_config.metadata).tryParse() || null,
203
209
  id: dsId,
@@ -7,6 +7,7 @@ import { ConnectorService } from '@sre/Core/ConnectorsService';
7
7
  import { AccessCandidate } from '@sre/Security/AccessControl/AccessCandidate.class';
8
8
  import { IAgent as Agent } from '@sre/types/Agent.types';
9
9
  import { DataSourceComponent } from './DataSourceComponent.class';
10
+ import envConfig from '@sre/config';
10
11
 
11
12
  // Note: LLMHelper renamed to LLMInference
12
13
  class LLMInference {
@@ -40,10 +41,15 @@ export class DataSourceLookup extends DataSourceComponent {
40
41
 
41
42
  async process(input, config, agent: Agent) {
42
43
  await super.process(input, config, agent);
43
- if (!config.data.version || config.data.version === 'v1') {
44
- return await this.processV1(input, config, agent);
45
- } else if (config.data.version === 'v2') {
44
+ // if (!config.data.version || config.data.version === 'v1') {
45
+ // return await this.processV1(input, config, agent);
46
+ // } else if (config.data.version === 'v2') {
47
+ // return await this.processV2(input, config, agent);
48
+ // }
49
+ if (envConfig.env.ROLLOUT_RAG_V2) {
46
50
  return await this.processV2(input, config, agent);
51
+ } else {
52
+ return await this.processV1(input, config, agent);
47
53
  }
48
54
  }
49
55
  async processV1(input, config, agent: Agent) {
@@ -158,10 +164,8 @@ export class DataSourceLookup extends DataSourceComponent {
158
164
  outputs[con.name] = '';
159
165
  }
160
166
 
161
- const namespaceLabel = /^c[a-z0-9]{24}.+$/.test(config.data.namespace)
162
- ? config.data.namespace.split('_').slice(1).join('_')
163
- : config.data.namespace;
164
- const namespaceId = config.data.namespace;
167
+ const namespaceLabelorId = config.data.namespace;
168
+ // const namespaceId = config.data.namespace;
165
169
  const model = config.data?.model || 'gpt-4o-mini';
166
170
  const includeMetadata = config.data?.includeMetadata || false;
167
171
 
@@ -175,14 +179,14 @@ export class DataSourceLookup extends DataSourceComponent {
175
179
  // let vectorDbConnector = ConnectorService.getVectorDBConnector();
176
180
  // let existingNs = await vectorDbConnector.requester(AccessCandidate.team(teamId)).namespaceExists(namespaceLabel);
177
181
 
178
- const vecDbConnector = await this.resolveVectorDbConnector(namespaceId, teamId);
182
+ const { vecDbConnector, namespaceRecord } = await this.resolveVectorDbConnector(namespaceLabelorId, teamId);
179
183
 
180
184
  let results: string[] | { content: string; metadata: any; score?: number }[];
181
185
  let _error;
182
186
  try {
183
187
  const response = await vecDbConnector
184
188
  .requester(AccessCandidate.team(teamId))
185
- .search(namespaceLabel, _input, { topK, includeMetadata: true });
189
+ .search(namespaceRecord.label, _input, { topK, includeMetadata: true });
186
190
 
187
191
  results = response.slice(0, config.data.topK).map((result) => ({
188
192
  content: result.text,
@@ -211,7 +215,7 @@ export class DataSourceLookup extends DataSourceComponent {
211
215
  return includeMetadata || includeScore ? transformedResult : result.content;
212
216
  });
213
217
 
214
- debugOutput += `[Results] \nLoaded ${results.length} results from namespace: ${namespaceLabel}\n\n`;
218
+ debugOutput += `[Results] \nLoaded ${results.length} results from namespace: ${namespaceRecord.label}\n\n`;
215
219
  } catch (error) {
216
220
  debugOutput += `Error: ${error instanceof Error ? error.message : error.toString()}\n\n`;
217
221
  _error = error instanceof Error ? error.message : error.toString();
@@ -39,6 +39,7 @@ export class ScrapflyWebScrape extends Component {
39
39
  javascriptRendering: Joi.boolean().default(false).label('Enable JavaScript Rendering'),
40
40
  autoScroll: Joi.boolean().default(false).label('Enable Auto Scroll'),
41
41
  format: Joi.string().default('markdown').label('Format').optional(),
42
+ countries: Joi.array().items(Joi.string()).label('Countries').optional().allow(''),
42
43
  });
43
44
 
44
45
  constructor() {
@@ -96,6 +97,10 @@ export class ScrapflyWebScrape extends Component {
96
97
 
97
98
  async scrapeURL(url, data, key) {
98
99
  try {
100
+ // Handle countries - convert array to comma-separated string (e.g., "us,ca,mx")
101
+ const countryValue =
102
+ data.countries && Array.isArray(data.countries) && data.countries.length > 0 ? data.countries.join(',').toLowerCase() : undefined;
103
+
99
104
  const response = await axios({
100
105
  method: 'get',
101
106
  url: 'https://api.scrapfly.io/scrape',
@@ -107,8 +112,10 @@ export class ScrapflyWebScrape extends Component {
107
112
  ...(data.antiScrapingProtection && { asp: true }),
108
113
  ...(data.javascriptRendering && { render_js: true }),
109
114
  ...(data.autoScroll && { auto_scroll: true, render_js: true }),
115
+ ...(countryValue && { country: countryValue }),
110
116
  },
111
117
  });
118
+
112
119
  return {
113
120
  content: response.data?.result?.content,
114
121
  success: true,
package/src/config.ts CHANGED
@@ -6,6 +6,7 @@ const config = {
6
6
  LOG_LEVEL: process.env.LOG_LEVEL || 'none',
7
7
  LOG_FILTER: process.env.LOG_FILTER || '',
8
8
  NODE_ENV: process.env?.NODE_ENV,
9
+ ROLLOUT_RAG_V2: process.env.ROLLOUT_RAG_V2,
9
10
  },
10
11
  agent: {
11
12
  ENDPOINT_PREFIX: '/api',