Mejorado para que la API de Debug sea solo un wrap y no necesite actualizacion

This commit is contained in:
Miguel 2025-06-05 18:27:56 +02:00
parent 60401010a6
commit 1bf1e47c04
5 changed files with 503 additions and 570 deletions

462
LLM_DEBUG_API_GUIDE.md Normal file
View File

@ -0,0 +1,462 @@
# Simple Debug API - Guía para LLM
## Propósito
Esta API permite debuggear la **Calculadora MAV CAS** sin modificar código fuente ni crear scripts adicionales.
**Principio**: **texto → texto** tal como se muestra en la aplicación.
**NO duplica lógica**, solo encapsula llamadas directas al motor existente.
Como LLM, puedes usar esta herramienta para:
- **Diagnosticar problemas** en el motor de evaluación
- **Verificar comportamiento** de tipos personalizados (FourBytes, IntBase, IP4Mask, etc.)
- **Inspeccionar el estado interno** del motor (variables, contexto, tipos registrados)
- **Testing de regresión** para verificar que cambios no rompan funcionalidad existente
## Flujo de Trabajo Recomendado
### 1. Crear archivo JSON con queries
### 2. Ejecutar: `python simple_debug.py archivo.json`
### 3. Analizar resultados en el archivo `*_results.json`
---
## Tipos de Query
### Query tipo `input`
Evalúa expresiones como si el usuario las escribiera en la calculadora.
**Resultado**: texto tal como se muestra en la aplicación.
```json
{"index": 0, "type": "input", "content": "10.1.1.1 + 1"}
```
### Query tipo `exec`
Ejecuta código Python para inspeccionar el estado interno del motor.
**Resultado**: valor directo de la evaluación Python.
```json
{"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"}
```
---
## Plantilla Base
```json
{
"queries": [
{"index": 0, "type": "input", "content": "EXPRESION_A_EVALUAR"},
{"index": 1, "type": "exec", "content": "CODIGO_PYTHON_INSPECCION"}
]
}
```
---
## Casos de Uso Comunes
### Debugging de Tipos Personalizados
**Problema**: Verificar si FourBytes maneja correctamente operaciones IP.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "192.168.1.1 + 1"},
{"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"},
{"index": 2, "type": "exec", "content": "engine.last_result.original"},
{"index": 3, "type": "exec", "content": "engine.last_result._numeric_value"},
{"index": 4, "type": "input", "content": "10.0.0.0/8 + 256"},
{"index": 5, "type": "exec", "content": "engine.last_result.has_symbols"}
]
}
```
### Debugging de Tokenización
**Problema**: La expresión `192.168.1.x + 16#FF` no se tokeniza correctamente.
```json
{
"queries": [
{"index": 0, "type": "exec", "content": "engine.parser.process_expression('192.168.1.x + 16#FF')"},
{"index": 1, "type": "input", "content": "192.168.1.x + 16#FF"},
{"index": 2, "type": "exec", "content": "engine.parser.get_tokenization_info()"},
{"index": 3, "type": "exec", "content": "len(engine.parser.tokenizer.tokenization_rules)"}
]
}
```
### Debugging de Errores
**Problema**: IP4Mask rechaza máscaras que deberían ser válidas.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "mask = 255.240.0.0"},
{"index": 1, "type": "input", "content": "IP4Mask(mask)"},
{"index": 2, "type": "exec", "content": "engine.last_result"},
{"index": 3, "type": "input", "content": "IP4Mask(255.240.0.3)"},
{"index": 4, "type": "exec", "content": "engine.last_result"}
]
}
```
### Debugging de Estado del Motor
**Problema**: Las variables no se están guardando correctamente.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "x = 5"},
{"index": 1, "type": "input", "content": "y = x + 10"},
{"index": 2, "type": "exec", "content": "engine.symbol_table"},
{"index": 3, "type": "exec", "content": "len(engine.symbol_table)"},
{"index": 4, "type": "exec", "content": "list(engine.symbol_table.keys())"},
{"index": 5, "type": "input", "content": "solve(z**2 + x, z)"},
{"index": 6, "type": "exec", "content": "len(engine.equations)"}
]
}
```
---
## Interpretación de Resultados
### Estructura del Resultado
```json
{
"execution_info": {
"timestamp": "2025-06-05T18:25:22.256442Z",
"total_queries": 5,
"successful": 4,
"failed": 1
},
"results": [...]
}
```
### Resultado Individual (Query `input`)
```json
{
"index": 0,
"input": "10.1.1.1 + 1",
"output": "10.1.1.2", // Texto tal como se muestra en la app
"result_type": "FourBytes", // Tipo del objeto resultado
"success": true,
"error": null
}
```
### Resultado Individual (Query `exec`)
```json
{
"index": 1,
"input": "type(engine.last_result).__name__",
"output": "FourBytes", // String del resultado
"result_type": "str", // Tipo del resultado de la evaluación
"success": true,
"error": null,
"exec_result": "FourBytes" // Valor directo (serializado si es necesario)
}
```
### Resultado con Error
```json
{
"index": 2,
"input": "IP4Mask(255.240.0.3)",
"output": "None",
"result_type": "NoneType",
"success": false,
"error": "❌ Máscara inválida: 255.240.0.3..."
}
```
---
## Funciones de Inspección Útiles
### Estado del Motor
```python
"engine.symbol_table" # Variables actuales
"engine.last_result" # Último resultado
"engine.symbolic_mode" # ¿Modo simbólico activo?
"len(engine.equations)" # Cantidad de ecuaciones en el sistema
"engine.debug" # ¿Debug habilitado?
"list(engine.base_context.keys())[:10]" # Funciones disponibles (muestra 10)
```
### Información de Tipos
```python
"engine.get_available_types()" # Info completa de tipos registrados
"list(engine.registered_types_info['registered_classes'].keys())" # Lista de tipos
"engine.registered_types_info['class_count']" # Cantidad de tipos registrados
"type(engine.last_result).__name__" # Tipo del último resultado
"hasattr(engine.last_result, 'has_symbols')" # ¿El resultado tiene símbolos?
```
### Tokenización y Parsing
```python
"engine.parser.get_tokenization_info()" # Info completa de tokenización
"engine.parser.process_expression('test')" # Procesar expresión específica
"len(engine.parser.tokenizer.tokenization_rules)" # Cantidad de reglas
"engine._classify_line('x = 5')" # Clasificar tipo de línea
"engine._extract_variable_names('x + y')" # Extraer nombres de variables
```
### Análisis de Objetos Específicos
```python
# Para FourBytes
"engine.last_result.original" # String original
"engine.last_result._numeric_value" # Valor numérico interno
"engine.last_result.has_symbols" # ¿Tiene símbolos?
# Para IntBase
"engine.last_result.base" # Base numérica (10, 16, 8, 2)
"engine.last_result.value_str" # String del valor
"engine.last_result._symbols" # Símbolos detectados
# Para IP4Mask
"engine.last_result.get_prefix_int()" # Prefijo como entero
"engine.last_result.is_valid()" # ¿Es máscara válida?
```
---
## Patrones de Debugging
### 1. Debugging de Regresión
**Cuándo usar**: Verificar que cambios no rompan funcionalidad existente.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "10.1.1.1 + 1"},
{"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"},
{"index": 2, "type": "input", "content": "16#FF + 10"},
{"index": 3, "type": "exec", "content": "engine.last_result.base"},
{"index": 4, "type": "input", "content": "IP4Mask(255.255.0.0)"},
{"index": 5, "type": "exec", "content": "engine.last_result.get_prefix_int()"}
]
}
```
### 2. Debugging de Nuevas Funcionalidades
**Cuándo usar**: Verificar que nueva funcionalidad trabaja correctamente.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "NUEVA_FUNCIONALIDAD_AQUI"},
{"index": 1, "type": "exec", "content": "type(engine.last_result)"},
{"index": 2, "type": "exec", "content": "dir(engine.last_result)"},
{"index": 3, "type": "exec", "content": "str(engine.last_result)"},
{"index": 4, "type": "exec", "content": "engine.symbol_table"}
]
}
```
### 3. Debugging de Performance
**Cuándo usar**: Identificar operaciones lentas o problemáticas.
```json
{
"queries": [
{"index": 0, "type": "exec", "content": "import time; start = time.time()"},
{"index": 1, "type": "input", "content": "OPERACION_LENTA"},
{"index": 2, "type": "exec", "content": "time.time() - start"},
{"index": 3, "type": "exec", "content": "len(engine.symbol_table)"}
]
}
```
### 4. Debugging de Comportamiento
**Cuándo usar**: Verificar comportamiento específico de tipos o funciones.
```json
{
"queries": [
{"index": 0, "type": "input", "content": "EXPRESION"},
{"index": 1, "type": "exec", "content": "str(engine.last_result)"},
{"index": 2, "type": "exec", "content": "repr(engine.last_result)"},
{"index": 3, "type": "exec", "content": "type(engine.last_result).__name__"}
]
}
```
---
## Comandos de Ejecución
```bash
# Ejecución básica
python simple_debug.py mi_debug.json
# Con archivo de salida específico
python simple_debug.py mi_debug.json --output resultados.json
# Modo verboso (para ver progreso)
python simple_debug.py mi_debug.json --verbose
```
---
## Templates Existentes para Reutilizar
### Casos Básicos
**Archivo**: `debug_templates/basic_test.json`
- Operaciones con FourBytes e IntBase
- Variables y SymPy básico
### Testing de Errores
**Archivo**: `debug_templates/error_debug.json`
- Máscaras inválidas
- IPs fuera de rango
- Expresiones malformadas
### Información de Contexto
**Archivo**: `debug_templates/context_debug.json`
- Estado completo del motor
- Tipos registrados
- Sistema de ecuaciones
### Tokenización
**Archivo**: `debug_templates/tokenization_test.json`
- Debug del sistema de parsing
- Reglas de tokenización
---
## Flujo de Resolución de Problemas
### 1. Identificar el Problema
- ¿Es un error de evaluación?
- ¿Es un problema de tokenización?
- ¿Es un problema de estado del motor?
### 2. Crear Query de Diagnóstico
- Usar query `input` para reproducir el problema
- Usar query `exec` para inspeccionar el estado
### 3. Analizar Resultados
- Verificar `success: true/false`
- Examinar `error` si hay fallo
- Comparar `output` (texto de la app) con `exec_result` (valor interno)
### 4. Iterar
- Crear nuevas queries basadas en hallazgos
- Profundizar en áreas problemáticas
- Verificar soluciones con queries adicionales
---
## Ejemplo Completo de Debugging
**Problema**: "La operación `192.168.1.x + 1` no funciona correctamente"
### Paso 1: Crear archivo de debug
```json
{
"queries": [
{"index": 0, "type": "input", "content": "192.168.1.x + 1"},
{"index": 1, "type": "exec", "content": "type(engine.last_result).__name__"},
{"index": 2, "type": "exec", "content": "engine.last_result.has_symbols"},
{"index": 3, "type": "exec", "content": "engine.parser.process_expression('192.168.1.x + 1')"},
{"index": 4, "type": "input", "content": "ip = 192.168.1.x"},
{"index": 5, "type": "exec", "content": "type(engine.symbol_table['ip'])"},
{"index": 6, "type": "input", "content": "ip.substitute(x=5)"}
]
}
```
### Paso 2: Ejecutar
```bash
python simple_debug.py debug_problema.json
```
### Paso 3: Analizar resultados
- Verificar si `192.168.1.x + 1` se evalúa correctamente (campo `output`)
- Comprobar el tipo resultante con `exec`
- Verificar si tiene símbolos
- Examinar cómo se tokeniza la expresión
- Probar operaciones relacionadas
Este flujo te permite identificar exactamente dónde está el problema y verificar la solución.
---
## Consejos para LLMs
1. **Siempre usa índices secuenciales** en las queries para facilitar la lectura
2. **Combina queries `input` y `exec`** para obtener contexto completo
3. **El campo `output` es texto tal como se muestra en la aplicación**
4. **El campo `exec_result` es el valor directo de la evaluación Python**
5. **Usa los templates existentes** como punto de partida
6. **Examina `success` y `error`** antes de analizar resultados
7. **Crea queries incrementales** que construyan sobre resultados anteriores
8. **NO intentes duplicar lógica de la aplicación** - usa solo llamadas directas
---
## Referencia Rápida
### Query Básica
```json
{"index": N, "type": "input|exec", "content": "CONTENIDO"}
```
### Comandos Esenciales
```bash
python simple_debug.py archivo.json # Ejecutar debug
python simple_debug.py archivo.json --verbose # Con detalles
python simple_debug.py archivo.json -o resultado.json # Salida específica
```
### Inspección Básica del Motor
```python
"engine.last_result" # Último resultado
"type(engine.last_result)" # Tipo del resultado
"engine.symbol_table" # Variables actuales
"engine.get_available_types()" # Tipos registrados
"engine.parser.get_tokenization_info()" # Info de parsing
```
### Análisis de Resultado
```python
"success": true/false # ¿Éxito?
"error": "mensaje" # Error si falla
"output": "resultado" # Texto tal como se muestra en la app
"exec_result": valor # Valor directo (exec queries)
```
### Templates Disponibles
- `debug_templates/basic_test.json` - Pruebas básicas
- `debug_templates/error_debug.json` - Testing de errores
- `debug_templates/context_debug.json` - Estado del motor
- `debug_templates/tokenization_test.json` - Debug de parsing
### Workflow Típico
1. **Identificar problema** → Crear query `input` para reproducir
2. **Inspeccionar estado** → Añadir queries `exec` para diagnosticar
3. **Analizar resultados** → Examinar `success`, `error`, `output`
4. **Iterar** → Crear nuevas queries basadas en hallazgos
**Principio clave**: texto → texto. La API no interpreta ni procesa, solo encapsula llamadas directas al motor existente.

View File

@ -1,6 +1,6 @@
{
"execution_info": {
"timestamp": "2025-06-05T18:10:43.323812Z",
"timestamp": "2025-06-05T18:25:22.256442Z",
"total_queries": 8,
"successful": 8,
"failed": 0,
@ -13,73 +13,7 @@
"output": "10.1.1.2",
"result_type": "FourBytes",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"custom_type",
"10.1.1.2"
],
[
"class_hint",
"[FourBytes]"
]
],
"formatted_text": "[custom_type]10.1.1.2[class_hint][FourBytes]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[FourBytes]"
"error": null
},
{
"index": 1,
@ -96,73 +30,7 @@
"output": "16#FF",
"result_type": "IntBase",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"custom_type",
"16#FF"
],
[
"class_hint",
"[IntBase]"
]
],
"formatted_text": "[custom_type]16#FF[class_hint][IntBase]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[IntBase]"
"error": null
},
{
"index": 3,
@ -179,69 +47,7 @@
"output": "255.255.0.0",
"result_type": "FourBytes",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"info",
"mask = 255.255.0.0"
]
],
"formatted_text": "[info]mask = 255.255.0.0",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[FourBytes]"
"error": null
},
{
"index": 5,
@ -250,7 +56,9 @@
"result_type": "dict",
"success": true,
"error": null,
"exec_result": "{'mask': FourBytes('255.255.0.0')}"
"exec_result": {
"mask": "255.255.0.0"
}
},
{
"index": 6,
@ -258,73 +66,7 @@
"output": "[-I, I]",
"result_type": "list",
"success": true,
"error": null,
"output_raw": {
"parts": [
[
"result",
"[-I, I]"
],
[
"class_hint",
"[List]"
]
],
"formatted_text": "[result][-I, I][class_hint][List]",
"tag_info": {
"error": {
"fg": "#ff6b6b",
"font": "bold"
},
"result": {
"fg": "#abdbe3"
},
"symbolic": {
"fg": "#82aaff"
},
"numeric": {
"fg": "#c3e88d"
},
"equation": {
"fg": "#c792ea"
},
"info": {
"fg": "#ffcb6b"
},
"comment": {
"fg": "#546e7a"
},
"class_hint": {
"fg": "#888888"
},
"type_hint": {
"fg": "#6a6a6a"
},
"custom_type": {
"fg": "#f9a825"
},
"hex": {
"fg": "#f9a825"
},
"bin": {
"fg": "#4fc3f7"
},
"ip": {
"fg": "#fff176"
},
"date": {
"fg": "#ff8a80"
},
"chr_type": {
"fg": "#80cbc4"
},
"helper": {
"fg": "#ffd700",
"font": "italic"
}
}
},
"display_class": "[list]"
"error": null
},
{
"index": 7,

View File

@ -52,10 +52,10 @@ python simple_debug.py debug_templates/basic_test.json --verbose
- Sistema de ecuaciones
### `display_format_test.json`
**Propósito**: Demostración de formatos de display y output_raw
**Propósito**: Demostración de diferentes tipos de resultados
- Diferentes tipos de resultados (IP, hex, símbolos, matrices)
- Comentarios y errores
- Comparación entre `output` y `output_raw`
- Verificación del comportamiento de la aplicación
## Formato de Template
@ -142,47 +142,36 @@ Cada template es un archivo JSON con esta estructura:
"output": "10.1.1.2",
"result_type": "FourBytes",
"success": true,
"error": null
}
```
### Resultado Individual para Query `exec`
```json
{
"index": 1,
"input": "type(engine.last_result).__name__",
"output": "FourBytes",
"result_type": "str",
"success": true,
"error": null,
"display_class": "[FourBytes]",
"output_raw": {
"parts": [
["custom_type", "10.1.1.2"],
["class_hint", "[FourBytes]"]
],
"formatted_text": "[custom_type]10.1.1.2[class_hint][FourBytes]",
"tag_info": {
"custom_type": {"fg": "#f9a825"},
"class_hint": {"fg": "#888888"}
}
}
"exec_result": "FourBytes"
}
```
### Campos del Resultado
- **`output`**: Resultado básico (antes del post-procesamiento)
- **`output_raw`**: Resultado exacto como se muestra en la aplicación
- **`parts`**: Lista de tuplas `[tag, contenido]` con información de color/formato
- **`formatted_text`**: Texto formateado como se vería en la aplicación
- **`tag_info`**: Información de colores y estilos para cada tag
- **`output`**: Texto tal como se muestra en la aplicación (queries `input`)
- **`exec_result`**: Valor directo de la evaluación Python (queries `exec`)
- **`result_type`**: Tipo del objeto resultado
- **`display_class`**: Nombre de clase para mostrar
### Tags de Color Disponibles
- **`error`**: `#ff6b6b` (rojo) - Errores
- **`result`**: `#abdbe3` (azul claro) - Resultados generales
- **`symbolic`**: `#82aaff` (azul) - Expresiones simbólicas
- **`numeric`**: `#c3e88d` (verde) - Aproximaciones numéricas
- **`custom_type`**: `#f9a825` (naranja) - Tipos personalizados
- **`ip`**: `#fff176` (amarillo) - Direcciones IP
- **`hex`**: `#f9a825` (naranja) - Números hexadecimales
- **`class_hint`**: `#888888` (gris) - Pistas de clase
- **`success`**: `true` si la evaluación fue exitosa
- **`error`**: Mensaje de error si `success` es `false`
## Tips
- Usa `index` secuencial para facilitar la lectura de resultados
- Combina queries `input` y `exec` para verificar comportamiento y estado
- El archivo de resultados contiene información detallada de éxito/error
- **Usa `output_raw` para entender exactamente cómo se mostraría el resultado en la aplicación**
- **Usa `output` para ver exactamente el texto que muestra la aplicación**
- Puedes copiar y modificar templates existentes para casos específicos

