MrNoOne07's picture
Upload folder using huggingface_hub
a06c9f9 verified
Raw
History Blame Contribute Delete
3.6 kB
"""
Nimbus Bank Triage β€” LLM Client Wrappers
Centralized model configuration for Claude Haiku (fast tasks)
and Claude Sonnet (quality drafting), with retry logic.
"""
import os
from functools import lru_cache
from dotenv import load_dotenv
from langchain_anthropic import ChatAnthropic
from tenacity import retry, stop_after_attempt, wait_exponential
# Load env vars
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
load_dotenv(os.path.join(ROOT_DIR, ".env"))
# ── Model IDs ────────────────────────────────────────────────
FAST_MODEL = os.environ.get("FAST_MODEL", "claude-haiku-4-5-20251001")
# Default the drafter to the same known-working model unless overridden by env.
DRAFTER_MODEL = os.environ.get("DRAFTER_MODEL", "claude-haiku-4-5-20251001")
def _get_anthropic_api_key() -> str:
"""Return a non-empty Anthropic API key or raise a clear config error."""
api_key = os.environ.get("ANTHROPIC_API_KEY", "").strip()
if not api_key:
raise RuntimeError(
"ANTHROPIC_API_KEY is not configured. Add it to your environment or Hugging Face Space secrets, then restart the app."
)
return api_key
def anthropic_api_key_configured() -> bool:
"""Whether a non-empty Anthropic API key is currently configured."""
return bool(os.environ.get("ANTHROPIC_API_KEY", "").strip())
@lru_cache(maxsize=4)
def _build_llm(model: str, temperature: float, max_tokens: int, api_key: str) -> ChatAnthropic:
"""Build and cache Anthropic chat clients by model settings and API key."""
return ChatAnthropic(
model=model,
temperature=temperature,
max_tokens=max_tokens,
api_key=api_key,
)
@lru_cache(maxsize=1)
def get_fast_llm() -> ChatAnthropic:
"""
Claude Haiku β€” used for classification, compliance checks,
and injection detection. Low latency, low cost, deterministic.
"""
return _build_llm(
model=FAST_MODEL,
temperature=0.0,
max_tokens=1024,
api_key=_get_anthropic_api_key(),
)
@lru_cache(maxsize=1)
def get_drafter_llm() -> ChatAnthropic:
"""
Claude Sonnet β€” used only by the Response Drafter.
Higher quality for nuanced, empathetic customer responses.
"""
return _build_llm(
model=DRAFTER_MODEL,
temperature=0.5,
max_tokens=2048,
api_key=_get_anthropic_api_key(),
)
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=1, max=10),
reraise=True,
)
def invoke_with_retry(llm: ChatAnthropic, messages: list) -> str:
"""
Invoke an LLM with automatic retry on transient failures.
Exponential backoff: 1s, 2s, 4s. Max 3 attempts.
Returns the string content of the response.
"""
response = llm.invoke(messages)
return response.content
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=1, max=10),
reraise=True,
)
def invoke_structured_with_retry(
llm: ChatAnthropic,
messages: list,
schema: type,
) -> dict:
"""
Invoke an LLM with structured output (Pydantic schema) and retry.
Uses LangChain's with_structured_output for type-safe responses.
Returns a dict matching the schema.
"""
structured_llm = llm.with_structured_output(schema)
response = structured_llm.invoke(messages)
# Pydantic model -> dict
if hasattr(response, "model_dump"):
return response.model_dump()
return response