added /models cmd
This commit is contained in:
@@ -136,6 +136,49 @@ func fetchContextWindow(cfg *Cfg) int {
|
||||
return 262144
|
||||
}
|
||||
|
||||
// listModels queries the /models path on the configured inference endpoint and
|
||||
// returns a plain newline-separated list of supported model IDs. The currently
|
||||
// configured model is marked with a leading "* ". On transport or HTTP errors a
|
||||
// non-nil error is returned so the caller can surface it.
|
||||
func listModels(cfg *Cfg) (string, error) {
|
||||
t := cfg.Timeout
|
||||
if t < 10 { t = 10 }
|
||||
client := &http.Client{Timeout: time.Duration(t) * time.Second}
|
||||
req, err := http.NewRequest("GET", strings.TrimRight(cfg.Endpoint, "/")+"/models", nil)
|
||||
if err != nil { return "", err }
|
||||
req.Header.Set("User-Agent", "Mozilla/5.0 (compatible; Bantam/1.0)")
|
||||
if cfg.APIKey != "" && cfg.APIKey != "-" { req.Header.Set("Authorization", "Bearer "+cfg.APIKey) }
|
||||
resp, err := client.Do(req)
|
||||
if err != nil { return "", err }
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= 400 {
|
||||
return "", fmt.Errorf("HTTP %d from /models", resp.StatusCode)
|
||||
}
|
||||
var res struct {
|
||||
Data []map[string]any `json:"data"`
|
||||
Models []map[string]any `json:"models"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&res); err != nil {
|
||||
return "", fmt.Errorf("failed to parse /models response: %v", err)
|
||||
}
|
||||
list := res.Data
|
||||
if len(list) == 0 { list = res.Models }
|
||||
if len(list) == 0 {
|
||||
return "No models returned by the endpoint.", nil
|
||||
}
|
||||
var b strings.Builder
|
||||
for _, item := range list {
|
||||
id, _ := item["id"].(string)
|
||||
if id == "" { continue }
|
||||
if id == cfg.Model || strings.EqualFold(id, cfg.Model) {
|
||||
b.WriteString("* " + id + "\n")
|
||||
} else {
|
||||
b.WriteString(" " + id + "\n")
|
||||
}
|
||||
}
|
||||
return b.String(), nil
|
||||
}
|
||||
|
||||
func getCfg(path string) Cfg {
|
||||
cfg := defCfg
|
||||
cfg.Raw = map[string]string{
|
||||
@@ -1042,6 +1085,14 @@ func last(msgs []Message) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// lastRole returns the role of the last message in the conversation, or "" if
|
||||
// it is empty. It is used to detect when a turn ended on a tool result rather
|
||||
// than a normal assistant reply, so the conversation can be auto-continued.
|
||||
func lastRole(msgs []Message) string {
|
||||
if len(msgs) == 0 { return "" }
|
||||
return msgs[len(msgs)-1].Role
|
||||
}
|
||||
|
||||
func AL(ctx context.Context, cfg *Cfg, msgs []Message, sp string, depth int) ([]Message, Usage, error) {
|
||||
done := false
|
||||
var turnUsage Usage
|
||||
@@ -1545,6 +1596,11 @@ func main() {
|
||||
sigCtx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
var usg Usage
|
||||
msgs, usg, err = AL(sigCtx, &cfg, msgs, sp, 0)
|
||||
if lastRole(msgs) == "tool" {
|
||||
msgs = append(msgs, Message{Role: "user", Content: strp("continue")})
|
||||
fmt.Println(c("[auto continue: last message was a tool result]", 33))
|
||||
msgs, usg, err = AL(sigCtx, &cfg, msgs, sp, 0)
|
||||
}
|
||||
interrupted := sigCtx.Err() != nil
|
||||
cancel()
|
||||
if err != nil {
|
||||
@@ -1638,12 +1694,21 @@ func main() {
|
||||
fmt.Println(c("Usage: /cfg <param> [val]", 31))
|
||||
}
|
||||
continue
|
||||
case u == "/models":
|
||||
ml, err := listModels(&cfg)
|
||||
if err != nil {
|
||||
fmt.Println(c("[error querying /models: "+err.Error()+"]", 31))
|
||||
continue
|
||||
}
|
||||
fmt.Println(c("Supported models at "+cfg.Endpoint+":", 1, 36))
|
||||
fmt.Print(renderMD(ml))
|
||||
continue
|
||||
case strings.HasPrefix(u, "!"):
|
||||
runDirectShell(strings.TrimPrefix(u, "!"), cfg.ShellTimeout)
|
||||
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"}, {"/cfg <k> [v]", "get/set config"}, {"!<cmd>", "run shell command directly"}, {"/help", "show help"}} {
|
||||
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"}, {"!<cmd>", "run shell command directly"}, {"/models", "list models at endpoint"}, {"/help", "show help"}} {
|
||||
fmt.Println(c(fmt.Sprintf(" %-15s", kv[0]), 1, 32) + kv[1])
|
||||
}
|
||||
continue
|
||||
@@ -1652,6 +1717,11 @@ func main() {
|
||||
turnMsgs = append(turnMsgs, Message{Role: "user", Content: strp(u)})
|
||||
sigCtx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
resMsgs, usg, err := AL(sigCtx, &cfg, turnMsgs, sp, 0)
|
||||
if lastRole(resMsgs) == "tool" {
|
||||
resMsgs = append(resMsgs, Message{Role: "user", Content: strp("continue")})
|
||||
fmt.Println(c("[auto continue: last message was a tool result]", 33))
|
||||
resMsgs, usg, err = AL(sigCtx, &cfg, resMsgs, sp, 0)
|
||||
}
|
||||
interrupted := sigCtx.Err() != nil
|
||||
cancel()
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user