Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ public interface IInstructService
/// <param name="files"></param>
/// <param name="codeOptions"></param>
/// <param name="fileOptions"></param>
/// <param name="renderData">Data used to render the instruction or template. When null, the conversation states are used.</param>
/// <returns></returns>
Task<InstructResult> Execute(string agentId, RoleDialogModel message,
string? instruction = null, string? templateName = null,
IEnumerable<InstructFileModel>? files = null,
CodeInstructOptions? codeOptions = null,
FileInstructOptions? fileOptions = null,
ResponseFormatType? responseFormat = null);
ResponseFormatType? responseFormat = null,
IDictionary<string, object>? renderData = null);

/// <summary>
/// A generic way to execute completion by using specified instruction or template
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@ namespace BotSharp.Abstraction.Instructs.Options;

public class CodeInstructOptions
{
/// <summary>
/// Skip the code execution and go straight to the llm completion
/// </summary>
[JsonPropertyName("disabled")]
public bool Disabled { get; set; }

/// <summary>
/// Code processor provider
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
using BotSharp.Abstraction.Templating;
using BotSharp.Core.Infrastructures;
using BotSharp.Abstraction.Instructs;
using BotSharp.Abstraction.Instructs.Options;

namespace BotSharp.Core.Rules.Criteria.Llm;

/// <summary>
/// Evaluates rule trigger criteria by asking an LLM whether the request meets a
/// natural-language condition. Renders the "criteria_check" template (which instructs
/// the model to answer "1" for met / "0" for not met) as the system prompt and calls
/// the chat completion provider directly.
/// natural-language condition. Runs the "criteria_check" template (which instructs
/// the model to answer "1" for met / "0" for not met) as the system prompt through the
/// instruct service, so the call is captured in the instruction log.
///
/// Note: LLM evaluation is non-deterministic and network-dependent. This evaluator is the
/// last resort the rule engine falls back to, so it never returns null: it fails closed
Expand Down Expand Up @@ -52,43 +52,28 @@ public LlmCriteriaEvaluator(
return true;
}

// Render the template as the system instruction, exposing the request states.
var render = _services.GetRequiredService<ITemplateRender>();
// The criteria template is rendered off the request states, which are not yet
// in the conversation state at evaluation time, so pass them as the render data.
var template = innerAgent.Templates.FirstOrDefault(x => x.Name.IsEqualTo(templateName));
if (template == null || string.IsNullOrWhiteSpace(template.Content))
{
_logger.LogWarning($"Unable to find agent template for {msg}");
return true;
}

var instruction = render.Render(template.Content, BuildRenderData(context));

// Prefer the template's own LLM config when it is fully specified.
var llmConfig = innerAgent.LlmConfig;
if (template.LlmConfig?.IsValid == true)
{
llmConfig = new AgentLlmConfig(template.LlmConfig);
}

var completer = CompletionProvider.GetChatCompletion(_services, agentConfig: llmConfig);
if (completer == null)
{
_logger.LogWarning($"Unable to resolve chat completion provider for {msg}");
return false;
}

var response = await completer.GetChatCompletions(new Agent
{
Id = innerAgent.Id,
Name = innerAgent.Name,
Instruction = instruction,
LlmConfig = llmConfig
}, new List<RoleDialogModel>
{
new RoleDialogModel(AgentRole.User, input)
});

var answer = response?.Content?.Trim() ?? string.Empty;
// "#TEMPLATE#" makes the instruct service use the rendered template as the system
// instruction and the input as the user message.
var instructService = _services.GetRequiredService<IInstructService>();
var response = await instructService.Execute(
agentId,
new RoleDialogModel(AgentRole.User, input),
instruction: "#TEMPLATE#",
templateName: templateName,
// The rule engine already gave the code evaluator its turn; keep this one llm-only.
codeOptions: new CodeInstructOptions { Disabled = true },
renderData: BuildRenderData(context));

var answer = response?.Text?.Trim() ?? string.Empty;
if (string.IsNullOrEmpty(answer))
{
_logger.LogWarning($"Empty llm response for {msg}");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ public async Task<InstructResult> Execute(
IEnumerable<InstructFileModel>? files = null,
CodeInstructOptions? codeOptions = null,
FileInstructOptions? fileOptions = null,
ResponseFormatType? responseFormat = null)
ResponseFormatType? responseFormat = null,
IDictionary<string, object>? renderData = null)
{
var agentService = _services.GetRequiredService<IAgentService>();
var agent = await agentService.LoadAgent(agentId);
Expand Down Expand Up @@ -55,7 +56,7 @@ public async Task<InstructResult> Execute(
return codeResponse;
}

response = await RunLlm(agent, message, instruction, templateName, files, fileOptions, responseFormat);
response = await RunLlm(agent, message, instruction, templateName, files, fileOptions, responseFormat, renderData);
return response;
}

Expand All @@ -80,6 +81,11 @@ public async Task<InstructResult> Execute(
return instructResult;
}

if (codeOptions?.Disabled == true)
{
return instructResult;
}

var agentService = _services.GetRequiredService<IAgentService>();
var state = _services.GetRequiredService<IConversationStateService>();
var codingSettings = _services.GetRequiredService<CodingSettings>();
Expand Down Expand Up @@ -209,7 +215,8 @@ private async Task<InstructResult> RunLlm(
string? templateName,
IEnumerable<InstructFileModel>? files = null,
FileInstructOptions? fileOptions = null,
ResponseFormatType? responseFormat = null)
ResponseFormatType? responseFormat = null,
IDictionary<string, object>? renderData = null)
{
var agentService = _services.GetRequiredService<IAgentService>();
var state = _services.GetRequiredService<IConversationStateService>();
Expand Down Expand Up @@ -247,7 +254,7 @@ private async Task<InstructResult> RunLlm(

if (!string.IsNullOrEmpty(templateName))
{
prompt = agentService.RenderTemplate(agent, templateName);
prompt = agentService.RenderTemplate(agent, templateName, renderData);
var templateLlmConfig = agent.Templates?.FirstOrDefault(x => x.Name.IsEqualTo(templateName))?.LlmConfig;
if (templateLlmConfig?.IsValid == true)
{
Expand All @@ -256,7 +263,7 @@ private async Task<InstructResult> RunLlm(
}
else
{
prompt = agentService.RenderInstruction(agent);
prompt = agentService.RenderInstruction(agent, renderData);
}

var completer = CompletionProvider.GetCompletion(_services,
Expand Down
Loading