TigerGraph-Hack / services /metrics_service.py
Meshyboi's picture
Update services/metrics_service.py
77bbb4b verified
Raw
History Blame Contribute Delete
5.18 kB
import time
import logging
import os
import evaluate
from huggingface_hub import InferenceClient
logger = logging.getLogger(__name__)
# YOUR EXACT PROMPT
JUDGE_PROMPT = """Grade the system's answer.
Question: {q}
Correct answer: {correct}
System answer: {answer}
Reply with only PASS or FAIL.
PASS = the system answer correctly addresses the question with no major errors.
FAIL = the answer is wrong, missing, or contradicts the correct answer."""
class MetricsService:
def __init__(self, model_name="gemini-1.5-flash"):
self.model_name = model_name.lower()
self.hf_token = os.environ.get("HF_TOKEN")
# Setup as per your implementation
self.client = InferenceClient(
model="meta-llama/Llama-3.1-8B-Instruct",
token=self.hf_token
)
self.bertscore = evaluate.load("bertscore")
self.tiers = {
"flash": {"input": 0.075, "output": 0.30},
"pro": {"input": 3.50, "output": 10.50},
"groq_large": {"input": 0.59, "output": 0.79},
"groq_small": {"input": 0.05, "output": 0.08},
}
def warmup(self):
"""Proactively load BERTScore and check Judge connectivity"""
logger.info("PRE-LOADING METRICS MODELS (BERTScore)...")
try:
# Dummy compute to force model download/load into memory/MPS
self.bertscore.compute(
predictions=["warmup"],
references=["warmup"],
lang="en"
)
logger.info("METRICS MODELS LOADED SUCCESSFULLY.")
except Exception as e:
logger.warning(f"Metrics warmup failed: {e}")
def calculate_cost(self, input_tokens, output_tokens, model_override=None):
m_name = (model_override or self.model_name).lower()
if "70b" in m_name or "versatile" in m_name:
prices = self.tiers["groq_large"]
elif "8b" in m_name or "instant" in m_name:
prices = self.tiers["groq_small"]
elif "pro" in m_name:
prices = self.tiers["pro"]
else:
prices = self.tiers["flash"]
return ((input_tokens / 1_000_000) * prices["input"]) + ((output_tokens / 1_000_000) * prices["output"])
def process_metrics(self, client, query, answer, context, usage_metadata, start_time, abstracts_list=None, ground_truth=None, model_name=None):
"""EXACT implementation with NO truncation."""
# 1. Prepare "Correct Answer"
# If ground_truth is provided (from benchmark), use it. Otherwise fallback to abstracts/context.
final_ground_truth = ground_truth or ("\n\n".join(abstracts_list) if abstracts_list else context)
# 2. LLM-as-a-Judge β€” strip citation markers and disclaimer sentences before judging
import re as _re
clean_for_judge = _re.sub(r'\[Paper \d+\]', '', answer).strip()
# Strip sentences where the model hedges about missing context β€” these cause false judge FAILs
_DISCLAIMER = _re.compile(
r'[^.!?]*\b(not explicitly (mentioned|stated|provided)|'
r'not (mentioned|provided|specified) in (the )?(provided |given )?context|'
r'is not clear from|cannot be (determined|found) from|'
r'no (explicit |direct )?mention)\b[^.!?]*[.!?]?',
_re.IGNORECASE
)
clean_for_judge = _DISCLAIMER.sub('', clean_for_judge).strip()
prompt = JUDGE_PROMPT.format(
q=query,
correct=final_ground_truth,
answer=clean_for_judge
)
try:
verdict = self.client.chat_completion(
[{"role": "user", "content": prompt}],
max_tokens=20,
temperature=0.0
)
judge_pass = "PASS" in verdict.choices[0].message.content.upper()
except Exception as e:
logger.error(f"Judge error: {e}")
judge_pass = False
# 3. BERTScore β€” reuse the already-cleaned answer from above
clean_answer = clean_for_judge
try:
bert_results = self.bertscore.compute(
predictions=[clean_answer],
references=[final_ground_truth],
lang="en",
rescale_with_baseline=True
)
f1_score = float(bert_results["f1"][0])
except Exception as e:
logger.error(f"BERTScore error: {e}")
f1_score = 0.0
# 4. Latency and Cost
end_time = time.time()
input_tokens = usage_metadata.prompt_token_count if usage_metadata else 0
output_tokens = usage_metadata.candidates_token_count if usage_metadata else 0
return {
"tokens": {"input": input_tokens, "output": output_tokens, "total": input_tokens + output_tokens},
"latency_seconds": round(end_time - start_time, 2),
"cost_usd": float(self.calculate_cost(input_tokens, output_tokens, model_override=model_name)),
"accuracy": {
"llm_judge": "PASS" if judge_pass else "FAIL",
"bert_score": f1_score
}
}