add llm processing notification

This commit is contained in:
leonardotrapani
2026-01-31 21:03:24 +01:00
parent 692339f430
commit e2b5871d55
8 changed files with 61 additions and 3 deletions
+4
View File
@@ -91,6 +91,7 @@ type MessageConfig struct {
type MessagesConfig struct { type MessagesConfig struct {
RecordingStarted MessageConfig `toml:"recording_started"` RecordingStarted MessageConfig `toml:"recording_started"`
Transcribing MessageConfig `toml:"transcribing"` Transcribing MessageConfig `toml:"transcribing"`
LLMProcessing MessageConfig `toml:"llm_processing"`
ConfigReloaded MessageConfig `toml:"config_reloaded"` ConfigReloaded MessageConfig `toml:"config_reloaded"`
OperationCancelled MessageConfig `toml:"operation_cancelled"` OperationCancelled MessageConfig `toml:"operation_cancelled"`
RecordingAborted MessageConfig `toml:"recording_aborted"` RecordingAborted MessageConfig `toml:"recording_aborted"`
@@ -721,6 +722,9 @@ keywords = []
# [notifications.messages.transcribing] # [notifications.messages.transcribing]
# title = "Hyprvoice" # title = "Hyprvoice"
# body = "Recording Ended... Transcribing" # body = "Recording Ended... Transcribing"
# [notifications.messages.llm_processing]
# title = "Hyprvoice"
# body = "Processing..."
# [notifications.messages.config_reloaded] # [notifications.messages.config_reloaded]
# title = "Hyprvoice" # title = "Hyprvoice"
# body = "Config Reloaded" # body = "Config Reloaded"
+13
View File
@@ -190,6 +190,7 @@ func (d *Daemon) toggle() {
go d.notifier.Send(notify.MsgRecordingStarted) go d.notifier.Send(notify.MsgRecordingStarted)
go d.monitorPipelineErrors(p) go d.monitorPipelineErrors(p)
go d.monitorPipelineNotifications(p)
case pipeline.Recording: case pipeline.Recording:
d.stopPipeline() d.stopPipeline()
@@ -240,3 +241,15 @@ func (d *Daemon) monitorPipelineErrors(p pipeline.Pipeline) {
} }
} }
} }
func (d *Daemon) monitorPipelineNotifications(p pipeline.Pipeline) {
notifyCh := p.GetNotifyCh()
for {
select {
case mt := <-notifyCh:
d.notifier.Send(mt)
case <-d.ctx.Done():
return
}
}
}
+4
View File
@@ -9,6 +9,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/leonardotrapani/hyprvoice/internal/notify"
"github.com/leonardotrapani/hyprvoice/internal/pipeline" "github.com/leonardotrapani/hyprvoice/internal/pipeline"
) )
@@ -480,3 +481,6 @@ func (m *MockPipeline) GetErrorCh() <-chan pipeline.PipelineError {
return make(chan pipeline.PipelineError) return make(chan pipeline.PipelineError)
} }
func (m *MockPipeline) GetActionCh() chan<- pipeline.Action { return make(chan pipeline.Action) } func (m *MockPipeline) GetActionCh() chan<- pipeline.Action { return make(chan pipeline.Action) }
func (m *MockPipeline) GetNotifyCh() <-chan notify.MessageType {
return make(chan notify.MessageType)
}
+2
View File
@@ -6,6 +6,7 @@ type MessageType int
const ( const (
MsgRecordingStarted MessageType = iota MsgRecordingStarted MessageType = iota
MsgTranscribing MsgTranscribing
MsgLLMProcessing
MsgConfigReloaded MsgConfigReloaded
MsgOperationCancelled MsgOperationCancelled
MsgRecordingAborted MsgRecordingAborted
@@ -25,6 +26,7 @@ type MessageDef struct {
var MessageDefs = []MessageDef{ var MessageDefs = []MessageDef{
{MsgRecordingStarted, "recording_started", "Hyprvoice", "Recording Started", false}, {MsgRecordingStarted, "recording_started", "Hyprvoice", "Recording Started", false},
{MsgTranscribing, "transcribing", "Hyprvoice", "Recording Ended... Transcribing", false}, {MsgTranscribing, "transcribing", "Hyprvoice", "Recording Ended... Transcribing", false},
{MsgLLMProcessing, "llm_processing", "Hyprvoice", "Processing...", false},
{MsgConfigReloaded, "config_reloaded", "Hyprvoice", "Config Reloaded", false}, {MsgConfigReloaded, "config_reloaded", "Hyprvoice", "Config Reloaded", false},
{MsgOperationCancelled, "operation_cancelled", "Hyprvoice", "Operation Cancelled", false}, {MsgOperationCancelled, "operation_cancelled", "Hyprvoice", "Operation Cancelled", false},
{MsgRecordingAborted, "recording_aborted", "", "Recording Aborted", true}, {MsgRecordingAborted, "recording_aborted", "", "Recording Aborted", true},
+2 -2
View File
@@ -98,8 +98,8 @@ func TestNotifierInterface(t *testing.T) {
func TestMessageDefs(t *testing.T) { func TestMessageDefs(t *testing.T) {
// Verify MessageDefs contains expected entries // Verify MessageDefs contains expected entries
if len(MessageDefs) != 6 { if len(MessageDefs) != 7 {
t.Errorf("Expected 6 MessageDefs, got %d", len(MessageDefs)) t.Errorf("Expected 7 MessageDefs, got %d", len(MessageDefs))
} }
// Verify each has required fields // Verify each has required fields
+19
View File
@@ -9,6 +9,7 @@ import (
"github.com/leonardotrapani/hyprvoice/internal/config" "github.com/leonardotrapani/hyprvoice/internal/config"
"github.com/leonardotrapani/hyprvoice/internal/injection" "github.com/leonardotrapani/hyprvoice/internal/injection"
"github.com/leonardotrapani/hyprvoice/internal/llm" "github.com/leonardotrapani/hyprvoice/internal/llm"
"github.com/leonardotrapani/hyprvoice/internal/notify"
"github.com/leonardotrapani/hyprvoice/internal/recording" "github.com/leonardotrapani/hyprvoice/internal/recording"
"github.com/leonardotrapani/hyprvoice/internal/transcriber" "github.com/leonardotrapani/hyprvoice/internal/transcriber"
) )
@@ -41,12 +42,14 @@ type Pipeline interface {
Status() Status Status() Status
GetActionCh() chan<- Action GetActionCh() chan<- Action
GetErrorCh() <-chan PipelineError GetErrorCh() <-chan PipelineError
GetNotifyCh() <-chan notify.MessageType
} }
type pipeline struct { type pipeline struct {
status Status status Status
actionCh chan Action actionCh chan Action
errorCh chan PipelineError errorCh chan PipelineError
notifyCh chan notify.MessageType
config *config.Config config *config.Config
mu sync.RWMutex mu sync.RWMutex
@@ -61,6 +64,7 @@ func New(cfg *config.Config) Pipeline {
return &pipeline{ return &pipeline{
actionCh: make(chan Action, 1), actionCh: make(chan Action, 1),
errorCh: make(chan PipelineError, 10), errorCh: make(chan PipelineError, 10),
notifyCh: make(chan notify.MessageType, 10),
config: cfg, config: cfg,
} }
} }
@@ -189,6 +193,12 @@ func (p *pipeline) GetErrorCh() <-chan PipelineError {
return p.errorCh return p.errorCh
} }
func (p *pipeline) GetNotifyCh() <-chan notify.MessageType {
p.mu.RLock()
defer p.mu.RUnlock()
return p.notifyCh
}
func (p *pipeline) sendError(title, message string, err error) { func (p *pipeline) sendError(title, message string, err error) {
pipelineErr := PipelineError{ pipelineErr := PipelineError{
Title: title, Title: title,
@@ -203,6 +213,14 @@ func (p *pipeline) sendError(title, message string, err error) {
} }
} }
func (p *pipeline) sendNotify(mt notify.MessageType) {
select {
case p.notifyCh <- mt:
default:
log.Printf("Pipeline: Notify channel full, dropping notification")
}
}
func (p *pipeline) handleInjectAction(ctx context.Context, recorder *recording.Recorder, t transcriber.Transcriber) { func (p *pipeline) handleInjectAction(ctx context.Context, recorder *recording.Recorder, t transcriber.Transcriber) {
status := p.Status() status := p.Status()
@@ -232,6 +250,7 @@ func (p *pipeline) handleInjectAction(ctx context.Context, recorder *recording.R
textToInject := transcriptionText textToInject := transcriptionText
if p.config.IsLLMEnabled() { if p.config.IsLLMEnabled() {
p.setStatus(Processing) p.setStatus(Processing)
p.sendNotify(notify.MsgLLMProcessing)
log.Printf("Pipeline: LLM post-processing enabled, processing text") log.Printf("Pipeline: LLM post-processing enabled, processing text")
llmCfg := p.config.ToLLMConfig() llmCfg := p.config.ToLLMConfig()
+16
View File
@@ -189,3 +189,19 @@ Key decisions:
- Providers section at top for visibility - Providers section at top for visibility
- Keywords before any table definitions (TOML syntax requirement) - Keywords before any table definitions (TOML syntax requirement)
- Concise comments, full reference at bottom - Concise comments, full reference at bottom
## Task 11: Add LLM processing notification - COMPLETE
Added notification when LLM post-processing starts:
- Added `MsgLLMProcessing` to `notify/message.go` (default: "Hyprvoice", "Processing...")
- Added `LLMProcessing` field to `MessagesConfig` in config.go (toml: `llm_processing`)
- Added notification channel to pipeline (`GetNotifyCh()` method)
- Pipeline sends `MsgLLMProcessing` when entering Processing status
- Daemon monitors `notifyCh` via `monitorPipelineNotifications` goroutine
- Updated default config template with `llm_processing` message example
- Fixed tests: MockPipeline implements `GetNotifyCh`, notify test expects 7 MessageDefs
Key decisions:
- Notification channel approach (vs direct notifier access) keeps pipeline decoupled
- Notification sent at same time status changes to Processing
- Configurable like all other notifications via `[notifications.messages.llm_processing]`
+1 -1
View File
@@ -216,7 +216,7 @@
"Configurable in config", "Configurable in config",
"Typecheck passes" "Typecheck passes"
], ],
"passes": false "passes": true
}, },
{ {
"title": "Update README documentation", "title": "Update README documentation",