Skip to content

Commit 00e3c67

Browse files
authored
Merge pull request lucoiso#86 from lucoiso/development
HttpGPT v1.5.7
2 parents dbe5b06 + e27fd53 commit 00e3c67

File tree

15 files changed

+291
-36
lines changed

15 files changed

+291
-36
lines changed

HttpGPT.uplugin

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"FileVersion": 3,
3-
"Version": 19,
4-
"VersionName": "1.5.6",
3+
"Version": 20,
4+
"VersionName": "1.5.7",
55
"FriendlyName": "HttpGPT - GPT Integration",
66
"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.",
77
"Category": "Game Features",

Source/HttpGPTChatModule/Private/Tasks/HttpGPTChatRequest.cpp

+55-11
Original file line numberDiff line numberDiff line change
@@ -29,34 +29,35 @@
2929
#if WITH_EDITOR
3030
UHttpGPTChatRequest* UHttpGPTChatRequest::EditorTask(const TArray<FHttpGPTChatMessage>& Messages, const FHttpGPTChatOptions Options)
3131
{
32-
UHttpGPTChatRequest* const NewAsyncTask = SendMessages_CustomOptions(GEditor->GetEditorWorldContext().World(), Messages, FHttpGPTCommonOptions(), Options);
32+
UHttpGPTChatRequest* const NewAsyncTask = SendMessages_CustomOptions(GEditor->GetEditorWorldContext().World(), Messages, TArray<FHttpGPTFunction>(), FHttpGPTCommonOptions(), Options);
3333
NewAsyncTask->bIsEditorTask = true;
3434

3535
return NewAsyncTask;
3636
}
3737
#endif
3838

39-
UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessage_DefaultOptions(UObject* const WorldContextObject, const FString& Message)
39+
UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessage_DefaultOptions(UObject* const WorldContextObject, const FString& Message, const TArray<FHttpGPTFunction>& Functions)
4040
{
41-
return SendMessage_CustomOptions(WorldContextObject, Message, FHttpGPTCommonOptions(), FHttpGPTChatOptions());
41+
return SendMessage_CustomOptions(WorldContextObject, Message, Functions, FHttpGPTCommonOptions(), FHttpGPTChatOptions());
4242
}
4343

44-
UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessages_DefaultOptions(UObject* const WorldContextObject, const TArray<FHttpGPTChatMessage>& Messages)
44+
UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessages_DefaultOptions(UObject* const WorldContextObject, const TArray<FHttpGPTChatMessage>& Messages, const TArray<FHttpGPTFunction>& Functions)
4545
{
46-
return SendMessages_CustomOptions(WorldContextObject, Messages, FHttpGPTCommonOptions(), FHttpGPTChatOptions());
46+
return SendMessages_CustomOptions(WorldContextObject, Messages, Functions, FHttpGPTCommonOptions(), FHttpGPTChatOptions());
4747
}
4848

49-
UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessage_CustomOptions(UObject* const WorldContextObject, const FString& Message, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions)
49+
UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessage_CustomOptions(UObject* const WorldContextObject, const FString& Message, const TArray<FHttpGPTFunction>& Functions, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions)
5050
{
51-
return SendMessages_CustomOptions(WorldContextObject, { FHttpGPTChatMessage(EHttpGPTChatRole::User, Message) }, CommonOptions, ChatOptions);
51+
return SendMessages_CustomOptions(WorldContextObject, { FHttpGPTChatMessage(EHttpGPTChatRole::User, Message) }, Functions, CommonOptions, ChatOptions);
5252
}
5353

