feat(electrobun): project wizard phases 1-5 (WIP)
- sanitize.ts: input sanitization (trim, control chars, path traversal) - provider-scanner.ts: detect Claude/Codex/Ollama/Gemini availability - model-fetcher.ts: live model lists from 4 provider APIs - ModelConfigPanel.svelte: per-provider config (thinking, effort, sandbox, temperature) - WizardStep1-3.svelte: split wizard into composable steps - CustomDropdown/Checkbox/Radio: themed UI components - provider-handlers.ts: provider.scan + provider.models RPC - Wire providers into wizard step 3 (live detection + model lists) - Replace native selects in 5 settings panels with CustomDropdown
This commit is contained in:
parent
b7fc3a0f9b
commit
d4014a193d
25 changed files with 2112 additions and 759 deletions
107
ui-electrobun/src/bun/model-fetcher.ts
Normal file
107
ui-electrobun/src/bun/model-fetcher.ts
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* Model fetcher — retrieves available models from each provider's API.
|
||||
*
|
||||
* Each function returns a sorted list of model IDs.
|
||||
* Network errors return empty arrays (non-fatal).
|
||||
*/
|
||||
|
||||
export interface ModelInfo {
|
||||
id: string;
|
||||
name: string;
|
||||
provider: string;
|
||||
}
|
||||
|
||||
const TIMEOUT = 8000;
|
||||
|
||||
export async function fetchClaudeModels(): Promise<ModelInfo[]> {
|
||||
const apiKey = process.env.ANTHROPIC_API_KEY;
|
||||
if (!apiKey) return [];
|
||||
try {
|
||||
const res = await fetch('https://api.anthropic.com/v1/models', {
|
||||
headers: {
|
||||
'x-api-key': apiKey,
|
||||
'anthropic-version': '2023-06-01',
|
||||
},
|
||||
signal: AbortSignal.timeout(TIMEOUT),
|
||||
});
|
||||
if (!res.ok) return [];
|
||||
const data = await res.json() as { data?: Array<{ id: string; display_name?: string }> };
|
||||
return (data.data ?? [])
|
||||
.map(m => ({ id: m.id, name: m.display_name ?? m.id, provider: 'claude' }))
|
||||
.sort((a, b) => a.id.localeCompare(b.id));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchCodexModels(): Promise<ModelInfo[]> {
|
||||
const apiKey = process.env.OPENAI_API_KEY;
|
||||
if (!apiKey) return [];
|
||||
try {
|
||||
const res = await fetch('https://api.openai.com/v1/models', {
|
||||
headers: { 'Authorization': `Bearer ${apiKey}` },
|
||||
signal: AbortSignal.timeout(TIMEOUT),
|
||||
});
|
||||
if (!res.ok) return [];
|
||||
const data = await res.json() as { data?: Array<{ id: string }> };
|
||||
return (data.data ?? [])
|
||||
.map(m => ({ id: m.id, name: m.id, provider: 'codex' }))
|
||||
.sort((a, b) => a.id.localeCompare(b.id));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchOllamaModels(): Promise<ModelInfo[]> {
|
||||
try {
|
||||
const res = await fetch('http://localhost:11434/api/tags', {
|
||||
signal: AbortSignal.timeout(TIMEOUT),
|
||||
});
|
||||
if (!res.ok) return [];
|
||||
const data = await res.json() as { models?: Array<{ name: string; model?: string }> };
|
||||
return (data.models ?? [])
|
||||
.map(m => ({ id: m.name, name: m.name, provider: 'ollama' }))
|
||||
.sort((a, b) => a.id.localeCompare(b.id));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchGeminiModels(): Promise<ModelInfo[]> {
|
||||
const apiKey = process.env.GEMINI_API_KEY;
|
||||
if (!apiKey) return [];
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://generativelanguage.googleapis.com/v1beta/models?key=${apiKey}`,
|
||||
{ signal: AbortSignal.timeout(TIMEOUT) },
|
||||
);
|
||||
if (!res.ok) return [];
|
||||
const data = await res.json() as {
|
||||
models?: Array<{ name: string; displayName?: string }>;
|
||||
};
|
||||
return (data.models ?? [])
|
||||
.map(m => ({
|
||||
id: m.name.replace('models/', ''),
|
||||
name: m.displayName ?? m.name,
|
||||
provider: 'gemini',
|
||||
}))
|
||||
.sort((a, b) => a.id.localeCompare(b.id));
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch models for a specific provider.
|
||||
*/
|
||||
export async function fetchModelsForProvider(
|
||||
provider: string,
|
||||
): Promise<ModelInfo[]> {
|
||||
switch (provider) {
|
||||
case 'claude': return fetchClaudeModels();
|
||||
case 'codex': return fetchCodexModels();
|
||||
case 'ollama': return fetchOllamaModels();
|
||||
case 'gemini': return fetchGeminiModels();
|
||||
default: return [];
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue