I posted an example using the API here: Recursive Azure OpenAI Function Calling
// *** Define the Functions ***
string fnTodosPOSTDescription = "Creates a new TODO item. ";
fnTodosPOSTDescription += "Use this function to add ";
fnTodosPOSTDescription += "a new TODO item to the list";
string fnTodosGETDescription = "Retrieves the TODO list. ";
fnTodosGETDescription += "Use this function to view the TODO list.";
string fnTodosDELETEDescription = "Deletes a specific TODO item ";
fnTodosDELETEDescription += "from the list. Use this function to ";
fnTodosDELETEDescription += "remove a TODO item from the list.";
var fnTodosPOST = new FunctionDefinition();
fnTodosPOST.Name = "Todos_POST";
fnTodosPOST.Description = fnTodosPOSTDescription;
fnTodosPOST.Parameters = BinaryData.FromObjectAsJson(new JsonObject
{
["type"] = "object",
["properties"] = new JsonObject
{
["TodoRequest"] = new JsonObject
{
["type"] = "object",
["properties"] = new JsonObject
{
["todo"] = new JsonObject
{
["type"] = "string",
["description"] = @"The TODO item to be added."
}
},
["required"] = new JsonArray { "todo" }
}
},
["required"] = new JsonArray { "TodoRequest" }
});
var fnTodosGET = new FunctionDefinition();
fnTodosGET.Name = "Todos_GET";
fnTodosGET.Description = fnTodosGETDescription;
fnTodosGET.Parameters = BinaryData.FromObjectAsJson(new JsonObject
{
["type"] = "object",
["properties"] = new JsonObject { }
});
var fnTodosDELETE = new FunctionDefinition();
fnTodosDELETE.Name = "Todos_DELETE";
fnTodosDELETE.Description = fnTodosDELETEDescription;
fnTodosDELETE.Parameters = BinaryData.FromObjectAsJson(new JsonObject
{
["type"] = "object",
["properties"] = new JsonObject
{
["TodoIndexRequest"] = new JsonObject
{
["type"] = "object",
["properties"] = new JsonObject
{
["todoIdx"] = new JsonObject
{
["type"] = "integer",
["description"] = @"The index of the TODO item to be deleted."
}
},
["required"] = new JsonArray { "todoIdx" }
}
},
["required"] = new JsonArray { "TodoIndexRequest" }
});
// Create a new list of FunctionDefinition objects
List<FunctionDefinition> DefinedFunctions = new List<FunctionDefinition>();
// Add the FunctionDefinition objects to the list
DefinedFunctions.Add(fnTodosPOST);
DefinedFunctions.Add(fnTodosGET);
DefinedFunctions.Add(fnTodosDELETE);
Call Azure OpenAI Service
// Create a new ChatCompletionsOptions object
var chatCompletionsOptions = new ChatCompletionsOptions()
{
Temperature = (float)0.7,
MaxTokens = 2000,
NucleusSamplingFactor = (float)0.95,
FrequencyPenalty = 0,
PresencePenalty = 0,
};
chatCompletionsOptions.Functions = DefinedFunctions;
chatCompletionsOptions.FunctionCall = FunctionDefinition.Auto;
// Add the prompt to the chatCompletionsOptions object
foreach (var message in ChatMessages)
{
chatCompletionsOptions.Messages.Add(message);
}
// Call the GetChatCompletionsAsync method
Response<ChatCompletions> responseWithoutStream =
await client.GetChatCompletionsAsync(
DeploymentOrModelName,
chatCompletionsOptions);
// Get the ChatCompletions object from the response
ChatCompletions result = responseWithoutStream.Value;
// Create a new Message object with the response and other details
// and add it to the messages list
var choice = result.Choices.FirstOrDefault();
if (choice != null)
{
if (choice.Message != null)
{
ChatMessages.Add(choice.Message);
}
}
// Update the total number of tokens used by the API
TotalTokens = TotalTokens + result.Usage.TotalTokens;
See if as a response ChatGPT wants to call a function
if (result.Choices.FirstOrDefault().FinishReason == "function_call")
{
// Chat GPT wants to call a function
// To allow ChatGPT to call multiple functions
// We need to start a While loop
bool FunctionCallingComplete = false;
while (!FunctionCallingComplete)
{
// Call the function
ChatMessages = ExecuteFunction(result, ChatMessages);
// *** Call Azure OpenAI Service ***
// Get a response from ChatGPT
// (now that is has the results of the function)
// Create a new ChatCompletionsOptions object
chatCompletionsOptions = new ChatCompletionsOptions()
{
Temperature = (float)0.7,
MaxTokens = 2000,
NucleusSamplingFactor = (float)0.95,
FrequencyPenalty = 0,
PresencePenalty = 0,
};
chatCompletionsOptions.Functions = DefinedFunctions;
chatCompletionsOptions.FunctionCall = FunctionDefinition.Auto;
// Add the prompt to the chatCompletionsOptions object
foreach (var message in ChatMessages)
{
chatCompletionsOptions.Messages.Add(message);
}
// Call the GetChatCompletionsAsync method
Response<ChatCompletions> responseWithoutStreamFn =
await client.GetChatCompletionsAsync(
DeploymentOrModelName,
chatCompletionsOptions);
// Get the ChatCompletions object from the response
result = responseWithoutStreamFn.Value;
var FunctionResult = result.Choices.FirstOrDefault();
// Create a new Message object with the response and other details
// and add it to the messages list
if (FunctionResult.Message != null)
{
ChatMessages.Add(FunctionResult.Message);
}
if (FunctionResult.FinishReason == "function_call")
{
// Keep looping
FunctionCallingComplete = false;
}
else
{
// Break out of the loop
FunctionCallingComplete = true;
}
}
}
}
catch (Exception ex)
{
// Set ErrorMessage to the exception message if an error occurs
ErrorMessage = ex.Message;
}
finally
{
// Clear the prompt variable
prompt = "";
// Set Processing to false to indicate
// that the method is done processing
Processing = false;
// Call StateHasChanged to refresh the UI
StateHasChanged();
}
Execute a function
private List<ChatMessage> ExecuteFunction(
ChatCompletions ChatCompletionResult, List<ChatMessage> ParamChatPrompts)
{
// Get the arguments
var functionArgs =
ChatCompletionResult.Choices.FirstOrDefault()
.Message.FunctionCall.Arguments.ToString();
// Get the function name
var functionName =
ChatCompletionResult.Choices.FirstOrDefault()
.Message.FunctionCall.Name;
// Variable to hold the function result
string functionResult = "";
// Use select case to call the function
switch (functionName)
{
case "Todos_POST":
var NewTODO =
JsonSerializer.Deserialize<ToDoAddRequest>(functionArgs);
if (NewTODO != null)
{
functionResult = AddTodo(NewTODO.TodoRequest.todo);
}
break;
case "Todos_GET":
functionResult = GetTodos();
break;
case "Todos_DELETE":
var DeleteTODO =
JsonSerializer.Deserialize<ToDoRemoveRequest>(functionArgs);
if (DeleteTODO != null)
{
functionResult =
DeleteTodo(DeleteTODO.TodoIndexRequest.todoIdx);
}
break;
default:
break;
}
// Return with the results of the function
var ChatFunctionMessage = new ChatMessage();
ChatFunctionMessage.Role = ChatRole.Function;
ChatFunctionMessage.Content = functionResult;
ChatFunctionMessage.Name = functionName;
ParamChatPrompts.Add(ChatFunctionMessage);
return ParamChatPrompts;
}