Skip to content

Commit a317fa4

Browse files
authored
Merge pull request #1147 from iceljc/features/refine-chart-handler
refine chart handler instruction & allow sending multiple messages
2 parents 27ada7c + 462eaf0 commit a317fa4

File tree

11 files changed

+181
-129
lines changed

11 files changed

+181
-129
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ public class ChatResponseDto : InstructResult
4242
[JsonPropertyName("is_streaming")]
4343
public bool IsStreaming { get; set; }
4444

45+
[JsonPropertyName("is_append")]
46+
public bool IsAppend { get; set; }
47+
4548
[JsonPropertyName("created_at")]
4649
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
4750
}

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,13 @@ public class RoleDialogModel : ITrackableMessage
127127
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
128128
public bool IsStreaming { get; set; }
129129

130+
/// <summary>
131+
/// Additional messages that can be sent sequentially and save to db
132+
/// </summary>
133+
[JsonIgnore(Condition = JsonIgnoreCondition.Always)]
134+
public ChatMessageWrapper? AdditionalMessageWrapper { get; set; }
135+
136+
130137
public RoleDialogModel()
131138
{
132139
}
@@ -171,7 +178,15 @@ public static RoleDialogModel From(RoleDialogModel source,
171178
Instruction = source.Instruction,
172179
Data = source.Data,
173180
IsStreaming = source.IsStreaming,
174-
Annotations = source.Annotations
181+
Annotations = source.Annotations,
182+
AdditionalMessageWrapper = source.AdditionalMessageWrapper
175183
};
176184
}
177185
}
186+
187+
public class ChatMessageWrapper
188+
{
189+
public int IntervalMilliSeconds { get; set; } = 1000;
190+
public bool SaveToDb { get; set; }
191+
public List<RoleDialogModel>? Messages { get; set; }
192+
}

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationService.SendMessage.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
using BotSharp.Abstraction.Hooks;
21
using BotSharp.Abstraction.Infrastructures.Enums;
32
using BotSharp.Abstraction.Messaging;
43
using BotSharp.Abstraction.Messaging.Models.RichContent;

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs

Lines changed: 74 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using BotSharp.Abstraction.Conversations.Models;
12
using BotSharp.Abstraction.Messaging;
23
using BotSharp.Abstraction.Messaging.Models.RichContent;
34
using BotSharp.Abstraction.Options;
@@ -31,63 +32,21 @@ public void Append(string conversationId, IEnumerable<RoleDialogModel> dialogs)
3132

3233
foreach ( var dialog in dialogs)
3334
{
34-
if (dialog.Role == AgentRole.Function)
35+
var innerList = new List<RoleDialogModel> { dialog };
36+
if (dialog.AdditionalMessageWrapper != null
37+
&& dialog.AdditionalMessageWrapper.SaveToDb
38+
&& dialog.AdditionalMessageWrapper.Messages?.Count > 0)
3539
{
36-
var meta = new DialogMetaData
37-
{
38-
Role = dialog.Role,
39-
AgentId = dialog.CurrentAgentId,
40-
MessageId = dialog.MessageId,
41-
MessageType = dialog.MessageType,
42-
FunctionName = dialog.FunctionName,
43-
FunctionArgs = dialog.FunctionArgs,
44-
ToolCallId = dialog.ToolCallId,
45-
CreatedTime = dialog.CreatedAt
46-
};
47-
48-
var content = dialog.Content.RemoveNewLine();
49-
if (string.IsNullOrEmpty(content))
50-
{
51-
continue;
52-
}
53-
dialogElements.Add(new DialogElement
54-
{
55-
MetaData = meta,
56-
Content = dialog.Content,
57-
SecondaryContent = dialog.SecondaryContent,
58-
Payload = dialog.Payload
59-
});
40+
innerList.AddRange(dialog.AdditionalMessageWrapper.Messages);
6041
}
61-
else
62-
{
63-
var meta = new DialogMetaData
64-
{
65-
Role = dialog.Role,
66-
AgentId = dialog.CurrentAgentId,
67-
MessageId = dialog.MessageId,
68-
MessageType = dialog.MessageType,
69-
SenderId = dialog.SenderId,
70-
FunctionName = dialog.FunctionName,
71-
CreatedTime = dialog.CreatedAt
72-
};
7342

74-
var content = dialog.Content.RemoveNewLine();
75-
if (string.IsNullOrEmpty(content))
43+
foreach (var item in innerList)
44+
{
45+
var element = BuildDialogElement(item);
46+
if (element != null)
7647
{
77-
continue;
48+
dialogElements.Add(element);
7849
}
79-
80-
var richContent = dialog.RichContent != null ? JsonSerializer.Serialize(dialog.RichContent, _options.JsonSerializerOptions) : null;
81-
var secondaryRichContent = dialog.SecondaryRichContent != null ? JsonSerializer.Serialize(dialog.SecondaryRichContent, _options.JsonSerializerOptions) : null;
82-
dialogElements.Add(new DialogElement
83-
{
84-
MetaData = meta,
85-
Content = dialog.Content,
86-
SecondaryContent = dialog.SecondaryContent,
87-
RichContent = richContent,
88-
SecondaryRichContent = secondaryRichContent,
89-
Payload = dialog.Payload
90-
});
9150
}
9251
}
9352

@@ -148,4 +107,67 @@ public List<RoleDialogModel> GetDialogs(string conversationId)
148107

149108
return results;
150109
}
110+
111+
private DialogElement? BuildDialogElement(RoleDialogModel dialog)
112+
{
113+
DialogElement? element = null;
114+
115+
if (dialog.Role == AgentRole.Function)
116+
{
117+
var meta = new DialogMetaData
118+
{
119+
Role = dialog.Role,
120+
AgentId = dialog.CurrentAgentId,
121+
MessageId = dialog.MessageId,
122+
MessageType = dialog.MessageType,
123+
FunctionName = dialog.FunctionName,
124+
FunctionArgs = dialog.FunctionArgs,
125+
ToolCallId = dialog.ToolCallId,
126+
CreatedTime = dialog.CreatedAt
127+
};
128+
129+
var content = dialog.Content.RemoveNewLine();
130+
if (!string.IsNullOrEmpty(content))
131+
{
132+
element = new DialogElement
133+
{
134+
MetaData = meta,
135+
Content = dialog.Content,
136+
SecondaryContent = dialog.SecondaryContent,
137+
Payload = dialog.Payload
138+
};
139+
}
140+
}
141+
else
142+
{
143+
var meta = new DialogMetaData
144+
{
145+
Role = dialog.Role,
146+
AgentId = dialog.CurrentAgentId,
147+
MessageId = dialog.MessageId,
148+
MessageType = dialog.MessageType,
149+
SenderId = dialog.SenderId,
150+
FunctionName = dialog.FunctionName,
151+
CreatedTime = dialog.CreatedAt
152+
};
153+
154+
var content = dialog.Content.RemoveNewLine();
155+
if (!string.IsNullOrEmpty(content))
156+
{
157+
var richContent = dialog.RichContent != null ? JsonSerializer.Serialize(dialog.RichContent, _options.JsonSerializerOptions) : null;
158+
var secondaryRichContent = dialog.SecondaryRichContent != null ? JsonSerializer.Serialize(dialog.SecondaryRichContent, _options.JsonSerializerOptions) : null;
159+
element = new DialogElement
160+
{
161+
MetaData = meta,
162+
Content = dialog.Content,
163+
SecondaryContent = dialog.SecondaryContent,
164+
RichContent = richContent,
165+
SecondaryRichContent = secondaryRichContent,
166+
Payload = dialog.Payload
167+
};
168+
}
169+
}
170+
171+
return element;
172+
}
151173
}

src/Infrastructure/BotSharp.Core/Routing/RoutingService.InvokeFunction.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ public async Task<bool> InvokeFunction(string name, RoleDialogModel message, Inv
6565
message.StopCompletion = clonedMessage.StopCompletion;
6666
message.RichContent = clonedMessage.RichContent;
6767
message.Data = clonedMessage.Data;
68+
message.AdditionalMessageWrapper = clonedMessage.AdditionalMessageWrapper;
6869
}
6970
catch (JsonException ex)
7071
{

src/Plugins/BotSharp.Plugin.ChartHandler/BotSharp.Plugin.ChartHandler.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,5 @@
3030

3131
<ItemGroup>
3232
<ProjectReference Include="..\..\Infrastructure\BotSharp.Core\BotSharp.Core.csproj" />
33-
<ProjectReference Include="..\BotSharp.Plugin.ChatHub\BotSharp.Plugin.ChatHub.csproj" />
3433
</ItemGroup>
3534
</Project>

src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs

Lines changed: 25 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,12 @@
1-
using BotSharp.Abstraction.Conversations.Dtos;
2-
using BotSharp.Abstraction.Conversations.Enums;
31
using BotSharp.Abstraction.Messaging.Models.RichContent.Template;
4-
using BotSharp.Abstraction.Users;
5-
using BotSharp.Plugin.ChatHub.Helpers;
6-
using System.Runtime.CompilerServices;
72

83
namespace BotSharp.Plugin.ChartHandler.Functions;
94

105
public class PlotChartFn : IFunctionCallback
116
{
127
private readonly IServiceProvider _services;
138
private readonly ILogger<PlotChartFn> _logger;
14-
private readonly BotSharpOptions _options;
9+
private readonly ChartHandlerSettings _settings;
1510

1611
public string Name => "util-chart-plot_chart";
1712
public string Indication => "Plotting chart";
@@ -20,11 +15,11 @@ public class PlotChartFn : IFunctionCallback
2015
public PlotChartFn(
2116
IServiceProvider services,
2217
ILogger<PlotChartFn> logger,
23-
BotSharpOptions options)
18+
ChartHandlerSettings settings)
2419
{
2520
_services = services;
2621
_logger = logger;
27-
_options = options;
22+
_settings = settings;
2823
}
2924

3025
public async Task<bool> Execute(RoleDialogModel message)
@@ -43,7 +38,7 @@ public async Task<bool> Execute(RoleDialogModel message)
4338
Instruction = inst,
4439
LlmConfig = new AgentLlmConfig
4540
{
46-
MaxOutputTokens = 8192
41+
MaxOutputTokens = _settings?.ChartPlot?.MaxOutputTokens ?? 8192
4742
},
4843
TemplateDict = new Dictionary<string, object>
4944
{
@@ -52,7 +47,8 @@ public async Task<bool> Execute(RoleDialogModel message)
5247
}
5348
};
5449

55-
var response = await GetChatCompletion(innerAgent, [
50+
var response = await GetChatCompletion(innerAgent,
51+
[
5652
new RoleDialogModel(AgentRole.User, "Please follow the instruction to generate the javascript code.")
5753
{
5854
CurrentAgentId = message.CurrentAgentId,
@@ -72,59 +68,32 @@ public async Task<bool> Execute(RoleDialogModel message)
7268
}
7369
};
7470

75-
// Send report summary after 1.5 seconds if exists
7671
if (!string.IsNullOrEmpty(obj?.ReportSummary))
7772
{
78-
_ = Task.Run(async () =>
73+
message.AdditionalMessageWrapper = new()
7974
{
80-
var services = _services.CreateScope().ServiceProvider;
81-
await Task.Delay(1500);
82-
await SendDelayedMessage(services, obj.ReportSummary, convService.ConversationId, agent.Id, agent.Name);
83-
});
75+
IntervalMilliSeconds = 1500,
76+
SaveToDb = true,
77+
Messages = new List<RoleDialogModel>
78+
{
79+
new()
80+
{
81+
Role = AgentRole.Assistant,
82+
MessageId = message.MessageId,
83+
CurrentAgentId = message.CurrentAgentId,
84+
Content = obj.ReportSummary,
85+
FunctionName = message.FunctionName,
86+
FunctionArgs = message.FunctionArgs,
87+
CreatedAt = DateTime.UtcNow
88+
}
89+
}
90+
};
8491
}
8592

8693
message.StopCompletion = true;
8794
return true;
8895
}
8996

90-
private async Task SendDelayedMessage(IServiceProvider services, string text, string conversationId, string agentId, string agentName)
91-
{
92-
try
93-
{
94-
var messageId = Guid.NewGuid().ToString();
95-
var messageData = new ChatResponseDto
96-
{
97-
ConversationId = conversationId,
98-
MessageId = messageId,
99-
Text = text,
100-
Sender = new() { FirstName = agentName, LastName = "", Role = AgentRole.Assistant }
101-
};
102-
103-
var dialogModel = new RoleDialogModel(AgentRole.Assistant, text)
104-
{
105-
MessageId = messageId,
106-
CurrentAgentId = agentId,
107-
CreatedAt = DateTime.UtcNow
108-
};
109-
110-
var storage = services.GetService<IConversationStorage>();
111-
storage?.Append(conversationId, dialogModel);
112-
await SendEvent(services, ChatEvent.OnMessageReceivedFromAssistant, conversationId, messageData);
113-
114-
}
115-
catch (Exception ex)
116-
{
117-
_logger.LogError(ex, "Failed to send delayed message");
118-
}
119-
}
120-
121-
private async Task SendEvent<T>(IServiceProvider services, string @event, string conversationId, T data, [CallerMemberName] string callerName = "")
122-
{
123-
var user = services.GetService<IUserIdentity>();
124-
var json = JsonSerializer.Serialize(data, _options.JsonSerializerOptions);
125-
await EventEmitter.SendChatEvent(services, _logger, @event, conversationId, user?.Id, json, nameof(PlotChartFn), callerName);
126-
}
127-
12897
private async Task<string> GetChatCompletion(Agent agent, List<RoleDialogModel> dialogs)
12998
{
13099
try
@@ -169,12 +138,11 @@ private string GetChartPlotInstruction(string agentId)
169138
var model = "gpt-5";
170139

171140
var state = _services.GetRequiredService<IConversationStateService>();
172-
var settings = _services.GetRequiredService<ChartHandlerSettings>();
173141
provider = state.GetState("chart_plot_llm_provider")
174-
.IfNullOrEmptyAs(settings.ChartPlot?.LlmProvider)
142+
.IfNullOrEmptyAs(_settings.ChartPlot?.LlmProvider)
175143
.IfNullOrEmptyAs(provider);
176144
model = state.GetState("chart_plot_llm_model")
177-
.IfNullOrEmptyAs(settings.ChartPlot?.LlmModel)
145+
.IfNullOrEmptyAs(_settings.ChartPlot?.LlmModel)
178146
.IfNullOrEmptyAs(model);
179147

180148
return (provider, model);

src/Plugins/BotSharp.Plugin.ChartHandler/Settings/ChartHandlerSettings.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ public class ChartHandlerSettings
77

88
public class ChartPlotSetting
99
{
10-
public string LlmProvider { get; set; }
11-
public string LlmModel { get; set; }
10+
public string? LlmProvider { get; set; }
11+
public string? LlmModel { get; set; }
12+
public int? MaxOutputTokens { get; set; }
1213
}

0 commit comments

Comments
 (0)