Updated Games module
This commit is contained in:
parent
90dd47e013
commit
ca64765c34
12 changed files with 230 additions and 168 deletions
|
@ -1,6 +1,5 @@
|
|||
#nullable disable
|
||||
using EllieBot.Common.ModuleBehaviors;
|
||||
using EllieBot.Db.Models;
|
||||
using EllieBot.Modules.Games.Common;
|
||||
using EllieBot.Modules.Games.Common.ChatterBot;
|
||||
using EllieBot.Modules.Patronage;
|
||||
|
@ -58,18 +57,21 @@ public class ChatterBotService : IExecOnMessage
|
|||
|
||||
Log.Information("Cleverbot will not work as the api key is missing");
|
||||
return null;
|
||||
case ChatBotImplementation.Gpt:
|
||||
case ChatBotImplementation.OpenAi:
|
||||
var data = _gcs.Data;
|
||||
if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey))
|
||||
return new OfficialGptSession(_creds.Gpt3ApiKey,
|
||||
_gcs.Data.ChatGpt.ModelName,
|
||||
_gcs.Data.ChatGpt.ChatHistory,
|
||||
_gcs.Data.ChatGpt.MaxTokens,
|
||||
_gcs.Data.ChatGpt.MinTokens,
|
||||
_gcs.Data.ChatGpt.PersonalityPrompt,
|
||||
return new OpenAiApiSession(
|
||||
data.ChatGpt.ApiUrl,
|
||||
_creds.Gpt3ApiKey,
|
||||
data.ChatGpt.ModelName,
|
||||
data.ChatGpt.ChatHistory,
|
||||
data.ChatGpt.MaxTokens,
|
||||
data.ChatGpt.MinTokens,
|
||||
data.ChatGpt.PersonalityPrompt,
|
||||
_client.CurrentUser.Username,
|
||||
_httpFactory);
|
||||
|
||||
Log.Information("Gpt3 will not work as the api key is missing");
|
||||
Log.Information("Openai Api will likely not work as the api key is missing");
|
||||
return null;
|
||||
default:
|
||||
return null;
|
||||
|
@ -88,15 +90,15 @@ public class ChatterBotService : IExecOnMessage
|
|||
|
||||
public string PrepareMessage(IUserMessage msg)
|
||||
{
|
||||
var ellieId = _client.CurrentUser.Id;
|
||||
var normalMention = $"<@{ellieId}> ";
|
||||
var nickMention = $"<@!{ellieId}> ";
|
||||
var nadekoId = _client.CurrentUser.Id;
|
||||
var normalMention = $"<@{nadekoId}> ";
|
||||
var nickMention = $"<@!{nadekoId}> ";
|
||||
string message;
|
||||
if (msg.Content.StartsWith(normalMention, StringComparison.InvariantCulture))
|
||||
message = msg.Content[normalMention.Length..].Trim();
|
||||
else if (msg.Content.StartsWith(nickMention, StringComparison.InvariantCulture))
|
||||
message = msg.Content[nickMention.Length..].Trim();
|
||||
else if (msg.ReferencedMessage?.Author.Id == ellieId)
|
||||
else if (msg.ReferencedMessage?.Author.Id == nadekoId)
|
||||
message = msg.Content;
|
||||
else
|
||||
return null;
|
||||
|
|
9
src/EllieBot/Modules/Games/ChatterBot/_common/Choice.cs
Normal file
9
src/EllieBot/Modules/Games/ChatterBot/_common/Choice.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace EllieBot.Modules.Games.Common.ChatterBot;
|
||||
|
||||
public class Choice
|
||||
{
|
||||
[JsonPropertyName("message")]
|
||||
public Message Message { get; init; }
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
#nullable disable
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace EllieBot.Modules.Games.Common.ChatterBot;
|
||||
|
||||
public class OpenAiCompletionResponse
|
||||
{
|
||||
[JsonPropertyName("choices")]
|
||||
public Choice[] Choices { get; set; }
|
||||
|
||||
[JsonPropertyName("usage")]
|
||||
public OpenAiUsageData Usage { get; set; }
|
||||
}
|
||||
|
||||
public class OpenAiUsageData
|
||||
{
|
||||
[JsonPropertyName("prompt_tokens")]
|
||||
public int PromptTokens { get; set; }
|
||||
|
||||
[JsonPropertyName("completion_tokens")]
|
||||
public int CompletionTokens { get; set; }
|
||||
|
||||
[JsonPropertyName("total_tokens")]
|
||||
public int TotalTokens { get; set; }
|
||||
}
|
||||
|
||||
public class Choice
|
||||
{
|
||||
[JsonPropertyName("message")]
|
||||
public Message Message { get; init; }
|
||||
}
|
||||
|
||||
public class Message {
|
||||
[JsonPropertyName("content")]
|
||||
public string Content { get; init; }
|
||||
}
|
||||
|
||||
public class Gpt3ApiRequest
|
||||
{
|
||||
[JsonPropertyName("model")]
|
||||
public string Model { get; init; }
|
||||
|
||||
[JsonPropertyName("messages")]
|
||||
public List<GPTMessage> Messages { get; init; }
|
||||
|
||||
[JsonPropertyName("temperature")]
|
||||
public int Temperature { get; init; }
|
||||
|
||||
[JsonPropertyName("max_tokens")]
|
||||
public int MaxTokens { get; init; }
|
||||
}
|
||||
|
||||
public class GPTMessage
|
||||
{
|
||||
[JsonPropertyName("role")]
|
||||
public string Role {get; init;}
|
||||
[JsonPropertyName("content")]
|
||||
public string Content {get; init;}
|
||||
[JsonPropertyName("name")]
|
||||
public string Name {get; init;}
|
||||
}
|
9
src/EllieBot/Modules/Games/ChatterBot/_common/Message.cs
Normal file
9
src/EllieBot/Modules/Games/ChatterBot/_common/Message.cs
Normal file
|
@ -0,0 +1,9 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace EllieBot.Modules.Games.Common.ChatterBot;
|
||||
|
||||
public class Message
|
||||
{
|
||||
[JsonPropertyName("content")]
|
||||
public string Content { get; init; }
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace EllieBot.Modules.Games.Common.ChatterBot;
|
||||
|
||||
public class OpenAiApiMessage
|
||||
{
|
||||
[JsonPropertyName("role")]
|
||||
public string Role { get; init; }
|
||||
|
||||
[JsonPropertyName("content")]
|
||||
public string Content { get; init; }
|
||||
|
||||
[JsonPropertyName("name")]
|
||||
public string Name { get; init; }
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace EllieBot.Modules.Games.Common.ChatterBot;
|
||||
|
||||
public class OpenAiApiRequest
|
||||
{
|
||||
[JsonPropertyName("model")]
|
||||
public string Model { get; init; }
|
||||
|
||||
[JsonPropertyName("messages")]
|
||||
public List<OpenAiApiMessage> Messages { get; init; }
|
||||
|
||||
[JsonPropertyName("temperature")]
|
||||
public int Temperature { get; init; }
|
||||
|
||||
[JsonPropertyName("max_tokens")]
|
||||
public int MaxTokens { get; init; }
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace EllieBot.Modules.Games.Common.ChatterBot;
|
||||
|
||||
public class OpenAiApiUsageData
|
||||
{
|
||||
[JsonPropertyName("prompt_tokens")]
|
||||
public int PromptTokens { get; set; }
|
||||
|
||||
[JsonPropertyName("completion_tokens")]
|
||||
public int CompletionTokens { get; set; }
|
||||
|
||||
[JsonPropertyName("total_tokens")]
|
||||
public int TotalTokens { get; set; }
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#nullable disable
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace EllieBot.Modules.Games.Common.ChatterBot;
|
||||
|
||||
public class OpenAiCompletionResponse
|
||||
{
|
||||
[JsonPropertyName("choices")]
|
||||
public Choice[] Choices { get; set; }
|
||||
|
||||
[JsonPropertyName("usage")]
|
||||
public OpenAiApiUsageData Usage { get; set; }
|
||||
}
|
|
@ -1,60 +1,61 @@
|
|||
#nullable disable
|
||||
using Newtonsoft.Json;
|
||||
using OneOf.Types;
|
||||
using System.Net.Http.Json;
|
||||
using SharpToken;
|
||||
using System.CodeDom;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace EllieBot.Modules.Games.Common.ChatterBot;
|
||||
|
||||
public partial class OfficialGptSession : IChatterBotSession
|
||||
public partial class OpenAiApiSession : IChatterBotSession
|
||||
{
|
||||
private string Uri
|
||||
=> $"https://api.openai.com/v1/chat/completions";
|
||||
|
||||
private readonly string _baseUrl;
|
||||
private readonly string _apiKey;
|
||||
private readonly string _model;
|
||||
private readonly int _maxHistory;
|
||||
private readonly int _maxTokens;
|
||||
private readonly int _minTokens;
|
||||
private readonly string _ellieUsername;
|
||||
private readonly string _nadekoUsername;
|
||||
private readonly GptEncoding _encoding;
|
||||
private List<GPTMessage> messages = new();
|
||||
private List<OpenAiApiMessage> messages = new();
|
||||
private readonly IHttpClientFactory _httpFactory;
|
||||
|
||||
|
||||
public OfficialGptSession(
|
||||
public OpenAiApiSession(
|
||||
string url,
|
||||
string apiKey,
|
||||
ChatGptModel model,
|
||||
string model,
|
||||
int chatHistory,
|
||||
int maxTokens,
|
||||
int minTokens,
|
||||
string personality,
|
||||
string ellieUsername,
|
||||
string nadekoUsername,
|
||||
IHttpClientFactory factory)
|
||||
{
|
||||
_apiKey = apiKey;
|
||||
_httpFactory = factory;
|
||||
|
||||
_model = model switch
|
||||
if (string.IsNullOrWhiteSpace(url) || !Uri.TryCreate(url, UriKind.Absolute, out _))
|
||||
{
|
||||
ChatGptModel.Gpt35Turbo => "gpt-3.5-turbo",
|
||||
ChatGptModel.Gpt4o => "gpt-4o",
|
||||
_ => throw new ArgumentException("Unknown, unsupported or obsolete model", nameof(model))
|
||||
};
|
||||
throw new ArgumentException("Invalid OpenAi api url provided", nameof(url));
|
||||
}
|
||||
|
||||
_baseUrl = url.TrimEnd('/');
|
||||
|
||||
_apiKey = apiKey;
|
||||
_model = model;
|
||||
_httpFactory = factory;
|
||||
_maxHistory = chatHistory;
|
||||
_maxTokens = maxTokens;
|
||||
_minTokens = minTokens;
|
||||
_ellieUsername = UsernameCleaner().Replace(ellieUsername, "");
|
||||
_encoding = GptEncoding.GetEncodingForModel(_model);
|
||||
messages.Add(new()
|
||||
_nadekoUsername = UsernameCleaner().Replace(nadekoUsername, "");
|
||||
_encoding = GptEncoding.GetEncodingForModel("gpt-4o");
|
||||
if (!string.IsNullOrWhiteSpace(personality))
|
||||
{
|
||||
Role = "system",
|
||||
Content = personality,
|
||||
Name = _ellieUsername
|
||||
});
|
||||
messages.Add(new()
|
||||
{
|
||||
Role = "system",
|
||||
Content = personality,
|
||||
Name = _nadekoUsername
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -92,15 +93,16 @@ public partial class OfficialGptSession : IChatterBotSession
|
|||
}
|
||||
else
|
||||
{
|
||||
return new Error<string>("Token count exceeded, please increase the number of tokens in the bot config and restart.");
|
||||
return new Error<string>(
|
||||
"Token count exceeded, please increase the number of tokens in the bot config and restart.");
|
||||
}
|
||||
}
|
||||
|
||||
using var http = _httpFactory.CreateClient();
|
||||
http.DefaultRequestHeaders.Authorization = new("Bearer", _apiKey);
|
||||
|
||||
var data = await http.PostAsJsonAsync(Uri,
|
||||
new Gpt3ApiRequest()
|
||||
var data = await http.PostAsJsonAsync($"{_baseUrl}/v1/chat/completions",
|
||||
new OpenAiApiRequest()
|
||||
{
|
||||
Model = _model,
|
||||
Messages = messages,
|
||||
|
@ -113,7 +115,7 @@ public partial class OfficialGptSession : IChatterBotSession
|
|||
{
|
||||
var response = JsonConvert.DeserializeObject<OpenAiCompletionResponse>(dataString);
|
||||
|
||||
Log.Information("Received response: {response} ", dataString);
|
||||
// Log.Information("Received response: {Response} ", dataString);
|
||||
var res = response?.Choices?[0];
|
||||
var message = res?.Message?.Content;
|
||||
|
||||
|
@ -126,7 +128,7 @@ public partial class OfficialGptSession : IChatterBotSession
|
|||
{
|
||||
Role = "assistant",
|
||||
Content = message,
|
||||
Name = _ellieUsername
|
||||
Name = _nadekoUsername
|
||||
});
|
||||
|
||||
return new ThinkResult()
|
||||
|
@ -143,10 +145,3 @@ public partial class OfficialGptSession : IChatterBotSession
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ThinkResult
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public int TokensIn { get; set; }
|
||||
public int TokensOut { get; set; }
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
#nullable disable
|
||||
using System.CodeDom;
|
||||
|
||||
namespace EllieBot.Modules.Games.Common.ChatterBot;
|
||||
|
||||
public sealed class ThinkResult
|
||||
{
|
||||
public string Text { get; set; }
|
||||
public int TokensIn { get; set; }
|
||||
public int TokensOut { get; set; }
|
||||
}
|
|
@ -8,7 +8,7 @@ namespace EllieBot.Modules.Games.Common;
|
|||
public sealed partial class GamesConfig : ICloneable<GamesConfig>
|
||||
{
|
||||
[Comment("DO NOT CHANGE")]
|
||||
public int Version { get; set; } = 4;
|
||||
public int Version { get; set; } = 5;
|
||||
|
||||
[Comment("Hangman related settings (.hangman command)")]
|
||||
public HangmanConfig Hangman { get; set; } = new()
|
||||
|
@ -103,10 +103,13 @@ public sealed partial class GamesConfig : ICloneable<GamesConfig>
|
|||
}
|
||||
];
|
||||
|
||||
[Comment(@"Which chatbot API should bot use.
|
||||
'cleverbot' - bot will use Cleverbot API.
|
||||
'gpt' - bot will use GPT API")]
|
||||
public ChatBotImplementation ChatBot { get; set; } = ChatBotImplementation.Gpt;
|
||||
[Comment(
|
||||
"""
|
||||
Which chatbot API should bot use.
|
||||
'cleverbot' - bot will use Cleverbot API.
|
||||
'openai' - bot will use OpenAi API
|
||||
""")]
|
||||
public ChatBotImplementation ChatBot { get; set; } = ChatBotImplementation.OpenAi;
|
||||
|
||||
public ChatGptConfig ChatGpt { get; set; } = new();
|
||||
}
|
||||
|
@ -114,19 +117,38 @@ public sealed partial class GamesConfig : ICloneable<GamesConfig>
|
|||
[Cloneable]
|
||||
public sealed partial class ChatGptConfig
|
||||
{
|
||||
[Comment(@"Which GPT Model should bot use.
|
||||
gpt35turbo - cheapest
|
||||
gpt4o - more expensive, higher quality
|
||||
")]
|
||||
public ChatGptModel ModelName { get; set; } = ChatGptModel.Gpt35Turbo;
|
||||
[Comment("""
|
||||
Url to any openai api compatible url.
|
||||
Make sure to modify the modelName appropriately
|
||||
DO NOT add /v1/chat/completions suffix to the url
|
||||
""")]
|
||||
public string ApiUrl { get; set; } = "https://api.openai.com";
|
||||
|
||||
[Comment(@"How should the chat bot behave, what's its personality? (Usage of this counts towards the max tokens)")]
|
||||
public string PersonalityPrompt { get; set; } = "You are a chat bot willing to have a conversation with anyone about anything.";
|
||||
[Comment("""
|
||||
Which GPT Model should bot use.
|
||||
gpt-3.5-turbo - cheapest
|
||||
gpt-4o - more expensive, higher quality
|
||||
|
||||
[Comment(@"The maximum number of messages in a conversation that can be remembered. (This will increase the number of tokens used)")]
|
||||
If you are using another openai compatible api, you may use any of the models supported by that api
|
||||
""")]
|
||||
public string ModelName { get; set; } = "gpt-3.5-turbo";
|
||||
|
||||
[Comment("""
|
||||
How should the chatbot behave, what's its personality?
|
||||
This will be sent as a system message.
|
||||
Usage of this counts towards the max tokens.
|
||||
""")]
|
||||
public string PersonalityPrompt { get; set; } =
|
||||
"You are a chat bot willing to have a conversation with anyone about anything.";
|
||||
|
||||
[Comment(
|
||||
"""
|
||||
The maximum number of messages in a conversation that can be remembered.
|
||||
This will increase the number of tokens used.
|
||||
""")]
|
||||
public int ChatHistory { get; set; } = 5;
|
||||
|
||||
[Comment(@"The maximum number of tokens to use per GPT API call")]
|
||||
[Comment(@"The maximum number of tokens to use per OpenAi API call")]
|
||||
public int MaxTokens { get; set; } = 100;
|
||||
|
||||
[Comment(@"The minimum number of tokens to use per GPT API call, such that chat history is removed to make room.")]
|
||||
|
@ -147,9 +169,9 @@ public sealed partial class TriviaConfig
|
|||
public long CurrencyReward { get; set; }
|
||||
|
||||
[Comment("""
|
||||
Users won't be able to start trivia games which have
|
||||
a smaller win requirement than the one specified by this setting.
|
||||
""")]
|
||||
Users won't be able to start trivia games which have
|
||||
a smaller win requirement than the one specified by this setting.
|
||||
""")]
|
||||
public int MinimumWinReq { get; set; } = 1;
|
||||
}
|
||||
|
||||
|
@ -163,18 +185,11 @@ public sealed partial class RaceAnimal
|
|||
public enum ChatBotImplementation
|
||||
{
|
||||
Cleverbot,
|
||||
OpenAi = 1,
|
||||
|
||||
[Obsolete]
|
||||
Gpt = 1,
|
||||
|
||||
[Obsolete]
|
||||
Gpt3 = 1,
|
||||
}
|
||||
|
||||
public enum ChatGptModel
|
||||
{
|
||||
[Obsolete]
|
||||
Gpt4,
|
||||
[Obsolete]
|
||||
Gpt432k,
|
||||
|
||||
Gpt35Turbo,
|
||||
Gpt4o,
|
||||
}
|
|
@ -32,29 +32,21 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
|
|||
gs => gs.ChatBot,
|
||||
ConfigParsers.InsensitiveEnum,
|
||||
ConfigPrinters.ToString);
|
||||
|
||||
AddParsedProp("gpt.apiUrl",
|
||||
gs => gs.ChatGpt.ApiUrl,
|
||||
ConfigParsers.String,
|
||||
ConfigPrinters.ToString);
|
||||
|
||||
AddParsedProp("gpt.modelName",
|
||||
gs => gs.ChatGpt.ModelName,
|
||||
ConfigParsers.InsensitiveEnum,
|
||||
ConfigParsers.String,
|
||||
ConfigPrinters.ToString);
|
||||
|
||||
AddParsedProp("gpt.personality",
|
||||
gs => gs.ChatGpt.PersonalityPrompt,
|
||||
ConfigParsers.String,
|
||||
ConfigPrinters.ToString);
|
||||
AddParsedProp("gpt.chathistory",
|
||||
gs => gs.ChatGpt.ChatHistory,
|
||||
int.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val > 0);
|
||||
AddParsedProp("gpt.max_tokens",
|
||||
gs => gs.ChatGpt.MaxTokens,
|
||||
int.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val > 0);
|
||||
AddParsedProp("gpt.min_tokens",
|
||||
gs => gs.ChatGpt.MinTokens,
|
||||
int.TryParse,
|
||||
ConfigPrinters.ToString,
|
||||
val => val > 0);
|
||||
|
||||
Migrate();
|
||||
}
|
||||
|
@ -78,7 +70,7 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
|
|||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 3;
|
||||
c.ChatGpt.ModelName = ChatGptModel.Gpt35Turbo;
|
||||
c.ChatGpt.ModelName = "gpt35turbo";
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -89,11 +81,40 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
|
|||
c.Version = 4;
|
||||
#pragma warning disable CS0612 // Type or member is obsolete
|
||||
c.ChatGpt.ModelName =
|
||||
c.ChatGpt.ModelName == ChatGptModel.Gpt4 || c.ChatGpt.ModelName == ChatGptModel.Gpt432k
|
||||
? ChatGptModel.Gpt4o
|
||||
: c.ChatGpt.ModelName;
|
||||
c.ChatGpt.ModelName.Equals("gpt4", StringComparison.OrdinalIgnoreCase)
|
||||
|| c.ChatGpt.ModelName.Equals("gpt432k", StringComparison.OrdinalIgnoreCase)
|
||||
? "gpt-4o"
|
||||
: "gpt-3.5-turbo";
|
||||
#pragma warning restore CS0612 // Type or member is obsolete
|
||||
});
|
||||
}
|
||||
|
||||
if (data.Version < 5)
|
||||
{
|
||||
ModifyConfig(c =>
|
||||
{
|
||||
c.Version = 5;
|
||||
c.ChatBot = c.ChatBot == ChatBotImplementation.OpenAi
|
||||
? ChatBotImplementation.OpenAi
|
||||
: c.ChatBot;
|
||||
|
||||
if (c.ChatGpt.ModelName.Equals("gpt4o", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
c.ChatGpt.ModelName = "gpt-4o";
|
||||
}
|
||||
else if (c.ChatGpt.ModelName.Equals("gpt35turbo", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
c.ChatGpt.ModelName = "gpt-3.5-turbo";
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning(
|
||||
"Unknown OpenAI api model name: {ModelName}. "
|
||||
+ "It will be reset to 'gpt-3.5-turbo' only this time",
|
||||
c.ChatGpt.ModelName);
|
||||
c.ChatGpt.ModelName = "gpt-3.5-turbo";
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue