Enha (rag): one table to manage files and data loaded
This commit is contained in:
125
tables.go
125
tables.go
@@ -247,9 +247,49 @@ func formatSize(size int64) string {
|
|||||||
return fmt.Sprintf("%.1f%s", s, units[i])
|
return fmt.Sprintf("%.1f%s", s, units[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeRAGTable(fileList []string) *tview.Flex {
|
type ragFileInfo struct {
|
||||||
actions := []string{"load", "delete"}
|
name string
|
||||||
rows, cols := len(fileList), len(actions)+2
|
inRAGDir bool
|
||||||
|
isLoaded bool
|
||||||
|
fullPath string
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRAGTable(fileList []string, loadedFiles []string) *tview.Flex {
|
||||||
|
// Build set of loaded files for quick lookup
|
||||||
|
loadedSet := make(map[string]bool)
|
||||||
|
for _, f := range loadedFiles {
|
||||||
|
loadedSet[f] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build merged list: files from ragdir + orphaned files from DB
|
||||||
|
ragFiles := make([]ragFileInfo, 0, len(fileList)+len(loadedFiles))
|
||||||
|
seen := make(map[string]bool)
|
||||||
|
|
||||||
|
// Add files from ragdir
|
||||||
|
for _, f := range fileList {
|
||||||
|
ragFiles = append(ragFiles, ragFileInfo{
|
||||||
|
name: f,
|
||||||
|
inRAGDir: true,
|
||||||
|
isLoaded: loadedSet[f],
|
||||||
|
fullPath: path.Join(cfg.RAGDir, f),
|
||||||
|
})
|
||||||
|
seen[f] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add orphaned files (in DB but not in ragdir)
|
||||||
|
for _, f := range loadedFiles {
|
||||||
|
if !seen[f] {
|
||||||
|
ragFiles = append(ragFiles, ragFileInfo{
|
||||||
|
name: f,
|
||||||
|
inRAGDir: false,
|
||||||
|
isLoaded: true,
|
||||||
|
fullPath: "",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := len(ragFiles)
|
||||||
|
cols := 4 // File Name | Preview | Action | Delete
|
||||||
fileTable := tview.NewTable().
|
fileTable := tview.NewTable().
|
||||||
SetBorders(true)
|
SetBorders(true)
|
||||||
longStatusView := tview.NewTextView()
|
longStatusView := tview.NewTextView()
|
||||||
@@ -273,7 +313,7 @@ func makeRAGTable(fileList []string) *tview.Flex {
|
|||||||
SetAlign(tview.AlignCenter).
|
SetAlign(tview.AlignCenter).
|
||||||
SetSelectable(false))
|
SetSelectable(false))
|
||||||
fileTable.SetCell(0, 2,
|
fileTable.SetCell(0, 2,
|
||||||
tview.NewTableCell("Load").
|
tview.NewTableCell("Load/Unload").
|
||||||
SetTextColor(tcell.ColorWhite).
|
SetTextColor(tcell.ColorWhite).
|
||||||
SetAlign(tview.AlignCenter).
|
SetAlign(tview.AlignCenter).
|
||||||
SetSelectable(false))
|
SetSelectable(false))
|
||||||
@@ -284,18 +324,29 @@ func makeRAGTable(fileList []string) *tview.Flex {
|
|||||||
SetSelectable(false))
|
SetSelectable(false))
|
||||||
// Add the file rows starting from row 1
|
// Add the file rows starting from row 1
|
||||||
for r := 0; r < rows; r++ {
|
for r := 0; r < rows; r++ {
|
||||||
|
f := ragFiles[r]
|
||||||
for c := 0; c < cols; c++ {
|
for c := 0; c < cols; c++ {
|
||||||
color := tcell.ColorWhite
|
color := tcell.ColorWhite
|
||||||
switch {
|
switch {
|
||||||
case c == 0:
|
case c == 0:
|
||||||
|
displayName := f.name
|
||||||
|
if !f.inRAGDir {
|
||||||
|
displayName = f.name + " (orphaned)"
|
||||||
|
}
|
||||||
fileTable.SetCell(r+1, c,
|
fileTable.SetCell(r+1, c,
|
||||||
tview.NewTableCell(fileList[r]).
|
tview.NewTableCell(displayName).
|
||||||
SetTextColor(color).
|
SetTextColor(color).
|
||||||
SetAlign(tview.AlignCenter).
|
SetAlign(tview.AlignCenter).
|
||||||
SetSelectable(false))
|
SetSelectable(false))
|
||||||
case c == 1:
|
case c == 1:
|
||||||
fpath := path.Join(cfg.RAGDir, fileList[r])
|
if !f.inRAGDir {
|
||||||
if fi, err := os.Stat(fpath); err == nil {
|
// Orphaned file - no preview available
|
||||||
|
fileTable.SetCell(r+1, c,
|
||||||
|
tview.NewTableCell("not in ragdir").
|
||||||
|
SetTextColor(tcell.ColorYellow).
|
||||||
|
SetAlign(tview.AlignCenter).
|
||||||
|
SetSelectable(false))
|
||||||
|
} else if fi, err := os.Stat(f.fullPath); err == nil {
|
||||||
size := fi.Size()
|
size := fi.Size()
|
||||||
modTime := fi.ModTime()
|
modTime := fi.ModTime()
|
||||||
preview := fmt.Sprintf("%s | %s", formatSize(size), modTime.Format("2006-01-02 15:04"))
|
preview := fmt.Sprintf("%s | %s", formatSize(size), modTime.Format("2006-01-02 15:04"))
|
||||||
@@ -312,15 +363,32 @@ func makeRAGTable(fileList []string) *tview.Flex {
|
|||||||
SetSelectable(false))
|
SetSelectable(false))
|
||||||
}
|
}
|
||||||
case c == 2:
|
case c == 2:
|
||||||
|
actionText := "load"
|
||||||
|
if f.isLoaded {
|
||||||
|
actionText = "unload"
|
||||||
|
}
|
||||||
|
if !f.inRAGDir {
|
||||||
|
// Orphaned file - can only unload
|
||||||
|
actionText = "unload"
|
||||||
|
}
|
||||||
fileTable.SetCell(r+1, c,
|
fileTable.SetCell(r+1, c,
|
||||||
tview.NewTableCell("load").
|
tview.NewTableCell(actionText).
|
||||||
SetTextColor(color).
|
|
||||||
SetAlign(tview.AlignCenter))
|
|
||||||
default:
|
|
||||||
fileTable.SetCell(r+1, c,
|
|
||||||
tview.NewTableCell("delete").
|
|
||||||
SetTextColor(color).
|
SetTextColor(color).
|
||||||
SetAlign(tview.AlignCenter))
|
SetAlign(tview.AlignCenter))
|
||||||
|
case c == 3:
|
||||||
|
if !f.inRAGDir {
|
||||||
|
// Orphaned file - cannot delete from ragdir (not there)
|
||||||
|
fileTable.SetCell(r+1, c,
|
||||||
|
tview.NewTableCell("-").
|
||||||
|
SetTextColor(tcell.ColorDarkGray).
|
||||||
|
SetAlign(tview.AlignCenter).
|
||||||
|
SetSelectable(false))
|
||||||
|
} else {
|
||||||
|
fileTable.SetCell(r+1, c,
|
||||||
|
tview.NewTableCell("delete").
|
||||||
|
SetTextColor(color).
|
||||||
|
SetAlign(tview.AlignCenter))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -376,12 +444,16 @@ func makeRAGTable(fileList []string) *tview.Flex {
|
|||||||
pages.RemovePage(RAGPage)
|
pages.RemovePage(RAGPage)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// For file rows, get the filename (row index - 1 because of the exit row at index 0)
|
// For file rows, get the file info (row index - 1 because of the exit row at index 0)
|
||||||
fpath := fileList[row-1] // -1 to account for the exit row at index 0
|
f := ragFiles[row-1]
|
||||||
// notification := fmt.Sprintf("chat: %s; action: %s", fpath, tc.Text)
|
// Handle "-" case (orphaned file with no delete option)
|
||||||
|
if tc.Text == "-" {
|
||||||
|
pages.RemovePage(RAGPage)
|
||||||
|
return
|
||||||
|
}
|
||||||
switch tc.Text {
|
switch tc.Text {
|
||||||
case "load":
|
case "load":
|
||||||
fpath = path.Join(cfg.RAGDir, fpath)
|
fpath := path.Join(cfg.RAGDir, f.name)
|
||||||
longStatusView.SetText("clicked load")
|
longStatusView.SetText("clicked load")
|
||||||
go func() {
|
go func() {
|
||||||
if err := ragger.LoadRAG(fpath); err != nil {
|
if err := ragger.LoadRAG(fpath); err != nil {
|
||||||
@@ -398,8 +470,25 @@ func makeRAGTable(fileList []string) *tview.Flex {
|
|||||||
})
|
})
|
||||||
}()
|
}()
|
||||||
return
|
return
|
||||||
|
case "unload":
|
||||||
|
longStatusView.SetText("clicked unload")
|
||||||
|
go func() {
|
||||||
|
if err := ragger.RemoveFile(f.name); err != nil {
|
||||||
|
logger.Error("failed to unload file from RAG", "filename", f.name, "error", err)
|
||||||
|
_ = notifyUser("RAG", "failed to unload file; error: "+err.Error())
|
||||||
|
app.QueueUpdate(func() {
|
||||||
|
pages.RemovePage(RAGPage)
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = notifyUser("RAG", "file unloaded successfully")
|
||||||
|
app.QueueUpdate(func() {
|
||||||
|
pages.RemovePage(RAGPage)
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
return
|
||||||
case "delete":
|
case "delete":
|
||||||
fpath = path.Join(cfg.RAGDir, fpath)
|
fpath := path.Join(cfg.RAGDir, f.name)
|
||||||
if err := os.Remove(fpath); err != nil {
|
if err := os.Remove(fpath); err != nil {
|
||||||
logger.Error("failed to delete file", "filename", fpath, "error", err)
|
logger.Error("failed to delete file", "filename", fpath, "error", err)
|
||||||
return
|
return
|
||||||
|
|||||||
19
tui.go
19
tui.go
@@ -913,6 +913,7 @@ func init() {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Get files from ragdir
|
||||||
fileList := []string{}
|
fileList := []string{}
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
if f.IsDir() {
|
if f.IsDir() {
|
||||||
@@ -920,22 +921,14 @@ func init() {
|
|||||||
}
|
}
|
||||||
fileList = append(fileList, f.Name())
|
fileList = append(fileList, f.Name())
|
||||||
}
|
}
|
||||||
chatRAGTable := makeRAGTable(fileList)
|
// Get loaded files from vector DB
|
||||||
pages.AddPage(RAGPage, chatRAGTable, true, true)
|
loadedFiles, err := ragger.ListLoaded()
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if event.Key() == tcell.KeyCtrlY { // Use Ctrl+Y to list loaded RAG files
|
|
||||||
// List files already loaded into the RAG system
|
|
||||||
fileList, err := ragger.ListLoaded()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to list loaded RAG files", "error", err)
|
logger.Error("failed to list loaded RAG files", "error", err)
|
||||||
if notifyerr := notifyUser("failed to list RAG files", err.Error()); notifyerr != nil {
|
loadedFiles = []string{} // Continue with empty list on error
|
||||||
logger.Error("failed to send notification", "error", notifyerr)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
chatLoadedRAGTable := makeLoadedRAGTable(fileList)
|
chatRAGTable := makeRAGTable(fileList, loadedFiles)
|
||||||
pages.AddPage(RAGLoadedPage, chatLoadedRAGTable, true, true)
|
pages.AddPage(RAGPage, chatRAGTable, true, true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if event.Key() == tcell.KeyRune && event.Modifiers() == tcell.ModAlt && event.Rune() == '1' {
|
if event.Key() == tcell.KeyRune && event.Modifiers() == tcell.ModAlt && event.Rune() == '1' {
|
||||||
|
|||||||
Reference in New Issue
Block a user