@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 +145 -44
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +145 -44
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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} **
|
|
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
|
-
|
|
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} **
|
|
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 ? `
|
|
7047
|
-
this.streamBuffer.write(`\u2705
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
7174
|
-
1.
|
|
7175
|
-
2. Review the returned column details and sample values carefully
|
|
7176
|
-
3.
|
|
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
|
|
7181
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|