Se agregó la opción "Claude Web Search" al menú y se implementó la lógica para manejar las solicitudes a la API de Claude, incluyendo la extracción de enlaces de búsqueda. Se mejoró la detección de idioma y se estableció español como predeterminado en casos específicos. Además, se optimizó el procesamiento de respuestas para el modo Claude Web Search.
This commit is contained in:
parent
a48e64f372
commit
a8aca9a82a
|
@ -81,6 +81,7 @@ namespace GTPCorrgir
|
||||||
case Opciones.modoDeUso.Corregir:
|
case Opciones.modoDeUso.Corregir:
|
||||||
case Opciones.modoDeUso.Ortografia:
|
case Opciones.modoDeUso.Ortografia:
|
||||||
case Opciones.modoDeUso.PreguntaRespuesta:
|
case Opciones.modoDeUso.PreguntaRespuesta:
|
||||||
|
case Opciones.modoDeUso.ClaudeWebSearch:
|
||||||
case Opciones.modoDeUso.Traducir_a_Espanol:
|
case Opciones.modoDeUso.Traducir_a_Espanol:
|
||||||
case Opciones.modoDeUso.Traducir_a_Ingles:
|
case Opciones.modoDeUso.Traducir_a_Ingles:
|
||||||
case Opciones.modoDeUso.Traducir_a_Italiano:
|
case Opciones.modoDeUso.Traducir_a_Italiano:
|
||||||
|
@ -155,9 +156,9 @@ namespace GTPCorrgir
|
||||||
$"Corrección en: {Math.Round(stopwatch.ElapsedMilliseconds / 1000.0, 1)} s");
|
$"Corrección en: {Math.Round(stopwatch.ElapsedMilliseconds / 1000.0, 1)} s");
|
||||||
|
|
||||||
if (Opciones.Instance.modo == Opciones.modoDeUso.Corregir || Opciones.Instance.modo == Opciones.modoDeUso.Ortografia ||
|
if (Opciones.Instance.modo == Opciones.modoDeUso.Corregir || Opciones.Instance.modo == Opciones.modoDeUso.Ortografia ||
|
||||||
Opciones.Instance.modo == Opciones.modoDeUso.PreguntaRespuesta || Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Espanol ||
|
Opciones.Instance.modo == Opciones.modoDeUso.PreguntaRespuesta || Opciones.Instance.modo == Opciones.modoDeUso.ClaudeWebSearch ||
|
||||||
Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Ingles || Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Italiano ||
|
Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Espanol || Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Ingles ||
|
||||||
Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Portugues)
|
Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Italiano || Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Portugues)
|
||||||
{
|
{
|
||||||
if (Opciones.Instance.FuncionesOpcionales == Opciones.funcionesOpcionales.MostrarPopUp)
|
if (Opciones.Instance.FuncionesOpcionales == Opciones.funcionesOpcionales.MostrarPopUp)
|
||||||
{
|
{
|
||||||
|
|
|
@ -47,6 +47,7 @@ namespace GTPCorrgir
|
||||||
new MenuOption { DisplayName = "Revisar ortografía", Value = Opciones.modoDeUso.Ortografia },
|
new MenuOption { DisplayName = "Revisar ortografía", Value = Opciones.modoDeUso.Ortografia },
|
||||||
new MenuOption { DisplayName = "Chat", Value = Opciones.modoDeUso.Chat },
|
new MenuOption { DisplayName = "Chat", Value = Opciones.modoDeUso.Chat },
|
||||||
new MenuOption { DisplayName = "Pregunta-Respuesta", Value = Opciones.modoDeUso.PreguntaRespuesta },
|
new MenuOption { DisplayName = "Pregunta-Respuesta", Value = Opciones.modoDeUso.PreguntaRespuesta },
|
||||||
|
new MenuOption { DisplayName = "Claude Web Search", Value = Opciones.modoDeUso.ClaudeWebSearch },
|
||||||
new MenuOption { DisplayName = "Traducir a inglés", Value = Opciones.modoDeUso.Traducir_a_Ingles },
|
new MenuOption { DisplayName = "Traducir a inglés", Value = Opciones.modoDeUso.Traducir_a_Ingles },
|
||||||
new MenuOption { DisplayName = "Traducir a italiano", Value = Opciones.modoDeUso.Traducir_a_Italiano },
|
new MenuOption { DisplayName = "Traducir a italiano", Value = Opciones.modoDeUso.Traducir_a_Italiano },
|
||||||
new MenuOption { DisplayName = "Traducir a español", Value = Opciones.modoDeUso.Traducir_a_Espanol },
|
new MenuOption { DisplayName = "Traducir a español", Value = Opciones.modoDeUso.Traducir_a_Espanol },
|
||||||
|
@ -167,6 +168,12 @@ namespace GTPCorrgir
|
||||||
var modeValue = (Opciones.modoDeUso)selectedMode.Value;
|
var modeValue = (Opciones.modoDeUso)selectedMode.Value;
|
||||||
var llmValue = (Opciones.LLM_a_Usar)selectedLLM.Value;
|
var llmValue = (Opciones.LLM_a_Usar)selectedLLM.Value;
|
||||||
|
|
||||||
|
// Si se selecciona Claude Web Search, forzar el uso de Claude
|
||||||
|
if (modeValue == Opciones.modoDeUso.ClaudeWebSearch)
|
||||||
|
{
|
||||||
|
llmValue = Opciones.LLM_a_Usar.Claude;
|
||||||
|
}
|
||||||
|
|
||||||
Opciones.Instance.modo = modeValue;
|
Opciones.Instance.modo = modeValue;
|
||||||
Opciones.Instance.LLM = llmValue;
|
Opciones.Instance.LLM = llmValue;
|
||||||
|
|
||||||
|
@ -182,7 +189,7 @@ namespace GTPCorrgir
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logfile.log");
|
string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logfile.log");
|
||||||
|
|
||||||
if (File.Exists(logFilePath))
|
if (File.Exists(logFilePath))
|
||||||
{
|
{
|
||||||
// Intentar abrir con el programa predeterminado
|
// Intentar abrir con el programa predeterminado
|
||||||
|
@ -194,13 +201,13 @@ namespace GTPCorrgir
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MessageBox.Show("El archivo de log no existe aún.", "Información",
|
MessageBox.Show("El archivo de log no existe aún.", "Información",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
MessageBox.Show($"Error al abrir el archivo de log: {ex.Message}", "Error",
|
MessageBox.Show($"Error al abrir el archivo de log: {ex.Message}", "Error",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -210,22 +217,22 @@ namespace GTPCorrgir
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = MessageBox.Show(
|
var result = MessageBox.Show(
|
||||||
"¿Está seguro de que desea limpiar el archivo de log?\nEsta acción no se puede deshacer.",
|
"¿Está seguro de que desea limpiar el archivo de log?\nEsta acción no se puede deshacer.",
|
||||||
"Confirmar limpieza de log",
|
"Confirmar limpieza de log",
|
||||||
MessageBoxButton.YesNo,
|
MessageBoxButton.YesNo,
|
||||||
MessageBoxImage.Warning);
|
MessageBoxImage.Warning);
|
||||||
|
|
||||||
if (result == MessageBoxResult.Yes)
|
if (result == MessageBoxResult.Yes)
|
||||||
{
|
{
|
||||||
string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logfile.log");
|
string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logfile.log");
|
||||||
|
|
||||||
if (File.Exists(logFilePath))
|
if (File.Exists(logFilePath))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Obtener todos los listeners de tipo TextWriterTraceListener
|
// Obtener todos los listeners de tipo TextWriterTraceListener
|
||||||
var textWriterListeners = new List<System.Diagnostics.TextWriterTraceListener>();
|
var textWriterListeners = new List<System.Diagnostics.TextWriterTraceListener>();
|
||||||
|
|
||||||
foreach (System.Diagnostics.TraceListener listener in System.Diagnostics.Trace.Listeners)
|
foreach (System.Diagnostics.TraceListener listener in System.Diagnostics.Trace.Listeners)
|
||||||
{
|
{
|
||||||
if (listener is System.Diagnostics.TextWriterTraceListener textListener)
|
if (listener is System.Diagnostics.TextWriterTraceListener textListener)
|
||||||
|
@ -233,7 +240,7 @@ namespace GTPCorrgir
|
||||||
textWriterListeners.Add(textListener);
|
textWriterListeners.Add(textListener);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cerrar y remover temporalmente los listeners
|
// Cerrar y remover temporalmente los listeners
|
||||||
foreach (var listener in textWriterListeners)
|
foreach (var listener in textWriterListeners)
|
||||||
{
|
{
|
||||||
|
@ -241,53 +248,53 @@ namespace GTPCorrgir
|
||||||
listener.Close();
|
listener.Close();
|
||||||
System.Diagnostics.Trace.Listeners.Remove(listener);
|
System.Diagnostics.Trace.Listeners.Remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Esperar un momento para asegurar que el archivo se libere
|
// Esperar un momento para asegurar que el archivo se libere
|
||||||
System.Threading.Thread.Sleep(100);
|
System.Threading.Thread.Sleep(100);
|
||||||
|
|
||||||
// Limpiar el archivo
|
// Limpiar el archivo
|
||||||
File.WriteAllText(logFilePath, string.Empty);
|
File.WriteAllText(logFilePath, string.Empty);
|
||||||
|
|
||||||
// Recrear los listeners
|
// Recrear los listeners
|
||||||
foreach (var listener in textWriterListeners)
|
foreach (var listener in textWriterListeners)
|
||||||
{
|
{
|
||||||
var newListener = new System.Diagnostics.TextWriterTraceListener(logFilePath);
|
var newListener = new System.Diagnostics.TextWriterTraceListener(logFilePath);
|
||||||
System.Diagnostics.Trace.Listeners.Add(newListener);
|
System.Diagnostics.Trace.Listeners.Add(newListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reactivar AutoFlush
|
// Reactivar AutoFlush
|
||||||
System.Diagnostics.Trace.AutoFlush = true;
|
System.Diagnostics.Trace.AutoFlush = true;
|
||||||
|
|
||||||
MessageBox.Show("El archivo de log ha sido limpiado correctamente.", "Éxito",
|
MessageBox.Show("El archivo de log ha sido limpiado correctamente.", "Éxito",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
}
|
}
|
||||||
catch (UnauthorizedAccessException)
|
catch (UnauthorizedAccessException)
|
||||||
{
|
{
|
||||||
MessageBox.Show(
|
MessageBox.Show(
|
||||||
"No se puede acceder al archivo de log. Puede que esté siendo usado por otro proceso.\n" +
|
"No se puede acceder al archivo de log. Puede que esté siendo usado por otro proceso.\n" +
|
||||||
"Intente cerrar todas las instancias de la aplicación y vuelva a intentarlo.",
|
"Intente cerrar todas las instancias de la aplicación y vuelva a intentarlo.",
|
||||||
"Acceso denegado",
|
"Acceso denegado",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
}
|
}
|
||||||
catch (IOException ioEx)
|
catch (IOException ioEx)
|
||||||
{
|
{
|
||||||
MessageBox.Show(
|
MessageBox.Show(
|
||||||
$"Error de E/S al acceder al archivo de log:\n{ioEx.Message}\n\n" +
|
$"Error de E/S al acceder al archivo de log:\n{ioEx.Message}\n\n" +
|
||||||
"El archivo puede estar siendo usado por otro proceso.",
|
"El archivo puede estar siendo usado por otro proceso.",
|
||||||
"Error de archivo",
|
"Error de archivo",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MessageBox.Show("El archivo de log no existe.", "Información",
|
MessageBox.Show("El archivo de log no existe.", "Información",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
MessageBox.Show($"Error al limpiar el archivo de log: {ex.Message}", "Error",
|
MessageBox.Show($"Error al limpiar el archivo de log: {ex.Message}", "Error",
|
||||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -307,47 +314,47 @@ namespace GTPCorrgir
|
||||||
};
|
};
|
||||||
|
|
||||||
var stackPanel = new StackPanel { Margin = new Thickness(20) };
|
var stackPanel = new StackPanel { Margin = new Thickness(20) };
|
||||||
|
|
||||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||||
{
|
{
|
||||||
Text = "GTPCorregir",
|
Text = "GTPCorregir",
|
||||||
FontSize = 18,
|
FontSize = 18,
|
||||||
FontWeight = FontWeights.Bold,
|
FontWeight = FontWeights.Bold,
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
||||||
Margin = new Thickness(0, 0, 0, 10)
|
Margin = new Thickness(0, 0, 0, 10)
|
||||||
});
|
});
|
||||||
|
|
||||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||||
{
|
{
|
||||||
Text = "Herramienta de corrección de texto con IA",
|
Text = "Herramienta de corrección de texto con IA",
|
||||||
FontSize = 12,
|
FontSize = 12,
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
||||||
Margin = new Thickness(0, 0, 0, 15)
|
Margin = new Thickness(0, 0, 0, 15)
|
||||||
});
|
});
|
||||||
|
|
||||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||||
{
|
{
|
||||||
Text = "• Corrección de ortografía y gramática",
|
Text = "• Corrección de ortografía y gramática",
|
||||||
Margin = new Thickness(0, 2, 0, 2)
|
Margin = new Thickness(0, 2, 0, 2)
|
||||||
});
|
});
|
||||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||||
{
|
{
|
||||||
Text = "• Traducción a múltiples idiomas",
|
Text = "• Traducción a múltiples idiomas",
|
||||||
Margin = new Thickness(0, 2, 0, 2)
|
Margin = new Thickness(0, 2, 0, 2)
|
||||||
});
|
});
|
||||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||||
{
|
{
|
||||||
Text = "• OCR con PaddleOCR",
|
Text = "• OCR con PaddleOCR",
|
||||||
Margin = new Thickness(0, 2, 0, 2)
|
Margin = new Thickness(0, 2, 0, 2)
|
||||||
});
|
});
|
||||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||||
{
|
{
|
||||||
Text = "• Soporte para múltiples LLMs",
|
Text = "• Soporte para múltiples LLMs",
|
||||||
Margin = new Thickness(0, 2, 0, 2)
|
Margin = new Thickness(0, 2, 0, 2)
|
||||||
});
|
});
|
||||||
|
|
||||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||||
{
|
{
|
||||||
Text = $"Versión: {System.Reflection.Assembly.GetExecutingAssembly().GetName().Version}",
|
Text = $"Versión: {System.Reflection.Assembly.GetExecutingAssembly().GetName().Version}",
|
||||||
FontSize = 10,
|
FontSize = 10,
|
||||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
||||||
|
|
|
@ -38,6 +38,7 @@ namespace GTPCorrgir
|
||||||
Traducir_a_Portugues,
|
Traducir_a_Portugues,
|
||||||
OCRaTexto,
|
OCRaTexto,
|
||||||
Menu,
|
Menu,
|
||||||
|
ClaudeWebSearch,
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<LLM_a_Usar, string> nombreLLM = new Dictionary<LLM_a_Usar, string>
|
public Dictionary<LLM_a_Usar, string> nombreLLM = new Dictionary<LLM_a_Usar, string>
|
||||||
|
@ -130,6 +131,8 @@ namespace GTPCorrgir
|
||||||
Opciones.Instance.modo = Opciones.modoDeUso.Traducir_a_Portugues;
|
Opciones.Instance.modo = Opciones.modoDeUso.Traducir_a_Portugues;
|
||||||
if (arg.Contains("OCRaTexto"))
|
if (arg.Contains("OCRaTexto"))
|
||||||
Opciones.Instance.modo = Opciones.modoDeUso.OCRaTexto;
|
Opciones.Instance.modo = Opciones.modoDeUso.OCRaTexto;
|
||||||
|
if (arg.Contains("ClaudeWebSearch"))
|
||||||
|
Opciones.Instance.modo = Opciones.modoDeUso.ClaudeWebSearch;
|
||||||
if (arg.Contains("AutoCopy"))
|
if (arg.Contains("AutoCopy"))
|
||||||
Opciones.Instance.AutoCopy = true;
|
Opciones.Instance.AutoCopy = true;
|
||||||
if (arg.Contains("Menu"))
|
if (arg.Contains("Menu"))
|
||||||
|
|
388
gtpask.cs
388
gtpask.cs
|
@ -176,16 +176,43 @@ namespace GTPCorrgir
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
string detectedLanguageCode = _languageDetector.Detect(TextoACorregir);
|
// Si el texto es solo una URL, usar español como predeterminado
|
||||||
IdiomaDetectado = _languageMap.GetValueOrDefault(detectedLanguageCode, "Desconocido");
|
if (EsSoloURL(TextoACorregir))
|
||||||
|
{
|
||||||
|
IdiomaDetectado = _languageMap["es"]; // Español como predeterminado
|
||||||
|
Log.Log($"Texto es URL, usando idioma predeterminado: {IdiomaDetectado}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Si el texto es muy corto, usar español como predeterminado
|
||||||
|
if (string.IsNullOrWhiteSpace(TextoACorregir) || TextoACorregir.Trim().Length < 10)
|
||||||
|
{
|
||||||
|
IdiomaDetectado = _languageMap["es"]; // Español como predeterminado
|
||||||
|
Log.Log($"Texto muy corto, usando idioma predeterminado: {IdiomaDetectado}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string detectedLanguageCode = _languageDetector.Detect(TextoACorregir);
|
||||||
|
|
||||||
|
// Si no se detectó idioma o es desconocido, usar español como fallback
|
||||||
|
if (string.IsNullOrEmpty(detectedLanguageCode) || !_languageMap.ContainsKey(detectedLanguageCode))
|
||||||
|
{
|
||||||
|
IdiomaDetectado = _languageMap["es"]; // Español como fallback
|
||||||
|
Log.Log($"Idioma no detectado, usando fallback: {IdiomaDetectado}");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdiomaDetectado = _languageMap[detectedLanguageCode];
|
||||||
Log.Log($"Idioma detectado: {IdiomaDetectado}");
|
Log.Log($"Idioma detectado: {IdiomaDetectado}");
|
||||||
return IdiomaDetectado != "Desconocido";
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Log.Log($"Error al detectar idioma: {ex.Message}");
|
Log.Log($"Error al detectar idioma: {ex.Message}");
|
||||||
return false;
|
// En caso de error, usar español como fallback
|
||||||
|
IdiomaDetectado = _languageMap["es"];
|
||||||
|
Log.Log($"Error en detección, usando fallback: {IdiomaDetectado}");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,26 +254,37 @@ namespace GTPCorrgir
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string respuestaLLM;
|
string respuestaLLM;
|
||||||
|
string respuestaCompleta = "";
|
||||||
|
|
||||||
switch (Opciones.Instance.LLM)
|
// Si es el modo Claude Web Search, usar el método específico
|
||||||
|
if (Opciones.Instance.modo == Opciones.modoDeUso.ClaudeWebSearch)
|
||||||
{
|
{
|
||||||
case Opciones.LLM_a_Usar.OpenAI:
|
var resultadoWebSearch = await CallClaudeWebSearchApiCompleto(textoMarcado);
|
||||||
respuestaLLM = await CallOpenAiApi(textoMarcado);
|
respuestaLLM = resultadoWebSearch.texto;
|
||||||
break;
|
respuestaCompleta = resultadoWebSearch.respuestaCompleta;
|
||||||
case Opciones.LLM_a_Usar.Ollama:
|
}
|
||||||
respuestaLLM = await CallOllamaApi(textoMarcado);
|
else
|
||||||
break;
|
{
|
||||||
case Opciones.LLM_a_Usar.Groq:
|
switch (Opciones.Instance.LLM)
|
||||||
respuestaLLM = await CallGroqAiApi(textoMarcado);
|
{
|
||||||
break;
|
case Opciones.LLM_a_Usar.OpenAI:
|
||||||
case Opciones.LLM_a_Usar.Grok:
|
respuestaLLM = await CallOpenAiApi(textoMarcado);
|
||||||
respuestaLLM = await CallGrokApi(textoMarcado);
|
break;
|
||||||
break;
|
case Opciones.LLM_a_Usar.Ollama:
|
||||||
case Opciones.LLM_a_Usar.Claude:
|
respuestaLLM = await CallOllamaApi(textoMarcado);
|
||||||
respuestaLLM = await CallClaudeApi(textoMarcado);
|
break;
|
||||||
break;
|
case Opciones.LLM_a_Usar.Groq:
|
||||||
default:
|
respuestaLLM = await CallGroqAiApi(textoMarcado);
|
||||||
throw new ArgumentException("LLM no válido");
|
break;
|
||||||
|
case Opciones.LLM_a_Usar.Grok:
|
||||||
|
respuestaLLM = await CallGrokApi(textoMarcado);
|
||||||
|
break;
|
||||||
|
case Opciones.LLM_a_Usar.Claude:
|
||||||
|
respuestaLLM = await CallClaudeApi(textoMarcado);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentException("LLM no válido");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(respuestaLLM))
|
if (string.IsNullOrEmpty(respuestaLLM))
|
||||||
|
@ -254,7 +292,7 @@ namespace GTPCorrgir
|
||||||
throw new ApplicationException("No se recibió respuesta del LLM");
|
throw new ApplicationException("No se recibió respuesta del LLM");
|
||||||
}
|
}
|
||||||
|
|
||||||
ProcesarRespuestaLLM(respuestaLLM);
|
ProcesarRespuestaLLM(respuestaLLM, respuestaCompleta);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
@ -263,26 +301,141 @@ namespace GTPCorrgir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcesarRespuestaLLM(string respuestaLLM)
|
private void ProcesarRespuestaLLM(string respuestaLLM, string respuestaCompleta)
|
||||||
{
|
{
|
||||||
string respuestaExtraida = ExtraerValorUnicoJSON(respuestaLLM);
|
// Para Claude Web Search, la respuesta ya viene en formato de texto final
|
||||||
if (respuestaExtraida == null)
|
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)
|
||||||
|
{
|
||||||
|
// Si no se puede extraer JSON, usamos la respuesta completa
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
{
|
||||||
|
TextoCorregido += $"\n\n## Fuentes consultadas:\n{enlacesFormateados}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Para otros LLMs, procesamiento normal con JSON requerido
|
||||||
|
string respuestaExtraidaNormal = ExtraerValorUnicoJSON(respuestaLLM);
|
||||||
|
if (respuestaExtraidaNormal == null)
|
||||||
{
|
{
|
||||||
throw new ApplicationException("Error al extraer el texto corregido de la respuesta JSON");
|
throw new ApplicationException("Error al extraer el texto corregido de la respuesta JSON");
|
||||||
}
|
}
|
||||||
|
|
||||||
respuestaExtraida = System.Text.RegularExpressions.Regex.Replace(respuestaExtraida, @"\*\*(.*?)\*\*", "$1");
|
respuestaExtraidaNormal = System.Text.RegularExpressions.Regex.Replace(respuestaExtraidaNormal, @"\*\*(.*?)\*\*", "$1");
|
||||||
respuestaExtraida = _markdownProcessor.RemoveTechnicalTermMarkers_IgnoreCase(respuestaExtraida).Trim('"');
|
respuestaExtraidaNormal = _markdownProcessor.RemoveTechnicalTermMarkers_IgnoreCase(respuestaExtraidaNormal).Trim('"');
|
||||||
respuestaExtraida = _markdownProcessor.RemoveDoubleBrackets(respuestaExtraida);
|
respuestaExtraidaNormal = _markdownProcessor.RemoveDoubleBrackets(respuestaExtraidaNormal);
|
||||||
|
|
||||||
// Para el modo Pregunta-Respuesta, combinar pregunta original con la respuesta
|
// Para el modo Pregunta-Respuesta, combinar pregunta original con la respuesta
|
||||||
if (Opciones.Instance.modo == Opciones.modoDeUso.PreguntaRespuesta)
|
if (Opciones.Instance.modo == Opciones.modoDeUso.PreguntaRespuesta)
|
||||||
{
|
{
|
||||||
TextoCorregido = $"{TextoACorregir}\n{respuestaExtraida}";
|
TextoCorregido = $"{TextoACorregir}\n{respuestaExtraidaNormal}";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
TextoCorregido = respuestaExtraida;
|
TextoCorregido = respuestaExtraidaNormal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ExtraerEnlacesWebSearch(string respuestaCompleta)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(respuestaCompleta))
|
||||||
|
{
|
||||||
|
Log.Log("ExtraerEnlacesWebSearch: respuestaCompleta está vacía");
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Log($"ExtraerEnlacesWebSearch: Procesando respuesta de {respuestaCompleta.Length} caracteres");
|
||||||
|
|
||||||
|
var enlaces = new List<string>();
|
||||||
|
|
||||||
|
// Deserializar la respuesta completa para extraer los resultados de búsqueda
|
||||||
|
var data = JsonConvert.DeserializeObject<dynamic>(respuestaCompleta);
|
||||||
|
|
||||||
|
if (data?.content != null)
|
||||||
|
{
|
||||||
|
Log.Log($"ExtraerEnlacesWebSearch: Encontrados {data.content.Count} elementos de contenido");
|
||||||
|
|
||||||
|
foreach (var contentItem in data.content)
|
||||||
|
{
|
||||||
|
Log.Log($"ExtraerEnlacesWebSearch: Procesando elemento tipo: {contentItem.type}");
|
||||||
|
|
||||||
|
// Buscar elementos de tipo "web_search_tool_result"
|
||||||
|
if (contentItem.type == "web_search_tool_result" && contentItem.content != null)
|
||||||
|
{
|
||||||
|
Log.Log($"ExtraerEnlacesWebSearch: Encontrado web_search_tool_result con {contentItem.content.Count} resultados");
|
||||||
|
|
||||||
|
foreach (var searchResult in contentItem.content)
|
||||||
|
{
|
||||||
|
if (searchResult.type == "web_search_result")
|
||||||
|
{
|
||||||
|
string titulo = searchResult.title?.ToString() ?? "Sin título";
|
||||||
|
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}";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
enlaceFormateado = $"* [{titulo}]({url})";
|
||||||
|
}
|
||||||
|
|
||||||
|
enlaces.Add(enlaceFormateado);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Log.Log("ExtraerEnlacesWebSearch: No se encontró contenido en la respuesta");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eliminar duplicados y devolver como string
|
||||||
|
var enlacesUnicos = enlaces.Distinct().ToList();
|
||||||
|
|
||||||
|
Log.Log($"ExtraerEnlacesWebSearch: Extraídos {enlacesUnicos.Count} enlaces únicos");
|
||||||
|
|
||||||
|
if (enlacesUnicos.Any())
|
||||||
|
{
|
||||||
|
return string.Join("\n", enlacesUnicos);
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Log($"Error al extraer enlaces de web search: {ex.Message}");
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -336,6 +489,17 @@ namespace GTPCorrgir
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string CrearMensajeDeWebSearch()
|
||||||
|
{
|
||||||
|
return "You are a helpful assistant specialized in industrial automation and technical topics with web search capabilities. " +
|
||||||
|
"When users ask questions, you can use web search to find current and accurate information to provide comprehensive answers. " +
|
||||||
|
"If the question contains words in double brackets [[like this]], preserve them exactly as they appear in your response. " +
|
||||||
|
"Use the web search tool when you need to find recent information, current data, or specific technical details that might not be in your training data. " +
|
||||||
|
"Always provide well-researched, accurate answers and cite your sources when using web search results. " +
|
||||||
|
"Please write your answer in " + IdiomaDetectado + ". " +
|
||||||
|
"You can respond in JSON format like { \"Reply_text\": \"Your answer here\" } or provide a direct text response.";
|
||||||
|
}
|
||||||
|
|
||||||
private string CrearMensajeDeUsuario(string texto) =>
|
private string CrearMensajeDeUsuario(string texto) =>
|
||||||
Opciones.Instance.modo switch
|
Opciones.Instance.modo switch
|
||||||
{
|
{
|
||||||
|
@ -348,6 +512,9 @@ namespace GTPCorrgir
|
||||||
Opciones.modoDeUso.PreguntaRespuesta =>
|
Opciones.modoDeUso.PreguntaRespuesta =>
|
||||||
texto, // Para pregunta-respuesta, enviamos el texto directamente como la pregunta
|
texto, // Para pregunta-respuesta, enviamos el texto directamente como la pregunta
|
||||||
|
|
||||||
|
Opciones.modoDeUso.ClaudeWebSearch =>
|
||||||
|
texto, // Para web search, enviamos la pregunta directamente
|
||||||
|
|
||||||
Opciones.modoDeUso.Traducir_a_Ingles =>
|
Opciones.modoDeUso.Traducir_a_Ingles =>
|
||||||
$"Please check the following text for spelling errors and provide the corrected version tranlated to English. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
|
$"Please check the following text for spelling errors and provide the corrected version tranlated to English. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
|
||||||
|
|
||||||
|
@ -503,6 +670,103 @@ namespace GTPCorrgir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<string> CallClaudeWebSearchApi(string input)
|
||||||
|
{
|
||||||
|
var resultado = await CallClaudeWebSearchApiCompleto(input);
|
||||||
|
return resultado.texto;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<(string texto, string respuestaCompleta)> CallClaudeWebSearchApiCompleto(string input)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_httpClient.DefaultRequestHeaders.Clear();
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("x-api-key", _claudeApiKey);
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("anthropic-version", "2023-06-01");
|
||||||
|
_httpClient.DefaultRequestHeaders.Add("anthropic-beta", "web-search-2025-03-05");
|
||||||
|
|
||||||
|
var requestData = new
|
||||||
|
{
|
||||||
|
model = "claude-sonnet-4-20250514",
|
||||||
|
max_tokens = 4166,
|
||||||
|
temperature = 1,
|
||||||
|
system = CrearMensajeDeWebSearch(),
|
||||||
|
messages = new[]
|
||||||
|
{
|
||||||
|
new { role = "user", content = input } // Para web search, enviamos la pregunta directamente
|
||||||
|
},
|
||||||
|
tools = new[]
|
||||||
|
{
|
||||||
|
new
|
||||||
|
{
|
||||||
|
name = "web_search",
|
||||||
|
type = "web_search_20250305"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
thinking = new
|
||||||
|
{
|
||||||
|
type = "enabled",
|
||||||
|
budget_tokens = 4123
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var content = new StringContent(
|
||||||
|
JsonConvert.SerializeObject(requestData),
|
||||||
|
Encoding.UTF8,
|
||||||
|
"application/json"
|
||||||
|
);
|
||||||
|
|
||||||
|
Log.Log($"Enviando solicitud a https://api.anthropic.com/v1/messages");
|
||||||
|
Log.Log($"Datos de solicitud: {JsonConvert.SerializeObject(requestData)}");
|
||||||
|
|
||||||
|
using var response = await _httpClient.PostAsync("https://api.anthropic.com/v1/messages", content);
|
||||||
|
|
||||||
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
// Para Claude Web Search, filtrar campos encrypted_content largos del log
|
||||||
|
string logContent = responseContent;
|
||||||
|
if (responseContent.Contains("encrypted_content") || responseContent.Contains("signature"))
|
||||||
|
{
|
||||||
|
logContent = FiltrarEncryptedContentParaLog(responseContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Log($"Respuesta recibida: {logContent}");
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
throw new HttpRequestException(
|
||||||
|
$"Error en la solicitud HTTP: {response.StatusCode} - {responseContent}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = JsonConvert.DeserializeObject<dynamic>(responseContent);
|
||||||
|
|
||||||
|
if (data.content != null && data.content.Count > 0)
|
||||||
|
{
|
||||||
|
// Buscar el elemento con type = "text" en el array de content
|
||||||
|
foreach (var contentItem in data.content)
|
||||||
|
{
|
||||||
|
if (contentItem.type == "text")
|
||||||
|
{
|
||||||
|
return (contentItem.text.ToString(), responseContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Si no encuentra un elemento con type="text", usar el primer elemento como fallback
|
||||||
|
if (data.content[0].text != null)
|
||||||
|
{
|
||||||
|
return (data.content[0].text.ToString(), responseContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ApplicationException("No se encontró contenido en la respuesta de Claude Web Search");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Log($"Error en llamada a Claude Web Search API: {ex.Message}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<string> EnviarSolicitudLLM(string endpoint, object requestData)
|
private async Task<string> EnviarSolicitudLLM(string endpoint, object requestData)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -519,7 +783,15 @@ namespace GTPCorrgir
|
||||||
using var response = await _httpClient.PostAsync(endpoint, content);
|
using var response = await _httpClient.PostAsync(endpoint, content);
|
||||||
|
|
||||||
var responseContent = await response.Content.ReadAsStringAsync();
|
var responseContent = await response.Content.ReadAsStringAsync();
|
||||||
Log.Log($"Respuesta recibida: {responseContent}");
|
|
||||||
|
// Para Claude Web Search, filtrar campos encrypted_content largos del log
|
||||||
|
string logContent = responseContent;
|
||||||
|
if (endpoint.Contains("anthropic") && (responseContent.Contains("encrypted_content") || responseContent.Contains("signature")))
|
||||||
|
{
|
||||||
|
logContent = FiltrarEncryptedContentParaLog(responseContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.Log($"Respuesta recibida: {logContent}");
|
||||||
|
|
||||||
if (!response.IsSuccessStatusCode)
|
if (!response.IsSuccessStatusCode)
|
||||||
{
|
{
|
||||||
|
@ -575,6 +847,58 @@ namespace GTPCorrgir
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string FiltrarEncryptedContentParaLog(string responseContent)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Usar regex para reemplazar campos encrypted_content largos con un placeholder
|
||||||
|
var regex = new System.Text.RegularExpressions.Regex(
|
||||||
|
@"""encrypted_content""\s*:\s*""[^""]{50,}""",
|
||||||
|
System.Text.RegularExpressions.RegexOptions.IgnoreCase
|
||||||
|
);
|
||||||
|
|
||||||
|
string filtered = regex.Replace(responseContent, @"""encrypted_content"": ""[CONTENT_FILTERED_FOR_LOG]""");
|
||||||
|
|
||||||
|
// También filtrar encrypted_index si es muy largo
|
||||||
|
var regexIndex = new System.Text.RegularExpressions.Regex(
|
||||||
|
@"""encrypted_index""\s*:\s*""[^""]{50,}""",
|
||||||
|
System.Text.RegularExpressions.RegexOptions.IgnoreCase
|
||||||
|
);
|
||||||
|
|
||||||
|
filtered = regexIndex.Replace(filtered, @"""encrypted_index"": ""[INDEX_FILTERED_FOR_LOG]""");
|
||||||
|
|
||||||
|
// Filtrar signature si es muy largo
|
||||||
|
var regexSignature = new System.Text.RegularExpressions.Regex(
|
||||||
|
@"""signature""\s*:\s*""[^""]{20,}""",
|
||||||
|
System.Text.RegularExpressions.RegexOptions.IgnoreCase
|
||||||
|
);
|
||||||
|
|
||||||
|
filtered = regexSignature.Replace(filtered, @"""signature"": ""[SIGNATURE_FILTERED_FOR_LOG]""");
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Log.Log($"Error al filtrar encrypted_content: {ex.Message}");
|
||||||
|
// Si hay error al filtrar, devolver contenido original
|
||||||
|
return responseContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool EsSoloURL(string texto)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(texto))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
Dispose(true);
|
Dispose(true);
|
||||||
|
|
Loading…
Reference in New Issue