vignette/apps/api/app/services/llm_audit.py
2026-07-15 21:31:30 +09:00

47 lines
1.4 KiB
Python

"""LLM 호출 계량과 감사 훅의 공통 실행 경로."""
from __future__ import annotations
import time
from collections.abc import Awaitable, Callable
from typing import Any
from ..engine_client import EngineClient, GenerateRequest, GenerateResponse
LlmAuditHook = Callable[[dict[str, Any]], Awaitable[None]]
async def record_llm_audit(
audit_hook: LlmAuditHook | None,
**payload: Any,
) -> None:
"""감사 저장소 장애가 사용자 응답을 막지 않도록 훅 실패를 격리한다."""
if audit_hook is None:
return
try:
await audit_hook(payload)
except Exception:
return
async def generate_with_audit(
engine: EngineClient,
request: GenerateRequest,
audit_hook: LlmAuditHook | None,
) -> GenerateResponse:
"""비스트리밍 LLM 호출의 지연·토큰·비용 기록을 한 계약으로 고정한다."""
started = time.perf_counter()
response = await engine.generate(request)
latency_ms = int((time.perf_counter() - started) * 1000)
await record_llm_audit(
audit_hook,
session_id=request.session_id,
provider=response.provider,
model=response.model,
tokens_in=response.tokens_in,
tokens_out=response.tokens_out,
cost_usd=response.cost_usd,
inference_geo=response.inference_geo,
latency_ms=latency_ms,
)
return response