Skip to content

Commit 3e7b1f7

Browse files
authored
Merge pull request #1321 from iceljc/features/refine-template-config
Features/refine template config
2 parents 14db7f1 + db1f99d commit 3e7b1f7

18 files changed

Lines changed: 352 additions & 26 deletions

File tree

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ public interface IAgentService
5353
/// <param name="id"></param>
5454
/// <returns>Original agent information</returns>
5555
Task<Agent> GetAgent(string id);
56-
56+
Task<AgentTemplate?> GetAgentTemplateDetail(string agentId, string templateName);
57+
5758
Task<bool> DeleteAgent(string id, AgentDeleteOptions? options = null);
5859
Task UpdateAgent(Agent agent, AgentField updateField);
5960

src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentTemplate.cs

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
namespace BotSharp.Abstraction.Agents.Models;
22

3-
public class AgentTemplate
3+
public class AgentTemplate : AgentTemplateConfig
44
{
5-
public string Name { get; set; }
6-
public string Content { get; set; }
5+
public string Content { get; set; } = string.Empty;
76

87
public AgentTemplate()
98
{
@@ -20,3 +19,23 @@ public override string ToString()
2019
return Name;
2120
}
2221
}
22+
23+
public class AgentTemplateConfig
24+
{
25+
public string Name { get; set; }
26+
27+
/// <summary>
28+
/// Response format: json, xml, markdown, yaml, etc.
29+
/// </summary>
30+
[JsonPropertyName("response_format")]
31+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
32+
public string? ResponseFormat { get; set; }
33+
34+
[JsonPropertyName("llm_config")]
35+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
36+
public AgentTemplateLlmConfig? LlmConfig { get; set; }
37+
}
38+
39+
public class AgentTemplateLlmConfig : LlmConfigBase
40+
{
41+
}

src/Infrastructure/BotSharp.Abstraction/MCP/Models/McpServerConfigModel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public class McpHttpServerConfig : McpHttpServerConfigBase
4040
{
4141
}
4242

43-
public class McpHttpServerConfigBase
43+
public abstract class McpHttpServerConfigBase
4444
{
4545
public string EndPoint { get; set; } = null!;
4646
public TimeSpan ConnectionTimeout { get; init; } = TimeSpan.FromSeconds(30);

src/Infrastructure/BotSharp.Abstraction/Models/LlmConfigBase.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,15 @@ public class LlmConfigBase : LlmProviderModel
55
/// <summary>
66
/// Llm maximum output tokens
77
/// </summary>
8+
[JsonPropertyName("max_output_tokens")]
9+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
810
public int? MaxOutputTokens { get; set; }
911

1012
/// <summary>
11-
/// Llm reasoning effort level
13+
/// Llm reasoning effort level, thinking level
1214
/// </summary>
15+
[JsonPropertyName("reasoning_effort_level")]
16+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
1317
public string? ReasoningEffortLevel { get; set; }
1418
}
1519

@@ -22,4 +26,7 @@ public class LlmProviderModel
2226
[JsonPropertyName("model")]
2327
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
2428
public string? Model { get; set; }
29+
30+
[JsonIgnore]
31+
public bool IsValid => !string.IsNullOrEmpty(Provider) && !string.IsNullOrEmpty(Model);
2532
}

src/Infrastructure/BotSharp.Abstraction/Repositories/IBotSharpRepository.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,8 @@ Task<List<string>> GetAgentResponses(string agentId, string prefix, string inten
8888
=> throw new NotImplementedException();
8989
Task<string> GetAgentTemplate(string agentId, string templateName)
9090
=> throw new NotImplementedException();
91+
Task<AgentTemplate?> GetAgentTemplateDetail(string agentId, string templateName)
92+
=> throw new NotImplementedException();
9193
Task<bool> PatchAgentTemplate(string agentId, AgentTemplate template)
9294
=> throw new NotImplementedException();
9395
Task<bool> UpdateAgentLabels(string agentId, List<string> labels)

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.CreateAgent.cs

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public async Task<Agent> CreateAgent(Agent agent)
3030
var userService = _services.GetRequiredService<IUserService>();
3131
var auth = await userService.GetUserAuthorizations();
3232

33-
await _db.BulkInsertAgents(new List<Agent> { agentRecord });
33+
await _db.BulkInsertAgents([agentRecord]);
3434
if (auth.IsAdmin || auth.Permissions.Contains(UserPermission.CreateAgent))
3535
{
3636
await _db.BulkInsertUserAgents(new List<UserAgent>
@@ -39,7 +39,7 @@ await _db.BulkInsertUserAgents(new List<UserAgent>
3939
{
4040
UserId = user.Id,
4141
AgentId = agentRecord.Id,
42-
Actions = new List<string> { UserAction.Edit, UserAction.Train, UserAction.Evaluate, UserAction.Chat },
42+
Actions = [UserAction.Edit, UserAction.Train, UserAction.Evaluate, UserAction.Chat],
4343
CreatedTime = DateTime.UtcNow,
4444
UpdatedTime = DateTime.UtcNow
4545
}
@@ -98,20 +98,51 @@ private List<AgentTemplate> GetTemplatesFromFile(string fileDir)
9898
var templateDir = Path.Combine(fileDir, "templates");
9999
if (!Directory.Exists(templateDir)) return templates;
100100

101+
// Load template configs
102+
var configs = GetAgentTemplateConfigs(fileDir);
103+
101104
foreach (var file in Directory.GetFiles(templateDir))
102105
{
103106
var extension = Path.GetExtension(file).Substring(1);
104107
if (extension.IsEqualTo(_agentSettings.TemplateFormat))
105108
{
106109
var name = Path.GetFileNameWithoutExtension(file);
107110
var content = File.ReadAllText(file);
108-
templates.Add(new AgentTemplate(name, content));
111+
var template = new AgentTemplate(name, content);
112+
var config = configs.FirstOrDefault(x => x.Name.IsEqualTo(name));
113+
if (config != null)
114+
{
115+
template.ResponseFormat = config.ResponseFormat;
116+
template.LlmConfig = config.LlmConfig;
117+
}
118+
templates.Add(template);
109119
}
110120
}
111-
121+
112122
return templates;
113123
}
114124

125+
private IEnumerable<AgentTemplateConfig> GetAgentTemplateConfigs(string baseDir)
126+
{
127+
var configFile = Path.Combine(baseDir, "template_configs.json");
128+
var configs = new List<AgentTemplateConfig>();
129+
130+
try
131+
{
132+
if (File.Exists(configFile))
133+
{
134+
var configJson = File.ReadAllText(configFile);
135+
configs = JsonSerializer.Deserialize<List<AgentTemplateConfig>>(configJson, _options.JsonSerializerOptions) ?? [];
136+
}
137+
return configs;
138+
}
139+
catch (Exception ex)
140+
{
141+
_logger.LogError(ex, "Error when loading template configs in {configFile}", configFile);
142+
return configs;
143+
}
144+
}
145+
115146
private List<FunctionDef> GetFunctionsFromFile(string fileDir)
116147
{
117148
var functions = new List<FunctionDef>();

src/Infrastructure/BotSharp.Core/Agents/Services/AgentService.GetAgents.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,36 @@ private void AddDefaultInstruction(Agent agent, string instruction)
105105
agent.ChannelInstructions = instructions;
106106
}
107107

108+
#if !DEBUG
109+
[SharpCache(10, perInstanceCache: true)]
110+
#endif
111+
public async Task<AgentTemplate?> GetAgentTemplateDetail(string agentId, string templateName)
112+
{
113+
var template = await _db.GetAgentTemplateDetail(agentId, templateName);
114+
if (template == null)
115+
{
116+
return template;
117+
}
118+
119+
if (template.LlmConfig == null || !template.LlmConfig.IsValid)
120+
{
121+
var agent = await _db.GetAgent(agentId);
122+
if (!string.IsNullOrEmpty(agent?.LlmConfig?.Provider)
123+
&& !string.IsNullOrEmpty(agent?.LlmConfig?.Model))
124+
{
125+
template.LlmConfig = new AgentTemplateLlmConfig
126+
{
127+
Provider = agent.LlmConfig.Provider,
128+
Model = agent.LlmConfig.Model,
129+
MaxOutputTokens = agent.LlmConfig.MaxOutputTokens,
130+
ReasoningEffortLevel = agent.LlmConfig.ReasoningEffortLevel
131+
};
132+
}
133+
}
134+
135+
return template.DeepClone();
136+
}
137+
108138
public async Task InheritAgent(Agent agent)
109139
{
110140
if (string.IsNullOrWhiteSpace(agent?.InheritAgentId))

src/Infrastructure/BotSharp.Core/BotSharp.Core.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
<None Remove="data\agents\01dcc3e5-0af7-49e6-ad7a-a760bd12dc4b\functions\human_intervention_needed.json" />
6969

7070
<None Remove="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\agent.json" />
71+
<None Remove="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\template_configs.json" />
7172
<None Remove="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\instructions\instruction.liquid" />
7273
<None Remove="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\functions\get_weather.json" />
7374
<None Remove="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\functions\get_fun_events.json" />
@@ -131,6 +132,9 @@
131132
<Content Include="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\agent.json">
132133
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
133134
</Content>
135+
<Content Include="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\template_configs.json">
136+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
137+
</Content>
134138
<Content Include="data\agents\01e2fc5c-2c89-4ec7-8470-7688608b496c\instructions\instruction.liquid">
135139
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
136140
</Content>

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Execute.cs

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -242,12 +242,31 @@ private async Task<InstructResult> RunLlm(
242242
var result = string.Empty;
243243

244244
// Render prompt
245-
var prompt = string.IsNullOrEmpty(templateName) ?
246-
agentService.RenderInstruction(agent) :
247-
agentService.RenderTemplate(agent, templateName);
245+
var prompt = string.Empty;
246+
var llmConfig = agent.LlmConfig;
247+
248+
if (!string.IsNullOrEmpty(templateName))
249+
{
250+
prompt = agentService.RenderTemplate(agent, templateName);
251+
var templateLlmConfig = agent.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(templateName))?.LlmConfig;
252+
if (templateLlmConfig?.IsValid == true)
253+
{
254+
llmConfig = new AgentLlmConfig
255+
{
256+
Provider = templateLlmConfig.Provider,
257+
Model = templateLlmConfig.Model,
258+
MaxOutputTokens = templateLlmConfig.MaxOutputTokens,
259+
ReasoningEffortLevel = templateLlmConfig.ReasoningEffortLevel
260+
};
261+
}
262+
}
263+
else
264+
{
265+
prompt = agentService.RenderInstruction(agent);
266+
}
248267

249268
var completer = CompletionProvider.GetCompletion(_services,
250-
agentConfig: agent.LlmConfig);
269+
agentConfig: llmConfig);
251270

252271
if (completer is ITextCompletion textCompleter)
253272
{
@@ -292,7 +311,7 @@ private async Task<InstructResult> RunLlm(
292311
}
293312
else
294313
{
295-
result = await GetChatCompletion(chatCompleter, agent, instruction, prompt, message.MessageId, files);
314+
result = await GetChatCompletion(chatCompleter, agent, instruction, prompt, message.MessageId, llmConfig, files);
296315
}
297316

298317
// Repair JSON format if needed
@@ -343,14 +362,15 @@ private async Task<string> GetChatCompletion(
343362
string instruction,
344363
string text,
345364
string messageId,
365+
AgentLlmConfig? llmConfig = null,
346366
IEnumerable<InstructFileModel>? files = null)
347367
{
348368
var result = await chatCompleter.GetChatCompletions(new Agent
349369
{
350370
Id = agent.Id,
351371
Name = agent.Name,
352372
Instruction = instruction,
353-
LlmConfig = agent.LlmConfig
373+
LlmConfig = llmConfig ?? agent.LlmConfig
354374
}, new List<RoleDialogModel>
355375
{
356376
new RoleDialogModel(AgentRole.User, text)

src/Infrastructure/BotSharp.Core/Instructs/Services/InstructService.Instruct.cs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public partial class InstructService
5656
private async Task<Agent> BuildInnerAgent(InstructOptions? options)
5757
{
5858
Agent? agent = null;
59+
AgentLlmConfig? llmConfig = null;
5960
string? instruction = null;
6061

6162
if (!string.IsNullOrWhiteSpace(options?.AgentId))
@@ -65,8 +66,19 @@ private async Task<Agent> BuildInnerAgent(InstructOptions? options)
6566

6667
if (!string.IsNullOrWhiteSpace(options?.TemplateName))
6768
{
68-
var template = agent?.Templates?.FirstOrDefault(x => x.Name == options.TemplateName)?.Content ?? string.Empty;
69-
instruction = BuildInstruction(template, options?.Data ?? []);
69+
var template = agent?.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(options.TemplateName));
70+
instruction = BuildInstruction(template?.Content ?? string.Empty, options?.Data ?? []);
71+
var templateLlmConfig = template?.LlmConfig;
72+
if (templateLlmConfig?.IsValid == true)
73+
{
74+
llmConfig = new AgentLlmConfig
75+
{
76+
Provider = templateLlmConfig.Provider,
77+
Model = templateLlmConfig.Model,
78+
MaxOutputTokens = templateLlmConfig.MaxOutputTokens,
79+
ReasoningEffortLevel = templateLlmConfig.ReasoningEffortLevel
80+
};
81+
}
7082
}
7183
}
7284

@@ -75,7 +87,7 @@ private async Task<Agent> BuildInnerAgent(InstructOptions? options)
7587
Id = agent?.Id ?? Guid.Empty.ToString(),
7688
Name = agent?.Name ?? "Unknown",
7789
Instruction = instruction,
78-
LlmConfig = agent?.LlmConfig ?? new()
90+
LlmConfig = llmConfig ?? agent?.LlmConfig ?? new()
7991
};
8092
}
8193

0 commit comments

Comments
 (0)