@superatomai/sdk-node 0.0.17-mds → 0.0.18-mds

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.mjs CHANGED
@@ -5022,6 +5022,38 @@ var StreamBuffer = class {
5022
5022
  this.callback = void 0;
5023
5023
  }
5024
5024
  };
5025
+ var TaggedStreamBuffer = class extends StreamBuffer {
5026
+ constructor(callId, parent, sourceName, intent) {
5027
+ super();
5028
+ this.started = false;
5029
+ this.callId = callId;
5030
+ this.parent = parent;
5031
+ this.sourceName = sourceName;
5032
+ this.intent = intent;
5033
+ }
5034
+ hasCallback() {
5035
+ return this.parent.hasCallback();
5036
+ }
5037
+ getFullText() {
5038
+ return this.parent.getFullText();
5039
+ }
5040
+ write(chunk) {
5041
+ if (!this.parent.hasCallback()) return;
5042
+ if (!this.started) {
5043
+ this.started = true;
5044
+ const escapedName = this.sourceName.replace(/\|/g, "-");
5045
+ const escapedIntent = this.intent.replace(/\|/g, " ").replace(/"/g, """).substring(0, 200);
5046
+ this.parent.write(`__SB_${this.callId}_START__${escapedName}|${escapedIntent}__SB_END__`);
5047
+ }
5048
+ this.parent.write(`__SB_${this.callId}_MSG__${chunk}__SB_END__`);
5049
+ }
5050
+ flush() {
5051
+ this.parent.flush();
5052
+ }
5053
+ dispose() {
5054
+ this.parent.flush();
5055
+ }
5056
+ };
5025
5057
  function streamDelay(ms = STREAM_DELAY_MS) {
5026
5058
  return new Promise((resolve) => setTimeout(resolve, ms));
5027
5059
  }
