From cf40008a8e8fa004d22d11a9535ecac35a015f42 Mon Sep 17 00:00:00 2001 From: lucoiso Date: Fri, 15 Mar 2024 12:29:19 +0000 Subject: [PATCH] Adjustments --- Config/DefaultHttpGPT.ini | 40 +- HttpGPT.uplugin | 126 ++-- .../HttpGPTChatModule.Build.cs | 39 +- .../Private/HttpGPTChatModule.cpp | 2 +- .../Private/Tasks/HttpGPTChatRequest.cpp | 692 +++++++++--------- .../Public/HttpGPTChatModule.h | 4 +- .../Public/Tasks/HttpGPTChatRequest.h | 94 ++- .../HttpGPTCommonModule.Build.cs | 39 +- .../Private/HttpGPTCommonModule.cpp | 2 +- .../Private/LogHttpGPT.cpp | 2 +- .../Private/Management/HttpGPTSettings.cpp | 102 +-- .../Private/Structures/HttpGPTChatTypes.cpp | 132 ++-- .../Private/Structures/HttpGPTCommonTypes.cpp | 20 +- .../Private/Structures/HttpGPTImageTypes.cpp | 16 +- .../Private/Tasks/HttpGPTBaseTask.cpp | 390 +++++----- .../Private/Utils/HttpGPTHelper.cpp | 422 +++++------ .../Public/HttpGPTCommonModule.h | 4 +- .../Public/HttpGPTInternalFuncs.h | 65 +- .../HttpGPTCommonModule/Public/LogHttpGPT.h | 3 +- .../Public/Management/HttpGPTSettings.h | 82 ++- .../Public/Structures/HttpGPTChatTypes.h | 225 +++--- .../Public/Structures/HttpGPTCommonTypes.h | 45 +- .../Public/Structures/HttpGPTImageTypes.h | 69 +- .../Public/Tasks/HttpGPTBaseTask.h | 84 ++- .../Public/Utils/HttpGPTHelper.h | 55 +- .../HttpGPTEditorModule.Build.cs | 55 +- .../Private/Chat/HttpGPTMessagingHandler.cpp | 74 +- .../Private/Chat/HttpGPTMessagingHandler.h | 30 +- .../Private/Chat/SHttpGPTChatItem.cpp | 153 ++-- .../Private/Chat/SHttpGPTChatItem.h | 35 +- .../Private/Chat/SHttpGPTChatShell.cpp | 479 ++++++------ .../Private/Chat/SHttpGPTChatShell.h | 39 +- .../Private/Chat/SHttpGPTChatView.cpp | 585 +++++++-------- .../Private/Chat/SHttpGPTChatView.h | 59 +- .../Private/HttpGPTEditorModule.cpp | 82 +-- .../Private/ImageGen/HttpGPTImageGetter.cpp | 85 ++- .../Private/ImageGen/HttpGPTImageGetter.h | 38 +- .../Private/ImageGen/SHttpGPTImageGenItem.cpp | 153 ++-- .../Private/ImageGen/SHttpGPTImageGenItem.h | 35 +- .../ImageGen/SHttpGPTImageGenItemData.cpp | 87 ++- .../ImageGen/SHttpGPTImageGenItemData.h | 23 +- .../Private/ImageGen/SHttpGPTImageGenView.cpp | 186 ++--- .../Private/ImageGen/SHttpGPTImageGenView.h | 39 +- .../Public/HttpGPTEditorModule.h | 10 +- .../HttpGPTImageModule.Build.cs | 39 +- .../Private/HttpGPTImageModule.cpp | 2 +- .../Private/Tasks/HttpGPTImageRequest.cpp | 304 ++++---- .../Public/HttpGPTImageModule.h | 4 +- .../Public/Tasks/HttpGPTImageRequest.h | 71 +- 49 files changed, 2659 insertions(+), 2762 deletions(-) diff --git a/Config/DefaultHttpGPT.ini b/Config/DefaultHttpGPT.ini index 465e762..1914744 100644 --- a/Config/DefaultHttpGPT.ini +++ b/Config/DefaultHttpGPT.ini @@ -1,33 +1,33 @@ [CoreRedirects] ;v1.5.0 ; Classes -+ClassRedirects=(OldName="/Script/HttpGPT.HttpGPTRequest", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest") -+ClassRedirects=(OldName="/Script/HttpGPT.HttpGPTHelper", NewName="/Script/HttpGPTCommonModule.HttpGPTHelper") -+ClassRedirects=(OldName="/Script/HttpGPT.HttpGPTSettings", NewName="/Script/HttpGPTCommonModule.HttpGPTSettings") ++ClassRedirects = (OldName="/Script/HttpGPT.HttpGPTRequest", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest") ++ClassRedirects = (OldName="/Script/HttpGPT.HttpGPTHelper", NewName="/Script/HttpGPTCommonModule.HttpGPTHelper") ++ClassRedirects = (OldName="/Script/HttpGPT.HttpGPTSettings", NewName="/Script/HttpGPTCommonModule.HttpGPTSettings") ; Delegates -+ClassRedirects=(OldName="/Script/HttpGPT.HttpGPTResponseDelegate", NewName="/Script/HttpGPTChatModule.HttpGPTChatResponseDelegate") -+ClassRedirects=(OldName="/Script/HttpGPT.HttpGPTGenericDelegate", NewName="/Script/HttpGPTCommonModule.HttpGPTGenericDelegate") ++ClassRedirects = (OldName="/Script/HttpGPT.HttpGPTResponseDelegate", NewName="/Script/HttpGPTChatModule.HttpGPTChatResponseDelegate") ++ClassRedirects = (OldName="/Script/HttpGPT.HttpGPTGenericDelegate", NewName="/Script/HttpGPTCommonModule.HttpGPTGenericDelegate") ; Enumerations -+EnumRedirects=(OldName="/Script/HttpGPT.EHttpGPTRole",NewName="/Script/HttpGPTCommonModule.EHttpGPTChatRole") -+EnumRedirects=(OldName="/Script/HttpGPT.EHttpGPTModel",NewName="/Script/HttpGPTCommonModule.EHttpGPTChatModel") ++EnumRedirects = (OldName="/Script/HttpGPT.EHttpGPTRole",NewName="/Script/HttpGPTCommonModule.EHttpGPTChatRole") ++EnumRedirects = (OldName="/Script/HttpGPT.EHttpGPTModel",NewName="/Script/HttpGPTCommonModule.EHttpGPTChatModel") ; Structures -+StructRedirects=(OldName="/Script/HttpGPT.HttpGPTError",NewName="/Script/HttpGPTCommonModule.HttpGPTCommonError") -+StructRedirects=(OldName="/Script/HttpGPT.HttpGPTResponse",NewName="/Script/HttpGPTCommonModule.HttpGPTChatResponse") -+StructRedirects=(OldName="/Script/HttpGPT.HttpGPTMessage",NewName="/Script/HttpGPTCommonModule.HttpGPTChatMessage") -+StructRedirects=(OldName="/Script/HttpGPT.HttpGPTChoice",NewName="/Script/HttpGPTCommonModule.HttpGPTChatChoice") -+StructRedirects=(OldName="/Script/HttpGPT.HttpGPTUsage",NewName="/Script/HttpGPTCommonModule.HttpGPTChatUsage") -+StructRedirects=(OldName="/Script/HttpGPT.HttpGPTOptions",NewName="/Script/HttpGPTCommonModule.HttpGPTChatOptions") ++StructRedirects = (OldName="/Script/HttpGPT.HttpGPTError",NewName="/Script/HttpGPTCommonModule.HttpGPTCommonError") ++StructRedirects = (OldName="/Script/HttpGPT.HttpGPTResponse",NewName="/Script/HttpGPTCommonModule.HttpGPTChatResponse") ++StructRedirects = (OldName="/Script/HttpGPT.HttpGPTMessage",NewName="/Script/HttpGPTCommonModule.HttpGPTChatMessage") ++StructRedirects = (OldName="/Script/HttpGPT.HttpGPTChoice",NewName="/Script/HttpGPTCommonModule.HttpGPTChatChoice") ++StructRedirects = (OldName="/Script/HttpGPT.HttpGPTUsage",NewName="/Script/HttpGPTCommonModule.HttpGPTChatUsage") ++StructRedirects = (OldName="/Script/HttpGPT.HttpGPTOptions",NewName="/Script/HttpGPTCommonModule.HttpGPTChatOptions") ; Properties -+PropertyRedirects=(OldName="/Script/HttpGPTCommonModule.HttpGPTChatOptions.APIKey", NewName="/Script/HttpGPTCommonModule.HttpGPTCommonOptions.APIKey") -+PropertyRedirects=(OldName="/Script/HttpGPTCommonModule.HttpGPTChatOptions.User", NewName="/Script/HttpGPTCommonModule.HttpGPTCommonOptions.User") -+PropertyRedirects=(OldName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessage_CustomOptions.Options", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessage_CustomOptions.ChatOptions") -+PropertyRedirects=(OldName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessages_CustomOptions.Options", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessages_CustomOptions.ChatOptions") ++PropertyRedirects = (OldName="/Script/HttpGPTCommonModule.HttpGPTChatOptions.APIKey", NewName="/Script/HttpGPTCommonModule.HttpGPTCommonOptions.APIKey") ++PropertyRedirects = (OldName="/Script/HttpGPTCommonModule.HttpGPTChatOptions.User", NewName="/Script/HttpGPTCommonModule.HttpGPTCommonOptions.User") ++PropertyRedirects = (OldName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessage_CustomOptions.Options", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessage_CustomOptions.ChatOptions") ++PropertyRedirects = (OldName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessages_CustomOptions.Options", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessages_CustomOptions.ChatOptions") ; Functions -+FunctionRedirects=(OldName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessage", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessage_DefaultOptions") -+FunctionRedirects=(OldName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessages", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessages_DefaultOptions") -+FunctionRedirects=(OldName="/Script/HttpGPTChatModule.HttpGPTChatRequest.GetTaskOptions", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest.GetChatOptions") \ No newline at end of file ++FunctionRedirects = (OldName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessage", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessage_DefaultOptions") ++FunctionRedirects = (OldName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessages", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest.SendMessages_DefaultOptions") ++FunctionRedirects = (OldName="/Script/HttpGPTChatModule.HttpGPTChatRequest.GetTaskOptions", NewName="/Script/HttpGPTChatModule.HttpGPTChatRequest.GetChatOptions") \ No newline at end of file diff --git a/HttpGPT.uplugin b/HttpGPT.uplugin index 0bbe5dc..c3c75a7 100644 --- a/HttpGPT.uplugin +++ b/HttpGPT.uplugin @@ -1,65 +1,65 @@ { - "FileVersion": 3, - "Version": 21, - "VersionName": "1.5.8", - "FriendlyName": "HttpGPT - GPT Integration", - "Description": "HttpGPT is an Unreal Engine plugin that facilitates integration with OpenAI's GPT based services (ChatGPT and DALL-E) through asynchronous REST requests, making it easy for developers to communicate with these services. HttpGPT also includes new Editor Tools to integrate Chat GPT and DALL-E image generation directly in the Engine.", - "Category": "Game Features", - "CreatedBy": "Lucas Vilas-Boas", - "CreatedByURL": "https://github.com/lucoiso", - "DocsURL": "https://github.com/lucoiso/UEHttpGPT/wiki", - "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/433c180835184aeca0172680a69497ee", - "SupportURL": "https://github.com/lucoiso/UEHttpGPT/issues", - "CanContainContent": false, - "IsBetaVersion": true, - "IsExperimentalVersion": false, - "Installed": false, - "Modules": [ - { - "Name": "HttpGPTChatModule", - "Type": "Runtime", - "LoadingPhase": "Default", - "PlatformAllowList": [ - "Win64", - "Mac", - "Linux", - "IOS", - "Android" - ] - }, - { - "Name": "HttpGPTImageModule", - "Type": "Runtime", - "LoadingPhase": "Default", - "PlatformAllowList": [ - "Win64", - "Mac", - "Linux", - "IOS", - "Android" - ] - }, - { - "Name": "HttpGPTCommonModule", - "Type": "Runtime", - "LoadingPhase": "Default", - "PlatformAllowList": [ - "Win64", - "Mac", - "Linux", - "IOS", - "Android" - ] - }, - { - "Name": "HttpGPTEditorModule", - "Type": "Editor", - "PlatformAllowList": [ - "Win64", - "Mac", - "Linux" - ], - "LoadingPhase": "Default" - } - ] + "FileVersion": 3, + "Version": 22, + "VersionName": "1.5.9", + "FriendlyName": "HttpGPT - GPT Integration", + "Description": "HttpGPT is an Unreal Engine plugin that facilitates integration with OpenAI's GPT based services (ChatGPT and DALL-E) through asynchronous REST requests, making it easy for developers to communicate with these services. HttpGPT also includes new Editor Tools to integrate Chat GPT and DALL-E image generation directly in the Engine.", + "Category": "Game Features", + "CreatedBy": "Lucas Vilas-Boas", + "CreatedByURL": "https://github.com/lucoiso", + "DocsURL": "https://github.com/lucoiso/UEHttpGPT/wiki", + "MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/433c180835184aeca0172680a69497ee", + "SupportURL": "https://github.com/lucoiso/UEHttpGPT/issues", + "CanContainContent": false, + "IsBetaVersion": true, + "IsExperimentalVersion": false, + "Installed": false, + "Modules": [ + { + "Name": "HttpGPTChatModule", + "Type": "Runtime", + "LoadingPhase": "Default", + "PlatformAllowList": [ + "Win64", + "Mac", + "Linux", + "IOS", + "Android" + ] + }, + { + "Name": "HttpGPTImageModule", + "Type": "Runtime", + "LoadingPhase": "Default", + "PlatformAllowList": [ + "Win64", + "Mac", + "Linux", + "IOS", + "Android" + ] + }, + { + "Name": "HttpGPTCommonModule", + "Type": "Runtime", + "LoadingPhase": "Default", + "PlatformAllowList": [ + "Win64", + "Mac", + "Linux", + "IOS", + "Android" + ] + }, + { + "Name": "HttpGPTEditorModule", + "Type": "Editor", + "PlatformAllowList": [ + "Win64", + "Mac", + "Linux" + ], + "LoadingPhase": "Default" + } + ] } diff --git a/Source/HttpGPTChatModule/HttpGPTChatModule.Build.cs b/Source/HttpGPTChatModule/HttpGPTChatModule.Build.cs index 2831337..af1ab94 100644 --- a/Source/HttpGPTChatModule/HttpGPTChatModule.Build.cs +++ b/Source/HttpGPTChatModule/HttpGPTChatModule.Build.cs @@ -6,28 +6,25 @@ public class HttpGPTChatModule : ModuleRules { - public HttpGPTChatModule(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - CppStandard = CppStandardVersion.Cpp17; + public HttpGPTChatModule(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + CppStandard = CppStandardVersion.Cpp17; - PublicDependencyModuleNames.AddRange(new[] - { - "Core", - "HTTP", - "Json", - "HttpGPTCommonModule" - }); + PublicDependencyModuleNames.AddRange(new[] + { + "Core", + "HTTP", + "Json", + "HttpGPTCommonModule" + }); - PrivateDependencyModuleNames.AddRange(new[] - { - "Engine", - "CoreUObject" - }); + PrivateDependencyModuleNames.AddRange(new[] + { + "Engine", + "CoreUObject" + }); - if (Target.bBuildEditor) - { - PrivateDependencyModuleNames.Add("UnrealEd"); - } - } + if (Target.bBuildEditor) PrivateDependencyModuleNames.Add("UnrealEd"); + } } \ No newline at end of file diff --git a/Source/HttpGPTChatModule/Private/HttpGPTChatModule.cpp b/Source/HttpGPTChatModule/Private/HttpGPTChatModule.cpp index 15cfdc9..69fe1cf 100644 --- a/Source/HttpGPTChatModule/Private/HttpGPTChatModule.cpp +++ b/Source/HttpGPTChatModule/Private/HttpGPTChatModule.cpp @@ -16,4 +16,4 @@ void FHttpGPTChatModule::ShutdownModule() #undef LOCTEXT_NAMESPACE -IMPLEMENT_MODULE(FHttpGPTChatModule, HttpGPTChatModule) \ No newline at end of file +IMPLEMENT_MODULE(FHttpGPTChatModule, HttpGPTChatModule) diff --git a/Source/HttpGPTChatModule/Private/Tasks/HttpGPTChatRequest.cpp b/Source/HttpGPTChatModule/Private/Tasks/HttpGPTChatRequest.cpp index 79b6eb1..87b811b 100644 --- a/Source/HttpGPTChatModule/Private/Tasks/HttpGPTChatRequest.cpp +++ b/Source/HttpGPTChatModule/Private/Tasks/HttpGPTChatRequest.cpp @@ -27,412 +27,416 @@ #endif #if WITH_EDITOR -UHttpGPTChatRequest* UHttpGPTChatRequest::EditorTask(const TArray& Messages, const FHttpGPTChatOptions Options) +UHttpGPTChatRequest* UHttpGPTChatRequest::EditorTask(const TArray& Messages, const FHttpGPTChatOptions& Options) { - UHttpGPTChatRequest* const NewAsyncTask = SendMessages_CustomOptions(GEditor->GetEditorWorldContext().World(), Messages, TArray(), FHttpGPTCommonOptions(), Options); - NewAsyncTask->bIsEditorTask = true; + UHttpGPTChatRequest* const NewAsyncTask = SendMessages_CustomOptions(GEditor->GetEditorWorldContext().World(), Messages, + TArray(), FHttpGPTCommonOptions(), Options); + NewAsyncTask->bIsEditorTask = true; - return NewAsyncTask; + return NewAsyncTask; } #endif -UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessage_DefaultOptions(UObject* const WorldContextObject, const FString& Message, const TArray& Functions) +UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessage_DefaultOptions(UObject* const WorldContextObject, const FString& Message, + const TArray& Functions) { - return SendMessage_CustomOptions(WorldContextObject, Message, Functions, FHttpGPTCommonOptions(), FHttpGPTChatOptions()); + return SendMessage_CustomOptions(WorldContextObject, Message, Functions, FHttpGPTCommonOptions(), FHttpGPTChatOptions()); } -UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessages_DefaultOptions(UObject* const WorldContextObject, const TArray& Messages, const TArray& Functions) +UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessages_DefaultOptions(UObject* const WorldContextObject, const TArray& Messages, + const TArray& Functions) { - return SendMessages_CustomOptions(WorldContextObject, Messages, Functions, FHttpGPTCommonOptions(), FHttpGPTChatOptions()); + return SendMessages_CustomOptions(WorldContextObject, Messages, Functions, FHttpGPTCommonOptions(), FHttpGPTChatOptions()); } -UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessage_CustomOptions(UObject* const WorldContextObject, const FString& Message, const TArray& Functions, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions) +UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessage_CustomOptions(UObject* const WorldContextObject, const FString& Message, + const TArray& Functions, + const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions) { - return SendMessages_CustomOptions(WorldContextObject, { FHttpGPTChatMessage(EHttpGPTChatRole::User, Message) }, Functions, CommonOptions, ChatOptions); + return SendMessages_CustomOptions(WorldContextObject, {FHttpGPTChatMessage(EHttpGPTChatRole::User, Message)}, Functions, CommonOptions, + ChatOptions); } -UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessages_CustomOptions(UObject* const WorldContextObject, const TArray& Messages, const TArray& Functions, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions) +UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessages_CustomOptions(UObject* const WorldContextObject, const TArray& Messages, + const TArray& Functions, + const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions) { - UHttpGPTChatRequest* const NewAsyncTask = NewObject(); - NewAsyncTask->Messages = Messages; - NewAsyncTask->CommonOptions = CommonOptions; - NewAsyncTask->ChatOptions = ChatOptions; - NewAsyncTask->Functions = Functions; + UHttpGPTChatRequest* const NewAsyncTask = NewObject(); + NewAsyncTask->Messages = Messages; + NewAsyncTask->CommonOptions = CommonOptions; + NewAsyncTask->ChatOptions = ChatOptions; + NewAsyncTask->Functions = Functions; - NewAsyncTask->RegisterWithGameInstance(WorldContextObject); + NewAsyncTask->RegisterWithGameInstance(WorldContextObject); - return NewAsyncTask; + return NewAsyncTask; } bool UHttpGPTChatRequest::CanActivateTask() const { - if (!Super::CanActivateTask()) - { - return false; - } - - if (HttpGPT::Internal::HasEmptyParam(Messages)) - { - UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Can't activate task: Invalid Messages."), *FString(__func__), GetUniqueID()); - return false; - } - - return true; + if (!Super::CanActivateTask()) + { + return false; + } + + if (HttpGPT::Internal::HasEmptyParam(Messages)) + { + UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Can't activate task: Invalid Messages."), *FString(__func__), GetUniqueID()); + return false; + } + + return true; } bool UHttpGPTChatRequest::CanBindProgress() const { - return GetChatOptions().bStream; + return GetChatOptions().bStream; } FString UHttpGPTChatRequest::GetEndpointURL() const { - return FString::Format(TEXT("{0}/{1}"), { GetCommonOptions().Endpoint, UHttpGPTHelper::GetEndpointForModel(GetChatOptions().Model, GetCommonOptions().bIsAzureOpenAI, GetCommonOptions().AzureOpenAIAPIVersion) }); + return FString::Format(TEXT("{0}/{1}"), { + GetCommonOptions().Endpoint, + UHttpGPTHelper::GetEndpointForModel(GetChatOptions().Model, GetCommonOptions().bIsAzureOpenAI, + GetCommonOptions().AzureOpenAIAPIVersion) + }); } const FHttpGPTChatOptions UHttpGPTChatRequest::GetChatOptions() const { - return ChatOptions; + return ChatOptions; } FString UHttpGPTChatRequest::SetRequestContent() { - FScopeLock Lock(&Mutex); - - if (!HttpRequest.IsValid()) - { - return FString(); - } - - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Mounting content"), *FString(__func__), GetUniqueID()); - - const TSharedPtr JsonRequest = MakeShared(); - JsonRequest->SetStringField("model", UHttpGPTHelper::ModelToName(GetChatOptions().Model).ToString().ToLower()); - JsonRequest->SetNumberField("max_tokens", GetChatOptions().MaxTokens); - JsonRequest->SetNumberField("temperature", GetChatOptions().Temperature); - JsonRequest->SetNumberField("top_p", GetChatOptions().TopP); - JsonRequest->SetNumberField("n", GetChatOptions().Choices); - JsonRequest->SetNumberField("presence_penalty", GetChatOptions().PresencePenalty); - JsonRequest->SetNumberField("frequency_penalty", GetChatOptions().FrequencyPenalty); - JsonRequest->SetBoolField("stream", GetChatOptions().bStream); - - if (!HttpGPT::Internal::HasEmptyParam(GetCommonOptions().User)) - { - JsonRequest->SetStringField("user", GetCommonOptions().User.ToString()); - } - - if (!HttpGPT::Internal::HasEmptyParam(GetChatOptions().Stop)) - { - TArray> StopJson; - for (const FName& Iterator : GetChatOptions().Stop) - { - StopJson.Add(MakeShared(Iterator.ToString())); - } - - JsonRequest->SetArrayField("stop", StopJson); - } - - if (!HttpGPT::Internal::HasEmptyParam(GetChatOptions().LogitBias)) - { - TSharedPtr LogitBiasJson = MakeShared(); - for (const TPair& Iterator : GetChatOptions().LogitBias) - { - LogitBiasJson->SetNumberField(FString::FromInt(Iterator.Key), Iterator.Value); - } - - JsonRequest->SetObjectField("logit_bias", LogitBiasJson); - } - - if (UHttpGPTHelper::ModelSupportsChat(GetChatOptions().Model)) - { - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Selected model supports Chat API. Mounting section history."), *FString(__func__), GetUniqueID()); - - TArray> MessagesJson; - for (const FHttpGPTChatMessage& Iterator : Messages) - { - MessagesJson.Add(Iterator.GetMessage()); - } - - JsonRequest->SetArrayField("messages", MessagesJson); - - if (!Functions.IsEmpty()) - { - TArray> FunctionsJson; - for (const FHttpGPTFunction& Iterator : Functions) - { - FunctionsJson.Add(Iterator.GetFunction()); - } - - JsonRequest->SetArrayField("functions", FunctionsJson); - } - } - else - { - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Selected model does not supports Chat API. Using last message as prompt content."), *FString(__func__), GetUniqueID()); - JsonRequest->SetStringField("prompt", Messages.Top().Content); - } - - FString RequestContentString; - const TSharedRef> Writer = TJsonWriterFactory<>::Create(&RequestContentString); - FJsonSerializer::Serialize(JsonRequest.ToSharedRef(), Writer); - - HttpRequest->SetContentAsString(RequestContentString); - - return RequestContentString; + FScopeLock Lock(&Mutex); + + if (!HttpRequest.IsValid()) + { + return FString(); + } + + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Mounting content"), *FString(__func__), GetUniqueID()); + + const TSharedPtr JsonRequest = MakeShared(); + JsonRequest->SetStringField("model", UHttpGPTHelper::ModelToName(GetChatOptions().Model).ToString().ToLower()); + JsonRequest->SetNumberField("max_tokens", GetChatOptions().MaxTokens); + JsonRequest->SetNumberField("temperature", GetChatOptions().Temperature); + JsonRequest->SetNumberField("top_p", GetChatOptions().TopP); + JsonRequest->SetNumberField("n", GetChatOptions().Choices); + JsonRequest->SetNumberField("presence_penalty", GetChatOptions().PresencePenalty); + JsonRequest->SetNumberField("frequency_penalty", GetChatOptions().FrequencyPenalty); + JsonRequest->SetBoolField("stream", GetChatOptions().bStream); + + if (!HttpGPT::Internal::HasEmptyParam(GetCommonOptions().User)) + { + JsonRequest->SetStringField("user", GetCommonOptions().User.ToString()); + } + + if (!HttpGPT::Internal::HasEmptyParam(GetChatOptions().Stop)) + { + TArray> StopJson; + for (const FName& Iterator : GetChatOptions().Stop) + { + StopJson.Add(MakeShared(Iterator.ToString())); + } + + JsonRequest->SetArrayField("stop", StopJson); + } + + if (!HttpGPT::Internal::HasEmptyParam(GetChatOptions().LogitBias)) + { + const TSharedPtr LogitBiasJson = MakeShared(); + for (const TPair& Iterator : GetChatOptions().LogitBias) + { + LogitBiasJson->SetNumberField(FString::FromInt(Iterator.Key), Iterator.Value); + } + + JsonRequest->SetObjectField("logit_bias", LogitBiasJson); + } + + if (UHttpGPTHelper::ModelSupportsChat(GetChatOptions().Model)) + { + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Selected model supports Chat API. Mounting section history."), *FString(__func__), + GetUniqueID()); + + TArray> MessagesJson; + for (const FHttpGPTChatMessage& Iterator : Messages) + { + MessagesJson.Add(Iterator.GetMessage()); + } + + JsonRequest->SetArrayField("messages", MessagesJson); + + if (!Functions.IsEmpty()) + { + TArray> FunctionsJson; + for (const FHttpGPTFunction& Iterator : Functions) + { + FunctionsJson.Add(Iterator.GetFunction()); + } + + JsonRequest->SetArrayField("functions", FunctionsJson); + } + } + else + { + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Selected model does not supports Chat API. Using last message as prompt content."), + *FString(__func__), GetUniqueID()); + JsonRequest->SetStringField("prompt", Messages.Top().Content); + } + + FString RequestContentString; + const TSharedRef> Writer = TJsonWriterFactory<>::Create(&RequestContentString); + FJsonSerializer::Serialize(JsonRequest.ToSharedRef(), Writer); + + HttpRequest->SetContentAsString(RequestContentString); + + return RequestContentString; } void UHttpGPTChatRequest::OnProgressUpdated(const FString& Content, int32 BytesSent, int32 BytesReceived) { - FScopeLock Lock(&Mutex); - - if (HttpGPT::Internal::HasEmptyParam(Content)) - { - return; - } - - TArray Deltas = GetDeltasFromContent(Content); - - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Progress Updated"), *FString(__func__), GetUniqueID()); - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Content: %s; Bytes Sent: %d; Bytes Received: %d"), *FString(__func__), GetUniqueID(), *Deltas.Top(), BytesSent, BytesReceived); - - DeserializeStreamedResponse(Deltas); - - if (!Response.bSuccess) - { - return; - } - - if (!bInitialized) - { - bInitialized = true; - - AsyncTask(ENamedThreads::GameThread, - [this] - { - FScopeLock Lock(&Mutex); - ProgressStarted.Broadcast(Response); - } - ); - } - - AsyncTask(ENamedThreads::GameThread, - [this] - { - FScopeTryLock Lock(&Mutex); - - if (Lock.IsLocked()) - { - ProgressUpdated.Broadcast(Response); - } - } - ); + FScopeLock Lock(&Mutex); + + if (HttpGPT::Internal::HasEmptyParam(Content)) + { + return; + } + + TArray Deltas = GetDeltasFromContent(Content); + + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Progress Updated"), *FString(__func__), GetUniqueID()); + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Content: %s; Bytes Sent: %d; Bytes Received: %d"), *FString(__func__), GetUniqueID(), + *Deltas.Top(), BytesSent, BytesReceived); + + DeserializeStreamedResponse(Deltas); + + if (!Response.bSuccess) + { + return; + } + + if (!bInitialized) + { + bInitialized = true; + + AsyncTask(ENamedThreads::GameThread, [this] + { + FScopeLock Lock(&Mutex); + ProgressStarted.Broadcast(Response); + }); + } + + AsyncTask(ENamedThreads::GameThread, [this] + { + const FScopeTryLock Lock(&Mutex); + + if (Lock.IsLocked()) + { + ProgressUpdated.Broadcast(Response); + } + }); } void UHttpGPTChatRequest::OnProgressCompleted(const FString& Content, const bool bWasSuccessful) { - FScopeLock Lock(&Mutex); - - if (!bWasSuccessful || HttpGPT::Internal::HasEmptyParam(Content)) - { - UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Request failed"), *FString(__func__), GetUniqueID()); - AsyncTask(ENamedThreads::GameThread, - [this] - { - RequestFailed.Broadcast(); - } - ); - - return; - } - - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Process Completed"), *FString(__func__), GetUniqueID()); - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Content: %s"), *FString(__func__), GetUniqueID(), *Content); - - if (!GetChatOptions().bStream) - { - DeserializeSingleResponse(Content); - } - else - { - TArray Deltas = GetDeltasFromContent(Content); - DeserializeStreamedResponse(Deltas); - } - - if (Response.bSuccess) - { - AsyncTask(ENamedThreads::GameThread, - [this] - { - FScopeLock Lock(&Mutex); - - if (!GetChatOptions().bStream) - { - ProgressStarted.Broadcast(Response); - } - - ProcessCompleted.Broadcast(Response); - } - ); - } - else - { - UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Request failed"), *FString(__func__), GetUniqueID()); - AsyncTask(ENamedThreads::GameThread, - [this] - { - FScopeLock Lock(&Mutex); - ErrorReceived.Broadcast(Response); - } - ); - } + FScopeLock Lock(&Mutex); + + if (!bWasSuccessful || HttpGPT::Internal::HasEmptyParam(Content)) + { + UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Request failed"), *FString(__func__), GetUniqueID()); + AsyncTask(ENamedThreads::GameThread, [this] + { + RequestFailed.Broadcast(); + }); + + return; + } + + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Process Completed"), *FString(__func__), GetUniqueID()); + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Content: %s"), *FString(__func__), GetUniqueID(), *Content); + + if (!GetChatOptions().bStream) + { + DeserializeSingleResponse(Content); + } + else + { + const TArray Deltas = GetDeltasFromContent(Content); + DeserializeStreamedResponse(Deltas); + } + + if (Response.bSuccess) + { + AsyncTask(ENamedThreads::GameThread, [this] + { + FScopeLock Lock(&Mutex); + + if (!GetChatOptions().bStream) + { + ProgressStarted.Broadcast(Response); + } + + ProcessCompleted.Broadcast(Response); + }); + } + else + { + UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Request failed"), *FString(__func__), GetUniqueID()); + AsyncTask(ENamedThreads::GameThread, [this] + { + FScopeLock Lock(&Mutex); + ErrorReceived.Broadcast(Response); + }); + } } TArray UHttpGPTChatRequest::GetDeltasFromContent(const FString& Content) const { - TArray Deltas; - Content.ParseIntoArray(Deltas, TEXT("data: ")); + TArray Deltas; + Content.ParseIntoArray(Deltas, TEXT("data: ")); - if (Deltas.Top().Contains("[done]", ESearchCase::IgnoreCase)) - { - Deltas.Pop(); - } + if (Deltas.Top().Contains("[done]", ESearchCase::IgnoreCase)) + { + Deltas.Pop(); + } - if (HttpGPT::Internal::HasEmptyParam(Deltas)) - { - Deltas.Add(Content); - } + if (HttpGPT::Internal::HasEmptyParam(Deltas)) + { + Deltas.Add(Content); + } - return Deltas; + return Deltas; } void UHttpGPTChatRequest::DeserializeStreamedResponse(const TArray& Deltas) { - FScopeLock Lock(&Mutex); + FScopeLock Lock(&Mutex); - Response.Choices.Empty(Deltas.Num()); - for (const FString& Delta : Deltas) - { - DeserializeSingleResponse(Delta); - } + Response.Choices.Empty(Deltas.Num()); + for (const FString& Delta : Deltas) + { + DeserializeSingleResponse(Delta); + } } void UHttpGPTChatRequest::DeserializeSingleResponse(const FString& Content) { - FScopeLock Lock(&Mutex); - - if (HttpGPT::Internal::HasEmptyParam(Content)) - { - return; - } - - const TSharedRef> Reader = TJsonReaderFactory<>::Create(Content); - TSharedPtr JsonResponse = MakeShared(); - FJsonSerializer::Deserialize(Reader, JsonResponse); - - if (CheckError(JsonResponse, Response.Error)) - { - Response.bSuccess = false; - return; - } - - Response.bSuccess = true; - - Response.ID = *JsonResponse->GetStringField("id"); - Response.Object = *JsonResponse->GetStringField("object"); - Response.Created = JsonResponse->GetNumberField("created"); - - const TArray> ChoicesArr = JsonResponse->GetArrayField("choices"); - - for (auto Iterator = ChoicesArr.CreateConstIterator(); Iterator; ++Iterator) - { - const TSharedPtr ChoiceObj = (*Iterator)->AsObject(); - const int32 ChoiceIndex = ChoiceObj->GetIntegerField("index"); - - FHttpGPTChatChoice* Choice = Response.Choices.FindByPredicate( - [this, ChoiceIndex](const FHttpGPTChatChoice& Element) - { - return Element.Index == ChoiceIndex; - } - ); - - if (!Choice) - { - FHttpGPTChatChoice NewChoice; - NewChoice.Index = ChoiceIndex; - Choice = &Response.Choices.Add_GetRef(NewChoice); - } - - if (const TSharedPtr* MessageObj; ChoiceObj->TryGetObjectField("message", MessageObj)) - { - if (FString RoleStr; (*MessageObj)->TryGetStringField("role", RoleStr)) - { - Choice->Message.Role = RoleStr == "user" ? EHttpGPTChatRole::User : EHttpGPTChatRole::Assistant; - } - - if (FString ContentStr; (*MessageObj)->TryGetStringField("content", ContentStr)) - { - Choice->Message.Content = ContentStr; - } - - if (const TSharedPtr* FunctionObj; (*MessageObj)->TryGetObjectField("function_call", FunctionObj)) - { - if (FString FunctionNameStr; (*FunctionObj)->TryGetStringField("name", FunctionNameStr)) - { - Choice->Message.FunctionCall.Name = *FunctionNameStr; - } - if (FString FunctionArgumentsStr; (*FunctionObj)->TryGetStringField("arguments", FunctionArgumentsStr)) - { - Choice->Message.FunctionCall.Arguments = FunctionArgumentsStr; - } - } - } - else if (const TSharedPtr* DeltaObj; ChoiceObj->TryGetObjectField("delta", DeltaObj)) - { - if (FString RoleStr; (*DeltaObj)->TryGetStringField("role", RoleStr)) - { - Choice->Message.Role = UHttpGPTHelper::NameToRole(*RoleStr); - } - else if (FString ContentStr; (*DeltaObj)->TryGetStringField("content", ContentStr)) - { - Choice->Message.Content += ContentStr; - } - - if (const TSharedPtr* FunctionObj; (*DeltaObj)->TryGetObjectField("function_call", FunctionObj)) - { - if (FString FunctionNameStr; (*FunctionObj)->TryGetStringField("name", FunctionNameStr)) - { - Choice->Message.FunctionCall.Name = *FunctionNameStr; - } - if (FString FunctionArgumentsStr; (*FunctionObj)->TryGetStringField("arguments", FunctionArgumentsStr)) - { - Choice->Message.FunctionCall.Arguments += FunctionArgumentsStr; - } - } - } - else if (FString MessageText; ChoiceObj->TryGetStringField("text", MessageText)) - { - Choice->Message.Role = EHttpGPTChatRole::Assistant; - Choice->Message.Content += MessageText; - } - - while (Choice->Message.Content.StartsWith("\n")) - { - Choice->Message.Content.RemoveAt(0); - } - - if (FString FinishReasonStr; ChoiceObj->TryGetStringField("finish_reason", FinishReasonStr)) - { - Choice->FinishReason = *FinishReasonStr; - } - } - - if (const TSharedPtr* UsageObj; JsonResponse->TryGetObjectField("usage", UsageObj)) - { - Response.Usage = FHttpGPTChatUsage((*UsageObj)->GetNumberField("prompt_tokens"), (*UsageObj)->GetNumberField("completion_tokens"), (*UsageObj)->GetNumberField("total_tokens")); - } + FScopeLock Lock(&Mutex); + + if (HttpGPT::Internal::HasEmptyParam(Content)) + { + return; + } + + const TSharedRef> Reader = TJsonReaderFactory<>::Create(Content); + TSharedPtr JsonResponse = MakeShared(); + FJsonSerializer::Deserialize(Reader, JsonResponse); + + if (CheckError(JsonResponse, Response.Error)) + { + Response.bSuccess = false; + return; + } + + Response.bSuccess = true; + + Response.ID = *JsonResponse->GetStringField("id"); + Response.Object = *JsonResponse->GetStringField("object"); + Response.Created = JsonResponse->GetNumberField("created"); + + const TArray> ChoicesArr = JsonResponse->GetArrayField("choices"); + + for (auto Iterator = ChoicesArr.CreateConstIterator(); Iterator; ++Iterator) + { + const TSharedPtr ChoiceObj = (*Iterator)->AsObject(); + const int32 ChoiceIndex = ChoiceObj->GetIntegerField("index"); + + FHttpGPTChatChoice* Choice = Response.Choices.FindByPredicate([this, ChoiceIndex](const FHttpGPTChatChoice& Element) + { + return Element.Index == ChoiceIndex; + }); + + if (!Choice) + { + FHttpGPTChatChoice NewChoice; + NewChoice.Index = ChoiceIndex; + Choice = &Response.Choices.Add_GetRef(NewChoice); + } + + if (const TSharedPtr* MessageObj; ChoiceObj->TryGetObjectField("message", MessageObj)) + { + if (FString RoleStr; (*MessageObj)->TryGetStringField("role", RoleStr)) + { + Choice->Message.Role = RoleStr == "user" ? EHttpGPTChatRole::User : EHttpGPTChatRole::Assistant; + } + + if (FString ContentStr; (*MessageObj)->TryGetStringField("content", ContentStr)) + { + Choice->Message.Content = ContentStr; + } + + if (const TSharedPtr* FunctionObj; (*MessageObj)->TryGetObjectField("function_call", FunctionObj)) + { + if (FString FunctionNameStr; (*FunctionObj)->TryGetStringField("name", FunctionNameStr)) + { + Choice->Message.FunctionCall.Name = *FunctionNameStr; + } + if (FString FunctionArgumentsStr; (*FunctionObj)->TryGetStringField("arguments", FunctionArgumentsStr)) + { + Choice->Message.FunctionCall.Arguments = FunctionArgumentsStr; + } + } + } + else if (const TSharedPtr* DeltaObj; ChoiceObj->TryGetObjectField("delta", DeltaObj)) + { + if (FString RoleStr; (*DeltaObj)->TryGetStringField("role", RoleStr)) + { + Choice->Message.Role = UHttpGPTHelper::NameToRole(*RoleStr); + } + else if (FString ContentStr; (*DeltaObj)->TryGetStringField("content", ContentStr)) + { + Choice->Message.Content += ContentStr; + } + + if (const TSharedPtr* FunctionObj; (*DeltaObj)->TryGetObjectField("function_call", FunctionObj)) + { + if (FString FunctionNameStr; (*FunctionObj)->TryGetStringField("name", FunctionNameStr)) + { + Choice->Message.FunctionCall.Name = *FunctionNameStr; + } + if (FString FunctionArgumentsStr; (*FunctionObj)->TryGetStringField("arguments", FunctionArgumentsStr)) + { + Choice->Message.FunctionCall.Arguments += FunctionArgumentsStr; + } + } + } + else if (FString MessageText; ChoiceObj->TryGetStringField("text", MessageText)) + { + Choice->Message.Role = EHttpGPTChatRole::Assistant; + Choice->Message.Content += MessageText; + } + + while (Choice->Message.Content.StartsWith("\n")) + { + Choice->Message.Content.RemoveAt(0); + } + + if (FString FinishReasonStr; ChoiceObj->TryGetStringField("finish_reason", FinishReasonStr)) + { + Choice->FinishReason = *FinishReasonStr; + } + } + + if (const TSharedPtr* UsageObj; JsonResponse->TryGetObjectField("usage", UsageObj)) + { + Response.Usage = FHttpGPTChatUsage((*UsageObj)->GetNumberField("prompt_tokens"), (*UsageObj)->GetNumberField("completion_tokens"), + (*UsageObj)->GetNumberField("total_tokens")); + } } UHttpGPTChatRequest* UHttpGPTChatHelper::CastToHttpGPTChatRequest(UObject* const Object) { - return Cast(Object); -} \ No newline at end of file + return Cast(Object); +} diff --git a/Source/HttpGPTChatModule/Public/HttpGPTChatModule.h b/Source/HttpGPTChatModule/Public/HttpGPTChatModule.h index 73397f5..37e8ed6 100644 --- a/Source/HttpGPTChatModule/Public/HttpGPTChatModule.h +++ b/Source/HttpGPTChatModule/Public/HttpGPTChatModule.h @@ -10,6 +10,6 @@ class FHttpGPTChatModule : public IModuleInterface { public: - virtual void StartupModule() override; - virtual void ShutdownModule() override; + virtual void StartupModule() override; + virtual void ShutdownModule() override; }; diff --git a/Source/HttpGPTChatModule/Public/Tasks/HttpGPTChatRequest.h b/Source/HttpGPTChatModule/Public/Tasks/HttpGPTChatRequest.h index 9a19147..8d3c86a 100644 --- a/Source/HttpGPTChatModule/Public/Tasks/HttpGPTChatRequest.h +++ b/Source/HttpGPTChatModule/Public/Tasks/HttpGPTChatRequest.h @@ -19,68 +19,82 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FHttpGPTChatResponseDelegate, const UCLASS(NotPlaceable, Category = "HttpGPT | Chat", meta = (ExposedAsyncProxy = AsyncTask)) class HTTPGPTCHATMODULE_API UHttpGPTChatRequest : public UHttpGPTBaseTask { - GENERATED_BODY() + GENERATED_BODY() public: - UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Chat") - FHttpGPTChatResponseDelegate ProcessCompleted; + UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Chat") + FHttpGPTChatResponseDelegate ProcessCompleted; - UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Chat") - FHttpGPTChatResponseDelegate ProgressUpdated; + UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Chat") + FHttpGPTChatResponseDelegate ProgressUpdated; - UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Chat") - FHttpGPTChatResponseDelegate ProgressStarted; + UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Chat") + FHttpGPTChatResponseDelegate ProgressStarted; - UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Chat") - FHttpGPTChatResponseDelegate ErrorReceived; + UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Chat") + FHttpGPTChatResponseDelegate ErrorReceived; #if WITH_EDITOR - static UHttpGPTChatRequest* EditorTask(const TArray& Messages, const FHttpGPTChatOptions Options); + static UHttpGPTChatRequest* EditorTask(const TArray& Messages, const FHttpGPTChatOptions& Options); #endif - UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Default", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Message with Default Options", AutoCreateRefTerm = "Functions")) - static UHttpGPTChatRequest* SendMessage_DefaultOptions(UObject* const WorldContextObject, const FString& Message, const TArray& Functions); - - UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Default", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Messages with Default Options", AutoCreateRefTerm = "Functions")) - static UHttpGPTChatRequest* SendMessages_DefaultOptions(UObject* const WorldContextObject, const TArray& Messages, const TArray& Functions); - - UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Custom", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Message with Custom Options", AutoCreateRefTerm = "Functions")) - static UHttpGPTChatRequest* SendMessage_CustomOptions(UObject* const WorldContextObject, const FString& Message, const TArray& Functions, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions); - - UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Custom", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Messages with Custom Options", AutoCreateRefTerm = "Functions")) - static UHttpGPTChatRequest* SendMessages_CustomOptions(UObject* const WorldContextObject, const TArray& Messages, const TArray& Functions, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions); - - UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat") - const FHttpGPTChatOptions GetChatOptions() const; + UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Default", + meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Message with Default Options", + AutoCreateRefTerm = "Functions")) + static UHttpGPTChatRequest* SendMessage_DefaultOptions(UObject* const WorldContextObject, const FString& Message, + const TArray& Functions); + + UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Default", + meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Messages with Default Options", + AutoCreateRefTerm = "Functions")) + static UHttpGPTChatRequest* SendMessages_DefaultOptions(UObject* const WorldContextObject, const TArray& Messages, + const TArray& Functions); + + UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Custom", + meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Message with Custom Options", + AutoCreateRefTerm = "Functions")) + static UHttpGPTChatRequest* SendMessage_CustomOptions(UObject* const WorldContextObject, const FString& Message, + const TArray& Functions, const FHttpGPTCommonOptions CommonOptions, + const FHttpGPTChatOptions ChatOptions); + + UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Custom", + meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Messages with Custom Options", + AutoCreateRefTerm = "Functions")) + static UHttpGPTChatRequest* SendMessages_CustomOptions(UObject* const WorldContextObject, const TArray& Messages, + const TArray& Functions, const FHttpGPTCommonOptions CommonOptions, + const FHttpGPTChatOptions ChatOptions); + + UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat") + const FHttpGPTChatOptions GetChatOptions() const; protected: - TArray Messages; - TArray Functions; - FHttpGPTChatOptions ChatOptions; + TArray Messages; + TArray Functions; + FHttpGPTChatOptions ChatOptions; - virtual bool CanActivateTask() const override; - virtual bool CanBindProgress() const override; - virtual FString GetEndpointURL() const override; + virtual bool CanActivateTask() const override; + virtual bool CanBindProgress() const override; + virtual FString GetEndpointURL() const override; - virtual FString SetRequestContent() override; - virtual void OnProgressUpdated(const FString& Content, int32 BytesSent, int32 BytesReceived) override; - virtual void OnProgressCompleted(const FString& Content, const bool bWasSuccessful) override; + virtual FString SetRequestContent() override; + virtual void OnProgressUpdated(const FString& Content, int32 BytesSent, int32 BytesReceived) override; + virtual void OnProgressCompleted(const FString& Content, const bool bWasSuccessful) override; - TArray GetDeltasFromContent(const FString& Content) const; + TArray GetDeltasFromContent(const FString& Content) const; - void DeserializeStreamedResponse(const TArray& Deltas); - void DeserializeSingleResponse(const FString& Content); + void DeserializeStreamedResponse(const TArray& Deltas); + void DeserializeSingleResponse(const FString& Content); private: - FHttpGPTChatResponse Response; + FHttpGPTChatResponse Response; }; UCLASS(NotPlaceable, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Chat Helper")) class HTTPGPTCHATMODULE_API UHttpGPTChatHelper final : public UBlueprintFunctionLibrary { - GENERATED_BODY() + GENERATED_BODY() public: - UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", Meta = (DisplayName = "Cast to HttpGPT Chat Request")) - static UHttpGPTChatRequest* CastToHttpGPTChatRequest(UObject* const Object); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", Meta = (DisplayName = "Cast to HttpGPT Chat Request")) + static UHttpGPTChatRequest* CastToHttpGPTChatRequest(UObject* const Object); }; diff --git a/Source/HttpGPTCommonModule/HttpGPTCommonModule.Build.cs b/Source/HttpGPTCommonModule/HttpGPTCommonModule.Build.cs index 6812d5a..c2ea2ea 100644 --- a/Source/HttpGPTCommonModule/HttpGPTCommonModule.Build.cs +++ b/Source/HttpGPTCommonModule/HttpGPTCommonModule.Build.cs @@ -6,28 +6,25 @@ public class HttpGPTCommonModule : ModuleRules { - public HttpGPTCommonModule(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - CppStandard = CppStandardVersion.Cpp17; + public HttpGPTCommonModule(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + CppStandard = CppStandardVersion.Cpp17; - PublicDependencyModuleNames.AddRange(new[] - { - "Core", - "HTTP", - "Json" - }); + PublicDependencyModuleNames.AddRange(new[] + { + "Core", + "HTTP", + "Json" + }); - PrivateDependencyModuleNames.AddRange(new[] - { - "Engine", - "CoreUObject", - "DeveloperSettings" - }); + PrivateDependencyModuleNames.AddRange(new[] + { + "Engine", + "CoreUObject", + "DeveloperSettings" + }); - if (Target.bBuildEditor) - { - PrivateDependencyModuleNames.Add("UnrealEd"); - } - } + if (Target.bBuildEditor) PrivateDependencyModuleNames.Add("UnrealEd"); + } } \ No newline at end of file diff --git a/Source/HttpGPTCommonModule/Private/HttpGPTCommonModule.cpp b/Source/HttpGPTCommonModule/Private/HttpGPTCommonModule.cpp index 1ba7d85..0da38b7 100644 --- a/Source/HttpGPTCommonModule/Private/HttpGPTCommonModule.cpp +++ b/Source/HttpGPTCommonModule/Private/HttpGPTCommonModule.cpp @@ -16,4 +16,4 @@ void FHttpGPTCommonModule::ShutdownModule() #undef LOCTEXT_NAMESPACE -IMPLEMENT_MODULE(FHttpGPTCommonModule, HttpGPTCommonModule) \ No newline at end of file +IMPLEMENT_MODULE(FHttpGPTCommonModule, HttpGPTCommonModule) diff --git a/Source/HttpGPTCommonModule/Private/LogHttpGPT.cpp b/Source/HttpGPTCommonModule/Private/LogHttpGPT.cpp index 1528f86..3b9cd41 100644 --- a/Source/HttpGPTCommonModule/Private/LogHttpGPT.cpp +++ b/Source/HttpGPTCommonModule/Private/LogHttpGPT.cpp @@ -5,4 +5,4 @@ #include "LogHttpGPT.h" DEFINE_LOG_CATEGORY(LogHttpGPT); -DEFINE_LOG_CATEGORY(LogHttpGPT_Internal); \ No newline at end of file +DEFINE_LOG_CATEGORY(LogHttpGPT_Internal); diff --git a/Source/HttpGPTCommonModule/Private/Management/HttpGPTSettings.cpp b/Source/HttpGPTCommonModule/Private/Management/HttpGPTSettings.cpp index b2006be..fd582f0 100644 --- a/Source/HttpGPTCommonModule/Private/Management/HttpGPTSettings.cpp +++ b/Source/HttpGPTCommonModule/Private/Management/HttpGPTSettings.cpp @@ -10,118 +10,120 @@ #include UE_INLINE_GENERATED_CPP_BY_NAME(HttpGPTSettings) #endif -UHttpGPTSettings::UHttpGPTSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer), bUseCustomSystemContext(false), CustomSystemContext(FString()), GeneratedImagesDir("HttpGPT_Generated"), bEnableInternalLogs(false) +UHttpGPTSettings::UHttpGPTSettings(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer), bUseCustomSystemContext(false), + CustomSystemContext(FString()), + GeneratedImagesDir("HttpGPT_Generated"), bEnableInternalLogs(false) { - CategoryName = TEXT("Plugins"); + CategoryName = TEXT("Plugins"); - SetToDefaults(); + SetToDefaults(); } const UHttpGPTSettings* UHttpGPTSettings::Get() { - const UHttpGPTSettings* const Instance = GetDefault(); - return Instance; + const UHttpGPTSettings* const Instance = GetDefault(); + return Instance; } FHttpGPTCommonOptions UHttpGPTSettings::GetCommonOptions() { - return GetDefault()->CommonOptions; + return GetDefault()->CommonOptions; } void UHttpGPTSettings::SetCommonOptions(const FHttpGPTCommonOptions& Value) { - UHttpGPTSettings* const Settings = GetMutableDefault(); - Settings->CommonOptions = Value; + UHttpGPTSettings* const Settings = GetMutableDefault(); + Settings->CommonOptions = Value; - Settings->SaveAndReload(GET_MEMBER_NAME_CHECKED(UHttpGPTSettings, CommonOptions)); + Settings->SaveAndReload(GET_MEMBER_NAME_CHECKED(UHttpGPTSettings, CommonOptions)); } FHttpGPTChatOptions UHttpGPTSettings::GetChatOptions() { - return GetDefault()->ChatOptions; + return GetDefault()->ChatOptions; } void UHttpGPTSettings::SetChatOptions(const FHttpGPTChatOptions& Value) { - UHttpGPTSettings* const Settings = GetMutableDefault(); - Settings->ChatOptions = Value; + UHttpGPTSettings* const Settings = GetMutableDefault(); + Settings->ChatOptions = Value; - Settings->SaveAndReload(GET_MEMBER_NAME_CHECKED(UHttpGPTSettings, ChatOptions)); + Settings->SaveAndReload(GET_MEMBER_NAME_CHECKED(UHttpGPTSettings, ChatOptions)); } FHttpGPTImageOptions UHttpGPTSettings::GetImageOptions() { - return GetDefault()->ImageOptions; + return GetDefault()->ImageOptions; } void UHttpGPTSettings::SetImageOptions(const FHttpGPTImageOptions& Value) { - UHttpGPTSettings* const Settings = GetMutableDefault(); - Settings->ImageOptions = Value; + UHttpGPTSettings* const Settings = GetMutableDefault(); + Settings->ImageOptions = Value; - Settings->SaveAndReload(GET_MEMBER_NAME_CHECKED(UHttpGPTSettings, ImageOptions)); + Settings->SaveAndReload(GET_MEMBER_NAME_CHECKED(UHttpGPTSettings, ImageOptions)); } #if WITH_EDITOR void UHttpGPTSettings::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { - Super::PostEditChangeProperty(PropertyChangedEvent); + Super::PostEditChangeProperty(PropertyChangedEvent); - if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UHttpGPTSettings, bEnableInternalLogs)) - { - ToggleInternalLogs(); - } + if (PropertyChangedEvent.Property->GetFName() == GET_MEMBER_NAME_CHECKED(UHttpGPTSettings, bEnableInternalLogs)) + { + ToggleInternalLogs(); + } } #endif void UHttpGPTSettings::PostInitProperties() { - Super::PostInitProperties(); - ToggleInternalLogs(); + Super::PostInitProperties(); + ToggleInternalLogs(); } void UHttpGPTSettings::SetToDefaults() { - CommonOptions.APIKey = NAME_None; - CommonOptions.User = NAME_None; - CommonOptions.bIsAzureOpenAI = false; - CommonOptions.Endpoint = TEXT("https://api.openai.com/"); - CommonOptions.AzureOpenAIAPIVersion = TEXT("2023-05-15"); - - ChatOptions.Model = EHttpGPTChatModel::gpt35turbo; - ChatOptions.MaxTokens = 2048; - ChatOptions.Temperature = 1.f; - ChatOptions.TopP = 1.f; - ChatOptions.Choices = 1; - ChatOptions.bStream = true; - ChatOptions.Stop = TArray(); - ChatOptions.PresencePenalty = 0.f; - ChatOptions.FrequencyPenalty = 0.f; - ChatOptions.LogitBias = TMap(); - - ImageOptions.ImagesNum = 1; - ImageOptions.Size = EHttpGPTImageSize::x256; - ImageOptions.Format = EHttpGPTResponseFormat::url; + CommonOptions.APIKey = NAME_None; + CommonOptions.User = NAME_None; + CommonOptions.bIsAzureOpenAI = false; + CommonOptions.Endpoint = TEXT("https://api.openai.com/"); + CommonOptions.AzureOpenAIAPIVersion = TEXT("2023-05-15"); + + ChatOptions.Model = EHttpGPTChatModel::gpt35turbo; + ChatOptions.MaxTokens = 2048; + ChatOptions.Temperature = 1.f; + ChatOptions.TopP = 1.f; + ChatOptions.Choices = 1; + ChatOptions.bStream = true; + ChatOptions.Stop = TArray(); + ChatOptions.PresencePenalty = 0.f; + ChatOptions.FrequencyPenalty = 0.f; + ChatOptions.LogitBias = TMap(); + + ImageOptions.ImagesNum = 1; + ImageOptions.Size = EHttpGPTImageSize::x256; + ImageOptions.Format = EHttpGPTResponseFormat::url; } void UHttpGPTSettings::SaveAndReload(const FName& PropertyName) { - SaveConfig(); + SaveConfig(); - uint32 PropagationFlags = 0u; + uint32 PropagationFlags = 0u; #if ENGINE_MAJOR_VERSION >= 5 - PropagationFlags = UE::ELoadConfigPropagationFlags::LCPF_PropagateToChildDefaultObjects; + PropagationFlags = UE::ELoadConfigPropagationFlags::LCPF_PropagateToChildDefaultObjects; #else PropagationFlags = UE4::ELoadConfigPropagationFlags::LCPF_PropagateToChildDefaultObjects; #endif - ReloadConfig(GetClass(), *GetDefaultConfigFilename(), PropagationFlags, GetClass()->FindPropertyByName(PropertyName)); + ReloadConfig(GetClass(), *GetDefaultConfigFilename(), PropagationFlags, GetClass()->FindPropertyByName(PropertyName)); } void UHttpGPTSettings::ToggleInternalLogs() { #if !UE_BUILD_SHIPPING - LogHttpGPT_Internal.SetVerbosity(bEnableInternalLogs ? ELogVerbosity::Display : ELogVerbosity::NoLogging); + LogHttpGPT_Internal.SetVerbosity(bEnableInternalLogs ? ELogVerbosity::Display : ELogVerbosity::NoLogging); #endif -} \ No newline at end of file +} diff --git a/Source/HttpGPTCommonModule/Private/Structures/HttpGPTChatTypes.cpp b/Source/HttpGPTCommonModule/Private/Structures/HttpGPTChatTypes.cpp index 4b37439..fa01887 100644 --- a/Source/HttpGPTCommonModule/Private/Structures/HttpGPTChatTypes.cpp +++ b/Source/HttpGPTCommonModule/Private/Structures/HttpGPTChatTypes.cpp @@ -13,86 +13,86 @@ TSharedPtr FHttpGPTFunction::GetFunction() const { - TSharedPtr JsonObject = MakeShared(); - JsonObject->SetStringField("name", Name.ToString()); - JsonObject->SetStringField("description", Description); - - TSharedPtr ParametersObject = MakeShared(); - ParametersObject->SetStringField("type", "object"); - - TSharedPtr PropertiesObject = MakeShared(); - for (const FHttpGPTFunctionProperty& PropIt : Properties) - { - TSharedPtr PropertyObject = MakeShared(); - PropertyObject->SetStringField("type", UHttpGPTHelper::PropertyTypeToName(PropIt.Type).ToString().ToLower()); - PropertyObject->SetStringField("description", PropIt.Description); - - TArray> EnumArr; - for (const FName& EnumIt : PropIt.Enum) - { - EnumArr.Emplace(MakeShared(EnumIt.ToString())); - } - PropertyObject->SetArrayField("enum", EnumArr); - - PropertiesObject->SetObjectField(PropIt.Name.ToString(), PropertyObject); - } - - ParametersObject->SetObjectField("properties", PropertiesObject); - - TArray> RequiredParams; - for (const FName& ReqIt : RequiredProperties) - { - RequiredParams.Emplace(MakeShared(ReqIt.ToString())); - } - - ParametersObject->SetArrayField("required", RequiredParams); - JsonObject->SetObjectField("parameters", ParametersObject); - - return MakeShared(JsonObject); + TSharedPtr JsonObject = MakeShared(); + JsonObject->SetStringField("name", Name.ToString()); + JsonObject->SetStringField("description", Description); + + const TSharedPtr ParametersObject = MakeShared(); + ParametersObject->SetStringField("type", "object"); + + const TSharedPtr PropertiesObject = MakeShared(); + for (const FHttpGPTFunctionProperty& PropIt : Properties) + { + TSharedPtr PropertyObject = MakeShared(); + PropertyObject->SetStringField("type", UHttpGPTHelper::PropertyTypeToName(PropIt.Type).ToString().ToLower()); + PropertyObject->SetStringField("description", PropIt.Description); + + TArray> EnumArr; + for (const FName& EnumIt : PropIt.Enum) + { + EnumArr.Emplace(MakeShared(EnumIt.ToString())); + } + PropertyObject->SetArrayField("enum", EnumArr); + + PropertiesObject->SetObjectField(PropIt.Name.ToString(), PropertyObject); + } + + ParametersObject->SetObjectField("properties", PropertiesObject); + + TArray> RequiredParams; + for (const FName& ReqIt : RequiredProperties) + { + RequiredParams.Emplace(MakeShared(ReqIt.ToString())); + } + + ParametersObject->SetArrayField("required", RequiredParams); + JsonObject->SetObjectField("parameters", ParametersObject); + + return MakeShared(JsonObject); } FHttpGPTChatMessage::FHttpGPTChatMessage(const FName& InRole, const FString& InContent) { - Role = UHttpGPTHelper::NameToRole(InRole); - Content = InContent; + Role = UHttpGPTHelper::NameToRole(InRole); + Content = InContent; } TSharedPtr FHttpGPTChatMessage::GetMessage() const { - TSharedPtr JsonObject = MakeShared(); - JsonObject->SetStringField("role", UHttpGPTHelper::RoleToName(Role).ToString().ToLower()); - - if (Role == EHttpGPTChatRole::Function) - { - JsonObject->SetStringField("name", FunctionCall.Name.ToString()); - JsonObject->SetStringField("content", FunctionCall.Arguments); - } - else - { - JsonObject->SetStringField("content", Content); - } - - return MakeShared(JsonObject); + TSharedPtr JsonObject = MakeShared(); + JsonObject->SetStringField("role", UHttpGPTHelper::RoleToName(Role).ToString().ToLower()); + + if (Role == EHttpGPTChatRole::Function) + { + JsonObject->SetStringField("name", FunctionCall.Name.ToString()); + JsonObject->SetStringField("content", FunctionCall.Arguments); + } + else + { + JsonObject->SetStringField("content", Content); + } + + return MakeShared(JsonObject); } FHttpGPTChatOptions::FHttpGPTChatOptions() { - SetDefaults(); + SetDefaults(); } void FHttpGPTChatOptions::SetDefaults() { - if (const UHttpGPTSettings* const Settings = GetDefault()) - { - Model = Settings->ChatOptions.Model; - MaxTokens = Settings->ChatOptions.MaxTokens; - Temperature = Settings->ChatOptions.Temperature; - TopP = Settings->ChatOptions.TopP; - Choices = Settings->ChatOptions.Choices; - bStream = Settings->ChatOptions.bStream; - Stop = Settings->ChatOptions.Stop; - PresencePenalty = Settings->ChatOptions.PresencePenalty; - FrequencyPenalty = Settings->ChatOptions.FrequencyPenalty; - LogitBias = Settings->ChatOptions.LogitBias; - } + if (const UHttpGPTSettings* const Settings = GetDefault()) + { + Model = Settings->ChatOptions.Model; + MaxTokens = Settings->ChatOptions.MaxTokens; + Temperature = Settings->ChatOptions.Temperature; + TopP = Settings->ChatOptions.TopP; + Choices = Settings->ChatOptions.Choices; + bStream = Settings->ChatOptions.bStream; + Stop = Settings->ChatOptions.Stop; + PresencePenalty = Settings->ChatOptions.PresencePenalty; + FrequencyPenalty = Settings->ChatOptions.FrequencyPenalty; + LogitBias = Settings->ChatOptions.LogitBias; + } } diff --git a/Source/HttpGPTCommonModule/Private/Structures/HttpGPTCommonTypes.cpp b/Source/HttpGPTCommonModule/Private/Structures/HttpGPTCommonTypes.cpp index ad8e59a..6387fc9 100644 --- a/Source/HttpGPTCommonModule/Private/Structures/HttpGPTCommonTypes.cpp +++ b/Source/HttpGPTCommonModule/Private/Structures/HttpGPTCommonTypes.cpp @@ -11,17 +11,17 @@ FHttpGPTCommonOptions::FHttpGPTCommonOptions() { - SetDefaults(); + SetDefaults(); } void FHttpGPTCommonOptions::SetDefaults() { - if (const UHttpGPTSettings* const Settings = GetDefault()) - { - APIKey = Settings->CommonOptions.APIKey; - User = Settings->CommonOptions.User; - bIsAzureOpenAI = Settings->CommonOptions.bIsAzureOpenAI; - Endpoint = Settings->CommonOptions.Endpoint; - AzureOpenAIAPIVersion = Settings->CommonOptions.AzureOpenAIAPIVersion; - } -} \ No newline at end of file + if (const UHttpGPTSettings* const Settings = GetDefault()) + { + APIKey = Settings->CommonOptions.APIKey; + User = Settings->CommonOptions.User; + bIsAzureOpenAI = Settings->CommonOptions.bIsAzureOpenAI; + Endpoint = Settings->CommonOptions.Endpoint; + AzureOpenAIAPIVersion = Settings->CommonOptions.AzureOpenAIAPIVersion; + } +} diff --git a/Source/HttpGPTCommonModule/Private/Structures/HttpGPTImageTypes.cpp b/Source/HttpGPTCommonModule/Private/Structures/HttpGPTImageTypes.cpp index 86437d0..2df0a65 100644 --- a/Source/HttpGPTCommonModule/Private/Structures/HttpGPTImageTypes.cpp +++ b/Source/HttpGPTCommonModule/Private/Structures/HttpGPTImageTypes.cpp @@ -7,15 +7,15 @@ FHttpGPTImageOptions::FHttpGPTImageOptions() { - SetDefaults(); + SetDefaults(); } void FHttpGPTImageOptions::SetDefaults() { - if (const UHttpGPTSettings* const Settings = GetDefault()) - { - ImagesNum = Settings->ImageOptions.ImagesNum; - Size = Settings->ImageOptions.Size; - Format = Settings->ImageOptions.Format; - } -} \ No newline at end of file + if (const UHttpGPTSettings* const Settings = GetDefault()) + { + ImagesNum = Settings->ImageOptions.ImagesNum; + Size = Settings->ImageOptions.Size; + Format = Settings->ImageOptions.Format; + } +} diff --git a/Source/HttpGPTCommonModule/Private/Tasks/HttpGPTBaseTask.cpp b/Source/HttpGPTCommonModule/Private/Tasks/HttpGPTBaseTask.cpp index b863331..8a5edf7 100644 --- a/Source/HttpGPTCommonModule/Private/Tasks/HttpGPTBaseTask.cpp +++ b/Source/HttpGPTCommonModule/Private/Tasks/HttpGPTBaseTask.cpp @@ -27,292 +27,280 @@ void UHttpGPTBaseTask::Activate() { - Super::Activate(); - - UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Activating task"), *FString(__func__), GetUniqueID()); - - bIsTaskActive = true; - if (!CommonOptions.Endpoint.EndsWith(TEXT("/"))) - { - CommonOptions.Endpoint += TEXT("/"); - } - - if (!CanActivateTask()) - { - UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Failed to activate task."), *FString(__func__), GetUniqueID()); - RequestFailed.Broadcast(); - SetReadyToDestroy(); - return; - } - - AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, - [this] - { - SendRequest(); - } - ); + Super::Activate(); + + UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Activating task"), *FString(__func__), GetUniqueID()); + + bIsTaskActive = true; + if (!CommonOptions.Endpoint.EndsWith(TEXT("/"))) + { + CommonOptions.Endpoint += TEXT("/"); + } + + if (!CanActivateTask()) + { + UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Failed to activate task."), *FString(__func__), GetUniqueID()); + RequestFailed.Broadcast(); + SetReadyToDestroy(); + return; + } + + AsyncTask(ENamedThreads::AnyBackgroundThreadNormalTask, [this] + { + SendRequest(); + }); #if WITH_EDITOR - if (bIsEditorTask) - { - SetFlags(RF_Standalone); - } - else - { - FEditorDelegates::PrePIEEnded.AddUObject(this, &UHttpGPTBaseTask::PrePIEEnded); - } + if (bIsEditorTask) + { + SetFlags(RF_Standalone); + } + else + { + FEditorDelegates::PrePIEEnded.AddUObject(this, &UHttpGPTBaseTask::PrePIEEnded); + } #endif } void UHttpGPTBaseTask::StopHttpGPTTask() { - FScopeLock Lock(&Mutex); + FScopeLock Lock(&Mutex); - if (!bIsTaskActive) - { - return; - } + if (!bIsTaskActive) + { + return; + } - UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Stopping task"), *FString(__func__), GetUniqueID()); + UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Stopping task"), *FString(__func__), GetUniqueID()); - bIsTaskActive = false; + bIsTaskActive = false; - if (HttpRequest.IsValid()) - { - HttpRequest->CancelRequest(); - HttpRequest.Reset(); - } + if (HttpRequest.IsValid()) + { + HttpRequest->CancelRequest(); + HttpRequest.Reset(); + } - SetReadyToDestroy(); + SetReadyToDestroy(); } void UHttpGPTBaseTask::SetReadyToDestroy() { - FScopeLock Lock(&Mutex); + FScopeLock Lock(&Mutex); - if (bIsReadyToDestroy) - { - return; - } + if (bIsReadyToDestroy) + { + return; + } - UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Setting task as Ready to Destroy"), *FString(__func__), GetUniqueID()); + UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Setting task as Ready to Destroy"), *FString(__func__), GetUniqueID()); #if WITH_EDITOR - if (bIsEditorTask) - { - ClearFlags(RF_Standalone); + if (bIsEditorTask) + { + ClearFlags(RF_Standalone); #if ENGINE_MAJOR_VERSION >= 5 - MarkAsGarbage(); + MarkAsGarbage(); #else MarkPendingKill(); #endif - } + } - if (FEditorDelegates::PrePIEEnded.IsBoundToObject(this)) - { - FEditorDelegates::PrePIEEnded.RemoveAll(this); - } + if (FEditorDelegates::PrePIEEnded.IsBoundToObject(this)) + { + FEditorDelegates::PrePIEEnded.RemoveAll(this); + } #endif - bIsReadyToDestroy = true; - bIsTaskActive = false; + bIsReadyToDestroy = true; + bIsTaskActive = false; - Super::SetReadyToDestroy(); + Super::SetReadyToDestroy(); } const FHttpGPTCommonOptions UHttpGPTBaseTask::GetCommonOptions() const { - return CommonOptions; + return CommonOptions; } #if WITH_EDITOR void UHttpGPTBaseTask::PrePIEEnded(bool bIsSimulating) { - if (!IsValid(this)) - { - return; - } + if (!IsValid(this)) + { + return; + } - UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Trying to finish task due to PIE end"), *FString(__func__), GetUniqueID()); + UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Trying to finish task due to PIE end"), *FString(__func__), GetUniqueID()); - bEndingPIE = true; - StopHttpGPTTask(); + bEndingPIE = true; + StopHttpGPTTask(); } #endif bool UHttpGPTBaseTask::CanActivateTask() const { - if (HttpGPT::Internal::HasEmptyParam(GetCommonOptions().APIKey)) - { - UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Can't activate task: Invalid API Key."), *FString(__func__), GetUniqueID()); - return false; - } + if (HttpGPT::Internal::HasEmptyParam(GetCommonOptions().APIKey)) + { + UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Can't activate task: Invalid API Key."), *FString(__func__), GetUniqueID()); + return false; + } - return true; + return true; } bool UHttpGPTBaseTask::CanBindProgress() const { - return true; + return true; } FString UHttpGPTBaseTask::GetEndpointURL() const { - return FString(); + return FString(); } void UHttpGPTBaseTask::InitializeRequest() { - FScopeLock Lock(&Mutex); + FScopeLock Lock(&Mutex); - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Initializing request object"), *FString(__func__), GetUniqueID()); + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Initializing request object"), *FString(__func__), GetUniqueID()); - HttpRequest = FHttpModule::Get().CreateRequest(); - HttpRequest->SetURL(GetEndpointURL()); - HttpRequest->SetVerb("POST"); - HttpRequest->SetHeader("Content-Type", "application/json"); - HttpRequest->SetHeader("Authorization", FString::Format(TEXT("Bearer {0}"), { GetCommonOptions().APIKey.ToString() })); + HttpRequest = FHttpModule::Get().CreateRequest(); + HttpRequest->SetURL(GetEndpointURL()); + HttpRequest->SetVerb("POST"); + HttpRequest->SetHeader("Content-Type", "application/json"); + HttpRequest->SetHeader("Authorization", FString::Format(TEXT("Bearer {0}"), {GetCommonOptions().APIKey.ToString()})); } void UHttpGPTBaseTask::BindRequestCallbacks() { - FScopeLock Lock(&Mutex); - - if (!HttpRequest.IsValid()) - { - return; - } - - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Binding callbacks"), *FString(__func__), GetUniqueID()); - - if (CanBindProgress()) - { - HttpRequest->OnRequestProgress().BindLambda( - [this](FHttpRequestPtr Request, int32 BytesSent, int32 BytesReceived) - { - FScopeTryLock Lock(&Mutex); - - if (!Lock.IsLocked() || !IsValid(this) || !bIsTaskActive || !Request.IsValid()) - { - return; - } - - if (const FHttpResponsePtr Response = Request->GetResponse(); Response.IsValid()) - { - OnProgressUpdated(Response->GetContentAsString(), BytesSent, BytesReceived); - } - } - ); - } - - HttpRequest->OnProcessRequestComplete().BindLambda( - [this](FHttpRequestPtr Request, FHttpResponsePtr RequestResponse, bool bWasSuccessful) - { - FScopeTryLock Lock(&Mutex); - - if (!Lock.IsLocked() || !IsValid(this) || !bIsTaskActive) - { - return; - } - - OnProgressCompleted(RequestResponse->GetContentAsString(), bWasSuccessful); - SetReadyToDestroy(); - } - ); + FScopeLock Lock(&Mutex); + + if (!HttpRequest.IsValid()) + { + return; + } + + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Binding callbacks"), *FString(__func__), GetUniqueID()); + + if (CanBindProgress()) + { + HttpRequest->OnRequestProgress().BindLambda([this](const FHttpRequestPtr& Request, int32 BytesSent, int32 BytesReceived) + { + const FScopeTryLock Lock(&Mutex); + + if (!Lock.IsLocked() || !IsValid(this) || !bIsTaskActive || !Request.IsValid()) + { + return; + } + + if (const FHttpResponsePtr Response = Request->GetResponse(); Response.IsValid()) + { + OnProgressUpdated(Response->GetContentAsString(), BytesSent, BytesReceived); + } + }); + } + + HttpRequest->OnProcessRequestComplete().BindLambda([this](FHttpRequestPtr Request, const FHttpResponsePtr& RequestResponse, bool bWasSuccessful) + { + const FScopeTryLock Lock(&Mutex); + + if (!Lock.IsLocked() || !IsValid(this) || !bIsTaskActive) + { + return; + } + + OnProgressCompleted(RequestResponse->GetContentAsString(), bWasSuccessful); + SetReadyToDestroy(); + }); } void UHttpGPTBaseTask::SendRequest() { - FScopeLock Lock(&Mutex); - - InitializeRequest(); - const FString ContentString = SetRequestContent(); - BindRequestCallbacks(); - - if (!HttpRequest.IsValid()) - { - UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Failed to send request: Request object is invalid"), *FString(__func__), GetUniqueID()); - - AsyncTask(ENamedThreads::GameThread, - [this] - { - RequestFailed.Broadcast(); - SetReadyToDestroy(); - } - ); - - return; - } - - UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Sending request"), *FString(__func__), GetUniqueID()); - - if (HttpRequest->ProcessRequest()) - { - UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Request sent"), *FString(__func__), GetUniqueID()); - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Request content body:\n%s"), *FString(__func__), GetUniqueID(), *ContentString); - - AsyncTask(ENamedThreads::GameThread, - [this] - { - RequestSent.Broadcast(); - } - ); - } - else - { - UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Failed to initialize the request process"), *FString(__func__), GetUniqueID()); - AsyncTask(ENamedThreads::GameThread, - [this] - { - RequestFailed.Broadcast(); - SetReadyToDestroy(); - } - ); - } + FScopeLock Lock(&Mutex); + + InitializeRequest(); + const FString ContentString = SetRequestContent(); + BindRequestCallbacks(); + + if (!HttpRequest.IsValid()) + { + UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Failed to send request: Request object is invalid"), *FString(__func__), GetUniqueID()); + + AsyncTask(ENamedThreads::GameThread, [this] + { + RequestFailed.Broadcast(); + SetReadyToDestroy(); + }); + + return; + } + + UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Sending request"), *FString(__func__), GetUniqueID()); + + if (HttpRequest->ProcessRequest()) + { + UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Request sent"), *FString(__func__), GetUniqueID()); + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Request content body:\n%s"), *FString(__func__), GetUniqueID(), *ContentString); + + AsyncTask(ENamedThreads::GameThread, [this] + { + RequestSent.Broadcast(); + }); + } + else + { + UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Failed to initialize the request process"), *FString(__func__), GetUniqueID()); + AsyncTask(ENamedThreads::GameThread, [this] + { + RequestFailed.Broadcast(); + SetReadyToDestroy(); + }); + } } const bool UHttpGPTBaseTask::CheckError(const TSharedPtr& JsonObject, FHttpGPTCommonError& OutputError) const { - if (JsonObject->HasField("error")) - { - const TSharedPtr ErrorObj = JsonObject->GetObjectField("error"); - if (FString ErrorMessage; ErrorObj->TryGetStringField("message", ErrorMessage)) - { - OutputError.Message = *ErrorMessage; - } - if (FString ErrorCode; ErrorObj->TryGetStringField("code", ErrorCode)) - { - OutputError.Code = *ErrorCode; - } - if (FString ErrorType; ErrorObj->TryGetStringField("type", ErrorType)) - { - OutputError.Type = *ErrorType; - } - - return true; - } - - return false; + if (JsonObject->HasField("error")) + { + const TSharedPtr ErrorObj = JsonObject->GetObjectField("error"); + if (FString ErrorMessage; ErrorObj->TryGetStringField("message", ErrorMessage)) + { + OutputError.Message = *ErrorMessage; + } + if (FString ErrorCode; ErrorObj->TryGetStringField("code", ErrorCode)) + { + OutputError.Code = *ErrorCode; + } + if (FString ErrorType; ErrorObj->TryGetStringField("type", ErrorType)) + { + OutputError.Type = *ErrorType; + } + + return true; + } + + return false; } bool UHttpGPTTaskStatus::IsTaskActive(const UHttpGPTBaseTask* Test) { - return IsValid(Test) && Test->bIsTaskActive; + return IsValid(Test) && Test->bIsTaskActive; } bool UHttpGPTTaskStatus::IsTaskReadyToDestroy(const UHttpGPTBaseTask* Test) { - return IsValid(Test) && Test->bIsReadyToDestroy; + return IsValid(Test) && Test->bIsReadyToDestroy; } bool UHttpGPTTaskStatus::IsTaskStillValid(const UHttpGPTBaseTask* Test) { - bool bOutput = IsValid(Test) && !IsTaskReadyToDestroy(Test); + bool bOutput = IsValid(Test) && !IsTaskReadyToDestroy(Test); #if WITH_EDITOR - bOutput = bOutput && !Test->bEndingPIE; + bOutput = bOutput && !Test->bEndingPIE; #endif - return bOutput; -} \ No newline at end of file + return bOutput; +} diff --git a/Source/HttpGPTCommonModule/Private/Utils/HttpGPTHelper.cpp b/Source/HttpGPTCommonModule/Private/Utils/HttpGPTHelper.cpp index 10bb711..e9528d3 100644 --- a/Source/HttpGPTCommonModule/Private/Utils/HttpGPTHelper.cpp +++ b/Source/HttpGPTCommonModule/Private/Utils/HttpGPTHelper.cpp @@ -11,288 +11,288 @@ const FName UHttpGPTHelper::ModelToName(const EHttpGPTChatModel Model) { - switch (Model) - { - case EHttpGPTChatModel::gpt4: - return "gpt-4"; + switch (Model) + { + case EHttpGPTChatModel::gpt4: + return "gpt-4"; - case EHttpGPTChatModel::gpt432k: - return "gpt-4-32k"; + case EHttpGPTChatModel::gpt432k: + return "gpt-4-32k"; - case EHttpGPTChatModel::gpt35turbo: - return "gpt-3.5-turbo"; + case EHttpGPTChatModel::gpt35turbo: + return "gpt-3.5-turbo"; - case EHttpGPTChatModel::gpt35turbo16k: - return "gpt-3.5-turbo-16k"; + case EHttpGPTChatModel::gpt35turbo16k: + return "gpt-3.5-turbo-16k"; - case EHttpGPTChatModel::textdavinci003: - return "text-davinci-003"; + case EHttpGPTChatModel::textdavinci003: + return "text-davinci-003"; - case EHttpGPTChatModel::textdavinci002: - return "text-davinci-002"; + case EHttpGPTChatModel::textdavinci002: + return "text-davinci-002"; - case EHttpGPTChatModel::codedavinci002: - return "code-davinci-002"; + case EHttpGPTChatModel::codedavinci002: + return "code-davinci-002"; - default: break; - } + default: break; + } - return NAME_None; + return NAME_None; } const EHttpGPTChatModel UHttpGPTHelper::NameToModel(const FName Model) { - if (Model.IsEqual("gpt-4", ENameCase::IgnoreCase)) - { - return EHttpGPTChatModel::gpt4; - } - else if (Model.IsEqual("gpt-4-32k", ENameCase::IgnoreCase)) - { - return EHttpGPTChatModel::gpt432k; - } - else if (Model.IsEqual("gpt-3.5-turbo", ENameCase::IgnoreCase)) - { - return EHttpGPTChatModel::gpt35turbo; - } - else if (Model.IsEqual("gpt-3.5-turbo-16k", ENameCase::IgnoreCase)) - { - return EHttpGPTChatModel::gpt35turbo16k; - } - else if (Model.IsEqual("text-davinci-003", ENameCase::IgnoreCase)) - { - return EHttpGPTChatModel::textdavinci003; - } - else if (Model.IsEqual("text-davinci-002", ENameCase::IgnoreCase)) - { - return EHttpGPTChatModel::textdavinci002; - } - else if (Model.IsEqual("code-davinci-002", ENameCase::IgnoreCase)) - { - return EHttpGPTChatModel::codedavinci002; - } - - return EHttpGPTChatModel::gpt35turbo; + if (Model.IsEqual("gpt-4", ENameCase::IgnoreCase)) + { + return EHttpGPTChatModel::gpt4; + } + if (Model.IsEqual("gpt-4-32k", ENameCase::IgnoreCase)) + { + return EHttpGPTChatModel::gpt432k; + } + if (Model.IsEqual("gpt-3.5-turbo", ENameCase::IgnoreCase)) + { + return EHttpGPTChatModel::gpt35turbo; + } + if (Model.IsEqual("gpt-3.5-turbo-16k", ENameCase::IgnoreCase)) + { + return EHttpGPTChatModel::gpt35turbo16k; + } + if (Model.IsEqual("text-davinci-003", ENameCase::IgnoreCase)) + { + return EHttpGPTChatModel::textdavinci003; + } + if (Model.IsEqual("text-davinci-002", ENameCase::IgnoreCase)) + { + return EHttpGPTChatModel::textdavinci002; + } + if (Model.IsEqual("code-davinci-002", ENameCase::IgnoreCase)) + { + return EHttpGPTChatModel::codedavinci002; + } + + return EHttpGPTChatModel::gpt35turbo; } const FName UHttpGPTHelper::RoleToName(const EHttpGPTChatRole Role) { - switch (Role) - { - case EHttpGPTChatRole::Assistant: - return "assistant"; + switch (Role) + { + case EHttpGPTChatRole::Assistant: + return "assistant"; - case EHttpGPTChatRole::User: - return "user"; + case EHttpGPTChatRole::User: + return "user"; - case EHttpGPTChatRole::System: - return "system"; + case EHttpGPTChatRole::System: + return "system"; - case EHttpGPTChatRole::Function: - return "function"; + case EHttpGPTChatRole::Function: + return "function"; - default: - break; - } + default: + break; + } - return NAME_None; + return NAME_None; } const EHttpGPTChatRole UHttpGPTHelper::NameToRole(const FName Role) { - if (Role.IsEqual("user", ENameCase::IgnoreCase)) - { - return EHttpGPTChatRole::User; - } - else if (Role.IsEqual("assistant", ENameCase::IgnoreCase)) - { - return EHttpGPTChatRole::Assistant; - } - else if (Role.IsEqual("system", ENameCase::IgnoreCase)) - { - return EHttpGPTChatRole::System; - } - else if (Role.IsEqual("function", ENameCase::IgnoreCase)) - { - return EHttpGPTChatRole::Function; - } - - return EHttpGPTChatRole::User; + if (Role.IsEqual("user", ENameCase::IgnoreCase)) + { + return EHttpGPTChatRole::User; + } + if (Role.IsEqual("assistant", ENameCase::IgnoreCase)) + { + return EHttpGPTChatRole::Assistant; + } + if (Role.IsEqual("system", ENameCase::IgnoreCase)) + { + return EHttpGPTChatRole::System; + } + if (Role.IsEqual("function", ENameCase::IgnoreCase)) + { + return EHttpGPTChatRole::Function; + } + + return EHttpGPTChatRole::User; } const FName UHttpGPTHelper::PropertyTypeToName(const EHttpGPTPropertyType Type) { - switch (Type) - { - case EHttpGPTPropertyType::Boolean: - return "bool"; + switch (Type) + { + case EHttpGPTPropertyType::Boolean: + return "bool"; - case EHttpGPTPropertyType::Number: - return "number"; + case EHttpGPTPropertyType::Number: + return "number"; - case EHttpGPTPropertyType::String: - return "string"; + case EHttpGPTPropertyType::String: + return "string"; - default: - break; - } + default: + break; + } - return NAME_None; + return NAME_None; } const EHttpGPTPropertyType UHttpGPTHelper::NameToPropertyType(const FName Type) { - if (Type.IsEqual("bool", ENameCase::IgnoreCase)) - { - return EHttpGPTPropertyType::Boolean; - } - else if (Type.IsEqual("number", ENameCase::IgnoreCase)) - { - return EHttpGPTPropertyType::Number; - } - else if (Type.IsEqual("string", ENameCase::IgnoreCase)) - { - return EHttpGPTPropertyType::String; - } - - return EHttpGPTPropertyType::Boolean; + if (Type.IsEqual("bool", ENameCase::IgnoreCase)) + { + return EHttpGPTPropertyType::Boolean; + } + if (Type.IsEqual("number", ENameCase::IgnoreCase)) + { + return EHttpGPTPropertyType::Number; + } + if (Type.IsEqual("string", ENameCase::IgnoreCase)) + { + return EHttpGPTPropertyType::String; + } + + return EHttpGPTPropertyType::Boolean; } const TArray UHttpGPTHelper::GetAvailableGPTModels() { - TArray Output; + TArray Output; - for (uint8 Iterator = static_cast(EHttpGPTChatModel::gpt4); Iterator <= static_cast(EHttpGPTChatModel::codedavinci002); ++Iterator) - { - if (const FName ModelName = ModelToName(static_cast(Iterator)); !HttpGPT::Internal::HasEmptyParam(ModelName)) - { - Output.Add(ModelName); - } - } + for (uint8 Iterator = static_cast(EHttpGPTChatModel::gpt4); Iterator <= static_cast(EHttpGPTChatModel::codedavinci002); ++Iterator) + { + if (const FName ModelName = ModelToName(static_cast(Iterator)); !HttpGPT::Internal::HasEmptyParam(ModelName)) + { + Output.Add(ModelName); + } + } - return Output; + return Output; } const FString UHttpGPTHelper::GetEndpointForModel(const EHttpGPTChatModel Model, const bool bIsAzureOpenAI, const FString& AzureOpenAIAPIVersion) { - switch (Model) - { - case EHttpGPTChatModel::gpt4: - case EHttpGPTChatModel::gpt432k: - case EHttpGPTChatModel::gpt35turbo: - case EHttpGPTChatModel::gpt35turbo16k: - if (bIsAzureOpenAI) - { - return FString::Format(TEXT("/openai/deployments/{0}/chat/completions?api-version={1}"), { ModelToName(Model).ToString(), AzureOpenAIAPIVersion }); - } - else - { - return "v1/chat/completions"; - } - - case EHttpGPTChatModel::textdavinci003: - case EHttpGPTChatModel::textdavinci002: - case EHttpGPTChatModel::codedavinci002: - if (bIsAzureOpenAI) - { - return FString::Format(TEXT("/openai/deployments/{0}/completions?api-version={1}"), { ModelToName(Model).ToString(), AzureOpenAIAPIVersion }); - } - else - { - return "v1/completions"; - } - - default: break; - } - - return FString(); + switch (Model) + { + case EHttpGPTChatModel::gpt4: + case EHttpGPTChatModel::gpt432k: + case EHttpGPTChatModel::gpt35turbo: + case EHttpGPTChatModel::gpt35turbo16k: + { + if (bIsAzureOpenAI) + { + return FString::Format( + TEXT("/openai/deployments/{0}/chat/completions?api-version={1}"), {ModelToName(Model).ToString(), AzureOpenAIAPIVersion}); + } + return "v1/chat/completions"; + } + + case EHttpGPTChatModel::textdavinci003: + case EHttpGPTChatModel::textdavinci002: + case EHttpGPTChatModel::codedavinci002: + { + if (bIsAzureOpenAI) + { + return FString::Format( + TEXT("/openai/deployments/{0}/completions?api-version={1}"), {ModelToName(Model).ToString(), AzureOpenAIAPIVersion}); + } + return "v1/completions"; + } + + default: break; + } + + return FString(); } const bool UHttpGPTHelper::ModelSupportsChat(const EHttpGPTChatModel Model) { - switch (Model) - { - case EHttpGPTChatModel::gpt4: - case EHttpGPTChatModel::gpt432k: - case EHttpGPTChatModel::gpt35turbo: - case EHttpGPTChatModel::gpt35turbo16k: - return true; - - case EHttpGPTChatModel::textdavinci003: - case EHttpGPTChatModel::textdavinci002: - case EHttpGPTChatModel::codedavinci002: - return false; - - default: break; - } - - return false; + switch (Model) + { + case EHttpGPTChatModel::gpt4: + case EHttpGPTChatModel::gpt432k: + case EHttpGPTChatModel::gpt35turbo: + case EHttpGPTChatModel::gpt35turbo16k: + return true; + + case EHttpGPTChatModel::textdavinci003: + case EHttpGPTChatModel::textdavinci002: + case EHttpGPTChatModel::codedavinci002: + return false; + + default: break; + } + + return false; } const FName UHttpGPTHelper::SizeToName(const EHttpGPTImageSize Size) { - switch (Size) - { - case EHttpGPTImageSize::x256: - return "256x256"; + switch (Size) + { + case EHttpGPTImageSize::x256: + return "256x256"; - case EHttpGPTImageSize::x512: - return "512x512"; + case EHttpGPTImageSize::x512: + return "512x512"; - case EHttpGPTImageSize::x1024: - return "1024x1024"; + case EHttpGPTImageSize::x1024: + return "1024x1024"; - default: - break; - } + default: + break; + } - return NAME_None; + return NAME_None; } const EHttpGPTImageSize UHttpGPTHelper::NameToSize(const FName Size) { - if (Size.IsEqual("256x256", ENameCase::IgnoreCase)) - { - return EHttpGPTImageSize::x256; - } - else if (Size.IsEqual("512x512", ENameCase::IgnoreCase)) - { - return EHttpGPTImageSize::x512; - } - else if (Size.IsEqual("1024x1024", ENameCase::IgnoreCase)) - { - return EHttpGPTImageSize::x1024; - } - - return EHttpGPTImageSize::x256; + if (Size.IsEqual("256x256", ENameCase::IgnoreCase)) + { + return EHttpGPTImageSize::x256; + } + if (Size.IsEqual("512x512", ENameCase::IgnoreCase)) + { + return EHttpGPTImageSize::x512; + } + if (Size.IsEqual("1024x1024", ENameCase::IgnoreCase)) + { + return EHttpGPTImageSize::x1024; + } + + return EHttpGPTImageSize::x256; } const FName UHttpGPTHelper::FormatToName(const EHttpGPTResponseFormat Format) { - switch (Format) - { - case EHttpGPTResponseFormat::url: - return "url"; + switch (Format) + { + case EHttpGPTResponseFormat::url: + return "url"; - case EHttpGPTResponseFormat::b64_json: - return "b64_json"; + case EHttpGPTResponseFormat::b64_json: + return "b64_json"; - default: - break; - } + default: + break; + } - return NAME_None; + return NAME_None; } const EHttpGPTResponseFormat UHttpGPTHelper::NameToFormat(const FName Format) { - if (Format.IsEqual("url", ENameCase::IgnoreCase)) - { - return EHttpGPTResponseFormat::url; - } - else if (Format.IsEqual("b64_json", ENameCase::IgnoreCase)) - { - return EHttpGPTResponseFormat::b64_json; - } - - return EHttpGPTResponseFormat::url; -} \ No newline at end of file + if (Format.IsEqual("url", ENameCase::IgnoreCase)) + { + return EHttpGPTResponseFormat::url; + } + if (Format.IsEqual("b64_json", ENameCase::IgnoreCase)) + { + return EHttpGPTResponseFormat::b64_json; + } + + return EHttpGPTResponseFormat::url; +} diff --git a/Source/HttpGPTCommonModule/Public/HttpGPTCommonModule.h b/Source/HttpGPTCommonModule/Public/HttpGPTCommonModule.h index a8a1441..dc24bb3 100644 --- a/Source/HttpGPTCommonModule/Public/HttpGPTCommonModule.h +++ b/Source/HttpGPTCommonModule/Public/HttpGPTCommonModule.h @@ -10,6 +10,6 @@ class FHttpGPTCommonModule : public IModuleInterface { public: - virtual void StartupModule() override; - virtual void ShutdownModule() override; + virtual void StartupModule() override; + virtual void ShutdownModule() override; }; diff --git a/Source/HttpGPTCommonModule/Public/HttpGPTInternalFuncs.h b/Source/HttpGPTCommonModule/Public/HttpGPTInternalFuncs.h index 81d4368..68abebf 100644 --- a/Source/HttpGPTCommonModule/Public/HttpGPTInternalFuncs.h +++ b/Source/HttpGPTCommonModule/Public/HttpGPTInternalFuncs.h @@ -12,43 +12,40 @@ * */ -namespace HttpGPT +namespace HttpGPT::Internal { - namespace Internal - { - template - constexpr const bool HasEmptyParam(const Ty& Arg1) - { - if constexpr (std::is_base_of()) - { - return Arg1.IsEmpty(); - } - else if constexpr (std::is_base_of()) - { - return Arg1.IsNone(); - } - else if constexpr (std::is_base_of()) - { - return Arg1.IsEmptyOrWhitespace(); - } - else if constexpr (std::is_base_of()) - { - return Arg1.empty(); - } - else - { + template + constexpr const bool HasEmptyParam(const Ty& Arg1) + { + if constexpr (std::is_base_of()) + { + return Arg1.IsEmpty(); + } + else if constexpr (std::is_base_of()) + { + return Arg1.IsNone(); + } + else if constexpr (std::is_base_of()) + { + return Arg1.IsEmptyOrWhitespace(); + } + else if constexpr (std::is_base_of()) + { + return Arg1.empty(); + } + else + { #if ENGINE_MAJOR_VERSION >= 5 - return Arg1.IsEmpty(); + return Arg1.IsEmpty(); #else return Arg1.Num() == 0; #endif - } - } + } + } - template - constexpr const bool HasEmptyParam(const Ty& Arg1, Args&& ...args) - { - return HasEmptyParam(Arg1) || HasEmptyParam(std::forward(args)...); - } - } -} \ No newline at end of file + template + constexpr const bool HasEmptyParam(const Ty& Arg1, Args&&... args) + { + return HasEmptyParam(Arg1) || HasEmptyParam(std::forward(args)...); + } +} diff --git a/Source/HttpGPTCommonModule/Public/LogHttpGPT.h b/Source/HttpGPTCommonModule/Public/LogHttpGPT.h index bbff5cd..0e510ef 100644 --- a/Source/HttpGPTCommonModule/Public/LogHttpGPT.h +++ b/Source/HttpGPTCommonModule/Public/LogHttpGPT.h @@ -11,4 +11,5 @@ */ HTTPGPTCOMMONMODULE_API DECLARE_LOG_CATEGORY_EXTERN(LogHttpGPT, Display, All); -HTTPGPTCOMMONMODULE_API DECLARE_LOG_CATEGORY_EXTERN(LogHttpGPT_Internal, NoLogging, All); \ No newline at end of file + +HTTPGPTCOMMONMODULE_API DECLARE_LOG_CATEGORY_EXTERN(LogHttpGPT_Internal, NoLogging, All); diff --git a/Source/HttpGPTCommonModule/Public/Management/HttpGPTSettings.h b/Source/HttpGPTCommonModule/Public/Management/HttpGPTSettings.h index ac7d478..c99d23c 100644 --- a/Source/HttpGPTCommonModule/Public/Management/HttpGPTSettings.h +++ b/Source/HttpGPTCommonModule/Public/Management/HttpGPTSettings.h @@ -17,67 +17,73 @@ UCLASS(Config = Plugins, DefaultConfig, meta = (DisplayName = "HttpGPT")) class HTTPGPTCOMMONMODULE_API UHttpGPTSettings : public UDeveloperSettings { - GENERATED_BODY() + GENERATED_BODY() public: - explicit UHttpGPTSettings(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); + explicit UHttpGPTSettings(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); - static const UHttpGPTSettings* Get(); + static const UHttpGPTSettings* Get(); - UPROPERTY(GlobalConfig, EditAnywhere, Category = "Default Options", Meta = (DisplayName = "Common Options")) - FHttpGPTCommonOptions CommonOptions; + UPROPERTY(GlobalConfig, EditAnywhere, Category = "Default Options", Meta = (DisplayName = "Common Options")) + FHttpGPTCommonOptions CommonOptions; - UPROPERTY(GlobalConfig, EditAnywhere, Category = "Default Options", Meta = (DisplayName = "Chat Options")) - FHttpGPTChatOptions ChatOptions; + UPROPERTY(GlobalConfig, EditAnywhere, Category = "Default Options", Meta = (DisplayName = "Chat Options")) + FHttpGPTChatOptions ChatOptions; - UPROPERTY(GlobalConfig, EditAnywhere, Category = "Default Options", Meta = (DisplayName = "Image Options")) - FHttpGPTImageOptions ImageOptions; + UPROPERTY(GlobalConfig, EditAnywhere, Category = "Default Options", Meta = (DisplayName = "Image Options")) + FHttpGPTImageOptions ImageOptions; - /* Enable custom system context in HttpGPT Chat Editor Tool */ - UPROPERTY(GlobalConfig, EditAnywhere, Category = "Editor | HttpGPT Chat", Meta = (DisplayName = "Use Custom System Context")) - bool bUseCustomSystemContext; + /* Enable custom system context in HttpGPT Chat Editor Tool */ + UPROPERTY(GlobalConfig, EditAnywhere, Category = "Editor | HttpGPT Chat", Meta = (DisplayName = "Use Custom System Context")) + bool bUseCustomSystemContext; - /* Custom system context to use in HttpGPT Chat Editor Tool */ - UPROPERTY(GlobalConfig, EditAnywhere, Category = "Editor | HttpGPT Chat", Meta = (DisplayName = "Custom System Context", EditCondition = "bUseCustomSystemContext")) - FString CustomSystemContext; + /* Custom system context to use in HttpGPT Chat Editor Tool */ + UPROPERTY(GlobalConfig, EditAnywhere, Category = "Editor | HttpGPT Chat", + Meta = (DisplayName = "Custom System Context", EditCondition = "bUseCustomSystemContext")) + FString CustomSystemContext; - /* Directory to store images generated by HttpGPT Image Generator Editor Tool */ - UPROPERTY(GlobalConfig, EditAnywhere, Category = "Editor | HttpGPT Image Generator", Meta = (DisplayName = "Generated Images Directory")) - FString GeneratedImagesDir; + /* Directory to store images generated by HttpGPT Image Generator Editor Tool */ + UPROPERTY(GlobalConfig, EditAnywhere, Category = "Editor | HttpGPT Image Generator", Meta = (DisplayName = "Generated Images Directory")) + FString GeneratedImagesDir; - /* Will print extra internal informations in log */ - UPROPERTY(GlobalConfig, EditAnywhere, Category = "Logging", Meta = (DisplayName = "Enable Internal Logs")) - bool bEnableInternalLogs; + /* Will print extra internal informations in log */ + UPROPERTY(GlobalConfig, EditAnywhere, Category = "Logging", Meta = (DisplayName = "Enable Internal Logs")) + bool bEnableInternalLogs; - UFUNCTION(BlueprintPure, Category = "HttpGPT | Settings", meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Get Common Options", CompactNodeTitle = "HttpGPT Common Options")) - static FHttpGPTCommonOptions GetCommonOptions(); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Settings", + meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Get Common Options", CompactNodeTitle = "HttpGPT Common Options")) + static FHttpGPTCommonOptions GetCommonOptions(); - UFUNCTION(BlueprintCallable, Category = "HttpGPT | Settings", meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Set Common Options")) - static void SetCommonOptions(const FHttpGPTCommonOptions& Value); + UFUNCTION(BlueprintCallable, Category = "HttpGPT | Settings", + meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Set Common Options")) + static void SetCommonOptions(const FHttpGPTCommonOptions& Value); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Settings", meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Get Chat Options", CompactNodeTitle = "HttpGPT Chat Options")) - static FHttpGPTChatOptions GetChatOptions(); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Settings", + meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Get Chat Options", CompactNodeTitle = "HttpGPT Chat Options")) + static FHttpGPTChatOptions GetChatOptions(); - UFUNCTION(BlueprintCallable, Category = "HttpGPT | Settings", meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Set Chat Options")) - static void SetChatOptions(const FHttpGPTChatOptions& Value); + UFUNCTION(BlueprintCallable, Category = "HttpGPT | Settings", meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Set Chat Options")) + static void SetChatOptions(const FHttpGPTChatOptions& Value); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Settings", meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Get Image Options", CompactNodeTitle = "HttpGPT Image Options")) - static FHttpGPTImageOptions GetImageOptions(); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Settings", + meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Get Image Options", CompactNodeTitle = "HttpGPT Image Options")) + static FHttpGPTImageOptions GetImageOptions(); - UFUNCTION(BlueprintCallable, Category = "HttpGPT | Settings", meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Set Image Options")) - static void SetImageOptions(const FHttpGPTImageOptions& Value); + UFUNCTION(BlueprintCallable, Category = "HttpGPT | Settings", + meta = (HidePin = "Self", DefaultToSelf = "Self", DisplayName = "Set Image Options")) + static void SetImageOptions(const FHttpGPTImageOptions& Value); protected: #if WITH_EDITOR - virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; + virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; #endif - virtual void PostInitProperties() override; + virtual void PostInitProperties() override; - virtual void SetToDefaults(); + virtual void SetToDefaults(); - void SaveAndReload(const FName& PropertyName); + void SaveAndReload(const FName& PropertyName); private: - void ToggleInternalLogs(); + void ToggleInternalLogs(); }; diff --git a/Source/HttpGPTCommonModule/Public/Structures/HttpGPTChatTypes.h b/Source/HttpGPTCommonModule/Public/Structures/HttpGPTChatTypes.h index 378b336..f716001 100644 --- a/Source/HttpGPTCommonModule/Public/Structures/HttpGPTChatTypes.h +++ b/Source/HttpGPTCommonModule/Public/Structures/HttpGPTChatTypes.h @@ -14,204 +14,217 @@ UENUM(BlueprintType, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Property Type")) enum class EHttpGPTPropertyType : uint8 { - Number, - Boolean, - String + Number, + Boolean, + String }; USTRUCT(BlueprintType, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Function Parameter")) struct HTTPGPTCOMMONMODULE_API FHttpGPTFunctionProperty { - GENERATED_BODY() + GENERATED_BODY() - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Name")) - FName Name = NAME_None; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Name")) + FName Name = NAME_None; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Type")) - EHttpGPTPropertyType Type = EHttpGPTPropertyType::String; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Type")) + EHttpGPTPropertyType Type = EHttpGPTPropertyType::String; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Description")) - FString Description; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Description")) + FString Description; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Enum")) - TArray Enum; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Enum")) + TArray Enum; }; USTRUCT(BlueprintType, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Function")) struct HTTPGPTCOMMONMODULE_API FHttpGPTFunction { - GENERATED_BODY() + GENERATED_BODY() - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Name")) - FName Name = NAME_None; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Name")) + FName Name = NAME_None; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Description")) - FString Description; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Description")) + FString Description; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Properties")) - TArray Properties; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Properties")) + TArray Properties; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Required Properties")) - TArray RequiredProperties; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Required Properties")) + TArray RequiredProperties; - TSharedPtr GetFunction() const; + TSharedPtr GetFunction() const; }; USTRUCT(BlueprintType, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Function Call")) struct HTTPGPTCOMMONMODULE_API FHttpGPTFunctionCall { - GENERATED_BODY() + GENERATED_BODY() - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Name")) - FName Name = NAME_None; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Name")) + FName Name = NAME_None; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Arguments")) - FString Arguments; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Arguments")) + FString Arguments; }; UENUM(BlueprintType, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Chat Role")) enum class EHttpGPTChatRole : uint8 { - User, - Assistant, - System, - Function + User, + Assistant, + System, + Function }; USTRUCT(BlueprintType, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Chat Message")) struct HTTPGPTCOMMONMODULE_API FHttpGPTChatMessage { - GENERATED_BODY() + GENERATED_BODY() - FHttpGPTChatMessage() = default; - FHttpGPTChatMessage(const EHttpGPTChatRole& InRole, const FString& InContent) : Role(InRole), Content(InContent) {} - FHttpGPTChatMessage(const FName& InRole, const FString& InContent); + FHttpGPTChatMessage() = default; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - EHttpGPTChatRole Role = EHttpGPTChatRole::User; + FHttpGPTChatMessage(const EHttpGPTChatRole& InRole, const FString& InContent) : Role(InRole), Content(InContent) + { + } - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - FString Content; + FHttpGPTChatMessage(const FName& InRole, const FString& InContent); - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (EditCondition = "Role == EHttpGPTChatRole::Function")) - FHttpGPTFunctionCall FunctionCall; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + EHttpGPTChatRole Role = EHttpGPTChatRole::User; - TSharedPtr GetMessage() const; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + FString Content; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (EditCondition = "Role == EHttpGPTChatRole::Function")) + FHttpGPTFunctionCall FunctionCall; + + TSharedPtr GetMessage() const; }; USTRUCT(BlueprintType, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Chat Choice")) struct HTTPGPTCOMMONMODULE_API FHttpGPTChatChoice { - GENERATED_BODY() + GENERATED_BODY() - FHttpGPTChatChoice() = default; + FHttpGPTChatChoice() = default; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - int32 Index = 0; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + int32 Index = 0; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - FHttpGPTChatMessage Message; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + FHttpGPTChatMessage Message; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - FName FinishReason = NAME_None; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + FName FinishReason = NAME_None; }; USTRUCT(BlueprintType, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Chat Usage")) struct HTTPGPTCOMMONMODULE_API FHttpGPTChatUsage { - GENERATED_BODY() + GENERATED_BODY() - FHttpGPTChatUsage() = default; - FHttpGPTChatUsage(const int32& PromptTokens, const int32& CompletionTokens, const int32& TotalTokens) : PromptTokens(PromptTokens), CompletionTokens(CompletionTokens), TotalTokens(TotalTokens) {} + FHttpGPTChatUsage() = default; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - int32 PromptTokens = 0; + FHttpGPTChatUsage(const int32& PromptTokens, const int32& CompletionTokens, const int32& TotalTokens) : PromptTokens(PromptTokens), + CompletionTokens(CompletionTokens), TotalTokens(TotalTokens) + { + } - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - int32 CompletionTokens = 0; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + int32 PromptTokens = 0; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - int32 TotalTokens = 0; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + int32 CompletionTokens = 0; + + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + int32 TotalTokens = 0; }; USTRUCT(BlueprintType, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Chat Response")) struct HTTPGPTCOMMONMODULE_API FHttpGPTChatResponse { - GENERATED_BODY() + GENERATED_BODY() - FHttpGPTChatResponse() = default; + FHttpGPTChatResponse() = default; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - FName ID = NAME_None; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + FName ID = NAME_None; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - FName Object = NAME_None; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + FName Object = NAME_None; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - int32 Created = 0; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + int32 Created = 0; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - TArray Choices; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + TArray Choices; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - FHttpGPTChatUsage Usage; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + FHttpGPTChatUsage Usage; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - bool bSuccess = false; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + bool bSuccess = false; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") - FHttpGPTCommonError Error; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat") + FHttpGPTCommonError Error; }; UENUM(BlueprintType, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Chat Model")) enum class EHttpGPTChatModel : uint8 { - gpt4 UMETA(DisplayName = "gpt-4"), - gpt432k UMETA(DisplayName = "gpt-4-32k"), - gpt35turbo UMETA(DisplayName = "gpt-3.5-turbo"), - gpt35turbo16k UMETA(DisplayName = "gpt-3.5-turbo-16k"), - textdavinci003 UMETA(DisplayName = "text-davinci-003"), - textdavinci002 UMETA(DisplayName = "text-davinci-002"), - codedavinci002 UMETA(DisplayName = "code-davinci-002"), + gpt4 UMETA(DisplayName = "gpt-4"), + gpt432k UMETA(DisplayName = "gpt-4-32k"), + gpt35turbo UMETA(DisplayName = "gpt-3.5-turbo"), + gpt35turbo16k UMETA(DisplayName = "gpt-3.5-turbo-16k"), + textdavinci003 UMETA(DisplayName = "text-davinci-003"), + textdavinci002 UMETA(DisplayName = "text-davinci-002"), + codedavinci002 UMETA(DisplayName = "code-davinci-002"), }; USTRUCT(BlueprintType, Category = "HttpGPT | Chat", Meta = (DisplayName = "HttpGPT Chat Options")) struct HTTPGPTCOMMONMODULE_API FHttpGPTChatOptions { - GENERATED_BODY() + GENERATED_BODY() - FHttpGPTChatOptions(); + FHttpGPTChatOptions(); - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Model")) - EHttpGPTChatModel Model; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Model")) + EHttpGPTChatModel Model; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Temperature", ClampMin = "0.0", UIMin = "0.0", ClampMax = "2.0", UIMax = "2.0")) - float Temperature; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", + Meta = (DisplayName = "Temperature", ClampMin = "0.0", UIMin = "0.0", ClampMax = "2.0", UIMax = "2.0")) + float Temperature; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "TopP", ClampMin = "0.01", UIMin = "0.01", ClampMax = "1.0", UIMax = "1.0")) - float TopP; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", + Meta = (DisplayName = "TopP", ClampMin = "0.01", UIMin = "0.01", ClampMax = "1.0", UIMax = "1.0")) + float TopP; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Choices", ClampMin = "1", UIMin = "1")) - int32 Choices; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Choices", ClampMin = "1", UIMin = "1")) + int32 Choices; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Stream")) - bool bStream; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Stream")) + bool bStream; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Stop")) - TArray Stop; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Stop")) + TArray Stop; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Presence Penalty", ClampMin = "-2.0", UIMin = "-2.0", ClampMax = "2.0", UIMax = "2.0")) - float PresencePenalty; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", + Meta = (DisplayName = "Presence Penalty", ClampMin = "-2.0", UIMin = "-2.0", ClampMax = "2.0", UIMax = "2.0")) + float PresencePenalty; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Frequency Penalty", ClampMin = "-2.0", UIMin = "-2.0", ClampMax = "2.0", UIMax = "2.0")) - float FrequencyPenalty; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", + Meta = (DisplayName = "Frequency Penalty", ClampMin = "-2.0", UIMin = "-2.0", ClampMax = "2.0", UIMax = "2.0")) + float FrequencyPenalty; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Max Tokens", ClampMin = "1", UIMin = "1", ClampMax = "32768", UIMax = "32768")) - int32 MaxTokens; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", + Meta = (DisplayName = "Max Tokens", ClampMin = "1", UIMin = "1", ClampMax = "32768", UIMax = "32768")) + int32 MaxTokens; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Logit Bias")) - TMap LogitBias; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Chat", Meta = (DisplayName = "Logit Bias")) + TMap LogitBias; private: - void SetDefaults(); -}; \ No newline at end of file + void SetDefaults(); +}; diff --git a/Source/HttpGPTCommonModule/Public/Structures/HttpGPTCommonTypes.h b/Source/HttpGPTCommonModule/Public/Structures/HttpGPTCommonTypes.h index 128148d..e51609f 100644 --- a/Source/HttpGPTCommonModule/Public/Structures/HttpGPTCommonTypes.h +++ b/Source/HttpGPTCommonModule/Public/Structures/HttpGPTCommonTypes.h @@ -10,42 +10,43 @@ USTRUCT(BlueprintType, Category = "HttpGPT | Common", Meta = (DisplayName = "HttpGPT Common Error")) struct HTTPGPTCOMMONMODULE_API FHttpGPTCommonError { - GENERATED_BODY() + GENERATED_BODY() - FHttpGPTCommonError() = default; + FHttpGPTCommonError() = default; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common") - FName Type; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common") + FName Type; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common") - FName Code; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common") + FName Code; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common") - FString Message; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common") + FString Message; }; USTRUCT(BlueprintType, Category = "HttpGPT | Common", Meta = (DisplayName = "HttpGPT Common Options")) struct HTTPGPTCOMMONMODULE_API FHttpGPTCommonOptions { - GENERATED_BODY() + GENERATED_BODY() - FHttpGPTCommonOptions(); + FHttpGPTCommonOptions(); - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common", Meta = (DisplayName = "API Key")) - FName APIKey; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common", Meta = (DisplayName = "API Key")) + FName APIKey; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common", Meta = (DisplayName = "User")) - FName User; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common", Meta = (DisplayName = "User")) + FName User; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common", Meta = (DisplayName = "Is Azure OpenAI")) - bool bIsAzureOpenAI; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common", Meta = (DisplayName = "Is Azure OpenAI")) + bool bIsAzureOpenAI; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common", Meta = (DisplayName = "Endpoint", EditCondition = "bIsAzureOpenAI")) - FString Endpoint; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common", Meta = (DisplayName = "Endpoint", EditCondition = "bIsAzureOpenAI")) + FString Endpoint; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common", Meta = (DisplayName = "Azure OpenAI API Version", EditCondition = "bIsAzureOpenAI")) - FString AzureOpenAIAPIVersion; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Common", + Meta = (DisplayName = "Azure OpenAI API Version", EditCondition = "bIsAzureOpenAI")) + FString AzureOpenAIAPIVersion; private: - void SetDefaults(); -}; \ No newline at end of file + void SetDefaults(); +}; diff --git a/Source/HttpGPTCommonModule/Public/Structures/HttpGPTImageTypes.h b/Source/HttpGPTCommonModule/Public/Structures/HttpGPTImageTypes.h index 0393b8e..93c601f 100644 --- a/Source/HttpGPTCommonModule/Public/Structures/HttpGPTImageTypes.h +++ b/Source/HttpGPTCommonModule/Public/Structures/HttpGPTImageTypes.h @@ -13,69 +13,72 @@ UENUM(BlueprintType, Category = "HttpGPT | Image", Meta = (DisplayName = "HttpGPT Image Size")) enum class EHttpGPTImageSize : uint8 { - x256 UMETA(DisplayName = "256x256"), - x512 UMETA(DisplayName = "512x512"), - x1024 UMETA(DisplayName = "1024x1024"), + x256 UMETA(DisplayName = "256x256"), + x512 UMETA(DisplayName = "512x512"), + x1024 UMETA(DisplayName = "1024x1024"), }; UENUM(BlueprintType, Category = "HttpGPT | Image", Meta = (DisplayName = "HttpGPT Image Response Format")) enum class EHttpGPTResponseFormat : uint8 { - url UMETA(DisplayName = "URL"), - b64_json UMETA(DisplayName = "B64") -}; + url UMETA(DisplayName = "URL"), + b64_json UMETA(DisplayName = "B64")}; USTRUCT(BlueprintType, Category = "HttpGPT | Image", Meta = (DisplayName = "HttpGPT Image Data")) struct HTTPGPTCOMMONMODULE_API FHttpGPTImageData { - GENERATED_BODY() + GENERATED_BODY() + + FHttpGPTImageData() = default; - FHttpGPTImageData() = default; - FHttpGPTImageData(const FString& Data, const EHttpGPTResponseFormat& DataFormat) : Content(Data), Format(DataFormat) {} + FHttpGPTImageData(const FString& Data, const EHttpGPTResponseFormat& DataFormat) : Content(Data), Format(DataFormat) + { + } - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") - FString Content; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") + FString Content; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") - EHttpGPTResponseFormat Format = EHttpGPTResponseFormat::b64_json; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") + EHttpGPTResponseFormat Format = EHttpGPTResponseFormat::b64_json; }; USTRUCT(BlueprintType, Category = "HttpGPT | Image", Meta = (DisplayName = "HttpGPT Image Response")) struct HTTPGPTCOMMONMODULE_API FHttpGPTImageResponse { - GENERATED_BODY() + GENERATED_BODY() - FHttpGPTImageResponse() = default; + FHttpGPTImageResponse() = default; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") - int32 Created = 0; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") + int32 Created = 0; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") - TArray Data; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") + TArray Data; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") - bool bSuccess = false; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") + bool bSuccess = false; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") - FHttpGPTCommonError Error; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image") + FHttpGPTCommonError Error; }; USTRUCT(BlueprintType, Category = "HttpGPT | Image", Meta = (DisplayName = "HttpGPT Image Options")) struct HTTPGPTCOMMONMODULE_API FHttpGPTImageOptions { - GENERATED_BODY() + GENERATED_BODY() - FHttpGPTImageOptions(); + FHttpGPTImageOptions(); - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image", Meta = (DisplayName = "Number of Images", ClampMin = "1", UIMin = "1", ClampMax = "10", UIMax = "10")) - int32 ImagesNum; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image", + Meta = (DisplayName = "Number of Images", ClampMin = "1", UIMin = "1", ClampMax = "10", UIMax = "10")) + int32 ImagesNum; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image", Meta = (DisplayName = "Image Size")) - EHttpGPTImageSize Size = EHttpGPTImageSize::x256; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image", Meta = (DisplayName = "Image Size")) + EHttpGPTImageSize Size = EHttpGPTImageSize::x256; - UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image", Meta = (DisplayName = "Response Format")) - EHttpGPTResponseFormat Format = EHttpGPTResponseFormat::b64_json; + UPROPERTY(BlueprintReadWrite, EditAnywhere, Category = "HttpGPT | Image", Meta = (DisplayName = "Response Format")) + EHttpGPTResponseFormat Format = EHttpGPTResponseFormat::b64_json; private: - void SetDefaults(); -}; \ No newline at end of file + void SetDefaults(); +}; diff --git a/Source/HttpGPTCommonModule/Public/Tasks/HttpGPTBaseTask.h b/Source/HttpGPTCommonModule/Public/Tasks/HttpGPTBaseTask.h index 1ef5a38..bed6ca1 100644 --- a/Source/HttpGPTCommonModule/Public/Tasks/HttpGPTBaseTask.h +++ b/Source/HttpGPTCommonModule/Public/Tasks/HttpGPTBaseTask.h @@ -19,73 +19,79 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE(FHttpGPTGenericDelegate); UCLASS(NotPlaceable, Category = "HttpGPT", meta = (ExposedAsyncProxy = AsyncTask)) class HTTPGPTCOMMONMODULE_API UHttpGPTBaseTask : public UBlueprintAsyncActionBase { - GENERATED_BODY() + GENERATED_BODY() - friend class UHttpGPTTaskStatus; + friend class UHttpGPTTaskStatus; public: - UPROPERTY(BlueprintAssignable, Category = "HttpGPT") - FHttpGPTGenericDelegate RequestFailed; + UPROPERTY(BlueprintAssignable, Category = "HttpGPT") + FHttpGPTGenericDelegate RequestFailed; - UPROPERTY(BlueprintAssignable, Category = "HttpGPT") - FHttpGPTGenericDelegate RequestSent; + UPROPERTY(BlueprintAssignable, Category = "HttpGPT") + FHttpGPTGenericDelegate RequestSent; - virtual void Activate() override; + virtual void Activate() override; - UFUNCTION(BlueprintCallable, Category = "HttpGPT", meta = (DisplayName = "Stop HttpGPT Task")) - void StopHttpGPTTask(); + UFUNCTION(BlueprintCallable, Category = "HttpGPT", meta = (DisplayName = "Stop HttpGPT Task")) + void StopHttpGPTTask(); - virtual void SetReadyToDestroy() override; + virtual void SetReadyToDestroy() override; - UFUNCTION(BlueprintPure, Category = "HttpGPT", Meta = (DisplayName = "Get API Key")) - const FHttpGPTCommonOptions GetCommonOptions() const; + UFUNCTION(BlueprintPure, Category = "HttpGPT", Meta = (DisplayName = "Get API Key")) + const FHttpGPTCommonOptions GetCommonOptions() const; protected: - TSharedPtr HttpRequest; - FHttpGPTCommonOptions CommonOptions; + TSharedPtr HttpRequest; + FHttpGPTCommonOptions CommonOptions; - mutable FCriticalSection Mutex; + mutable FCriticalSection Mutex; - virtual bool CanActivateTask() const; - virtual bool CanBindProgress() const; - virtual FString GetEndpointURL() const; + virtual bool CanActivateTask() const; + virtual bool CanBindProgress() const; + virtual FString GetEndpointURL() const; - void SendRequest(); + void SendRequest(); - /* Return true if contains error */ - const bool CheckError(const TSharedPtr& JsonObject, FHttpGPTCommonError& OutputError) const; + /* Return true if contains error */ + const bool CheckError(const TSharedPtr& JsonObject, FHttpGPTCommonError& OutputError) const; - void InitializeRequest(); - void BindRequestCallbacks(); + void InitializeRequest(); + void BindRequestCallbacks(); - virtual FString SetRequestContent() { return FString(); }; - virtual void OnProgressUpdated(const FString& Content, int32 BytesSent, int32 BytesReceived) {}; - virtual void OnProgressCompleted(const FString& Content, const bool bWasSuccessful) {}; + virtual FString SetRequestContent() { return FString(); }; - bool bInitialized = false; - bool bIsReadyToDestroy = false; - bool bIsTaskActive = false; + virtual void OnProgressUpdated(const FString& Content, int32 BytesSent, int32 BytesReceived) + { + }; + + virtual void OnProgressCompleted(const FString& Content, const bool bWasSuccessful) + { + }; + + bool bInitialized = false; + bool bIsReadyToDestroy = false; + bool bIsTaskActive = false; #if WITH_EDITOR - bool bIsEditorTask = false; - bool bEndingPIE = false; + bool bIsEditorTask = false; + bool bEndingPIE = false; - virtual void PrePIEEnded(bool bIsSimulating); + virtual void PrePIEEnded(bool bIsSimulating); #endif }; UCLASS(NotPlaceable, Category = "HttpGPT") class HTTPGPTCOMMONMODULE_API UHttpGPTTaskStatus final : public UBlueprintFunctionLibrary { - GENERATED_BODY() + GENERATED_BODY() public: - UFUNCTION(BlueprintPure, Category = "HttpGPT") - static bool IsTaskActive(const UHttpGPTBaseTask* Test); + UFUNCTION(BlueprintPure, Category = "HttpGPT") + static bool IsTaskActive(const UHttpGPTBaseTask* Test); - UFUNCTION(BlueprintPure, Category = "HttpGPT") - static bool IsTaskReadyToDestroy(const UHttpGPTBaseTask* Test); + UFUNCTION(BlueprintPure, Category = "HttpGPT") + static bool IsTaskReadyToDestroy(const UHttpGPTBaseTask* Test); - UFUNCTION(BlueprintPure, Category = "HttpGPT") - static bool IsTaskStillValid(const UHttpGPTBaseTask* Test); + UFUNCTION(BlueprintPure, Category = "HttpGPT") + static bool IsTaskStillValid(const UHttpGPTBaseTask* Test); }; diff --git a/Source/HttpGPTCommonModule/Public/Utils/HttpGPTHelper.h b/Source/HttpGPTCommonModule/Public/Utils/HttpGPTHelper.h index 0c0fc88..aa5c69b 100644 --- a/Source/HttpGPTCommonModule/Public/Utils/HttpGPTHelper.h +++ b/Source/HttpGPTCommonModule/Public/Utils/HttpGPTHelper.h @@ -16,45 +16,46 @@ UCLASS(NotPlaceable, Category = "HttpGPT") class HTTPGPTCOMMONMODULE_API UHttpGPTHelper final : public UBlueprintFunctionLibrary { - GENERATED_BODY() + GENERATED_BODY() public: - UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert HttpGPT Model to Name")) - static const FName ModelToName(const EHttpGPTChatModel Model); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert HttpGPT Model to Name")) + static const FName ModelToName(const EHttpGPTChatModel Model); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert Name to HttpGPT Model")) - static const EHttpGPTChatModel NameToModel(const FName Model); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert Name to HttpGPT Model")) + static const EHttpGPTChatModel NameToModel(const FName Model); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert HttpGPT Role to Name")) - static const FName RoleToName(const EHttpGPTChatRole Role); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert HttpGPT Role to Name")) + static const FName RoleToName(const EHttpGPTChatRole Role); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert Name to HttpGPT Role")) - static const EHttpGPTChatRole NameToRole(const FName Role); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert Name to HttpGPT Role")) + static const EHttpGPTChatRole NameToRole(const FName Role); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert HttpGPT Param Type to Name")) - static const FName PropertyTypeToName(const EHttpGPTPropertyType Type); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert HttpGPT Param Type to Name")) + static const FName PropertyTypeToName(const EHttpGPTPropertyType Type); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert Name to HttpGPT Param Type")) - static const EHttpGPTPropertyType NameToPropertyType(const FName Type); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Convert Name to HttpGPT Param Type")) + static const EHttpGPTPropertyType NameToPropertyType(const FName Type); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Get Available GPT Models")) - static const TArray GetAvailableGPTModels(); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Get Available GPT Models")) + static const TArray GetAvailableGPTModels(); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Get Endpoint for Model")) - static const FString GetEndpointForModel(const EHttpGPTChatModel Model, const bool bIsAzureOpenAI = false, const FString& AzureOpenAIAPIVersion = TEXT("None")); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Get Endpoint for Model")) + static const FString GetEndpointForModel(const EHttpGPTChatModel Model, const bool bIsAzureOpenAI = false, + const FString& AzureOpenAIAPIVersion = TEXT("None")); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Model Supports Chat")) - static const bool ModelSupportsChat(const EHttpGPTChatModel Model); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat", meta = (DisplayName = "Model Supports Chat")) + static const bool ModelSupportsChat(const EHttpGPTChatModel Model); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Image", meta = (DisplayName = "Convert HttpGPT Size to Name")) - static const FName SizeToName(const EHttpGPTImageSize Size); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Image", meta = (DisplayName = "Convert HttpGPT Size to Name")) + static const FName SizeToName(const EHttpGPTImageSize Size); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Image", meta = (DisplayName = "Convert Name to HttpGPT Size")) - static const EHttpGPTImageSize NameToSize(const FName Size); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Image", meta = (DisplayName = "Convert Name to HttpGPT Size")) + static const EHttpGPTImageSize NameToSize(const FName Size); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Image", meta = (DisplayName = "Convert HttpGPT Format to Name")) - static const FName FormatToName(const EHttpGPTResponseFormat Format); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Image", meta = (DisplayName = "Convert HttpGPT Format to Name")) + static const FName FormatToName(const EHttpGPTResponseFormat Format); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Image", meta = (DisplayName = "Convert Name to HttpGPT Format")) - static const EHttpGPTResponseFormat NameToFormat(const FName Format); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Image", meta = (DisplayName = "Convert Name to HttpGPT Format")) + static const EHttpGPTResponseFormat NameToFormat(const FName Format); }; diff --git a/Source/HttpGPTEditorModule/HttpGPTEditorModule.Build.cs b/Source/HttpGPTEditorModule/HttpGPTEditorModule.Build.cs index 23c567b..59bcfea 100644 --- a/Source/HttpGPTEditorModule/HttpGPTEditorModule.Build.cs +++ b/Source/HttpGPTEditorModule/HttpGPTEditorModule.Build.cs @@ -2,38 +2,37 @@ // Year: 2023 // Repo: https://github.com/lucoiso/UEHttpGPT -using System.IO; using UnrealBuildTool; public class HttpGPTEditorModule : ModuleRules { - public HttpGPTEditorModule(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - CppStandard = CppStandardVersion.Cpp17; + public HttpGPTEditorModule(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + CppStandard = CppStandardVersion.Cpp17; - PublicDependencyModuleNames.AddRange(new[] - { - "Core" - }); + PublicDependencyModuleNames.AddRange(new[] + { + "Core" + }); - PrivateDependencyModuleNames.AddRange(new[] - { - "Engine", - "CoreUObject", - "InputCore", - "Slate", - "SlateCore", - "UnrealEd", - "ToolMenus", - "EditorStyle", - "WorkspaceMenuStructure", - "Projects", - "AssetRegistry", - "HttpGPTCommonModule", - "HttpGPTChatModule", - "HttpGPTImageModule", - "Json" - }); - } + PrivateDependencyModuleNames.AddRange(new[] + { + "Engine", + "CoreUObject", + "InputCore", + "Slate", + "SlateCore", + "UnrealEd", + "ToolMenus", + "EditorStyle", + "WorkspaceMenuStructure", + "Projects", + "AssetRegistry", + "HttpGPTCommonModule", + "HttpGPTChatModule", + "HttpGPTImageModule", + "Json" + }); + } } \ No newline at end of file diff --git a/Source/HttpGPTEditorModule/Private/Chat/HttpGPTMessagingHandler.cpp b/Source/HttpGPTEditorModule/Private/Chat/HttpGPTMessagingHandler.cpp index dc1c092..d93ffa1 100644 --- a/Source/HttpGPTEditorModule/Private/Chat/HttpGPTMessagingHandler.cpp +++ b/Source/HttpGPTEditorModule/Private/Chat/HttpGPTMessagingHandler.cpp @@ -16,69 +16,67 @@ UHttpGPTMessagingHandler::UHttpGPTMessagingHandler(const FObjectInitializer& Obj void UHttpGPTMessagingHandler::RequestSent() { - OnMessageContentUpdated.ExecuteIfBound("Waiting for response..."); + OnMessageContentUpdated.ExecuteIfBound("Waiting for response..."); } void UHttpGPTMessagingHandler::RequestFailed() { - OnMessageContentUpdated.ExecuteIfBound("Request Failed.\nPlease check the logs. (Enable internal logs in Project Settings -> Plugins -> HttpGPT)."); - Destroy(); + OnMessageContentUpdated.ExecuteIfBound( + "Request Failed.\nPlease check the logs. (Enable internal logs in Project Settings -> Plugins -> HttpGPT)."); + Destroy(); } void UHttpGPTMessagingHandler::ProcessUpdated(const FHttpGPTChatResponse& Response) { - ProcessResponse(Response); + ProcessResponse(Response); } void UHttpGPTMessagingHandler::ProcessCompleted(const FHttpGPTChatResponse& Response) { - ProcessResponse(Response); - Destroy(); + ProcessResponse(Response); + Destroy(); } void UHttpGPTMessagingHandler::ProcessResponse(const FHttpGPTChatResponse& Response) { - bool bScrollToEnd = false; - if (ScrollBoxReference.IsValid()) - { - bScrollToEnd = FMath::Abs(ScrollBoxReference->GetScrollOffsetOfEnd() - ScrollBoxReference->GetScrollOffset()) <= 8.f; - } + bool bScrollToEnd = false; + if (ScrollBoxReference.IsValid()) + { + bScrollToEnd = FMath::Abs(ScrollBoxReference->GetScrollOffsetOfEnd() - ScrollBoxReference->GetScrollOffset()) <= 8.f; + } - if (!Response.bSuccess) - { - const FStringFormatOrderedArguments Arguments_ErrorDetails{ - 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 - }; + if (!Response.bSuccess) + { + const FStringFormatOrderedArguments Arguments_ErrorDetails{ + 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)); - } - else if (Response.bSuccess && !HttpGPT::Internal::HasEmptyParam(Response.Choices)) - { - OnMessageContentUpdated.ExecuteIfBound(Response.Choices[0].Message.Content); - } - else - { - return; - } + 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)) + { + OnMessageContentUpdated.ExecuteIfBound(Response.Choices[0].Message.Content); + } + else + { + return; + } - if (ScrollBoxReference.IsValid() && bScrollToEnd) - { - ScrollBoxReference->ScrollToEnd(); - } + if (ScrollBoxReference.IsValid() && bScrollToEnd) + { + ScrollBoxReference->ScrollToEnd(); + } } void UHttpGPTMessagingHandler::Destroy() { - ClearFlags(RF_Standalone); + ClearFlags(RF_Standalone); #if ENGINE_MAJOR_VERSION >= 5 - MarkAsGarbage(); + MarkAsGarbage(); #else MarkPendingKill(); #endif -} \ No newline at end of file +} diff --git a/Source/HttpGPTEditorModule/Private/Chat/HttpGPTMessagingHandler.h b/Source/HttpGPTEditorModule/Private/Chat/HttpGPTMessagingHandler.h index d4bab7a..74bdd09 100644 --- a/Source/HttpGPTEditorModule/Private/Chat/HttpGPTMessagingHandler.h +++ b/Source/HttpGPTEditorModule/Private/Chat/HttpGPTMessagingHandler.h @@ -14,29 +14,29 @@ DECLARE_DELEGATE_OneParam(FMessageContentUpdated, FString); UCLASS(MinimalAPI, NotBlueprintable, NotPlaceable, Category = "Implementation") class UHttpGPTMessagingHandler : public UObject { - GENERATED_BODY() + GENERATED_BODY() public: - explicit UHttpGPTMessagingHandler(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); + explicit UHttpGPTMessagingHandler(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); - FMessageContentUpdated OnMessageContentUpdated; + FMessageContentUpdated OnMessageContentUpdated; - UFUNCTION() - void RequestSent(); + UFUNCTION() + void RequestSent(); - UFUNCTION() - void RequestFailed(); + UFUNCTION() + void RequestFailed(); - UFUNCTION() - void ProcessUpdated(const FHttpGPTChatResponse& Response); + UFUNCTION() + void ProcessUpdated(const FHttpGPTChatResponse& Response); - UFUNCTION() - void ProcessCompleted(const FHttpGPTChatResponse& Response); + UFUNCTION() + void ProcessCompleted(const FHttpGPTChatResponse& Response); - TSharedPtr ScrollBoxReference; + TSharedPtr ScrollBoxReference; - void Destroy(); + void Destroy(); private: - void ProcessResponse(const FHttpGPTChatResponse& Response); -}; \ No newline at end of file + void ProcessResponse(const FHttpGPTChatResponse& Response); +}; diff --git a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatItem.cpp b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatItem.cpp index db88be3..7d6e94c 100644 --- a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatItem.cpp +++ b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatItem.cpp @@ -8,114 +8,99 @@ void SHttpGPTChatItem::Construct(const FArguments& InArgs) { - MessageRole = InArgs._MessageRole; - InputText = InArgs._InputText; - - if (MessageRole == EHttpGPTChatRole::Assistant) - { - MessagingHandlerObject = NewObject(); - MessagingHandlerObject->SetFlags(RF_Standalone); - MessagingHandlerObject->ScrollBoxReference = InArgs._ScrollBox; - - MessagingHandlerObject->OnMessageContentUpdated.BindLambda( - [this](FString Content) - { - if (!Message.IsValid()) - { - return; - } + MessageRole = InArgs._MessageRole; + InputText = InArgs._InputText; + + if (MessageRole == EHttpGPTChatRole::Assistant) + { + MessagingHandlerObject = NewObject(); + MessagingHandlerObject->SetFlags(RF_Standalone); + MessagingHandlerObject->ScrollBoxReference = InArgs._ScrollBox; + + MessagingHandlerObject->OnMessageContentUpdated.BindLambda([this](FString Content) + { + if (!Message.IsValid()) + { + return; + } #if ENGINE_MAJOR_VERSION > 5 || (ENGINE_MAJOR_VERSION == 5 && ENGINE_MINOR_VERSION >= 1) - const FTextSelection SelectedText = Message->GetSelection(); - Message->SetText(FText::FromString(Content)); - Message->SelectText(SelectedText.GetBeginning(), SelectedText.GetEnd()); + const FTextSelection SelectedText = Message->GetSelection(); + Message->SetText(FText::FromString(Content)); + Message->SelectText(SelectedText.GetBeginning(), SelectedText.GetEnd()); #else Message->SetText(FText::FromString(Content)); #endif - } - ); - } - - ChildSlot - [ - ConstructContent() - ]; + }); + } + + ChildSlot + [ + ConstructContent() + ]; } static FSlateColor& operator*=(FSlateColor& Lhs, const float Rhs) { - FLinearColor NewColor = Lhs.GetSpecifiedColor() * Rhs; - NewColor.A = 1.f; - Lhs = FSlateColor(NewColor); + FLinearColor NewColor = Lhs.GetSpecifiedColor() * Rhs; + NewColor.A = 1.f; + Lhs = FSlateColor(NewColor); - return Lhs; + return Lhs; } TSharedRef SHttpGPTChatItem::ConstructContent() { - constexpr float SlotPadding = 4.0f; - constexpr float PaddingMultiplier = 32.0f; - - FText RoleText = FText::FromString(TEXT("User:")); - FMargin BoxMargin(SlotPadding * PaddingMultiplier, SlotPadding, SlotPadding, SlotPadding); - FSlateColor MessageColor(FLinearColor::White); - - if (MessageRole == EHttpGPTChatRole::Assistant) - { - RoleText = FText::FromString(TEXT("Assistant:")); - BoxMargin = FMargin(SlotPadding, SlotPadding, SlotPadding * PaddingMultiplier, SlotPadding); - MessageColor *= 0.3f; - } - else if (MessageRole == EHttpGPTChatRole::System) - { - RoleText = FText::FromString(TEXT("System:")); - BoxMargin = FMargin(SlotPadding * PaddingMultiplier * 0.5f, SlotPadding); - MessageColor *= 0.f; - } - - const FMargin MessageMargin(SlotPadding * 4.f, SlotPadding, SlotPadding, SlotPadding); + constexpr float SlotPadding = 4.0f; + constexpr float PaddingMultiplier = 32.0f; + + FText RoleText = FText::FromString(TEXT("User:")); + FMargin BoxMargin(SlotPadding * PaddingMultiplier, SlotPadding, SlotPadding, SlotPadding); + FSlateColor MessageColor(FLinearColor::White); + + if (MessageRole == EHttpGPTChatRole::Assistant) + { + RoleText = FText::FromString(TEXT("Assistant:")); + BoxMargin = FMargin(SlotPadding, SlotPadding, SlotPadding * PaddingMultiplier, SlotPadding); + MessageColor *= 0.3f; + } + else if (MessageRole == EHttpGPTChatRole::System) + { + RoleText = FText::FromString(TEXT("System:")); + BoxMargin = FMargin(SlotPadding * PaddingMultiplier * 0.5f, SlotPadding); + MessageColor *= 0.f; + } + + const FMargin MessageMargin(SlotPadding * 4.f, SlotPadding, SlotPadding, SlotPadding); #if ENGINE_MAJOR_VERSION < 5 using FAppStyle = FEditorStyle; #endif - return SNew(SBox) - .Padding(BoxMargin) - [ - SNew(SBorder) - .BorderImage(FAppStyle::Get().GetBrush("Menu.Background")) - .BorderBackgroundColor(MessageColor) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .Padding(SlotPadding) - .AutoHeight() - [ - SAssignNew(Role, STextBlock) - .Font(FCoreStyle::GetDefaultFontStyle("Bold", 10)) - .Text(RoleText) - ] - + SVerticalBox::Slot() - .Padding(MessageMargin) - .FillHeight(1.f) - [ - SAssignNew(Message, SMultiLineEditableText) - .AllowMultiLine(true) - .AutoWrapText(true) - .IsReadOnly(true) - .AllowContextMenu(true) - .Text(FText::FromString(InputText)) - ] - ] - ]; + return SNew(SBox).Padding(BoxMargin) + [ + SNew(SBorder).BorderImage(FAppStyle::Get().GetBrush("Menu.Background")).BorderBackgroundColor(MessageColor) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot().Padding(SlotPadding).AutoHeight() + [ + SAssignNew(Role, STextBlock).Font(FCoreStyle::GetDefaultFontStyle("Bold", 10)).Text(RoleText) + ] + + SVerticalBox::Slot().Padding(MessageMargin).FillHeight(1.f) + [ + SAssignNew(Message, SMultiLineEditableText).AllowMultiLine(true).AutoWrapText(true).IsReadOnly(true).AllowContextMenu(true).Text( + FText::FromString(InputText)) + ] + ] + ]; } FString SHttpGPTChatItem::GetRoleText() const { - return Role.IsValid() ? Role->GetText().ToString() : FString(); + return Role.IsValid() ? Role->GetText().ToString() : FString(); } FString SHttpGPTChatItem::GetMessageText() const { - return Message.IsValid() ? Message->GetText().ToString() : FString(); -} \ No newline at end of file + return Message.IsValid() ? Message->GetText().ToString() : FString(); +} diff --git a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatItem.h b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatItem.h index 2a6ff2c..274f711 100644 --- a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatItem.h +++ b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatItem.h @@ -13,29 +13,30 @@ static const FName NewSessionName = TEXT("New Session"); class SHttpGPTChatItem final : public SCompoundWidget { public: - SLATE_BEGIN_ARGS(SHttpGPTChatItem) : _MessageRole(), _InputText(), _ScrollBox() - { - } - SLATE_ARGUMENT(EHttpGPTChatRole, MessageRole) - SLATE_ARGUMENT(FString, InputText) - SLATE_ARGUMENT(TSharedPtr, ScrollBox) - SLATE_END_ARGS() + SLATE_BEGIN_ARGS(SHttpGPTChatItem) : _MessageRole(), _InputText(), _ScrollBox() + { + } - void Construct(const FArguments& InArgs); + SLATE_ARGUMENT(EHttpGPTChatRole, MessageRole) + SLATE_ARGUMENT(FString, InputText) + SLATE_ARGUMENT(TSharedPtr, ScrollBox) + SLATE_END_ARGS() - FString GetRoleText() const; - FString GetMessageText() const; + void Construct(const FArguments& InArgs); - TWeakObjectPtr MessagingHandlerObject; + FString GetRoleText() const; + FString GetMessageText() const; + + TWeakObjectPtr MessagingHandlerObject; private: - TSharedRef ConstructContent(); + TSharedRef ConstructContent(); - EHttpGPTChatRole MessageRole; - FString InputText; + EHttpGPTChatRole MessageRole; + FString InputText; - TSharedPtr Role; - TSharedPtr Message; + TSharedPtr Role; + TSharedPtr Message; }; -typedef TSharedPtr SHttpGPTChatItemPtr; \ No newline at end of file +using SHttpGPTChatItemPtr = TSharedPtr; diff --git a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatShell.cpp b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatShell.cpp index bc23841..cd46b1c 100644 --- a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatShell.cpp +++ b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatShell.cpp @@ -8,289 +8,280 @@ #include #include -typedef TDelegate FOnChatSessionNameChanged; +using FOnChatSessionNameChanged = TDelegate; class SHttpGPTChatSessionOption : public STableRow { public: - SLATE_BEGIN_ARGS(SHttpGPTChatSessionOption) - { - } - SLATE_EVENT(FOnChatSessionNameChanged, OnNameChanged) - SLATE_END_ARGS() - - void Construct(const FArguments& InArgs, const TSharedRef& InOwnerTableView, FNamePtr InItem) - { - OnNameChanged = InArgs._OnNameChanged; - Item = InItem; - - STableRow::Construct(STableRow::FArguments() - .Padding(8.f) - .Content() - [ - SAssignNew(SessionName, STextBlock) - .Text(this, &SHttpGPTChatSessionOption::GetName) - .ToolTipText(this, &SHttpGPTChatSessionOption::GetName) - ], InOwnerTableView); - } - - FText GetName() const - { - return FText::FromName(Item.IsValid() ? *Item : TEXT("Invalid")); - } - - void EnableEditMode() - { - TSharedRef TextEntry = - SNew(STextEntryPopup) - .Label(FText::FromString("Rename Session")) - .OnTextCommitted(this, &SHttpGPTChatSessionOption::OnNameCommited); - - FSlateApplication& SlateApp = FSlateApplication::Get(); - - SlateApp.PushMenu( - AsShared(), - FWidgetPath(), - TextEntry, - SlateApp.GetCursorPos(), - FPopupTransitionEffect::TypeInPopup - ); - } + SLATE_BEGIN_ARGS(SHttpGPTChatSessionOption) + { + } + + SLATE_EVENT(FOnChatSessionNameChanged, OnNameChanged) + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs, const TSharedRef& InOwnerTableView, const FNamePtr& InItem) + { + OnNameChanged = InArgs._OnNameChanged; + Item = InItem; + + STableRow::Construct( + STableRow::FArguments().Padding(8.f).Content()[SAssignNew(SessionName, STextBlock) + .Text(this, &SHttpGPTChatSessionOption::GetName) + .ToolTipText(this, &SHttpGPTChatSessionOption::GetName)], InOwnerTableView); + } + + FText GetName() const + { + return FText::FromName(Item.IsValid() ? *Item : TEXT("Invalid")); + } + + void EnableEditMode() + { + const TSharedRef TextEntry = SNew(STextEntryPopup).Label(FText::FromString("Rename Session")).OnTextCommitted( + this, &SHttpGPTChatSessionOption::OnNameCommited); + + FSlateApplication& SlateApp = FSlateApplication::Get(); + + SlateApp.PushMenu(AsShared(), FWidgetPath(), TextEntry, SlateApp.GetCursorPos(), FPopupTransitionEffect::TypeInPopup); + } private: - FNamePtr Item; - FOnChatSessionNameChanged OnNameChanged; - - TSharedPtr SessionName; - - void OnNameCommited(const FText& NewText, ETextCommit::Type CommitInfo) - { - if (!SessionName.IsValid()) - { - return; - } - - if (CommitInfo == ETextCommit::OnEnter) - { - OnNameChanged.ExecuteIfBound(Item, FName(*NewText.ToString())); - *Item = *NewText.ToString(); - - FSlateApplication::Get().DismissAllMenus(); - } - else if (CommitInfo == ETextCommit::OnCleared) - { - FSlateApplication::Get().DismissAllMenus(); - } - } + FNamePtr Item; + FOnChatSessionNameChanged OnNameChanged; + + TSharedPtr SessionName; + + void OnNameCommited(const FText& NewText, const ETextCommit::Type CommitInfo) + { + if (!SessionName.IsValid()) + { + return; + } + + if (CommitInfo == ETextCommit::OnEnter) + { + OnNameChanged.ExecuteIfBound(Item, FName(*NewText.ToString())); + *Item = *NewText.ToString(); + + FSlateApplication::Get().DismissAllMenus(); + } + else if (CommitInfo == ETextCommit::OnCleared) + { + FSlateApplication::Get().DismissAllMenus(); + } + } }; void SHttpGPTChatShell::Construct([[maybe_unused]] const FArguments&) { - ChildSlot - [ - ConstructContent() - ]; + ChildSlot + [ + ConstructContent() + ]; - InitializeChatSessionOptions(); + InitializeChatSessionOptions(); } SHttpGPTChatShell::~SHttpGPTChatShell() = default; TSharedRef SHttpGPTChatShell::ConstructContent() { - return SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .FillWidth(0.2f) - [ - SAssignNew(ChatSessionListView, SListView) - .ListItemsSource(&ChatSessions) - .OnGenerateRow(this, &SHttpGPTChatShell::OnGenerateChatSessionRow) - .OnSelectionChanged(this, &SHttpGPTChatShell::OnChatSessionSelectionChanged) - .SelectionMode(ESelectionMode::Single) - .ClearSelectionOnClick(false) - .OnMouseButtonDoubleClick(this, &SHttpGPTChatShell::OnChatSessionDoubleClicked) - .OnKeyDownHandler(this, &SHttpGPTChatShell::OnChatSessionKeyDown) - - ] - + SHorizontalBox::Slot() - .FillWidth(0.8f) - [ - SAssignNew(ShellBox, SBox) - .HAlign(HAlign_Fill) - .VAlign(VAlign_Fill) - ]; + return SNew(SHorizontalBox) + + SHorizontalBox::Slot().FillWidth(0.2f) + [ + SAssignNew(ChatSessionListView, SListView) + .ListItemsSource(&ChatSessions) + .OnGenerateRow(this, &SHttpGPTChatShell::OnGenerateChatSessionRow) + .OnSelectionChanged(this, &SHttpGPTChatShell::OnChatSessionSelectionChanged) + .SelectionMode(ESelectionMode::Single) + .ClearSelectionOnClick(false) + .OnMouseButtonDoubleClick(this, &SHttpGPTChatShell::OnChatSessionDoubleClicked) + .OnKeyDownHandler(this, &SHttpGPTChatShell::OnChatSessionKeyDown) + + ] + + SHorizontalBox::Slot().FillWidth(0.8f) + [ + SAssignNew(ShellBox, SBox).HAlign(HAlign_Fill).VAlign(VAlign_Fill) + ]; } void SHttpGPTChatShell::InitializeChatSessionOptions() { - ChatSessions.Empty(); - - if (const FString SessionsPath = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("HttpGPT")); FPaths::DirectoryExists(SessionsPath)) - { - TArray FoundFiles; - IFileManager::Get().FindFilesRecursive(FoundFiles, *SessionsPath, TEXT("*.json"), true, false, true); - - TArray FoundBaseFileNames; - Algo::Transform(FoundFiles, FoundBaseFileNames, [](const FString& Iterator) { return FPaths::GetBaseFilename(Iterator); }); - - for (const FString& FileIt : FoundBaseFileNames) - { - ChatSessions.EmplaceAt(FileIt.Equals(NewSessionName.ToString()) ? 0 : ChatSessions.Num(), MakeShared(FileIt)); - } - - if (FoundBaseFileNames.IsEmpty() || !FoundBaseFileNames.Contains(NewSessionName.ToString())) - { - ChatSessions.EmplaceAt(0, MakeShared(NewSessionName)); - } - - InitializeChatSession(ChatSessions[0]); - } - else if (IFileManager::Get().MakeDirectory(*SessionsPath, true)) - { - InitializeChatSessionOptions(); - return; - } - - if (ChatSessionListView.IsValid()) - { - ChatSessionListView->RequestListRefresh(); - } + ChatSessions.Empty(); + + if (const FString SessionsPath = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("HttpGPT")); FPaths::DirectoryExists(SessionsPath)) + { + TArray FoundFiles; + IFileManager::Get().FindFilesRecursive(FoundFiles, *SessionsPath, TEXT("*.json"), true, false, true); + + TArray FoundBaseFileNames; + Algo::Transform(FoundFiles, FoundBaseFileNames, [](const FString& Iterator) + { + return FPaths::GetBaseFilename(Iterator); + }); + + for (const FString& FileIt : FoundBaseFileNames) + { + ChatSessions.EmplaceAt(FileIt.Equals(NewSessionName.ToString()) ? 0 : ChatSessions.Num(), MakeShared(FileIt)); + } + + if (FoundBaseFileNames.IsEmpty() || !FoundBaseFileNames.Contains(NewSessionName.ToString())) + { + ChatSessions.EmplaceAt(0, MakeShared(NewSessionName)); + } + + InitializeChatSession(ChatSessions[0]); + } + else if (IFileManager::Get().MakeDirectory(*SessionsPath, true)) + { + InitializeChatSessionOptions(); + return; + } + + if (ChatSessionListView.IsValid()) + { + ChatSessionListView->RequestListRefresh(); + } } -void SHttpGPTChatShell::InitializeChatSession(FNamePtr InItem) +void SHttpGPTChatShell::InitializeChatSession(const FNamePtr& InItem) { - if (!ShellBox.IsValid()) - { - return; - } - - ShellBox->SetContent(SAssignNew(CurrentView, SHttpGPTChatView).SessionID(*InItem)); - - if (ChatSessionListView.IsValid()) - { - if (!ChatSessionListView->IsItemSelected(InItem)) - { - ChatSessionListView->SetSelection(InItem); - } - - ChatSessionListView->RequestListRefresh(); - } + if (!ShellBox.IsValid()) + { + return; + } + + ShellBox->SetContent(SAssignNew(CurrentView, SHttpGPTChatView).SessionID(*InItem)); + + if (ChatSessionListView.IsValid()) + { + if (!ChatSessionListView->IsItemSelected(InItem)) + { + ChatSessionListView->SetSelection(InItem); + } + + ChatSessionListView->RequestListRefresh(); + } } TSharedRef SHttpGPTChatShell::OnGenerateChatSessionRow(FNamePtr InItem, const TSharedRef& OwnerTable) { - return SNew(SHttpGPTChatSessionOption, OwnerTable, InItem) - .OnNameChanged(this, &SHttpGPTChatShell::OnChatSessionNameChanged); + return SNew(SHttpGPTChatSessionOption, OwnerTable, InItem).OnNameChanged(this, &SHttpGPTChatShell::OnChatSessionNameChanged); } -void SHttpGPTChatShell::OnChatSessionSelectionChanged(FNamePtr InItem, [[maybe_unused]] ESelectInfo::Type SelectInfo) +void SHttpGPTChatShell::OnChatSessionSelectionChanged(const FNamePtr InItem, [[maybe_unused]] ESelectInfo::Type SelectInfo) { - if (!InItem.IsValid()) - { - return; - } + if (!InItem.IsValid()) + { + return; + } - InitializeChatSession(InItem); + InitializeChatSession(InItem); } -void SHttpGPTChatShell::OnChatSessionNameChanged(FNamePtr InItem, const FName& NewName) +void SHttpGPTChatShell::OnChatSessionNameChanged(const FNamePtr InItem, const FName& NewName) { - if (!InItem.IsValid()) - { - return; - } - - if (InItem->IsEqual(NewSessionName)) - { - ChatSessions.EmplaceAt(0, MakeShared(NewSessionName)); - } - - if (const FString SessionPath = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("HttpGPT"), InItem->ToString()); FPaths::FileExists(SessionPath)) - { - const FString NewPath = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("HttpGPT"), NewName.ToString()); - IFileManager::Get().Move(*SessionPath, *NewPath, true, true, false, false); - } - - if (!CurrentView.IsValid()) - { - return; - } - - if (CurrentView.IsValid() && CurrentView->GetSessionID().IsEqual(*InItem)) - { - CurrentView->SetSessionID(NewName); - } - - *InItem = NewName; - - if (ChatSessionListView.IsValid()) - { - ChatSessionListView->RequestListRefresh(); - } + if (!InItem.IsValid()) + { + return; + } + + if (InItem->IsEqual(NewSessionName)) + { + ChatSessions.EmplaceAt(0, MakeShared(NewSessionName)); + } + + if (const FString SessionPath = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("HttpGPT"), InItem->ToString()); FPaths::FileExists(SessionPath)) + { + const FString NewPath = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("HttpGPT"), NewName.ToString()); + IFileManager::Get().Move(*SessionPath, *NewPath, true, true, false, false); + } + + if (!CurrentView.IsValid()) + { + return; + } + + if (CurrentView.IsValid() && CurrentView->GetSessionID().IsEqual(*InItem)) + { + CurrentView->SetSessionID(NewName); + } + + *InItem = NewName; + + if (ChatSessionListView.IsValid()) + { + ChatSessionListView->RequestListRefresh(); + } } -void SHttpGPTChatShell::OnChatSessionDoubleClicked(FNamePtr InItem) +void SHttpGPTChatShell::OnChatSessionDoubleClicked(const FNamePtr InItem) { - if (!InItem.IsValid() || !ChatSessionListView.IsValid()) - { - return; - } - - if (const TSharedPtr Row = ChatSessionListView->WidgetFromItem(InItem); Row.IsValid()) - { - if (const TSharedPtr Session = StaticCastSharedPtr(Row); Session.IsValid()) - { - Session->EnableEditMode(); - ChatSessionListView->RequestListRefresh(); - } - } + if (!InItem.IsValid() || !ChatSessionListView.IsValid()) + { + return; + } + + if (const TSharedPtr Row = ChatSessionListView->WidgetFromItem(InItem); Row.IsValid()) + { + if (const TSharedPtr Session = StaticCastSharedPtr(Row); Session.IsValid()) + { + Session->EnableEditMode(); + ChatSessionListView->RequestListRefresh(); + } + } } FReply SHttpGPTChatShell::OnChatSessionKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent) { - if (!ChatSessionListView.IsValid()) - { - return FReply::Unhandled(); - } - - if (InKeyEvent.GetKey() != EKeys::Delete) - { - return FReply::Unhandled(); - } - - if (FMessageDialog::Open(EAppMsgType::YesNo, FText::FromString("Are you sure you want to delete this session?")) == EAppReturnType::No) - { - return FReply::Unhandled(); - } - - const FNamePtr SelectedItem = ChatSessionListView->GetNumItemsSelected() == 0 ? nullptr : ChatSessionListView->GetSelectedItems()[0]; - if (!SelectedItem.IsValid()) - { - return FReply::Unhandled(); - } - - if (CurrentView.IsValid()) - { - CurrentView->ClearChat(); - } - - if (const FString SessionPath = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("HttpGPT"), SelectedItem->ToString() + TEXT(".json")); FPaths::FileExists(SessionPath)) - { - IFileManager::Get().Delete(*SessionPath, true, true); - } - - if (SelectedItem->IsEqual(NewSessionName) || !ChatSessions.ContainsByPredicate([](const FNamePtr& Item) { return Item->IsEqual(NewSessionName); })) - { - ChatSessions.EmplaceAt(0, MakeShared(NewSessionName)); - } - - ChatSessions.Remove(SelectedItem); - - if (ChatSessionListView.IsValid()) - { - ChatSessionListView->RequestListRefresh(); - ChatSessionListView->SetSelection(ChatSessions[0]); - } - - return FReply::Handled(); -} \ No newline at end of file + if (!ChatSessionListView.IsValid()) + { + return FReply::Unhandled(); + } + + if (InKeyEvent.GetKey() != EKeys::Delete) + { + return FReply::Unhandled(); + } + + if (FMessageDialog::Open(EAppMsgType::YesNo, FText::FromString("Are you sure you want to delete this session?")) == EAppReturnType::No) + { + return FReply::Unhandled(); + } + + const FNamePtr SelectedItem = ChatSessionListView->GetNumItemsSelected() == 0 ? nullptr : ChatSessionListView->GetSelectedItems()[0]; + if (!SelectedItem.IsValid()) + { + return FReply::Unhandled(); + } + + if (CurrentView.IsValid()) + { + CurrentView->ClearChat(); + } + + if (const FString SessionPath = FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("HttpGPT"), SelectedItem->ToString() + TEXT(".json")); + FPaths::FileExists(SessionPath)) + { + IFileManager::Get().Delete(*SessionPath, true, true); + } + + if (SelectedItem->IsEqual(NewSessionName) || !ChatSessions.ContainsByPredicate([](const FNamePtr& Item) + { + return Item->IsEqual(NewSessionName); + })) + { + ChatSessions.EmplaceAt(0, MakeShared(NewSessionName)); + } + + ChatSessions.Remove(SelectedItem); + + if (ChatSessionListView.IsValid()) + { + ChatSessionListView->RequestListRefresh(); + ChatSessionListView->SetSelection(ChatSessions[0]); + } + + return FReply::Handled(); +} diff --git a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatShell.h b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatShell.h index d3a190a..c2e1d30 100644 --- a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatShell.h +++ b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatShell.h @@ -7,35 +7,36 @@ #include #include -typedef TSharedPtr FNamePtr; +using FNamePtr = TSharedPtr; class SHttpGPTChatShell final : public SCompoundWidget { public: - SLATE_USER_ARGS(SHttpGPTChatShell) - { - } - SLATE_END_ARGS() + SLATE_USER_ARGS(SHttpGPTChatShell) + { + } - void Construct(const FArguments& InArgs); - ~SHttpGPTChatShell(); + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs); + virtual ~SHttpGPTChatShell() override; private: - TSharedRef ConstructContent(); + TSharedRef ConstructContent(); - void InitializeChatSessionOptions(); - void InitializeChatSession(FNamePtr InItem); + void InitializeChatSessionOptions(); + void InitializeChatSession(const FNamePtr& InItem); - TSharedPtr ShellBox; - TSharedPtr CurrentView; + TSharedPtr ShellBox; + TSharedPtr CurrentView; - TSharedPtr> ChatSessionListView; - TArray ChatSessions; + TSharedPtr> ChatSessionListView; + TArray ChatSessions; - TSharedRef OnGenerateChatSessionRow(FNamePtr InItem, const TSharedRef& OwnerTable); + TSharedRef OnGenerateChatSessionRow(FNamePtr InItem, const TSharedRef& OwnerTable); - void OnChatSessionSelectionChanged(FNamePtr InItem, ESelectInfo::Type SelectInfo); - void OnChatSessionNameChanged(FNamePtr InItem, const FName& NewName); - void OnChatSessionDoubleClicked(FNamePtr InItem); - FReply OnChatSessionKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent); + void OnChatSessionSelectionChanged(const FNamePtr InItem, ESelectInfo::Type SelectInfo); + void OnChatSessionNameChanged(const FNamePtr InItem, const FName& NewName); + void OnChatSessionDoubleClicked(const FNamePtr InItem); + FReply OnChatSessionKeyDown(const FGeometry& MyGeometry, const FKeyEvent& InKeyEvent); }; diff --git a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatView.cpp b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatView.cpp index 10b6f13..27fba33 100644 --- a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatView.cpp +++ b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatView.cpp @@ -19,386 +19,337 @@ void SHttpGPTChatView::Construct(const FArguments& InArgs) { - SetSessionID(InArgs._SessionID); + SetSessionID(InArgs._SessionID); - ModelsComboBox = SNew(STextComboBox) - .OptionsSource(&AvailableModels) - .ToolTipText(FText::FromString(TEXT("GPT Model"))); + ModelsComboBox = SNew(STextComboBox).OptionsSource(&AvailableModels).ToolTipText(FText::FromString(TEXT("GPT Model"))); - for (const FName& ModelName : UHttpGPTHelper::GetAvailableGPTModels()) - { - AvailableModels.Add(MakeShared(ModelName.ToString())); + for (const FName& ModelName : UHttpGPTHelper::GetAvailableGPTModels()) + { + AvailableModels.Add(MakeShared(ModelName.ToString())); - if (ModelsComboBox.IsValid() && ModelName.IsEqual(UHttpGPTHelper::ModelToName(EHttpGPTChatModel::gpt35turbo))) - { - ModelsComboBox->SetSelectedItem(AvailableModels.Top()); - } - } + if (ModelsComboBox.IsValid() && ModelName.IsEqual(UHttpGPTHelper::ModelToName(EHttpGPTChatModel::gpt35turbo))) + { + ModelsComboBox->SetSelectedItem(AvailableModels.Top()); + } + } - ChildSlot - [ - ConstructContent() - ]; + ChildSlot + [ + ConstructContent() + ]; - LoadChatHistory(); + LoadChatHistory(); } SHttpGPTChatView::~SHttpGPTChatView() { - SaveChatHistory(); + SaveChatHistory(); } bool SHttpGPTChatView::IsSendMessageEnabled() const { - return (!RequestReference.IsValid() || !UHttpGPTTaskStatus::IsTaskActive(RequestReference.Get())) && !HttpGPT::Internal::HasEmptyParam(InputTextBox->GetText()); + return (!RequestReference.IsValid() || !UHttpGPTTaskStatus::IsTaskActive(RequestReference.Get())) && !HttpGPT::Internal::HasEmptyParam( + InputTextBox->GetText()); } bool SHttpGPTChatView::IsClearChatEnabled() const { - return !HttpGPT::Internal::HasEmptyParam(ChatItems); + return !HttpGPT::Internal::HasEmptyParam(ChatItems); } FString SHttpGPTChatView::GetHistoryPath() const { - return FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("HttpGPT"), SessionID.ToString() + TEXT(".json")); + return FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("HttpGPT"), SessionID.ToString() + TEXT(".json")); } void SHttpGPTChatView::SetSessionID(const FName& NewSessionID) { - const FName NewValidSessionID = *FPaths::MakeValidFileName(NewSessionID.ToString()); - if (SessionID == NewValidSessionID) - { - return; - } - - if (SessionID.IsNone()) - { - SessionID = NewValidSessionID; - return; - } - - if (const FString OldPath = GetHistoryPath(); FPaths::FileExists(OldPath)) - { - IFileManager::Get().Delete(*OldPath, true, true, true); - } - - SessionID = NewValidSessionID; - SaveChatHistory(); + const FName NewValidSessionID = *FPaths::MakeValidFileName(NewSessionID.ToString()); + if (SessionID == NewValidSessionID) + { + return; + } + + if (SessionID.IsNone()) + { + SessionID = NewValidSessionID; + return; + } + + if (const FString OldPath = GetHistoryPath(); FPaths::FileExists(OldPath)) + { + IFileManager::Get().Delete(*OldPath, true, true, true); + } + + SessionID = NewValidSessionID; + SaveChatHistory(); } FName SHttpGPTChatView::GetSessionID() const { - return SessionID; + return SessionID; } void SHttpGPTChatView::ClearChat() { - ChatItems.Empty(); - if (ChatBox.IsValid()) - { - ChatBox->ClearChildren(); - } - - if (RequestReference.IsValid()) - { - RequestReference->StopHttpGPTTask(); - RequestReference.Reset(); - } + ChatItems.Empty(); + if (ChatBox.IsValid()) + { + ChatBox->ClearChildren(); + } + + if (RequestReference.IsValid()) + { + RequestReference->StopHttpGPTTask(); + RequestReference.Reset(); + } } TSharedRef SHttpGPTChatView::ConstructContent() { - constexpr float SlotPadding = 4.0f; - - return SNew(SVerticalBox) - + SVerticalBox::Slot() - .Padding(SlotPadding) - .FillHeight(1.f) - [ - SAssignNew(ChatScrollBox, SScrollBox) - + SScrollBox::Slot() - [ - SAssignNew(ChatBox, SVerticalBox) - ] - ] - + SVerticalBox::Slot() - .Padding(SlotPadding) - .AutoHeight() - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .Padding(SlotPadding) - .FillWidth(1.f) - [ - SAssignNew(InputTextBox, SEditableTextBox) - .AllowContextMenu(true) - .IsReadOnly(false) - .OnTextCommitted_Lambda( - [this]([[maybe_unused]] const FText& Text, ETextCommit::Type CommitType) - { - if (IsSendMessageEnabled() && CommitType == ETextCommit::OnEnter) - { - HandleSendMessageButton(EHttpGPTChatRole::User); - } - } - ) - ] - + SHorizontalBox::Slot() - .Padding(SlotPadding) - .AutoWidth() - [ - SNew(SButton) - .Text(FText::FromString(TEXT("Send"))) - .ToolTipText(FText::FromString(TEXT("Send Message"))) - .OnClicked(this, &SHttpGPTChatView::HandleSendMessageButton, EHttpGPTChatRole::User) - .IsEnabled(this, &SHttpGPTChatView::IsSendMessageEnabled) - ] - + SHorizontalBox::Slot() - .Padding(SlotPadding) - .AutoWidth() - [ - SNew(SButton) - .Text(FText::FromString(TEXT("System"))) - .ToolTipText(FText::FromString(TEXT("Send Message as System Context"))) - .OnClicked(this, &SHttpGPTChatView::HandleSendMessageButton, EHttpGPTChatRole::System) - .IsEnabled(this, &SHttpGPTChatView::IsSendMessageEnabled) - ] - + SHorizontalBox::Slot() - .Padding(SlotPadding) - .AutoWidth() - [ - ModelsComboBox.ToSharedRef() - ] - + SHorizontalBox::Slot() - .Padding(SlotPadding) - .AutoWidth() - [ - SNew(SButton) - .Text(FText::FromString(TEXT("Clear"))) - .ToolTipText(FText::FromString(TEXT("Clear Chat History"))) - .OnClicked(this, &SHttpGPTChatView::HandleClearChatButton) - .IsEnabled(this, &SHttpGPTChatView::IsClearChatEnabled) - ] - ]; + constexpr float SlotPadding = 4.0f; + + return SNew(SVerticalBox) + + SVerticalBox::Slot().Padding(SlotPadding).FillHeight(1.f) + [ + SAssignNew(ChatScrollBox, SScrollBox) + + SScrollBox::Slot() + [ + SAssignNew(ChatBox, SVerticalBox) + ] + ] + + SVerticalBox::Slot().Padding(SlotPadding).AutoHeight() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot().Padding(SlotPadding).FillWidth(1.f) + [ + SAssignNew(InputTextBox, SEditableTextBox).AllowContextMenu(true).IsReadOnly(false).OnTextCommitted_Lambda( + [this]([[maybe_unused]] const FText& Text, ETextCommit::Type CommitType) + { + if (IsSendMessageEnabled() && CommitType == ETextCommit::OnEnter) + { + HandleSendMessageButton(EHttpGPTChatRole::User); + } + }) + ] + + SHorizontalBox::Slot().Padding(SlotPadding).AutoWidth() + [ + SNew(SButton) + .Text(FText::FromString(TEXT("Send"))) + .ToolTipText(FText::FromString(TEXT("Send Message"))) + .OnClicked(this, &SHttpGPTChatView::HandleSendMessageButton, EHttpGPTChatRole::User) + .IsEnabled(this, &SHttpGPTChatView::IsSendMessageEnabled) + ] + + SHorizontalBox::Slot().Padding(SlotPadding).AutoWidth() + [ + SNew(SButton) + .Text(FText::FromString(TEXT("System"))) + .ToolTipText(FText::FromString(TEXT("Send Message as System Context"))) + .OnClicked(this, &SHttpGPTChatView::HandleSendMessageButton, EHttpGPTChatRole::System) + .IsEnabled(this, &SHttpGPTChatView::IsSendMessageEnabled) + ] + + SHorizontalBox::Slot().Padding(SlotPadding).AutoWidth() + [ + ModelsComboBox.ToSharedRef() + ] + + SHorizontalBox::Slot().Padding(SlotPadding).AutoWidth() + [ + SNew(SButton) + .Text(FText::FromString(TEXT("Clear"))) + .ToolTipText(FText::FromString(TEXT("Clear Chat History"))) + .OnClicked(this, &SHttpGPTChatView::HandleClearChatButton) + .IsEnabled(this, &SHttpGPTChatView::IsClearChatEnabled) + ] + ]; } FReply SHttpGPTChatView::HandleSendMessageButton(const EHttpGPTChatRole Role) { - const SHttpGPTChatItemPtr NewMessage = SNew(SHttpGPTChatItem) - .MessageRole(Role) - .InputText(InputTextBox->GetText().ToString()); - - ChatBox->AddSlot() - .AutoHeight() - [ - NewMessage.ToSharedRef() - ]; - ChatItems.Add(NewMessage); - - if (Role == EHttpGPTChatRole::System) - { - ChatScrollBox->ScrollToEnd(); - InputTextBox->SetText(FText::GetEmpty()); - - return FReply::Handled(); - } - - const SHttpGPTChatItemPtr AssistantMessage = SNew(SHttpGPTChatItem) - .MessageRole(EHttpGPTChatRole::Assistant) - .ScrollBox(ChatScrollBox); - - FHttpGPTChatOptions Options; - Options.Model = UHttpGPTHelper::NameToModel(*(*ModelsComboBox->GetSelectedItem().Get())); - Options.bStream = true; - - RequestReference = UHttpGPTChatRequest::EditorTask(GetChatHistory(), Options); - 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(); - - ChatBox->AddSlot() - .AutoHeight() - [ - AssistantMessage.ToSharedRef() - ]; - ChatItems.Add(AssistantMessage); - - ChatScrollBox->ScrollToEnd(); - InputTextBox->SetText(FText::GetEmpty()); - - return FReply::Handled(); + const SHttpGPTChatItemPtr NewMessage = SNew(SHttpGPTChatItem).MessageRole(Role).InputText(InputTextBox->GetText().ToString()); + + ChatBox->AddSlot().AutoHeight()[NewMessage.ToSharedRef()]; + ChatItems.Add(NewMessage); + + if (Role == EHttpGPTChatRole::System) + { + ChatScrollBox->ScrollToEnd(); + InputTextBox->SetText(FText::GetEmpty()); + + return FReply::Handled(); + } + + const SHttpGPTChatItemPtr AssistantMessage = SNew(SHttpGPTChatItem).MessageRole(EHttpGPTChatRole::Assistant).ScrollBox(ChatScrollBox); + + FHttpGPTChatOptions Options; + Options.Model = UHttpGPTHelper::NameToModel(*(*ModelsComboBox->GetSelectedItem().Get())); + Options.bStream = true; + + RequestReference = UHttpGPTChatRequest::EditorTask(GetChatHistory(), Options); + 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(); + + ChatBox->AddSlot().AutoHeight()[AssistantMessage.ToSharedRef()]; + ChatItems.Add(AssistantMessage); + + ChatScrollBox->ScrollToEnd(); + InputTextBox->SetText(FText::GetEmpty()); + + return FReply::Handled(); } FReply SHttpGPTChatView::HandleClearChatButton() { - ClearChat(); - return FReply::Handled(); + ClearChat(); + return FReply::Handled(); } TArray SHttpGPTChatView::GetChatHistory() const { - TArray Output - { - FHttpGPTChatMessage(EHttpGPTChatRole::System, GetDefaultSystemContext()) - }; - - for (const SHttpGPTChatItemPtr& Item : ChatItems) - { - FString RoleText = Item->GetRoleText(); - RoleText.RemoveFromEnd(TEXT(":")); - Output.Add(FHttpGPTChatMessage(*RoleText, Item->GetMessageText())); - } - - return Output; + TArray Output{FHttpGPTChatMessage(EHttpGPTChatRole::System, GetDefaultSystemContext())}; + + for (const SHttpGPTChatItemPtr& Item : ChatItems) + { + FString RoleText = Item->GetRoleText(); + RoleText.RemoveFromEnd(TEXT(":")); + Output.Add(FHttpGPTChatMessage(*RoleText, Item->GetMessageText())); + } + + return Output; } FString SHttpGPTChatView::GetDefaultSystemContext() const { - if (const UHttpGPTSettings* const Settings = UHttpGPTSettings::Get(); Settings->bUseCustomSystemContext) - { - return Settings->CustomSystemContext; - } - - FString SupportedModels; - for (const FTextDisplayStringPtr& Model : AvailableModels) - { - SupportedModels.Append(*Model.Get() + ", "); - } - SupportedModels.RemoveFromEnd(", "); - - const TSharedPtr PluginInterface = IPluginManager::Get().FindPlugin("HttpGPT"); - - const FString PluginShortName = "HttpGPT"; - const FString EngineVersion = FString::Printf(TEXT("%d.%d"), ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION); - - const FStringFormatOrderedArguments Arguments_PluginInfo{ - EngineVersion, - PluginShortName, - PluginInterface->GetDescriptor().VersionName, - PluginInterface->GetDescriptor().CreatedBy, - PluginInterface->GetDescriptor().Description - }; - - const FString PluginInformation = FString::Format(TEXT("You are in the Unreal Engine {0} plugin {1} version {2}, which was developed by {3}. The description of HttpGPT is: \"{4}\""), Arguments_PluginInfo); - - const FStringFormatOrderedArguments Arguments_SupportInfo{ - PluginInterface->GetDescriptor().DocsURL, - PluginInterface->GetDescriptor().SupportURL, - }; - - const FString PluginSupport = FString::Format(TEXT("You can find the HttpGPT documentation at {0} and support at {1}."), Arguments_SupportInfo); - - const FStringFormatOrderedArguments Arguments_Models{ - *ModelsComboBox->GetSelectedItem().Get(), - SupportedModels - }; - - const FString ModelsInformation = FString::Format(TEXT("You're using the model {0} and HttpGPT currently supports all these OpenAI Models: {1}."), Arguments_Models); - - const FStringFormatOrderedArguments Arguments_EngineDocumentation{ - EngineVersion - }; - - const FString EngineDocumentation_General = FString::Format(TEXT("You can find the Unreal Engine {0} general documentation at https://docs.unrealengine.com/{0}/en-US/."), Arguments_EngineDocumentation); - const FString EngineDocumentation_CPP = FString::Format(TEXT("You can find the Unreal Engine {0} API documentation for C++ at https://docs.unrealengine.com/{0}/en-US/API/."), Arguments_EngineDocumentation); - const FString EngineDocumentation_BP = FString::Format(TEXT("You can find the Unreal Engine {0} API documentation for Blueprints at https://docs.unrealengine.com/{0}/en-US/BlueprintAPI/."), Arguments_EngineDocumentation); - - const FStringFormatOrderedArguments Arguments_SystemContext{ - PluginInformation, - PluginSupport, - ModelsInformation, - EngineDocumentation_General, - EngineDocumentation_CPP, - EngineDocumentation_BP - }; - - return FString::Format(TEXT("You are an assistant that will help with the development of projects in Unreal Engine in general.\n{0}\n{1}\n{2}\n{3}\n{4}\n{5}"), Arguments_SystemContext); + if (const UHttpGPTSettings* const Settings = UHttpGPTSettings::Get(); Settings->bUseCustomSystemContext) + { + return Settings->CustomSystemContext; + } + + FString SupportedModels; + for (const FTextDisplayStringPtr& Model : AvailableModels) + { + SupportedModels.Append(*Model.Get() + ", "); + } + SupportedModels.RemoveFromEnd(", "); + + const TSharedPtr PluginInterface = IPluginManager::Get().FindPlugin("HttpGPT"); + + const FString PluginShortName = "HttpGPT"; + const FString EngineVersion = FString::Printf(TEXT("%d.%d"), ENGINE_MAJOR_VERSION, ENGINE_MINOR_VERSION); + + const FStringFormatOrderedArguments Arguments_PluginInfo{ + EngineVersion, PluginShortName, PluginInterface->GetDescriptor().VersionName, PluginInterface->GetDescriptor().CreatedBy, + PluginInterface->GetDescriptor().Description + }; + + const FString PluginInformation = FString::Format( + TEXT("You are in the Unreal Engine {0} plugin {1} version {2}, which was developed by {3}. The description of HttpGPT is: \"{4}\""), + Arguments_PluginInfo); + + const FStringFormatOrderedArguments Arguments_SupportInfo{PluginInterface->GetDescriptor().DocsURL, PluginInterface->GetDescriptor().SupportURL,}; + + const FString PluginSupport = FString::Format(TEXT("You can find the HttpGPT documentation at {0} and support at {1}."), Arguments_SupportInfo); + + const FStringFormatOrderedArguments Arguments_Models{*ModelsComboBox->GetSelectedItem().Get(), SupportedModels}; + + const FString ModelsInformation = FString::Format( + TEXT("You're using the model {0} and HttpGPT currently supports all these OpenAI Models: {1}."), Arguments_Models); + + const FStringFormatOrderedArguments Arguments_EngineDocumentation{EngineVersion}; + + const FString EngineDocumentation_General = FString::Format( + TEXT("You can find the Unreal Engine {0} general documentation at https://docs.unrealengine.com/{0}/en-US/."), Arguments_EngineDocumentation); + const FString EngineDocumentation_CPP = FString::Format( + TEXT("You can find the Unreal Engine {0} API documentation for C++ at https://docs.unrealengine.com/{0}/en-US/API/."), + Arguments_EngineDocumentation); + const FString EngineDocumentation_BP = FString::Format( + TEXT("You can find the Unreal Engine {0} API documentation for Blueprints at https://docs.unrealengine.com/{0}/en-US/BlueprintAPI/."), + Arguments_EngineDocumentation); + + const FStringFormatOrderedArguments Arguments_SystemContext{ + PluginInformation, PluginSupport, ModelsInformation, EngineDocumentation_General, EngineDocumentation_CPP, EngineDocumentation_BP + }; + + return FString::Format( + TEXT("You are an assistant that will help with the development of projects in Unreal Engine in general.\n{0}\n{1}\n{2}\n{3}\n{4}\n{5}"), + Arguments_SystemContext); } void SHttpGPTChatView::LoadChatHistory() { - if (SessionID.IsNone()) - { - return; - } - - if (const FString LoadPath = GetHistoryPath(); FPaths::FileExists(LoadPath)) - { - FString FileContent; - if (!FFileHelper::LoadFileToString(FileContent, *LoadPath)) - { - return; - } - - TSharedPtr JsonParsed; - TSharedRef> Reader = TJsonReaderFactory<>::Create(FileContent); - if (FJsonSerializer::Deserialize(Reader, JsonParsed)) - { - const TArray> SessionData = JsonParsed->GetArrayField("Data"); - for (const TSharedPtr& Item : SessionData) - { - if (const TSharedPtr MessageItObj = Item->AsObject()) - { - if (FString RoleString; MessageItObj->TryGetStringField("role", RoleString)) - { - const EHttpGPTChatRole Role = UHttpGPTHelper::NameToRole(*RoleString); - - if (FString Message; MessageItObj->TryGetStringField("content", Message)) - { - if (Role == EHttpGPTChatRole::System && Message == GetDefaultSystemContext()) - { - continue; - } - - ChatItems.Emplace( - SNew(SHttpGPTChatItem) - .MessageRole(Role) - .InputText(Message) - .ScrollBox(ChatScrollBox) - ); - } - } - } - } - } - } - - for (const SHttpGPTChatItemPtr& Item : ChatItems) - { - ChatBox->AddSlot() - .AutoHeight() - [ - Item.ToSharedRef() - ]; - } + if (SessionID.IsNone()) + { + return; + } + + if (const FString LoadPath = GetHistoryPath(); FPaths::FileExists(LoadPath)) + { + FString FileContent; + if (!FFileHelper::LoadFileToString(FileContent, *LoadPath)) + { + return; + } + + TSharedPtr JsonParsed; + const TSharedRef> Reader = TJsonReaderFactory<>::Create(FileContent); + if (FJsonSerializer::Deserialize(Reader, JsonParsed)) + { + const TArray> SessionData = JsonParsed->GetArrayField("Data"); + for (const TSharedPtr& Item : SessionData) + { + if (const TSharedPtr MessageItObj = Item->AsObject()) + { + if (FString RoleString; MessageItObj->TryGetStringField("role", RoleString)) + { + const EHttpGPTChatRole Role = UHttpGPTHelper::NameToRole(*RoleString); + + if (FString Message; MessageItObj->TryGetStringField("content", Message)) + { + if (Role == EHttpGPTChatRole::System && Message == GetDefaultSystemContext()) + { + continue; + } + + ChatItems.Emplace(SNew(SHttpGPTChatItem).MessageRole(Role).InputText(Message).ScrollBox(ChatScrollBox)); + } + } + } + } + } + } + + for (const SHttpGPTChatItemPtr& Item : ChatItems) + { + ChatBox->AddSlot().AutoHeight()[Item.ToSharedRef()]; + } } void SHttpGPTChatView::SaveChatHistory() const { - if (SessionID.IsNone() || ChatItems.IsEmpty()) - { - return; - } + if (SessionID.IsNone() || ChatItems.IsEmpty()) + { + return; + } - const TSharedPtr JsonRequest = MakeShared(); + const TSharedPtr JsonRequest = MakeShared(); - TArray> Data; - for (const FHttpGPTChatMessage& Item : GetChatHistory()) - { - Data.Add(Item.GetMessage()); - } + TArray> Data; + for (const FHttpGPTChatMessage& Item : GetChatHistory()) + { + Data.Add(Item.GetMessage()); + } - JsonRequest->SetArrayField("Data", Data); + JsonRequest->SetArrayField("Data", Data); - FString RequestContentString; - const TSharedRef> Writer = TJsonWriterFactory<>::Create(&RequestContentString); + FString RequestContentString; + const TSharedRef> Writer = TJsonWriterFactory<>::Create(&RequestContentString); - if (FJsonSerializer::Serialize(JsonRequest.ToSharedRef(), Writer)) - { - FFileHelper::SaveStringToFile(RequestContentString, *GetHistoryPath()); - } -} \ No newline at end of file + if (FJsonSerializer::Serialize(JsonRequest.ToSharedRef(), Writer)) + { + FFileHelper::SaveStringToFile(RequestContentString, *GetHistoryPath()); + } +} diff --git a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatView.h b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatView.h index eacb7d6..4f487c7 100644 --- a/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatView.h +++ b/Source/HttpGPTEditorModule/Private/Chat/SHttpGPTChatView.h @@ -12,47 +12,48 @@ class SHttpGPTChatView final : public SCompoundWidget { public: - SLATE_USER_ARGS(SHttpGPTChatView) - : _SessionID(NAME_None) - { - } - SLATE_ARGUMENT(FName, SessionID) - SLATE_END_ARGS() + SLATE_USER_ARGS(SHttpGPTChatView) + : _SessionID(NAME_None) + { + } - void Construct(const FArguments& InArgs); - ~SHttpGPTChatView(); + SLATE_ARGUMENT(FName, SessionID) + SLATE_END_ARGS() - bool IsSendMessageEnabled() const; - bool IsClearChatEnabled() const; - FString GetHistoryPath() const; + void Construct(const FArguments& InArgs); + virtual ~SHttpGPTChatView() override; - void SetSessionID(const FName& NewSessionID); - FName GetSessionID() const; + bool IsSendMessageEnabled() const; + bool IsClearChatEnabled() const; + FString GetHistoryPath() const; - void ClearChat(); + void SetSessionID(const FName& NewSessionID); + FName GetSessionID() const; + + void ClearChat(); private: - TSharedRef ConstructContent(); + TSharedRef ConstructContent(); - FReply HandleSendMessageButton(const EHttpGPTChatRole Role); - FReply HandleClearChatButton(); + FReply HandleSendMessageButton(const EHttpGPTChatRole Role); + FReply HandleClearChatButton(); - TArray GetChatHistory() const; - FString GetDefaultSystemContext() const; + TArray GetChatHistory() const; + FString GetDefaultSystemContext() const; - void LoadChatHistory(); - void SaveChatHistory() const; + void LoadChatHistory(); + void SaveChatHistory() const; - FName SessionID; + FName SessionID; - TSharedPtr ChatBox; - TArray ChatItems; - TSharedPtr ChatScrollBox; + TSharedPtr ChatBox; + TArray ChatItems; + TSharedPtr ChatScrollBox; - TSharedPtr InputTextBox; + TSharedPtr InputTextBox; - TSharedPtr ModelsComboBox; - TArray AvailableModels; + TSharedPtr ModelsComboBox; + TArray AvailableModels; - TWeakObjectPtr RequestReference; + TWeakObjectPtr RequestReference; }; diff --git a/Source/HttpGPTEditorModule/Private/HttpGPTEditorModule.cpp b/Source/HttpGPTEditorModule/Private/HttpGPTEditorModule.cpp index 836428a..9cc62ea 100644 --- a/Source/HttpGPTEditorModule/Private/HttpGPTEditorModule.cpp +++ b/Source/HttpGPTEditorModule/Private/HttpGPTEditorModule.cpp @@ -17,70 +17,68 @@ static const FName HttpGPTImageGeneratorTabName("HttpGPTImageGenerator"); void FHttpGPTEditorModule::StartupModule() { - const FSimpleDelegate RegisterDelegate = FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FHttpGPTEditorModule::RegisterMenus); - UToolMenus::RegisterStartupCallback(RegisterDelegate); + const FSimpleDelegate RegisterDelegate = FSimpleMulticastDelegate::FDelegate::CreateRaw(this, &FHttpGPTEditorModule::RegisterMenus); + UToolMenus::RegisterStartupCallback(RegisterDelegate); } void FHttpGPTEditorModule::ShutdownModule() { - UToolMenus::UnRegisterStartupCallback(this); - UToolMenus::UnregisterOwner(this); + UToolMenus::UnRegisterStartupCallback(this); + UToolMenus::UnregisterOwner(this); - FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(HttpGPTChatTabName); + FGlobalTabmanager::Get()->UnregisterNomadTabSpawner(HttpGPTChatTabName); } TSharedRef FHttpGPTEditorModule::OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs) const { - const FName TabId = *SpawnTabArgs.GetTabId().ToString(); - - TSharedPtr OutContent; - if (TabId.IsEqual(HttpGPTChatTabName)) - { - OutContent = SNew(SHttpGPTChatShell); - } - else if (TabId.IsEqual(HttpGPTImageGeneratorTabName)) - { - OutContent = SNew(SHttpGPTImageGenView); - } - - if (OutContent.IsValid()) - { - return SNew(SDockTab) - .TabRole(NomadTab) - [ - OutContent.ToSharedRef() - ]; - } - - return SNew(SDockTab); + const FName TabId = *SpawnTabArgs.GetTabId().ToString(); + + TSharedPtr OutContent; + if (TabId.IsEqual(HttpGPTChatTabName)) + { + OutContent = SNew(SHttpGPTChatShell); + } + else if (TabId.IsEqual(HttpGPTImageGeneratorTabName)) + { + OutContent = SNew(SHttpGPTImageGenView); + } + + if (OutContent.IsValid()) + { + return SNew(SDockTab).TabRole(NomadTab) + [ + OutContent.ToSharedRef() + ]; + } + + return SNew(SDockTab); } void FHttpGPTEditorModule::RegisterMenus() { - FToolMenuOwnerScoped OwnerScoped(this); - const FOnSpawnTab EditorTabSpawnerDelegate = FOnSpawnTab::CreateRaw(this, &FHttpGPTEditorModule::OnSpawnTab); + FToolMenuOwnerScoped OwnerScoped(this); + const FOnSpawnTab EditorTabSpawnerDelegate = FOnSpawnTab::CreateRaw(this, &FHttpGPTEditorModule::OnSpawnTab); #if ENGINE_MAJOR_VERSION < 5 const FName AppStyleName = FEditorStyle::GetStyleSetName(); #else - const FName AppStyleName = FAppStyle::GetAppStyleSetName(); + const FName AppStyleName = FAppStyle::GetAppStyleSetName(); #endif - const TSharedRef Menu = WorkspaceMenu::GetMenuStructure().GetToolsCategory()->AddGroup(LOCTEXT("HttpGPTCategory", "HttpGPT"), LOCTEXT("HttpGPTCategoryTooltip", "HttpGPT Plugin Tabs"), FSlateIcon(AppStyleName, "Icons.Documentation")); + const TSharedRef Menu = WorkspaceMenu::GetMenuStructure().GetToolsCategory()->AddGroup( + LOCTEXT("HttpGPTCategory", "HttpGPT"), LOCTEXT("HttpGPTCategoryTooltip", "HttpGPT Plugin Tabs"), + FSlateIcon(AppStyleName, "Icons.Documentation")); - FGlobalTabmanager::Get()->RegisterNomadTabSpawner(HttpGPTChatTabName, EditorTabSpawnerDelegate) - .SetDisplayName(FText::FromString(TEXT("HttpGPT Chat"))) - .SetTooltipText(FText::FromString(TEXT("Open HttpGPT Chat"))) - .SetIcon(FSlateIcon(AppStyleName, "DerivedData.ResourceUsage")) - .SetGroup(Menu); + FGlobalTabmanager::Get()->RegisterNomadTabSpawner(HttpGPTChatTabName, EditorTabSpawnerDelegate). + SetDisplayName(FText::FromString(TEXT("HttpGPT Chat"))).SetTooltipText(FText::FromString(TEXT("Open HttpGPT Chat"))). + SetIcon(FSlateIcon(AppStyleName, "DerivedData.ResourceUsage")).SetGroup(Menu); - FGlobalTabmanager::Get()->RegisterNomadTabSpawner(HttpGPTImageGeneratorTabName, EditorTabSpawnerDelegate) - .SetDisplayName(FText::FromString(TEXT("HttpGPT Image Generator"))) - .SetTooltipText(FText::FromString(TEXT("Open HttpGPT Image Generator"))) - .SetIcon(FSlateIcon(AppStyleName, "LevelEditor.Tabs.Viewports")) - .SetGroup(Menu); + FGlobalTabmanager::Get()->RegisterNomadTabSpawner(HttpGPTImageGeneratorTabName, EditorTabSpawnerDelegate). + SetDisplayName(FText::FromString(TEXT("HttpGPT Image Generator"))).SetTooltipText( + FText::FromString(TEXT("Open HttpGPT Image Generator"))).SetIcon( + FSlateIcon(AppStyleName, "LevelEditor.Tabs.Viewports")).SetGroup(Menu); } #undef LOCTEXT_NAMESPACE -IMPLEMENT_MODULE(FHttpGPTEditorModule, HttpGPTEditor) \ No newline at end of file +IMPLEMENT_MODULE(FHttpGPTEditorModule, HttpGPTEditor) diff --git a/Source/HttpGPTEditorModule/Private/ImageGen/HttpGPTImageGetter.cpp b/Source/HttpGPTEditorModule/Private/ImageGen/HttpGPTImageGetter.cpp index 37e9cd6..13c7967 100644 --- a/Source/HttpGPTEditorModule/Private/ImageGen/HttpGPTImageGetter.cpp +++ b/Source/HttpGPTEditorModule/Private/ImageGen/HttpGPTImageGetter.cpp @@ -16,72 +16,69 @@ UHttpGPTImageGetter::UHttpGPTImageGetter(const FObjectInitializer& ObjectInitial void UHttpGPTImageGetter::RequestSent() { - OnStatusChanged.ExecuteIfBound("Request Sent. Waiting for the response..."); + OnStatusChanged.ExecuteIfBound("Request Sent. Waiting for the response..."); } void UHttpGPTImageGetter::RequestFailed() { - OnStatusChanged.ExecuteIfBound("Request Failed. Check the Logs."); - Destroy(); + OnStatusChanged.ExecuteIfBound("Request Failed. Check the Logs."); + Destroy(); } void UHttpGPTImageGetter::ProcessCompleted(const FHttpGPTImageResponse& Response) { - if (!Response.bSuccess) - { - const FStringFormatOrderedArguments Arguments_ErrorDetails{ - 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 - }; - - OnStatusChanged.ExecuteIfBound(FString::Format(TEXT("{0}\n{1}\n\n{2}\n{3}\n{4}\n{5}"), Arguments_ErrorDetails)); - Destroy(); - return; - } - - DataSize = Response.Data.Num(); - OnStatusChanged.ExecuteIfBound("Request Completed."); - - OnImageGenerated_Internal.BindUFunction(this, TEXT("ImageGenerated")); - - for (const FHttpGPTImageData& Data : Response.Data) - { - ProcessImage(Data); - } + if (!Response.bSuccess) + { + const FStringFormatOrderedArguments Arguments_ErrorDetails{ + 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 + }; + + OnStatusChanged.ExecuteIfBound(FString::Format(TEXT("{0}\n{1}\n\n{2}\n{3}\n{4}\n{5}"), Arguments_ErrorDetails)); + Destroy(); + return; + } + + DataSize = Response.Data.Num(); + OnStatusChanged.ExecuteIfBound("Request Completed."); + + OnImageGenerated_Internal.BindUFunction(this, TEXT("ImageGenerated")); + + for (const FHttpGPTImageData& Data : Response.Data) + { + ProcessImage(Data); + } } void UHttpGPTImageGetter::ProcessImage(const FHttpGPTImageData& Data) { - UHttpGPTImageHelper::GenerateImage(Data, OnImageGenerated_Internal); + UHttpGPTImageHelper::GenerateImage(Data, OnImageGenerated_Internal); } void UHttpGPTImageGetter::ImageGenerated(UTexture2D* const Texture) { - OnImageGenerated.ExecuteIfBound(Texture); - - ++GeneratedImages; - if (GeneratedImages >= DataSize) - { - if (OutScrollBox.IsValid()) - { - OutScrollBox->ScrollToEnd(); - } - - Destroy(); - } + OnImageGenerated.ExecuteIfBound(Texture); + + ++GeneratedImages; + if (GeneratedImages >= DataSize) + { + if (OutScrollBox.IsValid()) + { + OutScrollBox->ScrollToEnd(); + } + + Destroy(); + } } void UHttpGPTImageGetter::Destroy() { - ClearFlags(RF_Standalone); + ClearFlags(RF_Standalone); #if ENGINE_MAJOR_VERSION >= 5 - MarkAsGarbage(); + MarkAsGarbage(); #else MarkPendingKill(); #endif -} \ No newline at end of file +} diff --git a/Source/HttpGPTEditorModule/Private/ImageGen/HttpGPTImageGetter.h b/Source/HttpGPTEditorModule/Private/ImageGen/HttpGPTImageGetter.h index f58dd18..2321fb3 100644 --- a/Source/HttpGPTEditorModule/Private/ImageGen/HttpGPTImageGetter.h +++ b/Source/HttpGPTEditorModule/Private/ImageGen/HttpGPTImageGetter.h @@ -15,35 +15,35 @@ DECLARE_DELEGATE_OneParam(FImageStatusChanged, FString); UCLASS(MinimalAPI, NotBlueprintable, NotPlaceable, Category = "Implementation") class UHttpGPTImageGetter : public UObject { - GENERATED_BODY() + GENERATED_BODY() public: - explicit UHttpGPTImageGetter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); + explicit UHttpGPTImageGetter(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()); - FImageGenerated OnImageGenerated; - FImageStatusChanged OnStatusChanged; + FImageGenerated OnImageGenerated; + FImageStatusChanged OnStatusChanged; - UFUNCTION() - void RequestSent(); + UFUNCTION() + void RequestSent(); - UFUNCTION() - void RequestFailed(); + UFUNCTION() + void RequestFailed(); - UFUNCTION() - void ProcessCompleted(const FHttpGPTImageResponse& Response); + UFUNCTION() + void ProcessCompleted(const FHttpGPTImageResponse& Response); - void Destroy(); + void Destroy(); - TSharedPtr OutScrollBox; + TSharedPtr OutScrollBox; private: - void ProcessImage(const FHttpGPTImageData& Data); + void ProcessImage(const FHttpGPTImageData& Data); - FHttpGPTImageGenerate OnImageGenerated_Internal; + FHttpGPTImageGenerate OnImageGenerated_Internal; - UFUNCTION() - void ImageGenerated(UTexture2D* const Texture); + UFUNCTION() + void ImageGenerated(UTexture2D* const Texture); - uint8 GeneratedImages; - uint8 DataSize; -}; \ No newline at end of file + uint8 GeneratedImages; + uint8 DataSize; +}; diff --git a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItem.cpp b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItem.cpp index ca5d0d1..6cd57fd 100644 --- a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItem.cpp +++ b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItem.cpp @@ -13,112 +13,91 @@ void SHttpGPTImageGenItem::Construct(const FArguments& InArgs) { - Prompt = InArgs._Prompt; + Prompt = InArgs._Prompt; - HttpGPTImageGetterObject = NewObject(); - HttpGPTImageGetterObject->SetFlags(RF_Standalone); - HttpGPTImageGetterObject->OutScrollBox = InArgs._OutScrollBox; + HttpGPTImageGetterObject = NewObject(); + HttpGPTImageGetterObject->SetFlags(RF_Standalone); + HttpGPTImageGetterObject->OutScrollBox = InArgs._OutScrollBox; - HttpGPTImageGetterObject->OnImageGenerated.BindLambda( - [this](UTexture2D* const Texture) - { - if (Texture && ItemViewBox.IsValid()) - { - ItemViewBox->AddSlot() - .AutoWidth() - [ - SNew(SHttpGPTImageGenItemData) - .Texture(Texture) - ]; - } - } - ); + HttpGPTImageGetterObject->OnImageGenerated.BindLambda([this](UTexture2D* const Texture) + { + if (Texture && ItemViewBox.IsValid()) + { + ItemViewBox->AddSlot().AutoWidth()[SNew(SHttpGPTImageGenItemData).Texture(Texture)]; + } + }); - HttpGPTImageGetterObject->OnStatusChanged.BindLambda( - [this](FString NewStatus) - { - if (HttpGPT::Internal::HasEmptyParam(NewStatus) || !Status.IsValid()) - { - return; - } + HttpGPTImageGetterObject->OnStatusChanged.BindLambda([this](const FString& NewStatus) + { + if (HttpGPT::Internal::HasEmptyParam(NewStatus) || !Status.IsValid()) + { + return; + } - Status->SetText(FText::FromString(TEXT("Status: ") + NewStatus)); - } - ); + Status->SetText(FText::FromString(TEXT("Status: ") + NewStatus)); + }); - FHttpGPTImageOptions Options; - Options.Format = EHttpGPTResponseFormat::b64_json; - Options.Size = UHttpGPTHelper::NameToSize(*InArgs._Size); - Options.ImagesNum = FCString::Atoi(*InArgs._Num); + FHttpGPTImageOptions Options; + Options.Format = EHttpGPTResponseFormat::b64_json; + Options.Size = UHttpGPTHelper::NameToSize(*InArgs._Size); + Options.ImagesNum = FCString::Atoi(*InArgs._Num); - RequestReference = UHttpGPTImageRequest::EditorTask(Prompt, Options); + RequestReference = UHttpGPTImageRequest::EditorTask(Prompt, Options); - RequestReference->ProcessCompleted.AddDynamic(HttpGPTImageGetterObject.Get(), &UHttpGPTImageGetter::ProcessCompleted); - RequestReference->ErrorReceived.AddDynamic(HttpGPTImageGetterObject.Get(), &UHttpGPTImageGetter::ProcessCompleted); - RequestReference->RequestFailed.AddDynamic(HttpGPTImageGetterObject.Get(), &UHttpGPTImageGetter::RequestFailed); - RequestReference->RequestSent.AddDynamic(HttpGPTImageGetterObject.Get(), &UHttpGPTImageGetter::RequestSent); + RequestReference->ProcessCompleted.AddDynamic(HttpGPTImageGetterObject.Get(), &UHttpGPTImageGetter::ProcessCompleted); + RequestReference->ErrorReceived.AddDynamic(HttpGPTImageGetterObject.Get(), &UHttpGPTImageGetter::ProcessCompleted); + RequestReference->RequestFailed.AddDynamic(HttpGPTImageGetterObject.Get(), &UHttpGPTImageGetter::RequestFailed); + RequestReference->RequestSent.AddDynamic(HttpGPTImageGetterObject.Get(), &UHttpGPTImageGetter::RequestSent); - RequestReference->Activate(); + RequestReference->Activate(); - ChildSlot - [ - ConstructContent() - ]; + ChildSlot + [ + ConstructContent() + ]; } SHttpGPTImageGenItem::~SHttpGPTImageGenItem() { - if (RequestReference.IsValid()) - { - RequestReference->StopHttpGPTTask(); - } + if (RequestReference.IsValid()) + { + RequestReference->StopHttpGPTTask(); + } } TSharedRef SHttpGPTImageGenItem::ConstructContent() { - constexpr float SlotPadding = 4.0f; + constexpr float SlotPadding = 4.0f; #if ENGINE_MAJOR_VERSION < 5 using FAppStyle = FEditorStyle; #endif - return SNew(SBox) - .Padding(SlotPadding) - [ - SNew(SBorder) - .BorderImage(FAppStyle::Get().GetBrush("Menu.Background")) - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .Padding(SlotPadding) - .AutoHeight() - [ - SNew(SVerticalBox) - + SVerticalBox::Slot() - .AutoHeight() - [ - SNew(STextBlock) - .Font(FCoreStyle::GetDefaultFontStyle("Bold", 10)) - .Text(FText::FromString(TEXT("Prompt: ") + Prompt)) - ] - + SVerticalBox::Slot() - .AutoHeight() - [ - SAssignNew(Status, STextBlock) - .Text(FText::FromString(TEXT("Status: Sending request..."))) - ] - ] - + SVerticalBox::Slot() - .Padding(SlotPadding) - .FillHeight(1.f) - [ - SNew(SScrollBox) - .Orientation(EOrientation::Orient_Horizontal) - + SScrollBox::Slot() - [ - SAssignNew(ItemViewBox, SHorizontalBox) - ] - ] - ] - ]; -} \ No newline at end of file + return SNew(SBox).Padding(SlotPadding) + [ + SNew(SBorder).BorderImage(FAppStyle::Get().GetBrush("Menu.Background")) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot().Padding(SlotPadding).AutoHeight() + [ + SNew(SVerticalBox) + + SVerticalBox::Slot().AutoHeight() + [ + SNew(STextBlock).Font(FCoreStyle::GetDefaultFontStyle("Bold", 10)).Text(FText::FromString(TEXT("Prompt: ") + Prompt)) + ] + + SVerticalBox::Slot().AutoHeight() + [ + SAssignNew(Status, STextBlock).Text(FText::FromString(TEXT("Status: Sending request..."))) + ] + ] + + SVerticalBox::Slot().Padding(SlotPadding).FillHeight(1.f) + [ + SNew(SScrollBox).Orientation(Orient_Horizontal) + + SScrollBox::Slot() + [ + SAssignNew(ItemViewBox, SHorizontalBox) + ] + ] + ] + ]; +} diff --git a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItem.h b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItem.h index 66cdd25..ac7c2fb 100644 --- a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItem.h +++ b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItem.h @@ -10,29 +10,30 @@ class SHttpGPTImageGenItem final : public SCompoundWidget { public: - SLATE_BEGIN_ARGS(SHttpGPTImageGenItem) : _OutScrollBox(), _Prompt(), _Num(), _Size() - { - } - SLATE_ARGUMENT(TSharedPtr, OutScrollBox) - SLATE_ARGUMENT(FString, Prompt) - SLATE_ARGUMENT(FString, Num) - SLATE_ARGUMENT(FString, Size) - SLATE_END_ARGS() + SLATE_BEGIN_ARGS(SHttpGPTImageGenItem) : _OutScrollBox(), _Prompt(), _Num(), _Size() + { + } - void Construct(const FArguments& InArgs); - ~SHttpGPTImageGenItem(); + SLATE_ARGUMENT(TSharedPtr, OutScrollBox) + SLATE_ARGUMENT(FString, Prompt) + SLATE_ARGUMENT(FString, Num) + SLATE_ARGUMENT(FString, Size) + SLATE_END_ARGS() - TWeakObjectPtr HttpGPTImageGetterObject; + void Construct(const FArguments& InArgs); + virtual ~SHttpGPTImageGenItem() override; + + TWeakObjectPtr HttpGPTImageGetterObject; private: - TSharedRef ConstructContent(); + TSharedRef ConstructContent(); - FString Prompt; + FString Prompt; - TSharedPtr Status; - TSharedPtr ItemViewBox; + TSharedPtr Status; + TSharedPtr ItemViewBox; - TWeakObjectPtr RequestReference; + TWeakObjectPtr RequestReference; }; -typedef TSharedPtr SHttpGPTImageGenItemPtr; \ No newline at end of file +using SHttpGPTImageGenItemPtr = TSharedPtr; diff --git a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItemData.cpp b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItemData.cpp index 61e5a00..bf95c93 100644 --- a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItemData.cpp +++ b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItemData.cpp @@ -10,84 +10,79 @@ void SHttpGPTImageGenItemData::Construct(const FArguments& InArgs) { - Texture = InArgs._Texture; + Texture = InArgs._Texture; - ChildSlot - [ - ConstructContent() - ]; + ChildSlot + [ + ConstructContent() + ]; } TSharedRef SHttpGPTImageGenItemData::ConstructContent() { - constexpr float SlotPadding = 4.0f; - - return SNew(SVerticalBox) - + SVerticalBox::Slot() - .Padding(SlotPadding) - .FillHeight(1.f) - [ - SNew(SImage) - .Image(Texture.IsValid() ? new FSlateImageBrush(Texture.Get(), FVector2D(256.f, 256.f)) : nullptr) - ] - + SVerticalBox::Slot() - .Padding(SlotPadding) - .AutoHeight() - [ - SNew(SButton) - .Text(FText::FromString(TEXT("Save"))) - .HAlign(HAlign_Center) - .OnClicked(this, &SHttpGPTImageGenItemData::HandleSaveButton) - .IsEnabled(this, &SHttpGPTImageGenItemData::IsSaveEnabled) - ]; + constexpr float SlotPadding = 4.0f; + + return SNew(SVerticalBox) + + SVerticalBox::Slot().Padding(SlotPadding).FillHeight(1.f) + [ + SNew(SImage).Image(Texture.IsValid() ? new FSlateImageBrush(Texture.Get(), FVector2D(256.f, 256.f)) : nullptr) + ] + + SVerticalBox::Slot().Padding(SlotPadding).AutoHeight() + [ + SNew(SButton) + .Text(FText::FromString(TEXT("Save"))) + .HAlign(HAlign_Center) + .OnClicked(this, &SHttpGPTImageGenItemData::HandleSaveButton) + .IsEnabled(this, &SHttpGPTImageGenItemData::IsSaveEnabled) + ]; } FReply SHttpGPTImageGenItemData::HandleSaveButton() { - const FString AssetName = FString::FromInt(Texture->GetUniqueID()); - FString TargetFilename = FPaths::Combine(TEXT("/Game/"), UHttpGPTSettings::Get()->GeneratedImagesDir, AssetName); - FPaths::NormalizeFilename(TargetFilename); + const FString AssetName = FString::FromInt(Texture->GetUniqueID()); + FString TargetFilename = FPaths::Combine(TEXT("/Game/"), UHttpGPTSettings::Get()->GeneratedImagesDir, AssetName); + FPaths::NormalizeFilename(TargetFilename); - UPackage* const Package = CreatePackage(*TargetFilename); - UTexture2D* const SavedTexture = NewObject(Package, *AssetName, RF_Public | RF_Standalone); + UPackage* const Package = CreatePackage(*TargetFilename); + UTexture2D* const SavedTexture = NewObject(Package, *AssetName, RF_Public | RF_Standalone); #if ENGINE_MAJOR_VERSION >= 5 - SavedTexture->SetPlatformData(Texture->GetPlatformData()); + SavedTexture->SetPlatformData(Texture->GetPlatformData()); #else SavedTexture->PlatformData = Texture->PlatformData; #endif - SavedTexture->UpdateResource(); + SavedTexture->UpdateResource(); #if ENGINE_MAJOR_VERSION >= 5 - SavedTexture->Source.Init(Texture->GetSizeX(), Texture->GetSizeY(), 1, Texture->GetPlatformData()->Mips.Num(), ETextureSourceFormat::TSF_BGRA8); + SavedTexture->Source.Init(Texture->GetSizeX(), Texture->GetSizeY(), 1, Texture->GetPlatformData()->Mips.Num(), TSF_BGRA8); #else SavedTexture->Source.Init(Texture->GetSizeX(), Texture->GetSizeY(), 1, Texture->PlatformData->Mips.Num(), ETextureSourceFormat::TSF_BGRA8); #endif - SavedTexture->PostEditChange(); + SavedTexture->PostEditChange(); - SavedTexture->MarkPackageDirty(); - FAssetRegistryModule::AssetCreated(SavedTexture); + SavedTexture->MarkPackageDirty(); + FAssetRegistryModule::AssetCreated(SavedTexture); - const FString TempPackageFilename = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension()); + const FString TempPackageFilename = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension()); #if ENGINE_MAJOR_VERSION >= 5 - FSavePackageArgs SaveArgs; - SaveArgs.SaveFlags = RF_Public | RF_Standalone; - UPackage::SavePackage(Package, SavedTexture, *TempPackageFilename, SaveArgs); + FSavePackageArgs SaveArgs; + SaveArgs.SaveFlags = RF_Public | RF_Standalone; + UPackage::SavePackage(Package, SavedTexture, *TempPackageFilename, SaveArgs); #else UPackage::SavePackage(Package, SavedTexture, RF_Public | RF_Standalone, *TempPackageFilename); #endif - TArray SyncAssets; - SyncAssets.Add(FAssetData(SavedTexture)); - GEditor->SyncBrowserToObjects(SyncAssets); + TArray SyncAssets; + SyncAssets.Add(FAssetData(SavedTexture)); + GEditor->SyncBrowserToObjects(SyncAssets); - return FReply::Handled(); + return FReply::Handled(); } bool SHttpGPTImageGenItemData::IsSaveEnabled() const { - return Texture.IsValid(); -} \ No newline at end of file + return Texture.IsValid(); +} diff --git a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItemData.h b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItemData.h index 1d1dddb..05d552a 100644 --- a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItemData.h +++ b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenItemData.h @@ -10,21 +10,22 @@ class SHttpGPTImageGenItemData final : public SCompoundWidget { public: - SLATE_BEGIN_ARGS(SHttpGPTImageGenItemData) : _Texture() - { - } - SLATE_ARGUMENT(class UTexture2D*, Texture) - SLATE_END_ARGS() + SLATE_BEGIN_ARGS(SHttpGPTImageGenItemData) : _Texture() + { + } - void Construct(const FArguments& InArgs); + SLATE_ARGUMENT(class UTexture2D*, Texture) + SLATE_END_ARGS() - FReply HandleSaveButton(); - bool IsSaveEnabled() const; + void Construct(const FArguments& InArgs); + + FReply HandleSaveButton(); + bool IsSaveEnabled() const; private: - TSharedRef ConstructContent(); + TSharedRef ConstructContent(); - TWeakObjectPtr Texture; + TWeakObjectPtr Texture; }; -typedef TSharedPtr SHttpGPTImageGenItemDataPtr; \ No newline at end of file +using SHttpGPTImageGenItemDataPtr = TSharedPtr; diff --git a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenView.cpp b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenView.cpp index 64e6239..2d6168f 100644 --- a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenView.cpp +++ b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenView.cpp @@ -11,142 +11,116 @@ void SHttpGPTImageGenView::Construct(const FArguments& InArgs) { - InitializeImageNumOptions(); - ImageNumComboBox = SNew(STextComboBox) - .OptionsSource(&ImageNum) - .InitiallySelectedItem(ImageNum[0]) - .ToolTipText(FText::FromString(TEXT("Number of Generated Images"))); + InitializeImageNumOptions(); + ImageNumComboBox = SNew(STextComboBox).OptionsSource(&ImageNum).InitiallySelectedItem(ImageNum[0]).ToolTipText( + FText::FromString(TEXT("Number of Generated Images"))); - InitializeImageSizeOptions(); - ImageSizeComboBox = SNew(STextComboBox) - .OptionsSource(&ImageSize) - .InitiallySelectedItem(ImageSize[0]) - .ToolTipText(FText::FromString(TEXT("Size of Generated Images"))); + InitializeImageSizeOptions(); + ImageSizeComboBox = SNew(STextComboBox).OptionsSource(&ImageSize).InitiallySelectedItem(ImageSize[0]).ToolTipText( + FText::FromString(TEXT("Size of Generated Images"))); - ChildSlot - [ - ConstructContent() - ]; + ChildSlot + [ + ConstructContent() + ]; } TSharedRef SHttpGPTImageGenView::ConstructContent() { - constexpr float SlotPadding = 4.0f; + constexpr float SlotPadding = 4.0f; - return SNew(SVerticalBox) - + SVerticalBox::Slot() - .Padding(SlotPadding) - .FillHeight(1.f) - [ - SAssignNew(ViewScrollBox, SScrollBox) - + SScrollBox::Slot() - [ - SAssignNew(ViewBox, SVerticalBox) - ] - ] - + SVerticalBox::Slot() - .Padding(SlotPadding) - .AutoHeight() - [ - SNew(SHorizontalBox) - + SHorizontalBox::Slot() - .Padding(SlotPadding) - .FillWidth(1.f) - [ - SAssignNew(InputTextBox, SEditableTextBox) - .AllowContextMenu(true) - .IsReadOnly(false) - .OnTextCommitted_Lambda( - [this]([[maybe_unused]] const FText& Text, ETextCommit::Type CommitType) - { - if (IsSendRequestEnabled() && CommitType == ETextCommit::OnEnter) - { - HandleSendRequestButton(); - } - } - ) - ] - + SHorizontalBox::Slot() - .Padding(SlotPadding) - .AutoWidth() - [ - SNew(SButton) - .Text(FText::FromString(TEXT("Generate"))) - .ToolTipText(FText::FromString(TEXT("Request Images Generation"))) - .OnClicked(this, &SHttpGPTImageGenView::HandleSendRequestButton) - .IsEnabled(this, &SHttpGPTImageGenView::IsSendRequestEnabled) - ] - + SHorizontalBox::Slot() - .Padding(SlotPadding) - .AutoWidth() - [ - ImageNumComboBox.ToSharedRef() - ] - + SHorizontalBox::Slot() - .Padding(SlotPadding) - .AutoWidth() - [ - ImageSizeComboBox.ToSharedRef() - ] - + SHorizontalBox::Slot() - .Padding(SlotPadding) - .AutoWidth() - [ - SNew(SButton) - .Text(FText::FromString(TEXT("Clear"))) - .ToolTipText(FText::FromString(TEXT("Clear Generation History"))) - .OnClicked(this, &SHttpGPTImageGenView::HandleClearViewButton) - .IsEnabled(this, &SHttpGPTImageGenView::IsClearViewEnabled) - ] - ]; + return SNew(SVerticalBox) + + SVerticalBox::Slot().Padding(SlotPadding).FillHeight(1.f) + [ + SAssignNew(ViewScrollBox, SScrollBox) + + SScrollBox::Slot() + [ + SAssignNew(ViewBox, SVerticalBox) + ] + ] + + SVerticalBox::Slot().Padding(SlotPadding).AutoHeight() + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot().Padding(SlotPadding).FillWidth(1.f) + [ + SAssignNew(InputTextBox, SEditableTextBox).AllowContextMenu(true).IsReadOnly(false).OnTextCommitted_Lambda( + [this]([[maybe_unused]] const FText& Text, ETextCommit::Type CommitType) + { + if (IsSendRequestEnabled() && CommitType == ETextCommit::OnEnter) + { + HandleSendRequestButton(); + } + }) + ] + + SHorizontalBox::Slot().Padding(SlotPadding).AutoWidth() + [ + SNew(SButton) + .Text(FText::FromString(TEXT("Generate"))) + .ToolTipText(FText::FromString(TEXT("Request Images Generation"))) + .OnClicked(this, &SHttpGPTImageGenView::HandleSendRequestButton) + .IsEnabled(this, &SHttpGPTImageGenView::IsSendRequestEnabled) + ] + + SHorizontalBox::Slot().Padding(SlotPadding).AutoWidth() + [ + ImageNumComboBox.ToSharedRef() + ] + + SHorizontalBox::Slot().Padding(SlotPadding).AutoWidth() + [ + ImageSizeComboBox.ToSharedRef() + ] + + SHorizontalBox::Slot().Padding(SlotPadding).AutoWidth() + [ + SNew(SButton) + .Text(FText::FromString(TEXT("Clear"))) + .ToolTipText(FText::FromString(TEXT("Clear Generation History"))) + .OnClicked(this, &SHttpGPTImageGenView::HandleClearViewButton) + .IsEnabled(this, &SHttpGPTImageGenView::IsClearViewEnabled) + ] + ]; } FReply SHttpGPTImageGenView::HandleSendRequestButton() { - ViewBox->AddSlot() - .AutoHeight() - [ - SNew(SHttpGPTImageGenItem) - .OutScrollBox(ViewScrollBox) - .Prompt(InputTextBox->GetText().ToString()) - .Num(*ImageNumComboBox->GetSelectedItem().Get()) - .Size(*ImageSizeComboBox->GetSelectedItem().Get()) - ]; + ViewBox->AddSlot().AutoHeight()[SNew(SHttpGPTImageGenItem) + .OutScrollBox(ViewScrollBox) + .Prompt(InputTextBox->GetText().ToString()) + .Num(*ImageNumComboBox->GetSelectedItem().Get()) + .Size(*ImageSizeComboBox->GetSelectedItem().Get())]; - ViewScrollBox->ScrollToEnd(); - InputTextBox->SetText(FText::GetEmpty()); + ViewScrollBox->ScrollToEnd(); + InputTextBox->SetText(FText::GetEmpty()); - return FReply::Handled(); + return FReply::Handled(); } bool SHttpGPTImageGenView::IsSendRequestEnabled() const { - return !HttpGPT::Internal::HasEmptyParam(InputTextBox->GetText()); + return !HttpGPT::Internal::HasEmptyParam(InputTextBox->GetText()); } FReply SHttpGPTImageGenView::HandleClearViewButton() { - ViewBox->ClearChildren(); - return FReply::Handled(); + ViewBox->ClearChildren(); + return FReply::Handled(); } bool SHttpGPTImageGenView::IsClearViewEnabled() const { - return ViewBox->NumSlots() > 0; + return ViewBox->NumSlots() > 0; } void SHttpGPTImageGenView::InitializeImageNumOptions() { - constexpr uint8 MaxNum = 10u; - for (uint8 Iterator = 1u; Iterator <= MaxNum; ++Iterator) - { - ImageNum.Add(MakeShared(FString::FromInt(Iterator))); - } + constexpr uint8 MaxNum = 10u; + for (uint8 Iterator = 1u; Iterator <= MaxNum; ++Iterator) + { + ImageNum.Add(MakeShared(FString::FromInt(Iterator))); + } } void SHttpGPTImageGenView::InitializeImageSizeOptions() { - ImageSize.Add(MakeShared(UHttpGPTHelper::SizeToName(EHttpGPTImageSize::x256).ToString())); - ImageSize.Add(MakeShared(UHttpGPTHelper::SizeToName(EHttpGPTImageSize::x512).ToString())); - ImageSize.Add(MakeShared(UHttpGPTHelper::SizeToName(EHttpGPTImageSize::x1024).ToString())); -} \ No newline at end of file + ImageSize.Add(MakeShared(UHttpGPTHelper::SizeToName(EHttpGPTImageSize::x256).ToString())); + ImageSize.Add(MakeShared(UHttpGPTHelper::SizeToName(EHttpGPTImageSize::x512).ToString())); + ImageSize.Add(MakeShared(UHttpGPTHelper::SizeToName(EHttpGPTImageSize::x1024).ToString())); +} diff --git a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenView.h b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenView.h index 489a8fb..f8d17a0 100644 --- a/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenView.h +++ b/Source/HttpGPTEditorModule/Private/ImageGen/SHttpGPTImageGenView.h @@ -10,33 +10,34 @@ class SHttpGPTImageGenView final : public SCompoundWidget { public: - SLATE_USER_ARGS(SHttpGPTImageGenView) - { - } - SLATE_END_ARGS() + SLATE_USER_ARGS(SHttpGPTImageGenView) + { + } - void Construct(const FArguments& InArgs); + SLATE_END_ARGS() - bool IsSendRequestEnabled() const; - bool IsClearViewEnabled() const; + void Construct(const FArguments& InArgs); + + bool IsSendRequestEnabled() const; + bool IsClearViewEnabled() const; private: - TSharedRef ConstructContent(); + TSharedRef ConstructContent(); - FReply HandleSendRequestButton(); - FReply HandleClearViewButton(); + FReply HandleSendRequestButton(); + FReply HandleClearViewButton(); - void InitializeImageNumOptions(); - void InitializeImageSizeOptions(); + void InitializeImageNumOptions(); + void InitializeImageSizeOptions(); - TSharedPtr ViewBox; - TSharedPtr ViewScrollBox; + TSharedPtr ViewBox; + TSharedPtr ViewScrollBox; - TSharedPtr InputTextBox; + TSharedPtr InputTextBox; - TSharedPtr ImageNumComboBox; - TArray ImageNum; + TSharedPtr ImageNumComboBox; + TArray ImageNum; - TSharedPtr ImageSizeComboBox; - TArray ImageSize; + TSharedPtr ImageSizeComboBox; + TArray ImageSize; }; diff --git a/Source/HttpGPTEditorModule/Public/HttpGPTEditorModule.h b/Source/HttpGPTEditorModule/Public/HttpGPTEditorModule.h index 284f1ea..09c388d 100644 --- a/Source/HttpGPTEditorModule/Public/HttpGPTEditorModule.h +++ b/Source/HttpGPTEditorModule/Public/HttpGPTEditorModule.h @@ -9,12 +9,12 @@ class FHttpGPTEditorModule : public IModuleInterface { protected: - virtual void StartupModule() override; - virtual void ShutdownModule() override; + virtual void StartupModule() override; + virtual void ShutdownModule() override; - TSharedRef OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs) const; + TSharedRef OnSpawnTab(const FSpawnTabArgs& SpawnTabArgs) const; private: - void RegisterMenus(); - FPropertyEditorModule* PropertyEditorModule = nullptr; + void RegisterMenus(); + FPropertyEditorModule* PropertyEditorModule = nullptr; }; diff --git a/Source/HttpGPTImageModule/HttpGPTImageModule.Build.cs b/Source/HttpGPTImageModule/HttpGPTImageModule.Build.cs index 8113d7d..65c19e9 100644 --- a/Source/HttpGPTImageModule/HttpGPTImageModule.Build.cs +++ b/Source/HttpGPTImageModule/HttpGPTImageModule.Build.cs @@ -6,28 +6,25 @@ public class HttpGPTImageModule : ModuleRules { - public HttpGPTImageModule(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; - CppStandard = CppStandardVersion.Cpp17; + public HttpGPTImageModule(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; + CppStandard = CppStandardVersion.Cpp17; - PublicDependencyModuleNames.AddRange(new[] - { - "Core", - "HTTP", - "Json", - "HttpGPTCommonModule" - }); + PublicDependencyModuleNames.AddRange(new[] + { + "Core", + "HTTP", + "Json", + "HttpGPTCommonModule" + }); - PrivateDependencyModuleNames.AddRange(new[] - { - "Engine", - "CoreUObject" - }); + PrivateDependencyModuleNames.AddRange(new[] + { + "Engine", + "CoreUObject" + }); - if (Target.bBuildEditor) - { - PrivateDependencyModuleNames.Add("UnrealEd"); - } - } + if (Target.bBuildEditor) PrivateDependencyModuleNames.Add("UnrealEd"); + } } \ No newline at end of file diff --git a/Source/HttpGPTImageModule/Private/HttpGPTImageModule.cpp b/Source/HttpGPTImageModule/Private/HttpGPTImageModule.cpp index 52427cb..a6b1609 100644 --- a/Source/HttpGPTImageModule/Private/HttpGPTImageModule.cpp +++ b/Source/HttpGPTImageModule/Private/HttpGPTImageModule.cpp @@ -16,4 +16,4 @@ void FHttpGPTImageModule::ShutdownModule() #undef LOCTEXT_NAMESPACE -IMPLEMENT_MODULE(FHttpGPTImageModule, HttpGPTImageModule) \ No newline at end of file +IMPLEMENT_MODULE(FHttpGPTImageModule, HttpGPTImageModule) diff --git a/Source/HttpGPTImageModule/Private/Tasks/HttpGPTImageRequest.cpp b/Source/HttpGPTImageModule/Private/Tasks/HttpGPTImageRequest.cpp index 4be8351..34eca29 100644 --- a/Source/HttpGPTImageModule/Private/Tasks/HttpGPTImageRequest.cpp +++ b/Source/HttpGPTImageModule/Private/Tasks/HttpGPTImageRequest.cpp @@ -32,224 +32,218 @@ #if WITH_EDITOR UHttpGPTImageRequest* UHttpGPTImageRequest::EditorTask(const FString& Prompt, const FHttpGPTImageOptions Options) { - UHttpGPTImageRequest* const NewAsyncTask = RequestImages_CustomOptions(GEditor->GetEditorWorldContext().World(), Prompt, FHttpGPTCommonOptions(), Options); - NewAsyncTask->bIsEditorTask = true; + UHttpGPTImageRequest* const NewAsyncTask = RequestImages_CustomOptions(GEditor->GetEditorWorldContext().World(), Prompt, FHttpGPTCommonOptions(), + Options); + NewAsyncTask->bIsEditorTask = true; - return NewAsyncTask; + return NewAsyncTask; } #endif UHttpGPTImageRequest* UHttpGPTImageRequest::RequestImages_DefaultOptions(UObject* const WorldContextObject, const FString& Prompt) { - return RequestImages_CustomOptions(WorldContextObject, Prompt, FHttpGPTCommonOptions(), FHttpGPTImageOptions()); + return RequestImages_CustomOptions(WorldContextObject, Prompt, FHttpGPTCommonOptions(), FHttpGPTImageOptions()); } -UHttpGPTImageRequest* UHttpGPTImageRequest::RequestImages_CustomOptions(UObject* const WorldContextObject, const FString& Prompt, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTImageOptions ImageOptions) +UHttpGPTImageRequest* UHttpGPTImageRequest::RequestImages_CustomOptions(UObject* const WorldContextObject, const FString& Prompt, + const FHttpGPTCommonOptions CommonOptions, + const FHttpGPTImageOptions ImageOptions) { - UHttpGPTImageRequest* const NewAsyncTask = NewObject(); - NewAsyncTask->Prompt = Prompt; - NewAsyncTask->CommonOptions = CommonOptions; - NewAsyncTask->ImageOptions = ImageOptions; + UHttpGPTImageRequest* const NewAsyncTask = NewObject(); + NewAsyncTask->Prompt = Prompt; + NewAsyncTask->CommonOptions = CommonOptions; + NewAsyncTask->ImageOptions = ImageOptions; - NewAsyncTask->RegisterWithGameInstance(WorldContextObject); + NewAsyncTask->RegisterWithGameInstance(WorldContextObject); - return NewAsyncTask; + return NewAsyncTask; } const FHttpGPTImageOptions UHttpGPTImageRequest::GetImageOptions() const { - return ImageOptions; + return ImageOptions; } const FString UHttpGPTImageRequest::GetPrompt() const { - return Prompt; + return Prompt; } bool UHttpGPTImageRequest::CanActivateTask() const { - if (!Super::CanActivateTask()) - { - return false; - } - - if (HttpGPT::Internal::HasEmptyParam(Prompt)) - { - UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Can't activate task: Invalid Prompt."), *FString(__func__), GetUniqueID()); - return false; - } - - return true; + if (!Super::CanActivateTask()) + { + return false; + } + + if (HttpGPT::Internal::HasEmptyParam(Prompt)) + { + UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Can't activate task: Invalid Prompt."), *FString(__func__), GetUniqueID()); + return false; + } + + return true; } bool UHttpGPTImageRequest::CanBindProgress() const { - return false; + return false; } FString UHttpGPTImageRequest::GetEndpointURL() const { - if (CommonOptions.bIsAzureOpenAI) - { - return FString::Format(TEXT("{0}/openai/images/generations:submit?api-version={1}"), { GetCommonOptions().Endpoint, GetCommonOptions().AzureOpenAIAPIVersion }); - } - else - { - return FString::Format(TEXT("{0}/v1/images/generations"), { GetCommonOptions().Endpoint }); - } + if (CommonOptions.bIsAzureOpenAI) + { + return FString::Format( + TEXT("{0}/openai/images/generations:submit?api-version={1}"), {GetCommonOptions().Endpoint, GetCommonOptions().AzureOpenAIAPIVersion}); + } + return FString::Format(TEXT("{0}/v1/images/generations"), {GetCommonOptions().Endpoint}); } FString UHttpGPTImageRequest::SetRequestContent() { - FScopeLock Lock(&Mutex); + FScopeLock Lock(&Mutex); - if (!HttpRequest.IsValid()) - { - return FString(); - } + if (!HttpRequest.IsValid()) + { + return FString(); + } - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Mounting content"), *FString(__func__), GetUniqueID()); + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Mounting content"), *FString(__func__), GetUniqueID()); - const TSharedPtr JsonRequest = MakeShared(); - JsonRequest->SetStringField("prompt", Prompt); - JsonRequest->SetNumberField("n", GetImageOptions().ImagesNum); - JsonRequest->SetStringField("size", UHttpGPTHelper::SizeToName(GetImageOptions().Size).ToString()); - JsonRequest->SetStringField("response_format", UHttpGPTHelper::FormatToName(GetImageOptions().Format).ToString().ToLower()); + const TSharedPtr JsonRequest = MakeShared(); + JsonRequest->SetStringField("prompt", Prompt); + JsonRequest->SetNumberField("n", GetImageOptions().ImagesNum); + JsonRequest->SetStringField("size", UHttpGPTHelper::SizeToName(GetImageOptions().Size).ToString()); + JsonRequest->SetStringField("response_format", UHttpGPTHelper::FormatToName(GetImageOptions().Format).ToString().ToLower()); - if (!HttpGPT::Internal::HasEmptyParam(GetCommonOptions().User)) - { - JsonRequest->SetStringField("user", GetCommonOptions().User.ToString()); - } + if (!HttpGPT::Internal::HasEmptyParam(GetCommonOptions().User)) + { + JsonRequest->SetStringField("user", GetCommonOptions().User.ToString()); + } - FString RequestContentString; - const TSharedRef> Writer = TJsonWriterFactory<>::Create(&RequestContentString); - FJsonSerializer::Serialize(JsonRequest.ToSharedRef(), Writer); + FString RequestContentString; + const TSharedRef> Writer = TJsonWriterFactory<>::Create(&RequestContentString); + FJsonSerializer::Serialize(JsonRequest.ToSharedRef(), Writer); - HttpRequest->SetContentAsString(RequestContentString); + HttpRequest->SetContentAsString(RequestContentString); - return RequestContentString; + return RequestContentString; } void UHttpGPTImageRequest::OnProgressCompleted(const FString& Content, const bool bWasSuccessful) { - FScopeLock Lock(&Mutex); - - if (!bWasSuccessful || HttpGPT::Internal::HasEmptyParam(Content)) - { - UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Request failed"), *FString(__func__), GetUniqueID()); - AsyncTask(ENamedThreads::GameThread, - [this] - { - RequestFailed.Broadcast(); - } - ); - - return; - } - - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Process Completed"), *FString(__func__), GetUniqueID()); - UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Content: %s"), *FString(__func__), GetUniqueID(), *Content); - - DeserializeResponse(Content); - - if (Response.bSuccess) - { - AsyncTask(ENamedThreads::GameThread, - [this] - { - FScopeLock Lock(&Mutex); - ProcessCompleted.Broadcast(Response); - } - ); - } - else - { - UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Request failed"), *FString(__func__), GetUniqueID()); - AsyncTask(ENamedThreads::GameThread, - [this] - { - FScopeLock Lock(&Mutex); - ErrorReceived.Broadcast(Response); - } - ); - } + FScopeLock Lock(&Mutex); + + if (!bWasSuccessful || HttpGPT::Internal::HasEmptyParam(Content)) + { + UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Request failed"), *FString(__func__), GetUniqueID()); + AsyncTask(ENamedThreads::GameThread, [this] + { + RequestFailed.Broadcast(); + }); + + return; + } + + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Process Completed"), *FString(__func__), GetUniqueID()); + UE_LOG(LogHttpGPT_Internal, Display, TEXT("%s (%d): Content: %s"), *FString(__func__), GetUniqueID(), *Content); + + DeserializeResponse(Content); + + if (Response.bSuccess) + { + AsyncTask(ENamedThreads::GameThread, [this] + { + FScopeLock Lock(&Mutex); + ProcessCompleted.Broadcast(Response); + }); + } + else + { + UE_LOG(LogHttpGPT, Error, TEXT("%s (%d): Request failed"), *FString(__func__), GetUniqueID()); + AsyncTask(ENamedThreads::GameThread, [this] + { + FScopeLock Lock(&Mutex); + ErrorReceived.Broadcast(Response); + }); + } } void UHttpGPTImageRequest::DeserializeResponse(const FString& Content) { - FScopeLock Lock(&Mutex); - - if (HttpGPT::Internal::HasEmptyParam(Content)) - { - return; - } - - const TSharedRef> Reader = TJsonReaderFactory<>::Create(Content); - TSharedPtr JsonResponse = MakeShared(); - FJsonSerializer::Deserialize(Reader, JsonResponse); - - if (CheckError(JsonResponse, Response.Error)) - { - Response.bSuccess = false; - return; - } - - Response.bSuccess = true; - Response.Created = JsonResponse->GetNumberField("created"); - - const TArray> DataArray = JsonResponse->GetArrayField("data"); - for (auto Iterator = DataArray.CreateConstIterator(); Iterator; ++Iterator) - { - Response.Data.Add(FHttpGPTImageData((*Iterator)->AsObject()->GetStringField(UHttpGPTHelper::FormatToName(GetImageOptions().Format).ToString()), GetImageOptions().Format)); - } + FScopeLock Lock(&Mutex); + + if (HttpGPT::Internal::HasEmptyParam(Content)) + { + return; + } + + const TSharedRef> Reader = TJsonReaderFactory<>::Create(Content); + TSharedPtr JsonResponse = MakeShared(); + FJsonSerializer::Deserialize(Reader, JsonResponse); + + if (CheckError(JsonResponse, Response.Error)) + { + Response.bSuccess = false; + return; + } + + Response.bSuccess = true; + Response.Created = JsonResponse->GetNumberField("created"); + + const TArray> DataArray = JsonResponse->GetArrayField("data"); + for (auto Iterator = DataArray.CreateConstIterator(); Iterator; ++Iterator) + { + Response.Data.Add(FHttpGPTImageData( + (*Iterator)->AsObject()->GetStringField(UHttpGPTHelper::FormatToName(GetImageOptions().Format).ToString()), GetImageOptions().Format)); + } } UHttpGPTImageRequest* UHttpGPTImageHelper::CastToHttpGPTImageRequest(UObject* const Object) { - return Cast(Object); + return Cast(Object); } void UHttpGPTImageHelper::GenerateImage(const FHttpGPTImageData& ImageData, const FHttpGPTImageGenerate& Callback) { - switch (ImageData.Format) - { - case EHttpGPTResponseFormat::url: - GenerateImageFromURL(ImageData, Callback); - break; - - case EHttpGPTResponseFormat::b64_json: - GenerateImageFromB64(ImageData, Callback); - break; - - default: - break; - } + switch (ImageData.Format) + { + case EHttpGPTResponseFormat::url: + GenerateImageFromURL(ImageData, Callback); + break; + + case EHttpGPTResponseFormat::b64_json: + GenerateImageFromB64(ImageData, Callback); + break; + + default: + break; + } } void UHttpGPTImageHelper::GenerateImageFromURL(const FHttpGPTImageData& ImageData, const FHttpGPTImageGenerate& Callback) { - TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); - HttpRequest->SetURL(ImageData.Content); - HttpRequest->SetVerb("GET"); - HttpRequest->OnProcessRequestComplete().BindLambda( - [=](FHttpRequestPtr Request, FHttpResponsePtr Response, bool bSuccess) - { - if (bSuccess && Response.IsValid()) - { - Callback.ExecuteIfBound(FImageUtils::ImportBufferAsTexture2D(Response->GetContent())); - } - else - { - Callback.ExecuteIfBound(nullptr); - } - } - ); - HttpRequest->ProcessRequest(); + const TSharedRef HttpRequest = FHttpModule::Get().CreateRequest(); + HttpRequest->SetURL(ImageData.Content); + HttpRequest->SetVerb("GET"); + HttpRequest->OnProcessRequestComplete().BindLambda([=](FHttpRequestPtr Request, const FHttpResponsePtr& Response, bool bSuccess) + { + if (bSuccess && Response.IsValid()) + { + Callback.ExecuteIfBound(FImageUtils::ImportBufferAsTexture2D(Response->GetContent())); + } + else + { + Callback.ExecuteIfBound(nullptr); + } + }); + HttpRequest->ProcessRequest(); } void UHttpGPTImageHelper::GenerateImageFromB64(const FHttpGPTImageData& ImageData, const FHttpGPTImageGenerate& Callback) { - TArray DecodedBytes; - FBase64::Decode(ImageData.Content, DecodedBytes); - Callback.ExecuteIfBound(FImageUtils::ImportBufferAsTexture2D(DecodedBytes)); -} \ No newline at end of file + TArray DecodedBytes; + FBase64::Decode(ImageData.Content, DecodedBytes); + Callback.ExecuteIfBound(FImageUtils::ImportBufferAsTexture2D(DecodedBytes)); +} diff --git a/Source/HttpGPTImageModule/Public/HttpGPTImageModule.h b/Source/HttpGPTImageModule/Public/HttpGPTImageModule.h index 4c82c24..7b1773c 100644 --- a/Source/HttpGPTImageModule/Public/HttpGPTImageModule.h +++ b/Source/HttpGPTImageModule/Public/HttpGPTImageModule.h @@ -10,6 +10,6 @@ class FHttpGPTImageModule : public IModuleInterface { public: - virtual void StartupModule() override; - virtual void ShutdownModule() override; + virtual void StartupModule() override; + virtual void ShutdownModule() override; }; diff --git a/Source/HttpGPTImageModule/Public/Tasks/HttpGPTImageRequest.h b/Source/HttpGPTImageModule/Public/Tasks/HttpGPTImageRequest.h index 6e879c3..844df04 100644 --- a/Source/HttpGPTImageModule/Public/Tasks/HttpGPTImageRequest.h +++ b/Source/HttpGPTImageModule/Public/Tasks/HttpGPTImageRequest.h @@ -25,52 +25,55 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FHttpGPTImageResponseDelegate, const UCLASS(NotPlaceable, Category = "HttpGPT | Image", meta = (ExposedAsyncProxy = AsyncTask)) class HTTPGPTIMAGEMODULE_API UHttpGPTImageRequest : public UHttpGPTBaseTask { - GENERATED_BODY() + GENERATED_BODY() public: - UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Image") - FHttpGPTImageResponseDelegate ProcessCompleted; + UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Image") + FHttpGPTImageResponseDelegate ProcessCompleted; - UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Image") - FHttpGPTImageResponseDelegate ProgressUpdated; + UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Image") + FHttpGPTImageResponseDelegate ProgressUpdated; - UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Image") - FHttpGPTImageResponseDelegate ProgressStarted; + UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Image") + FHttpGPTImageResponseDelegate ProgressStarted; - UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Image") - FHttpGPTImageResponseDelegate ErrorReceived; + UPROPERTY(BlueprintAssignable, Category = "HttpGPT | Image") + FHttpGPTImageResponseDelegate ErrorReceived; #if WITH_EDITOR - static UHttpGPTImageRequest* EditorTask(const FString& Prompt, const FHttpGPTImageOptions Options); + static UHttpGPTImageRequest* EditorTask(const FString& Prompt, const FHttpGPTImageOptions Options); #endif - UFUNCTION(BlueprintCallable, Category = "HttpGPT | Image | Default", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Request Images with Default Options")) - static UHttpGPTImageRequest* RequestImages_DefaultOptions(UObject* const WorldContextObject, const FString& Prompt); + UFUNCTION(BlueprintCallable, Category = "HttpGPT | Image | Default", + meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Request Images with Default Options")) + static UHttpGPTImageRequest* RequestImages_DefaultOptions(UObject* const WorldContextObject, const FString& Prompt); - UFUNCTION(BlueprintCallable, Category = "HttpGPT | Image | Custom", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Request Images with Custom Options")) - static UHttpGPTImageRequest* RequestImages_CustomOptions(UObject* const WorldContextObject, const FString& Prompt, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTImageOptions ImageOptions); + UFUNCTION(BlueprintCallable, Category = "HttpGPT | Image | Custom", + meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Request Images with Custom Options")) + static UHttpGPTImageRequest* RequestImages_CustomOptions(UObject* const WorldContextObject, const FString& Prompt, + const FHttpGPTCommonOptions CommonOptions, const FHttpGPTImageOptions ImageOptions); - UFUNCTION(BlueprintPure, Category = "HttpGPT | Image") - const FHttpGPTImageOptions GetImageOptions() const; + UFUNCTION(BlueprintPure, Category = "HttpGPT | Image") + const FHttpGPTImageOptions GetImageOptions() const; - UFUNCTION(BlueprintPure, Category = "HttpGPT | Image") - const FString GetPrompt() const; + UFUNCTION(BlueprintPure, Category = "HttpGPT | Image") + const FString GetPrompt() const; protected: - FString Prompt; - FHttpGPTImageOptions ImageOptions; + FString Prompt; + FHttpGPTImageOptions ImageOptions; - virtual bool CanActivateTask() const override; - virtual bool CanBindProgress() const override; - virtual FString GetEndpointURL() const override; + virtual bool CanActivateTask() const override; + virtual bool CanBindProgress() const override; + virtual FString GetEndpointURL() const override; - virtual FString SetRequestContent() override; - virtual void OnProgressCompleted(const FString& Content, const bool bWasSuccessful) override; + virtual FString SetRequestContent() override; + virtual void OnProgressCompleted(const FString& Content, const bool bWasSuccessful) override; - void DeserializeResponse(const FString& Content); + void DeserializeResponse(const FString& Content); private: - FHttpGPTImageResponse Response; + FHttpGPTImageResponse Response; }; DECLARE_DYNAMIC_DELEGATE_OneParam(FHttpGPTImageGenerate, class UTexture2D*, Image); @@ -78,16 +81,16 @@ DECLARE_DYNAMIC_DELEGATE_OneParam(FHttpGPTImageGenerate, class UTexture2D*, Imag UCLASS(NotPlaceable, Category = "HttpGPT | Image", Meta = (DisplayName = "HttpGPT Image Helper")) class HTTPGPTIMAGEMODULE_API UHttpGPTImageHelper final : public UBlueprintFunctionLibrary { - GENERATED_BODY() + GENERATED_BODY() public: - UFUNCTION(BlueprintPure, Category = "HttpGPT | Image", Meta = (DisplayName = "Cast to HttpGPT Image Request")) - static UHttpGPTImageRequest* CastToHttpGPTImageRequest(UObject* const Object); + UFUNCTION(BlueprintPure, Category = "HttpGPT | Image", Meta = (DisplayName = "Cast to HttpGPT Image Request")) + static UHttpGPTImageRequest* CastToHttpGPTImageRequest(UObject* const Object); - UFUNCTION(BlueprintCallable, Category = "HttpGPT | Image") - static void GenerateImage(const FHttpGPTImageData& ImageData, const FHttpGPTImageGenerate& Callback); + UFUNCTION(BlueprintCallable, Category = "HttpGPT | Image") + static void GenerateImage(const FHttpGPTImageData& ImageData, const FHttpGPTImageGenerate& Callback); private: - static void GenerateImageFromURL(const FHttpGPTImageData& ImageData, const FHttpGPTImageGenerate& Callback); - static void GenerateImageFromB64(const FHttpGPTImageData& ImageData, const FHttpGPTImageGenerate& Callback); + static void GenerateImageFromURL(const FHttpGPTImageData& ImageData, const FHttpGPTImageGenerate& Callback); + static void GenerateImageFromB64(const FHttpGPTImageData& ImageData, const FHttpGPTImageGenerate& Callback); };