Skip to content
Open
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using OpenAI;

// <SnippetUsingAIFunctionArguments>
AIFunction functionWithArgs = AIFunctionFactory.Create(
(AIFunctionArguments args) =>
{
// Access named parameters from the arguments dictionary.
string location = args["location"]?.ToString() ?? "Unknown";
string unit = args["unit"]?.ToString() ?? "celsius";

// Access the Context dictionary for additional data.
if (args.Context.TryGetValue("userId", out var userId))
{
Console.WriteLine($"Getting weather for user: {userId}");
}

return $"Weather in {location}: 22°{unit}";
},
name: "get_weather",
description: "Get the current weather");
// </SnippetUsingAIFunctionArguments>

// <SnippetUsingIServiceProvider>
// Set up dependency injection.
var services = new ServiceCollection();
services.AddSingleton<IWeatherService, WeatherService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();

// Function that accepts IServiceProvider to resolve dependencies.
AIFunction functionWithDI = AIFunctionFactory.Create(
(string location, IServiceProvider services) =>
{
var weatherService = services.GetRequiredService<IWeatherService>();
return weatherService.GetWeather(location);
},
name: "get_weather",
description: "Get the current weather");

// When invoking, provide the service provider.
var args = new AIFunctionArguments
{
["location"] = "Seattle",
Services = serviceProvider
};
// </SnippetUsingIServiceProvider>

// <SnippetUsingCurrentContext>
// Function that uses CurrentContext to access invocation metadata.
AIFunction functionWithContext = AIFunctionFactory.Create(
(string location) =>
{
// Access the current function invocation context.
var context = FunctionInvokingChatClient.CurrentContext;

if (context != null)
{
Console.WriteLine($"Function: {context.Function.Metadata.Name}");
Console.WriteLine($"Call ID: {context.CallId}");

// Access chat history or other context data.
foreach (var message in context.ChatHistory)
{
Console.WriteLine($"Previous message: {message.Text}");
}
}

return $"Weather in {location}: Sunny, 75°F";
},
name: "get_weather",
description: "Get the current weather");
// </SnippetUsingCurrentContext>

// <SnippetCustomParameterBinding>
// Custom parameter binding to source data from Context dictionary.
var options = new AIFunctionFactoryOptions
{
ConfigureParameterBinding = (ParameterInfo parameter) =>
{
// Bind 'userId' parameter from Context instead of arguments.
if (parameter.Name == "userId")
{
return new AIFunctionFactoryOptions.ParameterBindingOptions
{
// Custom binding logic.
BindParameter = (paramInfo, args) =>
{
// Get value from Context dictionary.
if (args.Context.TryGetValue("userId", out var value))
{
return value;
}

// Return default if not found.
return paramInfo.HasDefaultValue ? paramInfo.DefaultValue : null;
}
};
}

// Use default binding for other parameters.
return default;
}
};

// Create function with custom parameter binding.
AIFunction functionWithCustomBinding = AIFunctionFactory.Create(
method: (string location, string userId) =>
{
Console.WriteLine($"Getting weather for user {userId} in {location}");
return $"Weather in {location}: Cloudy, 65°F";
},
options: options);

// When invoking, pass userId via Context.
var customArgs = new AIFunctionArguments
{
["location"] = "Portland",
Context = { ["userId"] = "user123" }
};
// </SnippetCustomParameterBinding>

// Set up services.
var services = new ServiceCollection();
services.AddSingleton<IUserContext, UserContext>();
IServiceProvider provider = services.BuildServiceProvider();

// Configure AI client.
IConfigurationRoot config = new ConfigurationBuilder().AddUserSecrets<Program>().Build();
string? model = config["ModelName"];
string? key = config["OpenAIKey"];

IChatClient client =
new ChatClientBuilder(new OpenAIClient(key).GetChatClient(model ?? "gpt-4o").AsIChatClient())
.UseFunctionInvocation()
.Build();

