Fix Story 12: Claude API key storage now working

- Fixed silent API key save failure by switching from keyring to Tauri store
- Removed keyring dependency (didn't work in macOS dev mode for unsigned apps)
- Implemented reliable cross-platform storage using tauri-plugin-store
- Added pendingMessageRef to preserve user message during API key dialog flow
- Refactored sendMessage to accept optional message parameter for retry
- Removed all debug logging and test code
- Removed unused entitlements.plist and macOS config
- API key now persists correctly between sessions
- Auto-retry after saving key works properly

Story 12 complete and archived.
This commit is contained in:
Dave
2025-12-27 20:08:24 +00:00
parent 2976c854d0
commit c2da7f9f18
5 changed files with 981 additions and 898 deletions

View File

@@ -0,0 +1,117 @@
# Story 12: Be Able to Use Claude
## User Story
As a user, I want to be able to select Claude (via Anthropic API) as my LLM provider so I can use Claude models instead of only local Ollama models.
## Acceptance Criteria
- [x] Claude models appear in the unified model dropdown (same dropdown as Ollama models)
- [x] Dropdown is organized with section headers: "Anthropic" and "Ollama" with models listed under each
- [x] When user first selects a Claude model, a dialog prompts for Anthropic API key
- [x] API key is stored securely (using Tauri store plugin for reliable cross-platform storage)
- [x] Provider is auto-detected from model name (starts with `claude-` = Anthropic, otherwise = Ollama)
- [x] Chat requests route to Anthropic API when Claude model is selected
- [x] Streaming responses work with Claude (token-by-token display)
- [x] Tool calling works with Claude (using Anthropic's tool format)
- [x] Context window calculation accounts for Claude models (200k tokens)
- [x] User's model selection persists between sessions
- [x] Clear error messages if API key is missing or invalid
## Out of Scope
- Support for other providers (OpenAI, Google, etc.) - can be added later
- API key management UI (rotation, multiple keys, view/edit key after initial entry)
- Cost tracking or usage monitoring
- Model fine-tuning or custom models
- Switching models mid-conversation (user can start new session)
- Fetching available Claude models from API (hardcoded list is fine)
## Technical Notes
- Anthropic API endpoint: `https://api.anthropic.com/v1/messages`
- API key should be stored securely (environment variable or secure storage)
- Claude models support tool use (function calling)
- Context windows: claude-3-5-sonnet (200k), claude-3-5-haiku (200k)
- Streaming uses Server-Sent Events (SSE)
- Tool format differs from OpenAI/Ollama - needs conversion
## Design Considerations
- Single unified model dropdown with section headers ("Anthropic", "Ollama")
- Use `<optgroup>` in HTML select for visual grouping
- API key dialog appears on-demand (first use of Claude model)
- Store API key in OS keychain using `keyring` crate (cross-platform)
- Backend auto-detects provider from model name pattern
- Handle API key in backend only (don't expose to frontend logs)
- Alphabetical sorting within each provider section
## Implementation Approach
### Backend (Rust)
1. Add `anthropic` feature/module for Claude API client
2. Create `AnthropicClient` with streaming support
3. Convert tool definitions to Anthropic format
4. Handle Anthropic streaming response format
5. Add API key storage (encrypted or environment variable)
### Frontend (TypeScript)
1. Add hardcoded list of Claude models (claude-3-5-sonnet-20241022, claude-3-5-haiku-20241022)
2. Merge Ollama and Claude models into single dropdown with `<optgroup>` sections
3. Create API key input dialog/modal component
4. Trigger API key dialog when Claude model selected and no key stored
5. Add Tauri command to check if API key exists in keychain
6. Add Tauri command to set API key in keychain
7. Update context window calculations for Claude models (200k tokens)
### API Differences
- Anthropic uses `messages` array format (similar to OpenAI)
- Tools are called `tools` with different schema
- Streaming events have different structure
- Need to map our tool format to Anthropic's format
## Security Considerations
- API key stored in OS keychain (not in files or environment variables)
- Use `keyring` crate for cross-platform secure storage
- Never log API key in console or files
- Backend validates API key format before making requests
- Handle API errors gracefully (rate limits, invalid key, network errors)
- API key only accessible to the app process
## UI Flow
1. User opens model dropdown → sees "Anthropic" section with Claude models, "Ollama" section with local models
2. User selects `claude-3-5-sonnet-20241022`
3. Backend checks Tauri store for saved API key
4. If not found → Frontend shows dialog: "Enter your Anthropic API key"
5. User enters key → Backend stores in Tauri store (persistent JSON file)
6. Chat proceeds with Anthropic API
7. Future sessions: API key auto-loaded from store (no prompt)
## Implementation Notes (Completed)
### Storage Solution
Initially attempted to use the `keyring` crate for OS keychain integration, but encountered issues in macOS development mode:
- Unsigned Tauri apps in dev mode cannot reliably access the system keychain
- The `keyring` crate reported successful saves but keys were not persisting
- No macOS keychain permission dialogs appeared
**Solution:** Switched to Tauri's `store` plugin (`tauri-plugin-store`)
- Provides reliable cross-platform persistent storage
- Stores data in a JSON file managed by Tauri
- Works consistently in both development and production builds
- Simpler implementation without platform-specific entitlements
### Key Files Modified
- `src-tauri/src/commands/chat.rs`: API key storage/retrieval using Tauri store
- `src/components/Chat.tsx`: API key dialog and flow with pending message preservation
- `src-tauri/Cargo.toml`: Removed `keyring` dependency, kept `tauri-plugin-store`
- `src-tauri/src/llm/anthropic.rs`: Anthropic API client with streaming support
### Frontend Implementation
- Added `pendingMessageRef` to preserve user's message when API key dialog is shown
- Modified `sendMessage()` to accept optional message parameter for retry scenarios
- API key dialog appears on first Claude model usage
- After saving key, automatically retries sending the pending message
### Backend Implementation
- `get_anthropic_api_key_exists()`: Checks if API key exists in store
- `set_anthropic_api_key()`: Saves API key to store with verification
- `get_anthropic_api_key()`: Retrieves API key for Anthropic API calls
- Provider auto-detection based on `claude-` model name prefix
- Tool format conversion from internal format to Anthropic's schema
- SSE streaming implementation for real-time token display