Files

94 lines
3.7 KiB
C#

// Reference headless host: prompt in, one JSON line per session event out.
// This is not a UI; it shows the minimum a host needs and doubles as a smoke test.
//
// dotnet run --project samples/Volcanic.AgenticCore.Sample.Headless -- [--faux] [--model provider/id] [--cwd path] [--quiet] "prompt"
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Volcanic.AgenticCore.Abstractions.Events;
using Volcanic.AgenticCore.Abstractions.Json;
using Volcanic.AgenticCore.Abstractions.Messages;
using Volcanic.AgenticCore.Abstractions.Providers;
using Volcanic.AgenticCore.Extensions.Anthropic;
using Volcanic.AgenticCore.Extensions.ClaudeCode;
using Volcanic.AgenticCore.Extensions.OpenAI;
using Volcanic.AgenticCore.Features.Agent;
using Volcanic.AgenticCore.Hosting;
using Volcanic.AgenticCore.Testing;
string? model = null;
string? cwd = null;
var faux = false;
var quiet = false;
var prompt = new List<string>();
for (var i = 0; i < args.Length; i++)
{
switch (args[i])
{
case "--model": model = args[++i]; break;
case "--cwd": cwd = args[++i]; break;
case "--faux": faux = true; break;
case "--quiet": quiet = true; break;
default: prompt.Add(args[i]); break;
}
}
if (prompt.Count == 0)
{
Console.Error.WriteLine("usage: [--faux] [--model provider/id] [--cwd path] [--quiet] \"prompt\"");
return 2;
}
var fauxProvider = new FauxProvider();
if (faux)
{
fauxProvider.Enqueue(
FauxTurn.Say("Let me look around.").WithTool("ls", new { }),
FauxTurn.Say("Done. That is what the working directory contains."));
model ??= FauxProvider.ModelKey;
}
var services = new ServiceCollection();
services.AddLogging(b => b.AddSimpleConsole(o => o.SingleLine = true).SetMinimumLevel(quiet ? LogLevel.Warning : LogLevel.Information));
services.AddAgenticCore(o =>
{
o.DefaultModel = model ?? "anthropic/claude-opus-5";
o.AddExtension<AnthropicProviderExtension>();
o.AddExtension<OpenAIProviderExtension>();
o.AddExtension<ClaudeCodeProviderExtension>();
if (faux) o.AddExtension(fauxProvider.Extension);
});
await using var provider = services.BuildServiceProvider();
var factory = provider.GetRequiredService<IAgentSessionFactory>();
await using var session = await factory.CreateAsync(new AgentSessionOptions { Cwd = cwd ?? Directory.GetCurrentDirectory() });
if (session.Model is null)
{
Console.Error.WriteLine($"Model '{model}' is not registered. Known models: {string.Join(", ", session.Models.ListModels().Select(m => m.Key))}");
return 1;
}
if (!session.Models.IsAvailable(session.Model))
{
Console.Error.WriteLine($"Model '{session.Model.Key}' has no credentials. Set {session.Model.Provider.ToUpperInvariant().Replace('-', '_')}_API_KEY or add it to auth.json.");
return 1;
}
foreach (var error in session.ExtensionErrors) Console.Error.WriteLine($"extension error: {error.Source}: {error.Error}");
var stdout = Console.Out;
session.Subscribe(e =>
{
// Deltas are noisy; in quiet mode print only the text as it streams.
if (quiet)
{
if (e is AgentSessionEvent.MessageUpdate { AssistantMessageEvent: AssistantMessageEvent.TextDelta d }) stdout.Write(d.Delta);
else if (e is AgentSessionEvent.ToolExecutionStart s) stdout.WriteLine($"\n[tool {s.ToolName} {s.Args.GetRawText()}]");
else if (e is AgentSessionEvent.MessageEnd { Message: AssistantMessage { StopReason: StopReason.Error } a }) stdout.WriteLine($"\n[error] {a.ErrorMessage}");
else if (e is AgentSessionEvent.AgentSettled) stdout.WriteLine();
return;
}
stdout.WriteLine(VolcanicJson.Serialize(e));
});
await session.PromptAsync(string.Join(' ', prompt));
return 0;