Advertisement
Guest User

Untitled

a guest
Feb 19th, 2025
34
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 8.43 KB | None | 0 0
  1. public class Conversation
  2.     {
  3.         private const string CmdSplitToken = "_";
  4.         //------------------------
  5.        
  6.        
  7.         public Conversation(OpenAIAPIClient client)
  8.         {
  9.             Client = client;
  10.             IsRunning = false;
  11.         }
  12.  
  13.         public OpenAIAPIClient Client;
  14.  
  15.         [SerializeField]public string ModelOverride = "";
  16.        
  17.         [Header("Status")]
  18.         [SerializeField]public bool IsRunning = false;
  19.         [SerializeField]public string Log = "";
  20.  
  21.        
  22.         [Header("Addenda")]
  23.         public string TextInjection = "";
  24.  
  25.  
  26.         [Header("Setting")]
  27.         public Setting Loc;
  28.  
  29.         [Header("People")]
  30.         [SerializeField] public List<Actor> Conversants = new();
  31.  
  32.         [Header("Events")]
  33.         [SerializeField] public List<string> PreviousEvents = new(); //Mix of JSON and raw strings
  34.         [SerializeField] public List<Proposal> ProposedActions = new();
  35.        
  36.         //Events
  37.         public CoreEvent<string> OnRawReceive = new();
  38.         public CoreEvent<string> OnSuccessfullyProcessed = new();
  39.  
  40.         [Header("Debug")]
  41.         [SerializeField] private string rawSend;
  42.         [SerializeField] private string rawResult;
  43.         [TextArea(3,10)]
  44.         [SerializeField] private string processedResult;
  45.  
  46.         [TextArea(3,10)][SerializeField] private string Result;
  47.        
  48.        
  49.         //State
  50.         [NonSerialized]public Actor lastSpeaker;
  51.        
  52.         // ---------------------------
  53.  
  54.         public string GetEventsAsString()
  55.         {
  56.             StringBuilder sb = new StringBuilder();
  57.             for (int i = 0; i < PreviousEvents.Count; i++)
  58.             {
  59.                 //try to convert to json
  60.                 try
  61.                 {
  62.                     var json = JsonUtility.FromJson<LLMCommandJSON>(PreviousEvents[i]);
  63.                     sb.AppendLine($"{i}: {json.Speaker} to {json.Listener}: {json.Dialogue}");
  64.                 }
  65.                 catch
  66.                 {
  67.                     sb.AppendLine($"{i}: {PreviousEvents[i]}");
  68.                 }
  69.             }
  70.  
  71.             return sb.ToString().Trim();
  72.         }
  73.        
  74.         public string GetProposalsAsString()
  75.         {
  76.             StringBuilder sb = new StringBuilder();
  77.             for (int i = 0; i < ProposedActions.Count; i++)
  78.                 sb.AppendLine($"{i}: {ProposedActions[i].Source.Name} to {ProposedActions[i].Target.Name}: [{ProposedActions[i].Key}] {ProposedActions[i].Description}");
  79.             return sb.ToString().Trim();
  80.         }
  81.        
  82.         public bool HasProposal(Actor target, string key) => ProposedActions.Exists(p => p.Target == target && string.Equals(p.Key, key, StringComparison.OrdinalIgnoreCase));
  83.         public bool HasProposal(string target, string key) => ProposedActions.Exists(p => String.Equals(p.Target.Name, target, StringComparison.OrdinalIgnoreCase) && string.Equals(p.Key, key, StringComparison.OrdinalIgnoreCase));
  84.  
  85.         // INFERENCING -------------------
  86.  
  87.         private string GeneratePrompt(Actor currentSpeaker, string pastEvents) => PromptGenerator.GenerateConversationPrompt(currentSpeaker, LLMCmdRegistry.SortedDefs, GetEventsAsString, Conversants);
  88.        
  89.         public void InferenceWithSpeaker(Actor speaker)
  90.         {
  91.             IsRunning = true;
  92.             lastSpeaker = speaker;
  93.             rawSend = TextInjection + " " + GeneratePrompt(speaker, GetEventsAsString());
  94.             Client.SimpleRequest(rawSend, OnResponse, ModelOverride);
  95.         }
  96.  
  97.         public void InferenceNextSpeaker() => GetNameOfNextSpeaker(TryInference);
  98.  
  99.         private void TryInference(string? speakerName)
  100.         {
  101.             if (TryGetActorByName(speakerName, out var speaker))
  102.                 InferenceWithSpeaker(speaker);
  103.             else
  104.             {
  105.                 IsRunning = false;
  106.                 Log = "Failed to find speaker: " + speakerName;
  107.             }
  108.         }
  109.        
  110.         // ------------------------------
  111.  
  112.         private void OnResponse(string str)
  113.         {
  114.             rawResult = str;
  115.             OnRawReceive?.Invoke(str);
  116.             int start = str.IndexOf('{');
  117.             str = str.Substring(start, str.LastIndexOf('}') - start + 1);
  118.             OnProcessed(str.Trim());
  119.         }
  120.  
  121.         private void OnProcessed(string str)
  122.         {
  123.             processedResult = str;
  124.             OnSuccessfullyProcessed?.Invoke(str);
  125.             try
  126.             {
  127.                 LLMCommandJSON raw = JsonUtility.FromJson<LLMCommandJSON>(str.Trim());
  128.                 raw.Speaker = lastSpeaker.Name;
  129.                 LLMCommandJSON json = raw;
  130.                 string jsonStr = JsonUtility.ToJson(json);
  131.                 PreviousEvents.Add(jsonStr);
  132.                 processedResult = str;
  133.                 Result = $"{raw.Speaker} [{raw.Action} - {raw.ActionTarget}] to {raw.Listener}: {raw.Dialogue}";
  134.                 OnSuccessfullyProcessed?.Invoke(str);
  135.                 TryExecute(lastSpeaker, json);
  136.             }
  137.             catch
  138.             {
  139.                 Debug.LogError($"Failed to process JSON: {str}");
  140.                 Log = "Failed to process JSON: " + str;
  141.                 IsRunning = false;
  142.             }
  143.         }
  144.        
  145.        
  146.         // NEXT ----------------------------
  147.  
  148.         class LLMNextSpeakerJSON  //Container json for next speaker prompt results.
  149.         {
  150.             public string NextSpeaker;
  151.         }
  152.        
  153.         public void GetNameOfNextSpeaker(Action<string> callback)
  154.         {
  155.             string prompt = PromptGenerator.GetNextSpeakerPrompt(this);
  156.  
  157.             void MergedCallback(string s)
  158.             {
  159.                 string name = ProcessNameOfNextSpeaker(s, callback);
  160.                 callback(name);
  161.             }
  162.  
  163.             Client.SimpleRequest(prompt, MergedCallback, ModelOverride);
  164.         }
  165.        
  166.         private string ProcessNameOfNextSpeaker(string raw, Action<string> callback)
  167.         {
  168.             try
  169.             {
  170.                 var json = JsonUtility.FromJson<LLMNextSpeakerJSON>(raw);
  171.                 return json.NextSpeaker;
  172.             }
  173.             catch
  174.             {
  175.                 Debug.LogError($"Failed to process JSON: {raw}");
  176.                 Log = "Failed to process JSON: " + raw;
  177.                 IsRunning = false;
  178.                 return "";
  179.             }
  180.         }
  181.        
  182.         // ACTIONS ----------------------------
  183.  
  184.         private void TryExecute(Actor speaker, LLMCommandJSON json)
  185.         {
  186.             if (String.IsNullOrEmpty(json.Action))
  187.                 return;
  188.  
  189.             string[] actions = json.Action.Split(',');
  190.             foreach (var action in actions)
  191.             {
  192.                 if (LLMCmdRegistry.TryGetCmdDef(action, out var cmdDef))
  193.                     ExecuteStandard(action, cmdDef);
  194.                 else //Procedural
  195.                     ExecuteProcedural(action);
  196.             }
  197.  
  198.             IsRunning = false;
  199.  
  200.             return;
  201.  
  202.             void ExecuteStandard(string action, CmdDef cmdDef)
  203.             {
  204.                 Actor? target;
  205.                 TryGetActorByName(json.ActionTarget, out target);
  206.                 cmdDef.Execute(this, speaker, target, out var resultEvent);
  207.                 if (String.IsNullOrEmpty(resultEvent) == false)
  208.                     PreviousEvents.Add(resultEvent);
  209.             }
  210.  
  211.             void ExecuteProcedural(string action)
  212.             {
  213.                 string[] split = action.Replace("#", "").Split(CmdSplitToken);
  214.                 if (split.Length < 2)
  215.                     return;
  216.  
  217.                 Actor? target;
  218.                 TryGetActorByName(json.ActionTarget, out target);
  219.  
  220.                 if (!LLMCmdRegistry.TryGetCmdDef(split[0], out var procCmdDef)) return;
  221.                 if (procCmdDef is ProcCmdDef procCmd == false)
  222.                     return;
  223.                 procCmd.ExecuteProcedural(this, speaker, target, split, out var resultEvent);
  224.                 if (String.IsNullOrEmpty(resultEvent) == false)
  225.                     PreviousEvents.Add(resultEvent);
  226.             }
  227.         }
  228.  
  229.         bool TryGetActorByName(string str, out Actor? actor)
  230.         {
  231.             actor = null;
  232.             if (String.IsNullOrEmpty(str))
  233.                 return false;
  234.             foreach (var c in Conversants)
  235.                 if (String.Equals(str, c.Name, StringComparison.OrdinalIgnoreCase))
  236.                     actor = c;
  237.             return actor != null;
  238.         }
  239.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement