Skip to content

Commit ef56e7b

Browse files
author
Jicheng Lu
committed
Merge branch 'master' of https://github.com/SciSharp/BotSharp into features/refine-template-config
2 parents e4f8581 + 14db7f1 commit ef56e7b

34 files changed

Lines changed: 1765 additions & 743 deletions

File tree

README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,57 @@ BotSharp uses component design, the kernel is kept to a minimum, and business fu
140140
- [x] Multi-Agent Routing
141141
- [x] Knowledge Base
142142

143+
144+
### FAQ
145+
146+
#### General Questions
147+
148+
**What is BotSharp?**
149+
BotSharp is an open-source .NET machine learning framework for building AI Bot platforms. It provides out-of-the-box components for natural language understanding, computer vision, and audio processing, enabling enterprise developers to integrate AI into business systems efficiently.
150+
151+
**What .NET versions are supported?**
152+
BotSharp runs on .NET Core, which is fully cross-platform. It supports .NET 6+ and works on Windows, Linux, and macOS.
153+
154+
**Is BotSharp free to use?**
155+
Yes, BotSharp is licensed under Apache 2.0, allowing free use in both personal and commercial projects.
156+
157+
#### LLM Provider Configuration
158+
159+
**Which LLM providers are supported?**
160+
BotSharp supports multiple AI platforms including ChatGPT (3.5/4o/o1), Gemini 2, LLaMA 3, Claude Sonnet 3.5, DeepSeek V3, HuggingFace, and more through its modular plugin system.
161+
162+
**How do I configure a custom LLM provider?**
163+
Create a plugin that implements the LLM provider interface, or use one of the built-in plugins like `BotSharp.Plugin.OpenAI` or `BotSharp.Plugin.AnthropicAI`. Configure the API keys in your `appsettings.json`.
164+
165+
#### Agent Development
166+
167+
**How do I create a custom agent?**
168+
Define an agent profile with its responsibilities, then implement the required hooks and plugins. BotSharp's advanced Agent abstraction layer handles the complex orchestration automatically.
169+
170+
**Can multiple agents cooperate on a task?**
171+
Yes, BotSharp supports multi-agent scenarios where agents with different responsibilities can cooperate to complete complex tasks. The routing and planning system handles agent coordination.
172+
173+
#### MCP Integration
174+
175+
**What is MCP and how does BotSharp support it?**
176+
MCP (Model Context Protocol) enables large model calls to external tools. BotSharp has built-in MCP integration with visual management capabilities, supporting mainstream services like mcp.so.
177+
178+
#### Deployment
179+
180+
**How do I deploy BotSharp in production?**
181+
Run the backend service using `dotnet run` and deploy it behind a reverse proxy (Nginx/IIS). The Admin UI (built with SvelteKit) can be deployed separately. See the [documentation](https://botsharp.readthedocs.io) for detailed deployment guides.
182+
183+
**Can I use BotSharp without the UI?**
184+
Yes, BotSharp provides RESTful Open API and WebSocket real-time communication. You can integrate it directly with your own frontend or messaging channels.
185+
186+
#### Troubleshooting
187+
188+
**BotSharp fails to start with dependency errors**
189+
Ensure you have .NET 6+ SDK installed and all NuGet packages are restored. Run `dotnet restore` before `dotnet run`.
190+
191+
**LLM calls are timing out**
192+
Check your API key configuration in `appsettings.json`, verify network connectivity to the LLM provider, and ensure the model name is correct.
193+
143194
### Documents
144195

145196
Read the docs: https://botsharp.readthedocs.io?wt.mc_id=AI-MVP-5005183

src/Infrastructure/BotSharp.Abstraction/Conversations/Dtos/ChatResponseDto.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ public class ChatResponseDto : InstructResult
4646
[JsonPropertyName("is_streaming")]
4747
public bool IsStreaming { get; set; }
4848

49+
[JsonPropertyName("thought")]
50+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
51+
public Dictionary<string, string?>? Thought { get; set; }
52+
4953
[JsonPropertyName("meta_data")]
5054
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
5155
public Dictionary<string, string?>? MetaData { get; set; }

src/Infrastructure/BotSharp.Abstraction/Conversations/Models/Conversation.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ public class DialogMetaData
114114
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
115115
public string? ToolCallId { get; set; }
116116

117+
[JsonPropertyName("thought")]
118+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
119+
public Dictionary<string, string?>? Thought { get; set; }
120+
117121
[JsonPropertyName("meta_data")]
118122
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
119123
public Dictionary<string, string?>? MetaData { get; set; }

src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ public class RoleDialogModel : ITrackableMessage
7676
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
7777
public string? ToolCallId { get; set; }
7878

79+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
80+
public Dictionary<string, string?>? Thought { get; set; }
81+
7982
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
8083
public Dictionary<string, string?>? MetaData { get; set; }
8184

@@ -141,7 +144,6 @@ public class RoleDialogModel : ITrackableMessage
141144
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
142145
public bool IsStreaming { get; set; }
143146

144-
145147
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
146148
public bool IsFromUser => Role == AgentRole.User;
147149

@@ -210,6 +212,7 @@ public static RoleDialogModel From(RoleDialogModel source,
210212
Data = source.Data,
211213
IsStreaming = source.IsStreaming,
212214
Annotations = source.Annotations,
215+
Thought = source.Thought != null ? new(source.Thought) : null,
213216
MetaData = source.MetaData != null ? new(source.MetaData) : null
214217
};
215218
}

