V18 / 8d896b8a86e5
ProviderClient.Thinking.cs
Lilith/Core/Providers/ProviderClient.Thinking.cs
Changed in this versionCS
using System.Text.Json;
using Lilith.Core.Thinking;
namespace Lilith.Core.Providers;
public abstract partial class ProviderClient
{
private RequestThinking? _thinking;
public bool ThinkingEnabled=>_thinking is not null;
/// <summary>Host-only planning inspection. It never executes the proposed action or grants approval.</summary>
public async Task<ThinkingChoice> PreviewThinkingAsync(string input,CancellationToken cancellationToken=default)
{
using var operation=EnterOperation();
return await (_thinking??throw new InvalidOperationException("Thinking is not configured.")).Plan(input,cancellationToken);
}
public void ConfigureThinking(string? scenarioRoot=null)
{
using var operation=EnterOperation();
if(_thinking is not null)throw new InvalidOperationException("Thinking is already configured.");
_thinking=new(this,AddonScenarioFiles?.RootDirectory??scenarioRoot);
}
private async Task<string> ThinkReplyAsync(string input,List<string>? images,CancellationToken ct)
{
var plan=await _thinking!.Plan(input,ct);
if(images is {Count:>0})return await StreamReplyCoreAsync(input,images,ct); // Retain existing vision-provider transport after routing.
if(plan.Kind=="scenario")
{
var call=new Lilith.Core.Tools.ToolCall(new("run_scenario",JsonSerializer.SerializeToElement(new{scenario_name=plan.Scenario,inputs=plan.Inputs})),"thinking-"+Guid.NewGuid().ToString("N"));
// Preserve the real user turn; existing host code executes and checks the receipt.
return await StreamReplyCoreAsync(input,null,ct,new(new("assistant","",ToolCalls:[call]),true,null,null),requireSuccess:false);
}
string reply=plan.Kind=="clarify"?plan.Question:await _thinking.Reply(input,ct);
return await StreamReplyCoreAsync(input,null,ct,new(new("assistant",reply),true,null,null));
}
}