View File

@ -1,6 +1,6 @@
{
"execution_info": {
"timestamp": "2025-06-05T18:01:38.085095Z",
"timestamp": "2025-06-05T18:27:08.607608Z",
"total_queries": 11,
"successful": 9,
"failed": 2,
@ -13,8 +13,7 @@
"output": "255.240.0.3",
"result_type": "FourBytes",
"success": true,
"error": null,
"display_class": "[FourBytes]"
"error": null
},
{
"index": 1,
@ -22,8 +21,7 @@
"output": "None",
"result_type": "NoneType",
"success": false,
"error": "\n❌ Máscara inválida: 255.240.0.3 (0xFFF00003)\n\n🔍 Análisis binario: 11111111.11110000.00000000.00000011\n Los bits deben ser contiguos: todos los 1s seguidos de todos los 0s\n\n✅ Ejemplos de máscaras válidas:\n /24 → 255.255.255.0 (11111111.11111111.11111111.00000000)\n /20 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n /16 → 255.255.0.0 (11111111.11111111.00000000.00000000)\n /12 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n\n💡 ¿Quizás querías usar 255.240.0.0 en lugar de 255.240.0.3?\n",
"display_class": "[NoneType]"
"error": "\n❌ Máscara inválida: 255.240.0.3 (0xFFF00003)\n\n🔍 Análisis binario: 11111111.11110000.00000000.00000011\n Los bits deben ser contiguos: todos los 1s seguidos de todos los 0s\n\n✅ Ejemplos de máscaras válidas:\n /24 → 255.255.255.0 (11111111.11111111.11111111.00000000)\n /20 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n /16 → 255.255.0.0 (11111111.11111111.00000000.00000000)\n /12 → 255.240.0.0 (11111111.11110000.00000000.00000000)\n\n💡 ¿Quizás querías usar 255.240.0.0 en lugar de 255.240.0.3?\n"
},
{
"index": 2,
@ -40,8 +38,7 @@
"output": "None",
"result_type": "NoneType",
"success": false,
"error": "Error en asignación: Elemento inválido: '300'",
"display_class": "[NoneType]"
"error": "Error en asignación: Elemento inválido: '300'"
},
{
"index": 4,
@ -58,8 +55,7 @@
"output": "16",
"result_type": "Integer",
"success": true,
"error": null,
"display_class": "[Integer]"
"error": null
},
{
"index": 6,
@ -76,8 +72,7 @@
"output": "zoo",
"result_type": "ComplexInfinity",
"success": true,
"error": null,
"display_class": "[ComplexInfinity]"
"error": null
},
{
"index": 8,
@ -94,8 +89,7 @@
"output": "undefined_var + 5",
"result_type": "Add",
"success": true,
"error": null,
"display_class": "[Add]"
"error": null
},
{
"index": 10,

View File

@ -6,6 +6,9 @@ API CLI simple que usa el motor de evaluación existente para generar debug trac
sin modificar el código base. Permite debuggear "como si usaras la aplicación"
mediante archivos JSON.
Principio: texto texto tal como se muestra en la aplicación.
No duplica lógica, solo encapsula llamadas directas.
Uso:
python simple_debug.py debug_input.json
python simple_debug.py debug_input.json --output debug_results.json
@ -20,238 +23,6 @@ from pathlib import Path
# Importar el motor de evaluación existente
from main_evaluation import HybridEvaluationEngine
import sympy
def _process_evaluation_result_for_debug(result, engine):
"""
Procesa el resultado de evaluación tal como lo haría la aplicación
Retorna información de formato, colores y contenido exacto
"""
output_parts = []
if result.is_error:
# Intentar obtener ayuda como lo hace la app
ayuda = _obtener_ayuda_simulada(result.original_line, engine)
if ayuda:
ayuda_linea = ayuda.replace("\n", " ").replace("\r", " ")
if len(ayuda_linea) > 120:
ayuda_linea = ayuda_linea[:117] + "..."
output_parts.append(("helper", ayuda_linea))
else:
output_parts.append(("error", f"Error: {result.error}"))
elif result.result_type == "comment":
output_parts.append(("comment", result.original_line))
elif result.result_type == "equation_added":
output_parts.append(("equation", result.symbolic_result))
elif result.result_type == "assignment":
output_parts.append(("info", result.symbolic_result))
# Mostrar evaluación numérica para asignaciones si existe
if result.numeric_result is not None and result.numeric_result != result.result:
output_parts.append(("numeric", f"{result.numeric_result}"))
else:
# Resultado normal
if result.result is not None:
# Determinar tag basado en tipo (simulando lógica dinámica)
tag = _get_result_tag_dynamic_debug(result.result, engine)
# Verificar si es resultado interactivo (simulado)
if _is_interactive_result(result.result):
interactive_tag, display_text = _create_interactive_tag_debug(result.result)
if interactive_tag:
output_parts.append((interactive_tag, display_text))
else:
output_parts.append((tag, str(result.result)))
else:
output_parts.append((tag, str(result.result)))
# Añadir pista de clase para el resultado principal
class_display_name = _get_class_display_name_dynamic_debug(result.result, engine)
if class_display_name:
output_parts.append(("class_hint", f"[{class_display_name}]"))
# Mostrar evaluación numérica si existe
if result.numeric_result is not None and result.numeric_result != result.result:
output_parts.append(("numeric", f"{result.numeric_result}"))
# Mostrar información adicional
if result.info:
output_parts.append(("info", f"({result.info})"))
# Convertir partes a string formateado como la app
formatted_output = _format_output_parts_debug(output_parts)
return {
'parts': output_parts,
'formatted_text': formatted_output,
'tag_info': _get_tag_color_info()
}
def _obtener_ayuda_simulada(input_str, engine):
"""Simula la obtención de ayuda como lo hace la app"""
try:
# Intentar usar los helpers del engine si están disponibles
if hasattr(engine, 'HELPERS'):
for helper in engine.HELPERS:
try:
ayuda = helper(input_str)
if ayuda:
return ayuda
except:
continue
except:
pass
return None
def _get_result_tag_dynamic_debug(result, engine):
"""Simula _get_result_tag_dynamic de la app"""
try:
registered_classes = engine.get_available_types().get('registered_classes', {})
# Verificar si es una instancia de alguna clase registrada
for name, cls in registered_classes.items():
if isinstance(result, cls):
name_lower = name.lower()
if name_lower == "hex":
return "hex"
elif name_lower == "bin":
return "bin"
elif name_lower in ["ip4", "ip"]:
return "ip"
elif name_lower == "chr":
return "chr_type"
elif name_lower == "date":
return "date"
else:
return "custom_type"
except Exception:
pass
# Fallback a tags existentes para tipos no registrados
if isinstance(result, sympy.Basic):
return "symbolic"
else:
return "result"
def _get_class_display_name_dynamic_debug(obj, engine):
"""Simula _get_class_display_name_dynamic de la app"""
try:
# Verificar si es una clase registrada dinámicamente
registered_classes = engine.get_available_types().get('registered_classes', {})
for name, cls in registered_classes.items():
if isinstance(obj, cls):
return name
except Exception:
pass
# Fallback a lógica existente para tipos nativos
if isinstance(obj, sympy.logic.boolalg.BooleanAtom):
return "Boolean"
elif isinstance(obj, sympy.Basic):
if hasattr(obj, 'is_number') and obj.is_number:
if hasattr(obj, 'is_Integer') and obj.is_Integer:
return "Integer"
elif hasattr(obj, 'is_Rational') and obj.is_Rational and not obj.is_Integer:
return "Rational"
elif hasattr(obj, 'is_Float') and obj.is_Float:
return "Float"
else:
return "SympyNumber"
else:
return "Sympy"
elif isinstance(obj, bool):
return "Boolean"
elif isinstance(obj, (int, float, str, list, dict, tuple, type(None))):
class_display_name = type(obj).__name__.capitalize()
if class_display_name == "Nonetype":
class_display_name = "None"
return class_display_name
return ""
def _is_interactive_result(result):
"""Simula la lógica is_interactive de EvaluationResult"""
try:
# Intentar importar PlotResult si existe
from tl_popup import PlotResult
if isinstance(result, PlotResult):
return True
except ImportError:
pass
return isinstance(result, sympy.Matrix) or \
(isinstance(result, list) and len(result) > 3)
def _create_interactive_tag_debug(result):
"""Simula la creación de tags interactivos"""
try:
from tl_popup import PlotResult
if isinstance(result, PlotResult):
return f"plot_{id(result)}", f"📊 Ver {result.plot_type.title()}"
except ImportError:
pass
if isinstance(result, sympy.Matrix):
rows, cols = result.shape
return f"matrix_{id(result)}", f"📋 Ver Matriz {rows}×{cols}"
elif isinstance(result, list) and len(result) > 5:
return f"list_{id(result)}", f"📋 Ver Lista ({len(result)} elementos)"
elif isinstance(result, dict) and len(result) > 3:
return f"dict_{id(result)}", f"🔍 Ver Diccionario ({len(result)} entradas)"
return None, None
def _format_output_parts_debug(output_parts):
"""Simula cómo _display_output formatea las partes para mostrar"""
formatted_lines = []
for part_idx, (tag, content) in enumerate(output_parts):
if not content:
continue
prefix = ""
if part_idx > 0:
prev_tag, prev_content = output_parts[part_idx-1] if part_idx > 0 else (None, None)
if tag not in ["class_hint", "numeric", "info"] and prev_content:
prefix = " ; "
elif tag in ["numeric", "info"] and prev_content:
prefix = " "
formatted_lines.append(f"{prefix}[{tag}]{content}")
return "".join(formatted_lines)
def _get_tag_color_info():
"""Información sobre los colores de los tags como en setup_output_tags"""
return {
"error": {"fg": "#ff6b6b", "font": "bold"},
"result": {"fg": "#abdbe3"},
"symbolic": {"fg": "#82aaff"},
"numeric": {"fg": "#c3e88d"},
"equation": {"fg": "#c792ea"},
"info": {"fg": "#ffcb6b"},
"comment": {"fg": "#546e7a"},
"class_hint": {"fg": "#888888"},
"type_hint": {"fg": "#6a6a6a"},
"custom_type": {"fg": "#f9a825"},
"hex": {"fg": "#f9a825"},
"bin": {"fg": "#4fc3f7"},
"ip": {"fg": "#fff176"},
"date": {"fg": "#ff8a80"},
"chr_type": {"fg": "#80cbc4"},
"helper": {"fg": "#ffd700", "font": "italic"}
}
def run_debug(input_file: str, output_file: str = None, verbose: bool = False):
@ -298,6 +69,7 @@ def run_debug(input_file: str, output_file: str = None, verbose: bool = False):
# Query de tipo input: evaluar expresión como si fuera entrada del usuario
result = engine.evaluate_line(query['content'])
# Capturar resultado directo sin procesamiento
output = {
'index': query['index'],
'input': query['content'],
@ -307,17 +79,6 @@ def run_debug(input_file: str, output_file: str = None, verbose: bool = False):
'error': result.error if hasattr(result, 'is_error') and result.is_error else None
}
# Generar output_raw: resultado exacto como se muestra en la aplicación
try:
output_raw_data = _process_evaluation_result_for_debug(result, engine)
output['output_raw'] = output_raw_data
except Exception as e:
output['output_raw'] = f"Error generando output_raw: {e}"
# Añadir información adicional si está disponible
if hasattr(result, 'result') and hasattr(result.result, '__class__'):
output['display_class'] = f"[{result.result.__class__.__name__}]"
if not (hasattr(result, 'is_error') and result.is_error):
successful += 1
else:
@ -377,22 +138,7 @@ def run_debug(input_file: str, output_file: str = None, verbose: bool = False):
# Guardar resultados
try:
# Usar un encoder personalizado para objetos no serializables
def json_serializer(obj):
"""Serializar objetos no estándar a string"""
try:
# Intentar conversión normal primero
json.dumps(obj)
return obj
except TypeError:
# Si no es serializable, convertir a string
return str(obj)
# Aplicar serialización personalizada a exec_result
for result in final_output['results']:
if 'exec_result' in result:
result['exec_result'] = json_serializer(result['exec_result'])
# Serialización simple con fallback a string
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(final_output, f, indent=2, ensure_ascii=False, default=str)