Skip to main content

xains

PyPI version License: MIT Tests

Python Downloads

Ruff Last commit Contributions welcome Code of Conduct

xains generates explainable AI (XAI) Narratives - hence the name. It turns technical XAI outputs, feature-importance attributions (SHAP or LIME) and counterfactuals (like DiCE), into clear natural-language narrative that makes the model's decision understandable to a broad audience.

Installation

xains is intended to work with Python 3.11 and above. Installation can be done via pip :

pip install xains

Or via uv :

uv add xains

Quickstart

Imagine a classifier flagged this applicant as a likely default. The raw feature importances (for example from SHAP) are: {debt_to_income: +0.37, salary: -0.21, age: -0.12}. Accurate, but is that understandable to a broad audience? xains turns it into a narrative.

xains needs three things: a schema (what the features and target mean), a request (this instance plus its importances), and an explainer (which model verbalizes it).

import xains
import xains.prompts

schema = xains.DatasetSchema(
    modality=xains.Modality.TABULAR,
    name="credit_risk",
    description="Predicts 24-month default on personal loans.",
    target=xains.TargetSchema(
        name="default",
        description="Whether the applicant defaulted.",
        classes={0: "Repaid", 1: "Defaulted"},
    ),
    features=[
        xains.FeatureSchema(name="age", dtype="numeric", unit="years",
                           description="Applicant age at application."),
        xains.FeatureSchema(name="salary", dtype="numeric", unit="EUR",
                           description="Annual gross salary."),
        xains.FeatureSchema(name="debt_to_income", dtype="numeric",
                           description="Debt-to-income ratio."),
    ],
)

request = xains.TabularExplanationRequest(
    features={"age": 29, "salary": 52000, "debt_to_income": 0.41},
    prediction=xains.Prediction(predicted_class=1, probabilities={0: 0.2, 1: 0.8}),
    contributions=[
        xains.TabularContribution(name="debt_to_income", value=0.41, importance=0.37),
        xains.TabularContribution(name="salary", value=52000, importance=-0.21),
        xains.TabularContribution(name="age", value=29, importance=-0.12),
    ],
)

llm = xains.AnthropicProvider(model="claude-haiku-4-5", max_tokens=512)
explainer = xains.Explainer(
    schema=schema,
    generator=xains.LLMNarrativeGenerator(
        prompt_template=xains.prompts.FeatureImportanceTabularPromptTemplate(),
        llm=llm,
    ),
    config=xains.ExplanationConfig(
        mode="feature_importance", audience="end_user",
        max_length_words=40, extract_narrative=True,
    ),
    judge_llm=llm,  # required when extract_narrative=True
)

result = explainer.explain(request)
print(result.text)

The resulting XAIN (XAI narrative):

Your profile indicates elevated default risk. A debt-to-income ratio of 0.41
substantially increases this concern, signaling that your debt obligations
consume a meaningful portion of earnings. Although your salary of EUR 52,000
and relatively young age of 29 provide some protective factors that work
against default, they ultimately prove insufficient to offset the debt burden
weighing on your financial stability.

Scoring the narrative

A narrative is only useful if it is faithful to the attributions and reads well. xains scores both. grade_extraction checks the claims the narrative makes against the input attributions - sign, value, and rank fidelity, coverage, and hallucination count:

grades = xains.grade_extraction(
    extraction=result.narrative_extraction,
    request=request,
    schema=schema,
    narrative_text=result.text,
    k=5,
)
print(xains.render_grades(extraction=grades))
Verbalization fidelity
  sign_faithfulness ↑: 1.0
  value_faithfulness ↑: 1.0
  rank_correlation ↑: 1.0
  coverage ↑: 1.0
  hallucination_count ↓: 0

Arrows mark the desired direction for each metric ( higher is better, lower is better). render_grades also accepts a narrativity= argument and emits a second Narrativity section.

grade_narrativity scores how well the text reads as a narrative, using the metrics from Cedro & Martens 2026. It needs a perplexity provider (any OpenAI-compatible endpoint that returns logprobs):

from xains.metrics import OpenAICompatibleEchoProvider

ppl = OpenAICompatibleEchoProvider(
    base_url="https://api.together.xyz/v1",
    model="meta-llama/Meta-Llama-3-8B-Instruct-Lite",
    api_key_env_var="TOGETHER_API_KEY",
)
narrativity = xains.grade_narrativity(result.text, ppl)
print(xains.render_grades(narrativity=narrativity, scored_only=True))
Narrativity
  csr ↑: 0.27
  dcpr ↓: 1.3
  ccpr ↓: 203.14
  cecpr ↓: 29252.68
  fdr ↑: 0.29
  ttcpr ↓: 2.29
  vcpr ↓: 50.79

