Skip to content

Refactor: slim akd-core — Protocols, ConfigBindingMixin, drop bloat#426

Merged
NISH1001 merged 17 commits into
developfrom
refactor/akd-protocols
Apr 20, 2026
Merged

Refactor: slim akd-core — Protocols, ConfigBindingMixin, drop bloat#426
NISH1001 merged 17 commits into
developfrom
refactor/akd-protocols

Conversation

@NISH1001
Copy link
Copy Markdown
Collaborator

@NISH1001 NISH1001 commented Apr 20, 2026

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

  • Type Protocols — new AKDExecutable, AKDTool, RunContextProtocol structural contracts. Downstream framework adapters can satisfy these without inheriting BaseAgent / BaseTool. Backend code can annotate against the contract instead of the concrete class.
  • ConfigBindingMixin — one mixin now owns config property binding (agent.xagent.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.
  • AbstractBaseMeta removed — 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).
  • MRO reorder in AbstractBaseGeneric[In, Out] now comes before ABC in the bases, matching the layout used by pydantic-ai and most typing-era libraries. Unlocks multi-inheritance with framework classes.
  • Dropped UnrestrictedAbstractBase — sole consumer (_DoclingMetadataExtractor) migrated to a plain utility class.
  • Removed ParamExposureMixin / exposed_param — backend no longer uses runtime parameter exposure; ConfigBindingMixin covers the remaining use case.
  • Folded mandatory mixins into AbstractBaseStreamingMixin + AsyncRunMixin merged in (zero standalone users). ToolCallingMixin folded into AKDAgent (its only consumer).

Minor Changes

  • effective_system_prompt property on BaseAgent — explicit name for the computed prompt (config.system_prompt + description). _system_prompt kept as a backward-compat alias.
  • AKDBaseAgent = BaseAgent alias for naming symmetry with framework adapters (OpenAIBaseAgent, PydanticAIBaseAgent).
  • _format_schema_fields now handles union output schemas uniformly — removes the BaseAgent._output_schema_info override previously needed for that case.
  • validate_input validate_output validate_schema standalone functions exported from akd._base for non-inheriting adapters.
  • Docs updatedCLAUDE.md, docs/specs/AKD_BASE.md, docs/specs/TOOL_CALLING.md, docs/specs/STREAMING.md rewritten to match the new state (no more references to AbstractBaseMeta, UnrestrictedAbstractBase, ToolCallingMixin, etc.).

Bugfixes

  • Granite guardian tests now skip per specific Ollama model availability instead of matching any "granite" substring. Partial model setups (e.g., only one of the required models pulled) no longer mark tests as runnable only to fail mid-call.
  • Union output schemas now produce correct `OUTPUT FIELD DESCRIPTIONS` in agent descriptions.

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)
  • Dead _input_schema_info / _output_schema_info properties
  • Orphaned test cases for removed features

Framework-adapter usage

Any class satisfying the shared contract works out of the box — no inheritance from akd-core required:

from akd._base import AKDExecutable, ConfigBindingMixin
import pydantic_ai

# Adapter skipping BaseAgent entirely — only inherits the framework class
class PydanticAIAgent(ConfigBindingMixin, pydantic_ai.Agent):
    input_schema = MyIn
    output_schema = MyOut
    config_schema = MyConfig

    async def arun(self, params, run_context=None, **kw): ...
    async def astream(self, params, run_context=None, **kw): ...

# Structural conformance check — no inheritance required
assert isinstance(PydanticAIAgent(...), AKDExecutable)

Test plan

  • uv run pytest tests/ — 554 passed, 6 skipped (pre-existing unrelated skips only)
  • akd-ext OpenAIBaseAgent verified against this branch: isinstance(agent, AKDExecutable) passes, MRO resolves cleanly, effective_system_prompt works, name/description binding works
  • Protocol conformance checks pass for BaseAgent BaseTool concrete subclasses

Next Steps

  • Remove unnecessary/unused modules like Deep search agent, code search agent. etc

NISH1001 added 17 commits April 16, 2026 10:56
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
@github-actions
Copy link
Copy Markdown

❌ Tests failed (exit code: 1)

📊 Test Results

  • Passed: 550
  • Failed: 2
  • Skipped: 39
  • Warnings: 178
  • Coverage: 76%

Branch: refactor/akd-protocols
PR: #426
Commit: 4447cd9

📋 Full coverage report and logs are available in the workflow run.

Copy link
Copy Markdown
Collaborator

@sanzog03 sanzog03 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving with grace

@NISH1001 NISH1001 merged commit 0e0494f into develop Apr 20, 2026
1 check passed
@NISH1001 NISH1001 deleted the refactor/akd-protocols branch April 20, 2026 20:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants