llm-cpp

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

C++에 LLM 기능을, .hpp 하나씩.

스트리밍, 재시도, 캐싱, 비용 추정, RAG, 리랭킹, 트레이싱, 구조화된 출력, 에이전트를 위한 싱글 헤더 라이브러리 26개. 필요한 파일을 프로젝트에 복사하면 끝입니다. SDK도, 패키지 매니저도, 프레임워크도 필요 없습니다.

26
헤더
14
표준 라이브러리만
필요
12
libcurl만
필요
210-572
헤더당
줄 수
llm_stream.hppllm_cost.hppllm_guard.hpp
llm_cache.hppC++17총 210줄
x64 Native Tools
C:\demo> curl -fsSLO https://gitlab.com/mattbusel/llm-cache/-/raw/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

설치는 파일 하나로 끝.

라이브러리마다 저장소가 따로 있지만, 프로젝트에 필요한 것은 include/llm_<name>.hpp 하나뿐입니다. 선언은 어디서든 include해서 쓰고, 구현을 컴파일하려면 딱 한 .cpp에서 LLM_<NAME>_IMPLEMENTATION을 정의하세요.

gitlab.com/mattbusel/llm-cache저장소

  • examples/
  • └─ include/
  •    └─ llm_cache.hpp라이브러리 본체
  • CMakeLists.txt
  • README.md
  • LICENSE

your-project/내 프로젝트

  • src/
  • ├─ main.cpp
  • └─ llm_impl_cache.cpp // #define ..._IMPLEMENTATION
  • third_party/
  • └─ llm_cache.hpp
  • CMakeLists.txt // 변경 없음

모든 헤더를 실제 비율로 그렸습니다. 가장 큰 것이 572줄, 26개를 모두 합쳐도 8,948줄이라서, 뭔가 이상할 때는 한자리에서 다 읽을 수 있는 파일 하나만 열면 됩니다.

표준 라이브러리만libcurl 사용
  1. 572format
  2. 537parse
  3. 481stream
  4. 469finetune
  5. 441json
  6. 388audio
  7. 385agent
  8. 379embed
  9. 362rag
  10. 362batch
  11. 357cost
  12. 351chat
  13. 337vision
  14. 334ab
  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

카탈로그.

"없음"은 완전 오프라인, 표준 라이브러리만 쓴다는 뜻입니다. "libcurl"은 구현이 OpenAI 및/또는 Anthropic에 HTTPS 호출을 한다는 뜻입니다. 원하는 것을 체크하면 설치 섹션이 명령을 대신 써 줍니다.

// 하고 싶은 일

llm_stream.hpp481줄

llm-stream

OpenAI와 Anthropic 채팅 응답을 SSE로 토큰 단위 스트리밍

의존성
libcurl
그룹
코어
#define LLM_STREAM_IMPLEMENTATION
llm_retry.hpp267줄

llm-retry

지터를 넣은 지수 백오프, 제공자 페일오버, 서킷 브레이커

의존성
없음
그룹
코어
#define LLM_RETRY_IMPLEMENTATION
llm_cost.hpp357줄

llm-cost

내장된 OpenAI·Anthropic 모델의 대략적인 토큰 계산과 비용 추정, 예산 확인

의존성
없음
그룹
코어
#define LLM_COST_IMPLEMENTATION
실행 결과
llm_cache.hpp210줄

llm-cache

TTL과 적중/미스 통계를 갖춘 LRU 응답 캐시로, 같은 프롬프트는 API를 건너뜁니다

의존성
없음
그룹
코어
#define LLM_CACHE_IMPLEMENTATION
실행 결과
llm_format.hpp572줄

llm-format

스키마를 정의하고 모델 JSON을 검증해, 출력이 맞을 때까지 다시 프롬프트

의존성
없음
그룹
코어
#define LLM_FORMAT_IMPLEMENTATION
실행 결과
llm_json.hpp441줄

llm-json

요청 본문과 모델 출력을 위한 작은 JSON 파서 겸 빌더

