diff --git a/src/Plugins/BotSharp.Plugin.Membase/Interfaces/IMembaseApi.cs b/src/Plugins/BotSharp.Plugin.Membase/Interfaces/IMembaseApi.cs index 290cee488..124fd1d19 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Interfaces/IMembaseApi.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Interfaces/IMembaseApi.cs @@ -47,6 +47,9 @@ public interface IMembaseApi #endregion #region PGT + [Get("/graph/{graphId}/pgt-definitions/{definitionId}")] + Task GetPgtDefinitionAsync(string graphId, string definitionId); + [Post("/graph/{graphId}/pgt-definitions/{definitionId}/simulate")] Task SimulatePgtDefinitionAsync(string graphId, string definitionId, [Body] PgtSimulationRequest request); diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/PgtConfig.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/PgtConfig.cs new file mode 100644 index 000000000..bc39b6872 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/PgtConfig.cs @@ -0,0 +1,72 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Plugin.Membase.Models; + +/// +/// Full configuration block returned by the pgt-definitions endpoint. +/// +public class PgtConfig +{ + [JsonPropertyName("startId")] + public string? StartId { get; set; } + + [JsonPropertyName("maxDepth")] + public int MaxDepth { get; set; } + + [JsonPropertyName("renderDepth")] + public int RenderDepth { get; set; } + + [JsonPropertyName("appendTopology")] + public bool AppendTopology { get; set; } + + [JsonPropertyName("fuzzyThreshold")] + public double FuzzyThreshold { get; set; } + + [JsonPropertyName("maxNodes")] + public int MaxNodes { get; set; } + + [JsonPropertyName("strategy")] + public string? Strategy { get; set; } + + [JsonPropertyName("maxVisitsPerNode")] + public int MaxVisitsPerNode { get; set; } + + [JsonPropertyName("timeoutMs")] + public int TimeoutMs { get; set; } + + [JsonPropertyName("maxSubgraphNesting")] + public int MaxSubgraphNesting { get; set; } + + [JsonPropertyName("recordTrace")] + public bool RecordTrace { get; set; } + + [JsonPropertyName("persistRun")] + public bool PersistRun { get; set; } + + [JsonPropertyName("validationChecks")] + public List? ValidationChecks { get; set; } + + [JsonPropertyName("targetIdsRaw")] + public string? TargetIdsRaw { get; set; } + + [JsonPropertyName("allowedEdgeTypesRaw")] + public string? AllowedEdgeTypesRaw { get; set; } + + /// JSON string containing the environment key-value pairs. + [JsonPropertyName("environmentJson")] + public string? EnvironmentJson { get; set; } + + /// JSON string containing the initial context key-value pairs. + [JsonPropertyName("initialContextJson")] + public string? InitialContextJson { get; set; } + + /// + /// JSON array string of actor descriptors. Each element has an actor_id + /// field used as the dictionary key when building a traverse request. + /// + [JsonPropertyName("actorsJson")] + public string? ActorsJson { get; set; } + + [JsonPropertyName("dag")] + public bool Dag { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/PgtDefinition.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/PgtDefinition.cs new file mode 100644 index 000000000..b949be65a --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/PgtDefinition.cs @@ -0,0 +1,36 @@ +using System.Text.Json.Serialization; + +namespace BotSharp.Plugin.Membase.Models; + +/// +/// Response from GET /graph/{graphId}/pgt-definitions/{definitionId} +/// +public class PgtDefinition +{ + [JsonPropertyName("id")] + public string Id { get; set; } = string.Empty; + + [JsonPropertyName("graphId")] + public string GraphId { get; set; } = string.Empty; + + [JsonPropertyName("name")] + public string Name { get; set; } = string.Empty; + + [JsonPropertyName("description")] + public string? Description { get; set; } + + [JsonPropertyName("ownerUserId")] + public string? OwnerUserId { get; set; } + + [JsonPropertyName("config")] + public PgtConfig Config { get; set; } = new(); + + [JsonPropertyName("version")] + public int Version { get; set; } + + [JsonPropertyName("createdAt")] + public DateTimeOffset? CreatedAt { get; set; } + + [JsonPropertyName("updatedAt")] + public DateTimeOffset? UpdatedAt { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/PgtTraversalRequest.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/PgtTraversalRequest.cs index ba804f6de..a1104e1ec 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/PgtTraversalRequest.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/Requests/PgtTraversalRequest.cs @@ -4,10 +4,103 @@ namespace BotSharp.Plugin.Membase.Models; public class PgtTraversalRequest { + [JsonPropertyName("startId")] public string StartId { get; set; } = string.Empty; [JsonPropertyName("options")] public PgtTraversalOptions? Options { get; set; } + + /// + /// Builds a traverse request from a fetched , + /// merging caller overrides on top of stored config values. + /// + public static PgtTraversalRequest FromDefinition( + PgtDefinition definition, + string? runId = null, + Dictionary? environmentOverrides = null, + Dictionary? initialContextOverrides = null, + bool stream = false, + bool debug = false, + string[]? pauseOn = null, + int? debugIdleTimeoutMs = null) + { + var cfg = definition.Config; + + return new PgtTraversalRequest + { + StartId = cfg.StartId ?? string.Empty, + Options = new PgtTraversalOptions + { + MaxDepth = cfg.MaxDepth, + MaxVisitsPerNode = cfg.MaxVisitsPerNode, + TimeoutMs = cfg.TimeoutMs, + MaxSubGrapNesting = cfg.MaxSubgraphNesting, + Strategy = cfg.Strategy, + RecordTrace = cfg.RecordTrace, + PersistRun = cfg.PersistRun, + RunId = runId, + Stream = stream, + Debug = debug, + PauseOn = pauseOn, + DebugIdleTimeoutMs = debugIdleTimeoutMs, + Actors = ParseActorsJson(cfg.ActorsJson), + Environment = MergeJsonDict(cfg.EnvironmentJson, environmentOverrides), + InitialContext = MergeJsonDict(cfg.InitialContextJson, initialContextOverrides), + }, + }; + } + + private static Dictionary? ParseActorsJson(string? actorsJson) + { + if (string.IsNullOrWhiteSpace(actorsJson)) + return null; + + var array = JsonSerializer.Deserialize(actorsJson); + if (array is null || array.Length == 0) + return null; + + var dict = new Dictionary(StringComparer.Ordinal); + foreach (var element in array) + { + if (!element.TryGetProperty("actor_id", out var idProp)) + continue; + + var actorId = idProp.GetString() ?? string.Empty; + dict[actorId] = JsonSerializer.Deserialize(element.GetRawText()) ?? element; + } + + return dict.Count > 0 ? dict : null; + } + + private static Dictionary? MergeJsonDict( + string? existingJson, + Dictionary? overrides) + { + var merged = new Dictionary(StringComparer.Ordinal); + + if (!string.IsNullOrWhiteSpace(existingJson)) + { + var existing = JsonSerializer.Deserialize>(existingJson); + if (existing is not null) + { + foreach (var kv in existing) + merged[kv.Key] = kv.Value; + } + } + + if (overrides is not null && overrides.Count > 0) + { + foreach (var kv in overrides) + { + if (kv.Value is null) + merged.Remove(kv.Key); + else + merged[kv.Key] = kv.Value; + } + } + + return merged.Count > 0 ? merged : null; + } } public class PgtTraversalOptions