ollama_endpoint#
What a project's start script needs to know before it relies on a local model: which models the project uses, which of them an Ollama already has, and whether the machine can take the rest. Nothing is pulled until that last question has an answer.
A project lists its models by purpose in one env-style file:
and a start does, in one call:
source scripts/script-helpers/helpers.sh
shlib_import logging ollama_endpoint
# "|| exit": a refusal must stop the start.
ollama_project_ensure_models ai-models.env .env || exit $?
That reads the project's .env as data (it is never sourced), takes the
address from OLLAMA_URL, OLLAMA_BASE_URL or OLLAMA_HOST, and pulls what is
missing only when it fits. The same in steps, for a start that needs its own
order:
models="$(ollama_models_required ai-models.env OLLAMA_MODEL CLASSIFY_MODEL OLLAMA_EMBED_MODEL)" || exit $?
# shellcheck disable=SC2086 # one model per word
ollama_endpoint_ensure_models "http://127.0.0.1:11434" "$(ollama_models_dir)" $models || exit $?
Works against an Ollama on this machine and one in a container alike: every
question is asked over its HTTP API, and the ollama CLI is not needed.
Requires curl, awk, sed, grep, df, tail, tr, cut, dirname.
Runs on bash 3.2 and BSD userland, under set -euo pipefail or without it.
Expected imports#
logging (print_info, print_warning, print_error). Every message goes
to stderr; stdout carries only what a function is documented to print.
What a message shows of the configuration: an address is named by its scheme, host and port, never with its credentials, its path or its query (a proxy path may hold a key), and a value that could not be read as an address is named by its variable and not shown at all.
The models file#
One assignment per line, read as an env file is read: export before the
name, spaces around =, one pair of quotes around the value, a trailing
# comment and a carriage return are not part of the value. A byte-order
mark at the start of the file is ignored.
Environment#
OLLAMA_PULL_MISSING-- off never pulls; a missing model is then a refusal. ask pulls only after a yes on a terminal, once the budget check has passed; no answer, or no terminal (stdin and stderr), is a refusal. Default on.OLLAMA_DISK_RESERVE_GB-- free space that must remain after the download. Default10. A GB is 10^9 bytes throughout, as Ollama shows a model's size.OLLAMA_MEM_HEADROOM_PERCENT-- added to the largest model's size for the memory check (context, runtime). Default20.OLLAMA_IGNORE_BUDGET-- on turns each refusal of the budget into a warning. Default off.OLLAMA_REGISTRY_URL-- where model sizes are asked. Defaulthttps://registry.ollama.ai.OLLAMA_REGISTRY_TIMEOUT-- seconds per size request. Default15.OLLAMA_PULL_STALL_SECONDS-- a pull is given up when nothing arrives for this long. Default600. There is no deadline for the whole download.OLLAMA_BUDGET_DISK_FREE_BYTES,OLLAMA_BUDGET_MEM_TOTAL_BYTES,OLLAMA_BUDGET_MEM_AVAILABLE_BYTES,OLLAMA_BUDGET_GPU_BYTES-- state a figure instead of having it read. The figures read are this machine's: for an Ollama on another machine, state all four (ollama_project_ensure_modelsrequires the first two and never counts this machine's own for the other two).
On is 1, true, yes or on; off is 0, false, no, off or never; case does not matter.
Any other value is reported and read as off: nothing is pulled, and no check is skipped.
Numbers are decimal: 08 is eight. A value that is not a whole number of at most 15 digits is not used.
Functions#
- ollama_models_file_get file NAME
- Purpose: Print the value of
NAMEin a models file. - Behavior: The last line for a name wins; a longer name does not match.
-
Returns: 0, printing an empty line when the file or the name is absent; 2 when
NAMEis not a variable name (it is never put into a pattern unchecked). -
ollama_models_file_names file
-
Purpose: Print every name the file assigns, one per line, in file order, each once.
-
ollama_model_tagged model
- Purpose: Print the model as Ollama lists it: with a tag (
:latestwhen none is given), without the default registry's host or itslibrary/namespace. -
Behavior: The tag is looked for in the last path segment only, so
registry.example:5000/team/modelgets:latestrather than being read as tagged5000/team/model. -
ollama_models_file_roles file
- Purpose: Print the roles a models file names (
NAME), one per line, in file order. - Behavior: Every assigned name except a role's class columns (a name ending in
_SMALL,_LARGEor_XLARGE), their floors (_MIN_USABLE_VRAM_GB,_VRAM_GBor_RAM_GBafter that) and theAI_TIER_settings: those are figures for the pick, not models. -
Returns: 0, also when the file is missing.
-
ollama_models_required file [NAME...]
- Purpose: Print the models a start needs, as Ollama lists them, one per line, each once.
- Behavior: With no
NAME, the file's roles (ollama_models_file_roles). Each name gives the model this machine gets (ollama_model_for_class): a non-blank value in the environment wins, trimmed; load the project's.envfirst if it should count. -
Returns: 0; 2, printing nothing, when a
NAMEis not a variable name,AI_MODEL_TIERis not a class, or a reserve in the file is not valid (checked before any name, so a typo is never an empty list). A list cut short at the bad name would start a project with some of its models. 1, printing nothing, whenfileis named and is not there: a mistyped path would otherwise read as "needs no models". Pass""for no file, to take the names from the environment alone. -
ollama_endpoint_models base_url [timeout_seconds=5]
- Purpose: Print the models the Ollama at
base_urlhas, as it names them. - Behavior: A
namenested inside a model's details is not a model. An answer spread over lines is read the same as one on a single line. -
Returns: 0; 1 when nothing answers there, or when what answers is not an Ollama (a web server that says 200 to everything).
-
ollama_models_missing needed present
- Purpose: Given two newline-separated lists, print the needed models that are not present.
-
Behavior: Whole names only:
qwen:7bis not satisfied byqwen:7b-instruct. Letter case does not count, as it does not to Ollama. -
ollama_registry_size_bytes model
- Purpose: Print the size of the model's download in bytes, from the registry manifest.
- Behavior: Sums every layer and the config. Layers the machine already holds for another model are counted again, so the figure can only overstate. An answer without
layers(a list of manifests, a web page) is not a manifest and is not summed. -
Returns: 0; 1 when the registry does not answer, answers with something else, or the argument is not a model reference; 2 for a model on another registry host (for example
hf.co/...orlocalhost:5000/...), which is not asked for; 3 when the registry answers that it has no such model or tag (HTTP 404 withMANIFEST_UNKNOWNorNAME_UNKNOWN). A 404 from anything else is 1: a mistypedOLLAMA_REGISTRY_URLis not a mistyped model. -
ollama_models_dir
- Purpose: Print where an Ollama on this machine keeps its models, for the disk check.
- Behavior:
OLLAMA_MODELSwhen set (Ollama's own variable); else/usr/share/ollama/.ollama/modelswhen it or the service's home/usr/share/ollamaexists, because the Linux installer sets Ollama up as a service with a user of its own, whose home is often closed to other users; else~/.ollama/models(an Ollama started by hand, and macOS). The directory need not exist yet. -
Note: not for an Ollama in a container; pass Docker's data root there (
docker info -f '{{.DockerRootDir}}'). -
ollama_disk_free_bytes path
-
Purpose: Print the bytes free on the filesystem holding
path, or its nearest existing parent. Prints nothing when it cannot be told. -
ollama_mem_total_bytes / ollama_mem_available_bytes
-
Purpose: Print the machine's memory, and what a new process could have now. Linux reads
/proc/meminfo; macOS readssysctl hw.memsizeandvm_stat(free plus inactive pages). Prints nothing when it cannot be told. -
ollama_gpu_mem_bytes
-
Purpose: Print the memory of this machine's GPUs together, or
0: Ollama spreads a model over them. NVIDIA fromnvidia-smi, AMD from the amdgpu driver's/sys/class/drm/cardN/device/mem_info_vram_total(a connector such ascard0-DP-1is not a GPU). A driver that cannot be reached counts as no GPU. Apple silicon shares memory with its GPU and is covered by the total.OLLAMA_BUDGET_GPU_BYTESstates it. -
ollama_gpu_mem_largest_bytes
- Purpose: Print the memory of the largest single GPU, or
0. A model runs on one GPU, so this, not the sum, says which models the machine can run: two 12 GB cards are not a 24 GB card.OLLAMA_BUDGET_GPU_LARGEST_BYTESstates it. -
Per system: Linux,
nvidia-smiand amdgpu sysfs. Windows (Git Bash), every display adapter's dedicated memory from the registry (HardwareInformation.qwMemorySize, throughpowershell.exe). macOS, an Intel Mac's discrete card fromsystem_profiler; on Apple silicon (hw.optional.arm64, so also under Rosetta) two thirds of the machine's memory, the share macOS lets its GPU use.ollama_gpu_mem_bytesstays 0 there, so the budget check does not count that memory twice. -
ollama_machine_figures
-
Purpose: Print
<memory GiB>:<largest GPU GiB>, whole GiB rounded down, the two figures the class is picked by. Memory is empty (:0) when it cannot be read. -
ollama_model_for_class file NAME
- Purpose: Print the model this machine gets for
NAME: the highest class column it qualifies for that names a model, elseNAME. - Classes (fleet model registry; figures in whole GiB as the machine reports them, so a 32 GB machine is 31), by usable capacity:
xlarge: its largest GPU has at leastNAME_XLARGE_MIN_USABLE_VRAM_GBusable.large: its largest GPU has at leastNAME_LARGE_MIN_USABLE_VRAM_GBusable, or memory hasAI_TIER_LARGE_USABLE_RAM_GBusable.small: less thanAI_TIER_SMALL_USABLE_RAM_GBusable in memory and in the largest GPU (a small machine with a good GPU is not small).- otherwise the default,
NAME.
- Usable: the largest GPU less
AI_TIER_GPU_RESERVE_GBGiB, memory lessAI_TIER_RAM_RESERVE_PERCENTpercent (rounded down); both 0 when not set. A usable floor is compared as the installed figure that leaves it, so the result is exact. - Older fields:
NAME_XLARGE_VRAM_GB,NAME_LARGE_VRAM_GB,AI_TIER_LARGE_RAM_GBandAI_TIER_SMALL_RAM_GBare installed figures, compared as before: the reserves do not apply to them. A column with both uses the usable one. - A class with no model for
NAMEfalls to the next column below (small to the default). A non-blankNAMEin the environment wins on any machine.AI_MODEL_TIER=small|standard|large|xlargenames the class instead of measuring (mediumisstandard, any case). Memory that cannot be read picks the default, never a guessed class. -
Returns: 0, printing nothing when neither
NAMEnor a column has a value; 2 for aNAMEthat is not a variable name, anAI_MODEL_TIERthat is not a class, or a reserve that is not a whole number (a memory reserve must be below 100). -
ollama_budget_check pull_bytes largest_model_bytes models_dir
- Purpose: Say whether the machine can take a download of
pull_bytesintomodels_dirand then load a model oflargest_model_bytes. - Behavior:
- Disk: what is free after the download must be at least
OLLAMA_DISK_RESERVE_GB. - Memory: the largest model plus
OLLAMA_MEM_HEADROOM_PERCENTmust fit the machine's memory and its GPUs' together, since Ollama splits a model between them. More than that is a refusal. Not fitting what is available right now is a warning only: that changes by the minute, and a check that refuses a machine that could run the model gets switched off. - A figure that cannot be read is said and skipped, not guessed.
- Each refusal is a message on stderr with the numbers and the setting that governs it.
- Disk: what is free after the download must be at least
- Returns: 0 fits; 1 disk; 2 memory; 3 both; 4 a size given is not a whole number of at most 15 digits (nothing was checked). With
OLLAMA_IGNORE_BUDGET=1: 0 in place of 1, 2 and 3; still 4. -
Note: the memory figure is an estimate (file size plus headroom). What a model needs to load also depends on the context length it is run with.
-
ollama_endpoint_pull base_url model
- Purpose: Ask that Ollama to pull one model and wait for it, however long the download takes.
- Behavior: Asked for as a stream, so progress keeps the connection alive; given up when nothing arrives for
OLLAMA_PULL_STALL_SECONDS. Each tenth of a layer of 100 MB or more is said on stderr as it arrives (qwen2.5:7b: 40% of 4.7 GB); a layer that Ollama already holds says nothing. An error anywhere in the stream is a failure whatever the HTTP status. What is not a model reference (letters, digits and. _ - / :; no@, so neither credentials nor a digest; no empty part, as inqwen3:orqwen3/) is not sent. -
Returns: 0 when the stream ends in success; 1 otherwise, with what Ollama said last on stderr.
-
ollama_endpoint_ensure_models base_url models_dir model...
- Purpose: Make sure that Ollama has every model listed, pulling what is missing only when everything fits.
- Behavior: Lists what the endpoint has; if nothing is missing, returns without asking the registry anything. Otherwise sizes every needed model (the missing ones add up to the download, the largest of all of them is what must fit in memory), checks the budget, and only then pulls, in the order given. Nothing is pulled unless everything fits: a start that ends with half its models is worse than one that says no. A credential in
base_urlis used and never printed. models_diris where that Ollama keeps its models, for the disk check:ollama_models_dirfor one on this machine, or Docker's data root for an Ollama in a container.- Returns:
- 0 every model is there, or none was asked for
- 1, 2, 3 the budget refused (as
ollama_budget_check); nothing was pulled - 4 nothing answers at
base_urlas an Ollama - 5 models are missing and
OLLAMA_PULL_MISSING=0, oraskwas not answered yes, or there was no terminal to ask on - 6 a pull failed
- 7 the budget could not be checked: a missing model's size, or the free disk space, could not be learned (
OLLAMA_IGNORE_BUDGET=1pulls anyway). A missing model the registry says it does not have is 7 too, with a message of its own: the name is wrong (or the model is private and the registry does not show it), and waiting does not help. - 8 an argument is not a model reference
- A model that is already there and whose size cannot be learned does not stop the rest; it is said that memory was not checked for it.
A project's own configuration#
Projects name their Ollama's address in different ways, some store the API
path with it, and many keep it in a .env their start script never sources.
Where the Ollama runs: OLLAMA_MODE#
A project says in its .env where its models are served. It is not guessed
when it is set.
OLLAMA_MODE |
Meaning | What the start check does |
|---|---|---|
local (or host) |
an Ollama on this machine | lists, sizes, checks this machine's disk (ollama_models_dir) and memory, pulls what is missing |
docker |
an Ollama in a container on this machine | the same, at the published port and against Docker's disk |
remote |
an API on another machine: an Ollama, or a hosted API | measures nothing and pulls nothing unless asked; names what a remote Ollama lacks; passes over what is not an Ollama |
not set, or auto |
decided by the address | local when the address is this machine, otherwise as remote, except that nothing answering stays a refusal (4) |
A typical split: local on a developer's machine, docker where the stack
brings its own Ollama, remote in production against a hosted endpoint, and
either for an integration or QA environment.
# .env
OLLAMA_MODE=docker
OLLAMA_URL=http://ollama:11434 # what the backend container uses
OLLAMA_PORT=11435 # the port published on this machine
- ollama_project_ensure_models models_file [env_file] [NAME...]
- Approved set: the models file is the project's approved models, one per role and class of machine. A
NAMEin the environment or.envset to any other model is a manual override: on a terminal it is named as not approved, with the approved ones, and used only after a yes; with no terminal and in CI (CI=true,GITHUB_ACTIONS) it is refused (9), never asked. ANAMEthe file does not name is not judged. - Class: the models are this machine's picks (
ollama_model_for_class). One above the default that the disk or memory check refuses (1-3) falls back one column (xlarge, large, default) and is checked again, with a warning naming the new picks; when the default does not fit either, the refusal stands.AI_MODEL_TIERandOLLAMA_BUDGET_GPU_LARGEST_BYTESmay be in.env. For an Ollama on another machine this machine's memory and GPU are not its own: its class isAI_MODEL_TIER, or comes from its statedOLLAMA_BUDGET_MEM_TOTAL_BYTES(andOLLAMA_BUDGET_GPU_LARGEST_BYTES, else no GPU); with neither, the default column. - Purpose: The whole start check from a project's own configuration:
.env, models file, address, mode, thenollama_endpoint_ensure_models. - Configuration:
env_file(""for none; a file that is not there yet is no.env) is read as data for the model names, the address,OLLAMA_MODE,OLLAMA_PORT,OLLAMA_HOST_PORT,OLLAMA_MODELSand every setting under Environment above. A value in the environment that is not blank wins. Nothing read from it is left in the caller's environment, and the caller'sIFSdoes not change what is read. - Models: as
ollama_models_required models_file [NAME...]. Pass""for the file when the project names its models in.envalone. One value is one model:"small:3b embed"is refused (8), not read as two. - Address: the first variable of
OLLAMA_URL_VARSthat has a value (defaultOLLAMA_URL OLLAMA_BASE_URL OLLAMA_HOST; set it to the project's own name, for exampleOLLAMA_URL_VARS=MYAPP_OLLAMA_URL, in the environment or in.env), made a base URL byollama_endpoint_base_url;http://127.0.0.1:11434when none has. On the host,host.docker.internalis read as this machine. local: an address that is not this machine is a contradiction (9), with the mode that would fit named.docker: the address a container uses for it (http://ollama:11434, a compose service: one label, no dots) does not resolve from a start script on the host, so there it is read as127.0.0.1, atOLLAMA_PORTorOLLAMA_HOST_PORTwhen one is set and at the address's own port otherwise. A published port that is not a number from 1 to 65535 is a mistake (9), not "use the address's port": that port is often the native Ollama's. Inside a container the name is used as written. A name with a dot, an IP address or an IPv6 literal that is not this machine is another machine, anddockeris the wrong word for it (9, withremotenamed). An address that is this machine already is used as it is. The disk measured isOLLAMA_MODELSwhen stated, else Docker's data root (docker info), else/var/lib/docker.remote: asked what it has. A model it lacks is a refusal (5) that says so, and nothing is pulled there unlessOLLAMA_PULL_MISSINGis set on. What does not answer as an Ollama (a hosted API of another kind, or one that is not up) is said and passed over (0): a start script on this machine can neither check nor mend it.- A pull into another machine, asked for with
OLLAMA_PULL_MISSING, needsOLLAMA_BUDGET_DISK_FREE_BYTESandOLLAMA_BUDGET_MEM_TOTAL_BYTESstated for that machine, orOLLAMA_IGNORE_BUDGET=1; without them it is a refusal (7), not a check against this machine. This machine's GPU and free memory are never counted for it:OLLAMA_BUDGET_GPU_BYTESis 0 andOLLAMA_BUDGET_MEM_AVAILABLE_BYTESthe stated total unless they are stated too. - Returns: as
ollama_endpoint_ensure_models, and 9 for the project's configuration: no models file and noNAME(nothing would be checked), a models file that is not there, aNAMEor an entry ofOLLAMA_URL_VARSthat is not a variable name, an address that is not one, anOLLAMA_MODEthat is none of the three (trimmed at the ends only, in any case; the value is not repeated in the message), one the address contradicts, a published port that is not one, or a setting in.envthat the caller holds read-only. So 1, 2 and 3 are always the disk and the memory. -
Note: 4 (nothing answers) is returned as it is for
local,dockerand an unset mode. For an address on another machine in an unset mode, one listing that fails is already 4: it is not asked again, so nothing is pulled into it after a second answer. A project whose backend waits for Ollama by itself may go on from it:ollama_project_ensure_models ... || { rc=$?; [[ $rc -eq 4 ]] || exit $rc; }. With no mode set and a one-word host that does not answer, a second message says what a compose service name is and to setOLLAMA_MODE=docker. -
ollama_endpoint_base_url address
- Purpose: Print the base URL of the Ollama at an address as a project configures it.
- Behavior:
host,host:portor:portgetshttp://, and port 11434 when none is given, as Ollama readsOLLAMA_HOST; no host is127.0.0.1, and an IPv6 address without brackets gets them. A URL keeps its scheme, its port (none given stays none, for an Ollama behind a proxy), its credentials and a path a proxy serves it under. The API path some projects store with it is removed when it ends the URL, however deep it was written (/api/v1/chat/completionstoo) (/api,/v1,/api/generate,/api/chat,/v1/chat/completionsand the other endpoints of the two APIs), with a query, a fragment and a trailing slash;/api/ollamais a proxy's path and stays. -
Returns: 0; 1, printing nothing, for what is not an address: empty, another scheme,
http:without its slashes, a space or a control character, a port that is not a number from 1 to 65535, a host with a character no host has (an IPv6 literal holds hex digits, colons and dots, and a%zone), or an@after the first/,?or#(a password with such a character in it, which must be percent-encoded). -
ollama_endpoint_is_local url
- Purpose: Say whether a URL points at this machine, so that this machine's disk and memory are the ones a pull would use.
-
Behavior: 0 for a loopback name or address (also IPv4 written inside IPv6),
0.0.0.0,::, this host's name and its own addresses (fromipand fromifconfig, whichever are there), and forhost.docker.internalandgateway.docker.internalwhen this is not a container itself. Inside a container (/.dockerenvor/run/.containerenvpresent, orKUBERNETES_SERVICE_HOSTset) those two are the host: another machine. 1 for anything else, including a name that only resolves to this machine: read as another machine, nothing is pulled into it unasked. -
ollama_endpoint_container_reach url [network]
- Purpose: For a project whose containers call this machine's Ollama at
url(as the container writes it, for examplehttp://host.docker.internal:11434): say whether a container can reach it. An Ollama that answers on127.0.0.1but listens on loopback only is not on Docker's networks unless a forwarder puts it there, and a forwarder onto the default bridge does not reach a compose project's own network. - Behavior: When an image with a shell and wget or curl is already on this machine (
OLLAMA_REACH_IMAGE, else the first ofbusybox,alpine,curlimages/curl; nothing is pulled), a throwaway container onnetwork(the default bridge when it is not given; a new network made for the probe and removed after whennetworkdoes not exist yet, since a forwarder or firewall rule may cover the default bridge and not a compose network) asksGET <url>/api/tags, withhost.docker.internalmapped tohost-gateway. Otherwise, or when that container cannot run (said, with the image), the default bridge's IPv4 gateway is asked from127.0.0.1with curl's--connect-to(the URL's scheme, credentials, proxy path, port and TLS name kept), and it says the project's network was not checked. A loopback name inurlis only asked the second way. Only an answer with"models"in it counts. Checks nothing and returns 0 whenurlis another machine (ollama_endpoint_is_local), when there is no Docker, when the bridge has no IPv4 gateway, and when the gateway is not one of this machine's addresses (Docker Desktop, a VM, rootless Docker or a remoteDOCKER_HOST; said: not checked). -
Returns: 0; 4 when no Ollama answers from a container (the forwarder is named); 9 when
urlis not an address. -
ollama_runtime [env_file]
- Purpose: Which Ollama the project uses:
OLLAMA_RUNTIMEfrom the environment, else fromenv_file(blank is not set), elsehost. -
Returns: prints
hostorcontainer; 9 for any other value, naming both. -
ollama_project_compose_files root [--compose FILE] [--override FILE] [--by-runtime] [--env-file FILE]
- Purpose: The
COMPOSE_FILEvalue for a compose call: the main file (defaultroot/docker-compose.yml) and the optional Ollama override (defaultdocker-ollama.ymlnext to it) when that file is there, as absolute paths joined byCOMPOSE_PATH_SEPARATOR(default:). - Behavior: The override is included whatever
OLLAMA_RUNTIMEsays, sostopalso stops a container started earlier.--by-runtimeincludes it only withOLLAMA_RUNTIME=container, for the other verbs. -
Returns: 0; 9 for an unknown option or one without its value, a path holding
COMPOSE_PATH_SEPARATOR(COMPOSE_FILEwould split it), or with--by-runtimean unknown runtime. -
ollama_project_prepare root --mode docker|local --url-var NAME --volume NAME [option...]
- Purpose: The whole "which Ollama, and does it have the models" step of a start, in one call. Not a subshell: it exports
OLLAMA_RUNTIME,COMPOSE_FILE, the address inNAMEand the model variables for compose and the app. - Options:
--url-varrepeated (older names the app also reads: in host mode the first one set is used; only the first is exported),--url-suffix PATH(an API path the app keeps in the address, added to the addresses made here, the defaults and--container-url, never to a value read from the environment or.env),--compose FILE,--override FILE,--env-file FILE(default.env),--models-file FILE(defaultai-models.env),--model-var NAME(repeatable; exported from the environment,.envor the models file; none means every name in the models file),--standard-tier(the app reads one model variable, not the class columns:AI_MODEL_TIER=standard),--skip-var NAME(set up, check nothing, when it is1),--port-var NAME(defaultOLLAMA_CONTAINER_PORT, value default 11435),--container-url URL(defaulthttp://ollama:11434),--network NAME(for the reach check; default<project>_defaultwhen it exists),--no-container-pull(a container with no published port and no egress: not started or checked). Relative paths are underroot. - Host (default):
COMPOSE_FILEis the main file. The address isNAME(environment, else.env), elsehttp://host.docker.internal:11434(docker) orhttp://127.0.0.1:11434(local), plus the suffix; one naming the container's service is refused (9). Thenollama_project_ensure_models(withOLLAMA_MODE=localwhen the address is this machine, whateverOLLAMA_MODEsays), and with--mode dockerollama_endpoint_container_reachfrom the project's network. - Container (
OLLAMA_RUNTIME=container, never chosen by itself):COMPOSE_FILEis main and override. The external volume is created when missing (never removed),docker compose up -d ollama(with--env-file), a wait on127.0.0.1:<port>(OLLAMA_CONTAINER_WAIT_SECONDS, default 60), then the model check withOLLAMA_MODE=docker. The app gets--container-url(docker) orhttp://127.0.0.1:<port>(local). OLLAMA_PULL_MISSINGdefaults toaskfor an Ollama on this machine only.-
Returns: 0; 1-8 from
ollama_project_ensure_models; 4 also when the container does not answer, and in host mode after naming the host's Ollama first andOLLAMA_RUNTIME=containersecond; 9 for the call or the configuration (an option,OLLAMA_RUNTIME, an address,--container-url, a volume name, a port or 11434 for the container, a missing override, a compose path holdingCOMPOSE_PATH_SEPARATOR); 10 when Docker fails for the container (absent, the volume, compose up). -
ollama_env_file_export file NAME...
- Purpose: Export each
NAMEfrom an env-style file, unless the environment already has a value for it that is not blank (a blank one is no choice: the file's is used). - Behavior: The file is read as the models file is, as data: it is never sourced, so
$, a backquote or a space in a value is only a value. A file that is not there exports nothing. ANAMEmay be any variable name, also one this module uses itself (name,value,file). -
Returns: 0; 2, exporting nothing, when a
NAMEis not a variable name; 9 when the file sets aNAMEthe caller holds read-only (its blank would otherwise stand for the file's value, silently). -
ollama_model_tiers_file
- Purpose: Print the path of the type x tier data file:
OLLAMA_MODEL_TIERS_FILE, elseconfig/ollama_model_tiers.txtin this library. -
Format:
threshold <key> <GiB>,type <type> <small> <standard> <large> <xlarge>and optionalsize <model> <class> <GPU GiB> <memory GiB or ->(a model's floor), one per line;#comments.scripts/ollama_tiers_check.shchecks it. The file is generated data: every type names a model for all four tiers. -
ollama_model_tier_types
-
Purpose: Print every type the data file names (
general,reasoning,classify,extract,vision,embed,code, ...), one per line. -
ollama_host_tier
- Purpose: Print this host's tier:
small,standard,largeorxlarge. - Behavior:
AI_MODEL_TIERnames it (any case;mediumisstandard). Otherwise memory and the largest GPU (ollama_machine_figures) against the data file's thresholds: xlarge when the GPU hasxlarge_vram_gb; large when the GPU haslarge_vram_gbor memorylarge_ram_gb; small when memory and GPU are both belowsmall_below_ram_gb; else standard. Memory that cannot be read is standard. -
Returns: 0; 2 with no data file to measure against (and no
AI_MODEL_TIER), or a threshold in it that is not a whole number of GiB; 3, printing nothing on stdout, for anAI_MODEL_TIERthat is not a tier (the message names the variable and the value). -
ollama_tier_model type [tier]
- Purpose: Print the approved model for
typeontier(default:ollama_host_tier;mediumisstandard). - Behavior: When the tier is measured (no
tierargument, noAI_MODEL_TIER), a model with a size line must fit this host: its largest GPU at least<GPU GiB>, or memory at least<memory GiB>(-: GPU only). Otherwise the next lower tier's model for the type is used, down to small. A stated tier, and the standard tier given when memory cannot be read, are used as stated. -
Returns: 0; 2 for an unknown type (the known ones are named), a row with no valid model for the tier, a malformed size line, or no data file; 3 for a bad tier.
-
ai_model_resolve type ollama_url [env_file] [NAME] [default_model]
- Purpose: Print the one model a project uses for a role, on stdout alone (messages on stderr), so
model="$(ai_model_resolve ...)"captures it. - Y is
ollama_tier_model type(a lower tier's model when this host is under the tier model's floor; the question then names that tier); X isNAMEfrom the environment, else fromenv_file, elsedefault_model. NAME_CHOICEinenv_filewith noNAME_CHOICE_FORline is a choice set by hand: used as it is, never asked about.- X empty or the same as Y (case and an omitted
:latestdo not count): Y. - X differs, on a terminal (never in CI:
CIset to anything butfalseor0, orGITHUB_ACTIONS): asks once, "1) pull X (your env) 2) use Y (approved for this tier)", default 2 (an empty answer or end of input); any other answer is asked again, and after three the default is used with a warning; "project default" in place of "your env" when X isdefault_model. The answer is stored inenv_fileasNAME_CHOICE=<model>,NAME_CHOICE_FOR=<Y>andNAME_CHOICE_PROJECT=<X>(with noNAME:AI_MODEL_<TYPE>_CHOICE). It is asked again when Y changes or X changes (a stored X that differs from the current X; a stored Y whoseNAME_CHOICE_PROJECTdiffers from the current X). An answer of Y stored withoutNAME_CHOICE_PROJECT(an olderenv_file) is asked about once more on a terminal; with no terminal it is used. All three lines are written in one step: a temporary file next toenv_file(a symlink is followed) is moved over it. A hard-linkedenv_file, or one in a directory that is not writable, is written in place instead: the content is built underTMPDIR(else/tmp), copied over it in one write and read back, so inode, mode, owner and every link are kept. A newenv_fileis created with mode 600; an existing one keeps its mode, its other lines and comments, and Windows line ends; repeated runs leave one line per key; two runs writing it at once take turns (a lock next to it, or underTMPDIRwhen its directory is not writable, naming the owner's pid), so neither drops the other's answer; a lock left by a killed run is taken over at once, and a live one is waited on for up to about 30 seconds. Anenv_filein a git work tree that git does not ignore is not written: the answer is used, with a warning, and asked again next time. - X differs, no terminal or CI: Y if the Ollama has it, else X if it has it, else 12, naming both and pulling nothing.
- The chosen model is pulled if missing through
ollama_endpoint_ensure_models(disk and memory check first).OLLAMA_PULL_MISSING:1pulls without asking,0never pulls,ask(the default here) asks on a terminal and pulls nothing without one or in CI. A model just chosen on the terminal is pulled without a second question unlessOLLAMA_PULL_MISSING=0. No model is ever removed. - An Ollama on another machine: the tier is
AI_MODEL_TIER, else measured from a statedOLLAMA_BUDGET_MEM_TOTAL_BYTES, else standard. - Returns: 0; 1-8 as
ollama_endpoint_ensure_models(4 no Ollama answers, 5 missing and not pulled); 10 bad arguments (unknown type, aNAMEthat is not a variable name, a model that is not a model reference, no data file, a threshold in it that is not a whole number or a malformed size line, anenv_filethat cannot be read or written); 11 a badAI_MODEL_TIER; 12 no terminal and the Ollama has neither model. - Example:
model="$(ai_model_resolve general http://127.0.0.1:11434 .env OLLAMA_MODEL qwen2.5:7b)" || exit $?
What this module does not do#
It reads where a project says its Ollama is (OLLAMA_MODE and the address);
it does not find, start or stop one. Starting a container, and sharing one
Ollama between projects, is the next step and is not in this module yet.