의존성
없음
그룹
코어
#define LLM_JSON_IMPLEMENTATION
실행 결과
llm_parse.hpp537줄

llm-parse

HTML과 마크다운을 걷어내고 제목, 링크, 헤딩, 코드 블록을 추출하며 텍스트를 청크로 분할

의존성
없음
그룹
데이터
#define LLM_PARSE_IMPLEMENTATION
llm_embed.hpp379줄

llm-embed

OpenAI 임베딩, 코사인/내적/유클리드 유사도, 작은 디스크 기반 벡터 저장소

의존성
libcurl
그룹
데이터
#define LLM_EMBED_IMPLEMENTATION
llm_rag.hpp362줄

llm-rag

엔드투엔드 RAG: 청크 분할, 임베딩, 인덱스 저장, top-k 검색과 답변

의존성
libcurl
그룹
데이터
#define LLM_RAG_IMPLEMENTATION
llm_rank.hpp302줄

llm-rank

오프라인 BM25, LLM 관련도 점수, 또는 둘을 섞은 하이브리드로 문단 리랭킹

libcurl(링크 필요, BM25 자체는 오프라인)

의존성
libcurl
그룹
데이터
#define LLM_RANK_IMPLEMENTATION
llm_compress.hpp290줄

llm-compress

대화 기록 줄이기: 앞/뒤/스마트 잘라내기, 슬라이딩 윈도, LLM 요약

없음(LLM_COMPRESS_SUMMARIZE를 쓸 때만 libcurl 필요)

의존성
없음
그룹
데이터
#define LLM_COMPRESS_IMPLEMENTATION
실행 결과
llm_batch.hpp362줄

llm-batch

JSONL 파일의 프롬프트를 스레드 풀로 처리하며, 속도 제한과 재개 가능한 체크포인트 지원

의존성
libcurl
그룹
데이터
#define LLM_BATCH_IMPLEMENTATION
llm_log.hpp261줄

llm-log

모든 호출을 지연 시간, 토큰, 비용과 함께 구조화된 JSONL 로그로 기록하고, 조회와 요약도 지원

의존성
없음
그룹
운영
#define LLM_LOG_IMPLEMENTATION
llm_trace.hpp248줄

llm-trace

부모/자식 중첩이 되는 RAII 스팬, 토큰·비용 속성, OTLP 스타일 JSON 내보내기

의존성
없음
그룹
운영
#define LLM_TRACE_IMPLEMENTATION
llm_pool.hpp309줄

llm-pool

우선순위 큐와 분당 요청 수·분당 토큰 수 제한을 갖춘 워커 풀

의존성
없음
그룹
운영
#define LLM_POOL_IMPLEMENTATION
llm_mock.hpp248줄

llm-mock

스크립트, 패턴, 무작위, 에코 응답을 돌려주는 가짜 LLM. 지연 시간과 스트리밍도 흉내 냅니다

의존성
없음
그룹
운영
#define LLM_MOCK_IMPLEMENTATION
llm_eval.hpp315줄

llm-eval

프롬프트를 N번 실행해 일관성을 측정하고, 모델이나 프롬프트를 비교하고, 응답에 점수 매기기

의존성
libcurl
그룹
운영
#define LLM_EVAL_IMPLEMENTATION
llm_ab.hpp334줄

llm-ab

Welch's t-검정, Cohen's d, 사용자 정의 채점기로 프롬프트나 모델을 A/B 테스트

의존성
libcurl
그룹
운영
#define LLM_AB_IMPLEMENTATION
llm_chat.hpp351줄

llm-chat

토큰 예산에 맞춘 정리, 고정 시스템 프롬프트, 저장과 복원을 지원하는 멀티턴 대화

의존성
libcurl
그룹
앱
#define LLM_CHAT_IMPLEMENTATION
llm_agent.hpp385줄

llm-agent

도구 호출 에이전트 루프: C++ 람다를 도구로 등록하고 모델이 호출하게 합니다

의존성
libcurl
그룹
앱
#define LLM_AGENT_IMPLEMENTATION
llm_vision.hpp337줄

llm-vision

