feat: provider plugin lifecycle management (closes #162)#170
Open
luceinaltis wants to merge 2 commits into
Open
feat: provider plugin lifecycle management (closes #162)#170luceinaltis wants to merge 2 commits into
luceinaltis wants to merge 2 commits into
Conversation
Add optional initialize/health_check/shutdown hooks for data and LLM providers. Adapters without hooks remain supported — the helpers duck-type each method so partial implementations are fine. - New qracer/provider_lifecycle.py exposes LifecycleProvider protocol plus async/sync helpers (initialize_provider, shutdown_provider, shutdown_all_providers). - _build_registries() now runs initialize + health_check on each adapter before registration; failures are warned and the provider is excluded instead of crashing the registry build. - qracer serve's finally block calls shutdown_all_providers_sync so long-lived adapters (HTTP sessions, WebSockets) get a clean teardown on signal. - 23 new tests cover protocol detection, async/sync hooks, unhealthy and raising paths, dedup across shared adapters, and the running-loop guard in the sync wrappers. - Architecture docs refreshed with a working third-party adapter example and the startup/shutdown flow.
Collapse a multi-line function signature that was over-wrapped for the CI ruff-format check.
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.
Summary
Closes #162.
Adds the missing lifecycle layer to the provider plugin system. Adapters (built-in or entry-point-discovered) can now opt into
initialize()/health_check()/shutdown()hooks._build_registries()runs the startup hooks before registration and skips unhealthy providers with a warning instead of letting them crash on first use;qracer serveruns the teardown hooks on exit.All three hooks are optional — existing adapters (
YfinanceAdapter,FinnhubAdapter,FredAdapter,ClaudeAdapter,OpenAIAdapter,GeminiAdapter) keep working unchanged because the helpers duck-type each method.What changed
qracer/provider_lifecycle.py(new) — self-contained lifecycle module:LifecycleProvider— runtime-checkableProtocolcovering all three hooks.initialize_provider(name, adapter)— async helper. Runsinitialize()thenhealth_check(); returnsFalsewhen a hook raises or reports unhealthy (both paths logged as warnings). Adapters without hooks are alwaysTrue.shutdown_provider(name, adapter)— async helper. Swallows exceptions so a bad provider can't block the rest of the teardown sweep.initialize_provider_sync/shutdown_all_providers_sync— sync wrappers for use from CLI setup code. Detect an already-running loop and no-op instead of blowing up (defensive — the real call sites are sync).shutdown_all_providers(*registries)— walks the_adapters/_providersbuckets ofDataRegistryandLLMRegistry, deduping adapters registered under multiple capabilities/roles.qracer/cli.py::_build_registries— for both data- and llm-provider branches, callinitialize_provider_sync(name, adapter)after instantiation. If it returnsFalse, append a human-readable warning and skip registration. Existing credential-missing and import-error paths are untouched.qracer/cli.py::serve— wrapsasyncio.run(server.run())in afinallythat callsshutdown_all_providers_sync(data_registry, llm_registry). A secondtry/exceptguards the shutdown itself so the PID-file release always runs.docs/architecture.md— replaces the "구현 예정" note in the Provider Plugin System section with the actualLifecycleProviderprotocol, a working third-party adapter example (PolygonAdapter), and the updated startup + shutdown flow diagram.Scope mapping (#162)
initialize()/health_check()/shutdown()protocolLifecycleProviderinqracer/provider_lifecycle.pyproviders.tomltoprovider_catalog.py_build_registries()(config.providers.providers loop)_build_registries()andServer.shutdown()_build_registries; shutdown inserve()finallyhealth_check()failurePolygonAdapterexample indocs/architecture.mdTest plan
uv run pytest— 801 passed, 14 skipped (23 new lifecycle tests).uv run pytest tests/test_provider_lifecycle.py tests/test_cli.py -x— 24 passed.uv run ruff check— all checks passed.uv run pyright qracer/provider_lifecycle.py tests/test_provider_lifecycle.py— 0 errors. (qracer/cli.pyreports one pre-existinguvicornimport warning unrelated to this change — reproduced onmain.)New tests cover
LifecycleProvider, no-hook adapter does not.initialize_provider— async + sync hook styles, both hooks absent, unhealthyhealth_check,initializeraises (health_check skipped),health_checkraises.shutdown_provider— async + sync hook styles, no-hook no-op, exception swallowed.initialize_provider_syncandshutdown_all_providers_syncexercised under a live loop.shutdown_all_providers— dedupes an adapter registered under multiple capabilities; supports both_adapters(DataRegistry-style) and_providers(LLMRegistry-style) buckets; empty registry is a no-op; mixed data+llm registries handled in a single call; one failing sibling doesn't prevent others from shutting down.Manual verification