| | | 1 | | namespace NexusLabs.Needlr.AgentFramework.Progress; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Default <see cref="IProgressReporter"/> that fans out events to all registered sinks. |
| | | 5 | | /// Sink exceptions are surfaced via an <see cref="IProgressReporterErrorHandler"/> instead |
| | | 6 | | /// of being silently swallowed. |
| | | 7 | | /// </summary> |
| | | 8 | | [DoNotAutoRegister] |
| | | 9 | | internal sealed class ProgressReporter : IProgressReporter |
| | | 10 | | { |
| | | 11 | | private readonly IReadOnlyList<IProgressSink> _sinks; |
| | | 12 | | private readonly IProgressSequence _sequence; |
| | | 13 | | private readonly IProgressReporterErrorHandler _errorHandler; |
| | | 14 | | private readonly string? _parentAgentId; |
| | | 15 | | |
| | 34 | 16 | | internal ProgressReporter( |
| | 34 | 17 | | string workflowId, |
| | 34 | 18 | | IReadOnlyList<IProgressSink> sinks, |
| | 34 | 19 | | IProgressSequence sequence, |
| | 34 | 20 | | IProgressReporterErrorHandler? errorHandler = null, |
| | 34 | 21 | | string? agentId = null, |
| | 34 | 22 | | string? parentAgentId = null, |
| | 34 | 23 | | int depth = 0) |
| | | 24 | | { |
| | 34 | 25 | | WorkflowId = workflowId; |
| | 34 | 26 | | _sinks = sinks; |
| | 34 | 27 | | _sequence = sequence; |
| | 34 | 28 | | _errorHandler = errorHandler ?? new NullProgressReporterErrorHandler(); |
| | 34 | 29 | | AgentId = agentId; |
| | 34 | 30 | | _parentAgentId = parentAgentId; |
| | 34 | 31 | | Depth = depth; |
| | 34 | 32 | | } |
| | | 33 | | |
| | | 34 | | /// <inheritdoc /> |
| | 41 | 35 | | public string WorkflowId { get; } |
| | | 36 | | |
| | | 37 | | /// <inheritdoc /> |
| | 32 | 38 | | public string? AgentId { get; } |
| | | 39 | | |
| | | 40 | | /// <inheritdoc /> |
| | 34 | 41 | | public int Depth { get; } |
| | | 42 | | |
| | | 43 | | /// <inheritdoc /> |
| | 26 | 44 | | public long NextSequence() => _sequence.Next(); |
| | | 45 | | |
| | | 46 | | /// <inheritdoc /> |
| | | 47 | | public void Report(IProgressEvent progressEvent) |
| | | 48 | | { |
| | 33 | 49 | | if (_sinks.Count == 0) return; |
| | | 50 | | |
| | 128 | 51 | | for (int i = 0; i < _sinks.Count; i++) |
| | | 52 | | { |
| | 33 | 53 | | var sink = _sinks[i]; |
| | | 54 | | try |
| | | 55 | | { |
| | 33 | 56 | | var task = sink.OnEventAsync(progressEvent, CancellationToken.None); |
| | 33 | 57 | | if (!task.IsCompletedSuccessfully) |
| | | 58 | | { |
| | 1 | 59 | | var handler = _errorHandler; |
| | 1 | 60 | | var capturedSink = sink; |
| | 1 | 61 | | var capturedEvent = progressEvent; |
| | 1 | 62 | | task.AsTask().ContinueWith( |
| | 1 | 63 | | t => |
| | 1 | 64 | | { |
| | 1 | 65 | | var ex = t.Exception?.GetBaseException(); |
| | 1 | 66 | | if (ex is not null) |
| | 1 | 67 | | handler.OnSinkException(capturedSink, capturedEvent, ex); |
| | 1 | 68 | | }, |
| | 1 | 69 | | TaskContinuationOptions.OnlyOnFaulted | TaskContinuationOptions.ExecuteSynchronously); |
| | | 70 | | } |
| | 33 | 71 | | } |
| | 0 | 72 | | catch (Exception ex) |
| | | 73 | | { |
| | 0 | 74 | | _errorHandler.OnSinkException(sink, progressEvent, ex); |
| | 0 | 75 | | } |
| | | 76 | | } |
| | 31 | 77 | | } |
| | | 78 | | |
| | | 79 | | /// <inheritdoc /> |
| | | 80 | | public IProgressReporter CreateChild(string agentId) => |
| | 7 | 81 | | new ProgressReporter( |
| | 7 | 82 | | WorkflowId, |
| | 7 | 83 | | _sinks, |
| | 7 | 84 | | _sequence, |
| | 7 | 85 | | _errorHandler, |
| | 7 | 86 | | agentId: agentId, |
| | 7 | 87 | | parentAgentId: AgentId, |
| | 7 | 88 | | depth: Depth + 1); |
| | | 89 | | } |