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": { "execution_info": {
"timestamp": "2025-06-05T18:10:43.323812Z", "timestamp": "2025-06-05T18:25:22.256442Z",
"total_queries": 8, "total_queries": 8,
"successful": 8, "successful": 8,
"failed": 0, "failed": 0,
@ -13,73 +13,7 @@
"output": "10.1.1.2", "output": "10.1.1.2",
"result_type": "FourBytes", "result_type": "FourBytes",
"success": true, "success": true,
"error": null, "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]"
}, },
{ {
"index": 1, "index": 1,
@ -96,73 +30,7 @@
"output": "16#FF", "output": "16#FF",
"result_type": "IntBase", "result_type": "IntBase",
"success": true, "success": true,
"error": null, "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]"
}, },
{ {
"index": 3, "index": 3,
@ -179,69 +47,7 @@
"output": "255.255.0.0", "output": "255.255.0.0",
"result_type": "FourBytes", "result_type": "FourBytes",
"success": true, "success": true,
"error": null, "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]"
}, },
{ {
"index": 5, "index": 5,
@ -250,7 +56,9 @@
"result_type": "dict", "result_type": "dict",
"success": true, "success": true,
"error": null, "error": null,
"exec_result": "{'mask': FourBytes('255.255.0.0')}" "exec_result": {
"mask": "255.255.0.0"
}
}, },
{ {
"index": 6, "index": 6,
@ -258,73 +66,7 @@
"output": "[-I, I]", "output": "[-I, I]",
"result_type": "list", "result_type": "list",
"success": true, "success": true,
"error": null, "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]"
}, },
{ {
"index": 7, "index": 7,

View File

@ -52,10 +52,10 @@ python simple_debug.py debug_templates/basic_test.json --verbose
- Sistema de ecuaciones - Sistema de ecuaciones
### `display_format_test.json` ### `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) - Diferentes tipos de resultados (IP, hex, símbolos, matrices)
- Comentarios y errores - Comentarios y errores
- Comparación entre `output` y `output_raw` - Verificación del comportamiento de la aplicación
## Formato de Template ## Formato de Template
@ -142,47 +142,36 @@ Cada template es un archivo JSON con esta estructura:
"output": "10.1.1.2", "output": "10.1.1.2",
"result_type": "FourBytes", "result_type": "FourBytes",
"success": true, "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, "error": null,
"display_class": "[FourBytes]", "exec_result": "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"}
}
}
} }
``` ```
### Campos del Resultado ### Campos del Resultado
- **`output`**: Resultado básico (antes del post-procesamiento) - **`output`**: Texto tal como se muestra en la aplicación (queries `input`)
- **`output_raw`**: Resultado exacto como se muestra en la aplicación - **`exec_result`**: Valor directo de la evaluación Python (queries `exec`)
- **`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
- **`result_type`**: Tipo del objeto resultado - **`result_type`**: Tipo del objeto resultado
- **`display_class`**: Nombre de clase para mostrar - **`success`**: `true` si la evaluación fue exitosa
- **`error`**: Mensaje de error si `success` es `false`
### 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
## Tips ## Tips
- Usa `index` secuencial para facilitar la lectura de resultados - Usa `index` secuencial para facilitar la lectura de resultados
- Combina queries `input` y `exec` para verificar comportamiento y estado - Combina queries `input` y `exec` para verificar comportamiento y estado
- El archivo de resultados contiene información detallada de éxito/error - 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 - Puedes copiar y modificar templates existentes para casos específicos

View File

@ -1,6 +1,6 @@
{ {
"execution_info": { "execution_info": {
"timestamp": "2025-06-05T18:01:38.085095Z", "timestamp": "2025-06-05T18:27:08.607608Z",
"total_queries": 11, "total_queries": 11,
"successful": 9, "successful": 9,
"failed": 2, "failed": 2,
@ -13,8 +13,7 @@
"output": "255.240.0.3", "output": "255.240.0.3",
"result_type": "FourBytes", "result_type": "FourBytes",
"success": true, "success": true,
"error": null, "error": null
"display_class": "[FourBytes]"
}, },
{ {
"index": 1, "index": 1,
@ -22,8 +21,7 @@
"output": "None", "output": "None",
"result_type": "NoneType", "result_type": "NoneType",
"success": false, "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", "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]"
}, },
{ {
"index": 2, "index": 2,
@ -40,8 +38,7 @@
"output": "None", "output": "None",
"result_type": "NoneType", "result_type": "NoneType",
"success": false, "success": false,
"error": "Error en asignación: Elemento inválido: '300'", "error": "Error en asignación: Elemento inválido: '300'"
"display_class": "[NoneType]"
}, },
{ {
"index": 4, "index": 4,
@ -58,8 +55,7 @@
"output": "16", "output": "16",
"result_type": "Integer", "result_type": "Integer",
"success": true, "success": true,
"error": null, "error": null
"display_class": "[Integer]"
}, },
{ {
"index": 6, "index": 6,
@ -76,8 +72,7 @@
"output": "zoo", "output": "zoo",
"result_type": "ComplexInfinity", "result_type": "ComplexInfinity",
"success": true, "success": true,
"error": null, "error": null
"display_class": "[ComplexInfinity]"
}, },
{ {
"index": 8, "index": 8,
@ -94,8 +89,7 @@
"output": "undefined_var + 5", "output": "undefined_var + 5",
"result_type": "Add", "result_type": "Add",
"success": true, "success": true,
"error": null, "error": null
"display_class": "[Add]"
}, },
{ {
"index": 10, "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" sin modificar el código base. Permite debuggear "como si usaras la aplicación"
mediante archivos JSON. mediante archivos JSON.
Principio: texto texto tal como se muestra en la aplicación.
No duplica lógica, solo encapsula llamadas directas.
Uso: Uso:
python simple_debug.py debug_input.json python simple_debug.py debug_input.json
python simple_debug.py debug_input.json --output debug_results.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 # Importar el motor de evaluación existente
from main_evaluation import HybridEvaluationEngine 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): 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 # Query de tipo input: evaluar expresión como si fuera entrada del usuario
result = engine.evaluate_line(query['content']) result = engine.evaluate_line(query['content'])
# Capturar resultado directo sin procesamiento
output = { output = {
'index': query['index'], 'index': query['index'],
'input': query['content'], '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 '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): if not (hasattr(result, 'is_error') and result.is_error):
successful += 1 successful += 1
else: else:
@ -377,22 +138,7 @@ def run_debug(input_file: str, output_file: str = None, verbose: bool = False):
# Guardar resultados # Guardar resultados
try: try:
# Usar un encoder personalizado para objetos no serializables # Serialización simple con fallback a string
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'])
with open(output_file, 'w', encoding='utf-8') as f: with open(output_file, 'w', encoding='utf-8') as f:
json.dump(final_output, f, indent=2, ensure_ascii=False, default=str) json.dump(final_output, f, indent=2, ensure_ascii=False, default=str)