Skip to content

Commit df87b43

Browse files
authored
Merge pull request #1357 from iceljc/features/add-resolve-block
add membase util
2 parents a7be958 + 6c26b9e commit df87b43

26 files changed

Lines changed: 497 additions & 86 deletions

src/Infrastructure/BotSharp.Abstraction/Agents/IInstructionResolver.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ namespace BotSharp.Abstraction.Agents;
22

33
public interface IInstructionResolver
44
{
5-
string Name { get; }
5+
string Provider { get; }
66

77
Task<string> ResolveAsync(Agent agent, string instruction, IEnumerable<object?> args, IDictionary<string, object?> kwArgs)
88
=> Task.FromResult(instruction);

src/Infrastructure/BotSharp.Abstraction/Rules/RuleGraph.cs renamed to src/Infrastructure/BotSharp.Abstraction/Graph/FlowGraph.cs

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,26 @@
1-
using BotSharp.Abstraction.Rules.Models;
1+
using BotSharp.Abstraction.Graph.Models;
22

33
namespace BotSharp.Abstraction.Rules;
44

5-
public class RuleGraph
5+
public class FlowGraph
66
{
77
private string _id = Guid.NewGuid().ToString();
8-
private List<RuleNode> _nodes = [];
9-
private List<RuleEdge> _edges = [];
8+
private List<FlowNode> _nodes = [];
9+
private List<FlowEdge> _edges = [];
1010

11-
public RuleGraph()
11+
public FlowGraph()
1212
{
1313
_id = Guid.NewGuid().ToString();
1414
_nodes = [];
1515
_edges = [];
1616
}
1717

18-
public static RuleGraph Init()
18+
public static FlowGraph Init()
1919
{
20-
return new RuleGraph();
20+
return new FlowGraph();
2121
}
2222

23-
public RuleNode? GetRootNode(string? name = null)
23+
public FlowNode? GetRootNode(string? name = null)
2424
{
2525
if (!string.IsNullOrEmpty(name))
2626
{
@@ -30,7 +30,7 @@ public static RuleGraph Init()
3030
return _nodes.FirstOrDefault(x => x.Type.IsEqualTo("root") || x.Type.IsEqualTo("start"));
3131
}
3232

33-
public (RuleNode? Node, IEnumerable<RuleEdge> IncomingEdges, IEnumerable<RuleEdge> OutgoingEdges) GetNode(string id)
33+
public (FlowNode? Node, IEnumerable<FlowEdge> IncomingEdges, IEnumerable<FlowEdge> OutgoingEdges) GetNode(string id)
3434
{
3535
var node = _nodes.FirstOrDefault(x => x.Id.IsEqualTo(id));
3636
if (node == null)
@@ -55,12 +55,12 @@ public string GetGraphId()
5555
return _id;
5656
}
5757

58-
public IEnumerable<RuleNode> GetNodes(Func<RuleNode, bool>? filter = null)
58+
public IEnumerable<FlowNode> GetNodes(Func<FlowNode, bool>? filter = null)
5959
{
6060
return filter == null ? [.. _nodes] : [.. _nodes.Where(filter)];
6161
}
6262

63-
public IEnumerable<RuleEdge> GetEdges(Func<RuleEdge, bool>? filter = null)
63+
public IEnumerable<FlowEdge> GetEdges(Func<FlowEdge, bool>? filter = null)
6464
{
6565
return filter == null ? [.. _edges] : [.. _edges.Where(filter)];
6666
}
@@ -70,17 +70,17 @@ public void SetGraphId(string id)
7070
_id = id;
7171
}
7272

73-
public void SetNodes(IEnumerable<RuleNode> nodes)
73+
public void SetNodes(IEnumerable<FlowNode> nodes)
7474
{
7575
_nodes = [.. nodes?.ToList() ?? []];
7676
}
7777

78-
public void SetEdges(IEnumerable<RuleEdge> edges)
78+
public void SetEdges(IEnumerable<FlowEdge> edges)
7979
{
8080
_edges = [.. edges?.ToList() ?? []];
8181
}
8282

83-
public void AddNode(RuleNode node)
83+
public void AddNode(FlowNode node)
8484
{
8585
var found = _nodes.Exists(x => x.Id.IsEqualTo(node.Id));
8686
if (!found)
@@ -89,7 +89,7 @@ public void AddNode(RuleNode node)
8989
}
9090
}
9191

92-
public void AddEdge(RuleNode from, RuleNode to, EdgeItemPayload payload)
92+
public void AddEdge(FlowNode from, FlowNode to, EdgeItemPayload payload)
9393
{
9494
var sourceFound = _nodes.Exists(x => x.Id.IsEqualTo(from.Id));
9595
var targetFound = _nodes.Exists(x => x.Id.IsEqualTo(to.Id));
@@ -107,7 +107,7 @@ public void AddEdge(RuleNode from, RuleNode to, EdgeItemPayload payload)
107107

108108
if (!edgeFound)
109109
{
110-
_edges.Add(new RuleEdge(from, to)
110+
_edges.Add(new FlowEdge(from, to)
111111
{
112112
Id = payload.Id,
113113
Name = payload.Name,
@@ -121,7 +121,7 @@ public void AddEdge(RuleNode from, RuleNode to, EdgeItemPayload payload)
121121
}
122122
}
123123

124-
public IEnumerable<(RuleNode, RuleEdge)> GetParentNodes(RuleNode node, bool ascending = false)
124+
public IEnumerable<(FlowNode, FlowEdge)> GetParentNodes(FlowNode node, bool ascending = false)
125125
{
126126
var filtered = _edges.Where(e => e.To != null && e.To.Id.IsEqualTo(node.Id));
127127
var ordered = ascending
@@ -131,7 +131,7 @@ public void AddEdge(RuleNode from, RuleNode to, EdgeItemPayload payload)
131131
return ordered.Select(e => (e.From, e)).ToList();
132132
}
133133

134-
public IEnumerable<(RuleNode, RuleEdge)> GetChildrenNodes(RuleNode node, bool ascending = false)
134+
public IEnumerable<(FlowNode, FlowEdge)> GetChildrenNodes(FlowNode node, bool ascending = false)
135135
{
136136
var filtered = _edges.Where(e => e.From != null && e.From.Id.IsEqualTo(node.Id));
137137
var ordered = ascending
@@ -141,7 +141,7 @@ public void AddEdge(RuleNode from, RuleNode to, EdgeItemPayload payload)
141141
return ordered.Select(e => (e.To, e)).ToList();
142142
}
143143

144-
public RuleGraphInfo GetGraphInfo()
144+
public FlowGraphInfo GetGraphInfo()
145145
{
146146
return new()
147147
{
@@ -157,9 +157,9 @@ public void Clear()
157157
_edges = [];
158158
}
159159

160-
public static RuleGraph FromGraphInfo(RuleGraphInfo graphInfo)
160+
public static FlowGraph FromGraphInfo(FlowGraphInfo graphInfo)
161161
{
162-
var graph = new RuleGraph();
162+
var graph = new FlowGraph();
163163
graph.SetGraphId(graphInfo.GraphId.IfNullOrEmptyAs(Guid.NewGuid().ToString())!);
164164
graph.SetNodes(graphInfo.Nodes);
165165
graph.SetEdges(graphInfo.Edges);
@@ -172,7 +172,7 @@ public override string ToString()
172172
}
173173
}
174174

175-
public class RuleNode : GraphItem
175+
public class FlowNode : GraphItem
176176
{
177177
/// <summary>
178178
/// Node type: root, criteria, action, etc.
@@ -200,22 +200,22 @@ public override string ToString()
200200
}
201201
}
202202

203-
public class RuleEdge : GraphItem
203+
public class FlowEdge : GraphItem
204204
{
205205
/// <summary>
206206
/// Edge type: is_next, etc.
207207
/// </summary>
208208
public override string Type { get; set; } = "next";
209209

210-
public RuleNode From { get; set; }
211-
public RuleNode To { get; set; }
210+
public FlowNode From { get; set; }
211+
public FlowNode To { get; set; }
212212

213-
public RuleEdge() : base()
213+
public FlowEdge() : base()
214214
{
215-
215+
216216
}
217217

218-
public RuleEdge(RuleNode from, RuleNode to) : base()
218+
public FlowEdge(FlowNode from, FlowNode to) : base()
219219
{
220220
Id = Guid.NewGuid().ToString();
221221
From = from;
@@ -279,11 +279,11 @@ public class EdgeItemPayload : GraphItem
279279

280280
}
281281

282-
public class RuleGraphInfo
282+
public class FlowGraphInfo
283283
{
284284
[JsonPropertyName("graph_id")]
285285
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
286286
public string GraphId { get; set; }
287-
public IEnumerable<RuleNode> Nodes { get; set; } = [];
288-
public IEnumerable<RuleEdge> Edges { get; set; } = [];
287+
public IEnumerable<FlowNode> Nodes { get; set; } = [];
288+
public IEnumerable<FlowEdge> Edges { get; set; } = [];
289289
}

src/Infrastructure/BotSharp.Abstraction/Rules/Models/FlowUnitSchema.cs renamed to src/Infrastructure/BotSharp.Abstraction/Graph/Models/FlowUnitSchema.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
using System.Text.Json.Serialization;
2-
3-
namespace BotSharp.Abstraction.Rules.Models;
1+
namespace BotSharp.Abstraction.Graph.Models;
42

53
/// <summary>
64
/// Describes the input or output contract of a rule flow unit (action or condition).
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
using BotSharp.Abstraction.Graph.Models;
2+
using BotSharp.Abstraction.Rules;
3+
using System.Text.Json;
4+
using System.Text.RegularExpressions;
5+
6+
namespace BotSharp.Abstraction.Graph.Utils;
7+
8+
public static class GraphBuilder
9+
{
10+
public static FlowGraph Build(GraphQueryResult result, IDictionary<string, string>? data = null)
11+
{
12+
var graph = FlowGraph.Init();
13+
if (result.Values.IsNullOrEmpty())
14+
{
15+
return graph;
16+
}
17+
18+
foreach (var item in result.Values)
19+
{
20+
// Try to deserialize nodes and edge from the dictionary
21+
if (!item.TryGetValue<JsonElement>("sourceNode", out var sourceNodeElement) ||
22+
!item.TryGetValue<JsonElement>("targetNode", out var targetNodeElement) ||
23+
!item.TryGetValue<JsonElement>("edge", out var edgeElement))
24+
{
25+
continue;
26+
}
27+
28+
// Parse source node
29+
var sourceNodeId = sourceNodeElement.TryGetProperty("id", out var sId) ? sId.GetString() : null;
30+
var sourceNodeLabels = sourceNodeElement.TryGetProperty("labels", out var sLabels)
31+
? sLabels.EnumerateArray().Select(x => x.GetString() ?? "").ToList()
32+
: [];
33+
var sourceNodeProps = sourceNodeElement.TryGetProperty("properties", out var sProps)
34+
? sProps
35+
: default;
36+
37+
// Parse target node
38+
var targetNodeId = targetNodeElement.TryGetProperty("id", out var tId) ? tId.GetString() : null;
39+
var targetNodeLabels = targetNodeElement.TryGetProperty("labels", out var tLabels)
40+
? tLabels.EnumerateArray().Select(x => x.GetString() ?? "").ToList()
41+
: [];
42+
var targetNodeProps = targetNodeElement.TryGetProperty("properties", out var tProps)
43+
? tProps
44+
: default;
45+
46+
// Parse edge
47+
var edgeId = edgeElement.TryGetProperty("id", out var eId) ? eId.GetString() : null;
48+
var edgeProps = edgeElement.TryGetProperty("properties", out var eProps)
49+
? eProps
50+
: default;
51+
52+
// Create source node
53+
var sourceNode = new FlowNode()
54+
{
55+
Id = sourceNodeId ?? Guid.NewGuid().ToString(),
56+
Labels = sourceNodeLabels,
57+
Name = GetGraphItemAttribute(sourceNodeProps, key: "name", data: data),
58+
Type = GetGraphItemAttribute(sourceNodeProps, key: "type", data: data),
59+
Description = GetGraphItemAttribute(sourceNodeProps, key: "description", data: data),
60+
Config = GetConfig(sourceNodeProps, data)
61+
};
62+
63+
// Create target node
64+
var targetNode = new FlowNode()
65+
{
66+
Id = targetNodeId ?? Guid.NewGuid().ToString(),
67+
Labels = targetNodeLabels,
68+
Name = GetGraphItemAttribute(targetNodeProps, key: "name", data: data),
69+
Type = GetGraphItemAttribute(targetNodeProps, key: "type", data: data),
70+
Description = GetGraphItemAttribute(targetNodeProps, key: "description", data: data),
71+
Config = GetConfig(targetNodeProps, data)
72+
};
73+
74+
// Create edge payload
75+
var edgePayload = new EdgeItemPayload()
76+
{
77+
Id = edgeId ?? Guid.NewGuid().ToString(),
78+
Name = GetGraphItemAttribute(edgeProps, key: "name", data: data),
79+
Type = GetGraphItemAttribute(edgeProps, key: "type", defaultValue: "NEXT", data: data),
80+
Description = GetGraphItemAttribute(edgeProps, key: "description", data: data),
81+
Config = GetConfig(edgeProps, data)
82+
};
83+
84+
// Add edge to graph
85+
graph.AddEdge(sourceNode, targetNode, edgePayload);
86+
}
87+
88+
return graph;
89+
}
90+
91+
private static string GetGraphItemAttribute(JsonElement? properties, string key, string defaultValue = null, IDictionary<string, string>? data = null)
92+
{
93+
if (properties == null || properties.Value.ValueKind == JsonValueKind.Undefined)
94+
{
95+
return defaultValue;
96+
}
97+
98+
if (properties.Value.TryGetProperty(key, out var name) && name.ValueKind == JsonValueKind.String)
99+
{
100+
return ReplacePlaceholders(name.GetString(), data) ?? defaultValue;
101+
}
102+
103+
return defaultValue;
104+
}
105+
106+
private static Dictionary<string, string?> GetConfig(JsonElement? properties, IDictionary<string, string>? data = null)
107+
{
108+
var config = new Dictionary<string, string?>();
109+
110+
if (properties == null || properties.Value.ValueKind == JsonValueKind.Undefined)
111+
{
112+
return config;
113+
}
114+
115+
// Convert all properties to config dictionary
116+
foreach (var prop in properties.Value.EnumerateObject())
117+
{
118+
config[prop.Name] = ReplacePlaceholders(prop.Value.ConvertToString(), data);
119+
}
120+
121+
return config;
122+
}
123+
124+
private static readonly Regex PlaceholderRegex = new(@"\{\{(\w+)\}\}", RegexOptions.Compiled);
125+
126+
private static string? ReplacePlaceholders(string? value, IDictionary<string, string>? data)
127+
{
128+
if (data == null || string.IsNullOrEmpty(value) || !value.Contains("{{"))
129+
{
130+
return value;
131+
}
132+
133+
return PlaceholderRegex.Replace(value, match =>
134+
{
135+
var key = match.Groups[1].Value;
136+
return data.TryGetValue(key, out var replacement) && replacement != null
137+
? replacement
138+
: match.Value;
139+
});
140+
}
141+
}
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1+
using BotSharp.Abstraction.Graph;
12
using BotSharp.Abstraction.Hooks;
23
using BotSharp.Abstraction.Rules.Models;
34

45
namespace BotSharp.Abstraction.Rules.Hooks;
56

67
public interface IRuleTriggerHook : IHookBase
78
{
8-
Task BeforeRuleConditionExecuting(Agent agent, RuleNode conditionNode, RuleEdge incomingEdge, IRuleTrigger trigger, RuleFlowContext context) => Task.CompletedTask;
9-
Task AfterRuleConditionExecuted(Agent agent, RuleNode conditionNode, RuleEdge incomingEdge, IRuleTrigger trigger, RuleFlowContext context, RuleNodeResult result) => Task.CompletedTask;
9+
Task BeforeRuleConditionExecuting(Agent agent, FlowNode conditionNode, FlowEdge incomingEdge, IRuleTrigger trigger, RuleFlowContext context) => Task.CompletedTask;
10+
Task AfterRuleConditionExecuted(Agent agent, FlowNode conditionNode, FlowEdge incomingEdge, IRuleTrigger trigger, RuleFlowContext context, RuleNodeResult result) => Task.CompletedTask;
1011

11-
Task BeforeRuleActionExecuting(Agent agent, RuleNode actionNode, RuleEdge incomingEdge, IRuleTrigger trigger, RuleFlowContext context) => Task.CompletedTask;
12-
Task AfterRuleActionExecuted(Agent agent, RuleNode actionNode, RuleEdge incomingEdge, IRuleTrigger trigger, RuleFlowContext context, RuleNodeResult result) => Task.CompletedTask;
12+
Task BeforeRuleActionExecuting(Agent agent, FlowNode actionNode, FlowEdge incomingEdge, IRuleTrigger trigger, RuleFlowContext context) => Task.CompletedTask;
13+
Task AfterRuleActionExecuted(Agent agent, FlowNode actionNode, FlowEdge incomingEdge, IRuleTrigger trigger, RuleFlowContext context, RuleNodeResult result) => Task.CompletedTask;
1314
}

src/Infrastructure/BotSharp.Abstraction/Rules/IRuleEngine.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using BotSharp.Abstraction.Graph;
12
using BotSharp.Abstraction.Rules.Models;
23

34
namespace BotSharp.Abstraction.Rules;
@@ -25,5 +26,5 @@ Task<IEnumerable<string>> Triggered(IRuleTrigger trigger, string text, IEnumerab
2526
/// <param name="trigger"></param>
2627
/// <param name="options"></param>
2728
/// <returns></returns>
28-
Task ExecuteGraphNode(RuleNode node, RuleGraph graph, string agentId, IRuleTrigger trigger, RuleNodeExecutionOptions options);
29+
Task ExecuteGraphNode(FlowNode node, FlowGraph graph, string agentId, IRuleTrigger trigger, RuleNodeExecutionOptions options);
2930
}

src/Infrastructure/BotSharp.Abstraction/Rules/IRuleFlowUnit.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using BotSharp.Abstraction.Rules.Models;
1+
using BotSharp.Abstraction.Graph.Models;
22

33
namespace BotSharp.Abstraction.Rules;
44

0 commit comments

Comments
 (0)