feat(plugins): backend-engine-agentic-synthesizer plugin (Phase 6 #53)#288
Conversation
Ships `plugins/backend-engine-agentic-synthesizer/` — contributes the `langgraph-default` `AgenticTaskProvider` for both the `synthesizer` and `super_synthesizer` tasks declared in `GraphTypeComposition.agentic_tasks` on the default graph type. Identity is `(task_name, provider_id)` — the registry narrows by `task_name` first, so a single `langgraph-default` id covers both tasks via two `AgenticTaskContribution` entries. Matches the shape `DefaultGraphTypePlugin.composition().agentic_tasks` declares. Per-call delegation pattern: 1. Dispatcher (synthesis worker) packs `agent_context`, `state`, and optional `recursion_limit` into `AgenticTaskContext.options`. 2. Provider constructs the relevant `SynthesizerAgent` / `SuperSynthesizerAgent` with the supplied AgentContext + model override, compiles the LangGraph graph, invokes `ainvoke`. 3. Returns the raw agent output — downstream Hatchet tasks narrow at their typed boundary. Lazy-imports `kt_worker_synthesis` inside `run()` so the plugin's `pyproject.toml` doesn't reverse the workers-depend-on-plugins direction. Contract: the synthesis worker must co-install this plugin for registry dispatch to work; if a worker loads the plugin without the agent package, `run` raises `ImportError` — fail-fast signal, not silent degradation. Fail-fast paths: - `task_name` mismatch (registry misrouting) → `ValueError` - Dispatcher forgot to pack `agent_context` → `RuntimeError` Registered in `kt_config.plugin.load_default_plugins`. **No worker wiring this PR** — `synthesizer_wf` + `super_synthesizer_wf` continue to construct agents directly until a follow-up threads `plugin_registry.get_agentic_task_provider` through the dispatchers (mirrors the definition / sync / source-cache / source-contribution plugin-then-wire sequencing). Tests: 9 contract tests — plugin identity, `is_enabled` default, both `(task_name, provider_id)` contributions, factory round-trip, registry dispatch, task_name-mismatch rejection, missing-agent_context rejection, happy-path delegation (LangGraph compile + ainvoke) for both synthesizer and super-synthesizer.
CI Backend Lint + every unit+integration job failed with: error: The lockfile at `uv.lock` needs to be updated, but `--frozen` was provided: Missing workspace member `kt-plugin-be-agentic-synthesizer`. The plugin package was added to plugins/ but its workspace entry didn't make it into uv.lock in the initial commit — the lock update was dropped when reverting the release-bot version bump. Re-run `uv lock` so every uv.sync (CI runs --frozen) picks up the new workspace member.
#1 Raise default recursion_limit 100 → 500. Legacy workflows compute max(explore_budget * 30, 500); the old default silently truncated normal runs partway through if the dispatcher forgot to pack the knob. 500 matches the legacy floor so a regressed packing path stays viable; dispatchers still SHOULD pass their own computed limit. Module-level _DEFAULT_RECURSION_LIMIT constant names the floor. #2 Drop the `self._gateway` dead field. Agents pick up the gateway off the dispatcher-supplied AgentContext at run time, so stashing the factory argument was cargo-cult and hid the real dependency edge. __init__ now consumes+discards the gateway to match the AgenticTaskContribution factory shape. #3 State coercion caveat documented in module docstring. Workflows pass Pydantic SynthesizerState today; the wiring-PR dispatcher MUST pass either a model_dump() or a raw model instance so the agent's validators still run. dict(options.get('state')) narrows a Pydantic instance silently — flagged at the boundary. #4 Extend _ctx test helper with model_id_override kwarg. Removes the rebuild-the-context dance in the synthesizer delegation test (previously had to reconstruct AgenticTaskContext just to set the override). #5 Factor shared plumbing into _LanggraphAgentProviderBase. Subclass diff is three classvars (_task_name, _module_path, _agent_attr); run() + constructor + task_name/provider_id properties live on the base. A third task keyed by the same provider id lands as another one-liner subclass. Tests: 9 contract tests green. Recursion-limit assertion updated to 500 with a doc comment explaining the floor.
|
Applied review feedback in commit dfd174a: #1 recursion_limit 100 → 500. Module-level #2 Dropped #3 State coercion caveat documented in module docstring. #4 #5 Factored common base. Tests: 9 contract tests green; default-recursion-limit assertion updated from 100 → 500 with a doc comment explaining the floor. Full pre-push suite green. |
|
I have read the CLA Document and I hereby sign the CLA You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot. |
Summary
plugins/backend-engine-agentic-synthesizer/— contributes thelanggraph-defaultAgenticTaskProviderfor both thesynthesizerandsuper_synthesizertasks.What changes
AgenticTaskContributionentries, both keyed by(task_name, provider_id="langgraph-default")— matchesDefaultGraphTypePlugin.composition().agentic_tasks.agent_context,state, optionalrecursion_limitintoAgenticTaskContext.options; provider constructs the relevant LangGraph agent with suppliedAgentContext+ model override, compiles the graph, invokesainvoke.kt_worker_synthesisinsiderun()so the plugin'spyproject.tomldoesn't reverse the workers-depend-on-plugins direction. Worker must co-install plugin; missing agent package →ImportError(fail-fast signal).task_namemismatch →ValueError; missingagent_contextoption →RuntimeError.kt_config.plugin.load_default_plugins. No worker wiring this PR —synthesizer_wf/super_synthesizer_wfcontinue to construct agents directly until a follow-up threadsplugin_registry.get_agentic_task_providerthrough the dispatchers (mirrors plugin-then-wire sequencing across feat(plugins): backend-engine-fact-decomposition plugin (Phase 4 #44) #279 / feat(plugins): backend-engine-definition plugin (Phase 5 #47) #283 / feat(plugins): backend-engine-sync plugin (Phase 5 #49) #284 / feat(plugins): backend-engine-source-cache plugin (Phase 5 #50) #285 / feat(plugins): backend-engine-source-contribution plugin (Phase 5 #50) #286).Test plan
uv run --project plugins/backend-engine-agentic-synthesizer pytest -x -v(9 contract tests green — plugin identity,is_enabled, both(task_name, provider_id)contributions, factory round-trip, registry dispatch, task_name-mismatch rejection, missing-agent_context rejection, happy-path delegation for both tasks viasys.modulesinjection)uv run --project libs/kt-config pytest -x -v(236 green)uv run --frozen ruff format --check ./ruff check .🤖 Generated with Claude Code