Ollama Architecture: How It Actually Works
How to read this page
Symvanta parsed this repository into a code graph: every function, class, and method is a node, and every call or import between them is an edge. Everything on this page is computed from that graph at the commit shown above. The terms:
- Module (or cluster)
- A group of symbols that call each other far more than they call anything else. An algorithm called Louvain community detection finds these groups from the call traffic alone; nobody draws them by hand.
- Modularity (the Q number)
- A 0-to-1 score of how cleanly those groups separate. Higher means more call traffic stays inside its own group; scores around 0.7 and above read as clean boundaries.
- Hub
- The most depended-upon symbol inside one module.
- Load-bearing symbols
- PageRank, the algorithm Google originally used to rank web pages, run over the call graph instead: it surfaces the functions the rest of the codebase leans on hardest.
- Arrows and their numbers
- How many calls cross from one module into another. A heavier arrow means tighter coupling between those two parts.
- Dependency cycle
- File A imports B, which imports A again, sometimes through a longer loop. Cycles are not bugs, but a change inside one tends to ripple around the whole loop.
- Mutually recursive symbols
- Functions that call each other, usually the natural shape of parsers and tree-walking code.
Ollama is a local model runtime with an HTTP server in front of it: it resolves
a model reference, loads the weights, schedules a runner, and streams tokens
back. Symvanta's graph of the repo at 26936be detects 52 functional modules at
modularity Q=0.76, and the largest are the ones that description predicts: chat
request handling and prompt templating, model names and manifests, the launcher
that manages installed models, and an MLX runner for Apple silicon.
Two things about the shape stand out. The chat path concentrates in a single
handler: server.Server.ChatHandler runs from line 2417 to line 2911 of
server/routes.go, and GenerateHandler above it is a similar size. And the
MLX cluster is almost as large as the chat cluster, 793 symbols against 804,
which is what a second inference path for one hardware family costs in a
codebase.
Module map
The diagram shows the 10 largest of the 52 detected modules, with edges weighted by how many calls cross between them. Module names come from the packages their members live in; the symbol counts, hubs, and edge weights are what the graph computed.
Where to start reading
These are the most depended-upon symbols by PageRank over the call graph,
followed by the HTTP surface registered in server/routes.go. Two entries from
the raw ranking are left out below: Set and Error are one-word Go methods
that many packages define independently, and the graph groups them by name, so
they rank high without pointing anywhere useful.
template.Template.Containsmlx.DefaultStreamagent.SkillCatalog.Dirmlxthread.Thread.Domlxthread.Thread.enqueuemlx.Array.Equaldialog.MsgBuilder.Infoenvconfig.Varmodel.Name.EqualFoldKVPOST /api/chatPOST /api/generateGET /api/versionPOST /v1/chat/completionsPOST /v1/completionsPOST /v1/embeddingsGET /v1/modelsGET /v1/models/:modelPOST /v1/responsesPOST /v1/audio/transcriptionsPOST /v1/messages
The endpoint list is worth a second look. Alongside its own /api/* surface,
Ollama serves an OpenAI-compatible /v1/chat/completions and an
Anthropic-compatible /v1/messages, which is why the codebase carries dedicated
translation clusters for both.
Key subsystems
Chat rendering and templates
Prompt templating sits with terminal chat rendering (chat.wrapChatText,
chat.stripANSI, chat.chatDisplayWidthCut) in the largest cluster on the map,
804 symbols behind the hub template.Template.Contains. The same package builds
the prompt and paints the reply.
MLX arrays and devices
Apple silicon runtime: mlx.DefaultStream, mlx.DefaultDevice, the array
operations, and the Cocoa bindings. At 793 symbols it is within a rounding error
of the chat cluster, and it calls into the MLX parameter and decode cluster 78
times.
API message and tool-call types
The wire types live here, 750 symbols of Message, ToolCall,
ToolCallFunction, ThinkValue, and their conversion helpers. Every surface,
native or compatibility, funnels through them.
Model launcher
Which models are installed and which one starts: launch.fallbackLaunchModel,
launch.launchModelsFromNames, launch.lookupCloudModelLimit, and the
environment host resolution around them, 647 symbols in total.
Model names and manifests
Parsing and validating model references (model.ParseName,
model.Name.IsFullyQualified, model.isValidPart) and finding their blobs on
disk (manifest.BlobsPath). 451 symbols, and all of it runs before a single
weight is loaded.
Canonical request flow
A chat request enters at the gin route and stays inside one handler for most of
its life. Every step below is a call edge out of server.Server.ChatHandler.
r.POST./api/chat (server/routes.go:1891)server.Server.ChatHandlerserver.parseAndValidateModelRefserver.GetModelserver.Model.Capabilitiesserver.chatModeForModelserver.chatPromptserver.filterThinkTags
Health signals
Symvanta detected 2 dependency cycles across 52 modules (modularity Q=0.76). The largest cycle spans 2 files in the components area. 7 sets of mutually recursive symbols were also detected, the largest being mlxrunner (3 symbols).
Both cycles sit in the desktop UI, under app/ui/app/src. The Go server has
none.
Auto-generated by Symvanta from the public repo ollama/ollama at commit 26936be , licensed MIT .
Machine-readable companion: data.json (module counts, subsystems, load-bearing symbols, health signals).