|
5 | 5 | import { aiAnalysisHelpers } from '$lib/stores/ai-analysis.js';
|
6 | 6 | import { loadThemePreset, themePresets } from '$lib/stores/theme.js';
|
7 | 7 | import { uiHelpers } from '$lib/stores/ui';
|
| 8 | + import { fetchOpenRouterModels } from '$lib/utils/ai-analysis.js'; |
8 | 9 |
|
9 | 10 | let apiKeyInput = '';
|
10 | 11 | let guardianName = '';
|
|
23 | 24 |
|
24 | 25 | const displayKey = (k: keyof typeof themePresets) => (k === 'tokyoNight' ? 'tokyo-night' : k);
|
25 | 26 |
|
26 |
| - // OpenRouter model selection |
27 |
| - const models = [ |
28 |
| - 'anthropic/claude-3.5-sonnet', |
29 |
| - 'openai/gpt-4o-mini', |
30 |
| - 'google/gemini-1.5-pro', |
31 |
| - 'mistralai/mixtral-8x7b-instruct', |
32 |
| - ] as const; |
| 27 | + // OpenRouter model selection (dynamic; fallback list shown until fetched) |
| 28 | + let modelList: string[] = [ |
| 29 | + 'openai/gpt-oss-20b:free', |
| 30 | + 'mistralai/mixtral-8x7b-instruct:free', |
| 31 | + 'google/gemma-2-9b-it:free', |
| 32 | + 'openchat/openchat-7b:free', |
| 33 | + ]; |
33 | 34 | let currentModelIndex = 0;
|
| 35 | + let modelsLoading = false; |
34 | 36 |
|
35 | 37 | function loadModelFromStorage() {
|
36 | 38 | const saved = guardianHelpers.load();
|
37 | 39 | const savedModel = saved?.model as string | undefined;
|
38 | 40 | if (savedModel) {
|
39 |
| - const idx = models.indexOf(savedModel as any); |
| 41 | + const idx = modelList.indexOf(savedModel); |
40 | 42 | currentModelIndex = idx >= 0 ? idx : 0;
|
41 | 43 | } else {
|
42 | 44 | currentModelIndex = 0;
|
43 | 45 | }
|
44 | 46 | }
|
45 | 47 |
|
46 | 48 | function displayModel() {
|
47 |
| - return models[currentModelIndex] || models[0]; |
| 49 | + return modelList[currentModelIndex] || modelList[0]; |
48 | 50 | }
|
49 | 51 |
|
50 | 52 | function persistModel() {
|
|
53 | 55 |
|
54 | 56 | function cycleModel(dir: 'prev' | 'next') {
|
55 | 57 | if (dir === 'next') {
|
56 |
| - currentModelIndex = (currentModelIndex + 1) % models.length; |
| 58 | + currentModelIndex = modelList.length ? (currentModelIndex + 1) % modelList.length : 0; |
57 | 59 | } else {
|
58 |
| - currentModelIndex = (currentModelIndex - 1 + models.length) % models.length; |
| 60 | + currentModelIndex = modelList.length ? (currentModelIndex - 1 + modelList.length) % modelList.length : 0; |
59 | 61 | }
|
60 | 62 | persistModel();
|
61 | 63 | }
|
62 | 64 |
|
| 65 | + async function loadModels() { |
| 66 | + if (!apiKeyInput) return; |
| 67 | + modelsLoading = true; |
| 68 | + try { |
| 69 | + const list = await fetchOpenRouterModels(apiKeyInput, true); |
| 70 | + if (Array.isArray(list) && list.length) { |
| 71 | + modelList = list; |
| 72 | + // Re-align index to saved model if present |
| 73 | + const savedModel = guardianHelpers.load()?.model as string | undefined; |
| 74 | + if (savedModel) { |
| 75 | + const idx = modelList.indexOf(savedModel); |
| 76 | + currentModelIndex = idx >= 0 ? idx : 0; |
| 77 | + } else { |
| 78 | + currentModelIndex = 0; |
| 79 | + } |
| 80 | + } |
| 81 | + } catch (e) { |
| 82 | + // Ignore; keep fallback list |
| 83 | + } finally { |
| 84 | + modelsLoading = false; |
| 85 | + } |
| 86 | + } |
| 87 | +
|
63 | 88 | onMount(() => {
|
64 | 89 | // Load saved guardian data
|
65 | 90 | const saved = guardianHelpers.load();
|
|
76 | 101 |
|
77 | 102 | // Get model selection from guardian storage
|
78 | 103 | loadModelFromStorage();
|
| 104 | + // Fetch model list from OpenRouter if API key is present |
| 105 | + loadModels(); |
79 | 106 | });
|
80 | 107 |
|
81 | 108 | function toggleTheme(direction: 'prev' | 'next') {
|
|
233 | 260 | <!-- Model selection (OpenRouter) -->
|
234 | 261 | <div class="cli-row px-2 py-1">
|
235 | 262 | <span class="label" style="color: var(--petalytics-foam);">model</span>
|
236 |
| - <span class="value" style="color: var(--petalytics-text);">{displayModel()}</span> |
| 263 | + <span class="value" style="color: var(--petalytics-text);">{displayModel()} {#if modelsLoading}(loading...){/if}</span> |
237 | 264 | <div class="ml-2 flex items-center space-x-1">
|
238 | 265 | <button type="button" class="arrow-btn" onclick={() => cycleModel('prev')} aria-label="Previous model"><</button>
|
239 | 266 | <button type="button" class="arrow-btn" onclick={() => cycleModel('next')} aria-label="Next model">></button>
|
| 267 | + <button type="button" class="arrow-btn" onclick={loadModels} aria-label="Refresh models">↻</button> |
240 | 268 | </div>
|
241 | 269 | </div>
|
242 | 270 |
|
|
0 commit comments