Skip to content

Commit ad49405

Browse files
Enhance CodexClient for deferred completion handling
Updated the CodexClient class to manage deferred completion of turn notifications. Added fields for tracking status and errors, and implemented logic to defer completion until necessary data is available. Introduced a background task for scheduling deferred completions and modified the completion method to accommodate these changes.
1 parent e6b1275 commit ad49405

2 files changed

Lines changed: 70 additions & 2 deletions

File tree

Buffaly.CodexEmbedded.Core/CodexClient.cs

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -482,11 +482,16 @@ public static TurnNotification ForCompleted(string? threadId, string status, str
482482

483483
private sealed class TurnTracker
484484
{
485+
private static readonly TimeSpan DeferredCompletionDelay = TimeSpan.FromMilliseconds(50);
485486
private readonly StringBuilder _text = new();
486487
private readonly object _lock = new();
487488
private readonly List<TurnNotification> _queuedNotifications = new();
488489
private string? _lastError;
489490
private bool _isPrimed;
491+
private bool _hasDeferredCompletion;
492+
private string _deferredCompletionStatus = "unknown";
493+
private string? _deferredCompletionErrorMessage;
494+
private bool _deferredCompletionScheduled;
490495

491496
public string ThreadId { get; }
492497
public string TurnId { get; }
@@ -565,19 +570,82 @@ private void ApplyNotificationLocked(TurnNotification notification)
565570

566571
_text.Append(notification.Delta);
567572
Progress?.Report(new CodexDelta(notification.ThreadId ?? ThreadId, TurnId, notification.Delta));
573+
574+
// Guard against out-of-order notification timing where completion is observed before
575+
// the first delta for a turn. Finalize once delta text is available.
576+
if (_hasDeferredCompletion)
577+
{
578+
CompleteTurnLocked(_deferredCompletionStatus, _deferredCompletionErrorMessage);
579+
}
568580
return;
569581
}
570582
case TurnNotificationKind.Completed:
571583
{
572-
var aggregated = _text.ToString();
573584
var finalError = notification.ErrorMessage ?? _lastError;
574-
Completion.TrySetResult(new CodexTurnResult(ThreadId, TurnId, notification.Status, finalError, aggregated));
585+
if (_text.Length == 0 &&
586+
string.IsNullOrWhiteSpace(finalError) &&
587+
string.Equals(notification.Status, "completed", StringComparison.OrdinalIgnoreCase))
588+
{
589+
_hasDeferredCompletion = true;
590+
_deferredCompletionStatus = notification.Status;
591+
_deferredCompletionErrorMessage = finalError;
592+
ScheduleDeferredCompletion();
593+
return;
594+
}
595+
596+
CompleteTurnLocked(notification.Status, finalError);
575597
return;
576598
}
577599
default:
578600
return;
579601
}
580602
}
603+
604+
private void CompleteTurnLocked(string status, string? errorMessage)
605+
{
606+
if (Completion.Task.IsCompleted)
607+
{
608+
return;
609+
}
610+
611+
_hasDeferredCompletion = false;
612+
var aggregated = _text.ToString();
613+
Completion.TrySetResult(new CodexTurnResult(ThreadId, TurnId, status, errorMessage, aggregated));
614+
}
615+
616+
private void ScheduleDeferredCompletion()
617+
{
618+
if (_deferredCompletionScheduled)
619+
{
620+
return;
621+
}
622+
623+
_deferredCompletionScheduled = true;
624+
_ = Task.Run(async () =>
625+
{
626+
try
627+
{
628+
await Task.Delay(DeferredCompletionDelay);
629+
lock (_lock)
630+
{
631+
if (_hasDeferredCompletion)
632+
{
633+
CompleteTurnLocked(_deferredCompletionStatus, _deferredCompletionErrorMessage);
634+
}
635+
}
636+
}
637+
catch
638+
{
639+
}
640+
finally
641+
{
642+
lock (_lock)
643+
{
644+
_deferredCompletionScheduled = false;
645+
}
646+
}
647+
});
648+
}
581649
}
582650
}
583651

1.5 KB
Binary file not shown.

0 commit comments

Comments
 (0)