aiwg
Version:
Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo
438 lines (339 loc) • 15 kB
Markdown
# ADR-RF-005: External API Integration Strategy
## Metadata
- **ID**: ADR-RF-005
- **Title**: External API Integration Strategy for Research Framework
- **Status**: Accepted
- **Created**: 2026-01-25
- **Updated**: 2026-01-25
- **Decision Makers**: Research Framework Architecture Team
- **Related ADRs**: ADR-RF-001 (Agent Orchestration), ADR-RF-002 (Provenance Storage)
## Context
The Research Framework relies on external APIs for core functionality:
1. **Semantic Scholar API**: Paper discovery, citation data, author information (200M+ papers)
2. **CrossRef API**: DOI resolution, metadata verification
3. **Unpaywall API**: Open access PDF locations
4. **LLM APIs**: Claude for summarization, extraction, quality analysis
5. **Optional**: Scite API (citation context), ORCID API (author verification)
These integrations introduce challenges:
- **Rate Limiting**: APIs enforce request limits (Semantic Scholar: 100 requests/5 min)
- **Availability**: External services may be unavailable
- **Data Freshness**: How long to cache results
- **Error Handling**: Graceful degradation when APIs fail
- **Cost**: LLM APIs have usage-based pricing
- **Offline Support**: NFR-RF-X-001 requires core functionality offline
### Decision Drivers
1. **Reliability**: NFR-RF-R-001 requires automatic retry with exponential backoff
2. **Performance**: NFR-RF-P-001 requires <10s search response time
3. **Offline Operation**: Core functionality must work without internet
4. **Cost Efficiency**: Minimize unnecessary API calls
5. **Reproducibility**: API results must be traceable for provenance
6. **Simplicity**: Avoid over-engineering for solo developer context
### Current API Characteristics
| API | Rate Limit | Cacheability | Offline Impact |
|-----|-----------|--------------|----------------|
| Semantic Scholar | 100/5min (free), 1/sec | High (papers don't change often) | Discovery blocked |
| CrossRef | 50/sec | Very High (DOIs are stable) | Metadata verification blocked |
| Unpaywall | 100K/day | High (OA status stable) | PDF acquisition limited |
| Claude API | Token-based | Medium (prompts vary) | Summarization blocked |
| Scite | Varies | Medium (context evolves) | Citation context blocked |
## Decision
**Adopt a Layered API Abstraction with Aggressive Caching and Graceful Degradation.**
### Architecture
```
┌────────────────────────────────────────────────────────────────┐
│ Research Agents │
│ (Discovery, Acquisition, Documentation, Quality, etc.) │
└────────────────────────────┬───────────────────────────────────┘
│
▼
┌────────────────────────────────────────────────────────────────┐
│ API Abstraction Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────┐│
│ │ Caching │ │ Rate Limiter │ │ Error Handler ││
│ │ (Local) │ │ (Per-API) │ │ (Retry/Fallback/Degrade) ││
│ └──────────────┘ └──────────────┘ └──────────────────────────┘│
└────────────────────────────┬───────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Semantic Scholar│ │ CrossRef │ │ Claude API │
│ API │ │ API │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
### Key Components
#### 1. API Abstraction Layer
All external API calls go through a unified abstraction that provides:
```typescript
interface APIClient {
// Execute request with caching, rate limiting, retry
request<T>(endpoint: string, options: RequestOptions): Promise<T>;
// Check if API is available (health check)
isAvailable(): Promise<boolean>;
// Get cached result if available
getCached<T>(key: string): T | null;
// Clear cache (for refresh)
clearCache(pattern?: string): void;
}
```
#### 2. Caching Strategy
**Cache Location**: `.aiwg/research/.cache/` (gitignored)
**Cache Policies**:
| API | TTL | Strategy |
|-----|-----|----------|
| Semantic Scholar (search) | 24 hours | Results may change with new papers |
| Semantic Scholar (paper) | 30 days | Paper metadata stable |
| CrossRef | 90 days | DOI data very stable |
| Unpaywall | 7 days | OA status can change |
| LLM responses | Permanent | Deterministic for same input |
**Cache Key Format**:
```
{api}-{operation}-{hash(params)}
e.g., semantic-scholar-paper-sha256:a1b2c3...
```
#### 3. Rate Limiting
**Token Bucket Algorithm** per API:
```typescript
interface RateLimiter {
tokens: number; // Current tokens
maxTokens: number; // Bucket size
refillRate: number; // Tokens per second
lastRefill: number; // Last refill timestamp
acquire(): Promise<void>; // Wait for token
tryAcquire(): boolean; // Non-blocking check
}
```
**Configured Limits**:
| API | Max Tokens | Refill Rate | Burst |
|-----|-----------|-------------|-------|
| Semantic Scholar | 100 | 20/min | 10 |
| CrossRef | 50 | 50/sec | 50 |
| Claude | 10 | 1/sec | 5 |
#### 4. Error Handling and Retry
**Retry Policy**:
- 3 attempts with exponential backoff (1s, 2s, 4s)
- Jitter to prevent thundering herd
- Circuit breaker after 5 consecutive failures
**Error Classification**:
| Error Type | Action | User Message |
|------------|--------|--------------|
| 429 Rate Limit | Wait and retry | "Rate limit reached, waiting..." |
| 503 Unavailable | Retry with backoff | "Service temporarily unavailable, retrying..." |
| 401 Unauthorized | Fail fast | "API key invalid. Check configuration." |
| 404 Not Found | Return null | "Resource not found." |
| Timeout | Retry once | "Request timed out, retrying..." |
| Network Error | Check offline | "Network unavailable. Working offline." |
#### 5. Graceful Degradation
When APIs are unavailable, framework degrades gracefully:
| Feature | Online | Offline | Degraded |
|---------|--------|---------|----------|
| Discovery (search) | Full | Cached only | Partial results |
| Acquisition (PDF) | Full | Blocked | Manual download |
| Metadata lookup | Full | Cached | User input |
| Summarization | Full | Blocked | Manual summary |
| Citation network | Full | Cached | Stale data |
| Quality scoring | Full | Partial (no external signals) | Limited accuracy |
### Offline Operation Support
Per NFR-RF-X-001, core functionality works offline:
**Offline-Capable**:
- Metadata management (edit existing)
- Note-taking (literature/permanent notes)
- Local search (index.json)
- Bibliography generation (from cache)
- Quality scoring (without citation count)
**Online-Required**:
- New paper discovery
- PDF acquisition
- LLM summarization
- Citation network updates
- Gap analysis (requires fresh data)
**Offline Indicator**:
```bash
$ aiwg research status
Research Framework Status:
Offline Mode: ACTIVE
Cached Sources: 45
Last Sync: 2026-01-25T10:00:00Z
Available: Metadata, Notes, Search (cached), Export
Unavailable: Discovery, Acquisition, Summarization
```
## Consequences
### Positive
1. **Reliability**: Retry logic and caching ensure resilience to transient failures
2. **Performance**: Aggressive caching reduces API calls and improves response time
3. **Cost Control**: Caching LLM responses prevents duplicate API spend
4. **Offline Support**: Core workflows continue without internet
5. **Transparency**: Clear feedback when operating in degraded mode
6. **Maintainability**: Unified abstraction simplifies API changes
### Negative
1. **Stale Data**: Cached results may be outdated (mitigated by TTL)
2. **Cache Size**: Large corpora may accumulate significant cache (mitigated by TTL expiry)
3. **Complexity**: Abstraction layer adds code to maintain
4. **Debugging**: Cached responses can mask API issues
### Risks
| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|
| API deprecation | Low | High | Abstract API clients, monitor deprecation notices |
| Rate limit changes | Medium | Medium | Configurable rate limits, adaptive throttling |
| Cache corruption | Low | Medium | Validate cache entries, rebuild on corruption |
| API key exposure | Low | High | Follow token security rules, environment variables |
| Cost overruns (LLM) | Medium | Medium | Usage tracking, budget alerts, batch processing |
## Alternatives Considered
### Option A: Direct API Calls
**Description**: Call APIs directly from agents without abstraction layer.
**Example**:
```typescript
// In Discovery Agent
const results = await fetch(`https://api.semanticscholar.org/graph/v1/paper/search?query=${query}`);
```
**Pros**:
- Simple implementation
- No abstraction overhead
- Direct control
**Cons**:
- Rate limiting logic duplicated across agents
- No caching benefits
- Harder to test (mock each API)
- No graceful degradation
- Fragile to API changes
**Decision**: Rejected. Benefits of abstraction outweigh simplicity gains.
### Option B: External Caching Service
**Description**: Use Redis or similar for API response caching.
**Pros**:
- Proven caching infrastructure
- Cross-process cache sharing
- Sophisticated eviction policies
**Cons**:
- External dependency (Redis)
- Infrastructure overhead
- Violates offline-first principle
- Overkill for solo developer
**Decision**: Rejected. File-based cache sufficient for target scale.
### Option C: Background Sync Service
**Description**: Background process that pre-fetches and syncs API data.
**Example**:
```
[Background Worker] ---> [API] ---> [Local Database]
[Research Agents] ---> [Local Database]
```
**Pros**:
- Agents always have local data
- No API calls during user workflows
- True offline-first
**Cons**:
- Complex architecture (worker process)
- Storage overhead (pre-fetch everything)
- Stale data risk
- Hard to know what to pre-fetch
**Decision**: Rejected. On-demand caching simpler and more efficient.
### Option D: API Gateway/Proxy
**Description**: Route all API calls through local gateway that handles caching/rate-limiting.
**Pros**:
- Language-agnostic
- Centralized API management
- Could add authentication, logging
**Cons**:
- Additional process to run
- Network hop overhead
- Overkill for single-user tool
- Infrastructure complexity
**Decision**: Rejected. In-process abstraction sufficient for CLI tool.
## Implementation Notes
### Cache File Structure
```
.aiwg/research/.cache/
├── semantic-scholar/
│ ├── search/ # Search result caches
│ │ └── {hash}.json
│ └── paper/ # Paper metadata caches
│ └── {paperId}.json
├── crossref/
│ └── {doi-encoded}.json
├── unpaywall/
│ └── {doi-encoded}.json
├── llm/
│ └── {prompt-hash}.json
└── meta.json # Cache statistics
```
### API Client Configuration
`.aiwg/research/config/api-config.yaml`:
```yaml
semantic_scholar:
base_url: "https://api.semanticscholar.org/graph/v1"
rate_limit:
requests_per_minute: 100
burst: 10
cache:
search_ttl: 86400 # 24 hours
paper_ttl: 2592000 # 30 days
retry:
max_attempts: 3
backoff_base: 1000 # ms
crossref:
base_url: "https://api.crossref.org/works"
rate_limit:
requests_per_second: 50
cache:
ttl: 7776000 # 90 days
claude:
# Key loaded from environment: ANTHROPIC_API_KEY
model: "claude-opus-4-5"
rate_limit:
requests_per_second: 1
cache:
ttl: -1 # Permanent (deterministic)
```
### Provenance for API Calls
All API calls logged for reproducibility:
```json
{
"activity": {
"aiwg:api-call-001": {
"prov:type": "aiwg:APICall",
"aiwg:api": "semantic-scholar",
"aiwg:endpoint": "/paper/search",
"aiwg:params": { "query": "oauth security" },
"aiwg:cached": false,
"aiwg:response_hash": "sha256:abc123...",
"prov:startTime": "2026-01-25T10:00:00Z"
}
}
}
```
### Health Check Command
```bash
$ aiwg research health
API Health Status:
Semantic Scholar: OK (latency: 145ms)
CrossRef: OK (latency: 89ms)
Unpaywall: OK (latency: 112ms)
Claude API: OK (latency: 234ms)
Cache Status:
Total Size: 45MB
Entries: 1,234
Hit Rate (24h): 78%
```
### Token Security
Per @$AIWG_ROOT/agentic/code/frameworks/sdlc-complete/rules/token-security.md:
```typescript
// Load API keys securely
const semanticScholarKey = process.env.SEMANTIC_SCHOLAR_API_KEY
|| await readFile('~/.aiwg/config/api-keys.env', 'utf8')
.then(parse)
.catch(() => null);
// Never log API keys
logger.info('API configured', { api: 'semantic-scholar', hasKey: !!semanticScholarKey });
```
## References
- @$AIWG_ROOT/agentic/code/frameworks/research-complete/elaboration/nfr/NFR-RF-specifications.md - NFR-RF-R-001 (Retry), NFR-RF-P-001 (Performance), NFR-RF-X-001 (Offline)
- @$AIWG_ROOT/agentic/code/frameworks/research-complete/elaboration/use-cases/UC-RF-001-discover-research-papers.md - Discovery use case
- @$AIWG_ROOT/agentic/code/frameworks/research-complete/elaboration/use-cases/UC-RF-002-acquire-research-source.md - Acquisition use case
- @$AIWG_ROOT/agentic/code/frameworks/research-complete/inception/initial-risk-assessment.md - Risk T-02 (API Rate Limits)
- @$AIWG_ROOT/agentic/code/frameworks/sdlc-complete/rules/token-security.md - API key security patterns
- [Semantic Scholar API](https://api.semanticscholar.org/api-docs/) - API documentation
- [CrossRef API](https://api.crossref.org/) - DOI resolution
**Document Status**: Accepted
**Review Date**: 2026-01-25
**Next Review**: End of Construction Phase