Skip to content

AI scenarios

Agent failure

Bit.BlazorUI.AI Scripted transport

A step that fails, with the failure attached to the step rather than replacing the whole run.

Live demo

Ship the release: the build passes, the test suite does not, and the agent stops before deploying.

\n**A run with a failed step**: ```razor @implements IAsyncDisposable
``` ```csharp private readonly BitAiChatSession session = new(AiDemoAgentTransports.Fails()); private readonly string[] suggestions = ["Deploy the current branch"]; public ValueTask DisposeAsync() => session.DisposeAsync(); ``` `AiDemoAgentTransports.cs`: ```csharp // AiScriptedTransport and AiScript are the demo's helpers - see the Basic chat scenario. public static IBitAiTransport Fails() => new AiScriptedTransport((request, token) => AiScript.Stream(token, async yielder => { // A run of its own for every turn, so asking again starts a new run rather than rewriting the one above. var runId = BitAiId.NewRunId(); await yielder(new BitAiMessageStartedEvent()); await yielder(new BitAiAgentRunStartedEvent(runId) { AgentName = "Deploy agent", Goal = "Ship the release" }); await yielder(new BitAiAgentPlanEvent(runId, [ new BitAiAgentPlanStep("s1", "Build"), new BitAiAgentPlanStep("s2", "Run the test suite"), new BitAiAgentPlanStep("s3", "Deploy"), ])); await yielder(new BitAiAgentStepStartedEvent(runId, "s1")); await yielder(new BitAiAgentStepCompletedEvent(runId, "s1") { Summary = "Build succeeded in 41s" }); // The failure belongs to the step, not to the whole run. await yielder(new BitAiAgentStepStartedEvent(runId, "s2")); await yielder(new BitAiAgentStepCompletedEvent(runId, "s2", BitAiAgentStepStatus.Failed) { Error = new BitAiError("3 of 812 tests failed.") { Detail = "Checkout.AppliesDiscount\nCheckout.RoundsTax\nCheckout.HandlesRefund", IsRetryable = true, }, }); await yielder(new BitAiAgentRunCompletedEvent(runId, BitAiAgentRunStatus.Failed)); await foreach (var evt in AiScript.Write("I stopped before deploying. Three checkout tests fail, and all three touch money.", 4, 12, token)) await yielder(evt); await yielder(new BitAiMessageCompletedEvent()); })); ```

How it works

The transport fails the second step with BitAiAgentStepCompletedEvent(runId, "s2", BitAiAgentStepStatus.Failed), carrying a BitAiError - its message, the failing tests as its Detail, and IsRetryable - then ends the run with BitAiAgentRunCompletedEvent(runId, BitAiAgentRunStatus.Failed), and still writes its answer. The failing step keeps its error, shown on the step itself; the completed steps keep their summaries; the step the run never reached (Deploy) is marked Skipped rather than left pending; and the run is marked Failed. Nothing that already succeeded is lost, which is what lets the answer explain what happened rather than just report that something did.

The failure is the step's, not the reply's: the turn itself succeeds. A failure that ends the reply is a terminal BitAiErrorEvent (see Errors and retry), and one that does not - one of several parallel calls failed, and the rest are still worth showing - is the same event with isTerminal: false, which adds a BitAiErrorPart inside the message and lets it stream on.

Components used

Feedback

Found a mistake, a gap, or something that could be clearer? Every page and every component is one click from its source.