| | | 1 | | using System.Text.Json; |
| | | 2 | | |
| | | 3 | | using Microsoft.Extensions.AI; |
| | | 4 | | |
| | | 5 | | namespace NexusLabs.Needlr.AgentFramework.Diagnostics; |
| | | 6 | | |
| | | 7 | | /// <summary> |
| | | 8 | | /// Computes character counts of chat messages, responses, tool arguments, and tool |
| | | 9 | | /// results for diagnostics capture. Character counts are a direct programmatic |
| | | 10 | | /// measure of payload size — distinct from the LLM-provider-reported |
| | | 11 | | /// <see cref="TokenUsage"/> — and are useful for detecting chat-reducer drift, |
| | | 12 | | /// prompt bloat, and unexpectedly large tool responses during evaluation. |
| | | 13 | | /// </summary> |
| | | 14 | | /// <remarks> |
| | | 15 | | /// All methods are null-safe and exception-tolerant: any failure to measure |
| | | 16 | | /// returns <c>0</c> rather than throwing. This matches the diagnostics alpha |
| | | 17 | | /// invariant that capture must never destabilize the live path. |
| | | 18 | | /// </remarks> |
| | | 19 | | public static class DiagnosticsCharCounter |
| | | 20 | | { |
| | 1 | 21 | | private static readonly JsonSerializerOptions _options = new() |
| | 1 | 22 | | { |
| | 1 | 23 | | WriteIndented = false, |
| | 1 | 24 | | }; |
| | | 25 | | |
| | | 26 | | /// <summary> |
| | | 27 | | /// Sums the character count of all text contents across the supplied chat |
| | | 28 | | /// messages. Non-text content (images, function calls) is ignored. |
| | | 29 | | /// </summary> |
| | | 30 | | public static long ChatMessagesLength(IEnumerable<ChatMessage>? messages) |
| | | 31 | | { |
| | 608 | 32 | | if (messages is null) |
| | | 33 | | { |
| | 1 | 34 | | return 0; |
| | | 35 | | } |
| | | 36 | | |
| | 607 | 37 | | long total = 0; |
| | 3266 | 38 | | foreach (var message in messages) |
| | | 39 | | { |
| | 1026 | 40 | | total += SumTextContents(message.Contents); |
| | | 41 | | } |
| | 607 | 42 | | return total; |
| | | 43 | | } |
| | | 44 | | |
| | | 45 | | /// <summary> |
| | | 46 | | /// Sums the character count of all text contents across every message in the |
| | | 47 | | /// supplied <see cref="ChatResponse"/>. Returns <c>0</c> when the response |
| | | 48 | | /// is <see langword="null"/> or contains no text content. |
| | | 49 | | /// </summary> |
| | | 50 | | public static long ChatResponseLength(ChatResponse? response) |
| | | 51 | | { |
| | 298 | 52 | | if (response is null) |
| | | 53 | | { |
| | 1 | 54 | | return 0; |
| | | 55 | | } |
| | | 56 | | |
| | 297 | 57 | | return ChatMessagesLength(response.Messages); |
| | | 58 | | } |
| | | 59 | | |
| | | 60 | | /// <summary> |
| | | 61 | | /// Returns the character length of the JSON-serialized representation of |
| | | 62 | | /// the supplied value. Null-safe and exception-tolerant: returns <c>0</c> |
| | | 63 | | /// when the value is <see langword="null"/> or cannot be serialized. |
| | | 64 | | /// </summary> |
| | | 65 | | public static long JsonLength(object? value) |
| | | 66 | | { |
| | 325 | 67 | | if (value is null) |
| | | 68 | | { |
| | 77 | 69 | | return 0; |
| | | 70 | | } |
| | | 71 | | |
| | | 72 | | try |
| | | 73 | | { |
| | 248 | 74 | | var json = JsonSerializer.Serialize(value, _options); |
| | 247 | 75 | | return json.Length; |
| | | 76 | | } |
| | 1 | 77 | | catch |
| | | 78 | | { |
| | 1 | 79 | | return 0; |
| | | 80 | | } |
| | 248 | 81 | | } |
| | | 82 | | |
| | | 83 | | private static long SumTextContents(IList<AIContent>? contents) |
| | | 84 | | { |
| | 1026 | 85 | | if (contents is null || contents.Count == 0) |
| | | 86 | | { |
| | 0 | 87 | | return 0; |
| | | 88 | | } |
| | | 89 | | |
| | 1026 | 90 | | long total = 0; |
| | 4132 | 91 | | foreach (var content in contents) |
| | | 92 | | { |
| | 1040 | 93 | | if (content is TextContent text && text.Text is not null) |
| | | 94 | | { |
| | 707 | 95 | | total += text.Text.Length; |
| | | 96 | | } |
| | 333 | 97 | | else if (content is TextReasoningContent reasoning && reasoning.Text is not null) |
| | | 98 | | { |
| | 3 | 99 | | total += reasoning.Text.Length; |
| | | 100 | | } |
| | | 101 | | } |
| | 1026 | 102 | | return total; |
| | | 103 | | } |
| | | 104 | | } |