We are minutes from releasing a new version of FetchXML Builder, including a completely new feature:
AI Chat
Now we can interact with AI-robots that have a deep focus on Fetch XML.

FetchXML Builder is just the first tool to integrate AI, and there are more to follow this initial attempt.
Because of the fact that more people want to try it in their tools, we have extracted common features from a code perspective. This is what I will explain below.
Rappen.AI.WinForm
project
The Rappen.AI.WinForm
is a shared project in the repository at https://github.com/rappen/Rappen.XTB.Helper that contains three classes:
ChatMessageHistory
– which keeps track of the conversation and, more importantly, can display all the communication between you and the AI in a WinForm application.ChatMessageLog
– primarily handles the UI for each message and is internal, so you don’t need to be aware of it.AiCommunication
– which handles all calls to and from the AI to make it easier for us developers.
ChatMessageHistory
class
This class can handle all messages, both incoming and outgoing, between you and the AI, and keep track of which AI supplier, model, and API key is needed during communications.
Constructor
In the constructor, pass a panel where the dialog will be displayed, the supplier and the model you are using.
public ChatMessageHistory(Panel parent, string supplier, string model, string apikey, string user = null)
...
// Example
var chatHistory = new ChatMessageHistory(panAiConversation, supplier, model, apikey, myName);
Send to AI
When we want to send questions to the AI, we should make sure we have initiated the dialog:
// Example from FetchXML Builder
if (!chatHistory.Initialized)
{
var intro = PromptSystem.Replace("{fetchxml}", fxb.GetFetchString());
chatHistory.Initialize(intro);
}
After that, we can call the AiCommunitation.
Add in/out
We may send more information to ChatMessageHistory
to give more details to the AI codewise. These messages can also set the flag to use it internally only, without displaying it visually in the panel.
public void Add(ChatRole role, string content, bool hidden)
public void Add(ChatResponse response)
public void Add(ChatResponse response, bool hidden)
Save
There are two methods to save the entire dialog from this session to a text file: either by specifying the folder, which will generate a proper file name, or by keeping to a specific file name.
public string Save(string folder, string tool)
public void Save(string file)
...
// Example call from FetchXML Builder when closing the tool
chatHistory.Save(Paths.LogsPath, "FXB");
// Second example
var sfd = new SaveFileDialog
{
Title = "Select a location to save your dialog",
Filter = "Text file (*.txt)|*.txt",
};
if (sfd.ShowDialog() == DialogResult.OK)
{
chatHistory.Save(sfd.FileName);
}
Static UI color properties
There are eight static color parameters that can be set before constructing the ChatMessageHistory
to tailor to your flavor.
internal static Color AssistansBackgroundColor;
internal static Color AssistansTextColor;
internal static Color UserBackgroundColor;
internal static Color UserTextColor;
internal static Color OtherBackgroundColor;
internal static Color OtherTextColor;
internal static Color BackColor;
internal static Color WaitingBackColor;
Note: These have default values that match my own Swedish yellow/blue preferences. You may prefer other colors; feel free to adjust the parameters!
ChatMessageLog
internal class

This class contains one block in the visual dialog, and also knows the messages since it inherits the ChatMessage
class from the Microsoft.Extensions.AI
library..
In the picture, you can see that one with a blue background is a message from the user, and one with a yellow background is a message from the AI.
AiCommunication
class
This is a static class, meaning it doesn’t require construction.
CallingAI
There are two versions on how to call the AI – one from an XrmToolBox tool that does it async, and the other one where we don’t know of any WorkAsync
method.
public static void CallingAIAsync(PluginControlBase tool, ChatMessageHistory chatMessageHistory, string prompt, Action<ChatResponse> handleResponse, params Func<string, string>[] internalTools)
public static ChatResponse CallingAI(ChatMessageHistory chatMessageHistory, string prompt, params Func<string, string>[] internalTools)
Here is how FXB is calling it
chatHistory.IsRunning = true;
EnableButtons();
AiCommunication.CallingAIAsync(
fxb,
chatHistory,
txtAiChat.Text,
HandlingResponseFromAi,
ExecuteFetchXMLQuery,
UpdateCurrentFetchXmlQuery,
GetMetadataForUnknownEntity,
GetMetadataForUnknownAttribute);
txtAiChat.Clear();
Note that lines 8-11 contain methods that the AI might want to call, referred to as Internal Tools, according to the AI libraries. You may include x amounts, as well as none.
Internal Tool – example
This is one example:
[Description("Executes FetchXML Query")]
private string ExecuteFetchXMLQuery([Description("The FetchXML Query to be Executed. This is the current FetchXML, as specified in the conversation with the assistant.")] string fetchXml)
{
try
{
var result = fxb.RetrieveMultipleSync(fetchXml, null, null);
return "Query executed successfully";
}
catch (Exception ex)
{
return $"Error executing query: {ex.Message}";
}
}
The Descriptions in the first two lines are crucial, as AI uses them to determine if this internal tool can, in this case, execute the fetch XML.
Not just me, myself and I
This development was done in close cooperation with me and Andreas Adner. He knows new stuff, like AI. That’s not me… However, I have some knowledge of how to handle code properly, which should be generally shared, and which should be shared, as well as what is very tool-specific.
If you want to reach out to Andreas, these are the links: LinkedIn, GitHub, Nullpointer.se
Development evolving continues
This is the first version of the official helping code. I can definitely promise updates, improvements, changes (but I try my best not to destroy existing code from me and you) may happen, the AI thing is a vibrant world, so to close out this post, I’d just say: