* 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:
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 }
|
||||
|
||||
@@ -62,6 +62,15 @@ object EventParser {
|
||||
WsEvent.ThinkingDelta(token, sessionId)
|
||||
}
|
||||
|
||||
"reasoning.delta" -> {
|
||||
val token = payload?.get("text") as? String ?: ""
|
||||
WsEvent.ReasoningDelta(token, sessionId)
|
||||
}
|
||||
|
||||
"reasoning.available" -> {
|
||||
WsEvent.ReasoningAvailable(sessionId)
|
||||
}
|
||||
|
||||
"message.complete" -> {
|
||||
val text = payload?.get("text") as? String ?: ""
|
||||
WsEvent.MessageComplete(text, sessionId)
|
||||
|
||||
@@ -30,6 +30,15 @@ sealed class WsEvent {
|
||||
val sessionId: String?,
|
||||
) : WsEvent()
|
||||
|
||||
data class ReasoningDelta(
|
||||
val token: String,
|
||||
val sessionId: String?,
|
||||
) : WsEvent()
|
||||
|
||||
data class ReasoningAvailable(
|
||||
val sessionId: String?,
|
||||
) : WsEvent()
|
||||
|
||||
data class MessageComplete(
|
||||
val text: String,
|
||||
val sessionId: String?,
|
||||
|
||||
Reference in new issue
Block a user