@probelabs/probe 0.6.0-rc61 → 0.6.0-rc63

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.
@@ -25,6 +25,13 @@ import {
25
25
  clearToolExecutionData
26
26
  } from './probeTool.js';
27
27
  import { listFilesByLevel } from '../index.js';
28
+ import {
29
+ cleanSchemaResponse,
30
+ isJsonSchema,
31
+ validateJsonResponse,
32
+ createJsonCorrectionPrompt,
33
+ validateAndFixMermaidResponse
34
+ } from './schemaUtils.js';
28
35
 
29
36
  // Maximum tool iterations to prevent infinite loops - configurable via MAX_TOOL_ITERATIONS env var
30
37
  const MAX_TOOL_ITERATIONS = parseInt(process.env.MAX_TOOL_ITERATIONS || '30', 10);
@@ -421,15 +428,25 @@ When troubleshooting:
421
428
  * Answer a question using the agentic flow
422
429
  * @param {string} message - The user's question
423
430
  * @param {Array} [images] - Optional array of image data (base64 strings or URLs)
424
- * @param {Object} [options] - Additional options
425
- * @param {string} [options.schema] - Output schema (if provided, adds extra iterations for formatting/validation)
431
+ * @param {Object|string} [schemaOrOptions] - Can be either:
432
+ * - A string: JSON schema for structured output (backwards compatible)
433
+ * - An object: Options object with schema and other options
434
+ * @param {string} [schemaOrOptions.schema] - JSON schema string for structured output
426
435
  * @returns {Promise<string>} - The final answer
427
436
  */
428
- async answer(message, images = [], options = {}) {
437
+ async answer(message, images = [], schemaOrOptions = {}) {
429
438
  if (!message || typeof message !== 'string' || message.trim().length === 0) {
430
439
  throw new Error('Message is required and must be a non-empty string');
431
440
  }
432
441
 
442
+ // Handle backwards compatibility - if third argument is a string, treat it as schema
443
+ let options = {};
444
+ if (typeof schemaOrOptions === 'string') {
445
+ options = { schema: schemaOrOptions };
446
+ } else {
447
+ options = schemaOrOptions || {};
448
+ }
449
+
433
450
  try {
434
451
  // Generate system message
435
452
  const systemMessage = await this.getSystemMessage();
@@ -704,6 +721,88 @@ When troubleshooting:
704
721
  // Update token counter with final history
705
722
  this.tokenCounter.updateHistory(this.history);
706
723
 
724
+ // Schema handling - format response according to provided schema
725
+ if (options.schema && !options._schemaFormatted) {
726
+ if (this.debug) {
727
+ console.log('[DEBUG] Schema provided, applying automatic formatting...');
728
+ }
729
+
730
+ try {
731
+ // Step 1: Make a follow-up call to format according to schema
732
+ const schemaPrompt = `Now you need to respond according to this schema:\n\n${options.schema}\n\nPlease reformat your previous response to match this schema exactly. Only return the formatted response, no additional text.`;
733
+
734
+ // Call answer recursively with _schemaFormatted flag to prevent infinite loop
735
+ finalResult = await this.answer(schemaPrompt, [], {
736
+ ...options,
737
+ _schemaFormatted: true
738
+ });
739
+
740
+ // Step 2: Clean the response (remove code blocks)
741
+ finalResult = cleanSchemaResponse(finalResult);
742
+
743
+ // Step 3: Validate and fix Mermaid diagrams if present
744
+ try {
745
+ const mermaidValidation = await validateAndFixMermaidResponse(finalResult, {
746
+ debug: this.debug,
747
+ path: this.allowedFolders[0],
748
+ provider: this.clientApiProvider,
749
+ model: this.model
750
+ });
751
+
752
+ if (mermaidValidation.wasFixed) {
753
+ finalResult = mermaidValidation.fixedResponse;
754
+ if (this.debug) {
755
+ console.log(`[DEBUG] Mermaid diagrams fixed`);
756
+ if (mermaidValidation.fixingResults) {
757
+ mermaidValidation.fixingResults.forEach((fixResult, index) => {
758
+ if (fixResult.wasFixed) {
759
+ console.log(`[DEBUG] Fixed diagram ${index + 1}: ${fixResult.originalError}`);
760
+ }
761
+ });
762
+ }
763
+ }
764
+ }
765
+ } catch (error) {
766
+ if (this.debug) {
767
+ console.log(`[DEBUG] Mermaid validation failed: ${error.message}`);
768
+ }
769
+ }
770
+
771
+ // Step 4: Validate and potentially correct JSON responses
772
+ if (isJsonSchema(options.schema)) {
773
+ const validation = validateJsonResponse(finalResult);
774
+
775
+ if (!validation.isValid) {
776
+ if (this.debug) {
777
+ console.log('[DEBUG] JSON validation failed:', validation.error);
778
+ }
779
+
780
+ // Attempt correction once
781
+ const correctionPrompt = createJsonCorrectionPrompt(
782
+ finalResult,
783
+ options.schema,
784
+ validation.error
785
+ );
786
+
787
+ finalResult = await this.answer(correctionPrompt, [], {
788
+ ...options,
789
+ _schemaFormatted: true
790
+ });
791
+ finalResult = cleanSchemaResponse(finalResult);
792
+
793
+ // Final validation
794
+ const finalValidation = validateJsonResponse(finalResult);
795
+ if (!finalValidation.isValid && this.debug) {
796
+ console.log('[DEBUG] JSON still invalid after correction:', finalValidation.error);
797
+ }
798
+ }
799
+ }
800
+ } catch (error) {
801
+ console.error('[ERROR] Schema formatting failed:', error);
802
+ // Return the original result if schema formatting fails
803
+ }
804
+ }
805
+
707
806
  return finalResult;
708
807
 
709
808
  } catch (error) {