This repository contains two C# samples (Console + .NET MAUI Blazor Hybrid) that demonstrate a common pain point for .NET developers using the OpenAI Realtime API with Azure OpenAI: integrating Microsoft.Extensions.AI AIFunctions as Realtime conversation tools.
Most C# examples of the Realtime API:
- Hard‑code
ConversationFunctionToolJSON parameter schemas manually. - Do not leverage the strongly typed
AIFunction/AIFunctionFactorymodel binding & description metadata. - Leave developers duplicating function shape, descriptions, and enums in fragile JSON.
When moving from regular chat completions (where AIFunction works seamlessly) to the Realtime streaming model, developers often ask:
"How do I reuse my existing AIFunction definitions as Realtime tools without rewriting JSON?"
Both samples show how to:
- Define strongly typed functions using
AIFunctionFactory.Create(...)with[Description]attributes and enums. - Convert those
AIFunctioninstances intoConversationFunctionToolobjects expected by the Realtime API. - Capture tool invocation events (function call IDs + JSON argument payload) and map them back to the original
AIFunctionfor execution. - Serialize the tool result back to the Realtime session using
RealtimeItem.CreateFunctionCallOutput.
A small shared pattern enables this:
public static ConversationFunctionTool ConversationTool(this AIFunction function) =>
new(function.Name) { Description = function.Description, Parameters = BinaryData.FromString(function.JsonSchema.ToString()) };(The full version lives in each project as AIExtensions.cs).
Then:
var tools = GetTools(); // AIFunction[]
var conversationTools = tools.Select(t => t.ConversationTool());
sessionOptions.Tools.AddRange(conversationTools);And when a tool call finishes streaming:
var tool = tools.First(t => t.Name == functionName);
var result = await tool.InvokeAsync(parsedArgs);
await session.AddItemAsync(RealtimeItem.CreateFunctionCallOutput(callId, result?.ToString() ?? ""));A minimal, linear sample to grasp the core concept:
- Shows the end‑to‑end flow with a single weather function.
- Easiest place to copy the pattern into your own app.
A richer, event‑centric view:
- Visual grouping of
RealtimeUpdateobjects. - Demonstrates how Realtime emits: session start, input speech events, delta/content streaming, tool invocation, function completion, and response finalization.
- Helps you understand ordering and why you must wait for
OutputStreamingFinishedUpdatebefore invoking a tool.
- You can reuse your existing
AIFunctiondefinitions—no manual JSON duplication required. - The JsonSchema already produced by
AIFunctionis directly consumable byConversationFunctionTool. - Always respond to a tool call with a
FunctionCallOutputitem referencing the originalfunction_call_id. - After supplying tool outputs, trigger a new model turn (
StartResponseAsync) so the model can incorporate results.
Add more functions by returning them from GetTools() in each project. Enums and descriptions automatically flow into the tool schema.
- .NET 9
- Azure OpenAI Realtime deployment (environment variables used:
AzureOpenAI:gpt4o-realtime:Endpoint,Key,Deployment).
These samples bridge the gap between the higher-level Microsoft.Extensions.AI function abstraction and the lower-level OpenAI Realtime tool protocol, giving you both a simple and an introspective learning path.
Feel free to adapt the extension method into a shared library for broader reuse.