Se implementó un sistema de fallback utilizando Grok para explicar errores durante el procesamiento, mejorando la gestión de excepciones. Se ajustó el tiempo de espera del cliente HTTP a 60 segundos y se añadieron métodos para obtener líneas recientes y últimas del log. Además, se optimizó la extracción de contenido estructurado en el modo Claude Web Search.
This commit is contained in:
parent
a8aca9a82a
commit
25810b7e6a
118
App.xaml.cs
118
App.xaml.cs
|
@ -183,7 +183,27 @@ Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Italiano || Opciones.Ins
|
|||
catch (Exception ex)
|
||||
{
|
||||
GTP.Log.Log($"Error en el procesamiento final: {ex.Message}");
|
||||
ShowCustomNotification("Error", "Error al procesar el resultado");
|
||||
try
|
||||
{
|
||||
// Intentar usar el fallback con Grok para este error también
|
||||
string recentLogs = GTP.Log.GetRecentLogs("Iniciando corrección de texto");
|
||||
string resultadoFallback = await GTP.ExplicarErrorConGrok(GTP.TextoACorregir ?? "Texto no disponible", recentLogs);
|
||||
|
||||
if (!string.IsNullOrEmpty(resultadoFallback))
|
||||
{
|
||||
await ClipboardHelper.SetText(resultadoFallback);
|
||||
ShowCustomNotification("Error explicado", "Error analizado y copiado al portapapeles");
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowCustomNotification("Error", "Error al procesar el resultado");
|
||||
}
|
||||
}
|
||||
catch (Exception fallbackEx)
|
||||
{
|
||||
GTP.Log.Log($"Error en fallback para procesamiento final: {fallbackEx.Message}");
|
||||
ShowCustomNotification("Error", "Error al procesar el resultado");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -195,19 +215,43 @@ Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Italiano || Opciones.Ins
|
|||
catch (OperationCanceledException)
|
||||
{
|
||||
GTP.Log.Log("Operación cancelada por timeout");
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
await Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
ShowCustomNotification("Error", "La operación excedió el tiempo límite");
|
||||
Application.Current.Shutdown();
|
||||
try
|
||||
{
|
||||
// Intentar usar el fallback con Grok para explicar el timeout
|
||||
await TryErrorFallbackWithGrok(new TimeoutException("La operación excedió el tiempo límite"));
|
||||
}
|
||||
catch (Exception fallbackEx)
|
||||
{
|
||||
GTP.Log.Log($"Error en fallback para timeout: {fallbackEx.Message}");
|
||||
ShowCustomNotification("Error", "La operación excedió el tiempo límite");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GTP.Log.Log($"Error no controlado: {ex.Message}");
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
await Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
ShowCustomNotification("Error", "Se produjo un error inesperado");
|
||||
Application.Current.Shutdown();
|
||||
try
|
||||
{
|
||||
// Intentar usar el fallback con Grok para explicar el error
|
||||
await TryErrorFallbackWithGrok(ex);
|
||||
}
|
||||
catch (Exception fallbackEx)
|
||||
{
|
||||
GTP.Log.Log($"Error en fallback: {fallbackEx.Message}");
|
||||
ShowCustomNotification("Error", "Se produjo un error inesperado");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Application.Current.Shutdown();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -270,6 +314,66 @@ Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Italiano || Opciones.Ins
|
|||
GTP.Log.Log("Cronómetro detenido");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Intenta usar Grok como fallback para explicar errores cuando el procesamiento principal falla
|
||||
/// </summary>
|
||||
private async Task TryErrorFallbackWithGrok(Exception originalError)
|
||||
{
|
||||
try
|
||||
{
|
||||
GTP.Log.Log("Iniciando sistema de fallback para explicar error");
|
||||
|
||||
// Obtener los logs recientes de la última interacción
|
||||
string recentLogs = GTP.Log.GetRecentLogs("Iniciando corrección de texto");
|
||||
|
||||
if (string.IsNullOrEmpty(recentLogs))
|
||||
{
|
||||
GTP.Log.Log("No se pudieron obtener logs recientes para el fallback");
|
||||
ShowCustomNotification("Error", "Se produjo un error inesperado");
|
||||
return;
|
||||
}
|
||||
|
||||
GTP.Log.Log("Intentando explicar error con Grok usando logs recientes");
|
||||
|
||||
// Usar el método de fallback de Grok para explicar el error
|
||||
string resultadoFallback = await GTP.ExplicarErrorConGrok(GTP.TextoACorregir ?? "Texto no disponible", recentLogs);
|
||||
|
||||
if (!string.IsNullOrEmpty(resultadoFallback))
|
||||
{
|
||||
// Si Grok pudo explicar el error, copiar el resultado al portapapeles
|
||||
await ClipboardHelper.SetText(resultadoFallback);
|
||||
|
||||
DetenerCronometro();
|
||||
ShowCustomNotification("Error explicado", "Error analizado y copiado al portapapeles");
|
||||
|
||||
// Mostrar ventana de resultado si está configurado
|
||||
if (Opciones.Instance.FuncionesOpcionales == Opciones.funcionesOpcionales.MostrarPopUp)
|
||||
{
|
||||
var resultadoWindow = new VentanaResultado(resultadoFallback);
|
||||
resultadoWindow.Show();
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Restaurar ventana y pegar automáticamente
|
||||
await pasteLogic.RestoreAndSimulatePaste();
|
||||
}
|
||||
|
||||
GTP.Log.Log("Fallback con Grok completado exitosamente");
|
||||
}
|
||||
else
|
||||
{
|
||||
GTP.Log.Log("Grok no pudo explicar el error, usando notificación de error estándar");
|
||||
ShowCustomNotification("Error", "Se produjo un error inesperado");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
GTP.Log.Log($"Error en sistema de fallback: {ex.Message}");
|
||||
throw; // Re-lanzar para que sea manejado por el caller
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
86
Logger.cs
86
Logger.cs
|
@ -12,20 +12,98 @@ namespace GTPCorrgir
|
|||
|
||||
public class Logger
|
||||
{
|
||||
private readonly string _logFilePath;
|
||||
|
||||
public Logger()
|
||||
{
|
||||
string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logfile.log");
|
||||
Trace.Listeners.Add(new TextWriterTraceListener(logFilePath));
|
||||
_logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logfile.log");
|
||||
Trace.Listeners.Add(new TextWriterTraceListener(_logFilePath));
|
||||
Trace.AutoFlush = true;
|
||||
}
|
||||
|
||||
|
||||
public void Log(string message)
|
||||
{
|
||||
// Formato de timestamp [AAAA-MM-DD HH:mm:ss]
|
||||
string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
Trace.WriteLine($"[{timestamp}] {message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtiene las últimas líneas del log desde una interacción específica
|
||||
/// </summary>
|
||||
/// <param name="sinceMessage">Mensaje desde el cual comenzar a leer</param>
|
||||
/// <returns>String con las líneas del log desde el mensaje especificado</returns>
|
||||
public string GetRecentLogs(string sinceMessage = "Iniciando corrección de texto")
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(_logFilePath))
|
||||
return "No hay archivo de log disponible";
|
||||
|
||||
var lines = File.ReadAllLines(_logFilePath);
|
||||
var recentLines = new List<string>();
|
||||
bool foundStartPoint = false;
|
||||
|
||||
// Buscar desde la última ocurrencia del mensaje especificado
|
||||
for (int i = lines.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (lines[i].Contains(sinceMessage))
|
||||
{
|
||||
foundStartPoint = true;
|
||||
// Tomar todas las líneas desde este punto hasta el final
|
||||
for (int j = i; j < lines.Length; j++)
|
||||
{
|
||||
recentLines.Add(lines[j]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundStartPoint)
|
||||
{
|
||||
// Si no encontramos el mensaje específico, tomar las últimas 20 líneas
|
||||
int startIndex = Math.Max(0, lines.Length - 20);
|
||||
for (int i = startIndex; i < lines.Length; i++)
|
||||
{
|
||||
recentLines.Add(lines[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return string.Join("\n", recentLines);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Error al leer el log: {ex.Message}";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Obtiene las últimas N líneas del log
|
||||
/// </summary>
|
||||
/// <param name="lineCount">Número de líneas a obtener</param>
|
||||
/// <returns>String con las últimas líneas del log</returns>
|
||||
public string GetLastLogLines(int lineCount = 20)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!File.Exists(_logFilePath))
|
||||
return "No hay archivo de log disponible";
|
||||
|
||||
var lines = File.ReadAllLines(_logFilePath);
|
||||
int startIndex = Math.Max(0, lines.Length - lineCount);
|
||||
|
||||
var lastLines = new List<string>();
|
||||
for (int i = startIndex; i < lines.Length; i++)
|
||||
{
|
||||
lastLines.Add(lines[i]);
|
||||
}
|
||||
|
||||
return string.Join("\n", lastLines);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return $"Error al leer el log: {ex.Message}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace GTPCorrgir
|
|||
|
||||
private void InitializeHttpClient()
|
||||
{
|
||||
_httpClient.Timeout = TimeSpan.FromSeconds(30);
|
||||
_httpClient.Timeout = TimeSpan.FromSeconds(60);
|
||||
_httpClient.DefaultRequestHeaders.Clear();
|
||||
_httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
# Sistema de Fallback con Grok para Explicación de Errores
|
||||
|
||||
## Descripción
|
||||
|
||||
Se ha implementado un sistema de fallback que utiliza Grok para explicar errores cuando el procesamiento principal de la aplicación falla. Esta funcionalidad proporciona al usuario una explicación comprensible de qué salió mal cuando no se puede generar una respuesta normal.
|
||||
|
||||
## Funcionamiento
|
||||
|
||||
1. **Detección de Error**: Cuando el procesamiento principal falla (por ejemplo, error 529 de Claude por sobrecarga del servidor), el sistema captura la excepción.
|
||||
|
||||
2. **Obtención de Logs**: Se obtienen los logs de la última interacción usando el método `GetRecentLogs()` del Logger, que busca desde "Iniciando corrección de texto" hasta el final.
|
||||
|
||||
3. **Llamada a Grok**: Se envía a Grok un prompt del sistema que lo instruye como "asistente técnico especializado en explicar errores de software" junto con los logs de error.
|
||||
|
||||
4. **Presentación del Resultado**: Si Grok puede explicar el error, se presenta al usuario:
|
||||
```
|
||||
[Texto original]
|
||||
|
||||
ERROR: [Explicación de Grok]
|
||||
```
|
||||
|
||||
5. **Fallback del Fallback**: Si Grok también falla, se muestra el último mensaje de error encontrado en los logs.
|
||||
|
||||
## Archivos Modificados
|
||||
|
||||
### Logger.cs
|
||||
- **Nuevo**: `GetRecentLogs(string sinceMessage)` - Obtiene logs desde un mensaje específico
|
||||
- **Nuevo**: `GetLastLogLines(int lineCount)` - Obtiene las últimas N líneas del log
|
||||
- **Modificado**: Almacena la ruta del archivo de log para poder leerlo
|
||||
|
||||
### gtpask.cs
|
||||
- **Nuevo**: `ExplicarErrorConGrok(string originalText, string errorLogs)` - Método principal del fallback
|
||||
- **Nuevo**: `CallGrokForErrorExplanation(string systemPrompt, string userPrompt)` - Llamada específica a Grok para explicaciones
|
||||
- **Nuevo**: `GetLastErrorMessage(string errorLogs)` - Extrae el último mensaje de error de los logs
|
||||
|
||||
### App.xaml.cs
|
||||
- **Nuevo**: `TryErrorFallbackWithGrok(Exception originalError)` - Método que coordina el fallback
|
||||
- **Modificado**: Catch de `ProcessCorreccionWithTimeout()` - Ahora usa el fallback
|
||||
- **Modificado**: Catch de timeout - Ahora usa el fallback
|
||||
- **Modificado**: Catch de error en procesamiento final - Ahora usa el fallback
|
||||
|
||||
## Configuración
|
||||
|
||||
### Prerequisitos
|
||||
- API key de Grok configurada en `appsettings.json` bajo `ApiKeys.Grok`
|
||||
|
||||
### Parámetros de Grok
|
||||
- **Modelo**: `grok-beta`
|
||||
- **Temperatura**: `0.3` (para respuestas consistentes)
|
||||
- **Max Tokens**: `500` (para explicaciones concisas)
|
||||
- **Stream**: `false`
|
||||
|
||||
## Ejemplo de Uso
|
||||
|
||||
### Escenario: Error 529 de Claude (servidor sobrecargado)
|
||||
|
||||
**Logs de entrada:**
|
||||
```
|
||||
[2025-06-17 12:48:42] Iniciando corrección de texto
|
||||
[2025-06-17 12:48:42] Iniciando proceso de corrección
|
||||
[2025-06-17 12:48:42] Texto original: Que modelos de SEW en profinet existen?
|
||||
[2025-06-17 12:48:42] Idioma detectado: Spanish
|
||||
[2025-06-17 12:48:42] Texto marcado: Que modelos de SEW en profinet existen?
|
||||
[2025-06-17 12:48:42] Enviando solicitud a https://api.anthropic.com/v1/messages
|
||||
[2025-06-17 12:49:12] Respuesta recibida: {"type":"error","error":{"type":"overloaded_error","message":"Overloaded"}}
|
||||
[2025-06-17 12:49:12] Error en llamada a Claude Web Search API: Error en la solicitud HTTP: 529 - {"type":"error","error":{"type":"overloaded_error","message":"Overloaded"}}
|
||||
[2025-06-17 12:49:12] Error no controlado: Error en la solicitud HTTP: 529 - {"type":"error","error":{"type":"overloaded_error","message":"Overloaded"}}
|
||||
```
|
||||
|
||||
**Resultado esperado:**
|
||||
```
|
||||
Que modelos de SEW en profinet existen?
|
||||
|
||||
ERROR: El servidor de Claude está actualmente sobrecargado y no puede procesar la solicitud. Esto es un problema temporal del proveedor del servicio. Intente nuevamente en unos minutos.
|
||||
```
|
||||
|
||||
## Características de Seguridad
|
||||
|
||||
1. **No Recursivo**: El sistema de fallback se ejecuta solo una vez por error, no recursivamente.
|
||||
2. **Manejo de Errores**: Si Grok falla, se muestra el error original sin causar fallos adicionales.
|
||||
3. **Timeouts**: Usa los mismos timeouts configurados para las otras APIs.
|
||||
4. **Logging**: Todas las operaciones del fallback se registran en el log para debugging.
|
||||
|
||||
## Casos de Error Cubiertos
|
||||
|
||||
- Errores de API (429, 500, 529, etc.)
|
||||
- Timeouts de operación
|
||||
- Errores de procesamiento
|
||||
- Errores de formato de respuesta
|
||||
- Errores de conectividad
|
||||
|
||||
## Limitaciones
|
||||
|
||||
- Requiere que la API key de Grok esté configurada
|
||||
- Solo funciona si Grok está disponible
|
||||
- No funciona para errores de inicialización de la aplicación
|
||||
- Limitado a 500 tokens para mantener respuestas concisas
|
287
gtpask.cs
287
gtpask.cs
|
@ -121,7 +121,7 @@ namespace GTPCorrgir
|
|||
|
||||
private void InitializeHttpClient()
|
||||
{
|
||||
_httpClient.Timeout = TimeSpan.FromSeconds(30);
|
||||
_httpClient.Timeout = TimeSpan.FromSeconds(60);
|
||||
_httpClient.DefaultRequestHeaders.Clear();
|
||||
_httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
|
||||
}
|
||||
|
@ -306,29 +306,31 @@ namespace GTPCorrgir
|
|||
// Para Claude Web Search, la respuesta ya viene en formato de texto final
|
||||
if (Opciones.Instance.modo == Opciones.modoDeUso.ClaudeWebSearch)
|
||||
{
|
||||
// Claude Web Search ya devuelve el texto final con formato JSON interno,
|
||||
// intentamos extraer JSON primero, si falla usamos el texto completo
|
||||
string respuestaExtraida = ExtraerValorUnicoJSON(respuestaLLM);
|
||||
if (respuestaExtraida == null)
|
||||
// Extraer contenido estructurado (textos + enlaces)
|
||||
string contenidoEstructurado = ExtraerEnlacesWebSearch(respuestaCompleta);
|
||||
|
||||
if (!string.IsNullOrEmpty(contenidoEstructurado))
|
||||
{
|
||||
// Si no se puede extraer JSON, usamos la respuesta completa
|
||||
respuestaExtraida = respuestaLLM;
|
||||
Log.Log("Claude Web Search: No se encontró JSON, usando respuesta completa");
|
||||
// Si hay contenido estructurado, usarlo directamente
|
||||
TextoCorregido = $"{TextoACorregir}\n\n## Respuesta:\n{contenidoEstructurado}";
|
||||
Log.Log("Claude Web Search: Usando contenido estructurado con textos y enlaces");
|
||||
}
|
||||
|
||||
respuestaExtraida = System.Text.RegularExpressions.Regex.Replace(respuestaExtraida, @"\*\*(.*?)\*\*", "$1");
|
||||
respuestaExtraida = _markdownProcessor.RemoveTechnicalTermMarkers_IgnoreCase(respuestaExtraida).Trim('"');
|
||||
respuestaExtraida = _markdownProcessor.RemoveDoubleBrackets(respuestaExtraida);
|
||||
|
||||
// Extraer y formatear enlaces de búsqueda web
|
||||
string enlacesFormateados = ExtraerEnlacesWebSearch(respuestaCompleta);
|
||||
|
||||
// Para el modo Pregunta-Respuesta y Claude Web Search, combinar pregunta original con la respuesta y enlaces
|
||||
TextoCorregido = $"{TextoACorregir}\n\n{respuestaExtraida}";
|
||||
|
||||
if (!string.IsNullOrEmpty(enlacesFormateados))
|
||||
else
|
||||
{
|
||||
TextoCorregido += $"\n\n## Fuentes consultadas:\n{enlacesFormateados}";
|
||||
// Fallback: intentar extraer JSON primero, si falla usar respuesta completa
|
||||
string respuestaExtraida = ExtraerValorUnicoJSON(respuestaLLM);
|
||||
if (respuestaExtraida == null)
|
||||
{
|
||||
respuestaExtraida = respuestaLLM;
|
||||
Log.Log("Claude Web Search: No se encontró JSON, usando respuesta completa");
|
||||
}
|
||||
|
||||
respuestaExtraida = System.Text.RegularExpressions.Regex.Replace(respuestaExtraida, @"\*\*(.*?)\*\*", "$1");
|
||||
respuestaExtraida = _markdownProcessor.RemoveTechnicalTermMarkers_IgnoreCase(respuestaExtraida).Trim('"');
|
||||
respuestaExtraida = _markdownProcessor.RemoveDoubleBrackets(respuestaExtraida);
|
||||
|
||||
TextoCorregido = $"{TextoACorregir}\n\n{respuestaExtraida}";
|
||||
Log.Log("Claude Web Search: Usando respuesta fallback");
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -368,9 +370,9 @@ namespace GTPCorrgir
|
|||
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Procesando respuesta de {respuestaCompleta.Length} caracteres");
|
||||
|
||||
var enlaces = new List<string>();
|
||||
var contenidoFormateado = new List<string>();
|
||||
|
||||
// Deserializar la respuesta completa para extraer los resultados de búsqueda
|
||||
// Deserializar la respuesta completa para extraer los resultados
|
||||
var data = JsonConvert.DeserializeObject<dynamic>(respuestaCompleta);
|
||||
|
||||
if (data?.content != null)
|
||||
|
@ -379,12 +381,52 @@ namespace GTPCorrgir
|
|||
|
||||
foreach (var contentItem in data.content)
|
||||
{
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Procesando elemento tipo: {contentItem.type}");
|
||||
string tipoElemento = contentItem.type?.ToString() ?? "";
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Procesando elemento tipo: {tipoElemento}");
|
||||
|
||||
// Buscar elementos de tipo "web_search_tool_result"
|
||||
if (contentItem.type == "web_search_tool_result" && contentItem.content != null)
|
||||
// Procesar elementos de texto con sus citas
|
||||
if (tipoElemento == "text" && contentItem.text != null)
|
||||
{
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Encontrado web_search_tool_result con {contentItem.content.Count} resultados");
|
||||
string textoRespuesta = contentItem.text.ToString().Trim();
|
||||
|
||||
// Solo procesar textos que no sean muy cortos y tengan contenido útil
|
||||
if (!string.IsNullOrWhiteSpace(textoRespuesta) && textoRespuesta.Length > 10)
|
||||
{
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Texto encontrado: {textoRespuesta.Substring(0, Math.Min(50, textoRespuesta.Length))}...");
|
||||
|
||||
// Agregar el texto principal como viñeta
|
||||
contenidoFormateado.Add($"* {textoRespuesta}");
|
||||
|
||||
// Verificar si hay citas asociadas
|
||||
if (contentItem.citations != null && contentItem.citations.Count > 0)
|
||||
{
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Encontradas {contentItem.citations.Count} citas para este texto");
|
||||
|
||||
foreach (var citation in contentItem.citations)
|
||||
{
|
||||
if (citation.type == "web_search_result_location")
|
||||
{
|
||||
string titulo = citation.title?.ToString() ?? "Sin título";
|
||||
string url = citation.url?.ToString() ?? "";
|
||||
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
{
|
||||
// Agregar el enlace como sub-viñeta indentada
|
||||
contenidoFormateado.Add($" * [{titulo}]({url})");
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Cita agregada - {titulo}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// También capturar resultados de búsqueda web como fuentes adicionales
|
||||
else if (tipoElemento == "web_search_tool_result" && contentItem.content != null)
|
||||
{
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Procesando resultados de búsqueda web");
|
||||
|
||||
var enlacesBusqueda = new List<string>();
|
||||
|
||||
foreach (var searchResult in contentItem.content)
|
||||
{
|
||||
|
@ -394,24 +436,29 @@ namespace GTPCorrgir
|
|||
string url = searchResult.url?.ToString() ?? "";
|
||||
string pageAge = searchResult.page_age?.ToString() ?? "";
|
||||
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Resultado - Título: {titulo}, URL: {url}");
|
||||
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
{
|
||||
string enlaceFormateado;
|
||||
if (!string.IsNullOrEmpty(pageAge))
|
||||
{
|
||||
enlaceFormateado = $"* [{titulo}]({url}) - {pageAge}";
|
||||
enlaceFormateado = $" * [{titulo}]({url}) - {pageAge}";
|
||||
}
|
||||
else
|
||||
{
|
||||
enlaceFormateado = $"* [{titulo}]({url})";
|
||||
enlaceFormateado = $" * [{titulo}]({url})";
|
||||
}
|
||||
|
||||
enlaces.Add(enlaceFormateado);
|
||||
enlacesBusqueda.Add(enlaceFormateado);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si hay resultados de búsqueda adicionales, agregarlos al final
|
||||
if (enlacesBusqueda.Any())
|
||||
{
|
||||
contenidoFormateado.Add("* Fuentes adicionales consultadas:");
|
||||
contenidoFormateado.AddRange(enlacesBusqueda.Take(5)); // Limitar a 5 para no sobrecargar
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -420,14 +467,11 @@ namespace GTPCorrgir
|
|||
Log.Log("ExtraerEnlacesWebSearch: No se encontró contenido en la respuesta");
|
||||
}
|
||||
|
||||
// Eliminar duplicados y devolver como string
|
||||
var enlacesUnicos = enlaces.Distinct().ToList();
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Generadas {contenidoFormateado.Count} líneas de contenido formateado");
|
||||
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Extraídos {enlacesUnicos.Count} enlaces únicos");
|
||||
|
||||
if (enlacesUnicos.Any())
|
||||
if (contenidoFormateado.Any())
|
||||
{
|
||||
return string.Join("\n", enlacesUnicos);
|
||||
return string.Join("\n", contenidoFormateado);
|
||||
}
|
||||
|
||||
return "";
|
||||
|
@ -892,11 +936,168 @@ namespace GTPCorrgir
|
|||
|
||||
texto = texto.Trim();
|
||||
|
||||
// Verificar si el texto es principalmente una URL
|
||||
return texto.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
|
||||
texto.StartsWith("https://", StringComparison.OrdinalIgnoreCase) ||
|
||||
texto.StartsWith("www.", StringComparison.OrdinalIgnoreCase) ||
|
||||
(texto.Contains(".") && texto.Split(' ').Length == 1);
|
||||
// Lista de patrones comunes de URL
|
||||
string[] patrones = {
|
||||
"http://", "https://", "www.", "ftp://", "ftps://",
|
||||
".com", ".net", ".org", ".edu", ".gov", ".mil",
|
||||
".es", ".mx", ".ar", ".cl", ".co", ".pe"
|
||||
};
|
||||
|
||||
return patrones.Any(patron => texto.ToLower().Contains(patron));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Método de fallback que usa Grok para explicar errores cuando el procesamiento principal falla
|
||||
/// </summary>
|
||||
/// <param name="originalText">Texto original que se intentó procesar</param>
|
||||
/// <param name="errorLogs">Logs de la interacción que falló</param>
|
||||
/// <returns>Texto original + explicación del error, o solo el error si Grok también falla</returns>
|
||||
public async Task<string> ExplicarErrorConGrok(string originalText, string errorLogs)
|
||||
{
|
||||
try
|
||||
{
|
||||
Log.Log("Iniciando fallback con Grok para explicar el error");
|
||||
|
||||
// Verificar que tenemos la API key de Grok
|
||||
if (string.IsNullOrEmpty(_grokApiKey))
|
||||
{
|
||||
Log.Log("Error fallback: No hay API key de Grok disponible");
|
||||
return GetLastErrorMessage(errorLogs);
|
||||
}
|
||||
|
||||
// Preparar el prompt para Grok
|
||||
string systemPrompt = "Eres un asistente técnico especializado en explicar errores de software. " +
|
||||
"Analiza los logs de error proporcionados y explica de manera simple y clara qué salió mal y por qué, " +
|
||||
"en español. Sé conciso y útil para el usuario final.";
|
||||
|
||||
string userPrompt = $"¿Podrías explicar qué no ha funcionado y por qué según estos logs?\n\n{errorLogs}";
|
||||
|
||||
// Llamar a Grok específicamente para explicar el error
|
||||
string explicacion = await CallGrokForErrorExplanation(systemPrompt, userPrompt);
|
||||
|
||||
if (!string.IsNullOrEmpty(explicacion))
|
||||
{
|
||||
Log.Log("Grok proporcionó explicación del error exitosamente");
|
||||
return $"{originalText}\n\nERROR: {explicacion}";
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Log("Grok no pudo proporcionar explicación del error");
|
||||
return GetLastErrorMessage(errorLogs);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Log($"Error en fallback de explicación con Grok: {ex.Message}");
|
||||
return GetLastErrorMessage(errorLogs);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Método específico para llamar a Grok para explicaciones de errores
|
||||
/// </summary>
|
||||
private async Task<string> CallGrokForErrorExplanation(string systemPrompt, string userPrompt)
|
||||
{
|
||||
try
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.Clear();
|
||||
_httpClient.DefaultRequestHeaders.Add("Authorization", $"Bearer {_grokApiKey}");
|
||||
|
||||
var requestData = new
|
||||
{
|
||||
messages = new[]
|
||||
{
|
||||
new { role = "system", content = systemPrompt },
|
||||
new { role = "user", content = userPrompt }
|
||||
},
|
||||
model = "grok-beta",
|
||||
stream = false,
|
||||
temperature = 0.3, // Baja temperatura para respuestas más consistentes en explicaciones de errores
|
||||
max_tokens = 500 // Limitar tokens para explicaciones concisas
|
||||
};
|
||||
|
||||
var content = new StringContent(
|
||||
JsonConvert.SerializeObject(requestData),
|
||||
Encoding.UTF8,
|
||||
"application/json"
|
||||
);
|
||||
|
||||
Log.Log("Enviando solicitud de explicación de error a Grok");
|
||||
|
||||
using var response = await _httpClient.PostAsync("https://api.x.ai/v1/chat/completions", content);
|
||||
var responseContent = await response.Content.ReadAsStringAsync();
|
||||
|
||||
Log.Log($"Respuesta de Grok para explicación de error: {responseContent}");
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
Log.Log($"Error en solicitud a Grok para explicación: {response.StatusCode} - {responseContent}");
|
||||
return null;
|
||||
}
|
||||
|
||||
var data = JsonConvert.DeserializeObject<dynamic>(responseContent);
|
||||
|
||||
if (data?.choices != null && data.choices.Count > 0)
|
||||
{
|
||||
string explanation = data.choices[0].message.content?.ToString();
|
||||
return explanation?.Trim();
|
||||
}
|
||||
|
||||
Log.Log("No se encontró contenido en la respuesta de Grok para explicación de error");
|
||||
return null;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Log($"Error en llamada a Grok para explicación de error: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extrae el último mensaje de error de los logs
|
||||
/// </summary>
|
||||
private string GetLastErrorMessage(string errorLogs)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(errorLogs))
|
||||
return "Error desconocido";
|
||||
|
||||
// Buscar la última línea que contenga "Error no controlado" o "Error"
|
||||
var lines = errorLogs.Split('\n');
|
||||
|
||||
for (int i = lines.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (lines[i].Contains("Error no controlado:") ||
|
||||
lines[i].Contains("Error durante la corrección:") ||
|
||||
lines[i].Contains("Error en CorregirTexto:"))
|
||||
{
|
||||
// Extraer solo la parte del error después de los dos puntos
|
||||
var parts = lines[i].Split(':', 2);
|
||||
if (parts.Length > 1)
|
||||
{
|
||||
return parts[1].Trim();
|
||||
}
|
||||
return lines[i].Trim();
|
||||
}
|
||||
}
|
||||
|
||||
// Si no encuentra un error específico, devolver la última línea no vacía
|
||||
for (int i = lines.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(lines[i]))
|
||||
{
|
||||
return lines[i].Trim();
|
||||
}
|
||||
}
|
||||
|
||||
return "Error desconocido";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Log($"Error al extraer último mensaje de error: {ex.Message}");
|
||||
return "Error desconocido";
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
|
|
Loading…
Reference in New Issue