Thuta Learning
IntermediateAIbeginner

The Local AI Software Ecosystem

What you'll walk away with

  • Explain the core ideas behind The Local AI Software Ecosystem
  • Read the diagram and trace how data or requests flow through the architecture
  • Decide what this means for your own hardware and use case

Build the mental model

When people say “local AI,” they often picture one thing: a single app you download and chat with. In practice, the local AI ecosystem is made of several distinct layers, each solving a different problem, and understanding the layers matters more than memorizing brand names.

  • Model file — at the bottom, weights saved in a format like GGUF or safetensors, just numbers on disk with no way to talk to it directly.
  • Runtime — the engine that loads those weights into memory and actually performs inference; llama.cpp and Ollama's internal engine are both runtimes.
  • API layer — usually a small local HTTP server exposing endpoints like /api/chat or /v1/chat/completions, so other software can send a prompt and get a JSON response back.
  • Application layer — desktop chat apps like LM Studio or Jan, developer frameworks like LangChain or LlamaIndex, and agent frameworks like CrewAI or AutoGen.
  • Model hubs — such as Hugging Face and Ollama's library, where you discover and download model files.
  • Vector databases — such as Chroma and Qdrant, which store embeddings for retrieval.

Alongside this vertical stack, model hubs and vector databases are two more categories that cut across it. Recognizing which category a tool belongs to tells you what problem it actually solves, and stops you from expecting a model hub to run inference or a chat app to serve an API to other programs.

Runtime
The engine that loads a model's weights into memory and performs the actual computation to generate output. llama.cpp and Ollama's internal engine are both runtimes.
Inference Server
A runtime wrapped with a network-facing API (usually HTTP), so other programs — not just a single command line — can send it requests and get responses, typically running continuously in the background.
text
MODEL FILE TO APPLICATION STACK
-------------------------------
------------------------------------------------------------
APPLICATION   LM Studio, LangChain, LlamaIndex, CrewAI, AutoGen
                 ^  (chat apps, dev frameworks, agent frameworks)
                 |
              calls
                 |
API LAYER     HTTP server: /api/chat, /v1/chat/completions
                 ^  (JSON request in, JSON response out)
                 |
          exposed by
                 |
RUNTIME       llama.cpp, Ollama engine
                 ^  (loads weights, runs inference)
                 |
             loads
                 |
MODEL FILE    weights on disk: model.gguf, model.safetensors
              (just numbers -- cannot be "talked to" directly)

SIDE CATEGORIES (cut across the stack, not the vertical flow):
  MODEL HUBS        Hugging Face, Ollama Library -> source of files
  VECTOR DATABASES  Chroma, Qdrant                -> store embeddings

Connect it to a real scenario

Imagine your team wants to add a “chat with your documents” feature to an internal tool, and someone drops a list of names into a planning doc: Ollama, LM Studio, LangChain, Chroma, Hugging Face, CrewAI. Without a mental map, that list looks like seven competing products you have to choose between.

  • One runtime — to actually run a model (Ollama, since the team is comfortable in a terminal and wants an API by default).
  • One model hub — to pick a model from (Hugging Face, to compare options, though you'll likely pull the final choice through Ollama's own library).
  • One vector database — to store document embeddings (Chroma, because it's simple to run locally for a first version).
  • One developer framework (optional) — to wire the pieces together instead of writing all the HTTP calls by hand (LangChain).

LM Studio and CrewAI drop out entirely, not because they're worse, but because a GUI desktop app and a multi-agent orchestrator aren't the layer this feature needs yet. The exercise below asks you to do exactly this sorting exercise yourself with a different tool list, so the habit of asking “what layer does this actually sit at” becomes automatic before you touch any code.

Try the working example

python
CATEGORY_BY_TOOL = {
    "LM Studio": "Desktop App",
    "Jan": "Desktop App",
    "GPT4All": "Desktop App",
    "Ollama": "CLI Runtime",
    "llama.cpp": "CLI Runtime",
    "vLLM": "Inference Server",
    "text-generation-webui": "Inference Server",
    "LangChain": "Developer Framework",
    "LlamaIndex": "Developer Framework",
    "Hugging Face Hub": "Model Hub",
    "Ollama Library": "Model Hub",
    "Chroma": "Vector Database",
    "Qdrant": "Vector Database",
    "AutoGen": "Agent Framework",
    "CrewAI": "Agent Framework",
}


def classify_tools(tool_names):
    result = {}
    for name in tool_names:
        result[name] = CATEGORY_BY_TOOL.get(name, "Uncategorized")
    return result


if __name__ == "__main__":
    tools = ["Ollama", "LM Studio", "vLLM", "LangChain", "Chroma", "CrewAI", "Hugging Face Hub"]
    classified = classify_tools(tools)
    for tool_name, category in classified.items():
        print(f"{tool_name}: {category}")
You should see
Running the script prints each tool name mapped to its category, one per line, using the exact classification: Ollama: CLI Runtime, LM Studio: Desktop App, vLLM: Inference Server, LangChain: Developer Framework, Chroma: Vector Database, CrewAI: Agent Framework, Hugging Face Hub: Model Hub.

5-minute try-it

Add three more tools to `CATEGORY_BY_TOOL` — pick real ones from a category not yet represented (for example a desktop app like Jan, or an agent framework like AutoGen) — then run `classify_tools` on a mixed list that includes at least one tool you added and one that isn't in the dictionary at all, and confirm the unknown one correctly prints as `Uncategorized` instead of crashing.

One important caution

Treating 'local AI' as a single downloadable app instead of a stack of layers with different tools at each level.

Expecting a tool from one category to do another category's job — for example, assuming a model hub can run inference, or a chat app can serve an API to other programs.

Ollama on GitHubLocal AI / Local LLM

Easy traps

  • Treating 'local AI' as a single downloadable app instead of a stack of layers with different tools at each level.
  • Expecting a tool from one category to do another category's job — for example, assuming a model hub can run inference, or a chat app can serve an API to other programs.
  • Try a new model or tool at a small scale before wiring it into a production or daily-use workflow.

Exercise

Add three more tools to `CATEGORY_BY_TOOL` — pick real ones from a category not yet represented (for example a desktop app like Jan, or an agent framework like AutoGen) — then run `classify_tools` on a mixed list that includes at least one tool you added and one that isn't in the dictionary at all, and confirm the unknown one correctly prints as `Uncategorized` instead of crashing.

You'll know it worked when: Running the script prints each tool name mapped to its category, one per line, using the exact classification: Ollama: CLI Runtime, LM Studio: Desktop App, vLLM: Inference Server, LangChain: Developer Framework, Chroma: Vector Database, CrewAI: Agent Framework, Hugging Face Hub: Model Hub.

The Local AI Software Ecosystem | Thuta Learning