Repository navigation
feat: managed MTPLX backend - #17
Draft
alextra-lab wants to merge 20 commits into
Draft
alextra-lab wants to merge 20 commits into
alextra-lab wants to merge 20 commits into
Conversation
Adds a managed `backend: "mtplx"` design: launcher flags, router translation of chat_template_kwargs, startup and error handling, a declared memory budget, tests, and a live verification plan. No code changes. The spec builds on llamacpp-pinned-build, so that branch merges to main first. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Answers the 11 findings from the Seshat review: - A disabled model id returns 404 instead of 503; router-raised errors stay unscored by the watchdog. - The memory guard refuses two or more heavy entries when any lacks peak_memory_gib. - Effort keys in an MTPLX entry's config kwargs are a validation error; only request kwargs are lifted to top level. - HTTP 507 gets the watchdog's ignore verdict, not caller fault. - mtp_depth is checked against the pack's mtp_depth_max. - MTPLX cached and reasoning tokens reach the request span. - Documents the background warm-up after the port opens, the missing keep-alive on non-streaming requests, the client-disconnect limit, the merge order, and a Seshat end-to-end verification step. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
The router's first-byte stall rule clears on any backend chunk, so MTPLX keep-alive comments (and llama.cpp's SSE ping) hide a wedged backend. Stall detection for MTPLX relies on its own stream-stall deadline; generation-aware health (FRE-1474) is the complete fix. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
FRE-1474 recommends a Seshat-side generation probe that detects a dead backend but cannot restart one. The spec now treats generation-aware health as an owner decision spanning FRE-1474 and slm_server's watchdog, not as a fix already tracked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
BACKEND_LOG_NAME only matched llama/mlx prefixes, so mtplx-*.log files grew without bound. Add mtplx to the pattern and cover it with a test. Also correct config.py Field descriptions for temp/top_p/top_k/ presence_penalty/chat_template_kwargs/reasoning_parser, which claimed llamacpp- or mlx-only use even though mtplx uses them too. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
README's watchdog table still said all 5xx trip a restart; add the 507 exception (MTPLX's memory-refusal status, which the code already ignores). Add mtplx to the field reference: the backend row, the shared llamacpp/mtplx fields, the log-name pattern, and a new MTPLX fields table with the seven mtplx-only fields. Note that preserve_thinking, like reasoning_effort, must stay out of an MTPLX entry's chat_template_kwargs. Fix CLAUDE.md's "five command builders" list (previously named four) and add MTPLX to the architecture diagram. Correct the spec's Telemetry section, which described the cache_reuse fallback as MTPLX-specific when it applies to any backend without llama.cpp timings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL
…ors, 400 for request-level 500s
Owner
Author
|
Merged Merge condition 1 is met. PR #16 merged into The merge brings in from
Verified in an isolated env with the dev, mlx and llamacpp extras, importing from this branch: 287 tests pass (main's 230 plus 57 from this PR), and Still a draft. Conditions 2 (the FRE-1517 study window has ended) and 3 (live verification, spec §8 step 3) are open and need the owner's call. |
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
Adds a managed MTPLX backend (
backend: "mtplx"): the launcher startsmtplx serve, the router routes to it by model id, and the watchdog restarts it. llama.cpp stays the default backend.Spec:
docs/superpowers/specs/2026-09-14-mtplx-backend-design.mdPlan:
docs/superpowers/plans/2026-09-14-mtplx-backend.mdDraft: do not merge yet. Merge only when all three conditions hold:
llamacpp-pinned-build) has merged intomain. This branch contains that PR's commits. After feat: pin the llama.cpp build, add sidecar draft models, fix backend reliability #16 merges, merge or rebasemainhere, so that this diff shows only the MTPLX work.Changes
Config (
config.py)backend: "mtplx"and the fieldsmtp_depth,reasoning_effort,preserve_thinking(defaultoff),mtplx_profile,mtplx_batching_preset(unset = serial),mtplx_fan_mode, andpeak_memory_gib(all backends).config.jsonandmtplx_runtime.json.mtp_depthmust be at most the pack'smtp_depth_max. Effort keys are rejected inside an MTPLX entry's configchat_template_kwargs. A warning is given for llama.cpp-only fields, a serialmax_concurrencyabove 1, an unusualhost, and a fan mode without the thermalforge socket.check_memory_budget():SLM_MEMORY_BUDGET_GIB(default 100). An error for two or more heavy entries where any lackspeak_memory_gib, or a declared total over the budget.Launcher (
start_backends.py,start.sh)find_mtplx_binary()(SLM_MTPLX_BIN, thenPATH, then~/.mtplx/bin/mtplx) andbuild_mtplx_command(). Always--host 127.0.0.1 --no-auth.main()exits before starting any backend when the memory budget reports an error.start.sh:SLM_BACKEND_READY_TIMEOUT(default 180 s) replaces the fixed readiness loop.Watchdog (
watchdog.py)ignoreverdict: no restart, and no reset of the failure streak.mtplx-<id>-<port>.log.Router and telemetry (
router.py,telemetry.py)_prepare_chat_template_kwargs()at chat completions and both/v1/responsesfallbacks. For MTPLX it merges config and request kwargs (request wins) and lifts a request'sreasoning_effortto the top level. llama.cpp and MLX behaviour is unchanged.cache_reusefalls back tousage.prompt_tokens_details.cached_tokenswhen llama.cpptimingsare absent. Newreasoning_tokensdoc field andslm.reasoning_tokensspan attribute.Docs
config/models.yaml.example: a disabled MTPLX entry. CLAUDE.md: module descriptions.Behaviour changes for existing clients
llm_client,sub_agentandexecutor).reasoning_tokenskey.cache_reusenow also fills in for backends without llama.cpptimings, such as MLX.reasoning_effortorpreserve_thinkingvalue outside the new allowed values now fails config validation. The owner's live config still loads.start.shwaits up toSLM_BACKEND_READY_TIMEOUT(default 180 s) for backend ports. A backend that never comes up now takes up to 3 minutes to report, not about 40 s.start.shexits 1, and the whole server stays down, the router included. Two or more enabledlmormultimodalentries where any lackspeak_memory_gibis an error. Before the first restart on this code, runcheck_memory_budget(load_model_config())against the live config.[]. Warning: the one enabled heavy entry has nopeak_memory_gib. A single heavy entry only warns, so today's config starts.peak_memory_gibon both entries. Otherwise the launcher refuses to start.Tests
uv run pytest285 passed,uv run ruff check src/ tests/clean,uv run mypy src/clean.uv run ruff format --check src/ tests/flags only the 5 rerank files that already fail onmain.test_config_mtplx.py,test_start_backends_mtplx.py,test_memory_budget.py,test_router_mtplx.py. Extended:test_watchdog.py,test_router_watchdog.py,test_router_model_selection.py,test_router_rerank.py,test_backend_log_trim.py,test_start_backends_main.py.Test plan
cache_reuseandreasoning_tokens, which requires the final usage chunk in the streammainmerged into this branch (10972fe). Mac at 10972fe: pytest 285 passed, ruff check clean, mypy clean.Known limits (not in this PR)
stop.shhas no sweep for an orphanedmtplx serveprocess. Processes are stopped by port.🤖 Generated with Claude Code
https://claude.ai/code/session_01HKZSVJuS3ehHkGcCt6msQL