Files
obsidian-memex-chat/docs/superpowers/specs/2026-03-27-fetch-models-design.md
svemagie a5b1ef8158 fresh
2026-03-31 14:20:47 +02:00

4.1 KiB

Fetch Models Design

Date: 2026-03-27 Status: Approved

Summary

Add an "Aktualisieren" button to the Model setting in the settings tab. When clicked, it fetches the 3 newest Claude models from the Anthropic Models API and updates the dropdown. Falls back to the hardcoded MODELS list if the request fails or returns no models.

ClaudeClient changes

Add a new method fetchModels(apiKey: string): Promise<{id: string, name: string}[]> to ClaudeClient.

  • URL: "https://api.anthropic.com/v1/models" — inline string or a separate private constant; do not use or modify baseUrl (which points to /v1/messages)
  • Call requestUrl with throw: false (same pattern as streamChat/chat) and this.headers(apiKey)
  • Response shape: { data: [{ id: string, created: number, display_name: string, ... }] }
  • Throw on response.status >= 400 with the response text
  • If data is empty, throw an error ("No models returned") — do not return an empty array
  • Sort data descending by created, take top 3
  • Return { id, name: id } for each — use id as the display name (not display_name). Note: fetched entries will show raw IDs (e.g. claude-opus-4-6) while hardcoded MODELS show human-friendly names (e.g. "Claude Opus 4.6 (Stärkste)"). This is intentional — keeps the implementation simple and avoids relying on API-provided display strings.

SettingsTab changes

Import addition: Add Notice, ButtonComponent, DropdownComponent to the import { ... } from "obsidian" line.

Convert the existing "Modell" Setting to capture both the DropdownComponent and ButtonComponent references by chaining addDropdown() and addButton() on the same Setting instance:

let modelDrop: DropdownComponent;
let refreshBtn: ButtonComponent;

new Setting(containerEl)
  .setName("Modell")
  .setDesc("Welches Claude-Modell verwenden?")
  .addDropdown((drop) => {
    modelDrop = drop;
    for (const m of MODELS) drop.addOption(m.id, m.name);
    drop.setValue(this.plugin.settings.model).onChange(async (value) => {
      this.plugin.settings.model = value;
      await this.plugin.saveSettings();
    });
  })
  .addButton((btn) => {
    refreshBtn = btn;
    btn.setButtonText("Aktualisieren").onClick(async () => { /* see click flow */ });
  });

Click flow:

  1. Capture current value: const prev = modelDrop.getValue()
  2. refreshBtn.setDisabled(true) and refreshBtn.setButtonText("...")
  3. In a try/catch/finally:
    • try: Call this.plugin.claude.fetchModels(this.plugin.settings.apiKey)
    • On success: clear dropdown with modelDrop.selectEl.empty(), repopulate via modelDrop.addOption(id, name) for each fetched model, then set value to prev if it exists among the fetched ids, otherwise the first fetched id; save via this.plugin.settings.model = modelDrop.getValue(); await this.plugin.saveSettings()
    • catch: new Notice("Modelle konnten nicht geladen werden: " + err.message) — dropdown is not modified on error (hardcoded options remain)
    • finally: refreshBtn.setDisabled(false) and refreshBtn.setButtonText("Aktualisieren")

Fallback: The hardcoded MODELS array in SettingsTab.ts is unchanged and remains the initial population of the dropdown on every settings open.

Data flow

[Aktualisieren button click]
  → capture prev = modelDrop.getValue()
  → disable button, show "..."
  → this.plugin.claude.fetchModels(apiKey)  [throw: false, separate URL]
    → throw if status >= 400 or data empty
    → sort by created desc, take 3
    → return [{id, name: id}]
  → clear selectEl, repopulate, restore selection
  → save model to settings
  → finally: restore button

Error handling

Scenario Behaviour
No API key (401) Notice shown; dropdown unchanged
Network failure Notice shown; dropdown unchanged
Empty data array Treated as error; Notice shown; dropdown unchanged
Fewer than 3 models returned Take all returned (no error)

Out of scope

  • Persisting fetched models across restarts
  • Auto-fetching on settings open or plugin startup
  • Configurable count of models to show
  • Updating DEFAULT_SETTINGS.model after a fetch