using System.Security.Cryptography; using System.Text; namespace AIStudio.Assistants.BeyondFactsLab; /// /// Gives each chat of the BeyondFacts Lab an ID which follows from where it lives. /// /// /// The provenance store keeps what a chat received by the ID of the chat, and its cleanup at the start removes what /// belongs to no chat on the disk. With IDs which follow from the place of a chat, such as the preparation and the /// step, the cleanup finds the chats of the lab from the folders alone, without reading a single chat; see /// ProvenanceCleanup. A chat started over keeps its ID, so what the old conversation received becomes unreferenced /// and goes at the next cleanup. The ID is a version 8 UUID after RFC 9562, built from a SHA-256 hash. /// public static class BflThreadId { /// /// What sets the IDs of the lab apart from any other name-based ID. /// private const string NAMESPACE = "beyondfacts-lab/thread/"; /// /// The ID of the conversation in which the lab tells whether it fits a task. /// public static readonly Guid ONBOARDING = FromName("onboarding"); /// /// Gets the ID of the chat of a step. /// /// The ID of the preparation. /// The step. /// The ID, the same on every computer and in every run. public static Guid Of(Guid projectId, BflStep step) => FromName($"{projectId:D}/{step.FolderName}"); /// /// Builds the ID of a chat from the name of its place. /// /// The name of the place, which no other place has. /// The ID, the same on every computer and in every run. private static Guid FromName(string name) { var bytesOfName = Encoding.UTF8.GetBytes($"{NAMESPACE}{name}"); Span hash = stackalloc byte[SHA256.HashSizeInBytes]; SHA256.HashData(bytesOfName, hash); var bytes = hash[..16]; bytes[6] = (byte)((bytes[6] & 0x0F) | 0x80); bytes[8] = (byte)((bytes[8] & 0x3F) | 0x80); return new Guid(bytes, bigEndian: true); } }