Skip to content

Commit

Permalink
adapts to new API, improves json handling, v2.0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
pardeike committed Dec 5, 2023
1 parent 3135799 commit 669d1dc
Show file tree
Hide file tree
Showing 14 changed files with 161 additions and 80 deletions.
1 change: 1 addition & 0 deletions About/About.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
</li>
</modDependencies>
<packageId>brrainz.rimgpt</packageId>
<modVersion>2.0.1.0</modVersion>
<description>OpenAI runs your raids. Good luck.

Powered by Harmony Patch Library
Expand Down
Binary file modified Assemblies/RimGPT.dll
Binary file not shown.
75 changes: 49 additions & 26 deletions Source/AI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ namespace RimGPT
{
public class AI
{
public static JsonSerializerSettings settings = new() { NullValueHandling = NullValueHandling.Ignore };

#pragma warning disable CS0649
public class Input
{
Expand All @@ -39,20 +41,29 @@ public string SystemPrompt(Persona currentPersona)
var playerName = Tools.PlayerName();
var player = playerName == null ? "the player" : $"the player named '{playerName}'";
var otherObservers = RimGPTMod.Settings.personas.Where(p => p != currentPersona).Join(persona => $"'{persona.name}'");
var exampleInput = JsonConvert.SerializeObject(new Input
{
CurrentWindow = "<Info about currently open window>",
CurrentGameState = ["Event1", "Event2", "Event3"],
PreviousHistoricalKeyEvents = ["OldEvent1", "OldEvent2", "OldEvent3"],
LastSpokenText = "<Previous Output>"
}, settings);
var exampleOutput = JsonConvert.SerializeObject(new Output
{
ResponseText = "<New Output>",
NewHistoricalKeyEvents = ["OldEventsSummary", "Event 1 and 2", "Event3"]
}, settings);

return new List<string>
{
$"System instruction: Begin{(otherObservers.Any() ? " multi-instance" : "")} role-playing simulation.\n",
$"You are '{currentPersona.name}', an observer.\n",
$"Act as the following role and personality: {currentPersona.personality}\n",
otherObservers.Any() ? $"Along with you, other observers named {otherObservers}, are watching {player} play the game Rimworld.\n" : $"You are watching {player} play the game Rimworld.\n",
$"Important rules to follow strictly:\n",
$"- Your input comes from the game and will be in JSON format. It has the following keys: 'CurrentWindow', 'PreviousHistoricalKeyEvents', 'LastSpokenText', and 'CurrentGameState', which is a list of recent events.",
$"- You also get what other observers say in form of 'X said: ...'.",
$"- Return your output in JSON format with keys 'ResponseText' and 'NewHistoricalKeyEvents'.",
$"- Your response called 'ResponseText' must be in {Tools.PersonalityLanguage(currentPersona)}.",
$"- Add what you want to remember as list of historical key facts called 'NewHistoricalKeyEvents'.",
$"- Limit 'ResponseText' to no more than {currentPersona.phraseMaxWordCount} words.",
$"- Limit 'NewHistoricalKeyEvents' to no more than {currentPersona.historyMaxWordCount} words."
$"You are role-playing a commentator called '{currentPersona.name}'. ",
(otherObservers.Any() ? $"Your co-moderators are {otherObservers} and you all" : $"You") + $" are watching '{player}' playing the popular game Rimworld.\n",
$"Your input comes from the current game and will be json like this: {exampleInput}\n",
$"Your output must only be in json like this: {exampleOutput}\n",
$"Limit ResponseText to no more than {currentPersona.phraseMaxWordCount} words.\n",
$"Limit NewHistoricalKeyEvents to no more than {currentPersona.historyMaxWordCount} words.\n",
$"Your role/personality is: {currentPersona.personality}\n",
$"Remember: your output is in the format: {{\"ResponseText\":\"...\",\"NewHistoricalKeyEvents\":[\"...\",\"...\"]}}",
}.Join(delimiter: "").ApplyVoiceStyle(currentPersona);
}

Expand Down Expand Up @@ -82,26 +93,34 @@ public async Task<string> Evaluate(Persona persona, IEnumerable<Phrase> observat
}
}

var input = JsonConvert.SerializeObject(gameInput);

if (Tools.DEBUG)
Logger.Warning($"INPUT: {input}");
var input = JsonConvert.SerializeObject(gameInput, settings);

var systemPrompt = SystemPrompt(persona);
var completionResponse = await OpenAI.CreateChatCompletion(new CreateChatCompletionRequest()
var request = new CreateChatCompletionRequest()
{
Model = RimGPTMod.Settings.chatGPTModel,
Messages = new()
{
ResponseFormat = RimGPTMod.Settings.chatGPTModel.Contains("1106") ? new ResponseFormat { Type = "json_object" } : null,
// FrequencyPenalty = 1.0f,
// PresencePenalty = 1.0f,
// Temperature = 1.5f,
Messages =
[
new ChatMessage() { Role = "system", Content = systemPrompt },
new ChatMessage() { Role = "user", Content = input }
}
}, error => Logger.Error(error));
]
};

