feature(event-sourcing): baseline for the architecture review fixes.
- fixed the tool overlay in tui. - fixed tool calling - added schemas. - getting all sessionIds via event store. - instead of sending all events on the replay - there's a new way for replaying. - session snapshot is now a thing getting sent for previously created sessions. - added logging to important parts. - revert workflowId from WorkflowCompletedEvent.
This commit is contained in:
+7
-1
@@ -16,16 +16,22 @@ class DefaultOrchestrationReducer : OrchestrationReducer {
|
||||
event: StoredEvent,
|
||||
): OrchestrationState = when (val p = event.payload) {
|
||||
is WorkflowStartedEvent -> state.copy(
|
||||
workflowId = p.workflowId,
|
||||
status = OrchestrationStatus.RUNNING,
|
||||
currentStageId = p.startStageId,
|
||||
retryPolicy = p.retryPolicy,
|
||||
)
|
||||
|
||||
is WorkflowCompletedEvent -> state.copy(
|
||||
status = OrchestrationStatus.COMPLETED,
|
||||
currentStageId = p.terminalStageId,
|
||||
)
|
||||
|
||||
is WorkflowFailedEvent -> state.copy(status = OrchestrationStatus.FAILED, failureReason = p.reason)
|
||||
is WorkflowFailedEvent -> state.copy(
|
||||
status = OrchestrationStatus.FAILED,
|
||||
failureReason = p.reason,
|
||||
)
|
||||
|
||||
is OrchestrationPausedEvent -> state.copy(
|
||||
status = OrchestrationStatus.PAUSED,
|
||||
pendingApproval = true,
|
||||
|
||||
+2
-2
@@ -94,7 +94,7 @@ class DefaultSessionOrchestrator(
|
||||
}
|
||||
|
||||
is TransitionDecision.Stay -> if (isTerminal(enriched.graph, enriched.currentStageId)) {
|
||||
completeWorkflow(enriched.sessionId, enriched.currentStageId, enriched.stageCount, enriched.graph.id)
|
||||
completeWorkflow(enriched.sessionId, enriched.currentStageId, enriched.stageCount)
|
||||
} else {
|
||||
failWorkflow(
|
||||
sessionId = enriched.sessionId,
|
||||
@@ -140,7 +140,7 @@ class DefaultSessionOrchestrator(
|
||||
if (isTerminal(ctx.graph, nextStageId)) {
|
||||
// Terminal stage is a sentinel — not executed, so don't count it.
|
||||
return StepResult.Terminal(
|
||||
completeWorkflow(ctx.sessionId, nextStageId, ctx.stageCount, ctx.graph.id),
|
||||
completeWorkflow(ctx.sessionId, nextStageId, ctx.stageCount),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -1,5 +1,6 @@
|
||||
package com.correx.core.kernel.orchestration
|
||||
|
||||
import com.correx.core.approvals.DefaultApprovalRepository
|
||||
import com.correx.core.artifacts.repository.ArtifactRepository
|
||||
import com.correx.core.events.stores.EventStore
|
||||
import com.correx.core.inference.InferenceRepository
|
||||
@@ -11,4 +12,5 @@ data class OrchestratorRepositories(
|
||||
val orchestrationRepository: OrchestrationRepository,
|
||||
val sessionRepository: DefaultSessionRepository,
|
||||
val artifactRepository: ArtifactRepository,
|
||||
val approvalRepository: DefaultApprovalRepository,
|
||||
)
|
||||
|
||||
+2
-2
@@ -87,7 +87,7 @@ class ReplayOrchestrator(
|
||||
val nextStageId = decision.to
|
||||
|
||||
if (isTerminal(ctx.graph, nextStageId)) {
|
||||
return completeWorkflow(ctx.sessionId, nextStageId, ctx.stageCount, ctx.graph.id)
|
||||
return completeWorkflow(ctx.sessionId, nextStageId, ctx.stageCount)
|
||||
}
|
||||
|
||||
when (val result = enterStage(ctx, nextStageId, session)) {
|
||||
@@ -101,7 +101,7 @@ class ReplayOrchestrator(
|
||||
}
|
||||
|
||||
is TransitionDecision.Stay -> if (isTerminal(ctx.graph, ctx.currentStageId)) {
|
||||
completeWorkflow(ctx.sessionId, ctx.currentStageId, ctx.stageCount, ctx.graph.id)
|
||||
completeWorkflow(ctx.sessionId, ctx.currentStageId, ctx.stageCount)
|
||||
} else {
|
||||
step(ctx)
|
||||
}
|
||||
|
||||
+14
-10
@@ -112,7 +112,6 @@ abstract class SessionOrchestrator(
|
||||
private val toolExecutor: ToolExecutor? = engines.toolExecutor
|
||||
private val toolRegistry: ToolRegistry? = engines.toolRegistry
|
||||
private val inferenceRepository: InferenceRepository = repositories.inferenceRepository
|
||||
private val artifactRepository = repositories.artifactRepository
|
||||
internal val orchestrationRepository: OrchestrationRepository = repositories.orchestrationRepository
|
||||
internal abstract val cancellations: ConcurrentHashMap<SessionId, AtomicBoolean>
|
||||
internal val pendingApprovals: ConcurrentHashMap<ApprovalRequestId, CompletableDeferred<ApprovalDecision>> =
|
||||
@@ -145,7 +144,7 @@ abstract class SessionOrchestrator(
|
||||
.onFailure {
|
||||
log.error(
|
||||
"[SessionOrchestrator] stage=${stageId.value}: " +
|
||||
"failed to load prompt '$path': ${it.message}",
|
||||
"failed to load prompt '$path': ${it.message}",
|
||||
)
|
||||
}
|
||||
.getOrNull()
|
||||
@@ -168,7 +167,7 @@ abstract class SessionOrchestrator(
|
||||
.onFailure {
|
||||
log.error(
|
||||
"[SessionOrchestrator] stage=${stageId.value}: " +
|
||||
"failed to load default system prompt '$path': ${it.message}",
|
||||
"failed to load default system prompt '$path': ${it.message}",
|
||||
)
|
||||
}
|
||||
.getOrNull()
|
||||
@@ -192,7 +191,7 @@ abstract class SessionOrchestrator(
|
||||
.onFailure {
|
||||
log.error(
|
||||
"[SessionOrchestrator] stage=${stageId.value}: " +
|
||||
"failed to load prompt '$path': ${it.message}",
|
||||
"failed to load prompt '$path': ${it.message}",
|
||||
)
|
||||
}
|
||||
.getOrNull()
|
||||
@@ -214,7 +213,7 @@ abstract class SessionOrchestrator(
|
||||
val llmEmittedSlots = stageConfig.produces.filter { it.kind.llmEmitted }
|
||||
require(llmEmittedSlots.size <= 1) {
|
||||
"Stage '${stageId.value}' declares ${llmEmittedSlots.size} LLM-emitted artifact kinds; " +
|
||||
"only 0 or 1 is supported"
|
||||
"only 0 or 1 is supported"
|
||||
}
|
||||
val responseFormat = llmEmittedSlots.firstOrNull()
|
||||
?.let { ResponseFormat.Json(it.kind.deriveJsonSchema()) }
|
||||
@@ -269,6 +268,7 @@ abstract class SessionOrchestrator(
|
||||
emitToolArtifacts(sessionId, stageId, stageConfig)
|
||||
verifyProduces(sessionId, stageId, stageConfig)
|
||||
}
|
||||
|
||||
is StageExecutionResult.Failure -> outcome
|
||||
}
|
||||
}
|
||||
@@ -321,6 +321,8 @@ abstract class SessionOrchestrator(
|
||||
validationReportId = ValidationReportId(UUID.randomUUID().toString()),
|
||||
riskSummaryId = null,
|
||||
timestamp = Clock.System.now(),
|
||||
toolName = toolCall.function.name,
|
||||
preview = toolCall.function.arguments.take(200),
|
||||
)
|
||||
emit(sessionId, OrchestrationPausedEvent(sessionId, stageId, "APPROVAL_PENDING"))
|
||||
emit(
|
||||
@@ -333,6 +335,8 @@ abstract class SessionOrchestrator(
|
||||
sessionId = sessionId,
|
||||
stageId = stageId,
|
||||
projectId = null,
|
||||
toolName = toolCall.function.name,
|
||||
preview = toolCall.function.arguments.take(200),
|
||||
),
|
||||
)
|
||||
val deferred = CompletableDeferred<ApprovalDecision>()
|
||||
@@ -399,8 +403,8 @@ abstract class SessionOrchestrator(
|
||||
if (responseFormat !is ResponseFormat.Json) return emptyList()
|
||||
val compactSchema = Json.encodeToString(JsonSchema.serializer(), responseFormat.schema)
|
||||
val instruction = "Respond with a single JSON object matching this schema. " +
|
||||
"Do not include markdown, code fences, or commentary outside the JSON. " +
|
||||
"Schema: $compactSchema"
|
||||
"Do not include markdown, code fences, or commentary outside the JSON. " +
|
||||
"Schema: $compactSchema"
|
||||
return listOf(
|
||||
ContextEntry(
|
||||
id = ContextEntryId(UUID.randomUUID().toString()),
|
||||
@@ -442,7 +446,7 @@ abstract class SessionOrchestrator(
|
||||
val missingIds = missing.joinToString(", ") { it.value }
|
||||
log.error(
|
||||
"[Orchestrator] stage missing produced artifacts " +
|
||||
"session=${sessionId.value} stage=${stageId.value} missing=$missingIds",
|
||||
"session=${sessionId.value} stage=${stageId.value} missing=$missingIds",
|
||||
)
|
||||
return StageExecutionResult.Failure(
|
||||
"stage ${stageId.value} did not produce declared artifacts: $missingIds",
|
||||
@@ -598,13 +602,12 @@ abstract class SessionOrchestrator(
|
||||
sessionId: SessionId,
|
||||
terminalStageId: StageId,
|
||||
stageCount: Int,
|
||||
workflowId: String,
|
||||
): WorkflowResult.Completed {
|
||||
log.debug(
|
||||
"[Orchestrator] COMPLETED session={} terminalStage={} stages={}",
|
||||
sessionId.value, terminalStageId.value, stageCount,
|
||||
)
|
||||
emit(sessionId, WorkflowCompletedEvent(sessionId, terminalStageId, stageCount, workflowId))
|
||||
emit(sessionId, WorkflowCompletedEvent(sessionId, terminalStageId, stageCount))
|
||||
cancellations.remove(sessionId)
|
||||
return WorkflowResult.Completed(sessionId, terminalStageId)
|
||||
}
|
||||
@@ -642,6 +645,7 @@ abstract class SessionOrchestrator(
|
||||
// --- event emission ---
|
||||
|
||||
internal suspend fun emit(sessionId: SessionId, payload: EventPayload) {
|
||||
log.debug("[session {}] emitting event, payload: {}", sessionId.value.substring(0, 7), payload)
|
||||
eventStore.append(
|
||||
NewEvent(
|
||||
metadata = EventMetadata(
|
||||
|
||||
Reference in New Issue
Block a user