diff --git a/docs/ai/how-to/access-data-in-functions.md b/docs/ai/how-to/access-data-in-functions.md new file mode 100644 index 0000000000000..291b974d6a2f5 --- /dev/null +++ b/docs/ai/how-to/access-data-in-functions.md @@ -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 type represents a function that can be described to an AI service and invoked. You can create `AIFunction` objects by calling one of the overloads. But is also a base class, and you can derive from it and implement your own AI function type. 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 . If you're creating your own function, you can populate `AdditionalProperties` however you want. If you use to create the function, you can populate data using . + +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 . The following sections describe how to access argument data using either approach. + +### Manual function invocation + +If you manually invoke an by calling , you pass in . The type includes: + +- A dictionary of named arguments. +- : An arbitrary `IDictionary` for passing additional ambient data into the function. +- : An 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 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"::: + + 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` + + publishes state about the current invocation to , including not only the arguments, but all of the input `ChatMessage` objects, the , and details on which function is being invoked (out of how many). You can add any data you want into 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 to invoke functions automatically, that client configures an 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 , 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 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. diff --git a/docs/ai/how-to/snippets/access-data/ArgumentsExample.cs b/docs/ai/how-to/snippets/access-data/ArgumentsExample.cs new file mode 100644 index 0000000000000..969d18fc26814 --- /dev/null +++ b/docs/ai/how-to/snippets/access-data/ArgumentsExample.cs @@ -0,0 +1,78 @@ +// +using Azure; +using Azure.AI.OpenAI; +using Microsoft.Extensions.AI; +using Microsoft.Extensions.Configuration; + +class ArgumentsExample +{ + public static async Task RunManual() + { + // + 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}"); + // + } + public static async Task UseFICC() + { + IConfigurationRoot config = new ConfigurationBuilder() + .AddUserSecrets() + .Build(); + + string endpoint = config["AZURE_OPENAI_ENDPOINT"]; + string apiKey = config["AZURE_OPENAI_API_KEY"]; + string model = config["AZURE_OPENAI_GPT_NAME"]; + + // + 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 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}"); + // + } +} diff --git a/docs/ai/how-to/snippets/access-data/Program.cs b/docs/ai/how-to/snippets/access-data/Program.cs new file mode 100644 index 0000000000000..b475775d3536b --- /dev/null +++ b/docs/ai/how-to/snippets/access-data/Program.cs @@ -0,0 +1,2 @@ +//await ArgumentsExample.RunManual(); +await ArgumentsExample.UseFICC(); diff --git a/docs/ai/how-to/snippets/access-data/Project.csproj b/docs/ai/how-to/snippets/access-data/Project.csproj new file mode 100644 index 0000000000000..8dc5dffa5c556 --- /dev/null +++ b/docs/ai/how-to/snippets/access-data/Project.csproj @@ -0,0 +1,21 @@ + + + + Exe + net10.0 + enable + enable + 4d162886-0da5-4b62-a4db-d09780d06911 + + + + + + + + + + + + + diff --git a/docs/ai/quickstarts/includes/create-ai-service.md b/docs/ai/quickstarts/includes/create-ai-service.md index d000ddb2f3c88..0290feaddcd67 100644 --- a/docs/ai/quickstarts/includes/create-ai-service.md +++ b/docs/ai/quickstarts/includes/create-ai-service.md @@ -10,4 +10,5 @@ dotnet user-secrets init dotnet user-secrets set AZURE_OPENAI_ENDPOINT dotnet user-secrets set AZURE_OPENAI_GPT_NAME + dotnet user-secrets set AZURE_OPENAI_API_KEY ``` diff --git a/docs/ai/quickstarts/snippets/function-calling/azure-openai/FunctionCallingAI.csproj b/docs/ai/quickstarts/snippets/function-calling/azure-openai/FunctionCallingAI.csproj index e4747a83d5ed9..8dc5dffa5c556 100644 --- a/docs/ai/quickstarts/snippets/function-calling/azure-openai/FunctionCallingAI.csproj +++ b/docs/ai/quickstarts/snippets/function-calling/azure-openai/FunctionCallingAI.csproj @@ -2,7 +2,7 @@ Exe - net9.0 + net10.0 enable enable 4d162886-0da5-4b62-a4db-d09780d06911 @@ -11,7 +11,7 @@ - + diff --git a/docs/ai/quickstarts/snippets/function-calling/azure-openai/Program.cs b/docs/ai/quickstarts/snippets/function-calling/azure-openai/Program.cs index a8d3a53750ed0..3bf804265772a 100644 --- a/docs/ai/quickstarts/snippets/function-calling/azure-openai/Program.cs +++ b/docs/ai/quickstarts/snippets/function-calling/azure-openai/Program.cs @@ -1,39 +1,42 @@ -// -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().Build(); +// +IConfigurationRoot config = new ConfigurationBuilder().AddUserSecrets().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(); // -// 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 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()}"); diff --git a/docs/ai/quickstarts/snippets/function-calling/openai/Program.cs b/docs/ai/quickstarts/snippets/function-calling/openai/Program.cs index d515895d5c11c..5238a08618d6d 100644 --- a/docs/ai/quickstarts/snippets/function-calling/openai/Program.cs +++ b/docs/ai/quickstarts/snippets/function-calling/openai/Program.cs @@ -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")] }; // diff --git a/docs/ai/quickstarts/use-function-calling.md b/docs/ai/quickstarts/use-function-calling.md index ca51d59d16a31..b6b700c2241b9 100644 --- a/docs/ai/quickstarts/use-function-calling.md +++ b/docs/ai/quickstarts/use-function-calling.md @@ -1,7 +1,7 @@ --- 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. @@ -9,7 +9,7 @@ zone_pivot_groups: openai-library # 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 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 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" @@ -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. @@ -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] - > 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 @@ -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/) diff --git a/docs/ai/toc.yml b/docs/ai/toc.yml index bded8c4e59964..b49af499ac0ef 100644 --- a/docs/ai/toc.yml +++ b/docs/ai/toc.yml @@ -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 @@ -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