웹훅 & Slack 통합

웹훅 & Slack 통합 (Webhooks & Slack Integration)

Langfuse에서 프롬프트 버전이 생성, 업데이트 또는 삭제될 때 실시간 알림을 받기 위해 웹훅을 사용할 수 있어요. 이를 통해 API를 폴링하지 않고도 CI/CD 파이프라인을 트리거하거나, 프롬프트 카탈로그를 동기화하거나, 변경 사항을 감사할 수 있어요.

출처: 문서

본문

왜 웹훅을 쓰나요?

  • 프로덕션 모니터링: 프로덕션 프롬프트가 업데이트되면 알림 받기
  • 팀 협업: 프롬프트 변경에 대해 모두에게 알리기
  • 동기화: 프롬프트 카탈로그를 다른 시스템과 동기화

시작하기

Prompts로 이동해 Automations를 클릭하세요.

Create Automation을 클릭하세요.

감시할 이벤트를 선택하세요.

웹훅을 트리거할 프롬프트 버전 액션을 선택하세요:

  • Created: 새 버전이 추가됨.
  • Updated: 라벨이나 태그가 변경됨(두 개의 이벤트가 발생: 라벨/태그를 얻는 버전 하나와 잃는 버전 하나).
  • Deleted: 버전이 제거됨.

(선택) 특정 프롬프트에서만 트리거되도록 필터링.

Webhook CallSlack Message

요청 구성

  • URL: POST 요청을 받는 HTTPS 엔드포인트.
  • Headers: 기본 헤더는 다음과 같아요:
    • Content-Type: application/json
    • User-Agent: Langfuse/1.0
    • x-langfuse-signature: t=<timestamp>,v1=<signature> (아래 HMAC 서명 검증 참고)
  • 필요하면 커스텀 정적 헤더 추가.

페이로드 검사

엔드포인트가 다음과 같은 JSON 본문을 받아요:

webhook-payload.json

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "timestamp": "2024-07-10T10:30:00Z",
  "type": "prompt-version",
  "apiVersion": "v1",
  "action": "created",
  "prompt": {
    "id": "prompt_abc123",
    "name": "movie-critic",
    "version": 3,
    "projectId": "xyz789",
    "labels": ["production", "latest"],
    "prompt": "As a {{criticLevel}} movie critic, rate {{movie}} out of 10.",
    "type": "text",
    "config": { "key": "value" },
    "commitMessage": "Improved critic persona",
    "tags": ["entertainment"],
    "createdAt": "2024-07-10T10:30:00Z",
    "updatedAt": "2024-07-10T10:30:00Z"
  }
}

전달 확인

핸들러는 다음을 해야 해요:

  • 수신을 확인하기 위해 HTTP 2xx 상태를 반환해야 함.
  • 멱등적이어야 함 — Langfuse는 성공 응답을 받을 때까지 (지수 백오프로) 재시도할 수 있음.

진위 확인(권장)

각 요청은 x-langfuse-signature 헤더에 HMAC SHA-256 서명을 담고 있어요. 웹훅을 만들 때 시크릿(secret)을 얻으세요(나중에 재생성할 수 있어요).

Python SDKJS/TS SDK

import hmac
import hashlib
from typing import Optional

def verify_langfuse_signature(
    raw_body: str,
    signature_header: str,
    secret: str,
) -> bool:
    """
    Validate a Langfuse webhook/event signature.

    Parameters
    ----------
    raw_body : str
        The request body exactly as received (no decoding or reformatting).
    signature_header : str
        The value of the `x-langfuse-signature` header, e.g. "t=1720701136,v1=0123abcd...".
    secret : str
        Your Langfuse signing secret.

    Returns
    -------
    bool
        True if the signature is valid, otherwise False.
    """
    # Split "t=timestamp,v1=signature" into the two expected key/value chunks
    try:
        ts_pair, sig_pair = signature_header.split(",", 1)
    except ValueError:  # wrong format / missing comma
        return False

    # Extract values (everything after the first "=")
    if "=" not in ts_pair or "=" not in sig_pair:
        return False
    timestamp = ts_pair.split("=", 1)[1]
    received_sig_hex = sig_pair.split("=", 1)[1]

    # Recreate the message and compute the expected HMAC-SHA256 hex digest
    message = f"{timestamp}.{raw_body}".encode("utf-8")
    expected_sig_hex = hmac.new(
        secret.encode("utf-8"), message, hashlib.sha256
    ).hexdigest()

    # Use constant-time comparison on the *decoded* byte strings
    try:
        return hmac.compare_digest(
            bytes.fromhex(received_sig_hex), bytes.fromhex(expected_sig_hex)
        )
    except ValueError:  # received_sig_hex isn't valid hex
        return False
import crypto from "crypto";

export function verifyLangfuseSignature(
  rawBody: string,
  signatureHeader: string,
  secret: string
): boolean {
  const [tsPair, sigPair] = signatureHeader.split(",");
  if (!tsPair || !sigPair) return false;

  const timestamp = tsPair.split("=")[1];
  const receivedSig = sigPair.split("=")[1];
  const expectedSig = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`, "utf8")
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(receivedSig, "hex"),
    Buffer.from(expectedSig, "hex")
  );
}

Langfuse로 Slack 인증하기

  • Langfuse는 OAuth를 통해 Slack에 연결해요.
  • Slack에 대한 시크릿을 데이터베이스에 암호화해서 저장해요.

알림을 보낼 채널 선택

  • 알림을 보내고 싶은 채널을 선택할 수 있어요.
  • 드라이 런(dry run)을 실행해 메시지가 채널에 도착하는지 확인할 수 있어요.

Slack에서 메시지 보기

더 알아보기 (Learn more)