@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.js CHANGED
@@ -5082,6 +5082,38 @@ var StreamBuffer = class {
5082
5082
  this.callback = void 0;
5083
5083
  }
5084
5084
  };
5085
+ var TaggedStreamBuffer = class extends StreamBuffer {
5086
+ constructor(callId, parent, sourceName, intent) {
5087
+ super();
5088
+ this.started = false;
5089
+ this.callId = callId;
5090
+ this.parent = parent;
5091
+ this.sourceName = sourceName;
5092
+ this.intent = intent;
5093
+ }
5094
+ hasCallback() {
5095
+ return this.parent.hasCallback();
5096
+ }
5097
+ getFullText() {
5098
+ return this.parent.getFullText();
5099
+ }
5100
+ write(chunk) {
5101
+ if (!this.parent.hasCallback()) return;
5102
+ if (!this.started) {
5103
+ this.started = true;
5104
+ const escapedName = this.sourceName.replace(/\|/g, "-");
5105
+ const escapedIntent = this.intent.replace(/\|/g, " ").replace(/"/g, """).substring(0, 200);
5106
+ this.parent.write(`__SB_${this.callId}_START__${escapedName}|${escapedIntent}__SB_END__`);
5107
+ }
5108
+ this.parent.write(`__SB_${this.callId}_MSG__${chunk}__SB_END__`);
5109
+ }
5110
+ flush() {
5111
+ this.parent.flush();
5112
+ }
5113
+ dispose() {
5114
+ this.parent.flush();
5115
+ }
5116
+ };
5085
5117
  function streamDelay(ms = STREAM_DELAY_MS) {
5086
5118
  return new Promise((resolve) => setTimeout(resolve, ms));
5087
5119
  }
