Skip to content

Commit 81661e7

Browse files
committed
Here's a concise commit message following the Conventional Commits specification:
``` feat(config): add legacy config migration tests and handling - Add comprehensive test suite for legacy LLM config migration - Implement backward compatibility for ModelType serialization - Add recovery logic for corrupted configs - Map legacy "Others" model type to "Default" ``` The message starts with a short imperative sentence under 50 characters, followed by key bullet points highlighting the main changes. It follows the Conventional Commits format with "feat" prefix since this adds new functionality for handling legacy configurations.
1 parent 1e0e57f commit 81661e7

File tree

2 files changed

+388
-5
lines changed

2 files changed

+388
-5
lines changed

core/src/main/kotlin/cc/unitmesh/devti/llm2/model/LlmConfig.kt

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
package cc.unitmesh.devti.llm2.model
22

33
import cc.unitmesh.devti.settings.AutoDevSettingsState
4-
import kotlinx.serialization.Contextual
5-
import kotlinx.serialization.Serializable
4+
import kotlinx.serialization.*
5+
import kotlinx.serialization.descriptors.*
6+
import kotlinx.serialization.encoding.*
67
import kotlinx.serialization.json.*
78
import kotlin.text.ifEmpty
89

@@ -128,14 +129,68 @@ data class LlmConfig(
128129
}
129130

130131
val configs: List<LlmConfig> = try {
131-
Json.decodeFromString(llms)
132+
// Use a JSON configuration that's more lenient with unknown properties
133+
val json = Json {
134+
ignoreUnknownKeys = true
135+
isLenient = true
136+
}
137+
json.decodeFromString(llms)
132138
} catch (e: Exception) {
133-
throw Exception("Failed to load custom llms: $e")
139+
// Log the error but don't throw - try to recover
140+
println("Warning: Failed to load custom llms, attempting recovery: $e")
141+
142+
// Try to recover by attempting to parse individual configs
143+
try {
144+
recoverFromCorruptedConfig(llms)
145+
} catch (recoveryException: Exception) {
146+
println("Error: Could not recover from corrupted config: $recoveryException")
147+
throw Exception("Failed to load custom llms: $e")
148+
}
134149
}
135150

136151
return configs
137152
}
138153

154+
/**
155+
* Attempt to recover from corrupted configuration by parsing individual configs
156+
*/
157+
private fun recoverFromCorruptedConfig(llms: String): List<LlmConfig> {
158+
val json = Json {
159+
ignoreUnknownKeys = true
160+
isLenient = true
161+
}
162+
163+
// Try to parse as JSON array and fix individual items
164+
val jsonElement = json.parseToJsonElement(llms)
165+
if (jsonElement !is JsonArray) {
166+
return emptyList()
167+
}
168+
169+
val recoveredConfigs = mutableListOf<LlmConfig>()
170+
171+
for (element in jsonElement) {
172+
try {
173+
if (element is JsonObject) {
174+
// Check if this config has the legacy "Others" modelType and fix it
175+
val mutableElement = element.toMutableMap()
176+
val modelType = mutableElement["modelType"]
177+
if (modelType is JsonPrimitive && modelType.content == "Others") {
178+
mutableElement["modelType"] = JsonPrimitive("Default")
179+
}
180+
181+
val fixedElement = JsonObject(mutableElement)
182+
val config = json.decodeFromJsonElement<LlmConfig>(fixedElement)
183+
recoveredConfigs.add(config)
184+
}
185+
} catch (e: Exception) {
186+
println("Warning: Skipping corrupted config item: $e")
187+
// Continue with next config
188+
}
189+
}
190+
191+
return recoveredConfigs
192+
}
193+
139194
/**
140195
* Check if a model ID represents a GitHub Copilot model
141196
*/
@@ -287,7 +342,33 @@ data class LlmConfig(
287342
}
288343
}
289344

290-
@Serializable
345+
/**
346+
* Custom serializer for ModelType to handle backward compatibility with legacy "Others" type
347+
*/
348+
@Serializer(forClass = ModelType::class)
349+
object ModelTypeSerializer : KSerializer<ModelType> {
350+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("ModelType", PrimitiveKind.STRING)
351+
352+
override fun serialize(encoder: Encoder, value: ModelType) {
353+
encoder.encodeString(value.name)
354+
}
355+
356+
override fun deserialize(decoder: Decoder): ModelType {
357+
val value = decoder.decodeString()
358+
return when (value) {
359+
"Others" -> ModelType.Default // Map legacy "Others" to "Default"
360+
"Default" -> ModelType.Default
361+
"Plan" -> ModelType.Plan
362+
"Act" -> ModelType.Act
363+
"Completion" -> ModelType.Completion
364+
"Embedding" -> ModelType.Embedding
365+
"FastApply" -> ModelType.FastApply
366+
else -> ModelType.Default // Fallback to Default for any unknown types
367+
}
368+
}
369+
}
370+
371+
@Serializable(with = ModelTypeSerializer::class)
291372
enum class ModelType {
292373
Default, Plan, Act, Completion, Embedding, FastApply
293374
}

0 commit comments

Comments
 (0)