Generating an AIBOM
Produce a CycloneDX 1.6 AI Bill of Materials with xgrep sbom --include aibom — an inventory of the AI/LLM SDKs, frameworks, inference runtimes, and models a codebase uses, across 16 languages, fully offline.
Generating an AIBOM
xgrep sbom --include aibom builds an AI Bill of Materials (AIBOM): a machine-readable
inventory of the AI building blocks a codebase uses — which model providers it
calls (OpenAI, Anthropic, Google Vertex/Gemini, AWS Bedrock, Together AI, …), which
orchestration frameworks it is built on (LangChain, LlamaIndex, Spring AI, the
Vercel AI SDK, …), which local inference runtimes it embeds (Ollama, llama.cpp,
Hugging Face Transformers, ONNX Runtime), and which models it requests (gpt-4o,
claude-3-5-sonnet, …).
xgrep sbom --include aibom .The path is optional — xgrep sbom --include aibom with no path scans the current
directory, just like xgrep scan.
The scan is fully offline and reads source code, detecting AI usage with
xgrep's language-aware rule engine — the AIBOM reflects what the code actually
imports and calls, not what a manifest merely declares. Where a detected library
also appears in the project's dependency manifests, its version and package
URL (purl) are attached to the component (see Versions below); a
library the code imports but no manifest pins simply stays version-less. The
output is a CycloneDX 1.6 JSON
document on stdout:
xgrep sbom --include aibom --output aibom.cdx.json .--include aibom selects the AI-inventory bill of materials on its own — it emits
AI assets only, with no software dependencies. To get the AI inventory and your
dependencies (and optionally cryptographic assets) as one merged CycloneDX
document, list them together:
xgrep sbom --include sca,aibom --output bom.cdx.json .See Combined bills of materials
for the full content grid. Because AI-inventory detections are a distinct category,
they never appear in a normal xgrep scan — an AIBOM is an inventory, not a list of
vulnerabilities.
The older
xgrep sbom --aibomflag still works as a deprecated alias for--include aibom, and is slated for removal in v0.3.0.
What it detects
Every detected asset is a CycloneDX component carrying an aibom:* property set.
The aibom:kind facet classifies it, so a consumer can filter to just the
providers, just the models, and so on:
aibom:kind | What it is | Examples |
|---|---|---|
provider | a hosted/managed LLM API SDK | OpenAI, Anthropic, Cohere, Together, Groq, AWS Bedrock, watsonx |
framework | an orchestration / agent framework | LangChain, LlamaIndex, Spring AI, Vercel AI SDK, Semantic Kernel |
inference | a local / self-hosted inference runtime | Ollama, llama.cpp, Hugging Face Transformers, ONNX Runtime |
ml-core | a general ML framework | PyTorch, TensorFlow, Candle, DJL |
vector-db | a RAG vector-store client | Pinecone, Chroma, Qdrant, Milvus, Weaviate |
observability | an LLM tracing / observability / eval platform | LangSmith, Langfuse, Arize Phoenix, TruLens |
guardrails | an LLM guardrail / safety I-O-validation library | Guardrails AI, NeMo Guardrails, LLM Guard, LlamaFirewall |
evaluation | an LLM evaluation / testing framework | Ragas, DeepEval, Braintrust, Patronus AI, Inspect AI |
model | a specific model requested in code | gpt-4o, claude-3-5-sonnet, gemini-1.5-pro |
Other facets carried per asset:
aibom:provider— the canonical provider (e.g.Google Geminifor both the new and legacy Google SDKs), so distinct SDKs still roll up to one provider.aibom:hosting—managed-api,cloud-platform, orlocal.aibom:deprecated— set on superseded SDKs (e.g. Google's legacygoogle-generativeai,@xenova/transformers) so legacy AI usage is surfaced, not hidden.aibom:openai-compatible— set on providers reached through the OpenAI SDK with a custom endpoint (see below).
Providers, frameworks, inference runtimes, and models are emitted as CycloneDX
library components; models are emitted as machine-learning-model components.
The document's dependencies graph links each detected model to the provider it is
served by.
Observability & tracing platforms
The observability kind inventories the LLM tracing / observability / evaluation
platforms a codebase integrates — LangSmith, Langfuse, and Arize Phoenix
(Python and JavaScript/TypeScript), plus Arize AX and TruLens (Python only,
the languages with a first-party SDK). A
traced application ships its prompts and completions to that third-party service,
so its presence is a supply-chain and data-egress signal — a governance team can
filter to aibom:kind=observability to see which external services receive model
inputs and outputs. Detection is import-based and anchored precisely (for example,
the phoenix import for Arize Phoenix is matched so the unrelated Apache
phoenixdb driver is not). Tools enabled only through environment variables
(LangSmith auto-tracing when LangChain is present) or a proxy/config (Helicone,
promptfoo) are not import-detectable and are a documented gap.
Guardrail & safety tooling
The guardrails kind inventories the LLM guardrail / safety libraries an application
uses to defend its model inputs and outputs — prompt-injection and jailbreak
detection, PII redaction, content moderation, output validation. This is the inverse
of the observability signal: filter to aibom:kind=guardrails to see whether — and
with what — an LLM app validates its I/O. Detected today: Guardrails AI, NeMo
Guardrails, LLM Guard, LlamaFirewall, and the legacy Rebuff (Python;
Rebuff also JS/TS), plus OpenAI Guardrails (JavaScript/TypeScript). Detection is
import-based. General-purpose tools that merely get used near an LLM (e.g. Microsoft
Presidio for PII) are excluded, since an import alone is not a reliable
guardrail signal; API-only services (Lakera) and models rather than libraries
(Llama Guard) are out of scope.
Evaluation & testing frameworks
The evaluation kind inventories the LLM evaluation / testing frameworks an
application uses to score or test model output — RAG and answer-quality metrics,
faithfulness checks, pytest-style assertions, model-graded evals. It's the
quality-assurance member of the AI-ops trio (alongside observability and guardrails):
filter to aibom:kind=evaluation to see whether — and with what — an app's output
quality is tested. Detected today: Ragas, DeepEval, Braintrust,
Patronus AI (Python; DeepEval, Braintrust, and Patronus also JavaScript/TypeScript)
and Inspect AI (Python). Detection is import-based; general-purpose ML tooling
(Evidently, DeepChecks, HuggingFace evaluate) and config/CLI-only tools (promptfoo)
are excluded, since an import alone is not a reliable LLM-evaluation signal.
Versions
When a detected library is also declared in the project's dependency manifests,
its component carries the pinned version and a purl
(e.g. pkg:pypi/openai@1.35.7, pkg:npm/openai@4.52.1, pkg:maven/com.openai/openai-java@0.31.0),
so the inventory can be matched against advisory feeds. Versions are read offline
from each ecosystem's manifests and lockfiles — package.json and npm lockfiles,
go.mod, requirements.txt and the Python lockfiles, Cargo.lock,
Gemfile.lock, composer.lock, pubspec.lock, Maven/Gradle (pom.xml),
NuGet (packages.lock.json/packages.config), mix.lock, Package.resolved,
conan.lock, Manifest.toml, and renv.lock — covering every language xgrep
detects AI libraries in.
The version is only ever attached to a library the code actually imports, never
synthesized from a manifest line nothing uses. A library with no matching manifest
entry stays version-less rather than guessing. Because one provider can be reached
from more than one ecosystem (say openai used from both Python and JavaScript),
a component whose usages resolve to different versions is left version-less — the
BOM states a version only when every usage agrees, and the per-file evidence still
records where each import was found.
Providers reached through a custom endpoint
Many providers are used through the OpenAI SDK pointed at a different base URL. xgrep resolves the endpoint host to the real provider:
- a known host (
api.deepseek.com,openrouter.ai,api.groq.com, NVIDIA NIM, …) resolves to that provider, markedopenai-compatible; - the canonical OpenAI/Azure endpoint adds no duplicate (the SDK component already represents it);
- an unknown public host becomes a generic OpenAI-compatible endpoint asset named by its host;
- a local host (loopback, an IP,
localhost, a.local/single-label name) collapses into a single self-hosted LLM endpoint asset (local inference) rather than one component per address.
AWS Bedrock
How Bedrock is reached depends on the language. In Python it has no dedicated
SDK — it rides the general boto3 cloud SDK with a bedrock/bedrock-runtime
service selector, so xgrep keys on that service signal: boto3.client("bedrock-runtime")
is inventoried while boto3.client("s3") is not. In JavaScript/TypeScript (and
other languages with a dedicated Bedrock client package — e.g. Java's
software.amazon.awssdk.services.bedrockruntime, Go's .../service/bedrockruntime,
.NET's Amazon.BedrockRuntime), the import itself is the signal and is matched
like any other SDK.
Models
When a call passes a string-literal model id — client.chat.completions.create(model="gpt-4o"),
ChatAnthropic(model="claude-3-5-sonnet-20241022") — xgrep promotes it to a
machine-learning-model component. This is not limited to chat: embedding, image,
and audio model calls (embeddings.create(model="text-embedding-3-small"),
images.generate(model="dall-e-3"), audio.speech.create(model="tts-1"),
audio.transcriptions.create(model="whisper-1")) are captured the same way, so an
inventory reflects the full set of models a codebase requests, not just chat ones.
Its provider is inferred from the model name
(gpt-* → OpenAI, claude-* → Anthropic, gemini-* → Google, …) rather than the
call shape, since API shapes like .messages.create and .chat.completions.create
are reused across SDKs; a model whose name matches no known provider is inventoried
without a provider link rather than misattributed. A model passed as a variable is
not turned into an asset (accuracy over recall).
Model-id capture is available for Python, JavaScript/TypeScript, Go, Java, Kotlin,
C#, Rust, Ruby, and PHP. Where a language's SDK sets the model on an AI-specific call node
(a params struct, a ModelId("…") wrapper, a new ChatClient(model: …)), any
string-literal model is captured. Where it is set by a generic builder method deep
in a chain (.model("…") / .modelName("…")), capture is limited to string-literal
model ids whose name is a recognized AI model, so an unrelated .model("users") is
never mistaken for a model asset. Model ids given as SDK enum constants rather than
strings are not captured.
AWS Bedrock invocations are captured too (Python, JavaScript/TypeScript, Go,
Java, and C#): the model id passed to an InvokeModel or Converse call — for
example boto3's client.invoke_model(modelId="anthropic.claude-3-5-sonnet-20240620-v1:0")
or new InvokeModelCommand({ modelId: "amazon.titan-text-express-v1" }) — is
promoted to a model component and attributed to AWS Bedrock, the provider that
serves it. Cross-region inference-profile ids (us.anthropic.claude-…) are
recognized as well.
Google Gemini / Vertex AI models are captured too (Python, JavaScript/TypeScript,
Go, and Java), across Google's overlapping SDKs — the unified google-genai
(models.generate_content(model="gemini-2.5-flash") / generateContent({ model })),
the legacy google-generativeai and Vertex vertexai (GenerativeModel("gemini-1.5-pro")),
and the LangChain adapters (ChatGoogleGenerativeAI / ChatVertexAI). The model
resolves by name to Google Gemini; a models/-prefixed id (models/gemini-1.5-pro)
is recognized too.
Mistral AI (Python, JavaScript/TypeScript) and Cohere (Python,
JavaScript/TypeScript, Go) native SDKs are covered as well — Mistral's
chat.complete("mistral-large-latest") / fim.complete("codestral-latest") and
Cohere's chat / embed / rerank calls (command-*, embed-*, rerank-*).
Mistral's open-mistral-* / open-mixtral-* open-weight models are attributed too.
Embedding / reranking specialists — Voyage AI, Nomic, and Mixedbread (Python,
JavaScript/TypeScript) — are inventoried as providers, together with the embedding
models they call (embed(model="voyage-3.5"), nomic-embed-text-v1.5,
mixedbread-ai/mxbai-embed-large-v1). These managed embedding + reranker APIs are a
common RAG building block.
Anthropic (Claude) model capture spans the native SDKs beyond Python/JS/Go —
the Ruby anthropic gem (client.messages.create(model: "claude-…")), the Kotlin
anthropic-java builder (.model("claude-…")), and the .NET Anthropic.SDK
(new MessageParameters { Model = "claude-…" }). Model ids given as SDK constants
(AnthropicModels.…, Model.CLAUDE_*) are not captured.
Local / open models are captured too (Python, JavaScript/TypeScript): the model
a codebase runs through a self-hosted runtime — Hugging Face Transformers
(AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B"),
pipeline(model="…")), SentenceTransformer("all-MiniLM-L6-v2"), vLLM
LLM(model="…"), and Ollama ollama.chat(model="llama3.1"). An open model has no
single managed provider, so it is listed as a model with no provider link (unless
its name matches a known provider); tokenizer/config loads are not mistaken for
models.
Language coverage
AI-library detection ships for 16 languages:
Python · JavaScript/TypeScript · Java · Kotlin · Scala · Go · C# · Rust · Ruby · PHP · Dart · Swift · Elixir · R · Julia · C/C++
Detection is import/usage-based and precise: a rule matches an AI SDK's import (or,
for C/C++, its local-inference header #include) and rejects look-alikes whose name
merely shares a prefix. Depth varies with each ecosystem — Python has the widest
coverage; custom-endpoint detection ships for Python, JavaScript/TypeScript, Go,
Kotlin, C#, Rust, Ruby, and PHP, and model-id capture ships for Python,
JavaScript/TypeScript, Go, Java, Kotlin, C#, Rust, Ruby, and PHP; C/C++ covers
local-inference runtime headers only (there are no managed-provider SDKs for it).
Coverage is inherently a moving target as the AI ecosystem grows, so read the
absence of a library as "not yet detected," not "no AI present."
Example output
{
"$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json",
"bomFormat": "CycloneDX",
"specVersion": "1.6",
"components": [
{
"type": "library",
"bom-ref": "ai/provider/openai",
"name": "openai",
"group": "OpenAI",
"properties": [
{ "name": "aibom:kind", "value": "provider" },
{ "name": "aibom:provider", "value": "OpenAI" },
{ "name": "aibom:hosting", "value": "managed-api" },
],
"evidence": { "occurrences": [{ "location": "app.py", "line": 1 }] },
},
{
"type": "machine-learning-model",
"bom-ref": "ai/model/gpt-4o",
"name": "gpt-4o",
"properties": [
{ "name": "aibom:kind", "value": "model" },
{ "name": "aibom:provider", "value": "OpenAI" },
],
},
],
"dependencies": [{ "ref": "ai/model/gpt-4o", "dependsOn": ["ai/provider/openai"] }],
}See also
- Generating an SBOM — the software-dependency bill of materials.
- SCA overview — how xgrep inventories and analyzes third-party code.