AI ChatBot
High-level ChatBot component — wraps OpenAI, Anthropic, Gemini or local LLMs in a one-line conversational front-end.
High-level ChatBot component — wraps OpenAI, Anthropic, Gemini or local LLMs in a one-line conversational front-end.
The microphone audio must be captured, so a speech-to-text system is needed to get the text that will be sent to OpenAI.
TsgcAIOpenAIChatBot| Standards & specs | OpenAI Chat Completions · Anthropic Messages |
| Component class | TsgcAIOpenAIChatBot (unit sgcAI_Chat) |
| Frameworks | VCL, FireMonkey, Lazarus / FPC, .NET |
| Platforms | Windows, macOS, Linux, iOS, Android |
The principal published / public properties used to configure and drive the component. Consult the online help for the full list.
OpenAIOptions | OpenAI API credentials and logging (ApiKey, Organization, LogOptions) shared by Whisper, ChatCompletion and Embeddings calls. |
ChatBotOptions | ChatBot behaviour: Transcription (Whisper model/language) and ChatCompletion (model, Enabled flag). |
TextToSpeech | Component used to speak the assistant reply (System, Google or Amazon TTS). |
AudioRecorder | Component used to capture microphone audio that is sent to Whisper for transcription. |
Embeddings | Optional embeddings store used to retrieve context (RAG) before a chat completion. |
Version | Read-only sgcWebSockets library version string. |
The principal public methods exposed by the component.
Start() | Starts microphone recording through the attached AudioRecorder. |
Stop() | Stops recording; the captured audio is then sent to Whisper for transcription. |
ChatAsUser() | Sends a text prompt as the user role and triggers a ChatCompletion reply (spoken via TextToSpeech). |
ChatAsSystem() | Sends a system-role instruction to steer the assistant; speech output is off by default. |
The component exposes the following published events; consult the online help for full event-handler signatures.
OnAudioStart | Fires when the AudioRecorder begins capturing microphone audio. |
OnAudioStop | Fires after microphone capture stops and before transcription is sent to Whisper. |
OnChatCompletion | Fires when the OpenAI ChatCompletion API returns an assistant reply; exposes Role and Content. |
OnTranscription | Fires when Whisper returns the speech-to-text result; lets you edit the text or reject it. |
Drop the component on a form, configure the properties below and activate it. The snippet that follows shows the typical Embeddings | ChatBot configuration sourced from the online help.
procedure AskToChatGPT(const aQuestion: string); var oChatBot: TsgcAIOpenAIChatBot; oEmbeddings: TsgcAIOpenAIEmbeddings; oFile: TsgcAIDatabaseVectorFile; vContext: string; begin oChatBot := TsgcAIOpenAIChatBot.Create(nil); Try oChatBot.OpenAIOptions.ApiKey := '<your api key>'; oEmbeddings := TsgcAIOpenAIEmbeddings.Create(nil); Try oChatBot.Embeddings := oEmbeddings; oFile := TsgcAIDatabaseVectorFile.Create(nil); Try oEmbeddings.Database := oFile; vContext := oChatBot.GetEmbedding(aQuestion); oChatBot.ChatAsUser('Answer the question based on the context below.\n\nContext:\n' + vContext + '\nQuestion:' + aQuestion + '\nAnswer:'); Finally oFile.Free; End; Finally oEmbeddings.Free; End; Finally FreeAndNil(oDialog); End; end;</code> <code class="delphi">
void AskToChatGPT(const std::string& aQuestion) { TsgcAIOpenAIChatBot* oChatBot = new TsgcAIOpenAIChatBot(NULL); try { oChatBot->OpenAIOptions->ApiKey = "<your api key>"; TsgcAIOpenAIEmbeddings* oEmbeddings = new TsgcAIOpenAIEmbeddings(NULL); try { oChatBot->Embeddings = oEmbeddings; TsgcAIDatabaseVectorFile* oFile = new TsgcAIDatabaseVectorFile(NULL); try { oEmbeddings->Database = oFile; std::string vContext = oChatBot->GetEmbedding(aQuestion); std::string message = "Answer the question based on the context below.\n\nContext:\n" + vContext + "\nQuestion:" + aQuestion + "\nAnswer:"; oChatBot->ChatAsUser(message.c_str()); } __finally { delete oFile; } } __finally { delete oEmbeddings; } } __finally { delete oChatBot; } }
public void AskToChatGPT(string aQuestion) { using (TsgcAIOpenAIChatBot oChatBot = new TsgcAIOpenAIChatBot()) { oChatBot.OpenAIOptions.ApiKey = "<your api key>"; using (TsgcAIOpenAIEmbeddings oEmbeddings = new TsgcAIOpenAIEmbeddings()) { oChatBot.Embeddings = oEmbeddings; using (TsgcAIDatabaseVectorFile oFile = new TsgcAIDatabaseVectorFile()) { oEmbeddings.Database = oFile; string vContext = oChatBot.GetEmbedding(aQuestion); string message = "Answer the question based on the context below.\n\nContext:\n" + vContext + "\nQuestion:" + aQuestion + "\nAnswer:"; oChatBot.ChatAsUser(message); } } } }
The following scenarios are lifted verbatim from the online help. Each shows the configuration and method calls needed to drive the component through a specific real-world flow.
To use the embeddings, first we must convert our data to vectors.
procedure ConvertFileToVector; var oDialog: TOpenDialog; oEmbeddings: TsgcAIOpenAIEmbeddings; oFile: TsgcAIDatabaseVectorFile; begin oDialog := TOpenDialog.Create(nil); Try oDialog.Filter := 'TXT Files|*.txt'; if oDialog.Execute then begin oEmbeddings := TsgcAIOpenAIEmbeddings.Create(nil); Try oFile := TsgcAIDatabaseVectorFile.Create(nil); Try oEmbeddings.Database := oFile; oEmbeddings.OpenAIOptions.ApiKey := '<your api key>'; oEmbeddings.CreateEmbeddingsFromFile(oDialog.FileName); Finally oFile.Free; End; Finally oEmbeddings.Free; End; end; Finally FreeAndNil(oDialog); End; end;
void ConvertFileToVector() { TOpenDialog* oDialog = new TOpenDialog(NULL); try { oDialog->Filter = "TXT Files|*.txt"; if (oDialog->Execute()) { TsgcAIOpenAIEmbeddings* oEmbeddings = new TsgcAIOpenAIEmbeddings(NULL); try { TsgcAIDatabaseVectorFile* oFile = new TsgcAIDatabaseVectorFile(NULL); try { oEmbeddings->Database = oFile; oEmbeddings->OpenAIOptions->ApiKey = "<your api key>"; oEmbeddings->CreateEmbeddingsFromFile(oDialog->FileName); } __finally { delete oFile; } } __finally { delete oEmbeddings; } } } __finally { delete oDialog; } }
public void ConvertFileToVector() { using (OpenFileDialog oDialog = new OpenFileDialog()) { oDialog.Filter = "TXT Files|*.txt"; if (oDialog.ShowDialog() == DialogResult.OK) { using (TsgcAIOpenAIEmbeddings oEmbeddings = new TsgcAIOpenAIEmbeddings()) { using (TsgcAIDatabaseVectorFile oFile = new TsgcAIDatabaseVectorFile()) { oEmbeddings.Database = oFile; oEmbeddings.OpenAIOptions.ApiKey = "<your api key>"; oEmbeddings.CreateEmbeddingsFromFile(oDialog.FileName); } } } } }
When creating your assistant, you will first define the functions under the tools param of the assistant.
Assistant := TsgcAIOpenAIAssistant.Create(nil); Assistant.OpenAIOptions.ApiKey := 'sk-askdjfalskdjfl23kjkjasdefasdfj'; Assistant.AssistantOptions.Name := 'Delphi Weather Bot'; Assistant.AssistantOptions.Instructions.Text := 'You are a weather bot. Use the provided functions to answer questions.'; Assistant.AssistantOptions.Model := 'gpt-4o'; Assistant.AssistantOptions.Tools.Functions.Enabled := False; Assistant.AssistantOptions.Tools.Functions.Functions.Text := '[{"type":"function","function":{"name":"get_current_temperature","description":"Get the current temperature for a specific location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g., San Francisco, CA"},"unit":{"type":"string","enum":["Celsius","Fahrenheit"],"description":"The temperature unit to use. Infer this from the user location."}},"required":["location","unit"]}}},{"type":"function","function":{"name":"get_rain_probability","description":"Get the probability of rain for a specific location","parameters":{"type":"object","properties":{"location":{"type":"string","description":"The city and state, e.g., San Francisco, CA"}},"required":["location"]}}}]' Assistant.AssistantOptions.Tools.FileSearch.Enabled := False; Assistant.AssistantOptions.Tools.CodeInterpreter.Enabled := False;
TsgcAIOpenAIAssistant *Assistant = new TsgcAIOpenAIAssistant(nullptr); Assistant->OpenAIOptions->ApiKey = "sk-askdjfalskdjfl23kjkjasdefasdfj"; Assistant->AssistantOptions->Name = "Delphi Weather Bot"; Assistant->AssistantOptions->Instructions->Text = "You are a weather bot. Use the provided functions to answer questions."; Assistant->AssistantOptions->Model = "gpt-4o"; Assistant->AssistantOptions->Tools->Functions->Enabled = false; Assistant->AssistantOptions->Tools->Functions->Functions->Text = "[{\"type\":\"function\",\"function\":{\"name\":\"get_current_temperature\",\"description\":\"Get the current temperature for a specific location\",\"parameters\":{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\",\"description\":\"The city and state, e.g., San Francisco, CA\"},\"unit\":{\"type\":\"string\",\"enum\":[\"Celsius\",\"Fahrenheit\"],\"description\":\"The temperature unit to use. Infer this from the user location.\"}},\"required\":[\"location\",\"unit\"]}}},{\"type\":\"function\",\"function\":{\"name\":\"get_rain_probability\",\"description\":\"Get the probability of rain for a specific location\",\"parameters\":{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\",\"description\":\"The city and state, e.g., San Francisco, CA\"}},\"required\":[\"location\"]}}}]"; Assistant->AssistantOptions->Tools->FileSearch->Enabled = false; Assistant->AssistantOptions->Tools->CodeInterpreter->Enabled = false;
var assistant = new TsgcAIOpenAIAssistant(null); assistant.OpenAIOptions.ApiKey = "sk-askdjfalskdjfl23kjkjasdefasdfj"; assistant.AssistantOptions.Name = "Delphi Weather Bot"; assistant.AssistantOptions.Instructions.Text = "You are a weather bot. Use the provided functions to answer questions."; assistant.AssistantOptions.Model = "gpt-4o"; assistant.AssistantOptions.Tools.Functions.Enabled = false; assistant.AssistantOptions.Tools.Functions.Functions.Text = "[{\"type\":\"function\",\"function\":{\"name\":\"get_current_temperature\",\"description\":\"Get the current temperature for a specific location\",\"parameters\":{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\",\"description\":\"The city and state, e.g., San Francisco, CA\"},\"unit\":{\"type\":\"string\",\"enum\":[\"Celsius\",\"Fahrenheit\"],\"description\":\"The temperature unit to use. Infer this from the user location.\"}},\"required\":[\"location\",\"unit\"]}}},{\"type\":\"function\",\"function\":{\"name\":\"get_rain_probability\",\"description\":\"Get the probability of rain for a specific location\",\"parameters\":{\"type\":\"object\",\"properties\":{\"location\":{\"type\":\"string\",\"description\":\"The city and state, e.g., San Francisco, CA\"}},\"required\":[\"location\"]}}}]"; assistant.AssistantOptions.Tools.FileSearch.Enabled = false; assistant.AssistantOptions.Tools.CodeInterpreter.Enabled = false;
Create a new assistant with file_search enabled in the tools parameter of the Assistant. Once the file_search tool is enabled, the model decides when to retrieve content based on user messages.
Assistant := TsgcAIOpenAIAssistant.Create(nil); Assistant.OpenAIOptions.ApiKey := 'sk-askdjfalskdjfl23kjkjasdefasdfj'; Assistant.AssistantOptions.Name := 'sgcWebSockets HelpDesk'; Assistant.AssistantOptions.Instructions.Text := 'You are a sgcWebSockets HelpDesk Agent. ' + 'Answer questions briefly, in a sentence or less. When asked a question,use the manual to answer the question.' Assistant.AssistantOptions.Model := 'gpt-4o-mini'; Assistant.AssistantOptions.Tools.FileSearch.Enabled := True; Assistant.AssistantOptions.Tools.CodeInterpreter.Enabled := False;
TsgcAIOpenAIAssistant* Assistant = new TsgcAIOpenAIAssistant(nullptr); Assistant->OpenAIOptions->ApiKey = "sk-askdjfalskdjfl23kjkjasdefasdfj"; Assistant->AssistantOptions->Name = "sgcWebSockets HelpDesk"; Assistant->AssistantOptions->Instructions->Text = "You are a sgcWebSockets HelpDesk Agent. " "Answer questions briefly, in a sentence or less. When asked a question, use the manual to answer the question."; Assistant->AssistantOptions->Model = "gpt-4o-mini"; Assistant->AssistantOptions->Tools->FileSearch->Enabled = true; Assistant->AssistantOptions->Tools->CodeInterpreter->Enabled = false;
var Assistant = new TsgcAIOpenAIAssistant(null); Assistant.OpenAIOptions.ApiKey = "sk-askdjfalskdjfl23kjkjasdefasdfj"; Assistant.AssistantOptions.Name = "sgcWebSockets HelpDesk"; Assistant.AssistantOptions.Instructions.Text = "You are a sgcWebSockets HelpDesk Agent. " + "Answer questions briefly, in a sentence or less. When asked a question, use the manual to answer the question."; Assistant.AssistantOptions.Model = "gpt-4o-mini"; Assistant.AssistantOptions.Tools.FileSearch.Enabled = true; Assistant.AssistantOptions.Tools.CodeInterpreter.Enabled = false;
Use the event OnStreamMessageDelta to read the server-sent stream message.
procedure OnStreamMessageDelta(Sender: TObject; const aMessageDelta: TsgcOpenAIClass_MessageDelta; const aRaw: string); var i: Integer; vResponse: string; vType: string; begin for i := Low(aMessageDelta.Content) to High(aMessageDelta.Content) do begin vType := aMessageDelta.Content[i]._Type; if vType = 'text' then begin vResponse := TsgcOpenAIClass_MessageDeltaContent_Text (aMessageDelta.Content[i]).Value; end; end; end;
void __fastcall OnStreamMessageDelta(TObject *Sender, const TsgcOpenAIClass_MessageDelta &aMessageDelta, const String &aRaw) { for (int i = aMessageDelta.Content.Low(); i <= aMessageDelta.Content.High(); i++) { String vType = aMessageDelta.Content[i]->_Type; if (vType == "text") { String vResponse = static_cast<TsgcOpenAIClass_MessageDeltaContent_Text*>(aMessageDelta.Content[i])->Value; } } }
void OnStreamMessageDelta(object sender, TsgcOpenAIClass_MessageDelta aMessageDelta, string aRaw) { foreach (var content in aMessageDelta.Content) { string vType = content._Type; if (vType == "text") { string vResponse = ((TsgcOpenAIClass_MessageDeltaContent_Text)content).Value; } } }
Create a Thread when a user starts a conversation and add Messages to the Thread as the user asks questions.
procedure SendMessage() var i: Integer; oMessage: TsgcOpenAIClass_Message; oMessages: TsgcOpenAIClass_Response_List_Messages; oRun: TsgcOpenAIClass_Run; begin DoLog('[user]: ' + memoMessage.Lines.Text); Screen.Cursor := crHourGlass; Try oMessage := Assistant.CreateMessageText('thread_id', 'What is the weather in San Francisco today and the likelihood it will rain?'); if Assigned(oMessage) then begin oRun := Assistant.CreateRunAndWait('thread_id'); if Assigned(oRun) then begin oMessages := Assistant.GetMessages('thread_id', oRun.Id); if Assigned(oMessages) and (Length(oMessages.Messages) > 0) then begin memoMessage.Lines.Text := ''; for i := 0 to Length(oMessages.Messages) - 1 do DoLog('[assistant]: ' + DoFormatResponse(oMessages.Messages[i] .ContentText + #13#10)); end; end; end; Finally Screen.Cursor := crDefault; End; end;
void SendMessage() { int i; TsgcOpenAIClass_Message* oMessage = nullptr; TsgcOpenAIClass_Response_List_Messages* oMessages = nullptr; TsgcOpenAIClass_Run* oRun = nullptr; DoLog("[user]: " + memoMessage->Lines->Text); Screen->Cursor = crHourGlass; // Change cursor to hourglass try { oMessage = Assistant->CreateMessageText("thread_id", "What is the weather in San Francisco today and the likelihood it will rain?"); if (oMessage != nullptr) { oRun = Assistant->CreateRunAndWait("thread_id"); if (oRun != nullptr) { oMessages = Assistant->GetMessages("thread_id", oRun->Id); if (oMessages != nullptr && oMessages->Messages.Length > 0) { memoMessage->Lines->Clear(); for (i = 0; i < oMessages->Messages.Length; i++) { DoLog("[assistant]: " + DoFormatResponse(oMessages->Messages[i].ContentText + "\r\n")); } } } } } __finally { Screen->Cursor = crDefault; // Reset cursor to default } }
public void SendMessage() { DoLog("[user]: " + memoMessage.Text); Cursor.Current = Cursors.WaitCursor; // Change cursor to hourglass try { var oMessage = Assistant.CreateMessageText("thread_id", "What is the weather in San Francisco today and the likelihood it will rain?"); if (oMessage != null) { var oRun = Assistant.CreateRunAndWait("thread_id"); if (oRun != null) { var oMessages = Assistant.GetMessages("thread_id", oRun.Id); if (oMessages != null && oMessages.Messages.Length > 0) { memoMessage.Text = ""; foreach (var message in oMessages.Messages) { DoLog("[assistant]: " + DoFormatResponse(message.ContentText + Environment.NewLine)); } } } } } finally { Cursor.Current = Cursors.Default; // Reset cursor to default } }
To access your files, the file_search tool uses the Vector Store object. Upload your files and create a Vector Store to contain them.
procedure UploadFile(); var oDialog: TOpenDialog; begin oDialog := TOpenDialog.Create(nil); Try if oDialog.Execute then begin Screen.Cursor := crHourGlass; Try Assistant.UploadVectorStoreFile('sgcVectorStore', oDialog.FileName); Finally Screen.Cursor := crDefault; End; end; Finally oDialog.Free; End; end;
void UploadFile() { TOpenDialog* oDialog = new TOpenDialog(nullptr); try { if (oDialog->Execute()) { Screen->Cursor = crHourGlass; // Change cursor to hourglass try { Assistant->UploadVectorStoreFile("sgcVectorStore", oDialog->FileName.c_str()); } __finally { Screen->Cursor = crDefault; // Reset cursor to default } } } __finally { delete oDialog; // Clean up dialog } }
public void UploadFile() { using (OpenFileDialog oDialog = new OpenFileDialog()) { if (oDialog.ShowDialog() == DialogResult.OK) { Cursor.Current = Cursors.WaitCursor; // Change cursor to hourglass try { Assistant.UploadVectorStoreFile("sgcVectorStore", oDialog.FileName); } finally { Cursor.Current = Cursors.Default; // Reset cursor to default } } } }
Every external claim links back to a primary source. The online-help references decode the canonical deep-link the company maintains for this component.
Demos\15.AI\02.Applications\01.ChatBot
.net\demos\15.AI\02.Applications\01.ChatBot