@@ -21,8 +21,22 @@ function State:init_file_state(available_providers)
21
21
self .file_state [prov ] = {
22
22
chat_model = nil ,
23
23
command_model = nil ,
24
+ cached_models = {},
24
25
}
25
26
end
27
+ else
28
+ -- Ensure existing providers have cached_models initialized
29
+ for _ , prov in ipairs (available_providers ) do
30
+ if self .file_state [prov ] then
31
+ self .file_state [prov ].cached_models = self .file_state [prov ].cached_models or {}
32
+ else
33
+ self .file_state [prov ] = {
34
+ chat_model = nil ,
35
+ command_model = nil ,
36
+ cached_models = {},
37
+ }
38
+ end
39
+ end
26
40
end
27
41
self .file_state .current_provider = self .file_state .current_provider or { chat = nil , command = nil }
28
42
end
33
47
function State :init_state (available_providers , available_models )
34
48
self ._state .current_provider = self ._state .current_provider or { chat = nil , command = nil }
35
49
for _ , provider in ipairs (available_providers ) do
36
- self ._state [provider ] = self ._state [provider ] or {
37
- chat_model = nil ,
38
- command_model = nil ,
39
- }
40
- self :load_models (provider , " chat_model" , available_models )
41
- self :load_models (provider , " command_model" , available_models )
50
+ self ._state [provider ] = self ._state [provider ]
51
+ or {
52
+ chat_model = nil ,
53
+ command_model = nil ,
54
+ cached_models = {},
55
+ }
56
+
57
+ -- Copy cached_models from file_state if they exist
58
+ if self .file_state [provider ] and self .file_state [provider ].cached_models then
59
+ self ._state [provider ].cached_models = self .file_state [provider ].cached_models
60
+ end
61
+
62
+ -- Only load models if the provider has available models
63
+ if available_models [provider ] and # available_models [provider ] > 0 then
64
+ self :load_models (provider , " chat_model" , available_models )
65
+ self :load_models (provider , " command_model" , available_models )
66
+ end
42
67
end
43
68
end
44
69
47
72
--- @param model_type string # Type of model (e.g., "chat_model", "command_model").
48
73
--- @param available_models table
49
74
function State :load_models (provider , model_type , available_models )
75
+ -- Ensure provider exists in available_models and has models
76
+ if not available_models [provider ] or not available_models [provider ][1 ] then
77
+ return
78
+ end
79
+
50
80
local state_model = self .file_state and self .file_state [provider ] and self .file_state [provider ][model_type ]
51
81
local is_valid_model = state_model and utils .contains (available_models [provider ], state_model )
52
82
85
115
86
116
--- Saves the current state to the state file.
87
117
function State :save ()
118
+ -- Merge cached_models from file_state into _state before saving
119
+ for provider , data in pairs (self .file_state ) do
120
+ if type (data ) == " table" and data .cached_models and self ._state [provider ] then
121
+ self ._state [provider ].cached_models = data .cached_models
122
+ end
123
+ end
124
+
88
125
futils .table_to_file (self ._state , self .state_file )
89
126
end
90
127
@@ -141,4 +178,108 @@ function State:get_last_chat()
141
178
return self ._state .last_chat
142
179
end
143
180
181
+ --- Sets cached models for a provider with timestamp
182
+ --- @param provider string # Provider name
183
+ --- @param models table # Array of model names
184
+ --- @param endpoint_hash string | nil # Hash of the endpoint configuration for validation
185
+ function State :set_cached_models (provider , models , endpoint_hash )
186
+ -- Ensure provider exists in file_state
187
+ if not self .file_state [provider ] then
188
+ self .file_state [provider ] = {
189
+ chat_model = nil ,
190
+ command_model = nil ,
191
+ cached_models = {},
192
+ }
193
+ end
194
+
195
+ -- Ensure cached_models table exists for this provider
196
+ self .file_state [provider ].cached_models = self .file_state [provider ].cached_models or {}
197
+
198
+ local cache_entry = {
199
+ models = models ,
200
+ timestamp = os.time (),
201
+ endpoint_hash = endpoint_hash ,
202
+ }
203
+
204
+ self .file_state [provider ].cached_models = cache_entry
205
+
206
+ -- Also sync to _state if it exists for immediate availability
207
+ if self ._state [provider ] then
208
+ self ._state [provider ].cached_models = cache_entry
209
+ end
210
+ end
211
+
212
+ --- Gets cached models for a provider if they exist and are valid
213
+ --- @param provider string # Provider name
214
+ --- @param cache_expiry_hours number # Cache expiry time in hours
215
+ --- @param endpoint_hash string | nil # Current endpoint hash for validation
216
+ --- @return table | nil # Array of cached model names or nil if cache is invalid/expired
217
+ function State :get_cached_models (provider , cache_expiry_hours , endpoint_hash )
218
+ if not self .file_state [provider ] or not self .file_state [provider ].cached_models then
219
+ return nil
220
+ end
221
+
222
+ local cached = self .file_state [provider ].cached_models
223
+ -- If cached_models is empty table, return nil
224
+ if not cached .models or not cached .timestamp then
225
+ return nil
226
+ end
227
+
228
+ local now = os.time ()
229
+ local expiry_seconds = cache_expiry_hours * 3600
230
+
231
+ -- Check if cache is expired
232
+ if (now - cached .timestamp ) > expiry_seconds then
233
+ return nil
234
+ end
235
+
236
+ -- Check if endpoint configuration changed (if hash is provided)
237
+ if endpoint_hash and cached .endpoint_hash and cached .endpoint_hash ~= endpoint_hash then
238
+ return nil
239
+ end
240
+
241
+ return cached .models
242
+ end
243
+
244
+ --- Checks if cached models are valid for a provider
245
+ --- @param provider string # Provider name
246
+ --- @param cache_expiry_hours number # Cache expiry time in hours
247
+ --- @param endpoint_hash string | nil # Current endpoint hash for validation
248
+ --- @return boolean
249
+ function State :is_cache_valid (provider , cache_expiry_hours , endpoint_hash )
250
+ return self :get_cached_models (provider , cache_expiry_hours , endpoint_hash ) ~= nil
251
+ end
252
+
253
+ --- Clears cached models for a provider or all providers
254
+ --- @param provider string | nil # Provider name, or nil to clear all caches
255
+ function State :clear_cache (provider )
256
+ if provider then
257
+ -- Clear cache for specific provider
258
+ if self .file_state [provider ] and self .file_state [provider ].cached_models then
259
+ self .file_state [provider ].cached_models = {}
260
+ end
261
+ else
262
+ -- Clear all caches
263
+ for prov_name , prov_data in pairs (self .file_state ) do
264
+ if type (prov_data ) == " table" and prov_data .cached_models then
265
+ prov_data .cached_models = {}
266
+ end
267
+ end
268
+ end
269
+ end
270
+
271
+ --- Cleans up cache entries for providers that no longer exist
272
+ --- @param available_providers table # Current list of available providers
273
+ function State :cleanup_cache (available_providers )
274
+ -- Remove entire provider entries that no longer exist
275
+ for prov_name , _ in pairs (self .file_state ) do
276
+ -- Skip special keys like current_provider
277
+ if prov_name ~= " current_provider" and prov_name ~= " last_chat" then
278
+ if not utils .contains (available_providers , prov_name ) then
279
+ self .file_state [prov_name ] = nil
280
+ end
281
+ end
282
+ end
283
+ end
284
+
144
285
return State
0 commit comments