Metadata-Version: 2.5
Name: llm-output-repair
Version: 0.2.0
Summary: FSM syntactic engine to repair truncated/broken JSON from LLMs (streaming, Pydantic, zero-regex core)
Project-URL: Homepage, https://github.com/sergioo7r/llm-json-repair
Project-URL: Repository, https://github.com/sergioo7r/llm-json-repair
Project-URL: Issues, https://github.com/sergioo7r/llm-json-repair/issues
Project-URL: Changelog, https://github.com/sergioo7r/llm-json-repair/blob/main/CHANGELOG.md
Author: Sergio
Maintainer: Sergio
License: MIT
License-File: LICENSE
Keywords: agents,json,langchain,llm,openai,parsing,repair
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Provides-Extra: all
Requires-Dist: dirtyjson>=1.0; extra == 'all'
Requires-Dist: json-repair>=0.30; extra == 'all'
Requires-Dist: openai>=1.0; extra == 'all'
Requires-Dist: pydantic>=2.0; extra == 'all'
Provides-Extra: benchmark
Requires-Dist: dirtyjson>=1.0; extra == 'benchmark'
Requires-Dist: json-repair>=0.30; extra == 'benchmark'
Provides-Extra: dev
Requires-Dist: build; extra == 'dev'
Requires-Dist: mkdocs>=1.5; extra == 'dev'
Requires-Dist: pytest-cov>=4; extra == 'dev'
Requires-Dist: pytest>=7; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Requires-Dist: twine; extra == 'dev'
Provides-Extra: openai
Requires-Dist: openai>=1.0; extra == 'openai'
Provides-Extra: pydantic
Requires-Dist: pydantic>=2.0; extra == 'pydantic'
Provides-Extra: test
Requires-Dist: pytest-cov>=4; extra == 'test'
Requires-Dist: pytest>=7; extra == 'test'
Description-Content-Type: text/markdown

# llm-json-repair

[![PyPI version](https://img.shields.io/pypi/v/llm-output-repair.svg)](https://pypi.org/project/llm-output-repair/)
[![Python 3.9-3.13](https://img.shields.io/badge/python-3.9--3.13-blue.svg)](https://pypi.org/project/llm-output-repair/)
[![Coverage](https://img.shields.io/codecov/c/github/sergioo7r/llm-json-repair.svg)](https://codecov.io/gh/sergioo7r/llm-json-repair)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

Repara JSON roto devuelto por LLMs y devuélvelo como `dict` válido. Sin dependencias (solo stdlib).

**Problema que resuelve:** los modelos (GPT, Claude, Llama, etc.) suelen devolver JSON con comas sueltas, comillas sin cerrar, bloques ` ```json ` incompletos, texto explicativo alrededor, literales Python (`True`/`None`), comentarios o corchetes sin cerrar. Esta librería lo repara automáticamente.

## v0.2: motor FSM

Nuevo núcleo sintáctico en `v0.2.0` (ver `CHANGELOG.md` y `docs/ARCHITECTURE.md`):

- Tokenizer char-by-char sin regex en el núcleo: recorre la entrada carácter a carácter, respetando strings y escapes.
- Autoclose de truncados: cierra strings, objetos y arrays incompletos (`{"a": [1, 2` → `{"a": [1, 2]}`).
- `repair_stream` / `parse_stream` / `StreamingRepairer`: reparación incremental para tokens en streaming.
- `repair_and_validate` con Pydantic (opcional): repara y valida contra un `BaseModel`.
- Benchmarks con 500 casos: comparativa vs `json-repair` y `dirtyjson` (ver `docs/BENCHMARKS.md`).

## Benchmark (resumen)

| Librería | Casos | Reparados | Tasa |
|---|---|---|---|
| `llm-output-repair` (v0.2.0) | 500 | 500 | 100.0% (0.04 ms/caso) |
| `json-repair` | 500 | 500 | 100.0% (0.06 ms/caso) |
| `dirtyjson` | 500 | 219 | 43.8% |

> Detalle completo y metodología en [`docs/BENCHMARKS.md`](docs/BENCHMARKS.md).

> **Compatibilidad API v0.1:** `repair_json(text) -> str`, `loads(text) -> Any` y `RepairError` mantienen la misma firma y semántica que en v0.1.0. Lo nuevo (`streaming`, `validation`) es solo aditivo, no rompe código existente.

## Ejemplo rápido

```python
from llm_json_repair import loads

broken = '''Here is your JSON:
```json
{"name": "Ana", "tags": ['ia', 'python',], "active": True,}
'''
print(loads(broken))
# {'name': 'Ana', 'tags': ['ia', 'python'], 'active': True}
```

## Instalación

```bash
pip install llm-output-repair
```

> PyPI: `llm-output-repair` · import: `llm_json_repair` (el nombre PyPI estaba ocupado).

Requiere Python >= 3.9. Sin dependencias de terceros.

Desde fuente:

```bash
git clone https://github.com/sergioo7r/llm-json-repair
pip install -e .
```

## Uso

```python
from llm_json_repair import repair_json, loads, RepairError

# 1. Reparar a string JSON válido
fixed_str = repair_json('{"a": 1,}')
# '{"a": 1}'

# 2. Reparar + parsear a objeto Python
data = loads('{"a": 1,}')
# {'a': 1}

# 3. Manejo de errores
try:
    data = loads("esto no es json")
except RepairError as e:
    print("irreparable:", e)
```

- `repair_json(text) -> str`: devuelve string JSON válido. Lanza `TypeError` si la entrada no es `str`, `RepairError` si no hay objeto/array reparable.
- `loads(text) -> Any`: repara y parsea con `json.loads`. Misma semántica de errores.
- `RepairError(ValueError)`: indica entrada irreparable (vacía, sin `{`/`[`, o inválida tras el pipeline).

## Tabla de reparaciones

| Caso | Input | Output |
|---|---|---|
| Coma final | `{"a": 1,}` | `{"a": 1}` |
| Comillas simples | `{'a': 'x'}` | `{"a": "x"}` |
| Claves sin comillas | `{name: "x"}` | `{"name": "x"}` |
| Literales Python | `{"a": True, "b": None}` | `{"a": true, "b": null}` |
| Comentarios | `{"a": 1 /* c */, "b": 2}` | `{"a": 1, "b": 2}` |
| Markdown cerrado | ```` ```json\n{"a": 1}\n``` ```` | `{"a": 1}` |
| Markdown sin cerrar | ```` ```json\n{"a": 1} ```` | `{"a": 1}` |
| Texto alrededor | `Here is your JSON: {"a": 1} done` | `{"a": 1}` |
| Llave sin cerrar | `{"a": 1` | `{"a": 1}` |
| Lista sin cerrar | `{"a": [1, 2` | `{"a": [1, 2]}` |
| String sin cerrar | `{"a": "hello}` | `{"a": "hello"}` |
| Placeholder `...` | `[1, 2, ...]` | `[1, 2]` |
| Salto literal en string | `{"a": "x<LF>y"}` | `{"a": "x\ny"}` |
| Combinado | ```` ```json\n{name: 'Bot', tags: ["ia",],}\n ```` | `{"name": "Bot", "tags": ["ia"]}` |

Ver detalle por etapa en `docs/PIPELINE.md` y referencia completa en `docs/API.md`.

## Integración con OpenAI

`examples/openai_integration_test.py` genera respuestas con la API de OpenAI y mide la tasa de reparación por modelo. Sirve como evidencia para solicitudes de créditos y como test de regresión.

```bash
export OPENAI_API_KEY=sk-...   # Windows: set OPENAI_API_KEY=sk-...
pip install openai
python examples/openai_integration_test.py --model gpt-4o-mini --n 50
# Reparadas: 48/50 (96.0%)
```

Quickstart sin claves:

```bash
python examples/quickstart.py
```

Ver `docs/OPENAI_PROGRAM.md` para justificación, coste estimado y evidencias.

## Desarrollo

```bash
pip install -e ".[test]"  # o: pip install pytest
pytest -q
```

Estructura:

```text
src/llm_json_repair/repair.py  # pipeline de 9 etapas (stdlib only)
tests/test_repair.py           # suite unitaria
examples/quickstart.py         # demo sin API keys
examples/openai_integration_test.py  # test suite masivo con OpenAI
docs/                          # documentación extendida
```

Convenciones y guía de contribución en `CONTRIBUTING.md`. Historial de cambios en `CHANGELOG.md`.

## Roadmap

- **v0.2:** streaming (reparación incremental), CLI (`llm-json-repair fix`), validación opcional con pydantic.
- **v0.3:** benchmarks multilingües y matriz de modelos ampliada, corpus de fallos reales.
- **v1.0:** API estable, garantía de no-regresión, política de versionado semántico.

Detalle en `ROADMAP.md`.

## Licencia

MIT. Ver `LICENSE`.

## FAQ

**1. ¿Sustituye a `json.loads`?**
No. Úsalo como fallback: intenta `json.loads` primero y `loads` de esta librería solo si falla. Así evitas transformaciones innecesarias en JSON válido.

**2. ¿Puede corromper JSON válido?**
El pipeline conserva el input válido intacto (test `test_valid_passthrough`). Las transformaciones solo actúan fuera de strings con comillas dobles.

**3. ¿Qué hace si la entrada es irreparable?**
Lanza `RepairError` (subclase de `ValueError`). Captúrala y aplica tu política: reintentar al modelo, pedir formato estricto o registrar el fallo.

**4. ¿Funciona sin internet ni API keys?**
Sí. Es 100 % local y sin dependencias. Solo `examples/openai_integration_test.py` requiere `OPENAI_API_KEY`.

**5. ¿Soporta JSONL, YAML o streaming?**
No en v0.1.0. Solo un objeto/array JSON por llamada. Streaming, CLI y validación pydantic están en el roadmap v0.2.
