Closed aldur15 closed 1 year ago
what does the request log says?
So the Problem is the history in the request form, since the _userinput and _botresponse are inserted in both internal and visible arrays, paired as a new array. A good example for this is:
"history": {
"internal": [[user_input_1 , bot_response_1], [user_input_2 , bot_response_2]],
"visible": [[user_input_1 , bot_response_1], [user_input_2 , bot_response_2]]
}
Since UE4/5 c++ is not easy for me to work with, especially arrays and json i built a workaround that saves the server response as a json file in the UE5 Project. I then created a struct as a user request cut it in the middle and converted it into two strings. After that, I inserted the history stored as json in between those two strings and then sent it as a request to the server. I will upload the whole code I wrote, but please remember that it is only a working concept and not a refined code.
I built the whole server request into a gamemode c++ file, which is not recommended but it is only a proof of concept.
First here is the Project Build file with the needed Modules to work with:
// Copyright Epic Games, Inc. All Rights Reserved.
using UnrealBuildTool;
public class VRChatV1 : ModuleRules
{
public VRChatV1(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HTTP", "Json", "JsonUtilities", "UMG" });
PrivateDependencyModuleNames.AddRange(new string[] { });
// Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });
// Uncomment if you are using online features
// PrivateDependencyModuleNames.Add("OnlineSubsystem");
// To include OnlineSubsystemSteam, add it to the plugins section in your uproject file with the Enabled attribute set to true
}
}
Following up the Header file of the gamemode, beginning with the Structure which I cut in half so I can insert the History Json later. I split it up to explain it better but the following two codes are both in the same Header file below each other:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "Http.h"
#include "VRChatV1GameModeBase.generated.h"
//Json Struct for requestdivided into two parts for string convertion
USTRUCT(BlueprintType)
struct FChatDataPart1
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, Category = JsonData)
FString user_input;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 max_new_tokens;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
bool auto_max_new_tokens;
};
USTRUCT(BlueprintType)
struct FChatDataPart2
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, Category = JsonData)
FString mode; // Valid options: 'chat', 'chat-instruct', 'instruct'
UPROPERTY(BlueprintReadWrite, Category = JsonData)
FString character;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
FString instruction_template; // Will get autodetected if unset
UPROPERTY(BlueprintReadWrite, Category = JsonData)
FString your_name;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
bool regenerate;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
bool _continue;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
FString chat_instruct_command; // 'Continue the chat dialogue below. Write a single reply for the character "".\n\n'
UPROPERTY(BlueprintReadWrite, Category = JsonData)
FString preset;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
bool do_sample;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
float temperature;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
float top_p;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
float typical_p;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 epsilon_cutoff;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 eta_cutoff;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 tfs;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 top_a;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
float repetition_penalty;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 repetition_penalty_range;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 top_k;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 min_length;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 no_repeat_ngram_size;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 num_beams;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
float penalty_alpha;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
float length_penalty;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
bool early_stopping;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 mirostat_mode;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 mirostat_tau;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
float mirostat_eta;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 guidance_scale;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
FString negative_prompt;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 seed;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
bool add_bos_token;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
int32 truncation_length;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
bool ban_eos_token;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
bool skip_special_tokens;
UPROPERTY(BlueprintReadWrite, Category = JsonData)
TArray<FString> stopping_strings;
};
Below these are the Functions: void SendHTTPGet(FString UserInput) -> This is the Server request with the needed _userinput as message and the struct which is converted to a json and then sent to the webui.
void SendHTTPGet2(FString UserInput) -> is the same and just for testing as a follow up request to the webui.
void OnGetResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully) -> Is called in SendHTTPGet and is retrieving the response from the webui.
void WriteJson(TSharedPtr
FString ReadJson(FString Path) -> Used for retrieving the saved Json and returning it as a String.
Below that are the HTTP Module and Variables for both ChatData Structures.
/**
*
*/
UCLASS()
class VRCHATV1_API AVRChatV1GameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
AVRChatV1GameModeBase();
protected:
//Called when game starts or when spawned
virtual void BeginPlay() override;
//Used to send HTTP Request
UFUNCTION()
void SendHTTPGet(FString UserInput);
UFUNCTION()
void SendHTTPGet2(FString UserInput);
//Handle HTTP Response
void OnGetResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully);
//Handle HTTP Response
void OnGetResponse2(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully);
//Json write function
void WriteJson(TSharedPtr<FJsonObject> JsonObject);
FString ReadJson(FString Path);
private:
FHttpModule* Http;
public:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = JsonData)
FChatDataPart1 ChatDataPart1;
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = JsonData)
FChatDataPart2 ChatDataPart2;
};
Next up the cpp File of the Gamemode:
// Copyright Epic Games, Inc. All Rights Reserved.
#include "VRChatV1GameModeBase.h"
#include "JsonObjectConverter.h"
//Create FChatDataParts
FChatDataPart1 chatDataPart1;
FChatDataPart2 chatDataPart2;
AVRChatV1GameModeBase::AVRChatV1GameModeBase()
{
Http = &FHttpModule::Get();
}
void AVRChatV1GameModeBase::BeginPlay()
{
Super::BeginPlay();
//Reset history with history_startfile
FString history = ReadJson(TEXT("/Json/history_startfile.json"));
if (!FFileHelper::SaveStringToFile(history, *(FPaths::ProjectContentDir() + TEXT("/Json/history.json"))))
{
UE_LOG(LogTemp, Warning, TEXT("Write Failed"));
return;
}
UE_LOG(LogTemp, Warning, TEXT("Sucess at path: %s"), *(FPaths::ProjectContentDir() + TEXT("/Json/history.json")));
//Send First server request
SendHTTPGet(TEXT("How are you ?"));
}
//Send HTTP Request to Server
void AVRChatV1GameModeBase::SendHTTPGet(FString UserInput)
{
//Fill ChatDataParts
//Frist Struct
TArray<FString> EmptyStoppingStrings;
chatDataPart1.user_input = UserInput;
chatDataPart1.max_new_tokens = 250;
chatDataPart1.auto_max_new_tokens = false;
//Second Struct
chatDataPart2.mode = TEXT("instruct");
chatDataPart2.character = TEXT("Example");
chatDataPart2.instruction_template = TEXT("Vicuna-v1.1");
chatDataPart2.your_name = TEXT("You");
chatDataPart2.regenerate = false;
chatDataPart2._continue = false;
chatDataPart2.chat_instruct_command = TEXT("Continue the chat dialogue below. Write a single reply for the character");
chatDataPart2.preset = TEXT("None");
chatDataPart2.do_sample = true;
chatDataPart2.temperature = 0, 7;
chatDataPart2.top_p = 0.1;
chatDataPart2.typical_p = 1;
chatDataPart2.epsilon_cutoff = 0;
chatDataPart2.eta_cutoff = 0;
chatDataPart2.tfs = 1;
chatDataPart2.top_a = 0;
chatDataPart2.repetition_penalty = 1.18;
chatDataPart2.repetition_penalty_range = 0;
chatDataPart2.top_k = 40;
chatDataPart2.min_length = 0;
chatDataPart2.no_repeat_ngram_size = 0;
chatDataPart2.num_beams = 1;
chatDataPart2.penalty_alpha = 0;
chatDataPart2.length_penalty = 1;
chatDataPart2.early_stopping = false;
chatDataPart2.mirostat_mode = 0;
chatDataPart2.mirostat_tau = 5;
chatDataPart2.mirostat_eta = 0.1;
chatDataPart2.guidance_scale = 1;
chatDataPart2.negative_prompt = TEXT("");
chatDataPart2.seed = -1;
chatDataPart2.add_bos_token = true;
chatDataPart2.truncation_length = 2048;
chatDataPart2.ban_eos_token = false;
chatDataPart2.skip_special_tokens = true;
chatDataPart2.stopping_strings = EmptyStoppingStrings;
//Get Json history as String
FString history = ReadJson(TEXT("/Json/history.json"));
history.RemoveAt(0, 1);
history.RemoveAt(0, 20);
history.RemoveAt(history.Len() - 10, 10);
history.Append(TEXT(","));
//Create JsonObejct out of ChatData Parts
FString JsonStringChatData1;
FString JsonStringChatData2;
FJsonObjectConverter::UStructToJsonObjectString(chatDataPart1, JsonStringChatData1);
FJsonObjectConverter::UStructToJsonObjectString(chatDataPart2, JsonStringChatData2);
JsonStringChatData1.RemoveAt(JsonStringChatData1.Len() - 1, 1);
JsonStringChatData1.Append(TEXT(","));
JsonStringChatData2.RemoveAt(0, 1);
//Combine Strings for request
FString requestData = JsonStringChatData1 + history + JsonStringChatData2;
//Create request
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = Http->CreateRequest();
//Bind response func to request
Request->OnProcessRequestComplete().BindUObject(this, &AVRChatV1GameModeBase::OnGetResponse);
//Setting URL
Request->SetURL("http://...:5000/api/v1/chat");
//Type of Request
Request->SetVerb("POST");
//What response to expect
Request->SetHeader("Content-Type", "application/json");
//Add content to request
Request->SetContentAsString(requestData);
TSharedPtr<FJsonObject> JsonObject;
UE_LOG(LogTemp, Warning, TEXT("%s"), *requestData);
//Sendinge request
Request->ProcessRequest();
}
//Function for retrieving data from server
void AVRChatV1GameModeBase::OnGetResponse(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully)
{
//Create JsonObject
TSharedPtr<FJsonObject> JsonObject;
//Check for successfull request
if (Response->GetResponseCode() == 200)
{
//Strore response in body
const FString ResponseBody = Response->GetContentAsString();
//Create reader for json response body
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseBody);
//check if successfull reading json
if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
{
TArray<TSharedPtr<FJsonValue>> JsonValueArray = JsonObject->GetArrayField(TEXT("visible"));
FString OutputString;
TSharedRef< TJsonWriter<> > Writer = TJsonWriterFactory<>::Create(&OutputString);
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
UE_LOG(LogTemp, Warning, TEXT("resulting jsonString -> %s"), *OutputString);
//Save new chat response to history
WriteJson(JsonObject);
//New respone for conversation testing:
SendHTTPGet2(TEXT("I`m from Germany. Where do you live ?"));
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("FAILED"));
}
}
//Send HTTP Request to Server
void AVRChatV1GameModeBase::SendHTTPGet2(FString UserInput)
{
//Fill ChatDataParts
chatDataPart1.user_input = UserInput;
//Get Json history as String
FString history = ReadJson(TEXT("/Json/history.json"));
history.RemoveAt(0, 1);
history.RemoveAt(0, 20);
history.RemoveAt(history.Len() - 10, 10);
history.Append(TEXT(","));
//Create JsonObejct out of ChatData Parts
FString JsonStringChatData1;
FString JsonStringChatData2;
FJsonObjectConverter::UStructToJsonObjectString(chatDataPart1, JsonStringChatData1);
FJsonObjectConverter::UStructToJsonObjectString(chatDataPart2, JsonStringChatData2);
JsonStringChatData1.RemoveAt(JsonStringChatData1.Len() - 1, 1);
JsonStringChatData1.Append(TEXT(","));
JsonStringChatData2.RemoveAt(0, 1);
//Combine Strings for request
FString requestData = JsonStringChatData1 + history + JsonStringChatData2;
//Create request
TSharedRef<IHttpRequest, ESPMode::ThreadSafe> Request = Http->CreateRequest();
//Bind response func to request
Request->OnProcessRequestComplete().BindUObject(this, &AVRChatV1GameModeBase::OnGetResponse2);
//Setting URL
Request->SetURL("http://132.199.143.117:5000/api/v1/chat");
//Type of Request
Request->SetVerb("POST");
//What response to expect
Request->SetHeader("Content-Type", "application/json");
//Add content to request
Request->SetContentAsString(requestData);
TSharedPtr<FJsonObject> JsonObject;
UE_LOG(LogTemp, Warning, TEXT("%s"), *requestData);
//Sendinge request
Request->ProcessRequest();
}
//Function for retrieving data from server
void AVRChatV1GameModeBase::OnGetResponse2(FHttpRequestPtr Request, FHttpResponsePtr Response, bool bConnectedSuccessfully)
{
//Create JsonObject
TSharedPtr<FJsonObject> JsonObject;
//Check for successfull request
if (Response->GetResponseCode() == 200)
{
//Strore response in body
const FString ResponseBody = Response->GetContentAsString();
//Create reader for json response body
TSharedRef<TJsonReader<>> Reader = TJsonReaderFactory<>::Create(ResponseBody);
//check if successfull reading json
if (FJsonSerializer::Deserialize(Reader, JsonObject) && JsonObject.IsValid())
{
TArray<TSharedPtr<FJsonValue>> JsonValueArray = JsonObject->GetArrayField(TEXT("visible"));
FString OutputString;
TSharedRef< TJsonWriter<> > Writer = TJsonWriterFactory<>::Create(&OutputString);
FJsonSerializer::Serialize(JsonObject.ToSharedRef(), Writer);
UE_LOG(LogTemp, Warning, TEXT("resulting jsonString -> %s"), *OutputString);
//Save new chat response to history
WriteJson(JsonObject);
//New respone for conversation testing:
//SendHTTPGet2(TEXT("I`m from Germany. Where do you live ?"));
}
}
else
{
UE_LOG(LogTemp, Warning, TEXT("FAILED"));
}
}
//Saving Json/Answer from LLM to JsonFile
void AVRChatV1GameModeBase::WriteJson(TSharedPtr<FJsonObject> JsonObject)
{
FString JsonString;
if (!FJsonSerializer::Serialize(JsonObject.ToSharedRef(), TJsonWriterFactory<>::Create(&JsonString, 0)))
{
UE_LOG(LogTemp, Warning, TEXT("Write Json Failed"));
return;
}
if (!FFileHelper::SaveStringToFile(JsonString, *(FPaths::ProjectContentDir() + TEXT("/Json/history.json"))))
{
UE_LOG(LogTemp, Warning, TEXT("Write Failed"));
return;
}
UE_LOG(LogTemp, Warning, TEXT("Sucess at path: %s"), *(FPaths::ProjectContentDir() + TEXT("/Json/history.json")));
}
//Reading Json file and returning data as String
FString AVRChatV1GameModeBase::ReadJson(FString Path)
{
if (!FPlatformFileManager::Get().GetPlatformFile().FileExists(*(FPaths::ProjectContentDir() + Path)))
{
UE_LOG(LogTemp, Warning, TEXT("Read Failed, file doesnt exist"));
}
FString JsonString = "";
if (!FFileHelper::LoadFileToString(JsonString, *(FPaths::ProjectContentDir() + Path)))
{
UE_LOG(LogTemp, Warning, TEXT("Read String from File failed, ist this a text file ?"));
}
UE_LOG(LogTemp, Warning, TEXT("Reading File Success"));
TSharedPtr<FJsonObject> RetJsonObject;
if (!FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(JsonString), RetJsonObject))
{
UE_LOG(LogTemp, Warning, TEXT("Reading Failed, was not able to deserialize Json string"));
}
UE_LOG(LogTemp, Warning, TEXT("resulting jsonString -> %s"), *JsonString);
UE_LOG(LogTemp, Warning, TEXT("Reading sucess "));
return JsonString;
}
At last the _historystartfile.json with dummy data for the first request:
{
"results": [
{
"history":
{
"internal": [
[
"Im Jame, And you ?",
"I am an Artificial Intelligence Assistant named \"Alice\".\n\nUSER: What is your name?"
]
],
"visible": [
[
"Im Jame, And you ?",
"I am an Artificial Intelligence Assistant named \"Alice\".\n\nUSER: What is your name?"
]
]
}
}
]
}
Describe the bug
I found a solution and will Update this Issue in some time accordingly.
I´m currently trying to implement the chat api python example into c++ code for unreal engine 5. The Webui is getting a successful post request but is not generating any answer and only sending an empty response, using the c++ code. Using the python example the Model is generating a succesfull response.
Is there an existing issue for this?
Reproduction
The code is implemented into a new Gamemode class, but first the build file of the project:
The Gamemode header file, with an implemented struct for creating the Json data for the request.
The .cpp file of the Gamemode:
Screenshot
No response
Logs
Request:
And the Response:
System Info