Advanced asynchronous management in Delphi using promises, enabling non-blocking execution and converting synchronous functions into asynchronous ones. Facilitates efficient interaction with generative AI models while structuring and mastering thought chains. Optimized for intelligent and scalable workflows.
To ensure controlled and orderly execution of a series of asynchronous methods while enabling comprehensive processing of intermediate results without causing any blocking.
When applied to asynchronous processing provided by the following wrappers, this approach enables the structuring of thought trees, thereby providing reasoning capabilities to models that lack them.
Furthermore, it offers the flexibility to control or dynamically adapt a new thought framework, which is not possible with a model that has a predefined reasoning structure.
We use the Promise design pattern, which enables efficient management of a series of asynchronous requests. This pattern offers several key benefits, including:
- Sequential execution of processes in successive steps.
- Centralized handling of intermediate results.
- A non-blocking operation, ensuring smooth workflow execution.
Refer to detail explanations
Here is a diagram illustrating the Promise pattern.
//uses GenAI, GenAI.Types, ASync.Promise;
function CreateChatPromise(const Prompt: string): TPromise<string>;
begin
var Client := TGenAIFactory.CreateInstance(My_Key);
Result := TPromise<string>.Create(
procedure(Resolve: TProc<string>; Reject: TProc<Exception>)
begin
Client.Chat.AsynCreate(
procedure(Params: TChatParams)
begin
Params.Model('gpt-4o');
Params.Messages([
FromUser(Prompt)
]);
end,
function: TAsynChat
begin
Result.OnSuccess :=
procedure(Sender: TObject; Chat: TChat)
begin
Resolve(Chat.Choices[0].Message.Content);
end;
Result.OnError :=
procedure(Sender: TObject; ErrorMessage: string)
begin
Reject(Exception.Create(ErrorMessage));
end;
end);
end);
end;
Note
We use the GenAI for OpenAI wrapper; therefore, we declare the GenAI
and GenAI.Types
units in the uses section.
//uses GenAI, GenAI.Types, ASync.Promise;
LastChoice: string = 'cherry'; //The gpt-4o model does not handle randomness very well, so to avoid repeating the same choice consecutively
var Prompt1 := 'From the array ["apple", "banana", "orange", "tomato", "nut", "tangerine", "pear", "cherry"], pick a random item. Always respond with ONE word. You can''t choice %s';
var Prompt2 := 'Indicate with a short sentence the characteristics of the fruit. : %s.';
var Prompt3 := 'Name another fruit that resembles : %s';
Memo1.Lines.Add('>>>>>> New attempt');
CreateChatPromise(Format(Prompt1, [LastChoice])) //Create the promise
.&Then<string>(
function(Value: string): string
begin
Memo1.Lines.Add('Step 1: ' + Value);
Result := Value;
LastChoice := Value;
end)
.&Then(
function(Value: string): TPromise<string>
begin
{--- We return the new promise directly without nesting the code }
Result := CreateChatPromise(Format(Prompt2, [Value]));
end)
.&Then<string>(
function(Value: string): string
begin
Result := Value;
Memo1.Lines.Add('Step 2: ' + Value);
end)
.&Then(
function(Value: string): TPromise<string>
begin
{--- We return the new promise directly without nesting the code }
Result := CreateChatPromise(Format(Prompt3, [Value]));
end)
.&Then<string>(
function(Value: string): string
begin
Result := Value;
Memo1.Lines.Add('Step 3: ' + Value);
Memo1.Lines.Add(sLineBreak);
end)
.&Catch( //Catch error
procedure(E: Exception)
begin
Memo1.Lines.Add('Erreur : ' + E.Message);
end);
Warning
Execution is asynchronous. It is crucial to ensure that the chained instruction is constructed as a single, continuous statement to avoid introducing intermediate processing steps, which would inherently be synchronous.
Refer to part I
Refer to part II
Pull requests are welcome. If you're planning to make a major change, please open an issue first to discuss your proposed changes.
This project is licensed under the MIT License.