@superatomai/sdk-node 0.0.58 → 0.0.60

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.d.mts CHANGED
@@ -2097,7 +2097,7 @@ declare abstract class BaseLLM {
2097
2097
  * @param componentStreamCallback - Optional callback to stream primary KPI component as soon as it's identified
2098
2098
  * @returns Object containing matched components, layout title/description, and follow-up actions
2099
2099
  */
2100
- matchComponentsFromAnalysis(analysisContent: string, components: Component[], apiKey?: string, logCollector?: any, componentStreamCallback?: (component: Component) => void, deferredTools?: any[], executedTools?: any[]): Promise<{
2100
+ matchComponentsFromAnalysis(analysisContent: string, components: Component[], apiKey?: string, logCollector?: any, componentStreamCallback?: (component: Component) => void, deferredTools?: any[], executedTools?: any[], collections?: any, userId?: string, userPrompt?: string): Promise<{
2101
2101
  components: Component[];
2102
2102
  layoutTitle: string;
2103
2103
  layoutDescription: string;
package/dist/index.d.ts CHANGED
@@ -2097,7 +2097,7 @@ declare abstract class BaseLLM {
2097
2097
  * @param componentStreamCallback - Optional callback to stream primary KPI component as soon as it's identified
2098
2098
  * @returns Object containing matched components, layout title/description, and follow-up actions
2099
2099
  */
2100
- matchComponentsFromAnalysis(analysisContent: string, components: Component[], apiKey?: string, logCollector?: any, componentStreamCallback?: (component: Component) => void, deferredTools?: any[], executedTools?: any[]): Promise<{
2100
+ matchComponentsFromAnalysis(analysisContent: string, components: Component[], apiKey?: string, logCollector?: any, componentStreamCallback?: (component: Component) => void, deferredTools?: any[], executedTools?: any[], collections?: any, userId?: string, userPrompt?: string): Promise<{
2101
2101
  components: Component[];
2102
2102
  layoutTitle: string;
2103
2103
  layoutDescription: string;
package/dist/index.js CHANGED
@@ -1642,6 +1642,7 @@ __export(utils_exports, {
1642
1642
  fixScalarSubqueries: () => fixScalarSubqueries,
1643
1643
  getDatabaseType: () => getDatabaseType,
1644
1644
  getJsonSizeInBytes: () => getJsonSizeInBytes,
1645
+ validateAndFixSqlQuery: () => validateAndFixSqlQuery,
1645
1646
  validateMessageSize: () => validateMessageSize
1646
1647
  });
1647
1648
  function getDatabaseType() {
@@ -1817,6 +1818,122 @@ function validateMessageSize(message, maxSize = 1048576) {
1817
1818
  maxSize
1818
1819
  };
1819
1820
  }
1821
+ function validateAndFixSqlQuery(query, dbType) {
1822
+ if (!query || query.trim().length === 0) {
1823
+ return { query, fixed: false, fixes: [] };
1824
+ }
1825
+ const databaseType = dbType || getDatabaseType();
1826
+ const _isMssql = databaseType === "mssql";
1827
+ let fixedQuery = query;
1828
+ const fixes = [];
1829
+ const aggregateFunctions = ["SUM", "COUNT", "AVG", "MIN", "MAX"];
1830
+ const aggregateInWherePattern = new RegExp(
1831
+ `\\bWHERE\\b[^]*?\\b(${aggregateFunctions.join("|")})\\s*\\([^)]*\\)\\s*(?:=|>|<|>=|<=|<>|!=)`,
1832
+ "i"
1833
+ );
1834
+ if (aggregateInWherePattern.test(fixedQuery)) {
1835
+ const aggregateConditionPattern = new RegExp(
1836
+ `\\s+(AND|OR)\\s+((?:COALESCE\\s*\\(\\s*)?(?:${aggregateFunctions.join("|")})\\s*\\([^)]+\\)[^)]*(?:\\))?\\s*(?:=|>|<|>=|<=|<>|!=)\\s*[^\\s,)]+)`,
1837
+ "gi"
1838
+ );
1839
+ const aggregateConditions = [];
1840
+ let match;
1841
+ while ((match = aggregateConditionPattern.exec(fixedQuery)) !== null) {
1842
+ aggregateConditions.push(match[2].trim());
1843
+ }
1844
+ if (aggregateConditions.length > 0) {
1845
+ fixedQuery = fixedQuery.replace(aggregateConditionPattern, "");
1846
+ fixedQuery = fixedQuery.replace(/\bWHERE\s+(AND|OR)\s+/gi, "WHERE ");
1847
+ fixedQuery = fixedQuery.replace(/\bWHERE\s+(GROUP\s+BY|ORDER\s+BY|HAVING|$)/gi, "$1");
1848
+ const hasGroupBy = /\bGROUP\s+BY\b/i.test(fixedQuery);
1849
+ if (hasGroupBy) {
1850
+ const hasHaving = /\bHAVING\b/i.test(fixedQuery);
1851
+ const havingClause = aggregateConditions.join(" AND ");
1852
+ if (hasHaving) {
1853
+ fixedQuery = fixedQuery.replace(
1854
+ /\bHAVING\b/i,
1855
+ `HAVING ${havingClause} AND`
1856
+ );
1857
+ } else {
1858
+ const orderByMatch = fixedQuery.match(/(\s+ORDER\s+BY\s+)/i);
1859
+ if (orderByMatch) {
1860
+ fixedQuery = fixedQuery.replace(
1861
+ orderByMatch[1],
1862
+ ` HAVING ${havingClause}${orderByMatch[1]}`
1863
+ );
1864
+ } else {
1865
+ fixedQuery = fixedQuery.trimEnd() + ` HAVING ${havingClause}`;
1866
+ }
1867
+ }
1868
+ fixes.push(`Moved aggregate condition(s) from WHERE to HAVING: ${havingClause}`);
1869
+ }
1870
+ }
1871
+ }
1872
+ let openCount = 0;
1873
+ let closeCount = 0;
1874
+ let inString = false;
1875
+ let stringChar = "";
1876
+ for (let i = 0; i < fixedQuery.length; i++) {
1877
+ const char = fixedQuery[i];
1878
+ const prevChar = i > 0 ? fixedQuery[i - 1] : "";
1879
+ if ((char === "'" || char === '"') && prevChar !== "\\") {
1880
+ if (!inString) {
1881
+ inString = true;
1882
+ stringChar = char;
1883
+ } else if (char === stringChar) {
1884
+ if (i + 1 < fixedQuery.length && fixedQuery[i + 1] === char) {
1885
+ i++;
1886
+ } else {
1887
+ inString = false;
1888
+ }
1889
+ }
1890
+ continue;
1891
+ }
1892
+ if (!inString) {
1893
+ if (char === "(") openCount++;
1894
+ else if (char === ")") closeCount++;
1895
+ }
1896
+ }
1897
+ if (openCount > closeCount) {
1898
+ const missingClose = openCount - closeCount;
1899
+ fixedQuery = fixedQuery.trimEnd();
1900
+ const hasSemicolon = fixedQuery.endsWith(";");
1901
+ if (hasSemicolon) {
1902
+ fixedQuery = fixedQuery.slice(0, -1);
1903
+ }
1904
+ fixedQuery = fixedQuery + ")".repeat(missingClose);
1905
+ if (hasSemicolon) {
1906
+ fixedQuery = fixedQuery + ";";
1907
+ }
1908
+ fixes.push(`Added ${missingClose} missing closing parenthesis(es)`);
1909
+ }
1910
+ const subqueryOrderByPattern = /\(\s*SELECT\s+(?!TOP\s)([^()]*?)\s+ORDER\s+BY\s+[^()]+\)/gi;
1911
+ let subqueryMatch;
1912
+ let subqueryFixed = false;
1913
+ while ((subqueryMatch = subqueryOrderByPattern.exec(fixedQuery)) !== null) {
1914
+ const fullMatch = subqueryMatch[0];
1915
+ if (!/\bTOP\s+\d+/i.test(fullMatch) && !/\bOFFSET\b/i.test(fullMatch)) {
1916
+ const fixedSubquery = fullMatch.replace(
1917
+ /\(\s*SELECT\s+/i,
1918
+ "(SELECT TOP 100 "
1919
+ );
1920
+ fixedQuery = fixedQuery.replace(fullMatch, fixedSubquery);
1921
+ subqueryFixed = true;
1922
+ }
1923
+ }
1924
+ if (subqueryFixed) {
1925
+ fixes.push("Added TOP to subquery with ORDER BY (MSSQL requirement)");
1926
+ }
1927
+ const originalLength = fixedQuery.length;
1928
+ fixedQuery = fixedQuery.replace(/\s+/g, " ").trim();
1929
+ if (fixedQuery.length !== originalLength) {
1930
+ }
1931
+ return {
1932
+ query: fixedQuery,
1933
+ fixed: fixes.length > 0,
1934
+ fixes
1935
+ };
1936
+ }
1820
1937
  var init_utils = __esm({
1821
1938
  "src/userResponse/utils.ts"() {
1822
1939
  "use strict";
@@ -5955,7 +6072,7 @@ var BaseLLM = class {
5955
6072
  * @param componentStreamCallback - Optional callback to stream primary KPI component as soon as it's identified
5956
6073
  * @returns Object containing matched components, layout title/description, and follow-up actions
5957
6074
  */
5958
- async matchComponentsFromAnalysis(analysisContent, components, apiKey, logCollector, componentStreamCallback, deferredTools, executedTools) {
6075
+ async matchComponentsFromAnalysis(analysisContent, components, apiKey, logCollector, componentStreamCallback, deferredTools, executedTools, collections, userId, userPrompt) {
5959
6076
  const methodStartTime = Date.now();
5960
6077
  const methodName = "matchComponentsFromAnalysis";
5961
6078
  logger.info(`[${this.getProviderName()}] [TIMING] START ${methodName} | model: ${this.getModelForTask("complex")}`);
@@ -6018,6 +6135,16 @@ ${fieldsText}`;
6018
6135
  }
6019
6136
  const schemaDoc = schema.generateSchemaDocumentation();
6020
6137
  const databaseRules = await promptLoader.loadDatabaseRules();
6138
+ let knowledgeBaseContext = "No additional knowledge base context available.";
6139
+ if (collections) {
6140
+ const kbResult = await knowledge_base_default.getAllKnowledgeBase({
6141
+ prompt: userPrompt || analysisContent,
6142
+ collections,
6143
+ userId,
6144
+ topK: 3
6145
+ });
6146
+ knowledgeBaseContext = kbResult.combinedContext || knowledgeBaseContext;
6147
+ }
6021
6148
  const prompts = await promptLoader.loadPrompts("match-text-components", {
6022
6149
  ANALYSIS_CONTENT: analysisContent,
6023
6150
  AVAILABLE_COMPONENTS: availableComponentsText,
@@ -6025,11 +6152,27 @@ ${fieldsText}`;
6025
6152
  DATABASE_RULES: databaseRules,
6026
6153
  DEFERRED_TOOLS: deferredToolsText,
6027
6154
  EXECUTED_TOOLS: executedToolsText,
6155
+ KNOWLEDGE_BASE_CONTEXT: knowledgeBaseContext,
6028
6156
  CURRENT_DATETIME: getCurrentDateTimeForPrompt()
6029
6157
  });
6030
6158
  logger.debug(`[${this.getProviderName()}] Loaded match-text-components prompts`);
6031
- const systemPromptStr = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
6032
- logger.logLLMPrompt("matchComponentsFromAnalysis", "system", systemPromptStr);
6159
+ const extractPromptText = (content) => {
6160
+ if (typeof content === "string") return content;
6161
+ if (Array.isArray(content)) {
6162
+ return content.map((item) => {
6163
+ if (typeof item === "string") return item;
6164
+ if (item && typeof item.text === "string") return item.text;
6165
+ if (item && item.content && typeof item.content === "string") return item.content;
6166
+ return JSON.stringify(item, null, 2);
6167
+ }).join("\n\n---\n\n");
6168
+ }
6169
+ if (content && typeof content === "object") {
6170
+ if (typeof content.text === "string") return content.text;
6171
+ return JSON.stringify(content, null, 2);
6172
+ }
6173
+ return String(content);
6174
+ };
6175
+ logger.logLLMPrompt("matchComponentsFromAnalysis", "system", extractPromptText(prompts.system));
6033
6176
  logger.logLLMPrompt("matchComponentsFromAnalysis", "user", `Text Analysis:
6034
6177
  ${analysisContent}
6035
6178
 
@@ -6254,6 +6397,18 @@ ${executedToolsText}`);
6254
6397
  cleanedProps.query = null;
6255
6398
  }
6256
6399
  }
6400
+ if (cleanedProps.query) {
6401
+ const queryStr = typeof cleanedProps.query === "string" ? cleanedProps.query : cleanedProps.query?.sql || "";
6402
+ const { query: fixedQuery, fixed, fixes } = validateAndFixSqlQuery(queryStr);
6403
+ if (fixed) {
6404
+ logger.warn(`[${this.getProviderName()}] SQL fixes applied to component query: ${fixes.join("; ")}`);
6405
+ if (typeof cleanedProps.query === "string") {
6406
+ cleanedProps.query = fixedQuery;
6407
+ } else if (cleanedProps.query?.sql) {
6408
+ cleanedProps.query.sql = fixedQuery;
6409
+ }
6410
+ }
6411
+ }
6257
6412
  if (cleanedProps.query && cleanedProps.externalTool) {
6258
6413
  logger.info(`[${this.getProviderName()}] Both query and externalTool exist, keeping both - frontend will decide`);
6259
6414
  }
@@ -6310,10 +6465,24 @@ ${executedToolsText}`);
6310
6465
  SCHEMA_DOC: schemaDoc || "No database schema available",
6311
6466
  CURRENT_DATETIME: getCurrentDateTimeForPrompt()
6312
6467
  });
6313
- const systemPrompt = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
6314
- const userPromptText = Array.isArray(prompts.user) ? prompts.user.join("\n") : prompts.user;
6315
- logger.logLLMPrompt("classifyQuestionCategory", "system", systemPrompt);
6316
- logger.logLLMPrompt("classifyQuestionCategory", "user", userPromptText);
6468
+ const extractTextContent = (content) => {
6469
+ if (typeof content === "string") return content;
6470
+ if (Array.isArray(content)) {
6471
+ return content.map((item) => {
6472
+ if (typeof item === "string") return item;
6473
+ if (item && typeof item.text === "string") return item.text;
6474
+ if (item && item.content && typeof item.content === "string") return item.content;
6475
+ return JSON.stringify(item, null, 2);
6476
+ }).join("\n\n---\n\n");
6477
+ }
6478
+ if (content && typeof content === "object") {
6479
+ if (typeof content.text === "string") return content.text;
6480
+ return JSON.stringify(content, null, 2);
6481
+ }
6482
+ return String(content);
6483
+ };
6484
+ logger.logLLMPrompt("classifyQuestionCategory", "system", extractTextContent(prompts.system));
6485
+ logger.logLLMPrompt("classifyQuestionCategory", "user", extractTextContent(prompts.user));
6317
6486
  const result = await LLM.stream(
6318
6487
  {
6319
6488
  sys: prompts.system,
@@ -6529,10 +6698,24 @@ ${executedToolsText}`);
6529
6698
  AVAILABLE_EXTERNAL_TOOLS: availableToolsDoc,
6530
6699
  CURRENT_DATETIME: getCurrentDateTimeForPrompt()
6531
6700
  });
6532
- const sysPrompt = Array.isArray(prompts.system) ? prompts.system.join("\n") : prompts.system;
6533
- const usrPrompt = Array.isArray(prompts.user) ? prompts.user.join("\n") : prompts.user;
6534
- logger.logLLMPrompt("generateTextResponse", "system", sysPrompt);
6535
- logger.logLLMPrompt("generateTextResponse", "user", usrPrompt);
6701
+ const extractText = (content) => {
6702
+ if (typeof content === "string") return content;
6703
+ if (Array.isArray(content)) {
6704
+ return content.map((item) => {
6705
+ if (typeof item === "string") return item;
6706
+ if (item && typeof item.text === "string") return item.text;
6707
+ if (item && item.content && typeof item.content === "string") return item.content;
6708
+ return JSON.stringify(item, null, 2);
6709
+ }).join("\n\n---\n\n");
6710
+ }
6711
+ if (content && typeof content === "object") {
6712
+ if (typeof content.text === "string") return content.text;
6713
+ return JSON.stringify(content, null, 2);
6714
+ }
6715
+ return String(content);
6716
+ };
6717
+ logger.logLLMPrompt("generateTextResponse", "system", extractText(prompts.system));
6718
+ logger.logLLMPrompt("generateTextResponse", "user", extractText(prompts.user));
6536
6719
  logger.debug(`[${this.getProviderName()}] Loaded text-response prompts with schema`);
6537
6720
  logger.debug(`[${this.getProviderName()}] System prompt length: ${prompts.system.length}, User prompt length: ${prompts.user.length}`);
6538
6721
  logCollector?.info("Generating text response with query execution capability...");
@@ -6565,7 +6748,13 @@ ${executedToolsText}`);
6565
6748
  (t) => t.executionType === "immediate" || t.executionType === "deferred" && t.userProvidedData
6566
6749
  );
6567
6750
  logger.info(`[${this.getProviderName()}] Executable tools: ${executableTools.length} of ${externalTools.length} total`);
6751
+ const addedToolIds = /* @__PURE__ */ new Set();
6568
6752
  executableTools.forEach((tool) => {
6753
+ if (addedToolIds.has(tool.id)) {
6754
+ logger.info(`[${this.getProviderName()}] Skipping duplicate tool definition: ${tool.id} (already added)`);
6755
+ return;
6756
+ }
6757
+ addedToolIds.add(tool.id);
6569
6758
  logger.info(`[${this.getProviderName()}] Processing executable tool:`, JSON.stringify(tool, null, 2));
6570
6759
  const properties = {};
6571
6760
  const required = [];
@@ -6635,7 +6824,7 @@ ${executedToolsText}`);
6635
6824
  input_schema: inputSchema
6636
6825
  });
6637
6826
  });
6638
- logger.info(`[${this.getProviderName()}] Added ${executableTools.length} executable tools to tool calling capability (${externalTools.length - executableTools.length} deferred tools await form input)`);
6827
+ logger.info(`[${this.getProviderName()}] Added ${addedToolIds.size} unique tool definitions from ${executableTools.length} tool calls (${externalTools.length - executableTools.length} deferred tools await form input)`);
6639
6828
  logger.info(`[${this.getProviderName()}] Complete tools array:`, JSON.stringify(tools, null, 2));
6640
6829
  }
6641
6830
  const queryAttempts = /* @__PURE__ */ new Map();
@@ -7056,7 +7245,10 @@ ${errorMsg}
7056
7245
  logCollector,
7057
7246
  componentStreamCallback,
7058
7247
  deferredTools,
7059
- executedToolsList
7248
+ executedToolsList,
7249
+ collections,
7250
+ userId,
7251
+ userPrompt
7060
7252
  );
7061
7253
  matchedComponents = matchResult.components;
7062
7254
  layoutTitle = matchResult.layoutTitle;
@@ -7139,7 +7331,6 @@ ${errorMsg}
7139
7331
  logger.logLLMPrompt("handleUserRequest", "user", `User Prompt: ${userPrompt}`);
7140
7332
  try {
7141
7333
  logger.info(`[${this.getProviderName()}] Step 1: Searching previous conversations...`);
7142
- logCollector?.info("Step 1: Searching for similar previous conversations...");
7143
7334
  const conversationMatch = await conversation_search_default.searchConversationsWithReranking({
7144
7335
  userPrompt,
7145
7336
  collections,