From e1d5a480b80dd54c9b4a29e64b79ada81af620fb Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Sun, 26 Apr 2026 18:43:13 -0500 Subject: [PATCH 1/2] fix: suppress CS0618 on AddZipkinExporter to unblock CI OpenTelemetry.Exporter.Zipkin 1.15.3 marks AddZipkinExporter as [Obsolete], which the compiler promotes to CS0618 error under TreatWarningsAsErrors. The project already dropped the Zipkin package version NU1902 in PR #497 but the call site pragma was never committed. Wrap the single call site with #pragma warning disable/restore CS0618 so CI compiles cleanly while backward-compat "zipkin" config support is maintained until a full OTLP migration is done. Failing file: src/JD.AI.Telemetry/Extensions/TelemetryServiceExtensions.cs Error: CS0618 @ line 101 (GitHub main) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/JD.AI.Telemetry/Extensions/TelemetryServiceExtensions.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/JD.AI.Telemetry/Extensions/TelemetryServiceExtensions.cs b/src/JD.AI.Telemetry/Extensions/TelemetryServiceExtensions.cs index 2b5652b5..2262f3f2 100644 --- a/src/JD.AI.Telemetry/Extensions/TelemetryServiceExtensions.cs +++ b/src/JD.AI.Telemetry/Extensions/TelemetryServiceExtensions.cs @@ -98,11 +98,13 @@ private static void ConfigureTraceExporter(TracerProviderBuilder tracing, Teleme break; case "zipkin": +#pragma warning disable CS0618 // OpenTelemetry deprecated Zipkin exporter; keep existing config support until migrated. tracing.AddZipkinExporter(o => { if (TryParseEndpoint(endpoint, out var uri)) o.Endpoint = uri; }); +#pragma warning restore CS0618 break; default: // "console" From 47766f31b78333f1813581b6742314c68ca01c22 Mon Sep 17 00:00:00 2001 From: JerrettDavis Date: Sun, 26 Apr 2026 18:46:10 -0500 Subject: [PATCH 2/2] fix: add --ollama-generate and --ollama-validate handlers to training Program.cs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The train-model workflow fails at "Train ML.NET model" (step 9) with a FileNotFoundException on ollama_training_data.jsonl. Root cause: Program.cs had no handler for --ollama-generate or --ollama-validate. The "Synthesize training examples" step passes `--ollama-generate 150 --output src/.../intent_classifier.zip` but Program.cs silently ignored the unknown flag and fell through to the built-in seed-corpus path, writing to /tmp/intent_training_data.csv instead of the expected src/JD.AI.Workflows/Models/ollama_training_data.jsonl. When "Train ML.NET model" then ran `--data ollama_training_data.jsonl`, the file didn't exist → FileNotFoundException → exit non-zero. Fix: - Add --ollama-generate handler: calls AiTrainingDataSynthesizer .GenerateAsync(count), writes results to ollama_training_data.jsonl in the same directory as --output, then exits 0. - Add --ollama-validate handler: reads --data file, calls ValidateAsync, writes disagreement count to validate_summary.txt, then exits 0. - Both handlers read OLLAMA_HOST env var (set by CI) for the Ollama URL. - Update AiTrainingDataSynthesizer constructor to accept ollamaHost param and forward it to OllamaClient. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../AiTrainingDataSynthesizer.cs | 4 +- tools/JD.AI.Workflows.Training/Program.cs | 74 +++++++++++++++++++ 2 files changed, 76 insertions(+), 2 deletions(-) diff --git a/tools/JD.AI.Workflows.Training/AiTrainingDataSynthesizer.cs b/tools/JD.AI.Workflows.Training/AiTrainingDataSynthesizer.cs index b5a72deb..a81c8d73 100644 --- a/tools/JD.AI.Workflows.Training/AiTrainingDataSynthesizer.cs +++ b/tools/JD.AI.Workflows.Training/AiTrainingDataSynthesizer.cs @@ -12,9 +12,9 @@ public sealed class AiTrainingDataSynthesizer : IDisposable private readonly string _model; private int _generated; - public AiTrainingDataSynthesizer(string model = "qwen3.5:9b") + public AiTrainingDataSynthesizer(string model = "qwen3.5:9b", string ollamaHost = "http://localhost:11434") { - _ollama = new OllamaClient(model); + _ollama = new OllamaClient(model, ollamaHost); _model = model; } diff --git a/tools/JD.AI.Workflows.Training/Program.cs b/tools/JD.AI.Workflows.Training/Program.cs index da9975c0..a1362aba 100644 --- a/tools/JD.AI.Workflows.Training/Program.cs +++ b/tools/JD.AI.Workflows.Training/Program.cs @@ -44,12 +44,86 @@ public static async Task Main(string[] args) ? args[outputArgIdx + 1] : "../../src/JD.AI.Workflows/Models/intent_classifier.zip"; + var ollamaGenerateArgIdx = Array.IndexOf(args, "--ollama-generate"); + int? ollamaGenerateCount = ollamaGenerateArgIdx >= 0 && ollamaGenerateArgIdx + 1 < args.Length + ? int.Parse(args[ollamaGenerateArgIdx + 1], System.Globalization.CultureInfo.InvariantCulture) + : null; + var ollamaValidate = args.Contains("--ollama-validate"); + if (benchmark) { RunBenchmark(); return 0; } + // ── Ollama-synthesized training data generation ────────────────── + if (ollamaGenerateCount.HasValue) + { + var ollamaHost = Environment.GetEnvironmentVariable("OLLAMA_HOST") ?? "http://localhost:11434"; + var dir = Path.GetDirectoryName(Path.GetFullPath(outputPath)); + var jsonlPath = dir is not null + ? Path.Combine(dir, "ollama_training_data.jsonl") + : "ollama_training_data.jsonl"; + + if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) + Directory.CreateDirectory(dir); + + List generated = []; + using (var synthesizer = new AiTrainingDataSynthesizer(ollamaHost: ollamaHost)) + { + await AnsiConsole.Progress() + .StartAsync(async ctx => + { + var task = ctx.AddTask("[green]Synthesizing training examples[/]", + maxValue: ollamaGenerateCount.Value); + generated = await synthesizer.GenerateAsync( + ollamaGenerateCount.Value, + (done, _) => task.Value = done); + }); + } + + TrainingDataGenerator.WriteCsv(generated, jsonlPath); + AnsiConsole.MarkupLine($"[green]Synthesized {generated.Count} examples → {jsonlPath}[/]"); + return 0; + } + + // ── Ollama-based validation ────────────────────────────────────── + if (ollamaValidate) + { + if (dataPath is null) + { + AnsiConsole.MarkupLine("[red]--ollama-validate requires --data [/]"); + return 1; + } + + if (!File.Exists(dataPath)) + { + AnsiConsole.MarkupLine($"[red]Data file not found: {dataPath}[/]"); + return 1; + } + + var ollamaHost = Environment.GetEnvironmentVariable("OLLAMA_HOST") ?? "http://localhost:11434"; + var prompts = TrainingDataGenerator.ReadCsv(dataPath); + + List discrepancies = []; + using (var synthesizer = new AiTrainingDataSynthesizer(ollamaHost: ollamaHost)) + { + await AnsiConsole.Progress() + .StartAsync(async ctx => + { + var task = ctx.AddTask("[green]Validating against Ollama[/]", + maxValue: prompts.Count); + discrepancies = await synthesizer.ValidateAsync( + prompts, + (done, _) => task.Value = done); + }); + } + + await File.WriteAllTextAsync("validate_summary.txt", discrepancies.Count.ToString()); + AnsiConsole.MarkupLine($"[cyan]Validation complete — {discrepancies.Count}/{prompts.Count} disagreements[/]"); + return 0; + } + if (generateOnly) { dataPath ??= Path.Combine(Path.GetTempPath(), "intent_training_data.csv");