src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ public class ReasoningSetting
9999
public class WebSearchSetting
100100
{
101101
public bool IsDefault { get; set; }
102+
103+
[Obsolete("Set SearchContextSize in Parameters")]
102104
public string? SearchContextSize { get; set; }
105+
public Dictionary<string, ModelParamSetting>? Parameters { get; set; }
103106
}
104107
#endregion
105108

src/Infrastructure/BotSharp.Abstraction/Plugins/Models/PluginMenuDef.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,9 @@ public class EmbeddingData
7676
/// </summary>
7777
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
7878
public string? AppendTokenName { get; set; }
79+
80+
/// <summary>
81+
/// Whether to enable full screen or not
82+
/// </summary>
83+
public bool FullScreen { get; set; }
7984
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,10 @@ public interface IRuleTrigger
2121
/// Explain the purpose of rule trigger (display purpose)
2222
/// </summary>
2323
string Statement => string.Empty;
24+
25+
/// <summary>
26+
/// Optional list of agent IDs this trigger is associated with.
27+
/// Used for display/filtering in UI only (not used in execution logic).
28+
/// </summary>
29+
string[]? AgentIds => null;
2430
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ public void AddEdge(RuleNode from, RuleNode to, EdgeItemPayload payload)
114114
Type = payload.Type,
115115
Labels = [.. payload.Labels ?? []],
116116
Weight = payload.Weight,
117-
Purpose = payload.Purpose,
117+
Alias = payload.Alias,
118118
Description = payload.Description,
119119
Config = new(payload.Config ?? [])
120120
});
@@ -234,9 +234,15 @@ public class GraphItem
234234
public virtual string Type { get; set; } = null!;
235235
public virtual IEnumerable<string> Labels { get; set; } = [];
236236
public virtual double Weight { get; set; } = 1.0;
237-
public virtual string? Purpose { get; set; }
238237
public virtual string? Description { get; set; }
239238
public virtual Dictionary<string, string?> Config { get; set; } = [];
239+
240+
private string? _alias;
241+
public virtual string Alias
242+
{
243+
get => string.IsNullOrEmpty(_alias) ? Name : _alias;
244+
set => _alias = value;
245+
}
240246
}
241247

242248
public class NodeItem : GraphItem

src/Infrastructure/BotSharp.Core.Rules/Conditions/LogicGateCondition.cs

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using System.Text.Json;
21
using BotSharp.Core.Rules.Models;
32

