llm-cpp

#include "llm_*.hpp" · C++17 · MIT

LLM features for C++, one .hpp at a time.

26 single-header libraries for streaming, retries, caching, cost estimates, RAG, reranking, tracing, structured output and agents. Copy the file you need into your project. No SDK, no package manager, no framework.

26
headers
14
need nothing
but the std lib
12
need only
libcurl
210-572
lines per
header
llm_stream.hppllm_cost.hppllm_guard.hpp
llm_cache.hppC++17210 lines total
x64 Native Tools
C:\demo> curl -fsSLO https://raw.githubusercontent.com/Mattbusel/llm-cache/main/include/llm_cache.hpp
C:\demo> cl /nologo /std:c++17 /EHsc cache.cpp && cache.exe
cache.cpp
What is RAII?            -> answer #1
what is raii?            -> answer #1
Explain move semantics   -> answer #2
What is SFINAE?          -> answer #3
What is RAII?            -> answer #4

api calls 4 | hits 1 | misses 4 | evictions 2
01

One file is the whole install.

Each library lives in its own repo, but the only thing your project needs from it is include/llm_<name>.hpp. Include it anywhere for the declarations; define LLM_<NAME>_IMPLEMENTATION in exactly one .cpp to compile the body.

github.com/Mattbusel/llm-cacherepo

  • examples/
  • └─ include/
  •    └─ llm_cache.hppthe library
  • CMakeLists.txt
  • README.md
  • LICENSE

your-project/yours

  • src/
  • ├─ main.cpp
  • └─ llm_impl_cache.cpp // #define ..._IMPLEMENTATION
  • third_party/
  • └─ llm_cache.hpp
  • CMakeLists.txt // unchanged

Every header, drawn to scale. The largest is 572 lines and all 26 together are 8,923, so when something misbehaves you open one file you can read in a sitting.

standard library onlyuses libcurl
  1. 572format
  2. 537parse
  3. 481stream
  4. 469finetune
  5. 441json
  6. 388audio
  7. 385agent
  8. 379embed
  9. 362rag
  10. 362batch
  11. 351chat
  12. 337vision
  13. 336cost
  14. 330ab
  15. 315eval
  16. 313guard
  17. 309pool
  18. 302rank
  19. 290compress
  20. 267retry
  21. 261log
  22. 248trace
  23. 248mock
  24. 219router
  25. 211template
  26. 210cache
02

The catalogue.

"none" means fully offline, standard library only. "libcurl" means the implementation makes HTTPS calls to OpenAI and/or Anthropic. Tick the ones you want and the install section writes the commands for you.

// I want to...

llm_stream.hpp481 lines

llm-stream

Stream OpenAI and Anthropic chat responses token by token over SSE

needs
libcurl
group
core
#define LLM_STREAM_IMPLEMENTATION
llm_retry.hpp267 lines

llm-retry

Exponential backoff with jitter, provider failover and a circuit breaker

needs
none
group
core
#define LLM_RETRY_IMPLEMENTATION
llm_cost.hpp336 lines

llm-cost

Approximate token counts and cost estimates for built-in OpenAI and Anthropic models, budget checks

needs
none
group
core
#define LLM_COST_IMPLEMENTATION
run output
llm_cache.hpp210 lines

llm-cache

LRU response cache with TTL and hit/miss stats, so identical prompts skip the API

needs
none
group
core
#define LLM_CACHE_IMPLEMENTATION
run output
llm_format.hpp572 lines

llm-format

Define a schema, validate model JSON against it, and re-prompt until the output conforms

needs
none
group
core
#define LLM_FORMAT_IMPLEMENTATION
run output
llm_json.hpp441 lines

llm-json

Small JSON parser and builder for request bodies and model output

needs
none
group
core
#define LLM_JSON_IMPLEMENTATION
run output
llm_parse.hpp537 lines

llm-parse

Strip HTML and markdown, extract titles, links, headings and code blocks, chunk text

needs
none
group
data
#define LLM_PARSE_IMPLEMENTATION
llm_embed.hpp379 lines

llm-embed

OpenAI embeddings, cosine/dot/euclidean similarity and a small on-disk vector store

needs
libcurl
group
data
#define LLM_EMBED_IMPLEMENTATION
llm_rag.hpp362 lines

llm-rag

End-to-end RAG: chunk, embed, persist an index, retrieve top-k and answer

needs
libcurl
group
data
#define LLM_RAG_IMPLEMENTATION
llm_rank.hpp302 lines

llm-rank

Rerank passages with offline BM25, LLM relevance scoring, or a hybrid of both

libcurl (linked; BM25 itself is offline)

needs
libcurl
group
data
#define LLM_RANK_IMPLEMENTATION
llm_compress.hpp290 lines

llm-compress

Shrink conversation history: head/tail/smart truncation, sliding window, LLM summary

none (libcurl only with LLM_COMPRESS_SUMMARIZE)

needs
none
group
data
#define LLM_COMPRESS_IMPLEMENTATION
run output
llm_batch.hpp362 lines

llm-batch

Run a JSONL file of prompts through a thread pool with rate limiting and resumable checkpoints

needs
libcurl
group
data
#define LLM_BATCH_IMPLEMENTATION
llm_log.hpp261 lines

llm-log

Structured JSONL log of every call with latency, tokens and cost, plus query and summary

needs
none
group
operations
#define LLM_LOG_IMPLEMENTATION
llm_trace.hpp248 lines

llm-trace

RAII spans with parent/child nesting, token and cost attributes, OTLP-style JSON export

needs
none
group
operations
#define LLM_TRACE_IMPLEMENTATION
llm_pool.hpp309 lines

llm-pool

Worker pool with priority queue and requests-per-minute and tokens-per-minute limits

needs
none
group
operations
#define LLM_POOL_IMPLEMENTATION
llm_mock.hpp248 lines

llm-mock

Fake LLM with scripted, pattern, random or echo responses, simulated latency and streaming

needs
none
group
operations
#define LLM_MOCK_IMPLEMENTATION
llm_eval.hpp315 lines

llm-eval

Run a prompt N times, measure consistency, compare models or prompts, score responses

needs
libcurl
group
operations
#define LLM_EVAL_IMPLEMENTATION
llm_ab.hpp330 lines

llm-ab

A/B test prompts or models with Welch's t-test, Cohen's d and custom scorers

needs
libcurl
group
operations
#define LLM_AB_IMPLEMENTATION
llm_chat.hpp351 lines

llm-chat

Multi-turn conversation with token-budget trimming, pinned system prompt, save and restore

needs
libcurl
group
application
#define LLM_CHAT_IMPLEMENTATION
llm_agent.hpp385 lines

llm-agent

Tool-calling agent loop: register C++ lambdas as tools and let the model call them

needs
libcurl
group
application
#define LLM_AGENT_IMPLEMENTATION
llm_vision.hpp337 lines

llm-vision

Send images (file or URL) plus a prompt to OpenAI or Anthropic vision models

needs
libcurl
group
application
#define LLM_VISION_IMPLEMENTATION
llm_template.hpp211 lines

llm-template

Mustache-style prompt templates with loops, conditionals and token-budget truncation

needs
none
group
application
#define LLM_TEMPLATE_IMPLEMENTATION
llm_router.hpp219 lines

llm-router

Pick a model per prompt from a complexity score and a cost, latency, quality or budget strategy

needs
none
group
application
#define LLM_ROUTER_IMPLEMENTATION
llm_guard.hpp313 lines

llm-guard

Detect and scrub PII (email, phone, SSN, card numbers, API keys) and score prompt-injection risk

needs
none
group
application
#define LLM_GUARD_IMPLEMENTATION
run output
llm_audio.hpp388 lines

llm-audio

Whisper transcription and translation, and text-to-speech, via the OpenAI API

needs
libcurl
group
application
#define LLM_AUDIO_IMPLEMENTATION
llm_finetune.hpp469 lines

llm-finetune

OpenAI fine-tuning lifecycle: write JSONL, upload, create, poll, cancel, list models

needs
libcurl
group
application
#define LLM_FINETUNE_IMPLEMENTATION

