diff --git a/src/System Application/App/AI/src/Azure OpenAI/Operation Response/AOAIOperationResponse.Codeunit.al b/src/System Application/App/AI/src/Azure OpenAI/Operation Response/AOAIOperationResponse.Codeunit.al
index c7ff6637d0..0ed3bc15ff 100644
--- a/src/System Application/App/AI/src/Azure OpenAI/Operation Response/AOAIOperationResponse.Codeunit.al
+++ b/src/System Application/App/AI/src/Azure OpenAI/Operation Response/AOAIOperationResponse.Codeunit.al
@@ -77,6 +77,43 @@ codeunit 7770 "AOAI Operation Response"
exit(AOAIFunctionResponse.IsFunctionCall());
end;
+ ///
+ /// Get whether there are any function responses for a given function name.
+ ///
+ /// The case sensitive function name to search for.
+ /// True if any function responses were found
+ procedure HasFunctionResponsesByName(FunctionName: Text): Boolean
+ var
+ MatchedAOAIFunctionResponses: List of [Codeunit "AOAI Function Response"];
+ begin
+ exit(TryGetFunctionReponsesByName(FunctionName, MatchedAOAIFunctionResponses));
+ end;
+
+ ///
+ /// Get all the function responses for a specified function name.
+ ///
+ /// The case sensitive function name to search for.
+ /// The function responses that match the given function name
+ /// True if any function responses were found
+ procedure TryGetFunctionReponsesByName(FunctionName: Text; var MatchedAOAIFunctionResponses: List of [Codeunit "AOAI Function Response"]): Boolean
+ var
+ AOAIFunctionResponse: Codeunit "AOAI Function Response";
+ begin
+ Clear(MatchedAOAIFunctionResponses);
+
+ if FunctionName = '' then
+ exit(false);
+
+ if not IsFunctionCall() then
+ exit(false);
+
+ foreach AOAIFunctionResponse in AOAIFunctionResponses do
+ if AOAIFunctionResponse.GetFunctionName() = FunctionName then
+ MatchedAOAIFunctionResponses.Add(AOAIFunctionResponse);
+
+ exit(MatchedAOAIFunctionResponses.Count() > 0);
+ end;
+
#if not CLEAN25
///
/// Get the function response codeunit which contains the response details.
diff --git a/src/System Application/Test Library/AI/src/AzureOpenAITestLibrary.Codeunit.al b/src/System Application/Test Library/AI/src/AzureOpenAITestLibrary.Codeunit.al
index 63dc7c9dad..342189321f 100644
--- a/src/System Application/Test Library/AI/src/AzureOpenAITestLibrary.Codeunit.al
+++ b/src/System Application/Test Library/AI/src/AzureOpenAITestLibrary.Codeunit.al
@@ -40,7 +40,8 @@ codeunit 132933 "Azure OpenAI Test Library"
procedure AddAOAIFunctionResponse(var AOAIOperationResponse: Codeunit "AOAI Operation Response"; var AOAIFunctionResponse: Codeunit "AOAI Function Response"; NewIsFunctionCall: Boolean; NewAOAIFunctionResponseStatus: Enum "AOAI Function Response Status"; NewFunctionCalled: Text; NewFunctionId: Text; NewArguments: Text; NewFunctionResult: Variant; NewFunctionError: Text; NewFunctionErrorCallStack: Text)
begin
- AOAIOperationResponse.SetOperationResponse(true, 200, '', '');
+ if AOAIOperationResponse.GetStatusCode() = 0 then
+ AOAIOperationResponse.SetOperationResponse(true, 200, '', '');
SetAOAIFunctionResponse(AOAIFunctionResponse, NewIsFunctionCall, NewAOAIFunctionResponseStatus, NewFunctionCalled, NewFunctionId, NewArguments, NewFunctionResult, NewFunctionError, NewFunctionErrorCallStack);
AOAIOperationResponse.AddFunctionResponse(AOAIFunctionResponse);
end;
diff --git a/src/System Application/Test/AI/src/AzureOpenAIToolsTest.Codeunit.al b/src/System Application/Test/AI/src/AzureOpenAIToolsTest.Codeunit.al
index 72a8567d88..2db4d5b97b 100644
--- a/src/System Application/Test/AI/src/AzureOpenAIToolsTest.Codeunit.al
+++ b/src/System Application/Test/AI/src/AzureOpenAIToolsTest.Codeunit.al
@@ -365,11 +365,14 @@ codeunit 132686 "Azure OpenAI Tools Test"
[Test]
procedure TestToolSelection()
var
+ AzureOpenAITestLibrary: Codeunit "Azure OpenAI Test Library";
AOAIChatMessages: Codeunit "AOAI Chat Messages";
TestFunction1: Codeunit "Test Function 1";
TestFunction2: Codeunit "Test Function 2";
ToolCallId: Text;
- ToolSelectionResponseLbl: Label '[{"id":"%1","type":"function","function":{"name":"%2","arguments":"{}"}}]', Locked = true;
+ ToolCalls: JsonArray;
+ ToolCall: JsonToken;
+ TestProperty: JsonToken;
begin
AOAIChatMessages.AddTool(TestFunction1);
AOAIChatMessages.AddTool(TestFunction2);
@@ -377,11 +380,22 @@ codeunit 132686 "Azure OpenAI Tools Test"
AOAIChatMessages.AddSystemMessage('test system message');
AOAIChatMessages.AddUserMessage('test user message');
- // Function is been selected by LLM
+ // LLM responds with tool calls
ToolCallId := 'call_of7GnOMuBT4H95XkuN14qfai';
- AOAIChatMessages.AddAssistantMessage(StrSubstNo(ToolSelectionResponseLbl, ToolCallId, TestFunction1.GetName()));
+ AzureOpenAITestLibrary.SetToolCalls(AOAIChatMessages, ToolCallId, TestFunction1.GetName());
+
+ ToolCalls := AOAIChatMessages.GetLastToolCalls();
+ LibraryAssert.AreEqual(1, ToolCalls.Count(), 'Tool calls should contain one tool call');
+ LibraryAssert.IsTrue(ToolCalls.Get(0, ToolCall), 'Could not get the tool call');
+
+ LibraryAssert.IsTrue(ToolCall.SelectToken('$.type', TestProperty), 'Could not find type parameter');
+ LibraryAssert.AreEqual('function', TestProperty.AsValue().AsText(), 'Type was not set to function');
- LibraryAssert.AreEqual(AOAIChatMessages.GetLastMessage(), StrSubstNo(ToolSelectionResponseLbl, ToolCallId, TestFunction1.GetName()), 'Last message should be the tool selection response.');
+ LibraryAssert.IsTrue(ToolCall.SelectToken('$.id', TestProperty), 'Could not find id parameter');
+ LibraryAssert.AreEqual(ToolCallId, TestProperty.AsValue().AsText(), 'Tool call id was not set correctly');
+
+ LibraryAssert.IsTrue(ToolCall.SelectToken('$.function.name', TestProperty), 'Could not find function name');
+ LibraryAssert.AreEqual(TestFunction1.GetName(), TestProperty.AsValue().AsText(), 'Function name was not set correctly');
end;
[Test]
@@ -402,7 +416,7 @@ codeunit 132686 "Azure OpenAI Tools Test"
AOAIChatMessages.AddSystemMessage('test system message');
AOAIChatMessages.AddUserMessage('test user message');
- // Function is been selected by LLM
+ // LLM responds with tool calls
ToolCallId := 'call_of7GnOMuBT4H95XkuN14qfai';
AzureOpenAITestLibrary.SetToolCalls(AOAIChatMessages, ToolCallId, TestFunction1.GetName());
@@ -433,7 +447,7 @@ codeunit 132686 "Azure OpenAI Tools Test"
AOAIChatMessages.AddSystemMessage('test system message');
AOAIChatMessages.AddUserMessage('test user message');
- // Function is been selected by LLM
+ // LLM responds with tool calls
ToolCallId := 'call_of7GnOMuBT4H95XkuN14qfai';
AzureOpenAITestLibrary.SetToolCalls(AOAIChatMessages, ToolCallId, TestFunction1.GetName());
@@ -465,7 +479,7 @@ codeunit 132686 "Azure OpenAI Tools Test"
AOAIChatMessages.AddSystemMessage('test system message');
AOAIChatMessages.AddUserMessage('test user message');
- // Function is been selected by LLM
+ // LLM responds with tool calls
ToolCallId := 'call_of7GnOMuBT4H95XkuN14qfai';
AzureOpenAITestLibrary.SetToolCalls(AOAIChatMessages, ToolCallId, TestFunction1.GetName());
@@ -504,7 +518,7 @@ codeunit 132686 "Azure OpenAI Tools Test"
AOAIChatMessages.AddSystemMessage('test system message');
AOAIChatMessages.AddUserMessage('test user message');
- // Function is been selected by LLM
+ // LLM responds with tool calls
ToolCallId := 'call_of7GnOMuBT4H95XkuN14qfai';
AzureOpenAITestLibrary.SetToolCalls(AOAIChatMessages, ToolCallId, TestFunction1.GetName());
@@ -611,7 +625,7 @@ codeunit 132686 "Azure OpenAI Tools Test"
AOAIChatMessages.AddSystemMessage('test system message');
AOAIChatMessages.AddUserMessage('test user message');
- // Function is been selected by LLM
+ // LLM responds with tool calls
ToolCallId := 'call_of7GnOMuBT4H95XkuN14qfai';
AzureOpenAITestLibrary.SetToolCalls(AOAIChatMessages, ToolCallId, TestFunction1.GetName());
@@ -645,7 +659,7 @@ codeunit 132686 "Azure OpenAI Tools Test"
AOAIChatMessages.AddSystemMessage('test system message');
AOAIChatMessages.AddUserMessage('test user message');
- // Function is been selected by LLM
+ // LLM responds with tool calls
ToolCallId := 'call_of7GnOMuBT4H95XkuN14qfai';
AzureOpenAITestLibrary.SetToolCalls(AOAIChatMessages, ToolCallId, TestFunction1.GetName());
@@ -683,7 +697,7 @@ codeunit 132686 "Azure OpenAI Tools Test"
AOAIChatMessages.AddSystemMessage('test system message');
AOAIChatMessages.AddUserMessage('test user message');
- // Function is been selected by LLM
+ // LLM responds with tool calls
ToolCallId := 'call_of7GnOMuBT4H95XkuN14qfai';
AzureOpenAITestLibrary.SetToolCalls(AOAIChatMessages, ToolCallId, TestFunction1.GetName());
@@ -735,7 +749,7 @@ codeunit 132686 "Azure OpenAI Tools Test"
AOAIChatMessages.AddSystemMessage('test system message');
AOAIChatMessages.AddUserMessage('test user message');
- // Function is been selected by LLM
+ // LLM responds with tool calls
ToolCallId := 'call_of7GnOMuBT4H95XkuN14qfai';
AzureOpenAITestLibrary.SetToolCalls(AOAIChatMessages, ToolCallId, TestFunction1.GetName());
@@ -751,6 +765,37 @@ codeunit 132686 "Azure OpenAI Tools Test"
end;
end;
+ [Test]
+ procedure TestTryGetFunctionReponsesByName()
+ var
+ AzureOpenAITestLibrary: Codeunit "Azure OpenAI Test Library";
+ AOAIOperationResponse: Codeunit "AOAI Operation Response";
+ AOAIFunctionResponse: Codeunit "AOAI Function Response";
+ AOAIFunctionResponses: List of [Codeunit "AOAI Function Response"];
+ ToolCallId: Text;
+ FunctionExecutionResult: Text;
+ Counter: Integer;
+ FunctionCount: Integer;
+ begin
+ for FunctionCount := 1 to 5 do begin
+ FunctionExecutionResult := 'test function execution result';
+ for Counter := 1 to FunctionCount do begin
+ Clear(AOAIFunctionResponse);
+ AzureOpenAITestLibrary.AddAOAIFunctionResponse(AOAIOperationResponse, AOAIFunctionResponse, true, Enum::"AOAI Function Response Status"::"Invoke Success", 'TestFunction' + Format(FunctionCount), ToolCallId, '', FunctionExecutionResult, '', '');
+ end;
+ end;
+
+ LibraryAssert.IsFalse(AOAIOperationResponse.HasFunctionResponsesByName(''), 'A function was found for an empty function name');
+ LibraryAssert.IsFalse(AOAIOperationResponse.HasFunctionResponsesByName('TestFunctionThatDoesNotExist'), 'A function was found for a function name that does not exist');
+ for FunctionCount := 1 to 5 do begin
+ LibraryAssert.IsTrue(AOAIOperationResponse.HasFunctionResponsesByName('TestFunction' + Format(FunctionCount)), 'Could not find the expected function');
+ LibraryAssert.IsTrue(AOAIOperationResponse.TryGetFunctionReponsesByName('TestFunction' + Format(FunctionCount), AOAIFunctionResponses), 'Could get the list of expected functions');
+ LibraryAssert.AreEqual(FunctionCount, AOAIFunctionResponses.Count(), 'Incorrect number of function responses returned');
+ foreach AOAIFunctionResponse in AOAIFunctionResponses do
+ LibraryAssert.AreEqual('TestFunction' + Format(FunctionCount), AOAIFunctionResponse.GetFunctionName(), 'Function name did not match');
+ end;
+ end;
+
[Test]
procedure TestFunctionCallInvokedManually()
var