Universal.Operative.Sdk
1.3.0
dotnet add package Universal.Operative.Sdk --version 1.3.0
NuGet\Install-Package Universal.Operative.Sdk -Version 1.3.0
<PackageReference Include="Universal.Operative.Sdk" Version="1.3.0" />
<PackageVersion Include="Universal.Operative.Sdk" Version="1.3.0" />
<PackageReference Include="Universal.Operative.Sdk" />
paket add Universal.Operative.Sdk --version 1.3.0
#r "nuget: Universal.Operative.Sdk, 1.3.0"
#:package Universal.Operative.Sdk@1.3.0
#addin nuget:?package=Universal.Operative.Sdk&version=1.3.0
#tool nuget:?package=Universal.Operative.Sdk&version=1.3.0
About
Universal.Operative.Sdk is a provider-neutral harness for running agentic LLM turns: stream a model response, dispatch any tool calls it produces, feed the results back, and loop until the model stops. It defines the domain model (Conversation, Message, content Blocks), the extension seams (IModel, ITool, ITransformer), and a ready-to-use Operative implementation — all independent of any specific provider. Pair it with Universal.Operative.Sdk.Anthropic or Universal.Operative.Sdk.OpenAI for a concrete IModel.
How to Use
Installation
dotnet add package Universal.Operative.Sdk
Quick start
using Universal.Operative.Sdk;
IModel model = /* an IModel, e.g. from Universal.Operative.Sdk.Anthropic or .OpenAI */;
IOperative operative = OperativeBuilder.FromDefaults(model)
.AddBaselineTools() // Read, Write, Edit, Glob, Grep, a shell, TodoWrite, NotebookEdit
.Build();
var conversation = new Conversation
{
System = new List<Block> { new TextBlock("You are a helpful assistant.") },
Messages = { new Message { Role = MessageRoles.User, Content = { new TextBlock("List the files in this repo.") } } },
};
await foreach (OperativeEvent e in operative.ExecuteAsync(conversation))
{
switch (e)
{
case ModelStreamEvent s: /* incremental provider events, for live UI */ break;
case AssistantMessageEvent a: Console.WriteLine(a.Message.Content); break;
case ToolCallStartedEvent t: Console.WriteLine($"Running {t.Call.Name}..."); break;
case TurnCompletedEvent done: Console.WriteLine($"Done: {done.Reason}"); break;
}
}
// conversation.Messages now holds the full transcript, including tool calls and results.
OperativeBuilder.FromDefaults wires a sensible pre-transformer stack (boundary-aware history windowing, tool-result budgeting, history snipping, auto-compaction) so long-running sessions don't blow the model's context window. Use new OperativeBuilder() instead if you want to opt into transformers individually.
Core concepts
Conversation/Message/Block— the provider-neutral transcript.Message.Contentis a list ofBlocks (TextBlock,ToolCallBlock,ToolResultBlock, …); round-tripping aConversationthrough JSON is enough to resume it later.IModel— the seam to a concrete provider. ImplementStreamAsyncto translate aConversationinto a provider request and its response stream intoStreamEvents;CompleteAsyncis provided for free as a fold over the stream.ITool/Tool<TArgs>— a callable the model can invoke. SubclassTool<TArgs>with a typed argument record and a hand-authored JSON-SchemaDefinition; the base class handles (de)serialization and turns malformed arguments into a model-readable error instead of an exception. SeeTools/BaselineTools.csandTools/FileSystem/GlobTool.csfor worked examples.ITransformer— rewrites the conversation (or sharedIContext) around a model call — compaction, tool-result pruning, history snipping.Operativeruns four stages:BeforeTurnTransformersonce before the turn's first iteration,BeforeIterationTransformersbefore each model call,AfterIterationTransformersafter the assistant message lands but before tools run, andAfterTurnTransformersonce after the loop ends. (PreTransformers/PostTransformersstill work as obsolete aliases for the before/after-iteration stages.)Operative/IOperative— drives the loop described above. Every step is aprotected virtualmethod, so subclass to override one piece (tool-result formatting, dispatch policy, timeouts) without reimplementing the whole turn.OperativeEvent— the single observable channel for a turn: model stream events, the settled assistant message, tool start/complete/error events, the synthesized tool-result message, transformer applications, and a terminalTurnCompletedEvent.
Writing a custom tool
using System.Text.Json;
using System.Text.Json.Serialization;
using Universal.Operative.Sdk.Tools;
public sealed class WeatherTool : Tool<WeatherTool.Args>
{
public override string Name => "get_weather";
public override bool IsConcurrencySafe => true; // read-only calls may run in parallel with others
public override ToolDefinition Definition { get; } = new()
{
Name = "get_weather",
Description = "Get the current weather for a city.",
Parameters = JsonDocument.Parse("""
{ "type": "object", "properties": { "city": { "type": "string" } }, "required": ["city"] }
""").RootElement.Clone(),
};
public sealed record Args
{
[JsonPropertyName("city")] public string City { get; init; } = string.Empty;
}
protected override async Task<ToolResult> ExecuteAsync(Args args, CancellationToken cancellationToken)
=> ToolResult.Json(new { city = args.City, tempF = 72 });
}
Register it with builder.AddTool(new WeatherTool()) and the harness auto-publishes its Definition on outbound messages and dispatches calls to it by name.
License
Free for noncommercial use under the Universal.Operative Noncommercial License. Source is closed; commercial use requires a separate license from Andrew Ong.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Universal.Operative.Sdk:
| Package | Downloads |
|---|---|
|
Universal.Operative.Sdk.Anthropic
Anthropic extensions for the Universal.Operative.Sdk. |
|
|
Universal.Operative.Sdk.OpenAI
OpenAI extensions for Universal.Operative.Sdk. |
GitHub repositories
This package is not used by any popular GitHub repositories.
Added SystemPromptTransformer for refreshing the system prompt from a caller-supplied source (with a SystemPromptTransformer.FromFile convenience factory). Added OperativeBuilder.ReplaceTransformer/RemoveTransformer(s)/AddTransformerBefore/AddTransformerAfter for editing the transformer pipeline by type instead of hand-rolling find-remove-add against the raw lists. Added FileSystemConversationPersistenceTransformer (wired via OperativeBuilder.WithFileSystemConversationPersistence) for durable, rotating, size-capped conversation snapshots to local disk, with LoadLatestConversationAsync for restoring the latest one at host startup.