mirror of
https://gitlab.com/Kwoth/nadekobot.git
synced 2025-09-10 17:28:27 -04:00
Merge branch 'v4' into 'v4'
Update GPT ChatterBot See merge request Kwoth/nadekobot!313
This commit is contained in:
@@ -79,8 +79,12 @@ public class ChatterBotService : IExecOnMessage
|
|||||||
case ChatBotImplementation.Gpt3:
|
case ChatBotImplementation.Gpt3:
|
||||||
if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey))
|
if (!string.IsNullOrWhiteSpace(_creds.Gpt3ApiKey))
|
||||||
return new OfficialGpt3Session(_creds.Gpt3ApiKey,
|
return new OfficialGpt3Session(_creds.Gpt3ApiKey,
|
||||||
_gcs.Data.ChatGpt.Model,
|
_gcs.Data.ChatGpt.ModelName,
|
||||||
|
_gcs.Data.ChatGpt.ChatHistory,
|
||||||
_gcs.Data.ChatGpt.MaxTokens,
|
_gcs.Data.ChatGpt.MaxTokens,
|
||||||
|
_gcs.Data.ChatGpt.MinTokens,
|
||||||
|
_gcs.Data.ChatGpt.PersonalityPrompt,
|
||||||
|
_client.CurrentUser.Username,
|
||||||
_httpFactory);
|
_httpFactory);
|
||||||
|
|
||||||
Log.Information("Gpt3 will not work as the api key is missing.");
|
Log.Information("Gpt3 will not work as the api key is missing.");
|
||||||
@@ -199,7 +203,7 @@ public class ChatterBotService : IExecOnMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
_ = channel.TriggerTypingAsync();
|
_ = channel.TriggerTypingAsync();
|
||||||
var response = await cbs.Think(message);
|
var response = await cbs.Think(message, usrMsg.Author.ToString());
|
||||||
await channel.SendConfirmAsync(_eb,
|
await channel.SendConfirmAsync(_eb,
|
||||||
title: null,
|
title: null,
|
||||||
response.SanitizeMentions(true)
|
response.SanitizeMentions(true)
|
||||||
|
@@ -11,7 +11,13 @@ public class Gpt3Response
|
|||||||
|
|
||||||
public class Choice
|
public class Choice
|
||||||
{
|
{
|
||||||
public string Text { get; set; }
|
[JsonPropertyName("message")]
|
||||||
|
public Message Message { get; init; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Message {
|
||||||
|
[JsonPropertyName("content")]
|
||||||
|
public string Content { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Gpt3ApiRequest
|
public class Gpt3ApiRequest
|
||||||
@@ -19,8 +25,8 @@ public class Gpt3ApiRequest
|
|||||||
[JsonPropertyName("model")]
|
[JsonPropertyName("model")]
|
||||||
public string Model { get; init; }
|
public string Model { get; init; }
|
||||||
|
|
||||||
[JsonPropertyName("prompt")]
|
[JsonPropertyName("messages")]
|
||||||
public string Prompt { get; init; }
|
public List<GPTMessage> Messages { get; init; }
|
||||||
|
|
||||||
[JsonPropertyName("temperature")]
|
[JsonPropertyName("temperature")]
|
||||||
public int Temperature { get; init; }
|
public int Temperature { get; init; }
|
||||||
@@ -28,3 +34,13 @@ public class Gpt3ApiRequest
|
|||||||
[JsonPropertyName("max_tokens")]
|
[JsonPropertyName("max_tokens")]
|
||||||
public int MaxTokens { get; init; }
|
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;}
|
||||||
|
}
|
@@ -3,5 +3,5 @@ namespace NadekoBot.Modules.Games.Common.ChatterBot;
|
|||||||
|
|
||||||
public interface IChatterBotSession
|
public interface IChatterBotSession
|
||||||
{
|
{
|
||||||
Task<string> Think(string input);
|
Task<string> Think(string input, string username);
|
||||||
}
|
}
|
@@ -18,7 +18,7 @@ public class OfficialCleverbotSession : IChatterBotSession
|
|||||||
_httpFactory = factory;
|
_httpFactory = factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> Think(string input)
|
public async Task<string> Think(string input, string username)
|
||||||
{
|
{
|
||||||
using var http = _httpFactory.CreateClient();
|
using var http = _httpFactory.CreateClient();
|
||||||
var dataString = await http.GetStringAsync(string.Format(QueryString, input, cs ?? ""));
|
var dataString = await http.GetStringAsync(string.Format(QueryString, input, cs ?? ""));
|
||||||
|
@@ -1,63 +1,101 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using System.Net.Http.Json;
|
using System.Net.Http.Json;
|
||||||
|
using SharpToken;
|
||||||
|
using Antlr.Runtime;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
|
||||||
namespace NadekoBot.Modules.Games.Common.ChatterBot;
|
namespace NadekoBot.Modules.Games.Common.ChatterBot;
|
||||||
|
|
||||||
public class OfficialGpt3Session : IChatterBotSession
|
public class OfficialGpt3Session : IChatterBotSession
|
||||||
{
|
{
|
||||||
private string Uri
|
private string Uri
|
||||||
=> $"https://api.openai.com/v1/completions";
|
=> $"https://api.openai.com/v1/chat/completions";
|
||||||
|
|
||||||
private readonly string _apiKey;
|
private readonly string _apiKey;
|
||||||
private readonly string _model;
|
private readonly string _model;
|
||||||
|
private readonly int _maxHistory;
|
||||||
private readonly int _maxTokens;
|
private readonly int _maxTokens;
|
||||||
|
private readonly int _minTokens;
|
||||||
|
private readonly string _nadekoUsername;
|
||||||
|
private readonly GptEncoding _encoding;
|
||||||
|
private List<GPTMessage> messages = new();
|
||||||
private readonly IHttpClientFactory _httpFactory;
|
private readonly IHttpClientFactory _httpFactory;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public OfficialGpt3Session(
|
public OfficialGpt3Session(
|
||||||
string apiKey,
|
string apiKey,
|
||||||
Gpt3Model model,
|
ChatGptModel model,
|
||||||
|
int chatHistory,
|
||||||
int maxTokens,
|
int maxTokens,
|
||||||
|
int minTokens,
|
||||||
|
string personality,
|
||||||
|
string nadekoUsername,
|
||||||
IHttpClientFactory factory)
|
IHttpClientFactory factory)
|
||||||
{
|
{
|
||||||
_apiKey = apiKey;
|
_apiKey = apiKey;
|
||||||
_httpFactory = factory;
|
_httpFactory = factory;
|
||||||
switch (model)
|
switch (model)
|
||||||
{
|
{
|
||||||
case Gpt3Model.Ada001:
|
case ChatGptModel.Gpt35Turbo:
|
||||||
_model = "text-ada-001";
|
_model = "gpt-3.5-turbo";
|
||||||
break;
|
break;
|
||||||
case Gpt3Model.Babbage001:
|
case ChatGptModel.Gpt4:
|
||||||
_model = "text-babbage-001";
|
_model = "gpt-4";
|
||||||
break;
|
break;
|
||||||
case Gpt3Model.Curie001:
|
case ChatGptModel.Gpt432k:
|
||||||
_model = "text-curie-001";
|
_model = "gpt-4-32k";
|
||||||
break;
|
|
||||||
case Gpt3Model.Davinci003:
|
|
||||||
_model = "text-davinci-003";
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
_maxHistory = chatHistory;
|
||||||
_maxTokens = maxTokens;
|
_maxTokens = maxTokens;
|
||||||
|
_minTokens = minTokens;
|
||||||
|
_nadekoUsername = nadekoUsername;
|
||||||
|
_encoding = GptEncoding.GetEncodingForModel(_model);
|
||||||
|
messages.Add(new GPTMessage(){Role = "user", Content = personality, Name = _nadekoUsername});
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<string> Think(string input)
|
public async Task<string> Think(string input, string username)
|
||||||
{
|
{
|
||||||
|
messages.Add(new GPTMessage(){Role = "user", Content = input, Name = username});
|
||||||
|
while(messages.Count > _maxHistory + 2){
|
||||||
|
messages.RemoveAt(1);
|
||||||
|
}
|
||||||
|
int tokensUsed = 0;
|
||||||
|
foreach(GPTMessage message in messages){
|
||||||
|
tokensUsed += _encoding.Encode(message.Content).Count;
|
||||||
|
}
|
||||||
|
tokensUsed *= 2; //Unsure why this is the case, but the token count chatgpt reports back is double what I calculate.
|
||||||
|
//check if we have the minimum number of tokens available to use. Remove messages until we have enough, otherwise exit out and inform the user why.
|
||||||
|
while(_maxTokens - tokensUsed <= _minTokens){
|
||||||
|
if(messages.Count > 2){
|
||||||
|
int tokens = _encoding.Encode(messages[1].Content).Count * 2;
|
||||||
|
tokensUsed -= tokens;
|
||||||
|
messages.RemoveAt(1);
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
return "Token count exceeded, please increase the number of tokens in the bot config and restart.";
|
||||||
|
}
|
||||||
|
}
|
||||||
using var http = _httpFactory.CreateClient();
|
using var http = _httpFactory.CreateClient();
|
||||||
http.DefaultRequestHeaders.Authorization = new("Bearer", _apiKey);
|
http.DefaultRequestHeaders.Authorization = new("Bearer", _apiKey);
|
||||||
var data = await http.PostAsJsonAsync(Uri, new Gpt3ApiRequest()
|
var data = await http.PostAsJsonAsync(Uri, new Gpt3ApiRequest()
|
||||||
{
|
{
|
||||||
Model = _model,
|
Model = _model,
|
||||||
Prompt = input,
|
Messages = messages,
|
||||||
MaxTokens = _maxTokens,
|
MaxTokens = _maxTokens - tokensUsed,
|
||||||
Temperature = 1,
|
Temperature = 1,
|
||||||
});
|
});
|
||||||
var dataString = await data.Content.ReadAsStringAsync();
|
var dataString = await data.Content.ReadAsStringAsync();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = JsonConvert.DeserializeObject<Gpt3Response>(dataString);
|
var response = JsonConvert.DeserializeObject<Gpt3Response>(dataString);
|
||||||
|
string message = response?.Choices[0]?.Message?.Content;
|
||||||
return response?.Choices[0]?.Text;
|
//Can't rely on the return to except, now that we need to add it to the messages list.
|
||||||
|
_ = message ?? throw new ArgumentNullException(nameof(message));
|
||||||
|
messages.Add(new GPTMessage(){Role = "assistant", Content = message, Name = _nadekoUsername});
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@@ -8,7 +8,7 @@ namespace NadekoBot.Modules.Games.Common;
|
|||||||
public sealed partial class GamesConfig : ICloneable<GamesConfig>
|
public sealed partial class GamesConfig : ICloneable<GamesConfig>
|
||||||
{
|
{
|
||||||
[Comment("DO NOT CHANGE")]
|
[Comment("DO NOT CHANGE")]
|
||||||
public int Version { get; set; } = 2;
|
public int Version { get; set; } = 3;
|
||||||
|
|
||||||
[Comment("Hangman related settings (.hangman command)")]
|
[Comment("Hangman related settings (.hangman command)")]
|
||||||
public HangmanConfig Hangman { get; set; } = new()
|
public HangmanConfig Hangman { get; set; } = new()
|
||||||
@@ -108,14 +108,22 @@ public sealed partial class GamesConfig : ICloneable<GamesConfig>
|
|||||||
public sealed partial class ChatGptConfig
|
public sealed partial class ChatGptConfig
|
||||||
{
|
{
|
||||||
[Comment(@"Which GPT-3 Model should bot use.
|
[Comment(@"Which GPT-3 Model should bot use.
|
||||||
'ada001' - cheapest and fastest
|
gpt35turbo - cheapest
|
||||||
'babbage001' - 2nd option
|
gpt4 - 30x more expensive, higher quality
|
||||||
'curie001' - 3rd option
|
gp432k - same model as above, but with a 32k token limit")]
|
||||||
'davinci003' - Most expensive, slowest")]
|
public ChatGptModel ModelName { get; set; } = ChatGptModel.Gpt35Turbo;
|
||||||
public Gpt3Model Model { get; set; } = Gpt3Model.Ada001;
|
|
||||||
|
[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(@"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-3 API call")]
|
[Comment(@"The maximum number of tokens to use per GPT-3 API call")]
|
||||||
public int MaxTokens { get; set; } = 100;
|
public int MaxTokens { get; set; } = 100;
|
||||||
|
|
||||||
|
[Comment(@"The minimum number of tokens to use per GPT-3 API call, such that chat history is removed to make room.")]
|
||||||
|
public int MinTokens { get; set; } = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Cloneable]
|
[Cloneable]
|
||||||
@@ -149,10 +157,9 @@ public enum ChatBotImplementation
|
|||||||
Gpt3
|
Gpt3
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Gpt3Model
|
public enum ChatGptModel
|
||||||
{
|
{
|
||||||
Ada001,
|
Gpt35Turbo,
|
||||||
Babbage001,
|
Gpt4,
|
||||||
Curie001,
|
Gpt432k
|
||||||
Davinci003
|
|
||||||
}
|
}
|
@@ -28,20 +28,33 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
|
|||||||
long.TryParse,
|
long.TryParse,
|
||||||
ConfigPrinters.ToString,
|
ConfigPrinters.ToString,
|
||||||
val => val >= 0);
|
val => val >= 0);
|
||||||
|
|
||||||
AddParsedProp("chatbot",
|
AddParsedProp("chatbot",
|
||||||
gs => gs.ChatBot,
|
gs => gs.ChatBot,
|
||||||
ConfigParsers.InsensitiveEnum,
|
ConfigParsers.InsensitiveEnum,
|
||||||
ConfigPrinters.ToString);
|
ConfigPrinters.ToString);
|
||||||
AddParsedProp("gpt.model",
|
AddParsedProp("gpt.modelName",
|
||||||
gs => gs.ChatGpt.Model,
|
gs => gs.ChatGpt.ModelName,
|
||||||
ConfigParsers.InsensitiveEnum,
|
ConfigParsers.InsensitiveEnum,
|
||||||
ConfigPrinters.ToString);
|
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",
|
AddParsedProp("gpt.max_tokens",
|
||||||
gs => gs.ChatGpt.MaxTokens,
|
gs => gs.ChatGpt.MaxTokens,
|
||||||
int.TryParse,
|
int.TryParse,
|
||||||
ConfigPrinters.ToString,
|
ConfigPrinters.ToString,
|
||||||
val => val > 0);
|
val => val > 0);
|
||||||
|
AddParsedProp("gpt.min_tokens",
|
||||||
|
gs => gs.ChatGpt.MinTokens,
|
||||||
|
int.TryParse,
|
||||||
|
ConfigPrinters.ToString,
|
||||||
|
val => val > 0);
|
||||||
|
|
||||||
Migrate();
|
Migrate();
|
||||||
}
|
}
|
||||||
@@ -68,5 +81,14 @@ public sealed class GamesConfigService : ConfigServiceBase<GamesConfig>
|
|||||||
c.ChatBot = ChatBotImplementation.Cleverbot;
|
c.ChatBot = ChatBotImplementation.Cleverbot;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (data.Version < 3)
|
||||||
|
{
|
||||||
|
ModifyConfig(c =>
|
||||||
|
{
|
||||||
|
c.Version = 3;
|
||||||
|
c.ChatGpt.ModelName = ChatGptModel.Gpt35Turbo;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -58,6 +58,7 @@
|
|||||||
<PackageReference Include="Scrutor" Version="4.2.0" />
|
<PackageReference Include="Scrutor" Version="4.2.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.1" />
|
||||||
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.1" />
|
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.1" />
|
||||||
|
<PackageReference Include="SharpToken" Version="1.2.14" />
|
||||||
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17" />
|
<PackageReference Include="SixLabors.Fonts" Version="1.0.0-beta17" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
|
<PackageReference Include="SixLabors.ImageSharp" Version="2.1.3" />
|
||||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14" />
|
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="1.0.0-beta14" />
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
# DO NOT CHANGE
|
# DO NOT CHANGE
|
||||||
version: 2
|
version: 3
|
||||||
# Hangman related settings (.hangman command)
|
# Hangman related settings (.hangman command)
|
||||||
hangman:
|
hangman:
|
||||||
# The amount of currency awarded to the winner of a hangman game
|
# The amount of currency awarded to the winner of a hangman game
|
||||||
@@ -57,14 +57,19 @@ raceAnimals:
|
|||||||
# Which chatbot API should bot use.
|
# Which chatbot API should bot use.
|
||||||
# 'cleverbot' - bot will use Cleverbot API.
|
# 'cleverbot' - bot will use Cleverbot API.
|
||||||
# 'gpt3' - bot will use GPT-3 API
|
# 'gpt3' - bot will use GPT-3 API
|
||||||
chatBot: gpt3
|
chatBot: Gpt3
|
||||||
|
|
||||||
chatGpt:
|
chatGpt:
|
||||||
# Which GPT-3 Model should bot use.
|
# Which GPT-3 Model should bot use.
|
||||||
# 'ada001' - cheapest and fastest
|
# gpt35turbo - cheapest
|
||||||
# 'babbage001' - 2nd option
|
# gpt4 - 30x more expensive, higher quality
|
||||||
# 'curie001' - 3rd option
|
# gp432k - same model as above, but with a 32k token limit
|
||||||
# 'davinci003' - Most expensive, slowest
|
modelName: Gpt35Turbo
|
||||||
model: davinci003
|
# How should the chat bot behave, whats its personality? (Usage of this counts towards the max tokens)
|
||||||
|
personalityPrompt: You are a chat bot willing to have a conversation with anyone about anything.
|
||||||
|
# The maximum number of messages in a conversation that can be remembered. (This will increase the number of tokens used)
|
||||||
|
chatHistory: 5
|
||||||
# The maximum number of tokens to use per GPT-3 API call
|
# The maximum number of tokens to use per GPT-3 API call
|
||||||
maxTokens: 100
|
maxTokens: 100
|
||||||
|
# The minimum number of tokens to use per GPT-3 API call, such that chat history is removed to make room.
|
||||||
|
minTokens: 30
|
Reference in New Issue
Block a user