// Create function with dependency injection.
var chatOptions = new ChatOptions
{
Tools = [AIFunctionFactory.Create(
(string location, IServiceProvider services) =>
{
var userContext = services.GetRequiredService<IUserContext>();
Console.WriteLine($"User {userContext.UserId} requesting weather for {location}");
return $"Weather in {location}: 70°F and sunny";
},
"get_weather",
"Get the current weather in a given location")]
};

// Prepare chat with service provider.
List<ChatMessage> chatHistory = [
new(ChatRole.System, "You are a helpful weather assistant.")
];
chatHistory.Add(new ChatMessage(ChatRole.User, "What's the weather in Boston?"));

// Pass service provider when making the request.
var functionArgs = new AIFunctionArguments
{
Services = provider
};

ChatResponse response = await client.GetResponseAsync(chatHistory, chatOptions);
Console.WriteLine($"Response: {response.Text}");

// Supporting types for examples.
public interface IWeatherService
{
string GetWeather(string location);
}

public class WeatherService : IWeatherService
{
public string GetWeather(string location)
{
return $"Weather in {location}: Partly cloudy, 68°F";
}
}

public interface IUserContext
{
string UserId { get; }
}

public class UserContext : IUserContext
{
public string UserId { get; set; } = "default-user";
}
61 changes: 60 additions & 1 deletion docs/ai/quickstarts/use-function-calling.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -132,6 +132,65 @@ The app uses the [`Microsoft.Extensions.AI`](https://www.nuget.org/packages/Micr

The app prints the completion response from the AI model, which includes data provided by the .NET function. The AI model understood that the registered function was available and called it automatically to generate a proper response.

## Pass data to AI functions

When creating AI functions, you often 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.

### Access function arguments

You can access all function arguments, including additional context data, by adding an <xref:Microsoft.Extensions.AI.AIFunctionArguments> parameter to your function delegate.

The `AIFunctionArguments` type provides:

- A dictionary of named arguments supplied by the AI model.
- A `Context` property for passing loosely typed additional data.
- A `Services` property for accessing dependency injection services.

The following example shows how to use `AIFunctionArguments`:

:::code language="csharp" source="snippets/function-calling/openai/AdvancedDataPassing.cs" id="UsingAIFunctionArguments":::

Parameters of type `AIFunctionArguments` aren't included in the JSON schema sent to the AI model because they're supplied by your code, not by the AI.

### Access dependency injection services

Functions can access services from a dependency injection container by adding an <xref:System.IServiceProvider> parameter. This is useful for accessing databases, HTTP clients, logging, or other services registered in your application's service collection.

The following example shows how to use `IServiceProvider` in a function:

:::code language="csharp" source="snippets/function-calling/openai/AdvancedDataPassing.cs" id="UsingIServiceProvider":::

Parameters of type `IServiceProvider` aren't included in the JSON schema sent to the AI model. If the parameter is optional (has a default value), the `Services` property is allowed to be `null`. Otherwise, it must be non-`null`, or the invocation fails with an exception.

### Access invocation context

During function execution, you can access the current invocation context using the <xref:Microsoft.Extensions.AI.FunctionInvokingChatClient.CurrentContext> static property. This property provides access to:

- Function metadata
- Call ID
- Chat history
- Other contextual information about the current invocation

The following example shows how to use `CurrentContext`:

:::code language="csharp" source="snippets/function-calling/openai/AdvancedDataPassing.cs" id="UsingCurrentContext":::

The `CurrentContext` value flows across async calls and is only available during function invocation.

### Custom parameter binding

For advanced scenarios, you can customize how function parameters are bound using <xref:Microsoft.Extensions.AI.AIFunctionFactoryOptions.ConfigureParameterBinding>. This allows you to:

- Source parameter values from the `Context` dictionary instead of function arguments.
- Exclude parameters from the JSON schema.
- Implement custom binding logic.

The following example shows how to bind a `userId` parameter from the `Context` dictionary:

:::code language="csharp" source="snippets/function-calling/openai/AdvancedDataPassing.cs" id="CustomParameterBinding":::

Custom parameter binding is useful when you need to pass trusted or validated data that shouldn't be supplied by the AI model.

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

## Clean up resources
Expand Down
Loading