refactor(context): aggregate resolved blocks by source snapshots and centralize elapsed-time activation refresh

This commit is contained in:
2026-03-25 20:13:55 +08:00
parent 264a45c85f
commit a9b925c614

View File

@@ -40,8 +40,8 @@ class ContextWorkspace {
val iterator = stateSet.iterator() val iterator = stateSet.iterator()
while (iterator.hasNext()) { while (iterator.hasNext()) {
val block = iterator.next() val block = iterator.next()
val activationScore = block.applyTimeFade() val fadedScore = block.applyTimeFade()
if (activationScore <= 0.0) { if (fadedScore <= 0.0) {
iterator.remove() iterator.remove()
continue continue
} }
@@ -51,6 +51,12 @@ class ContextWorkspace {
continue continue
} }
val activationScore = block.activate()
if (activationScore <= 0.0) {
iterator.remove()
continue
}
activeBlocks += ResolvedContextBlock( activeBlocks += ResolvedContextBlock(
block = block, block = block,
domainWeight = matchedDomains.sumOf { domainWeights.getValue(it) }, domainWeight = matchedDomains.sumOf { domainWeights.getValue(it) },
@@ -67,15 +73,25 @@ class ContextWorkspace {
.thenBy { it.activationScore } .thenBy { it.activationScore }
.thenBy { it.block.blockContent.encodeToXmlString() } .thenBy { it.block.blockContent.encodeToXmlString() }
) )
.map { resolved -> .groupBy { it.block.sourceKey }
if (resolved.forceFullRender) { .values
.map { groupedBlocks ->
if (groupedBlocks.size == 1) {
renderResolvedBlock(groupedBlocks.first())
} else {
AggregatedBlockContent(groupedBlocks)
}
}
ResolvedContext(blocks)
}
private fun renderResolvedBlock(resolved: ResolvedContextBlock): BlockContent {
return if (resolved.forceFullRender) {
resolved.block.blockContent resolved.block.blockContent
} else { } else {
resolved.block.render() resolved.block.render()
} }
} }
ResolvedContext(blocks)
}
/** /**
@@ -159,25 +175,29 @@ data class ContextBlock @JvmOverloads constructor(
get() = SourceKey(blockContent.blockName, blockContent.source) get() = SourceKey(blockContent.blockName, blockContent.source)
fun applyTimeFade(): Double { fun applyTimeFade(): Double {
val now = Instant.now() refreshByElapsedTime()
val elapsedSeconds = Duration.between(lastTouchedAt, now).toMillis() / 1000.0
activationScore = max(0.0, activationScore - elapsedSeconds * (timeFadeFactor / 60.0))
lastTouchedAt = now
return activationScore return activationScore
} }
fun applyReplaceFade(): Double { fun applyReplaceFade(): Double {
applyTimeFade() refreshByElapsedTime()
activationScore = max(0.0, activationScore - replaceFadeFactor) activationScore = max(0.0, activationScore - replaceFadeFactor)
return activationScore return activationScore
} }
fun activate(): Double { fun activate(): Double {
applyTimeFade() refreshByElapsedTime()
activationScore = min(100.0, activationScore + activateFactor) activationScore = min(100.0, activationScore + activateFactor)
return activationScore return activationScore
} }
private fun refreshByElapsedTime() {
val now = Instant.now()
val elapsedSeconds = Duration.between(lastTouchedAt, now).toMillis() / 1000.0
activationScore = max(0.0, activationScore - elapsedSeconds * (timeFadeFactor / 60.0))
lastTouchedAt = now
}
fun sameWith(contextBlock: ContextBlock): Boolean { fun sameWith(contextBlock: ContextBlock): Boolean {
return this.sourceKey == contextBlock.sourceKey return this.sourceKey == contextBlock.sourceKey
} }
@@ -196,6 +216,46 @@ data class ContextBlock @JvmOverloads constructor(
) )
} }
private class AggregatedBlockContent(
private val groupedBlocks: List<ResolvedContextBlock>
) : BlockContent(
groupedBlocks.first().block.sourceKey.blockName,
groupedBlocks.first().block.sourceKey.source,
groupedBlocks.maxByOrNull {
if (it.forceFullRender) it.block.blockContent.urgency.ordinal else it.block.render().urgency.ordinal
}?.let {
if (it.forceFullRender) it.block.blockContent.urgency else it.block.render().urgency
} ?: Urgency.NORMAL
) {
override fun fillXml(document: Document, root: Element) {
val snapshotIndex = groupedBlocks.withIndex()
.maxWithOrNull(
compareBy<IndexedValue<ResolvedContextBlock>> { it.value.activationScore }
.thenBy { it.index }
)?.index ?: 0
groupedBlocks.forEachIndexed { index, groupedBlock ->
val tagName = if (index == snapshotIndex) "snapshot" else "history_snapshot"
val wrapper = document.createElement(tagName)
val renderedBlock = if (groupedBlock.forceFullRender) {
groupedBlock.block.blockContent
} else {
groupedBlock.block.render()
}
wrapper.setAttribute("source", renderedBlock.source)
wrapper.setAttribute("urgency", renderedBlock.urgency.name.lowercase(Locale.ROOT))
root.appendChild(wrapper)
val encoded = renderedBlock.encodeToXml()
val childNodes = encoded.childNodes
for (childIndex in 0 until childNodes.length) {
wrapper.appendChild(document.importNode(childNodes.item(childIndex), true))
}
}
}
}
abstract class BlockContent @JvmOverloads protected constructor( abstract class BlockContent @JvmOverloads protected constructor(
val blockName: String, val blockName: String,
val source: String, val source: String,