Detail: personality picker beside Re-summarize

Chips: Neutral / Sarcastic / Funny / Dry wit. The chosen voice rides
the reprocess call and the button echoes it ('Re-summarize · Sarcastic');
an existing summary shows '· in a sarcastic voice' next to its heading.
compileKotlin + 39 unit tests green.
This commit is contained in:
avi 2026-09-15 14:24:36 -05:00
commit 994dbc3690
4 changed files with 61 additions and 10 deletions

View file

@ -311,11 +311,16 @@ class CustomShonarProvider(
* per-recording override). 409 = stage already running or nothing to
* summarize; surfaced as ProviderError.Transient with the server's
* detail message. */
suspend fun reprocess(recordingId: String, job: String, model: String? = null): String =
suspend fun reprocess(recordingId: String, job: String, model: String? = null,
tone: String? = null): String =
withContext(Dispatchers.IO) {
ensureConnected()
val base = currentOrigin()
val q = if (model != null) "&model=$model" else ""
val q = buildString {
if (model != null) append("&model=$model")
if (tone != null)
append("&tone=").append(java.net.URLEncoder.encode(tone, "UTF-8"))
}
executeAuthed(base) { token ->
post(base, "/api/v1/recordings/$recordingId/reprocess?job=$job$q", token, "{}")
}.use { resp ->

View file

@ -37,6 +37,8 @@ data class SummaryData(
val version: Int,
val provider: String = "",
val model: String? = null,
/** Voice the summary was written in ("sarcastic", …); null = neutral. */
val tone: String? = null,
/** Raw content dict; known keys read via [summaryString]/[summaryList]. */
val content: Map<String, Any?> = emptyMap(),
val editedByUser: Boolean = false,
@ -112,6 +114,7 @@ fun parseSummary(raw: String): SummaryData? = runCatching {
version = json.optInt("version", 0),
provider = json.optString("provider", ""),
model = json.optString("model", null).takeUnless { it.isNullOrBlank() },
tone = json.optString("tone", null).takeUnless { it.isNullOrBlank() },
content = content,
editedByUser = json.optBoolean("edited_by_user", false),
)