hyf

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

commit 3a33491efce98e7a2510fc7e6abbe0026b335a6d
parent d17f335f02e068ea59429f5de58a4ee5b1b4d96f
Author: triesap <tyson@radroots.org>
Date:   Mon, 21 Sep 2026 13:04:07 +0000

core: add versioned semantic question bundles

Diffstat:
Asrc/hyf_assist/questions.mojo | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtests/test_assist.mojo | 37+++++++++++++++++++++++++++++++++++++
2 files changed, 113 insertions(+), 0 deletions(-)

diff --git a/src/hyf_assist/questions.mojo b/src/hyf_assist/questions.mojo @@ -0,0 +1,76 @@ +from std.collections import List + + +@fieldwise_init +struct Question(Copyable, Movable): + var id: String + var kind: String + var instructions: String + var choices: List[String] + var rubric: List[String] + + +@fieldwise_init +struct QuestionBundle(Copyable, Movable): + var bundle_id: String + var version: String + var model: String + var questions: List[Question] + + +def _copy_strings(items: List[String]) -> List[String]: + var copied = List[String]() + for item in items: + copied.append(String(item)) + return copied^ + + +def choice_question(id: String, instructions: String, choices: List[String]) raises -> Question: + if id.strip() == "" or instructions.strip() == "": + raise Error("choice question requires id and instructions") + if len(choices) < 2: + raise Error("choice question requires at least two choices") + return Question( + id=String(id), kind="choice", instructions=String(instructions), + choices=_copy_strings(choices), rubric=List[String](), + ) + + +def noul_question(id: String, instructions: String) raises -> Question: + if id.strip() == "" or instructions.strip() == "": + raise Error("noul question requires id and instructions") + return Question( + id=String(id), kind="noul", instructions=String(instructions), + choices=List[String](), rubric=List[String](), + ) + + +def score_question(id: String, instructions: String, rubric: List[String]) raises -> Question: + if id.strip() == "" or instructions.strip() == "": + raise Error("score question requires id and instructions") + if len(rubric) < 2: + raise Error("score question requires at least two rubric levels") + return Question( + id=String(id), kind="score", instructions=String(instructions), + choices=List[String](), rubric=_copy_strings(rubric), + ) + + +def question_bundle( + bundle_id: String, version: String, model: String, questions: List[Question] +) raises -> QuestionBundle: + if bundle_id.strip() == "" or version.strip() == "" or model.strip() == "": + raise Error("question bundle requires id, version and model") + if len(questions) == 0: + raise Error("question bundle must contain at least one question") + var copied = List[Question]() + for question in questions: + copied.append(question.copy()) + return QuestionBundle( + bundle_id=String(bundle_id), version=String(version), + model=String(model), questions=copied^, + ) + + +def bundle_pins_configuration(bundle: QuestionBundle) -> Bool: + return bundle.version != "" and bundle.model != "" diff --git a/tests/test_assist.mojo b/tests/test_assist.mojo @@ -1,6 +1,7 @@ from std.collections import List from std.testing import TestSuite, assert_equal, assert_raises, assert_true +from hyf_assist.questions import Question from hyf_assist.evaluator import ( DisabledSemanticEvaluator, SemanticEvaluatorRequest, @@ -88,3 +89,39 @@ def test_scripted_evaluator_is_strict_and_bounded() raises: _ = scripted_evaluate( evaluator, SemanticEvaluatorRequest(state="", question_bundle="qb1") ) + + +from hyf_assist.questions import ( + bundle_pins_configuration, + choice_question, + noul_question, + question_bundle, + score_question, +) + + +def test_question_bundles_are_versioned_and_validated() raises: + var choices = List[String]() + choices.append("offered") + choices.append("forecast") + choices.append("unavailable") + choices.append("unclear") + var questions = List[Question]() + questions.append(choice_question("supply_status", "status?", choices)) + questions.append(noul_question("seconds_ok", "seconds permitted?")) + var rubric = List[String]() + rubric.append("unsuitable") + rubric.append("limited") + rubric.append("suitable") + questions.append(score_question("culinary_fit", "fit?", rubric)) + var bundle = question_bundle("qb1", "1", "jev-1.13.0", questions) + assert_true(bundle_pins_configuration(bundle)) + assert_equal(len(bundle.questions), 3) + var too_few = List[String]() + too_few.append("only") + with assert_raises(): + _ = choice_question("x", "y", too_few) + var one = List[String]() + one.append("only") + with assert_raises(): + _ = score_question("s", "i", one)