Repository navigation
feat(dotnet): add .NET SDK (Laya.Onnx) - #671
Conversation
|
Holding this for a maintainer decision rather than on anything wrong with the code, and I want to be clear about which it is so you can judge whether to keep investing. What you have built is careful. 10,100 lines of C# with 32,631 lines of golden fixtures generated against the Python path, covering all three checkpoints, both ONNX layouts, the router, language detection, email and shortlist. Golden parity fixtures are exactly how I would want a port verified, and running on ONNX Runtime with no Python process at runtime is squarely inside what open-source laya is for. What I cannot do in a merge pass is take on a second language toolchain. Concretely:
That is a real cost and it is mine to accept or decline, not yours to fix by writing more code. So: I am leaving this open and taking the decision rather than sitting on it silently. If you want to improve the odds, the single highest-value thing is a CI workflow that builds the project and runs the golden tests on every push, with the fixtures regenerated by a script in the repo so a Python-side change that breaks parity shows up as a red check. With that in place the drift argument goes away and this becomes a much easier yes. Same note on #672. |
…ery push Cache pinned fused and split exports, regenerate Python fixtures, and gate checkpoint-specific parity runs on TRX results. Build the SDK and samples separately, reject unexpected skips, and cover cache and skip-guard regressions.
b291f27 to
4f22e90
Compare
|
Thanks, that's a fair framing, and the CI lane is the right ask. It's in now. What's added (
Evidence it gates drift. Locally, each deliberate break turned it red:
The first real run on my fork also went red. It found genuine drift in language detection, router Still true, and your call: NuGet publishing and its release process are deliberately not part of |
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Cache identity, tokenizer initialization races, and Unicode parity issues remain unresolved.
Review effort: Balanced
Findings: 4
Open (4)
What changed in this PR
Adds the Laya.Onnx .NET SDK for in-process, local inference without a Python sidecar.
Changes:
- Introduces typed questions, sequence building, calibration, and ONNX configuration.
- Adds parity tests and Python-generated fixtures for three checkpoints.
- Provides samples, benchmarking tools, and package configuration.
| File | Description |
|---|---|
| laya-dotnet/tools/test_ci_tools.py | Tests skip guards and export-cache validation. |
| laya-dotnet/tools/sample_cases.py | Defines shared sample inputs. |
| laya-dotnet/tools/requirements-regen.txt | Pins fixture-generation dependencies. |
| laya-dotnet/tools/hashing_embedder.py | Defines reference demo embeddings. |
| laya-dotnet/tools/check_test_skips.py | Rejects unexpected test skips. |
| laya-dotnet/tests/Laya.Tests/TokenizerParityTests.cs | Checks tokenization and sequence parity. |
| laya-dotnet/tests/Laya.Tests/StubTokenizer.cs | Supplies a model-free tokenizer stub. |
| laya-dotnet/tests/Laya.Tests/ShortlistEndToEndTests.cs | Checks model-backed shortlist parity. |
| laya-dotnet/tests/Laya.Tests/RoutingGoldenData.cs | Loads routing reference fixtures. |
| laya-dotnet/tests/Laya.Tests/RouterEndToEndTests.cs | Tests routing and concurrent checkpoint switching. |
| laya-dotnet/tests/Laya.Tests/PythonJsonTests.cs | Checks Python-compatible JSON serialization. |
| laya-dotnet/tests/Laya.Tests/ModelArtifactsTests.cs | Tests artifact layouts and path resolution. |
| laya-dotnet/tests/Laya.Tests/LayaEmailTests.cs | Tests email cleaning and state construction. |
| laya-dotnet/tests/Laya.Tests/Laya.Tests.csproj | Configures the xUnit test project. |
| laya-dotnet/tests/Laya.Tests/HfTokenizerFallbackTests.cs | Tests special-token resolution. |
| laya-dotnet/tests/Laya.Tests/HashingEmbedderTests.cs | Checks demo embedding parity. |
| laya-dotnet/tests/Laya.Tests/HashingEmbedder.cs | Implements test embeddings. |
| laya-dotnet/tests/Laya.Tests/golden/typed-decisions/meta.json | Records typed-decisions reference configuration. |
| laya-dotnet/tests/Laya.Tests/golden/typed-decisions/index.json | Indexes typed-decisions cases. |
| laya-dotnet/tests/Laya.Tests/golden/typed-decisions/case_single_option.json | Records single-option behavior. |
| laya-dotnet/tests/Laya.Tests/golden/typed-decisions/case_mask_injection.json | Records mask-token handling. |
| laya-dotnet/tests/Laya.Tests/golden/typed-decisions/case_choice_criteria_list.json | Records list-based choice behavior. |
| laya-dotnet/tests/Laya.Tests/golden/routing/meta.json | Records routing fixture provenance. |
| laya-dotnet/tests/Laya.Tests/golden/multilingual/meta.json | Records multilingual reference configuration. |
| laya-dotnet/tests/Laya.Tests/golden/multilingual/index.json | Indexes multilingual cases. |
| laya-dotnet/tests/Laya.Tests/golden/multilingual/case_single_option.json | Records single-option behavior. |
| laya-dotnet/tests/Laya.Tests/golden/multilingual/case_mask_injection.json | Records mask-token scrubbing. |
| laya-dotnet/tests/Laya.Tests/golden/multilingual/case_list_state.json | Records conversation-list serialization and inference. |
| laya-dotnet/tests/Laya.Tests/golden/multilingual/case_choice_criteria_list.json | Records list-based choice behavior. |
| laya-dotnet/tests/Laya.Tests/golden/english/meta.json | Records English reference configuration. |
| laya-dotnet/tests/Laya.Tests/golden/english/index.json | Indexes English cases. |
| laya-dotnet/tests/Laya.Tests/golden/english/case_single_option.json | Records single-option behavior. |
| laya-dotnet/tests/Laya.Tests/golden/english/case_mask_injection.json | Records mask-token handling. |
| laya-dotnet/tests/Laya.Tests/golden/english/case_choice_criteria_list.json | Records list-based choice behavior. |
| laya-dotnet/tests/Laya.Tests/CollatorTests.cs | Tests batch padding and tensors. |
| laya-dotnet/tests/Laya.Tests/CalibrationTests.cs | Tests probability and confidence calculations. |
| laya-dotnet/src/Laya/Tokenization/ILayaTokenizer.cs | Defines the tokenizer contract. |
| laya-dotnet/src/Laya/SequenceBuilder.cs | Builds question sequences and marker positions. |
| laya-dotnet/src/Laya/Questions.cs | Defines typed questions and ordered sets. |
| laya-dotnet/src/Laya/LayaPresets.cs | Supplies workflow question presets. |
| laya-dotnet/src/Laya/LayaOptions.cs | Defines checkpoint and runtime options. |
| laya-dotnet/src/Laya/LayaConfig.cs | Parses checkpoint configuration and temperatures. |
| laya-dotnet/src/Laya/Laya.csproj | Configures SDK packaging and dependencies. |
| laya-dotnet/src/Laya/ILayaPredictor.cs | Defines the common prediction interface. |
| laya-dotnet/src/Laya/Collator.cs | Constructs padded inference batches. |
| laya-dotnet/src/Laya/Calibration.cs | Calculates calibrated probabilities and confidence. |
| laya-dotnet/samples/Laya.Sample/Program.cs | Demonstrates typed predictions. |
| laya-dotnet/samples/Laya.Sample/Laya.Sample.csproj | Configures the quickstart project. |
| laya-dotnet/samples/Laya.Sample/.gitignore | Excludes generated sample files. |
| laya-dotnet/samples/Laya.Routing/Laya.Routing.csproj | Configures the routing sample. |
| laya-dotnet/samples/Laya.Routing/DemoHashingEmbedder.cs | Supplies demo shortlist embeddings. |
| laya-dotnet/samples/Laya.Benchmark/README.md | Documents benchmark usage and metrics. |
| laya-dotnet/samples/Laya.Benchmark/Program.cs | Measures latency, memory, and answers. |
| laya-dotnet/samples/Laya.Benchmark/Laya.Benchmark.csproj | Configures the benchmark project. |
| laya-dotnet/samples/Laya.Benchmark/.gitignore | Excludes generated benchmark files. |
| laya-dotnet/Laya.slnx | Groups SDK, tests, and samples. |
| laya-dotnet/global.json | Selects the SDK and test runner. |
| laya-dotnet/Directory.Build.props | Sets shared build properties. |
| laya-dotnet/CHANGELOG.md | Documents initial features and known gaps. |
| laya-dotnet/.gitignore | Excludes build output and local artifacts. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| var lowered = text.ToLowerInvariant(); | ||
| return lowered.Contains('İ') ? lowered.Replace("İ", "i̇") : lowered; |
| public string HuggingFaceSubfolder | ||
| { | ||
| get => _huggingFaceSubfolder ?? CheckpointSubfolder(Checkpoint); | ||
| set => _huggingFaceSubfolder = value; |
| ids.Add(tokenizer.SepId); | ||
|
|
||
| var room = Math.Max(0, maxLen - ids.Count - 1); | ||
| var stateIds = tokenizer.Encode(Scrub(PythonJson.State(state), mask)); |
| /// instead. Each test receives the checkpoint under test and loads the tokenizer inline, so missing | ||
| /// artifacts for one checkpoint skip only that checkpoint's cases. | ||
| /// </remarks> | ||
| public sealed class TokenizerParityTests |
#671's lane is the drift detection I asked for, and it works: the goldens are re-recorded from the Python package at each commit and never cached, so a change to laya/ that moves an answer turns this lane red. Blocking on it would invert the dependency. The Python package is the product and laya-dotnet follows it, so a red .NET check on a Python pull request would stop that Python change until someone ports it to C#. The tax would land on whoever improved the core rather than on whoever maintains the port, and at the current merge rate that is most pull requests. Both jobs now carry `continue-on-error`, so the lane still runs on every push with no path filter, still attributes drift to the pull request that caused it, and no longer gates it. security.yml's optional-dependency CVE audit is advisory for the same reason ("visible here, not blocking PRs"). When this lane goes red the fix is a follow-up port, not a revert. Nothing else changes: #671 touches no file under laya/, and no gate in tests/test_packaging.py or scripts/test_suites.py references the .NET tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Merged for 0.3.26, with one change on the way in that I want to explain rather than slip past you. You met the condition, and you met it properlyI said the blocker was drift: 77k lines in a language nothing here builds means the golden fixtures stop being checked the moment this lands. What you added closes that, and the parts I checked are the parts that matter:
And the first real run finding genuine drift in language detection, router reasons, email cleaning and the options-exceed-head message, which you then ported from Python without loosening a tolerance, is better evidence than the gate design itself. That is the behaviour of someone who will keep it in step. The change I made: the lane is advisory, not blockingBoth jobs now carry The reason is direction, and it is a flaw in how I framed the ask rather than in what you built. Because the goldens regenerate from the Python side, a required lane means an improvement to
If you would rather it were blocking, make the argument and I will listen, but that is the one thing I would want to be deliberate about. What I am not claimingI have not reviewed the C# to the standard I hold
Worth stating for anyone reading later: this PR touches no file under Verified hereThe shared suite list reports all 79 suites passed, Thank you for taking the CI ask seriously instead of arguing it. That is what moved this. |

Closes #673
What is this
A .NET SDK in
laya-dotnet/: a port of the Python SDK's inference path (tokenization, sequence building, calibration, answers) running the official checkpoints exported to ONNX, on ONNX Runtime. No Python process runs at runtime.What it does
choice/score/noulanswers, withconfidenceandanswer_confidence, for all three checkpoints (multilingual, english, typed-decisions). It supports both the single-file and the split ONNX layouts.Laya.Onnx0.1.0 on NuGet, versioned on its own. It's ready to publish, but I haven't published anything; that's yours to do.What it unlocks
.NET services can call Laya in-process, with no Python sidecar.
Verification
laya(Python) 0.3.21 bylaya-dotnet/tools/dump_golden.pyand are checked in undertests/golden/. There are 17 cases per checkpoint, plus tokenizer and JSON probes and routing probes.skip:and the stub and math tiers still run.Notes for reviewers
laya-dotnet/changes. No PyPI, Python package, or CI changes.langandemailstill follow Python 0.3.6. Their goldens were re-dumped, and they are unchanged.min_confidence/ abstention,predict_longandlang_temperaturesfeatures aren't ported yet.tools/export_onnx.pythemselves, and one set of exports would serve every ONNX-based SDK.laya-dotnet-v*) and publishing to NuGet;dependency CVEscheck was already failing onmainbefore this PR. It's tracked in [bug]: dependency CVEs job in security.yml fails on main: 7 advisories in dependencies of optional extras #645 and unrelated to this change.