llm-kernel 0.16.0

Foundation library for Rust AI-native apps — provider catalog, LLM client, MCP server, search, telemetry, and safety
Documentation
[package]
name         = "llm-kernel"
version      = "0.16.0"
edition      = "2024"
description  = "Foundation library for Rust AI-native apps — provider catalog, LLM client, MCP server, search, telemetry, and safety"
license      = "Apache-2.0"
repository   = "https://github.com/epicsagas/llm-kernel"
homepage     = "https://github.com/epicsagas/llm-kernel"
rust-version = "1.92"
keywords     = ["llm", "ai", "mcp", "provider", "embedding"]
categories   = ["api-bindings", "asynchronous", "science"]
documentation = "https://docs.rs/llm-kernel"
readme       = "README.md"
exclude      = [".github/", "target/"]

[workspace]
members = [
    ".",
]

[lib]
name = "llm_kernel"
path = "src/lib.rs"

[features]
default = ["provider"]

# Core provider catalog (ProviderIndex, ServiceDescriptor, catalog.json)
provider = ["dep:serde_json"]

# Dynamic model discovery (models.dev, Ollama, OpenAI-compatible)
discovery = ["provider", "dep:ureq"]

# Asynchronous LLM client (reqwest-based) with JSON extraction
client-async = ["dep:reqwest", "dep:serde_json", "dep:async-trait", "dep:tokio", "dep:tokio-stream", "dep:futures-core"]

# Asynchronous model discovery (async DiscoverySource over reqwest)
discovery-async = ["discovery", "dep:reqwest", "dep:async-trait", "dep:tokio"]

# Catalog sync tooling — refresh catalog.json from models.dev (sync-catalog binary)
catalog-sync = ["provider", "discovery", "dep:clap", "dep:anyhow"]

# LLM response cache over a KvStore (wraps any LLMClient)
cache = ["client-async", "store"]

# Secret vault (dotenv-style credential management)
secrets = ["dep:tempfile"]

# SQLite store helpers (init_schema, PRAGMA, FTS5, KvStore)
store = ["dep:rusqlite"]

# TOML config loader
config = ["dep:toml"]

# Knowledge graph (SQLite + FTS5 + smart recall + BFS traversal)
graph = ["store", "dep:serde_json"]

# CJK-aware graph search (Rust-side segmentation; no schema change)
graph-cjk = ["graph"]

# PostgreSQL GraphBackend (PgGraph) + SQLite<->PostgreSQL migration CLI.
# Mirrors the SQLite graph over the synchronous `postgres` driver.
graph-pg = ["graph", "dep:postgres", "dep:clap"]

# TLS-enabled PgGraph connections (connect_tls / connect_native_tls) via
# postgres-native-tls — for Postgres servers requiring sslmode=require+.
graph-pg-tls = ["graph-pg", "dep:postgres-native-tls", "dep:native-tls"]

# MCP server framework (JSON-RPC 2.0, stdio transport, async handlers)
mcp = ["dep:serde_json", "dep:async-trait"]

# MCP remote transport over HTTP with optional SSE (axum + tokio)
mcp-http = ["mcp", "dep:axum", "dep:tokio", "dep:tokio-stream", "tokio/net", "tokio/io-util"]

# Token estimation (zero-dep heuristic)
tokens = []

# AI tool installation wizard
install = ["dep:serde_json"]

# Hybrid search — SearchProvider trait + RRF / weighted-sum / CombMNZ fusion
# over local result lists. Stays light (serde_json only): async cross-engine
# federation lives behind the dedicated `federation` feature so a `search`-only
# user compiles no async runtime.
search = ["dep:serde_json"]

# Embedding provider trait + cosine similarity (+ async vector index trait)
embedding = ["dep:async-trait"]

# TurboQuant compressed vector index (2-bit/4-bit, SIMD ANN search)
vector-index = ["embedding", "dep:turbovec", "dep:serde_json"]

# Qdrant AsyncVectorIndex (QdrantVectorIndex) for remote vector search.
qdrant = ["embedding", "dep:qdrant-client", "dep:serde_json"]

# Elasticsearch AsyncVectorIndex (ElasticsearchVectorIndex) over a hand-rolled
# reqwest client (the official `elasticsearch` crate is alpha-only). Mirrors the
# `qdrant` feature, reusing the reqwest already behind `client-async`.
elastic = ["embedding", "dep:reqwest", "dep:serde_json"]

