feat(artifacts): config-driven custom artifact kinds with schema validation
Users declare artifact kinds in [[artifacts]] (id + schema_path to a JSON-schema file + llm_emitted); ConfigArtifactKind registers them at startup via createWorkflowLoader(extraKinds). New JsonSchemaValidator validates any non-built-in kind generically against its declared deriveJsonSchema(), so an LLM-emitted custom kind is checked against its shape, never trusted. Removed the dead payloadSerializer from the ArtifactKind contract. Schema source is path-to-file (not inline TOML — the hand-rolled parser can't nest).
This commit is contained in:
@@ -1,10 +1,7 @@
|
||||
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,16 @@
|
||||
package com.correx.core.artifacts.kind
|
||||
|
||||
/**
|
||||
* A user-declared artifact kind whose schema comes from configuration rather than
|
||||
* a compile-time Kotlin type. The declared [schema] is authoritative: content is
|
||||
* validated against it generically (see the artifact-payload validator), so an
|
||||
* [llmEmitted] config kind is checked against its declared shape, never a
|
||||
* hardcoded type check.
|
||||
*/
|
||||
data class ConfigArtifactKind(
|
||||
override val id: String,
|
||||
private val schema: JsonSchema,
|
||||
override val llmEmitted: Boolean = false,
|
||||
) : ArtifactKind {
|
||||
override fun deriveJsonSchema(): JsonSchema = schema
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
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(
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
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(
|
||||
|
||||
+15
@@ -29,6 +29,21 @@ class ArtifactKindRegistryTest {
|
||||
assertEquals(false, registry.get("process_result")!!.llmEmitted)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `config-declared kind registers and exposes its declared schema`() {
|
||||
val schema = JsonSchema(
|
||||
type = "object",
|
||||
properties = mapOf("x" to JsonSchemaProperty(type = "string")),
|
||||
required = listOf("x"),
|
||||
)
|
||||
registry.register(ConfigArtifactKind(id = "custom", schema = schema, llmEmitted = true))
|
||||
|
||||
val kind = registry.get("custom")
|
||||
assertNotNull(kind)
|
||||
assertEquals(true, kind!!.llmEmitted)
|
||||
assertEquals(schema, kind.deriveJsonSchema())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `FileWrittenKind schema has required fields`() {
|
||||
val schema = FileWrittenKind.deriveJsonSchema()
|
||||
|
||||
Reference in New Issue
Block a user