DEV Community

Cover image for Using AIFunctions with OpenAI Realtime API in .NET
Mehran Davoudi
Mehran Davoudi

Posted on

Using AIFunctions with OpenAI Realtime API in .NET

If you've ever tried integrating Azure OpenAI's Realtime API into a .NET application, you've likely hit a frustrating wall: your beautifully crafted AIFunctions suddenly feel useless. The Realtime API demands raw JSON schemas for tools, and most examples out there force you to manually duplicate function definitions, stripping away the elegance of strongly typed C# models.

I ran into this exact pain point. So I built openai-realtime-sample, a comprehensive repo that shows how to reuse your existing AIFunctions as Realtime tools without rewriting a single line of JSON.

🚨 The Problem

.NET developers using Microsoft.Extensions.AI often define their functions using AIFunctionFactory.Create(...), complete with [Description] attributes and enums. This works beautifully for regular chat completions.

But when switching to the Realtime API, you're expected to:

  • Manually define JSON schemas for each tool
  • Duplicate function shapes and descriptions
  • Lose the benefits of strong typing and metadata

This leads to brittle code and a lot of unnecessary duplication.

The Solution

My repo shows how to elegantly convert AIFunctions into ConversationFunctionTool objects, making them compatible with the Realtime API. Here's the magic:

public static ConversationFunctionTool ConversationTool(this AIFunction function) =>
    new(function.Name)
    {
        Description = function.Description,
        Parameters = BinaryData.FromString(function.JsonSchema.ToString())
    };
Enter fullscreen mode Exit fullscreen mode

This extension method (found in AIExtensions.cs) lets you do:

var tools = GetTools(); // AIFunction[]
var conversationTools = tools.Select(t => t.ConversationTool());
sessionOptions.Tools.AddRange(conversationTools);
Enter fullscreen mode Exit fullscreen mode

And when a tool is invoked:

var tool = tools.First(t => t.Name == functionName);
var result = await tool.InvokeAsync(parsedArgs);
await session.AddItemAsync(RealtimeItem.CreateFunctionCallOutput(callId, result?.ToString() ?? ""));
Enter fullscreen mode Exit fullscreen mode

🧪 Two Sample Projects

  1. RealtimeSample.Console

    A minimal, linear example with a single weather function. Perfect for copying into your own app.

  2. RealtimeSample.BlazorHybrid (.NET MAUI)

    A richer, event-driven UI that visualizes RealtimeUpdate events like session start, speech input, delta streaming, and tool invocation.

💡 Key Takeaways

  • No manual JSON duplication, just reuse your AIFunctions
  • Enums and descriptions flow automatically into the tool schema
  • Respond to tool calls with FunctionCallOutput referencing the original function_call_id
  • Trigger a new model turn with StartResponseAsync after supplying tool outputs

🛠 Requirements

  • .NET 9
  • Azure OpenAI Realtime deployment (via environment variables)

🔁 Extend It

Want to add more tools? Just return them from GetTools(), your enums and descriptions will be automatically included.


This repo bridges the gap between high-level .NET abstractions and the low-level Realtime API protocol. Whether you're building a console app or a hybrid UI, this pattern gives you a clean, reusable way to integrate OpenAI tools without sacrificing type safety or maintainability.

Check out the full repo on GitHub and feel free to adapt the extension method into your own shared library.

Top comments (0)