namespace AIStudio.Assistants.VisualBriefing;
internal sealed partial class VisualBriefingBuildOrchestrator
{
///
/// Marks an intentionally reused stage as skipped.
///
/// The build record.
/// The stage.
/// The reused output hash.
private static void MarkSkipped(
VisualBriefingBuildRecord build,
VisualBriefingBuildStage stage,
string outputHash)
{
var record = GetStage(build, stage);
record.Status = VisualBriefingBuildStageStatus.SKIPPED;
record.StartedAtUtc ??= DateTimeOffset.UtcNow;
record.FinishedAtUtc = DateTimeOffset.UtcNow;
record.InputFingerprint = outputHash;
record.OutputHash = outputHash;
record.Failure = null;
build.UpdatedAtUtc = DateTimeOffset.UtcNow;
}
///
/// Gets or creates one stage record.
///
/// The build record.
/// The desired stage.
/// The stage record.
private static VisualBriefingBuildStageRecord GetStage(
VisualBriefingBuildRecord build,
VisualBriefingBuildStage stage)
{
var record = build.Stages.FirstOrDefault(candidate => candidate.Stage == stage);
if (record is not null)
return record;
record = new() { Stage = stage };
build.Stages.Add(record);
return record;
}
///
/// Persists a terminal build failure.
///
/// The build record.
/// The terminal status.
/// The safe failure.
/// The cancellation token.
private async Task SaveTerminalStateAsync(
VisualBriefingBuildRecord build,
VisualBriefingBuildStatus status,
VisualBriefingFailure failure,
CancellationToken token)
{
var stage = GetStage(build, failure.Stage);
var terminalStageStatus = status is VisualBriefingBuildStatus.CANCELED
? VisualBriefingBuildStageStatus.CANCELED
: VisualBriefingBuildStageStatus.FAILED;
foreach (var runningStage in build.Stages.Where(item =>
item.Status is VisualBriefingBuildStageStatus.RUNNING))
{
runningStage.Status = terminalStageStatus;
runningStage.FinishedAtUtc = DateTimeOffset.UtcNow;
runningStage.Failure = failure;
}
if (stage.Status is not (VisualBriefingBuildStageStatus.COMPLETED or VisualBriefingBuildStageStatus.SKIPPED))
{
stage.Status = terminalStageStatus;
stage.StartedAtUtc ??= DateTimeOffset.UtcNow;
stage.FinishedAtUtc = DateTimeOffset.UtcNow;
stage.Failure = failure;
}
build.Status = status;
build.Failure = failure;
build.UpdatedAtUtc = DateTimeOffset.UtcNow;
await this.store.SaveBuildAsync(build, token);
this.progressService.Publish(build);
}
///
/// Finishes diagnostics and creates a failed result.
///
/// The operation diagnostics.
/// The optional persisted build.
/// The safe failure.
/// Whether content can continue as a rebuild.
/// The failed result.
private static VisualBriefingBuildResult FinishFailure(
VisualBriefingOperationDiagnostics diagnostics,
VisualBriefingBuildRecord? build,
VisualBriefingFailure failure,
bool canContinueAsRebuild)
{
diagnostics.BuildId = build?.BuildId ?? diagnostics.BuildId;
diagnostics.Stage = failure.Stage;
diagnostics.FailureCode = failure.Code;
diagnostics.ValidationRule = failure.ValidationRule;
diagnostics.StructuredResponse = failure.StructuredResponse;
diagnostics.FinishedAtUtc = DateTimeOffset.UtcNow;
return new(
false,
null,
failure.UserMessage,
failure.Code,
diagnostics,
canContinueAsRebuild);
}
///
/// Creates a logging event from a stable identifier.
///
/// The stable event identifier.
/// The logging event.
private static EventId Event(VisualBriefingLogEventId eventId) => new((int)eventId, eventId.ToString());
}