Files
UE5-cyberHuman/Plugins/OpenAi/Source/OpenAI/Private/FuncLib/OpenAIFuncLib.cpp

702 lines
24 KiB
C++
Raw Normal View History

2025-04-07 18:31:41 -07:00
// OpenAI Sample, Copyright LifeEXE. All Rights Reserved.
#include "FuncLib/OpenAIFuncLib.h"
#include "Serialization/JsonReader.h"
#include "Serialization/JsonSerializer.h"
#include "Internationalization/Regex.h"
DEFINE_LOG_CATEGORY_STATIC(LogOpenAIFuncLib, All, All);
FString UOpenAIFuncLib::OpenAIAllModelToString(EAllModelEnum Model)
{
switch (Model)
{
case EAllModelEnum::Whisper_1: return "whisper-1";
case EAllModelEnum::GPT_3_5_Turbo_0301: return "gpt-3.5-turbo-0301";
case EAllModelEnum::GPT_3_5_Turbo: return "gpt-3.5-turbo";
case EAllModelEnum::GPT_3_5_Turbo_16k_0613: return "gpt-3.5-turbo-16k-0613";
case EAllModelEnum::GPT_3_5_Turbo_16k: return "gpt-3.5-turbo-16k";
case EAllModelEnum::GPT_3_5_Turbo_0613: return "gpt-3.5-turbo-0613";
case EAllModelEnum::GPT_3_5_Turbo_Instruct_0914: return "gpt-3.5-turbo-instruct-0914";
case EAllModelEnum::GPT_3_5_Turbo_Instruct: return "gpt-3.5-turbo-instruct";
case EAllModelEnum::Text_Embedding_Ada_002: return "text-embedding-ada-002";
case EAllModelEnum::GPT_4: return "gpt-4";
case EAllModelEnum::GPT_4_0314: return "gpt-4-0314";
case EAllModelEnum::GPT_4_0613: return "gpt-4-0613";
case EAllModelEnum::DALL_E_2: return "dall-e-2";
case EAllModelEnum::DALL_E_3: return "dall-e-3";
case EAllModelEnum::GPT_4_1106_Preview: return "gpt-4-1106-preview";
case EAllModelEnum::GPT_4_Vision_Preview: return "gpt-4-vision-preview";
case EAllModelEnum::GPT_3_5_Turbo_1106: return "gpt-3.5-turbo-1106";
case EAllModelEnum::TTS_1: return "tts-1";
case EAllModelEnum::TTS_1_HD: return "tts-1-hd";
case EAllModelEnum::TTS_1_1106: return "tts-1-1106";
case EAllModelEnum::TTS_1_HD_1106: return "tts-1-hd-1106";
case EAllModelEnum::Text_Embedding_3_Large: return "text-embedding-3-large";
case EAllModelEnum::GPT_4_32K_0314: return "gpt-4-32k-0314";
case EAllModelEnum::GPT_3_5_Turbo_0125: return "gpt-3.5-turbo-0125";
case EAllModelEnum::Text_Embedding_3_Small: return "text-embedding-3-small";
case EAllModelEnum::GPT_4_0125_Preview: return "gpt-4-0125-preview";
case EAllModelEnum::GPT_4_Turbo_Preview: return "gpt-4-turbo-preview";
case EAllModelEnum::GPT_4O_2024_05_13: return "gpt-4o-2024-05-13";
case EAllModelEnum::GPT_4O: return "gpt-4o";
case EAllModelEnum::GPT_4_Turbo_2024_04_09: return "gpt-4-turbo-2024-04-09";
case EAllModelEnum::GPT_4_Turbo: return "gpt-4-turbo";
case EAllModelEnum::GPT_4_1106_Vision_Preview: return "gpt-4-1106-vision-preview";
}
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIMainModelToString(EMainModelEnum Model)
{
switch (Model)
{
case EMainModelEnum::GPT_4O: return "gpt-4o";
case EMainModelEnum::GPT_4: return "gpt-4";
case EMainModelEnum::GPT_4_0314: return "gpt-4-0314";
case EMainModelEnum::GPT_4_0613: return "gpt-4-0613";
case EMainModelEnum::GPT_4_1106_Preview: return "gpt-4-1106-preview";
case EMainModelEnum::GPT_4_Vision_Preview: return "gpt-4-vision-preview";
case EMainModelEnum::GPT_3_5_Turbo_0301: return "gpt-3.5-turbo-0301";
case EMainModelEnum::GPT_3_5_Turbo: return "gpt-3.5-turbo";
case EMainModelEnum::GPT_3_5_Turbo_Instruct: return "gpt-3.5-turbo-instruct";
}
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIModerationModelToString(EModerationsModelEnum Model)
{
switch (Model)
{
case EModerationsModelEnum::Text_Moderation_Latest: return "text-moderation-latest";
case EModerationsModelEnum::Text_Moderation_Stable: return "text-moderation-stable";
}
checkNoEntry();
return {};
}
bool UOpenAIFuncLib::ModelSupportsVision(const FString& Model)
{
return OpenAIAllModelToString(EAllModelEnum::GPT_4_Vision_Preview).Equals(Model) ||
OpenAIAllModelToString(EAllModelEnum::GPT_4_1106_Vision_Preview).Equals(Model) ||
OpenAIAllModelToString(EAllModelEnum::GPT_4O).Equals(Model);
}
FString UOpenAIFuncLib::OpenAIAudioModelToString(EAudioModel Model)
{
switch (Model)
{
case EAudioModel::Whisper_1: return "whisper-1";
}
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAITTSModelToString(ETTSModel Model)
{
switch (Model)
{
case ETTSModel::TTS_1: return "tts-1";
case ETTSModel::TTS_1_HD: return "tts-1-hd";
}
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIVoiceToString(EVoice Voice)
{
switch (Voice)
{
case EVoice::Alloy: return "alloy";
case EVoice::Echo: return "echo";
case EVoice::Fable: return "fable";
case EVoice::Nova: return "nova";
case EVoice::Onyx: return "onyx";
case EVoice::Shimmer: return "shimmer";
}
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAITTSAudioFormatToString(ETTSAudioFormat Format)
{
switch (Format)
{
case ETTSAudioFormat::AAC: return "aac";
case ETTSAudioFormat::FLAC: return "flac";
case ETTSAudioFormat::MP3: return "mp3";
case ETTSAudioFormat::OPUS: return "opus";
}
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIImageModelToString(EImageModelEnum Model)
{
switch (Model)
{
case EImageModelEnum::DALL_E_2: return "dall-e-2";
case EImageModelEnum::DALL_E_3: return "dall-e-3";
}
checkNoEntry();
return {};
}
EImageModelEnum UOpenAIFuncLib::StringToOpenAIImageModel(const FString& Model)
{
if (Model.Equals("dall-e-2")) return EImageModelEnum::DALL_E_2;
if (Model.Equals("dall-e-3")) return EImageModelEnum::DALL_E_3;
UE_LOG(LogOpenAIFuncLib, Error, TEXT("Unknown EImageModelEnum: %s"), *Model);
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIImageSizeDalle2ToString(EImageSizeDalle2 ImageSize)
{
switch (ImageSize)
{
case EImageSizeDalle2::Size_256x256: return "256x256";
case EImageSizeDalle2::Size_512x512: return "512x512";
case EImageSizeDalle2::Size_1024x1024: return "1024x1024";
}
checkNoEntry();
return {};
}
EImageSizeDalle2 UOpenAIFuncLib::StringToOpenAIImageSizeDalle2(const FString& ImageSize)
{
if (ImageSize.Equals("256x256")) return EImageSizeDalle2::Size_256x256;
if (ImageSize.Equals("512x512")) return EImageSizeDalle2::Size_512x512;
if (ImageSize.Equals("1024x1024")) return EImageSizeDalle2::Size_1024x1024;
UE_LOG(LogOpenAIFuncLib, Error, TEXT("Unknown EImageSizeDalle2: %s"), *ImageSize);
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIImageSizeDalle3ToString(EImageSizeDalle3 ImageSize)
{
switch (ImageSize)
{
case EImageSizeDalle3::Size_1024x1024: return "1024x1024";
case EImageSizeDalle3::Size_1024x1792: return "1024x1792";
case EImageSizeDalle3::Size_1792x1024: return "1792x1024";
}
checkNoEntry();
return {};
}
EImageSizeDalle3 UOpenAIFuncLib::StringToOpenAIImageSizeDalle3(const FString& ImageSize)
{
if (ImageSize.Equals("1024x1024")) return EImageSizeDalle3::Size_1024x1024;
if (ImageSize.Equals("1024x1792")) return EImageSizeDalle3::Size_1024x1792;
if (ImageSize.Equals("1792x1024")) return EImageSizeDalle3::Size_1792x1024;
UE_LOG(LogOpenAIFuncLib, Error, TEXT("Unknown EImageSizeDalle3: %s"), *ImageSize);
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIImageFormatToString(EOpenAIImageFormat ImageFormat)
{
switch (ImageFormat)
{
case EOpenAIImageFormat::URL: return "url";
case EOpenAIImageFormat::B64_JSON: return "b64_json";
}
checkNoEntry();
return {};
}
EOpenAIImageFormat UOpenAIFuncLib::StringToOpenAIImageFormat(const FString& ImageFormat)
{
if (ImageFormat.Equals("url")) return EOpenAIImageFormat::URL;
if (ImageFormat.Equals("b64_json")) return EOpenAIImageFormat::B64_JSON;
UE_LOG(LogOpenAIFuncLib, Error, TEXT("Unknown EOpenAIImageFormat: %s"), *ImageFormat);
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIImageQualityToString(EOpenAIImageQuality ImageQuality)
{
switch (ImageQuality)
{
case EOpenAIImageQuality::HD: return "hd";
case EOpenAIImageQuality::Standard: return "standard";
}
checkNoEntry();
return {};
}
EOpenAIImageQuality UOpenAIFuncLib::StringToOpenAIImageQuality(const FString& ImageQuality)
{
if (ImageQuality.Equals("hd")) return EOpenAIImageQuality::HD;
if (ImageQuality.Equals("standard")) return EOpenAIImageQuality::Standard;
UE_LOG(LogOpenAIFuncLib, Error, TEXT("Unknown EOpenAIImageQuality: %s"), *ImageQuality);
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIImageStyleToString(EOpenAIImageStyle ImageStyle)
{
switch (ImageStyle)
{
case EOpenAIImageStyle::Natural: return "natural";
case EOpenAIImageStyle::Vivid: return "vivid";
}
checkNoEntry();
return {};
}
EOpenAIImageStyle UOpenAIFuncLib::StringToOpenAIImageStyle(const FString& ImageStyle)
{
if (ImageStyle.Equals("natural")) return EOpenAIImageStyle::Natural;
if (ImageStyle.Equals("vivid")) return EOpenAIImageStyle::Vivid;
UE_LOG(LogOpenAIFuncLib, Error, TEXT("Unknown EOpenAIImageStyle: %s"), *ImageStyle);
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIRoleToString(ERole Role)
{
switch (Role)
{
case ERole::System: return "system";
case ERole::User: return "user";
case ERole::Assistant: return "assistant";
case ERole::Function: return "function";
case ERole::Tool: return "tool";
}
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIFinishReasonToString(EOpenAIFinishReason FinishReason)
{
switch (FinishReason)
{
case EOpenAIFinishReason::Stop: return "stop";
case EOpenAIFinishReason::Length: return "length";
case EOpenAIFinishReason::Content_Filter: return "content_filter";
case EOpenAIFinishReason::Tool_Calls: return "tool_calls";
case EOpenAIFinishReason::Null: return "";
}
checkNoEntry();
return {};
}
EOpenAIFinishReason UOpenAIFuncLib::StringToOpenAIFinishReason(const FString& FinishReason)
{
if (FinishReason.Equals("stop")) return EOpenAIFinishReason::Stop;
if (FinishReason.Equals("length")) return EOpenAIFinishReason::Length;
if (FinishReason.Equals("content_filter")) return EOpenAIFinishReason::Content_Filter;
if (FinishReason.Equals("tool_calls")) return EOpenAIFinishReason::Tool_Calls;
if (FinishReason.IsEmpty()) return EOpenAIFinishReason::Null;
UE_LOG(LogOpenAIFuncLib, Error, TEXT("Unknown OpenAIFinishReason: %s"), *FinishReason);
checkNoEntry();
return {};
}
ERole UOpenAIFuncLib::StringToOpenAIRole(const FString& Role)
{
if (Role.ToLower().Equals("system")) return ERole::System;
if (Role.ToLower().Equals("user")) return ERole::User;
if (Role.ToLower().Equals("assistant")) return ERole::Assistant;
if (Role.ToLower().Equals("function")) return ERole::Function;
if (Role.ToLower().Equals("tool")) return ERole::Tool;
UE_LOG(LogOpenAIFuncLib, Error, TEXT("Unknown OpenAIRole: %s"), *Role);
checkNoEntry();
return {};
}
FOpenAIAuth UOpenAIFuncLib::LoadAPITokensFromFile(const FString& FilePath)
{
TArray<FString> FileLines;
if (!FFileHelper::LoadFileToStringArray(FileLines, *FilePath))
{
UE_LOG(LogOpenAIFuncLib, Error, TEXT("Failed loading file: %s"), *FilePath);
return {};
}
else if (FileLines.Num() < 2)
{
UE_LOG(LogOpenAIFuncLib, Error, TEXT("Auth file might have 2 or 3 lines only"));
return {};
}
FOpenAIAuth Auth;
FString ParamName, ParamValue;
FileLines[0].Split("=", &ParamName, &ParamValue);
Auth.APIKey = ParamValue;
FileLines[1].Split("=", &ParamName, &ParamValue);
Auth.OrganizationID = ParamValue;
if (FileLines.Num() > 2)
{
FileLines[2].Split("=", &ParamName, &ParamValue);
Auth.ProjectID = ParamValue;
}
return Auth;
}
FOpenAIAuth UOpenAIFuncLib::LoadAPITokensFromFileOnce(const FString& FilePath)
{
static FOpenAIAuth Auth;
if (Auth.IsEmpty())
{
Auth = LoadAPITokensFromFile(FilePath);
}
return Auth;
}
OpenAI::ServiceSecrets UOpenAIFuncLib::LoadServiceSecretsFromFile(const FString& FilePath)
{
TArray<FString> FileLines;
if (!FFileHelper::LoadFileToStringArray(FileLines, *FilePath))
{
UE_LOG(LogOpenAIFuncLib, Error, TEXT("Failed loading file: %s"), *FilePath);
return {};
}
OpenAI::ServiceSecrets Secrets;
for (const auto& Line : FileLines)
{
FString SecretName, SecretValue;
Line.Split("=", &SecretName, &SecretValue);
Secrets.Add(MakeTuple(SecretName, SecretValue));
}
return Secrets;
}
bool UOpenAIFuncLib::LoadSecretByName(const OpenAI::ServiceSecrets& Secrets, const FString& SecretName, FString& SecretValue)
{
const auto* Found =
Secrets.FindByPredicate([&](const TTuple<FString, FString>& SecretData) { return SecretData.Key.Equals(SecretName); });
if (Found)
{
SecretValue = *Found->Value;
return true;
}
SecretValue = {};
return false;
}
FString UOpenAIFuncLib::OpenAIAudioTranscriptToString(ETranscriptFormat TranscriptFormat)
{
switch (TranscriptFormat)
{
case ETranscriptFormat::JSON: return "json";
case ETranscriptFormat::Text: return "text";
case ETranscriptFormat::Str: return "str";
case ETranscriptFormat::Verbose_JSON: return "verbose_json";
case ETranscriptFormat::Vtt: return "vtt";
}
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIEmbeddingsEncodingFormatToString(EEmbeddingsEncodingFormat EmbeddingsEncodingFormat)
{
switch (EmbeddingsEncodingFormat)
{
case EEmbeddingsEncodingFormat::Float: return "float";
case EEmbeddingsEncodingFormat::Base64: return "base64";
}
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIChatResponseFormatToString(EChatResponseFormat ChatResponseFormat)
{
switch (ChatResponseFormat)
{
case EChatResponseFormat::Text: return "text";
case EChatResponseFormat::JSON_Object: return "json_object";
}
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::OpenAIModelToString(const FOpenAIModel& OpenAIModel)
{
FString Out = FString::Printf(TEXT("id: %s\n"), *OpenAIModel.ID);
Out.Append(FString::Printf(TEXT("object: %s\n"), *OpenAIModel.Object));
Out.Append(FString::Printf(TEXT("created: %i\n"), OpenAIModel.Created));
Out.Append(FString::Printf(TEXT("owned_by: %s\n"), *OpenAIModel.Owned_By));
Out.Append(FString::Printf(TEXT("root: %s\n"), *OpenAIModel.Root));
Out.Append(FString::Printf(TEXT("parent: %s\n"), *OpenAIModel.Parent));
return Out;
}
FString UOpenAIFuncLib::OpenAIMessageContentTypeToString(EMessageContentType MessageContentType)
{
switch (MessageContentType)
{
case EMessageContentType::Text: return "text";
case EMessageContentType::Image_URL: return "image_url";
}
checkNoEntry();
return {};
}
FString UOpenAIFuncLib::BoolToString(bool Value)
{
return Value ? TEXT("true") : TEXT("false");
}
FString UOpenAIFuncLib::RemoveWhiteSpaces(const FString& Input)
{
FString Result;
const TSet<TCHAR> Whitespaces{'\t', '\n', '\r'};
for (const TCHAR& Char : Input)
{
if (!Whitespaces.Contains(Char))
{
Result += Char;
}
}
return Result;
}
bool UOpenAIFuncLib::StringToJson(const FString& JsonString, TSharedPtr<FJsonObject>& JsonObject)
{
TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(JsonString);
return FJsonSerializer::Deserialize(JsonReader, JsonObject);
}
namespace
{
void ConvertKeysToLowercaseRecursive(TSharedPtr<FJsonValue> Value);
void ConvertObjectKeysToLowercase(TSharedPtr<FJsonObject> JsonObject)
{
TSharedPtr<FJsonObject> NewJsonObject = MakeShareable(new FJsonObject);
for (const auto& Elem : JsonObject->Values)
{
const FString LowerKey = Elem.Key.ToLower();
ConvertKeysToLowercaseRecursive(Elem.Value);
NewJsonObject->SetField(LowerKey, Elem.Value);
}
*JsonObject = *NewJsonObject;
}
void ConvertKeysToLowercaseRecursive(TSharedPtr<FJsonValue> Value)
{
if (Value->Type == EJson::Object)
{
ConvertObjectKeysToLowercase(Value->AsObject());
}
else if (Value->Type == EJson::Array)
{
const TArray<TSharedPtr<FJsonValue>>& Array = Value->AsArray();
for (const auto& Item : Array)
{
ConvertKeysToLowercaseRecursive(Item);
}
}
}
} // namespace
bool UOpenAIFuncLib::JsonToString(const TSharedPtr<FJsonObject>& JsonObject, FString& JsonString)
{
TSharedPtr<FJsonObject> NewJsonObject = JsonObject;
ConvertObjectKeysToLowercase(NewJsonObject);
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&JsonString);
return FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
}
FString UOpenAIFuncLib::OpenAIModerationsToString(const FModerationResults& ModerationResults)
{
FString Out;
Out.Append(FString::Printf(TEXT("hate: %s\n"), *BoolToString(ModerationResults.Categories.Hate)));
Out.Append(FString::Printf(TEXT("hate/threatening: %s\n"), *BoolToString(ModerationResults.Categories.Hate_Threatening)));
Out.Append(FString::Printf(TEXT("self-harm: %s\n"), *BoolToString(ModerationResults.Categories.Self_Harm)));
Out.Append(FString::Printf(TEXT("sexual: %s\n"), *BoolToString(ModerationResults.Categories.Sexual)));
Out.Append(FString::Printf(TEXT("sexual/minors: %s\n"), *BoolToString(ModerationResults.Categories.Sexual_Minors)));
Out.Append(FString::Printf(TEXT("violence: %s\n"), *BoolToString(ModerationResults.Categories.Violence)));
Out.Append(FString::Printf(TEXT("violence/graphic: %s\n\n"), *BoolToString(ModerationResults.Categories.Violence_Graphic)));
Out.Append(FString::Printf(TEXT("hate: %f\n"), ModerationResults.Category_Scores.Hate));
Out.Append(FString::Printf(TEXT("hate/threatening: %f\n"), ModerationResults.Category_Scores.Hate_Threatening));
Out.Append(FString::Printf(TEXT("self-harm: %f\n"), ModerationResults.Category_Scores.Self_Harm));
Out.Append(FString::Printf(TEXT("sexual: %f\n"), ModerationResults.Category_Scores.Sexual));
Out.Append(FString::Printf(TEXT("sexual/minors: %f\n"), ModerationResults.Category_Scores.Sexual_Minors));
Out.Append(FString::Printf(TEXT("violence: %f\n"), ModerationResults.Category_Scores.Violence));
Out.Append(FString::Printf(TEXT("violence/graphic: %f\n\n"), ModerationResults.Category_Scores.Violence_Graphic));
Out.Append(FString::Printf(TEXT("flagged: %s"), *BoolToString(ModerationResults.Flagged)));
return Out;
}
EOpenAIResponseError UOpenAIFuncLib::GetErrorCode(const FString& RawError)
{
if (RawError.Contains("ConnectionError")) return EOpenAIResponseError::NetworkError;
TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(RawError);
TSharedPtr<FJsonObject> JsonObject;
if (!FJsonSerializer::Deserialize(JsonReader, JsonObject))
{
return EOpenAIResponseError::Unknown;
}
if (JsonObject.IsValid() && JsonObject->HasField("error"))
{
const auto Error = JsonObject->GetObjectField("error");
if (Error->HasField("code"))
{
const auto Code = Error->GetStringField("code");
if (Code.Contains("invalid_api_key"))
{
return EOpenAIResponseError::InvalidAPIKey;
}
else if (Code.Contains("model_not_found"))
{
return EOpenAIResponseError::ModelNotFound;
}
}
}
return EOpenAIResponseError::Unknown;
}
FString UOpenAIFuncLib::GetErrorMessage(const FString& RawError)
{
TSharedRef<TJsonReader<>> JsonReader = TJsonReaderFactory<>::Create(RawError);
TSharedPtr<FJsonObject> JsonObject;
if (!FJsonSerializer::Deserialize(JsonReader, JsonObject)) return {};
if (JsonObject.IsValid() && JsonObject->HasField("error"))
{
const auto Error = JsonObject->GetObjectField("error");
if (Error->HasField("message"))
{
return Error->GetStringField("message");
}
}
return {};
}
FString UOpenAIFuncLib::ResponseErrorToString(EOpenAIResponseError Code)
{
switch (Code)
{
case EOpenAIResponseError::InvalidAPIKey: return "Invalid API key";
case EOpenAIResponseError::NetworkError: return "Network error";
case EOpenAIResponseError::ModelNotFound: return "Model not found";
case EOpenAIResponseError::Unknown: return "Unknown error";
}
return "Unknown error code";
}
// we need two markes to make clean JSON object during request serialization
// basically to remove quotes
const FString UOpenAIFuncLib::START_FUNCTION_OBJECT_MARKER = "START_FUNCTION_OBJECT_MARKER";
const FString UOpenAIFuncLib::END_FUNCTION_OBJECT_MARKER = "END_FUNCTION_OBJECT_MARKER";
FString UOpenAIFuncLib::MakeFunctionsString(const TSharedPtr<FJsonObject>& Json)
{
FString Functions;
TSharedRef<TJsonWriter<>> Writer = TJsonWriterFactory<>::Create(&Functions);
FJsonSerializer::Serialize(Json.ToSharedRef(), Writer);
Functions.Append(END_FUNCTION_OBJECT_MARKER);
Functions = START_FUNCTION_OBJECT_MARKER + Functions;
return RemoveWhiteSpaces(Functions);
}
FString UOpenAIFuncLib::CleanUpFunctionsObject(const FString& Input)
{
FString Output{Input};
const FString StartMarker = FString::Format(TEXT("\"{0}"), {START_FUNCTION_OBJECT_MARKER});
const FString EndMarker = FString::Format(TEXT("{0}\""), {END_FUNCTION_OBJECT_MARKER});
const auto Find = [&](const FString& Str, int32 StartIndex)
{ return Output.Find(Str, ESearchCase::IgnoreCase, ESearchDir::FromStart, StartIndex); };
int32 StartIndex = Find(StartMarker, 0);
int32 EndIndex = Find(EndMarker, StartIndex);
while (StartIndex != INDEX_NONE && EndIndex != INDEX_NONE)
{
int32 ContentStart = StartIndex + StartMarker.Len();
int32 ContentEnd = EndIndex;
// Extract the substring that needs to have backslashes removed
FString ContentToClean = Output.Mid(ContentStart, ContentEnd - ContentStart);
// Replace backslashes within the extracted content
FString CleanedContent = ContentToClean.Replace(TEXT("\\"), TEXT(""));
// Replace old content with cleaned content
Output = Output.Left(ContentStart) + CleanedContent + Output.Mid(EndIndex);
StartIndex = Find(StartMarker, EndIndex);
EndIndex = Find(EndMarker, StartIndex);
}
Output = Output.Replace(*StartMarker, TEXT(""));
Output = Output.Replace(*EndMarker, TEXT(""));
return Output;
}
FString UOpenAIFuncLib::MakeURLWithQuery(const FString& URL, const OpenAI::QueryPairs& Args)
{
FString URLWithQuery = URL + "?";
for (const auto& [Name, Param] : Args)
{
URLWithQuery.Append(Name).Append("=").Append(Param).Append("&");
}
URLWithQuery = URLWithQuery.LeftChop(1);
return URLWithQuery;
}
FString UOpenAIFuncLib::WrapBase64(const FString& Base64String)
{
return FString::Format(TEXT("data:image/png;base64,{0}"), {Base64String});
}
FString UOpenAIFuncLib::UnWrapBase64(const FString& Base64String)
{
const FString ToRemove = TEXT("data:image/png;base64,");
return Base64String.Replace(*ToRemove, TEXT(""));
}
FString UOpenAIFuncLib::FilePathToBase64(const FString& FilePath)
{
TArray<uint8> ImageData;
if (!FFileHelper::LoadFileToArray(ImageData, *FilePath))
{
return {};
}
const FString ImageInBase64 = FBase64::Encode(ImageData);
return UOpenAIFuncLib::WrapBase64(ImageInBase64);
}