Compare commits
6 Commits
28e7df02ae
...
25810b7e6a
Author | SHA1 | Date |
---|---|---|
|
25810b7e6a | |
|
a8aca9a82a | |
|
a48e64f372 | |
|
6d8f70d15b | |
|
bd55330739 | |
|
e9102f7e1e |
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md
|
||||
"name": ".NET Core Launch (console)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/bin/Debug/net8.0-windows/GTPCorrgir.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}",
|
||||
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/GTPCorrgir.sln",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/GTPCorrgir.sln",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/GTPCorrgir.sln"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
130
App.xaml.cs
130
App.xaml.cs
|
@ -20,16 +20,15 @@ namespace GTPCorrgir
|
|||
bool CorreccionFinalizada = false;
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
private notificacion notificationWindow;
|
||||
private readonly int TimeoutSeconds = 60; // Timeout máximo para esperar la respuesta
|
||||
private readonly int TimeoutSeconds = 60;
|
||||
private CancellationTokenSource _cancellationTokenSource;
|
||||
private Window _mainWindow;
|
||||
|
||||
KeyboardHelper pasteLogic = new KeyboardHelper();
|
||||
|
||||
private Window _mainWindow;
|
||||
|
||||
protected override async void OnStartup(StartupEventArgs e)
|
||||
{
|
||||
ShutdownMode = ShutdownMode.OnExplicitShutdown; // Añade esta línea
|
||||
ShutdownMode = ShutdownMode.OnExplicitShutdown;
|
||||
base.OnStartup(e);
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
|
||||
|
@ -81,9 +80,12 @@ namespace GTPCorrgir
|
|||
|
||||
case Opciones.modoDeUso.Corregir:
|
||||
case Opciones.modoDeUso.Ortografia:
|
||||
case Opciones.modoDeUso.PreguntaRespuesta:
|
||||
case Opciones.modoDeUso.ClaudeWebSearch:
|
||||
case Opciones.modoDeUso.Traducir_a_Espanol:
|
||||
case Opciones.modoDeUso.Traducir_a_Ingles:
|
||||
case Opciones.modoDeUso.Traducir_a_Italiano:
|
||||
case Opciones.modoDeUso.Traducir_a_Portugues:
|
||||
await HandleTextCorrection();
|
||||
return;
|
||||
}
|
||||
|
@ -107,6 +109,9 @@ namespace GTPCorrgir
|
|||
}
|
||||
|
||||
GTP.TextoACorregir = await ClipboardHelper.GetText();
|
||||
stopwatch.Start();
|
||||
ShowCustomNotification("Espera", $"Procesando texto con {Opciones.Instance.nombreDeLLM()}...");
|
||||
IniciarCronometro();
|
||||
await ProcessCorreccionWithTimeout();
|
||||
}
|
||||
|
||||
|
@ -151,8 +156,9 @@ namespace GTPCorrgir
|
|||
$"Corrección en: {Math.Round(stopwatch.ElapsedMilliseconds / 1000.0, 1)} s");
|
||||
|
||||
if (Opciones.Instance.modo == Opciones.modoDeUso.Corregir || Opciones.Instance.modo == Opciones.modoDeUso.Ortografia ||
|
||||
Opciones.Instance.modo == Opciones.modoDeUso.PreguntaRespuesta || Opciones.Instance.modo == Opciones.modoDeUso.ClaudeWebSearch ||
|
||||
Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Espanol || Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Ingles ||
|
||||
Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Italiano)
|
||||
Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Italiano || Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Portugues)
|
||||
{
|
||||
if (Opciones.Instance.FuncionesOpcionales == Opciones.funcionesOpcionales.MostrarPopUp)
|
||||
{
|
||||
|
@ -177,8 +183,28 @@ namespace GTPCorrgir
|
|||
catch (Exception ex)
|
||||
{
|
||||
GTP.Log.Log($"Error en el procesamiento final: {ex.Message}");
|
||||
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
|
||||
{
|
||||
GTP.Log.Log("Cerrando aplicación");
|
||||
|
@ -189,19 +215,43 @@ namespace GTPCorrgir
|
|||
catch (OperationCanceledException)
|
||||
{
|
||||
GTP.Log.Log("Operación cancelada por timeout");
|
||||
await Dispatcher.InvokeAsync(() =>
|
||||
await Dispatcher.InvokeAsync(async () =>
|
||||
{
|
||||
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 () =>
|
||||
{
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -210,6 +260,10 @@ namespace GTPCorrgir
|
|||
{
|
||||
_cancellationTokenSource?.Cancel();
|
||||
_cancellationTokenSource?.Dispose();
|
||||
|
||||
// Limpiar recursos de PaddleOCR
|
||||
PaddleOCRManager.Cleanup();
|
||||
|
||||
base.OnExit(e);
|
||||
}
|
||||
|
||||
|
@ -217,7 +271,7 @@ namespace GTPCorrgir
|
|||
{
|
||||
try
|
||||
{
|
||||
GTP.Log.Log($"Mostrando notificación: {title} - {message}");
|
||||
// GTP.Log.Log($"Mostrando notificación: {title} - {message}"); // Comentado para limpiar logs
|
||||
if (notificationWindow == null)
|
||||
{
|
||||
notificationWindow = new notificacion();
|
||||
|
@ -260,6 +314,66 @@ namespace GTPCorrgir
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,14 @@
|
|||
<Window x:Class="GTPCorrgir.ContextMenuWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:GTPCorrgir" Title="Menu"
|
||||
WindowStyle="None" AllowsTransparency="True" Background="Transparent" ShowInTaskbar="False" Topmost="True"
|
||||
SizeToContent="WidthAndHeight" KeyDown="Window_KeyDown" Deactivated="Window_Deactivated">
|
||||
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:GTPCorrgir"
|
||||
xmlns:av="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="av"
|
||||
x:Class="GTPCorrgir.ContextMenuWindow" Title="Menu" WindowStyle="None" AllowsTransparency="True"
|
||||
Background="Transparent" ShowInTaskbar="False" Topmost="True" SizeToContent="WidthAndHeight"
|
||||
KeyDown="Window_KeyDown" Deactivated="Window_Deactivated" av:DesignHeight="700">
|
||||
|
||||
<Window.Resources>
|
||||
<local:NullToBooleanConverter x:Key="NullToBooleanConverter" />
|
||||
<Style x:Key="CloseButtonStyle" TargetType="Button">
|
||||
<Style x:Key="CloseButtonStyle" TargetType="{x:Type Button}">
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Foreground" Value="#757575" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
|
@ -13,7 +16,7 @@
|
|||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="Button">
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Grid Background="Transparent">
|
||||
<Border x:Name="border" Background="{TemplateBinding Background}"
|
||||
BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2"
|
||||
|
@ -38,6 +41,27 @@
|
|||
</Window.Resources>
|
||||
|
||||
<Border Background="#F5F5F5" BorderBrush="#CCCCCC" BorderThickness="1" CornerRadius="3" Margin="5">
|
||||
<Border.ContextMenu>
|
||||
<ContextMenu>
|
||||
<MenuItem Header="Abrir Log" Click="OpenLog_Click">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock Text="📄" FontSize="14" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<MenuItem Header="Limpiar Log" Click="ClearLog_Click">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock Text="🗑️" FontSize="14" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
<Separator />
|
||||
<MenuItem Header="Acerca de" Click="About_Click">
|
||||
<MenuItem.Icon>
|
||||
<TextBlock Text="ℹ️" FontSize="14" />
|
||||
</MenuItem.Icon>
|
||||
</MenuItem>
|
||||
</ContextMenu>
|
||||
</Border.ContextMenu>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
|
@ -60,12 +84,12 @@
|
|||
<!-- Contenido principal -->
|
||||
<Grid Grid.Row="1" Margin="10,0,10,10">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<ListBox x:Name="ModeListBox" Height="200" Grid.Row="0" Margin="0,0,0,10">
|
||||
<ListBox x:Name="ModeListBox" Grid.Row="0" Margin="0,0,0,10">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding DisplayName}" Padding="5,3" />
|
||||
|
@ -75,7 +99,7 @@
|
|||
|
||||
<StackPanel Grid.Row="1" Margin="0,0,0,10">
|
||||
<TextBlock Text="Modelo LLM:" FontWeight="Bold" Margin="0,0,0,5" />
|
||||
<ListBox x:Name="LLMListBox" Height="120">
|
||||
<ListBox x:Name="LLMListBox" Height="205">
|
||||
<ListBox.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding DisplayName}" Padding="5,3" />
|
||||
|
@ -84,8 +108,8 @@
|
|||
</ListBox>
|
||||
</StackPanel>
|
||||
|
||||
<Button Grid.Row="2" Content="Aceptar" Height="30" Width="80" HorizontalAlignment="Right"
|
||||
Click="AcceptButton_Click" Margin="0,10,0,0">
|
||||
<Button Grid.Row="2" Content="Aceptar" Height="30" Width="80" HorizontalAlignment="Center"
|
||||
Click="AcceptButton_Click" Margin="0,0,0,0">
|
||||
<!-- ... resto del estilo del botón ... -->
|
||||
</Button>
|
||||
</Grid>
|
||||
|
|
|
@ -5,6 +5,9 @@ using System.Windows;
|
|||
using System.Windows.Forms;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Controls;
|
||||
using System.IO;
|
||||
using System.Diagnostics;
|
||||
using KeyEventArgs = System.Windows.Input.KeyEventArgs;
|
||||
using MessageBox = System.Windows.MessageBox;
|
||||
|
||||
|
@ -43,9 +46,12 @@ namespace GTPCorrgir
|
|||
new MenuOption { DisplayName = "Corregir texto", Value = Opciones.modoDeUso.Corregir },
|
||||
new MenuOption { DisplayName = "Revisar ortografía", Value = Opciones.modoDeUso.Ortografia },
|
||||
new MenuOption { DisplayName = "Chat", Value = Opciones.modoDeUso.Chat },
|
||||
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 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 portugués", Value = Opciones.modoDeUso.Traducir_a_Portugues },
|
||||
new MenuOption { DisplayName = "OCR a texto", Value = Opciones.modoDeUso.OCRaTexto }
|
||||
};
|
||||
|
||||
|
@ -54,7 +60,8 @@ namespace GTPCorrgir
|
|||
new MenuOption { DisplayName = "OpenAI", Value = Opciones.LLM_a_Usar.OpenAI },
|
||||
new MenuOption { DisplayName = "Ollama", Value = Opciones.LLM_a_Usar.Ollama },
|
||||
new MenuOption { DisplayName = "Groq", Value = Opciones.LLM_a_Usar.Groq },
|
||||
new MenuOption { DisplayName = "Grok", Value = Opciones.LLM_a_Usar.Grok }
|
||||
new MenuOption { DisplayName = "Grok", Value = Opciones.LLM_a_Usar.Grok },
|
||||
new MenuOption { DisplayName = "Claude", Value = Opciones.LLM_a_Usar.Claude }
|
||||
};
|
||||
|
||||
InitializeComponent();
|
||||
|
@ -122,14 +129,16 @@ namespace GTPCorrgir
|
|||
|
||||
private void CloseButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
this.Close();
|
||||
// Cerrar toda la aplicación cuando se hace clic en el botón de cerrar
|
||||
System.Windows.Application.Current.Shutdown();
|
||||
}
|
||||
|
||||
private void Window_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Escape)
|
||||
{
|
||||
this.Close();
|
||||
// Cerrar toda la aplicación cuando se presiona Esc
|
||||
System.Windows.Application.Current.Shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,6 +160,7 @@ namespace GTPCorrgir
|
|||
UpdateOptionsAndClose(selectedMode, selectedLLM);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateOptionsAndClose(MenuOption selectedMode, MenuOption selectedLLM)
|
||||
{
|
||||
optionsSelected = true;
|
||||
|
@ -158,6 +168,12 @@ namespace GTPCorrgir
|
|||
var modeValue = (Opciones.modoDeUso)selectedMode.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.LLM = llmValue;
|
||||
|
||||
|
@ -166,5 +182,198 @@ namespace GTPCorrgir
|
|||
// Eliminar DialogResult
|
||||
this.Close();
|
||||
}
|
||||
|
||||
// Manejadores del menú contextual
|
||||
private void OpenLog_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logfile.log");
|
||||
|
||||
if (File.Exists(logFilePath))
|
||||
{
|
||||
// Intentar abrir con el programa predeterminado
|
||||
Process.Start(new ProcessStartInfo
|
||||
{
|
||||
FileName = logFilePath,
|
||||
UseShellExecute = true
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("El archivo de log no existe aún.", "Información",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error al abrir el archivo de log: {ex.Message}", "Error",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void ClearLog_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = MessageBox.Show(
|
||||
"¿Está seguro de que desea limpiar el archivo de log?\nEsta acción no se puede deshacer.",
|
||||
"Confirmar limpieza de log",
|
||||
MessageBoxButton.YesNo,
|
||||
MessageBoxImage.Warning);
|
||||
|
||||
if (result == MessageBoxResult.Yes)
|
||||
{
|
||||
string logFilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logfile.log");
|
||||
|
||||
if (File.Exists(logFilePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
// Obtener todos los listeners de tipo TextWriterTraceListener
|
||||
var textWriterListeners = new List<System.Diagnostics.TextWriterTraceListener>();
|
||||
|
||||
foreach (System.Diagnostics.TraceListener listener in System.Diagnostics.Trace.Listeners)
|
||||
{
|
||||
if (listener is System.Diagnostics.TextWriterTraceListener textListener)
|
||||
{
|
||||
textWriterListeners.Add(textListener);
|
||||
}
|
||||
}
|
||||
|
||||
// Cerrar y remover temporalmente los listeners
|
||||
foreach (var listener in textWriterListeners)
|
||||
{
|
||||
listener.Flush();
|
||||
listener.Close();
|
||||
System.Diagnostics.Trace.Listeners.Remove(listener);
|
||||
}
|
||||
|
||||
// Esperar un momento para asegurar que el archivo se libere
|
||||
System.Threading.Thread.Sleep(100);
|
||||
|
||||
// Limpiar el archivo
|
||||
File.WriteAllText(logFilePath, string.Empty);
|
||||
|
||||
// Recrear los listeners
|
||||
foreach (var listener in textWriterListeners)
|
||||
{
|
||||
var newListener = new System.Diagnostics.TextWriterTraceListener(logFilePath);
|
||||
System.Diagnostics.Trace.Listeners.Add(newListener);
|
||||
}
|
||||
|
||||
// Reactivar AutoFlush
|
||||
System.Diagnostics.Trace.AutoFlush = true;
|
||||
|
||||
MessageBox.Show("El archivo de log ha sido limpiado correctamente.", "Éxito",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
catch (UnauthorizedAccessException)
|
||||
{
|
||||
MessageBox.Show(
|
||||
"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.",
|
||||
"Acceso denegado",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
catch (IOException ioEx)
|
||||
{
|
||||
MessageBox.Show(
|
||||
$"Error de E/S al acceder al archivo de log:\n{ioEx.Message}\n\n" +
|
||||
"El archivo puede estar siendo usado por otro proceso.",
|
||||
"Error de archivo",
|
||||
MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
MessageBox.Show("El archivo de log no existe.", "Información",
|
||||
MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
MessageBox.Show($"Error al limpiar el archivo de log: {ex.Message}", "Error",
|
||||
MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
|
||||
private void About_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var aboutDialog = new Window
|
||||
{
|
||||
Title = "Acerca de GTPCorregir",
|
||||
Width = 400,
|
||||
Height = 300,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
Owner = this,
|
||||
ResizeMode = ResizeMode.NoResize,
|
||||
ShowInTaskbar = false,
|
||||
WindowStyle = WindowStyle.ToolWindow
|
||||
};
|
||||
|
||||
var stackPanel = new StackPanel { Margin = new Thickness(20) };
|
||||
|
||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||
{
|
||||
Text = "GTPCorregir",
|
||||
FontSize = 18,
|
||||
FontWeight = FontWeights.Bold,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
||||
Margin = new Thickness(0, 0, 0, 10)
|
||||
});
|
||||
|
||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||
{
|
||||
Text = "Herramienta de corrección de texto con IA",
|
||||
FontSize = 12,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
||||
Margin = new Thickness(0, 0, 0, 15)
|
||||
});
|
||||
|
||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||
{
|
||||
Text = "• Corrección de ortografía y gramática",
|
||||
Margin = new Thickness(0, 2, 0, 2)
|
||||
});
|
||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||
{
|
||||
Text = "• Traducción a múltiples idiomas",
|
||||
Margin = new Thickness(0, 2, 0, 2)
|
||||
});
|
||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||
{
|
||||
Text = "• OCR con PaddleOCR",
|
||||
Margin = new Thickness(0, 2, 0, 2)
|
||||
});
|
||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||
{
|
||||
Text = "• Soporte para múltiples LLMs",
|
||||
Margin = new Thickness(0, 2, 0, 2)
|
||||
});
|
||||
|
||||
stackPanel.Children.Add(new System.Windows.Controls.TextBlock
|
||||
{
|
||||
Text = $"Versión: {System.Reflection.Assembly.GetExecutingAssembly().GetName().Version}",
|
||||
FontSize = 10,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
||||
Margin = new Thickness(0, 20, 0, 0)
|
||||
});
|
||||
|
||||
var closeButton = new System.Windows.Controls.Button
|
||||
{
|
||||
Content = "Cerrar",
|
||||
Width = 80,
|
||||
Height = 25,
|
||||
HorizontalAlignment = System.Windows.HorizontalAlignment.Center,
|
||||
Margin = new Thickness(0, 15, 0, 0)
|
||||
};
|
||||
closeButton.Click += (s, args) => aboutDialog.Close();
|
||||
stackPanel.Children.Add(closeButton);
|
||||
|
||||
aboutDialog.Content = stackPanel;
|
||||
aboutDialog.ShowDialog();
|
||||
}
|
||||
}
|
||||
}
|
Binary file not shown.
|
@ -0,0 +1,623 @@
|
|||
<?xml version="1.0"?>
|
||||
<doc>
|
||||
<assembly>
|
||||
<name>PaddleOCRSharp</name>
|
||||
</assembly>
|
||||
<members>
|
||||
<member name="T:PaddleOCRSharp.EngineBase">
|
||||
<summary>
|
||||
Base class for engine objects
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.EngineBase.PaddleOCRdllPath">
|
||||
<summary>
|
||||
Custom loading path for PaddleOCR.dll, default is empty. If specified, it needs to be assigned before engine instantiation.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.EngineBase.#ctor">
|
||||
<summary>
|
||||
Initialization
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.EngineBase.GetDllDirectory">
|
||||
<summary>
|
||||
Get the current path of the program
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.EngineBase.GetRootDirectory">
|
||||
<summary>
|
||||
Get the current path of the program
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.EngineBase.ImageToBytes(System.Drawing.Image)">
|
||||
<summary>
|
||||
Convert Image to Byte[]
|
||||
</summary>
|
||||
<param name="image"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.EngineBase.Dispose">
|
||||
<summary>
|
||||
Release memory
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.EngineBase.GetLastError">
|
||||
<summary>
|
||||
Get underlying error information
|
||||
</summary>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.JsonHelper">
|
||||
<summary>
|
||||
Json helper class
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.JsonHelper.DeserializeObject``1(System.String)">
|
||||
<summary>
|
||||
Json deserialization
|
||||
</summary>
|
||||
<typeparam name="T"></typeparam>
|
||||
<param name="json"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.OCRModelConfig">
|
||||
<summary>
|
||||
Model configuration object
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRModelConfig.det_infer">
|
||||
<summary>
|
||||
det_infer model path
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRModelConfig.cls_infer">
|
||||
<summary>
|
||||
cls_infer model path
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRModelConfig.rec_infer">
|
||||
<summary>
|
||||
rec_infer model path
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRModelConfig.keys">
|
||||
<summary>
|
||||
Full path of ppocr_keys.txt file
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.StructureModelConfig">
|
||||
<summary>
|
||||
Table model configuration object
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.StructureModelConfig.table_model_dir">
|
||||
<summary>
|
||||
table_model_dir model path
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.StructureModelConfig.table_char_dict_path">
|
||||
<summary>
|
||||
Table recognition dictionary
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.OCRParameter">
|
||||
<summary>
|
||||
OCR recognition parameters
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.use_gpu">
|
||||
<summary>
|
||||
Whether to use GPU; default false
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.gpu_id">
|
||||
<summary>
|
||||
GPU id, effective when using GPU; default 0
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.gpu_mem">
|
||||
<summary>
|
||||
Requested GPU memory; default 4000
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.cpu_math_library_num_threads">
|
||||
<summary>
|
||||
Number of threads for CPU prediction. When the machine has sufficient cores, the higher this value, the faster the prediction; default 10
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.enable_mkldnn">
|
||||
<summary>
|
||||
Whether to use mkldnn library; default true
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.det">
|
||||
<summary>
|
||||
Whether to perform text detection; default true
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.rec">
|
||||
<summary>
|
||||
Whether to perform text recognition; default true
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.cls">
|
||||
<summary>
|
||||
Whether to perform text orientation classification; default false
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.max_side_len">
|
||||
<summary>
|
||||
When the input image length and width are greater than 960, the image is scaled proportionally so that the longest side is 960; default 960
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.det_db_thresh">
|
||||
<summary>
|
||||
Used to filter the binary image predicted by DB. Setting to 0.-0.3 has no significant effect on results; default 0.3
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.det_db_box_thresh">
|
||||
<summary>
|
||||
DB post-processing threshold for filtering boxes. If detection has missing boxes, this can be reduced accordingly; default 0.5
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.det_db_unclip_ratio">
|
||||
<summary>
|
||||
Represents the tightness of the text box. Smaller values mean the text box is closer to the text; default 1.6
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.use_dilation">
|
||||
<summary>
|
||||
Whether to use dilation on the output map, default false
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.det_db_score_mode">
|
||||
<summary>
|
||||
true: use polygon box to calculate bbox; false: use rectangle box. Rectangle calculation is faster, polygon boxes are more accurate for curved text areas.
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.visualize">
|
||||
<summary>
|
||||
Whether to visualize the results. If true, the prediction results will be saved as an ocr_vis.png file in the current directory. Default false
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.use_angle_cls">
|
||||
<summary>
|
||||
Whether to use direction classifier, default false
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.cls_thresh">
|
||||
<summary>
|
||||
Score threshold for direction classifier, default 0.9
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.cls_batch_num">
|
||||
<summary>
|
||||
Direction classifier batchsize, default 1
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.rec_batch_num">
|
||||
<summary>
|
||||
Recognition model batchsize, default 6
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.rec_img_h">
|
||||
<summary>
|
||||
Recognition model input image height, default 48
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.rec_img_w">
|
||||
<summary>
|
||||
Recognition model input image width, default 320
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.show_img_vis">
|
||||
<summary>
|
||||
Whether to display prediction results, default false
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRParameter.use_tensorrt">
|
||||
<summary>
|
||||
When using GPU prediction, whether to enable tensorrt, default false
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.ModifyParameter">
|
||||
<summary>
|
||||
OCR modifiable parameters
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.ModifyParameter.m_det">
|
||||
<summary>
|
||||
Dynamically modify whether to detect. When OCRParameter.det=true, m_det can dynamically turn off the det parameter
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.ModifyParameter.m_rec">
|
||||
<summary>
|
||||
Dynamically modify whether to recognize. When OCRParameter.rec=true, m_rec can dynamically turn off the rec parameter
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.ModifyParameter.m_max_side_len">
|
||||
<summary>
|
||||
When the input image length and width are greater than 960, the image is scaled proportionally so that the longest side is 960; default 960
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.ModifyParameter.m_det_db_thresh">
|
||||
<summary>
|
||||
Used to filter the binary image predicted by DB. Setting to 0.-0.3 has no significant effect on results; default 0.3. Effective when m_det=true
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.ModifyParameter.m_det_db_box_thresh">
|
||||
<summary>
|
||||
DB post-processing threshold for filtering boxes. If detection has missing boxes, this can be reduced accordingly; default 0.5. Effective when m_det=true
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.ModifyParameter.m_det_db_unclip_ratio">
|
||||
<summary>
|
||||
Represents the tightness of the text box. Smaller values mean the text box is closer to the text; default 1.6. Effective when m_det=true
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.OCRResult">
|
||||
<summary>
|
||||
OCR recognition result
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRResult.TextBlocks">
|
||||
<summary>
|
||||
List of text blocks
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRResult.Text">
|
||||
<summary>
|
||||
Recognition result text
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRResult.JsonText">
|
||||
<summary>
|
||||
Recognition result text in Json format
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.OCRResult.ToString">
|
||||
<summary>
|
||||
Return string format
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.TextBlock">
|
||||
<summary>
|
||||
Recognized text block
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.TextBlock.BoxPoints">
|
||||
<summary>
|
||||
List of coordinate vertices around the text block
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.TextBlock.Text">
|
||||
<summary>
|
||||
Text block content
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.TextBlock.Score">
|
||||
<summary>
|
||||
Text recognition confidence
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.TextBlock.cls_score">
|
||||
<summary>
|
||||
Angle classification confidence
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.TextBlock.cls_label">
|
||||
<summary>
|
||||
Angle classification label
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.TextBlock.ToString">
|
||||
<summary>
|
||||
Return string format
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.OCRPoint">
|
||||
<summary>
|
||||
Point object
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRPoint.X">
|
||||
<summary>
|
||||
X coordinate, in pixels
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRPoint.Y">
|
||||
<summary>
|
||||
Y coordinate, in pixels
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.OCRPoint.#ctor">
|
||||
<summary>
|
||||
Default constructor
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.OCRPoint.#ctor(System.Int32,System.Int32)">
|
||||
<summary>
|
||||
Constructor
|
||||
</summary>
|
||||
<param name="x"></param>
|
||||
<param name="y"></param>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.OCRPoint.ToString">
|
||||
<summary>
|
||||
Return string format
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.OCRStructureResult">
|
||||
<summary>
|
||||
OCR structured recognition result
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.OCRStructureResult.#ctor">
|
||||
<summary>
|
||||
Table recognition result
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRStructureResult.RowCount">
|
||||
<summary>
|
||||
Number of rows
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRStructureResult.ColCount">
|
||||
<summary>
|
||||
Number of columns
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRStructureResult.Cells">
|
||||
<summary>
|
||||
List of cells
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.OCRStructureResult.TextBlocks">
|
||||
<summary>
|
||||
List of text blocks
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.StructureCells">
|
||||
<summary>
|
||||
Cell
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.StructureCells.#ctor">
|
||||
<summary>
|
||||
Cell constructor
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.StructureCells.Row">
|
||||
<summary>
|
||||
Row number
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.StructureCells.Col">
|
||||
<summary>
|
||||
Column number
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.StructureCells.TextBlocks">
|
||||
<summary>
|
||||
Text blocks
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.StructureCells.Text">
|
||||
<summary>
|
||||
Recognized text
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.PaddleOCREngine">
|
||||
<summary>
|
||||
PaddleOCR text recognition engine object
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.#ctor">
|
||||
<summary>
|
||||
Initialize OCR engine object with default parameters
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.#ctor(PaddleOCRSharp.OCRModelConfig)">
|
||||
<summary>
|
||||
Initialize OCR engine object with default parameters
|
||||
</summary>
|
||||
<param name="config">Model configuration object, if null then default values are used</param>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.#ctor(PaddleOCRSharp.OCRModelConfig,PaddleOCRSharp.OCRParameter)">
|
||||
<summary>
|
||||
PaddleOCR recognition engine object initialization
|
||||
</summary>
|
||||
<param name="config">Model configuration object, if null then default values are used</param>
|
||||
<param name="parameter">Recognition parameters, if null then default values are used</param>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.#ctor(PaddleOCRSharp.OCRModelConfig,System.String)">
|
||||
<summary>
|
||||
PaddleOCR recognition engine object initialization
|
||||
</summary>
|
||||
<param name="config">Model configuration object, if null then default values are used</param>
|
||||
<param name="parameterjson">Recognition parameters in json string format</param>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.GetDefaultConfig(System.String)">
|
||||
<summary>
|
||||
Get default configuration
|
||||
</summary>
|
||||
<param name="rootpath">Root directory</param>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectText(System.String)">
|
||||
<summary>
|
||||
Perform text recognition on image file
|
||||
</summary>
|
||||
<param name="imagefile">Image file</param>
|
||||
<returns>OCR recognition result</returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectText(System.Drawing.Bitmap)">
|
||||
<summary>
|
||||
Perform text recognition on image object
|
||||
</summary>
|
||||
<param name="image">Image</param>
|
||||
<returns>OCR recognition result</returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectText(System.Byte[])">
|
||||
<summary>
|
||||
Text recognition
|
||||
</summary>
|
||||
<param name="imagebyte">Image memory stream</param>
|
||||
<returns>OCR recognition result</returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectTextBase64(System.String)">
|
||||
<summary>
|
||||
Text recognition
|
||||
</summary>
|
||||
<param name="imagebase64">Image base64</param>
|
||||
<returns>OCR recognition result</returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectText(System.IntPtr,System.Int32,System.Int32,System.Int32)">
|
||||
<summary>
|
||||
Text recognition
|
||||
</summary>
|
||||
<param name="imgPtr">Image memory address</param>
|
||||
<param name="nWidth">Image width</param>
|
||||
<param name="nHeight">Image height</param>
|
||||
<param name="nChannel">Image channel, usually 3 or 1</param>
|
||||
<returns>OCR recognition result</returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.ConvertResult(System.IntPtr)">
|
||||
<summary>
|
||||
Result parsing
|
||||
</summary>
|
||||
<param name="ptrResult"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.DetectStructure(System.Drawing.Bitmap)">
|
||||
<summary>
|
||||
Structured text recognition
|
||||
</summary>
|
||||
<param name="image">Image</param>
|
||||
<returns>Table recognition result</returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.getzeroindexs(System.Int32[],System.Int32)">
|
||||
<summary>
|
||||
Calculate table splitting
|
||||
</summary>
|
||||
<param name="pixellist"></param>
|
||||
<param name="thresholdtozero"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.ModifyParameter(PaddleOCRSharp.ModifyParameter)">
|
||||
<summary>
|
||||
Dynamically modify parameters after initialization
|
||||
</summary>
|
||||
<param name="parameter">Modifiable parameter object</param>
|
||||
<returns>Whether successful, calling before initialization will result in failure</returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.EnableDetUseRect(System.Boolean)">
|
||||
<summary>
|
||||
Whether to enable rectangular processing of detection results. Single characters are easily detected as diamond shapes, processing them as rectangles can improve recognition accuracy. Only applicable for horizontal text.
|
||||
</summary>
|
||||
<param name="enable"></param>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleOCREngine.Dispose">
|
||||
<summary>
|
||||
Release object
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.PaddleStructureEngine">
|
||||
<summary>
|
||||
PaddleOCR table recognition engine object
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleStructureEngine.#ctor">
|
||||
<summary>
|
||||
PaddleStructureEngine recognition engine object initialization
|
||||
</summary>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleStructureEngine.#ctor(PaddleOCRSharp.StructureModelConfig)">
|
||||
<summary>
|
||||
PaddleStructureEngine recognition engine object initialization
|
||||
</summary>
|
||||
<param name="config">Model configuration object, if null then default values are used</param>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleStructureEngine.#ctor(PaddleOCRSharp.StructureModelConfig,PaddleOCRSharp.StructureParameter)">
|
||||
<summary>
|
||||
PaddleStructureEngine recognition engine object initialization
|
||||
</summary>
|
||||
<param name="config">Model configuration object, if null then default values are used</param>
|
||||
<param name="parameter">Recognition parameters, if null then default values are used</param>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleStructureEngine.#ctor(PaddleOCRSharp.StructureModelConfig,System.String)">
|
||||
<summary>
|
||||
PaddleStructureEngine recognition engine object initialization
|
||||
</summary>
|
||||
<param name="config">Model configuration object, if null then default values are used</param>
|
||||
<param name="parameterjson">Recognition parameters in Json format, if null then default values are used</param>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleStructureEngine.GetDefaultConfig(System.String)">
|
||||
<summary>
|
||||
Get default configuration
|
||||
</summary>
|
||||
<param name="rootpath">Root directory</param>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleStructureEngine.StructureDetectFile(System.String)">
|
||||
<summary>
|
||||
Perform table text recognition on image file
|
||||
</summary>
|
||||
<param name="imagefile">Image file</param>
|
||||
<returns>Table recognition result</returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleStructureEngine.StructureDetect(System.Drawing.Image)">
|
||||
<summary>
|
||||
Perform table text recognition on image object
|
||||
</summary>
|
||||
<param name="image">Image</param>
|
||||
<returns>Table recognition result</returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleStructureEngine.StructureDetect(System.Byte[])">
|
||||
<summary>
|
||||
Perform table text recognition on image Byte array
|
||||
</summary>
|
||||
<param name="imagebyte">Image byte array</param>
|
||||
<returns>Table recognition result</returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleStructureEngine.StructureDetectBase64(System.String)">
|
||||
<summary>
|
||||
Perform table text recognition on image Base64
|
||||
</summary>
|
||||
<param name="imagebase64">Image Base64</param>
|
||||
<returns>Table recognition result</returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleStructureEngine.ConvertResult(System.IntPtr)">
|
||||
<summary>
|
||||
Result parsing
|
||||
</summary>
|
||||
<param name="ptrResult"></param>
|
||||
<returns></returns>
|
||||
</member>
|
||||
<member name="M:PaddleOCRSharp.PaddleStructureEngine.Dispose">
|
||||
<summary>
|
||||
Release object
|
||||
</summary>
|
||||
</member>
|
||||
<member name="T:PaddleOCRSharp.StructureParameter">
|
||||
<summary>
|
||||
OCR recognition parameters
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.StructureParameter.table_max_len">
|
||||
<summary>
|
||||
When the input image length and width are greater than 488, the image is scaled proportionally, default 488
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.StructureParameter.merge_no_span_structure">
|
||||
<summary>
|
||||
Whether to merge empty cells
|
||||
</summary>
|
||||
</member>
|
||||
<member name="P:PaddleOCRSharp.StructureParameter.table_batch_num">
|
||||
<summary>
|
||||
Batch recognition quantity
|
||||
</summary>
|
||||
</member>
|
||||
</members>
|
||||
</doc>
|
|
@ -20,13 +20,17 @@
|
|||
</COMReference>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Include="paddleocr\det\inference\.DS_Store" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LanguageDetection" Version="1.2.0" />
|
||||
<PackageReference Include="Markdown.Xaml" Version="1.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
<PackageReference Include="Paddle.Runtime.win_x64" Version="3.0.0" />
|
||||
<PackageReference Include="PaddleOCRSharp" Version="5.0.0.1" />
|
||||
<PackageReference Include="System.Drawing.Common" Version="9.0.1" />
|
||||
<PackageReference Include="Tesseract" Version="5.2.0" />
|
||||
<PackageReference Include="Tesseract.Drawing" Version="5.2.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -39,15 +43,6 @@
|
|||
<None Update="appsettings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="tessdata\eng.traineddata">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="tessdata\ita.traineddata">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="tessdata\spa.traineddata">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
84
Logger.cs
84
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");
|
||||
}
|
||||
|
|
15
Program.cs
15
Program.cs
|
@ -14,7 +14,8 @@ namespace GTPCorrgir
|
|||
OpenAI,
|
||||
Ollama,
|
||||
Groq,
|
||||
Grok
|
||||
Grok,
|
||||
Claude
|
||||
}
|
||||
|
||||
[Flags]
|
||||
|
@ -30,11 +31,14 @@ namespace GTPCorrgir
|
|||
Corregir,
|
||||
Ortografia,
|
||||
Chat,
|
||||
PreguntaRespuesta,
|
||||
Traducir_a_Ingles,
|
||||
Traducir_a_Italiano,
|
||||
Traducir_a_Espanol,
|
||||
Traducir_a_Portugues,
|
||||
OCRaTexto,
|
||||
Menu,
|
||||
ClaudeWebSearch,
|
||||
}
|
||||
|
||||
public Dictionary<LLM_a_Usar, string> nombreLLM = new Dictionary<LLM_a_Usar, string>
|
||||
|
@ -43,6 +47,7 @@ namespace GTPCorrgir
|
|||
{ LLM_a_Usar.Groq, "Groq" },
|
||||
{ LLM_a_Usar.Grok, "Grok" },
|
||||
{ LLM_a_Usar.OpenAI, "OpenAI" },
|
||||
{ LLM_a_Usar.Claude, "Claude" },
|
||||
};
|
||||
|
||||
private static Opciones _instance;
|
||||
|
@ -100,6 +105,8 @@ namespace GTPCorrgir
|
|||
Opciones.Instance.LLM = Opciones.LLM_a_Usar.Grok;
|
||||
else if (arg.Contains("OpenAI"))
|
||||
Opciones.Instance.LLM = Opciones.LLM_a_Usar.OpenAI;
|
||||
else if (arg.Contains("Claude"))
|
||||
Opciones.Instance.LLM = Opciones.LLM_a_Usar.Claude;
|
||||
|
||||
if (arg.Contains("CtrlA"))
|
||||
Opciones.Instance.FuncionesOpcionales = Opciones.funcionesOpcionales.CtrlA;
|
||||
|
@ -108,6 +115,8 @@ namespace GTPCorrgir
|
|||
|
||||
if (arg.Contains("Chat"))
|
||||
Opciones.Instance.modo = Opciones.modoDeUso.Chat;
|
||||
if (arg.Contains("PreguntaRespuesta"))
|
||||
Opciones.Instance.modo = Opciones.modoDeUso.PreguntaRespuesta;
|
||||
if (arg.Contains("Ortografia"))
|
||||
Opciones.Instance.modo = Opciones.modoDeUso.Ortografia;
|
||||
if (arg.Contains("Corregir"))
|
||||
|
@ -118,8 +127,12 @@ namespace GTPCorrgir
|
|||
Opciones.Instance.modo = Opciones.modoDeUso.Traducir_a_Italiano;
|
||||
if (arg.Contains("Traducir_a_Espanol"))
|
||||
Opciones.Instance.modo = Opciones.modoDeUso.Traducir_a_Espanol;
|
||||
if (arg.Contains("Traducir_a_Portugues"))
|
||||
Opciones.Instance.modo = Opciones.modoDeUso.Traducir_a_Portugues;
|
||||
if (arg.Contains("OCRaTexto"))
|
||||
Opciones.Instance.modo = Opciones.modoDeUso.OCRaTexto;
|
||||
if (arg.Contains("ClaudeWebSearch"))
|
||||
Opciones.Instance.modo = Opciones.modoDeUso.ClaudeWebSearch;
|
||||
if (arg.Contains("AutoCopy"))
|
||||
Opciones.Instance.AutoCopy = true;
|
||||
if (arg.Contains("Menu"))
|
||||
|
|
10
README.md
10
README.md
|
@ -13,4 +13,12 @@ Los parametros son:
|
|||
### Modo de funcionamiento
|
||||
***********************
|
||||
* `--Chat` : Para crear una mini ventana que permite hacer chat con los modelos sellecionados.
|
||||
* `--Correccion` (default ) : Correccion desde el portapapeles y hacia el portapapeles
|
||||
* `--Corregir` (default) : Corrección desde el portapapeles y hacia el portapapeles
|
||||
* `--Ortografia` : Revisar ortografía del texto en el portapapeles
|
||||
* `--PreguntaRespuesta` : Usar el texto del portapapeles como pregunta directa al LLM y devolver pregunta + respuesta
|
||||
* `--Traducir_a_Ingles` : Traducir texto al inglés
|
||||
* `--Traducir_a_Italiano` : Traducir texto al italiano
|
||||
* `--Traducir_a_Espanol` : Traducir texto al español
|
||||
* `--Traducir_a_Portugues` : Traducir texto al portugués
|
||||
* `--OCRaTexto` : Capturar pantalla y convertir imagen a texto
|
||||
* `--Menu` : Mostrar menú de selección de opciones
|
|
@ -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
|
|
@ -6,8 +6,9 @@ using System.Windows.Media;
|
|||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Shapes;
|
||||
using Tesseract;
|
||||
using PaddleOCRSharp;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using Point = System.Windows.Point;
|
||||
using Rect = System.Windows.Rect;
|
||||
using Size = System.Windows.Size;
|
||||
|
@ -24,6 +25,7 @@ using System.Windows.Data;
|
|||
using Binding = System.Windows.Data.Binding;
|
||||
using Image = System.Windows.Controls.Image;
|
||||
|
||||
|
||||
namespace GTPCorrgir
|
||||
{
|
||||
public class ScreenCaptureWindow : Window
|
||||
|
@ -35,7 +37,7 @@ namespace GTPCorrgir
|
|||
private Canvas overlayCanvas;
|
||||
private readonly Cursor crosshairCursor = Cursors.Cross;
|
||||
private bool isSelecting;
|
||||
private TextBlock dimensionsText;
|
||||
private System.Windows.Controls.TextBlock dimensionsText;
|
||||
|
||||
public ScreenCaptureWindow()
|
||||
{
|
||||
|
@ -284,7 +286,7 @@ namespace GTPCorrgir
|
|||
var screenCapture = CaptureScreen(x, y, width, height);
|
||||
if (screenCapture == null) return;
|
||||
|
||||
// Resto del procesamiento OCR igual que antes
|
||||
// Procesamiento OCR con PaddleOCR
|
||||
using (var memoryStream = new MemoryStream())
|
||||
{
|
||||
var encoder = new PngBitmapEncoder();
|
||||
|
@ -292,14 +294,37 @@ namespace GTPCorrgir
|
|||
encoder.Save(memoryStream);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
string tesseractPath = GetTesseractExecutablePath();
|
||||
using (var engine = new TesseractEngine(tesseractPath, "eng", EngineMode.Default))
|
||||
try
|
||||
{
|
||||
using (var img = Pix.LoadFromMemory(memoryStream.ToArray()))
|
||||
{
|
||||
var result = engine.Process(img);
|
||||
string ocrText = result.GetText();
|
||||
// Convertir a array de bytes
|
||||
byte[] imageBytes = memoryStream.ToArray();
|
||||
|
||||
// Obtener el motor PaddleOCR
|
||||
var ocrEngine = PaddleOCRManager.GetEngine();
|
||||
|
||||
// Usar PaddleOCR para detectar texto
|
||||
OCRResult result = ocrEngine.DetectText(imageBytes);
|
||||
|
||||
string ocrText = "";
|
||||
if (result != null && result.TextBlocks != null && result.TextBlocks.Count > 0)
|
||||
{
|
||||
// Lista de caracteres permitidos (similar a la whitelist de Tesseract)
|
||||
string allowedChars = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-./";
|
||||
|
||||
// Filtrar texto por lista de caracteres permitidos
|
||||
var filteredBlocks = result.TextBlocks
|
||||
.Where(block => block.Score > 0.5) // Solo bloques con confianza > 50%
|
||||
.Select(block => new {
|
||||
Text = new string(block.Text.Where(c => allowedChars.Contains(c)).ToArray()),
|
||||
Score = block.Score
|
||||
})
|
||||
.OrderByDescending(block => block.Score);
|
||||
|
||||
ocrText = string.Join(" ", filteredBlocks.Select(b => b.Text));
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(ocrText))
|
||||
{
|
||||
using (var textProcessor = new OcrTextProcessor())
|
||||
{
|
||||
string processedText = await textProcessor.ProcessOcrText(ocrText);
|
||||
|
@ -308,6 +333,17 @@ namespace GTPCorrgir
|
|||
notificationWindow.Show();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var notificationWindow = new notificacion();
|
||||
notificationWindow.UpdateNotification("Sin texto", "No se pudo detectar texto en la imagen seleccionada");
|
||||
notificationWindow.Show();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Error en OCR: {ex.Message}");
|
||||
MessageBox.Show($"Error en OCR: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -329,20 +365,5 @@ namespace GTPCorrgir
|
|||
this.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private string GetTesseractExecutablePath()
|
||||
{
|
||||
string baseDir = AppDomain.CurrentDomain.BaseDirectory;
|
||||
string tessDataDir = Path.Combine(baseDir, "tessdata");
|
||||
|
||||
if (!Directory.Exists(tessDataDir))
|
||||
{
|
||||
throw new DirectoryNotFoundException(
|
||||
$"No se encontró el directorio tessdata en {tessDataDir}. " +
|
||||
"Asegúrate de que la carpeta tessdata existe y contiene los archivos de entrenamiento.");
|
||||
}
|
||||
|
||||
return tessDataDir;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Drawing;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows;
|
||||
using System.Linq;
|
||||
using PaddleOCRSharp;
|
||||
|
||||
namespace GTPCorrgir
|
||||
{
|
||||
// Clase auxiliar para gestionar PaddleOCR
|
||||
public static class PaddleOCRManager
|
||||
{
|
||||
private static PaddleOCREngine _engine;
|
||||
private static bool _initialized = false;
|
||||
private static readonly object _lockObj = new object();
|
||||
|
||||
public static PaddleOCREngine GetEngine()
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
lock (_lockObj)
|
||||
{
|
||||
if (!_initialized)
|
||||
{
|
||||
InitializeEngine();
|
||||
_initialized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _engine;
|
||||
}
|
||||
|
||||
private static void InitializeEngine()
|
||||
{
|
||||
try
|
||||
{
|
||||
string baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "paddleocr");
|
||||
|
||||
OCRModelConfig config = new OCRModelConfig
|
||||
{
|
||||
// Rutas a modelos de inferencia
|
||||
det_infer = Path.Combine(baseDir, "det", "inference"),
|
||||
cls_infer = Path.Combine(baseDir, "cls", "inference"),
|
||||
rec_infer = Path.Combine(baseDir, "rec", "inference"),
|
||||
keys = Path.Combine(baseDir, "keys", "ppocr_keys.txt")
|
||||
};
|
||||
|
||||
OCRParameter parameter = new OCRParameter
|
||||
{
|
||||
// Configurar parámetros de OCR
|
||||
use_angle_cls = true,
|
||||
cls_thresh = 0.9f,
|
||||
det_db_thresh = 0.3f,
|
||||
det_db_box_thresh = 0.6f,
|
||||
rec_batch_num = 6,
|
||||
rec_img_h = 48,
|
||||
enable_mkldnn = true,
|
||||
use_gpu = false,
|
||||
cpu_math_library_num_threads = Environment.ProcessorCount
|
||||
};
|
||||
|
||||
_engine = new PaddleOCREngine(config, parameter);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"Error al inicializar PaddleOCR: {ex.Message}");
|
||||
// Fallback - inicializar con configuración predeterminada
|
||||
_engine = new PaddleOCREngine();
|
||||
}
|
||||
}
|
||||
|
||||
public static void Cleanup()
|
||||
{
|
||||
lock (_lockObj)
|
||||
{
|
||||
if (_initialized && _engine != null)
|
||||
{
|
||||
_engine.Dispose();
|
||||
_engine = null;
|
||||
_initialized = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
636
gtpask.cs
636
gtpask.cs
|
@ -20,6 +20,7 @@ namespace GTPCorrgir
|
|||
public string OpenAI { get; set; }
|
||||
public string Groq { get; set; }
|
||||
public string Grok { get; set; }
|
||||
public string Claude { get; set; }
|
||||
}
|
||||
|
||||
public ApiKeySection ApiKeys { get; set; }
|
||||
|
@ -30,6 +31,7 @@ namespace GTPCorrgir
|
|||
private string _openAiApiKey;
|
||||
private string _groqApiKey;
|
||||
private string _grokApiKey;
|
||||
private string _claudeApiKey;
|
||||
private readonly HttpClient _httpClient;
|
||||
private bool _disposed;
|
||||
private readonly LanguageDetector _languageDetector;
|
||||
|
@ -90,6 +92,7 @@ namespace GTPCorrgir
|
|||
_openAiApiKey = settings?.ApiKeys?.OpenAI;
|
||||
_groqApiKey = settings?.ApiKeys?.Groq;
|
||||
_grokApiKey = settings?.ApiKeys?.Grok;
|
||||
_claudeApiKey = settings?.ApiKeys?.Claude;
|
||||
|
||||
ValidateApiKeys();
|
||||
}
|
||||
|
@ -107,6 +110,7 @@ namespace GTPCorrgir
|
|||
if (string.IsNullOrEmpty(_openAiApiKey)) missingKeys.Add("OpenAI");
|
||||
if (string.IsNullOrEmpty(_groqApiKey)) missingKeys.Add("Groq");
|
||||
if (string.IsNullOrEmpty(_grokApiKey)) missingKeys.Add("Grok");
|
||||
if (string.IsNullOrEmpty(_claudeApiKey)) missingKeys.Add("Claude");
|
||||
|
||||
if (missingKeys.Any())
|
||||
{
|
||||
|
@ -117,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");
|
||||
}
|
||||
|
@ -172,16 +176,43 @@ namespace GTPCorrgir
|
|||
return true;
|
||||
}
|
||||
|
||||
string detectedLanguageCode = _languageDetector.Detect(TextoACorregir);
|
||||
IdiomaDetectado = _languageMap.GetValueOrDefault(detectedLanguageCode, "Desconocido");
|
||||
// Si el texto es solo una URL, usar español como predeterminado
|
||||
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}");
|
||||
return IdiomaDetectado != "Desconocido";
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -189,7 +220,8 @@ namespace GTPCorrgir
|
|||
{
|
||||
return 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_Espanol ||
|
||||
Opciones.Instance.modo == Opciones.modoDeUso.Traducir_a_Portugues;
|
||||
}
|
||||
|
||||
private string ObtenerIdiomaObjetivo()
|
||||
|
@ -199,6 +231,7 @@ namespace GTPCorrgir
|
|||
Opciones.modoDeUso.Traducir_a_Ingles => _languageMap["en"],
|
||||
Opciones.modoDeUso.Traducir_a_Italiano => _languageMap["it"],
|
||||
Opciones.modoDeUso.Traducir_a_Espanol => _languageMap["es"],
|
||||
Opciones.modoDeUso.Traducir_a_Portugues => _languageMap["pt"],
|
||||
_ => throw new ArgumentException("Modo de traducción no válido")
|
||||
};
|
||||
}
|
||||
|
@ -221,7 +254,17 @@ namespace GTPCorrgir
|
|||
try
|
||||
{
|
||||
string respuestaLLM;
|
||||
string respuestaCompleta = "";
|
||||
|
||||
// Si es el modo Claude Web Search, usar el método específico
|
||||
if (Opciones.Instance.modo == Opciones.modoDeUso.ClaudeWebSearch)
|
||||
{
|
||||
var resultadoWebSearch = await CallClaudeWebSearchApiCompleto(textoMarcado);
|
||||
respuestaLLM = resultadoWebSearch.texto;
|
||||
respuestaCompleta = resultadoWebSearch.respuestaCompleta;
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (Opciones.Instance.LLM)
|
||||
{
|
||||
case Opciones.LLM_a_Usar.OpenAI:
|
||||
|
@ -236,16 +279,20 @@ namespace GTPCorrgir
|
|||
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))
|
||||
{
|
||||
throw new ApplicationException("No se recibió respuesta del LLM");
|
||||
}
|
||||
|
||||
ProcesarRespuestaLLM(respuestaLLM);
|
||||
ProcesarRespuestaLLM(respuestaLLM, respuestaCompleta);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
@ -254,16 +301,186 @@ namespace GTPCorrgir
|
|||
}
|
||||
}
|
||||
|
||||
private void ProcesarRespuestaLLM(string respuestaLLM)
|
||||
private void ProcesarRespuestaLLM(string respuestaLLM, string respuestaCompleta)
|
||||
{
|
||||
TextoCorregido = ExtraerValorUnicoJSON(respuestaLLM);
|
||||
if (TextoCorregido == null)
|
||||
// Para Claude Web Search, la respuesta ya viene en formato de texto final
|
||||
if (Opciones.Instance.modo == Opciones.modoDeUso.ClaudeWebSearch)
|
||||
{
|
||||
// Extraer contenido estructurado (textos + enlaces)
|
||||
string contenidoEstructurado = ExtraerEnlacesWebSearch(respuestaCompleta);
|
||||
|
||||
if (!string.IsNullOrEmpty(contenidoEstructurado))
|
||||
{
|
||||
// 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");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
TextoCorregido = _markdownProcessor.RemoveTechnicalTermMarkers_IgnoreCase(TextoCorregido).Trim('"');
|
||||
TextoCorregido = _markdownProcessor.RemoveDoubleBrackets(TextoCorregido);
|
||||
respuestaExtraidaNormal = System.Text.RegularExpressions.Regex.Replace(respuestaExtraidaNormal, @"\*\*(.*?)\*\*", "$1");
|
||||
respuestaExtraidaNormal = _markdownProcessor.RemoveTechnicalTermMarkers_IgnoreCase(respuestaExtraidaNormal).Trim('"');
|
||||
respuestaExtraidaNormal = _markdownProcessor.RemoveDoubleBrackets(respuestaExtraidaNormal);
|
||||
|
||||
// Para el modo Pregunta-Respuesta, combinar pregunta original con la respuesta
|
||||
if (Opciones.Instance.modo == Opciones.modoDeUso.PreguntaRespuesta)
|
||||
{
|
||||
TextoCorregido = $"{TextoACorregir}\n{respuestaExtraidaNormal}";
|
||||
}
|
||||
else
|
||||
{
|
||||
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 contenidoFormateado = new List<string>();
|
||||
|
||||
// Deserializar la respuesta completa para extraer los resultados
|
||||
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)
|
||||
{
|
||||
string tipoElemento = contentItem.type?.ToString() ?? "";
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Procesando elemento tipo: {tipoElemento}");
|
||||
|
||||
// Procesar elementos de texto con sus citas
|
||||
if (tipoElemento == "text" && contentItem.text != null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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() ?? "";
|
||||
|
||||
if (!string.IsNullOrEmpty(url))
|
||||
{
|
||||
string enlaceFormateado;
|
||||
if (!string.IsNullOrEmpty(pageAge))
|
||||
{
|
||||
enlaceFormateado = $" * [{titulo}]({url}) - {pageAge}";
|
||||
}
|
||||
else
|
||||
{
|
||||
enlaceFormateado = $" * [{titulo}]({url})";
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Log("ExtraerEnlacesWebSearch: No se encontró contenido en la respuesta");
|
||||
}
|
||||
|
||||
Log.Log($"ExtraerEnlacesWebSearch: Generadas {contenidoFormateado.Count} líneas de contenido formateado");
|
||||
|
||||
if (contenidoFormateado.Any())
|
||||
{
|
||||
return string.Join("\n", contenidoFormateado);
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Log($"Error al extraer enlaces de web search: {ex.Message}");
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SimularCorreccion()
|
||||
|
@ -307,12 +524,26 @@ namespace GTPCorrgir
|
|||
"You are an engineer working in industrial automation. Your task is to review texts and rewrite them in a simple and concise manner. If you find words enclosed in double brackets [[like this]], preserve them exactly as they appear without any modifications. For all other technical terms, write them normally without adding any brackets or special formatting. Preserve any existing markdown language if present. Please rewrite the following text in " + IdiomaDetectado + " and respond in the following JSON format: { \"Rewritten_text\": \"Your text here\" }. Important: Do not add any new brackets to words that aren't already enclosed in double brackets.",
|
||||
|
||||
Opciones.modoDeUso.Ortografia =>
|
||||
"Please check the following text for spelling errors and provide the corrected version. Do not change the meaning or structure of the sentences. If you find words enclosed in double brackets [[like this]], preserve them exactly as they appear. For all other words, only correct spelling mistakes while preserving technical terms and any markdown language if present. Please write in " + IdiomaDetectado + " and respond in the following JSON format: { \"Rewritten_text\": \"Your text here\" }.",
|
||||
"Please check the following text for spelling errors and provide the corrected version. Do not change the meaning or structure of the sentences. If you find words enclosed in double brackets [[like this]], preserve them exactly as they appear. For all other words, only correct spelling mistakes while preserving technical terms. Preserve any existing markdown language if present, but do not introduce new markdown styling (such as bold or italics) for emphasis unless it was part of the original input. Please write in " + IdiomaDetectado + " and respond in the following JSON format: { \"Rewritten_text\": \"Your text here\" }.",
|
||||
|
||||
Opciones.modoDeUso.PreguntaRespuesta =>
|
||||
"You are a helpful assistant specialized in industrial automation and technical topics. Please answer the user's question accurately and clearly. If the question contains words in double brackets [[like this]], preserve them exactly as they appear in your response. Please write your answer in " + IdiomaDetectado + " and respond in the following JSON format: { \"Reply_text\": \"Your answer here\" }.",
|
||||
|
||||
_ => "You are an engineer working specialized in industrial automation. If the question contains words in double brackets [[like this]], preserve them exactly as they appear. Please answer the following question in " + IdiomaDetectado + " and respond in the following JSON format: { \"Reply_text\": \"Your text here\" }."
|
||||
};
|
||||
}
|
||||
|
||||
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) =>
|
||||
Opciones.Instance.modo switch
|
||||
{
|
||||
|
@ -322,6 +553,12 @@ namespace GTPCorrgir
|
|||
Opciones.modoDeUso.Ortografia =>
|
||||
$"Please check the following text for spelling errors and provide the corrected version. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
|
||||
|
||||
Opciones.modoDeUso.PreguntaRespuesta =>
|
||||
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 =>
|
||||
$"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}\"",
|
||||
|
||||
|
@ -331,6 +568,9 @@ namespace GTPCorrgir
|
|||
Opciones.modoDeUso.Traducir_a_Espanol =>
|
||||
$"Please check the following text for spelling errors and provide the corrected version tranlated to Spanish. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
|
||||
|
||||
Opciones.modoDeUso.Traducir_a_Portugues =>
|
||||
$"Please check the following text for spelling errors and provide the corrected version tranlated to Portuguese. Do not change the meaning or structure of the sentences. Only correct any spelling mistakes you find on: \"{texto}\"",
|
||||
|
||||
_ => texto
|
||||
};
|
||||
|
||||
|
@ -440,6 +680,137 @@ namespace GTPCorrgir
|
|||
}
|
||||
}
|
||||
|
||||
private async Task<string> CallClaudeApi(string input)
|
||||
{
|
||||
try
|
||||
{
|
||||
_httpClient.DefaultRequestHeaders.Clear();
|
||||
_httpClient.DefaultRequestHeaders.Add("x-api-key", _claudeApiKey);
|
||||
_httpClient.DefaultRequestHeaders.Add("anthropic-version", "2023-06-01");
|
||||
|
||||
var requestData = new
|
||||
{
|
||||
model = "claude-sonnet-4-20250514",
|
||||
max_tokens = 4096,
|
||||
temperature = 1,
|
||||
system = CrearMensajeDeSistema(),
|
||||
messages = new[]
|
||||
{
|
||||
new { role = "user", content = CrearMensajeDeUsuario(input) }
|
||||
},
|
||||
thinking = new
|
||||
{
|
||||
type = "enabled",
|
||||
budget_tokens = 2048
|
||||
}
|
||||
};
|
||||
|
||||
return await EnviarSolicitudLLM("https://api.anthropic.com/v1/messages", requestData);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Log($"Error en llamada a Claude API: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
try
|
||||
|
@ -456,7 +827,15 @@ namespace GTPCorrgir
|
|||
using var response = await _httpClient.PostAsync(endpoint, content);
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -476,6 +855,26 @@ namespace GTPCorrgir
|
|||
}
|
||||
throw new ApplicationException("Formato de respuesta de Ollama inválido");
|
||||
}
|
||||
else if (endpoint.Contains("anthropic"))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
throw new ApplicationException("No se encontró contenido en la respuesta de Claude");
|
||||
}
|
||||
else // OpenAI, Groq, Grok
|
||||
{
|
||||
if (data.choices != null && data.choices.Count > 0)
|
||||
|
@ -492,6 +891,215 @@ 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();
|
||||
|
||||
// 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()
|
||||
{
|
||||
Dispose(true);
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,132 @@
|
|||
# Script para descargar e instalar modelos PaddleOCR
|
||||
# Guardar como: download-paddleocr-models.ps1
|
||||
|
||||
param(
|
||||
[string]$OutputDirectory = ".\paddleocr",
|
||||
[switch]$ForceDownload = $false
|
||||
)
|
||||
|
||||
# URLs de modelos PaddleOCR (inglés por defecto)
|
||||
$modelUrls = @{
|
||||
"det" = "https://paddleocr.bj.bcebos.com/PP-OCRv3/english/en_PP-OCRv3_det_slim_infer.tar"
|
||||
"rec" = "https://paddleocr.bj.bcebos.com/PP-OCRv3/english/en_PP-OCRv3_rec_slim_infer.tar"
|
||||
"cls" = "https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_cls_slim_infer.tar"
|
||||
"keys" = "https://raw.githubusercontent.com/PaddlePaddle/PaddleOCR/release/2.6/ppocr/utils/en_dict.txt"
|
||||
}
|
||||
|
||||
# Crear directorios si no existen
|
||||
function EnsureDirectory($path) {
|
||||
if (-not (Test-Path $path)) {
|
||||
New-Item -Path $path -ItemType Directory | Out-Null
|
||||
Write-Host "Creado directorio: $path" -ForegroundColor Green
|
||||
}
|
||||
}
|
||||
|
||||
# Descargar archivo con barra de progreso
|
||||
function DownloadFile($url, $outputFile) {
|
||||
$webClient = New-Object System.Net.WebClient
|
||||
$webClient.Encoding = [System.Text.Encoding]::UTF8
|
||||
|
||||
$downloadExists = Test-Path $outputFile
|
||||
if ($downloadExists -and -not $ForceDownload) {
|
||||
Write-Host "El archivo ya existe: $outputFile. Use -ForceDownload para sobrescribir." -ForegroundColor Yellow
|
||||
return $false
|
||||
}
|
||||
|
||||
Write-Host "Descargando $url..." -ForegroundColor Cyan
|
||||
try {
|
||||
$webClient.DownloadFile($url, $outputFile)
|
||||
Write-Host "Descarga completada: $outputFile" -ForegroundColor Green
|
||||
return $true
|
||||
} catch {
|
||||
Write-Host "Error al descargar $url : $_" -ForegroundColor Red
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# Extraer archivo TAR
|
||||
function ExtractTarFile($tarFile, $destDir) {
|
||||
Write-Host "Extrayendo $tarFile..." -ForegroundColor Cyan
|
||||
|
||||
# Verificar si tar está disponible
|
||||
$tarAvailable = $null -ne (Get-Command "tar" -ErrorAction SilentlyContinue)
|
||||
|
||||
if ($tarAvailable) {
|
||||
# Usar tar nativo
|
||||
try {
|
||||
tar -xf $tarFile -C $destDir
|
||||
Write-Host "Extracción completada: $tarFile" -ForegroundColor Green
|
||||
return $true
|
||||
} catch {
|
||||
Write-Host "Error al extraer con tar: $_" -ForegroundColor Red
|
||||
}
|
||||
}
|
||||
|
||||
# Fallback a .NET
|
||||
try {
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
|
||||
# Descomprimir TAR con .NET
|
||||
# Nota: Esto es un ejemplo simplificado. Necesitarías una biblioteca específica para TAR
|
||||
Write-Host "TAR nativo no disponible. Se requiere una biblioteca para manejar archivos TAR." -ForegroundColor Yellow
|
||||
|
||||
# Aquí puedes agregar código para usar SharpCompress u otra biblioteca para TAR
|
||||
# Por ejemplo:
|
||||
# [Reflection.Assembly]::LoadFrom("path\to\SharpCompress.dll")
|
||||
# $reader = [SharpCompress.Readers.ReaderFactory]::Open($tarFile)
|
||||
# ...
|
||||
|
||||
return $false
|
||||
} catch {
|
||||
Write-Host "Error al extraer $tarFile : $_" -ForegroundColor Red
|
||||
return $false
|
||||
}
|
||||
}
|
||||
|
||||
# Crear directorio principal
|
||||
EnsureDirectory $OutputDirectory
|
||||
|
||||
# Crear estructura de directorios
|
||||
$modelTypes = @("det", "rec", "cls", "keys")
|
||||
foreach ($type in $modelTypes) {
|
||||
$typeDir = Join-Path $OutputDirectory $type
|
||||
EnsureDirectory $typeDir
|
||||
|
||||
if ($type -ne "keys") {
|
||||
$inferenceDir = Join-Path $typeDir "inference"
|
||||
EnsureDirectory $inferenceDir
|
||||
}
|
||||
}
|
||||
|
||||
# Descargar y procesar cada modelo
|
||||
foreach ($entry in $modelUrls.GetEnumerator()) {
|
||||
$type = $entry.Key
|
||||
$url = $entry.Value
|
||||
$typeDir = Join-Path $OutputDirectory $type
|
||||
|
||||
if ($type -eq "keys") {
|
||||
# Para el archivo de claves, solo descargarlo directamente
|
||||
$outputFile = Join-Path $typeDir "ppocr_keys.txt"
|
||||
DownloadFile $url $outputFile
|
||||
} else {
|
||||
# Para los modelos, descargar TAR y extraerlo
|
||||
$tarFile = Join-Path $env:TEMP "$type.tar"
|
||||
|
||||
$downloaded = DownloadFile $url $tarFile
|
||||
if ($downloaded) {
|
||||
$extracted = ExtractTarFile $tarFile $typeDir
|
||||
|
||||
# Limpiar archivo temporal
|
||||
if (Test-Path $tarFile) {
|
||||
Remove-Item $tarFile -Force
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "`nInstalación de modelos PaddleOCR completada en: $OutputDirectory" -ForegroundColor Green
|
||||
Write-Host "Para usar estos modelos, configura PaddleOCREngine con las siguientes rutas:" -ForegroundColor Yellow
|
||||
Write-Host " - det_infer: $OutputDirectory\det\inference" -ForegroundColor White
|
||||
Write-Host " - rec_infer: $OutputDirectory\rec\inference" -ForegroundColor White
|
||||
Write-Host " - cls_infer: $OutputDirectory\cls\inference" -ForegroundColor White
|
||||
Write-Host " - keys: $OutputDirectory\keys\ppocr_keys.txt" -ForegroundColor White
|
|
@ -0,0 +1,95 @@
|
|||
0
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
||||
6
|
||||
7
|
||||
8
|
||||
9
|
||||
:
|
||||
;
|
||||
<
|
||||
=
|
||||
>
|
||||
?
|
||||
@
|
||||
A
|
||||
B
|
||||
C
|
||||
D
|
||||
E
|
||||
F
|
||||
G
|
||||
H
|
||||
I
|
||||
J
|
||||
K
|
||||
L
|
||||
M
|
||||
N
|
||||
O
|
||||
P
|
||||
Q
|
||||
R
|
||||
S
|
||||
T
|
||||
U
|
||||
V
|
||||
W
|
||||
X
|
||||
Y
|
||||
Z
|
||||
[
|
||||
\
|
||||
]
|
||||
^
|
||||
_
|
||||
`
|
||||
a
|
||||
b
|
||||
c
|
||||
d
|
||||
e
|
||||
f
|
||||
g
|
||||
h
|
||||
i
|
||||
j
|
||||
k
|
||||
l
|
||||
m
|
||||
n
|
||||
o
|
||||
p
|
||||
q
|
||||
r
|
||||
s
|
||||
t
|
||||
u
|
||||
v
|
||||
w
|
||||
x
|
||||
y
|
||||
z
|
||||
{
|
||||
|
|
||||
}
|
||||
~
|
||||
!
|
||||
"
|
||||
#
|
||||
$
|
||||
%
|
||||
&
|
||||
'
|
||||
(
|
||||
)
|
||||
*
|
||||
+
|
||||
,
|
||||
-
|
||||
.
|
||||
/
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue