From 3ff9c8281a42fac9d033c4860e50635399b5006b Mon Sep 17 00:00:00 2001 From: kami Date: Sun, 12 Jul 2026 12:49:46 +0400 Subject: [PATCH] fix(inference): single ContentNegotiation config + firstOrNull on empty choices --- .../llama/cpp/LlamaCppInferenceProvider.kt | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/infrastructure/inference/llama_cpp/src/main/kotlin/com/correx/infrastructure/inference/llama/cpp/LlamaCppInferenceProvider.kt b/infrastructure/inference/llama_cpp/src/main/kotlin/com/correx/infrastructure/inference/llama/cpp/LlamaCppInferenceProvider.kt index f0712812..54ba0906 100644 --- a/infrastructure/inference/llama_cpp/src/main/kotlin/com/correx/infrastructure/inference/llama/cpp/LlamaCppInferenceProvider.kt +++ b/infrastructure/inference/llama_cpp/src/main/kotlin/com/correx/infrastructure/inference/llama/cpp/LlamaCppInferenceProvider.kt @@ -42,22 +42,14 @@ private fun defaultHttpClient(): HttpClient = HttpClient(CIO) { Json { ignoreUnknownKeys = true isLenient = true - encodeDefaults = false + explicitNulls = false + encodeDefaults = true }, ) } install(HttpTimeout) { requestTimeoutMillis = DEFAULT_REQUEST_TIMEOUT_MS } - install(ContentNegotiation) { - json( - Json { - ignoreUnknownKeys = true - explicitNulls = false - encodeDefaults = true - }, - ) - } } private val json = Json { ignoreUnknownKeys = true @@ -199,7 +191,9 @@ class LlamaCppInferenceProvider( log.debug("got response from llm: {}", response) - val message = response.choices.first().message + val choice = response.choices.firstOrNull() + ?: error("llama-server returned no choices in the completion response") + val message = choice.message // Some local models emit the tool call as a JSON blob in `content` instead of the native // tool_calls array; salvage it so the orchestrator gets a real call rather than treating // the blob as a (failing) artifact and retry-looping to exhaustion. @@ -207,7 +201,7 @@ class LlamaCppInferenceProvider( val toolCalls = message.toolCalls.ifEmpty { salvaged } val finishReason = when { toolCalls.isNotEmpty() -> FinishReason.ToolCall - response.choices.first().finishReason.lowercase() == "length" -> FinishReason.Length + choice.finishReason.lowercase() == "length" -> FinishReason.Length else -> FinishReason.Stop }