fix(server): artifact viewer falls back to raw stage output when no content hash
Artifacts with no ArtifactContentStored mapping (older sessions, or stages where the slot->hash link was lost) showed a blank pane. listArtifacts now falls back to the stage's last InferenceCompleted output so the operator can still see what the stage produced. Empty-string content is normalised to null so the viewer shows a clear '(no content stored)' instead of a blank pane. Verified: role_pipeline sessions already resolve content from CAS (index + segments intact, store rooted at ~/.config/correx/artifacts); the previously-blank sessions are healthcheck/degenerate runs whose inference output was genuinely empty.
This commit is contained in:
@@ -10,6 +10,7 @@ import com.correx.core.events.events.ArtifactContentStoredEvent
|
||||
import com.correx.core.events.events.ArtifactCreatedEvent
|
||||
import com.correx.core.events.events.ArtifactValidatedEvent
|
||||
import com.correx.core.events.events.ArtifactValidatingEvent
|
||||
import com.correx.core.events.events.InferenceCompletedEvent
|
||||
import com.correx.core.events.types.ArtifactId
|
||||
import com.correx.core.events.types.SessionId
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -31,6 +32,11 @@ class StreamQueries(private val module: ServerModule) {
|
||||
suspend fun listArtifacts(sessionId: SessionId): ServerMessage.ArtifactList {
|
||||
val events = withContext(Dispatchers.IO) { module.eventStore.read(sessionId) }
|
||||
val accs = LinkedHashMap<String, ArtifactAcc>()
|
||||
// Per-stage raw inference output (last response), used as a content fallback for artifacts
|
||||
// that never recorded a slot->hash mapping (ArtifactContentStoredEvent) — e.g. older sessions
|
||||
// or stages where the validated-artifact link was lost. Lets the operator still see what the
|
||||
// stage actually produced instead of a blank pane.
|
||||
val lastInferenceByStage = HashMap<String, ArtifactId>()
|
||||
for (event in events) {
|
||||
when (val p = event.payload) {
|
||||
is ArtifactCreatedEvent -> accs.getOrPut(p.artifactId.value) { ArtifactAcc() }.apply {
|
||||
@@ -40,19 +46,21 @@ class StreamQueries(private val module: ServerModule) {
|
||||
is ArtifactValidatingEvent -> accs[p.artifactId.value]?.let { it.phase = "VALIDATING" }
|
||||
is ArtifactValidatedEvent -> accs[p.artifactId.value]?.let { it.phase = "VALIDATED" }
|
||||
is ArtifactContentStoredEvent -> accs[p.artifactId.value]?.let { it.contentHash = p.contentHash }
|
||||
is InferenceCompletedEvent -> lastInferenceByStage[p.stageId.value] = p.responseArtifactId
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
val artifacts = accs.map { (id, acc) ->
|
||||
val content = acc.contentHash?.let { hash ->
|
||||
withContext(Dispatchers.IO) { module.artifactStore.get(hash) }?.toString(Charsets.UTF_8)
|
||||
val hash = acc.contentHash ?: lastInferenceByStage[acc.stageId]
|
||||
val content = hash?.let {
|
||||
withContext(Dispatchers.IO) { module.artifactStore.get(it) }?.toString(Charsets.UTF_8)
|
||||
}
|
||||
ArtifactSummaryDto(
|
||||
artifactId = id,
|
||||
stageId = acc.stageId,
|
||||
schemaVersion = acc.schemaVersion,
|
||||
phase = acc.phase,
|
||||
content = content,
|
||||
content = content?.takeIf { it.isNotBlank() },
|
||||
)
|
||||
}
|
||||
return ServerMessage.ArtifactList(sessionId = sessionId, artifacts = artifacts)
|
||||
|
||||
Reference in New Issue
Block a user