These are the seven Cedro & Martens 2026 narrativity metrics. scored_only=True hides the nine auxiliary primitives (ppl_ordered, ttr, n_sentences, ...) which grade_narrativity also captures for paper replication; omit the flag to see them.

End-to-end notebooks

  • Feature-importance (notebooks/feature_importance_narratives.ipynb): load German Credit, train a RandomForest, compute SHAP, generate the narrative, extract structured claims, and score on faithfulness and narrativity.
  • Counterfactual (notebooks/counterfactual_narratives.ipynb): generate a counterfactual with DiCE, verbalize it two ways, and score change-fidelity, coverage, and invented features.
  • Hybrid (notebooks/hybrid_narratives.ipynb): compute both SHAP contributions and a DiCE counterfactual on one model's prediction, generate a two-section narrative, run dual extraction, and score both halves with grade_hybrid.

API keys

The remote LLM providers need API keys (set them in your environment or a .env file; see .env.example).

Choosing a model

Any LLMProvider drops into xains.LLMNarrativeGenerator(llm=...) - pick the provider for the model you want:

import xains

# Anthropic (reads ANTHROPIC_API_KEY)
llm = xains.AnthropicProvider(model="claude-haiku-4-5", max_tokens=512)

# OpenAI (reads OPENAI_API_KEY)
llm = xains.OpenAIProvider(model="gpt-4o-mini", max_tokens=512)

# OpenRouter - Llama, and many others (reads OPENROUTER_API_KEY)
llm = xains.OpenRouterProvider(model="meta-llama/llama-3.3-70b-instruct", max_tokens=512)

# Any OpenAI-compatible endpoint (Together, Groq, vLLM, ...) - set base_url + the env var to read
llm = xains.OpenAICompatibleProvider(
    base_url="https://api.together.xyz/v1",
    api_key_env_var="TOGETHER_API_KEY",
    model="meta-llama/Llama-3.3-70B-Instruct-Turbo",
    max_tokens=512,
)
# Local model on your own GPU via a local server (no API key, no hosted API)
llm = xains.LocalServerProvider(model="llama3.1", server="ollama")

Running local models

Run open models on your own GPU with no hosted API and no API key. There are two ways, and both work for generation and scoring (pass the provider as llm= and judge_llm=). Option 1 - a local server (LocalServerProvider). You run a local OpenAI-compatible server (Ollama, vLLM, or LM Studio) and xains talks to it over HTTP. The server loads and manages the model.

ollama serve
ollama pull llama3.1
import xains
# Name your server (resolves to its default local endpoint):
llm = xains.LocalServerProvider(model="llama3.1", server="ollama")   # or "vllm", "lmstudio"
# Or any OpenAI-compatible endpoint by URL:
llm = xains.LocalServerProvider(model="qwen2.5", base_url="http://localhost:8000/v1")

This uses the openai extra: pip install "xains[openai]". Option 2 - in-process weights (LocalModelProvider). xains loads the HuggingFace model directly in your Python process with transformers, no separate server. Good for a self-contained pipeline.

import xains
llm = xains.LocalModelProvider(model="meta-llama/Llama-3.1-8B-Instruct")
# Greedy by default; set do_sample=True for sampled variety.

The model loads on your GPU (device_map="auto") at construction and must be an instruct/chat model. This uses the local extra: pip install "xains[local]" (torch, transformers, accelerate).

License

MIT - see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

xains-0.2.0.tar.gz (59.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

xains-0.2.0-py3-none-any.whl (75.4 kB view details)

Uploaded Python 3

File details

Details for the file xains-0.2.0.tar.gz.

File metadata

  • Download URL: xains-0.2.0.tar.gz
  • Upload date:
  • Size: 59.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for xains-0.2.0.tar.gz
Algorithm Hash digest
SHA256 0727f3b2f8bd28fafd8c406ececddfb1a41735b7eef813b431d4b3463b6632d3
MD5 57460a8102e5a5b984822008fbad445d
BLAKE2b-256 5bd94eb2e5fc800a14db01506815207db2f50a90caaf446d7f6b889492a49e89

See more details on using hashes here.

File details

Details for the file xains-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: xains-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 75.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for xains-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 339c569807b635672da918ecf9ff287aa33c36a13d0eb2c21b5b0ca4f24096d6
MD5 6cdb292b24d718bc1c8b31757c398fc3
BLAKE2b-256 418591eafac281ebf6cf5a7b551e85695b886678c6a0312d0d94df0fd3e40cd4

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 files

0.1.1

2 files

0.1.0

2 files

0.0.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page