feat(kernel): RepoKnowledgeRetriever port + relevance-retrieved repo context with recency fallback
This commit is contained in:
@@ -4,6 +4,7 @@ package com.correx.core.kernel.orchestration
|
||||
// and to stay under detekt's function-count threshold (same pattern as isBackEdge).
|
||||
|
||||
import com.correx.core.artifacts.kind.ArtifactKind
|
||||
import com.correx.core.events.events.RepoKnowledgeHit
|
||||
import com.correx.core.context.model.ContextEntry
|
||||
import com.correx.core.context.model.ContextLayer
|
||||
import com.correx.core.context.model.EntryRole
|
||||
@@ -66,6 +67,22 @@ fun buildArtifactKindVocabularyEntry(kinds: Collection<ArtifactKind>): ContextEn
|
||||
)
|
||||
}
|
||||
|
||||
fun buildRelevantFilesEntry(hits: List<RepoKnowledgeHit>): ContextEntry {
|
||||
val content = buildString {
|
||||
append("## Relevant files (retrieved by semantic similarity)\n")
|
||||
hits.forEach { append("- ").append(it.text).append("\n") }
|
||||
}.trimEnd()
|
||||
return ContextEntry(
|
||||
id = ContextEntryId(UUID.randomUUID().toString()),
|
||||
layer = ContextLayer.L3,
|
||||
content = content,
|
||||
sourceType = "relevantFiles",
|
||||
sourceId = "repo-knowledge",
|
||||
tokenEstimate = content.length / 4,
|
||||
role = EntryRole.SYSTEM,
|
||||
)
|
||||
}
|
||||
|
||||
fun buildProjectProfileEntry(profile: BoundProjectProfile): ContextEntry {
|
||||
val content = buildString {
|
||||
append("## Project profile\n")
|
||||
|
||||
+2
-1
@@ -57,7 +57,8 @@ class DefaultSessionOrchestrator(
|
||||
private val decisionJournalRepository: DefaultDecisionJournalRepository,
|
||||
private val compactionService: JournalCompactionService? = null,
|
||||
artifactKindRegistry: ArtifactKindRegistry? = null,
|
||||
) : SessionOrchestrator(repositories, engines, artifactStore, decisionJournalRepository, artifactKindRegistry = artifactKindRegistry), ApprovalGateway {
|
||||
repoKnowledgeRetriever: RepoKnowledgeRetriever? = null,
|
||||
) : SessionOrchestrator(repositories, engines, artifactStore, decisionJournalRepository, artifactKindRegistry = artifactKindRegistry, repoKnowledgeRetriever = repoKnowledgeRetriever), ApprovalGateway {
|
||||
override val tokenizer: Tokenizer? = tokenizer
|
||||
override val cancellations: ConcurrentHashMap<SessionId, AtomicBoolean> =
|
||||
ConcurrentHashMap<SessionId, AtomicBoolean>()
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package com.correx.core.kernel.orchestration
|
||||
|
||||
import com.correx.core.events.events.RepoKnowledgeHit
|
||||
import com.correx.core.events.types.SessionId
|
||||
|
||||
interface RepoKnowledgeRetriever {
|
||||
suspend fun retrieve(sessionId: SessionId, query: String, k: Int): List<RepoKnowledgeHit>
|
||||
}
|
||||
+27
-1
@@ -35,6 +35,7 @@ import com.correx.core.events.events.ArtifactValidatingEvent
|
||||
import com.correx.core.events.events.EventMetadata
|
||||
import com.correx.core.events.events.EventPayload
|
||||
import com.correx.core.events.events.InferenceCompletedEvent
|
||||
import com.correx.core.events.events.RepoKnowledgeRetrievedEvent
|
||||
import com.correx.core.events.events.RepoMapComputedEvent
|
||||
import com.correx.core.events.events.InferenceFailedEvent
|
||||
import com.correx.core.events.events.InferenceStartedEvent
|
||||
@@ -155,6 +156,7 @@ abstract class SessionOrchestrator(
|
||||
private val decisionJournalRepository: DefaultDecisionJournalRepository,
|
||||
private val decisionJournalRenderer: DecisionJournalRenderer = DecisionJournalRenderer(),
|
||||
private val artifactKindRegistry: ArtifactKindRegistry? = null,
|
||||
private val repoKnowledgeRetriever: RepoKnowledgeRetriever? = null,
|
||||
) {
|
||||
private val log = LoggerFactory.getLogger(this::class.java)
|
||||
private val eventStore: EventStore = repositories.eventStore
|
||||
@@ -349,7 +351,7 @@ abstract class SessionOrchestrator(
|
||||
role = EntryRole.SYSTEM,
|
||||
),
|
||||
)
|
||||
val repoMapEntries = buildRepoMapEntries(sessionId)
|
||||
val repoMapEntries = buildContextualRepoEntries(sessionId, stageId, stageConfig)
|
||||
val sessionEvents = eventStore.read(sessionId)
|
||||
val criticIds = criticArtifactIds(sessionEvents, graph, stageId)
|
||||
val criticFrom = sessionEvents
|
||||
@@ -1024,6 +1026,30 @@ abstract class SessionOrchestrator(
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun buildContextualRepoEntries(
|
||||
sessionId: SessionId,
|
||||
stageId: StageId,
|
||||
stageConfig: StageConfig,
|
||||
): List<ContextEntry> {
|
||||
val recorded = eventStore.read(sessionId)
|
||||
.mapNotNull { it.payload as? RepoKnowledgeRetrievedEvent }
|
||||
.lastOrNull { it.stageId == stageId }
|
||||
if (recorded != null) return listOf(buildRelevantFilesEntry(recorded.hits))
|
||||
|
||||
val retriever = repoKnowledgeRetriever ?: return buildRepoMapEntries(sessionId)
|
||||
val query = (stageConfig.metadata["promptInline"] ?: stageConfig.metadata["prompt"] ?: stageId.value)
|
||||
.trim().ifBlank { stageId.value }
|
||||
val hits = runCatching { retriever.retrieve(sessionId, query, REPO_MAP_INJECT_TOP_K) }
|
||||
.getOrElse { e ->
|
||||
if (e is CancellationException) throw e
|
||||
log.warn("repo-knowledge retrieval failed for stage {}: {}", stageId.value, e.message)
|
||||
emptyList()
|
||||
}
|
||||
if (hits.isEmpty()) return buildRepoMapEntries(sessionId)
|
||||
emit(sessionId, RepoKnowledgeRetrievedEvent(sessionId, stageId, query, hits))
|
||||
return listOf(buildRelevantFilesEntry(hits))
|
||||
}
|
||||
|
||||
private suspend fun buildNeedsArtifactEntries(
|
||||
sessionId: SessionId,
|
||||
stageConfig: StageConfig,
|
||||
|
||||
Reference in New Issue
Block a user