forked from admin/VolcanicAgenticCore
94 lines
3.7 KiB
C#
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;
|