2024-10-02 11:42:27 -07:00
// Copyright (c) Microsoft Corporation. All rights reserved.
2024-04-26 09:21:46 -07:00
// Example03_Agent_FunctionCall.cs
using AutoGen.BasicSample ;
2024-05-09 15:36:20 -07:00
using AutoGen.Core ;
2024-08-27 14:37:47 -07:00
using AutoGen.OpenAI ;
using AutoGen.OpenAI.Extension ;
2024-04-26 09:21:46 -07:00
using FluentAssertions ;
2024-11-03 09:18:32 -08:00
using Microsoft.Extensions.AI ;
2024-04-26 09:21:46 -07:00
/// <summary>
/// This example shows how to add type-safe function call to an agent.
/// </summary>
public partial class Example03_Agent_FunctionCall
{
/// <summary>
/// upper case the message when asked.
/// </summary>
/// <param name="message"></param>
[Function]
public async Task < string > UpperCase ( string message )
{
return message . ToUpper ( ) ;
}
/// <summary>
/// Concatenate strings.
/// </summary>
/// <param name="strings">strings to concatenate</param>
[Function]
public async Task < string > ConcatString ( string [ ] strings )
{
return string . Join ( " " , strings ) ;
}
/// <summary>
/// calculate tax
/// </summary>
/// <param name="price">price, should be an integer</param>
/// <param name="taxRate">tax rate, should be in range (0, 1)</param>
2024-11-03 09:18:32 -08:00
[Function]
2024-04-26 09:21:46 -07:00
public async Task < string > CalculateTax ( int price , float taxRate )
{
return $"tax is {price * taxRate}" ;
}
2024-11-03 09:18:32 -08:00
/// <summary>
/// This example shows how to add type-safe function call using AutoGen.SourceGenerator.
/// The SourceGenerator will automatically generate FunctionDefinition and FunctionCallWrapper during compiling time.
///
/// For adding type-safe function call from M.E.A.I tools, please refer to <see cref="ToolCallWithMEAITools"/>.
/// </summary>
/// <returns></returns>
public static async Task ToolCallWithSourceGenerator ( )
2024-04-26 09:21:46 -07:00
{
var instance = new Example03_Agent_FunctionCall ( ) ;
2024-08-27 14:37:47 -07:00
var gpt4o = LLMConfiguration . GetOpenAIGPT4o_mini ( ) ;
2024-04-26 09:21:46 -07:00
// AutoGen makes use of AutoGen.SourceGenerator to automatically generate FunctionDefinition and FunctionCallWrapper for you.
// The FunctionDefinition will be created based on function signature and XML documentation.
// The return type of type-safe function needs to be Task<string>. And to get the best performance, please try only use primitive types and arrays of primitive types as parameters.
2024-08-27 14:37:47 -07:00
var toolCallMiddleware = new FunctionCallMiddleware (
functions : [
2024-04-26 09:21:46 -07:00
instance . ConcatStringFunctionContract ,
instance . UpperCaseFunctionContract ,
instance . CalculateTaxFunctionContract ,
2024-08-27 14:37:47 -07:00
] ,
2024-04-26 09:21:46 -07:00
functionMap : new Dictionary < string , Func < string , Task < string > > >
{
2024-08-27 14:37:47 -07:00
{ nameof ( instance . ConcatString ) , instance . ConcatStringWrapper } ,
{ nameof ( instance . UpperCase ) , instance . UpperCaseWrapper } ,
{ nameof ( instance . CalculateTax ) , instance . CalculateTaxWrapper } ,
} ) ;
var agent = new OpenAIChatAgent (
chatClient : gpt4o ,
name : "agent" ,
systemMessage : "You are a helpful AI assistant" )
. RegisterMessageConnector ( )
. RegisterStreamingMiddleware ( toolCallMiddleware )
2024-04-26 09:21:46 -07:00
. RegisterPrintMessage ( ) ;
// talk to the assistant agent
var upperCase = await agent . SendAsync ( "convert to upper case: hello world" ) ;
upperCase . GetContent ( ) ? . Should ( ) . Be ( "HELLO WORLD" ) ;
2024-05-20 22:48:19 -07:00
upperCase . Should ( ) . BeOfType < ToolCallAggregateMessage > ( ) ;
2024-04-26 09:21:46 -07:00
upperCase . GetToolCalls ( ) . Should ( ) . HaveCount ( 1 ) ;
upperCase . GetToolCalls ( ) . First ( ) . FunctionName . Should ( ) . Be ( nameof ( UpperCase ) ) ;
var concatString = await agent . SendAsync ( "concatenate strings: a, b, c, d, e" ) ;
concatString . GetContent ( ) ? . Should ( ) . Be ( "a b c d e" ) ;
2024-05-20 22:48:19 -07:00
concatString . Should ( ) . BeOfType < ToolCallAggregateMessage > ( ) ;
2024-04-26 09:21:46 -07:00
concatString . GetToolCalls ( ) . Should ( ) . HaveCount ( 1 ) ;
concatString . GetToolCalls ( ) . First ( ) . FunctionName . Should ( ) . Be ( nameof ( ConcatString ) ) ;
2024-11-03 09:18:32 -08:00
var calculateTax = await agent . SendAsync ( "calculate tax: 100, 0.1" ) ;
calculateTax . GetContent ( ) . Should ( ) . Be ( "tax is 10" ) ;
calculateTax . Should ( ) . BeOfType < ToolCallAggregateMessage > ( ) ;
calculateTax . GetToolCalls ( ) . Should ( ) . HaveCount ( 1 ) ;
calculateTax . GetToolCalls ( ) . First ( ) . FunctionName . Should ( ) . Be ( nameof ( CalculateTax ) ) ;
// parallel function calls
var calculateTaxes = await agent . SendAsync ( "calculate tax: 100, 0.1; calculate tax: 200, 0.2" ) ;
calculateTaxes . GetContent ( ) . Should ( ) . Be ( "tax is 10\ntax is 40" ) ; // "tax is 10\n tax is 40
calculateTaxes . Should ( ) . BeOfType < ToolCallAggregateMessage > ( ) ;
calculateTaxes . GetToolCalls ( ) . Should ( ) . HaveCount ( 2 ) ;
calculateTaxes . GetToolCalls ( ) . First ( ) . FunctionName . Should ( ) . Be ( nameof ( CalculateTax ) ) ;
// send aggregate message back to llm to get the final result
var finalResult = await agent . SendAsync ( calculateTaxes ) ;
}
/// <summary>
/// This example shows how to add type-safe function call from M.E.A.I tools.
///
/// For adding type-safe function call from source generator, please refer to <see cref="ToolCallWithSourceGenerator"/>.
/// </summary>
public static async Task ToolCallWithMEAITools ( )
{
var gpt4o = LLMConfiguration . GetOpenAIGPT4o_mini ( ) ;
var instance = new Example03_Agent_FunctionCall ( ) ;
AIFunction [ ] tools = [
AIFunctionFactory . Create ( instance . UpperCase ) ,
AIFunctionFactory . Create ( instance . ConcatString ) ,
AIFunctionFactory . Create ( instance . CalculateTax ) ,
] ;
var toolCallMiddleware = new FunctionCallMiddleware ( tools ) ;
var agent = new OpenAIChatAgent (
chatClient : gpt4o ,
name : "agent" ,
systemMessage : "You are a helpful AI assistant" )
. RegisterMessageConnector ( )
. RegisterStreamingMiddleware ( toolCallMiddleware )
. RegisterPrintMessage ( ) ;
// talk to the assistant agent
var upperCase = await agent . SendAsync ( "convert to upper case: hello world" ) ;
upperCase . GetContent ( ) ? . Should ( ) . Be ( "HELLO WORLD" ) ;
upperCase . Should ( ) . BeOfType < ToolCallAggregateMessage > ( ) ;
upperCase . GetToolCalls ( ) . Should ( ) . HaveCount ( 1 ) ;
upperCase . GetToolCalls ( ) . First ( ) . FunctionName . Should ( ) . Be ( nameof ( UpperCase ) ) ;
var concatString = await agent . SendAsync ( "concatenate strings: a, b, c, d, e" ) ;
concatString . GetContent ( ) ? . Should ( ) . Be ( "a b c d e" ) ;
concatString . Should ( ) . BeOfType < ToolCallAggregateMessage > ( ) ;
concatString . GetToolCalls ( ) . Should ( ) . HaveCount ( 1 ) ;
concatString . GetToolCalls ( ) . First ( ) . FunctionName . Should ( ) . Be ( nameof ( ConcatString ) ) ;
2024-04-26 09:21:46 -07:00
var calculateTax = await agent . SendAsync ( "calculate tax: 100, 0.1" ) ;
calculateTax . GetContent ( ) . Should ( ) . Be ( "tax is 10" ) ;
2024-05-20 22:48:19 -07:00
calculateTax . Should ( ) . BeOfType < ToolCallAggregateMessage > ( ) ;
2024-04-26 09:21:46 -07:00
calculateTax . GetToolCalls ( ) . Should ( ) . HaveCount ( 1 ) ;
calculateTax . GetToolCalls ( ) . First ( ) . FunctionName . Should ( ) . Be ( nameof ( CalculateTax ) ) ;
2024-05-20 20:19:07 -07:00
// parallel function calls
var calculateTaxes = await agent . SendAsync ( "calculate tax: 100, 0.1; calculate tax: 200, 0.2" ) ;
calculateTaxes . GetContent ( ) . Should ( ) . Be ( "tax is 10\ntax is 40" ) ; // "tax is 10\n tax is 40
2024-05-20 22:48:19 -07:00
calculateTaxes . Should ( ) . BeOfType < ToolCallAggregateMessage > ( ) ;
2024-05-20 20:19:07 -07:00
calculateTaxes . GetToolCalls ( ) . Should ( ) . HaveCount ( 2 ) ;
calculateTaxes . GetToolCalls ( ) . First ( ) . FunctionName . Should ( ) . Be ( nameof ( CalculateTax ) ) ;
// send aggregate message back to llm to get the final result
var finalResult = await agent . SendAsync ( calculateTaxes ) ;
2024-04-26 09:21:46 -07:00
}
}