# pgvector AsyncVectorIndex (PgVectorIndex) over `sqlx` + the pgvector
# extension. Mirrors `qdrant`/`elastic` — an async remote vector backend.
pgvector = ["embedding", "dep:sqlx", "dep:pgvector"]

# Cross-engine search federation — concurrent query over multiple
# `AsyncVectorIndex` backends with a per-backend timeout and RRF / weighted-sum
# fusion (`FederatedSearch`). Owns the `tokio` + `futures-util` deps so `search`
# and `embedding` stay light; a single-backend `qdrant`/`elastic` user compiles
# no federation runtime.
federation = ["search", "embedding", "dep:tokio", "dep:futures-util"]

# OpenAI text-embedding provider (requires ureq for sync HTTP)
embedding-openai = ["embedding", "dep:ureq", "dep:serde_json"]

# Local ONNX embedding via fastembed-rs (downloads models from HuggingFace).
# Statically links ONNX Runtime via `fastembed/ort-download-binaries-rustls-tls`:
# ort-sys downloads + links a prebuilt static archive at build time, producing a
# self-contained binary with no runtime dylib resolution. CAVEAT (#55): ort's
# prebuilt static archive requires glibc ≥2.38 (`__isoc23_strtol` etc.) on Linux
# and a current MSVC CRT on Windows; older baselines (ubuntu 22.04, glibc 2.35)
# fail at the *release link step* (`cargo check` stays green because it does
# not link). For those, use `embedding-fastembed-dynamic-linking` instead.
embedding-fastembed = ["embedding", "dep:fastembed", "dep:indexmap", "fastembed/hf-hub-rustls-tls", "fastembed/ort-download-binaries-rustls-tls"]

# Qwen3 embedding via fastembed-rs candle backend
embedding-fastembed-qwen3 = ["embedding-fastembed", "fastembed/qwen3", "dep:candle-core"]

# Nomic V2 MoE embedding via fastembed-rs candle backend
embedding-fastembed-nomic-moe = ["embedding-fastembed", "fastembed/nomic-v2-moe", "dep:candle-core"]

# DirectML GPU execution provider for FastembedProvider (Windows only)
embedding-fastembed-directml = ["embedding-fastembed", "dep:ort"]

# Opt-in dynamic ONNX Runtime linking (loads libonnxruntime.{so,dylib,dll} at
# runtime instead of statically linking a prebuilt archive at build time).
#
# Use this when your build/release target cannot satisfy the static archive's
# requirements — e.g. a glibc <2.38 Linux host (ort's prebuilt static archive
# needs glibc ≥2.38 and fails at the release link step on ubuntu 22.04; see
# #50, #55) — and you can guarantee `libonnxruntime.*` is present on the
# runtime host.
#
# This feature is MUTUALLY EXCLUSIVE with `embedding-fastembed` (and with any
# feature that implies it: `embedding-fastembed-qwen3`, `-nomic-moe`,
# `-directml`, or `full`). Cargo feature unification merges both
# `ort-load-dynamic` and `ort-download-binaries-*` into the shared
# `fastembed`/`ort-sys` crate, silently turning the static path into a no-op
# (the original #50 failure mode); a `compile_error!` in `src/lib.rs` makes
# the conflict a hard build error instead. Enable exactly one of the two modes.
embedding-fastembed-dynamic-linking = ["embedding", "dep:fastembed", "dep:indexmap", "fastembed/hf-hub-rustls-tls", "fastembed/ort-load-dynamic"]

# Knowledge graph with async wrappers (requires tokio)
graph-async = ["graph", "dep:tokio"]

# Knowledge graph with multi-connection async pool (rusqlite + tokio semaphore).
# Provides higher read concurrency than `graph-async` for WAL-mode workloads.
graph-pool = ["graph", "dep:tokio"]

# Telemetry framework (enum-gated events)
telemetry = ["dep:serde_json"]

# Safety utilities (secret masking, error classification)
safety = ["dep:regex"]

# Quality evaluation CLI (datasets-based metrics)
eval = ["dep:clap", "dep:anyhow", "tokens", "safety", "embedding", "search"]
eval-full = ["eval", "graph"]

