SystemOneDotNet 1.0.0
See the version list below for details.
dotnet add package SystemOneDotNet --version 1.0.0
NuGet\Install-Package SystemOneDotNet -Version 1.0.0
<PackageReference Include="SystemOneDotNet" Version="1.0.0" />
<PackageVersion Include="SystemOneDotNet" Version="1.0.0" />
<PackageReference Include="SystemOneDotNet" />
paket add SystemOneDotNet --version 1.0.0
#r "nuget: SystemOneDotNet, 1.0.0"
#:package SystemOneDotNet@1.0.0
#addin nuget:?package=SystemOneDotNet&version=1.0.0
#tool nuget:?package=SystemOneDotNet&version=1.0.0
SystemOneDotNet
A .NET Standard 2.0 client library for the TypeSafe System One API. System One is the class of models that Jev belongs to; the default model alias is jev-latest.
SystemOneDotNet wraps the POST https://api.typesafe.ai/v1/systemone endpoint with asynchronous,
cancellable calls, strongly typed questions and answers, and local validation of choice limits.
Install
dotnet add package SystemOneDotNet
Or reference the project directly:
<ProjectReference Include="path/to/src/SystemOneDotNet/SystemOneDotNet.csproj" />
Quick start
using SystemOneDotNet;
using SystemOneDotNet.Answers;
using ISystemOneClient systemOne = SystemOneClient.Create(apiKey);
string ticket = "Hi, I've been trying to connect my Stripe account for 3 days and it keeps failing. I'm losing sales. Please help ASAP.";
// Choose one option.
ChoiceAnswer<string> department = await systemOne.ChoiceAsync(
ticket,
"Which team should handle this?",
new[] { "billing", "technical", "sales" });
// Rate the state along an ordered scale.
ScoreAnswer frustration = await systemOne.ScoreAsync(
ticket,
"How frustrated is the customer?",
new[] { "Calm", "Frustrated", "Very angry" });
// Ask a yes/no question.
NoulAnswer urgent = await systemOne.NoulAsync(
ticket,
"Does this message convey urgency?");
Console.WriteLine(department.Choice); // "billing"
Console.WriteLine(frustration.Score); // 1.035
Console.WriteLine(urgent.Noul); // 0.999
Every method accepts an optional CancellationToken:
var answer = await systemOne.ChoiceAsync(ticket, "Which team?", teams, cancellationToken: ct);
Reusable questions and batches
Questions are created with the Question factory, are reusable, carry no response state, and can be
sent together in one request.
using SystemOneDotNet.Questions;
IChoiceQuestion<Team> department = Question.Choice(
"department",
"Which team should handle this?", teams);
INoulQuestion urgent = Question.Noul(
"is_urgent",
"Does this message convey urgency?");
IScoreQuestion frustration = Question.Score(
"frustration",
"How frustrated is the customer?",
new[] { "Calm", "Frustrated", "Very Angry" });
ISystemOneResult result = await systemOne.Query(ticket)
.Question(department)
.Question(urgent)
.Score(frustration)
.SendAsync(ct);
Team selected = result.Get(department).Choice; // the original Team instance
double confidence = result.Get(department).Confidence;
double score = result.Get(frustration).Score;
double probability = result.Get(urgent).Noul;
// Detailed single-question call:
ChoiceAnswer<Team> answer = await systemOne.AskAsync(ticket, department, ct);
result.Model and result.Usage (input and output tokens) are available for every batch.
Answers
ChoiceAnswer<T>.Choiceis the original selected value;.Optionsis an ordered list ofChoiceProbability<T>pairs with the original values and their probabilities;.Confidenceis the model's confidence.ScoreAnswer.Scoreis the probability-weighted value,.Legendis the ordered level list,.Probabilitiesis populated when the API supplies the distribution, and.Confidenceis the model's confidence.NoulAnswer.Noulis the probability that the answer is yes, from 0 to 1. There is no automatic boolean conversion.
Values are mapped back to the original objects, so Question.Choice<Team> returns the exact Team
instance that was supplied, without requiring equality or dictionary-compatible keys.
Answers live in SystemOneDotNet.Answers and are plain records with public constructors, so a test double
for ISystemOneClient can return new NoulAnswer(0.9) directly.
Options
var options = new SystemOneOptions
{
Endpoint = "https://api.typesafe.ai/v1/systemone", // default
Model = "jev-latest", // default
MaxChoiceProperties = 20, // default
};
using ISystemOneClient systemOne = SystemOneClient.Create(apiKey, options, httpClient);
EndpointandModeloverride the API target.MaxChoicePropertieslimits how many serialized properties a single choice option may contain.SystemOneOptionsis immutable: the values are validated once when the client is constructed. Usewithto derive a modified copy, for exampleoptions with { MaxChoiceProperties = 10 }.- A supplied
HttpClientis reused and never disposed by the client; when none is supplied, the client owns and disposes its internalHttpClient.
Choice options and property limits
Generic choice options are materialized once and each value is sent as its JSON description. Strings stay strings, enums are serialized by name, and POCOs are sent as structured objects:
var teams = new[]
{
new Team { Name = "Billing", Email = "billing@example.com" },
new Team { Name = "Technical", Email = "technical@example.com" },
};
var answer = await systemOne.ChoiceAsync(ticket, "Which team should handle this?", teams);
The API receives the options as a criteria map with deterministic ids:
{
"type": "choice",
"instructions": "Which team should handle this?",
"criteria": {
"0": { "Name": "Billing", "Email": "billing@example.com" },
"1": { "Name": "Technical", "Email": "technical@example.com" }
}
}
MaxChoiceProperties counts the properties in each option's serialized object tree, including
nested properties, dictionary entries, and properties inside array elements. JSON object/array
values (JsonNode, JsonElement) are counted exactly like their serialized form. Members marked
with [JsonIgnore] are not counted. The limit defaults to 20 and is validated before any HTTP
request is sent; options are never truncated. An oversized option produces an actionable error:
Choice option 2 contains 54 serialized properties; the limit is 20.
Use a dedicated smaller POCO or increase SystemOneOptions.MaxChoiceProperties
when creating the client.
Named string options are also supported for the criteria dictionary from the TypeSafe quickstart:
INamedChoiceQuestion department = Question.NamedChoice("department", "Which team should handle this?",
new Dictionary<string, string?>
{
["billing"] = "Payment or subscription issues",
["technical"] = "Bugs or integration problems",
["sales"] = null,
});
var result = await systemOne.Query(ticket).Question(department).SendAsync();
string selected = result.Get(department).Choice;
Error handling
All exceptions live in SystemOneDotNet.Exceptions and derive from SystemOneException.
SystemOneValidationException— local validation failed and no request was sent: missing or duplicate question ids, empty batches, fewer than two score levels, more than 255 choice options, null or cyclic options, an exceeded property limit, or a question that was not created by theQuestionfactory.SystemOneApiException— the API returned an unsuccessful status code.StatusCodeandResponseBodyare exposed.SystemOneProtocolException— the API returned a successful response that is malformed, missing an answer, has a mismatched answer type, or contains an unknown option id.OperationCanceledException— cancellation was requested. The originalCancellationTokenis propagated unchanged.
The client performs no automatic retries, caching, or blocking calls. A state that cannot be
serialized to JSON (for example a cyclic object graph) is reported locally as
SystemOneValidationException before any request is sent. To control timeouts, inject an HttpClient
configured with your own Timeout.
Runnable sample
samples/SystemOneDotNet.Sample is a console app that walks through
the library against the live API: direct calls, structured options, batches, named options,
validation, and cancellation. It requires an API key:
export SYSTEMONE_API_KEY="your-key"
dotnet run --project samples/SystemOneDotNet.Sample # every scenario
dotnet run --project samples/SystemOneDotNet.Sample -- batch # one scenario
Set SYSTEMONE_MODEL to override the default model, or pass --help to list the scenarios.
Conventions and scope
SystemOneDotNet is a standalone client library, not a hosted service, so a few deliberate choices differ
from service-oriented .NET conventions:
- It targets
netstandard2.0so older runtimes can consume it.src/SystemOneDotNet/Internal/IsExternalInit.cssupplies the marker type that C# records andinitaccessors require on that target. - The public surface is interfaces and records only.
ISystemOneClient,ISystemOneQuery,ISystemOneResult, and theIQuestionfamily are interfaces with internal implementations; answers, token usage, andSystemOneOptionsare immutable records. The two static factories,SystemOneClient.CreateandQuestion, are the only entry points into the internals, and the exception hierarchy is the only other set of public classes. - Namespaces group the surface by role:
SystemOneDotNetholds the client, batch, result, and options;SystemOneDotNet.Questionsthe question interfaces and factory;SystemOneDotNet.Answersthe answer records;SystemOneDotNet.Exceptionsthe exception hierarchy. Everything underSystemOneDotNet.Internalis an implementation detail. - Callers depend on
ISystemOneClientand can substitute it in tests. There is no DI container, hosted service, orILoggerdependency.HttpClientis supplied throughSystemOneClient.Createinstead ofIHttpClientFactory, and the client disposes only the instance it created. Failures surface as theSystemOneExceptionhierarchy and callers decide how to log them. - Tests use xUnit, AwesomeAssertions, and NSubstitute.
Building and verifying
dotnet build -c Release
dotnet test
dotnet run --project tests/SystemOneDotNet.Verification
dotnet pack src/SystemOneDotNet -c Release
tests/SystemOneDotNet.Testscontains the unit tests, written with xUnit, AwesomeAssertions, and NSubstitute.tests/SystemOneDotNet.Verificationis a small runnable verification program that exercises the library through a fakeHttpMessageHandler, without a test-framework dependency.
License
MIT. See LICENSE.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- System.Text.Json (>= 8.0.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.