54-
UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessages_CustomOptions(UObject* const WorldContextObject, const TArray<FHttpGPTChatMessage>& Messages, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions)
54+
UHttpGPTChatRequest* UHttpGPTChatRequest::SendMessages_CustomOptions(UObject* const WorldContextObject, const TArray<FHttpGPTChatMessage>& Messages, const TArray<FHttpGPTFunction>& Functions, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions)
5555
{
5656
UHttpGPTChatRequest* const NewAsyncTask = NewObject<UHttpGPTChatRequest>();
5757
NewAsyncTask->Messages = Messages;
5858
NewAsyncTask->CommonOptions = CommonOptions;
5959
NewAsyncTask->ChatOptions = ChatOptions;
60+
NewAsyncTask->Functions = Functions;
6061

6162
NewAsyncTask->RegisterWithGameInstance(WorldContextObject);
6263

@@ -86,7 +87,7 @@ bool UHttpGPTChatRequest::CanBindProgress() const
8687

8788
FString UHttpGPTChatRequest::GetEndpointURL() const
8889
{
89-
return FString::Format(TEXT("https://api.openai.com/{0}"), { UHttpGPTHelper::GetEndpointForModel(GetChatOptions().Model).ToString() });
90+
return FString::Format(TEXT("{0}/{1}"), { GetCommonOptions().Endpoint, UHttpGPTHelper::GetEndpointForModel(GetChatOptions().Model, GetCommonOptions().bIsAzureOpenAI, GetCommonOptions().AzureOpenAIAPIVersion) });
9091
}
9192

9293
const FHttpGPTChatOptions UHttpGPTChatRequest::GetChatOptions() const
@@ -153,6 +154,17 @@ FString UHttpGPTChatRequest::SetRequestContent()
153154
}
154155

155156
JsonRequest->SetArrayField("messages", MessagesJson);
157+
158+
if (!Functions.IsEmpty())
159+
{
160+
TArray<TSharedPtr<FJsonValue>> FunctionsJson;
161+
for (const FHttpGPTFunction& Iterator : Functions)
162+
{
163+
FunctionsJson.Add(Iterator.GetFunction());
164+
}
165+
166+
JsonRequest->SetArrayField("functions", FunctionsJson);
167+
}
156168
}
157169
else
158170
{
@@ -352,18 +364,50 @@ void UHttpGPTChatRequest::DeserializeSingleResponse(const FString& Content)
352364

353365
if (const TSharedPtr<FJsonObject>* MessageObj; ChoiceObj->TryGetObjectField("message", MessageObj))
354366
{
355-
Choice->Message = FHttpGPTChatMessage(*(*MessageObj)->GetStringField("role"), *(*MessageObj)->GetStringField("content"));
367+
if (FString RoleStr; (*MessageObj)->TryGetStringField("role", RoleStr))
368+
{
369+
Choice->Message.Role = RoleStr == "user" ? EHttpGPTChatRole::User : EHttpGPTChatRole::Assistant;
370+
}
371+
372+
if (FString ContentStr; (*MessageObj)->TryGetStringField("content", ContentStr))
373+
{
374+
Choice->Message.Content = ContentStr;
375+
}
376+
377+
if (const TSharedPtr<FJsonObject>* FunctionObj; (*MessageObj)->TryGetObjectField("function_call", FunctionObj))
378+
{
379+
if (FString FunctionNameStr; (*FunctionObj)->TryGetStringField("name", FunctionNameStr))
380+
{
381+
Choice->Message.FunctionCall.Name = *FunctionNameStr;
382+
}
383+
if (FString FunctionArgumentsStr; (*FunctionObj)->TryGetStringField("arguments", FunctionArgumentsStr))
384+
{
385+
Choice->Message.FunctionCall.Arguments = FunctionArgumentsStr;
386+
}
387+
}
356388
}
357389
else if (const TSharedPtr<FJsonObject>* DeltaObj; ChoiceObj->TryGetObjectField("delta", DeltaObj))
358390
{
359391
if (FString RoleStr; (*DeltaObj)->TryGetStringField("role", RoleStr))
360392
{
361-
Choice->Message.Role = RoleStr == "user" ? EHttpGPTChatRole::User : EHttpGPTChatRole::Assistant;
393+
Choice->Message.Role = UHttpGPTHelper::NameToRole(*RoleStr);
362394
}
363395
else if (FString ContentStr; (*DeltaObj)->TryGetStringField("content", ContentStr))
364396
{
365397
Choice->Message.Content += ContentStr;
366398
}
399+
400+
if (const TSharedPtr<FJsonObject>* FunctionObj; (*DeltaObj)->TryGetObjectField("function_call", FunctionObj))
401+
{
402+
if (FString FunctionNameStr; (*FunctionObj)->TryGetStringField("name", FunctionNameStr))
403+
{
404+
Choice->Message.FunctionCall.Name = *FunctionNameStr;
405+
}
406+
if (FString FunctionArgumentsStr; (*FunctionObj)->TryGetStringField("arguments", FunctionArgumentsStr))
407+
{
408+
Choice->Message.FunctionCall.Arguments += FunctionArgumentsStr;
409+
}
410+
}
367411
}
368412
else if (FString MessageText; ChoiceObj->TryGetStringField("text", MessageText))
369413
{

Source/HttpGPTChatModule/Public/Tasks/HttpGPTChatRequest.h

+9-8
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,24 @@ class HTTPGPTCHATMODULE_API UHttpGPTChatRequest : public UHttpGPTBaseTask
3838
static UHttpGPTChatRequest* EditorTask(const TArray<FHttpGPTChatMessage>& Messages, const FHttpGPTChatOptions Options);
3939
#endif
4040

41-
UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Default", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Message with Default Options"))
42-
static UHttpGPTChatRequest* SendMessage_DefaultOptions(UObject* const WorldContextObject, const FString& Message);
41+
UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Default", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Message with Default Options", AutoCreateRefTerm = "Functions"))
42+
static UHttpGPTChatRequest* SendMessage_DefaultOptions(UObject* const WorldContextObject, const FString& Message, const TArray<FHttpGPTFunction>& Functions);
4343

