bump version to 0.4.1, sanitize API responses, and update string resources
This commit is contained in:
@@ -171,22 +171,27 @@ class ApiRequestHandler(private val apiManager: ApiManager, context: Context) {
|
||||
* Handles:
|
||||
* 1. Markdown code blocks (```json ... ```)
|
||||
* 2. Conversational wrapping ("Here is the JSON: ...")
|
||||
* 3. Raw JSON
|
||||
* 3. Think blocks (<think>...</think>)
|
||||
* 4. Raw JSON
|
||||
*/
|
||||
private fun extractJsonFromResponse(text: String): String {
|
||||
val trimmed = text.trim()
|
||||
|
||||
// 0. First, strip any <think>...</think> blocks (may contain newlines)
|
||||
val thinkRegex = Regex("<think>[\\s\\S]*?</think>")
|
||||
val textWithoutThink = thinkRegex.replace(trimmed, "").trim()
|
||||
|
||||
// 1. Try to find content within Markdown code blocks
|
||||
// This regex looks for ``` (optional json) ... content ... ```
|
||||
val codeBlockRegex = Regex("```(?:json)?\\s*([\\s\\S]*?)\\s*```", RegexOption.IGNORE_CASE)
|
||||
val match = codeBlockRegex.find(trimmed)
|
||||
val match = codeBlockRegex.find(textWithoutThink)
|
||||
if (match != null) {
|
||||
return match.groupValues[1].trim()
|
||||
}
|
||||
|
||||
// 2. If no code block is found, try to find the outermost JSON structure (Object or Array)
|
||||
val firstBrace = trimmed.indexOf('{')
|
||||
val firstBracket = trimmed.indexOf('[')
|
||||
val firstBrace = textWithoutThink.indexOf('{')
|
||||
val firstBracket = textWithoutThink.indexOf('[')
|
||||
|
||||
var startIndex = -1
|
||||
var endIndex = -1
|
||||
@@ -195,18 +200,18 @@ class ApiRequestHandler(private val apiManager: ApiManager, context: Context) {
|
||||
// We pick whichever appears first
|
||||
if (firstBrace != -1 && (firstBracket == -1 || firstBrace < firstBracket)) {
|
||||
startIndex = firstBrace
|
||||
endIndex = trimmed.lastIndexOf('}')
|
||||
endIndex = textWithoutThink.lastIndexOf('}')
|
||||
} else if (firstBracket != -1) {
|
||||
startIndex = firstBracket
|
||||
endIndex = trimmed.lastIndexOf(']')
|
||||
endIndex = textWithoutThink.lastIndexOf(']')
|
||||
}
|
||||
|
||||
if (startIndex != -1 && endIndex != -1 && endIndex > startIndex) {
|
||||
return trimmed.substring(startIndex, endIndex + 1)
|
||||
return textWithoutThink.substring(startIndex, endIndex + 1)
|
||||
}
|
||||
|
||||
// 3. Fallback: return the original text (parsing will likely fail if it's not valid JSON)
|
||||
return trimmed
|
||||
// 3. Fallback: return the text without think blocks (parsing will likely fail if it's not valid JSON)
|
||||
return textWithoutThink
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,4 +271,4 @@ class ApiResponseException(message: String, cause: Throwable? = null) : Exceptio
|
||||
/**
|
||||
* Custom exception for API validation errors.
|
||||
*/
|
||||
class ApiValidationException(message: String, cause: Throwable? = null) : Exception(message, cause)
|
||||
class ApiValidationException(message: String, cause: Throwable? = null) : Exception(message, cause)
|
||||
|
||||
@@ -174,15 +174,19 @@ object JsonCleanUtil {
|
||||
private fun isolateJsonBlock(response: String): String {
|
||||
// Handle specific non-JSON tokens first
|
||||
|
||||
// Strip any <think>...</think> blocks (may contain newlines)
|
||||
val thinkRegex = Regex("<think>[\\s\\S]*?</think>")
|
||||
val cleanedResponse = thinkRegex.replace(response, "").trim()
|
||||
|
||||
// The rest of the function operates on the cleaned response
|
||||
val markdownRegex = Regex("```json\\s*([\\s\\S]*?)\\s*```")
|
||||
val markdownMatch = markdownRegex.find(response)
|
||||
val markdownMatch = markdownRegex.find(cleanedResponse)
|
||||
if (markdownMatch != null && markdownMatch.groupValues.size > 1) {
|
||||
return markdownMatch.groupValues[1]
|
||||
}
|
||||
|
||||
val firstBrace = response.indexOf('{')
|
||||
val firstBracket = response.indexOf('[')
|
||||
val firstBrace = cleanedResponse.indexOf('{')
|
||||
val firstBracket = cleanedResponse.indexOf('[')
|
||||
val startIndex = when {
|
||||
firstBrace == -1 -> firstBracket
|
||||
firstBracket == -1 -> firstBrace
|
||||
@@ -190,12 +194,12 @@ object JsonCleanUtil {
|
||||
}
|
||||
if (startIndex == -1) return ""
|
||||
|
||||
val lastBrace = response.lastIndexOf('}')
|
||||
val lastBracket = response.lastIndexOf(']')
|
||||
val lastBrace = cleanedResponse.lastIndexOf('}')
|
||||
val lastBracket = cleanedResponse.lastIndexOf(']')
|
||||
val endIndex = maxOf(lastBrace, lastBracket)
|
||||
if (endIndex == -1 || startIndex >= endIndex) return ""
|
||||
|
||||
return response.substring(startIndex, endIndex + 1)
|
||||
return cleanedResponse.substring(startIndex, endIndex + 1)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -289,4 +293,4 @@ fun formatJsonForDisplay(json: String): String {
|
||||
} catch (_: Exception) {
|
||||
json // Fallback to raw JSON if formatting fails
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -537,7 +537,7 @@ class DictionaryViewModel @Inject constructor(
|
||||
showErrorMessage(
|
||||
getApplication<Application>().getString(
|
||||
R.string.text_failed_to_fetch_manifest,
|
||||
e.message
|
||||
// e.message Let's not include that part here, since it would show the server IP to the user
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user