Skip to main content
The PAS2 library provides a Python API for detecting hallucinations programmatically. This allows you to integrate hallucination detection into your own applications, pipelines, and workflows.

Installation

Import the PAS2 class from the library:
from pas2 import PAS2

Initialization

Initialize the PAS2 detector with your API keys:
# Initialize with API keys directly
pas2 = PAS2(
    mistral_api_key="your-mistral-api-key",
    openai_api_key="your-openai-api-key"
)

API key priority

The library checks for API keys in the following order:
1

Constructor parameters

Keys passed directly to the PAS2 constructor
2

Hugging Face environment variables

HF_MISTRAL_API_KEY and HF_OPENAI_API_KEY
3

Standard environment variables

MISTRAL_API_KEY and OPENAI_API_KEY
self.mistral_api_key = mistral_api_key or os.environ.get("HF_MISTRAL_API_KEY") or os.environ.get("MISTRAL_API_KEY")
self.openai_api_key = openai_api_key or os.environ.get("HF_OPENAI_API_KEY") or os.environ.get("OPENAI_API_KEY")

if not self.mistral_api_key:
    raise ValueError("Mistral API key is required. Set it via HF_MISTRAL_API_KEY in Hugging Face Spaces secrets or pass it as a parameter.")

if not self.openai_api_key:
    raise ValueError("OpenAI API key is required. Set it via HF_OPENAI_API_KEY in Hugging Face Spaces secrets or pass it as a parameter.")

Progress callbacks

Optionally provide a callback function to track progress:
def progress_callback(stage, **kwargs):
    print(f"Stage: {stage}")
    if 'completed_responses' in kwargs:
        print(f"Progress: {kwargs['completed_responses']}/{kwargs['total_responses']}")

pas2 = PAS2(
    mistral_api_key="your-key",
    openai_api_key="your-key",
    progress_callback=progress_callback
)

Core methods

Detect hallucination

The main method for hallucination detection:
results = pas2.detect_hallucination(
    query="Who was the first person to land on the moon?",
    n_paraphrases=3
)
Parameters:
  • query (str): The question or prompt to analyze
  • n_paraphrases (int): Number of paraphrases to generate (default: 3)
Returns: A dictionary containing:
{
    "original_query": str,
    "original_response": str,
    "paraphrased_queries": List[str],
    "paraphrased_responses": List[str],
    "hallucination_detected": bool,
    "confidence_score": float,
    "conflicting_facts": List[Dict[str, Any]],
    "reasoning": str,
    "summary": str
}

Generate paraphrases

Generate semantic paraphrases of a query:
paraphrases = pas2.generate_paraphrases(
    query="What is the capital of France?",
    n_paraphrases=3
)
# Returns: [original_query, paraphrase_1, paraphrase_2, paraphrase_3]
Implementation details:
def generate_paraphrases(self, query: str, n_paraphrases: int = 3) -> List[str]:
    messages = [
        {
            "role": "system",
            "content": f"You are an expert at creating semantically equivalent paraphrases. Generate {n_paraphrases} different paraphrases of the given query that preserve the original meaning but vary in wording and structure. Return a JSON array of strings, each containing one paraphrase."
        },
        {
            "role": "user",
            "content": query
        }
    ]
    
    response = self.mistral_client.chat.complete(
        model=self.mistral_model,
        messages=messages,
        response_format={"type": "json_object"}
    )
Fallback mechanism: If paraphrase generation fails, the library provides automatic fallbacks:
fallback_paraphrases = [
    query,
    f"Could you tell me about {query.strip('?')}?",
    f"I'd like to know: {query}",
    f"Please provide information on {query.strip('?')}."
][:n_paraphrases+1]

Get responses

Get AI responses for multiple queries in parallel:
queries = [
    "What is the capital of France?",
    "Tell me about France's capital city.",
    "Which city is the capital of France?"
]

