hyf

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

commit 74b6721fc35090cd8cf77d939d0dbc0ca91a16f4
parent 79a7fbf061112916743c2754164f5fae4ec89ea8
Author: triesap <tyson@radroots.org>
Date:   Wed,  8 Apr 2026 19:56:15 +0000

capabilities: type semantic and explain input contracts

Diffstat:
Msrc/hyf_core/capabilities/explain_result.mojo | 13++++++++-----
Msrc/hyf_core/capabilities/ranking_support.mojo | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/hyf_core/capabilities/semantic_rank.mojo | 11++++++-----
Mtests/test_hyf.mojo | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 192 insertions(+), 10 deletions(-)

diff --git a/src/hyf_core/capabilities/explain_result.mojo b/src/hyf_core/capabilities/explain_result.mojo @@ -3,15 +3,16 @@ from std.collections import List from mojson import Value, loads from hyf_core.capabilities.query_analysis import ( - analyze_query, + analyze_query_text, build_deterministic_meta, query_signal_tags, serialize_extracted_filters, string_array_value, ) from hyf_core.capabilities.ranking_support import ( + ExplainResultRequest, evaluate_candidate, - parse_single_candidate, + parse_explain_result_request, ) from hyf_core.errors import ( CapabilityResult, @@ -97,9 +98,11 @@ def execute_explain_result( ) try: - var analysis = analyze_query(input, context, "explain_result") - var candidate = parse_single_candidate(input, "explain_result") - var evaluation = evaluate_candidate(candidate, analysis, context) + var request: ExplainResultRequest = parse_explain_result_request(input) + var analysis = analyze_query_text(request.query_text, context) + var evaluation = evaluate_candidate( + request.candidate, analysis, context + ) var signal_tags = query_signal_tags(analysis) for reason in evaluation.reasons: diff --git a/src/hyf_core/capabilities/ranking_support.mojo b/src/hyf_core/capabilities/ranking_support.mojo @@ -34,11 +34,36 @@ struct CandidateEvaluation(Copyable, Movable): var scope_match: Bool +@fieldwise_init +struct SemanticRankRequest(Copyable, Movable): + var query_text: String + var candidates: List[SemanticCandidate] + + +@fieldwise_init +struct ExplainResultRequest(Copyable, Movable): + var query_text: String + var candidate: SemanticCandidate + + 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 _copy_candidate(candidate: SemanticCandidate) -> SemanticCandidate: return SemanticCandidate( id=String(candidate.id), @@ -73,6 +98,15 @@ def _copy_evaluation(evaluation: CandidateEvaluation) -> CandidateEvaluation: def _parse_candidate(json: Value, context: String) raises -> SemanticCandidate: _require_object(json, context) + var allowed_keys = List[String]() + allowed_keys.append("id") + allowed_keys.append("title") + allowed_keys.append("farm") + allowed_keys.append("delivery") + allowed_keys.append("distance_km") + allowed_keys.append("freshness_minutes") + _require_allowed_keys(json, allowed_keys, context) + var id = get_string(json, "id") if collapse_whitespace(id) == "": raise Error(context + " field 'id' must not be empty") @@ -109,6 +143,67 @@ def _parse_candidate(json: Value, context: String) raises -> SemanticCandidate: ) +def _parse_query_text(input: Value, capability_name: String) raises -> String: + var field_count = 0 + if has_key(input, "text"): + field_count += 1 + if has_key(input, "query"): + field_count += 1 + + if field_count == 0: + raise Error( + capability_name + " input requires exactly one of 'text' or 'query'" + ) + if field_count > 1: + raise Error( + capability_name + + " input must provide exactly one of 'text' or 'query'" + ) + + var field_name = "text" if has_key(input, "text") else "query" + var text_value = input[field_name] + if not text_value.is_string(): + raise Error( + capability_name + " input field '" + field_name + "' must be a string" + ) + + var collapsed = collapse_whitespace(text_value.string_value()) + if collapsed == "": + raise Error(capability_name + " input text must not be empty") + return collapsed^ + + +def parse_semantic_rank_request(input: Value) raises -> SemanticRankRequest: + _require_object(input, "semantic_rank input") + + var allowed_keys = List[String]() + allowed_keys.append("text") + allowed_keys.append("query") + allowed_keys.append("candidates") + _require_allowed_keys(input, allowed_keys, "semantic_rank input") + + return SemanticRankRequest( + query_text=_parse_query_text(input, "semantic_rank"), + candidates=parse_candidate_array(input, "semantic_rank"), + ) + + +def parse_explain_result_request(input: Value) raises -> ExplainResultRequest: + _require_object(input, "explain_result input") + + var allowed_keys = List[String]() + allowed_keys.append("text") + allowed_keys.append("query") + allowed_keys.append("candidate") + allowed_keys.append("result") + _require_allowed_keys(input, allowed_keys, "explain_result input") + + return ExplainResultRequest( + query_text=_parse_query_text(input, "explain_result"), + candidate=parse_single_candidate(input, "explain_result"), + ) + + def parse_candidate_array( input: Value, capability_name: String ) raises -> List[SemanticCandidate]: diff --git a/src/hyf_core/capabilities/semantic_rank.mojo b/src/hyf_core/capabilities/semantic_rank.mojo @@ -3,7 +3,7 @@ from std.collections import List from mojson import Value, loads from hyf_core.capabilities.query_analysis import ( - analyze_query, + analyze_query_text, build_deterministic_meta, query_signal_tags, serialize_extracted_filters, @@ -11,7 +11,8 @@ from hyf_core.capabilities.query_analysis import ( ) from hyf_core.capabilities.ranking_support import ( CandidateEvaluation, - parse_candidate_array, + SemanticRankRequest, + parse_semantic_rank_request, rank_candidates, ) from hyf_core.errors import ( @@ -82,9 +83,9 @@ def execute_semantic_rank( ) try: - var analysis = analyze_query(input, context, "semantic_rank") - var candidates = parse_candidate_array(input, "semantic_rank") - var ranked = rank_candidates(candidates, analysis, context) + var request: SemanticRankRequest = parse_semantic_rank_request(input) + var analysis = analyze_query_text(request.query_text, context) + var ranked = rank_candidates(request.candidates, analysis, context) var signal_tags = query_signal_tags(analysis) signal_tags.append("candidate_set_evaluated") diff --git a/tests/test_hyf.mojo b/tests/test_hyf.mojo @@ -301,6 +301,38 @@ def test_semantic_rank_returns_ranked_ids_and_reasons() raises: ) +def test_semantic_rank_rejects_unknown_top_level_field() raises: + var result = _dispatch( + '{"version":1,"request_id":"rank-bad-top-1","capability":"semantic_rank","input":{"query":"eggs near me","candidates":[{"id":"lst_7ak2","title":"Pasture eggs","farm":"La Huerta del Sur","delivery":"pickup","distance_km":3.2,"freshness_minutes":2}],"tone":"brief"}}' + ) + + assert_equal(Int(result["version"].int_value()), 1) + assert_equal(result["ok"].bool_value(), False) + assert_equal(result["request_id"].string_value(), "rank-bad-top-1") + assert_equal(result["error"]["code"].string_value(), "invalid_request") + assert_true( + result["error"]["message"].string_value().find("unexpected field") + >= 0 + ) + + +def test_semantic_rank_rejects_unknown_candidate_field() raises: + var result = _dispatch( + '{"version":1,"request_id":"rank-bad-candidate-1","capability":"semantic_rank","input":{"query":"eggs near me","candidates":[{"id":"lst_7ak2","title":"Pasture eggs","farm":"La Huerta del Sur","delivery":"pickup","distance_km":3.2,"freshness_minutes":2,"rating":5}]}}' + ) + + assert_equal(Int(result["version"].int_value()), 1) + assert_equal(result["ok"].bool_value(), False) + assert_equal( + result["request_id"].string_value(), "rank-bad-candidate-1" + ) + assert_equal(result["error"]["code"].string_value(), "invalid_request") + assert_true( + result["error"]["message"].string_value().find("unexpected field") + >= 0 + ) + + def test_explain_result_returns_deterministic_summary_and_provenance() raises: var result = _dispatch( '{"version":1,"request_id":"explain-1","capability":"explain_result","context":{"consumer":"radroots-cli","return_provenance":true},"input":{"query":"eggs near me with weekend pickup","candidate":{"id":"lst_7ak2","title":"Pasture eggs","farm":"La Huerta del Sur","delivery":"pickup","distance_km":3.2,"freshness_minutes":2}}}' @@ -325,6 +357,57 @@ def test_explain_result_returns_deterministic_summary_and_provenance() raises: ) +def test_explain_result_accepts_result_alias() raises: + var result = _dispatch( + '{"version":1,"request_id":"explain-result-1","capability":"explain_result","input":{"query":"eggs near me with weekend pickup","result":{"id":"lst_7ak2","title":"Pasture eggs","farm":"La Huerta del Sur","delivery":"pickup","distance_km":3.2,"freshness_minutes":2}}}' + ) + + assert_equal(Int(result["version"].int_value()), 1) + assert_equal(result["ok"].bool_value(), True) + assert_equal( + result["output"]["result_id"].string_value(), + "lst_7ak2", + ) + assert_equal( + result["output"]["explanation_kind"].string_value(), + "deterministic", + ) + + +def test_explain_result_rejects_unknown_top_level_field() raises: + var result = _dispatch( + '{"version":1,"request_id":"explain-bad-top-1","capability":"explain_result","input":{"query":"eggs near me","candidate":{"id":"lst_7ak2","title":"Pasture eggs","farm":"La Huerta del Sur","delivery":"pickup","distance_km":3.2,"freshness_minutes":2},"tone":"brief"}}' + ) + + assert_equal(Int(result["version"].int_value()), 1) + assert_equal(result["ok"].bool_value(), False) + assert_equal( + result["request_id"].string_value(), "explain-bad-top-1" + ) + assert_equal(result["error"]["code"].string_value(), "invalid_request") + assert_true( + result["error"]["message"].string_value().find("unexpected field") + >= 0 + ) + + +def test_explain_result_rejects_unknown_candidate_field() raises: + var result = _dispatch( + '{"version":1,"request_id":"explain-bad-candidate-1","capability":"explain_result","input":{"query":"eggs near me","candidate":{"id":"lst_7ak2","title":"Pasture eggs","farm":"La Huerta del Sur","delivery":"pickup","distance_km":3.2,"freshness_minutes":2,"rating":5}}}' + ) + + assert_equal(Int(result["version"].int_value()), 1) + assert_equal(result["ok"].bool_value(), False) + assert_equal( + result["request_id"].string_value(), "explain-bad-candidate-1" + ) + assert_equal(result["error"]["code"].string_value(), "invalid_request") + assert_true( + result["error"]["message"].string_value().find("unexpected field") + >= 0 + ) + + def test_semantic_rank_invalid_input_returns_invalid_request() raises: var result = _dispatch( '{"version":1,"request_id":"rank-bad-1","trace_id":"trace-rank-bad-1","capability":"semantic_rank","input":{"query":"eggs near me with weekend pickup","candidates":[]}}'