Feat: add linter
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@ testlog
|
|||||||
elefant
|
elefant
|
||||||
history/
|
history/
|
||||||
*.db
|
*.db
|
||||||
|
config.toml
|
||||||
|
|||||||
32
.golangci.yml
Normal file
32
.golangci.yml
Normal 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
2
Makefile
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
lint: ## Run linters. Use make install-linters first.
|
||||||
|
golangci-lint run -c .golangci.yml ./...
|
||||||
72
bot.go
72
bot.go
@@ -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{
|
||||||
|
|||||||
1
main.go
1
main.go
@@ -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{}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
9
tools.go
9
tools.go
@@ -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
|
||||||
|
|
||||||
|
|||||||
45
tui.go
45
tui.go
@@ -11,15 +11,15 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
app *tview.Application
|
app *tview.Application
|
||||||
pages *tview.Pages
|
pages *tview.Pages
|
||||||
textArea *tview.TextArea
|
textArea *tview.TextArea
|
||||||
editArea *tview.TextArea
|
editArea *tview.TextArea
|
||||||
textView *tview.TextView
|
textView *tview.TextView
|
||||||
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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user