# Everything except Windows-only execution backends and dev-only CLI tooling.
# Intentionally excluded:
#   - embedding-fastembed-directml: pulls in the ort RC dependency, Windows-only.
#   - embedding-fastembed-dynamic-linking: opt-in dynamic ONNX linking (see #50).
#   - eval / eval-full: developer CLI tooling (the `llm-kernel-eval` binary),
#     not part of the library surface. Enable explicitly to run the eval suite.
full = ["provider", "discovery", "discovery-async", "client-async", "cache", "secrets", "store", "config", "graph", "graph-async", "graph-pool", "graph-cjk", "graph-pg", "graph-pg-tls", "mcp", "mcp-http", "tokens", "install", "search", "embedding", "embedding-openai", "embedding-fastembed", "embedding-fastembed-qwen3", "embedding-fastembed-nomic-moe", "vector-index", "qdrant", "elastic", "pgvector", "federation", "telemetry", "safety", "catalog-sync"]

[dependencies]
# Always required
serde       = { version = "1", features = ["derive"] }
thiserror   = "2"
tracing     = "0.1"

# Only the `eval` / `catalog-sync` binaries use anyhow; keep it out of the
# default `provider` build and every library consumer's dependency graph.
anyhow      = { version = "1", optional = true }

# Feature-gated
serde_json  = { version = "1", optional = true }
ureq        = { version = "3", features = ["json"], optional = true }
tempfile    = { version = "3", optional = true }
reqwest     = { version = "0.13", features = ["json", "rustls", "stream"], default-features = false, optional = true }
rusqlite    = { version = "0.37", features = ["bundled"], optional = true }
toml        = { version = "1", optional = true }
tokio       = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time"], optional = true }
tokio-stream = { version = "0.1", optional = true }
futures-core = { version = "0.3", optional = true }
futures-util = { version = "0.3", optional = true }
async-trait = { version = "0.1", optional = true }
regex       = { version = "1", optional = true }
# NOTE: fastembed's ort features (static download vs dynamic load) are selected
# by the `embedding-fastembed` / `embedding-fastembed-dynamic-linking` features
# below, NOT in this dependency declaration, so the two linking modes stay
# mutually exclusive at the feature level (see #55).
fastembed   = { version = "5", default-features = false, optional = true }
# Pinned to the newest ort release-candidate: ort 2.0.0 stable is not yet
# published (2.0.0-rc.12 is the latest on crates.io as of 2026-06), and
# fastembed 5 targets this same RC, so the pin must move in lockstep with a
# fastembed release built against stable. Switch to a caret range (`2`) once
# both conditions hold.
ort         = { version = "=2.0.0-rc.12", default-features = false, optional = true }
candle-core = { version = "0.10", optional = true }
indexmap    = { version = "2", optional = true }
clap        = { version = "4", features = ["derive"], optional = true }
turbovec    = { version = "0.9", optional = true }
axum        = { version = "0.8", default-features = false, features = ["http1", "json", "tokio"], optional = true }
postgres    = { version = "0.19", optional = true }
postgres-native-tls = { version = "0.5", optional = true }
native-tls  = { version = "0.2", optional = true }
qdrant-client = { version = "1", optional = true }
sqlx         = { version = "0.8", default-features = false, features = ["postgres", "runtime-tokio-rustls", "macros"], optional = true }
pgvector     = { version = "0.4", default-features = false, features = ["sqlx"], optional = true }

[dev-dependencies]
tempfile = "3"
criterion = { version = "0.8", features = ["html_reports"] }
tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] }

[[bench]]
name = "graph_bench"
harness = false
required-features = ["graph"]

[[bench]]
name = "compute_bench"
harness = false
required-features = ["search", "tokens", "embedding"]

[[bin]]
name = "llm-kernel-eval"
path = "src/bin/eval.rs"
required-features = ["eval"]

[[bin]]
name = "llm-kernel-migrate-graph"
path = "src/bin/migrate.rs"
required-features = ["graph-pg"]

[[bin]]
name = "llm-kernel-sync-catalog"
path = "src/bin/sync-catalog.rs"
required-features = ["catalog-sync"]

[profile.dev]
opt-level = 0
debug = 1

[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
strip = "symbols"
panic = "abort"

[profile.release.package."*"]
opt-level = 3
codegen-units = 4

[profile.dist]
inherits = "release"
lto = "thin"

[package.metadata.docs.rs]
# docs.rs defaults to `--all-features`, which would activate the
# mutually-exclusive `embedding-fastembed` + `embedding-fastembed-dynamic-linking`
# and trip the `compile_error!` in src/lib.rs. Build docs with `full` instead (#55).
features = ["full"]