fix: store actual rendered prompt artifact instead of contextpack.toString()
Extract message-rendering logic from LlamaCppInferenceProvider into a provider-agnostic PromptRenderer in core:inference. SessionOrchestrator now serializes the rendered ChatMessage list as JSON for the prompt artifact, so the audit trail reflects what the LLM actually saw. Resolves the TODO at SessionOrchestrator.kt:691.
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
package com.correx.core.inference
|
||||
|
||||
import com.correx.core.context.model.ContextLayer
|
||||
import com.correx.core.context.model.ContextPack
|
||||
import com.correx.core.context.model.EntryRole
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ChatMessage(
|
||||
val role: String,
|
||||
val content: String,
|
||||
)
|
||||
|
||||
object PromptRenderer {
|
||||
fun render(contextPack: ContextPack): List<ChatMessage> {
|
||||
val sorted = contextPack.layers.entries.sortedBy { it.key.ordinal }
|
||||
val systemContent = sorted
|
||||
.filter { it.key == ContextLayer.L0 }
|
||||
.flatMap { it.value }
|
||||
.joinToString("\n\n") { it.content }
|
||||
.takeIf { it.isNotBlank() }
|
||||
val conversationMessages = sorted.filter { it.key != ContextLayer.L0 }.flatMap { (_, entries) ->
|
||||
entries.map { entry ->
|
||||
ChatMessage(
|
||||
role = when (entry.role) {
|
||||
EntryRole.SYSTEM -> "system"
|
||||
EntryRole.ASSISTANT -> "assistant"
|
||||
EntryRole.TOOL -> "tool"
|
||||
EntryRole.USER -> "user"
|
||||
},
|
||||
content = entry.content,
|
||||
)
|
||||
}
|
||||
}
|
||||
val messages = buildList {
|
||||
systemContent?.let { add(ChatMessage("system", it)) }
|
||||
addAll(conversationMessages)
|
||||
}
|
||||
return messages.ifEmpty { listOf(ChatMessage("user", "")) }
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -61,6 +61,7 @@ import com.correx.core.inference.InferenceRepository
|
||||
import com.correx.core.inference.InferenceRequest
|
||||
import com.correx.core.inference.InferenceResponse
|
||||
import com.correx.core.inference.InferenceRouter
|
||||
import com.correx.core.inference.PromptRenderer
|
||||
import com.correx.core.inference.ResponseFormat
|
||||
import com.correx.core.inference.Tokenizer
|
||||
import com.correx.core.inference.ToolCallRequest
|
||||
@@ -91,6 +92,7 @@ import kotlinx.coroutines.TimeoutCancellationException
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import org.slf4j.LoggerFactory
|
||||
@@ -688,8 +690,8 @@ abstract class SessionOrchestrator(
|
||||
),
|
||||
)
|
||||
|
||||
// TODO: capture rendered prompt; currently contextpack.toString()
|
||||
val promptArtifactId = artifactStore.put(contextPack.toString().toByteArray())
|
||||
val renderedMessages = PromptRenderer.render(contextPack)
|
||||
val promptArtifactId = artifactStore.put(Json.encodeToString(renderedMessages).toByteArray())
|
||||
emit(sessionId, InferenceStartedEvent(requestId, sessionId, stageId, provider.id, promptArtifactId))
|
||||
|
||||
if (isCancelled(sessionId)) return InferenceResult.Cancelled
|
||||
|
||||
+4
-3
@@ -1,5 +1,6 @@
|
||||
package com.correx.infrastructure.inference.llama.cpp
|
||||
|
||||
import com.correx.core.inference.ChatMessage
|
||||
import com.correx.core.inference.ToolCallRequest
|
||||
import com.correx.core.inference.ToolDefinition
|
||||
import kotlinx.serialization.SerialName
|
||||
@@ -8,7 +9,7 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
data class ChatCompletionRequest(
|
||||
val model: String,
|
||||
val messages: List<ChatMessage>,
|
||||
val messages: List<LlamaCppChatMessage>,
|
||||
val temperature: Double,
|
||||
@SerialName("top_p") val topP: Double,
|
||||
@SerialName("max_tokens") val maxTokens: Int,
|
||||
@@ -20,7 +21,7 @@ data class ChatCompletionRequest(
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ChatMessage(
|
||||
data class LlamaCppChatMessage(
|
||||
val role: String,
|
||||
val content: String?,
|
||||
@SerialName("tool_calls") val toolCalls: List<ToolCallRequest> = emptyList(),
|
||||
@@ -36,7 +37,7 @@ data class ChatCompletionResponse(
|
||||
|
||||
@Serializable
|
||||
data class Choice(
|
||||
val message: ChatMessage,
|
||||
val message: LlamaCppChatMessage,
|
||||
@SerialName("finish_reason") val finishReason: String,
|
||||
)
|
||||
|
||||
|
||||
+8
-31
@@ -1,14 +1,12 @@
|
||||
package com.correx.infrastructure.inference.llama.cpp
|
||||
|
||||
import com.correx.core.context.model.ContextLayer
|
||||
import com.correx.core.context.model.ContextPack
|
||||
import com.correx.core.context.model.EntryRole
|
||||
import com.correx.core.events.types.ProviderId
|
||||
import com.correx.core.inference.CapabilityScore
|
||||
import com.correx.core.inference.FinishReason
|
||||
import com.correx.core.inference.InferenceProvider
|
||||
import com.correx.core.inference.InferenceRequest
|
||||
import com.correx.core.inference.InferenceResponse
|
||||
import com.correx.core.inference.PromptRenderer
|
||||
import com.correx.core.inference.ProviderHealth
|
||||
import com.correx.core.inference.ResponseFormat
|
||||
import com.correx.core.inference.TokenUsage
|
||||
@@ -79,7 +77,13 @@ class LlamaCppInferenceProvider(
|
||||
override suspend fun infer(request: InferenceRequest): InferenceResponse {
|
||||
val startTime = System.currentTimeMillis()
|
||||
|
||||
val messages = buildMessages(request.contextPack)
|
||||
val baseMessages = PromptRenderer.render(request.contextPack)
|
||||
val messages = baseMessages.map { msg ->
|
||||
LlamaCppChatMessage(
|
||||
role = msg.role,
|
||||
content = msg.content,
|
||||
)
|
||||
}
|
||||
|
||||
val grammar = when (val fmt = request.responseFormat) {
|
||||
is ResponseFormat.Json -> GbnfGrammarConverter.convert(fmt.schema)
|
||||
@@ -142,31 +146,4 @@ class LlamaCppInferenceProvider(
|
||||
}
|
||||
|
||||
override fun capabilities(): Set<CapabilityScore> = descriptor.capabilities
|
||||
|
||||
private fun buildMessages(contextPack: ContextPack): List<ChatMessage> {
|
||||
val sorted = contextPack.layers.entries.sortedBy { it.key.ordinal }
|
||||
val systemContent = sorted
|
||||
.filter { it.key == ContextLayer.L0 }
|
||||
.flatMap { it.value }
|
||||
.joinToString("\n\n") { it.content }
|
||||
.takeIf { it.isNotBlank() }
|
||||
val conversationMessages = sorted.filter { it.key != ContextLayer.L0 }.flatMap { (_, entries) ->
|
||||
entries.map { entry ->
|
||||
ChatMessage(
|
||||
role = when (entry.role) {
|
||||
EntryRole.SYSTEM -> "system"
|
||||
EntryRole.ASSISTANT -> "assistant"
|
||||
EntryRole.TOOL -> "tool"
|
||||
EntryRole.USER -> "user"
|
||||
},
|
||||
content = entry.content,
|
||||
)
|
||||
}
|
||||
}
|
||||
val messages = buildList {
|
||||
systemContent?.let { add(ChatMessage("system", it)) }
|
||||
addAll(conversationMessages)
|
||||
}
|
||||
return messages.ifEmpty { listOf(ChatMessage("user", "")) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user