JsonText.java
package org.egothor.methodatlas.ai;
/**
* Utility methods for extracting JSON fragments from free-form text produced by
* AI model responses.
*
* <p>
* Some AI providers may return textual responses that contain additional
* commentary or formatting around the actual JSON payload requested by the
* application. This helper provides a minimal extraction mechanism that
* isolates the first JSON object found within such responses so that it can be
* deserialized safely.
* </p>
*
* <p>
* The current implementation performs a simple structural search for the first
* opening brace (<code>{</code>) and the last closing brace (<code>}</code>),
* and returns the substring spanning those positions.
* </p>
*
* <p>
* The method is intentionally tolerant of provider-specific output formats and
* is primarily used as a defensive measure to recover valid JSON payloads from
* otherwise well-formed responses.
* </p>
*
* <p>
* This class is a non-instantiable utility holder.
* </p>
*
* @see AiSuggestionException
* @see AiClassSuggestion
*/
public final class JsonText {
/**
* Prevents instantiation of this utility class.
*/
private JsonText() {
}
/**
* Extracts the first JSON object found within a text response.
*
* <p>
* The method scans the supplied text for the first occurrence of an opening
* brace (<code>{</code>) and the last occurrence of a closing brace
* (<code>}</code>). The substring between these positions (inclusive) is
* returned as the extracted JSON object.
* </p>
*
* <p>
* This approach allows the application to recover structured data even when the
* model returns additional natural-language content or formatting around the
* JSON payload.
* </p>
*
* @param text text returned by the AI model
* @return extracted JSON object as text
*
* @throws AiSuggestionException if the input text is empty or if no valid JSON
* object boundaries can be located
*/
public static String extractFirstJsonObject(String text) throws AiSuggestionException {
if (text == null || text.isBlank()) {
throw new AiSuggestionException("Model returned an empty response");
}
int start = text.indexOf('{');
int end = text.lastIndexOf('}');
if (start < 0 || end < 0 || end < start) {
throw new AiSuggestionException("Model response does not contain a JSON object: " + text);
}
return text.substring(start, end + 1);
}
}