constructor.go

v0.6.2
Doc Versions Source
1
package tui
2
3
import (
4
	"github.com/charmbracelet/bubbles/textinput"
5
	"sourcecraft.dev/bigbes/claudio/cache"
6
	"sourcecraft.dev/bigbes/claudio/config"
7
	"sourcecraft.dev/bigbes/claudio/provider"
8
	"sourcecraft.dev/bigbes/claudio/provider/copilot"
9
)
10
11
func buildEntries(cfg *config.Config, showHidden bool) []entry {
12
	seen := make(map[string]bool)
13
	var entries []entry
14
	for _, key := range provider.Order {
15
		if !showHidden && cfg.Providers[key].Hidden {
16
			seen[key] = true
17
			continue
18
		}
19
		p := provider.Registry[key]
20
		// Merge ollama and ollama-cloud into a single entry with toggle.
21
		if key == "ollama" {
22
			entries = append(entries, entry{Key: key, Name: p.Name, Description: p.Description, AltKey: "ollama-cloud"})
23
			seen[key] = true
24
			seen["ollama-cloud"] = true
25
			continue
26
		}
27
		// Skip ollama-cloud as it's handled with ollama.
28
		if key == "ollama-cloud" {
29
			continue
30
		}
31
		// Merge zai and zai-coding into a single entry with toggle.
32
		if key == "zai" {
33
			entries = append(entries, entry{Key: key, Name: p.Name, Description: p.Description, AltKey: "zai-coding"})
34
			seen[key] = true
35
			seen["zai-coding"] = true
36
			continue
37
		}
38
		// Skip zai-coding as it's handled with zai.
39
		if key == "zai-coding" {
40
			continue
41
		}
42
		// Merge kimi variants into a single entry with toggle.
43
		if key == "kimi" {
44
			entries = append(entries, entry{
45
				Key:         key,
46
				Name:        p.Name,
47
				Description: "Kimi K2.5 / K2 via Moonshot",
48
				AltKeys:     []string{"kimi-api-cn", "kimi-api-intl"},
49
			})
50
			seen[key] = true
51
			seen["kimi-api-cn"] = true
52
			seen["kimi-api-intl"] = true
53
			continue
54
		}
55
		// Skip kimi-api-cn and kimi-api-intl as they're handled with kimi.
56
		if key == "kimi-api-cn" || key == "kimi-api-intl" {
57
			continue
58
		}
59
		entries = append(entries, entry{Key: key, Name: p.Name, Description: p.Description})
60
		seen[key] = true
61
	}
62
	for key, pc := range cfg.Providers {
63
		if seen[key] || (!showHidden && pc.Hidden) {
64
			continue
65
		}
66
		name := pc.Name
67
		if name == "" {
68
			name = key
69
		}
70
		desc := pc.Description
71
		if desc == "" && pc.Compat == "openai" {
72
			desc = "Custom OpenAI-compatible provider"
73
		}
74
		entries = append(entries, entry{Key: key, Name: name, Description: desc})
75
	}
76
	return entries
77
}
78
79
func New(cfg *config.Config, cdb *cache.DB) Model {
80
	ti := textinput.New()
81
	ti.Placeholder = "sk-..."
82
	ti.CharLimit = 256
83
	ti.Width = 60
84
	ti.EchoMode = textinput.EchoPassword
85
	ti.EchoCharacter = '•'
86
87
	entries := buildEntries(cfg, false)
88
89
	cursor := 0
90
	// Default to cloud mode. If ActiveProvider is "ollama", switch to local mode.
91
	// This way the last chosen variant is persisted via ActiveProvider.
92
	ollamaCloud := true
93
	// Default to Coding mode. If ActiveProvider is "zai", switch to API mode.
94
	// This way the last chosen variant is persisted via ActiveProvider.
95
	zaiCoding := true
96
	// Default to Coding (0). Other variants: 1 = api-cn, 2 = api-intl
97
	kimiVariant := 0
98
	for i, e := range entries {
99
		if e.Key == cfg.ActiveProvider {
100
			cursor = i
101
			// If the active provider is the local ollama, set cloud mode to false
102
			if e.Key == "ollama" && e.AltKey == "ollama-cloud" {
103
				ollamaCloud = false
104
			}
105
			// If the active provider is zai (API), set coding mode to false
106
			if e.Key == "zai" && e.AltKey == "zai-coding" {
107
				zaiCoding = false
108
			}
109
			// If the active provider is a kimi variant, set the appropriate index
110
			if e.Key == "kimi" && len(e.AltKeys) == 2 {
111
				switch cfg.ActiveProvider {
112
				case "kimi-api-cn":
113
					kimiVariant = 1
114
				case "kimi-api-intl":
115
					kimiVariant = 2
116
				default:
117
					kimiVariant = 0
118
				}
119
			}
120
			break
121
		}
122
		// If active provider is ollama-cloud, find the merged ollama entry.
123
		if e.Key == "ollama" && e.AltKey == "ollama-cloud" && cfg.ActiveProvider == "ollama-cloud" {
124
			cursor = i
125
			ollamaCloud = true
126
			break
127
		}
128
		// If active provider is zai-coding, find the merged zai entry.
129
		if e.Key == "zai" && e.AltKey == "zai-coding" && cfg.ActiveProvider == "zai-coding" {
130
			cursor = i
131
			zaiCoding = true
132
			break
133
		}
134
		// If active provider is a kimi variant, find the merged kimi entry.
135
		if e.Key == "kimi" && len(e.AltKeys) == 2 {
136
			if cfg.ActiveProvider == "kimi-api-cn" {
137
				cursor = i
138
				kimiVariant = 1
139
				break
140
			}
141
			if cfg.ActiveProvider == "kimi-api-intl" {
142
				cursor = i
143
				kimiVariant = 2
144
				break
145
			}
146
		}
147
	}
148
149
	fi := textinput.New()
150
	fi.Placeholder = "filter..."
151
	fi.CharLimit = 64
152
	fi.Width = 30
153
154
	return Model{
155
		cfg:         cfg,
156
		cache:       cdb,
157
		entries:     entries,
158
		phase:       phaseSelect,
159
		cursor:      cursor,
160
		ollamaCloud: ollamaCloud,
161
		zaiCoding:   zaiCoding,
162
		kimiVariant: kimiVariant,
163
		input:       ti,
164
		filterInput: fi,
165
	}
166
}
167
168
// NewModelPicker creates a Model that goes directly to the copilot model selection phase.
169
func NewModelPicker(cfg *config.Config, cdb *cache.DB, models []copilot.CopilotModel) Model {
170
	families, grouped := copilot.VendorFamilies(models)
171
	items := make(map[string][]PickerItem)
172
	for fam, cms := range grouped {
173
		for _, cm := range cms {
174
			items[fam] = append(items[fam], PickerItem{ID: cm.ID, Name: cm.DisplayName(), ContextLength: cm.ContextWindow})
175
		}
176
	}
177
	m := Model{
178
		cfg:            cfg,
179
		cache:          cdb,
180
		phase:          phaseSelectModel,
181
		pickerProvider: "copilot",
182
		families:       families,
183
		familyModels:   items,
184
	}
185
	m.preSelectModel()
186
	return m
187
}
188

Source Files