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.InferenceRequest
|
||||||
import com.correx.core.inference.InferenceResponse
|
import com.correx.core.inference.InferenceResponse
|
||||||
import com.correx.core.inference.InferenceRouter
|
import com.correx.core.inference.InferenceRouter
|
||||||
|
import com.correx.core.inference.PromptRenderer
|
||||||
import com.correx.core.inference.ResponseFormat
|
import com.correx.core.inference.ResponseFormat
|
||||||
import com.correx.core.inference.Tokenizer
|
import com.correx.core.inference.Tokenizer
|
||||||
import com.correx.core.inference.ToolCallRequest
|
import com.correx.core.inference.ToolCallRequest
|
||||||
@@ -91,6 +92,7 @@ import kotlinx.coroutines.TimeoutCancellationException
|
|||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.coroutines.withTimeout
|
import kotlinx.coroutines.withTimeout
|
||||||
import kotlinx.datetime.Clock
|
import kotlinx.datetime.Clock
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
@@ -688,8 +690,8 @@ abstract class SessionOrchestrator(
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: capture rendered prompt; currently contextpack.toString()
|
val renderedMessages = PromptRenderer.render(contextPack)
|
||||||
val promptArtifactId = artifactStore.put(contextPack.toString().toByteArray())
|
val promptArtifactId = artifactStore.put(Json.encodeToString(renderedMessages).toByteArray())
|
||||||
emit(sessionId, InferenceStartedEvent(requestId, sessionId, stageId, provider.id, promptArtifactId))
|
emit(sessionId, InferenceStartedEvent(requestId, sessionId, stageId, provider.id, promptArtifactId))
|
||||||
|
|
||||||
if (isCancelled(sessionId)) return InferenceResult.Cancelled
|
if (isCancelled(sessionId)) return InferenceResult.Cancelled
|
||||||
|
|||||||
+4
-3
@@ -1,5 +1,6 @@
|
|||||||
package com.correx.infrastructure.inference.llama.cpp
|
package com.correx.infrastructure.inference.llama.cpp
|
||||||
|
|
||||||
|
import com.correx.core.inference.ChatMessage
|
||||||
import com.correx.core.inference.ToolCallRequest
|
import com.correx.core.inference.ToolCallRequest
|
||||||
import com.correx.core.inference.ToolDefinition
|
import com.correx.core.inference.ToolDefinition
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
@@ -8,7 +9,7 @@ import kotlinx.serialization.Serializable
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class ChatCompletionRequest(
|
data class ChatCompletionRequest(
|
||||||
val model: String,
|
val model: String,
|
||||||
val messages: List<ChatMessage>,
|
val messages: List<LlamaCppChatMessage>,
|
||||||
val temperature: Double,
|
val temperature: Double,
|
||||||
@SerialName("top_p") val topP: Double,
|
@SerialName("top_p") val topP: Double,
|
||||||
@SerialName("max_tokens") val maxTokens: Int,
|
@SerialName("max_tokens") val maxTokens: Int,
|
||||||
@@ -20,7 +21,7 @@ data class ChatCompletionRequest(
|
|||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class ChatMessage(
|
data class LlamaCppChatMessage(
|
||||||
val role: String,
|
val role: String,
|
||||||
val content: String?,
|
val content: String?,
|
||||||
@SerialName("tool_calls") val toolCalls: List<ToolCallRequest> = emptyList(),
|
@SerialName("tool_calls") val toolCalls: List<ToolCallRequest> = emptyList(),
|
||||||
@@ -36,7 +37,7 @@ data class ChatCompletionResponse(
|
|||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class Choice(
|
data class Choice(
|
||||||
val message: ChatMessage,
|
val message: LlamaCppChatMessage,
|
||||||
@SerialName("finish_reason") val finishReason: String,
|
@SerialName("finish_reason") val finishReason: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
+8
-31
@@ -1,14 +1,12 @@
|
|||||||
package com.correx.infrastructure.inference.llama.cpp
|
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.events.types.ProviderId
|
||||||
import com.correx.core.inference.CapabilityScore
|
import com.correx.core.inference.CapabilityScore
|
||||||
import com.correx.core.inference.FinishReason
|
import com.correx.core.inference.FinishReason
|
||||||
import com.correx.core.inference.InferenceProvider
|
import com.correx.core.inference.InferenceProvider
|
||||||
import com.correx.core.inference.InferenceRequest
|
import com.correx.core.inference.InferenceRequest
|
||||||
import com.correx.core.inference.InferenceResponse
|
import com.correx.core.inference.InferenceResponse
|
||||||
|
import com.correx.core.inference.PromptRenderer
|
||||||
import com.correx.core.inference.ProviderHealth
|
import com.correx.core.inference.ProviderHealth
|
||||||
import com.correx.core.inference.ResponseFormat
|
import com.correx.core.inference.ResponseFormat
|
||||||
import com.correx.core.inference.TokenUsage
|
import com.correx.core.inference.TokenUsage
|
||||||
@@ -79,7 +77,13 @@ class LlamaCppInferenceProvider(
|
|||||||
override suspend fun infer(request: InferenceRequest): InferenceResponse {
|
override suspend fun infer(request: InferenceRequest): InferenceResponse {
|
||||||
val startTime = System.currentTimeMillis()
|
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) {
|
val grammar = when (val fmt = request.responseFormat) {
|
||||||
is ResponseFormat.Json -> GbnfGrammarConverter.convert(fmt.schema)
|
is ResponseFormat.Json -> GbnfGrammarConverter.convert(fmt.schema)
|
||||||
@@ -142,31 +146,4 @@ class LlamaCppInferenceProvider(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun capabilities(): Set<CapabilityScore> = descriptor.capabilities
|
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