@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.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} **
|
|
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
|
-
|
|
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} **
|
|
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 ? `
|
|
7107
|
-
this.streamBuffer.write(`\u2705
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
7234
|
-
1.
|
|
7235
|
-
2. Review the returned column details and sample values carefully
|
|
7236
|
-
3.
|
|
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
|
|
7241
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|