Tune, auto-detected - how the new tool profiles pick themselves #79
LottieOxford
announced in
Articles
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
The headline of v1.28.0 mentions that the tool surface was consolidated from 33 to 19 and that an automatic tune profile is now the default. Both halves of that sentence deserve a closer look, because the interesting design work is not in the count, it is in how a single config flag (
toolProfile: 'auto') decides at runtime which tools a given model sees, which prompt sections it gets, and how the answer changes the moment you switch models. This post walks through the mechanics: the consolidation itself, the parameter-count heuristic, the 5-layer config hierarchy that resolvestune, the interaction with development modes, and what local-model workflows gain from a profile that re-resolves live on model switch.Built by the Nano Collective - a community collective building AI tooling not for profit, but for the community.
The shortest possible description
Three concrete tool profiles exist (
full,minimal,nano) plus one meta-profile (auto) that picks between them from the active model id. The default isauto, so most users never set anything. The work in v1.28.0 was the consolidation that let the default be sensible, plus the live re-resolution that makes the default honest.A few details worth knowing about that shape:
fullis the empty array. Empty means "no filtering applied" - all registered tools, including MCP servers, remain available. The convention letsminimalandnanobe hand-maintained allowlists whilefullstays correct as new tools land.minimalandnanoby construction: only built-in tool names appear in the allowlists. A user with custom MCP servers who switches to a small model and is surprised at the missing tool is being told, honestly, that the surface area has been trimmed.source/tools/tool-profiles.spec.ts) iterates each profile and asserts that every named tool is registered withToolManager. A renamed tool that leaves a dangling entry behind fails the test on the next CI run, rather than silently filtering nothing.nanodrops theagenttool - subagent delegation is off the table for the smallest models. This is a deliberate scope cut: delegation adds prompt weight, error-recovery complexity, and a turn-multiplier that tiny local models do not benefit from.The consolidation: why 19
The 33-to-19 number is a result, not a target. Three buckets drove it:
write_tasks. The model no longer juggles task ids, completion flags, and per-task update calls; it just writes the current state of the list. That removed three tools and removed a class of bug where the model left stale ids behind.delete_file,move,copy, andcreate_directoryinto onefile_opwith anoperationdiscriminator. Four tools became one. The merger is shape-preserving for the common case and the move/copy/create variants keep their previous semantics under the same schema.status,diff,log,add,commit,pr. Rare operations - branch, checkout, stash, push, fetch, remote management - route throughexecute_bash. The git subagent workflow still uses the typed tools where it adds value; the model can always shell out when it needs the long tail.Roughly 6.3k lines went away across this work, including dedup across config loaders, git exec, command dispatch, and the conversation-loop flush path. The consolidation is also what made
autodefensible as a default: with a hand-curated allowlist for each profile, the heuristic at the top is small enough to be obviously right and the drift guard keeps it that way.The heuristic: how
autopicks from the model idThe decision lives in
inferToolProfile, and it is short on purpose:A few decisions worth being explicit about:
fullby default. A model id with no size hint (gpt-4o,claude-opus-4-8,o3-mini) is treated as "the user is paying for capability, give it the surface area". The threshold for "I know this is small" is a size suffix in the id; the absence of that hint is the signal of capability. A small open-source model without a:NxBtag will be misclassified asfull, which is the safer failure mode - a too-large tool set is a runtime perf issue, not a correctness issue.<=4Bboundary is the nano threshold,<=15Bis minimal, anything larger is full. The spec suite locks these:llama3.2:1b,deepseek-r1:1.5b,gemma2:2b,phi3:3.8b, andsmollm:135mall resolve tonano;qwen2.5-coder:7b,llama3.1:8b,mistral-nemo:12btominimal;gpt-oss:20b,qwen2.5-coder:32b,llama3.3:70btofull.qwen2.5-coder:7b) and base-model references where the size might be earlier in a longer string. Picking the last match is the same heuristic Ollama and LM Studio use when parsing tags; it is the least surprising choice.autopropagates through every helper.isSingleToolProfile('auto', 'llama3.2:1b')returns true.isNanoProfile('auto', 'qwen2.5-coder:7b')returns false.getToolsForProfile('auto', 'qwen2.5-coder:7b')returns the eight-tool minimal list. The auto meta-profile is resolved once per call, and the rest of the codebase never needs to knowautoexists.What the resolved profile actually changes
autoand its resolved concrete profile feed four distinct code paths.1. The tool list.
ToolManager.getAvailableToolNamesis the single authority for tool availability. It runs the tune profile first, then the mode exclusions, then the user-configureddisabledToolslist, in that order. WhentoolProfile: 'auto'resolves tominimalfor a 7B model, the tool list the model sees is the eight-tool allowlist above; the conversation loop builds the prompt against that exact list.2. The system prompt.
buildSystemPromptswitches its sections based on the resolved profile. Undernano, theCORE PRINCIPLESsection is dropped, theTASK APPROACH,FILE OPERATIONS, andCONSTRAINTSsections use the-nanovariants (each capped at a few lines), the verboseSYSTEM INFORMATIONblock becomes a single## SYSTEMline, andAGENTS.mdis omitted by default. The full doc puts the result at roughly 150-250 tokens fornano, versus 500-700 forminimaland the full multi-thousand-token default.3. Single-tool enforcement. Both
minimalandnanoenable single-tool mode in the conversation loop:Small local models struggle to choose one tool from a list and produce a coherent response. Truncating to the first call and forcing a wait-for-result cycle matches how the model wants to work and turns "model tried to do three things and forgot the schema on the second" into a recoverable failure mode.
4. The status bar and
/usage. The development-mode indicator resolves the profile against the live model and showstune: nano (auto)(or justtune: nanoon narrow terminals)./usagerebuilds from the live tune, mode, and model - thetoolDefinitionscount reflects what is actually loaded, and the system-prompt token count reflects the nano-variant sections. The spec explicitly guards this witht.true(nanoBreakdown.toolDefinitions < fullBreakdown.toolDefinitions)so a regression that lets/usagecount the full registry undernanois caught on the next run.The 5-layer config hierarchy
resolveTuneinsource/config/tune.tsis what makes the per-provider example from the docs work:Five layers, highest priority wins:
enabled: false,toolProfile: 'full',aggressiveCompact: false. The "do nothing" baseline.tuneblock inagents.config.json. Applies to every provider.tuneinside a provider entry. The docs' Ollama example pinstoolProfile: 'minimal'andaggressiveCompact: trueto that provider; switching to a different provider falls back to layer 2./tunechoices, persisted tonanocoder-preferences.json. Survives session restarts.The useful consequence is that an operator can ship a project-level
agents.config.jsonwithtune.toolProfile: 'auto'as a global default, layer a per-providertoolProfile: 'minimal'on top of the Ollama provider that everyone on the team uses for the 7B model, and let individual users still flip intonanofrom the/tuneUI without touching config. The merge is shallow (...resolved, ...layer), so a layer that setsenabled: truedoes not erasetoolProfileset by a lower layer - only the keys present on the higher layer win.Interaction with development modes
The tune profile is not the only thing filtering the tool list.
MODE_EXCLUDED_TOOLSremoves mode-incompatible tools on top of the profile filter, and the tuneenabledflag is independent of the mode. In practice, three combinations matter:plan+minimalis the small-model planning setup. The four exploration tools (read_file,find_files,search_file_contents,list_directory) survive the mode exclusions, the slim prompt keeps the model on task, and the typed plan output is the canonical small-model workflow. The companion local-model safety additions in v1.28.0 (read-before-edit guards and tool-call loop detection) cover the failure modes that come up most often when a small model is left running on its own.plan+nanotrims further. The exploration surface isread_fileandsearch_file_contentsonly, the plan-mode prompt uses the nano variant, andfind_filesis gone. Useful on the smallest hardware, where even a glob-style file finder is too expensive to invoke.plan+fullkeeps the full plan surface, including the read-only git tools and the diagnostics. This is the mode for a capable model that needs a structured plan before doing anything destructive.The mode filter runs after the tune filter in
getAvailableToolNames, so a profile that excludesfind_filesand a mode that excludesdelete_filetogether produce the intersection. The order is intentional: the profile says "this model cannot handle the full surface", the mode says "this session is read-only" - whichever is tighter wins.A small but important fix shipped alongside this: the executor used to capture the development mode once at startup and silently ignore any later Shift+Tab switch. That is now resolved at every approval decision, so a subagent that starts in
normaland gets promoted toyolomid-run actually runs inyolo. The tune profile does not change with a mode switch (it follows the model), but the mode policy applied on top of the profile does, which is the interaction that mattered.Live re-resolution on model switch
The
currentModelstate is the only input the profile helpers need, and it is the same React state passed into every consumer that builds a tool list or a prompt. When a user changes model mid-session - via the model picker, a CLI flag, or a config reload - the next render ofbuildSystemPrompt, the next call togetAvailableToolNames, and the next render of the status-bar indicator all see the new model id and produce output against the new resolved profile. There is no explicit "rebuild" call: the profile is recomputed from the live model each time, which is what makestune: nano (auto)switch totune: minimal (auto)the instant you swap fromqwen2.5-coder:1.5btoqwen2.5-coder:7b.warnIfHistoryWontFitruns alongside the switch and surfaces a warning if the previous conversation is too large for the new model's context window. The conversation itself is kept - the model change is not a session reset - but the next turn rebuilds the tool list and the prompt against the new model, so a transition from a 70B cloud model to a 7B local model is a graceful trim rather than a hard cut.For operators who want to pin a profile explicitly, the per-provider config layer is the place. The Ollama example in the docs is the canonical case: a team that uses Ollama for the 7B coding model and OpenRouter for the 70B model gets
minimalautomatically on Ollama andfullautomatically on OpenRouter, without touching the per-session state.What local-model workflows actually gain
In numbers, the gain is not subtle. A 7B model under
autoresolved tominimalsees an eight-tool registry instead of a 19- (or 33-) tool one, and a system prompt in the 500-700 token range instead of the multi-thousand-token default. A 1-3B model underautoresolved tonanosees five tools, a 150-250 token prompt, single-tool enforcement on top, andAGENTS.mdomitted. Both also skip theagenttool entirely, which removes a class of recovery bug that small models hit when subagent output drifts out of their context window.In workflow terms:
nanoprofile plus an unattended small model is a different shape of risk than the pre-v1.28.0 setup.autoresolved to, so you can see the trim happen rather than guessing whether the smaller tool set is in effect.What is still rough
A few honest limits:
:NxB(orNxM) hint getsfull. The safe failure mode, but worth knowing if you have a custom Ollama tag likemy-fine-tunefor a small base.minimal/nanois "exclude", not "reduce". A user with a dozen MCP servers and a small local model sees none of them, not a curated subset. Curating per-profile MCP allowlists is reasonable future work but is not in v1.28.0.autois silent on capability boundaries it cannot see. A 70B model on a slow inference server will still getfull; the profile does not account for throughput. If your cloud model is hitting context-budget or latency issues, the lever is still/tune, not the auto heuristic.Closing
The v1.28.0 tool consolidation was not really about the count. It was about making it cheap to ship hand-curated tool allowlists, which made it cheap to make
autothe default, which made the five-linemodelParamsBillionsheuristic the thing the user actually sees. The 5-layer config hierarchy is what lets an operator tune the default without forcing it, the development-mode filter is what keeps the profile honest about read-only and approval policy, and the live re-resolution is what makes the whole thing feel like one runtime rather than five configs you have to remember to flip.If you try
autoon a small local model and it picks the wrong profile, or if you want a model size to map differently, the easiest place to push issource/tools/tool-profiles.ts- the heuristic is small and the spec is the spec. Open an issue on the Nanocoder repo if you hit something that the heuristic cannot read.Beta Was this translation helpful? Give feedback.
All reactions