44-
UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Default", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Messages with Default Options"))
45-
static UHttpGPTChatRequest* SendMessages_DefaultOptions(UObject* const WorldContextObject, const TArray<FHttpGPTChatMessage>& Messages);
44+
UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Default", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Messages with Default Options", AutoCreateRefTerm = "Functions"))
45+
static UHttpGPTChatRequest* SendMessages_DefaultOptions(UObject* const WorldContextObject, const TArray<FHttpGPTChatMessage>& Messages, const TArray<FHttpGPTFunction>& Functions);
4646

47-
UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Custom", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Message with Custom Options"))
48-
static UHttpGPTChatRequest* SendMessage_CustomOptions(UObject* const WorldContextObject, const FString& Message, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions);
47+
UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Custom", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Message with Custom Options", AutoCreateRefTerm = "Functions"))
48+
static UHttpGPTChatRequest* SendMessage_CustomOptions(UObject* const WorldContextObject, const FString& Message, const TArray<FHttpGPTFunction>& Functions, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions);
4949

50-
UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Custom", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Messages with Custom Options"))
51-
static UHttpGPTChatRequest* SendMessages_CustomOptions(UObject* const WorldContextObject, const TArray<FHttpGPTChatMessage>& Messages, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions);
50+
UFUNCTION(BlueprintCallable, Category = "HttpGPT | Chat | Custom", meta = (BlueprintInternalUseOnly = "true", WorldContext = "WorldContextObject", DisplayName = "Send Messages with Custom Options", AutoCreateRefTerm = "Functions"))
51+
static UHttpGPTChatRequest* SendMessages_CustomOptions(UObject* const WorldContextObject, const TArray<FHttpGPTChatMessage>& Messages, const TArray<FHttpGPTFunction>& Functions, const FHttpGPTCommonOptions CommonOptions, const FHttpGPTChatOptions ChatOptions);
5252

5353
UFUNCTION(BlueprintPure, Category = "HttpGPT | Chat")
5454
const FHttpGPTChatOptions GetChatOptions() const;
5555

5656
protected:
5757
TArray<FHttpGPTChatMessage> Messages;
58+
TArray<FHttpGPTFunction> Functions;
5859
FHttpGPTChatOptions ChatOptions;
5960

6061
virtual bool CanActivateTask() const override;

