| | | 1 | | namespace NexusLabs.Needlr.AgentFramework.Diagnostics; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Diagnostics for a single tool/function invocation within an agent run. |
| | | 5 | | /// </summary> |
| | | 6 | | /// <remarks> |
| | | 7 | | /// <para> |
| | | 8 | | /// Tool calls are captured by the diagnostics function-calling middleware and ordered |
| | | 9 | | /// by their <paramref name="Sequence"/> number, which is reserved before the call begins. |
| | | 10 | | /// This ensures parallel tool calls are ordered by invocation time, not completion time. |
| | | 11 | | /// </para> |
| | | 12 | | /// <para> |
| | | 13 | | /// Custom metrics (e.g., cache hit/miss, provider name, byte counts) can be attached |
| | | 14 | | /// via <see cref="IToolMetricsAccessor.AttachMetric"/> during tool execution. |
| | | 15 | | /// </para> |
| | | 16 | | /// </remarks> |
| | | 17 | | /// <example> |
| | | 18 | | /// <code> |
| | | 19 | | /// foreach (var tool in diagnostics.ToolCalls) |
| | | 20 | | /// { |
| | | 21 | | /// Console.WriteLine($"{tool.ToolName}: {tool.Duration.TotalMilliseconds}ms " + |
| | | 22 | | /// $"({(tool.Succeeded ? "ok" : tool.ErrorMessage)})"); |
| | | 23 | | /// if (tool.CustomMetrics is { Count: > 0 }) |
| | | 24 | | /// foreach (var (k, v) in tool.CustomMetrics) |
| | | 25 | | /// Console.WriteLine($" {k} = {v}"); |
| | | 26 | | /// } |
| | | 27 | | /// </code> |
| | | 28 | | /// </example> |
| | | 29 | | /// <param name="Sequence">Zero-based invocation order within the agent run. Reserved before async execution begins.</pa |
| | | 30 | | /// <param name="ToolName">The name of the tool/function that was called (e.g., <c>"ReadFile"</c>, <c>"WebSearch"</c>).< |
| | | 31 | | /// <param name="Duration">Wall-clock time for the tool execution.</param> |
| | | 32 | | /// <param name="Succeeded">Whether the tool returned a result without throwing.</param> |
| | | 33 | | /// <param name="ErrorMessage">The exception message if the tool threw; <see langword="null"/> on success.</param> |
| | | 34 | | /// <param name="StartedAt">UTC timestamp when the tool invocation began.</param> |
| | | 35 | | /// <param name="CompletedAt">UTC timestamp when the tool invocation finished.</param> |
| | | 36 | | /// <param name="CustomMetrics">Arbitrary key-value pairs attached by the tool implementation via <see cref="IToolMetric |
| | 241 | 37 | | public sealed record ToolCallDiagnostics( |
| | 157 | 38 | | int Sequence, |
| | 92 | 39 | | string ToolName, |
| | 36 | 40 | | TimeSpan Duration, |
| | 77 | 41 | | bool Succeeded, |
| | 2 | 42 | | string? ErrorMessage, |
| | 9 | 43 | | DateTimeOffset StartedAt, |
| | 9 | 44 | | DateTimeOffset CompletedAt, |
| | 241 | 45 | | IReadOnlyDictionary<string, object?>? CustomMetrics) |
| | | 46 | | { |
| | | 47 | | /// <summary> |
| | | 48 | | /// The name of the agent that triggered this tool call, or <see langword="null"/> |
| | | 49 | | /// if the agent name was not available. Used to attribute tool calls to the |
| | | 50 | | /// correct agent in multi-agent workflows where diagnostics are aggregated. |
| | | 51 | | /// </summary> |
| | 186 | 52 | | public string? AgentName { get; init; } |
| | | 53 | | |
| | | 54 | | /// <summary> |
| | | 55 | | /// The arguments passed to the tool invocation, keyed by parameter name. |
| | | 56 | | /// <see langword="null"/> if the tool took no arguments or capture was unavailable. |
| | | 57 | | /// Captured losslessly to enable post-hoc replay and tool-call-accuracy evaluation. |
| | | 58 | | /// </summary> |
| | 180 | 59 | | public IReadOnlyDictionary<string, object?>? Arguments { get; init; } |
| | | 60 | | |
| | | 61 | | /// <summary> |
| | | 62 | | /// The value returned by the tool invocation, or <see langword="null"/> if the |
| | | 63 | | /// tool returned <see langword="null"/> or the call failed. Captured losslessly |
| | | 64 | | /// (the actual object returned, not a serialized form) to enable post-hoc |
| | | 65 | | /// evaluation and replay. |
| | | 66 | | /// </summary> |
| | 169 | 67 | | public object? Result { get; init; } |
| | | 68 | | |
| | | 69 | | /// <summary> |
| | | 70 | | /// Character count of the JSON-serialized representation of |
| | | 71 | | /// <see cref="Arguments"/>. <c>0</c> when arguments are absent or |
| | | 72 | | /// serialization failed. Useful for detecting unexpectedly large |
| | | 73 | | /// tool argument payloads. |
| | | 74 | | /// </summary> |
| | 175 | 75 | | public long ArgumentsCharCount { get; init; } |
| | | 76 | | |
| | | 77 | | /// <summary> |
| | | 78 | | /// Character count of the JSON-serialized representation of |
| | | 79 | | /// <see cref="Result"/>. <c>0</c> when the result is absent or |
| | | 80 | | /// serialization failed. Useful for detecting tool responses that |
| | | 81 | | /// bloat the chat window. |
| | | 82 | | /// </summary> |
| | 167 | 83 | | public long ResultCharCount { get; init; } |
| | | 84 | | } |