@superatomai/sdk-node 0.0.77 → 0.0.79

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
@@ -345,7 +345,8 @@ var UserQueryFiltersSchema = z3.object({
345
345
  username: z3.string().optional(),
346
346
  email: z3.string().optional(),
347
347
  role: z3.string().optional(),
348
- fullname: z3.string().optional()
348
+ fullname: z3.string().optional(),
349
+ id: z3.number().optional()
349
350
  });
350
351
  var UsersRequestPayloadSchema = z3.object({
351
352
  operation: z3.enum(["create", "update", "delete", "getAll", "getOne", "query"]),
@@ -695,6 +696,67 @@ var DashCompRequestMessageSchema = z3.object({
695
696
  type: z3.literal("DASH_COMP_REQ"),
696
697
  payload: DashCompRequestPayloadSchema
697
698
  });
699
+ var SchemaColumnStatisticsSchema = z3.object({
700
+ distinct: z3.number().optional(),
701
+ min: z3.number().optional(),
702
+ max: z3.number().optional()
703
+ });
704
+ var SchemaColumnSchema = z3.object({
705
+ name: z3.string(),
706
+ type: z3.string(),
707
+ nativeType: z3.string(),
708
+ nullable: z3.boolean(),
709
+ description: z3.string(),
710
+ statistics: SchemaColumnStatisticsSchema.optional(),
711
+ cardinality: z3.enum(["unique", "high", "medium", "low"]).optional(),
712
+ sampleValues: z3.array(z3.string()).optional()
713
+ });
714
+ var SchemaTableSchema = z3.object({
715
+ name: z3.string(),
716
+ fullName: z3.string(),
717
+ rowCount: z3.number(),
718
+ description: z3.string(),
719
+ columns: z3.array(SchemaColumnSchema)
720
+ });
721
+ var SchemaRelationshipSchema = z3.object({
722
+ from: z3.string(),
723
+ to: z3.string(),
724
+ type: z3.string(),
725
+ keys: z3.array(z3.string())
726
+ });
727
+ var DatabaseSchemaSchema = z3.object({
728
+ database: z3.string(),
729
+ databaseType: z3.string().optional(),
730
+ schema: z3.string(),
731
+ description: z3.string(),
732
+ tables: z3.array(SchemaTableSchema),
733
+ relationships: z3.array(SchemaRelationshipSchema).optional()
734
+ });
735
+ var SchemaRequestPayloadSchema = z3.object({
736
+ /** If true, returns the formatted documentation string in addition to raw JSON */
737
+ formatted: z3.boolean().optional()
738
+ });
739
+ var SchemaRequestMessageSchema = z3.object({
740
+ id: z3.string(),
741
+ from: MessageParticipantSchema,
742
+ type: z3.literal("SCHEMA_REQ"),
743
+ payload: SchemaRequestPayloadSchema
744
+ });
745
+ var SchemaResponsePayloadSchema = z3.object({
746
+ success: z3.boolean(),
747
+ error: z3.string().optional(),
748
+ data: z3.object({
749
+ schema: DatabaseSchemaSchema,
750
+ /** Formatted schema documentation (only if formatted: true was requested) */
751
+ formatted: z3.string().optional()
752
+ }).optional()
753
+ });
754
+ var SchemaResponseMessageSchema = z3.object({
755
+ id: z3.string(),
756
+ from: MessageParticipantSchema,
757
+ type: z3.literal("SCHEMA_RES"),
758
+ payload: SchemaResponsePayloadSchema
759
+ });
698
760
 
699
761
  // src/utils/logger.ts
700
762
  import fs from "fs";
@@ -3479,6 +3541,7 @@ You MUST respond with ONLY a valid JSON object (no markdown, no code blocks):
3479
3541
 
3480
3542
  **CRITICAL:**
3481
3543
  - Return ONLY valid JSON (no markdown code blocks, no text before/after)
3544
+ -\`componentName\`: MUST be the EXACT \`name\` field from the Available Components list. Never use a title or description as componentName.
3482
3545
  - \`componentId\`: For new components, MUST match an ID from the available components list. For updates, preserve the existing component's ID
3483
3546
  - \`isUpdate\`: Set to \`true\` if updating an existing component, \`false\` if creating new
3484
3547
  - \`dataSourceType\` indicates whether data comes from database or external tool
@@ -6476,6 +6539,7 @@ var STREAM_PREVIEW_MAX_CHARS = 200;
6476
6539
  var TOOL_TRACKING_MAX_ROWS = 5;
6477
6540
  var TOOL_TRACKING_MAX_CHARS = 200;
6478
6541
  var TOOL_TRACKING_SAMPLE_ROWS = 3;
6542
+ var DEFAULT_QUERY_LIMIT = 10;
6479
6543
  var MAX_COMPONENT_QUERY_LIMIT = 10;
6480
6544
  var EXACT_MATCH_SIMILARITY_THRESHOLD = 0.99;
6481
6545
  var DEFAULT_CONVERSATION_SIMILARITY_THRESHOLD = 0.8;
@@ -12166,6 +12230,7 @@ function formatComponentsForPrompt(components) {
12166
12230
  if (!components || components.length === 0) {
12167
12231
  return "No components available";
12168
12232
  }
12233
+ components = components.filter((c) => c.name !== "MultiComponentContainer");
12169
12234
  return components.map((comp, idx) => {
12170
12235
  const keywords = comp.keywords ? comp.keywords.join(", ") : "";
12171
12236
  const propsPreview = comp.props ? JSON.stringify(comp.props, null, 2) : "No props";
@@ -12212,7 +12277,7 @@ function sendDashCompResponse(id, res, sendMessage, clientId) {
12212
12277
  }
12213
12278
 
12214
12279
  // src/dashComp/pick-component.ts
12215
- async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, _collections, tools, dashCompModels) {
12280
+ async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApiKey, geminiApiKey, openaiApiKey, llmProviders, collections, tools, dashCompModels) {
12216
12281
  const errors = [];
12217
12282
  const availableComponentsText = formatComponentsForPrompt(components);
12218
12283
  const availableToolsText = formatToolsForPrompt(tools);
@@ -12268,7 +12333,8 @@ async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApi
12268
12333
  outputSchema: tool.outputSchema
12269
12334
  });
12270
12335
  logger.info(`[DASH_COMP_REQ] Tool ${tool.name} executed successfully`);
12271
- return JSON.stringify(result2, null, 2);
12336
+ const resultJson = JSON.stringify(result2, null, 2);
12337
+ return resultJson + "\n\n[REMINDER: The above is tool output for verification. You MUST still respond with ONLY the JSON component selection object. Do NOT summarize or describe these results.]";
12272
12338
  };
12273
12339
  const result = await LLM.streamWithTools(
12274
12340
  {
@@ -12286,16 +12352,59 @@ async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApi
12286
12352
  5
12287
12353
  // max iterations
12288
12354
  );
12289
- const jsonMatch = result.match(/\{[\s\S]*\}/);
12290
- const parsedResult = jsonMatch ? JSON.parse(jsonMatch[0]) : null;
12355
+ let jsonMatch = result.match(/\{[\s\S]*\}/);
12356
+ let parsedResult = jsonMatch ? (() => {
12357
+ try {
12358
+ return JSON.parse(jsonMatch[0]);
12359
+ } catch {
12360
+ return null;
12361
+ }
12362
+ })() : null;
12363
+ const isValidComponent = parsedResult && parsedResult.componentId && parsedResult.props;
12364
+ if (!isValidComponent && executedTools.length > 0) {
12365
+ const toolDataSummary = executedTools.map(
12366
+ (t) => `Tool "${t.name}" was called with params ${JSON.stringify(t.params)} and returned:
12367
+ ${JSON.stringify(t.result, null, 2).substring(0, 5e3)}`
12368
+ ).join("\n\n");
12369
+ const retryUserPrompt = `Original user request: ${prompt}
12370
+
12371
+ The following tool was already called and returned data:
12372
+ ${toolDataSummary}
12373
+
12374
+ Using this data, select the appropriate component and respond with ONLY the JSON component selection object. No explanation, no markdown, just JSON.`;
12375
+ const retryResult = await LLM.text(
12376
+ {
12377
+ sys: prompts.system,
12378
+ user: retryUserPrompt
12379
+ },
12380
+ {
12381
+ model,
12382
+ maxTokens: 4096,
12383
+ temperature: 0.1,
12384
+ apiKey
12385
+ }
12386
+ );
12387
+ jsonMatch = retryResult.match(/\{[\s\S]*\}/);
12388
+ parsedResult = jsonMatch ? (() => {
12389
+ try {
12390
+ return JSON.parse(jsonMatch[0]);
12391
+ } catch {
12392
+ return null;
12393
+ }
12394
+ })() : null;
12395
+ }
12291
12396
  if (!parsedResult) {
12292
12397
  errors.push("Failed to parse LLM response as JSON");
12398
+ errors.push(`LLM Response: ${result}`);
12399
+ logger.error(`[DASH_COMP_REQ] Failed to parse JSON from LLM response`);
12293
12400
  return { success: false, errors };
12294
12401
  }
12295
- logger.debug("[DASH_COMP_REQ] LLM response received");
12402
+ logger.info(`[DASH_COMP_REQ] Parsed component: ${parsedResult.componentName} (${parsedResult.componentId})`);
12296
12403
  logger.file("[DASH_COMP_REQ] LLM response:", JSON.stringify(parsedResult, null, 2));
12297
12404
  if (!parsedResult.componentId || !parsedResult.props) {
12298
12405
  errors.push("Invalid LLM response: missing componentId or props");
12406
+ errors.push(`LLM Response: ${result}`);
12407
+ logger.error(`[DASH_COMP_REQ] Invalid structure - missing componentId: ${!parsedResult.componentId}, missing props: ${!parsedResult.props}`);
12299
12408
  userPromptErrorLogger.logError("DASH_COMP_REQ", "Invalid LLM response structure", {
12300
12409
  prompt,
12301
12410
  result: parsedResult,
@@ -12306,6 +12415,7 @@ async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApi
12306
12415
  const originalComponent = components.find((c) => c.name === parsedResult.componentName);
12307
12416
  if (!originalComponent) {
12308
12417
  errors.push(`Component ${parsedResult.componentName} not found in available components`);
12418
+ errors.push(`LLM Response: ${result}`);
12309
12419
  userPromptErrorLogger.logError("DASH_COMP_REQ", "Component not found", {
12310
12420
  prompt,
12311
12421
  componentName: parsedResult.componentName,
@@ -12313,14 +12423,72 @@ async function pickComponentWithLLM(prompt, components, anthropicApiKey, groqApi
12313
12423
  });
12314
12424
  return { success: false, errors };
12315
12425
  }
12316
- const finalComponent = {
12426
+ let finalComponent = {
12317
12427
  ...originalComponent,
12318
12428
  props: {
12319
12429
  ...originalComponent.props,
12320
12430
  ...parsedResult.props
12321
12431
  }
12322
12432
  };
12433
+ if (finalComponent.props?.query) {
12434
+ const query = finalComponent.props.query;
12435
+ if (typeof query === "string") {
12436
+ finalComponent.props.query = ensureQueryLimit(query, DEFAULT_QUERY_LIMIT, MAX_COMPONENT_QUERY_LIMIT);
12437
+ } else if (query?.sql) {
12438
+ finalComponent.props.query = {
12439
+ ...query,
12440
+ sql: ensureQueryLimit(query.sql, DEFAULT_QUERY_LIMIT, MAX_COMPONENT_QUERY_LIMIT)
12441
+ };
12442
+ }
12443
+ }
12323
12444
  logger.info(`[DASH_COMP_REQ] Successfully picked component: ${finalComponent.name} (${finalComponent.type})`);
12445
+ if (finalComponent.props?.query && collections?.["database"]?.["execute"]) {
12446
+ logger.info(`[DASH_COMP_REQ] Validating query for component: ${finalComponent.name}`);
12447
+ const queryService = new QueryExecutionService({
12448
+ defaultLimit: DEFAULT_QUERY_LIMIT,
12449
+ getModelForTask: () => model,
12450
+ // Use the same model for query fixes
12451
+ getApiKey: () => apiKey,
12452
+ providerName: "DASH_COMP_REQ"
12453
+ });
12454
+ try {
12455
+ const validationResult = await queryService.validateSingleQuery(
12456
+ finalComponent,
12457
+ collections,
12458
+ apiKey
12459
+ );
12460
+ if (!validationResult.validated) {
12461
+ logger.error(`[DASH_COMP_REQ] Query validation failed for component: ${finalComponent.name}`);
12462
+ errors.push(`Query validation failed for component ${finalComponent.name}. The generated SQL query could not be executed.`);
12463
+ return {
12464
+ success: false,
12465
+ errors,
12466
+ data: {
12467
+ reasoning: parsedResult.reasoning || "Component selected but query validation failed",
12468
+ rawResponse: parsedResult
12469
+ }
12470
+ };
12471
+ }
12472
+ if (validationResult.component) {
12473
+ finalComponent = validationResult.component;
12474
+ }
12475
+ logger.info(`[DASH_COMP_REQ] Query validated successfully for component: ${finalComponent.name}`);
12476
+ } catch (validationError) {
12477
+ const validationErrorMsg = validationError instanceof Error ? validationError.message : String(validationError);
12478
+ logger.error(`[DASH_COMP_REQ] Query validation error: ${validationErrorMsg}`);
12479
+ errors.push(`Query validation error: ${validationErrorMsg}`);
12480
+ return {
12481
+ success: false,
12482
+ errors,
12483
+ data: {
12484
+ reasoning: parsedResult.reasoning || "Component selected but query validation encountered an error",
12485
+ rawResponse: parsedResult
12486
+ }
12487
+ };
12488
+ }
12489
+ } else if (finalComponent.props?.query && !collections?.["database"]?.["execute"]) {
12490
+ logger.warn(`[DASH_COMP_REQ] Skipping query validation - database execute function not available`);
12491
+ }
12324
12492
  if (parsedResult.props.query) {
12325
12493
  logger.info(`[DASH_COMP_REQ] Data source: Database query`);
12326
12494
  }
@@ -12430,12 +12598,14 @@ async function createFilterWithLLM(prompt, components, existingComponents, anthr
12430
12598
  const result = jsonMatch ? JSON.parse(jsonMatch[0]) : null;
12431
12599
  if (!result) {
12432
12600
  errors.push("Failed to parse LLM response as JSON");
12601
+ errors.push(`LLM Response: ${rawResult}`);
12433
12602
  return { success: false, errors };
12434
12603
  }
12435
12604
  logger.debug("[DASH_COMP_REQ:FILTER] LLM response received");
12436
12605
  logger.file("[DASH_COMP_REQ:FILTER] LLM response:", JSON.stringify(result, null, 2));
12437
12606
  if (!result.filterComponent) {
12438
12607
  errors.push("Invalid LLM response: missing filterComponent");
12608
+ errors.push(`LLM Response: ${rawResult}`);
12439
12609
  userPromptErrorLogger.logError("DASH_COMP_REQ:FILTER", "Invalid LLM response structure", {
12440
12610
  prompt,
12441
12611
  result,
@@ -12578,6 +12748,64 @@ async function handleDashCompRequest(data, components, sendMessage, anthropicApi
12578
12748
  logger.info(`[DASH_COMP_REQ] Response sent to client ${response.wsId || data.from?.id}`);
12579
12749
  }
12580
12750
 
12751
+ // src/handlers/schema-request.ts
12752
+ async function handleSchemaRequest(message, sendMessage) {
12753
+ const startTime = Date.now();
12754
+ try {
12755
+ const payload = message.payload;
12756
+ const formatted = payload?.formatted ?? false;
12757
+ logger.info(`[SchemaRequest] Processing schema request (formatted: ${formatted})`);
12758
+ const schemaData = schema.getSchema();
12759
+ if (!schemaData) {
12760
+ const response2 = {
12761
+ id: message.id,
12762
+ type: "SCHEMA_RES",
12763
+ from: { type: "data-agent" },
12764
+ to: message.from,
12765
+ payload: {
12766
+ success: false,
12767
+ error: "Schema not found or failed to load"
12768
+ }
12769
+ };
12770
+ sendMessage(response2);
12771
+ return;
12772
+ }
12773
+ const responseData = {
12774
+ schema: schemaData
12775
+ };
12776
+ if (formatted) {
12777
+ responseData.formatted = schema.generateSchemaDocumentation();
12778
+ }
12779
+ const executionMs = Date.now() - startTime;
12780
+ logger.info(`[SchemaRequest] Schema retrieved successfully in ${executionMs}ms`);
12781
+ const response = {
12782
+ id: message.id,
12783
+ type: "SCHEMA_RES",
12784
+ from: { type: "data-agent" },
12785
+ to: message.from,
12786
+ payload: {
12787
+ success: true,
12788
+ data: responseData
12789
+ }
12790
+ };
12791
+ sendMessage(response);
12792
+ } catch (error) {
12793
+ const errorMsg = error instanceof Error ? error.message : String(error);
12794
+ logger.error(`[SchemaRequest] Error: ${errorMsg}`);
12795
+ const response = {
12796
+ id: message.id,
12797
+ type: "SCHEMA_RES",
12798
+ from: { type: "data-agent" },
12799
+ to: message.from,
12800
+ payload: {
12801
+ success: false,
12802
+ error: errorMsg
12803
+ }
12804
+ };
12805
+ sendMessage(response);
12806
+ }
12807
+ }
12808
+
12581
12809
  // src/auth/user-manager.ts
12582
12810
  import fs7 from "fs";
12583
12811
  import path6 from "path";
@@ -13826,6 +14054,11 @@ var SuperatomSDK = class {
13826
14054
  logger.error("Failed to handle dash comp request:", error);
13827
14055
  });
13828
14056
  break;
14057
+ case "SCHEMA_REQ":
14058
+ handleSchemaRequest(parsed, (msg) => this.send(msg)).catch((error) => {
14059
+ logger.error("Failed to handle schema request:", error);
14060
+ });
14061
+ break;
13829
14062
  default:
13830
14063
  const handler = this.messageTypeHandlers.get(message.type);
13831
14064
  if (handler) {