Release the Ollama model after a run instead of holding it resident - #59
Open
tmartin2113 wants to merge 1 commit into
Open
Release the Ollama model after a run instead of holding it resident#59tmartin2113 wants to merge 1 commit into
tmartin2113 wants to merge 1 commit into
Conversation
Ollama keeps a model loaded after a request for OLLAMA_KEEP_ALIVE -- five
minutes by default, indefinitely when it is set to -1. MiroFish's models are
large (granite4:small-h is ~19 GB), so on a shared Ollama instance one run
can keep every other model out of memory long after the work is finished.
The obvious fix does not work. Ollama's OpenAI-compatible endpoint accepts a
`keep_alive` field and silently ignores it, so passing one through the
OpenAI client looks correct in review and does nothing. Verified on Ollama
0.32.1 with qwen3:0.6b:
POST /v1/chat/completions {"keep_alive": "30s"} -> UNTIL "Forever"
POST /api/chat {"keep_alive": "30s"} -> UNTIL "29 seconds from now"
So LLMClient.unload() posts keep_alive=0 to the native /api/generate --
what `ollama stop <model>` does -- deriving the native root by stripping the
/v1 suffix from the configured base URL.
It is called from a `finally` at the three places that drive the LLM, so a
failed run frees the model too:
- report generation (api/report.py)
- prepare_simulation (services/simulation_manager.py), which covers both
the profile and config generators, they being phases 2 and 3 of one call
- standalone profile generation (api/simulation.py)
Safe by construction: it is a no-op for non-Ollama providers, never raises
(a failure to free memory must not fail the caller), and can be turned off
with LLM_UNLOAD_AFTER_USE=0 to restore the previous behaviour.
Tested against a live Ollama: a resident model with 9 minutes left on its
keep-alive is released immediately; the non-Ollama, opt-out and
unreachable-host paths all return False without raising.
Happy to flip the default to opt-in if you would rather not change existing
behaviour -- that is one word in the env lookup.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Problem
Ollama keeps a model resident after a request for
OLLAMA_KEEP_ALIVE— five minutes by default, indefinitely when set to-1. MiroFish's models are large (granite4:small-his ~19 GB), so on a shared Ollama instance a single run can keep every other model out of memory long after the work has finished.On my box the report model was evicting the GPU-resident model used by everything else, and staying loaded until something forced it out.
Why the obvious fix doesn't work
Passing
keep_alivethrough the OpenAI client does nothing. Ollama's OpenAI-compatible endpoint accepts the field and silently ignores it — no error, no warning — while the native API honours it. Verified on Ollama 0.32.1 withqwen3:0.6b, watching theUNTILcolumn ofollama ps:UNTILPOST /v1/chat/completions{"keep_alive": "30s"}Forever— ignoredPOST /api/chat{"keep_alive": "30s"}29 seconds from nowWorth knowing independently of this PR: a
keep_aliveadded toextra_bodywould look correct in review and have no effect.Change
LLMClient.unload()postskeep_alive=0to the native/api/generate(whatollama stop <model>does), deriving the native root by stripping the/v1suffix from the configured base URL.It's called from a
finallyat the three places that drive the LLM, so a failed run frees the model too:api/report.pyprepare_simulation—services/simulation_manager.py, which covers both the profile and config generators, they being phases 2 and 3 of one callapi/simulation.pySafety
_is_ollama().LLM_UNLOAD_AFTER_USE=0restores the current behaviour exactly.Testing
Against a live Ollama, exercising the committed source directly:
/v1→ native URL rewriteFalse, no request sentLLM_UNLOAD_AFTER_USE=0→False, no request sentFalse, no exception escapesAlso run end to end on a real 2-section report and a
generate-profilescall: model released after each, andgenerate-profilesstill returned its profiles normally.No tests are included since the repo has no suite to extend — happy to add one if you'd like a particular shape.
Note on the default
This defaults to on, which is a behaviour change: the next run reloads the model. For a single dedicated Ollama that's a small cost; for a shared one it's the whole point. Happy to flip it to opt-in if you'd rather not change existing behaviour — it's one word in the env lookup.
🤖 Generated with Claude Code