Generating an AIBOM
Produce a CycloneDX 1.6 AI Bill of Materials with xgrep sbom --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 --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 --aibom .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 --aibom --output aibom.cdx.json .--aibom selects the AI-inventory bill of materials; it emits AI assets only and
does not include your software dependencies — run plain xgrep sbom
for those. 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.
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 |
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.
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), so the inventory can be
matched against advisory feeds. Versions are read from package.json and npm
lockfiles, go.mod, requirements.txt, and the common Python/Ruby/PHP/Rust/Java
lockfiles — all offline.
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. 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).
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, including custom-endpoint and model-id detection; 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.