Skip to content

feat: add iterative refinement to Reply Drafter (#2298)#2299

Merged
OwenMcGirr merged 1 commit into
mainfrom
feature/reply-drafter-guidance-2298
May 23, 2026
Merged

feat: add iterative refinement to Reply Drafter (#2298)#2299
OwenMcGirr merged 1 commit into
mainfrom
feature/reply-drafter-guidance-2298

Conversation

@OwenMcGirr

Copy link
Copy Markdown
Collaborator

Summary

Reply Drafter was one-shot today — open it, get 3-5 suggestions, tap one. When none fit, the only escape was to close and reopen and get a fresh roll of the dice. This adds a text field + Regenerate button at the bottom of the suggestions list for iterative refinement.

The key architectural choice: refinement sends the model the image + the previous reply set + the user's note, framed as "Here's what you wrote, here's what the user wants different, produce a new set." The note can't be mistaken for the message to reply to because the actual conversation is in the image and the previous replies clearly bracket what's being refined.

  • First open is unchanged. Empty previousReplies collapses the prompt to the original RULES_BLOCK + FORMAT_BLOCK, byte-for-byte identical to today.
  • Subsequent Regenerates carry the latest set forward, so you can iteratively tighten ("be more formal" → "now shorter" → "drop the question one").
  • Failed-state Retry uses fresh draft(), not refinement — nothing useful to refine against if generation failed.
  • Bottom placement is deliberate for switch-access: suggestion cards stay the primary scan targets; the field is the last scan stop and can be skipped entirely.

Files:

  • service/llm/ReplyDrafterTask.ktobjectdata class with previousReplies + refinement. Computed prompt inserts a REFINEMENT block between the rules and format blocks when previousReplies is non-empty. Format block stays last (recency benefit preserved).
  • screens/replydrafter/ReplyDrafterViewModel.kt — tracks lastReplies, exposes setGuidance(text) (200-char cap), regenerate(). draft() stays fresh; regenerate() refines.
  • screens/replydrafter/ReplyDrafterScreen.kt — pinned GuidanceInputRow (OutlinedTextField + Refresh IconButton), visible in Suggestions and Failed states.
  • screens/replydrafter/ReplyDrafterActivity.kt — observes guidance state, wires setGuidance and regenerate callbacks.
  • 2 new strings; 14 unit tests pass (11 parser unchanged + 3 new prompt-shape).

Closes #2298

Test plan

  • ./gradlew compileDebugKotlin testDebugUnitTest installDebug
  • Manual on S26 (AICore): first draft unchanged; "be more formal" produces formal alternatives; clearing the field still re-rolls
  • Manual: iterative chain ("be more formal" → "now shorter" → "drop the decline one") visibly refines step by step
  • Manual: MediaPipe fallback on a non-AICore device behaves the same (with weaker instruction-following — expected)

Reply Drafter was one-shot — open it, get 3-5 suggestions, tap one.
When none fit, the only escape was to close and reopen. This adds a
text field + Regenerate button at the bottom of the suggestions list
for iterative refinement.

The refinement passes the previous reply set AND the user's note to
the model, framed as "Here's what you wrote, here's what the user
wants different, produce a new set." That framing is unambiguous —
the model can't mistake the note for the message to reply to because
the actual conversation is in the image and the previous replies
clearly bracket what's being refined.

First open is unchanged: empty previousReplies collapses to the
original RULES_BLOCK + FORMAT_BLOCK, byte-for-byte. Failed-state
Retry also uses fresh draft() rather than refinement, since there
is nothing useful to refine against.

- ReplyDrafterTask: object -> data class with previousReplies +
  refinement; computed prompt inserts a REFINEMENT block between the
  rules and format blocks when previousReplies is non-empty
- ReplyDrafterViewModel: tracks lastReplies, exposes setGuidance()
  (200-char cap) + regenerate(); draft() stays fresh, regenerate()
  refines
- ReplyDrafterScreen: pinned GuidanceInputRow with OutlinedTextField
  + Refresh IconButton, visible in Suggestions and Failed states
- Bottom placement is deliberate for switch-access: suggestion cards
  stay the primary scan targets; the field is the last scan stop
- 2 new strings; 14 unit tests pass (11 parser + 3 new prompt-shape)

Closes #2298
@OwenMcGirr OwenMcGirr merged commit e046fa7 into main May 23, 2026
@OwenMcGirr OwenMcGirr deleted the feature/reply-drafter-guidance-2298 branch May 23, 2026 17:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reply Drafter: add a guidance text field

1 participant