diff options
| author | Adam Malczewski <[email protected]> | 2026-05-23 19:07:21 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-05-23 19:07:21 +0900 |
| commit | 997b00034435440d412f955e05e53f09bae83f9e (patch) | |
| tree | 22d1f530e9e1a97bd19286456d2f06793bd030f7 /packages/frontend/src/lib/components | |
| parent | ff00dec6ae2971bee38c74cb00fe034de9a839ee (diff) | |
| download | dispatch-997b00034435440d412f955e05e53f09bae83f9e.tar.gz dispatch-997b00034435440d412f955e05e53f09bae83f9e.zip | |
feat: google gemini provider, adaptive thinking for opus 4.7, model search filter
- Added Google (Gemini) as a provider: add-key UI, env var resolution via resolveApiKey, usage tracking via native models endpoint + gemini.google.com cookie scraping
- @ai-sdk/anthropic upgraded to v3 (adaptive thinking support) with LanguageModelV1 cast for ai v4 compat
- Claude Opus 4.7 uses adaptive thinking (type: adaptive); all other models keep explicit budget tokens
- Model selector modal: search filter with space matching dash/underscore
- Copy button: all tool results truncated at 300 chars
- Sidebar layout fix: Claude Reset panel removed from flex-1 fill to prevent overlap
Diffstat (limited to 'packages/frontend/src/lib/components')
| -rw-r--r-- | packages/frontend/src/lib/components/KeyUsage.svelte | 53 | ||||
| -rw-r--r-- | packages/frontend/src/lib/components/ModelSelector.svelte | 46 |
2 files changed, 88 insertions, 11 deletions
diff --git a/packages/frontend/src/lib/components/KeyUsage.svelte b/packages/frontend/src/lib/components/KeyUsage.svelte index 49c3cf6..96a4b08 100644 --- a/packages/frontend/src/lib/components/KeyUsage.svelte +++ b/packages/frontend/src/lib/components/KeyUsage.svelte @@ -404,6 +404,59 @@ function hasBucketData(bucket: UsageBucket | undefined): boolean { <span class="text-xs text-base-content/40">Resets: {formatDate(entry.data.resetAt)}</span> {/if} </div> + {:else if entry.data.provider === "google"} + <div class="flex flex-col gap-0.5 pl-1"> + <!-- Cookie-scraped usage from gemini.google.com --> + {#if entry.data.currentUsage} + {@const u = entry.data.currentUsage} + <div class="flex flex-col gap-0.5"> + <div class="flex items-center justify-between"> + <span class="text-xs text-base-content/50">Current</span> + <span class="text-xs font-mono">{u.percentUsed}%</span> + </div> + <progress class="progress w-full h-2 {progressClass(u.percentUsed / 100)}" value={u.percentUsed} max="100"></progress> + {#if u.resetsAt} + <span class="text-xs text-base-content/40">Resets: {u.resetsAt}</span> + {/if} + </div> + {/if} + {#if entry.data.weeklyUsage} + {@const w = entry.data.weeklyUsage} + <div class="flex flex-col gap-0.5"> + <div class="flex items-center justify-between"> + <span class="text-xs text-base-content/50">Weekly</span> + <span class="text-xs font-mono">{w.percentUsed}%</span> + </div> + <progress class="progress w-full h-2 {progressClass(w.percentUsed / 100)}" value={w.percentUsed} max="100"></progress> + {#if w.resetsAt} + <span class="text-xs text-base-content/40">Resets: {w.resetsAt}</span> + {/if} + </div> + {/if} + <!-- API key rate limits --> + {#if !entry.data.currentUsage && entry.data.models && entry.data.models.length > 0} + {@const m = entry.data.models[0]} + <div class="flex items-center justify-between"> + <span class="text-xs text-base-content/50">Models</span> + <span class="text-xs font-mono">{entry.data.models.length} available</span> + </div> + {#if m.rpm > 0} + <div class="flex items-center justify-between"> + <span class="text-xs text-base-content/50">RPM</span> + <span class="text-xs font-mono">{m.rpm}</span> + </div> + {/if} + {#if m.requestsPerDay > 0} + <div class="flex items-center justify-between"> + <span class="text-xs text-base-content/50">RPD</span> + <span class="text-xs font-mono">{m.requestsPerDay.toLocaleString()}</span> + </div> + {/if} + {#if !entry.data.currentUsage} + <p class="text-xs text-base-content/40 mt-0.5">Set GEMINI_COOKIE (__Secure-1PSID) for usage %</p> + {/if} + {/if} + </div> {/if} {/if} </div> diff --git a/packages/frontend/src/lib/components/ModelSelector.svelte b/packages/frontend/src/lib/components/ModelSelector.svelte index bbbf036..235cf3b 100644 --- a/packages/frontend/src/lib/components/ModelSelector.svelte +++ b/packages/frontend/src/lib/components/ModelSelector.svelte @@ -62,6 +62,7 @@ const modelCache = new Map<string, string[]>(); let loadingModels = $state(false); let modelError = $state<string | null>(null); let sliderDragging = $state<number | null>(null); + let modelSearch = $state(""); let cwdExists = $state<boolean | null>(null); let cwdCheckTimer: ReturnType<typeof setTimeout> | null = null; @@ -126,6 +127,7 @@ const modelCache = new Map<string, string[]>(); if (!keyId) return; showModelModal = true; modelError = null; + modelSearch = ""; // Check session cache if (modelCache.has(keyId)) { @@ -393,17 +395,39 @@ const modelCache = new Map<string, string[]>(); <span>{modelError}</span> </div> {:else} - <div class="mt-4 flex flex-col gap-1 max-h-96 overflow-y-auto"> - {#each availableModels as model} - <button - class="btn {model === activeModelId - ? 'btn-primary' - : 'btn-ghost'} justify-start font-mono text-base" - onclick={() => selectModel(model)} - > - {model} - </button> - {/each} + {@const search = modelSearch.toLowerCase().trim()} + {@const searchRegex = search + ? new RegExp( + search.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/ /g, "[ _-]"), + ) + : null} + {@const filteredModels = searchRegex + ? availableModels.filter((m) => searchRegex.test(m.toLowerCase())) + : availableModels} + <div class="mt-4 flex flex-col gap-1"> + <input + type="text" + class="input input-bordered input-sm w-full" + placeholder="Filter models..." + bind:value={modelSearch} + /> + <div class="mt-2 max-h-96 overflow-y-auto flex flex-col gap-1"> + {#each filteredModels as model} + <button + class="btn {model === activeModelId + ? 'btn-primary' + : 'btn-ghost'} justify-start font-mono text-base" + onclick={() => selectModel(model)} + > + {model} + </button> + {/each} + {#if filteredModels.length === 0} + <p class="text-xs text-base-content/50 py-2 text-center"> + {search ? 'No models match your search.' : 'No models available.'} + </p> + {/if} + </div> </div> {/if} <div class="modal-action"> |
