Skip to content

refactor(engine): keep the general probes each in one folder - #118

Open
3mei wants to merge 7 commits into
yev/fix/gate-prompt-ratingsfrom
yev/refactor/general-probes-own-folders
Open

3mei wants to merge 7 commits into
yev/fix/gate-prompt-ratingsfrom
yev/refactor/general-probes-own-folders

Conversation

@3mei

@3mei 3mei commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

What changed and why

general_educational and general_open_ended each live in one folder: the probe module is src/usersim/engine/probes/<label>/probe.py, and its banks are beside it in assets/<label>/, the folder the class names as its assets_root. That is the layout a probe shipped in its own package already uses, so the same probe can live in either place. Their seeds are one file per locale, <locale>/topics.json or <locale>/subjects.json, as the other locale-keyed probes lay out their banks. Labels, prompts, seed contents and stored columns are unchanged.

Builds on #117.

  • prompts.yaml is read where the probe's banks are (first commit). load_prompt_file resolves the probe's folder through probe_assets_dir, as the bank loaders do, so a root on USERSIM_ASSET_PATH that holds other probes' banks does not hide a probe's prompts, and a probe that declares assets_root has its prompts.yaml read from there.
  • Packaging (second commit). Package data includes probes/*/assets/**, and scripts/check_dist.py requires a bank's Markdown in a probe's folder to ship, as under engine/assets/.
  • The pinned packaged root (third commit). A pinned asset root is searched alone. Pinning the packaged directory, which --assets-dir can do and which simulate --inputs does for rows stored by a run that named no folder and set no override, pins the assets that ship with User Sim, so it reaches a probe whose banks are in its own folder inside the package. An extension's root is outside the package, so the pin stays strict for an extension.
  • The two moves (fourth and fifth commits). One commit per probe: the module renamed to probe.py, its banks moved, assets_root set, and the entry in BUILTIN_PROBE_MODULES updated.
    • Tests that read these probes' seeds or prompts from the packaged directory resolve them through the probe's folder, and fail rather than skip when the seeds are missing.
    • test_assets_discovery.py accepts a built-in whose banks are in its own folder inside the package, and checks that such a built-in names the assets/ folder beside its module.
  • The guide and template (sixth commit). Step 2 of docs/engine/AUTHORING_A_PROBE.md has a probe keep its banks beside its code and set assets_root, and the worked bookings example is laid out that way. The probe template is copied to probe.py, and the templates that load a bank declare assets_root.
  • Seeds in locale folders (seventh commit). Each probe's seeds are in <locale>/topics.json or <locale>/subjects.json under its assets, with no topics/ or subjects/ level above the locale. The English file is in en_US/, and load_seeds reads it for a locale that ships no file of its own, such as en_IN and en_SG. The engine README and notebook 01 name the new paths.

How it was verified

At each commit, on that commit's tree:

$ make check-all && make test && make smoke
All checks passed!
349 files already formatted
4647 passed in 109.82s (0:01:49)
All checks passed.

The test count is 4648, 4651, 4652, 4652, 4652 and 4652 at the second to seventh commits.

At the head:

$ make check-license-headers
🎉 All files have correct license headers!
$ make check-doc-links
✓ every relative link, and every link into this repository's main branch, resolves
$ make check-notebooks-clean
OK 4 notebook(s) carry no outputs
$ make check-dist
✓ installed package runs from a clean environment
$ make check-extensions
all extension seams work against an installed distribution
  • Each fix's tests fail without it:

    FAILED tests/engine/core/test_prompt_resolution.py::TestPromptFileIsReadWhereTheBanksAre::test_a_root_holding_other_probes_does_not_hide_the_shipped_prompts
    FAILED tests/engine/core/test_prompt_resolution.py::TestPromptFileIsReadWhereTheBanksAre::test_a_declared_root_supplies_its_probes_prompts
    FAILED tests/test_check_dist.py::test_a_bank_schema_must_ship[probe-folder]
    FAILED tests/engine/core/test_assets_discovery.py::TestPinnedPackagedRoot::test_it_reaches_a_probe_whose_banks_are_in_its_own_folder_in_the_package
    
  • The worked bookings example, installed in tree with the guide's files, passes probe_conformance_problems, runs in en_US and pt_BR with run_episode, passes assert_hosted_parity, and usersim smoke checks its assets with the other probes:

    [OK]   probe assets          11 probe(s) checked their assets: bookings, financial_services, general_educational, general_open_ended, identity_disclosure, safety_agentic, safety_chat_pressure, sov_ai_dynamic, sov_ai_facts, sov_ai_multilingual_parity, tool_calling
    
  • make health-check ran against a custom provider config for each moved probe, two turns each, general_open_ended in en_SG, which reads the en_US topics, and general_educational in ko_KR:

    |-- general_open_ended turn 1/2: 21.7s (assistant + asst_judge + user + user_judge)
    |-- general_open_ended turn 2/2: 26.3s (assistant + asst_judge, last turn)
    ✓ simulate, eval and report all completed
    
    |-- general_educational turn 1/2: 18.0s (assistant + asst_judge + user + user_judge)
    |-- general_educational turn 2/2: 11.0s (assistant + asst_judge, last turn)
    ✓ simulate, eval and report all completed
    

    Every model's requests show failed=0, the evaluator's included.

  • usersim simulate and usersim eval --scorers auto ran against the same config, general_open_ended in en_IN and general_educational in pt_BR. Each stored row's topic or subject is in the file its locale reads, and neither probe declares scorers of its own, so the generic ones ran:

    theme 'school_family' in general_open_ended/en_US/topics.json: True
    theme 'civics' in general_educational/pt_BR/subjects.json: True
    judge: scored
    language_compliance: ok (error=None)
    refusal_basics: ok (error=None)
    response_shape: ok (error=None)
    

Results impact

Conversations, prompts and scores are unchanged. The setup fingerprint of a simulate --inputs run hashes the contents of every asset root, so it changes, as after any bank edit, and such a run gives its rows new trajectory ids rather than skipping rows it wrote before this change.

3mei added 6 commits November 2, 2026 16:46
…e to

load_prompt_file reads a probe's prompts.yaml from the folder probe_assets_dir resolves for it, as the probe's bank loaders do: the first root on the asset search path that holds the probe, or the assets_root its class declares. A root on USERSIM_ASSET_PATH that holds other probes' banks does not hide a probe's prompts, and a probe that declares assets_root has its prompts.yaml read from there.

A test covers each case.

Signed-off-by: Yev Meyer <ymeyer@nvidia.com>
Package data includes the files under probes/<label>/assets/ in usersim.engine, and check_dist requires a bank's Markdown there to ship, as it does under engine/assets/.

Signed-off-by: Yev Meyer <ymeyer@nvidia.com>
A pinned asset root is searched alone. Pinning the packaged directory, as --assets-dir can and as rows stored by a run that named no folder and set no override do, pins the assets that ship with User Sim. For a probe that directory does not hold, probe_assets_dir returns the folder under the root the probe's class declares when that root is inside the package. An extension's root is outside it, so the pin stays strict for an extension.

Signed-off-by: Yev Meyer <ymeyer@nvidia.com>
The probe module is probes/general_educational/probe.py and its banks are in probes/general_educational/assets/general_educational/, the folder the class names as its assets_root. Its label, prompts, seeds and stored columns are unchanged.

Tests that read its seeds or prompts from the packaged directory resolve them through the probe's folder, and the asset discovery tests accept a built-in whose banks are in its own folder inside the package and check that such a built-in names the assets folder beside its module.

Signed-off-by: Yev Meyer <ymeyer@nvidia.com>
The probe module is probes/general_open_ended/probe.py and its banks are in probes/general_open_ended/assets/general_open_ended/, the folder the class names as its assets_root. Its label, prompts, topics and stored columns are unchanged.

The two pipeline tests that read its topics load them through the probe's folder, and fail rather than skip when they are missing.

Signed-off-by: Yev Meyer <ymeyer@nvidia.com>
Step 2 of the authoring guide has a probe keep its banks beside its code and set assets_root, and the worked bookings example is laid out that way, with probe.py and its banks in assets/bookings/. The probe template is copied to probe.py, the templates that load a bank declare assets_root, and notebook 01 says each probe's assets ship with the package.

Signed-off-by: Yev Meyer <ymeyer@nvidia.com>
@3mei
3mei added this pull request to stack #113 November 2, 2026 21:57
@3mei
3mei requested review from a team as code owners November 2, 2026 21:57
general_open_ended keeps its topics in <locale>/topics.json and general_educational its subjects in <locale>/subjects.json, one folder per locale under each probe's assets, as the other locale-keyed probes lay out their banks. The English file is in en_US/, and load_seeds reads it for a locale that ships no file of its own, such as en_IN and en_SG. The seed files' contents are unchanged.

The seed tests build the same layout, and the engine README and notebook 01 name the new paths.

Signed-off-by: Yev Meyer <ymeyer@nvidia.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant