From 07b9330097aefae255b3cb2a2162c40afe81b913 Mon Sep 17 00:00:00 2001 From: lucoiso Date: Tue, 28 Mar 2023 16:41:02 -0300 Subject: [PATCH 1/2] Adjustments to avoid crashes #29 --- Source/HttpGPT/Private/HttpGPTRequest.cpp | 40 ++++++++----- Source/HttpGPT/Public/HttpGPTRequest.h | 27 +++++++-- .../Private/SHttpGPTChatView.cpp | 58 +++++++++++++------ .../HttpGPTEditor/Private/SHttpGPTChatView.h | 19 +++--- 4 files changed, 97 insertions(+), 47 deletions(-) diff --git a/Source/HttpGPT/Private/HttpGPTRequest.cpp b/Source/HttpGPT/Private/HttpGPTRequest.cpp index cfb03a4..f655672 100644 --- a/Source/HttpGPT/Private/HttpGPTRequest.cpp +++ b/Source/HttpGPT/Private/HttpGPTRequest.cpp @@ -55,14 +55,14 @@ void UHttpGPTRequest::StopHttpGPTTask() { FScopeLock Lock(&Mutex); - if (!bIsActive) + if (!bIsTaskActive) { return; } UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Stopping task"), *FString(__func__), GetUniqueID()); - bIsActive = false; + bIsTaskActive = false; if (HttpRequest.IsValid()) { @@ -73,13 +73,6 @@ void UHttpGPTRequest::StopHttpGPTTask() SetReadyToDestroy(); } -const bool UHttpGPTRequest::IsTaskActive() const -{ - FScopeLock Lock(&Mutex); - - return bIsActive; -} - const FHttpGPTOptions UHttpGPTRequest::GetTaskOptions() const { return TaskOptions; @@ -91,7 +84,7 @@ void UHttpGPTRequest::Activate() UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Activating task"), *FString(__func__), GetUniqueID()); - bIsActive = true; + bIsTaskActive = true; if (HttpGPT::Internal::HasEmptyParam(Messages) || HttpGPT::Internal::HasEmptyParam(TaskOptions.APIKey)) { @@ -132,7 +125,7 @@ void UHttpGPTRequest::SetReadyToDestroy() #endif bIsReadyToDestroy = true; - bIsActive = false; + bIsTaskActive = false; Super::SetReadyToDestroy(); } @@ -304,7 +297,7 @@ void UHttpGPTRequest::BindRequestCallbacks() { FScopeTryLock Lock(&Mutex); - if (!Lock.IsLocked() || !IsValid(this) || !bIsActive) + if (!Lock.IsLocked() || !IsValid(this) || !bIsTaskActive) { return; } @@ -319,7 +312,7 @@ void UHttpGPTRequest::BindRequestCallbacks() { FScopeTryLock Lock(&Mutex); - if (!Lock.IsLocked() || !IsValid(this) || !bIsActive) + if (!Lock.IsLocked() || !IsValid(this) || !bIsTaskActive) { return; } @@ -559,3 +552,24 @@ void UHttpGPTRequest::DeserializeSingleResponse(const FString& Content) Response.Usage = FHttpGPTUsage((*UsageObj)->GetNumberField("prompt_tokens"), (*UsageObj)->GetNumberField("completion_tokens"), (*UsageObj)->GetNumberField("total_tokens")); } } + +bool UHttpGPTTaskStatus::IsTaskActive(const UHttpGPTRequest* Test) +{ + return IsValid(Test) && Test->bIsTaskActive; +} + +bool UHttpGPTTaskStatus::IsTaskReadyToDestroy(const UHttpGPTRequest* Test) +{ + return IsValid(Test) && Test->bIsReadyToDestroy; +} + +bool UHttpGPTTaskStatus::IsTaskStillValid(const UHttpGPTRequest* Test) +{ + bool bOutput = IsValid(Test) && !IsTaskReadyToDestroy(Test); + +#if WITH_EDITOR + bOutput = bOutput && !Test->bEndingPIE; +#endif + + return bOutput; +} diff --git a/Source/HttpGPT/Public/HttpGPTRequest.h b/Source/HttpGPT/Public/HttpGPTRequest.h index 9132460..b1c8ecd 100644 --- a/Source/HttpGPT/Public/HttpGPTRequest.h +++ b/Source/HttpGPT/Public/HttpGPTRequest.h @@ -5,8 +5,9 @@ #pragma once #include -#include #include +#include +#include #include "HttpGPTTypes.h" #include "HttpGPTRequest.generated.h" @@ -21,6 +22,8 @@ class HTTPGPT_API UHttpGPTRequest : public UBlueprintAsyncActionBase { GENERATED_BODY() + friend class UHttpGPTTaskStatus; + public: UPROPERTY(BlueprintAssignable, Category = "HttpGPT") FHttpGPTResponseDelegate ProcessCompleted; @@ -55,9 +58,7 @@ class HTTPGPT_API UHttpGPTRequest : public UBlueprintAsyncActionBase UFUNCTION(BlueprintCallable, Category = "HttpGPT", meta = (DisplayName = "Stop HttpGPT Task")) void StopHttpGPTTask(); - const bool IsTaskActive() const; - - UFUNCTION(BlueprintPure, Category = "AzSpeech") + UFUNCTION(BlueprintPure, Category = "HttpGPT") const FHttpGPTOptions GetTaskOptions() const; virtual void Activate() override; @@ -88,7 +89,7 @@ class HTTPGPT_API UHttpGPTRequest : public UBlueprintAsyncActionBase bool bInitialized = false; bool bIsReadyToDestroy = false; - bool bIsActive = false; + bool bIsTaskActive = false; #if WITH_EDITOR virtual void PrePIEEnded(bool bIsSimulating); @@ -96,3 +97,19 @@ class HTTPGPT_API UHttpGPTRequest : public UBlueprintAsyncActionBase bool bEndingPIE = false; #endif }; + +UCLASS(NotPlaceable, Category = "HttpGPT") +class HTTPGPT_API UHttpGPTTaskStatus final : public UBlueprintFunctionLibrary +{ + GENERATED_BODY() + +public: + UFUNCTION(BlueprintPure, Category = "HttpGPT") + static bool IsTaskActive(const UHttpGPTRequest* Test); + + UFUNCTION(BlueprintPure, Category = "HttpGPT") + static bool IsTaskReadyToDestroy(const UHttpGPTRequest* Test); + + UFUNCTION(BlueprintPure, Category = "HttpGPT") + static bool IsTaskStillValid(const UHttpGPTRequest* Test); +}; \ No newline at end of file diff --git a/Source/HttpGPTEditor/Private/SHttpGPTChatView.cpp b/Source/HttpGPTEditor/Private/SHttpGPTChatView.cpp index fa0d745..1820073 100644 --- a/Source/HttpGPTEditor/Private/SHttpGPTChatView.cpp +++ b/Source/HttpGPTEditor/Private/SHttpGPTChatView.cpp @@ -19,12 +19,13 @@ UHttpGPTMessagingHandler::UHttpGPTMessagingHandler(const FObjectInitializer& Obj void UHttpGPTMessagingHandler::RequestSent() { - Message.Content = "Waiting for response..."; + OnMessageContentUpdated.ExecuteIfBound("Waiting for response..."); } void UHttpGPTMessagingHandler::RequestFailed() { - Message.Content = "Request Failed.\nPlease check the logs. (Enable internal logs in Project Settings -> Plugins -> HttpGPT)."; + OnMessageContentUpdated.ExecuteIfBound("Request Failed.\nPlease check the logs. (Enable internal logs in Project Settings -> Plugins -> HttpGPT)."); + Destroy(); } void UHttpGPTMessagingHandler::ProcessUpdated(const FHttpGPTResponse& Response) @@ -35,8 +36,7 @@ void UHttpGPTMessagingHandler::ProcessUpdated(const FHttpGPTResponse& Response) void UHttpGPTMessagingHandler::ProcessCompleted(const FHttpGPTResponse& Response) { ProcessResponse(Response); - - ScrollBoxReference.Reset(); + Destroy(); } void UHttpGPTMessagingHandler::ProcessResponse(const FHttpGPTResponse& Response) @@ -52,11 +52,11 @@ void UHttpGPTMessagingHandler::ProcessResponse(const FHttpGPTResponse& Response) "\tError Message: " + Response.Error.Message }; - Message.Content = FString::Format(TEXT("{0}\n{1}\n\n{2}\n{3}\n{4}\n{5}"), Arguments_ErrorDetails); + OnMessageContentUpdated.ExecuteIfBound(FString::Format(TEXT("{0}\n{1}\n\n{2}\n{3}\n{4}\n{5}"), Arguments_ErrorDetails)); } else if (Response.bSuccess && !HttpGPT::Internal::HasEmptyParam(Response.Choices)) { - Message = Response.Choices[0].Message; + OnMessageContentUpdated.ExecuteIfBound(Response.Choices[0].Message.Content); } else { @@ -69,10 +69,26 @@ void UHttpGPTMessagingHandler::ProcessResponse(const FHttpGPTResponse& Response) } } +void UHttpGPTMessagingHandler::Destroy() +{ +#if ENGINE_MAJOR_VERSION >= 5 + MarkAsGarbage(); +#else + MarkPendingKill(); +#endif +} + void SHttpGPTChatItem::Construct(const FArguments& InArgs) { + Message = FHttpGPTMessage(InArgs._MessageRole, InArgs._InputText); + MessagingHandlerObject = NewObject(); - MessagingHandlerObject->Message = FHttpGPTMessage(InArgs._MessageRole, InArgs._InputText); + MessagingHandlerObject->OnMessageContentUpdated.BindLambda( + [this](FString Content) + { + Message.Content = Content; + } + ); #if ENGINE_MAJOR_VERSION < 5 using FAppStyle = FEditorStyle; @@ -84,7 +100,7 @@ void SHttpGPTChatItem::Construct(const FArguments& InArgs) [ SNew(SVerticalBox) + SVerticalBox::Slot() - .Padding(MessagingHandlerObject->Message.Role == EHttpGPTRole::User ? FMargin(Slot_Padding * 16.f, Slot_Padding, Slot_Padding, Slot_Padding) : FMargin(Slot_Padding, Slot_Padding, Slot_Padding * 16.f, Slot_Padding)) + .Padding(Message.Role == EHttpGPTRole::User ? FMargin(Slot_Padding * 16.f, Slot_Padding, Slot_Padding, Slot_Padding) : FMargin(Slot_Padding, Slot_Padding, Slot_Padding * 16.f, Slot_Padding)) [ SNew(SBorder) .BorderImage(AppStyle.GetBrush("Menu.Background")) @@ -96,7 +112,7 @@ void SHttpGPTChatItem::Construct(const FArguments& InArgs) [ SNew(STextBlock) .Font(FCoreStyle::GetDefaultFontStyle("Bold", 10)) - .Text(FText::FromString(MessagingHandlerObject->Message.Role == EHttpGPTRole::User ? "User:" : "Assistant:")) + .Text(FText::FromString(Message.Role == EHttpGPTRole::User ? "User:" : "Assistant:")) ] + SVerticalBox::Slot() .Padding(FMargin(Slot_Padding * 4, Slot_Padding, Slot_Padding, Slot_Padding)) @@ -113,7 +129,7 @@ void SHttpGPTChatItem::Construct(const FArguments& InArgs) FText SHttpGPTChatItem::GetMessageText() const { - return FText::FromString(MessagingHandlerObject->Message.Content); + return FText::FromString(Message.Content); } void SHttpGPTChatView::Construct([[maybe_unused]] const FArguments&) @@ -203,12 +219,12 @@ FReply SHttpGPTChatView::HandleSendMessageButton() RequestReference = UHttpGPTRequest::SendMessages_CustomOptions(GEditor->GetEditorWorldContext().World(), GetChatHistory(), Options); - RequestReference->ProgressStarted.AddDynamic(AssistantMessage->MessagingHandlerObject, &UHttpGPTMessagingHandler::ProcessUpdated); - RequestReference->ProgressUpdated.AddDynamic(AssistantMessage->MessagingHandlerObject, &UHttpGPTMessagingHandler::ProcessUpdated); - RequestReference->ProcessCompleted.AddDynamic(AssistantMessage->MessagingHandlerObject, &UHttpGPTMessagingHandler::ProcessCompleted); - RequestReference->ErrorReceived.AddDynamic(AssistantMessage->MessagingHandlerObject, &UHttpGPTMessagingHandler::ProcessCompleted); - RequestReference->RequestFailed.AddDynamic(AssistantMessage->MessagingHandlerObject, &UHttpGPTMessagingHandler::RequestFailed); - RequestReference->RequestSent.AddDynamic(AssistantMessage->MessagingHandlerObject, &UHttpGPTMessagingHandler::RequestSent); + RequestReference->ProgressStarted.AddDynamic(AssistantMessage->MessagingHandlerObject.Get(), &UHttpGPTMessagingHandler::ProcessUpdated); + RequestReference->ProgressUpdated.AddDynamic(AssistantMessage->MessagingHandlerObject.Get(), &UHttpGPTMessagingHandler::ProcessUpdated); + RequestReference->ProcessCompleted.AddDynamic(AssistantMessage->MessagingHandlerObject.Get(), &UHttpGPTMessagingHandler::ProcessCompleted); + RequestReference->ErrorReceived.AddDynamic(AssistantMessage->MessagingHandlerObject.Get(), &UHttpGPTMessagingHandler::ProcessCompleted); + RequestReference->RequestFailed.AddDynamic(AssistantMessage->MessagingHandlerObject.Get(), &UHttpGPTMessagingHandler::RequestFailed); + RequestReference->RequestSent.AddDynamic(AssistantMessage->MessagingHandlerObject.Get(), &UHttpGPTMessagingHandler::RequestSent); RequestReference->Activate(); @@ -224,7 +240,7 @@ FReply SHttpGPTChatView::HandleSendMessageButton() bool SHttpGPTChatView::IsSendMessageEnabled() const { - return (!IsValid(RequestReference) || !RequestReference->IsTaskActive()) && !HttpGPT::Internal::HasEmptyParam(InputTextBox->GetText()); + return (!RequestReference.IsValid() || !UHttpGPTTaskStatus::IsTaskActive(RequestReference.Get())) && !HttpGPT::Internal::HasEmptyParam(InputTextBox->GetText()); } FReply SHttpGPTChatView::HandleClearChatButton() @@ -232,10 +248,14 @@ FReply SHttpGPTChatView::HandleClearChatButton() ChatItems.Empty(); ChatBox->ClearChildren(); - if (RequestReference) + if (RequestReference.IsValid()) { RequestReference->StopHttpGPTTask(); } + else + { + RequestReference.Reset(); + } return FReply::Handled(); } @@ -254,7 +274,7 @@ TArray SHttpGPTChatView::GetChatHistory() const for (const auto& Item : ChatItems) { - Output.Add(Item->MessagingHandlerObject->Message); + Output.Add(Item->Message); } return Output; diff --git a/Source/HttpGPTEditor/Private/SHttpGPTChatView.h b/Source/HttpGPTEditor/Private/SHttpGPTChatView.h index 00a40fa..b907f9f 100644 --- a/Source/HttpGPTEditor/Private/SHttpGPTChatView.h +++ b/Source/HttpGPTEditor/Private/SHttpGPTChatView.h @@ -11,6 +11,8 @@ #include #include "SHttpGPTChatView.generated.h" +DECLARE_DELEGATE_OneParam(FMessageContentUpdated, FString); + UCLASS(MinimalAPI, NotBlueprintable, NotPlaceable, Category = "Implementation") class UHttpGPTMessagingHandler : public UObject { @@ -19,6 +21,8 @@ class UHttpGPTMessagingHandler : public UObject public: explicit UHttpGPTMessagingHandler(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); + FMessageContentUpdated OnMessageContentUpdated; + UFUNCTION() void RequestSent(); @@ -31,16 +35,14 @@ class UHttpGPTMessagingHandler : public UObject UFUNCTION() void ProcessCompleted(const FHttpGPTResponse& Response); - FHttpGPTMessage Message; - TSharedPtr ScrollBoxReference; + void Destroy(); + private: void ProcessResponse(const FHttpGPTResponse& Response); }; -typedef UHttpGPTMessagingHandler* UHttpGPTMessagingHandlerPtr; - class SHttpGPTChatItem final : public SCompoundWidget { public: @@ -55,7 +57,8 @@ class SHttpGPTChatItem final : public SCompoundWidget FText GetMessageText() const; - UHttpGPTMessagingHandlerPtr MessagingHandlerObject; + TWeakObjectPtr MessagingHandlerObject; + FHttpGPTMessage Message; private: TSharedPtr MessageBox; @@ -96,9 +99,5 @@ class SHttpGPTChatView final : public SCompoundWidget TArray> AvailableModels; -#if ENGINE_MAJOR_VERSION >= 5 - TObjectPtr RequestReference; -#else - class UHttpGPTRequest* RequestReference; -#endif + TWeakObjectPtr RequestReference; }; From 3a348bc6d2dd3f91dbaeedfb22d048d7e2329622 Mon Sep 17 00:00:00 2001 From: lucoiso Date: Tue, 28 Mar 2023 16:48:06 -0300 Subject: [PATCH 2/2] UE4.27 arguments fix --- Source/HttpGPTEditor/Private/SHttpGPTChatView.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/HttpGPTEditor/Private/SHttpGPTChatView.cpp b/Source/HttpGPTEditor/Private/SHttpGPTChatView.cpp index 1820073..c3977da 100644 --- a/Source/HttpGPTEditor/Private/SHttpGPTChatView.cpp +++ b/Source/HttpGPTEditor/Private/SHttpGPTChatView.cpp @@ -44,12 +44,12 @@ void UHttpGPTMessagingHandler::ProcessResponse(const FHttpGPTResponse& Response) if (!Response.bSuccess) { const FStringFormatOrderedArguments Arguments_ErrorDetails{ - "Request Failed.", - "Please check the logs. (Enable internal logs in Project Settings -> Plugins -> HttpGPT).", - "Error Details: ", - "\tError Code: " + Response.Error.Code.ToString(), - "\tError Type: " + Response.Error.Type.ToString(), - "\tError Message: " + Response.Error.Message + FString("Request Failed."), + FString("Please check the logs. (Enable internal logs in Project Settings -> Plugins -> HttpGPT)."), + FString("Error Details: "), + FString("\tError Code: ") + Response.Error.Code.ToString(), + FString("\tError Type: ") + Response.Error.Type.ToString(), + FString("\tError Message: ") + Response.Error.Message }; OnMessageContentUpdated.ExecuteIfBound(FString::Format(TEXT("{0}\n{1}\n\n{2}\n{3}\n{4}\n{5}"), Arguments_ErrorDetails));