if (Tools.DEBUG)
Log.Warning($"INPUT: {JsonConvert.SerializeObject(request, settings)}");

var completionResponse = await OpenAI.CreateChatCompletion(request, error => Logger.Error(error));
RimGPTMod.Settings.charactersSentOpenAI += systemPrompt.Length + input.Length;

if (completionResponse.Choices?.Count > 0)
{
var response = (completionResponse.Choices[0].Message.Content ?? "").Trim();
var response = (completionResponse.Choices[0].Message.Content ?? "");
RimGPTMod.Settings.charactersSentOpenAI += response.Length;
response = response.Trim();
var firstIdx = response.IndexOf("{");
if (firstIdx >= 0)
{
Expand Down Expand Up @@ -145,16 +164,20 @@ public void ReplaceHistory(string[] reason)
var completionResponse = await OpenAI.CreateChatCompletion(new CreateChatCompletionRequest()
{
Model = RimGPTMod.Settings.chatGPTModel,
Messages = new()
{
Messages =
[
new ChatMessage() { Role = "system", Content = "You are a creative poet answering in 12 words or less." },
new ChatMessage() { Role = "user", Content = input }
}
]
}, e => requestError = e);
RimGPTMod.Settings.charactersSentOpenAI += input.Length;

if (completionResponse.Choices?.Count > 0)
return (completionResponse.Choices[0].Message.Content, null);
{
var response = (completionResponse.Choices[0].Message.Content ?? "");
RimGPTMod.Settings.charactersSentOpenAI += response.Length;
return (response, null);
}

return (null, requestError);
}
Expand Down
6 changes: 3 additions & 3 deletions Source/Defs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ public static class Defs
[StaticConstructorOnStartup]
public static class Graphics
{
public static readonly Texture2D[] ButtonAdd = new[] { ContentFinder<Texture2D>.Get("ButtonAdd0", true), ContentFinder<Texture2D>.Get("ButtonAdd1", true) };
public static readonly Texture2D[] ButtonDel = new[] { ContentFinder<Texture2D>.Get("ButtonDel0", true), ContentFinder<Texture2D>.Get("ButtonDel1", true) };
public static readonly Texture2D[] ButtonDup = new[] { ContentFinder<Texture2D>.Get("ButtonDup0", true), ContentFinder<Texture2D>.Get("ButtonDup1", true) };
public static readonly Texture2D[] ButtonAdd = [ContentFinder<Texture2D>.Get("ButtonAdd0", true), ContentFinder<Texture2D>.Get("ButtonAdd1", true)];
public static readonly Texture2D[] ButtonDel = [ContentFinder<Texture2D>.Get("ButtonDel0", true), ContentFinder<Texture2D>.Get("ButtonDel1", true)];
public static readonly Texture2D[] ButtonDup = [ContentFinder<Texture2D>.Get("ButtonDup0", true), ContentFinder<Texture2D>.Get("ButtonDup1", true)];
}
}
30 changes: 19 additions & 11 deletions Source/OpenAI/DataTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,20 +77,28 @@ public class OpenAIModelResponse : OpenAIModel, IResponse
}
#endregion

public sealed class ResponseFormat
{
[JsonProperty("type")] public string Type { get; set; }
}

#region Chat API Data Types
public sealed class CreateChatCompletionRequest
{
public string Model { get; set; }
public List<ChatMessage> Messages { get; set; }
public float? Temperature { get; set; } = 1;
public int? N { get; set; } = 1;
public bool? Stream { get; set; } = false;
public string Stop { get; set; }
public int? MaxTokens { get; set; }
public float? PresencePenalty { get; set; } = 0;
public float? FrequencyPenalty { get; set; } = 0;
public Dictionary<string, string> LogitBias { get; set; }
public string User { get; set; }
[JsonProperty("messages")] public List<ChatMessage> Messages { get; set; }
[JsonProperty("model")] public string Model { get; set; }
[JsonProperty("frequency_penalty")] public float? FrequencyPenalty { get; set; } = 0;
[JsonProperty("logit_bias")] public Dictionary<string, string> LogitBias { get; set; }
[JsonProperty("max_tokens")] public int? MaxTokens { get; set; }
[JsonProperty("n")] public int? N { get; set; } = 1;
[JsonProperty("presence_penalty")] public float? PresencePenalty { get; set; } = 0;
[JsonProperty("response_format")] public ResponseFormat ResponseFormat { get; set; }
[JsonProperty("seed")] public int? Seed { get; set; }
[JsonProperty("stop")] public string Stop { get; set; }
[JsonProperty("stream")] public bool? Stream { get; set; } = false;
[JsonProperty("temperature")] public float? Temperature { get; set; } = 1;
[JsonProperty("top_p")] public int? TopP { get; set; }
[JsonProperty("user")] public string User { get; set; }
}

public struct CreateChatCompletionResponse : IResponse
Expand Down
8 changes: 4 additions & 4 deletions Source/Patches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public static void Postfix()

// reset history when a game is loaded
//
[HarmonyPatch(typeof(GameDataSaveLoader), nameof(GameDataSaveLoader.LoadGame), new Type[] { typeof(string) })]
[HarmonyPatch(typeof(GameDataSaveLoader), nameof(GameDataSaveLoader.LoadGame), [typeof(string)])]
public static class GameDataSaveLoader_LoadGame_Patch
{
public static void Postfix(string saveFileName)
Expand Down Expand Up @@ -481,15 +481,15 @@ public static class UIRoot_UIRootUpdate_Patch
{
static int lastTicks = 0;
static int lastTotal = -1;
static readonly HashSet<ThingCategoryDef> thingCategories = new()
{
static readonly HashSet<ThingCategoryDef> thingCategories =
[
ThingCategoryDefOf.Foods,
ThingCategoryDefOf.FoodMeals,
ThingCategoryDefOf.Medicine,
ThingCategoryDefOf.StoneBlocks,
ThingCategoryDefOf.Manufactured,
ThingCategoryDefOf.ResourcesRaw
};
];

public static bool Reportable(this KeyValuePair<ThingDef, int> pair)
{
Expand Down
2 changes: 1 addition & 1 deletion Source/Persona.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public class SettingAttribute : Attribute { }

public string name = "RimGPT";
public AI ai = new();
public OrderedHashSet<Phrase> phrases = new();
public OrderedHashSet<Phrase> phrases = [];
public string lastSpokenText = "";
public DateTime nextPhraseTime = DateTime.Now;

Expand Down
29 changes: 15 additions & 14 deletions Source/RimGPT.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<Copyright>Copyright Andreas Pardeike</Copyright>
<Configurations>Release;Debug</Configurations>
<Version>2.0.0.0</Version>
<ReleaseVersion>2.0.0</ReleaseVersion>
<Version>2.0.1.0</Version>
<ReleaseVersion>2.0.1</ReleaseVersion>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
Expand All @@ -33,7 +33,7 @@

<ItemGroup>
<PackageReference Include="Brrainz.RimWorld.CrossPromotion" Version="1.1.1" />
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.4.3704" GeneratePathProperty="true" />
<PackageReference Include="Krafs.Rimworld.Ref" Version="1.4.3901" GeneratePathProperty="true" />
<PackageReference Include="Lib.Harmony" Version="2.2.2" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.NETCore.Platforms" Version="7.0.4" />
<PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies.net472" Version="1.0.3">
Expand All @@ -44,9 +44,9 @@
</ItemGroup>

<ItemGroup>
<Reference Include="Newtonsoft.Json">
<HintPath>..\Libs\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>..\Libs\Newtonsoft.Json.dll</HintPath>
</Reference>
</ItemGroup>

<Target Name="MyCode" BeforeTargets="UpdateReferences">
Expand Down Expand Up @@ -76,14 +76,15 @@
</Target>

<PropertyGroup>
<PostBuildEvent>echo Postprocessing
where ModBuilder 2&gt; nul | find /i "ModBuilder.exe"
if not errorlevel 1 (
ModBuilder AssemblyVersion -file "$(MSBuildProjectDirectory)\$(OutputPath)$(AssemblyName).dll" -save "$(MSBuildProjectName)-version"
)
if defined INSTALL_MOD (
"%INSTALL_MOD%" "$(Configuration)" "$(MSBuildProjectDirectory)\..\" "$(MSBuildProjectName)" "About Assemblies Defs Textures" ""
)
<PostBuildEvent>
echo Postprocessing
where ModBuilder 2&gt; nul | find /i "ModBuilder.exe"
if not errorlevel 1 (
ModBuilder AssemblyVersion -file "$(MSBuildProjectDirectory)\$(OutputPath)$(AssemblyName).dll" -save "$(MSBuildProjectName)-version"
)
if defined INSTALL_MOD (
"%INSTALL_MOD%" "$(Configuration)" "$(MSBuildProjectDirectory)\..\" "$(MSBuildProjectName)" "About Assemblies Defs Textures" ""
)
</PostBuildEvent>
<Company>Brrainz</Company>
<Authors>Andreas Pardeike</Authors>
Expand Down
2 changes: 1 addition & 1 deletion Source/SSML/Ssml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace Kevsoft.Ssml
{
public class Ssml : ISsml
{
private readonly List<ISsmlWriter> _says = new();
private readonly List<ISsmlWriter> _says = [];
private readonly string _lang;
private readonly string _version;
private SsmlConfiguration _configuration;
Expand Down
10 changes: 5 additions & 5 deletions Source/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ namespace RimGPT
{
public class RimGPTSettings : ModSettings
{
public List<Persona> personas = new()
{
public List<Persona> personas =
[
new Persona()
{
name = "Tynan",
Expand Down Expand Up @@ -47,7 +47,7 @@ public class RimGPTSettings : ModSettings
personality = "You are a mod developer. You mostly respond to Tynan but sometimes talk to the player. You are sceptical about everything Tynan says. You support everything the player does in the game.",
personalityLanguage = "English"
}
};
];
public bool enabled = true;
public string chatGPTKey = "";
public string chatGPTModel = Tools.chatGPTModels.First();
Expand Down Expand Up @@ -109,7 +109,7 @@ public override void ExposeData()
if (azureVoice != null && personas.NullOrEmpty())
{
chatGPTModel = Tools.chatGPTModels.First();
personas ??= new List<Persona>();
personas ??= [];
personas.Add(new Persona()
{
name = "Default",
Expand Down Expand Up @@ -153,7 +153,7 @@ public void DoWindowContents(Rect inRect)
// for three columns with 20px spacing
var width = (list.ColumnWidth - 2 * 20) / 3;

list.Label("FFFF00", "OpenAI - ChatGPT", $"{charactersSentOpenAI} chars sent");
list.Label("FFFF00", "OpenAI - ChatGPT", $"{charactersSentOpenAI} chars total");
prevKey = chatGPTKey;
list.TextField(ref chatGPTKey, "API Key (paste only)", true, () => chatGPTKey = "");
if (chatGPTKey != "" && chatGPTKey != prevKey)
Expand Down
6 changes: 3 additions & 3 deletions Source/TTS.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ public class VoiceStyle
public string Value { get; private set; }
public string Tooltip { get; private set; }

public static readonly VoiceStyle[] Values = new VoiceStyle[]
{
public static readonly VoiceStyle[] Values =
[
new VoiceStyle("Default", "default", null),
new VoiceStyle("Advertisement Upbeat", "advertisement_upbeat", "Expresses an excited and high-energy tone for promoting a product or service"),
new VoiceStyle("Affectionate", "affectionate", "Expresses a warm and affectionate tone, with higher pitch and vocal energy. The speaker is in a state of attracting the attention of the listener. The personality of the speaker is often endearing in nature"),
Expand Down Expand Up @@ -90,7 +90,7 @@ public class VoiceStyle
new VoiceStyle("Whispering", "whispering", "Speaks very softly and make a quiet and gentle sound"),
new VoiceStyle("Terrified", "terrified", "Expresses a very scared tone, with faster pace and a shakier voice. It sounds like the speaker is in an unsteady and frantic status"),
new VoiceStyle("Unfriendly", "unfriendly", "Expresses a cold and indifferent tone")
};
];

public static VoiceStyle From(string shortName) => Values.FirstOrDefault(s => s.Value == shortName);
}
Expand Down
Loading

0 comments on commit 669d1dc

Please sign in to comment.