hyf

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

commit c45c48a7cfca903ea187cf1b57773ae47d54159e
parent 18359a016c6499e98679d2cf4ca597e4195a2b32
Author: triesap <tyson@radroots.org>
Date:   Sun, 12 Apr 2026 00:56:19 +0000

contract: activate negotiated request context fields

Diffstat:
Msrc/hyf_core/request_context.mojo | 44+++++++++++++++++++++++++++++++++++++++++++-
Mtests/fixtures/v1/scenarios/capabilities_ok.json | 8+++++++-
Mtests/fixtures/v1/scenarios/query_rewrite_local_pickup_weekend.json | 10+++++++++-
Mtests/fixtures/v1/scenarios/status_ok.json | 8+++++++-
Mtests/test_hyf.mojo | 19++++++++++++++++---
5 files changed, 82 insertions(+), 7 deletions(-)

diff --git a/src/hyf_core/request_context.mojo b/src/hyf_core/request_context.mojo @@ -1,7 +1,7 @@ from std.collections import List, Optional from mojson import Value -from mojson.deserialize import get_bool, get_string +from mojson.deserialize import get_bool, get_int, get_string def _has_key(value: Value, key: String) -> Bool: @@ -34,6 +34,11 @@ def _require_non_empty(value: String, context: String) raises: raise Error(context + " must not be empty") +def _require_positive_int(value: Int, context: String) raises: + if value <= 0: + raise Error(context + " must be greater than zero") + + def _parse_string_list(value: Value, context: String) raises -> List[String]: if value.is_null(): return List[String]() @@ -85,8 +90,13 @@ def request_context_allowed_keys() -> List[String]: var features = List[String]() features.append("consumer") features.append("execution_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^ @@ -94,8 +104,14 @@ def accepted_request_context_feature_names() -> List[String]: var features = List[String]() features.append("consumer") features.append("execution_mode_preference") + features.append("deadline_ms") features.append("scope.listing_ids") + features.append("time_range.start") + features.append("time_range.end") + features.append("evidence_limit") + features.append("consistency") features.append("return_provenance") + features.append("explain_plan") return features^ @@ -191,12 +207,38 @@ def parse_request_context(json: Value) raises -> RequestContext: "request context execution_mode_preference must be 'deterministic' or 'assisted'" ) + if _has_key(json, "deadline_ms"): + context.deadline_ms = get_int(json, "deadline_ms") + _require_positive_int( + context.deadline_ms, "request context deadline_ms" + ) + 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") + _require_positive_int( + context.evidence_limit, "request context evidence_limit" + ) + + if _has_key(json, "consistency"): + context.consistency = get_string(json, "consistency") + _require_non_empty( + context.consistency, "request context consistency" + ) + 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/tests/fixtures/v1/scenarios/capabilities_ok.json b/tests/fixtures/v1/scenarios/capabilities_ok.json @@ -35,8 +35,14 @@ "output.request_context_contract.accepted_features": [ "consumer", "execution_mode_preference", + "deadline_ms", "scope.listing_ids", - "return_provenance" + "time_range.start", + "time_range.end", + "evidence_limit", + "consistency", + "return_provenance", + "explain_plan" ], "output.request_context_contract.effective_features": [ "execution_mode_preference", diff --git a/tests/fixtures/v1/scenarios/query_rewrite_local_pickup_weekend.json b/tests/fixtures/v1/scenarios/query_rewrite_local_pickup_weekend.json @@ -9,7 +9,15 @@ "trace_id": "trace-rewrite-fixture-1", "capability": "query_rewrite", "context": { - "return_provenance": true + "deadline_ms": 2500, + "time_range": { + "start": "2026-04-12", + "end": "2026-04-13" + }, + "evidence_limit": 5, + "consistency": "default", + "return_provenance": true, + "explain_plan": true }, "input": { "query": "apples near me with weekend pickup" diff --git a/tests/fixtures/v1/scenarios/status_ok.json b/tests/fixtures/v1/scenarios/status_ok.json @@ -43,8 +43,14 @@ "output.request_context_contract.accepted_features": [ "consumer", "execution_mode_preference", + "deadline_ms", "scope.listing_ids", - "return_provenance" + "time_range.start", + "time_range.end", + "evidence_limit", + "consistency", + "return_provenance", + "explain_plan" ], "output.request_context_contract.effective_features": [ "execution_mode_preference", diff --git a/tests/test_hyf.mojo b/tests/test_hyf.mojo @@ -183,7 +183,7 @@ def _array_string_values(value: Value) raises -> List[String]: def test_decode_request_parses_context_and_input() raises: var request = decode_request( - '{"version":1,"request_id":"req-1","trace_id":"trace-1","capability":"query_rewrite","context":{"consumer":"radroots-cli","execution_mode_preference":"deterministic","return_provenance":true},"input":{"query":"eggs' + '{"version":1,"request_id":"req-1","trace_id":"trace-1","capability":"query_rewrite","context":{"consumer":"radroots-cli","execution_mode_preference":"deterministic","deadline_ms":2500,"time_range":{"start":"2026-04-12","end":"2026-04-13"},"evidence_limit":5,"consistency":"default","return_provenance":true,"explain_plan":true},"input":{"query":"eggs' ' near me"}}' ) @@ -193,7 +193,13 @@ def test_decode_request_parses_context_and_input() raises: assert_equal(request.capability, "query_rewrite") assert_equal(request.context.consumer, "radroots-cli") assert_equal(request.context.execution_mode_preference, "deterministic") + assert_equal(request.context.deadline_ms, 2500) + assert_equal(request.context.time_range.value().start, "2026-04-12") + assert_equal(request.context.time_range.value().end, "2026-04-13") + assert_equal(request.context.evidence_limit, 5) + assert_equal(request.context.consistency, "default") assert_equal(request.context.return_provenance, True) + assert_equal(request.context.explain_plan, True) assert_equal(request.input["query"].string_value(), "eggs near me") @@ -216,10 +222,17 @@ def test_decode_request_requires_input_object() raises: ) -def test_decode_request_rejects_unsupported_context_field() raises: +def test_decode_request_rejects_unknown_context_field() raises: with assert_raises(): _ = decode_request( - '{"version":1,"request_id":"req-ctx-1","capability":"query_rewrite","context":{"deadline_ms":2500},"input":{"query":"eggs"}}' + '{"version":1,"request_id":"req-ctx-1","capability":"query_rewrite","context":{"planner":"strict"},"input":{"query":"eggs"}}' + ) + + +def test_decode_request_rejects_invalid_activated_context_field() raises: + with assert_raises(): + _ = decode_request( + '{"version":1,"request_id":"req-ctx-2","capability":"query_rewrite","context":{"deadline_ms":0},"input":{"query":"eggs"}}' )