Files

174 lines
5.4 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Fetch Models Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Add an "Aktualisieren" button next to the model dropdown in settings that fetches the 3 newest Claude models from the Anthropic API and repopulates the dropdown.
**Architecture:** `ClaudeClient` gains a `fetchModels()` method (reusing existing `requestUrl`/`headers` patterns). `SettingsTab` captures the `DropdownComponent` and `ButtonComponent` references, wires the button to call `fetchModels`, and rebuilds the dropdown on success.
**Tech Stack:** TypeScript, Obsidian plugin API (`requestUrl`, `Setting`, `DropdownComponent`, `ButtonComponent`, `Notice`), Anthropic Models API (`GET /v1/models`)
---
## File Map
| File | Change |
|---|---|
| `src/ClaudeClient.ts` | Add `fetchModels(apiKey)` method |
| `src/SettingsTab.ts` | Update imports; refactor "Modell" `Setting` to capture dropdown + add button |
---
### Task 1: Add `fetchModels` to `ClaudeClient`
**Files:**
- Modify: `src/ClaudeClient.ts`
- [ ] **Step 1: Add the method after the `chat()` method**
In `src/ClaudeClient.ts`, add after line 86 (after `chat()`'s closing brace):
```typescript
/** Fetch the 3 newest Claude models from the Anthropic Models API. */
async fetchModels(apiKey: string): Promise<{ id: string; name: string }[]> {
const response = await requestUrl({
url: "https://api.anthropic.com/v1/models",
method: "GET",
headers: this.headers(apiKey),
throw: false,
});
if (response.status >= 400) {
throw new Error(`API Error ${response.status}: ${response.text}`);
}
const data: { id: string; created: number }[] = response.json.data ?? [];
if (data.length === 0) {
throw new Error("No models returned");
}
return data
.sort((a, b) => b.created - a.created)
.slice(0, 3)
.map((m) => ({ id: m.id, name: m.id }));
}
```
- [ ] **Step 2: Verify build passes**
```bash
npm run build
```
Expected: no TypeScript errors, `main.js` written successfully.
- [ ] **Step 3: Commit**
```bash
git add src/ClaudeClient.ts
git commit -m "feat: add fetchModels to ClaudeClient"
```
---
### Task 2: Update SettingsTab — imports and model setting
**Files:**
- Modify: `src/SettingsTab.ts:1` (import line)
- Modify: `src/SettingsTab.ts:151-160` (the "Modell" Setting block)
- [ ] **Step 1: Extend the import from `"obsidian"`**
Replace the current import line 1:
```typescript
import { App, PluginSettingTab, Setting } from "obsidian";
```
With:
```typescript
import { App, ButtonComponent, DropdownComponent, Notice, PluginSettingTab, Setting } from "obsidian";
```
- [ ] **Step 2: Replace the "Modell" Setting block**
Replace lines 151160:
```typescript
new Setting(containerEl)
.setName("Modell")
.setDesc("Welches Claude-Modell verwenden?")
.addDropdown((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();
});
});
```
With:
```typescript
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 () => {
const prev = modelDrop.getValue();
refreshBtn.setDisabled(true);
refreshBtn.setButtonText("...");
try {
const models = await this.plugin.claude.fetchModels(this.plugin.settings.apiKey);
modelDrop.selectEl.empty();
for (const m of models) modelDrop.addOption(m.id, m.name);
modelDrop.setValue(prev);
this.plugin.settings.model = modelDrop.getValue();
await this.plugin.saveSettings();
} catch (err) {
new Notice("Modelle konnten nicht geladen werden: " + (err as Error).message);
} finally {
refreshBtn.setDisabled(false);
refreshBtn.setButtonText("Aktualisieren");
}
});
});
```
- [ ] **Step 3: Verify build passes**
```bash
npm run build
```
Expected: no TypeScript errors, `main.js` written successfully.
- [ ] **Step 4: Manual smoke test in Obsidian**
1. Copy `main.js`, `manifest.json`, `styles.css` to `.obsidian/plugins/memex-chat/` in your vault
2. Reload the plugin (or restart Obsidian)
3. Open Settings → Memex Chat
4. Confirm the "Modell" row has a dropdown and an "Aktualisieren" button
5. With a valid API key set, click "Aktualisieren" — button should show "...", then restore; dropdown should show 3 model IDs
6. With no API key, click "Aktualisieren" — a Notice should appear with an error message; dropdown should be unchanged
- [ ] **Step 5: Commit**
```bash
git add src/SettingsTab.ts
git commit -m "feat: add Aktualisieren button to fetch models from API"
```