|
1 | 1 | package cc.unitmesh.devti.llm2.model
|
2 | 2 |
|
3 | 3 | 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.* |
6 | 7 | import kotlinx.serialization.json.*
|
7 | 8 | import kotlin.text.ifEmpty
|
8 | 9 |
|
@@ -128,14 +129,68 @@ data class LlmConfig(
|
128 | 129 | }
|
129 | 130 |
|
130 | 131 | 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) |
132 | 138 | } 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 | + } |
134 | 149 | }
|
135 | 150 |
|
136 | 151 | return configs
|
137 | 152 | }
|
138 | 153 |
|
| 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 | + |
139 | 194 | /**
|
140 | 195 | * Check if a model ID represents a GitHub Copilot model
|
141 | 196 | */
|
@@ -287,7 +342,33 @@ data class LlmConfig(
|
287 | 342 | }
|
288 | 343 | }
|
289 | 344 |
|
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) |
291 | 372 | enum class ModelType {
|
292 | 373 | Default, Plan, Act, Completion, Embedding, FastApply
|
293 | 374 | }
|
0 commit comments