Compare commits
4 Commits
beb944c390
...
850ca103e5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
850ca103e5 | ||
|
|
b7b5fcbf79 | ||
|
|
1e13c7796d | ||
|
|
9a727b21ad |
62
extra/tts.go
62
extra/tts.go
@@ -92,8 +92,6 @@ func (o *KokoroOrator) stoproutine() {
|
|||||||
|
|
||||||
func (o *KokoroOrator) readroutine() {
|
func (o *KokoroOrator) readroutine() {
|
||||||
tokenizer, _ := english.NewSentenceTokenizer(nil)
|
tokenizer, _ := english.NewSentenceTokenizer(nil)
|
||||||
// var sentenceBuf bytes.Buffer
|
|
||||||
// var remainder strings.Builder
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case chunk := <-TTSTextChan:
|
case chunk := <-TTSTextChan:
|
||||||
@@ -106,24 +104,28 @@ func (o *KokoroOrator) readroutine() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
text := o.textBuffer.String()
|
text := o.textBuffer.String()
|
||||||
o.mu.Unlock()
|
|
||||||
sentences := tokenizer.Tokenize(text)
|
sentences := tokenizer.Tokenize(text)
|
||||||
o.logger.Debug("adding chunk", "chunk", chunk, "text", text, "sen-len", len(sentences))
|
o.logger.Debug("adding chunk", "chunk", chunk, "text", text, "sen-len", len(sentences))
|
||||||
for i, sentence := range sentences {
|
if len(sentences) <= 1 {
|
||||||
if i == len(sentences)-1 { // last sentence
|
o.mu.Unlock()
|
||||||
o.mu.Lock()
|
continue
|
||||||
o.textBuffer.Reset()
|
}
|
||||||
_, err := o.textBuffer.WriteString(sentence.Text)
|
completeSentences := sentences[:len(sentences)-1]
|
||||||
o.mu.Unlock()
|
remaining := sentences[len(sentences)-1].Text
|
||||||
if err != nil {
|
o.textBuffer.Reset()
|
||||||
o.logger.Warn("failed to write to stringbuilder", "error", err)
|
o.textBuffer.WriteString(remaining)
|
||||||
continue
|
o.mu.Unlock()
|
||||||
}
|
|
||||||
continue // if only one (often incomplete) sentence; wait for next chunk
|
for _, sentence := range completeSentences {
|
||||||
|
o.mu.Lock()
|
||||||
|
interrupted := o.interrupt
|
||||||
|
o.mu.Unlock()
|
||||||
|
if interrupted {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
cleanedText := models.CleanText(sentence.Text)
|
cleanedText := models.CleanText(sentence.Text)
|
||||||
if cleanedText == "" {
|
if cleanedText == "" {
|
||||||
continue // Skip empty text after cleaning
|
continue
|
||||||
}
|
}
|
||||||
o.logger.Debug("calling Speak with sentence", "sent", cleanedText)
|
o.logger.Debug("calling Speak with sentence", "sent", cleanedText)
|
||||||
if err := o.Speak(cleanedText); err != nil {
|
if err := o.Speak(cleanedText); err != nil {
|
||||||
@@ -338,24 +340,28 @@ func (o *GoogleTranslateOrator) readroutine() {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
text := o.textBuffer.String()
|
text := o.textBuffer.String()
|
||||||
o.mu.Unlock()
|
|
||||||
sentences := tokenizer.Tokenize(text)
|
sentences := tokenizer.Tokenize(text)
|
||||||
o.logger.Debug("adding chunk", "chunk", chunk, "text", text, "sen-len", len(sentences))
|
o.logger.Debug("adding chunk", "chunk", chunk, "text", text, "sen-len", len(sentences))
|
||||||
for i, sentence := range sentences {
|
if len(sentences) <= 1 {
|
||||||
if i == len(sentences)-1 { // last sentence
|
o.mu.Unlock()
|
||||||
o.mu.Lock()
|
continue
|
||||||
o.textBuffer.Reset()
|
}
|
||||||
_, err := o.textBuffer.WriteString(sentence.Text)
|
completeSentences := sentences[:len(sentences)-1]
|
||||||
o.mu.Unlock()
|
remaining := sentences[len(sentences)-1].Text
|
||||||
if err != nil {
|
o.textBuffer.Reset()
|
||||||
o.logger.Warn("failed to write to stringbuilder", "error", err)
|
o.textBuffer.WriteString(remaining)
|
||||||
continue
|
o.mu.Unlock()
|
||||||
}
|
|
||||||
continue // if only one (often incomplete) sentence; wait for next chunk
|
for _, sentence := range completeSentences {
|
||||||
|
o.mu.Lock()
|
||||||
|
interrupted := o.interrupt
|
||||||
|
o.mu.Unlock()
|
||||||
|
if interrupted {
|
||||||
|
return
|
||||||
}
|
}
|
||||||
cleanedText := models.CleanText(sentence.Text)
|
cleanedText := models.CleanText(sentence.Text)
|
||||||
if cleanedText == "" {
|
if cleanedText == "" {
|
||||||
continue // Skip empty text after cleaning
|
continue
|
||||||
}
|
}
|
||||||
o.logger.Debug("calling Speak with sentence", "sent", cleanedText)
|
o.logger.Debug("calling Speak with sentence", "sent", cleanedText)
|
||||||
if err := o.Speak(cleanedText); err != nil {
|
if err := o.Speak(cleanedText); err != nil {
|
||||||
|
|||||||
21
helpfuncs.go
21
helpfuncs.go
@@ -108,14 +108,17 @@ func refreshChatDisplay() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// stopTTSIfNotForUser: character specific context, not meant fot the human to hear
|
||||||
func stopTTSIfNotForUser(msg *models.RoleMsg) {
|
func stopTTSIfNotForUser(msg *models.RoleMsg) {
|
||||||
|
if strings.Contains(cfg.CurrentAPI, "/chat") || !cfg.CharSpecificContextEnabled {
|
||||||
|
return
|
||||||
|
}
|
||||||
viewingAs := cfg.UserRole
|
viewingAs := cfg.UserRole
|
||||||
if cfg.WriteNextMsgAs != "" {
|
if cfg.WriteNextMsgAs != "" {
|
||||||
viewingAs = cfg.WriteNextMsgAs
|
viewingAs = cfg.WriteNextMsgAs
|
||||||
}
|
}
|
||||||
// stop tts if msg is not for user
|
// stop tts if msg is not for user
|
||||||
if cfg.CharSpecificContextEnabled &&
|
if !slices.Contains(msg.KnownTo, viewingAs) && cfg.TTS_ENABLED {
|
||||||
!slices.Contains(msg.KnownTo, viewingAs) && cfg.TTS_ENABLED {
|
|
||||||
TTSDoneChan <- true
|
TTSDoneChan <- true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -354,13 +357,17 @@ func makeStatusLine() string {
|
|||||||
}
|
}
|
||||||
// Get model color based on load status for local llama.cpp models
|
// Get model color based on load status for local llama.cpp models
|
||||||
modelColor := getModelColor()
|
modelColor := getModelColor()
|
||||||
statusLine := fmt.Sprintf(statusLineTempl, boolColors[botRespMode], botRespMode, activeChatName,
|
statusLine := fmt.Sprintf(statusLineTempl, boolColors[botRespMode], activeChatName,
|
||||||
boolColors[cfg.ToolUse], cfg.ToolUse, modelColor, chatBody.Model, boolColors[cfg.SkipLLMResp],
|
boolColors[cfg.ToolUse], modelColor, chatBody.Model, boolColors[cfg.SkipLLMResp],
|
||||||
cfg.SkipLLMResp, cfg.CurrentAPI, boolColors[isRecording], isRecording, persona,
|
cfg.CurrentAPI, persona, botPersona)
|
||||||
botPersona)
|
if cfg.STT_ENABLED {
|
||||||
|
recordingS := fmt.Sprintf(" | [%s:-:b]voice recording[-:-:-] (ctrl+r)",
|
||||||
|
boolColors[isRecording])
|
||||||
|
statusLine += recordingS
|
||||||
|
}
|
||||||
// completion endpoint
|
// completion endpoint
|
||||||
if !strings.Contains(cfg.CurrentAPI, "chat") {
|
if !strings.Contains(cfg.CurrentAPI, "chat") {
|
||||||
roleInject := fmt.Sprintf(" | role injection [%s:-:b]%v[-:-:-] (alt+7)", boolColors[injectRole], injectRole)
|
roleInject := fmt.Sprintf(" | [%s:-:b]role injection[-:-:-] (alt+7)", boolColors[injectRole])
|
||||||
statusLine += roleInject
|
statusLine += roleInject
|
||||||
}
|
}
|
||||||
return statusLine + imageInfo + shellModeInfo
|
return statusLine + imageInfo + shellModeInfo
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -13,7 +13,7 @@ var (
|
|||||||
selectedIndex = int(-1)
|
selectedIndex = int(-1)
|
||||||
shellMode = false
|
shellMode = false
|
||||||
thinkingCollapsed = false
|
thinkingCollapsed = false
|
||||||
statusLineTempl = "help (F12) | llm turn: [%s:-:b]%v[-:-:-] (F6) | chat: [orange:-:b]%s[-:-:-] (F1) |tool-use: [%s:-:b]%v[-:-:-] (ctrl+k) | model: [%s:-:b]%s[-:-:-] (ctrl+l) | skip LLM resp: [%s:-:b]%v[-:-:-] (F10)\nAPI: [orange:-:b]%s[-:-:-] (ctrl+v) | voice recording: [%s:-:b]%v[-:-:-] (ctrl+r) | writing as: [orange:-:b]%s[-:-:-] (ctrl+q) | bot will write as [orange:-:b]%s[-:-:-] (ctrl+x)"
|
statusLineTempl = "help (F12) | [%s:-:b]llm writes[-:-:-] (F6 to interrupt) | chat: [orange:-:b]%s[-:-:-] (F1) | [%s:-:b]tool use[-:-:-] (ctrl+k) | model: [%s:-:b]%s[-:-:-] (ctrl+l) | [%s:-:b]skip LLM resp[-:-:-] (F10)\nAPI: [orange:-:b]%s[-:-:-] (ctrl+v) | writing as: [orange:-:b]%s[-:-:-] (ctrl+q) | bot will write as [orange:-:b]%s[-:-:-] (ctrl+x)"
|
||||||
focusSwitcher = map[tview.Primitive]tview.Primitive{}
|
focusSwitcher = map[tview.Primitive]tview.Primitive{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -61,14 +61,11 @@ func showModelSelectionPopup() {
|
|||||||
modelListWidget.SetCurrentItem(currentModelIndex)
|
modelListWidget.SetCurrentItem(currentModelIndex)
|
||||||
}
|
}
|
||||||
modelListWidget.SetSelectedFunc(func(index int, mainText string, secondaryText string, shortcut rune) {
|
modelListWidget.SetSelectedFunc(func(index int, mainText string, secondaryText string, shortcut rune) {
|
||||||
// Strip "(loaded)" suffix if present for local llama.cpp models
|
|
||||||
modelName := strings.TrimPrefix(mainText, "(loaded) ")
|
modelName := strings.TrimPrefix(mainText, "(loaded) ")
|
||||||
// Update the model in both chatBody and config
|
|
||||||
chatBody.Model = modelName
|
chatBody.Model = modelName
|
||||||
cfg.CurrentModel = chatBody.Model
|
cfg.CurrentModel = chatBody.Model
|
||||||
// Remove the popup page
|
|
||||||
pages.RemovePage("modelSelectionPopup")
|
pages.RemovePage("modelSelectionPopup")
|
||||||
// Update the status line to reflect the change
|
updateCachedModelColor()
|
||||||
updateStatusLine()
|
updateStatusLine()
|
||||||
})
|
})
|
||||||
modelListWidget.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
modelListWidget.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
@@ -162,10 +159,9 @@ func showAPILinkSelectionPopup() {
|
|||||||
chatBody.Model = newModelList[0]
|
chatBody.Model = newModelList[0]
|
||||||
cfg.CurrentModel = chatBody.Model
|
cfg.CurrentModel = chatBody.Model
|
||||||
}
|
}
|
||||||
// Remove the popup page
|
|
||||||
pages.RemovePage("apiLinkSelectionPopup")
|
pages.RemovePage("apiLinkSelectionPopup")
|
||||||
// Update the parser and status line to reflect the change
|
|
||||||
choseChunkParser()
|
choseChunkParser()
|
||||||
|
updateCachedModelColor()
|
||||||
updateStatusLine()
|
updateStatusLine()
|
||||||
})
|
})
|
||||||
apiListWidget.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
apiListWidget.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
|
|||||||
Reference in New Issue
Block a user