feat(recursive-loop): serve trained mlxlm/opd adapters via the scenario-bound resolver#1060
Merged
Conversation
…io-bound resolver Closes the last wiring step of the recursive loop for capable models. A run can now use the instruct fine-tune (mlxlm/opd LoRA adapter) a prior run trained and auto-activated, not just from-scratch mlx GPT checkpoints. - runner: record base_model + score_conditioned on the published registry record (via completion.metadata) so an adapter checkpoint carries the base it needs and the quality-prefix flag for score-conditioned serving. - scenario_bound_clients: add a pure plan_local_client() routing decision (mlx full checkpoint -> MLXClient; mlxlm/opd adapter -> MLXLMClient(base, adapter, score_conditioned=...)), resolve the active record capable-backend-first, and build the planned client. Adapter records missing a base_model fall back rather than crash. - tests: CI-safe (JSON registry, no MLX load) coverage of the routing decision, the resolution preference, and the publish -> route round-trip.
…ner path (review #1060) [P1] adapter backends (mlxlm/opd/grpo) advertised runtime_types=["checkpoint"] only, but the resolver queries runtime_type="provider" -> every runner-published adapter fell back instead of serving. Now that MLXLMClient serves base+adapter, these advertise ["provider", "checkpoint"]. The prior tests used ["provider"] directly and masked it; they now drive runtime_types from the backend's own supported_runtime_types(). [P2] a default 'autoctx train --backend mlxlm/opd' leaves config.base_model empty (the subprocess applies the backend default), so the recorded base was empty -> unservable. Backends now expose default_base_model() (the effective student/base), and the runner records 'config.base_model or backend.default_base_model()'. Also extend the bridge to grpo (mlx-lm LoRA, same serving path) so advertising provider on it is truthful; update the stale opd runtime-types assertion to the corrected contract; add regression tests for both findings + a full default-CLI publish->resolve->route case.
…ree module (CI) default_base_model() lazy-imported the autoresearch backend modules, which import mlx at module top -> ModuleNotFoundError on the no-mlx CI runner (passed locally on Mac). Extract the default base/student/teacher model ids to a dependency-free model_defaults.py and source them from there in both backends.py (now mlx-free) and the autoresearch modules (re-export, back-compat). Single source of truth: the base the runner records == the base the training subprocess trains against. Verified default_base_model() imports no mlx-scoped module.
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.
What
Closes the last wiring step of the recursive loop for capable models. Until now the loop could only auto-serve from-scratch
mlxGPT checkpoints (#1055). This routes an activemlxlm/opdLoRA adapter the harness trained and auto-activated back into the next run's agent role viaMLXLMClient(#1058's serving capability).The two halves wired here:
Publish carries what serving needs.
runner._publish_best_modelnow recordsbase_modelandscore_conditionedon the published registry record (throughcompletion.metadata). An adapter checkpoint is useless without the base it was trained against, and a score-conditioned model must be re-prompted with the quality prefix at inference.The resolver routes by backend.
scenario_bound_clientsgains a pureplan_local_client(record)decision:mlxfull checkpoint →MLXClient(checkpoint)(unchanged)mlxlm/opdadapter →MLXLMClient(base, adapter_path, score_conditioned=...)base_model, or unknown backend →None(fall back to the default client rather than serve something broken)The active record is resolved capable-backend-first (
opd→mlxlm→mlx), so an instruct fine-tune wins over the from-scratch GPT when both are active for a scenario. An explicitAUTOCONTEXT_MLX_MODEL_PATHstill takes precedence.Tests
CI-safe (JSON-file registry, no MLX load, no training): the routing decision for each backend, the score-conditioned flag carry-through, the unservable-adapter / unknown-backend fallbacks, the resolution preference, and a
publish_training_output→plan_local_clientround-trip that mirrors exactly what the runner wires up.Full local sweep green: new bridge suite +
test_mlx_agent_resolution+test_scenario_routing+test_model_registry+test_model_router_integration+test_module_size_limits; ruff + mypy clean.