Skip to content

refactor: metadata tag - #345

Open
nikbpetrov wants to merge 2 commits into
forecastingresearch:mainfrom
nikbpetrov:p1-metadata-tag
Open

nikbpetrov wants to merge 2 commits into
forecastingresearch:mainfrom
nikbpetrov:p1-metadata-tag

Conversation

@nikbpetrov

@nikbpetrov nikbpetrov commented Oct 25, 2026 •

Copy link
Copy Markdown
Collaborator

On top of #344

  • tag export in the __init__.py is to set up the nicer looking import, namely from metadata import tag rather than something like from metadata._tag import tag or equivalent; tests import ugly the specific funcs but they'll be okay
  • also opted for the annoying lazy export of tag/validate in the metadata/__init__.py as it gets fixed with Python 3.15's lazy keyword

Existing/legacy bug: a non-404 download error yields an empty frame and the job re-tags everything and overwrites the bucket file. Either file an issue and fix in a separate PR or fix here - @houtanb , wdyt?

@nikbpetrov
nikbpetrov marked this pull request as ready for review October 30, 2026 08:24
@nikbpetrov
nikbpetrov requested a review from houtanb October 30, 2026 08:24
@nikbpetrov
nikbpetrov force-pushed the p1-metadata-tag branch 2 times, most recently from 04db21a to d8aaec9 Compare November 1, 2026 09:54
@houtanb
houtanb force-pushed the p1-metadata-tag branch 2 times, most recently from 03b27c4 to a64616d Compare November 9, 2026 09:08
pyfixest 0.70.0 (released 2026-11-08) rejects a fixed-effect column of object dtype, and
`two_way_fixed_effects` passes `question_pk` and `model_pk` as plain object columns:

    FixedEffectEvaluationError: Fixed-effect column `model_pk` has unsupported object dtype.

The requirement had no upper bound, so every fresh install, CI and the next leaderboard deploy
alike, picks up 0.70.0 and the fit fails. Cap it at the last version the code was run with; casting
the key columns to `string` and lifting the cap is a separate change.
Port `src/metadata/tag_questions/main.py` to `src/metadata/_tag.py`, exported as `metadata.tag` (the
domain logic: category from the metadata model or fixed per source, English flag from Jev, merge
into the metadata frame; function bodies verbatim) and `src/orchestration/func_metadata_tag/` (the
Cloud Run job `func-metadata-tag-questions`: download the metadata, then one `tag()` call and one
upload per sampled source with the 2 s GCS write-rate sleep). `orchestration._io` gains
`load_question_metadata()` and `upload_question_metadata()`; `_schemas` gains `MetadataFrame`, which
types `valid_question` and `english` as bool or "" because the file carries the placeholder until
validate runs or when a Jev call failed. `tag()` and the two `_io` helpers take and return
`DataFrame[MetadataFrame]` / `DataFrame[QuestionFrame]` under `@pa.check_types`, so a malformed
metadata or question file fails the job instead of being written; `load_question_metadata()` fills
the columns a file written before they existed lacks. The module is private (`_tag.py`) because a
public `tag.py` would be shadowed by the re-exported function. The package exports `tag` through a
module `__getattr__` rather than an eager import, so a job that imports `metadata` for another
function does not load `_tag`'s Jev SDK, which only the tag job ships; a contract test pins that.
`src/metadata/tag_questions/` is removed and the root `Makefile` target `tag-questions` repointed.
Requirements drop `functions-framework` (the job runs `python3 main.py`) and `scipy` (nothing in the
staged import chain loads it). Tests pin the two production fixes the job carries, the 512-token
category budget (`c0d475e`) and the one-time key fetch before the concurrent calls (`1fb7ddd`), and
the (source, id) merge key.

Parity: on a mirror of the dev question bank, with the same deterministic model and Jev fakes on
both sides, the old and new jobs write a byte-identical `question_metadata.jsonl` (final and every
per-source upload) and send the same rows to the model and to Jev, in the as-is, perturbed and
empty-file scenarios.

Tests: `unit/metadata/test_tag.py`, `unit/orchestration/test_metadata_tag_job.py` and
`integration/test_metadata_io.py`, superseding `unit/metadata/test_tag_questions.py` and
`test_tag_questions_english.py`; `integration/test_metadata_drivers.py`,
`e2e/test_question_set_pipeline.py`, `contract/test_offline_imports.py` and
`contract/test_runtime_requirements.py` follow the moved modules.
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