-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.go
More file actions
154 lines (134 loc) · 4.62 KB
/
main.go
File metadata and controls
154 lines (134 loc) · 4.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
package main
import (
"bufio"
"flag"
"fmt"
"log"
"os"
"path/filepath"
"strconv"
"strings"
"prompt-cli/internal/agent"
"prompt-cli/internal/config"
"prompt-cli/internal/errors"
"prompt-cli/internal/logger"
"prompt-cli/internal/ollama"
"prompt-cli/internal/tui"
tea "github.com/charmbracelet/bubbletea"
)
// loadPrompt reads the contents of the prompt file located at the given path.
// It returns the file content as a string and any error that occurs.
func loadPrompt(path string) (string, error) {
content, err := os.ReadFile(path)
if err != nil {
return "", err
}
return string(content), nil
}
func main() {
// Define a command-line flag for chat-only mode. This allows the user to
// start the application without the system prompt that defines the tool-using agent persona.
chatOnly := flag.Bool("chatonly", false, "Enable chat-only mode, without the tool-using agent persona.")
// Determine the directory of the running executable.
exePath, err := os.Executable()
if err != nil {
log.Fatalf("Error finding executable path: %v", errors.IOError("Failed to get executable path", err))
}
exeDir := filepath.Dir(exePath)
// Build the path to the configuration file relative to the executable directory.
configPath := filepath.Join(exeDir, "config.json")
// Load configuration from the JSON file.
configs, err := config.LoadConfig(configPath)
if err != nil {
var appErr *errors.AppError
if ok := errors.As(err, &appErr); ok {
log.Fatalf("Error loading config from %s: %s (type: %s)", configPath, appErr.Message, appErr.Type)
} else {
log.Fatalf("Error loading config from %s: %v", configPath, err)
}
}
// Validate the loaded configuration to ensure required fields are set.
if err := config.ValidateConfig(configs); err != nil {
var appErr *errors.AppError
if ok := errors.As(err, &appErr); ok {
log.Fatalf("Invalid configuration: %s (type: %s)", appErr.Message, appErr.Type)
} else {
log.Fatalf("Invalid configuration: %v", err)
}
}
appLogger := logger.NewLogger()
if configs.LogEnabled {
appLogger.Toggle()
}
appLogger.Setup()
// Construct the base URL for the Ollama server, adding the HTTP scheme if missing.
var baseURL string
if strings.HasPrefix(configs.OllamaServerURL, "http") {
baseURL = fmt.Sprintf("%s:%d", configs.OllamaServerURL, configs.OllamaServerPort)
} else {
baseURL = fmt.Sprintf("http://%s:%d", configs.OllamaServerURL, configs.OllamaServerPort)
}
appLogger.Log(fmt.Sprintf("Connecting to Ollama at: %s", baseURL))
// Retrieve the list of available models from the Ollama server.
models, err := ollama.GetModels(baseURL, appLogger)
if err != nil {
var appErr *errors.AppError
if ok := errors.As(err, &appErr); ok {
log.Fatalf("Error getting models: %s (type: %s)", appErr.Message, appErr.Type)
} else {
log.Fatalf("Error getting models: %v", err)
}
}
if len(models) == 0 {
log.Fatal(errors.ValidationError("No models found on the Ollama server", map[string]interface{}{
"server": baseURL,
"action": "model_discovery",
}).Error())
}
flag.Parse()
// Determine which model to use: a default from config or user selection.
var selectedModel string
if configs.DefaultLLM != "" {
selectedModel = configs.DefaultLLM
} else {
fmt.Println("Please select a model:")
for i, m := range models {
fmt.Printf("%d: %s\n", i+1, m.Name)
}
// Prompt the user until a valid model index is entered.
var choice int
reader := bufio.NewReader(os.Stdin)
for {
fmt.Print("> ")
input, _ := reader.ReadString('\n')
choice, err = strconv.Atoi(strings.TrimSpace(input))
if err == nil && choice > 0 && choice <= len(models) {
break
}
fmt.Println("Invalid choice, please try again.")
}
selectedModel = models[choice-1].Name
}
// Load the system prompt from a Markdown file; fall back to a default prompt if missing.
var systemPrompt string
if *chatOnly {
systemPrompt = "You are a helpful assistant."
} else {
var err error
systemPrompt, err = loadPrompt("Prompt.MD")
if err != nil {
log.Printf("Warning: Could not load system prompt: %v", err)
systemPrompt = "You are a helpful assistant."
}
}
// Initialize the components.
ollamaClient := ollama.NewOllamaClient(baseURL, appLogger)
appAgent := agent.NewAgent(appLogger)
m := tui.NewModel(baseURL, selectedModel, configs.ContextLength, systemPrompt, configs.LogEnabled, appLogger, appAgent, ollamaClient)
// Create a new Bubble Tea program with alternate screen and mouse support.
p := tea.NewProgram(m, tea.WithAltScreen(), tea.WithMouseAllMotion())
// Run the TUI; terminate on error.
if _, err := p.Run(); err != nil {
log.Fatalf("Alas, there's been an error: %v", err)
}
}