feat(core:artifacts): ArtifactKind interface, registry, built-in kinds, and MaterializingArtifactWriter
This commit is contained in:
@@ -6,4 +6,5 @@ plugins {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":core:events"))
|
||||
testImplementation "org.jetbrains.kotlin:kotlin-test:$kotlin_version"
|
||||
}
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.correx.core.artifacts
|
||||
|
||||
import com.correx.core.artifacts.kind.FileWrittenArtifact
|
||||
import com.correx.core.artifacts.kind.FileWrittenPayload
|
||||
import java.nio.file.Path
|
||||
|
||||
interface MaterializingArtifactWriter {
|
||||
suspend fun materialize(payload: FileWrittenPayload, sandboxRoot: Path): MaterializationResult
|
||||
}
|
||||
|
||||
sealed interface MaterializationResult {
|
||||
data class Success(val artifact: FileWrittenArtifact, val resolvedPath: Path) : MaterializationResult
|
||||
data class Failure(val reason: String) : MaterializationResult
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.correx.core.artifacts.kind
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
|
||||
interface ArtifactKind {
|
||||
val id: String
|
||||
val payloadSerializer: KSerializer<*>
|
||||
fun deriveJsonSchema(): JsonSchema
|
||||
val llmEmitted: Boolean
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.correx.core.artifacts.kind
|
||||
|
||||
interface ArtifactKindRegistry {
|
||||
fun register(kind: ArtifactKind)
|
||||
fun get(id: String): ArtifactKind?
|
||||
}
|
||||
|
||||
class DefaultArtifactKindRegistry : ArtifactKindRegistry {
|
||||
private val kinds: MutableMap<String, ArtifactKind> = mutableMapOf()
|
||||
|
||||
init {
|
||||
register(FileWrittenKind)
|
||||
register(ProcessResultKind)
|
||||
}
|
||||
|
||||
override fun register(kind: ArtifactKind) {
|
||||
kinds[kind.id] = kind
|
||||
}
|
||||
|
||||
override fun get(id: String): ArtifactKind? = kinds[id]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.correx.core.artifacts.kind
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
|
||||
object FileWrittenKind : ArtifactKind {
|
||||
override val id: String = "file_written"
|
||||
override val payloadSerializer: KSerializer<*> = FileWrittenPayload.serializer()
|
||||
override val llmEmitted: Boolean = false
|
||||
|
||||
override fun deriveJsonSchema(): JsonSchema = JsonSchema(
|
||||
type = "object",
|
||||
properties = mapOf(
|
||||
"path" to JsonSchemaProperty(type = "string", description = "Relative file path, no .. components"),
|
||||
"content" to JsonSchemaProperty(type = "string", description = "File content"),
|
||||
"mode" to JsonSchemaProperty(type = "string", description = "Unix file mode", default = "0644"),
|
||||
),
|
||||
required = listOf("path", "content"),
|
||||
additionalProperties = false,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.correx.core.artifacts.kind
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class FileWrittenPayload(
|
||||
val path: String,
|
||||
val content: String,
|
||||
val mode: String = "0644",
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class FileWrittenArtifact(
|
||||
val path: String,
|
||||
val contentHash: String,
|
||||
val size: Long,
|
||||
val mode: String,
|
||||
)
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.correx.core.artifacts.kind
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class JsonSchema(
|
||||
val type: String,
|
||||
val properties: Map<String, JsonSchemaProperty> = emptyMap(),
|
||||
val required: List<String> = emptyList(),
|
||||
val additionalProperties: Boolean = false,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class JsonSchemaProperty(
|
||||
val type: String,
|
||||
val description: String = "",
|
||||
val default: String? = null,
|
||||
)
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.correx.core.artifacts.kind
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ProcessResultArtifact(
|
||||
val command: String,
|
||||
val exitCode: Int,
|
||||
val stdout: String,
|
||||
val stderr: String,
|
||||
val durationMs: Long,
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.correx.core.artifacts.kind
|
||||
|
||||
import kotlinx.serialization.KSerializer
|
||||
|
||||
object ProcessResultKind : ArtifactKind {
|
||||
override val id: String = "process_result"
|
||||
override val payloadSerializer: KSerializer<*> = ProcessResultArtifact.serializer()
|
||||
override val llmEmitted: Boolean = false
|
||||
|
||||
override fun deriveJsonSchema(): JsonSchema = JsonSchema(
|
||||
type = "object",
|
||||
properties = mapOf(
|
||||
"command" to JsonSchemaProperty(type = "string"),
|
||||
"exitCode" to JsonSchemaProperty(type = "integer"),
|
||||
"stdout" to JsonSchemaProperty(type = "string"),
|
||||
"stderr" to JsonSchemaProperty(type = "string"),
|
||||
"durationMs" to JsonSchemaProperty(type = "integer"),
|
||||
),
|
||||
required = listOf("command", "exitCode", "stdout", "stderr", "durationMs"),
|
||||
additionalProperties = false,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package com.correx.core.artifacts.kind
|
||||
|
||||
import com.correx.core.events.types.ArtifactId
|
||||
|
||||
data class TypedArtifactSlot(
|
||||
val name: ArtifactId,
|
||||
val kind: ArtifactKind,
|
||||
)
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.correx.core.artifacts.kind
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNotNull
|
||||
import kotlin.test.assertNull
|
||||
|
||||
class ArtifactKindRegistryTest {
|
||||
private val registry = DefaultArtifactKindRegistry()
|
||||
|
||||
@Test
|
||||
fun `built-in kinds are registered`() {
|
||||
assertNotNull(registry.get("file_written"))
|
||||
assertNotNull(registry.get("process_result"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `get returns null for unknown kind`() {
|
||||
assertNull(registry.get("nonexistent"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `FileWrittenKind is not llmEmitted`() {
|
||||
assertEquals(false, registry.get("file_written")!!.llmEmitted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `ProcessResultKind is not llmEmitted`() {
|
||||
assertEquals(false, registry.get("process_result")!!.llmEmitted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `FileWrittenKind schema has required fields`() {
|
||||
val schema = FileWrittenKind.deriveJsonSchema()
|
||||
assertEquals("object", schema.type)
|
||||
assert("path" in schema.required)
|
||||
assert("content" in schema.required)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user