bayesian-llm-guard 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- bayesian_llm_guard-1.0.0/LICENSE +17 -0
- bayesian_llm_guard-1.0.0/PKG-INFO +35 -0
- bayesian_llm_guard-1.0.0/README.md +11 -0
- bayesian_llm_guard-1.0.0/bayesian_llm_guard/__init__.py +21 -0
- bayesian_llm_guard-1.0.0/bayesian_llm_guard/core/__init__.py +12 -0
- bayesian_llm_guard-1.0.0/bayesian_llm_guard/core/agent_loop.py +79 -0
- bayesian_llm_guard-1.0.0/bayesian_llm_guard/core/config.py +38 -0
- bayesian_llm_guard-1.0.0/bayesian_llm_guard/core/guardrail.py +81 -0
- bayesian_llm_guard-1.0.0/bayesian_llm_guard/core/uq_engine.py +46 -0
- bayesian_llm_guard-1.0.0/pyproject.toml +30 -0
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
Copyright 2026 XAIDeep.com
|
|
6
|
+
|
|
7
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
8
|
+
you may not use this file except in compliance with the License.
|
|
9
|
+
You may obtain a copy of the License at
|
|
10
|
+
|
|
11
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
|
|
13
|
+
Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
See the License for the specific language governing permissions and
|
|
17
|
+
limitations under the License.
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: bayesian-llm-guard
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Epistemic Uncertainty Estimation & Guardrails for Agentic LLMs and RAG Systems
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Author: AI Research Team
|
|
8
|
+
Author-email: research@xaideep.com
|
|
9
|
+
Requires-Python: >=3.10,<4.0
|
|
10
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
18
|
+
Requires-Dist: numpy (>=1.24.0,<2.0.0)
|
|
19
|
+
Requires-Dist: pydantic (>=2.0.0,<3.0.0)
|
|
20
|
+
Requires-Dist: pydantic-settings (>=2.0.0,<3.0.0)
|
|
21
|
+
Requires-Dist: torch (>=2.0.0)
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# bayesian-llm-guard 🛡️
|
|
25
|
+
|
|
26
|
+
**bayesian-llm-guard** is a lightweight, production-ready Python library that bridges Bayesian Epistemic Uncertainty Estimation with Agentic LLM guardrails to mitigate hallucinations in RAG systems.
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
_(Coming soon to PyPI)_
|
|
31
|
+
|
|
32
|
+
## License
|
|
33
|
+
|
|
34
|
+
Apache 2.0
|
|
35
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# bayesian-llm-guard 🛡️
|
|
2
|
+
|
|
3
|
+
**bayesian-llm-guard** is a lightweight, production-ready Python library that bridges Bayesian Epistemic Uncertainty Estimation with Agentic LLM guardrails to mitigate hallucinations in RAG systems.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
_(Coming soon to PyPI)_
|
|
8
|
+
|
|
9
|
+
## License
|
|
10
|
+
|
|
11
|
+
Apache 2.0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"""
|
|
2
|
+
bayesian-llm-guard
|
|
3
|
+
Epistemic Uncertainty Estimation & Guardrails for Agentic LLMs.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from .core import (
|
|
7
|
+
UQGuardConfig,
|
|
8
|
+
UQEngine,
|
|
9
|
+
uq_guard,
|
|
10
|
+
UQGuardException,
|
|
11
|
+
AgentSelfCorrectionLoop
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__version__ = "0.1.0"
|
|
15
|
+
__all__ = [
|
|
16
|
+
"UQGuardConfig",
|
|
17
|
+
"UQEngine",
|
|
18
|
+
"uq_guard",
|
|
19
|
+
"UQGuardException",
|
|
20
|
+
"AgentSelfCorrectionLoop"
|
|
21
|
+
]
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from .config import UQGuardConfig
|
|
2
|
+
from .uq_engine import UQEngine
|
|
3
|
+
from .guardrail import uq_guard, UQGuardException
|
|
4
|
+
from .agent_loop import AgentSelfCorrectionLoop
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"UQGuardConfig",
|
|
8
|
+
"UQEngine",
|
|
9
|
+
"uq_guard",
|
|
10
|
+
"UQGuardException",
|
|
11
|
+
"AgentSelfCorrectionLoop"
|
|
12
|
+
]
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent Loop Module for bayesian-llm-guard.
|
|
3
|
+
Implements an autonomous self-correction loop to retry and refine
|
|
4
|
+
queries when high uncertainty (hallucination) is detected.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from typing import Any, Callable, Dict
|
|
9
|
+
|
|
10
|
+
from bayesian_llm_guard.core.guardrail import UQGuardException
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger("bayesian_agent_loop")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AgentSelfCorrectionLoop:
|
|
16
|
+
"""
|
|
17
|
+
Manages iterative query refinement and re-execution for RAG pipelines.
|
|
18
|
+
Automatically catches uncertainty exceptions and refines the user's prompt.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(
|
|
22
|
+
self,
|
|
23
|
+
max_retries: int = 3,
|
|
24
|
+
query_refiner: Callable[[str], str] = lambda q: q + " (provide more specific context)"
|
|
25
|
+
) -> None:
|
|
26
|
+
"""
|
|
27
|
+
Args:
|
|
28
|
+
max_retries (int): Maximum number of times to retry the pipeline.
|
|
29
|
+
query_refiner (Callable): A function that modifies the failed query to improve retrieval.
|
|
30
|
+
"""
|
|
31
|
+
self.max_retries = max_retries
|
|
32
|
+
self.query_refiner = query_refiner
|
|
33
|
+
|
|
34
|
+
def execute_with_fallback(
|
|
35
|
+
self,
|
|
36
|
+
rag_pipeline_func: Callable[..., Dict[str, Any]],
|
|
37
|
+
initial_query: str,
|
|
38
|
+
**kwargs: Any
|
|
39
|
+
) -> Dict[str, Any]:
|
|
40
|
+
"""
|
|
41
|
+
Executes the provided RAG function. If it triggers the UQ Guardrail,
|
|
42
|
+
the query is refined and retried automatically.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
rag_pipeline_func (Callable): The decorated RAG function to execute.
|
|
46
|
+
initial_query (str): The original user prompt.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Dict[str, Any]: The safe, validated result from the pipeline.
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
RuntimeError: If the maximum number of retries is exhausted.
|
|
53
|
+
"""
|
|
54
|
+
current_query = initial_query
|
|
55
|
+
attempt = 0
|
|
56
|
+
|
|
57
|
+
while attempt < self.max_retries:
|
|
58
|
+
try:
|
|
59
|
+
logger.info(f"Agent Loop Attempt {attempt + 1}/{self.max_retries}. Query: '{current_query}'")
|
|
60
|
+
|
|
61
|
+
# Attempt to execute the pipeline
|
|
62
|
+
return rag_pipeline_func(query=current_query, **kwargs)
|
|
63
|
+
|
|
64
|
+
except UQGuardException as exception:
|
|
65
|
+
attempt += 1
|
|
66
|
+
logger.warning(f"Guardrail intercepted the response: {exception}")
|
|
67
|
+
|
|
68
|
+
if attempt < self.max_retries:
|
|
69
|
+
logger.info("Refining query and retrying...")
|
|
70
|
+
current_query = self.query_refiner(current_query)
|
|
71
|
+
|
|
72
|
+
# If the loop finishes without returning, the model could not produce a safe answer
|
|
73
|
+
error_msg = (
|
|
74
|
+
f"Agent loop failed after {self.max_retries} attempts. "
|
|
75
|
+
"The model consistently exhibited high epistemic uncertainty. "
|
|
76
|
+
"Please check your knowledge base or refine the original prompt."
|
|
77
|
+
)
|
|
78
|
+
logger.error(error_msg)
|
|
79
|
+
raise RuntimeError(error_msg)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Configuration Module for bayesian-llm-guard.
|
|
3
|
+
Manages type-safe settings and environment variables.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
from pydantic_settings import BaseSettings
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class UQGuardConfig(BaseSettings):
|
|
11
|
+
"""
|
|
12
|
+
Core configuration for the Bayesian Guardrail.
|
|
13
|
+
Values can be overridden using environment variables with the 'UQ_' prefix.
|
|
14
|
+
Example: export UQ_THRESHOLD=0.03
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
threshold: float = Field(
|
|
18
|
+
default=0.05,
|
|
19
|
+
ge=0.0,
|
|
20
|
+
le=1.0,
|
|
21
|
+
description="Maximum allowed epistemic variance. Values above this trigger the guardrail."
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
mc_samples: int = Field(
|
|
25
|
+
default=30,
|
|
26
|
+
ge=5,
|
|
27
|
+
le=100,
|
|
28
|
+
description="Number of Monte Carlo forward passes for uncertainty estimation."
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
max_retries: int = Field(
|
|
32
|
+
default=3,
|
|
33
|
+
ge=0,
|
|
34
|
+
description="Maximum number of self-correction attempts when high uncertainty is detected."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
class Config:
|
|
38
|
+
env_prefix = "UQ_"
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Guardrail Module for bayesian-llm-guard.
|
|
3
|
+
Provides the @uq_guard decorator to intercept LLM/RAG pipelines
|
|
4
|
+
and evaluate epistemic uncertainty before returning results.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
from functools import wraps
|
|
9
|
+
from typing import Any, Callable, Dict, Optional
|
|
10
|
+
|
|
11
|
+
from bayesian_llm_guard.core.uq_engine import UQEngine
|
|
12
|
+
from bayesian_llm_guard.core.config import UQGuardConfig
|
|
13
|
+
|
|
14
|
+
# Setup basic logging for the guardrail
|
|
15
|
+
logger = logging.getLogger("bayesian_guardrail")
|
|
16
|
+
logging.basicConfig(level=logging.INFO)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class UQGuardException(Exception):
|
|
20
|
+
"""
|
|
21
|
+
Raised when the model's epistemic uncertainty exceeds the defined safety threshold.
|
|
22
|
+
Indicates a potential hallucination.
|
|
23
|
+
"""
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def uq_guard(
|
|
28
|
+
uq_engine: UQEngine,
|
|
29
|
+
config: Optional[UQGuardConfig] = None,
|
|
30
|
+
fallback_action: Optional[Callable[..., Any]] = None,
|
|
31
|
+
) -> Callable[..., Callable[..., Any]]:
|
|
32
|
+
"""
|
|
33
|
+
A decorator that wraps LLM/RAG execution functions.
|
|
34
|
+
It intercepts the returned tensor features, calculates the variance using the UQEngine,
|
|
35
|
+
and blocks the response if the model is too uncertain.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
uq_engine (UQEngine): The instantiated uncertainty quantification engine.
|
|
39
|
+
config (UQGuardConfig, optional): Configuration containing the safety threshold.
|
|
40
|
+
fallback_action (Callable, optional): A function to execute if the threshold is breached.
|
|
41
|
+
"""
|
|
42
|
+
conf = config or UQGuardConfig()
|
|
43
|
+
|
|
44
|
+
def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
|
|
45
|
+
@wraps(func)
|
|
46
|
+
def wrapper(*args: Any, **kwargs: Any) -> Dict[str, Any]:
|
|
47
|
+
# Execute the underlying LLM or RAG pipeline
|
|
48
|
+
result = func(*args, **kwargs)
|
|
49
|
+
|
|
50
|
+
# The wrapped function must return a dictionary containing 'tensor_features'
|
|
51
|
+
if isinstance(result, dict) and "tensor_features" in result:
|
|
52
|
+
features = result["tensor_features"]
|
|
53
|
+
|
|
54
|
+
# Calculate the maximum epistemic variance
|
|
55
|
+
max_variance = uq_engine.estimate_variance(features)
|
|
56
|
+
|
|
57
|
+
logger.info(
|
|
58
|
+
f"Calculated Epistemic Variance: {max_variance:.4f} "
|
|
59
|
+
f"(Threshold: {conf.threshold:.4f})"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Check if the uncertainty violates the safety threshold
|
|
63
|
+
if max_variance > conf.threshold:
|
|
64
|
+
logger.warning("Uncertainty threshold exceeded! High risk of hallucination.")
|
|
65
|
+
|
|
66
|
+
if fallback_action:
|
|
67
|
+
logger.info("Executing defined fallback action.")
|
|
68
|
+
return fallback_action(*args, **kwargs, uncertainty=max_variance)
|
|
69
|
+
|
|
70
|
+
# If no fallback is defined, raise an exception to stop execution
|
|
71
|
+
raise UQGuardException(
|
|
72
|
+
f"Safety block triggered: Uncertainty score {max_variance:.4f} "
|
|
73
|
+
f"is higher than the allowed threshold of {conf.threshold:.4f}."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Inject the variance metrics into the final result for downstream analytics
|
|
77
|
+
result["uq_metrics"] = {"epistemic_variance": max_variance}
|
|
78
|
+
|
|
79
|
+
return result
|
|
80
|
+
return wrapper
|
|
81
|
+
return decorator
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Uncertainty Quantification (UQ) Engine.
|
|
3
|
+
Implements Epistemic Uncertainty estimation using Monte Carlo Dropout.
|
|
4
|
+
Optimized for high-throughput GPU inference using Tensor Batching.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import torch
|
|
8
|
+
import torch.nn as nn
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class UQEngine:
|
|
12
|
+
"""
|
|
13
|
+
Executes vectorized stochastic forward passes through a PyTorch model
|
|
14
|
+
to calculate epistemic variance.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, model: nn.Module, mc_samples: int = 30) -> None:
|
|
18
|
+
self.model = model
|
|
19
|
+
self.mc_samples = mc_samples
|
|
20
|
+
|
|
21
|
+
def _enable_dropout(self) -> None:
|
|
22
|
+
"""Forces dropout layers to remain active during evaluation mode."""
|
|
23
|
+
for module in self.model.modules():
|
|
24
|
+
if isinstance(module, nn.Dropout):
|
|
25
|
+
module.train()
|
|
26
|
+
|
|
27
|
+
def estimate_variance(self, input_tensor: torch.Tensor) -> float:
|
|
28
|
+
"""
|
|
29
|
+
Calculates the maximum epistemic variance using vectorized Monte Carlo samples.
|
|
30
|
+
"""
|
|
31
|
+
self._enable_dropout()
|
|
32
|
+
|
|
33
|
+
with torch.no_grad():
|
|
34
|
+
# ENTERPRISE BATCHING: Duplicate the tensor along the batch dimension.
|
|
35
|
+
# If input is (1, 768), repeated_tensor becomes (30, 768).
|
|
36
|
+
# This allows the GPU to process all Monte Carlo samples in parallel.
|
|
37
|
+
repeat_dims = [self.mc_samples] + [1] * (input_tensor.dim() - 1)
|
|
38
|
+
repeated_tensor = input_tensor.repeat(*repeat_dims)
|
|
39
|
+
|
|
40
|
+
# Single highly-parallel forward pass
|
|
41
|
+
predictions = self.model(repeated_tensor)
|
|
42
|
+
|
|
43
|
+
# Calculate statistical variance across the batch dimension (dim=0)
|
|
44
|
+
variances = torch.var(predictions, dim=0, unbiased=True)
|
|
45
|
+
|
|
46
|
+
return float(variances.max().item())
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["poetry-core>=1.0.0"]
|
|
3
|
+
build-backend = "poetry.core.masonry.api"
|
|
4
|
+
|
|
5
|
+
[tool.poetry]
|
|
6
|
+
name = "bayesian-llm-guard"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Epistemic Uncertainty Estimation & Guardrails for Agentic LLMs and RAG Systems"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
authors = ["AI Research Team <research@xaideep.com>"]
|
|
11
|
+
license = "Apache-2.0"
|
|
12
|
+
|
|
13
|
+
[tool.poetry.dependencies]
|
|
14
|
+
python = "^3.10"
|
|
15
|
+
torch = ">=2.0.0"
|
|
16
|
+
numpy = "^1.24.0"
|
|
17
|
+
pydantic = "^2.0.0"
|
|
18
|
+
pydantic-settings = "^2.0.0"
|
|
19
|
+
|
|
20
|
+
[tool.poetry.group.dev.dependencies]
|
|
21
|
+
pytest = "^7.3.0"
|
|
22
|
+
black = "^23.3.0"
|
|
23
|
+
isort = "^5.12.0"
|
|
24
|
+
|
|
25
|
+
[tool.semantic_release]
|
|
26
|
+
version_toml = ["pyproject.toml:tool.poetry.version"]
|
|
27
|
+
branch = "main"
|
|
28
|
+
build_command = "pip install poetry && poetry build"
|
|
29
|
+
commit_message = "chore(release): v{version} [skip ci]"
|
|
30
|
+
commit_author = "github-actions[bot] <actions@github.com>"
|