10struct ParseFailure : std::runtime_error {
11 using std::runtime_error::runtime_error;
16 if (!result)
throw ParseFailure(
"Expected object");
17 for (
const auto& [
key,
value] : *result) {
21 if (!known)
throw ParseFailure(
"Unknown field: " +
key);
26 const auto it = o.find(
key);
27 if (it == o.end())
throw ParseFailure(std::string(
"Missing field: ") +
key);
30std::string
string(
const Value&
v) {
31 const auto*
value =
v.getIf<std::string>();
32 if (!
value)
throw ParseFailure(
"Expected string");
37 if (
const auto*
n =
v.getIf<
double>())
39 else if (
const auto*
n =
v.getIf<std::int64_t>())
42 throw ParseFailure(
"Expected number");
43 if (!std::isfinite(result))
throw ParseFailure(
"Nonfinite number");
46std::uint64_t unsignedInteger(
const Value&
v) {
47 if (
const auto*
n =
v.getIf<std::int64_t>()) {
48 if (*
n < 0)
throw ParseFailure(
"Expected unsigned integer");
49 return std::uint64_t(*
n);
52 std::uint64_t result = 0;
53 const auto parsed = std::from_chars(
text.data(),
text.data() +
text.size(), result);
54 if (parsed.ec != std::errc{} || parsed.ptr !=
text.data() +
text.size())
throw ParseFailure(
"Invalid integer");
57std::uint32_t u32(
const Value&
v) {
58 const auto n = unsignedInteger(
v);
59 if (
n > std::numeric_limits<std::uint32_t>::max())
throw ParseFailure(
"Integer overflow");
60 return std::uint32_t(
n);
64 if (!
a ||
a->size() >
limit)
throw ParseFailure(
"Invalid or oversized array");
67Observation observation(
const Value&
v) {
68 const auto& o =
object(
v, {
"features",
"legalActions",
"coverage",
"reward",
"outcome",
"finding"});
70 for (
const auto&
n : array(
required(o,
"features"), 1024)) {
72 if (std::abs(
value) > 1e6)
throw ParseFailure(
"Feature outside supported range");
73 result.features.push_back(
float(
value));
75 for (
const auto&
n : array(
required(o,
"legalActions"), 1024)) result.legalActions.push_back(u32(
n));
76 if (
auto it = o.find(
"coverage"); it != o.end())
78 if (
auto it = o.find(
"reward"); it != o.end()) result.reward =
number(it->second);
79 if (
auto it = o.find(
"finding"); it != o.end()) result.finding =
string(it->second);
80 if (
auto it = o.find(
"outcome"); it != o.end()) {
82 if (
name ==
"running")
84 else if (
name ==
"success")
86 else if (
name ==
"failure")
89 throw ParseFailure(
"Unknown outcome");
93Value observationValue(
const Observation& o) {
95 for (
auto n : o.features) features.emplace_back(
n);
96 for (
auto n : o.legalActions)
actions.emplace_back(
std::int64_t(
n));
97 for (
const auto&
key : o.coverage) coverage.emplace_back(
key);
99 {
"legalActions", std::move(
actions)},
100 {
"coverage", std::move(coverage)},
101 {
"reward", o.reward},
102 {
"finding", o.finding},
107template <
class T,
class F>
111 }
catch (
const ParseFailure& e) {
118 return parse<Config>([&] {
132 "mutationProbability",
140 auto integer = [&](
const char*
key, std::uint32_t&
target) {
141 if (
auto it = o.find(
key); it != o.end())
target = u32(it->second);
144 if (
auto it = o.find(
key); it != o.end())
target = unsignedInteger(it->second);
146 auto scalar = [&](
const char*
key,
double&
target) {
149 integer(
"featureCount",
c.featureCount);
150 integer(
"actionCount",
c.actionCount);
151 integer(
"population",
c.population);
152 integer(
"generations",
c.generations);
153 integer(
"horizon",
c.horizon);
154 integer(
"elites",
c.elites);
155 integer(
"hiddenWidth",
c.hiddenWidth);
156 integer(
"trainingEpochs",
c.trainingEpochs);
157 integer(
"maxFindings",
c.maxFindings);
158 seed(
"environmentSeed",
c.environmentSeed);
159 seed(
"searchSeed",
c.searchSeed);
160 seed(
"learningSeed",
c.learningSeed);
162 scalar(
"mutationProbability",
c.mutationProbability);
163 scalar(
"randomProbability",
c.randomProbability);
164 scalar(
"coverageWeight",
c.coverageWeight);
165 scalar(
"failureWeight",
c.failureWeight);
166 scalar(
"learningRate",
c.learningRate);
167 if (
auto it = o.find(
"strategy"); it != o.end()) {
169 if (
name ==
"random")
171 else if (
name !=
"evolution")
172 throw ParseFailure(
"Unknown strategy");
174 if (
auto it = o.find(
"backend"); it != o.end()) {
176 if (
name ==
"tensor")
178 else if (
name ==
"gpu")
180 else if (
name !=
"cpu")
181 throw ParseFailure(
"Unknown backend");
187 return parse<Observation>([&] {
return observation(
value); });
190 auto result = parse<Policy>([&] {
192 object(
value, {
"schemaId",
"schemaVersion",
"featureCount",
"actionCount",
"hiddenWidth",
"weights"});
195 p.schemaVersion = u32(
required(o,
"schemaVersion"));
196 p.featureCount = u32(
required(o,
"featureCount"));
197 p.actionCount = u32(
required(o,
"actionCount"));
198 p.hiddenWidth = u32(
required(o,
"hiddenWidth"));
199 for (
const auto&
n : array(
required(o,
"weights"), 140000))
p.weights.push_back(
number(
n));
202 if (!result)
return result;
208 return parse<Trace>([&] {
209 const auto& o =
object(
value, {
"schemaId",
"schemaVersion",
"environmentSeed",
"dt",
"initial",
"steps"});
212 t.schemaVersion = u32(
required(o,
"schemaVersion"));
213 if (
t.schemaId !=
"evengine.agent.trace" ||
t.schemaVersion != 1)
214 throw ParseFailure(
"Unknown trace schema/version");
215 t.environmentSeed = unsignedInteger(
required(o,
"environmentSeed"));
217 t.initial = observation(
required(o,
"initial"));
218 for (
const auto& entry : array(
required(o,
"steps"), 1024)) {
219 const auto&
step =
object(entry, {
"action",
"observation"});
227 for (
auto w :
p.weights)
weights.emplace_back(
w);
229 {
"schemaVersion", std::int64_t(
p.schemaVersion)},
230 {
"featureCount", std::int64_t(
p.featureCount)},
231 {
"actionCount", std::int64_t(
p.actionCount)},
232 {
"hiddenWidth", std::int64_t(
p.hiddenWidth)},
233 {
"weights", std::move(
weights)}});
237 for (
const auto&
s :
t.steps)
239 Value::object({{
"action", std::int64_t(s.action)}, {
"observation", observationValue(s.observation)}}));
241 {
"schemaVersion", std::int64_t(
t.schemaVersion)},
242 {
"environmentSeed", std::to_string(
t.environmentSeed)},
244 {
"initial", observationValue(
t.initial)},
245 {
"steps", std::move(
steps)}});
250 for (
const auto&
key :
r.coverage) coverage.emplace_back(
key);
251 return Value::object({{
"schemaId",
"evengine.agent.report"},
252 {
"schemaVersion", 1},
256 {
"coverage", std::move(coverage)},
257 {
"episodes", std::int64_t(
r.episodes)},
258 {
"steps", std::int64_t(
r.steps)},
259 {
"failures", std::int64_t(
r.failures)},
260 {
"trainingSamples", std::int64_t(
r.trainingSamples)},
261 {
"bestScore",
r.bestScore},
262 {
"backend",
r.backend},
263 {
"trainingBackend",
r.trainingBackend}});
std::vector< std::string > fields
std::vector< ActionSpec > actions
std::vector< ImportFinding > findings
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Move-only operation result carrying either a value or Status.
static Result success(T value)
Construct a successful result owning value.
static Result failure(Status status)
Construct a failed result from a structured status.
The canonical owning dynamic value used by data-facing protocols.
std::map< std::string, Value > Object
std::vector< Value > Array
static Value object(Object value)
Compatibility factory for an object value.
Value encodePolicy(const Policy &p)
Encode a runner-produced policy into owning versioned data; no references retained.
Result< Trace > decodeTrace(const Value &value)
Decode version-1 owning replay data; run-time consistency is checked before replay reset.
Result< Policy > decodePolicy(const Value &value)
Decode and validate version-1 owning policy; unknown keys/versions rejected atomically.
Result< void > validatePolicy(const Policy &policy)
Validate the complete owning policy before inference/import, with no mutation or callbacks.
Result< Observation > decodeObservation(const Value &value)
Decode owning observation data, rejecting malformed values; dimensions are checked by run/infer.
Value encodeReport(const Report &r)
Encode runner-produced report, including policy and replayable evidence, as owning data.
Value encodeTrace(const Trace &t)
Encode a runner-produced trace into owning versioned data; seeds use lossless decimal strings.
Result< Config > decodeConfig(const Value &value)
Decode strict script/JSON configuration; unknown keys rejected, no mutations or callbacks.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
const EditorValue * field(const EditorValue &value, const char *name)
ModelData parse(std::span< const uint8_t > bytes)
Parse.
SettlementPipeline::Stage fn
Bounded search configuration; seed streams for environment, search and learning are separate.
Owning version-1 portable network weights; import validates the entire value before use.
Owning search result; reward, coverage and failures remain separate evidence.
In-memory versioned replay evidence, independent of learned model state.