feat(context): Add priority_select for budget-aware section selection - #6826
Closed
patelchaitany wants to merge 2 commits into
Closed
feat(context): Add priority_select for budget-aware section selection#6826patelchaitany wants to merge 2 commits into
patelchaitany wants to merge 2 commits into
Conversation
Create the feast.context module with the token accounting that prompt assembly in an OnDemandFeatureView builds on. Tokenizers resolve the way online store types do in repo_config: a built-in name maps to a class path in TOKENIZER_CLASS_FOR_TYPE, and anything else is itself the path of a Tokenizer subclass with a no-argument constructor, loaded through import_class. Adding a tokenizer is therefore what adding a vector store is - write the class, pass its path, no registration call and no mutable global state. Feast ships cl100k_base, o200k_base and a dependency-free character estimate. TokenBudget is a frozen dataclass holding a resolved Tokenizer: consume() returns a new budget rather than mutating, and try_consume() returns None instead of raising, which is the primitive priority_select() will use to fill a budget greedily. TokenBudget.of() resolves a name, class path or instance and can refuse the fallback; is_approximate reports whether counts are exact. Tokenizers compare by value, so budgets built from the same name are interchangeable as dict keys and set members. When tiktoken cannot be loaded, get_tokenizer() degrades to the character estimate with a logged warning; fallback=False makes it fatal for paths that need exact counts. TiktokenTokenizer counts with disallowed_special=(), so a feature value containing a literal "<|endoftext|>" is treated as ordinary text instead of raising. tiktoken is not declared as a dependency yet, so the tests that assert exact encodings skip when it is absent. Part of RHOAIENG-80116 (PR 1/9). Signed-off-by: Chaitany Patel <patelchaitany93@gmail.com>
Decides which parts of a prompt survive a token budget. Sections carry a priority; selection keeps them from critical down until the allowance is spent, dropping the least important first and never dropping a critical one. The result carries the kept sections, the dropped ones and the budget left over, and renders to the assembled text. The budget is charged for the separator between kept sections, so the rendered prompt fits the allowance rather than overshooting it once the pieces are joined. Selection is a pure function of the sections and the budget, so an OnDemandFeatureView drops the same sections offline and online. Part of RHOAIENG-80116. Stacked on the TokenBudget PR. Signed-off-by: Chaitany Patel <patelchaitany93@gmail.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #6826 +/- ##
=======================================
Coverage 47.08% 47.08%
=======================================
Files 419 419
Lines 51877 51877
Branches 7525 7525
=======================================
Hits 24428 24428
Misses 25700 25700
Partials 1749 1749
*This pull request uses carry forward flags. Click here to find out more. Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does / why we need it:
Adds
priority_select(), the second piece offeast.context: it decides which parts of a prompt survive a token budget. Prompt assembly is a packing problem — a user profile, a watch history and a catalogue of candidates rarely fit together in a context window — and this is the mechanism that chooses what gives.Sections are considered most important first, and dropped least important first.
Priorityis anIntEnum(critical > high > medium > low), spaced by ten so a caller can slot a level in between. Ties keep declaration order. A section too large to fit is dropped and the scan continues, so a small low-priority section is still kept after a large high-priority one was refused. Acriticalsection is never dropped: if it does not fit,RequiredSectionErroris raised rather than quietly returning a prompt with its instructions missing. That error subclassesTokenBudgetExceededError, so callers already guarding assembly keep catching one type.The separator is charged against the budget. Counting sections alone and then joining them with
"\n\n"overshoots the allowance by one token per joint, which is exactly the sort of drift that turns into a 400 from a model server.priority_selectcharges one separator between each pair of kept sections, soSelection.render()fits what was budgeted. Tokenizers may merge text across a boundary, which only makes the charge an upper bound.The result is a value, not a list of strings.
Selectionreports what was kept, what was dropped, and the budget left over — the last of which matters when a caller wants to spend the remainder elsewhere. It iterates over the kept content and stringifies to the rendered prompt, so it still drops straight into a join or a template:Kept sections come back in declaration order by default, so the prompt reads as it was written;
order=SectionOrder.PRIORITYreturns them most important first instead. Sections given blank content are ignored rather than rendered as a doubled separator.Determinism. Selection is a pure function of the sections and the budget — no clocks, no iteration over unordered collections, no mutation of the budget passed in. The same ODFV therefore drops the same sections during training as it does at serving time, which is what AC-6 of the epic asks for.
One change outside the new file:
TokenBudgetExceededErrorgained a keyword-onlymessageoverride soRequiredSectionErrorcan name the offending section while keeping the same type and attributes. It is additive and backwards compatible.Which issue(s) this PR fixes:
No GitHub issue. Tracked internally as RHOAIENG-80116.
Checks
git commit -s)Testing Strategy
sdk/python/tests/unit/context/test_priority_select.py— 48 tests with tiktoken installed, 47 passing and 1 skipped without it. Covers priority parsing and ordering, section normalization from tuples, dropping order, critical sections listed last, keeping a smaller section after refusing a larger one, separator accounting (including the assertion that the rendered prompt costs exactly what was charged), an already-consumed budget, blank and empty input, and the error's shortfall reporting. One exact-tokenizer test asserts a rendered prompt fits a 64-tokencl100k_basebudget.Misc
Nothing in Feast imports
feast.contextyet, so the change remains additive and inert.ruff check,ruff format --checkandmypyare clean.