> ## Documentation Index
> Fetch the complete documentation index at: https://docs.folksbase.joselito.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# AI Integration

> How folksbase uses Claude Haiku for CSV column mapping — and what happens when the AI is unavailable.

## Overview

folksbase uses Anthropic's Claude Haiku model in exactly two places:

1. **Column mapping suggestions** during CSV import — the AI looks at your CSV headers and sample data, then suggests which contact field each column maps to
2. **Import summary generation** — after processing completes, the AI generates a human-readable summary of what was imported

Both uses follow the same principle: AI enhances the experience but never blocks it. If the AI is unavailable, the feature degrades gracefully and the user can continue without interruption.

## Column Mapping

When you upload a CSV, the system sends the headers and first two sample rows to Claude Haiku with a focused prompt:

```
You map CSV headers to contact fields. Reply ONLY with valid JSON, no markdown.
Return a flat JSON object where each key is the EXACT original CSV header
and each value is one of: email | first_name | last_name | phone | company | notes | null
Use null if unsure.
```

The AI returns something like:

```json theme={"dark"}
{
  "Email Address": "email",
  "First": "first_name",
  "Last": "last_name",
  "Mobile": "phone",
  "Organization": "company",
  "Random Column": null
}
```

### Confidence Scoring

Each suggestion gets a confidence score (`high` or `low`) based on how closely the header name matches the field name:

* **High confidence** — the normalized header contains the field name, or the Levenshtein distance is less than 3 (e.g., "Email" → `email`, "fname" → `first_name`)
* **Low confidence** — the AI suggested a mapping but the names don't closely match, or the AI returned `null`

The UI uses confidence scores to visually distinguish strong matches from uncertain ones, helping users quickly spot mappings that need manual adjustment.

### Response Parsing

The AI sometimes returns nested objects or changes header casing. The parser handles this by:

1. First checking if top-level keys match the CSV headers (case-insensitive)
2. If not, searching nested objects for one whose keys overlap with the headers
3. Building a case-insensitive lookup to map AI response keys back to original headers

This makes the system resilient to variations in AI output format.

## Caching

Column mapping results are cached in Redis for 1 hour. The cache key is a SHA-256 hash of the sorted headers, versioned with a `v2` prefix. This means:

* Uploading the same CSV structure twice hits the cache
* Different column orders produce the same cache key (headers are sorted)
* Cache version can be bumped if the prompt or model changes

## Graceful Fallback

This is the most important design decision in the AI integration. Every AI call is wrapped in a try/catch that returns a fallback value instead of throwing:

```typescript theme={"dark"}
try {
  const result = await callAnthropic(...)
  await redis.setex(cacheKey, 3600, JSON.stringify(result))
  return result
} catch (error) {
  logger.error('AI call failed, using fallback', { error, context })
  return fallbackValue  // never throw
}
```

### What the fallbacks look like

| Feature        | Fallback behavior                                                                          |
| -------------- | ------------------------------------------------------------------------------------------ |
| Column mapping | Returns all headers mapped to `null` with `low` confidence — user maps everything manually |
| Import summary | Uses a static fallback string instead of an AI-generated summary                           |

### Timeout Protection

Both AI call sites use `AbortSignal.timeout(10_000)` (10 seconds). If the Anthropic API is slow or unresponsive, the request aborts after 10 seconds and the fallback kicks in. This prevents background job steps from hanging indefinitely.

## Why Claude Haiku?

The model choice is intentional:

* **Speed** — Haiku responds in under a second for this use case, keeping the upload flow snappy
* **Cost** — column mapping is a simple classification task that doesn't need a larger model
* **Reliability** — the fast response time means timeouts are rare in practice

The model is pinned to `claude-3-haiku-20240307`. Upgrading to a different model should be tested against the existing prompt and response parsing logic, since different models may format JSON responses differently.