43
namespace BotSharp.Core.Rules.Conditions;
@@ -16,11 +15,13 @@ namespace BotSharp.Core.Rules.Conditions;
1615
/// (A AND B) OR (C AND NOT D)
1716
///
1817
/// Leaf node format:
19-
/// { "node": "node_name", "key": "data_key" }
20-
/// - "node": The Name of a parent condition node whose result to inspect.
21-
/// - "key": The key in the parent node's RuleNodeResult.Data dictionary that holds
22-
/// a boolean string ("true"/"false"). If omitted, falls back to the parent
23-
/// node's RuleNodeResult.Success flag.
18+
/// { "node_alias": "node_alias", "key": "data_key" }
19+
/// - "node_alias": The Alias of a parent condition node whose result to inspect.
20+
/// Using Alias instead of Name avoids collisions when multiple nodes
21+
/// share the same Name (e.g. several "http_request" nodes).
22+
/// - "key": The key in the parent node's RuleNodeResult.Data dictionary that holds
23+
/// a boolean string ("true"/"false"). If omitted, falls back to the parent
24+
/// node's RuleNodeResult.Success flag.
2425
///
2526
/// Node config:
2627
/// "expression" - A JSON-encoded LogicExpression tree.
@@ -30,20 +31,20 @@ namespace BotSharp.Core.Rules.Conditions;
3031
/// Example: work_order_valid AND (client_name_valid OR NOT affiliate_name_valid)
3132
///
3233
/// Given three parent condition nodes:
33-
/// - Node A ("check_work_order") returns Data["work_order_valid"] = "true"
34-
/// - Node B ("check_client") returns Data["client_name_valid"] = "false"
35-
/// - Node C ("check_affiliate") returns Data["affiliate_name_valid"] = "false"
34+
/// - Node A (node_alias "check_work_order") returns Data["work_order_valid"] = "true"
35+
/// - Node B (node_alias "check_client") returns Data["client_name_valid"] = "false"
36+
/// - Node C (node_alias "check_affiliate") returns Data["affiliate_name_valid"] = "false"
3637
///
3738
/// The gate node config would be:
3839
/// {
3940
/// "expression": {
4041
/// "op": "and",
4142
/// "children": [
42-
/// { "node": "check_work_order", "key": "work_order_valid" },
43+
/// { "node_alias": "check_work_order", "key": "work_order_valid" },
4344
/// { "op": "or", "children": [
44-
/// { "node": "check_client", "key": "client_name_valid" },
45+
/// { "node_alias": "check_client", "key": "client_name_valid" },
4546
/// { "op": "not", "children": [
46-
/// { "node": "check_affiliate", "key": "affiliate_name_valid" }
47+
/// { "node_alias": "check_affiliate", "key": "affiliate_name_valid" }
4748
/// ]}
4849
/// ]}
4950
/// ]
@@ -142,10 +143,10 @@ public async Task<RuleNodeResult> EvaluateAsync(
142143

143144
var defaultValue = currentNode.Config?.GetValueOrDefault("default_value") ?? "false";
144145

145-
// 3. Build lookup: parent node name → its latest RuleFlowStepResult
146+
// 3. Build lookup: parent node alias → its latest RuleFlowStepResult
146147
var parentResults = (context.PrevStepResults ?? [])
147148
.Where(r => parentNodeIds.Contains(r.Node.Id))
148-
.GroupBy(r => r.Node.Name, StringComparer.OrdinalIgnoreCase)
149+
.GroupBy(r => r.Node.Alias, StringComparer.OrdinalIgnoreCase)
149150
.ToDictionary(g => g.Key, g => g.Last(), StringComparer.OrdinalIgnoreCase);
150151

151152
// 4. Evaluate the expression tree
@@ -167,13 +168,13 @@ private bool Evaluate(
167168
Dictionary<string, RuleFlowStepResult> parentResults,
168169
string defaultValue)
169170
{
170-
// Leaf node: look up a specific parent's result
171-
if (!string.IsNullOrEmpty(expr.Node))
171+
// Leaf node: look up a specific parent's result by alias
172+
if (!string.IsNullOrEmpty(expr.NodeAlias))
172173
{
173-
if (!parentResults.TryGetValue(expr.Node, out var stepResult))
174+
if (!parentResults.TryGetValue(expr.NodeAlias, out var stepResult))
174175
{
175-
_logger.LogWarning("Logic gate: parent node '{Node}' not found in results, using default '{Default}'.",
176-
expr.Node, defaultValue);
176+
_logger.LogWarning("Logic gate: parent node alias '{Alias}' not found in results, using default '{Default}'.",
177+
expr.NodeAlias, defaultValue);
177178
return ParseBool(defaultValue);
178179
}
179180

src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ private async Task ExecuteGraphTraversal(
234234
Edge = nextEdge,
235235
Graph = graph,
236236
Text = text,
237-
Parameters = BuildParameters(nextNode.Config, states, innerData),
237+
Parameters = BuildParameters(states, innerData),
238238
PrevStepResults = results,
239239
JsonOptions = options?.JsonOptions
240240
};
@@ -702,17 +702,11 @@ private void ValidateGraphSchema(RuleGraph graph)
702702

703703
#region Private methods
704704
private Dictionary<string, string?> BuildParameters(
705-
Dictionary<string, string?>? config,
706705
IEnumerable<MessageState>? states,
707706
Dictionary<string, string?>? param = null)
708707
{
709708
var dict = new Dictionary<string, string?>();
710709

711-
if (config != null)
712-
{
713-
dict = new(config);
714-
}
715-
716710
if (!states.IsNullOrEmpty())
717711
{
718712
foreach (var state in states!)

0 commit comments

Comments
 (0)