Creazione progetto SynthData Pro: struttura WPF completa
Aggiunti tutti i file sorgente per la nuova applicazione desktop WPF "SynthData Pro" (namespace Dione) per la generazione dati tramite LLM locale/remoto. Inclusi: - Progetto .csproj, configurazione .NET 4.8.1, risorse e file di soluzione. - UI moderna con Material Design, sidebar, title bar custom, e navigazione tra Dashboard, Generazione Live, Impostazioni e Telemetria. - Modelli dati (AppSettings, DataProject, SchemaColumn, TelemetryLog) e layer dati SQLite con migrazione automatica. - ViewModel principali per dashboard KPI/grafici, generazione streaming, impostazioni, telemetria. - Tutte le View XAML e relativi code-behind. - Localizzazione italiana e attenzione all'usabilità. - Pronto per estensioni future (Data Designer, moduli placeholder).
This commit is contained in:
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<configuration>
|
||||||
|
<startup>
|
||||||
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8.1" />
|
||||||
|
</startup>
|
||||||
|
</configuration>
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
<Application x:Class="Dione.App"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:local="clr-namespace:Dione"
|
||||||
|
xmlns:md="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
|
StartupUri="MainWindow.xaml">
|
||||||
|
<Application.Resources>
|
||||||
|
<ResourceDictionary>
|
||||||
|
<ResourceDictionary.MergedDictionaries>
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Dark.xaml"/>
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.Blue.xaml"/>
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Teal.xaml"/>
|
||||||
|
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml"/>
|
||||||
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
|
<!-- Fix ComboBox dropdown items: black text on the white popup -->
|
||||||
|
<Style TargetType="ComboBoxItem">
|
||||||
|
<Setter Property="Foreground" Value="Black"/>
|
||||||
|
</Style>
|
||||||
|
</ResourceDictionary>
|
||||||
|
</Application.Resources>
|
||||||
|
</Application>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using Dione.Data;
|
||||||
|
|
||||||
|
namespace Dione
|
||||||
|
{
|
||||||
|
public partial class App : Application
|
||||||
|
{
|
||||||
|
protected override void OnStartup(StartupEventArgs e)
|
||||||
|
{
|
||||||
|
base.OnStartup(e);
|
||||||
|
SynthDataDbContext.EnsureCreated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Windows.Data;
|
||||||
|
|
||||||
|
namespace Dione.Converters
|
||||||
|
{
|
||||||
|
public class InverseBoolConverter : IValueConverter
|
||||||
|
{
|
||||||
|
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is bool b)
|
||||||
|
return !b;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (value is bool b)
|
||||||
|
return !b;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data.SQLite;
|
||||||
|
using Dione.Models;
|
||||||
|
|
||||||
|
namespace Dione.Data
|
||||||
|
{
|
||||||
|
public static class SynthDataDbContext
|
||||||
|
{
|
||||||
|
private static readonly string DbPath = System.IO.Path.Combine(
|
||||||
|
System.Environment.GetFolderPath(System.Environment.SpecialFolder.LocalApplicationData),
|
||||||
|
"SynthDataPro", "synthdata.db");
|
||||||
|
|
||||||
|
private static string ConnStr => $"Data Source={DbPath};Version=3;";
|
||||||
|
|
||||||
|
// ── Schema ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public static void EnsureCreated()
|
||||||
|
{
|
||||||
|
var dir = System.IO.Path.GetDirectoryName(DbPath);
|
||||||
|
if (!System.IO.Directory.Exists(dir))
|
||||||
|
System.IO.Directory.CreateDirectory(dir);
|
||||||
|
if (!System.IO.File.Exists(DbPath))
|
||||||
|
SQLiteConnection.CreateFile(DbPath);
|
||||||
|
|
||||||
|
using (var conn = new SQLiteConnection(ConnStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText = "CREATE TABLE IF NOT EXISTS AppSettings (" +
|
||||||
|
"Id INTEGER PRIMARY KEY DEFAULT 1," +
|
||||||
|
"ApiEndpoint TEXT DEFAULT 'http://127.0.0.1:1234/v1/chat/completions'," +
|
||||||
|
"ModelName TEXT DEFAULT ''," +
|
||||||
|
"ApiKey TEXT DEFAULT ''," +
|
||||||
|
"Temperature REAL NOT NULL DEFAULT 0.7," +
|
||||||
|
"MaxTokens INTEGER NOT NULL DEFAULT 2048," +
|
||||||
|
"SystemPrompt TEXT DEFAULT ''," +
|
||||||
|
"UserPrompt TEXT DEFAULT ''," +
|
||||||
|
"OutputDirectory TEXT DEFAULT ''," +
|
||||||
|
"OutputFilePrefix TEXT DEFAULT 'batch'," +
|
||||||
|
"MaxFileSizeMb INTEGER NOT NULL DEFAULT 250," +
|
||||||
|
"ApiTimeoutSeconds INTEGER NOT NULL DEFAULT 120," +
|
||||||
|
"TimeoutPerTokenRatio REAL NOT NULL DEFAULT 0.5," +
|
||||||
|
"EnableQualityVerification INTEGER NOT NULL DEFAULT 0," +
|
||||||
|
"UseSameModelForVerification INTEGER NOT NULL DEFAULT 1," +
|
||||||
|
"VerificationApiEndpoint TEXT DEFAULT ''," +
|
||||||
|
"VerificationModelName TEXT DEFAULT ''," +
|
||||||
|
"VerificationApiKey TEXT DEFAULT ''," +
|
||||||
|
"RevenuePerHighQualityRecord REAL NOT NULL DEFAULT 0.005," +
|
||||||
|
"ElectricityCostPerKwh REAL NOT NULL DEFAULT 0.25," +
|
||||||
|
"SystemPowerWatt REAL NOT NULL DEFAULT 350," +
|
||||||
|
"ApiCostType TEXT DEFAULT 'Free'," +
|
||||||
|
"ApiCostPerCall REAL NOT NULL DEFAULT 0," +
|
||||||
|
"ApiCostPerBlock REAL NOT NULL DEFAULT 0," +
|
||||||
|
"ApiBlockSize INTEGER NOT NULL DEFAULT 1000" +
|
||||||
|
");";
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText = "CREATE TABLE IF NOT EXISTS TelemetryLogs (" +
|
||||||
|
"Id INTEGER PRIMARY KEY AUTOINCREMENT," +
|
||||||
|
"Timestamp TEXT NOT NULL," +
|
||||||
|
"BatchId TEXT," +
|
||||||
|
"ModelUsed TEXT," +
|
||||||
|
"TokensPrompt INTEGER NOT NULL DEFAULT 0," +
|
||||||
|
"TokensCompletion INTEGER NOT NULL DEFAULT 0," +
|
||||||
|
"ExecutionTimeMs INTEGER NOT NULL DEFAULT 0," +
|
||||||
|
"OutputPreview TEXT," +
|
||||||
|
"ErrorMessage TEXT," +
|
||||||
|
"IsSuccess INTEGER NOT NULL DEFAULT 0," +
|
||||||
|
"QualityScore REAL NOT NULL DEFAULT 0," +
|
||||||
|
"Revenue REAL NOT NULL DEFAULT 0," +
|
||||||
|
"RecordsInBatch INTEGER NOT NULL DEFAULT 0" +
|
||||||
|
");";
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText = "INSERT OR IGNORE INTO AppSettings (Id) VALUES (1);";
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Migrate TelemetryLogs: add columns if missing ──
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
var existingCols = new System.Collections.Generic.HashSet<string>();
|
||||||
|
cmd.CommandText = "PRAGMA table_info(TelemetryLogs)";
|
||||||
|
using (var r = cmd.ExecuteReader())
|
||||||
|
while (r.Read())
|
||||||
|
existingCols.Add(r.GetString(1));
|
||||||
|
|
||||||
|
if (!existingCols.Contains("QualityScore"))
|
||||||
|
{ cmd.CommandText = "ALTER TABLE TelemetryLogs ADD COLUMN QualityScore REAL NOT NULL DEFAULT 0"; cmd.ExecuteNonQuery(); }
|
||||||
|
if (!existingCols.Contains("Revenue"))
|
||||||
|
{ cmd.CommandText = "ALTER TABLE TelemetryLogs ADD COLUMN Revenue REAL NOT NULL DEFAULT 0"; cmd.ExecuteNonQuery(); }
|
||||||
|
if (!existingCols.Contains("RecordsInBatch"))
|
||||||
|
{ cmd.CommandText = "ALTER TABLE TelemetryLogs ADD COLUMN RecordsInBatch INTEGER NOT NULL DEFAULT 0"; cmd.ExecuteNonQuery(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── AppSettings CRUD ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public static AppSettings LoadSettings()
|
||||||
|
{
|
||||||
|
EnsureCreated();
|
||||||
|
using (var conn = new SQLiteConnection(ConnStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText =
|
||||||
|
"SELECT ApiEndpoint,ModelName,ApiKey,Temperature,MaxTokens," +
|
||||||
|
"SystemPrompt,UserPrompt,OutputDirectory,OutputFilePrefix,MaxFileSizeMb," +
|
||||||
|
"ApiTimeoutSeconds,TimeoutPerTokenRatio," +
|
||||||
|
"EnableQualityVerification,UseSameModelForVerification," +
|
||||||
|
"VerificationApiEndpoint,VerificationModelName,VerificationApiKey," +
|
||||||
|
"RevenuePerHighQualityRecord,ElectricityCostPerKwh,SystemPowerWatt," +
|
||||||
|
"ApiCostType,ApiCostPerCall,ApiCostPerBlock,ApiBlockSize " +
|
||||||
|
"FROM AppSettings WHERE Id=1";
|
||||||
|
|
||||||
|
using (var r = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
if (r.Read())
|
||||||
|
return new AppSettings
|
||||||
|
{
|
||||||
|
ApiEndpoint = r.GetValue(0)?.ToString() ?? "",
|
||||||
|
ModelName = r.GetValue(1)?.ToString() ?? "",
|
||||||
|
ApiKey = r.GetValue(2)?.ToString() ?? "",
|
||||||
|
Temperature = r.IsDBNull(3) ? 0.7 : r.GetDouble(3),
|
||||||
|
MaxTokens = r.IsDBNull(4) ? 2048 : r.GetInt32(4),
|
||||||
|
SystemPrompt = r.GetValue(5)?.ToString() ?? "",
|
||||||
|
UserPrompt = r.GetValue(6)?.ToString() ?? "",
|
||||||
|
OutputDirectory = r.GetValue(7)?.ToString() ?? "",
|
||||||
|
OutputFilePrefix = r.GetValue(8)?.ToString() ?? "batch",
|
||||||
|
MaxFileSizeMb = r.IsDBNull(9) ? 250 : r.GetInt32(9),
|
||||||
|
ApiTimeoutSeconds = r.IsDBNull(10) ? 120 : r.GetInt32(10),
|
||||||
|
TimeoutPerTokenRatio = r.IsDBNull(11) ? 0.5 : r.GetDouble(11),
|
||||||
|
EnableQualityVerification = !r.IsDBNull(12) && r.GetInt32(12) != 0,
|
||||||
|
UseSameModelForVerification = r.IsDBNull(13) || r.GetInt32(13) != 0,
|
||||||
|
VerificationApiEndpoint = r.GetValue(14)?.ToString() ?? "",
|
||||||
|
VerificationModelName = r.GetValue(15)?.ToString() ?? "",
|
||||||
|
VerificationApiKey = r.GetValue(16)?.ToString() ?? "",
|
||||||
|
RevenuePerHighQualityRecord = r.IsDBNull(17) ? 0.005 : r.GetDouble(17),
|
||||||
|
ElectricityCostPerKwh = r.IsDBNull(18) ? 0.25 : r.GetDouble(18),
|
||||||
|
SystemPowerWatt = r.IsDBNull(19) ? 350 : r.GetDouble(19),
|
||||||
|
ApiCostType = r.GetValue(20)?.ToString() ?? "Free",
|
||||||
|
ApiCostPerCall = r.IsDBNull(21) ? 0 : r.GetDouble(21),
|
||||||
|
ApiCostPerBlock = r.IsDBNull(22) ? 0 : r.GetDouble(22),
|
||||||
|
ApiBlockSize = r.IsDBNull(23) ? 1000 : r.GetInt32(23),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new AppSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SaveSettings(AppSettings s)
|
||||||
|
{
|
||||||
|
EnsureCreated();
|
||||||
|
using (var conn = new SQLiteConnection(ConnStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText =
|
||||||
|
"UPDATE AppSettings SET " +
|
||||||
|
"ApiEndpoint=@api,ModelName=@model,ApiKey=@apikey," +
|
||||||
|
"Temperature=@temp,MaxTokens=@mt," +
|
||||||
|
"SystemPrompt=@sp,UserPrompt=@up," +
|
||||||
|
"OutputDirectory=@odir,OutputFilePrefix=@opfx,MaxFileSizeMb=@mfsz," +
|
||||||
|
"ApiTimeoutSeconds=@timeout,TimeoutPerTokenRatio=@tokratio," +
|
||||||
|
"EnableQualityVerification=@eqv,UseSameModelForVerification=@usesame," +
|
||||||
|
"VerificationApiEndpoint=@vapi,VerificationModelName=@vmodel,VerificationApiKey=@vapikey," +
|
||||||
|
"RevenuePerHighQualityRecord=@rev," +
|
||||||
|
"ElectricityCostPerKwh=@ecost,SystemPowerWatt=@spow," +
|
||||||
|
"ApiCostType=@actype,ApiCostPerCall=@acall,ApiCostPerBlock=@ablock,ApiBlockSize=@ablk " +
|
||||||
|
"WHERE Id=1";
|
||||||
|
cmd.Parameters.AddWithValue("@api", s.ApiEndpoint);
|
||||||
|
cmd.Parameters.AddWithValue("@model", s.ModelName);
|
||||||
|
cmd.Parameters.AddWithValue("@apikey", s.ApiKey);
|
||||||
|
cmd.Parameters.AddWithValue("@temp", s.Temperature);
|
||||||
|
cmd.Parameters.AddWithValue("@mt", s.MaxTokens);
|
||||||
|
cmd.Parameters.AddWithValue("@sp", s.SystemPrompt);
|
||||||
|
cmd.Parameters.AddWithValue("@up", s.UserPrompt);
|
||||||
|
cmd.Parameters.AddWithValue("@odir", s.OutputDirectory);
|
||||||
|
cmd.Parameters.AddWithValue("@opfx", s.OutputFilePrefix);
|
||||||
|
cmd.Parameters.AddWithValue("@mfsz", s.MaxFileSizeMb);
|
||||||
|
cmd.Parameters.AddWithValue("@timeout", s.ApiTimeoutSeconds);
|
||||||
|
cmd.Parameters.AddWithValue("@tokratio", s.TimeoutPerTokenRatio);
|
||||||
|
cmd.Parameters.AddWithValue("@eqv", s.EnableQualityVerification ? 1 : 0);
|
||||||
|
cmd.Parameters.AddWithValue("@usesame", s.UseSameModelForVerification ? 1 : 0);
|
||||||
|
cmd.Parameters.AddWithValue("@vapi", s.VerificationApiEndpoint);
|
||||||
|
cmd.Parameters.AddWithValue("@vmodel", s.VerificationModelName);
|
||||||
|
cmd.Parameters.AddWithValue("@vapikey", s.VerificationApiKey);
|
||||||
|
cmd.Parameters.AddWithValue("@rev", s.RevenuePerHighQualityRecord);
|
||||||
|
cmd.Parameters.AddWithValue("@ecost", s.ElectricityCostPerKwh);
|
||||||
|
cmd.Parameters.AddWithValue("@spow", s.SystemPowerWatt);
|
||||||
|
cmd.Parameters.AddWithValue("@actype", s.ApiCostType);
|
||||||
|
cmd.Parameters.AddWithValue("@acall", s.ApiCostPerCall);
|
||||||
|
cmd.Parameters.AddWithValue("@ablock", s.ApiCostPerBlock);
|
||||||
|
cmd.Parameters.AddWithValue("@ablk", s.ApiBlockSize);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Telemetry ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public static List<TelemetryLog> QueryAll()
|
||||||
|
{
|
||||||
|
var results = new List<TelemetryLog>();
|
||||||
|
if (!System.IO.File.Exists(DbPath)) return results;
|
||||||
|
using (var conn = new SQLiteConnection(ConnStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText =
|
||||||
|
"SELECT Id,Timestamp,BatchId,ModelUsed,TokensPrompt,TokensCompletion," +
|
||||||
|
"ExecutionTimeMs,OutputPreview,ErrorMessage,IsSuccess " +
|
||||||
|
"FROM TelemetryLogs ORDER BY Id DESC LIMIT 1000";
|
||||||
|
using (var r = cmd.ExecuteReader())
|
||||||
|
{
|
||||||
|
while (r.Read())
|
||||||
|
results.Add(new TelemetryLog
|
||||||
|
{
|
||||||
|
Id = r.GetInt32(0),
|
||||||
|
Timestamp = DateTime.TryParse(r.GetValue(1)?.ToString(), out var ts) ? ts : default,
|
||||||
|
BatchId = r.GetValue(2)?.ToString(),
|
||||||
|
ModelUsed = r.GetValue(3)?.ToString(),
|
||||||
|
TokensPrompt = r.IsDBNull(4) ? 0 : r.GetInt32(4),
|
||||||
|
TokensCompletion = r.IsDBNull(5) ? 0 : r.GetInt32(5),
|
||||||
|
ExecutionTimeMs = r.IsDBNull(6) ? 0 : r.GetInt64(6),
|
||||||
|
OutputPreview = r.GetValue(7)?.ToString(),
|
||||||
|
ErrorMessage = r.GetValue(8)?.ToString(),
|
||||||
|
IsSuccess = !r.IsDBNull(9) && r.GetInt32(9) != 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void InsertLog(TelemetryLog log)
|
||||||
|
{
|
||||||
|
EnsureCreated();
|
||||||
|
using (var conn = new SQLiteConnection(ConnStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
using (var cmd = conn.CreateCommand())
|
||||||
|
{
|
||||||
|
cmd.CommandText =
|
||||||
|
"INSERT INTO TelemetryLogs " +
|
||||||
|
"(Timestamp,BatchId,ModelUsed,TokensPrompt,TokensCompletion," +
|
||||||
|
"ExecutionTimeMs,OutputPreview,ErrorMessage,IsSuccess,QualityScore,Revenue,RecordsInBatch) " +
|
||||||
|
"VALUES (@ts,@bid,@model,@tp,@tc,@et,@op,@err,@ok,0,0,0)";
|
||||||
|
cmd.Parameters.AddWithValue("@ts", log.Timestamp.ToString("o"));
|
||||||
|
cmd.Parameters.AddWithValue("@bid", (object)log.BatchId ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("@model", (object)log.ModelUsed ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("@tp", log.TokensPrompt);
|
||||||
|
cmd.Parameters.AddWithValue("@tc", log.TokensCompletion);
|
||||||
|
cmd.Parameters.AddWithValue("@et", log.ExecutionTimeMs);
|
||||||
|
cmd.Parameters.AddWithValue("@op", (object)log.OutputPreview ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("@err", (object)log.ErrorMessage ?? DBNull.Value);
|
||||||
|
cmd.Parameters.AddWithValue("@ok", log.IsSuccess ? 1 : 0);
|
||||||
|
cmd.ExecuteNonQuery();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Reset ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public static void DeleteAllTelemetry()
|
||||||
|
{
|
||||||
|
if (!System.IO.File.Exists(DbPath)) return;
|
||||||
|
using (var conn = new SQLiteConnection(ConnStr))
|
||||||
|
{
|
||||||
|
conn.Open();
|
||||||
|
using (var cmd = conn.CreateCommand()) { cmd.CommandText = "DELETE FROM TelemetryLogs;"; cmd.ExecuteNonQuery(); }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ResetDatabase()
|
||||||
|
{
|
||||||
|
if (System.IO.File.Exists(DbPath)) System.IO.File.Delete(DbPath);
|
||||||
|
EnsureCreated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||||
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
|
<ProjectGuid>{C455F7DB-C47E-4D71-87D6-9C122F18F986}</ProjectGuid>
|
||||||
|
<OutputType>WinExe</OutputType>
|
||||||
|
<RootNamespace>Dione</RootNamespace>
|
||||||
|
<AssemblyName>Dione</AssemblyName>
|
||||||
|
<TargetFrameworkVersion>v4.8.1</TargetFrameworkVersion>
|
||||||
|
<FileAlignment>512</FileAlignment>
|
||||||
|
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
|
||||||
|
<Deterministic>true</Deterministic>
|
||||||
|
<RestoreProjectStyle>PackageReference</RestoreProjectStyle>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugSymbols>true</DebugSymbols>
|
||||||
|
<DebugType>full</DebugType>
|
||||||
|
<Optimize>false</Optimize>
|
||||||
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
|
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
|
<DebugType>pdbonly</DebugType>
|
||||||
|
<Optimize>true</Optimize>
|
||||||
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
|
<DefineConstants>TRACE</DefineConstants>
|
||||||
|
<ErrorReport>prompt</ErrorReport>
|
||||||
|
<WarningLevel>4</WarningLevel>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Data" />
|
||||||
|
<Reference Include="System.Windows.Forms" />
|
||||||
|
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||||
|
<Reference Include="System.Xml" />
|
||||||
|
<Reference Include="Microsoft.CSharp" />
|
||||||
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Xml.Linq" />
|
||||||
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
|
<Reference Include="System.Xaml">
|
||||||
|
<RequiredTargetFramework>4.0</RequiredTargetFramework>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="WindowsBase" />
|
||||||
|
<Reference Include="PresentationCore" />
|
||||||
|
<Reference Include="PresentationFramework" />
|
||||||
|
<Reference Include="System.Data.SQLite">
|
||||||
|
<HintPath>C:\Users\alber\.nuget\packages\stub.system.data.sqlite.core.netframework\1.0.119\lib\net46\System.Data.SQLite.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="System.Data.SQLite.EF6">
|
||||||
|
<HintPath>C:\Users\alber\.nuget\packages\system.data.sqlite.ef6\1.0.119\lib\net46\System.Data.SQLite.EF6.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="CommunityToolkit.Mvvm">
|
||||||
|
<HintPath>C:\Users\alber\.nuget\packages\communitytoolkit.mvvm\8.4.0\lib\netstandard2.0\CommunityToolkit.Mvvm.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="EntityFramework">
|
||||||
|
<HintPath>C:\Users\alber\.nuget\packages\entityframework\6.5.1\lib\net45\EntityFramework.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="Newtonsoft.Json">
|
||||||
|
<HintPath>C:\Users\alber\.nuget\packages\newtonsoft.json\13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="LiveCharts">
|
||||||
|
<HintPath>C:\Users\alber\.nuget\packages\livecharts\0.9.7\lib\net45\LiveCharts.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
<Reference Include="LiveCharts.Wpf">
|
||||||
|
<HintPath>C:\Users\alber\.nuget\packages\livecharts.wpf\0.9.7\lib\net45\LiveCharts.Wpf.dll</HintPath>
|
||||||
|
</Reference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="EntityFramework" Version="6.5.1" />
|
||||||
|
<PackageReference Include="System.Data.SQLite" Version="1.0.119.0" />
|
||||||
|
<PackageReference Include="System.Data.SQLite.EF6" Version="1.0.119.0" />
|
||||||
|
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
|
||||||
|
<PackageReference Include="MaterialDesignThemes" Version="4.9.0" />
|
||||||
|
<PackageReference Include="LiveCharts.Wpf" Version="0.9.7" />
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ApplicationDefinition Include="App.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</ApplicationDefinition>
|
||||||
|
<Compile Include="Converters\InverseBoolConverter.cs" />
|
||||||
|
<Compile Include="Models\AppSettings.cs" />
|
||||||
|
<Compile Include="ViewModels\LiveGenerationViewModel.cs" />
|
||||||
|
<Compile Include="ViewModels\TelemetryHistoryViewModel.cs" />
|
||||||
|
<Compile Include="Views\DataDesignerView.xaml.cs">
|
||||||
|
<DependentUpon>DataDesignerView.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Views\LiveGenerationView.xaml.cs">
|
||||||
|
<DependentUpon>LiveGenerationView.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Views\SettingsView.xaml.cs">
|
||||||
|
<DependentUpon>SettingsView.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Views\TelemetryHistoryView.xaml.cs">
|
||||||
|
<DependentUpon>TelemetryHistoryView.xaml</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Page Include="Views\DashboardView.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
|
<Page Include="Views\DataDesignerView.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
<Page Include="Views\LiveGenerationView.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
<Page Include="Views\PlaceholderView.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
|
<Page Include="MainWindow.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Page>
|
||||||
|
<Compile Include="App.xaml.cs">
|
||||||
|
<DependentUpon>App.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Data\SynthDataDbContext.cs" />
|
||||||
|
<Compile Include="Dione\Models\DataProject.cs" />
|
||||||
|
<Compile Include="Dione\Models\SchemaColumn.cs" />
|
||||||
|
<Compile Include="Dione\Models\TelemetryLog.cs" />
|
||||||
|
<Compile Include="Dione\ViewModels\MainWindowViewModel.cs" />
|
||||||
|
<Compile Include="Dione\Views\DashboardView.xaml.cs" />
|
||||||
|
<Compile Include="Dione\Views\PlaceholderView.xaml.cs" />
|
||||||
|
<Compile Include="Models\DataProject.cs" />
|
||||||
|
<Compile Include="Models\SchemaColumn.cs" />
|
||||||
|
<Compile Include="Models\TelemetryLog.cs" />
|
||||||
|
<Compile Include="ViewModels\DashboardViewModel.cs" />
|
||||||
|
<Compile Include="ViewModels\MainWindowViewModel.cs" />
|
||||||
|
<Compile Include="ViewModels\SettingsViewModel.cs" />
|
||||||
|
<Compile Include="Views\DashboardView.xaml.cs">
|
||||||
|
<DependentUpon>DashboardView.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Views\PlaceholderView.xaml.cs">
|
||||||
|
<DependentUpon>PlaceholderView.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="MainWindow.xaml.cs">
|
||||||
|
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Compile>
|
||||||
|
<Page Include="Views\SettingsView.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
<Page Include="Views\TelemetryHistoryView.xaml">
|
||||||
|
<Generator>MSBuild:Compile</Generator>
|
||||||
|
</Page>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Properties\Settings.Designer.cs">
|
||||||
|
<AutoGen>True</AutoGen>
|
||||||
|
<DependentUpon>Settings.settings</DependentUpon>
|
||||||
|
<DesignTimeSharedInput>True</DesignTimeSharedInput>
|
||||||
|
</Compile>
|
||||||
|
<EmbeddedResource Include="Properties\Resources.resx">
|
||||||
|
<Generator>ResXFileCodeGenerator</Generator>
|
||||||
|
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
|
||||||
|
</EmbeddedResource>
|
||||||
|
<None Include="Dione\Dione_new.csproj" />
|
||||||
|
<None Include="Properties\Settings.settings">
|
||||||
|
<Generator>SettingsSingleFileGenerator</Generator>
|
||||||
|
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<None Include="App.config" />
|
||||||
|
</ItemGroup>
|
||||||
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<Solution>
|
||||||
|
<Project Path="Dione.csproj" Id="c455f7db-c47e-4d71-87d6-9c122f18f986" />
|
||||||
|
</Solution>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<Project>
|
||||||
|
<!-- Fix for WPF _wpftmp project not resolving PackageReference assemblies -->
|
||||||
|
<Target Name="FixWpfTmpProjectPackageReferences"
|
||||||
|
BeforeTargets="MarkupCompilePass1;MarkupCompilePass2"
|
||||||
|
Condition="$(MSBuildProjectFile.Contains('_wpftmp'))">
|
||||||
|
<ItemGroup>
|
||||||
|
<ReferencePath Include="$(NuGetPackageRoot)communitytoolkit.mvvm\8.4.0\lib\netstandard2.0\CommunityToolkit.Mvvm.dll" />
|
||||||
|
<ReferencePath Include="$(NuGetPackageRoot)entityframework\6.5.1\lib\net45\EntityFramework.dll" />
|
||||||
|
<ReferencePath Include="$(NuGetPackageRoot)stub.system.data.sqlite.core.netframework\1.0.119\lib\net46\System.Data.SQLite.dll" />
|
||||||
|
<ReferencePath Include="$(NuGetPackageRoot)system.data.sqlite.ef6\1.0.119\lib\net46\System.Data.SQLite.EF6.dll" />
|
||||||
|
<ReferencePath Include="$(NuGetPackageRoot)newtonsoft.json\13.0.3\lib\net45\Newtonsoft.Json.dll" />
|
||||||
|
<ReferencePath Include="$(NuGetPackageRoot)livecharts\0.9.7\lib\net45\LiveCharts.dll" />
|
||||||
|
<ReferencePath Include="$(NuGetPackageRoot)livecharts.wpf\0.9.7\lib\net45\LiveCharts.Wpf.dll" />
|
||||||
|
<ReferencePath Include="$(FrameworkPathOverride)\System.Windows.Forms.dll" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,182 @@
|
|||||||
|
<Window x:Class="Dione.MainWindow"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:local="clr-namespace:Dione"
|
||||||
|
xmlns:vm="clr-namespace:Dione.ViewModels"
|
||||||
|
xmlns:views="clr-namespace:Dione.Views"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
Title="SynthData Pro"
|
||||||
|
Height="720" Width="1280"
|
||||||
|
WindowStyle="None"
|
||||||
|
AllowsTransparency="True"
|
||||||
|
Background="Transparent"
|
||||||
|
ResizeMode="CanResizeWithGrip"
|
||||||
|
WindowStartupLocation="CenterScreen"
|
||||||
|
UseLayoutRounding="True"
|
||||||
|
SnapsToDevicePixels="True"
|
||||||
|
TextOptions.TextFormattingMode="Display">
|
||||||
|
|
||||||
|
<Window.DataContext>
|
||||||
|
<vm:MainWindowViewModel/>
|
||||||
|
</Window.DataContext>
|
||||||
|
|
||||||
|
<Window.Resources>
|
||||||
|
<DataTemplate DataType="{x:Type vm:DashboardViewModel}">
|
||||||
|
<views:DashboardView/>
|
||||||
|
</DataTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type vm:SettingsViewModel}">
|
||||||
|
<views:SettingsView/>
|
||||||
|
</DataTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type vm:LiveGenerationViewModel}">
|
||||||
|
<views:LiveGenerationView/>
|
||||||
|
</DataTemplate>
|
||||||
|
<DataTemplate DataType="{x:Type vm:TelemetryHistoryViewModel}">
|
||||||
|
<views:TelemetryHistoryView/>
|
||||||
|
</DataTemplate>
|
||||||
|
|
||||||
|
<!-- Sidebar button style -->
|
||||||
|
<Style x:Key="SidebarBtn" TargetType="Button">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
<Setter Property="Foreground" Value="#78909C"/>
|
||||||
|
<Setter Property="FontSize" Value="13"/>
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Left"/>
|
||||||
|
<Setter Property="Padding" Value="20,12"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border x:Name="Bd" Background="{TemplateBinding Background}"
|
||||||
|
Padding="{TemplateBinding Padding}" CornerRadius="8" Margin="6,2">
|
||||||
|
<ContentPresenter HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="Bd" Property="Background" Value="#2A2A3E"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
</Window.Resources>
|
||||||
|
|
||||||
|
<Border CornerRadius="12" Background="#121218" BorderBrush="#2A2A3E" BorderThickness="1">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="220"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- SIDEBAR -->
|
||||||
|
<Border Grid.Column="0" Background="#16161E" CornerRadius="12,0,0,12">
|
||||||
|
<DockPanel>
|
||||||
|
<!-- App Title Bar -->
|
||||||
|
<Border DockPanel.Dock="Top" Padding="16,14" MouseLeftButtonDown="TitleBar_MouseLeftButtonDown">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="SynthData Pro" FontSize="18" FontWeight="Bold" Foreground="White"/>
|
||||||
|
<TextBlock Text="Local LLM Data Generator" FontSize="10" Foreground="#546E7A" Margin="0,2,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Separator DockPanel.Dock="Top" Background="#2A2A3E" Margin="16,0"/>
|
||||||
|
|
||||||
|
<!-- Navigation -->
|
||||||
|
<StackPanel DockPanel.Dock="Top" Margin="0,8,0,0">
|
||||||
|
<Button Style="{StaticResource SidebarBtn}" Command="{Binding NavigateCommand}" CommandParameter="Dashboard">
|
||||||
|
<StackPanel Orientation="Horizontal"><TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="16" VerticalAlignment="Center" Margin="0,0,10,0"/><TextBlock Text="Dashboard" VerticalAlignment="Center"/></StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button Style="{StaticResource SidebarBtn}" Command="{Binding NavigateCommand}" CommandParameter="LiveGeneration">
|
||||||
|
<StackPanel Orientation="Horizontal"><TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="16" VerticalAlignment="Center" Margin="0,0,10,0"/><TextBlock Text="Generazione" VerticalAlignment="Center"/></StackPanel>
|
||||||
|
</Button>
|
||||||
|
<Button Style="{StaticResource SidebarBtn}" Command="{Binding NavigateCommand}" CommandParameter="Telemetry">
|
||||||
|
<StackPanel Orientation="Horizontal"><TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="16" VerticalAlignment="Center" Margin="0,0,10,0"/><TextBlock Text="Telemetria" VerticalAlignment="Center"/></StackPanel>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Settings at bottom -->
|
||||||
|
<Button DockPanel.Dock="Bottom" Style="{StaticResource SidebarBtn}" Command="{Binding NavigateCommand}" CommandParameter="Settings" Margin="0,0,0,8">
|
||||||
|
<StackPanel Orientation="Horizontal"><TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="16" VerticalAlignment="Center" Margin="0,0,10,0"/><TextBlock Text="Impostazioni" VerticalAlignment="Center"/></StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<!-- Version footer -->
|
||||||
|
<TextBlock DockPanel.Dock="Bottom" Text="v1.0.0-alpha" FontSize="10"
|
||||||
|
Foreground="#37474F" HorizontalAlignment="Center" Margin="0,0,0,12"/>
|
||||||
|
<Border/> <!-- spacer -->
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- MAIN CONTENT -->
|
||||||
|
<DockPanel Grid.Column="1">
|
||||||
|
<!-- Custom Title Bar -->
|
||||||
|
<Border DockPanel.Dock="Top" Height="40" Background="#16161E"
|
||||||
|
CornerRadius="0,12,0,0" MouseLeftButtonDown="TitleBar_MouseLeftButtonDown">
|
||||||
|
<DockPanel LastChildFill="False">
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" Margin="0,0,8,0">
|
||||||
|
<Button Content="" FontFamily="Segoe MDL2 Assets" Width="40" Height="28" FontSize="10"
|
||||||
|
Command="{Binding MinimizeCommand}" Cursor="Hand">
|
||||||
|
<Button.Template>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border x:Name="Bd" Background="Transparent" CornerRadius="4">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="Bd" Property="Background" Value="#33FFFFFF"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Button.Template>
|
||||||
|
<Button.Foreground>
|
||||||
|
<SolidColorBrush Color="#B0BEC5"/>
|
||||||
|
</Button.Foreground>
|
||||||
|
</Button>
|
||||||
|
<Button Content="" FontFamily="Segoe MDL2 Assets" Width="40" Height="28" FontSize="10"
|
||||||
|
Command="{Binding MaximizeCommand}" Cursor="Hand">
|
||||||
|
<Button.Template>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border x:Name="Bd" Background="Transparent" CornerRadius="4">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="Bd" Property="Background" Value="#33FFFFFF"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Button.Template>
|
||||||
|
<Button.Foreground>
|
||||||
|
<SolidColorBrush Color="#B0BEC5"/>
|
||||||
|
</Button.Foreground>
|
||||||
|
</Button>
|
||||||
|
<Button Content="" FontFamily="Segoe MDL2 Assets" Width="40" Height="28" FontSize="10"
|
||||||
|
Command="{Binding CloseCommand}" Cursor="Hand">
|
||||||
|
<Button.Template>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border x:Name="Bd" Background="Transparent" CornerRadius="4">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="Bd" Property="Background" Value="#C62828"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Button.Template>
|
||||||
|
<Button.Foreground>
|
||||||
|
<SolidColorBrush Color="#EF5350"/>
|
||||||
|
</Button.Foreground>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Content Area -->
|
||||||
|
<ContentControl Content="{Binding CurrentView}" Margin="0"/>
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
</Window>
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Interop;
|
||||||
|
|
||||||
|
namespace Dione
|
||||||
|
{
|
||||||
|
public partial class MainWindow : Window
|
||||||
|
{
|
||||||
|
public MainWindow()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
SourceInitialized += OnSourceInitialized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.ClickCount == 2)
|
||||||
|
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
|
||||||
|
else
|
||||||
|
DragMove();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSourceInitialized(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
var handle = new WindowInteropHelper(this).Handle;
|
||||||
|
HwndSource.FromHwnd(handle)?.AddHook(WndProc);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
|
||||||
|
{
|
||||||
|
// WM_GETMINMAXINFO
|
||||||
|
if (msg == 0x0024)
|
||||||
|
{
|
||||||
|
var mmi = Marshal.PtrToStructure<MINMAXINFO>(lParam);
|
||||||
|
|
||||||
|
var monitor = MonitorFromWindow(hwnd, 0x00000002 /* MONITOR_DEFAULTTONEAREST */);
|
||||||
|
if (monitor != IntPtr.Zero)
|
||||||
|
{
|
||||||
|
var monitorInfo = new MONITORINFO { cbSize = Marshal.SizeOf<MONITORINFO>() };
|
||||||
|
if (GetMonitorInfo(monitor, ref monitorInfo))
|
||||||
|
{
|
||||||
|
var work = monitorInfo.rcWork;
|
||||||
|
var area = monitorInfo.rcMonitor;
|
||||||
|
|
||||||
|
mmi.ptMaxPosition.x = work.left - area.left;
|
||||||
|
mmi.ptMaxPosition.y = work.top - area.top;
|
||||||
|
mmi.ptMaxSize.x = work.right - work.left;
|
||||||
|
mmi.ptMaxSize.y = work.bottom - work.top;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Marshal.StructureToPtr(mmi, lParam, true);
|
||||||
|
handled = true;
|
||||||
|
}
|
||||||
|
return IntPtr.Zero;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi);
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct POINT { public int x, y; }
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct RECT { public int left, top, right, bottom; }
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct MINMAXINFO
|
||||||
|
{
|
||||||
|
public POINT ptReserved;
|
||||||
|
public POINT ptMaxSize;
|
||||||
|
public POINT ptMaxPosition;
|
||||||
|
public POINT ptMaxTrackSize;
|
||||||
|
public POINT ptMinTrackSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
private struct MONITORINFO
|
||||||
|
{
|
||||||
|
public int cbSize;
|
||||||
|
public RECT rcMonitor;
|
||||||
|
public RECT rcWork;
|
||||||
|
public uint dwFlags;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
namespace Dione.Models
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configurazione globale dell'applicazione — sempre un solo record (Id = 1).
|
||||||
|
/// </summary>
|
||||||
|
public class AppSettings
|
||||||
|
{
|
||||||
|
public int Id { get; set; } = 1;
|
||||||
|
|
||||||
|
// ?? API principale ??
|
||||||
|
public string ApiEndpoint { get; set; } = "http://127.0.0.1:1234/v1/chat/completions";
|
||||||
|
public string ModelName { get; set; } = "";
|
||||||
|
public string ApiKey { get; set; } = "";
|
||||||
|
public double Temperature { get; set; } = 0.7;
|
||||||
|
public int MaxTokens { get; set; } = 2048;
|
||||||
|
|
||||||
|
// ?? Prompt ??
|
||||||
|
public string SystemPrompt { get; set; } = "";
|
||||||
|
public string UserPrompt { get; set; } = "";
|
||||||
|
|
||||||
|
// ?? Output ??
|
||||||
|
public string OutputDirectory { get; set; } = "";
|
||||||
|
public string OutputFilePrefix { get; set; } = "batch";
|
||||||
|
public int MaxFileSizeMb { get; set; } = 250;
|
||||||
|
|
||||||
|
// ?? Timeout ??
|
||||||
|
public int ApiTimeoutSeconds { get; set; } = 120;
|
||||||
|
public double TimeoutPerTokenRatio { get; set; } = 0.5;
|
||||||
|
|
||||||
|
// ?? Verifica qualità ??
|
||||||
|
public bool EnableQualityVerification { get; set; } = false;
|
||||||
|
public bool UseSameModelForVerification { get; set; } = true;
|
||||||
|
public string VerificationApiEndpoint { get; set; } = "";
|
||||||
|
public string VerificationModelName { get; set; } = "";
|
||||||
|
public string VerificationApiKey { get; set; } = "";
|
||||||
|
public double RevenuePerHighQualityRecord { get; set; } = 0.005;
|
||||||
|
|
||||||
|
// ?? Costi ??
|
||||||
|
public double ElectricityCostPerKwh { get; set; } = 0.25;
|
||||||
|
public double SystemPowerWatt { get; set; } = 350;
|
||||||
|
public string ApiCostType { get; set; } = "Free";
|
||||||
|
public double ApiCostPerCall { get; set; } = 0.0;
|
||||||
|
public double ApiCostPerBlock { get; set; } = 0.0;
|
||||||
|
public int ApiBlockSize { get; set; } = 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Dione.Models
|
||||||
|
{
|
||||||
|
[Table("DataProjects")]
|
||||||
|
public class DataProject
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
[Required, MaxLength(200)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(512)]
|
||||||
|
public string Description { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(512)]
|
||||||
|
public string ApiEndpoint { get; set; } = "http://127.0.0.1:1234/v1/";
|
||||||
|
|
||||||
|
[MaxLength(128)]
|
||||||
|
public string ModelName { get; set; }
|
||||||
|
|
||||||
|
/// <summary>API Key per servizi remoti (OpenAI, Anthropic, Google AI, etc.)</summary>
|
||||||
|
[MaxLength(512)]
|
||||||
|
public string ApiKey { get; set; } = "";
|
||||||
|
|
||||||
|
public double Temperature { get; set; } = 0.7;
|
||||||
|
|
||||||
|
public int MaxTokens { get; set; } = 2048;
|
||||||
|
|
||||||
|
[MaxLength(16)]
|
||||||
|
public string OutputFormat { get; set; } = "JSON";
|
||||||
|
|
||||||
|
public string SystemPrompt { get; set; }
|
||||||
|
|
||||||
|
public string FewShotExamples { get; set; }
|
||||||
|
|
||||||
|
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
|
public DateTime? LastRunAt { get; set; }
|
||||||
|
|
||||||
|
public int RecordsPerFile { get; set; } = 100;
|
||||||
|
|
||||||
|
[MaxLength(512)]
|
||||||
|
public string OutputDirectory { get; set; } = "";
|
||||||
|
|
||||||
|
[MaxLength(256)]
|
||||||
|
public string OutputFileName { get; set; } = "fin_ticks";
|
||||||
|
|
||||||
|
public bool ContinuousMode { get; set; } = true;
|
||||||
|
|
||||||
|
public int MinFieldCount { get; set; } = 120;
|
||||||
|
|
||||||
|
[MaxLength(16)]
|
||||||
|
public string FieldSeparator { get; set; } = "|";
|
||||||
|
|
||||||
|
// ?? Cost Management ??
|
||||||
|
/// <summary>Costo elettricità in €/kWh</summary>
|
||||||
|
public double ElectricityCostPerKwh { get; set; } = 0.25;
|
||||||
|
|
||||||
|
/// <summary>Potenza stimata del sistema in Watt durante generazione</summary>
|
||||||
|
public double SystemPowerWatt { get; set; } = 350;
|
||||||
|
|
||||||
|
/// <summary>Costo API AI per chiamata (€)</summary>
|
||||||
|
public double ApiCostPerCall { get; set; } = 0.0;
|
||||||
|
|
||||||
|
/// <summary>Costo API AI per blocco di N chiamate (€)</summary>
|
||||||
|
public double ApiCostPerBlock { get; set; } = 0.0;
|
||||||
|
|
||||||
|
/// <summary>Numero chiamate per blocco tariffario</summary>
|
||||||
|
public int ApiBlockSize { get; set; } = 1000;
|
||||||
|
|
||||||
|
/// <summary>Tipo tariffazione: PerCall, PerBlock, Free</summary>
|
||||||
|
[MaxLength(16)]
|
||||||
|
public string ApiCostType { get; set; } = "Free";
|
||||||
|
|
||||||
|
// ?? Revenue / Verification ??
|
||||||
|
/// <summary>Endpoint AI di verifica (può essere lo stesso o diverso)</summary>
|
||||||
|
[MaxLength(512)]
|
||||||
|
public string VerificationApiEndpoint { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>Modello AI per verifica qualità</summary>
|
||||||
|
[MaxLength(128)]
|
||||||
|
public string VerificationModelName { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>API Key per servizio verifica qualità</summary>
|
||||||
|
[MaxLength(512)]
|
||||||
|
public string VerificationApiKey { get; set; } = "";
|
||||||
|
|
||||||
|
/// <summary>Se true, usa stesso endpoint/modello/key della generazione per la verifica</summary>
|
||||||
|
public bool UseSameModelForVerification { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>Abilita verifica qualità automatica</summary>
|
||||||
|
public bool EnableQualityVerification { get; set; } = false;
|
||||||
|
|
||||||
|
/// <summary>Valore base per record di alta qualità (€)</summary>
|
||||||
|
public double RevenuePerHighQualityRecord { get; set; } = 0.005;
|
||||||
|
|
||||||
|
/// <summary>Timeout base per chiamata API in secondi</summary>
|
||||||
|
public int ApiTimeoutSeconds { get; set; } = 120;
|
||||||
|
|
||||||
|
/// <summary>Timeout aggiuntivo per token (secondi per ogni 100 token)</summary>
|
||||||
|
public double TimeoutPerTokenRatio { get; set; } = 0.5;
|
||||||
|
|
||||||
|
public virtual ICollection<SchemaColumn> SchemaColumns { get; set; } = new List<SchemaColumn>();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Dione.Models
|
||||||
|
{
|
||||||
|
[Table("SchemaColumns")]
|
||||||
|
public class SchemaColumn
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public int DataProjectId { get; set; }
|
||||||
|
|
||||||
|
[Required, MaxLength(128)]
|
||||||
|
public string ColumnName { get; set; }
|
||||||
|
|
||||||
|
[Required, MaxLength(64)]
|
||||||
|
public string DataType { get; set; }
|
||||||
|
|
||||||
|
public int SortOrder { get; set; }
|
||||||
|
|
||||||
|
[ForeignKey(nameof(DataProjectId))]
|
||||||
|
public virtual DataProject DataProject { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Dione.Models
|
||||||
|
{
|
||||||
|
[Table("TelemetryLogs")]
|
||||||
|
public class TelemetryLog
|
||||||
|
{
|
||||||
|
[Key]
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||||
|
|
||||||
|
[MaxLength(64)]
|
||||||
|
public string BatchId { get; set; }
|
||||||
|
|
||||||
|
public int Seed { get; set; }
|
||||||
|
|
||||||
|
public string Prompt { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(128)]
|
||||||
|
public string ModelUsed { get; set; }
|
||||||
|
|
||||||
|
public int TokensPrompt { get; set; }
|
||||||
|
|
||||||
|
public int TokensCompletion { get; set; }
|
||||||
|
|
||||||
|
public long ExecutionTimeMs { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(1000)]
|
||||||
|
public string OutputPreview { get; set; }
|
||||||
|
|
||||||
|
[MaxLength(2000)]
|
||||||
|
public string ErrorMessage { get; set; }
|
||||||
|
|
||||||
|
public bool IsSuccess { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+71
@@ -0,0 +1,71 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// Codice generato da uno strumento.
|
||||||
|
// Versione runtime:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Le modifiche apportate a questo file possono causare un comportamento non corretto e andranno perse se
|
||||||
|
// il codice viene rigenerato.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Dione.Properties
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Classe di risorse fortemente tipizzata per la ricerca di stringhe localizzate e così via.
|
||||||
|
/// </summary>
|
||||||
|
// Questa classe è stata generata automaticamente dalla classe StronglyTypedResourceBuilder
|
||||||
|
// tramite uno strumento quale ResGen o Visual Studio.
|
||||||
|
// Per aggiungere o rimuovere un membro, modificare il file .ResX, quindi eseguire di nuovo ResGen
|
||||||
|
// con l'opzione /str oppure ricompilare il progetto VS.
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
internal class Resources
|
||||||
|
{
|
||||||
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
|
internal Resources()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Restituisce l'istanza di ResourceManager memorizzata nella cache e usata da questa classe.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Resources.ResourceManager ResourceManager
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if ((resourceMan == null))
|
||||||
|
{
|
||||||
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Dione.Properties.Resources", typeof(Resources).Assembly);
|
||||||
|
resourceMan = temp;
|
||||||
|
}
|
||||||
|
return resourceMan;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Esegue l'override della proprietà CurrentUICulture del thread corrente per tutte
|
||||||
|
/// le ricerche di risorse che utilizzano questa classe di risorse fortemente tipizzata.
|
||||||
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
|
internal static global::System.Globalization.CultureInfo Culture
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return resourceCulture;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
resourceCulture = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<root>
|
||||||
|
<!--
|
||||||
|
Microsoft ResX Schema
|
||||||
|
|
||||||
|
Version 2.0
|
||||||
|
|
||||||
|
The primary goals of this format is to allow a simple XML format
|
||||||
|
that is mostly human readable. The generation and parsing of the
|
||||||
|
various data types are done through the TypeConverter classes
|
||||||
|
associated with the data types.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
... ado.net/XML headers & schema ...
|
||||||
|
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||||
|
<resheader name="version">2.0</resheader>
|
||||||
|
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||||
|
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||||
|
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||||
|
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||||
|
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||||
|
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||||
|
</data>
|
||||||
|
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||||
|
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||||
|
<comment>This is a comment</comment>
|
||||||
|
</data>
|
||||||
|
|
||||||
|
There are any number of "resheader" rows that contain simple
|
||||||
|
name/value pairs.
|
||||||
|
|
||||||
|
Each data row contains a name, and value. The row also contains a
|
||||||
|
type or mimetype. Type corresponds to a .NET class that support
|
||||||
|
text/value conversion through the TypeConverter architecture.
|
||||||
|
Classes that don't support this are serialized and stored with the
|
||||||
|
mimetype set.
|
||||||
|
|
||||||
|
The mimetype is used for serialized objects, and tells the
|
||||||
|
ResXResourceReader how to depersist the object. This is currently not
|
||||||
|
extensible. For a given mimetype the value must be set accordingly:
|
||||||
|
|
||||||
|
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||||
|
that the ResXResourceWriter will generate, however the reader can
|
||||||
|
read any of the formats listed below.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.binary.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Serialization.Formatters.Binary.BinaryFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.soap.base64
|
||||||
|
value : The object must be serialized with
|
||||||
|
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
|
||||||
|
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||||
|
value : The object must be serialized into a byte array
|
||||||
|
: using a System.ComponentModel.TypeConverter
|
||||||
|
: and then encoded with base64 encoding.
|
||||||
|
-->
|
||||||
|
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||||
|
<xsd:element name="root" msdata:IsDataSet="true">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:choice maxOccurs="unbounded">
|
||||||
|
<xsd:element name="metadata">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="assembly">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:attribute name="alias" type="xsd:string" />
|
||||||
|
<xsd:attribute name="name" type="xsd:string" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="data">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
|
||||||
|
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||||
|
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
<xsd:element name="resheader">
|
||||||
|
<xsd:complexType>
|
||||||
|
<xsd:sequence>
|
||||||
|
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||||
|
</xsd:sequence>
|
||||||
|
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:choice>
|
||||||
|
</xsd:complexType>
|
||||||
|
</xsd:element>
|
||||||
|
</xsd:schema>
|
||||||
|
<resheader name="resmimetype">
|
||||||
|
<value>text/microsoft-resx</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="version">
|
||||||
|
<value>2.0</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="reader">
|
||||||
|
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
<resheader name="writer">
|
||||||
|
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||||
|
</resheader>
|
||||||
|
</root>
|
||||||
Generated
+30
@@ -0,0 +1,30 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
// Runtime Version:4.0.30319.42000
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace Dione.Properties
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
||||||
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
||||||
|
{
|
||||||
|
|
||||||
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
|
public static Settings Default
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return defaultInstance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
|
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
|
||||||
|
<Profiles>
|
||||||
|
<Profile Name="(Default)" />
|
||||||
|
</Profiles>
|
||||||
|
<Settings />
|
||||||
|
</SettingsFile>
|
||||||
@@ -0,0 +1,199 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using LiveCharts;
|
||||||
|
using LiveCharts.Wpf;
|
||||||
|
using Dione.Data;
|
||||||
|
|
||||||
|
namespace Dione.ViewModels
|
||||||
|
{
|
||||||
|
public class DashboardViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private long _totalTokensSpent;
|
||||||
|
public long TotalTokensSpent
|
||||||
|
{
|
||||||
|
get => _totalTokensSpent;
|
||||||
|
set => SetProperty(ref _totalTokensSpent, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int _validRecords;
|
||||||
|
public int ValidRecords
|
||||||
|
{
|
||||||
|
get => _validRecords;
|
||||||
|
set => SetProperty(ref _validRecords, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double _estimatedCostEur;
|
||||||
|
public double EstimatedCostEur
|
||||||
|
{
|
||||||
|
get => _estimatedCostEur;
|
||||||
|
set => SetProperty(ref _estimatedCostEur, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double _qualityScore;
|
||||||
|
public double QualityScore
|
||||||
|
{
|
||||||
|
get => _qualityScore;
|
||||||
|
set => SetProperty(ref _qualityScore, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SeriesCollection _tokensPerMinuteSeries;
|
||||||
|
public SeriesCollection TokensPerMinuteSeries
|
||||||
|
{
|
||||||
|
get => _tokensPerMinuteSeries;
|
||||||
|
set => SetProperty(ref _tokensPerMinuteSeries, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] _tokensPerMinuteLabels;
|
||||||
|
public string[] TokensPerMinuteLabels
|
||||||
|
{
|
||||||
|
get => _tokensPerMinuteLabels;
|
||||||
|
set => SetProperty(ref _tokensPerMinuteLabels, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SeriesCollection _successErrorSeries;
|
||||||
|
public SeriesCollection SuccessErrorSeries
|
||||||
|
{
|
||||||
|
get => _successErrorSeries;
|
||||||
|
set => SetProperty(ref _successErrorSeries, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SeriesCollection _latencySeries;
|
||||||
|
public SeriesCollection LatencySeries
|
||||||
|
{
|
||||||
|
get => _latencySeries;
|
||||||
|
set => SetProperty(ref _latencySeries, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string[] _latencyLabels;
|
||||||
|
public string[] LatencyLabels
|
||||||
|
{
|
||||||
|
get => _latencyLabels;
|
||||||
|
set => SetProperty(ref _latencyLabels, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public RelayCommand RefreshCommand { get; }
|
||||||
|
|
||||||
|
public DashboardViewModel()
|
||||||
|
{
|
||||||
|
RefreshCommand = new RelayCommand(RefreshFromDb);
|
||||||
|
|
||||||
|
// Initialize with empty series
|
||||||
|
TokensPerMinuteSeries = new SeriesCollection
|
||||||
|
{
|
||||||
|
new LineSeries { Title = "Tokens/min", Values = new ChartValues<double>() }
|
||||||
|
};
|
||||||
|
TokensPerMinuteLabels = Array.Empty<string>();
|
||||||
|
|
||||||
|
SuccessErrorSeries = new SeriesCollection
|
||||||
|
{
|
||||||
|
new PieSeries { Title = "Success", Values = new ChartValues<double> { 0 }, DataLabels = true },
|
||||||
|
new PieSeries { Title = "API Error", Values = new ChartValues<double> { 0 }, DataLabels = true },
|
||||||
|
new PieSeries { Title = "Parse Fail", Values = new ChartValues<double> { 0 }, DataLabels = true }
|
||||||
|
};
|
||||||
|
|
||||||
|
LatencySeries = new SeriesCollection
|
||||||
|
{
|
||||||
|
new ColumnSeries { Title = "Latency (ms)", Values = new ChartValues<double>() }
|
||||||
|
};
|
||||||
|
LatencyLabels = Array.Empty<string>();
|
||||||
|
|
||||||
|
RefreshFromDb();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshFromDb()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var logs = SynthDataDbContext.QueryAll();
|
||||||
|
|
||||||
|
if (logs.Count == 0)
|
||||||
|
{
|
||||||
|
TotalTokensSpent = 0;
|
||||||
|
ValidRecords = 0;
|
||||||
|
EstimatedCostEur = 0;
|
||||||
|
QualityScore = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- KPI Cards ---
|
||||||
|
long totalPrompt = logs.Sum(l => l.TokensPrompt);
|
||||||
|
long totalCompletion = logs.Sum(l => l.TokensCompletion);
|
||||||
|
TotalTokensSpent = totalPrompt + totalCompletion;
|
||||||
|
|
||||||
|
int successCount = logs.Count(l => l.IsSuccess);
|
||||||
|
ValidRecords = successCount;
|
||||||
|
|
||||||
|
// Estimate electric cost: ~0.3 kWh GPU idle, rough 0.0003 EUR/token
|
||||||
|
double totalSeconds = logs.Sum(l => l.ExecutionTimeMs) / 1000.0;
|
||||||
|
double kWh = (totalSeconds / 3600.0) * 0.3;
|
||||||
|
EstimatedCostEur = Math.Round(kWh * 0.25, 4);
|
||||||
|
|
||||||
|
int totalCount = logs.Count;
|
||||||
|
QualityScore = totalCount > 0
|
||||||
|
? Math.Round(100.0 * successCount / totalCount, 1)
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
// --- Tokens per minute (group by minute) ---
|
||||||
|
var byMinute = logs
|
||||||
|
.Where(l => l.Timestamp != default)
|
||||||
|
.GroupBy(l => new DateTime(l.Timestamp.Year, l.Timestamp.Month, l.Timestamp.Day,
|
||||||
|
l.Timestamp.Hour, l.Timestamp.Minute, 0))
|
||||||
|
.OrderBy(g => g.Key)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var tpmValues = new ChartValues<double>();
|
||||||
|
var tpmLabels = new List<string>();
|
||||||
|
foreach (var g in byMinute)
|
||||||
|
{
|
||||||
|
tpmValues.Add(g.Sum(x => x.TokensPrompt + x.TokensCompletion));
|
||||||
|
tpmLabels.Add(g.Key.ToString("HH:mm"));
|
||||||
|
}
|
||||||
|
|
||||||
|
TokensPerMinuteSeries = new SeriesCollection
|
||||||
|
{
|
||||||
|
new LineSeries
|
||||||
|
{
|
||||||
|
Title = "Tokens/min",
|
||||||
|
Values = tpmValues,
|
||||||
|
PointGeometry = null
|
||||||
|
}
|
||||||
|
};
|
||||||
|
TokensPerMinuteLabels = tpmLabels.ToArray();
|
||||||
|
|
||||||
|
// --- Success vs Error pie ---
|
||||||
|
int apiErrors = logs.Count(l => !l.IsSuccess && !string.IsNullOrEmpty(l.ErrorMessage)
|
||||||
|
&& l.ErrorMessage.IndexOf("parse", StringComparison.OrdinalIgnoreCase) < 0);
|
||||||
|
int parseErrors = logs.Count(l => !l.IsSuccess && !string.IsNullOrEmpty(l.ErrorMessage)
|
||||||
|
&& l.ErrorMessage.IndexOf("parse", StringComparison.OrdinalIgnoreCase) >= 0);
|
||||||
|
int otherErrors = totalCount - successCount - apiErrors - parseErrors;
|
||||||
|
apiErrors += otherErrors;
|
||||||
|
|
||||||
|
SuccessErrorSeries = new SeriesCollection
|
||||||
|
{
|
||||||
|
new PieSeries { Title = "Success", Values = new ChartValues<double> { successCount }, DataLabels = true },
|
||||||
|
new PieSeries { Title = "API Error", Values = new ChartValues<double> { apiErrors }, DataLabels = true },
|
||||||
|
new PieSeries { Title = "Parse Fail", Values = new ChartValues<double> { parseErrors }, DataLabels = true }
|
||||||
|
};
|
||||||
|
|
||||||
|
// --- Latency bar chart (last 20 requests) ---
|
||||||
|
var recent = logs.OrderByDescending(l => l.Timestamp).Take(20).Reverse().ToList();
|
||||||
|
var latValues = new ChartValues<double>(recent.Select(l => (double)l.ExecutionTimeMs));
|
||||||
|
var latLabels = recent.Select((l, i) => $"#{i + 1}").ToArray();
|
||||||
|
|
||||||
|
LatencySeries = new SeriesCollection
|
||||||
|
{
|
||||||
|
new ColumnSeries { Title = "Latency (ms)", Values = latValues }
|
||||||
|
};
|
||||||
|
LatencyLabels = latLabels;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// DB not yet initialized or empty - leave zeros
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,586 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Threading;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Dione.Data;
|
||||||
|
using Dione.Models;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
|
||||||
|
namespace Dione.ViewModels
|
||||||
|
{
|
||||||
|
public class LogEntry
|
||||||
|
{
|
||||||
|
public string Level { get; set; }
|
||||||
|
public string Message { get; set; }
|
||||||
|
public DateTime Timestamp { get; set; }
|
||||||
|
public string TimeLabel => Timestamp.ToString("HH:mm:ss");
|
||||||
|
}
|
||||||
|
|
||||||
|
public class LiveGenerationViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
// HttpClient senza timeout fisso: lo gestiamo per-request con CancellationToken
|
||||||
|
private static readonly HttpClient Http = new HttpClient { Timeout = Timeout.InfiniteTimeSpan };
|
||||||
|
|
||||||
|
// ── Observable properties ─────────────────────────────────────────────
|
||||||
|
|
||||||
|
private bool _isRunning;
|
||||||
|
public bool IsRunning { get => _isRunning; set { SetProperty(ref _isRunning, value); StartCommand.NotifyCanExecuteChanged(); StopCommand.NotifyCanExecuteChanged(); } }
|
||||||
|
|
||||||
|
private string _statusText = "Pronto";
|
||||||
|
public string StatusText { get => _statusText; set => SetProperty(ref _statusText, value); }
|
||||||
|
|
||||||
|
private string _elapsedTime = "00:00:00";
|
||||||
|
public string ElapsedTime { get => _elapsedTime; set => SetProperty(ref _elapsedTime, value); }
|
||||||
|
|
||||||
|
private int _completedBatches;
|
||||||
|
public int CompletedBatches { get => _completedBatches; set => SetProperty(ref _completedBatches, value); }
|
||||||
|
|
||||||
|
private int _successCount;
|
||||||
|
public int SuccessCount { get => _successCount; set => SetProperty(ref _successCount, value); }
|
||||||
|
|
||||||
|
private int _errorCount;
|
||||||
|
public int ErrorCount { get => _errorCount; set => SetProperty(ref _errorCount, value); }
|
||||||
|
|
||||||
|
private long _totalRecordsWritten;
|
||||||
|
public long TotalRecordsWritten { get => _totalRecordsWritten; set => SetProperty(ref _totalRecordsWritten, value); }
|
||||||
|
|
||||||
|
private int _filesCreated;
|
||||||
|
public int FilesCreated { get => _filesCreated; set => SetProperty(ref _filesCreated, value); }
|
||||||
|
|
||||||
|
private string _currentFileName = "";
|
||||||
|
public string CurrentFileName { get => _currentFileName; set => SetProperty(ref _currentFileName, value); }
|
||||||
|
|
||||||
|
private long _currentFileSizeBytes;
|
||||||
|
public long CurrentFileSizeBytes { get => _currentFileSizeBytes; set => SetProperty(ref _currentFileSizeBytes, value); }
|
||||||
|
|
||||||
|
private double _currentFileSizeMb;
|
||||||
|
public double CurrentFileSizeMb { get => _currentFileSizeMb; set => SetProperty(ref _currentFileSizeMb, value); }
|
||||||
|
|
||||||
|
private string _lastPreview = "";
|
||||||
|
public string LastPreview { get => _lastPreview; set => SetProperty(ref _lastPreview, value); }
|
||||||
|
|
||||||
|
private double _totalCostElectricity;
|
||||||
|
public double TotalCostElectricity { get => _totalCostElectricity; set => SetProperty(ref _totalCostElectricity, value); }
|
||||||
|
|
||||||
|
private double _totalCostApi;
|
||||||
|
public double TotalCostApi { get => _totalCostApi; set => SetProperty(ref _totalCostApi, value); }
|
||||||
|
|
||||||
|
private double _totalRevenue;
|
||||||
|
public double TotalRevenue { get => _totalRevenue; set => SetProperty(ref _totalRevenue, value); }
|
||||||
|
|
||||||
|
private double _netProfit;
|
||||||
|
public double NetProfit { get => _netProfit; set => SetProperty(ref _netProfit, value); }
|
||||||
|
|
||||||
|
private double _avgBatchTimeMs;
|
||||||
|
public double AvgBatchTimeMs { get => _avgBatchTimeMs; set => SetProperty(ref _avgBatchTimeMs, value); }
|
||||||
|
|
||||||
|
private int _tokensTotal;
|
||||||
|
public int TokensTotal { get => _tokensTotal; set => SetProperty(ref _tokensTotal, value); }
|
||||||
|
|
||||||
|
// Usato come Maximum dalla ProgressBar del file corrente
|
||||||
|
private double _maxFileSizeMbDouble = 250;
|
||||||
|
public double MaxFileSizeMbDouble { get => _maxFileSizeMbDouble; set => SetProperty(ref _maxFileSizeMbDouble, value); }
|
||||||
|
|
||||||
|
// ── Streaming ─────────────────────────────────────────────────────────
|
||||||
|
private string _streamingText = "";
|
||||||
|
public string StreamingText { get => _streamingText; set => SetProperty(ref _streamingText, value); }
|
||||||
|
|
||||||
|
private bool _isStreaming;
|
||||||
|
public bool IsStreaming { get => _isStreaming; set => SetProperty(ref _isStreaming, value); }
|
||||||
|
|
||||||
|
private int _streamingChars;
|
||||||
|
public int StreamingChars { get => _streamingChars; set => SetProperty(ref _streamingChars, value); }
|
||||||
|
|
||||||
|
private int _streamingTokensLive;
|
||||||
|
public int StreamingTokensLive { get => _streamingTokensLive; set => SetProperty(ref _streamingTokensLive, value); }
|
||||||
|
|
||||||
|
public ObservableCollection<LogEntry> LogEntries { get; } = new ObservableCollection<LogEntry>();
|
||||||
|
|
||||||
|
// ── Commands ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public RelayCommand StartCommand { get; }
|
||||||
|
public RelayCommand StopCommand { get; }
|
||||||
|
public RelayCommand ClearLogCommand { get; }
|
||||||
|
public RelayCommand CopyLogsCommand { get; }
|
||||||
|
|
||||||
|
// ── Private fields ────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private CancellationTokenSource _cts;
|
||||||
|
private Stopwatch _sessionSw;
|
||||||
|
private DispatcherTimer _elapsedTimer;
|
||||||
|
|
||||||
|
// ── Constructor ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public LiveGenerationViewModel()
|
||||||
|
{
|
||||||
|
StartCommand = new RelayCommand(StartGeneration, () => !IsRunning);
|
||||||
|
StopCommand = new RelayCommand(StopGeneration, () => IsRunning);
|
||||||
|
ClearLogCommand = new RelayCommand(() => LogEntries.Clear());
|
||||||
|
CopyLogsCommand = new RelayCommand(CopyLogs, () => LogEntries.Count > 0);
|
||||||
|
|
||||||
|
_elapsedTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(1) };
|
||||||
|
_elapsedTimer.Tick += (_, __) =>
|
||||||
|
{
|
||||||
|
if (_sessionSw == null) return;
|
||||||
|
var e = _sessionSw.Elapsed;
|
||||||
|
ElapsedTime = $"{(int)e.TotalHours:D2}:{e.Minutes:D2}:{e.Seconds:D2}";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Generation ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private async void StartGeneration()
|
||||||
|
{
|
||||||
|
var settings = SynthDataDbContext.LoadSettings();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(settings.ApiEndpoint)) { Log("ERR", "API Endpoint non configurato."); return; }
|
||||||
|
if (string.IsNullOrWhiteSpace(settings.SystemPrompt)) { Log("ERR", "System Prompt vuoto."); return; }
|
||||||
|
if (string.IsNullOrWhiteSpace(settings.UserPrompt)) { Log("ERR", "User Prompt vuoto."); return; }
|
||||||
|
if (string.IsNullOrWhiteSpace(settings.OutputDirectory))
|
||||||
|
{
|
||||||
|
Log("ERR", "Cartella output non configurata. Vai nelle Impostazioni.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!Directory.Exists(settings.OutputDirectory))
|
||||||
|
{
|
||||||
|
try { Directory.CreateDirectory(settings.OutputDirectory); }
|
||||||
|
catch { Log("ERR", "Impossibile creare la cartella output."); return; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset counters
|
||||||
|
IsRunning = true;
|
||||||
|
CompletedBatches = 0;
|
||||||
|
SuccessCount = 0;
|
||||||
|
ErrorCount = 0;
|
||||||
|
TotalRecordsWritten = 0;
|
||||||
|
FilesCreated = 0;
|
||||||
|
CurrentFileSizeBytes = 0;
|
||||||
|
CurrentFileSizeMb = 0;
|
||||||
|
TotalCostElectricity = 0;
|
||||||
|
TotalCostApi = 0;
|
||||||
|
TotalRevenue = 0;
|
||||||
|
NetProfit = 0;
|
||||||
|
AvgBatchTimeMs = 0;
|
||||||
|
TokensTotal = 0;
|
||||||
|
LastPreview = "";
|
||||||
|
CurrentFileName = "";
|
||||||
|
ElapsedTime = "00:00:00";
|
||||||
|
_sessionSw = Stopwatch.StartNew();
|
||||||
|
_cts = new CancellationTokenSource();
|
||||||
|
var token = _cts.Token;
|
||||||
|
_elapsedTimer.Start();
|
||||||
|
|
||||||
|
// Derived settings
|
||||||
|
var endpoint = settings.ApiEndpoint.Trim();
|
||||||
|
var apiKey = settings.ApiKey ?? "";
|
||||||
|
bool isLocal = IsLocalEndpoint(endpoint);
|
||||||
|
long maxBytes = (long)settings.MaxFileSizeMb * 1024 * 1024;
|
||||||
|
int calcTimeout = (int)(settings.ApiTimeoutSeconds + settings.MaxTokens * settings.TimeoutPerTokenRatio / 100.0);
|
||||||
|
MaxFileSizeMbDouble = settings.MaxFileSizeMb;
|
||||||
|
|
||||||
|
Log("INFO", $"Endpoint: {endpoint} | Model: {settings.ModelName}");
|
||||||
|
Log("INFO", $"Output: {settings.OutputDirectory} | Max file: {settings.MaxFileSizeMb} MB");
|
||||||
|
Log("INFO", $"Timeout: {calcTimeout}s | Max tokens: {settings.MaxTokens}");
|
||||||
|
if (!string.IsNullOrWhiteSpace(apiKey) && !isLocal)
|
||||||
|
Log("INFO", "Autenticazione API: attiva");
|
||||||
|
else if (!string.IsNullOrWhiteSpace(apiKey) && isLocal)
|
||||||
|
Log("WARN", "API Key ignorata (endpoint locale)");
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await Task.Run(async () =>
|
||||||
|
{
|
||||||
|
// ── File state ──
|
||||||
|
string filePath = null;
|
||||||
|
int fileIndex = NextFileIndex(settings.OutputDirectory, settings.OutputFilePrefix);
|
||||||
|
|
||||||
|
var batchSw = new Stopwatch();
|
||||||
|
long totalBatchMs = 0;
|
||||||
|
|
||||||
|
while (!token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
// ── Open new file if needed ──
|
||||||
|
if (filePath == null || !File.Exists(filePath) || new FileInfo(filePath).Length >= maxBytes)
|
||||||
|
{
|
||||||
|
filePath = Path.Combine(settings.OutputDirectory, $"{settings.OutputFilePrefix}_{fileIndex:D3}.jsonl");
|
||||||
|
fileIndex++;
|
||||||
|
UpdateUI(() => { FilesCreated++; CurrentFileName = Path.GetFileName(filePath); CurrentFileSizeBytes = 0; CurrentFileSizeMb = 0; });
|
||||||
|
Log("INFO", $"[NUOVO FILE] {Path.GetFileName(filePath)}");
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Build request (streaming) ──
|
||||||
|
var requestBody = new
|
||||||
|
{
|
||||||
|
model = settings.ModelName ?? "",
|
||||||
|
messages = new[]
|
||||||
|
{
|
||||||
|
new { role = "system", content = settings.SystemPrompt },
|
||||||
|
new { role = "user", content = settings.UserPrompt }
|
||||||
|
},
|
||||||
|
temperature = settings.Temperature,
|
||||||
|
max_tokens = settings.MaxTokens,
|
||||||
|
stream = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var json = JsonConvert.SerializeObject(requestBody);
|
||||||
|
var telelog = new TelemetryLog
|
||||||
|
{
|
||||||
|
Timestamp = DateTime.UtcNow,
|
||||||
|
BatchId = Guid.NewGuid().ToString("N").Substring(0, 12),
|
||||||
|
ModelUsed = settings.ModelName,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reset streaming UI state
|
||||||
|
UpdateUI(() => { StreamingText = ""; StreamingChars = 0; StreamingTokensLive = 0; IsStreaming = false; });
|
||||||
|
|
||||||
|
batchSw.Restart();
|
||||||
|
HttpResponseMessage response = null;
|
||||||
|
int retries = 0;
|
||||||
|
|
||||||
|
while (retries < 3 && response == null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using (var content = new StringContent(json, Encoding.UTF8, "application/json"))
|
||||||
|
using (var timeoutCts = new CancellationTokenSource(TimeSpan.FromSeconds(calcTimeout)))
|
||||||
|
using (var linked = CancellationTokenSource.CreateLinkedTokenSource(token, timeoutCts.Token))
|
||||||
|
{
|
||||||
|
var req = new HttpRequestMessage(HttpMethod.Post, endpoint) { Content = content };
|
||||||
|
if (!string.IsNullOrWhiteSpace(apiKey) && !isLocal)
|
||||||
|
req.Headers.Add("Authorization", $"Bearer {apiKey}");
|
||||||
|
|
||||||
|
// ResponseHeadersRead: leggiamo il body in streaming
|
||||||
|
response = await Http.SendAsync(req, HttpCompletionOption.ResponseHeadersRead, linked.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException) when (!token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
retries++;
|
||||||
|
Log("WARN", $"Timeout, tentativo {retries}/3");
|
||||||
|
if (retries < 3) await DelayAsync(2000, token);
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (!token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
retries++;
|
||||||
|
var msg = ex.Message + (ex.InnerException != null ? " | " + ex.InnerException.Message : "");
|
||||||
|
Log("ERR", $"Errore connessione: {msg}");
|
||||||
|
if (retries < 3) await DelayAsync(2000, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
batchSw.Stop();
|
||||||
|
|
||||||
|
if (token.IsCancellationRequested) break;
|
||||||
|
|
||||||
|
if (response == null)
|
||||||
|
{
|
||||||
|
telelog.IsSuccess = false;
|
||||||
|
telelog.ErrorMessage = "Tutti i retry esauriti.";
|
||||||
|
SynthDataDbContext.InsertLog(telelog);
|
||||||
|
UpdateUI(() => ErrorCount++);
|
||||||
|
await DelayAsync(3000, token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
telelog.ExecutionTimeMs = batchSw.ElapsedMilliseconds;
|
||||||
|
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
string errBody = "";
|
||||||
|
try { errBody = await response.Content.ReadAsStringAsync(); } catch { }
|
||||||
|
Log("ERR", $"HTTP {(int)response.StatusCode} {response.ReasonPhrase}");
|
||||||
|
if (!string.IsNullOrWhiteSpace(errBody))
|
||||||
|
Log("ERR", $"Body: {errBody.Substring(0, Math.Min(500, errBody.Length))}");
|
||||||
|
var err = $"HTTP {(int)response.StatusCode}: {errBody?.Substring(0, Math.Min(200, errBody?.Length ?? 0))}";
|
||||||
|
telelog.IsSuccess = false;
|
||||||
|
telelog.ErrorMessage = err;
|
||||||
|
SynthDataDbContext.InsertLog(telelog);
|
||||||
|
UpdateUI(() => ErrorCount++);
|
||||||
|
await DelayAsync(2000, token);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Leggi lo stream SSE ──
|
||||||
|
string generatedText = null;
|
||||||
|
int tokensPrompt = 0;
|
||||||
|
int tokensComp = 0;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var accumulated = new StringBuilder();
|
||||||
|
UpdateUI(() => IsStreaming = true);
|
||||||
|
|
||||||
|
using (var httpStream = await response.Content.ReadAsStreamAsync())
|
||||||
|
using (var reader = new StreamReader(httpStream))
|
||||||
|
{
|
||||||
|
string line;
|
||||||
|
while ((line = await reader.ReadLineAsync()) != null)
|
||||||
|
{
|
||||||
|
if (token.IsCancellationRequested) break;
|
||||||
|
if (string.IsNullOrEmpty(line)) continue;
|
||||||
|
if (!line.StartsWith("data:")) continue;
|
||||||
|
|
||||||
|
var payload = line.Substring(5).TrimStart();
|
||||||
|
if (payload == "[DONE]") break;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var chunk = JObject.Parse(payload);
|
||||||
|
|
||||||
|
// Aggiorna usage se presente nel chunk (alcuni provider lo inviano nell'ultimo chunk)
|
||||||
|
var usageNode = chunk["usage"];
|
||||||
|
if (usageNode != null)
|
||||||
|
{
|
||||||
|
tokensPrompt = usageNode["prompt_tokens"]?.Value<int>() ?? tokensPrompt;
|
||||||
|
tokensComp = usageNode["completion_tokens"]?.Value<int>() ?? tokensComp;
|
||||||
|
}
|
||||||
|
|
||||||
|
var delta = chunk["choices"]?[0]?["delta"]?["content"]?.Value<string>();
|
||||||
|
if (!string.IsNullOrEmpty(delta))
|
||||||
|
{
|
||||||
|
accumulated.Append(delta);
|
||||||
|
tokensComp++; // stima locale se il provider non invia usage inline
|
||||||
|
var snapshot = accumulated.ToString();
|
||||||
|
UpdateUI(() =>
|
||||||
|
{
|
||||||
|
StreamingText = snapshot;
|
||||||
|
StreamingChars = snapshot.Length;
|
||||||
|
StreamingTokensLive = tokensComp;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception chunkEx)
|
||||||
|
{
|
||||||
|
Log("WARN", $"Chunk SSE non parsabile: {chunkEx.Message}");
|
||||||
|
Log("WARN", $"Payload: {payload.Substring(0, Math.Min(200, payload.Length))}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedText = accumulated.ToString();
|
||||||
|
UpdateUI(() => { IsStreaming = false; LastPreview = generatedText.Substring(0, Math.Min(400, generatedText.Length)); });
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (!token.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
UpdateUI(() => { IsStreaming = false; });
|
||||||
|
Log("ERR", "Lettura stream fallita: " + ex.Message);
|
||||||
|
UpdateUI(() => ErrorCount++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(generatedText))
|
||||||
|
{
|
||||||
|
Log("WARN", "Stream vuoto: nessun testo ricevuto dall'API.");
|
||||||
|
if (!string.IsNullOrEmpty(generatedText))
|
||||||
|
Log("WARN", $"Contenuto parziale: {generatedText.Substring(0, Math.Min(300, generatedText.Length))}");
|
||||||
|
UpdateUI(() => ErrorCount++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
telelog.TokensPrompt = tokensPrompt;
|
||||||
|
telelog.TokensCompletion = tokensComp;
|
||||||
|
|
||||||
|
// ── Rimuovi blocchi <think> e log se presenti ──
|
||||||
|
var strippedText = StripThinkingBlocks(generatedText);
|
||||||
|
if (strippedText.Length != generatedText.Length)
|
||||||
|
Log("INFO", $"Rimossi blocchi <think>: {generatedText.Length - strippedText.Length} caratteri di ragionamento ignorati.");
|
||||||
|
generatedText = strippedText;
|
||||||
|
|
||||||
|
// ── Extract JSON array from text (handles markdown code blocks) ──
|
||||||
|
var jsonArray = ExtractJsonArray(generatedText);
|
||||||
|
if (jsonArray == null)
|
||||||
|
{
|
||||||
|
Log("WARN", "Nessun array JSON trovato nella risposta.");
|
||||||
|
Log("WARN", $"Risposta grezza ({generatedText.Length} car): {generatedText.Substring(0, Math.Min(500, generatedText.Length))}");
|
||||||
|
if (generatedText.Length > 500)
|
||||||
|
Log("WARN", $"...fine risposta: {generatedText.Substring(generatedText.Length - Math.Min(200, generatedText.Length))}");
|
||||||
|
UpdateUI(() => ErrorCount++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Write JSONL ──
|
||||||
|
int recordsInBatch = 0;
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
foreach (var obj in jsonArray)
|
||||||
|
{
|
||||||
|
sb.AppendLine(obj.ToString(Formatting.None));
|
||||||
|
recordsInBatch++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var jsonlChunk = sb.ToString();
|
||||||
|
File.AppendAllText(filePath, jsonlChunk, Encoding.UTF8);
|
||||||
|
|
||||||
|
// ── Cost calculation ──
|
||||||
|
double elecCost = (batchSw.Elapsed.TotalHours * settings.SystemPowerWatt / 1000.0) * settings.ElectricityCostPerKwh;
|
||||||
|
double apiCost = 0;
|
||||||
|
if (settings.ApiCostType == "PerCall") apiCost = settings.ApiCostPerCall;
|
||||||
|
if (settings.ApiCostType == "PerBlock") apiCost = (1.0 / Math.Max(1, settings.ApiBlockSize)) * settings.ApiCostPerBlock;
|
||||||
|
|
||||||
|
// ── Update state ──
|
||||||
|
totalBatchMs += batchSw.ElapsedMilliseconds;
|
||||||
|
telelog.IsSuccess = true;
|
||||||
|
telelog.OutputPreview = jsonlChunk.Substring(0, Math.Min(300, jsonlChunk.Length));
|
||||||
|
SynthDataDbContext.InsertLog(telelog);
|
||||||
|
|
||||||
|
long newSize = new FileInfo(filePath).Length;
|
||||||
|
|
||||||
|
UpdateUI(() =>
|
||||||
|
{
|
||||||
|
CompletedBatches++;
|
||||||
|
SuccessCount++;
|
||||||
|
TotalRecordsWritten += recordsInBatch;
|
||||||
|
TokensTotal += tokensPrompt + tokensComp;
|
||||||
|
TotalCostElectricity += elecCost;
|
||||||
|
TotalCostApi += apiCost;
|
||||||
|
NetProfit = TotalRevenue - TotalCostApi - TotalCostElectricity;
|
||||||
|
CurrentFileSizeBytes = newSize;
|
||||||
|
CurrentFileSizeMb = newSize / (1024.0 * 1024.0);
|
||||||
|
AvgBatchTimeMs = CompletedBatches > 0 ? (double)totalBatchMs / CompletedBatches : 0;
|
||||||
|
LastPreview = jsonlChunk.Substring(0, Math.Min(400, jsonlChunk.Length));
|
||||||
|
});
|
||||||
|
|
||||||
|
Log("OK", $"Batch #{CompletedBatches}: {recordsInBatch} record | {tokensPrompt + tokensComp} tok | {batchSw.ElapsedMilliseconds}ms");
|
||||||
|
}
|
||||||
|
|
||||||
|
Log("INFO", $"Generazione fermata. Batch: {CompletedBatches}, Record: {TotalRecordsWritten}, Errori: {ErrorCount}");
|
||||||
|
UpdateUI(() => { IsRunning = false; StatusText = "Fermato"; _elapsedTimer.Stop(); });
|
||||||
|
|
||||||
|
}, CancellationToken.None);
|
||||||
|
}
|
||||||
|
catch (OperationCanceledException)
|
||||||
|
{
|
||||||
|
_elapsedTimer.Stop();
|
||||||
|
Log("INFO", $"Generazione interrotta. Batch: {CompletedBatches}, Record: {TotalRecordsWritten}");
|
||||||
|
UpdateUI(() => { IsRunning = false; StatusText = "Fermato"; });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_elapsedTimer.Stop();
|
||||||
|
Log("ERR", $"Errore imprevisto: {ex.Message}");
|
||||||
|
UpdateUI(() => { IsRunning = false; StatusText = "Errore"; });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopGeneration()
|
||||||
|
{
|
||||||
|
_cts?.Cancel();
|
||||||
|
StatusText = "Arresto in corso...";
|
||||||
|
Log("WARN", "Stop richiesto...");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CopyLogs()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
for (int i = LogEntries.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var e = LogEntries[i];
|
||||||
|
sb.AppendLine($"{e.TimeLabel} [{e.Level,-4}] {e.Message}");
|
||||||
|
}
|
||||||
|
System.Windows.Clipboard.SetText(sb.ToString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Helpers ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private static bool IsLocalEndpoint(string url)
|
||||||
|
=> url.Contains("localhost") || url.Contains("127.0.0.1")
|
||||||
|
|| url.Contains("172.") || url.Contains("192.168.") || url.Contains("10.");
|
||||||
|
|
||||||
|
/// <summary>Attende ms millisecondi; non lancia eccezione se il token viene cancellato.</summary>
|
||||||
|
private static async Task DelayAsync(int ms, CancellationToken token)
|
||||||
|
{
|
||||||
|
try { await Task.Delay(ms, token); }
|
||||||
|
catch (OperationCanceledException) { /* attesa interrotta: normale al Stop */ }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Estrae il primo array JSON dalla risposta (gestisce anche ```json ... ``` markdown).
|
||||||
|
/// </summary>
|
||||||
|
private static string StripThinkingBlocks(string text)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(text)) return text;
|
||||||
|
// Tag di ragionamento usati da vari modelli
|
||||||
|
var tagPairs = new[] { ("<think>", "</think>"), ("<thought>", "</thought>") };
|
||||||
|
foreach (var (open, close) in tagPairs)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
int i = 0;
|
||||||
|
while (i < text.Length)
|
||||||
|
{
|
||||||
|
int openIdx = text.IndexOf(open, i, StringComparison.OrdinalIgnoreCase);
|
||||||
|
if (openIdx < 0) { sb.Append(text, i, text.Length - i); break; }
|
||||||
|
sb.Append(text, i, openIdx - i);
|
||||||
|
int closeIdx = text.IndexOf(close, openIdx, StringComparison.OrdinalIgnoreCase);
|
||||||
|
i = closeIdx < 0 ? text.Length : closeIdx + close.Length;
|
||||||
|
}
|
||||||
|
text = sb.ToString();
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static JArray ExtractJsonArray(string text)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(text)) return null;
|
||||||
|
|
||||||
|
// Rimuovi blocchi di ragionamento <think>...</think>
|
||||||
|
text = StripThinkingBlocks(text);
|
||||||
|
|
||||||
|
// Strip markdown code fences
|
||||||
|
var cleaned = text.Trim();
|
||||||
|
if (cleaned.StartsWith("```"))
|
||||||
|
{
|
||||||
|
var firstNewline = cleaned.IndexOf('\n');
|
||||||
|
var lastFence = cleaned.LastIndexOf("```");
|
||||||
|
if (firstNewline > 0 && lastFence > firstNewline)
|
||||||
|
cleaned = cleaned.Substring(firstNewline + 1, lastFence - firstNewline - 1).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find first [ ... ]
|
||||||
|
var start = cleaned.IndexOf('[');
|
||||||
|
var end = cleaned.LastIndexOf(']');
|
||||||
|
if (start < 0 || end <= start) return null;
|
||||||
|
|
||||||
|
try { return JArray.Parse(cleaned.Substring(start, end - start + 1)); }
|
||||||
|
catch { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int NextFileIndex(string dir, string prefix)
|
||||||
|
{
|
||||||
|
int max = 0;
|
||||||
|
if (!Directory.Exists(dir)) return 1;
|
||||||
|
foreach (var f in Directory.GetFiles(dir, $"{prefix}_*.jsonl"))
|
||||||
|
{
|
||||||
|
var name = Path.GetFileNameWithoutExtension(f);
|
||||||
|
var part = name.Replace(prefix + "_", "");
|
||||||
|
if (int.TryParse(part, out var n) && n > max) max = n;
|
||||||
|
}
|
||||||
|
return max + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Log(string level, string msg)
|
||||||
|
{
|
||||||
|
UpdateUI(() =>
|
||||||
|
{
|
||||||
|
LogEntries.Insert(0, new LogEntry { Level = level, Message = msg, Timestamp = DateTime.Now });
|
||||||
|
if (LogEntries.Count > 500) LogEntries.RemoveAt(LogEntries.Count - 1);
|
||||||
|
CopyLogsCommand?.NotifyCanExecuteChanged();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateUI(Action action)
|
||||||
|
{
|
||||||
|
if (System.Windows.Application.Current?.Dispatcher == null) { action(); return; }
|
||||||
|
if (System.Windows.Application.Current.Dispatcher.CheckAccess())
|
||||||
|
action();
|
||||||
|
else
|
||||||
|
System.Windows.Application.Current.Dispatcher.Invoke(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
|
||||||
|
namespace Dione.ViewModels
|
||||||
|
{
|
||||||
|
public class MainWindowViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private object _currentView;
|
||||||
|
public object CurrentView
|
||||||
|
{
|
||||||
|
get => _currentView;
|
||||||
|
set => SetProperty(ref _currentView, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private string _selectedMenu = "Dashboard";
|
||||||
|
public string SelectedMenu
|
||||||
|
{
|
||||||
|
get => _selectedMenu;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedMenu, value))
|
||||||
|
NavigateTo(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DashboardViewModel DashboardVm { get; }
|
||||||
|
public SettingsViewModel SettingsVm { get; }
|
||||||
|
public LiveGenerationViewModel LiveGenerationVm { get; }
|
||||||
|
public TelemetryHistoryViewModel TelemetryHistoryVm { get; }
|
||||||
|
|
||||||
|
public RelayCommand MinimizeCommand { get; }
|
||||||
|
public RelayCommand MaximizeCommand { get; }
|
||||||
|
public RelayCommand CloseCommand { get; }
|
||||||
|
public RelayCommand<string> NavigateCommand { get; }
|
||||||
|
|
||||||
|
public MainWindowViewModel()
|
||||||
|
{
|
||||||
|
DashboardVm = new DashboardViewModel();
|
||||||
|
SettingsVm = new SettingsViewModel();
|
||||||
|
LiveGenerationVm = new LiveGenerationViewModel();
|
||||||
|
TelemetryHistoryVm = new TelemetryHistoryViewModel();
|
||||||
|
|
||||||
|
CurrentView = DashboardVm;
|
||||||
|
|
||||||
|
MinimizeCommand = new RelayCommand(() =>
|
||||||
|
System.Windows.Application.Current.MainWindow.WindowState = System.Windows.WindowState.Minimized);
|
||||||
|
MaximizeCommand = new RelayCommand(() =>
|
||||||
|
{
|
||||||
|
var w = System.Windows.Application.Current.MainWindow;
|
||||||
|
w.WindowState = w.WindowState == System.Windows.WindowState.Maximized
|
||||||
|
? System.Windows.WindowState.Normal
|
||||||
|
: System.Windows.WindowState.Maximized;
|
||||||
|
});
|
||||||
|
CloseCommand = new RelayCommand(() => System.Windows.Application.Current.Shutdown());
|
||||||
|
NavigateCommand = new RelayCommand<string>(NavigateTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NavigateTo(string view)
|
||||||
|
{
|
||||||
|
switch (view)
|
||||||
|
{
|
||||||
|
case "Dashboard":
|
||||||
|
DashboardVm.RefreshFromDb();
|
||||||
|
CurrentView = DashboardVm;
|
||||||
|
break;
|
||||||
|
case "LiveGeneration":
|
||||||
|
CurrentView = LiveGenerationVm;
|
||||||
|
break;
|
||||||
|
case "Settings":
|
||||||
|
CurrentView = SettingsVm;
|
||||||
|
break;
|
||||||
|
case "Telemetry":
|
||||||
|
CurrentView = TelemetryHistoryVm;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
SelectedMenu = view;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
using System;
|
||||||
|
using System.Windows;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Dione.Data;
|
||||||
|
using Dione.Models;
|
||||||
|
using Microsoft.Win32;
|
||||||
|
|
||||||
|
namespace Dione.ViewModels
|
||||||
|
{
|
||||||
|
public class SettingsViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
// ── API ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private string _selectedEndpointPreset = "Custom";
|
||||||
|
public string SelectedEndpointPreset
|
||||||
|
{
|
||||||
|
get => _selectedEndpointPreset;
|
||||||
|
set { if (SetProperty(ref _selectedEndpointPreset, value) && value != "Custom") ApplyPreset(value); }
|
||||||
|
}
|
||||||
|
public string[] EndpointPresets { get; } = { "Custom", "OpenAI", "Anthropic", "Google AI", "Azure OpenAI", "LM Studio (Local)", "Ollama (Local)" };
|
||||||
|
|
||||||
|
private string _apiEndpoint = "http://127.0.0.1:1234/v1/chat/completions";
|
||||||
|
public string ApiEndpoint { get => _apiEndpoint; set => SetProperty(ref _apiEndpoint, value); }
|
||||||
|
|
||||||
|
private string _modelName = "";
|
||||||
|
public string ModelName { get => _modelName; set => SetProperty(ref _modelName, value); }
|
||||||
|
|
||||||
|
private string _apiKey = "";
|
||||||
|
public string ApiKey { get => _apiKey; set => SetProperty(ref _apiKey, value); }
|
||||||
|
|
||||||
|
private double _temperature = 0.7;
|
||||||
|
public double Temperature { get => _temperature; set => SetProperty(ref _temperature, value); }
|
||||||
|
|
||||||
|
private int _maxTokens = 2048;
|
||||||
|
public int MaxTokens { get => _maxTokens; set => SetProperty(ref _maxTokens, value); }
|
||||||
|
|
||||||
|
// ── Prompt ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private string _systemPrompt = "";
|
||||||
|
public string SystemPrompt { get => _systemPrompt; set => SetProperty(ref _systemPrompt, value); }
|
||||||
|
|
||||||
|
private string _userPrompt = "";
|
||||||
|
public string UserPrompt { get => _userPrompt; set => SetProperty(ref _userPrompt, value); }
|
||||||
|
|
||||||
|
// ── Output ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private string _outputDirectory = "";
|
||||||
|
public string OutputDirectory { get => _outputDirectory; set => SetProperty(ref _outputDirectory, value); }
|
||||||
|
|
||||||
|
private string _outputFilePrefix = "batch";
|
||||||
|
public string OutputFilePrefix { get => _outputFilePrefix; set => SetProperty(ref _outputFilePrefix, value); }
|
||||||
|
|
||||||
|
private int _maxFileSizeMb = 250;
|
||||||
|
public int MaxFileSizeMb { get => _maxFileSizeMb; set => SetProperty(ref _maxFileSizeMb, value); }
|
||||||
|
|
||||||
|
// ── Timeout ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private int _apiTimeoutSeconds = 120;
|
||||||
|
public int ApiTimeoutSeconds { get => _apiTimeoutSeconds; set => SetProperty(ref _apiTimeoutSeconds, value); }
|
||||||
|
|
||||||
|
private double _timeoutPerTokenRatio = 0.5;
|
||||||
|
public double TimeoutPerTokenRatio { get => _timeoutPerTokenRatio; set => SetProperty(ref _timeoutPerTokenRatio, value); }
|
||||||
|
|
||||||
|
// ── Verifica qualita ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private bool _enableQualityVerification = false;
|
||||||
|
public bool EnableQualityVerification { get => _enableQualityVerification; set => SetProperty(ref _enableQualityVerification, value); }
|
||||||
|
|
||||||
|
private bool _useSameModelForVerification = true;
|
||||||
|
public bool UseSameModelForVerification { get => _useSameModelForVerification; set => SetProperty(ref _useSameModelForVerification, value); }
|
||||||
|
|
||||||
|
private string _verificationApiEndpoint = "";
|
||||||
|
public string VerificationApiEndpoint { get => _verificationApiEndpoint; set => SetProperty(ref _verificationApiEndpoint, value); }
|
||||||
|
|
||||||
|
private string _verificationModelName = "";
|
||||||
|
public string VerificationModelName { get => _verificationModelName; set => SetProperty(ref _verificationModelName, value); }
|
||||||
|
|
||||||
|
private string _verificationApiKey = "";
|
||||||
|
public string VerificationApiKey { get => _verificationApiKey; set => SetProperty(ref _verificationApiKey, value); }
|
||||||
|
|
||||||
|
private double _revenuePerHighQualityRecord = 0.005;
|
||||||
|
public double RevenuePerHighQualityRecord { get => _revenuePerHighQualityRecord; set => SetProperty(ref _revenuePerHighQualityRecord, value); }
|
||||||
|
|
||||||
|
// ── Costi ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private double _electricityCostPerKwh = 0.25;
|
||||||
|
public double ElectricityCostPerKwh { get => _electricityCostPerKwh; set => SetProperty(ref _electricityCostPerKwh, value); }
|
||||||
|
|
||||||
|
private double _systemPowerWatt = 350;
|
||||||
|
public double SystemPowerWatt { get => _systemPowerWatt; set => SetProperty(ref _systemPowerWatt, value); }
|
||||||
|
|
||||||
|
private string _apiCostType = "Free";
|
||||||
|
public string ApiCostType { get => _apiCostType; set => SetProperty(ref _apiCostType, value); }
|
||||||
|
public string[] ApiCostTypes { get; } = { "Free", "PerCall", "PerBlock" };
|
||||||
|
|
||||||
|
private double _apiCostPerCall = 0;
|
||||||
|
public double ApiCostPerCall { get => _apiCostPerCall; set => SetProperty(ref _apiCostPerCall, value); }
|
||||||
|
|
||||||
|
private double _apiCostPerBlock = 0;
|
||||||
|
public double ApiCostPerBlock { get => _apiCostPerBlock; set => SetProperty(ref _apiCostPerBlock, value); }
|
||||||
|
|
||||||
|
private int _apiBlockSize = 1000;
|
||||||
|
public int ApiBlockSize { get => _apiBlockSize; set => SetProperty(ref _apiBlockSize, value); }
|
||||||
|
|
||||||
|
// ── Status ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private string _statusMessage = "";
|
||||||
|
public string StatusMessage { get => _statusMessage; set => SetProperty(ref _statusMessage, value); }
|
||||||
|
|
||||||
|
// ── Commands ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public RelayCommand SaveCommand { get; }
|
||||||
|
public RelayCommand BrowseOutputDirectoryCommand { get; }
|
||||||
|
public RelayCommand ResetTelemetryCommand { get; }
|
||||||
|
public RelayCommand ResetDatabaseCommand { get; }
|
||||||
|
public RelayCommand InsertDefaultBettingPromptCommand { get; }
|
||||||
|
|
||||||
|
// ── Constructor ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public SettingsViewModel()
|
||||||
|
{
|
||||||
|
SaveCommand = new RelayCommand(Save);
|
||||||
|
BrowseOutputDirectoryCommand = new RelayCommand(BrowseOutputDirectory);
|
||||||
|
ResetTelemetryCommand = new RelayCommand(ResetTelemetry);
|
||||||
|
ResetDatabaseCommand = new RelayCommand(ResetDatabase);
|
||||||
|
InsertDefaultBettingPromptCommand = new RelayCommand(InsertDefaultBettingPrompt);
|
||||||
|
|
||||||
|
Load();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Private methods ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var s = SynthDataDbContext.LoadSettings();
|
||||||
|
ApiEndpoint = s.ApiEndpoint;
|
||||||
|
ModelName = s.ModelName;
|
||||||
|
ApiKey = s.ApiKey;
|
||||||
|
Temperature = s.Temperature;
|
||||||
|
MaxTokens = s.MaxTokens;
|
||||||
|
SystemPrompt = s.SystemPrompt;
|
||||||
|
UserPrompt = s.UserPrompt;
|
||||||
|
OutputDirectory = s.OutputDirectory;
|
||||||
|
OutputFilePrefix = s.OutputFilePrefix;
|
||||||
|
MaxFileSizeMb = s.MaxFileSizeMb;
|
||||||
|
ApiTimeoutSeconds = s.ApiTimeoutSeconds;
|
||||||
|
TimeoutPerTokenRatio = s.TimeoutPerTokenRatio;
|
||||||
|
EnableQualityVerification = s.EnableQualityVerification;
|
||||||
|
UseSameModelForVerification = s.UseSameModelForVerification;
|
||||||
|
VerificationApiEndpoint = s.VerificationApiEndpoint;
|
||||||
|
VerificationModelName = s.VerificationModelName;
|
||||||
|
VerificationApiKey = s.VerificationApiKey;
|
||||||
|
RevenuePerHighQualityRecord = s.RevenuePerHighQualityRecord;
|
||||||
|
ElectricityCostPerKwh = s.ElectricityCostPerKwh;
|
||||||
|
SystemPowerWatt = s.SystemPowerWatt;
|
||||||
|
ApiCostType = s.ApiCostType;
|
||||||
|
ApiCostPerCall = s.ApiCostPerCall;
|
||||||
|
ApiCostPerBlock = s.ApiCostPerBlock;
|
||||||
|
ApiBlockSize = s.ApiBlockSize;
|
||||||
|
|
||||||
|
StatusMessage = "Impostazioni caricate.";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StatusMessage = "Errore caricamento: " + ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Save()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(OutputDirectory))
|
||||||
|
{
|
||||||
|
StatusMessage = "Seleziona una cartella di output.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
SynthDataDbContext.SaveSettings(new AppSettings
|
||||||
|
{
|
||||||
|
ApiEndpoint = ApiEndpoint,
|
||||||
|
ModelName = ModelName,
|
||||||
|
ApiKey = ApiKey,
|
||||||
|
Temperature = Temperature,
|
||||||
|
MaxTokens = MaxTokens,
|
||||||
|
SystemPrompt = SystemPrompt,
|
||||||
|
UserPrompt = UserPrompt,
|
||||||
|
OutputDirectory = OutputDirectory,
|
||||||
|
OutputFilePrefix = OutputFilePrefix,
|
||||||
|
MaxFileSizeMb = MaxFileSizeMb,
|
||||||
|
ApiTimeoutSeconds = ApiTimeoutSeconds,
|
||||||
|
TimeoutPerTokenRatio = TimeoutPerTokenRatio,
|
||||||
|
EnableQualityVerification = EnableQualityVerification,
|
||||||
|
UseSameModelForVerification = UseSameModelForVerification,
|
||||||
|
VerificationApiEndpoint = VerificationApiEndpoint,
|
||||||
|
VerificationModelName = VerificationModelName,
|
||||||
|
VerificationApiKey = VerificationApiKey,
|
||||||
|
RevenuePerHighQualityRecord = RevenuePerHighQualityRecord,
|
||||||
|
ElectricityCostPerKwh = ElectricityCostPerKwh,
|
||||||
|
SystemPowerWatt = SystemPowerWatt,
|
||||||
|
ApiCostType = ApiCostType,
|
||||||
|
ApiCostPerCall = ApiCostPerCall,
|
||||||
|
ApiCostPerBlock = ApiCostPerBlock,
|
||||||
|
ApiBlockSize = ApiBlockSize,
|
||||||
|
});
|
||||||
|
StatusMessage = "Impostazioni salvate.";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StatusMessage = "Errore salvataggio: " + ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BrowseOutputDirectory()
|
||||||
|
{
|
||||||
|
var dlg = new System.Windows.Forms.FolderBrowserDialog
|
||||||
|
{
|
||||||
|
Description = "Seleziona la cartella di output",
|
||||||
|
SelectedPath = OutputDirectory
|
||||||
|
};
|
||||||
|
if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
|
||||||
|
OutputDirectory = dlg.SelectedPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetTelemetry()
|
||||||
|
{
|
||||||
|
if (MessageBox.Show("Eliminare TUTTA la telemetria?", "Conferma", MessageBoxButton.YesNo, MessageBoxImage.Warning) != MessageBoxResult.Yes)
|
||||||
|
return;
|
||||||
|
try { SynthDataDbContext.DeleteAllTelemetry(); StatusMessage = "Telemetria eliminata."; }
|
||||||
|
catch (Exception ex) { StatusMessage = "Errore: " + ex.Message; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetDatabase()
|
||||||
|
{
|
||||||
|
if (MessageBox.Show("Resettare COMPLETAMENTE il database? Tutte le impostazioni e la telemetria verranno cancellate.",
|
||||||
|
"Conferma Reset", MessageBoxButton.YesNo, MessageBoxImage.Stop) != MessageBoxResult.Yes)
|
||||||
|
return;
|
||||||
|
try { SynthDataDbContext.ResetDatabase(); Load(); StatusMessage = "Database resettato."; }
|
||||||
|
catch (Exception ex) { StatusMessage = "Errore: " + ex.Message; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InsertDefaultBettingPrompt()
|
||||||
|
{
|
||||||
|
SystemPrompt =
|
||||||
|
"Sei il nodo validatore di una blockchain per una piattaforma di scommesse decentralizzata. " +
|
||||||
|
"Il tuo compito e generare transazioni fittizie ma realistiche in formato JSON puro. " +
|
||||||
|
"Non includere testo introduttivo o conclusivo, restituisci solo un array di oggetti JSON.\n\n" +
|
||||||
|
"Regole per la generazione dei dati:\n" +
|
||||||
|
"- Ogni transazione deve avere un tx_hash esadecimale casuale di 64 caratteri e un timestamp ISO 8601.\n" +
|
||||||
|
"- bet_type puo essere solo 'singola' o 'multipla'.\n" +
|
||||||
|
"- Genera un bankroll_at_time casuale tra 100 e 5000.\n" +
|
||||||
|
"- Logica dello Stake: Se 'singola', stake_amount deve essere intero e multiplo di 5. " +
|
||||||
|
"Se 'multipla', genera confidence_level tra 0.0 e 1.0. Se confidenza tra 0.9 e 1.0, " +
|
||||||
|
"stake_amount fino al 10% del bankroll. Altrimenti sotto l'1%.\n" +
|
||||||
|
"- Includi smart_contract_log che spiega come lo smart contract ha processato i fondi.";
|
||||||
|
|
||||||
|
UserPrompt = "Genera un blocco di 5 nuove transazioni sequenziali rispettando rigorosamente le regole di business.";
|
||||||
|
|
||||||
|
StatusMessage = "Prompt blockchain betting inseriti. Ricorda di salvare.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyPreset(string preset)
|
||||||
|
{
|
||||||
|
switch (preset)
|
||||||
|
{
|
||||||
|
case "OpenAI":
|
||||||
|
ApiEndpoint = "https://api.openai.com/v1/chat/completions";
|
||||||
|
ModelName = "gpt-4o";
|
||||||
|
StatusMessage = "Preset OpenAI applicato. Inserisci la API Key.";
|
||||||
|
break;
|
||||||
|
case "Anthropic":
|
||||||
|
ApiEndpoint = "https://api.anthropic.com/v1/messages";
|
||||||
|
ModelName = "claude-3-5-sonnet-20241022";
|
||||||
|
StatusMessage = "Preset Anthropic applicato. Inserisci la API Key.";
|
||||||
|
break;
|
||||||
|
case "Google AI":
|
||||||
|
ApiEndpoint = "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions";
|
||||||
|
ModelName = "gemini-2.0-flash-exp";
|
||||||
|
StatusMessage = "Preset Google AI applicato. Inserisci la API Key.";
|
||||||
|
break;
|
||||||
|
case "Azure OpenAI":
|
||||||
|
ApiEndpoint = "https://<resource>.openai.azure.com/openai/deployments/<deployment>/chat/completions?api-version=2024-02-15-preview";
|
||||||
|
ModelName = "gpt-4";
|
||||||
|
StatusMessage = "Sostituisci <resource> e <deployment>.";
|
||||||
|
break;
|
||||||
|
case "LM Studio (Local)":
|
||||||
|
ApiEndpoint = "http://127.0.0.1:1234/v1/chat/completions";
|
||||||
|
ModelName = "";
|
||||||
|
ApiKey = "";
|
||||||
|
StatusMessage = "Preset LM Studio applicato.";
|
||||||
|
break;
|
||||||
|
case "Ollama (Local)":
|
||||||
|
ApiEndpoint = "http://127.0.0.1:11434/v1/chat/completions";
|
||||||
|
ModelName = "llama3.3";
|
||||||
|
ApiKey = "";
|
||||||
|
StatusMessage = "Preset Ollama applicato.";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
|
using CommunityToolkit.Mvvm.Input;
|
||||||
|
using Dione.Data;
|
||||||
|
using Dione.Models;
|
||||||
|
|
||||||
|
namespace Dione.ViewModels
|
||||||
|
{
|
||||||
|
public class TelemetryHistoryViewModel : ObservableObject
|
||||||
|
{
|
||||||
|
private ObservableCollection<TelemetryLog> _logs = new ObservableCollection<TelemetryLog>();
|
||||||
|
public ObservableCollection<TelemetryLog> Logs { get => _logs; set => SetProperty(ref _logs, value); }
|
||||||
|
|
||||||
|
// Filters
|
||||||
|
private string _filterBatchId = "";
|
||||||
|
public string FilterBatchId { get => _filterBatchId; set => SetProperty(ref _filterBatchId, value); }
|
||||||
|
|
||||||
|
private string _filterModel = "";
|
||||||
|
public string FilterModel { get => _filterModel; set => SetProperty(ref _filterModel, value); }
|
||||||
|
|
||||||
|
private bool? _filterSuccess;
|
||||||
|
public bool? FilterSuccess { get => _filterSuccess; set => SetProperty(ref _filterSuccess, value); }
|
||||||
|
|
||||||
|
public string[] SuccessOptions { get; } = new[] { "All", "Success", "Errors" };
|
||||||
|
|
||||||
|
private string _selectedSuccessOption = "All";
|
||||||
|
public string SelectedSuccessOption
|
||||||
|
{
|
||||||
|
get => _selectedSuccessOption;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (SetProperty(ref _selectedSuccessOption, value))
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case "Success": FilterSuccess = true; break;
|
||||||
|
case "Errors": FilterSuccess = false; break;
|
||||||
|
default: FilterSuccess = null; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats
|
||||||
|
private int _totalCount;
|
||||||
|
public int TotalCount { get => _totalCount; set => SetProperty(ref _totalCount, value); }
|
||||||
|
|
||||||
|
private int _filteredCount;
|
||||||
|
public int FilteredCount { get => _filteredCount; set => SetProperty(ref _filteredCount, value); }
|
||||||
|
|
||||||
|
private string _statusMessage = "";
|
||||||
|
public string StatusMessage { get => _statusMessage; set => SetProperty(ref _statusMessage, value); }
|
||||||
|
|
||||||
|
public RelayCommand RefreshCommand { get; }
|
||||||
|
public RelayCommand ApplyFilterCommand { get; }
|
||||||
|
public RelayCommand ClearFilterCommand { get; }
|
||||||
|
|
||||||
|
public TelemetryHistoryViewModel()
|
||||||
|
{
|
||||||
|
RefreshCommand = new RelayCommand(Refresh);
|
||||||
|
ApplyFilterCommand = new RelayCommand(ApplyFilter);
|
||||||
|
ClearFilterCommand = new RelayCommand(ClearFilter);
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Refresh()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var all = SynthDataDbContext.QueryAll();
|
||||||
|
TotalCount = all.Count;
|
||||||
|
ApplyFilterToList(all);
|
||||||
|
StatusMessage = $"Loaded {TotalCount} total records from database.";
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StatusMessage = "Error: " + ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyFilter()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var all = SynthDataDbContext.QueryAll();
|
||||||
|
TotalCount = all.Count;
|
||||||
|
ApplyFilterToList(all);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
StatusMessage = "Error: " + ex.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyFilterToList(System.Collections.Generic.List<TelemetryLog> all)
|
||||||
|
{
|
||||||
|
var filtered = all.AsEnumerable();
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(FilterBatchId))
|
||||||
|
filtered = filtered.Where(l => (l.BatchId ?? "").IndexOf(FilterBatchId, StringComparison.OrdinalIgnoreCase) >= 0);
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(FilterModel))
|
||||||
|
filtered = filtered.Where(l => (l.ModelUsed ?? "").IndexOf(FilterModel, StringComparison.OrdinalIgnoreCase) >= 0);
|
||||||
|
|
||||||
|
if (FilterSuccess.HasValue)
|
||||||
|
filtered = filtered.Where(l => l.IsSuccess == FilterSuccess.Value);
|
||||||
|
|
||||||
|
var result = filtered.OrderByDescending(l => l.Timestamp).ToList();
|
||||||
|
FilteredCount = result.Count;
|
||||||
|
Logs = new ObservableCollection<TelemetryLog>(result);
|
||||||
|
StatusMessage = $"Showing {FilteredCount} of {TotalCount} records.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearFilter()
|
||||||
|
{
|
||||||
|
FilterBatchId = "";
|
||||||
|
FilterModel = "";
|
||||||
|
SelectedSuccessOption = "All";
|
||||||
|
Refresh();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
<UserControl x:Class="Dione.Views.DashboardView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:lvc="clr-namespace:LiveCharts.Wpf;assembly=LiveCharts.Wpf"
|
||||||
|
xmlns:vm="clr-namespace:Dione.ViewModels"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<UserControl.Resources>
|
||||||
|
<Style x:Key="StatCard" TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="#1E1E2E"/>
|
||||||
|
<Setter Property="CornerRadius" Value="12"/>
|
||||||
|
<Setter Property="Padding" Value="20,16"/>
|
||||||
|
<Setter Property="Margin" Value="8"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="StatValue" TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="28"/>
|
||||||
|
<Setter Property="FontWeight" Value="Bold"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="StatLabel" TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="12"/>
|
||||||
|
<Setter Property="Foreground" Value="#90CAF9"/>
|
||||||
|
<Setter Property="Margin" Value="0,4,0,0"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="ChartCard" TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="#1E1E2E"/>
|
||||||
|
<Setter Property="CornerRadius" Value="12"/>
|
||||||
|
<Setter Property="Padding" Value="16"/>
|
||||||
|
<Setter Property="Margin" Value="8"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="ChartTitle" TargetType="TextBlock">
|
||||||
|
<Setter Property="FontSize" Value="14"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
<Setter Property="Foreground" Value="#B0BEC5"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,0,8"/>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
|
||||||
|
<StackPanel Margin="16">
|
||||||
|
<DockPanel Margin="8,0,0,16">
|
||||||
|
<TextBlock Text="Dashboard" FontSize="26" FontWeight="Bold" Foreground="White" VerticalAlignment="Center"/>
|
||||||
|
<Button Content="Refresh" Command="{Binding RefreshCommand}" DockPanel.Dock="Right"
|
||||||
|
HorizontalAlignment="Right" Padding="16,6" FontSize="12"
|
||||||
|
Background="#2A2A3E" Foreground="#90CAF9" BorderThickness="0" Cursor="Hand">
|
||||||
|
<Button.Template>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="14" VerticalAlignment="Center" Margin="0,0,6,0"/>
|
||||||
|
<ContentPresenter VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Button.Template>
|
||||||
|
</Button>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<!-- Empty state guidance -->
|
||||||
|
<Border Background="#1A1A26" CornerRadius="10" Padding="24" Margin="8,0,8,16">
|
||||||
|
<Border.Style>
|
||||||
|
<Style TargetType="Border">
|
||||||
|
<Setter Property="Visibility" Value="Collapsed"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding ValidRecords}" Value="0">
|
||||||
|
<Setter Property="Visibility" Value="Visible"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Border.Style>
|
||||||
|
<StackPanel HorizontalAlignment="Center">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="36" Foreground="#37474F" HorizontalAlignment="Center"/>
|
||||||
|
<TextBlock Text="Nessun dato disponibile" FontSize="18" FontWeight="SemiBold" Foreground="#78909C" HorizontalAlignment="Center" Margin="0,10,0,6"/>
|
||||||
|
<TextBlock TextWrapping="Wrap" TextAlignment="Center" Foreground="#546E7A" FontSize="13" MaxWidth="500">
|
||||||
|
Per iniziare: vai in Settings per configurare un profilo, poi in Data Designer per generare il prompt, e infine in Live Generation per avviare la generazione dati. I grafici si popoleranno automaticamente.
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- KPI Cards -->
|
||||||
|
<UniformGrid Rows="1" Columns="4">
|
||||||
|
<Border Style="{StaticResource StatCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="TOKENS" FontSize="11" Foreground="#546E7A" FontWeight="Bold"/>
|
||||||
|
<TextBlock Text="{Binding TotalTokensSpent, StringFormat=N0}" Style="{StaticResource StatValue}"/>
|
||||||
|
<TextBlock Text="Total Tokens Spent" Style="{StaticResource StatLabel}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource StatCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="RECORDS" FontSize="11" Foreground="#546E7A" FontWeight="Bold"/>
|
||||||
|
<TextBlock Text="{Binding ValidRecords, StringFormat=N0}" Style="{StaticResource StatValue}"/>
|
||||||
|
<TextBlock Text="Valid Records" Style="{StaticResource StatLabel}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource StatCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="COST" FontSize="11" Foreground="#546E7A" FontWeight="Bold"/>
|
||||||
|
<TextBlock Style="{StaticResource StatValue}">
|
||||||
|
<TextBlock.Text>
|
||||||
|
<Binding Path="EstimatedCostEur" StringFormat="EUR {0:F2}"/>
|
||||||
|
</TextBlock.Text>
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock Text="Est. Electric Cost" Style="{StaticResource StatLabel}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border Style="{StaticResource StatCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="QUALITY" FontSize="11" Foreground="#546E7A" FontWeight="Bold"/>
|
||||||
|
<TextBlock Text="{Binding QualityScore, StringFormat={}{0:F1}%}" Style="{StaticResource StatValue}"/>
|
||||||
|
<TextBlock Text="Quality Score" Style="{StaticResource StatLabel}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</UniformGrid>
|
||||||
|
|
||||||
|
<!-- Charts Row 1 -->
|
||||||
|
<Grid Margin="0,8,0,0">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="2*"/>
|
||||||
|
<ColumnDefinition Width="1*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<Border Grid.Column="0" Style="{StaticResource ChartCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="Tokens Generated / Minute" Style="{StaticResource ChartTitle}"/>
|
||||||
|
<lvc:CartesianChart Height="220" Series="{Binding TokensPerMinuteSeries}" LegendLocation="None">
|
||||||
|
<lvc:CartesianChart.AxisX>
|
||||||
|
<lvc:Axis Labels="{Binding TokensPerMinuteLabels}" Foreground="#78909C"/>
|
||||||
|
</lvc:CartesianChart.AxisX>
|
||||||
|
<lvc:CartesianChart.AxisY>
|
||||||
|
<lvc:Axis Foreground="#78909C"/>
|
||||||
|
</lvc:CartesianChart.AxisY>
|
||||||
|
</lvc:CartesianChart>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Grid.Column="1" Style="{StaticResource ChartCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="Success vs Errors" Style="{StaticResource ChartTitle}"/>
|
||||||
|
<lvc:PieChart Height="220" Series="{Binding SuccessErrorSeries}" LegendLocation="Bottom" InnerRadius="50"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Latency bar chart -->
|
||||||
|
<Border Style="{StaticResource ChartCard}" Margin="8,8,8,8">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="API Latency (ms)" Style="{StaticResource ChartTitle}"/>
|
||||||
|
<lvc:CartesianChart Height="200" Series="{Binding LatencySeries}" LegendLocation="None">
|
||||||
|
<lvc:CartesianChart.AxisX>
|
||||||
|
<lvc:Axis Labels="{Binding LatencyLabels}" Foreground="#78909C"/>
|
||||||
|
</lvc:CartesianChart.AxisX>
|
||||||
|
<lvc:CartesianChart.AxisY>
|
||||||
|
<lvc:Axis Foreground="#78909C" Title="ms"/>
|
||||||
|
</lvc:CartesianChart.AxisY>
|
||||||
|
</lvc:CartesianChart>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Dione.Views
|
||||||
|
{
|
||||||
|
public partial class DashboardView : UserControl
|
||||||
|
{
|
||||||
|
public DashboardView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
<UserControl x:Class="Dione.Views.DataDesignerView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<UserControl.Resources>
|
||||||
|
<Style x:Key="FieldLabel" TargetType="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#90CAF9"/>
|
||||||
|
<Setter Property="FontSize" Value="12"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,0,4"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="EditorBox" TargetType="TextBox">
|
||||||
|
<Setter Property="Background" Value="#1E1E2E"/>
|
||||||
|
<Setter Property="Foreground" Value="#E0E0E0"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#2A2A3E"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="Padding" Value="10,8"/>
|
||||||
|
<Setter Property="FontFamily" Value="Consolas"/>
|
||||||
|
<Setter Property="FontSize" Value="12"/>
|
||||||
|
<Setter Property="AcceptsReturn" Value="True"/>
|
||||||
|
<Setter Property="TextWrapping" Value="Wrap"/>
|
||||||
|
<Setter Property="VerticalScrollBarVisibility" Value="Auto"/>
|
||||||
|
<Setter Property="CaretBrush" Value="White"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="ActionBtn" TargetType="Button">
|
||||||
|
<Setter Property="Background" Value="#2962FF"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
<Setter Property="Padding" Value="18,8"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,8,0"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="FontSize" Value="12"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="SecondaryBtn" BasedOn="{StaticResource ActionBtn}" TargetType="Button">
|
||||||
|
<Setter Property="Background" Value="#2A2A3E"/>
|
||||||
|
<Setter Property="Foreground" Value="#90CAF9"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="SectionCard" TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="#1A1A26"/>
|
||||||
|
<Setter Property="CornerRadius" Value="10"/>
|
||||||
|
<Setter Property="Padding" Value="20"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,0,16"/>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
|
<Grid Margin="24,16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- Title + profile selector -->
|
||||||
|
<StackPanel Grid.Row="0" Margin="0,0,0,16">
|
||||||
|
<TextBlock Text="Data Designer" FontSize="26" FontWeight="Bold" Foreground="White" Margin="0,0,0,12"/>
|
||||||
|
<DockPanel>
|
||||||
|
<Button Content="Auto-Generate" Command="{Binding GenerateTemplateCommand}" Style="{StaticResource ActionBtn}" DockPanel.Dock="Right"/>
|
||||||
|
<Button Content="Refresh" Command="{Binding RefreshCommand}" Style="{StaticResource SecondaryBtn}" DockPanel.Dock="Right" Margin="0,0,8,0"/>
|
||||||
|
<ComboBox ItemsSource="{Binding Profiles}" SelectedItem="{Binding SelectedProfile}"
|
||||||
|
DisplayMemberPath="Name"
|
||||||
|
Background="#1E1E2E" Foreground="White" BorderBrush="#2A2A3E" Padding="8,6" Margin="0,0,8,0"/>
|
||||||
|
</DockPanel>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Schema preview -->
|
||||||
|
<Border Grid.Row="1" Style="{StaticResource SectionCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="SCHEMA (from Settings)" FontSize="11" FontWeight="Bold" Foreground="#546E7A" Margin="0,0,0,6"/>
|
||||||
|
<TextBox Text="{Binding SchemaPreview, Mode=OneWay}" Style="{StaticResource EditorBox}" Height="60" IsReadOnly="True" Opacity="0.7"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Editors in two columns -->
|
||||||
|
<Grid Grid.Row="2">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="16"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- System Prompt -->
|
||||||
|
<Border Grid.Column="0" Style="{StaticResource SectionCard}">
|
||||||
|
<DockPanel>
|
||||||
|
<TextBlock Text="SYSTEM PROMPT" FontSize="11" FontWeight="Bold" Foreground="#546E7A" Margin="0,0,0,8" DockPanel.Dock="Top"/>
|
||||||
|
<TextBox Text="{Binding SystemPrompt, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource EditorBox}"/>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Few-Shot Examples -->
|
||||||
|
<Border Grid.Column="2" Style="{StaticResource SectionCard}">
|
||||||
|
<DockPanel>
|
||||||
|
<TextBlock Text="FEW-SHOT EXAMPLES" FontSize="11" FontWeight="Bold" Foreground="#546E7A" Margin="0,0,0,8" DockPanel.Dock="Top"/>
|
||||||
|
<TextBox Text="{Binding FewShotExamples, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource EditorBox}"/>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<!-- Bottom: Save + status -->
|
||||||
|
<DockPanel Grid.Row="3" Margin="0,8,0,0">
|
||||||
|
<Button Content="Save Prompt to Profile" Command="{Binding SavePromptCommand}" Style="{StaticResource ActionBtn}" DockPanel.Dock="Left"/>
|
||||||
|
<TextBlock Text="{Binding StatusMessage}" Foreground="#4CAF50" FontSize="12" VerticalAlignment="Center" Margin="8,0,0,0"/>
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Dione.Views
|
||||||
|
{
|
||||||
|
public partial class DataDesignerView : UserControl
|
||||||
|
{
|
||||||
|
public DataDesignerView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,643 @@
|
|||||||
|
<UserControl x:Class="Dione.Views.LiveGenerationView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<UserControl.Resources>
|
||||||
|
|
||||||
|
<!-- Colori di accento -->
|
||||||
|
<SolidColorBrush x:Key="AccentGreen" Color="#4CAF50"/>
|
||||||
|
<SolidColorBrush x:Key="AccentRed" Color="#EF5350"/>
|
||||||
|
<SolidColorBrush x:Key="AccentBlue" Color="#42A5F5"/>
|
||||||
|
<SolidColorBrush x:Key="AccentPurple" Color="#AB47BC"/>
|
||||||
|
<SolidColorBrush x:Key="AccentOrange" Color="#FFA726"/>
|
||||||
|
<SolidColorBrush x:Key="AccentTeal" Color="#26C6DA"/>
|
||||||
|
<SolidColorBrush x:Key="Muted" Color="#546E7A"/>
|
||||||
|
|
||||||
|
<!-- Card generica -->
|
||||||
|
<Style x:Key="Card" TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="#1A1A2E"/>
|
||||||
|
<Setter Property="CornerRadius" Value="14"/>
|
||||||
|
<Setter Property="Padding" Value="20,16"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,0,12"/>
|
||||||
|
<Setter Property="Effect">
|
||||||
|
<Setter.Value>
|
||||||
|
<DropShadowEffect BlurRadius="18" ShadowDepth="2" Opacity="0.25" Color="#000000"/>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- KPI pill card -->
|
||||||
|
<Style x:Key="KpiCard" TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="#1E1E32"/>
|
||||||
|
<Setter Property="CornerRadius" Value="12"/>
|
||||||
|
<Setter Property="Padding" Value="16,12"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,10,10"/>
|
||||||
|
<Setter Property="MinWidth" Value="110"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Bottone principale arrotondato -->
|
||||||
|
<Style x:Key="RoundBtn" TargetType="Button">
|
||||||
|
<Setter Property="Padding" Value="20,10"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,8,0"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="FontSize" Value="12"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border x:Name="Bd" Background="{TemplateBinding Background}"
|
||||||
|
CornerRadius="20" Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="Bd" Property="Opacity" Value="0.82"/>
|
||||||
|
</Trigger>
|
||||||
|
<Trigger Property="IsEnabled" Value="False">
|
||||||
|
<Setter TargetName="Bd" Property="Opacity" Value="0.35"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Bottone icona tondo -->
|
||||||
|
<Style x:Key="IconBtn" TargetType="Button">
|
||||||
|
<Setter Property="Width" Value="36"/>
|
||||||
|
<Setter Property="Height" Value="36"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,6,0"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="Foreground" Value="#78909C"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border x:Name="Bd" Background="#252540" CornerRadius="18">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="Bd" Property="Background" Value="#2E2E50"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Log list item style -->
|
||||||
|
<Style x:Key="LogItem" TargetType="ListBoxItem">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
<Setter Property="Padding" Value="0,2"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="ListBoxItem">
|
||||||
|
<Border x:Name="Bd" Background="Transparent" CornerRadius="4" Padding="6,3" Margin="0,1">
|
||||||
|
<ContentPresenter/>
|
||||||
|
</Border>
|
||||||
|
<ControlTemplate.Triggers>
|
||||||
|
<Trigger Property="IsMouseOver" Value="True">
|
||||||
|
<Setter TargetName="Bd" Property="Background" Value="#22FFFFFF"/>
|
||||||
|
</Trigger>
|
||||||
|
</ControlTemplate.Triggers>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- Separatore verticale leggero -->
|
||||||
|
<Style x:Key="VSep" TargetType="Border">
|
||||||
|
<Setter Property="Width" Value="1"/>
|
||||||
|
<Setter Property="Background" Value="#252540"/>
|
||||||
|
<Setter Property="Margin" Value="16,6"/>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
|
<Grid Margin="20,16,20,12">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/> <!-- Header toolbar -->
|
||||||
|
<RowDefinition Height="Auto"/> <!-- KPI strip -->
|
||||||
|
<RowDefinition Height="Auto"/> <!-- File progress bar -->
|
||||||
|
<RowDefinition Height="*"/> <!-- Preview + Log -->
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════════════
|
||||||
|
ROW 0 — HEADER TOOLBAR
|
||||||
|
══════════════════════════════════════════════════════════════ -->
|
||||||
|
<Border Grid.Row="0" Style="{StaticResource Card}" Margin="0,0,0,12">
|
||||||
|
<DockPanel VerticalAlignment="Center">
|
||||||
|
|
||||||
|
<!-- Titolo + stato pill -->
|
||||||
|
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
|
<TextBlock Text="Generazione" FontSize="20" FontWeight="Bold"
|
||||||
|
Foreground="White" VerticalAlignment="Center"/>
|
||||||
|
<Border CornerRadius="10" Padding="10,4" Margin="14,0,0,0">
|
||||||
|
<Border.Style>
|
||||||
|
<Style TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="#1B3A1F"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsRunning}" Value="False">
|
||||||
|
<Setter Property="Background" Value="#252540"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Border.Style>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<!-- Pallino animato -->
|
||||||
|
<Ellipse Width="7" Height="7" VerticalAlignment="Center" Margin="0,0,6,0">
|
||||||
|
<Ellipse.Style>
|
||||||
|
<Style TargetType="Ellipse">
|
||||||
|
<Setter Property="Fill" Value="#546E7A"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsRunning}" Value="True">
|
||||||
|
<Setter Property="Fill" Value="#4CAF50"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Ellipse.Style>
|
||||||
|
</Ellipse>
|
||||||
|
<TextBlock VerticalAlignment="Center" FontSize="11" FontWeight="SemiBold">
|
||||||
|
<TextBlock.Style>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="Text" Value="Fermo"/>
|
||||||
|
<Setter Property="Foreground" Value="#546E7A"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsRunning}" Value="True">
|
||||||
|
<Setter Property="Text" Value="In esecuzione"/>
|
||||||
|
<Setter Property="Foreground" Value="#4CAF50"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</TextBlock.Style>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Pulsanti a destra -->
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" VerticalAlignment="Center">
|
||||||
|
|
||||||
|
<!-- Timer -->
|
||||||
|
<Border CornerRadius="10" Background="#1E1E32" Padding="12,6" Margin="0,0,14,0">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="12"
|
||||||
|
Foreground="#546E7A" VerticalAlignment="Center" Margin="0,0,6,0"/>
|
||||||
|
<TextBlock Text="{Binding ElapsedTime}" Foreground="#90CAF9"
|
||||||
|
FontSize="13" FontFamily="Consolas" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Button Command="{Binding StartCommand}" Style="{StaticResource RoundBtn}" Background="#1B5E20">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="13" VerticalAlignment="Center" Margin="0,0,7,0"/>
|
||||||
|
<TextBlock Text="Avvia" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button Command="{Binding StopCommand}" Style="{StaticResource RoundBtn}" Background="#B71C1C">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="13" VerticalAlignment="Center" Margin="0,0,7,0"/>
|
||||||
|
<TextBlock Text="Stop" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════════════
|
||||||
|
ROW 1 — KPI STRIP
|
||||||
|
══════════════════════════════════════════════════════════════ -->
|
||||||
|
<UniformGrid Grid.Row="1" Rows="1" Margin="0,0,0,12">
|
||||||
|
|
||||||
|
<!-- Batch OK -->
|
||||||
|
<Border Style="{StaticResource KpiCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,6">
|
||||||
|
<Border Width="28" Height="28" CornerRadius="14" Background="#1B3A1F">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="13"
|
||||||
|
Foreground="#4CAF50" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<TextBlock Text="BATCH OK" Foreground="#546E7A" FontSize="10"
|
||||||
|
VerticalAlignment="Center" Margin="8,0,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Text="{Binding SuccessCount}" Foreground="#4CAF50"
|
||||||
|
FontSize="26" FontWeight="Bold"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Errori -->
|
||||||
|
<Border Style="{StaticResource KpiCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,6">
|
||||||
|
<Border Width="28" Height="28" CornerRadius="14" Background="#3E1A1A">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="13"
|
||||||
|
Foreground="#EF5350" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<TextBlock Text="ERRORI" Foreground="#546E7A" FontSize="10"
|
||||||
|
VerticalAlignment="Center" Margin="8,0,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Text="{Binding ErrorCount}" Foreground="#EF5350"
|
||||||
|
FontSize="26" FontWeight="Bold"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Record scritti -->
|
||||||
|
<Border Style="{StaticResource KpiCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,6">
|
||||||
|
<Border Width="28" Height="28" CornerRadius="14" Background="#0D2A3E">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="13"
|
||||||
|
Foreground="#42A5F5" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<TextBlock Text="RECORD" Foreground="#546E7A" FontSize="10"
|
||||||
|
VerticalAlignment="Center" Margin="8,0,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Text="{Binding TotalRecordsWritten}" Foreground="#42A5F5"
|
||||||
|
FontSize="26" FontWeight="Bold"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Token totali -->
|
||||||
|
<Border Style="{StaticResource KpiCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,6">
|
||||||
|
<Border Width="28" Height="28" CornerRadius="14" Background="#2A1A3E">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="13"
|
||||||
|
Foreground="#AB47BC" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<TextBlock Text="TOKEN" Foreground="#546E7A" FontSize="10"
|
||||||
|
VerticalAlignment="Center" Margin="8,0,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Text="{Binding TokensTotal, StringFormat={}{0:N0}}" Foreground="#AB47BC"
|
||||||
|
FontSize="26" FontWeight="Bold"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- File creati -->
|
||||||
|
<Border Style="{StaticResource KpiCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,6">
|
||||||
|
<Border Width="28" Height="28" CornerRadius="14" Background="#0D2E2E">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="13"
|
||||||
|
Foreground="#26C6DA" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<TextBlock Text="FILE" Foreground="#546E7A" FontSize="10"
|
||||||
|
VerticalAlignment="Center" Margin="8,0,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Text="{Binding FilesCreated}" Foreground="#26C6DA"
|
||||||
|
FontSize="26" FontWeight="Bold"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Tempo medio batch -->
|
||||||
|
<Border Style="{StaticResource KpiCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,0,6">
|
||||||
|
<Border Width="28" Height="28" CornerRadius="14" Background="#2E2A0D">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="13"
|
||||||
|
Foreground="#FFA726" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
<TextBlock Text="AVG BATCH" Foreground="#546E7A" FontSize="10"
|
||||||
|
VerticalAlignment="Center" Margin="8,0,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
<TextBlock Text="{Binding AvgBatchTimeMs, StringFormat={}{0:F0} ms}"
|
||||||
|
Foreground="#FFA726" FontSize="22" FontWeight="Bold"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</UniformGrid>
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════════════
|
||||||
|
ROW 2 — FILE PROGRESS
|
||||||
|
══════════════════════════════════════════════════════════════ -->
|
||||||
|
<Border Grid.Row="2" Style="{StaticResource Card}" Padding="20,14" Margin="0,0,0,12">
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="8"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<!-- File name -->
|
||||||
|
<StackPanel Grid.Row="0" Grid.Column="0" Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="13"
|
||||||
|
Foreground="#26C6DA" VerticalAlignment="Center" Margin="0,0,8,0"/>
|
||||||
|
<TextBlock Text="{Binding CurrentFileName, TargetNullValue='—'}"
|
||||||
|
Foreground="White" FontSize="12" FontWeight="SemiBold" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Dimensione a destra -->
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="2"
|
||||||
|
Foreground="#546E7A" FontSize="11" VerticalAlignment="Center">
|
||||||
|
<Run Text="{Binding CurrentFileSizeMb, StringFormat={}{0:F1}, Mode=OneWay}"/>
|
||||||
|
<Run Text=" / "/>
|
||||||
|
<Run Text="{Binding DataContext.MaxFileSizeMb, RelativeSource={RelativeSource AncestorType=UserControl}, StringFormat={}{0} MB, FallbackValue='250 MB'}"/>
|
||||||
|
</TextBlock>
|
||||||
|
|
||||||
|
<!-- Progress bar arrotondata -->
|
||||||
|
<Border Grid.Row="2" Grid.ColumnSpan="3" Height="8" CornerRadius="4" Background="#252540" Margin="0,6,0,0">
|
||||||
|
<Border CornerRadius="4" HorizontalAlignment="Left" Background="#26C6DA">
|
||||||
|
<Border.Style>
|
||||||
|
<Style TargetType="Border">
|
||||||
|
<Setter Property="MinWidth" Value="0"/>
|
||||||
|
</Style>
|
||||||
|
</Border.Style>
|
||||||
|
<!-- Width proporzionale tramite converter non disponibile: usiamo ProgressBar nascosta -->
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- ProgressBar trasparente che calcola la percentuale -->
|
||||||
|
<ProgressBar Grid.Row="2" Grid.ColumnSpan="3"
|
||||||
|
Height="8" Margin="0,6,0,0"
|
||||||
|
Minimum="0" Maximum="{Binding MaxFileSizeMbDouble, FallbackValue=250}"
|
||||||
|
Value="{Binding CurrentFileSizeMb, Mode=OneWay}"
|
||||||
|
Foreground="#26C6DA" Background="#252540">
|
||||||
|
<ProgressBar.Template>
|
||||||
|
<ControlTemplate TargetType="ProgressBar">
|
||||||
|
<Border CornerRadius="4" Background="{TemplateBinding Background}">
|
||||||
|
<Border x:Name="PART_Track" CornerRadius="4" Background="{TemplateBinding Background}">
|
||||||
|
<Border x:Name="PART_Indicator" HorizontalAlignment="Left"
|
||||||
|
CornerRadius="4" Background="{TemplateBinding Foreground}"/>
|
||||||
|
</Border>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</ProgressBar.Template>
|
||||||
|
</ProgressBar>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- ══════════════════════════════════════════════════════════════
|
||||||
|
ROW 3 — PREVIEW + LOG
|
||||||
|
══════════════════════════════════════════════════════════════ -->
|
||||||
|
<Grid Grid.Row="3">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="5*"/>
|
||||||
|
<ColumnDefinition Width="12"/>
|
||||||
|
<ColumnDefinition Width="7*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
|
||||||
|
<!-- Preview / Streaming -->
|
||||||
|
<Border Grid.Column="0" Style="{StaticResource Card}" Padding="16,14">
|
||||||
|
<DockPanel>
|
||||||
|
|
||||||
|
<!-- Header con badge dinamico -->
|
||||||
|
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,10">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="12"
|
||||||
|
Foreground="#26C6DA" VerticalAlignment="Center" Margin="0,0,7,0"/>
|
||||||
|
<TextBlock Foreground="#546E7A" FontSize="10" FontWeight="Bold"
|
||||||
|
VerticalAlignment="Center">
|
||||||
|
<TextBlock.Style>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="Text" Value="ULTIMO OUTPUT"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsStreaming}" Value="True">
|
||||||
|
<Setter Property="Text" Value="STREAMING IN CORSO"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</TextBlock.Style>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Badge LIVE / JSONL -->
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||||
|
|
||||||
|
<!-- Contatori streaming (visibili solo durante streaming) -->
|
||||||
|
<StackPanel Orientation="Horizontal" Margin="0,0,8,0">
|
||||||
|
<StackPanel.Style>
|
||||||
|
<Style TargetType="StackPanel">
|
||||||
|
<Setter Property="Visibility" Value="Collapsed"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsStreaming}" Value="True">
|
||||||
|
<Setter Property="Visibility" Value="Visible"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</StackPanel.Style>
|
||||||
|
<Border CornerRadius="4" Background="#1A2A4A" Padding="6,2" Margin="0,0,5,0">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="9" Foreground="#AB47BC"
|
||||||
|
VerticalAlignment="Center" Margin="0,0,4,0"/>
|
||||||
|
<TextBlock Text="{Binding StreamingTokensLive, StringFormat={}{0} tok}"
|
||||||
|
Foreground="#AB47BC" FontFamily="Consolas" FontSize="10"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
<Border CornerRadius="4" Background="#0D2E2E" Padding="6,2">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets"
|
||||||
|
FontSize="9" Foreground="#26C6DA"
|
||||||
|
VerticalAlignment="Center" Margin="0,0,4,0"/>
|
||||||
|
<TextBlock Text="{Binding StreamingChars, StringFormat={}{0} car}"
|
||||||
|
Foreground="#26C6DA" FontFamily="Consolas" FontSize="10"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
|
<!-- Badge LIVE (animato) -->
|
||||||
|
<Border CornerRadius="4" Padding="6,2">
|
||||||
|
<Border.Style>
|
||||||
|
<Style TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="#0D2E2E"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsStreaming}" Value="True">
|
||||||
|
<Setter Property="Background" Value="#1B3A1F"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Border.Style>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Ellipse Width="6" Height="6" VerticalAlignment="Center" Margin="0,0,5,0">
|
||||||
|
<Ellipse.Style>
|
||||||
|
<Style TargetType="Ellipse">
|
||||||
|
<Setter Property="Fill" Value="#26C6DA"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsStreaming}" Value="True">
|
||||||
|
<Setter Property="Fill" Value="#4CAF50"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Ellipse.Style>
|
||||||
|
</Ellipse>
|
||||||
|
<TextBlock FontSize="9" FontWeight="Bold">
|
||||||
|
<TextBlock.Style>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="Text" Value="JSONL"/>
|
||||||
|
<Setter Property="Foreground" Value="#26C6DA"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsStreaming}" Value="True">
|
||||||
|
<Setter Property="Text" Value="LIVE"/>
|
||||||
|
<Setter Property="Foreground" Value="#4CAF50"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</TextBlock.Style>
|
||||||
|
</TextBlock>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<!-- Cursore lampeggiante durante lo streaming -->
|
||||||
|
<Border DockPanel.Dock="Bottom" Height="2" CornerRadius="1" Margin="0,6,0,0">
|
||||||
|
<Border.Style>
|
||||||
|
<Style TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="Transparent"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsStreaming}" Value="True">
|
||||||
|
<Setter Property="Background" Value="#4CAF50"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Border.Style>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Testo: StreamingText durante streaming, LastPreview a riposo -->
|
||||||
|
<ScrollViewer x:Name="StreamScrollViewer" VerticalScrollBarVisibility="Auto">
|
||||||
|
<TextBox IsReadOnly="True" TextWrapping="Wrap"
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0" FontFamily="Consolas" FontSize="11"
|
||||||
|
VerticalContentAlignment="Top" Padding="0">
|
||||||
|
<TextBox.Style>
|
||||||
|
<Style TargetType="TextBox">
|
||||||
|
<Setter Property="Text" Value="{Binding LastPreview, Mode=OneWay}"/>
|
||||||
|
<Setter Property="Foreground" Value="#80CBC4"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding IsStreaming}" Value="True">
|
||||||
|
<Setter Property="Text" Value="{Binding StreamingText, Mode=OneWay}"/>
|
||||||
|
<Setter Property="Foreground" Value="#C8E6C9"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</TextBox.Style>
|
||||||
|
</TextBox>
|
||||||
|
</ScrollViewer>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Log -->
|
||||||
|
<Border Grid.Column="2" Style="{StaticResource Card}" Padding="16,14">
|
||||||
|
<DockPanel>
|
||||||
|
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,10">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="12"
|
||||||
|
Foreground="#FFA726" VerticalAlignment="Center" Margin="0,0,7,0"/>
|
||||||
|
<TextBlock Text="LOG IN TEMPO REALE" Foreground="#546E7A"
|
||||||
|
FontSize="10" FontWeight="Bold" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||||
|
<!-- Copia log -->
|
||||||
|
<Button Command="{Binding CopyLogsCommand}" Style="{StaticResource IconBtn}"
|
||||||
|
ToolTip="Copia tutti i log negli appunti">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="13"/>
|
||||||
|
</Button>
|
||||||
|
<!-- Pulisci log -->
|
||||||
|
<Button Command="{Binding ClearLogCommand}" Style="{StaticResource IconBtn}"
|
||||||
|
ToolTip="Pulisci log">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="13"/>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
<ListBox ItemsSource="{Binding LogEntries}"
|
||||||
|
Background="Transparent" BorderThickness="0"
|
||||||
|
ItemContainerStyle="{StaticResource LogItem}"
|
||||||
|
VirtualizingStackPanel.IsVirtualizing="True"
|
||||||
|
ScrollViewer.HorizontalScrollBarVisibility="Disabled">
|
||||||
|
<ListBox.ItemTemplate>
|
||||||
|
<DataTemplate>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<!-- Badge livello -->
|
||||||
|
<Border Grid.Column="0" CornerRadius="4" Padding="5,1" Margin="0,0,8,0"
|
||||||
|
VerticalAlignment="Top" MinWidth="38">
|
||||||
|
<Border.Style>
|
||||||
|
<Style TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="#1A2A4A"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding Level}" Value="ERR">
|
||||||
|
<Setter Property="Background" Value="#3E1A1A"/>
|
||||||
|
</DataTrigger>
|
||||||
|
<DataTrigger Binding="{Binding Level}" Value="WARN">
|
||||||
|
<Setter Property="Background" Value="#2E200A"/>
|
||||||
|
</DataTrigger>
|
||||||
|
<DataTrigger Binding="{Binding Level}" Value="OK">
|
||||||
|
<Setter Property="Background" Value="#1A3A1A"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</Border.Style>
|
||||||
|
<TextBlock Text="{Binding Level}" FontFamily="Consolas"
|
||||||
|
FontSize="9" FontWeight="Bold" TextAlignment="Center">
|
||||||
|
<TextBlock.Style>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#42A5F5"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding Level}" Value="ERR">
|
||||||
|
<Setter Property="Foreground" Value="#EF5350"/>
|
||||||
|
</DataTrigger>
|
||||||
|
<DataTrigger Binding="{Binding Level}" Value="WARN">
|
||||||
|
<Setter Property="Foreground" Value="#FFA726"/>
|
||||||
|
</DataTrigger>
|
||||||
|
<DataTrigger Binding="{Binding Level}" Value="OK">
|
||||||
|
<Setter Property="Foreground" Value="#4CAF50"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</TextBlock.Style>
|
||||||
|
</TextBlock>
|
||||||
|
</Border>
|
||||||
|
<!-- Messaggio -->
|
||||||
|
<TextBlock Grid.Column="1" Text="{Binding Message}"
|
||||||
|
FontFamily="Consolas" FontSize="11"
|
||||||
|
TextWrapping="Wrap" VerticalAlignment="Center">
|
||||||
|
<TextBlock.Style>
|
||||||
|
<Style TargetType="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#B0BEC5"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<DataTrigger Binding="{Binding Level}" Value="ERR">
|
||||||
|
<Setter Property="Foreground" Value="#EF9A9A"/>
|
||||||
|
</DataTrigger>
|
||||||
|
<DataTrigger Binding="{Binding Level}" Value="WARN">
|
||||||
|
<Setter Property="Foreground" Value="#FFCC80"/>
|
||||||
|
</DataTrigger>
|
||||||
|
<DataTrigger Binding="{Binding Level}" Value="OK">
|
||||||
|
<Setter Property="Foreground" Value="#A5D6A7"/>
|
||||||
|
</DataTrigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</TextBlock.Style>
|
||||||
|
</TextBlock>
|
||||||
|
<!-- Timestamp -->
|
||||||
|
<TextBlock Grid.Column="2" Text="{Binding TimeLabel}"
|
||||||
|
FontFamily="Consolas" FontSize="9"
|
||||||
|
Foreground="#37474F" VerticalAlignment="Center"
|
||||||
|
Margin="8,0,0,0"/>
|
||||||
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</ListBox.ItemTemplate>
|
||||||
|
</ListBox>
|
||||||
|
</DockPanel>
|
||||||
|
</Border>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using Dione.ViewModels;
|
||||||
|
|
||||||
|
namespace Dione.Views
|
||||||
|
{
|
||||||
|
public partial class LiveGenerationView : UserControl
|
||||||
|
{
|
||||||
|
public LiveGenerationView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
DataContextChanged += OnDataContextChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LiveGenerationViewModel _vm;
|
||||||
|
|
||||||
|
private void OnDataContextChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (_vm != null)
|
||||||
|
_vm.PropertyChanged -= OnVmPropertyChanged;
|
||||||
|
|
||||||
|
_vm = e.NewValue as LiveGenerationViewModel;
|
||||||
|
|
||||||
|
if (_vm != null)
|
||||||
|
_vm.PropertyChanged += OnVmPropertyChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnVmPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.PropertyName == nameof(LiveGenerationViewModel.StreamingText) && _vm.IsStreaming)
|
||||||
|
{
|
||||||
|
var sv = FindName("StreamScrollViewer") as ScrollViewer;
|
||||||
|
sv?.ScrollToBottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<UserControl x:Class="Dione.Views.PlaceholderView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
<Grid VerticalAlignment="Center" HorizontalAlignment="Center">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="48" HorizontalAlignment="Center" Foreground="#546E7A"/>
|
||||||
|
<TextBlock x:Name="TitleText" Text="Coming Soon" FontSize="24" FontWeight="SemiBold"
|
||||||
|
Foreground="#90CAF9" HorizontalAlignment="Center" Margin="0,12,0,0"/>
|
||||||
|
<TextBlock Text="This module is under development." FontSize="14"
|
||||||
|
Foreground="#546E7A" HorizontalAlignment="Center" Margin="0,8,0,0"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Dione.Views
|
||||||
|
{
|
||||||
|
public partial class PlaceholderView : UserControl
|
||||||
|
{
|
||||||
|
public PlaceholderView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlaceholderView(string title) : this()
|
||||||
|
{
|
||||||
|
TitleText.Text = title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
<UserControl x:Class="Dione.Views.SettingsView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:converters="clr-namespace:Dione.Converters"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<UserControl.Resources>
|
||||||
|
<converters:InverseBoolConverter x:Key="InverseBoolConverter"/>
|
||||||
|
|
||||||
|
<Style x:Key="SectionCard" TargetType="Border">
|
||||||
|
<Setter Property="Background" Value="#1A1A28"/>
|
||||||
|
<Setter Property="CornerRadius" Value="10"/>
|
||||||
|
<Setter Property="Padding" Value="20,16"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,0,14"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="Label" TargetType="TextBlock">
|
||||||
|
<Setter Property="Foreground" Value="#90CAF9"/>
|
||||||
|
<Setter Property="FontSize" Value="11"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,0,4"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="Field" TargetType="TextBox">
|
||||||
|
<Setter Property="Background" Value="#0D0D18"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#2A2A3E"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="Padding" Value="8,6"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,0,10"/>
|
||||||
|
<Setter Property="CaretBrush" Value="White"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="Combo" TargetType="ComboBox">
|
||||||
|
<Setter Property="Background" Value="#0D0D18"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#2A2A3E"/>
|
||||||
|
<Setter Property="Padding" Value="8,6"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,0,10"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="Btn" TargetType="Button">
|
||||||
|
<Setter Property="Background" Value="#2962FF"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
<Setter Property="Padding" Value="16,7"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,8,0"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="FontSize" Value="11"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="DangerBtn" BasedOn="{StaticResource Btn}" TargetType="Button">
|
||||||
|
<Setter Property="Background" Value="#C62828"/>
|
||||||
|
</Style>
|
||||||
|
<Style x:Key="GreenBtn" BasedOn="{StaticResource Btn}" TargetType="Button">
|
||||||
|
<Setter Property="Background" Value="#2E7D32"/>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
|
<ScrollViewer VerticalScrollBarVisibility="Auto" Padding="24,16">
|
||||||
|
<StackPanel MaxWidth="900">
|
||||||
|
|
||||||
|
<!-- Header -->
|
||||||
|
<DockPanel Margin="0,0,0,20">
|
||||||
|
<TextBlock Text="Impostazioni" FontSize="22" FontWeight="Bold" Foreground="White" VerticalAlignment="Center"/>
|
||||||
|
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal">
|
||||||
|
<Button Style="{StaticResource Btn}" Command="{Binding SaveCommand}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="13" VerticalAlignment="Center" Margin="0,0,6,0"/>
|
||||||
|
<TextBlock Text="Salva" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<!-- Status -->
|
||||||
|
<TextBlock Text="{Binding StatusMessage}" Foreground="#4CAF50" FontSize="11" Margin="0,0,0,16"/>
|
||||||
|
|
||||||
|
<!-- ═══ API CONFIGURATION ═══ -->
|
||||||
|
<Border Style="{StaticResource SectionCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="CONFIGURAZIONE API" FontSize="11" FontWeight="Bold" Foreground="#546E7A" Margin="0,0,0,14"/>
|
||||||
|
|
||||||
|
<TextBlock Text="Preimpostazione" Style="{StaticResource Label}"/>
|
||||||
|
<ComboBox ItemsSource="{Binding EndpointPresets}" SelectedItem="{Binding SelectedEndpointPreset}" Style="{StaticResource Combo}"/>
|
||||||
|
|
||||||
|
<TextBlock Text="API Endpoint" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding ApiEndpoint, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="16"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0">
|
||||||
|
<TextBlock Text="Modello" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding ModelName, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2">
|
||||||
|
<TextBlock Text="API Key (vuoto per locale)" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding ApiKey, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="16"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0">
|
||||||
|
<TextBlock Text="Temperature" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding Temperature, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2">
|
||||||
|
<TextBlock Text="Max Tokens" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding MaxTokens, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- ═══ PROMPT EDITOR ═══ -->
|
||||||
|
<Border Style="{StaticResource SectionCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<DockPanel Margin="0,0,0,14">
|
||||||
|
<TextBlock Text="PROMPT" FontSize="11" FontWeight="Bold" Foreground="#546E7A" VerticalAlignment="Center"/>
|
||||||
|
<Button DockPanel.Dock="Right" Style="{StaticResource GreenBtn}" Command="{Binding InsertDefaultBettingPromptCommand}">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="12" VerticalAlignment="Center" Margin="0,0,6,0"/>
|
||||||
|
<TextBlock Text="Preset Blockchain Betting" VerticalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Button>
|
||||||
|
</DockPanel>
|
||||||
|
|
||||||
|
<TextBlock Text="System Prompt" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding SystemPrompt, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Style="{StaticResource Field}" Height="160"
|
||||||
|
AcceptsReturn="True" TextWrapping="Wrap"
|
||||||
|
VerticalScrollBarVisibility="Auto" VerticalContentAlignment="Top"/>
|
||||||
|
|
||||||
|
<TextBlock Text="User Prompt (trigger batch)" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding UserPrompt, UpdateSourceTrigger=PropertyChanged}"
|
||||||
|
Style="{StaticResource Field}" Height="60"
|
||||||
|
AcceptsReturn="True" TextWrapping="Wrap"
|
||||||
|
VerticalScrollBarVisibility="Auto" VerticalContentAlignment="Top"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- ═══ OUTPUT ═══ -->
|
||||||
|
<Border Style="{StaticResource SectionCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="OUTPUT" FontSize="11" FontWeight="Bold" Foreground="#546E7A" Margin="0,0,0,14"/>
|
||||||
|
|
||||||
|
<TextBlock Text="Cartella di Output" Style="{StaticResource Label}"/>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="8"/>
|
||||||
|
<ColumnDefinition Width="Auto"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBox Grid.Column="0" Text="{Binding OutputDirectory, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
<Button Grid.Column="2" Style="{StaticResource Btn}" Command="{Binding BrowseOutputDirectoryCommand}" Margin="0,0,0,10">
|
||||||
|
<TextBlock Text="" FontFamily="Segoe MDL2 Assets" FontSize="14"/>
|
||||||
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="16"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0">
|
||||||
|
<TextBlock Text="Prefisso File (es. batch)" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding OutputFilePrefix, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2">
|
||||||
|
<TextBlock Text="Dimensione Massima File (MB)" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding MaxFileSizeMb, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<TextBlock Foreground="#546E7A" FontSize="10"
|
||||||
|
Text="I file vengono ruotati automaticamente quando raggiungono la dimensione massima (batch_001.jsonl, batch_002.jsonl, ...)"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- ═══ TIMEOUT ═══ -->
|
||||||
|
<Border Style="{StaticResource SectionCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="TIMEOUT" FontSize="11" FontWeight="Bold" Foreground="#546E7A" Margin="0,0,0,14"/>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="16"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0">
|
||||||
|
<TextBlock Text="Timeout Base (secondi)" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding ApiTimeoutSeconds, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2">
|
||||||
|
<TextBlock Text="Secondi Aggiuntivi per 100 Token" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding TimeoutPerTokenRatio, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- ═══ COSTI ═══ -->
|
||||||
|
<Border Style="{StaticResource SectionCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="COSTI" FontSize="11" FontWeight="Bold" Foreground="#546E7A" Margin="0,0,0,14"/>
|
||||||
|
<Grid>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="16"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="16"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0">
|
||||||
|
<TextBlock Text="Elettricita (EUR/kWh)" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding ElectricityCostPerKwh, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2">
|
||||||
|
<TextBlock Text="Potenza Sistema (Watt)" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding SystemPowerWatt, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="4">
|
||||||
|
<TextBlock Text="Tipo Tariffazione API" Style="{StaticResource Label}"/>
|
||||||
|
<ComboBox ItemsSource="{Binding ApiCostTypes}" SelectedItem="{Binding ApiCostType}" Style="{StaticResource Combo}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- ═══ VERIFICA QUALITA ═══ -->
|
||||||
|
<Border Style="{StaticResource SectionCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="VERIFICA QUALITA AI (opzionale)" FontSize="11" FontWeight="Bold" Foreground="#4CAF50" Margin="0,0,0,14"/>
|
||||||
|
<CheckBox Content="Abilita verifica qualita AI" IsChecked="{Binding EnableQualityVerification}" Foreground="#90CAF9" FontSize="12" Margin="0,0,0,10"/>
|
||||||
|
<CheckBox Content="Usa lo stesso modello/endpoint della generazione" IsChecked="{Binding UseSameModelForVerification}" Foreground="#78909C" FontSize="11" Margin="0,0,0,12"/>
|
||||||
|
|
||||||
|
<Grid IsEnabled="{Binding UseSameModelForVerification, Converter={StaticResource InverseBoolConverter}}">
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="16"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<StackPanel Grid.Column="0">
|
||||||
|
<TextBlock Text="Endpoint Verifica" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding VerificationApiEndpoint, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
<StackPanel Grid.Column="2">
|
||||||
|
<TextBlock Text="Modello Verifica" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding VerificationModelName, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Grid>
|
||||||
|
<TextBlock Text="API Key Verifica (se diversa)" Style="{StaticResource Label}"
|
||||||
|
IsEnabled="{Binding UseSameModelForVerification, Converter={StaticResource InverseBoolConverter}}"/>
|
||||||
|
<TextBox Text="{Binding VerificationApiKey, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}"
|
||||||
|
IsEnabled="{Binding UseSameModelForVerification, Converter={StaticResource InverseBoolConverter}}"/>
|
||||||
|
|
||||||
|
<TextBlock Text="Valore Base per Record Alta Qualita (EUR)" Style="{StaticResource Label}"/>
|
||||||
|
<TextBox Text="{Binding RevenuePerHighQualityRecord, UpdateSourceTrigger=PropertyChanged}" Style="{StaticResource Field}" Width="200" HorizontalAlignment="Left"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- ═══ RESET ═══ -->
|
||||||
|
<Border Style="{StaticResource SectionCard}">
|
||||||
|
<StackPanel>
|
||||||
|
<TextBlock Text="RESET" FontSize="11" FontWeight="Bold" Foreground="#FF5722" Margin="0,0,0,14"/>
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<Button Style="{StaticResource DangerBtn}" Command="{Binding ResetTelemetryCommand}">
|
||||||
|
<TextBlock Text="Elimina Telemetria"/>
|
||||||
|
</Button>
|
||||||
|
<Button Style="{StaticResource DangerBtn}" Command="{Binding ResetDatabaseCommand}">
|
||||||
|
<TextBlock Text="Reset Completo DB"/>
|
||||||
|
</Button>
|
||||||
|
</StackPanel>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
</StackPanel>
|
||||||
|
</ScrollViewer>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Dione.Views
|
||||||
|
{
|
||||||
|
public partial class SettingsView : UserControl
|
||||||
|
{
|
||||||
|
public SettingsView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
<UserControl x:Class="Dione.Views.TelemetryHistoryView"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
Background="Transparent">
|
||||||
|
|
||||||
|
<UserControl.Resources>
|
||||||
|
<Style x:Key="ActionBtn" TargetType="Button">
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
<Setter Property="Padding" Value="14,6"/>
|
||||||
|
<Setter Property="Margin" Value="0,0,8,0"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Setter Property="Cursor" Value="Hand"/>
|
||||||
|
<Setter Property="FontSize" Value="12"/>
|
||||||
|
<Setter Property="Template">
|
||||||
|
<Setter.Value>
|
||||||
|
<ControlTemplate TargetType="Button">
|
||||||
|
<Border Background="{TemplateBinding Background}" CornerRadius="6" Padding="{TemplateBinding Padding}">
|
||||||
|
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
</ControlTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</Style>
|
||||||
|
|
||||||
|
<!-- DataGrid styles -->
|
||||||
|
<Style TargetType="DataGrid" x:Key="DarkGrid">
|
||||||
|
<Setter Property="Background" Value="#0D0D14"/>
|
||||||
|
<Setter Property="Foreground" Value="#E0E0E0"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#2A2A3E"/>
|
||||||
|
<Setter Property="BorderThickness" Value="1"/>
|
||||||
|
<Setter Property="RowBackground" Value="#12121A"/>
|
||||||
|
<Setter Property="AlternatingRowBackground" Value="#16161E"/>
|
||||||
|
<Setter Property="GridLinesVisibility" Value="Horizontal"/>
|
||||||
|
<Setter Property="HorizontalGridLinesBrush" Value="#1E1E2E"/>
|
||||||
|
<Setter Property="HeadersVisibility" Value="Column"/>
|
||||||
|
<Setter Property="CanUserAddRows" Value="False"/>
|
||||||
|
<Setter Property="CanUserDeleteRows" Value="False"/>
|
||||||
|
<Setter Property="IsReadOnly" Value="True"/>
|
||||||
|
<Setter Property="AutoGenerateColumns" Value="False"/>
|
||||||
|
<Setter Property="SelectionMode" Value="Single"/>
|
||||||
|
<Setter Property="FontSize" Value="11"/>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="DataGridColumnHeader">
|
||||||
|
<Setter Property="Background" Value="#1A1A26"/>
|
||||||
|
<Setter Property="Foreground" Value="#90CAF9"/>
|
||||||
|
<Setter Property="Padding" Value="8,6"/>
|
||||||
|
<Setter Property="BorderBrush" Value="#2A2A3E"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0,0,1,1"/>
|
||||||
|
<Setter Property="FontSize" Value="11"/>
|
||||||
|
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||||
|
</Style>
|
||||||
|
<Style TargetType="DataGridCell">
|
||||||
|
<Setter Property="Padding" Value="6,4"/>
|
||||||
|
<Setter Property="BorderThickness" Value="0"/>
|
||||||
|
<Style.Triggers>
|
||||||
|
<Trigger Property="IsSelected" Value="True">
|
||||||
|
<Setter Property="Background" Value="#2962FF"/>
|
||||||
|
<Setter Property="Foreground" Value="White"/>
|
||||||
|
</Trigger>
|
||||||
|
</Style.Triggers>
|
||||||
|
</Style>
|
||||||
|
</UserControl.Resources>
|
||||||
|
|
||||||
|
<Grid Margin="24,16">
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
|
||||||
|
<TextBlock Grid.Row="0" Text="Telemetry History" FontSize="26" FontWeight="Bold" Foreground="White" Margin="0,0,0,16"/>
|
||||||
|
|
||||||
|
<!-- Filters -->
|
||||||
|
<Border Grid.Row="1" Background="#1A1A26" CornerRadius="10" Padding="16" Margin="0,0,0,12">
|
||||||
|
<StackPanel Orientation="Horizontal">
|
||||||
|
<TextBlock Text="Batch ID:" Foreground="#90CAF9" FontSize="12" VerticalAlignment="Center" Margin="0,0,6,0"/>
|
||||||
|
<TextBox Text="{Binding FilterBatchId, UpdateSourceTrigger=PropertyChanged}" Width="120"
|
||||||
|
Background="#1E1E2E" Foreground="White" BorderBrush="#2A2A3E" Padding="6,4" CaretBrush="White" VerticalContentAlignment="Center"/>
|
||||||
|
|
||||||
|
<TextBlock Text=" Model:" Foreground="#90CAF9" FontSize="12" VerticalAlignment="Center" Margin="16,0,6,0"/>
|
||||||
|
<TextBox Text="{Binding FilterModel, UpdateSourceTrigger=PropertyChanged}" Width="120"
|
||||||
|
Background="#1E1E2E" Foreground="White" BorderBrush="#2A2A3E" Padding="6,4" CaretBrush="White" VerticalContentAlignment="Center"/>
|
||||||
|
|
||||||
|
<TextBlock Text=" Status:" Foreground="#90CAF9" FontSize="12" VerticalAlignment="Center" Margin="16,0,6,0"/>
|
||||||
|
<ComboBox ItemsSource="{Binding SuccessOptions}" SelectedItem="{Binding SelectedSuccessOption}" Width="90"
|
||||||
|
Background="#1E1E2E" Foreground="White" BorderBrush="#2A2A3E" Padding="6,4"/>
|
||||||
|
|
||||||
|
<Button Content="Apply" Background="#2962FF" Command="{Binding ApplyFilterCommand}" Style="{StaticResource ActionBtn}" Margin="16,0,0,0"/>
|
||||||
|
<Button Content="Clear" Background="#2A2A3E" Command="{Binding ClearFilterCommand}" Style="{StaticResource ActionBtn}"/>
|
||||||
|
<Button Content="Refresh" Background="#2A2A3E" Command="{Binding RefreshCommand}" Style="{StaticResource ActionBtn}"/>
|
||||||
|
</StackPanel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<!-- Data Grid -->
|
||||||
|
<DataGrid Grid.Row="2" ItemsSource="{Binding Logs}" Style="{StaticResource DarkGrid}">
|
||||||
|
<DataGrid.Columns>
|
||||||
|
<DataGridTextColumn Header="ID" Binding="{Binding Id}" Width="50"/>
|
||||||
|
<DataGridTextColumn Header="Timestamp" Binding="{Binding Timestamp, StringFormat=yyyy-MM-dd HH:mm:ss}" Width="140"/>
|
||||||
|
<DataGridTextColumn Header="Batch" Binding="{Binding BatchId}" Width="100"/>
|
||||||
|
<DataGridTextColumn Header="Model" Binding="{Binding ModelUsed}" Width="120"/>
|
||||||
|
<DataGridTextColumn Header="Prompt Tk" Binding="{Binding TokensPrompt}" Width="70"/>
|
||||||
|
<DataGridTextColumn Header="Compl Tk" Binding="{Binding TokensCompletion}" Width="70"/>
|
||||||
|
<DataGridTextColumn Header="Time (ms)" Binding="{Binding ExecutionTimeMs}" Width="75"/>
|
||||||
|
<DataGridCheckBoxColumn Header="OK" Binding="{Binding IsSuccess, Mode=OneWay}" Width="40"/>
|
||||||
|
<DataGridTextColumn Header="Error" Binding="{Binding ErrorMessage}" Width="200"/>
|
||||||
|
<DataGridTextColumn Header="Preview" Binding="{Binding OutputPreview}" Width="300"/>
|
||||||
|
</DataGrid.Columns>
|
||||||
|
</DataGrid>
|
||||||
|
|
||||||
|
<!-- Status bar -->
|
||||||
|
<DockPanel Grid.Row="3" Margin="0,8,0,0">
|
||||||
|
<TextBlock Foreground="#78909C" FontSize="12" VerticalAlignment="Center">
|
||||||
|
<TextBlock.Text>
|
||||||
|
<MultiBinding StringFormat="Showing {0} of {1} records">
|
||||||
|
<Binding Path="FilteredCount"/>
|
||||||
|
<Binding Path="TotalCount"/>
|
||||||
|
</MultiBinding>
|
||||||
|
</TextBlock.Text>
|
||||||
|
</TextBlock>
|
||||||
|
<TextBlock Text="{Binding StatusMessage}" Foreground="#4CAF50" FontSize="12" HorizontalAlignment="Right" DockPanel.Dock="Right"/>
|
||||||
|
</DockPanel>
|
||||||
|
</Grid>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Windows.Controls;
|
||||||
|
|
||||||
|
namespace Dione.Views
|
||||||
|
{
|
||||||
|
public partial class TelemetryHistoryView : UserControl
|
||||||
|
{
|
||||||
|
public TelemetryHistoryView()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user