Skip to content

Commit 980468d

Browse files
committed
Merge branch 'feature/improve-preformance'
2 parents 5d8a241 + 1bdcafd commit 980468d

3 files changed

Lines changed: 299 additions & 78 deletions

File tree

cmd/config.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -158,22 +158,39 @@ func runInteractiveConfig() {
158158
fmt.Println("Anthropic provider uses Claude Code CLI - no API key needed.")
159159
}
160160

161-
// Dynamically generate available models
162161
availableModels := map[string][]string{
163162
"openai": {},
164-
"copilot": {"openai/gpt-5-mini"},
163+
"copilot": {},
165164
"anthropic": {},
166165
}
167166

168167
modelDisplayToID := map[string]string{}
169168

170-
if selectedProvider == "openai" {
169+
switch selectedProvider {
170+
case "copilot":
171+
copilotModels := []struct {
172+
Display string
173+
ID string
174+
}{
175+
176+
{Display: "Auto", ID: "auto"},
177+
{Display: "GPT-4.1", ID: "gpt-4.1"},
178+
{Display: "GPT-4o", ID: "gpt-4o"},
179+
{Display: "GPT-5 mini", ID: "gpt-5-mini"},
180+
{Display: "Grok Code Fast 1", ID: "grok-code-fast-1"},
181+
{Display: "Raptor mini (Preview)", ID: "raptor-mini"},
182+
}
183+
for _, m := range copilotModels {
184+
availableModels["copilot"] = append(availableModels["copilot"], m.Display)
185+
modelDisplayToID[m.Display] = m.ID
186+
}
187+
case "openai":
171188
for id, m := range models.OpenAIModels {
172189
display := fmt.Sprintf("%s (%s)", m.Name, string(id))
173190
availableModels["openai"] = append(availableModels["openai"], display)
174191
modelDisplayToID[display] = string(id)
175192
}
176-
} else if selectedProvider == "anthropic" {
193+
case "anthropic":
177194
for _, m := range models.AnthropicModels {
178195
display := fmt.Sprintf("%s (%s)", m.Name, m.APIModel)
179196
availableModels["anthropic"] = append(availableModels["anthropic"], display)
@@ -189,7 +206,7 @@ func runInteractiveConfig() {
189206
// Try to set the default to the current model if possible
190207
isValidDefault := false
191208
currentDisplay := ""
192-
if selectedProvider == "openai" || selectedProvider == "anthropic" {
209+
if selectedProvider == "openai" || selectedProvider == "anthropic" || selectedProvider == "copilot" {
193210
for display, id := range modelDisplayToID {
194211
if id == currentModel || display == currentModel {
195212
isValidDefault = true
@@ -218,7 +235,7 @@ func runInteractiveConfig() {
218235
}
219236

220237
selectedModel := selectedDisplay
221-
if selectedProvider == "openai" || selectedProvider == "anthropic" {
238+
if selectedProvider == "openai" || selectedProvider == "anthropic" || selectedProvider == "copilot" {
222239
selectedModel = modelDisplayToID[selectedDisplay]
223240
}
224241

internal/config/token_cache.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package config
2+
3+
import (
4+
"crypto/sha256"
5+
"encoding/hex"
6+
"encoding/json"
7+
"fmt"
8+
"os"
9+
"path/filepath"
10+
"sync"
11+
"time"
12+
)
13+
14+
// CopilotTokenCache holds a cached Copilot API token with expiration metadata.
15+
type CopilotTokenCache struct {
16+
Token string `json:"token"`
17+
ExpiresAt time.Time `json:"expires_at"`
18+
GitHubTokenHash string `json:"github_token_hash"`
19+
}
20+
21+
var (
22+
memoryCache *CopilotTokenCache
23+
memoryCacheMu sync.RWMutex
24+
)
25+
26+
func getCacheFilePath() string {
27+
return filepath.Join(getConfigDir(), ".lazycommit.token.cache")
28+
}
29+
30+
func hashToken(token string) string {
31+
h := sha256.Sum256([]byte(token))
32+
return hex.EncodeToString(h[:])
33+
}
34+
35+
// GetCachedCopilotToken returns a valid cached token for the given GitHub token, or nil if unavailable.
36+
func GetCachedCopilotToken(githubToken string) *CopilotTokenCache {
37+
tokenHash := hashToken(githubToken)
38+
39+
memoryCacheMu.RLock()
40+
if memoryCache != nil &&
41+
memoryCache.GitHubTokenHash == tokenHash &&
42+
isTokenValid(memoryCache) {
43+
cache := *memoryCache
44+
memoryCacheMu.RUnlock()
45+
return &cache
46+
}
47+
memoryCacheMu.RUnlock()
48+
49+
cache, err := loadCacheFromDisk()
50+
if err != nil {
51+
return nil
52+
}
53+
54+
if cache.GitHubTokenHash != tokenHash || !isTokenValid(cache) {
55+
return nil
56+
}
57+
58+
memoryCacheMu.Lock()
59+
memoryCache = cache
60+
memoryCacheMu.Unlock()
61+
62+
return cache
63+
}
64+
65+
// SaveCopilotTokenCache persists the token to memory and disk.
66+
func SaveCopilotTokenCache(token string, expiresAtUnix int64, githubToken string) error {
67+
cache := &CopilotTokenCache{
68+
Token: token,
69+
ExpiresAt: time.Unix(expiresAtUnix, 0),
70+
GitHubTokenHash: hashToken(githubToken),
71+
}
72+
73+
memoryCacheMu.Lock()
74+
memoryCache = cache
75+
memoryCacheMu.Unlock()
76+
77+
return saveCacheToDisk(cache)
78+
}
79+
80+
// InvalidateCopilotTokenCache clears the cached token from memory and disk.
81+
func InvalidateCopilotTokenCache() {
82+
memoryCacheMu.Lock()
83+
memoryCache = nil
84+
memoryCacheMu.Unlock()
85+
86+
_ = os.Remove(getCacheFilePath())
87+
}
88+
89+
func isTokenValid(cache *CopilotTokenCache) bool {
90+
if cache == nil || cache.Token == "" {
91+
return false
92+
}
93+
expirationBuffer := 60 * time.Second
94+
return time.Now().Add(expirationBuffer).Before(cache.ExpiresAt)
95+
}
96+
97+
func loadCacheFromDisk() (*CopilotTokenCache, error) {
98+
data, err := os.ReadFile(getCacheFilePath())
99+
if err != nil {
100+
return nil, err
101+
}
102+
103+
var cache CopilotTokenCache
104+
if err := json.Unmarshal(data, &cache); err != nil {
105+
return nil, err
106+
}
107+
108+
return &cache, nil
109+
}
110+
111+
func saveCacheToDisk(cache *CopilotTokenCache) error {
112+
data, err := json.Marshal(cache)
113+
if err != nil {
114+
return fmt.Errorf("failed to marshal token cache: %w", err)
115+
}
116+
117+
cacheDir := filepath.Dir(getCacheFilePath())
118+
if err := os.MkdirAll(cacheDir, 0700); err != nil {
119+
return fmt.Errorf("failed to create cache directory: %w", err)
120+
}
121+
122+
if err := os.WriteFile(getCacheFilePath(), data, 0600); err != nil {
123+
return fmt.Errorf("failed to write token cache: %w", err)
124+
}
125+
126+
return nil
127+
}

0 commit comments

Comments
 (0)