Universal.Operative.Sdk
1.0.3
See the version list below for details.
dotnet add package Universal.Operative.Sdk --version 1.0.3
NuGet\Install-Package Universal.Operative.Sdk -Version 1.0.3
<PackageReference Include="Universal.Operative.Sdk" Version="1.0.3" />
<PackageVersion Include="Universal.Operative.Sdk" Version="1.0.3" />
<PackageReference Include="Universal.Operative.Sdk" />
paket add Universal.Operative.Sdk --version 1.0.3
#r "nuget: Universal.Operative.Sdk, 1.0.3"
#:package Universal.Operative.Sdk@1.0.3
#addin nuget:?package=Universal.Operative.Sdk&version=1.0.3
#tool nuget:?package=Universal.Operative.Sdk&version=1.0.3
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) before or after a model call — compaction, tool-result pruning, history snipping.OperativerunsPreTransformersbefore each model call andPostTransformersafter the assistant message lands but before tools run.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.
CompactionTransformer fixed to provide proper context to the model after compaction.