Tornado Runner
Overview
TornadoRunner provides advanced execution control for agents, including features like guardrails, monitoring, and sophisticated error handling. It's designed for production scenarios requiring robust agent management.
Quick Start
csharp
using LlmTornado.Agents;
TornadoAgent agent = new TornadoAgent(api, model);
// TornadoRunner is used internally when calling RunAsync
Conversation result = await agent.RunAsync("Your query here");Features
Event Handling
- Monitor agent execution in real-time
- Track tool calls and completions
- Receive streaming updates
- Log all agent activities
Error Recovery
- Automatic retry on transient failures
- Graceful degradation
- Fallback strategies
- Comprehensive error reporting
Performance Monitoring
- Track execution times
- Monitor token usage
- Measure tool call latency
- Generate performance metrics
Agents Method for Running Tornado-Runner
csharp
public async Task<Conversation> RunAsync(
string input = "",
List<ChatMessage>? appendMessages = null,
GuardRailFunction? inputGuardRailFunction = null,
bool? streaming = null,
Func<AgentRunnerEvents, ValueTask>? onAgentRunnerEvent = null,
int maxTurns = 10,
string responseId = "",
Func<string, ValueTask<bool>>? toolPermissionHandle = null,
bool singleTurn = false,
CancellationToken cancellationToken = default)string input
User Prompt for the agent to process next
List<ChatMessage>? appendMessages
Optional list of messages to append to the conversation history for context
GuardRailFunction? inputGuardRailFunction
Optional input guardrail to stop execution
bool? streaming
Optional enable streaming for the agent run loop (requires OnAgentRunnerEvent set to get streaming results)
⚡ Func<AgentRunnerEvents, ValueTask>? onAgentRunnerEvent ⚡
Detailed Agent loop event system that provides insight into the agent loop as well as all the streaming results and errors.
int maxTurns
Max agent loops you want the agent to run on a single turn [Default = 10].
Agent loop:
- Prompt->Tool Request(turn 1)->Tool Result->Agent Response(turn 2)
- Prompt->Tool Request(turn 1)->Tool Result->Tool Request(turn 2)->Tool Result->Agent Response(turn 3)
string responseId
Optional ResponseId for responseAPI thread usage
Func<string, ValueTask<bool>>? toolPermissionHandle
Optional Handle to take care of Passing tool permission requirement to the runner while the loop is stalled. (Only required when you have tool permission = true set on a agent tool)
bool singleTurn
Optional debugging feature to stop agent from running tools
CancellationToken cancellationToken
Cancallation token to stop the agent execution early.
Advanced Usage
Custom Event Handlers
csharp
ValueTask HandleRunnerEvents(AgentRunnerEvents runEvent)
{
switch (runEvent.EventType)
{
case AgentRunnerEventTypes.Start:
Console.WriteLine("Agent started");
break;
case AgentRunnerEventTypes.ToolCall:
if (runEvent is AgentRunnerToolCallEvent toolEvent)
{
Console.WriteLine($"Tool called: {toolEvent.ToolName}");
}
break;
case AgentRunnerEventTypes.Streaming:
// Handle streaming
break;
case AgentRunnerEventTypes.Complete:
Console.WriteLine("Agent completed");
break;
case AgentRunnerEventTypes.Error:
if (runEvent is AgentRunnerErrorEvent errorEvent)
{
Console.WriteLine($"Error: {errorEvent.Error.Message}");
}
break;
}
return ValueTask.CompletedTask;
}
Conversation result = await agent.RunAsync(
input,
onAgentRunnerEvent: HandleRunnerEvents
);Execution Configuration
csharp
Conversation result = await agent.RunAsync(
input: "Your query",
appendMessages: previousMessages,
streaming: true,
onAgentRunnerEvent: eventHandler
);Using Guardrails
csharp
using LlmTornado.Agents;
public struct IsMath
{
public string Reasoning { get; set; }
public bool IsMathRequest { get; set; }
}
async ValueTask<GuardRailFunctionOutput> MathGuardRail(string? input = "")
{
TornadoAgent mathGuardrail = new TornadoAgent(api, ChatModel.OpenAi.Gpt41.V41Mini, instructions: "Check if the user is asking you a Math related question.", outputSchema: typeof(IsMath));
Conversation result = await TornadoRunner.RunAsync(mathGuardrail, input);
IsMath? isMath = result.Messages.Last().Content.JsonDecode<IsMath>();
return new GuardRailFunctionOutput(isMath?.Reasoning ?? "", !isMath?.IsMathRequest ?? false);
}
TornadoAgent agent = new TornadoAgent(
client: api,
model: ChatModel.OpenAi.Gpt41.V41,
instructions: "You are a useful assistant"
);
try
{
Conversation result = await agent.RunAsync("What is the weather?", inputGuardRailFunction: MathGuardRail);
Console.WriteLine(result.Messages.Last().Content);
}
catch (GuardRailTriggerException guardRailEx)
{
Console.WriteLine(guardRailEx.message)
}Tool Permission
csharp
[JsonConverter(typeof(StringEnumConverter))]
public enum Unit
{
Celsius,
Fahrenheit
}
[Description("Get the current weather in a given location")]
public static string GetCurrentWeather(
[Description("The city and state, e.g. Boston, MA")] string location,
[Description("unit of temperature measurement in C or F")] Unit unit = Unit.Celsius)
{
// Call the weather API here.
return $"31 C";
}
TornadoAgent agent = new TornadoAgent(
api,
ChatModel.OpenAi.Gpt41.V41Mini,
instructions: "You are a useful assistant.",
tools: [GetCurrentWeather],
streaming: true,
toolPermissionRequired:new Dictionary<string, bool>()
{
{ "GetCurrentWeather", true }
}
);
ValueTask runEventHandler(AgentRunnerEvents runEvent)
{
switch (runEvent.EventType)
{
case AgentRunnerEventTypes.Streaming:
if (runEvent is AgentRunnerStreamingEvent streamingEvent)
{
if (streamingEvent.ModelStreamingEvent is ModelStreamingOutputTextDeltaEvent deltaTextEvent)
{
Console.Write(deltaTextEvent.DeltaText); // Write the text delta directly
}
}
break;
default:
break;
}
return ValueTask.CompletedTask;
}
ValueTask<bool> toolApprovalHandler(string toolRequest)
{
Console.WriteLine(toolRequest);
Console.WriteLine("Do you approve? (y/n)");
string? input = Console.ReadLine();
return ValueTask.FromResult(input?.ToLower().StartsWith('y') ?? false);
}
Console.WriteLine("[User]: What is the weather in boston?");
Console.Write("[Agent]: ");
Conversation result = await agent.RunAsync(
input:"What is the weather in boston?",
onAgentRunnerEvent:runEventHandler,
toolPermissionHandle: toolApprovalHandler);
Console.WriteLine(result.Messages.Last().Content);Best Practices
- Always implement error handling
- Monitor agent performance metrics
- Log important events
- Test with various scenarios
- Implement timeout mechanisms