@@ -7010,16 +7042,6 @@ var SourceAgent = class {
7010
7042
  const startTime = Date.now();
7011
7043
  const { intent, aggregation = "raw" } = input;
7012
7044
  logger.info(`[SourceAgent:${this.tool.name}] Starting | intent: "${intent}" | aggregation: ${aggregation}`);
7013
- if (this.streamBuffer.hasCallback()) {
7014
- const sourceType = this.extractSourceType();
7015
- const sourceLabel = sourceType !== "unknown" ? ` (${sourceType})` : "";
7016
- this.streamBuffer.write(`
7017
-
7018
- \u{1F517} **Connecting to ${this.tool.name}**${sourceLabel}
7019
-
7020
- `);
7021
- await streamDelay();
7022
- }
7023
7045
  try {
7024
7046
  const prompts = await this.buildPrompt(intent, aggregation);
7025
7047
  logger.logLLMPrompt(`sourceAgent:${this.tool.name}`, "system", extractPromptText(prompts.system));
@@ -7029,38 +7051,46 @@ var SourceAgent = class {
7029
7051
  let resultData = [];
7030
7052
  let queryExecuted;
7031
7053
  let totalRowsMatched = 0;
7054
+ let querySucceeded = false;
7055
+ let successfulQueries = 0;
7032
7056
  const schemaSearchFn = this.tool.schemaSearchFn;
7033
7057
  const toolHandler = async (toolName, toolInput) => {
7034
7058
  if (toolName.endsWith("_search_schema") && schemaSearchFn) {
7035
7059
  const keywords = toolInput.keywords || [];
7036
7060
  logger.info(`[SourceAgent:${this.tool.name}] Schema search: ${keywords.join(", ")}`);
7037
7061
  if (this.streamBuffer.hasCallback()) {
7038
- this.streamBuffer.write(`\u{1F50D} **Searching schema for:** ${keywords.join(", ")}
7062
+ this.streamBuffer.write(`\u{1F50D} **Looking up tables:** ${keywords.join(", ")}
7039
7063
 
7040
7064
  `);
7041
7065
  await streamDelay();
7042
7066
  }
7043
7067
  const result = schemaSearchFn(keywords);
7044
- if (this.streamBuffer.hasCallback()) {
7045
- const matchCount = (result.match(/^• /gm) || []).length;
7046
- this.streamBuffer.write(`\u{1F4CB} Found ${matchCount} matching table(s)
7047
-
7048
- `);
7049
- }
7050
7068
  return `Schema search results:
7051
7069
 
7052
7070
  ${result}`;
7053
7071
  }
7072
+ if (successfulQueries >= 2) {
7073
+ return `\u26D4 Maximum successful queries reached (2). Use the data already retrieved.`;
7074
+ }
7054
7075
  this.attempts++;
7055
7076
  if (this.attempts > this.config.maxRetries) {
7056
7077
  throw new Error(`Max retry attempts (${this.config.maxRetries}) reached for ${this.tool.name}`);
7057
7078
  }
7058
7079
  if (this.attempts > 1 && this.streamBuffer.hasCallback()) {
7059
- this.streamBuffer.write(`
7080
+ if (querySucceeded) {
7081
+ const reason = toolInput.reason || "";
7082
+ this.streamBuffer.write(`
7083
+
7084
+ \u{1F4DD} **Follow-up query${reason ? `:** ${reason}` : "**"}
7085
+
7086
+ `);
7087
+ } else {
7088
+ this.streamBuffer.write(`
7060
7089
 
7061
- \u{1F504} **Query failed, retrying with corrected query** (attempt ${this.attempts}/${this.config.maxRetries})
7090
+ \u{1F504} **Retrying query** (attempt ${this.attempts}/${this.config.maxRetries})
7062
7091
 
7063
7092
  `);
7093
+ }
7064
7094
  await streamDelay();
7065
7095
  }
7066
7096
  const cappedInput = { ...toolInput };
@@ -7076,13 +7106,6 @@ ${result}`;
7076
7106
  ${queryDisplay}
7077
7107
  \`\`\`
7078
7108
 
7079
- `);
7080
- } else {
7081
- this.streamBuffer.write(`\u{1F4DD} **Query parameters:**
7082
- \`\`\`json
7083
- ${JSON.stringify(cappedInput, null, 2)}
7084
- \`\`\`
7085
-
7086
7109
  `);
7087
7110
  }
7088
7111
  await streamDelay();
@@ -7096,17 +7119,36 @@ ${JSON.stringify(cappedInput, null, 2)}
7096
7119
  if (result && result.error) {
7097
7120
  const errorMsg = typeof result.error === "string" ? result.error : JSON.stringify(result.error);
7098
7121
  logger.warn(`[SourceAgent:${this.tool.name}] Tool returned error (attempt ${this.attempts}/${this.config.maxRetries}): ${errorMsg}`);
7122
+ if (this.streamBuffer.hasCallback()) {
7123
+ this.streamBuffer.write(`\u274C **Query failed:** ${errorMsg}
7124
+
7125
+ `);
7126
+ }
7099
7127
  return `\u274C ERROR: ${errorMsg}
7100
7128
 
7101
7129
  Analyze the error and try again with a corrected query.`;
7102
7130
  }
7103
7131
  resultData = result.data || [];
7104
7132
  totalRowsMatched = result.metadata?.totalCount || result.count || resultData.length;
7133
+ querySucceeded = true;
7134
+ successfulQueries++;
7105
7135
  if (this.streamBuffer.hasCallback()) {
7106
- const totalInfo = totalRowsMatched > resultData.length ? ` (${totalRowsMatched} total matched)` : "";
7107
- this.streamBuffer.write(`\u2705 Retrieved ${resultData.length} rows${totalInfo}
7136
+ const totalInfo = totalRowsMatched > resultData.length ? ` of ${totalRowsMatched} total` : "";
7137
+ this.streamBuffer.write(`\u2705 **${resultData.length} rows${totalInfo} from ${this.tool.name}**
7138
+
7139
+ `);
7140
+ if (resultData.length > 0) {
7141
+ try {
7142
+ const previewData = resultData.slice(0, this.config.maxRowsPerSource);
7143
+ this.streamBuffer.write(`<DataTable>${JSON.stringify(previewData)}</DataTable>
7108
7144
 
7109
7145
  `);
7146
+ } catch {
7147
+ this.streamBuffer.write(`_Data preview not available_
7148
+
7149
+ `);
7150
+ }
7151
+ }
7110
7152
  }
7111
7153
  const formattedResult = formatToolResultForLLM(result, {
7112
7154
  toolName: this.tool.name,
@@ -7128,19 +7170,28 @@ Analyze the error and try again with a corrected query.`;
7128
7170
  sourceType: this.extractSourceType()
7129
7171
  };
7130
7172
  const formatted = typeof formattedResult === "string" ? formattedResult : JSON.stringify(formattedResult);
7131
- return `\u2705 Query executed successfully. ${resultData.length} rows returned (${totalRowsMatched} total matched). Data is ready \u2014 do NOT call the tool again.
7173
+ 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.";
7174
+ return `\u2705 Query executed successfully. ${resultData.length} rows returned (${totalRowsMatched} total matched). ${followUpNote}
7132
7175
 
7133
7176
  ${formatted}`;
7134
7177
  } catch (execError) {
7135
7178
  const errorMsg = execError instanceof Error ? execError.message : typeof execError === "object" && execError !== null ? execError.message || execError.error || JSON.stringify(execError) : String(execError);
7136
7179
  logger.warn(`[SourceAgent:${this.tool.name}] Tool execution failed (attempt ${this.attempts}/${this.config.maxRetries}): ${errorMsg}`);
7180
+ if (this.streamBuffer.hasCallback()) {
7181
+ this.streamBuffer.write(`\u274C **Query failed:** ${errorMsg}
7182
+
7183
+ `);
7184
+ }
7137
7185
  return `\u274C ERROR: ${errorMsg}
7138
7186
 
7139
7187
  Analyze the error and try again with a corrected query.`;
7140
7188
  }
7141
7189
  };
7142
7190
  const hasSchemaSearch = !!schemaSearchFn;
7143
- const maxIterations = this.config.maxRetries + 2 + (hasSchemaSearch ? 2 : 0);
7191
+ const queryIterations = this.config.maxRetries + 1;
7192
+ const schemaIterations = hasSchemaSearch ? 3 : 0;
7193
+ const responseIterations = 2;
7194
+ const maxIterations = queryIterations + schemaIterations + responseIterations;
7144
7195
  await LLM.streamWithTools(
7145
7196
  { sys: prompts.system, user: prompts.user },
7146
7197
  tools,
@@ -7193,7 +7244,7 @@ Analyze the error and try again with a corrected query.`;
7193
7244
  if (this.streamBuffer.hasCallback()) {
7194
7245
  this.streamBuffer.write(`
7195
7246
 
7196
- \u274C **${this.tool.name} failed:** ${errorMsg}
7247
+ \u274C **Could not retrieve data from ${this.tool.name}:** ${errorMsg}
7197
7248
 
7198
7249
  `);
7199
7250
  }
@@ -7224,26 +7275,43 @@ Analyze the error and try again with a corrected query.`;
7224
7275
  const sourceName = this.tool.name;
7225
7276
  const sourceType = this.extractSourceType();
7226
7277
  const fullSchema = this.tool.fullSchema || this.tool.description || "No schema available";
7278
+ const databaseRules = await promptLoader.loadDatabaseRulesForType(sourceType);
7279
+ const rowLimitSyntax = {
7280
+ "mssql": `Use SELECT TOP ${this.config.maxRowsPerSource} in every SELECT statement`,
7281
+ "postgres": `Add LIMIT ${this.config.maxRowsPerSource} at the end of every query`,
7282
+ "mysql": `Add LIMIT ${this.config.maxRowsPerSource} at the end of every query`,
7283
+ "excel": `Add LIMIT ${this.config.maxRowsPerSource} at the end of every query`,
7284
+ "csv": `Add LIMIT ${this.config.maxRowsPerSource} at the end of every query`
7285
+ };
7286
+ const rowLimitInstruction = rowLimitSyntax[sourceType] || `Limit results to ${this.config.maxRowsPerSource} rows`;
7227
7287
  const hasSchemaSearch = !!this.tool.schemaSearchFn;
7228
7288
  const schemaTier = this.tool.schemaTier || "";
7229
7289
  let schemaSearchInstructions = "";
7230
7290
  if (hasSchemaSearch && schemaTier === "very_large") {
7231
- schemaSearchInstructions = `## Schema Search
7291
+ schemaSearchInstructions = `## Schema Search \u2014 REQUIRED
7232
7292
  This source has a very large schema. The schema above shows only table names \u2014 no column details.
7233
- Before writing any query, you MUST use the search_schema tool to find exact table and column names.
7234
- 1. Search with keywords related to the requested data
7235
- 2. Review the returned column details and sample values carefully
7236
- 3. Then write your SQL query using the exact names from the search results
7237
- You may search multiple times with different keywords if the first search doesn't find what you need.`;
7293
+ **You MUST use search_schema BEFORE writing any query.** Do NOT guess column names.
7294
+ 1. FIRST: Call search_schema with keywords related to the requested data
7295
+ 2. Review the returned column details, types, and sample values carefully
7296
+ 3. THEN write your SQL query using the EXACT column names from the search results
7297
+ You may search multiple times with different keywords if the first search doesn't find what you need.
7298
+ **Never write a query without searching first \u2014 guessing column names wastes time on failed queries.**`;
7238
7299
  } else if (hasSchemaSearch) {
7239
- schemaSearchInstructions = `## Schema Search
7240
- This source has a large schema. The top tables are shown in detail above, but not all tables are listed with columns.
7241
- 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.`;
7300
+ schemaSearchInstructions = `## Schema Search \u2014 REQUIRED
7301
+ This source has a large schema. The top tables are shown in detail above, but not all tables have full column details.
7302
+ **You MUST use search_schema BEFORE writing any query** to verify the exact column names for the tables you plan to query.
7303
+ 1. FIRST: Call search_schema with the table names or keywords you intend to use
7304
+ 2. Review the returned column names, types, and sample values
7305
+ 3. THEN write your SQL query using the EXACT column names from the search results
7306
+ Even if a table appears in the detailed schema above, search_schema returns sample values that help you write correct WHERE filters.
7307
+ **Never skip the search step \u2014 it prevents failed queries and retries.**`;
7242
7308
  }
7243
7309
  const prompts = await promptLoader.loadPrompts("agent-source-query", {
7244
7310
  SOURCE_NAME: sourceName,
7245
7311
  SOURCE_TYPE: sourceType,
7246
7312
  FULL_SCHEMA: fullSchema,
7313
+ DATABASE_RULES: databaseRules,
7314
+ ROW_LIMIT_SYNTAX: rowLimitInstruction,
7247
7315
  SCHEMA_SEARCH_INSTRUCTIONS: schemaSearchInstructions,
7248
7316
  MAX_ROWS: String(this.config.maxRowsPerSource),
7249
7317
  AGGREGATION_MODE: aggregation,
@@ -7298,6 +7366,10 @@ If you need data from a table that is only listed in the catalog (no columns sho
7298
7366
  required.push(key);
7299
7367
  }
7300
7368
  });
7369
+ properties["reason"] = {
7370
+ type: "string",
7371
+ 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."
7372
+ };
7301
7373
  return {
7302
7374
  name: this.tool.id,
7303
7375
  description: this.tool.description || `Query ${this.tool.name}`,
@@ -7386,6 +7458,7 @@ var MainAgent = class {
7386
7458
  const tools = [...sourceToolDefs, ...directToolDefs];
7387
7459
  const sourceResults = [];
7388
7460
  const executedTools = [];
7461
+ let sourceCallCounter = 0;
7389
7462
  const toolHandler = async (toolName, toolInput) => {
7390
7463
  const externalTool = this.externalTools.find((t) => t.id === toolName);
7391
7464
  if (!externalTool) {
@@ -7400,7 +7473,15 @@ var MainAgent = class {
7400
7473
  aggregation: toolInput.aggregation || "raw"
7401
7474
  };
7402
7475
  logger.info(`[MainAgent] Dispatching SourceAgent for "${externalTool.name}" | intent: "${sourceInput.intent}"`);
7403
- const sourceAgent = new SourceAgent(externalTool, this.config, this.streamBuffer);
7476
+ sourceCallCounter++;
7477
+ const callId = `src_${sourceCallCounter}`;
7478
+ const taggedBuffer = new TaggedStreamBuffer(
7479
+ callId,
7480
+ this.streamBuffer,
7481
+ externalTool.name,
7482
+ sourceInput.intent
7483
+ );
7484
+ const sourceAgent = new SourceAgent(externalTool, this.config, taggedBuffer);
7404
7485
  const result = await sourceAgent.execute(sourceInput);
7405
7486
  sourceResults.push(result);
7406
7487
  if (result.success) {
@@ -7630,10 +7711,28 @@ Execution time: ${metadata.executionTimeMs}ms
7630
7711
  output += "No data returned.";
7631
7712
  return output;
7632
7713
  }
7633
- output += `### Results (${data.length} rows)
7714
+ const maxRowsForLLM = Math.min(data.length, 10);
7715
+ const truncatedData = data.slice(0, maxRowsForLLM).map((row) => {
7716
+ const truncatedRow = {};
7717
+ for (const [key, value] of Object.entries(row)) {
7718
+ if (typeof value === "string" && value.length > 200) {
7719
+ truncatedRow[key] = value.substring(0, 200) + "...";
7720
+ } else {
7721
+ truncatedRow[key] = value;
7722
+ }
7723
+ }
7724
+ return truncatedRow;
7725
+ });
7726
+ const truncationNote = data.length > maxRowsForLLM ? `
7727
+ (showing ${maxRowsForLLM} of ${data.length} rows)` : "";
7728
+ output += `### Results (${data.length} rows${truncationNote})
7634
7729
  `;
7635
7730
  output += "```json\n";
7636
- output += JSON.stringify(data, null, 2);
7731
+ try {
7732
+ output += JSON.stringify(truncatedData, null, 2);
7733
+ } catch {
7734
+ output += "[Data could not be serialized]";
7735
+ }
7637
7736
  output += "\n```\n";
7638
7737
  return output;
7639
7738
  }
@@ -7653,7 +7752,9 @@ var DEFAULT_AGENT_CONFIG = {
7653
7752
  sourceAgentModel: "",
7654
7753
  // will use the provider's default model
7655
7754
  maxRetries: 3,
7656
- maxIterations: 10
7755
+ // 3 retries = 4 total query attempts (1 initial + 3 retries for SQL errors)
7756
+ maxIterations: 15
7757
+ // must accommodate: schema searches (2-3) + query attempts (4) + LLM responses + final
7657
7758
  };
7658
7759
 
7659
7760
  // src/userResponse/utils/component-props-processor.ts