Repository navigation
LFX 2026 Term 3 Example Restoration: problem analysis of LangChain/pydantic dependency conflict breaking Ianvs Core #964
Replies: 7 comments
|
Sharing some findings as part of the LFX 2026 Term 3 pretest (full analysis in the linked Discussion). Following up with cross-Example evidence: this same pattern shows up in #535 (government_rag) — different trigger (missing requirements.txt + deprecated LangChain APIs vs. a version-pin conflict here), but same root cause category: LangChain's dependency churn reaching Ianvs Core through sedna.service.server.inference. Also worth flagging: PR #697 adds fastapi+pydantic to the root requirements.txt with no version bounds, which spreads this fragile pairing to every Example's base install rather than containing it. I think the real fix needs explicit pydantic/fastapi version bounds at the Core dependency layer, not per-Example patches — happy to elaborate if useful. (Static/traced evidence only — haven't executed this end-to-end myself.) |
|
Sharing some findings as part of the LFX 2026 Term 3 pretest (full analysis in the linked Discussion). Cross-referencing this with #666 (fastapi IncEx import failure via sedna.service.server.inference) — different symptom, but I think same root cause family: LangChain's version instability isn't being managed at the Ianvs Core/dependency level, so it surfaces differently in each LangChain-touching Example. This one lacks a requirements.txt entirely and hits deprecated LangChain APIs (langchain.text_splitter, RetrievalQA, persist()); #666 hits a pydantic version-pin conflict. Both point toward needing a Core-level dependency policy rather than fixing each Example in isolation. |
|
I'm anchoring this on Issue #666 and Issue #535. Why I picked these two #666 looks small on the surface — just an import error — but it's not actually about one broken Example. fastapi gets pulled into Ianvs through Core itself, via sedna.service.server.inference, and that needs pydantic>=2.9.0 to function. The catch is that literally any Example can break this for everyone. If an Example's requirements.txt installs an old langchain, that langchain forces pydantic down to something like 1.10.22, and now the shared environment is broken — not just for that Example, but for the whole ianvs command, for every user, on every Example. That's what makes this worth flagging as a root problem instead of a one-line bug: it's Core-level blast radius from an Example-level decision. #535 (government_rag) is the second data point. Different symptoms — no requirements.txt at all, and LangChain APIs that are already deprecated upstream (langchain.text_splitter, RetrievalQA, persist() removed from langchain-chroma) — but same root cause family: LangChain's version churn is not something Ianvs currently manages, and it keeps landing on whatever's downstream of it, including Core. Evidence #666's traceback is specific: ImportError: cannot import name 'IncEx' from 'pydantic.main', thrown from fastapi/types.py line 7. This fires when pydantic resolves below 2.9 — exactly the version LangChain 0.0.x-era pins force. Analysis This is Core-related, not Example-specific. The failure path runs straight through Core's own sedna.service.server.inference import — it doesn't matter which Example triggers the bad pydantic resolution, Core breaks either way. #666 and #535 are related but not identical: same root cause category (LangChain's dependency instability reaching shared Core code), different trigger mechanisms — one's a version-pin conflict, the other's deprecated-API breakage. That distinction is why I don't think these should be fixed one Example at a time. PR #697 already does exactly that (patches one Example) and even admits in its own description that the fix doesn't guarantee anything about how pydantic actually resolves — it just moves the risk into the base install. If every contributor fixes their own Example's pin independently, you get inconsistent bounds across the repo and the actual Core-level gap never closes. Uniqueness PR #697 names the unbounded-dependency risk but doesn't fix it — it scopes to one Example and pushes the fragile pair upstream without pinning it. What I'm adding here is the cross-Example pattern: #666 + #535 together show this isn't a one-off, it's what happens by default every time a new LangChain-based Example gets added to Ianvs. My position is that the actual fix belongs at the dependency/packaging layer of Core — explicit lower and upper bounds on pydantic/fastapi compatibility — not scattered per-Example patches that each guess at safe versions. Verification Boundary: I haven't executed either failure path myself yet. This is pure dependency resolution with no GPU/dataset/hardware blocker, so it's fully reproducible — I just haven't run it end-to-end. Everything above is static/traced evidence, not confirmed execution, and I'm marking it as such rather than claiming it passed. |
|
Task 2: Multi-PR Code Review Reviewing three PRs that connect to the root problem from Task 1: #697, #394, and #316. Selection #697 is my critical PR — this one's nowhere near Example-local. It touches the root #394 and #316 both go after Issue #430 — macOS/Windows setup failures on the cloud-edge-collaborative-inference-for-llm example, caused by Linux/GPU-bound deps like Motivation #697 matters because its blast radius is the whole repo, not one Example. Reviewing it tells you whether Core-level changes are being made carefully or not. #394 and #316 matter because they're what happens when two people solve the same Issue two different ways without talking to each other — one actually fixes it, the other just avoids hitting it on certain platforms. Put them side by side and you can see whether Ianvs risks merging both and ending up with inconsistent behavior depending on merge order. Recommendation
Alternative explanation I considered: maybe #316 is deliberate — fail fast instead of silently break. Fair point, but it only holds up if #394 never merges. Since #394 already solves the actual root cause, #316's value depends entirely on the two NOT being coordinated, which is exactly the risk I'm flagging. Review
Reproduce Haven't run any of these three end-to-end yet. No GPU/dataset blocker here — it's dependency resolution, so it's reproducible without special hardware — I just haven't executed it. Same boundary I marked in Task 1: static/traced evidence only, not claiming anything passed. Uniqueness Existing review on #697 already flags the #657 overlap. What I'm adding is tying that conflict pattern to the same category of problem from Task 1 — unscoped root-level dependency changes — and putting #394 and #316 side by side as competing fixes, which I haven't seen discussed together anywhere else. |
|
Task 3: Repair Boundary Analysis Problem Definition Pulling together what Tasks 1 and 2 turned up: the actual problem isn't confined to one Example. #666 and #535 are both symptoms of the same thing — LangChain's pydantic churn hitting shared Core code ( Getting the boundary wrong here isn't hypothetical — it's already happening. #697 already picked "Core/shared" as the layer but did it carelessly (no bounds, direct file conflicts with another PR). That's the exact failure mode this task is asking about. Layers I'm weighing: Dependency/Packaging (root requirements.txt) and Ianvs Core. I'm ruling out Example-local — the whole reason this is a root problem and not a one-off bug is that it's not contained to a single Example's directory, so patching it there just guarantees the next LangChain-based Example repeats the same failure. Evidence A common root cause does exist — it's not Example-local. Scope Decision
Uniqueness #697 already made a Core/root-level change, but did it without bounds or without checking for conflicts with #657 — my analysis provides the missing piece: pinning the bounds, and proposing a CI guard to catch this class of problem before merge instead of after. |
|
Task 4: Restoration Path Design Problem Definition Examples in scope: whichever LangChain-based Example #666 traces back to (broken right now by the pydantic downgrade), government_rag/#535 (broken separately — no requirements.txt, plus dead LangChain APIs), and the 4 Examples #697 touches (cityscapes-synthia, cloud-edge-collaborative-inference-for-llm, fedllm-peft, robot) — these aren't broken yet but they're one bad merge away from it. Why a structured path actually matters here, not just in theory: #697 already shows what happens without one. It tries to fix cityscapes-synthia and ends up reproducing the exact unbounded-dependency problem #666 already flagged, while also colliding with #657 on 19 files. That's not hypothetical risk, that already happened. Scope-wise I'm covering #666, #535, #697, #657, #394, #316 — leaving out #393/#357/#310 since those are dataset/platform issues under the same parent (#430), not part of this dependency chain. Dependency graph, roughly: A root-level bounds fix (pin pydantic/fastapi versions — doesn't exist as a PR yet) has to land before #697 and #657 merge, or they both keep inheriting the same unbounded risk. #697 and #657 conflict directly on 19 files — someone has to reconcile those before either merges. #666 gets fixed by the bounds fix alone, no PR needed beyond that. #535 is completely separate code, can move in parallel with everything. #394 and #316 are going after the same problem on the same Example, so they're not parallel-safe — pick one. Evidence
Path Design Order I'd go: (1) root bounds fix first, since it unblocks everything else and #666 alone. (2) reconcile #697 vs #657's file conflicts. (3) merge the reconciled version. (4) pick #394 over #316 since Task 2 already showed #394 fixes the real problem instead of dodging it. (5) #535 can go anytime, it doesn't touch anything else. Critical path is bounds fix → #697/#657 reconciliation → merge — that's the longest chain and what actually determines when the 4 Examples are safe again. #535 and the #394-vs-#316 call are both off that path, they can run whenever. #535's safe to parallelize because it shares zero files, zero API surface, zero state with anything else here. #394 and #316 aren't — same file, same Example, can't run both. Repair itself: bounds fix is basically a one-line change to root requirements.txt plus a CI check to catch this again. #697/#657 reconciliation is just manually diffing the 19 overlapping files and merging what doesn't conflict. Rollback: if the bounds fix somehow breaks something I haven't tested, revert it — it's one file, low risk either way. If the reconciled #697/#657 merge breaks something in dataset handling (worth flagging: Since bounds fix + #697/#657 affect 4 Examples together, I'd stage it: land the bounds fix, let it sit a few days, then merge the reconciled #697/#657 — not both at once. Verification Gate 1 — bounds fix: run Gate 1 ties straight back to the actual finding from Task 1 (the ImportError) — anyone can check it themselves, no special access needed. After Gate 1 passes, check #666's original repro doesn't fire anymore. After Gate 2, re-run Gate 1 to make sure reconciliation didn't quietly bring back an unbounded dependency. "Restored" here means: all 4 Examples touched by #697 actually run without the IncEx error, government_rag runs its full pipeline end to end, and #666's repro is gone — not just "the error message changed." Uniqueness Nobody's proposed an actual merge order between #697 and #657, or flagged the bounds fix as something that needs to land before either of them — that sequencing argument is mine. Same with the rollback plan scoped specifically to dataset.py given how many PRs are already touching it. Verification Boundary: none of this is executed — it's a plan built off issue reports and PR diffs. Gates 1 and 2 don't need any special hardware, I just haven't run them yet. Gate 3 would actually need both a Windows and a Linux/GPU box to fully check, which I don't have set up. |
|
Bonus Task — Supplementary Review Coverage Background Task 2 stayed inside the LangChain/pydantic dependency thread — Core-level packaging risk. These two reviews go somewhere Task 2 didn't touch: actual algorithm logic bugs inside the paradigm code, not dependency resolution. #573 is Core lifelong-learning training-loop logic; #339 is LLM benchmark runtime crash-handling. Different failure class entirely — logic bugs vs. dependency conflicts — so they genuinely broaden coverage rather than restating Task 2's findings from a different angle. Coverage Map #573 — Ianvs Core, lifelong learning paradigm (core/testcasecontroller/algorithm/paradigm/lifelong_learning/lifelong_learning.py) — fills gap: training-loop correctness bug, unrelated to dependency/packaging Qualification PR #573 — fix(core/lifelong): correct rounds condition to fix HAS_COMPLETED_INITIAL_TRAINING in standard mode Fixes a training-loop condition bug: in standard benchmark mode, the loop starts at r=1, but _train() used rounds < 1 to check if initial training completed — meaning the check never fires as intended in that mode. There's a real trap here worth flagging: the naive fix (rounds <= 1) looks correct for standard mode but silently regresses no-inference and hard-example-mining modes, which are zero-based instead of one-based. A prior review (visible on the PR) already caught this exact regression and the author landed a mode-aware fix instead. My read: the final version correctly distinguishes the two loop conventions, and I'd verify it against the actual mode matrix (standard / no-inference / hard-example-mining) rather than just the standard-mode case that motivated the fix. Review Depth Level: Analysis — I'm reasoning about the mode-matrix implications, not just observing the diff exists. PR #339 — "Fix KeyError crashes and division by zero in LLM inference benchmark" Addresses two separate bug reports on the same Example: #338 (KeyError crashes) and #337 (unguarded divisions crashing on empty datasets). Worth flagging as a Finding: bundling two independently-reported bugs into one PR makes it harder to isolate which fix caused which behavior change if something regresses later — reviewing them together risks treating them as one unit when they're actually two unrelated failure modes (missing-key handling vs. divide-by-zero guards) that happen to share a file. Review Depth Level: Observation — flagging the bundling risk, not yet verified whether the two fixes actually interact in code. Uniqueness For #573: the mode-matrix regression was already caught by a prior reviewer — I'm not claiming that as new. What I'm adding is the verification angle: confirming the final fix holds across all three modes, not just the one that prompted it, since a similar off-by-one mistake elsewhere in the same file wouldn't be caught by testing standard mode alone. For #339, plausible alternative explanation I considered: maybe bundling #338+#337 is fine since they're both "guard against bad input" fixes in the same benchmark file. But #338 is about missing dict keys and #337 is about division by zero — different failure triggers, different fix shapes — so I don't think "same file" is enough to justify one PR; I'd want to see them split so a revert of one doesn't have to revert both. Verification Boundary: Static/traced review only for both — I haven't executed either PR's branch or run the mode-matrix test on #573 myself. |
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Root problem analysis for LFX 2026 Term 3 Example Restoration pretest. Task 1–4 (and Bonus, if applicable) are posted as separate comments below.
All reactions