refactor(engine): extract InfraStack — infra assembly & lifecycle container#164
Merged
Conversation
added 2 commits
July 6, 2026 19:39
…d Engine property integration
…cture components This commit updates the test cases and the `Engine` class to consistently use the `infra` property for accessing infrastructure components such as `meta`, `milvus`, `artifact_cache`, and others. This change ensures that all infrastructure-related operations are properly encapsulated within the `InfraStack` and aligns with the recent addition of the `InfraStack` and its integration with the `Engine` class.
4 tasks
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.
Overview
Moves the code that "constructs the 8 process-level infra clients" and "connects / initializes / closes storage" in
startup/shutdownout ofEngineand into a newInfraStackcontainer component.Engineholdsself.infra, and all handle access goes throughself.infra.<handle>(eng.infra.<handle>in tests) — there are no longer anyEngine-level forwarding properties.This is the Engine infrastructure-layer extraction. It is a prerequisite for subsequent phases (refactoring, optimization, new logic): all business components depend on this set of infra clients, so first consolidating them into an independently testable container lets later components be constructed cleanly with
InfraStackas a dependency.Why it's needed
Engineis a god class of ~60 methods with 9 coupled responsibility areas. On the infrastructure side it has three specific problems:Assembly scattered across three places: the 8 infra clients (
meta/milvus/artifact_cache/tx_cache/embed/converter/vlm/summary) are constructed in__init__, connected/initialized instartup, and closed inshutdown— spread across three sites, with the construction-order dependency (tx_cachemust precedeembed/vlm/summarybecause they cache through it) expressed only in implicit code ordering.Ownership is not explicit: ~50 sites in
Engineaccess these clients directly viaself.meta.execute(...)/self.milvus.upsert(...), with nothing in the code showing who ownsmetaor manages its lifecycle.Tests can only go E2E: infra assembly is coupled to
Engine, soInfraStackcannot be constructed in isolation to verify its connect sequence, preload path, or shutdown order — the only option is to spin up the wholeEngineand run the full pipeline.After extracting
InfraStack: construction + lifecycle live in one place, ~85 lines that can be read independently; every call siteself.infra.metamakes ownership explicit;InfraStackis independently unit-testable (mock the 8 factories, assert the call sequence).Changes
New
engine/infra.py(82 lines)The
InfraStackclass, single responsibility: construct the 8 infra clients + own thestartup/shutdownlifecycle.__init__: constructs in dependency order (tx_cachebeforeembed/vlm/summary).startup(*, preload_local_models=False):load_builtin()→meta.connect→meta.init_schema→tx_cache.connect→milvus.connect→milvus.ensure_collection→ optional_preload_models._preload_models(): preloads the local ONNX embedding provider (only when the provider declares it should be preloaded), so the first embed call doesn't pay the model-load latency.shutdown(): closes onlymeta+tx_cache;milvus/artifact_cachehave no lifecycle methods and are left untouched.Enginechanges (engine/engine.py)__init__: 8 lines of infra construction →self.infra = InfraStack(cfg).startup: original steps 1–7 (load_builtin + storage connect + optional preload) folded intoawait self.infra.startup(...); steps 8–11 (_build_pipeline/_gc_orphan_chunks/_recover_job_lane/ job watcher) stay inline, to be moved out withPipelineSupervisorin 4b.shutdown: the trailingmeta.close()+tx_cache.close()folded intoawait self.infra.shutdown(); the preceding watcher/lane/consumer shutdown stays inline._preload_startup_models(logic moved intoInfraStack._preload_models).Engine-level properties (getter+setter formeta/milvus/artifact_cache/tx_cache/embed/converter/vlm/summary) — see design decisions below. 47 sitesself.<handle>→self.infra.<handle>.api/app.py1 site:
eng.meta.backend→eng.infra.meta.backend(the only place outsideEnginethat reads an infra handle).Tests
tests/test_infra_stack.py(233 lines, 5 unit tests): construction order +tx_cachesharing, startup sequence, preload path, shutdown closes only meta+tx_cache, Engine exposes handles viaeng.infra.eng.<handle>→eng.infra.<handle>(both reads and writes). This is the monkeypatch contract change — tests go fromeng.milvus = _FakeMilvus()toeng.infra.milvus = _FakeMilvus().File list
engine/infra.pyengine/engine.py_preload_startup_models; 47 sites rewrittenapi/app.pytests/test_infra_stack.pytests/test_engine_*.pyetc.eng.X→eng.infra.XDesign decisions
Handle access goes directly through
self.infra.<handle>, with noEngine-level properties. An earlier version added 8 read-write properties as a compatibility shim (so the ~47self.X+ 258eng.Xsites wouldn't need to change), but that was pure forwarding boilerplate (~32 lines of zero-logic code) that hid ownership and created two access paths. Going direct withself.infra.Xmakes ownership visible at every call site, with zero boilerplate and a single path. The cost is ~300 mechanical replacements, verified by tests.load_builtin()lives inInfraStack.startup. It is process bootstrap (registers built-in connector plugin classes; idempotent), at the same "make the process ready" level as connecting storage. This leaves the remaining lines ofEngine.startuppurely pipeline semantics, paving the way for 4b to move those lines wholesale intoPipelineSupervisor.Did not change the constructor signatures of already-shipped components.
ObjectRepository/ArtifactCacheService/ConnectorFactorystill take specific dependencies (meta/cfg/artifact_cache) rather than the wholeInfraStack— avoiding cross-file churn and risk with no behavioral benefit. Only the futurePipelineSupervisor(4b) will takeInfraStack, because it needs the seven-item set (embed/milvus/tx_cache/...)._gc_orphan_chunksstays onEngine. It is a startup reconcile (spanning milvus + objects), semantically a pipeline health check rather than an infrastructure connection. It moves withPipelineSupervisorin 4b; this PR leaves it alone.Did not add
close()formilvus/artifact_cache. The currentshutdowncloses onlymeta+tx_cache; that is the existing behavior (MilvusStorehas noclosemethod,LocalArtifactCachehas no lifecycle). Adding a close is a behavior change and belongs in a separate issue; this refactor introduces none.Behavioral equivalence
InfraStack's methods replicate the lifecycle in exactly the same order as the originalEngine:startupsteps 1–7 sequence:load_builtin→meta.connect→meta.init_schema→tx_cache.connect→milvus.connect→milvus.ensure_collection→ optional preload, matching one-to-one.shutdownsteps 4–5:meta.close→tx_cache.close;milvus/artifact_cacheuntouched (same as before)._preload_models'sshould_preload_on_server_startshort-circuit logic is line-for-line identical to the original_preload_startup_models.InfraStackconstructed, read byEngineviaself.infra.<handle>— identity is preserved, behavior unchanged.Engine.startup/shutdownexternal signatures and semantics are unchanged; call sites inapi/app.pyand__main__.pyare unchanged (only one site inapp.pychangeseng.meta.backend→eng.infra.meta.backend, reading the same object).Test impact
New unit tests (
tests/test_infra_stack.py, 5 tests):tx_cacheshared byembed/vlm/summary.startupcall sequence is exactly[load_builtin, meta.connect, meta.init_schema, tx_cache.connect, milvus.connect, milvus.ensure_collection];_preload_modelsis not called whenpreload_local_models=False.preload_local_models=Truewith a provider that opts in firespreload_provider; short-circuits whenshould_preloadis False.shutdownis exactly[meta.close, tx_cache.close];milvus.close/artifact_cache.closeare not called.Engine(cfg), the 8 handles live oneng.infra, not oneng;eng.infra.milvus = fakecan override.Existing tests mechanically rewritten: 258 sites across 20 files,
eng.<handle>→eng.infra.<handle>. This is the follow-on change from the monkeypatch contract change — tests inject fakes intoeng.infra.<handle>instead ofeng.<handle>. No logic changes;ruff formatpasses throughout.Regression result: full
uv run --extra dev pytest— 356 passed / 11 skipped, behavior-equivalent to before the refactor.Pre-existing failures (unrelated to this PR, confirmed by reproducing on baseline
072849awithgit stash):tests/test_connector_factory.py::TestValidateConfig, 2 tests (config validation not raisingValueError) — fail on the baseline.tests/test_feishu_oauth.py/tests/test_enumeration_since.py, 2 collection errors — the optional connector SDKlark_oapiis not installed (documented in CLAUDE.md).Risk & rollback
startup/shutdownsequences are preserved step-for-step, and handle identity is preserved. The main risk is a missed site among the ~300 mechanical rewrites — covered by the full test suite (356 passed).git revert 9fae00f d91d314fully reverts the change, with no side effects.