Source/HttpGPTCommonModule/Private/Management/HttpGPTSettings.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@ void UHttpGPTSettings::SetToDefaults()
8484
{
8585
CommonOptions.APIKey = NAME_None;
8686
CommonOptions.User = NAME_None;
87+
CommonOptions.bIsAzureOpenAI = false;
88+
CommonOptions.Endpoint = TEXT("https://api.openai.com/");
89+
CommonOptions.AzureOpenAIAPIVersion = TEXT("2023-05-15");
8790

8891
ChatOptions.Model = EHttpGPTChatModel::gpt35turbo;
8992
ChatOptions.MaxTokens = 2048;

Source/HttpGPTCommonModule/Private/Structures/HttpGPTChatTypes.cpp

+54-5
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,66 @@
1111
#include UE_INLINE_GENERATED_CPP_BY_NAME(HttpGPTChatTypes)
1212
#endif
1313

14-
FHttpGPTChatMessage::FHttpGPTChatMessage(const FName& Role, const FString& Content)
14+
TSharedPtr<FJsonValue> FHttpGPTFunction::GetFunction() const
1515
{
16-
this->Role = UHttpGPTHelper::NameToRole(Role);
17-
this->Content = Content;
16+
TSharedPtr<FJsonObject> JsonObject = MakeShared<FJsonObject>();
17+
JsonObject->SetStringField("name", Name.ToString());
18+
JsonObject->SetStringField("description", Description);
19+
20+
TSharedPtr<FJsonObject> ParametersObject = MakeShared<FJsonObject>();
21+
ParametersObject->SetStringField("type", "object");
22+
23+
TSharedPtr<FJsonObject> PropertiesObject = MakeShared<FJsonObject>();
24+
for (const FHttpGPTFunctionProperty& PropIt : Properties)
25+
{
26+
TSharedPtr<FJsonObject> PropertyObject = MakeShared<FJsonObject>();
27+
PropertyObject->SetStringField("type", UHttpGPTHelper::PropertyTypeToName(PropIt.Type).ToString().ToLower());
28+
PropertyObject->SetStringField("description", PropIt.Description);
29+
30+
TArray<TSharedPtr<FJsonValue>> EnumArr;
31+
for (const FName& EnumIt : PropIt.Enum)
32+
{
33+
EnumArr.Emplace(MakeShared<FJsonValueString>(EnumIt.ToString()));
34+
}
35+
PropertyObject->SetArrayField("enum", EnumArr);
36+
37+
PropertiesObject->SetObjectField(PropIt.Name.ToString(), PropertyObject);
38+
}
39+
40+
ParametersObject->SetObjectField("properties", PropertiesObject);
41+
42+
TArray<TSharedPtr<FJsonValue>> RequiredParams;
43+
for (const FName& ReqIt : RequiredProperties)
44+
{
45+
RequiredParams.Emplace(MakeShared<FJsonValueString>(ReqIt.ToString()));
46+
}
47+
48+
ParametersObject->SetArrayField("required", RequiredParams);
49+
JsonObject->SetObjectField("parameters", ParametersObject);
50+
51+
return MakeShared<FJsonValueObject>(JsonObject);
52+
}
53+
54+
FHttpGPTChatMessage::FHttpGPTChatMessage(const FName& InRole, const FString& InContent)
55+
{
56+
Role = UHttpGPTHelper::NameToRole(InRole);
57+
Content = InContent;
1858
}
1959

2060
TSharedPtr<FJsonValue> FHttpGPTChatMessage::GetMessage() const
2161
{
2262
TSharedPtr<FJsonObject> JsonObject = MakeShared<FJsonObject>();
2363
JsonObject->SetStringField("role", UHttpGPTHelper::RoleToName(Role).ToString().ToLower());
24-
JsonObject->SetStringField("content", Content);
64+
65+
if (Role == EHttpGPTChatRole::Function)
66+
{
67+
JsonObject->SetStringField("name", FunctionCall.Name.ToString());
68+
JsonObject->SetStringField("content", FunctionCall.Arguments);
69+
}
70+
else
71+
{
72+
JsonObject->SetStringField("content", Content);
73+
}
2574

2675
return MakeShared<FJsonValueObject>(JsonObject);
2776
}
@@ -46,4 +95,4 @@ void FHttpGPTChatOptions::SetDefaults()
4695
FrequencyPenalty = Settings->ChatOptions.FrequencyPenalty;
4796
LogitBias = Settings->ChatOptions.LogitBias;
4897
}
49-
}
98+
}

Source/HttpGPTCommonModule/Private/Structures/HttpGPTCommonTypes.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,8 @@ void FHttpGPTCommonOptions::SetDefaults()
2020
{
2121
APIKey = Settings->CommonOptions.APIKey;
2222
User = Settings->CommonOptions.User;
23+
bIsAzureOpenAI = Settings->CommonOptions.bIsAzureOpenAI;
24+
Endpoint = Settings->CommonOptions.Endpoint;
25+
AzureOpenAIAPIVersion = Settings->CommonOptions.AzureOpenAIAPIVersion;
2326
}
2427
}

Source/HttpGPTCommonModule/Private/Tasks/HttpGPTBaseTask.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ void UHttpGPTBaseTask::Activate()
3232
UE_LOG(LogHttpGPT, Display, TEXT("%s (%d): Activating task"), *FString(__func__), GetUniqueID());
3333

3434
bIsTaskActive = true;
35+
if (!CommonOptions.Endpoint.EndsWith(TEXT("/")))
36+
{
37+
CommonOptions.Endpoint += TEXT("/");
38+
}
3539

3640
if (!CanActivateTask())
3741
{

Source/HttpGPTCommonModule/Private/Utils/HttpGPTHelper.cpp

+66-4
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ const FName UHttpGPTHelper::RoleToName(const EHttpGPTChatRole Role)
8686

8787
case EHttpGPTChatRole::System:
8888
return "system";
89+
90+
case EHttpGPTChatRole::Function:
91+
return "function";
92+
93+
default:
94+
break;
8995
}
9096

9197
return NAME_None;
@@ -105,10 +111,52 @@ const EHttpGPTChatRole UHttpGPTHelper::NameToRole(const FName Role)
105111
{
106112
return EHttpGPTChatRole::System;
107113
}
114+
else if (Role.IsEqual("function", ENameCase::IgnoreCase))
115+
{
116+
return EHttpGPTChatRole::Function;
117+
}
108118

109119
return EHttpGPTChatRole::User;
110120
}
111121

122+
const FName UHttpGPTHelper::PropertyTypeToName(const EHttpGPTPropertyType Type)
123+
{
124+
switch (Type)
125+
{
126+
case EHttpGPTPropertyType::Boolean:
127+
return "bool";
128+
129+
case EHttpGPTPropertyType::Number:
130+
return "number";
131+
132+
case EHttpGPTPropertyType::String:
133+
return "string";
134+
135+
default:
136+
break;
137+
}
138+
139+
return NAME_None;
140+
}
141+
142+
const EHttpGPTPropertyType UHttpGPTHelper::NameToPropertyType(const FName Type)
143+
{
144+
if (Type.IsEqual("bool", ENameCase::IgnoreCase))
145+
{
146+
return EHttpGPTPropertyType::Boolean;
147+
}
148+
else if (Type.IsEqual("number", ENameCase::IgnoreCase))
149+
{
150+
return EHttpGPTPropertyType::Number;
151+
}
152+
else if (Type.IsEqual("string", ENameCase::IgnoreCase))
153+
{
154+
return EHttpGPTPropertyType::String;
155+
}
156+
157+
return EHttpGPTPropertyType::Boolean;
158+
}
159+
112160
const TArray<FName> UHttpGPTHelper::GetAvailableGPTModels()
113161
{
114162
TArray<FName> Output;
@@ -124,25 +172,39 @@ const TArray<FName> UHttpGPTHelper::GetAvailableGPTModels()
124172
return Output;
125173
}
126174

127-
const FName UHttpGPTHelper::GetEndpointForModel(const EHttpGPTChatModel Model)
175+
const FString UHttpGPTHelper::GetEndpointForModel(const EHttpGPTChatModel Model, const bool bIsAzureOpenAI, const FString& AzureOpenAIAPIVersion)
128176
{
129177
switch (Model)
130178
{
131179
case EHttpGPTChatModel::gpt4:
132180
case EHttpGPTChatModel::gpt432k:
133181
case EHttpGPTChatModel::gpt35turbo:
134182
case EHttpGPTChatModel::gpt35turbo16k:
135-
return "v1/chat/completions";
183+
if (bIsAzureOpenAI)
184+
{
185+
return FString::Format(TEXT("/openai/deployments/{0}/chat/completions?api-version={1}"), { ModelToName(Model).ToString(), AzureOpenAIAPIVersion });
186+
}
187+
else
188+
{
189+
return "v1/chat/completions";
190+
}
136191

137192
case EHttpGPTChatModel::textdavinci003:
138193
case EHttpGPTChatModel::textdavinci002:
139194
case EHttpGPTChatModel::codedavinci002:
140-
return "v1/completions";
195+
if (bIsAzureOpenAI)
196+
{
197+
return FString::Format(TEXT("/openai/deployments/{0}/completions?api-version={1}"), { ModelToName(Model).ToString(), AzureOpenAIAPIVersion });
198+
}
199+
else
200+
{
201+
return "v1/completions";
202+
}
141203

142204
default: break;
143205
}
144206

145-
return NAME_None;
207+
return FString();
146208
}
147209

148210
const bool UHttpGPTHelper::ModelSupportsChat(const EHttpGPTChatModel Model)

0 commit comments

Comments
 (0)