Visitar URL original
feat(dotnet): port predict_tournament and cached_embed_fn by kevin-gatimu · Pull Request #1387 · NandhaKishorM/laya · GitHub
Skip to content

feat(dotnet): port predict_tournament and cached_embed_fn - #1387

Open
kevin-gatimu wants to merge 3 commits into
NandhaKishorM:mainfrom
kevin-gatimu:feat/dotnet-tournament
Open

kevin-gatimu wants to merge 3 commits into
NandhaKishorM:mainfrom
kevin-gatimu:feat/dotnet-tournament

Conversation

@kevin-gatimu

Copy link
Copy Markdown
Contributor

Depends on #1382 (noul labels, on #1377 option_order); only the last commit is new.

Ports Python's predict_tournament and cached_embed_fn to the .NET SDK.

  • LayaShortlist.PredictTournament: rounds of contiguous groups, one predict per round, winners in group order, then one final call with each choice cut to its finalists. Result is the final call's plus Tournament metadata {labels, n, rounds}; same errors and option_order warning as laya/shortlist.py. An overload takes a predict function, so per-call options reach every round as **predict_kwargs do.
  • CachedEmbedFunction: LRU of float32 rows keyed on the exact text, misses embedded in one de-duplicated call, non-finite values cached as 0.0, dimension guard, CacheInfo / CacheClear.
  • Not expressible in the typed API: an unknown winner with list-form criteria (Python lets it through, the SDK raises as for dict criteria; only a custom predictor returning an unshown label can hit it).
  • Goldens plus two model-free probes (Python's own predict_tournament over a scripted predictor, and cached_embed_fn), shared with the Rust PR.

Tests: model-free 2264; lanes english 627, multilingual 639, typed-decisions 639, router 4; 0 failed, 0 skipped.

Copilot AI balanced review requested due to automatic review settings October 26, 2026 20:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@NandhaKishorM

Copy link
Copy Markdown
Owner

Held for integration, not on merit. This is one of nine PRs in the same chain that were each opened against main independently, so they conflict with each other as soon as any one lands. I merged #1370, #1371, #1373, #1374, #1375, #1379 and #1384 clean, and then the rest of the stack collided.

The full conflict map and what I need is on #1377, so I am not repeating it here: short version, pick an order, rebase each onto the one before it, and regenerate golden/*/index.json rather than hand-merging the appended case lists.

For this one specifically, the conflicting paths against the integration branch were:

CHANGELOG.md, README.md, src/Laya/LayaEngine.cs, src/Laya/SequenceBuilder.cs,
tools/dump_golden.py, tools/golden_cases.py,
tests/Laya.Tests/golden/{english,multilingual,typed-decisions}/index.json

NandhaKishorM added a commit that referenced this pull request Oct 28, 2026
24 PRs. Vietnamese kept off the English checkpoint (#1392), deciding-window
attribution preserved through end hooks (#1369), RemoteRouter identifying
itself with a configurable User-Agent (#1396), ten more inputs refused where
they enter (#1372, #1394, #1397, #1398, #1400, #1401, #1402, #1403, #1404,
#1405, #1406), per-language temperatures and per-call budgets in .NET and
Rust (#1370, #1371), .NET calibration files (#1384), the Go router lifecycle,
SHA-256 pins and routed inference (#1374, #1379, #1395), a C++ foundation
(#1399, #1407), and parity-lane sharding that replaces the budget headroom
added for 0.4.16 (#1373, #1375).

Verified before tagging: all 92 suites pass, 640 tests, up from 612. ruff
clean on both lists. laya-ts 1251 tests across 44 files, tsc --noEmit clean.
Every generated-artifact gate clean on CPython 3.12 / unicodedata 15.0.0:
Java 20 families match laya (9 unverified), UnicodeTables matches CPython
15.0.0, LanguageTables matches laya/lang.py, three Go tables_gen.go match,
both laya-cpp .inc files match. Version agrees across pyproject.toml,
laya.__version__ and nix/package.nix. README carries 0.4.17 only.

Security audit of the merge diff, 362766 added lines over 480 files: zero
obfuscation, zero secrets, zero boundary hits, zero binaries, no mode or
symlink changes, 20 of 20 actions SHA-pinned including the new cpp.yml, npm
install hooks unchanged, and no third-party host in any added line
(example.com 90, github.com 2, download.pytorch.org 1, docs.rs 1). The 12
dynamic-exec hits are the C++ test harness invoking its own checker through
sys.executable, the Go sample generator reading laya's own example by AST at
development time, and one comment in rust.yml.

Held: #1364 (unchanged, it moves a cross-port contract), #1381 (a #[doc(hidden)]
but pub Rust hook that would ship as public API able to replace the forward
pass), #1386 (route_batch switched from self.route to self._route, silently
bypassing subclass overrides the docstring promises), and nine stacked port
PRs that conflict with each other and need linearizing (#1377, #1378, #1380,
#1382, #1383, #1385, #1387, #1388, #1389, #1390).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@kevin-gatimu
kevin-gatimu force-pushed the feat/dotnet-tournament branch from 43cc192 to 2d1a8bc Compare October 30, 2026 11:06
@kevin-gatimu

Copy link
Copy Markdown
Contributor Author

Rebased onto #1382 (now on 0.4.17) with goldens regenerated; conflicts resolved. The diff will show #1377 and #1382 until those merge. The router parity lane ran past its 45m timeout on a fork run (tests passed with more time), so it may time out here.

ChoiceQuestion, ScoreQuestion and NoulQuestion gain OptionOrder and
WithOptionOrder (a permutation of the option slots). Options are encoded
and marked in slot order, the softmax runs in slot order and the
probabilities are mapped back to option order before the answer is
built, as laya/common.py and Agent do. The order is validated with
Python's text where Python validates it, joins the router's batch
grouping key, and is dropped with Python's warning when the shortlist
narrows the labels. Existing signatures are unchanged.

Goldens come from tools/regen_golden.py; the case lists are shared
verbatim with the Rust SDK.
NoulQuestion gains Labels, WithLabels and WithoutLabels: the false and
true option labels Python's noul questions accept. They are validated
and stripped as laya/common.py _resolve_noul_labels does, with Python's
text, at prediction as Agent._check_question does; rendered as
"<label>: <criterion or default text>"; part of the router's batch
grouping key. The answer is unchanged (P(true)). Existing signatures are
unchanged.

Goldens come from tools/regen_golden.py; the case lists are shared
verbatim with the Rust SDK.
LayaShortlist.PredictTournament narrows each large choice in rounds of
contiguous groups of groupSize labels, one predict call per round with
the group winners kept in order, then answers the full request once with
each choice cut to its finalists, as laya/shortlist.py predict_tournament
does: same group slicing, round ids, errors and option_order warning; the
result is the final call's with Tournament metadata {labels, n, rounds}.
An overload takes a predict function so per-call options reach every
round, as Python's **predict_kwargs.

CachedEmbedFunction ports cached_embed_fn: an LRU of float32 rows keyed
on the exact text, misses embedded in one de-duplicated call, non-finite
values cached as 0.0, the dimension guard, CacheInfo and CacheClear.

Goldens, a tournament probe and a cache probe come from
tools/regen_golden.py and Python's own functions; the case lists are
shared verbatim with the Rust SDK.
@kevin-gatimu
kevin-gatimu force-pushed the feat/dotnet-tournament branch from 80f1698 to 1361fa1 Compare November 1, 2026 16:35

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.

3 participants