이미지(파일 또는 URL)와 프롬프트를 OpenAI나 Anthropic 비전 모델에 전송

의존성
libcurl
그룹
앱
#define LLM_VISION_IMPLEMENTATION
llm_template.hpp211줄

llm-template

반복문, 조건문, 토큰 예산 기반 잘라내기를 지원하는 Mustache 스타일 프롬프트 템플릿

의존성
없음
그룹
앱
#define LLM_TEMPLATE_IMPLEMENTATION
llm_router.hpp219줄

llm-router

복잡도 점수와 비용·지연 시간·품질·예산 전략으로 프롬프트마다 모델 선택

의존성
없음
그룹
앱
#define LLM_ROUTER_IMPLEMENTATION
llm_guard.hpp313줄

llm-guard

개인정보(이메일, 전화번호, SSN, 카드 번호, API 키)를 찾아 지우고 프롬프트 인젝션 위험도를 점수화

의존성
없음
그룹
앱
#define LLM_GUARD_IMPLEMENTATION
실행 결과
llm_audio.hpp388줄

llm-audio

OpenAI API를 통한 Whisper 음성 받아쓰기와 번역, 텍스트 음성 변환

의존성
libcurl
그룹
앱
#define LLM_AUDIO_IMPLEMENTATION
llm_finetune.hpp469줄

llm-finetune

OpenAI 파인튜닝 전 과정: JSONL 작성, 업로드, 생성, 폴링, 취소, 모델 목록

의존성
libcurl
그룹
앱
#define LLM_FINETUNE_IMPLEMENTATION

조건에 맞는 헤더가 없습니다.

03

진짜 코드, 진짜 출력.

오프라인 라이브러리 6개를 골랐습니다. 각각 구현 매크로까지 한 파일에 담은 완전한 프로그램입니다. 옆의 출력은 실제로 찍힌 그대로이며, 주석으로 밝힌 곳 말고는 아무것도 모킹하지 않았습니다.

llm-cache (210줄, 의존성 없음). 같은 프롬프트는 API를 건너뜁니다. 키는 기본적으로 대소문자를 구분하지 않으며, 용량이 차면 가장 오래 쓰지 않은 항목을 축출합니다.

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> 

MSVC 19.44 x64(/std:c++17 /EHsc /O2)로 각 라이브러리의 현재 헤더에 맞춰 컴파일하고 2026-09-28에 실행했습니다. 소스: examples/offline, CI가 돌 때마다 g++로 다시 빌드합니다. 가격은 llm-cost의 내장 표에서 가져왔습니다.

04

여러 개를 함께 쓰기.

헤더는 어디서든 나란히 include해도 됩니다. 따로 떼어 둬야 하는 것은 구현뿐입니다.

.cpp 하나에 구현 하나.

일부 헤더는 같은 내부 헬퍼 이름을 씁니다(예: llm::detail::json_escape). 그래서 한 번역 단위에서 *_IMPLEMENTATION 매크로를 두 개 정의하면 컴파일이 실패할 수 있습니다. llm-log와 llm-stream이 그런 조합입니다.

llm_impl.cppLOG + STREAM을 한 파일에: 실패할 수 있음
llm_impl_log.cpp#define LLM_LOG_IMPLEMENTATION
llm_impl_retry.cpp#define LLM_RETRY_IMPLEMENTATION
llm_impl_stream.cpp#define LLM_STREAM_IMPLEMENTATION
2026-09-25 확인: 구현 26개를 각각 별도 .cpp에 두면 모두 하나의 바이너리로 컴파일·링크됩니다(MSVC 19.44 x64, libcurl은 vcpkg에서). CI가 푸시할 때마다 g++로 다시 확인합니다.
main.cpp: 스트리밍, 실패 시 재시도, 호출 로깅
#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

설치 명령을 대신 써 드립니다.

카탈로그에서 헤더를 고르세요. 여기 나오는 명령은 헤더를 third_party/로 받아 오고, 구현마다 별도의 .cpp를 만들고, 고른 것 중에 필요한 게 있을 때만 -lcurl을 붙입니다.