Refactor: slim akd-core — Protocols, ConfigBindingMixin, drop bloat#426
Merged
Conversation
AKDExecutable, AKDTool, RunContextProtocol as structural supertypes. Downstream framework adapters (pydantic-ai, etc) can satisfy these without inheriting from AbstractBase/BaseAgent/BaseTool directly. Also adds AKDRunContext alias for disambiguation.
Skips fields the framework class already owns to avoid shadowing. _bind_metadata() handles name/description/IO hints without AbstractBaseMeta.
- AbstractBase bases now just (ABC, metaclass=AbstractBaseMeta), zero mixins - astream/_astream moved from StreamingMixin into AbstractBase directly - run() moved from AsyncRunMixin into AbstractBase directly - Both mixin classes still exist standalone for any external consumers - Removed AsyncRunMixin from __init__.py exports (no longer on AbstractBase)
- Remove akd/agents/extraction.py (zero external consumers) - Remove create_extraction_agent from factory.py (never called) - Inline ExtractionInputSchema in test_mappers.py (only consumer was the test)
- Single validate_schema() core with union + dict coercion support - validate_input wraps with coerce_dict=True - validate_output wraps without dict coercion - Framework adapters call these directly, no mixin needed
- AbstractBase._validate_input/_validate_output now call validate_input/validate_output - Keeps the instance methods for backward compat with existing subclasses - Removes duplicated validation logic from _base.py
- AbstractBase now inherits ConfigBindingMixin - Remove _make_config_property/_make_computed_property helpers from _base.py - Remove _create_config_properties from AbstractBaseMeta (handled by mixin now) - _post_init calls self._bind_metadata() instead of inline name/desc/IO hint logic - Metaclass now only handles schema validation - Identical behavior for all 44 existing agent subclasses and 20 tool subclasses
- Remove AbstractBaseMeta entirely — schema validation moves to AbstractBase.__init__ - No more hardcoded class name skip list; abstract/intermediate classes can be defined freely, validation fires at instantiation where it matters - Remove UnrestrictedAbstractBase (only consumer was _DoclingMetadataExtractor, which is now a plain utility class — didn't need the framework) - Update scraper utility: _DoclingMetadataExtractor becomes a plain class with arun() instead of _arun() to match its one call site - Clean up exports in akd/_base/__init__.py and akd/__init__.py
- Desugar PEP 695 on AbstractBase to explicit TypeVar + Generic[T] - Puts Generic first in bases, matching the (Generic, ABC) layout used by pydantic-ai's AbstractAgent and most typing-era libs - Unlocks multi-inheritance with third-party framework classes that follow the (Generic, ABC) convention - BaseAgent and BaseTool keep their PEP 695 syntax — they inherit AbstractBase's already-fixed Generic ordering via MRO
- AKDAgent was the only consumer — mixin methods move onto the class directly - _find_tool, _execute_tool, _execute_tools_parallel now live on AKDAgent - tool_calling.py keeps ToolCall and ToolResult data models (used by adapters) - Drop ToolCallingMixin from akd._base exports
- Old is_granite_available() matched any "granite" substring, so tests would try to run even when the specific model they needed wasn't pulled - Replace with requires_model(GuardianModelID.X) decorator that checks exact model availability via Ollama's /api/tags - Each test now declares its required model(s); skip message tells you which "ollama pull" command is needed - Reuse GuardianModelID enum from akd core for model names (single source)
- Remove akd/nodes/ entirely — backend copied their own version into akd-framework; akd-core copy has zero external consumers - Remove tests/nodes/ (tested only the removed code) - Trim common_types.py: drop ToolType and AnyCallable (both dead); keep CallableSpec inlined since backend still imports it - ToolRunner no longer inherits AsyncRunMixin — it defines arun directly and nothing used the mixin's sync run() wrapper - Modernize tools/utils.py type hints (Optional/Union/Dict → | / dict)
…schema_fields - Remove AbstractBase._input_schema_info / _output_schema_info (dead after ConfigBindingMixin took over metadata binding) - Remove BaseAgent._output_schema_info override (orphaned, super() path gone) - Remove orphaned tests in test_instructor_base.py - Move union-output handling into _format_schema_fields() in config_binding.py; now supports both single and union types uniformly (input unions too, bonus) - Remove now-unused get_model_fields imports
…-compat alias - effective_system_prompt is the actual prompt sent to the LLM (config.system_prompt + agent description) - Renamed from _system_prompt to drop the misleading private-prefix; the computed result is the public thing callers should use - _system_prompt kept as a thin alias for subclasses that reference it - config.system_prompt stays pristine — computation happens on every access
- Backend no longer uses runtime parameter exposure; drop the feature - Remove ParamExposureMixin from BaseAgent bases - Replace the one @exposed_param usage in deep_search.py with a plain @Property - Delete akd/_base/exposure.py (257 lines) and its test file - BaseAgent MRO is now clean: BaseAgent -> AbstractBase -> Generic -> ConfigBindingMixin -> ABC
Matches the naming pattern used by framework adapters (OpenAIBaseAgent, PydanticAIBaseAgent) — AKDBaseAgent is the akd-native abstract agent base. BaseAgent still works for backward compat.
- CLAUDE.md: remove UnrestrictedAbstractBase reference; add ConfigBindingMixin and AKD*-protocol entries to the exports table; drop StreamingMixin / ToolCallingMixin mentions (no longer separate classes users need to know about) - docs/specs/AKD_BASE.md: rewrite mechanism section around __init_subclass__ + ConfigBindingMixin. Remove UnrestrictedAbstractBase section, metaclass implementation block, and hardcoded skip list. Add framework-adapter usage section. - docs/specs/TOOL_CALLING.md: drop standalone ToolCallingMixin section; note that helpers now live directly on AKDAgent - docs/specs/STREAMING.md: astream() now lives on AbstractBase directly; remove StreamingMixin and ToolCallingMixin from exports list
|
❌ Tests failed (exit code: 1) 📊 Test Results
Branch: 📋 Full coverage report and logs are available in the workflow run. |
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.
Flattens the akd-core base class hierarchy, removes dead abstraction layers, and introduces opt-in type protocols so future framework adapters (pydantic-ai, etc.) can inherit their framework class directly and still satisfy akd's contract without paying a proxy tax.
~1200 lines deleted, ~200 added. No runtime behavior changes for existing agent/tool subclasses.
Major Changes
AKDExecutable,AKDTool,RunContextProtocolstructural contracts. Downstream framework adapters can satisfy these without inheritingBaseAgent/BaseTool. Backend code can annotate against the contract instead of the concrete class.ConfigBindingMixin— one mixin now owns config property binding (agent.x→agent.config.x) plus name / description / IO-hint wiring via__init_subclass__+_bind_metadata(). Collision-aware so it plays nicely with framework classes that declare conflicting attributes.AbstractBaseMetaremoved — schema validation moved to__init__time (instantiation), eliminating the hardcoded class-name skip list that akd-core had to maintain for every intermediate base class (including those in downstream packages).AbstractBase—Generic[In, Out]now comes beforeABCin the bases, matching the layout used by pydantic-ai and most typing-era libraries. Unlocks multi-inheritance with framework classes.UnrestrictedAbstractBase— sole consumer (_DoclingMetadataExtractor) migrated to a plain utility class.ParamExposureMixin/exposed_param— backend no longer uses runtime parameter exposure;ConfigBindingMixincovers the remaining use case.AbstractBase—StreamingMixin+AsyncRunMixinmerged in (zero standalone users).ToolCallingMixinfolded intoAKDAgent(its only consumer).Minor Changes
effective_system_promptproperty onBaseAgent— explicit name for the computed prompt (config.system_prompt+ description)._system_promptkept as a backward-compat alias.AKDBaseAgent = BaseAgentalias for naming symmetry with framework adapters (OpenAIBaseAgent,PydanticAIBaseAgent)._format_schema_fieldsnow handles union output schemas uniformly — removes theBaseAgent._output_schema_infooverride previously needed for that case.validate_inputvalidate_outputvalidate_schemastandalone functions exported fromakd._basefor non-inheriting adapters.CLAUDE.md,docs/specs/AKD_BASE.md,docs/specs/TOOL_CALLING.md,docs/specs/STREAMING.mdrewritten to match the new state (no more references toAbstractBaseMeta,UnrestrictedAbstractBase,ToolCallingMixin, etc.).Bugfixes
Dead code removed
akd/nodes/package (zero external consumers; backend has its own copy)akd/agents/extraction.py(zero external consumers)common_types.py:ToolType, `AnyCallable(unused);CallableSpec` kept (backend still imports it)_input_schema_info/_output_schema_infopropertiesFramework-adapter usage
Any class satisfying the shared contract works out of the box — no inheritance from akd-core required:
Test plan
uv run pytest tests/— 554 passed, 6 skipped (pre-existing unrelated skips only)OpenAIBaseAgentverified against this branch: isinstance(agent, AKDExecutable) passes, MRO resolves cleanly, effective_system_prompt works, name/description binding worksBaseAgentBaseToolconcrete subclassesNext Steps