# LLM Response Cleaners

Fuente conceptual: MiroFish `backend/app/utils/llm_client.py` (AGPL-3.0)
Reimplementar desde cero. No copiar codigo.

## Funciones a implementar

### 1. strip_think_tags

- **Dominio:** core
- **Lang:** Python
- **Purity:** pure
- **Signature:** `strip_think_tags(text: str) -> str`
- **Descripcion:** Remueve tags `<think>...</think>` que algunos modelos (MiniMax, DeepSeek) incluyen en sus respuestas como "chain of thought" interno.
- **Algoritmo:** `re.sub(r'<think>[\s\S]*?</think>', '', text).strip()`
- **Deps:** `re`
- **Tests:** texto sin tags (sin cambio), texto con think tags, tags multilinea, multiples tags

### 2. strip_markdown_codeblock

- **Dominio:** core
- **Lang:** Python
- **Purity:** pure
- **Signature:** `strip_markdown_codeblock(text: str) -> str`
- **Descripcion:** Remueve wrapping de code blocks markdown que modelos a veces anaden al responder JSON. Ej: ````json\n{...}\n```` → `{...}`
- **Algoritmo:**
  1. Strip whitespace
  2. Remover prefijo: `re.sub(r'^```(?:json)?\s*\n?', '', text, flags=re.IGNORECASE)`
  3. Remover sufijo: `re.sub(r'\n?```\s*$', '', text)`
  4. Strip final
- **Deps:** `re`
- **Tests:** JSON sin codeblock, JSON con ```json wrapper, JSON con ``` solo, texto que no es JSON

### 3. parse_llm_json

- **Dominio:** core
- **Lang:** Python
- **Purity:** pure
- **Signature:** `parse_llm_json(response: str) -> dict`
- **Descripcion:** Parsea una respuesta de LLM como JSON, limpiando primero think tags y markdown codeblocks. Combina strip_think_tags + strip_markdown_codeblock + json.loads.
- **Algoritmo:**
  1. `strip_think_tags(response)`
  2. `strip_markdown_codeblock(result)`
  3. `json.loads(result)`
  4. Si falla: raise ValueError con el texto limpiado para debugging
- **Deps:** `json`, `re`
- **Error type:** ValueError (JSON invalido)
- **Tests:** JSON limpio, JSON con think tags y codeblock, JSON invalido (error)
