many improvements

This commit is contained in:
Luxferre
2026-08-15 08:27:24 +03:00
parent 398754dabb
commit e6acb066ca
9 changed files with 283 additions and 22 deletions
+91 -4
View File
@@ -42,9 +42,10 @@ type Cfg struct {
MaxALIterations int
Stream bool
Color string
Raw map[string]string
}
var defCfg = Cfg{"https://opencode.ai/zen/v1", "big-pickle", "-", 0.7, 300, 120, 1000, true, "auto"}
var defCfg = Cfg{"https://opencode.ai/zen/v1", "big-pickle", "-", 0.7, 300, 120, 1000, true, "auto", nil}
func atoiD(s string, d int) int {
if v, e := strconv.Atoi(strings.TrimSpace(s)); e == nil {
@@ -55,12 +56,19 @@ func atoiD(s string, d int) int {
func getCfg(path string) Cfg {
cfg := defCfg
cfg.Raw = map[string]string{
"endpoint": cfg.Endpoint, "model": cfg.Model, "temperature": fmt.Sprintf("%v", cfg.Temperature),
"api_key": cfg.APIKey, "stream": strconv.FormatBool(cfg.Stream), "color": cfg.Color,
"timeout": strconv.Itoa(cfg.Timeout), "shell_timeout": strconv.Itoa(cfg.ShellTimeout),
"max_al_iterations": strconv.Itoa(cfg.MaxALIterations),
}
if d, err := os.ReadFile(path); err == nil {
for _, ln := range strings.Split(string(d), "\n") {
ln = strings.TrimSpace(ln)
if ln == "" || ln[0] == '#' || !strings.Contains(ln, "=") { continue }
k, v, _ := strings.Cut(ln, "=")
k, v = strings.TrimSpace(k), strings.TrimSpace(v)
cfg.Raw[k] = v
switch k {
case "endpoint": cfg.Endpoint = v
case "model": cfg.Model = v
@@ -74,10 +82,41 @@ func getCfg(path string) Cfg {
}
}
}
if (cfg.APIKey == "" || cfg.APIKey == "-") && os.Getenv("OPENAI_API_KEY") != "" { cfg.APIKey = os.Getenv("OPENAI_API_KEY") }
if (cfg.APIKey == "" || cfg.APIKey == "-") && os.Getenv("OPENAI_API_KEY") != "" {
cfg.APIKey = os.Getenv("OPENAI_API_KEY")
cfg.Raw["api_key"] = cfg.APIKey
}
return cfg
}
func setCfg(path, key, val string) error {
var lines []string
found := false
if d, err := os.ReadFile(path); err == nil {
for _, ln := range strings.Split(string(d), "\n") {
trimmed := strings.TrimSpace(ln)
if !strings.HasPrefix(trimmed, "#") && strings.Contains(trimmed, "=") {
k, _, _ := strings.Cut(trimmed, "=")
if strings.TrimSpace(k) == key {
lines = append(lines, key+"="+val)
found = true
continue
}
}
lines = append(lines, ln)
}
}
if !found {
if len(lines) > 0 && lines[len(lines)-1] == "" {
lines[len(lines)-1] = key + "=" + val
lines = append(lines, "")
} else {
lines = append(lines, key+"="+val)
}
}
return os.WriteFile(path, []byte(strings.Join(lines, "\n")), 0644)
}
const defaultSystemPrompt = `You are Bantam, a tiny, powerful AI agent. Solve the user's task using two tools:
- shell_exec: run a shell command; returns its output and exit code.
- run_subagent: delegate a sub-task to a child agent; returns its reply.
@@ -182,6 +221,19 @@ func llm(cfg *Cfg, msgs []Message, tools []map[string]any) (Message, error) {
sanitizeMessages(msgs)
p := map[string]any{"model": cfg.Model, "temperature": cfg.Temperature, "messages": msgs, "stream": cfg.Stream}
if tools != nil { p["tools"] = tools }
for k, v := range cfg.Raw {
switch k {
case "endpoint", "api_key", "timeout", "shell_timeout", "max_al_iterations", "color":
continue
default:
var jv any
if err := json.Unmarshal([]byte(v), &jv); err == nil {
p[k] = jv
} else {
p[k] = v
}
}
}
body, _ := json.Marshal(p)
client := &http.Client{Transport: &http.Transport{
DialContext: (&net.Dialer{Timeout: time.Duration(cfg.Timeout) * time.Second}).DialContext,
@@ -669,6 +721,19 @@ func readLine(prompt string) (string, bool) {
}
switch rn {
case '\r':
if stdin.Buffered() > 0 {
if b, _ := stdin.Peek(1); len(b) > 0 && b[0] == '\n' {
stdin.ReadByte()
}
}
W := termWidth()
s := string(e.buf)
P := visibleLen(e.prompt)
er, _ := textPos(P, W, s, len([]rune(s)))
pr, _ := textPos(P, W, s, e.pos)
if down := er - pr; down > 0 {
fmt.Printf("\033[%dB", down)
}
fmt.Print("\r\n")
return string(e.buf), true
case '\n':
@@ -804,10 +869,32 @@ func main() {
fmt.Println(c(fmt.Sprintf("[compacted to %d messages]", len(msgs)), 32))
fmt.Println(c("--- summary ---", 33) + "\n" + c(sm, 2))
continue
case strings.HasPrefix(u, "/cfg"):
parts := strings.SplitN(u, " ", 3)
if len(parts) == 2 {
k := strings.TrimSpace(parts[1])
if v, ok := cfg.Raw[k]; ok {
fmt.Println(c(k+"="+v, 32))
} else {
fmt.Println(c(k+" not set", 31))
}
} else if len(parts) >= 3 {
k, v := strings.TrimSpace(parts[1]), strings.TrimSpace(parts[2])
if err := setCfg("model.cfg", k, v); err != nil {
fmt.Println(c("[cfg error: "+err.Error()+"]", 31))
continue
}
cfg = getCfg("model.cfg")
COL = col(cfg)
fmt.Println(c(fmt.Sprintf("[config updated: %s=%s]", k, v), 32))
} else {
fmt.Println(c("Usage: /cfg <param> [val]", 31))
}
continue
case u == "/help":
fmt.Println(c("Bantam commands:", 1, 36))
for _, kv := range [][2]string{{"/quit", "exit"}, {"/clear", "reset to system prompt"}, {"/save", "save session"}, {"/list", "list sessions"}, {"/load <id>", "load session"}, {"/compact", "compact context"}, {"/help", "show help"}} {
fmt.Println(c(fmt.Sprintf(" %-12s", kv[0]), 1, 32) + kv[1])
for _, kv := range [][2]string{{"/quit", "exit"}, {"/clear", "reset to system prompt"}, {"/save", "save session"}, {"/list", "list sessions"}, {"/load <id>", "load session"}, {"/compact", "compact context"}, {"/cfg <k> [v]", "get/set config"}, {"/help", "show help"}} {
fmt.Println(c(fmt.Sprintf(" %-15s", kv[0]), 1, 32) + kv[1])
}
continue
}