SAP-ERP-AI-Agent / src /data /_llm_client.py
daisysooyeon's picture
deploy: SAP ERP AI Agent (HF Spaces docker)
50efdc6
Raw
History Blame Contribute Delete
2.91 kB
"""
src/data/_llm_client.py
Shared OpenRouter LLM client and retry utility for all data generators.
"""
from __future__ import annotations
import logging
import os
import time
from dotenv import load_dotenv
load_dotenv()
from langchain_openai import ChatOpenAI
from src.config import get_config
logger = logging.getLogger(__name__)
# ---------------------------------------------------------------------------
# LLM 팩토리
# ---------------------------------------------------------------------------
def build_llm(
model_name: str | None = None,
temperature: float | None = None,
) -> ChatOpenAI:
"""
OpenRouter 기반 ChatOpenAI 인스턴스를 반환합니다.
Parameters
----------
model_name : Model name. If None, uses models.data_gen.name from configs.yaml.
temperature : Sampling temperature. If None, uses models.data_gen.temperature from configs.yaml.
"""
cfg = get_config()
api_key = os.getenv("OPENROUTER_API_KEY", "")
if not api_key:
raise EnvironmentError(
"OPENROUTER_API_KEY is not set. Check your .env file."
)
name = model_name or cfg.models.data_gen.name
temp = temperature if temperature is not None else cfg.models.data_gen.temperature
return ChatOpenAI(
model=name,
temperature=temp,
openai_api_key=api_key,
openai_api_base=cfg.openrouter.base_url,
default_headers={
"HTTP-Referer": "https://github.com/daisysooyeon/SAP-ERP-AI-Agent",
"X-Title": "SAP ERP AI Agent - Dataset Generator",
},
)
# ---------------------------------------------------------------------------
# Retry 래퍼
# ---------------------------------------------------------------------------
def invoke_with_retry(
chain,
inputs: dict,
*,
max_retries: int = 5,
initial_wait: float = 5.0,
label: str = "",
) -> str | None:
"""
Calls chain.invoke(inputs) with exponential backoff on 429 rate-limit errors.
Returns
-------
LLM response content string, or None if all retries fail.
"""
wait = initial_wait
for attempt in range(1, max_retries + 1):
try:
response = chain.invoke(inputs)
content = response.content if hasattr(response, "content") else str(response)
return content
except Exception as exc:
err_str = str(exc)
if "429" in err_str and attempt < max_retries:
logger.warning(
"[%s] Rate limited (429). Attempt %d/%d — waiting %.0fs …",
label or "llm", attempt, max_retries, wait,
)
time.sleep(wait)
wait = min(wait * 2, 120)
continue
logger.error("[%s] LLM 호출 실패 (attempt %d): %s", label or "llm", attempt, exc)
return None
return None