# AI Auto-Fix & Gemini Flash Provider Design

> **Date:** 2026-02-26
> **Status:** Approved

---

## Overview

Two combined features:

1. **AI Provider Abstraction** — Add Gemini 2.5 Flash as the default AI provider with Claude as fallback, behind a unified interface.
2. **AI Auto-Fix for Validation Errors** — When a flow fails validation, offer an AI-powered "Fix with AI" button that generates structural fixes for review.

---

## Section 1: AI Provider Abstraction

### Design

New `backend/app/core/ai_provider.py` with a unified interface:

```python
class AIProvider(ABC):
    async def generate_json(
        self,
        system_prompt: str,
        messages: list[dict],
        max_tokens: int = 4096,
    ) -> tuple[str, int, int]:
        """Returns (text, input_tokens, output_tokens)"""
```

Two implementations:

| Provider | Model | SDK | Role |
|----------|-------|-----|------|
| `GeminiProvider` | `gemini-2.5-flash` | `google-genai` | Default |
| `AnthropicProvider` | `claude-haiku-4-5-20251001` | `anthropic` | Fallback |

### Provider Selection

- `get_ai_provider()` factory reads `AI_PROVIDER` env var (default: `"gemini"`)
- Falls back to Anthropic if Gemini key is missing
- Existing `ai_tree_generator_service.py` swaps direct Anthropic calls for `get_ai_provider()`

### New Environment Variables

| Variable | Default | Purpose |
|----------|---------|---------|
| `AI_PROVIDER` | `"gemini"` | Which provider to use (`gemini` or `anthropic`) |
| `GOOGLE_AI_API_KEY` | — | Gemini API key |

Existing `ANTHROPIC_API_KEY` remains for fallback.

### Config Changes (`core/config.py`)

```python
AI_PROVIDER: str = "gemini"
GOOGLE_AI_API_KEY: str | None = None
AI_MODEL_GEMINI: str = "gemini-2.5-flash"
AI_MODEL_ANTHROPIC: str = "claude-haiku-4-5-20251001"
```

---

## Section 2: AI Auto-Fix Feature

### Backend Endpoint

**`POST /api/v1/ai/fix-tree`**

Request:
```json
{
  "tree_structure": { /* full tree */ },
  "tree_name": "Router Troubleshooting",
  "tree_type": "troubleshooting",
  "validation_errors": [
    {
      "node_id": "node_abc",
      "message": "Decision node must have at least 2 children (branches)"
    }
  ]
}
```

Response:
```json
{
  "fixes": [
    {
      "target_node_id": "node_abc",
      "error_message": "Decision node must have at least 2 children (branches)",
      "description": "Added second branch 'Check firmware version' with solution node",
      "original_node": { /* snapshot before fix */ },
      "fixed_node": { /* replacement node with corrected subtree */ }
    }
  ],
  "tokens_used": { "input": 1200, "output": 800 }
}
```

### How It Works

1. For each validation error tied to a `node_id`, extract that node + its parent + siblings from the tree.
2. Build a prompt with:
   - The **full tree structure** serialized as a simplified outline (node titles + types + structure) for context
   - The **specific failing node** highlighted with full JSON detail
   - The **validation error message**
   - Instructions: "Fix ONLY this node's structural issue. Keep all existing content. Generate domain-relevant additions that fit the flow's topic."
3. AI returns a corrected version of that node (with children/options adjusted).
4. Backend re-validates the fixed node before returning it.
5. If re-validation fails, retry once with the error fed back (corrective prompt pattern).

### Prompt Strategy

The prompt gives the AI the full tree as a compact outline, then zooms into the failing node:

```
You are fixing a validation error in a troubleshooting flow called "Router Troubleshooting".

FULL FLOW OUTLINE:
- [decision] Is the router powered on?
  - [action] Check power cable → [solution] Power restored
  - [decision] Are lights blinking? ← ERROR HERE
    - [solution] Contact ISP

ERROR: Decision node "Are lights blinking?" must have at least 2 children (branches).

FAILING NODE (full detail):
{...json...}

Fix this node by adding the minimum structure needed to resolve the error.
Return ONLY the fixed node as JSON.
```

### Frontend UX

1. **Trigger**: "Fix with AI" button in `ValidationSummary` — appears when there are fixable errors (structural errors with a `node_id`).
2. **Loading state**: Button shows spinner + "Generating fixes..." — disabled during request.
3. **Review modal** (`AIFixReviewModal`): Shows each proposed fix as a card:
   - Error message at top
   - Before/after view of the node change
   - "Apply" / "Skip" buttons per fix
   - "Apply All" button in footer
4. **Apply**: Each accepted fix calls `updateNode(targetNodeId, fixedNode)` in the tree editor store.
5. **Re-validate**: After applying fixes, auto-run `validate()` to confirm resolution.

---

## Section 3: Scope & Constraints

### Fixable Errors (Auto-Fix Scope)

Only structural validation errors with a `node_id`:
- Decision node missing children/branches
- Decision node missing options
- Action node missing `next_node_id`
- Dead-end decision nodes (no children)

### NOT Fixable

- Global checks (tree too small/large, not enough solutions) — require rethinking the whole tree
- Content quality issues — out of scope
- Errors without a `node_id` (root-level issues)

Non-fixable errors still show in ValidationSummary but without the "Fix with AI" option.

### Token Budget

- Tree outline: ~50-100 tokens for a typical 15-node tree
- Failing node detail: ~100-200 tokens
- System prompt + instructions: ~300 tokens
- **Total input per fix: ~500-600 tokens**
- One API call per failing node (not batched)

### Error Handling

- Provider failure (rate limit, network): toast error, user can retry
- Fix fails re-validation: "AI couldn't generate a valid fix" with retry option
- Max 1 retry with corrective prompt per attempt
- Both provider and fallback fail: surface error to user

### Auth

- Requires `engineer` role or above (`require_engineer_or_admin`)

---

## New Files

| File | Purpose |
|------|---------|
| `backend/app/core/ai_provider.py` | Provider abstraction + Gemini/Anthropic implementations |
| `backend/app/core/ai_fix_service.py` | Fix generation logic + prompt building |
| `backend/app/api/endpoints/ai.py` | `POST /ai/fix-tree` endpoint |
| `backend/app/schemas/ai.py` | Request/response schemas for AI endpoints |
| `frontend/src/components/tree-editor/AIFixReviewModal.tsx` | Review modal for proposed fixes |

## Modified Files

| File | Change |
|------|--------|
| `backend/app/core/config.py` | Add Gemini config vars |
| `backend/app/core/ai_tree_generator_service.py` | Swap Anthropic calls for provider abstraction |
| `backend/app/api/router.py` | Register `/ai` routes |
| `frontend/src/api/trees.ts` | Add `fixTree()` API call |
| `frontend/src/components/tree-editor/ValidationSummary.tsx` | Add "Fix with AI" button |
