mirror of
https://github.com/ollama/ollama.git
synced 2026-07-11 02:04:06 +00:00
cmd/tui/chat: show availability badge beside model name instead of plan in meta
This commit is contained in:
parent
1e004ef209
commit
eacaa79706
3 changed files with 64 additions and 8 deletions
|
|
@ -577,9 +577,56 @@ func agentModelOptions(ctx context.Context, client *api.Client) ([]agentchat.Mod
|
|||
add(name, agentLocalModelDescription(model), false, "", false)
|
||||
}
|
||||
|
||||
// Compute availability badges for cloud models based on account state.
|
||||
badges := cloudAvailabilityBadges(ctx, client, options)
|
||||
for i := range options {
|
||||
options[i].AvailabilityBadge = badges[options[i].Name]
|
||||
}
|
||||
|
||||
return options, nil
|
||||
}
|
||||
|
||||
// cloudAvailabilityBadges returns a map of model name → availability badge
|
||||
// for cloud models that require sign-in or a plan upgrade.
|
||||
func cloudAvailabilityBadges(ctx context.Context, client *api.Client, options []agentchat.ModelOption) map[string]string {
|
||||
badges := make(map[string]string)
|
||||
hasCloud := false
|
||||
for _, opt := range options {
|
||||
if opt.Cloud {
|
||||
hasCloud = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasCloud {
|
||||
return badges
|
||||
}
|
||||
|
||||
if disabled, known := agentCloudStatusDisabled(ctx, client); known && disabled {
|
||||
return badges
|
||||
}
|
||||
|
||||
whoamiCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
|
||||
defer cancel()
|
||||
user, err := client.Whoami(whoamiCtx)
|
||||
if err != nil {
|
||||
// Can't determine auth state — don't show badges.
|
||||
return badges
|
||||
}
|
||||
|
||||
signedIn := user != nil && user.Name != ""
|
||||
for _, opt := range options {
|
||||
if !opt.Cloud {
|
||||
continue
|
||||
}
|
||||
if !signedIn {
|
||||
badges[opt.Name] = "Sign in required"
|
||||
} else if opt.RequiredPlan != "" && !launch.PlanSatisfies(user.Plan, opt.RequiredPlan) {
|
||||
badges[opt.Name] = "Upgrade required"
|
||||
}
|
||||
}
|
||||
return badges
|
||||
}
|
||||
|
||||
func agentRecommendationDescription(rec api.ModelRecommendation) string {
|
||||
var parts []string
|
||||
if description := strings.TrimSpace(rec.Description); description != "" {
|
||||
|
|
|
|||
|
|
@ -36,11 +36,12 @@ var chatEmptyPrompts = []string{
|
|||
}
|
||||
|
||||
type ModelOption struct {
|
||||
Name string
|
||||
Description string
|
||||
Recommended bool
|
||||
RequiredPlan string
|
||||
Cloud bool
|
||||
Name string
|
||||
Description string
|
||||
Recommended bool
|
||||
RequiredPlan string
|
||||
Cloud bool
|
||||
AvailabilityBadge string
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ type chatPicker[T any] struct {
|
|||
fullFooter string
|
||||
itemTitle func(T) string
|
||||
itemMeta func(T) string
|
||||
itemBadge func(T) string
|
||||
}
|
||||
|
||||
type chatHistoryPopup struct {
|
||||
|
|
@ -318,6 +319,7 @@ func newChatModelPicker(models []ModelOption, current, filter string) *chatModel
|
|||
picker.fullFooter = "↑/↓ move • enter switch • type search • esc cancel"
|
||||
picker.itemTitle = func(model ModelOption) string { return model.Name }
|
||||
picker.itemMeta = func(model ModelOption) string { return modelOptionMeta(model, current) }
|
||||
picker.itemBadge = func(model ModelOption) string { return model.AvailabilityBadge }
|
||||
return picker
|
||||
}
|
||||
|
||||
|
|
@ -542,6 +544,7 @@ func newChatResumePicker(chats []appstore.ChatSummary, currentChatID string) *ch
|
|||
picker.fullFooter = "↑/↓ move • enter resume • type search • esc cancel"
|
||||
picker.itemTitle = resumeChatTitle
|
||||
picker.itemMeta = resumeChatMeta
|
||||
picker.itemBadge = func(appstore.ChatSummary) string { return "" }
|
||||
return picker
|
||||
}
|
||||
|
||||
|
|
@ -915,11 +918,15 @@ func (p *chatPicker[T]) render(width int) string {
|
|||
for i := start; i < end; i++ {
|
||||
item := filtered[i]
|
||||
selected := i == p.cursor
|
||||
badge := p.itemBadge(item)
|
||||
for j, line := range wrapChatText(p.itemTitle(item), max(10, width-2)) {
|
||||
marker := " "
|
||||
if selected && j == 0 {
|
||||
marker = "› "
|
||||
}
|
||||
if j == 0 && badge != "" {
|
||||
line += " " + chatResumeMetaStyle.Render("("+badge+")")
|
||||
}
|
||||
if selected {
|
||||
b.WriteString(chatResumeSelectedStyle.Render(marker + line))
|
||||
} else {
|
||||
|
|
@ -974,11 +981,15 @@ func (p *chatPicker[T]) renderInline(width int) []string {
|
|||
for i := start; i < end; i++ {
|
||||
item := filtered[i]
|
||||
selected := i == p.cursor
|
||||
badge := p.itemBadge(item)
|
||||
for j, line := range wrapChatText(p.itemTitle(item), max(10, width-2)) {
|
||||
marker := " "
|
||||
if selected && j == 0 {
|
||||
marker = "› "
|
||||
}
|
||||
if j == 0 && badge != "" {
|
||||
line += " " + chatResumeMetaStyle.Render("("+badge+")")
|
||||
}
|
||||
if selected {
|
||||
lines = append(lines, truncateRenderedLine(chatResumeSelectedStyle.Render(marker+line), width))
|
||||
} else {
|
||||
|
|
@ -1006,9 +1017,6 @@ func modelOptionMeta(model ModelOption, current string) string {
|
|||
if model.Description != "" {
|
||||
parts = append(parts, model.Description)
|
||||
}
|
||||
if model.RequiredPlan != "" {
|
||||
parts = append(parts, model.RequiredPlan+" plan")
|
||||
}
|
||||
return strings.Join(parts, " · ")
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue