diff --git a/Generation/Converters/Argumentum.AssetConverter/Argumentum.AssetConverter.csproj b/Generation/Converters/Argumentum.AssetConverter/Argumentum.AssetConverter.csproj index 02638de24..9d60d54c3 100644 --- a/Generation/Converters/Argumentum.AssetConverter/Argumentum.AssetConverter.csproj +++ b/Generation/Converters/Argumentum.AssetConverter/Argumentum.AssetConverter.csproj @@ -14,8 +14,7 @@ - - + diff --git a/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/DatasetUpdaterConfig.cs b/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/DatasetUpdaterConfig.cs index 3fd629147..32622bf28 100644 --- a/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/DatasetUpdaterConfig.cs +++ b/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/DatasetUpdaterConfig.cs @@ -7,11 +7,8 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using OpenAI.ObjectModels; using System.Threading; -using OpenAI.Utilities.FunctionCalling; using Newtonsoft.Json; -using static System.Runtime.InteropServices.JavaScript.JSType; using Argumentum.AssetConverter.Entities; using Argumentum.AssetConverter.Mindmapper; @@ -32,7 +29,7 @@ public class DatasetUpdaterConfig public string OpenAIKeyPath { get; set; } = @"G:\Mon Drive\MyIA\Argumentum\Fallacies\Gestion\OpenAI-Key.txt"; - public string Model { get; set; } = Models.Gpt_4_1106_preview; + public string Model { get; set; } = "gpt-4.1"; public int MaxTokensPerMinute { get; set; } = 70000; @@ -486,7 +483,7 @@ private async Task DoChatGPTCall(CancellationToken ct, List().Select(definition => (definition.Function, (object)recordsUpdator)).ToList(); + dataPrompt.Functions = GetRecordsUpdaterToolDefinitions(recordsUpdator); if (!string.IsNullOrEmpty(FunctionName)) { dataPrompt.FunctionName = FunctionName; @@ -531,4 +528,26 @@ private async Task DoChatGPTCall(CancellationToken ct, List GetRecordsUpdaterToolDefinitions(RecordsUpdater recordsUpdater) + { + var toolDef = new FunctionToolDef( + name: "UpdateRecord", + description: "Updates a record's field given its primary key, the field's name and the new value for that field, returns both values separated by a line", + methodName: "UpdateRecord", + parametersJson: """ + { + "type": "object", + "properties": { + "primaryKey": { "type": "string", "description": "The primary key value of the record to update" }, + "fieldName": { "type": "string", "description": "The name of the field to update" }, + "newValue": { "type": "string", "description": "The new value for the field" } + }, + "required": ["primaryKey", "fieldName", "newValue"] + } + """ + ); + toolDef.TargetObject = recordsUpdater; + return new List { toolDef }; + } } \ No newline at end of file diff --git a/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/DatasetUpdaterRootConfig.cs b/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/DatasetUpdaterRootConfig.cs index 3d4c78527..4bf5a3545 100644 --- a/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/DatasetUpdaterRootConfig.cs +++ b/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/DatasetUpdaterRootConfig.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Threading.Tasks; -using OpenAI.ObjectModels; namespace Argumentum.AssetConverter.DatasetUpdater; @@ -58,7 +57,7 @@ public async Task Apply(AssetConverterConfig config) AssistantAnswerPath = PromptsRootPath + "VirtuesJsonPromptSampleAssistant.json" } }, - Model = Models.Gpt_3_5_Turbo_1106, + Model = "gpt-4.1-mini", MaxTokensPerMinute = 70000, DivisionMode = DivisionMode.SequentialChunks, ChunkSize = 3, @@ -113,7 +112,7 @@ public async Task Apply(AssetConverterConfig config) AssistantAnswerPath = PromptsRootPath + "PromptInstructionsAssistantDescription.txt" } }, - Model = Models.Gpt_4_1106_preview, + Model = "gpt-4.1", MaxTokensPerMinute = 70000, DivisionMode = DivisionMode.PKHierarchicalChar, PKHierarchyLevel = 3, @@ -177,7 +176,7 @@ public async Task Apply(AssetConverterConfig config) AssistantAnswerPath = PromptsRootPath + "PromptInstructionsLightAssistantExamples.txt" } }, - Model = Models.Gpt_4_0125_preview, + Model = "gpt-4.1", MaxTokensPerMinute = 70000, DivisionMode = DivisionMode.PKHierarchicalChar, PKHierarchyLevel = 3, @@ -240,7 +239,7 @@ public async Task Apply(AssetConverterConfig config) AssistantAnswerPath = PromptsRootPath + "PromptTranslateFrEnInstructionsAssistant.txt" } }, - Model = Models.Gpt_4_0125_preview, + Model = "gpt-4.1", MaxTokensPerMinute = 70000, DivisionMode = DivisionMode.PKHierarchicalChar, PKHierarchyLevel = 3, @@ -302,7 +301,7 @@ public async Task Apply(AssetConverterConfig config) AssistantAnswerPath = PromptsRootPath + "PromptTranslateRuInstructionsAssistant.txt" } }, - Model = Models.Gpt_4_0125_preview, + Model = "gpt-4.1", MaxTokensPerMinute = 70000, DivisionMode = DivisionMode.SequentialChunks, PKHierarchyLevel = 3, @@ -365,7 +364,7 @@ public async Task Apply(AssetConverterConfig config) AssistantAnswerPath = PromptsRootPath + "PromptTranslatePtInstructionsAssistant.txt" } }, - Model = Models.Gpt_4_0125_preview, + Model = "gpt-4.1", MaxTokensPerMinute = 70000, DivisionMode = DivisionMode.SequentialChunks, PKHierarchyLevel = 3, @@ -437,7 +436,7 @@ public async Task Apply(AssetConverterConfig config) AssistantAnswerPath = PromptsRootPath + "PromptTranslateCleanupInstructionsAssistant.txt" } }, - Model = Models.Gpt_4_turbo, + Model = "gpt-4.1", MaxTokensPerMinute = 70000, DivisionMode = DivisionMode.SequentialChunks, PKHierarchyLevel = 3, diff --git a/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/Prompt.cs b/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/Prompt.cs index ef26f788c..03d9e99c0 100644 --- a/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/Prompt.cs +++ b/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/Prompt.cs @@ -1,152 +1,170 @@ -using System; +#nullable enable +using System; using System.Collections.Generic; -using System.Globalization; using System.Linq; -using System.Net.Http; +using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Microsoft.Playwright; -using OpenAI; -using OpenAI.Managers; -using OpenAI.ObjectModels; -using OpenAI.ObjectModels.RequestModels; -using OpenAI.Utilities.FunctionCalling; - +using OpenAI.Chat; namespace Argumentum.AssetConverter; public class Prompt { - - private OpenAIService openAiService ; - - public string Model { get; set; } = Models.Gpt_3_5_Turbo_1106; - - public string ApiKey { get; set; } - - public string SystemPrompt { get; set; } - - public List DialogPrompts { get; set; } = new(); - - public string UserPrompt { get; set; } - - - public Action Tokenizer { get; set; } - - public OpenAIService OpenAiService - { - get - { - if (openAiService == null) - { - openAiService = new OpenAIService(new OpenAiOptions() - { - ApiKey = ApiKey - }); - } - return openAiService; - } - } - - public List<(FunctionDefinition functionDefinition, object targetObject)> Functions { get; set; } - - public string FunctionName { get; set; } - - public async Task Send(CancellationToken cancellationToken, Action log) - { - if (Tokenizer != null) - { - Tokenizer(SystemPrompt); - if (this.DialogPrompts != null) - { - foreach (var dialogPrompt in DialogPrompts) - { - Tokenizer(dialogPrompt.UserPrompt); - Tokenizer(dialogPrompt.AssistantAnswer); - } - } - - Tokenizer(UserPrompt); - } - - var messages = new List - { - ChatMessage.FromSystem(SystemPrompt) - }; - if (this.DialogPrompts != null) - { - foreach (var dialogPrompt in DialogPrompts) - { - messages.Add(ChatMessage.FromUser(dialogPrompt.UserPrompt)); - messages.Add(ChatMessage.FromAssistant(dialogPrompt.AssistantAnswer)); - } - } - - messages.Add(ChatMessage.FromUser(UserPrompt)); - - var chatCompletionCreateRequest = new ChatCompletionCreateRequest - { - - Messages = messages, - Model = Model, - //MaxTokens = 500//optional - }; - if (Functions != null) - { - chatCompletionCreateRequest.Tools = Functions.Select(tuple => ToolDefinition.DefineFunction(tuple.functionDefinition)).ToList(); - //chatCompletionCreateRequest.ChatResponseFormat = ChatCompletionCreateRequest.ResponseFormats.Json; - if (FunctionName != null) - { - chatCompletionCreateRequest.ToolChoice = ToolChoice.FunctionChoice(FunctionName); - } - } - - - var completionResult = await OpenAiService.ChatCompletion.CreateCompletion(chatCompletionCreateRequest, cancellationToken: cancellationToken); - - - if (completionResult.Successful) - { - var chatMessage = completionResult.Choices.First().Message; - - - if (chatMessage.FunctionCall != null) - { - var functionCall = chatMessage.FunctionCall; - var result = CallFunction(functionCall); - //chatMessage.Content = result.ToString(CultureInfo.CurrentCulture); - } - - if (chatMessage.ToolCalls != null && chatMessage.ToolCalls.Count > 0) - { - foreach (var chatMessageToolCall in chatMessage.ToolCalls) - { - var functionCall = chatMessageToolCall.FunctionCall; - var result = CallFunction(functionCall); - log($"Function call {functionCall.Name} with arguments {functionCall.Arguments}"); - } - } - - if (chatMessage.Content != null) - { - var messageContent = chatMessage.Content; - - if (Tokenizer != null) - { - Tokenizer(messageContent); - - } - return messageContent; - } - return ""; - } - throw new ApplicationException(completionResult.Error?.Message ?? "Unsuccessful"); - } - - private string CallFunction(FunctionCall functionCall) - { - var result = FunctionCallingHelper.CallFunction(functionCall, - Functions.First(tuple => tuple.functionDefinition.Name == functionCall.Name).targetObject); - return result; - } -} \ No newline at end of file + private OpenAI.OpenAIClient? _openAIClient; + private ChatClient? _chatClient; + + public string Model { get; set; } = "gpt-4.1-mini"; + + public string ApiKey { get; set; } = ""; + + public string SystemPrompt { get; set; } = ""; + + public List DialogPrompts { get; set; } = new(); + + public string UserPrompt { get; set; } = ""; + + public Action? Tokenizer { get; set; } + + public ChatClient ChatClient + { + get + { + if (_chatClient == null) + { + _openAIClient = new OpenAI.OpenAIClient(ApiKey); + _chatClient = _openAIClient.GetChatClient(Model); + } + return _chatClient; + } + } + + public List Functions { get; set; } = new(); + + public string? FunctionName { get; set; } + + public async Task Send(CancellationToken cancellationToken, Action log) + { + if (Tokenizer != null) + { + Tokenizer(SystemPrompt); + if (DialogPrompts != null) + { + foreach (var dialogPrompt in DialogPrompts) + { + Tokenizer(dialogPrompt.UserPrompt); + Tokenizer(dialogPrompt.AssistantAnswer); + } + } + Tokenizer(UserPrompt); + } + + var messages = new List + { + new SystemChatMessage(SystemPrompt) + }; + + if (DialogPrompts != null) + { + foreach (var dialogPrompt in DialogPrompts) + { + messages.Add(new UserChatMessage(dialogPrompt.UserPrompt)); + messages.Add(new AssistantChatMessage(dialogPrompt.AssistantAnswer)); + } + } + + messages.Add(new UserChatMessage(UserPrompt)); + + var options = new ChatCompletionOptions(); + + if (Functions != null && Functions.Count > 0) + { + foreach (var func in Functions) + { + options.Tools.Add(func.ToChatTool()); + } + + if (FunctionName != null) + { + options.ToolChoice = ChatToolChoice.CreateFunctionChoice(FunctionName); + } + } + + var completion = await ChatClient.CompleteChatAsync(messages, options, cancellationToken); + + var chatMessage = completion.Value.Content.FirstOrDefault()?.Text; + + if (completion.Value.ToolCalls.Count > 0) + { + foreach (var toolCall in completion.Value.ToolCalls) + { + var result = CallFunction(toolCall.FunctionName, toolCall.FunctionArguments.ToString()); + log($"Function call {toolCall.FunctionName} with arguments {toolCall.FunctionArguments}"); + } + } + + if (chatMessage != null) + { + if (Tokenizer != null) + { + Tokenizer(chatMessage); + } + return chatMessage; + } + + return ""; + } + + private string CallFunction(string functionName, string argumentsJson) + { + var funcDef = Functions.FirstOrDefault(f => f.Name == functionName); + if (funcDef == null || funcDef.TargetObject == null) + return "function not found"; + + var method = funcDef.TargetObject.GetType().GetMethod(funcDef.MethodName); + if (method == null) + return "method not found"; + + using var doc = JsonDocument.Parse(argumentsJson); + var args = new List(); + foreach (var param in method.GetParameters()) + { + if (param.Name != null && doc.RootElement.TryGetProperty(param.Name, out var elem)) + { + args.Add(elem.ValueKind == JsonValueKind.String + ? elem.GetString() + : elem.GetRawText()); + } + else + { + args.Add(null); + } + } + + var result = method.Invoke(funcDef.TargetObject, args.ToArray()); + return result?.ToString() ?? ""; + } +} + +public class FunctionToolDef +{ + public string Name { get; } + public string Description { get; } + public string ParametersJson { get; } + public string MethodName { get; } + public object TargetObject { get; set; } + + public FunctionToolDef(string name, string description, string methodName, string parametersJson) + { + Name = name; + Description = description; + MethodName = methodName; + ParametersJson = parametersJson; + TargetObject = null!; + } + + public ChatTool ToChatTool() + { + return ChatTool.CreateFunctionTool(Name, Description, BinaryData.FromString(ParametersJson)); + } +} diff --git a/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/RecordsUpdater.cs b/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/RecordsUpdater.cs index 589606f1b..a452522f6 100644 --- a/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/RecordsUpdater.cs +++ b/Generation/Converters/Argumentum.AssetConverter/DatasetUpdater/RecordsUpdater.cs @@ -3,7 +3,6 @@ using System.Text; using System.Text.RegularExpressions; using System.Web; -using OpenAI.Utilities.FunctionCalling; namespace Argumentum.AssetConverter; @@ -13,7 +12,6 @@ public class RecordsUpdater public List> Records { get; set; } - [FunctionDescription("Updates a record's field given its primary key, the field's name and the new value for that field, returns both values separated by a line")] public string UpdateRecord(string primaryKey, string fieldName, string newValue) { newValue = DecodeValue(newValue);