customer-support-agent / src /generation /response_generator.py
pro580's picture
Fix rate limiter to use X-Forwarded-For header behind HF proxy
e323466
Raw
History Blame Contribute Delete
4.63 kB
"""LLM response generation logic supporting Anthropic API and HuggingFace fallback."""
import os
from typing import Optional
from dotenv import load_dotenv
from loguru import logger
from src.generation.prompt_templates import get_template, format_user_prompt
load_dotenv()
class ResponseGenerator:
"""Generates customer support responses using an LLM backend.
Supports two backends controlled by the 'provider' config key:
- 'anthropic': Uses the Anthropic Claude API (preferred).
- 'huggingface': Uses a local HuggingFace pipeline as fallback.
Args:
cfg: Full config dict loaded from config.yaml.
"""
def __init__(self, cfg: dict) -> None:
gc = cfg["generation"]
self.provider: str = gc.get("provider", "anthropic")
self.max_tokens: int = gc.get("max_tokens", 300)
self.temperature: float = gc.get("temperature", 0.3)
self.top_p: float = gc.get("top_p", 0.9)
if self.provider == "anthropic":
self._init_anthropic(gc)
else:
self._init_huggingface(gc)
def _init_anthropic(self, gc: dict) -> None:
try:
import anthropic # type: ignore
api_key = os.environ.get("ANTHROPIC_API_KEY")
if not api_key:
raise EnvironmentError(
"ANTHROPIC_API_KEY environment variable is not set. "
"Create a .env file with your key or set it in the environment."
)
self.client = anthropic.Anthropic(api_key=api_key)
self.model_name: str = gc.get("model", "claude-haiku-4-5-20251001")
logger.info(f"Anthropic provider initialised with model '{self.model_name}'.")
except ImportError as e:
raise ImportError(
"anthropic package not installed. Run: pip install anthropic"
) from e
def _init_huggingface(self, gc: dict) -> None:
try:
from transformers import pipeline as hf_pipeline # type: ignore
import torch
model_name = gc.get("hf_model", "mistralai/Mistral-7B-Instruct-v0.2")
device = 0 if torch.cuda.is_available() else -1
logger.info(f"Loading HuggingFace model '{model_name}' (device={device})…")
self._hf_pipe = hf_pipeline(
"text-generation",
model=model_name,
device=device,
torch_dtype="auto",
)
self.model_name = model_name
logger.info("HuggingFace pipeline ready.")
except ImportError as e:
raise ImportError(
"transformers package not installed. Run: pip install transformers"
) from e
def generate(self, query: str, intent: str) -> tuple:
"""Generate a support response and return (response_text, context_used)."""
template = get_template(intent)
system_msg = template["system"]
user_msg = format_user_prompt(intent, query)
context = system_msg + "\n\n" + user_msg
if self.provider == "anthropic":
response = self._generate_anthropic(system_msg, user_msg)
else:
response = self._generate_huggingface(system_msg, user_msg)
return response, context
def _generate_anthropic(self, system_msg: str, user_msg: str) -> str:
"""Call Anthropic Messages API and return the response text."""
try:
message = self.client.messages.create(
model=self.model_name,
max_tokens=self.max_tokens,
temperature=self.temperature,
system=system_msg,
messages=[{"role": "user", "content": user_msg}],
)
return message.content[0].text.strip()
except Exception as e:
logger.error(f"Anthropic API call failed: {e}")
raise
def _generate_huggingface(self, system_msg: str, user_msg: str) -> str:
"""Call HuggingFace pipeline with instruction format and return the response text."""
prompt = (
f"<s>[INST] {system_msg}\n\n{user_msg} [/INST]"
)
try:
outputs = self._hf_pipe(
prompt,
max_new_tokens=self.max_tokens,
temperature=self.temperature,
top_p=self.top_p,
do_sample=True,
return_full_text=False,
)
return outputs[0]["generated_text"].strip()
except Exception as e:
logger.error(f"HuggingFace generation failed: {e}")
raise