responses = pas2.get_responses(queries)
# Returns: List of response strings
Parallel execution: The library uses thread pooling for efficient parallel API calls:
def get_responses(self, queries: List[str]) -> List[str]:
    with ThreadPoolExecutor(max_workers=min(len(queries), 5)) as executor:
        future_to_index = {
            executor.submit(self._get_single_response, query, i): i 
            for i, query in enumerate(queries)
        }
        
        responses = [""] * len(queries)
        
        for future in concurrent.futures.as_completed(future_to_index):
            index = future_to_index[future]
            responses[index] = future.result()

Judge hallucination

Use the judge model to analyze responses for inconsistencies:
judgment = pas2.judge_hallucination(
    original_query="Who invented the telephone?",
    original_response="Alexander Graham Bell invented the telephone in 1876.",
    paraphrased_queries=[
        "Tell me about the invention of the telephone.",
        "Who created the first telephone?"
    ],
    paraphrased_responses=[
        "The telephone was invented by Alexander Graham Bell in 1876.",
        "Thomas Edison invented the telephone in 1877."
    ]
)
Returns: A HallucinationJudgment object with:
class HallucinationJudgment(BaseModel):
    hallucination_detected: bool
    confidence_score: float  # 0-1
    conflicting_facts: List[Dict[str, Any]]
    reasoning: str
    summary: str
Judge implementation:
system_prompt = """
You are a judge evaluating whether an AI is hallucinating across different responses to semantically equivalent questions.
Analyze all responses carefully to identify any factual inconsistencies or contradictions.
Focus on factual discrepancies, not stylistic differences.
A hallucination is when the AI states different facts in response to questions that are asking for the same information.

Your response should be a JSON with the following fields:
- hallucination_detected: boolean indicating whether hallucinations were found
- confidence_score: number between 0 and 1 representing your confidence in the judgment
- conflicting_facts: an array of objects describing any conflicting information found
- reasoning: detailed explanation for your judgment
- summary: a concise summary of your analysis
"""

response = self.openai_client.chat.completions.create(
    model=self.openai_model,
    messages=[
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": f"Evaluate these responses for hallucinations:\n\n{context}"}
    ],
    response_format={"type": "json_object"}
)

Model configuration

The library uses specific models for different tasks:
self.mistral_model = "mistral-large-latest"  # For paraphrases and responses
self.openai_model = "o3-mini"  # For judgment

Complete example

Here’s a full example demonstrating the library workflow:
from pas2 import PAS2

# Initialize
pas2 = PAS2(
    mistral_api_key="your-mistral-key",
    openai_api_key="your-openai-key"
)

# Detect hallucinations
results = pas2.detect_hallucination(
    query="Who was the first person to land on the moon?",
    n_paraphrases=3
)

# Check results
if results["hallucination_detected"]:
    print(f"Hallucination detected with {results['confidence_score']:.2f} confidence")
    print(f"Summary: {results['summary']}")
    print(f"Conflicting facts: {results['conflicting_facts']}")
else:
    print("No hallucination detected")

Error handling

The library includes comprehensive error handling:
try:
    results = pas2.detect_hallucination(query)
except ValueError as e:
    # API key issues
    print(f"Configuration error: {e}")
except Exception as e:
    # Other errors (API failures, network issues, etc.)
    print(f"Error during detection: {e}")
Fallback judgments: If the judge model fails, a safe fallback is returned:
return HallucinationJudgment(
    hallucination_detected=False,
    confidence_score=0.0,
    conflicting_facts=[],
    reasoning="Failed to obtain judgment from the model.",
    summary="Analysis failed due to API error."
)

Logging

Enable detailed logging to debug issues:
import logging

logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s [%(levelname)s] %(message)s'
)

pas2 = PAS2(
    mistral_api_key="your-key",
    openai_api_key="your-key"
)
Log messages include:
  • Paraphrase generation timing and results
  • Response retrieval progress
  • Judgment execution details
  • Error stack traces

Build docs developers (and LLMs) love