fetch.go

v0.7.0
Doc Versions Source
1
package tui
2
3
import (
4
	"encoding/json"
5
	"errors"
6
	"fmt"
7
8
	tea "github.com/charmbracelet/bubbletea"
9
	"sourcecraft.dev/bigbes/claudio/config"
10
	"sourcecraft.dev/bigbes/claudio/provider/vkproxy"
11
)
12
13
var errZipNotFound = fmt.Errorf("claude-code-config.zip not found")
14
15
func (m Model) loadVKProxyModels(forceRefresh bool) ([]vkproxy.ProxyModel, error) {
16
	// Try cache first (unless forcing refresh).
17
	if !forceRefresh && m.cache != nil {
18
		if data, ok := m.cache.GetModels("vkproxy"); ok {
19
			var models []vkproxy.ProxyModel
20
			if json.Unmarshal(data, &models) == nil && len(models) > 0 {
21
				return models, nil
22
			}
23
		}
24
	}
25
26
	// Parse from zip.
27
	zipPath := vkproxy.FindZip()
28
	if zipPath == "" {
29
		return nil, errZipNotFound
30
	}
31
	return m.loadVKProxyModelsFromPath(zipPath)
32
}
33
34
func (m Model) loadVKProxyModelsFromPath(zipPath string) ([]vkproxy.ProxyModel, error) {
35
	models, err := vkproxy.ParseZip(zipPath)
36
	if err != nil {
37
		return nil, err
38
	}
39
	if len(models) == 0 {
40
		return nil, fmt.Errorf("no models found in zip")
41
	}
42
43
	// Update cache.
44
	if m.cache != nil {
45
		if data, err := json.Marshal(models); err == nil {
46
			_ = m.cache.SetModels("vkproxy", data)
47
		}
48
	}
49
	return models, nil
50
}
51
52
func (m Model) startVKProxyPicker() (tea.Model, tea.Cmd) {
53
	return m.startVKProxyPickerWithRefresh(false)
54
}
55
56
func (m Model) startVKProxyPickerWithRefresh(forceRefresh bool) (tea.Model, tea.Cmd) {
57
	models, err := m.loadVKProxyModels(forceRefresh)
58
	if errors.Is(err, errZipNotFound) {
59
		return m.showZipFilePicker()
60
	}
61
	if err != nil {
62
		m.modelsErr = err
63
		m.phase = phaseSelect
64
		return m, nil
65
	}
66
	return m.showVKProxyModels(models)
67
}
68
69
func (m Model) showVKProxyModels(models []vkproxy.ProxyModel) (tea.Model, tea.Cmd) {
70
	// Provider-level fields the local proxy needs (auth + upstream URL).
71
	// Per-model env is forwarded later, at selection time.
72
	pc := m.cfg.Providers["vkproxy"]
73
	pc.APIKey = models[0].AuthToken
74
	pc.BaseURL = models[0].BaseURL
75
	m.cfg.Providers["vkproxy"] = pc
76
	m.cfg.ActiveProvider = "vkproxy"
77
78
	// Build the id → env lookup so model_select can forward every variable
79
	// declared in the install script for the chosen function.
80
	m.vkproxyEnv = make(map[string]map[string]string, len(models))
81
	for _, pm := range models {
82
		m.vkproxyEnv[pm.ModelID] = pm.Env
83
	}
84
85
	// Group models by provider for the two-column picker.
86
	providers, grouped := vkproxy.GroupByProvider(models)
87
	familyMap := make(map[string][]PickerItem)
88
	for prov, pms := range grouped {
89
		for _, pm := range pms {
90
			familyMap[prov] = append(familyMap[prov], PickerItem{
91
				ID:            pm.ModelID,
92
				Name:          pm.Name,
93
				ContextLength: pm.ContextWindow,
94
			})
95
		}
96
	}
97
98
	m.phase = phaseSelectModel
99
	m.pickerProvider = "vkproxy"
100
	m.families = providers
101
	m.familyModels = familyMap
102
	m.preSelectModel()
103
	return m, nil
104
}
105
106
// applyVKProxyEnv copies the per-model env block (captured from the install
107
// script's function body) into pc.Env. ANTHROPIC_BASE_URL and
108
// ANTHROPIC_AUTH_TOKEN are skipped — the local proxy hijacks those so claude
109
// talks to claudio while claudio talks to the upstream using pc.BaseURL /
110
// pc.APIKey. Everything else (CLAUDE_CODE_*, ANTHROPIC_*_MODEL, API_TIMEOUT_MS,
111
// AI_LOG_ENDPOINT, ENABLE_LSP_TOOL, …) is forwarded verbatim, since pc.Env is
112
// applied last in cmd/root.go and overrides the harness defaults.
113
func (m Model) applyVKProxyEnv(pc config.ProviderConfig, modelID string) config.ProviderConfig {
114
	env := m.vkproxyEnv[modelID]
115
	if len(env) == 0 {
116
		return pc
117
	}
118
	if pc.Env == nil {
119
		pc.Env = make(map[string]string, len(env))
120
	}
121
	for k, v := range env {
122
		switch k {
123
		case "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN":
124
			continue
125
		}
126
		pc.Env[k] = v
127
	}
128
	return pc
129
}
130

Source Files