initial upload

This commit is contained in:
tom.hempel
2025-09-21 22:42:26 +02:00
commit d03bcd4ba5
6231 changed files with 351582 additions and 0 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: b390e5b7c8d51a44fa4f560d93e8067f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,54 @@
using System;
using Convai.Scripts.Runtime.LoggerSystem;
using UnityEngine;
using UnityEngine.Events;
namespace Convai.Scripts.Runtime.Features
{
/// <summary>
/// Data class for Section Change Events
/// </summary>
[Serializable]
public class SectionChangeEventsData
{
[SerializeField] public string id;
[SerializeField] public UnityEvent onSectionStart;
[SerializeField] public UnityEvent onSectionEnd;
private NarrativeDesignManager _manager;
private string SectionName
{
get
{
if (_manager == null) return string.Empty;
SectionData sectionData = _manager.sectionDataList.Find(s => s.sectionId == id);
return sectionData?.sectionName ?? "Unknown Section";
}
}
/// <summary>
/// Initialize the Section Change Events
/// </summary>
/// <param name="manager"> The Narrative Design Manager </param>
public void Initialize(NarrativeDesignManager manager)
{
_manager = manager;
onSectionStart.RemoveListener(LogSectionStart);
onSectionStart.AddListener(LogSectionStart);
onSectionEnd.RemoveListener(LogSectionEnd);
onSectionEnd.AddListener(LogSectionEnd);
}
private void LogSectionStart()
{
ConvaiLogger.DebugLog($"Section {SectionName} started", ConvaiLogger.LogCategory.Character);
}
private void LogSectionEnd()
{
ConvaiLogger.DebugLog($"Section {SectionName} ended", ConvaiLogger.LogCategory.Character);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 32b4f9b2242945c19917f76c7ceb0a71
timeCreated: 1707390195

View File

@ -0,0 +1,38 @@
using System;
using Convai.Scripts.Runtime.Attributes;
using Newtonsoft.Json;
using UnityEngine;
namespace Convai.Scripts.Runtime.Features
{
/// <summary>
/// Data class for Section Data
/// </summary>
[Serializable]
public class SectionData
{
[JsonProperty("section_id")] [ReadOnly] [SerializeField]
public string sectionId;
[JsonProperty("section_name")] [ReadOnly] [SerializeField]
public string sectionName;
[JsonProperty("bt_constants")] [HideInInspector] [SerializeField]
public string behaviorTreeConstants;
[JsonProperty("objective")] [ReadOnly] [SerializeField]
public string objective;
[JsonProperty("character_id")] [ReadOnly] [HideInInspector] [SerializeField]
public string characterId;
[JsonProperty("decisions")] [ReadOnly] public object Decisions;
[JsonProperty("parents")] [ReadOnly] public object Parents;
[JsonProperty("triggers")] [ReadOnly] public object Triggers;
[JsonProperty("updated_character_data")] [ReadOnly]
public object UpdatedCharacterData;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 98ee0e1dea8bb8c4ba9fe2daf30e9960
timeCreated: 1706764030

View File

@ -0,0 +1,26 @@
using System;
using Convai.Scripts.Runtime.Attributes;
using Newtonsoft.Json;
using UnityEngine;
namespace Convai.Scripts.Runtime.Features
{
[Serializable]
public class TriggerData
{
[JsonProperty("trigger_id")] [ReadOnly] [SerializeField]
public string triggerId;
[JsonProperty("trigger_name")] [ReadOnly] [SerializeField]
public string triggerName;
[JsonProperty("trigger_message")] [ReadOnly] [SerializeField]
public string triggerMessage;
[JsonProperty("destination_section")] [ReadOnly] [SerializeField]
public string destinationSection;
[JsonProperty("character_id")] [HideInInspector] [SerializeField]
public string characterId;
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ed95a107485d00746a551222f53d0950
timeCreated: 1706764014

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: ecfb3b2eea944946a1d2336205b5eaad
timeCreated: 1706853590

View File

@ -0,0 +1,151 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using Convai.Scripts.Runtime.LoggerSystem;
using Newtonsoft.Json;
namespace Convai.Scripts.Runtime.Features
{
/// <summary>
/// API client for the Narrative Design API.
/// </summary>
public class NarrativeDesignAPI
{
private const string BASE_URL = "https://api.convai.com/character/narrative/";
private readonly HttpClient _httpClient;
/// <summary>
/// Initializes a new instance of the <see cref="NarrativeDesignAPI" /> class.
/// </summary>
public NarrativeDesignAPI()
{
_httpClient = new HttpClient
{
// Set a default request timeout if needed
Timeout = TimeSpan.FromSeconds(30) // Example: 30 seconds
};
// Get the API key from the ConvaiAPIKeySetup object
if (ConvaiAPIKeySetup.GetAPIKey(out string apiKey))
{
// Set default request headers here
_httpClient.DefaultRequestHeaders.Add("CONVAI-API-KEY", apiKey);
// Set default headers like Accept to expect a JSON response
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
}
public async Task<string> CreateSectionAsync(string characterId, string objective, string sectionName, string behaviorTreeCode = null, string btConstants = null)
{
string endpoint = "create-section";
HttpContent content = CreateHttpContent(new Dictionary<string, object>
{
{ "character_id", characterId },
{ "objective", objective },
{ "section_name", sectionName },
{ "behavior_tree_code", behaviorTreeCode },
{ "bt_constants", btConstants }
});
return await SendPostRequestAsync(endpoint, content);
}
public async Task<string> GetSectionAsync(string characterId, string sectionId)
{
string endpoint = "get-section";
HttpContent content = CreateHttpContent(new Dictionary<string, object>
{
{ "character_id", characterId },
{ "section_id", sectionId }
});
return await SendPostRequestAsync(endpoint, content);
}
/// <summary>
/// Get a list of sections for a character.
/// </summary>
/// <param name="characterId"> The character ID. </param>
/// <returns> A JSON string containing the list of sections. </returns>
public async Task<string> ListSectionsAsync(string characterId)
{
string endpoint = "list-sections";
HttpContent content = CreateHttpContent(new Dictionary<string, object>
{
{ "character_id", characterId }
});
return await SendPostRequestAsync(endpoint, content);
}
public async Task<string> CreateTriggerAsync(string characterId, string triggerName, string triggerMessage = null, string destinationSection = null)
{
string endpoint = "create-trigger";
HttpContent content = CreateHttpContent(new Dictionary<string, object>
{
{ "character_id", characterId },
{ "trigger_message", triggerMessage },
{ "destination_section", destinationSection }
});
return await SendPostRequestAsync(endpoint, content);
}
public async Task<string> GetTriggerAsync(string characterId, string triggerId)
{
string endpoint = "get-trigger";
HttpContent content = CreateHttpContent(new Dictionary<string, object>
{
{ "character_id", characterId },
{ "trigger_id", triggerId }
});
return await SendPostRequestAsync(endpoint, content);
}
/// <summary>
/// Get a list of triggers for a character.
/// </summary>
/// <param name="characterId"> The character ID. </param>
/// <returns> A JSON string containing the list of triggers. </returns>
public async Task<string> GetTriggerListAsync(string characterId)
{
string endpoint = "list-triggers";
HttpContent content = CreateHttpContent(new Dictionary<string, object>
{
{ "character_id", characterId }
});
return await SendPostRequestAsync(endpoint, content);
}
private static HttpContent CreateHttpContent(Dictionary<string, object> data)
{
//Dictionary where all values are not null
Dictionary<string, object> dataToSend =
data.Where(keyValuePair => keyValuePair.Value != null).ToDictionary(keyValuePair => keyValuePair.Key, keyValuePair => keyValuePair.Value);
// Serialize the dictionary to JSON
string json = JsonConvert.SerializeObject(dataToSend);
// Convert JSON to HttpContent
return new StringContent(json, Encoding.UTF8, "application/json");
}
private async Task<string> SendPostRequestAsync(string endpoint, HttpContent content)
{
try
{
HttpResponseMessage response = await _httpClient.PostAsync(BASE_URL + endpoint, content);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
catch (HttpRequestException e)
{
ConvaiLogger.Exception($"Request to {endpoint} failed: {e.Message}", ConvaiLogger.LogCategory.GRPC);
return null;
}
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 13850aaec3d742c2867a15ab2273a1e0
timeCreated: 1706546525

View File

@ -0,0 +1,42 @@
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Convai.Scripts.Runtime.Features
{
public class NarrativeDesignKeyController : MonoBehaviour
{
public List<NarrativeDesignKey> narrativeDesignKeys;
[Serializable]
public class NarrativeDesignKey
{
public string name;
public string value;
}
public void SetTemplateKey(Dictionary<string, string> keyValuePairs)
{
narrativeDesignKeys.Clear();
narrativeDesignKeys.AddRange(from item in keyValuePairs
select new NarrativeDesignKey { name = item.Key, value = item.Value });
}
public void AddTemplateKey(string name, string value)
{
narrativeDesignKeys.Add(new NarrativeDesignKey { name = name, value = value });
}
public void RemoveTemplateKey(string name)
{
NarrativeDesignKey reference = narrativeDesignKeys.Find(x => x.name == name);
if(reference == null) return;
narrativeDesignKeys.Remove(reference);
}
public void UpdateTemplateKey(string name, string value)
{
NarrativeDesignKey reference = narrativeDesignKeys.Find(x => x.name == name);
if (reference == null) return;
reference.value = value;
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d526d030017744949b7412a85e6134d3
timeCreated: 1725884181

View File

@ -0,0 +1,154 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Convai.Scripts.Runtime.Core;
using Convai.Scripts.Runtime.LoggerSystem;
using Newtonsoft.Json;
using UnityEngine;
namespace Convai.Scripts.Runtime.Features
{
/// <summary>
/// Manages the narrative design for a ConvaiNPC.
/// </summary>
[RequireComponent(typeof(ConvaiNPC))]
public class NarrativeDesignManager : MonoBehaviour
{
public List<SectionChangeEventsData> sectionChangeEventsDataList = new();
public List<SectionData> sectionDataList = new();
public List<TriggerData> triggerDataList = new();
private ConvaiNPC _convaiNpc;
private string _currentSectionID;
private NarrativeDesignAPI _narrativeDesignAPI;
private NarrativeDesignAPI NarrativeDesignAPI => _narrativeDesignAPI ??= new NarrativeDesignAPI();
private ConvaiNPC ConvaiNpc => _convaiNpc ??= GetComponent<ConvaiNPC>();
private string CharacterID => ConvaiNpc.characterID;
private async void Awake()
{
_convaiNpc = GetComponent<ConvaiNPC>();
await Task.WhenAll(UpdateSectionListAsync(), UpdateTriggerListAsync());
}
private async void Reset()
{
await Task.WhenAll(UpdateSectionListAsync(), UpdateTriggerListAsync());
}
/// <summary>
/// Updates the section list from the server.
/// </summary>
public async Task UpdateSectionListAsync()
{
List<SectionData> updatedSectionList = await GetSectionListFromServerAsync();
UpdateSectionDataList(updatedSectionList);
}
/// <summary>
/// Updates the trigger list from the server.
/// </summary>
public async Task UpdateTriggerListAsync()
{
await ListTriggersAsync(CharacterID);
}
/// <summary>
/// Invoked when the section event list changes.
/// </summary>
public void OnSectionEventListChange()
{
foreach (SectionChangeEventsData sectionChangeEventsData in sectionChangeEventsDataList) sectionChangeEventsData.Initialize(this);
}
private async Task<List<SectionData>> GetSectionListFromServerAsync()
{
try
{
string sections = await NarrativeDesignAPI.ListSectionsAsync(CharacterID);
return JsonConvert.DeserializeObject<List<SectionData>>(sections);
}
catch (Exception e)
{
ConvaiLogger.Error($"Please setup API Key properly. FormatException occurred: {e.Message}", ConvaiLogger.LogCategory.Character);
throw;
}
}
public event Action OnTriggersUpdated;
private async Task ListTriggersAsync(string characterId)
{
try
{
string triggers = await NarrativeDesignAPI.GetTriggerListAsync(characterId);
triggerDataList = JsonConvert.DeserializeObject<List<TriggerData>>(triggers);
OnTriggersUpdated?.Invoke();
}
catch (FormatException e)
{
ConvaiLogger.Exception($"Format Exception occurred: {e.Message}", ConvaiLogger.LogCategory.Character);
throw;
}
}
/// <summary>
/// Updates the current section.
/// </summary>
/// <param name="sectionID"> The section ID to update to. </param>
public void UpdateCurrentSection(string sectionID)
{
if (string.IsNullOrEmpty(_currentSectionID))
{
_currentSectionID = sectionID;
InvokeSectionEvent(_currentSectionID, true);
return;
}
if (_currentSectionID.Equals(sectionID))
return;
InvokeSectionEvent(_currentSectionID, false);
_currentSectionID = sectionID;
InvokeSectionEvent(_currentSectionID, true);
}
private void InvokeSectionEvent(string id, bool isStarting)
{
SectionChangeEventsData sectionChangeEventsData = sectionChangeEventsDataList.Find(x => x.id == id);
if (sectionChangeEventsData == null)
{
ConvaiLogger.Info($"No Section Change Events have been created for sectionID: {id}", ConvaiLogger.LogCategory.Actions);
return;
}
if (isStarting)
sectionChangeEventsData.onSectionStart?.Invoke();
else
sectionChangeEventsData.onSectionEnd?.Invoke();
}
private void UpdateSectionDataList(List<SectionData> updatedSectionList)
{
Dictionary<string, SectionData> updatedSectionDictionary = updatedSectionList.ToDictionary(s => s.sectionId);
// Remove sections that no longer exist
sectionDataList.RemoveAll(currentSection => !updatedSectionDictionary.ContainsKey(currentSection.sectionId));
foreach (SectionData currentSection in sectionDataList.ToList())
if (updatedSectionDictionary.TryGetValue(currentSection.sectionId, out SectionData updatedSection))
{
currentSection.sectionName = updatedSection.sectionName;
currentSection.objective = updatedSection.objective;
updatedSectionDictionary.Remove(currentSection.sectionId);
}
foreach (SectionData newSection in updatedSectionDictionary.Values) sectionDataList.Add(newSection);
foreach (SectionChangeEventsData sectionChangeEvent in sectionChangeEventsDataList) sectionChangeEvent.Initialize(this);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 3d56bc33052944e0aba5a0fe7073c3ab
timeCreated: 1706546743

View File

@ -0,0 +1,77 @@
using System.Collections.Generic;
using System.Linq;
using Convai.Scripts.Runtime.Core;
using UnityEngine;
using UnityEngine.Events;
namespace Convai.Scripts.Runtime.Features
{
public class NarrativeDesignTrigger : MonoBehaviour
{
public ConvaiNPC convaiNPC;
[HideInInspector] public int selectedTriggerIndex;
[HideInInspector] public List<string> availableTriggers = new();
public UnityEvent onTriggerEvent;
private NarrativeDesignManager _narrativeDesignManager;
private void Awake()
{
UpdateNarrativeDesignManager();
}
private void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag("Player")) InvokeSelectedTrigger();
}
private void OnValidate()
{
UpdateNarrativeDesignManager();
}
private void UpdateNarrativeDesignManager()
{
if (convaiNPC != null)
{
_narrativeDesignManager = convaiNPC.GetComponent<NarrativeDesignManager>();
if (_narrativeDesignManager != null) UpdateAvailableTriggers();
}
else
{
availableTriggers.Clear();
selectedTriggerIndex = -1;
}
}
public void UpdateAvailableTriggers()
{
if (_narrativeDesignManager != null)
{
availableTriggers = _narrativeDesignManager.triggerDataList.Select(trigger => trigger.triggerName).ToList();
if (selectedTriggerIndex >= availableTriggers.Count) selectedTriggerIndex = availableTriggers.Count - 1;
}
}
public void InvokeSelectedTrigger()
{
if (convaiNPC != null && availableTriggers != null && selectedTriggerIndex >= 0 && selectedTriggerIndex < availableTriggers.Count)
{
string selectedTriggerName = availableTriggers[selectedTriggerIndex];
ConvaiNPCManager.Instance.SetActiveConvaiNPC(convaiNPC, false);
onTriggerEvent?.Invoke();
convaiNPC.TriggerEvent(selectedTriggerName);
}
}
public void InvokeSpeech(string message)
{
if (convaiNPC != null && !string.IsNullOrEmpty(message))
{
ConvaiNPCManager.Instance.SetActiveConvaiNPC(convaiNPC, false);
onTriggerEvent?.Invoke();
convaiNPC.TriggerSpeech(message);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 1c783ed73e64a204ebf38b93c054b566
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: