hyf

Context-aware query service for Radroots
git clone https://radroots.dev/git/hyf.git
Log | Files | Refs | README | LICENSE

commit b2b99bcb5fb3f6250eedac5fbbe61db8ff8be6dd
parent 122c10d1a03848af81fd309a49f21e7dca37aa72
Author: triesap <tyson@radroots.org>
Date:   Wed,  8 Apr 2026 17:40:54 +0000

core: add shared hyf context and provenance models

- add typed request-context defaults and semantic validation in hyf_core
- add transport-agnostic provenance and core result error structs
- extend wire requests with optional shared context parsed through core
- expose request-context feature discovery through the stdio control plane

Diffstat:
Msrc/hyf_core/errors.mojo | 44++++++++++++++++++++++++++++++++++++++++++--
Msrc/hyf_core/provenance.mojo | 40++++++++++++++++++++++++++++++++++++++--
Msrc/hyf_core/request_context.mojo | 236++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/hyf_stdio/control/capabilities.mojo | 6+++++-
Msrc/hyf_stdio/control/status.mojo | 6+++++-
Msrc/hyf_stdio/envelope.mojo | 25+++++++++++++++++++++++--
6 files changed, 347 insertions(+), 10 deletions(-)

diff --git a/src/hyf_core/errors.mojo b/src/hyf_core/errors.mojo @@ -1,3 +1,43 @@ -def core_error_module_name() -> String: - return "hyf_core.errors" +from std.collections import Optional +from mojson import Value + +from hyf_core.provenance import CoreResponseMeta + + +@fieldwise_init +struct CoreError(Copyable, Movable): + var code: String + var message: String + var retryable: Bool + + +@fieldwise_init +struct CapabilitySuccess(Copyable, Movable): + var output: Value + var meta: Optional[CoreResponseMeta] + + +@fieldwise_init +struct CapabilityFailure(Copyable, Movable): + var error: CoreError + + +def invalid_context_error(message: String) -> CoreError: + return CoreError(code="invalid_context", message=message, retryable=False) + + +def capability_not_implemented_error(capability: String) -> CoreError: + return CoreError( + code="capability_not_implemented", + message="core capability '" + capability + "' is not implemented yet", + retryable=False, + ) + + +def backend_unavailable_error(backend: String) -> CoreError: + return CoreError( + code="backend_unavailable", + message="backend '" + backend + "' is unavailable", + retryable=True, + ) diff --git a/src/hyf_core/provenance.mojo b/src/hyf_core/provenance.mojo @@ -1,3 +1,39 @@ -def provenance_module_name() -> String: - return "hyf_core.provenance" +from std.collections import List, Optional + +@fieldwise_init +struct ProvenanceSourceRef(Copyable, Movable): + var source_kind: String + var source_ref: String + + +@fieldwise_init +struct ProvenanceFallback(Copyable, Movable): + var fallback_kind: String + var reason: String + + +@fieldwise_init +struct ExecutionProvenance(Copyable, Movable): + var kind: String + var signal_tags: List[String] + var source_refs: List[ProvenanceSourceRef] + var fallback: Optional[ProvenanceFallback] + var evidence_set_id: Optional[String] + + +@fieldwise_init +struct CoreResponseMeta(Copyable, Movable): + var mode: String + var backend: String + var latency_ms: Int + var provenance: Optional[ExecutionProvenance] + + +def deterministic_response_meta() -> CoreResponseMeta: + return CoreResponseMeta( + mode="a", + backend="heuristic", + latency_ms=0, + provenance=None, + ) diff --git a/src/hyf_core/request_context.mojo b/src/hyf_core/request_context.mojo @@ -1,3 +1,235 @@ -def request_context_module_name() -> String: - return "hyf_core.request_context" +from std.collections import List, Optional +from mojson import Value +from mojson.deserialize import get_bool, get_int, get_string + + +def _has_key(value: Value, key: String) -> Bool: + for candidate in value.object_keys(): + if candidate == key: + return True + return False + + +def _require_object(value: Value, context: String) raises: + if not value.is_object(): + raise Error(context + " must be a JSON object") + + +def _require_allowed_keys( + value: Value, allowed_keys: List[String], context: String +) raises: + for key in value.object_keys(): + var allowed = False + for allowed_key in allowed_keys: + if key == allowed_key: + allowed = True + break + if not allowed: + raise Error(context + " contains unexpected field '" + key + "'") + + +def _require_non_empty(value: String, context: String) raises: + if value == "": + raise Error(context + " must not be empty") + + +def _parse_string_list(value: Value, context: String) raises -> List[String]: + if value.is_null(): + return List[String]() + if not value.is_array(): + raise Error(context + " must be a JSON array") + + var items = List[String]() + for item in value.array_items(): + if not item.is_string(): + raise Error(context + " must contain only strings") + _require_non_empty(item.string_value(), context + " item") + items.append(item.string_value()) + return items^ + + +@fieldwise_init +struct RequestScope(Copyable, Movable): + var listing_ids: List[String] + var farm_ids: List[String] + var account_ids: List[String] + var platform_ids: List[String] + var object_filters: Optional[Value] + + +@fieldwise_init +struct TimeRange(Copyable, Movable): + var start: String + var end: String + + +@fieldwise_init +struct RequestContext(Copyable, Movable): + var consumer: String + var mode_preference: String + var deadline_ms: Int + var scope: Optional[RequestScope] + var time_range: Optional[TimeRange] + var evidence_limit: Int + var consistency: String + var return_provenance: Bool + var explain_plan: Bool + + +def request_context_feature_names() -> List[String]: + var features = List[String]() + features.append("consumer") + features.append("mode_preference") + features.append("deadline_ms") + features.append("scope") + features.append("time_range") + features.append("evidence_limit") + features.append("consistency") + features.append("return_provenance") + features.append("explain_plan") + return features^ + + +def default_request_context() -> RequestContext: + return RequestContext( + consumer="unknown", + mode_preference="a", + deadline_ms=2500, + scope=None, + time_range=None, + evidence_limit=10, + consistency="default", + return_provenance=False, + explain_plan=False, + ) + + +def _parse_scope(json: Value) raises -> RequestScope: + _require_object(json, "request context scope") + + var allowed_keys = List[String]() + allowed_keys.append("listing_ids") + allowed_keys.append("farm_ids") + allowed_keys.append("account_ids") + allowed_keys.append("platform_ids") + allowed_keys.append("object_filters") + _require_allowed_keys(json, allowed_keys, "request context scope") + + var object_filters: Optional[Value] = None + if _has_key(json, "object_filters"): + var raw_filters = json["object_filters"].clone() + if not raw_filters.is_null(): + object_filters = raw_filters^ + + var listing_ids_json = Value(None) + if _has_key(json, "listing_ids"): + listing_ids_json = json["listing_ids"].clone() + + var farm_ids_json = Value(None) + if _has_key(json, "farm_ids"): + farm_ids_json = json["farm_ids"].clone() + + var account_ids_json = Value(None) + if _has_key(json, "account_ids"): + account_ids_json = json["account_ids"].clone() + + var platform_ids_json = Value(None) + if _has_key(json, "platform_ids"): + platform_ids_json = json["platform_ids"].clone() + + return RequestScope( + listing_ids=_parse_string_list( + listing_ids_json, "request context scope listing_ids" + ), + farm_ids=_parse_string_list( + farm_ids_json, "request context scope farm_ids" + ), + account_ids=_parse_string_list( + account_ids_json, "request context scope account_ids" + ), + platform_ids=_parse_string_list( + platform_ids_json, "request context scope platform_ids" + ), + object_filters=object_filters^, + ) + + +def _parse_time_range(json: Value) raises -> TimeRange: + _require_object(json, "request context time_range") + + var allowed_keys = List[String]() + allowed_keys.append("start") + allowed_keys.append("end") + _require_allowed_keys(json, allowed_keys, "request context time_range") + + var start = get_string(json, "start") + _require_non_empty(start, "request context time_range start") + + var end = get_string(json, "end") + _require_non_empty(end, "request context time_range end") + + return TimeRange(start=start, end=end) + + +def parse_request_context(json: Value) raises -> RequestContext: + if json.is_null(): + return default_request_context() + + _require_object(json, "request context") + + var allowed_keys = request_context_feature_names() + _require_allowed_keys(json, allowed_keys, "request context") + + var context = default_request_context() + + if _has_key(json, "consumer"): + context.consumer = get_string(json, "consumer") + _require_non_empty(context.consumer, "request context consumer") + + if _has_key(json, "mode_preference"): + context.mode_preference = get_string(json, "mode_preference") + if context.mode_preference != "a" and context.mode_preference != "b": + raise Error( + "request context mode_preference must be 'a' or 'b'" + ) + + if _has_key(json, "deadline_ms"): + context.deadline_ms = get_int(json, "deadline_ms") + if context.deadline_ms <= 0: + raise Error("request context deadline_ms must be greater than zero") + + if _has_key(json, "scope"): + var scope_json = json["scope"].clone() + if not scope_json.is_null(): + context.scope = _parse_scope(scope_json) + + if _has_key(json, "time_range"): + var time_range_json = json["time_range"].clone() + if not time_range_json.is_null(): + context.time_range = _parse_time_range(time_range_json) + + if _has_key(json, "evidence_limit"): + context.evidence_limit = get_int(json, "evidence_limit") + if context.evidence_limit <= 0: + raise Error( + "request context evidence_limit must be greater than zero" + ) + + if _has_key(json, "consistency"): + context.consistency = get_string(json, "consistency") + if ( + context.consistency != "default" + and context.consistency != "strong" + ): + raise Error( + "request context consistency must be 'default' or 'strong'" + ) + + if _has_key(json, "return_provenance"): + context.return_provenance = get_bool(json, "return_provenance") + + if _has_key(json, "explain_plan"): + context.explain_plan = get_bool(json, "explain_plan") + + return context^ diff --git a/src/hyf_stdio/control/capabilities.mojo b/src/hyf_stdio/control/capabilities.mojo @@ -3,6 +3,7 @@ from std.collections import List from mojson import Value, loads from hyf_core.capabilities.registry import canonical_business_capabilities +from hyf_core.request_context import request_context_feature_names def _string_array(values: List[String]) raises -> Value: @@ -48,5 +49,8 @@ def build_capabilities_output() raises -> Value: output.set("business_capabilities", capabilities) output.set("backend_assisted_capabilities", loads("[]")) - output.set("request_context_features", loads("[]")) + output.set( + "request_context_features", + _string_array(request_context_feature_names()), + ) return output^ diff --git a/src/hyf_stdio/control/status.mojo b/src/hyf_stdio/control/status.mojo @@ -7,6 +7,7 @@ from hyf_core.capabilities.registry import ( bootstrap_enabled_capabilities, deferred_capabilities, ) +from hyf_core.request_context import request_context_feature_names def _string_array(values: List[String]) raises -> Value: @@ -52,7 +53,10 @@ def build_status_output() raises -> Value: var limits = loads("{}") limits.set("max_requests_per_process", Value(1)) - limits.set("request_context_features", loads("[]")) + limits.set( + "request_context_features", + _string_array(request_context_feature_names()), + ) output.set("limits", limits) return output^ diff --git a/src/hyf_stdio/envelope.mojo b/src/hyf_stdio/envelope.mojo @@ -1,6 +1,7 @@ from mojson import Value, loads from mojson.deserialize import Deserializable, get_string +from hyf_core.request_context import RequestContext, parse_request_context from hyf_stdio.errors import WireError @@ -18,16 +19,29 @@ def _require_non_empty(value: String, field_name: String) raises: def _require_request_keys(value: Value) raises: for key in value.object_keys(): - if key != "request_id" and key != "capability" and key != "input": + if ( + key != "request_id" + and key != "capability" + and key != "context" + and key != "input" + ): raise Error( - "request envelope contains unexpected field '" + key + "'" + "request envelope contains unexpected field '" + key + "'" ) +def _has_key(value: Value, key: String) -> Bool: + for candidate in value.object_keys(): + if candidate == key: + return True + return False + + @fieldwise_init struct WireRequest(Deserializable, Copyable, Movable): var request_id: String var capability: String + var context: RequestContext var input: Value @staticmethod @@ -41,9 +55,16 @@ struct WireRequest(Deserializable, Copyable, Movable): var capability = get_string(json, "capability") _require_non_empty(capability, "capability") + var context_json = Value(None) + if _has_key(json, "context"): + context_json = json["context"].clone() + + var context = parse_request_context(context_json) + return Self( request_id=request_id, capability=capability, + context=context^, input=json["input"].clone(), )