Skip to content

Metadata-driven System One decisions

Include <jevt/system_one.hpp>. C++20 declarations carry the decision ID and docstring, named field descriptions, enum option descriptions, and ordered score rubrics. All fields are evaluated independently against one owned state through one backend::predict_batch call. Laya overrides this with a tensor batch; other backends may use the default sequential compatibility implementation.

enum class Category { billing, technical, sales, spam };
enum class Urgency { low = 0, medium = 1, high = 2 };
constexpr auto categories = jevt::schema<Category, "support.categories">(
jevt::option<Category::billing>("Invoices, payments, or refund requests."),
jevt::option<Category::technical>("Software bugs, crashes, or login failures."),
jevt::option<Category::sales>("Upgrades, enterprise pricing, or new purchases."),
jevt::option<Category::spam>("Unsolicited marketing or automated noise."));
constexpr auto urgency = jevt::schema<Urgency, "support.urgency">(
jevt::option<Urgency::low>("Customer is patient and calm."),
jevt::option<Urgency::medium>("Issue blocks work but has a workaround."),
jevt::option<Urgency::high>("Complete outage or severe frustration."));
constexpr auto ticket = jevt::decision_model<"support.ticket">(
"Evaluate the incoming customer support ticket.",
jevt::choice<"category">("Which team should own this request?", categories, 0.4F),
jevt::noul<"is_urgent">("Does this require attention within one hour?", {0.2F, 0.8F}),
jevt::score<"urgency_score">("Rate the frustration level against the rubric.", urgency),
jevt::probability<"sentiment_probability">("The customer is angry."));
auto runner = jevt::bind_system_one(ticket, inference_backend);
auto response = runner.evaluate(jevt::json_state(R"({
"ticket": "Nobody can log in since this morning.",
"customer": {"plan": "enterprise"},
"routing_policy": "Outages need immediate engineering attention."
})"));
if (!response) { /* inspect response.error_value() */ }
else if (response->abstained()) { /* route to review */ }
else {
const auto category = response->get<"category">().value();
const float impact = response->get<"urgency_score">().score();
const float angry = response->get<"sentiment_probability">().value();
}

Choice has a typed enum value, the complete normalized probabilities(), values() in matching order, legend(), and entropy-based confidence(). The confidence gate abstains below its threshold. A threshold of zero disables abstention. A tied distribution selects the first criterion unless the gate abstains. Fields are identified at compile time with get<"name">().

Score accepts 2–10 distinct, strictly ascending enum levels. score() is the probability-weighted position from 0 to N−1, including fractional values; the enum’s underlying storage values do not change the position scale. value() returns the most probable typed enum level, not a rounded mean. Score also preserves all probabilities, the rubric legend, and confidence.

Noul exposes probability_true() and an optional boolean in selected(). With {0.2F, 0.8F}, probabilities at or below 0.2 are false, at or above 0.8 are true, and the intervening range abstains. Equal thresholds define a single binary threshold, with equality interpreted as true. Noul has no separate confidence statistic. probability<...> explicitly maps a Noul proposition to a floating-point P(true); it does not perform arbitrary float extraction.

value() on an abstained Choice, Score or boolean Noul throws; inspect abstained() or selected() first. Full distributions remain available after abstention. Output vectors must have the right cardinality, finite nonnegative weights and positive mass. An invalid field fails the entire response.

The confidence metric is 1 - H(p)/log(N), bounded to [0,1]. It is 0 for a uniform distribution and 1 for a point mass (also 1 for a one-option Choice). This documents jevtpp’s own metric, not a claim of numerical parity with Jev’s proprietary confidence calculation. Thresholds require domain evaluation.

answers() exposes a typed tuple. map() applies a callable to each answer in declaration order, allowing ordinary C++20 structs without reflection:

struct TicketDecision {
Category category;
bool is_urgent;
Urgency urgency_score;
float sentiment_probability;
};
// Check response and abstentions before this projection.
auto decision = response->map([](https://github.com/wiatrM/jevtpp/blob/main/docs/const auto& category, const auto& urgent,
const auto& score, const auto& probability) {
return TicketDecision{category.value(), urgent.value(), score.value(),
probability.value()};
});

text_state(string) and json_state(string) own their serialization and carry a content kind. JSON state rejects empty/whitespace-only input; syntax validation belongs to the application’s JSON serializer. JSON objects, arrays and scalars can be passed without adding a JSON dependency to jevtpp. The serialized text is passed unchanged to the backend. Metadata and state remain separate.

Use runner.evaluate(record, serializer) where the callable returns a jevt::state_value, or provide to_jevt_state(const Record&) in the record’s namespace for ADL customization. The serializer is invoked exactly once per evaluation, and all questions share its result. runner.evaluate(string_view) is shorthand for text state.

runner.request(state) exposes the owning batch for inspection. Its views() object provides the inference requests; keep both the owner and views alive through backend use. Rebuild views after copying or moving the owner. A field’s question joins the model docstring and that field’s description. Every field has the same input state; no other field’s answer becomes implicit context.

Names and descriptions must be nonempty, field names must be unique, enum options must have one enum type and unique values, Score levels must be ordered and within the 2–10 limit, and probability thresholds must be within [0,1]. Descriptions are string views intended for static string literals.

Use text_metadata(...) or json_metadata(...) with option, choice, score, probability and decision_model. These constructors retain the existing compile-time schema checks. JSON metadata can hold an object, array or scalar; core does not parse it. A remote backend that consumes JSON validates it before sending a request. Existing string overloads retain their behavior.

enum class Route { billing, technical };
constexpr auto routes = jevt::schema<Route, "routes">(
jevt::option<Route::billing>(jevt::json_metadata(R"({"topic":"billing","examples":["refund"]})")),
jevt::option<Route::technical>("Technical support"));
constexpr auto routing = jevt::decision_model<"routing">(
jevt::json_metadata(R"({"role":"support triage"})"),
jevt::choice<"route">("Select the team", routes),
jevt::noul<"escalate">("Escalation needed?",
"The request can be resolved by the support team",
"The request requires a human specialist"));

Noul’s two descriptions are ordered false, true. Each can instead be a content_view from text_metadata or json_metadata; policy thresholds remain an optional final argument. Default Noul descriptions and local tokenization remain unchanged when custom criteria are omitted.

Low-level requests preserve input_kind, instructions and criteria_metadata. When model and field instructions are both text, instructions is the existing model description, two newlines, then the field description. If either is JSON, it is a JSON object with model and field members, preserving each original JSON value or text string. Projections retain model instructions. The owning request copies all metadata and state.

Local backends use the supplied metadata serialization verbatim as text: the model and field descriptions are joined with two newlines, and custom Noul criteria are prefixed with false: and true: . They do not parse, reorder or canonicalize JSON. This makes local rendering deterministic for the supplied bytes while leaving structured values available to remote backends.

Each System One field answer exposes metadata() with provider, model revision, optional token usage, optional provider confidence/score, and attempt count. Provider confidence does not replace confidence() or entropy_confidence(); provider score does not replace the computed Score expectation. A provider’s selected Choice index is retained when it identifies a maximum-probability tie. Classic choose() answers expose this information through metadata().execution; predicate answers expose metadata().

Diagnostic token totals are observed usage, not provider billing totals. System One counts each shared usage report once per evaluation; distinct reports are summed. If a queue combines independent evaluations into one remote provider request, the provider’s aggregate report can be observed by multiple evaluations and counted more than once. The library does not estimate a per-field allocation or retain an unbounded global history of reports to deduplicate billing.

Local ONNX/native adapters report exact per-row input token counts, so their usage remains additive across queued evaluations. Their output token count is zero because inference returns probabilities rather than generated text. Sampling recent traces does not affect these sums; reset() clears them. Unavailable usage remains an empty pointer rather than an invented estimate.

Pass evaluation_options{.deadline = ..., .cancellation = stop_source.get_token()} as the second argument to evaluate(state, options) or request(state, options). Serializer overloads accept it after the serializer. Classic choose() and predicate evaluate() accept it after the optional diagnostic tag, and choose_async() accepts it after the owned input string. Deadlines use std::chrono::steady_clock.

Already expired or cancelled work is rejected before backend execution. Local inference checks controls around execution but cannot interrupt a running ONNX or native GPU call; a late result can return a timeout/cancellation error after that call finishes. Remote and queue adapters apply their documented controls. Errors retain their category and optional HTTP status_code without turning technical failures into abstentions.

This interface follows the Jev primitives and shared-state independent-question model described in the supplied project reference. It does not imply equivalent model quality or calibration.