No header matches that filter.

03

Real code, real output.

Six of the offline libraries, each a complete program with its implementation macro in the same file. The output beside each one is exactly what it printed; nothing is mocked except where a comment says so.

llm-cache (210 lines, no dependencies). Identical prompts skip the API. Keys are case-insensitive by default, and the least recently used entry is evicted at capacity.

cache.cpp
#define LLM_CACHE_IMPLEMENTATION
#include "llm_cache.hpp"
#include <cstdio>

int main() {
    llm::CacheConfig cfg;
    cfg.max_entries = 2;                  // tiny, to show LRU eviction
    llm::ResponseCache cache(cfg);

    int api_calls = 0;
    auto ask = [&](const std::string& prompt) {
        return cache.get_or_compute(prompt, [&] {
            ++api_calls;                  // your real model call goes here
            return "answer #" + std::to_string(api_calls);
        });
    };

    for (const char* p : {"What is RAII?", "what is raii?",
                          "Explain move semantics", "What is SFINAE?",
                          "What is RAII?"})
        std::printf("%-24s -> %s\n", p, ask(p).c_str());

    auto s = cache.stats();
    std::printf("\napi calls %d | hits %zu | misses %zu | evictions %zu\n",
                api_calls, s.hits, s.misses, s.evictions);
}
x64 Native Tools
C:\demo> cl /nologo /std:c++17 /EHsc /O2 cache.cpp
cache.cpp
C:\demo> cache.exe
What is RAII?            -> answer #1
what is raii?            -> answer #1
Explain move semantics   -> answer #2
What is SFINAE?          -> answer #3
What is RAII?            -> answer #4

api calls 4 | hits 1 | misses 4 | evictions 2
C:\demo> 

Compiled with MSVC 19.44 x64 (/std:c++17 /EHsc /O2) against each library's current header and run on 2026-09-25. Sources: examples/offline, rebuilt with g++ on every CI run. Prices come from llm-cost's built-in table.

04

Using several together.

Headers can be included side by side anywhere. Implementations are the one thing to keep apart.

One implementation per .cpp.

Several headers use the same internal helper names (for example llm::detail::json_escape), so defining two *_IMPLEMENTATION macros in one translation unit can fail to compile. llm-log with llm-stream is one such pair.

llm_impl.cppLOG + STREAM in one file: can fail
llm_impl_log.cpp#define LLM_LOG_IMPLEMENTATION
llm_impl_retry.cpp#define LLM_RETRY_IMPLEMENTATION
llm_impl_stream.cpp#define LLM_STREAM_IMPLEMENTATION
Checked 2026-09-25: all 26 implementations, each in its own .cpp, compile and link into one binary (MSVC 19.44 x64, libcurl from vcpkg).
main.cpp: stream, retry on failure, log the call
#include "llm_log.hpp"
#include "llm_retry.hpp"
#include "llm_stream.hpp"
#include <cstdlib>
#include <iostream>

int main() {
    const char* key = std::getenv("OPENAI_API_KEY");
    if (!key) { std::cerr << "set OPENAI_API_KEY\n"; return 1; }

    llm::Config cfg;
    cfg.api_key = key;
    cfg.model   = "gpt-4o-mini";
    const std::string prompt = "Explain backpressure in one paragraph.";

    llm::Logger logger(llm::LogConfig{"calls.jsonl"});
    llm::Logger::ScopedCall call(logger, cfg.model, prompt);   // written on scope exit

    auto result = llm::with_retry<std::string>([&]() -> std::string {
        std::string text, error;
        llm::stream(prompt, cfg,
            [&](std::string_view tok) { std::cout << tok << std::flush; text += tok; },
            nullptr,
            [&](std::string_view err) { error = err; });
        if (!error.empty()) throw llm::LLMError{0, error, true};   // retry
        return text;
    });

    call.set_response(result.value);
    std::cout << "\n(" << result.attempts_used << " attempt(s))\n";
}
05

Your install, written for you.

Pick headers in the catalogue. This fetches them into third_party/, gives each implementation its own .cpp, and adds -lcurl only if something you picked needs it.