feat(#525): render reasoning.delta / reasoning.available stream (#543)

* feat(#525): add ReasoningDelta + ReasoningAvailable WsEvent types

* feat(#525): parse reasoning.delta + reasoning.available; route through reducer + StreamingState

* feat(#525): add reasoning streaming buffer to ChatStreamingController

* feat(#525): dispatch ReasoningDelta/ReasoningAvailable in ChatViewModel

* feat(#525): render collapsible ReasoningIndicator in chat list

* fix(#525): address review — icons+semantics, LocalHermesStatusColors, drop redundant ReasoningAvailable branch

* feat(#525): persist reasoningText on model, entity, mapper + Room migration 3->4

* feat(#525): reducer + streaming carry reasoningText onto message

* feat(#525): render persisted reasoning above each assistant bubble, drop global indicator

* nix: flake

* nix: flake

---------

Co-authored-by: M57 <m57@hermes-control.app>
This commit is contained in:
M57andM57 authored and GitHub committed 2026-07-10 23:41:53 +03:00
1 parent d04b74ee0a
commit 53b3966931
17 files changed
+441 -33

No files matched your search

@@ -28,6 +28,8 @@ data class ChatMessageEntity(
val role: String,
@ColumnInfo(name = "content")
val content: String,
@ColumnInfo(name = "reasoning_text")
val reasoningText: String = "",
@ColumnInfo(name = "timestamp")
val timestamp: Long,
@ColumnInfo(name = "tool_name")
@@ -22,6 +22,7 @@ fun ChatMessageEntity.toUiModel(): ChatMessage =
else -> MessageRole.ASSISTANT
},
content = content,
reasoningText = reasoningText,
timestamp = timestamp,
isStreaming = isStreaming,
toolName = toolName,
@@ -40,6 +41,7 @@ fun ChatMessage.toEntity(sessionId: String): ChatMessageEntity =
sessionId = sessionId,
role = role.name,
content = content,
reasoningText = reasoningText,
timestamp = timestamp,
toolName = toolName,
toolStatus = toolStatus?.name,
@@ -11,7 +11,7 @@ import java.io.File
@Database(
entities = [ChatMessageEntity::class],
version = 3,
version = 4,
exportSchema = true,
)
abstract class HermesDatabase : RoomDatabase() {
@@ -45,13 +45,22 @@ abstract class HermesDatabase : RoomDatabase() {
}
}
val migration3to4 =
object : Migration(3, 4) {
override fun migrate(db: SupportSQLiteDatabase) {
db.execSQL(
"ALTER TABLE `chat_messages` ADD COLUMN `reasoning_text` TEXT NOT NULL DEFAULT ''",
)
}
}
instance ?: Room
.databaseBuilder(
context.applicationContext,
HermesDatabase::class.java,
"hermes_control.db",
).openHelperFactory(factory)
.addMigrations(migration2to3)
.addMigrations(migration2to3, migration3to4)
.fallbackToDestructiveMigration(false)
.build()
.also { instance = it }