Feat: add linter

This commit is contained in:
Grail Finder
2024-11-28 17:57:50 +03:00
parent 14d706f94a
commit 34d415c930
9 changed files with 109 additions and 59 deletions

1
.gitignore vendored
View File

@@ -4,3 +4,4 @@ testlog
elefant elefant
history/ history/
*.db *.db
config.toml

32
.golangci.yml Normal file
View File

@@ -0,0 +1,32 @@
run:
timeout: 1m
concurrency: 2
tests: false
linters:
enable-all: false
disable-all: true
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- typecheck
- unused
- prealloc
presets:
- performance
linters-settings:
funlen:
lines: 80
statements: 50
lll:
line-length: 80
issues:
exclude:
# Display all issues
max-issues-per-linter: 0
max-same-issues: 0

2
Makefile Normal file
View File

@@ -0,0 +1,2 @@
lint: ## Run linters. Use make install-linters first.
golangci-lint run -c .golangci.yml ./...

72
bot.go
View File

@@ -53,15 +53,15 @@ func formMsg(chatBody *models.ChatBody, newMsg, role string) io.Reader {
} }
// func sendMsgToLLM(body io.Reader) (*models.LLMRespChunk, error) { // func sendMsgToLLM(body io.Reader) (*models.LLMRespChunk, error) {
func sendMsgToLLM(body io.Reader) (any, error) { func sendMsgToLLM(body io.Reader) {
// nolint
resp, err := httpClient.Post(cfg.APIURL, "application/json", body) resp, err := httpClient.Post(cfg.APIURL, "application/json", body)
if err != nil { if err != nil {
logger.Error("llamacpp api", "error", err) logger.Error("llamacpp api", "error", err)
return nil, err return
} }
defer resp.Body.Close() defer resp.Body.Close()
llmResp := []models.LLMRespChunk{} // llmResp := []models.LLMRespChunk{}
// chunkChan <- cfg.AssistantIcon
reader := bufio.NewReader(resp.Body) reader := bufio.NewReader(resp.Body)
counter := 0 counter := 0
for { for {
@@ -90,9 +90,9 @@ func sendMsgToLLM(body io.Reader) (any, error) {
if err := json.Unmarshal(line, &llmchunk); err != nil { if err := json.Unmarshal(line, &llmchunk); err != nil {
logger.Error("failed to decode", "error", err, "line", string(line)) logger.Error("failed to decode", "error", err, "line", string(line))
streamDone <- true streamDone <- true
return nil, err return
} }
llmResp = append(llmResp, llmchunk) // llmResp = append(llmResp, llmchunk)
// logger.Info("streamview", "chunk", llmchunk) // logger.Info("streamview", "chunk", llmchunk)
// if llmchunk.Choices[len(llmchunk.Choices)-1].FinishReason != "chat.completion.chunk" { // if llmchunk.Choices[len(llmchunk.Choices)-1].FinishReason != "chat.completion.chunk" {
if llmchunk.Choices[len(llmchunk.Choices)-1].FinishReason == "stop" { if llmchunk.Choices[len(llmchunk.Choices)-1].FinishReason == "stop" {
@@ -105,7 +105,6 @@ func sendMsgToLLM(body io.Reader) (any, error) {
answerText := strings.ReplaceAll(llmchunk.Choices[0].Delta.Content, "\n\n", "\n") answerText := strings.ReplaceAll(llmchunk.Choices[0].Delta.Content, "\n\n", "\n")
chunkChan <- answerText chunkChan <- answerText
} }
return llmResp, nil
} }
func chatRound(userMsg, role string, tv *tview.TextView) { func chatRound(userMsg, role string, tv *tview.TextView) {
@@ -117,8 +116,8 @@ func chatRound(userMsg, role string, tv *tview.TextView) {
} }
go sendMsgToLLM(reader) go sendMsgToLLM(reader)
if userMsg != "" { // no need to write assistant icon since we continue old message if userMsg != "" { // no need to write assistant icon since we continue old message
fmt.Fprintf(tv, fmt.Sprintf("(%d) ", len(chatBody.Messages))) fmt.Fprintf(tv, "(%d) ", len(chatBody.Messages))
fmt.Fprintf(tv, cfg.AssistantIcon) fmt.Fprint(tv, cfg.AssistantIcon)
} }
respText := strings.Builder{} respText := strings.Builder{}
out: out:
@@ -126,7 +125,7 @@ out:
select { select {
case chunk := <-chunkChan: case chunk := <-chunkChan:
// fmt.Printf(chunk) // fmt.Printf(chunk)
fmt.Fprintf(tv, chunk) fmt.Fprint(tv, chunk)
respText.WriteString(chunk) respText.WriteString(chunk)
tv.ScrollToEnd() tv.ScrollToEnd()
case <-streamDone: case <-streamDone:
@@ -163,7 +162,7 @@ func findCall(msg string, tv *tview.TextView) {
// call a func // call a func
f, ok := fnMap[fc.Name] f, ok := fnMap[fc.Name]
if !ok { if !ok {
m := fmt.Sprintf("%s is not implemented", fc.Name) m := fc.Name + "%s is not implemented"
chatRound(m, cfg.ToolRole, tv) chatRound(m, cfg.ToolRole, tv)
return return
} }
@@ -188,30 +187,30 @@ func chatToText(showSys bool) string {
return strings.Join(s, "") return strings.Join(s, "")
} }
func textToMsg(rawMsg string) models.MessagesStory { // func textToMsg(rawMsg string) models.MessagesStory {
msg := models.MessagesStory{} // msg := models.MessagesStory{}
// system and tool? // // system and tool?
if strings.HasPrefix(rawMsg, cfg.AssistantIcon) { // if strings.HasPrefix(rawMsg, cfg.AssistantIcon) {
msg.Role = cfg.AssistantRole // msg.Role = cfg.AssistantRole
msg.Content = strings.TrimPrefix(rawMsg, cfg.AssistantIcon) // msg.Content = strings.TrimPrefix(rawMsg, cfg.AssistantIcon)
return msg // return msg
} // }
if strings.HasPrefix(rawMsg, cfg.UserIcon) { // if strings.HasPrefix(rawMsg, cfg.UserIcon) {
msg.Role = cfg.UserRole // msg.Role = cfg.UserRole
msg.Content = strings.TrimPrefix(rawMsg, cfg.UserIcon) // msg.Content = strings.TrimPrefix(rawMsg, cfg.UserIcon)
return msg // return msg
} // }
return msg // return msg
} // }
func textSliceToChat(chat []string) []models.MessagesStory { // func textSliceToChat(chat []string) []models.MessagesStory {
resp := make([]models.MessagesStory, len(chat)) // resp := make([]models.MessagesStory, len(chat))
for i, rawMsg := range chat { // for i, rawMsg := range chat {
msg := textToMsg(rawMsg) // msg := textToMsg(rawMsg)
resp[i] = msg // resp[i] = msg
} // }
return resp // return resp
} // }
func init() { func init() {
cfg = config.LoadConfigOrDefault("config.example.toml") cfg = config.LoadConfigOrDefault("config.example.toml")
@@ -233,7 +232,10 @@ func init() {
store = storage.NewProviderSQL("test.db", logger) store = storage.NewProviderSQL("test.db", logger)
// https://github.com/coreydaley/ggerganov-llama.cpp/blob/master/examples/server/README.md // https://github.com/coreydaley/ggerganov-llama.cpp/blob/master/examples/server/README.md
// load all chats in memory // load all chats in memory
loadHistoryChats() if _, err := loadHistoryChats(); err != nil {
logger.Error("failed to load chat", "error", err)
return
}
lastChat := loadOldChatOrGetNew() lastChat := loadOldChatOrGetNew()
logger.Info("loaded history") logger.Info("loaded history")
chatBody = &models.ChatBody{ chatBody = &models.ChatBody{

View File

@@ -9,7 +9,6 @@ import (
var ( var (
botRespMode = false botRespMode = false
editMode = false editMode = false
botMsg = "no"
selectedIndex = int(-1) selectedIndex = int(-1)
indexLine = "F12 to show keys help; bot resp mode: %v; current chat: %s" indexLine = "F12 to show keys help; bot resp mode: %v; current chat: %s"
focusSwitcher = map[tview.Primitive]tview.Primitive{} focusSwitcher = map[tview.Primitive]tview.Primitive{}

View File

@@ -3,6 +3,7 @@ package main
import ( import (
"elefant/models" "elefant/models"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"os/exec" "os/exec"
"strings" "strings"
@@ -19,7 +20,7 @@ func historyToSJSON(msgs []models.MessagesStory) (string, error) {
return "", err return "", err
} }
if data == nil { if data == nil {
return "", fmt.Errorf("nil data") return "", errors.New("nil data")
} }
return string(data), nil return string(data), nil
} }
@@ -61,7 +62,7 @@ func loadHistoryChats() ([]string, error) {
func loadHistoryChat(chatName string) ([]models.MessagesStory, error) { func loadHistoryChat(chatName string) ([]models.MessagesStory, error) {
chat, ok := chatMap[chatName] chat, ok := chatMap[chatName]
if !ok { if !ok {
err := fmt.Errorf("failed to read chat") err := errors.New("failed to read chat")
logger.Error("failed to read chat", "name", chatName) logger.Error("failed to read chat", "name", chatName)
return nil, err return nil, err
} }

View File

@@ -55,6 +55,7 @@ func (p ProviderSQL) UpsertChat(chat *models.Chat) (*models.Chat, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer stmt.Close()
// Execute the query and scan the result into a new chat object // Execute the query and scan the result into a new chat object
var resp models.Chat var resp models.Chat
err = stmt.Get(&resp, chat) err = stmt.Get(&resp, chat)

View File

@@ -73,8 +73,11 @@ func memorise(args ...string) []byte {
Mind: args[1], Mind: args[1],
UpdatedAt: time.Now(), UpdatedAt: time.Now(),
} }
store.Memorise(memory) if _, err := store.Memorise(memory); err != nil {
msg := fmt.Sprintf("info saved under the topic: %s", args[0]) logger.Error("failed to save memory", "err", err, "memoory", memory)
return []byte("failed to save info")
}
msg := "info saved under the topic:" + args[0]
return []byte(msg) return []byte(msg)
} }
@@ -104,7 +107,7 @@ func recallTopics(args ...string) []byte {
return []byte(joinedS) return []byte(joinedS)
} }
func fullMemoryLoad() {} // func fullMemoryLoad() {}
type fnSig func(...string) []byte type fnSig func(...string) []byte

29
tui.go
View File

@@ -19,7 +19,7 @@ var (
position *tview.TextView position *tview.TextView
helpView *tview.TextView helpView *tview.TextView
flex *tview.Flex flex *tview.Flex
chatActModal *tview.Modal // chatActModal *tview.Modal
sysModal *tview.Modal sysModal *tview.Modal
indexPickWindow *tview.InputField indexPickWindow *tview.InputField
renameWindow *tview.InputField renameWindow *tview.InputField
@@ -145,7 +145,9 @@ func init() {
if event.Key() == tcell.KeyEscape && editMode { if event.Key() == tcell.KeyEscape && editMode {
editedMsg := editArea.GetText() editedMsg := editArea.GetText()
if editedMsg == "" { if editedMsg == "" {
notifyUser("edit", "no edit provided") if err := notifyUser("edit", "no edit provided"); err != nil {
logger.Error("failed to send notification", "error", err)
}
pages.RemovePage("editArea") pages.RemovePage("editArea")
editMode = false editMode = false
return nil return nil
@@ -165,7 +167,6 @@ func init() {
SetAcceptanceFunc(tview.InputFieldInteger). SetAcceptanceFunc(tview.InputFieldInteger).
SetDoneFunc(func(key tcell.Key) { SetDoneFunc(func(key tcell.Key) {
pages.RemovePage("getIndex") pages.RemovePage("getIndex")
return
}) })
indexPickWindow.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { indexPickWindow.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
si := indexPickWindow.GetText() si := indexPickWindow.GetText()
@@ -183,9 +184,13 @@ func init() {
editArea.SetText(m.Content, true) editArea.SetText(m.Content, true)
} }
if !editMode && event.Key() == tcell.KeyEnter { if !editMode && event.Key() == tcell.KeyEnter {
copyToClipboard(m.Content) if err := copyToClipboard(m.Content); err != nil {
logger.Error("failed to copy to clipboard", "error", err)
}
notification := fmt.Sprintf("msg '%s' was copied to the clipboard", m.Content[:30]) notification := fmt.Sprintf("msg '%s' was copied to the clipboard", m.Content[:30])
notifyUser("copied", notification) if err := notifyUser("copied", notification); err != nil {
logger.Error("failed to send notification", "error", err)
}
} }
return event return event
}) })
@@ -196,7 +201,6 @@ func init() {
SetAcceptanceFunc(tview.InputFieldMaxLength(100)). SetAcceptanceFunc(tview.InputFieldMaxLength(100)).
SetDoneFunc(func(key tcell.Key) { SetDoneFunc(func(key tcell.Key) {
pages.RemovePage("renameW") pages.RemovePage("renameW")
return
}) })
renameWindow.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { renameWindow.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyEnter { if event.Key() == tcell.KeyEnter {
@@ -214,14 +218,15 @@ func init() {
logger.Error("failed to upsert chat", "error", err, "chat", currentChat) logger.Error("failed to upsert chat", "error", err, "chat", currentChat)
} }
notification := fmt.Sprintf("renamed chat to '%s'", activeChatName) notification := fmt.Sprintf("renamed chat to '%s'", activeChatName)
notifyUser("renamed", notification) if err := notifyUser("renamed", notification); err != nil {
logger.Error("failed to send notification", "error", err)
}
} }
return event return event
}) })
// //
helpView = tview.NewTextView().SetDynamicColors(true).SetText(helpText).SetDoneFunc(func(key tcell.Key) { helpView = tview.NewTextView().SetDynamicColors(true).SetText(helpText).SetDoneFunc(func(key tcell.Key) {
pages.RemovePage("helpView") pages.RemovePage("helpView")
return
}) })
helpView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { helpView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() { switch event.Key() {
@@ -281,9 +286,13 @@ func init() {
// copy msg to clipboard // copy msg to clipboard
editMode = false editMode = false
m := chatBody.Messages[len(chatBody.Messages)-1] m := chatBody.Messages[len(chatBody.Messages)-1]
copyToClipboard(m.Content) if err := copyToClipboard(m.Content); err != nil {
logger.Error("failed to copy to clipboard", "error", err)
}
notification := fmt.Sprintf("msg '%s' was copied to the clipboard", m.Content[:30]) notification := fmt.Sprintf("msg '%s' was copied to the clipboard", m.Content[:30])
notifyUser("copied", notification) if err := notifyUser("copied", notification); err != nil {
logger.Error("failed to send notification", "error", err)
}
return nil return nil
} }
if event.Key() == tcell.KeyF8 { if event.Key() == tcell.KeyF8 {