@@ -6950,16 +6982,6 @@ var SourceAgent = class {
6950
6982
  const startTime = Date.now();
6951
6983
  const { intent, aggregation = "raw" } = input;
6952
6984
  logger.info(`[SourceAgent:${this.tool.name}] Starting | intent: "${intent}" | aggregation: ${aggregation}`);
6953
- if (this.streamBuffer.hasCallback()) {
6954
- const sourceType = this.extractSourceType();
6955
- const sourceLabel = sourceType !== "unknown" ? ` (${sourceType})` : "";
6956
- this.streamBuffer.write(`
6957
-
6958
- \u{1F517} **Connecting to ${this.tool.name}**${sourceLabel}
6959
-
6960
- `);
6961
- await streamDelay();
6962
- }
6963
6985
  try {
6964
6986
  const prompts = await this.buildPrompt(intent, aggregation);
6965
6987
  logger.logLLMPrompt(`sourceAgent:${this.tool.name}`, "system", extractPromptText(prompts.system));
@@ -6969,38 +6991,46 @@ var SourceAgent = class {
6969
6991
  let resultData = [];
6970
6992
  let queryExecuted;
6971
6993
  let totalRowsMatched = 0;
6994
+ let querySucceeded = false;
6995
+ let successfulQueries = 0;
6972
6996
  const schemaSearchFn = this.tool.schemaSearchFn;
6973
6997
  const toolHandler = async (toolName, toolInput) => {
6974
6998
  if (toolName.endsWith("_search_schema") && schemaSearchFn) {
6975
6999
  const keywords = toolInput.keywords || [];
6976
7000
  logger.info(`[SourceAgent:${this.tool.name}] Schema search: ${keywords.join(", ")}`);
6977
7001
  if (this.streamBuffer.hasCallback()) {
6978
- this.streamBuffer.write(`\u{1F50D} **Searching schema for:** ${keywords.join(", ")}
7002
+ this.streamBuffer.write(`\u{1F50D} **Looking up tables:** ${keywords.join(", ")}
6979
7003
 
6980
7004
  `);
6981
7005
  await streamDelay();
6982
7006
  }
6983
7007
  const result = schemaSearchFn(keywords);
6984
- if (this.streamBuffer.hasCallback()) {
6985
- const matchCount = (result.match(/^• /gm) || []).length;
6986
- this.streamBuffer.write(`\u{1F4CB} Found ${matchCount} matching table(s)
6987
-
6988
- `);
6989
- }
6990
7008
  return `Schema search results:
6991
7009
 
6992
7010
  ${result}`;
6993
7011
  }
7012
+ if (successfulQueries >= 2) {
7013
+ return `\u26D4 Maximum successful queries reached (2). Use the data already retrieved.`;
7014
+ }
6994
7015
  this.attempts++;
6995
7016
  if (this.attempts > this.config.maxRetries) {
6996
7017
  throw new Error(`Max retry attempts (${this.config.maxRetries}) reached for ${this.tool.name}`);
6997
7018
  }
6998
7019
  if (this.attempts > 1 && this.streamBuffer.hasCallback()) {
6999
- this.streamBuffer.write(`
7020
+ if (querySucceeded) {
7021
+ const reason = toolInput.reason || "";
7022
+ this.streamBuffer.write(`
7023
+
7024
+ \u{1F4DD} **Follow-up query${reason ? `:** ${reason}` : "**"}
7025
+
7026
+ `);
7027
+ } else {
7028
+ this.streamBuffer.write(`
7000
7029
 
7001
- \u{1F504} **Query failed, retrying with corrected query** (attempt ${this.attempts}/${this.config.maxRetries})
7030
+ \u{1F504} **Retrying query** (attempt ${this.attempts}/${this.config.maxRetries})
7002
7031
 
7003
7032
  `);
7033
+ }
7004
7034
  await streamDelay();
7005
7035
  }
7006
7036
  const cappedInput = { ...toolInput };
@@ -7016,13 +7046,6 @@ ${result}`;
7016
7046
  ${queryDisplay}
7017
7047
  \`\`\`
7018
7048
 
7019
- `);
7020
- } else {
7021
- this.streamBuffer.write(`\u{1F4DD} **Query parameters:**
7022
- \`\`\`json
7023
- ${JSON.stringify(cappedInput, null, 2)}
7024
- \`\`\`
7025
-
7026
7049
  `);
7027
7050
  }
7028
7051
  await streamDelay();
@@ -7036,17 +7059,36 @@ ${JSON.stringify(cappedInput, null, 2)}
7036
7059
  if (result && result.error) {
7037
7060
  const errorMsg = typeof result.error === "string" ? result.error : JSON.stringify(result.error);
7038
7061
  logger.warn(`[SourceAgent:${this.tool.name}] Tool returned error (attempt ${this.attempts}/${this.config.maxRetries}): ${errorMsg}`);
7062
+ if (this.streamBuffer.hasCallback()) {
7063
+ this.streamBuffer.write(`\u274C **Query failed:** ${errorMsg}
7064
+
7065
+ `);
7066
+ }
7039
7067
  return `\u274C ERROR: ${errorMsg}
7040
7068
 
7041
7069
  Analyze the error and try again with a corrected query.`;
7042
7070
  }
7043
7071
  resultData = result.data || [];
7044
7072
  totalRowsMatched = result.metadata?.totalCount || result.count || resultData.length;
7073
+ querySucceeded = true;
7074
+ successfulQueries++;
7045
7075
  if (this.streamBuffer.hasCallback()) {
7046
- const totalInfo = totalRowsMatched > resultData.length ? ` (${totalRowsMatched} total matched)` : "";
7047
- this.streamBuffer.write(`\u2705 Retrieved ${resultData.length} rows${totalInfo}
7076
+ const totalInfo = totalRowsMatched > resultData.length ? ` of ${totalRowsMatched} total` : "";
7077
+ this.streamBuffer.write(`\u2705 **${resultData.length} rows${totalInfo} from ${this.tool.name}**
7078
+
7079
+ `);
7080
+ if (resultData.length > 0) {
7081
+ try {
7082
+ const previewData = resultData.slice(0, this.config.maxRowsPerSource);
7083
+ this.streamBuffer.write(`<DataTable>${JSON.stringify(previewData)}</DataTable>
7048
7084
 
7049
7085
  `);
7086
+ } catch {
7087
+ this.streamBuffer.write(`_Data preview not available_
7088
+
7089
+ `);
7090
+ }
7091
+ }
7050
7092
  }
7051
7093
  const formattedResult = formatToolResultForLLM(result, {
7052
7094
  toolName: this.tool.name,
@@ -7068,19 +7110,28 @@ Analyze the error and try again with a corrected query.`;
7068
7110
  sourceType: this.extractSourceType()
7069
7111
  };
7070
7112
  const formatted = typeof formattedResult === "string" ? formattedResult : JSON.stringify(formattedResult);
7071
- return `\u2705 Query executed successfully. ${resultData.length} rows returned (${totalRowsMatched} total matched). Data is ready \u2014 do NOT call the tool again.
7113
+ const followUpNote = successfulQueries < 2 ? "You may make ONE follow-up query if this data is incomplete for the intent. Otherwise, STOP." : "Maximum queries reached. Use the data you have.";
7114
+ return `\u2705 Query executed successfully. ${resultData.length} rows returned (${totalRowsMatched} total matched). ${followUpNote}
7072
7115
 
7073
7116
  ${formatted}`;
7074
7117
  } catch (execError) {
7075
7118
  const errorMsg = execError instanceof Error ? execError.message : typeof execError === "object" && execError !== null ? execError.message || execError.error || JSON.stringify(execError) : String(execError);
7076
7119
  logger.warn(`[SourceAgent:${this.tool.name}] Tool execution failed (attempt ${this.attempts}/${this.config.maxRetries}): ${errorMsg}`);
7120
+ if (this.streamBuffer.hasCallback()) {
7121
+ this.streamBuffer.write(`\u274C **Query failed:** ${errorMsg}
7122
+
7123
+ `);
7124
+ }
7077
7125
  return `\u274C ERROR: ${errorMsg}
7078
7126
 
7079
7127
  Analyze the error and try again with a corrected query.`;
7080
7128
  }
7081
7129
  };
7082
7130
  const hasSchemaSearch = !!schemaSearchFn;
7083
- const maxIterations = this.config.maxRetries + 2 + (hasSchemaSearch ? 2 : 0);
7131
+ const queryIterations = this.config.maxRetries + 1;
7132
+ const schemaIterations = hasSchemaSearch ? 3 : 0;
7133
+ const responseIterations = 2;
7134
+ const maxIterations = queryIterations + schemaIterations + responseIterations;
7084
7135
  await LLM.streamWithTools(
7085
7136
  { sys: prompts.system, user: prompts.user },
7086
7137
  tools,
@@ -7133,7 +7184,7 @@ Analyze the error and try again with a corrected query.`;
7133
7184
  if (this.streamBuffer.hasCallback()) {
7134
7185
  this.streamBuffer.write(`
7135
7186
 
7136
- \u274C **${this.tool.name} failed:** ${errorMsg}
7187
+ \u274C **Could not retrieve data from ${this.tool.name}:** ${errorMsg}
7137
7188
 
7138
7189
  `);
7139
7190
  }
@@ -7164,26 +7215,43 @@ Analyze the error and try again with a corrected query.`;
7164
7215
  const sourceName = this.tool.name;
7165
7216
  const sourceType = this.extractSourceType();
7166
7217
  const fullSchema = this.tool.fullSchema || this.tool.description || "No schema available";
7218
+ const databaseRules = await promptLoader.loadDatabaseRulesForType(sourceType);
7219
+ const rowLimitSyntax = {
7220
+ "mssql": `Use SELECT TOP ${this.config.maxRowsPerSource} in every SELECT statement`,
7221
+ "postgres": `Add LIMIT ${this.config.maxRowsPerSource} at the end of every query`,
7222
+ "mysql": `Add LIMIT ${this.config.maxRowsPerSource} at the end of every query`,
7223
+ "excel": `Add LIMIT ${this.config.maxRowsPerSource} at the end of every query`,
7224
+ "csv": `Add LIMIT ${this.config.maxRowsPerSource} at the end of every query`
7225
+ };
7226
+ const rowLimitInstruction = rowLimitSyntax[sourceType] || `Limit results to ${this.config.maxRowsPerSource} rows`;
7167
7227
  const hasSchemaSearch = !!this.tool.schemaSearchFn;
7168
7228
  const schemaTier = this.tool.schemaTier || "";
7169
7229
  let schemaSearchInstructions = "";
7170
7230
  if (hasSchemaSearch && schemaTier === "very_large") {
7171
- schemaSearchInstructions = `## Schema Search
7231
+ schemaSearchInstructions = `## Schema Search \u2014 REQUIRED
7172
7232
  This source has a very large schema. The schema above shows only table names \u2014 no column details.
7173
- Before writing any query, you MUST use the search_schema tool to find exact table and column names.
7174
- 1. Search with keywords related to the requested data
7175
- 2. Review the returned column details and sample values carefully
7176
- 3. Then write your SQL query using the exact names from the search results
7177
- You may search multiple times with different keywords if the first search doesn't find what you need.`;
7233
+ **You MUST use search_schema BEFORE writing any query.** Do NOT guess column names.
7234
+ 1. FIRST: Call search_schema with keywords related to the requested data
7235
+ 2. Review the returned column details, types, and sample values carefully
7236
+ 3. THEN write your SQL query using the EXACT column names from the search results
7237
+ You may search multiple times with different keywords if the first search doesn't find what you need.
7238
+ **Never write a query without searching first \u2014 guessing column names wastes time on failed queries.**`;
7178
7239
  } else if (hasSchemaSearch) {
7179
- schemaSearchInstructions = `## Schema Search
7180
- This source has a large schema. The top tables are shown in detail above, but not all tables are listed with columns.
7181
- If you need data from a table that is only listed in the catalog (no columns shown), use the search_schema tool to look up its column details before writing a query.`;
7240
+ schemaSearchInstructions = `## Schema Search \u2014 REQUIRED
7241
+ This source has a large schema. The top tables are shown in detail above, but not all tables have full column details.
7242
+ **You MUST use search_schema BEFORE writing any query** to verify the exact column names for the tables you plan to query.
7243
+ 1. FIRST: Call search_schema with the table names or keywords you intend to use
7244
+ 2. Review the returned column names, types, and sample values
7245
+ 3. THEN write your SQL query using the EXACT column names from the search results
7246
+ Even if a table appears in the detailed schema above, search_schema returns sample values that help you write correct WHERE filters.
7247
+ **Never skip the search step \u2014 it prevents failed queries and retries.**`;
7182
7248
  }
7183
7249
  const prompts = await promptLoader.loadPrompts("agent-source-query", {
7184
7250
  SOURCE_NAME: sourceName,
7185
7251
  SOURCE_TYPE: sourceType,
7186
7252
  FULL_SCHEMA: fullSchema,
7253
+ DATABASE_RULES: databaseRules,
7254
+ ROW_LIMIT_SYNTAX: rowLimitInstruction,
7187
7255
  SCHEMA_SEARCH_INSTRUCTIONS: schemaSearchInstructions,
7188
7256
  MAX_ROWS: String(this.config.maxRowsPerSource),
7189
7257
  AGGREGATION_MODE: aggregation,
@@ -7238,6 +7306,10 @@ If you need data from a table that is only listed in the catalog (no columns sho
7238
7306
  required.push(key);
7239
7307
  }
7240
7308
  });
7309
+ properties["reason"] = {
7310
+ type: "string",
7311
+ description: "string (optional) - Brief reason for this query. Required for follow-up queries after a successful first query. Explain what additional data you need and why."
7312
+ };
7241
7313
  return {
7242
7314
  name: this.tool.id,
7243
7315
  description: this.tool.description || `Query ${this.tool.name}`,
@@ -7326,6 +7398,7 @@ var MainAgent = class {
7326
7398
  const tools = [...sourceToolDefs, ...directToolDefs];
7327
7399
  const sourceResults = [];
7328
7400
  const executedTools = [];
7401
+ let sourceCallCounter = 0;
7329
7402
  const toolHandler = async (toolName, toolInput) => {
7330
7403
  const externalTool = this.externalTools.find((t) => t.id === toolName);
7331
7404
  if (!externalTool) {
@@ -7340,7 +7413,15 @@ var MainAgent = class {
7340
7413
  aggregation: toolInput.aggregation || "raw"
7341
7414
  };
7342
7415
  logger.info(`[MainAgent] Dispatching SourceAgent for "${externalTool.name}" | intent: "${sourceInput.intent}"`);
7343
- const sourceAgent = new SourceAgent(externalTool, this.config, this.streamBuffer);
7416
+ sourceCallCounter++;
7417
+ const callId = `src_${sourceCallCounter}`;
7418
+ const taggedBuffer = new TaggedStreamBuffer(
7419
+ callId,
7420
+ this.streamBuffer,
7421
+ externalTool.name,
7422
+ sourceInput.intent
7423
+ );
7424
+ const sourceAgent = new SourceAgent(externalTool, this.config, taggedBuffer);
7344
7425
  const result = await sourceAgent.execute(sourceInput);
7345
7426
  sourceResults.push(result);
7346
7427
  if (result.success) {
@@ -7570,10 +7651,28 @@ Execution time: ${metadata.executionTimeMs}ms
7570
7651
  output += "No data returned.";
7571
7652
  return output;
7572
7653
  }
7573
- output += `### Results (${data.length} rows)
7654
+ const maxRowsForLLM = Math.min(data.length, 10);
7655
+ const truncatedData = data.slice(0, maxRowsForLLM).map((row) => {
7656
+ const truncatedRow = {};
7657
+ for (const [key, value] of Object.entries(row)) {
7658
+ if (typeof value === "string" && value.length > 200) {
7659
+ truncatedRow[key] = value.substring(0, 200) + "...";
7660
+ } else {
7661
+ truncatedRow[key] = value;
7662
+ }
7663
+ }
7664
+ return truncatedRow;
7665
+ });
7666
+ const truncationNote = data.length > maxRowsForLLM ? `
7667
+ (showing ${maxRowsForLLM} of ${data.length} rows)` : "";
7668
+ output += `### Results (${data.length} rows${truncationNote})
7574
7669
  `;
7575
7670
  output += "```json\n";
7576
- output += JSON.stringify(data, null, 2);
7671
+ try {
7672
+ output += JSON.stringify(truncatedData, null, 2);
7673
+ } catch {
7674
+ output += "[Data could not be serialized]";
7675
+ }
7577
7676
  output += "\n```\n";
7578
7677
  return output;
7579
7678
  }
@@ -7593,7 +7692,9 @@ var DEFAULT_AGENT_CONFIG = {
7593
7692
  sourceAgentModel: "",
7594
7693
  // will use the provider's default model
7595
7694
  maxRetries: 3,
7596
- maxIterations: 10
7695
+ // 3 retries = 4 total query attempts (1 initial + 3 retries for SQL errors)
7696
+ maxIterations: 15
7697
+ // must accommodate: schema searches (2-3) + query attempts (4) + LLM responses + final
7597
7698
  };
7598
7699
 
7599
7700
  // src/userResponse/utils/component-props-processor.ts