返回 DeepSeek-Reasonix
provider.go
根目录 / internal / cli / provider.go
1 package cli
2
3 import (
4 "fmt"
5 "strings"
6
7 tea "charm.land/bubbletea/v2"
8
9 "reasonix/internal/config"
10 "reasonix/internal/i18n"
11 )
12
13 // runProviderCommand handles "/provider": with no argument it opens the provider
14 // picker; "/provider <name>" switches to that
15 // provider's default model (or prompts the user to pick one when multiple models
16 // are configured).
17 func (m *chatTUI) runProviderCommand(input string) {
18 args := tokenizeArgs(input) // args[0] == "/provider"
19 if len(args) < 2 {
20 m.openProviderPicker()
21 return
22 }
23 name := args[1]
24 m.switchToProvider(name)
25 }
26
27 func (m *chatTUI) openProviderPicker() {
28 cfg, err := config.Load()
29 if err != nil {
30 m.notice("provider: " + err.Error())
31 return
32 }
33 curProvider := strings.SplitN(m.modelRef, "/", 2)[0]
34 var items []quickPickerItem
35 selected := 0
36 for i := range cfg.Providers {
37 p := &cfg.Providers[i]
38 if !p.Configured() {
39 continue
40 }
41 models := p.ChatModelList()
42 if len(models) == 0 {
43 models = p.ModelList()
44 }
45 status := ""
46 if p.Name == curProvider {
47 status = "active"
48 selected = len(items)
49 }
50 items = append(items, quickPickerItem{
51 ID: p.Name, Label: p.Name,
52 Description: fmt.Sprintf("%s · %d model(s)", p.Kind, len(models)), Status: status,
53 })
54 }
55 if len(items) == 0 {
56 m.notice("provider: no configured providers")
57 return
58 }
59 m.quickPick = &quickPicker{kind: quickPickerProvider, title: "Select provider", items: items, selected: selected}
60 }
61
62 // switchToProvider switches the session to the named provider's default model.
63 // If the provider has multiple models, it shows an interactive picker (in the
64 // setup/CLI style) if running in a TTY, or falls back to a notice listing
65 // available models.
66 func (m *chatTUI) switchToProvider(name string) {
67 cfg, err := config.Load()
68 if err != nil {
69 m.notice("provider: " + err.Error())
70 return
71 }
72 var entry *config.ProviderEntry
73 for i := range cfg.Providers {
74 p := &cfg.Providers[i]
75 if p.Name == name && p.Configured() {
76 entry = p
77 break
78 }
79 }
80 if entry == nil {
81 m.notice(fmt.Sprintf(i18n.M.ProviderUnknownFmt, name))
82 return
83 }
84
85 // Determine current provider.
86 curProvider := ""
87 if parts := strings.SplitN(m.modelRef, "/", 2); len(parts) == 2 {
88 curProvider = parts[0]
89 }
90
91 models := entry.ChatModelList()
92 if len(models) == 0 {
93 models = entry.ModelList()
94 }
95 if len(models) == 0 {
96 m.notice(fmt.Sprintf(i18n.M.ProviderNoModelsFmt, name))
97 return
98 }
99
100 // If only one model, switch directly.
101 if len(models) == 1 {
102 ref := entry.Name + "/" + models[0]
103 if entry.Name == curProvider && models[0] == "" {
104 m.notice(fmt.Sprintf(i18n.M.ProviderAlreadyOnFmt, name))
105 return
106 }
107 m.runModelSubcommand("/model " + ref)
108 return
109 }
110
111 items := make([]quickPickerItem, 0, len(models))
112 selected := 0
113 for _, model := range models {
114 ref := entry.Name + "/" + model
115 status := ""
116 if ref == m.modelRef {
117 status = "active"
118 selected = len(items)
119 }
120 items = append(items, quickPickerItem{ID: ref, Label: model, Description: entry.Name, Status: status})
121 }
122 m.quickPick = &quickPicker{
123 kind: quickPickerProviderModel, title: fmt.Sprintf(i18n.M.ProviderPickLabel, name),
124 items: items, selected: selected,
125 }
126 }
127
128 func (m chatTUI) handleQuickPickerKey(msg tea.KeyPressMsg) (tea.Model, tea.Cmd) {
129 p := m.quickPick
130 if p == nil {
131 return m, nil
132 }
133 result := p.handleKey(msg)
134 if result.cancelled {
135 m.quickPick = nil
136 return m, nil
137 }
138 if result.choice == nil {
139 return m, nil
140 }
141 kind := p.kind
142 choice := *result.choice
143 m.quickPick = nil
144 switch kind {
145 case quickPickerModel, quickPickerProviderModel:
146 m.runModelSubcommand("/model " + choice.ID)
147 if m.pendingModelSwitch != nil {
148 return m, m.pendingModelSwitch
149 }
150 case quickPickerProvider:
151 m.switchToProvider(choice.ID)
152 if m.pendingModelSwitch != nil {
153 return m, m.pendingModelSwitch
154 }
155 }
156 return m, nil
157 }
158
159 func (m chatTUI) renderQuickPicker() string {
160 if m.quickPick == nil {
161 return ""
162 }
163 return m.quickPick.render(m.width)
164 }
165
165 lines GO