Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions docs/ai/how-to/access-data-in-functions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: Access data in AI functions
description: Learn how to pass data to AIFunction objects and how to access the data within the function delegate.
ms.date: 11/17/2025
---

# Access data in AI functions

When you create AI functions, you might need to access contextual data beyond the parameters provided by the AI model. The `Microsoft.Extensions.AI` library provides several mechanisms to pass data to function delegates.

## `AIFunction` class

The <xref:Microsoft.Extensions.AI.AIFunction> type represents a function that can be described to an AI service and invoked. You can create `AIFunction` objects by calling one of the <xref:Microsoft.Extensions.AI.AIFunctionFactory.Create*?displayProperty=nameWithType> overloads. But <xref:Microsoft.Extensions.AI.AIFunction> is also a base class, and you can derive from it and implement your own AI function type. <xref:Microsoft.Extensions.AI.DelegatingAIFunction> provides an easy way to wrap an existing `AIFunction` and layer in additional functionality, including capturing additional data to be used.

## Pass data

You can associate data with the function at the time it's created, either via closure or via <xref:Microsoft.Extensions.AI.ChatOptions.AdditionalProperties>. If you're creating your own function, you can populate `AdditionalProperties` however you want. If you use <xref:Microsoft.Extensions.AI.AIFunctionFactory> to create the function, you can populate data using <xref:Microsoft.Extensions.AI.AIFunctionFactoryOptions.AdditionalProperties?displayProperty=nameWithType>.

You can also capture any references to data as part of the delegate provided to `AIFunctionFactory`. That is, you can bake in whatever you want to reference as part of the `AIFunction` itself.

## Access data in function delegates

You might call your `AIFunction` directly, or you might call it indirectly by using <xref:Microsoft.Extensions.AI.FunctionInvokingChatClient>. The following sections describe how to access argument data using either approach.

### Manual function invocation

If you manually invoke an <xref:Microsoft.Extensions.AI.AIFunction> by calling <xref:Microsoft.Extensions.AI.AIFunction.InvokeAsync(Microsoft.Extensions.AI.AIFunctionArguments,System.Threading.CancellationToken)?displayProperty=nameWithType>, you pass in <xref:Microsoft.Extensions.AI.AIFunctionArguments>. The <xref:Microsoft.Extensions.AI.AIFunctionArguments> type includes:

- A dictionary of named arguments.
- <xref:Microsoft.Extensions.AI.AIFunctionArguments.Context>: An arbitrary `IDictionary<object, object>` for passing additional ambient data into the function.
- <xref:Microsoft.Extensions.AI.AIFunctionArguments.Services>: An <xref:System.IServiceProvider> that lets the `AIFunction` resolve arbitrary state from a [dependency injection (DI)](../../core/extensions/dependency-injection.md) container.

If you want to access either the `AIFunctionArguments` or the `IServiceProvider` from within your <xref:Microsoft.Extensions.AI.AIFunctionFactory.Create*?displayProperty=nameWithType> delegate, create a parameter typed as `IServiceProvider` or `AIFunctionArguments`. That parameter will be bound to the relevant data from the `AIFunctionArguments` passed to `AIFunction.InvokeAsync()`.

The following code shows an example:

:::code language="csharp" source="snippets/access-data/ArgumentsExample.cs" id="UseAIFunctionArguments":::

<xref:System.Threading.CancellationToken> is also special-cased: if the `AIFunctionFactory.Create` delegate or lambda has a `CancellationToken` parameter, it will be bound to the `CancellationToken` that was passed to `AIFunction.InvokeAsync()`.

### Invocation through `FunctionInvokingChatClient`

<xref:Microsoft.Extensions.AI.FunctionInvokingChatClient> publishes state about the current invocation to <xref:Microsoft.Extensions.AI.FunctionInvokingChatClient.CurrentContext?displayProperty=nameWithType>, including not only the arguments, but all of the input `ChatMessage` objects, the <xref:Microsoft.Extensions.AI.ChatOptions>, and details on which function is being invoked (out of how many). You can add any data you want into <xref:Microsoft.Extensions.AI.ChatOptions.AdditionalProperties?displayProperty=nameWithType> and extract that inside of your `AIFunction` from `FunctionInvokingChatClient.CurrentContext.Options.AdditionalProperties`.

The following code shows an example:

:::code language="csharp" source="snippets/access-data/ArgumentsExample.cs" id="UseAdditionalProperties":::

#### Dependency injection

If you use <xref:Microsoft.Extensions.AI.FunctionInvokingChatClient> to invoke functions automatically, that client configures an <xref:Microsoft.Extensions.AI.AIFunctionArguments> object that it passes into the `AIFunction`. Because `AIFunctionArguments` includes the `IServiceProvider` that the `FunctionInvokingChatClient` was itself provided with, if you construct your client using standard DI means, that `IServiceProvider` is passed all the way into your `AIFunction`. At that point, you can query it for anything you want from DI.

## Advanced techniques

If you want more fine-grained control over how parameters are bound, you can use <xref:Microsoft.Extensions.AI.AIFunctionFactoryOptions.ConfigureParameterBinding?displayProperty=nameWithType>, which puts you in control over how each parameter is populated. For example, the [MCP C# SDK uses this technique](https://github.com/modelcontextprotocol/csharp-sdk/blob/d344c651203841ec1c9e828736d234a6e4aebd07/src/ModelContextProtocol.Core/Server/AIFunctionMcpServerTool.cs#L83-L107) to automatically bind parameters from DI.

If you use the <xref:Microsoft.Extensions.AI.AIFunctionFactory.Create(System.Reflection.MethodInfo,System.Func{Microsoft.Extensions.AI.AIFunctionArguments,System.Object},Microsoft.Extensions.AI.AIFunctionFactoryOptions)?displayProperty=nameWithType> overload, you can also run your own arbitrary logic when you create the target object that the instance method will be called on, each time. And you can do whatever you want to configure that instance.
78 changes: 78 additions & 0 deletions docs/ai/how-to/snippets/access-data/ArgumentsExample.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// <SnippetGetChatClient>
using Azure;
using Azure.AI.OpenAI;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;

class ArgumentsExample
{
public static async Task RunManual()
{
// <SnippetUseAIFunctionArguments>
Delegate getWeatherDelegate = (AIFunctionArguments args) =>
{
// Access named parameters from the arguments dictionary.
string? location = args.TryGetValue("location", out object? loc) ? loc.ToString() : "Unknown";
string? units = args.TryGetValue("units", out object? u) ? u.ToString() : "celsius";

return $"Weather in {location}: 35°{units}";
};

// Create the AIFunction.
AIFunction getWeather = AIFunctionFactory.Create(getWeatherDelegate);

// Call the function manually.
var result = await getWeather.InvokeAsync(new AIFunctionArguments
{
{ "location", "Seattle" },
{ "units", "F" }
});
Console.WriteLine($"Function result: {result}");
// </SnippetUseAIFunctionArguments>
}
public static async Task UseFICC()
{
IConfigurationRoot config = new ConfigurationBuilder()
.AddUserSecrets<Program>()
.Build();

string endpoint = config["AZURE_OPENAI_ENDPOINT"];
string apiKey = config["AZURE_OPENAI_API_KEY"];
string model = config["AZURE_OPENAI_GPT_NAME"];

// <SnippetUseAdditionalProperties>
FunctionInvokingChatClient client = new FunctionInvokingChatClient(
new AzureOpenAIClient(new Uri(endpoint), new AzureKeyCredential(apiKey))
.GetChatClient(model).AsIChatClient());

AIFunction getWeather = AIFunctionFactory.Create(() =>
{
// Access named parameters from the arguments dictionary.
AdditionalPropertiesDictionary props =
FunctionInvokingChatClient.CurrentContext.Options.AdditionalProperties;

string location = props["location"].ToString();
string units = props["units"].ToString();

return $"Weather in {location}: 35°{units}";
});

var chatOptions = new ChatOptions
{
Tools = [getWeather],
AdditionalProperties = new AdditionalPropertiesDictionary {
["location"] = "Seattle",
["units"] = "F"
},
};

List<ChatMessage> chatHistory = [
new(ChatRole.System, "You're a helpful weather assistant.")
];
chatHistory.Add(new ChatMessage(ChatRole.User, "What's the weather like?"));

ChatResponse response = await client.GetResponseAsync(chatHistory, chatOptions);
Console.WriteLine($"Response: {response.Text}");
// </SnippetUseAdditionalProperties>
}
}
2 changes: 2 additions & 0 deletions docs/ai/how-to/snippets/access-data/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
//await ArgumentsExample.RunManual();
await ArgumentsExample.UseFICC();
21 changes: 21 additions & 0 deletions docs/ai/how-to/snippets/access-data/Project.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>4d162886-0da5-4b62-a4db-d09780d06911</UserSecretsId>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.17.0" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.AI" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.4.0-preview.1.25207.5" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.10" />

</ItemGroup>

</Project>
1 change: 1 addition & 0 deletions docs/ai/quickstarts/includes/create-ai-service.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@
dotnet user-secrets init
dotnet user-secrets set AZURE_OPENAI_ENDPOINT <your-Azure-OpenAI-endpoint>
dotnet user-secrets set AZURE_OPENAI_GPT_NAME <your-Azure-OpenAI-model-name>
dotnet user-secrets set AZURE_OPENAI_API_KEY <your-Azure-OpenAI-key>
```
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<UserSecretsId>4d162886-0da5-4b62-a4db-d09780d06911</UserSecretsId>
Expand All @@ -11,7 +11,7 @@
<ItemGroup>
<PackageReference Include="Azure.Identity" Version="1.17.0" />
<PackageReference Include="Azure.AI.OpenAI" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.AI" Version="9.10.1" />
<PackageReference Include="Microsoft.Extensions.AI" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.AI.OpenAI" Version="9.4.0-preview.1.25207.5" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="9.0.10" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="9.0.10" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
// <SnippetGetChatClient>
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.AI;
using Azure;
using Azure.AI.OpenAI;
using Azure.Identity;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;

var config = new ConfigurationBuilder().AddUserSecrets<Program>().Build();
// <SnippetGetChatClient>
IConfigurationRoot config = new ConfigurationBuilder().AddUserSecrets<Program>().Build();
string endpoint = config["AZURE_OPENAI_ENDPOINT"];
string deployment = config["AZURE_OPENAI_GPT_NAME"];
string apiKey = config["AZURE_OPENAI_API_KEY"];

IChatClient client =
new ChatClientBuilder(
new AzureOpenAIClient(new Uri(endpoint), new DefaultAzureCredential())
new AzureOpenAIClient(new Uri(endpoint), new AzureKeyCredential(apiKey))
.GetChatClient(deployment).AsIChatClient())
.UseFunctionInvocation()
.Build();
// </SnippetGetChatClient>

// Add a new plugin with a local .NET function that should be available to the AI model
// Add a new plugin with a local .NET function
// that should be available to the AI model.
var chatOptions = new ChatOptions
{
Tools = [AIFunctionFactory.Create((string location, string unit) =>
{
// Here you would call a weather API to get the weather for the location
return "Periods of rain or drizzle, 15 C";
},
"get_current_weather",
"Get the current weather in a given location")]
{
// Here you would call a weather API to
// get the weather for the location.
return "Periods of rain or drizzle, 15 C";
},
"get_current_weather",
"Get the current weather in a given location")]
};

// System prompt to provide context
// System prompt to provide context.
List<ChatMessage> chatHistory = [new(ChatRole.System, """
You are a hiking enthusiast who helps people discover fun hikes in their area. You are upbeat and friendly.
""")];
You are a hiking enthusiast who helps people discover fun hikes in their area. You are upbeat and friendly.
""")];

// Weather conversation relevant to the registered function
// Weather conversation relevant to the registered function.
chatHistory.Add(new ChatMessage(ChatRole.User,
"I live in Montreal and I'm looking for a moderate intensity hike. What's the current weather like? "));
Console.WriteLine($"{chatHistory.Last().Role} >>> {chatHistory.Last()}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
return "Periods of rain or drizzle, 15 C";
},
"get_current_weather",
"Get the current weather in a given location")]
"Gets the current weather in a given location")]
};
// </SnippetAddOptions>

Expand Down
12 changes: 4 additions & 8 deletions docs/ai/quickstarts/use-function-calling.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
---
title: Quickstart - Extend OpenAI using Tools and execute a local Function with .NET
description: Create a simple chat app using OpenAI and extend the model to execute a local function.
ms.date: 03/13/2025
ms.date: 11/13/2025
ms.topic: quickstart
zone_pivot_groups: openai-library
# CustomerIntent: As a .NET developer new to OpenAI, I want deploy and use sample code to interact to learn from the sample code how to extend the model using Tools.
---

# Invoke .NET functions using an AI model

In this quickstart, you create a .NET console AI chat app to connect to an AI model with local function calling enabled. The app uses the <xref:Microsoft.Extensions.AI> library so you can write code using AI abstractions rather than a specific SDK. AI abstractions enable you to change the underlying AI model with minimal code changes.
In this quickstart, you create a .NET console AI chat app that connects to an AI model with local function calling enabled. The app uses the <xref:Microsoft.Extensions.AI> library so you can write code using AI abstractions rather than a specific SDK. AI abstractions enable you to change the underlying AI model with minimal code changes.

:::zone target="docs" pivot="openai"

Expand All @@ -23,8 +23,6 @@ In this quickstart, you create a .NET console AI chat app to connect to an AI mo

:::zone-end

[!INCLUDE [semantic-kernel](includes/semantic-kernel.md)]

## Create the app

Complete the following steps to create a .NET console app to connect to an AI model.
Expand Down Expand Up @@ -103,10 +101,7 @@ The app uses the [`Microsoft.Extensions.AI`](https://www.nuget.org/packages/Micr

:::zone target="docs" pivot="azure-openai"

:::code language="csharp" source="snippets/function-calling/azure-openai/program.cs" id="GetChatClient":::

> [!NOTE]
> <xref:Azure.Identity.DefaultAzureCredential> searches for authentication credentials from your local tooling. If you aren't using the `azd` template to provision the Azure OpenAI resource, you'll need to assign the `Azure AI Developer` role to the account you used to sign in to Visual Studio or the Azure CLI. For more information, see [Authenticate to Azure AI services with .NET](../azure-ai-services-authentication.md).
:::code language="csharp" source="snippets/function-calling/azure-openai/Program.cs" id="GetChatClient":::

:::zone-end

Expand Down Expand Up @@ -145,5 +140,6 @@ If you no longer need them, delete the Azure OpenAI resource and GPT-4 model dep

## Next steps

- [Access data in AI functions](../how-to/access-data-in-functions.md)
- [Quickstart - Build an AI chat app with .NET](build-chat-app.md)
- [Generate text and conversations with .NET and Azure OpenAI Completions](/training/modules/open-ai-dotnet-text-completions/)
18 changes: 12 additions & 6 deletions docs/ai/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,18 @@ items:
href: conceptual/rag.md
- name: OpenAI function calling
href: conceptual/understanding-openai-functions.md
- name: Call functions
items:
- name: "Quickstart: Execute a local function"
href: quickstarts/use-function-calling.md
- name: Access data in AI functions
href: how-to/access-data-in-functions.md
- name: Text to image
items:
- name: Generate images using MEAI
href: quickstarts/text-to-image.md
- name: Generate images using OpenAI.Images.ImageClient
href: quickstarts/generate-images.md
- name: Chat with your data (RAG)
items:
- name: Get started with the RAG sample
Expand All @@ -68,12 +80,6 @@ items:
href: quickstarts/build-mcp-server.md
- name: Build a minimal MCP client
href: quickstarts/build-mcp-client.md
- name: Text to image
items:
- name: Generate images using MEAI
href: quickstarts/text-to-image.md
- name: Generate images using OpenAI.Images.ImageClient
href: quickstarts/generate-images.md
- name: Security and content safety
items:
- name: Authentication for Azure-hosted apps and services
Expand Down