웹훅

웹훅

웹훅은 MLOps 관련 기능의 기반이에요. 특정 저장소 또는 특정 사용자/조직 집합에 속한 모든 저장소에 새 변경사항이 생겼는지 들을 수 있게 해줘요(자신의 저장소뿐 아니라 어떤 저장소든).

출처: 문서

본문

웹훅으로 모델 자동 변환, 커뮤니티 봇 구축, 모델·데이터셋·Spaces·버킷의 CI/CD 구축(그 외 많은 것) 등을 할 수 있어요. 웹훅은 저장소 이벤트에 응답해 Job을 트리거해 컴퓨트 작업을 자동화할 수도 있어요.

웹훅 문서는 아래와 같아요. 웹훅의 몇 가지 가능한 사용 사례를 보여주는 가이드도 둘러볼 수 있어요:

웹훅 만들기

웹훅 설정에서 새 웹훅을 만들고 기존 것을 편집할 수 있어요.

웹훅은 저장소 업데이트, 풀 리퀘스트, 토론, 새 댓글을 감시할 수 있어요. 웹훅에 반응하는 Space를 만드는 것도 가능해요!

웹훅 페이로드

웹훅을 등록하면 지정된 대상 URL에 HTTP POST 호출로 새 이벤트를 알림받아요. 페이로드는 JSON으로 인코딩돼요.

웹훅 설정 페이지의 activity 탭에서 전송된 페이로드 이력을 볼 수 있고, 디버깅을 쉽게 하기 위해 과거 웹훅을 재생(replay)할 수도 있어요.

예를 들어, 풀 리퀘스트가 열리면 전체 페이로드는 다음과 같아요:

{
  "event": {
    "action": "create",
    "scope": "discussion"
  },
  "repo": {
    "type": "model",
    "name": "openai-community/gpt2",
    "id": "621ffdc036468d709f17434d",
    "private": false,
    "url": {
      "web": "https://huggingface.co/openai-community/gpt2",
      "api": "https://huggingface.co/api/models/openai-community/gpt2"
    },
    "owner": {
      "id": "628b753283ef59b5be89e937"
    }
  },
  "discussion": {
    "id": "6399f58518721fdd27fc9ca9",
    "title": "Update co2 emissions",
    "url": {
      "web": "https://huggingface.co/openai-community/gpt2/discussions/19",
      "api": "https://huggingface.co/api/models/openai-community/gpt2/discussions/19"
    },
    "status": "open",
    "author": {
      "id": "61d2f90c3c2083e1c08af22d"
    },
    "num": 19,
    "isPullRequest": true,
    "changes": {
      "base": "refs/heads/main"
    }
  },
  "comment": {
    "id": "6399f58518721fdd27fc9caa",
    "author": {
      "id": "61d2f90c3c2083e1c08af22d"
    },
    "content": "Add co2 emissions information to the model card",
    "hidden": false,
    // Note: when `hidden` is `true`, `content` will be undefined
    "url": {
      "web": "https://huggingface.co/openai-community/gpt2/discussions/19#6399f58518721fdd27fc9caa"
    }
  },
  "webhook": {
    "id": "6390e855e30d9209411de93b",
    "version": 3
  }
}

Event

최상위 속성 event는 항상 지정되며 이벤트의 성격을 결정하는 데 쓰여요.

event.actionevent.scope 두 개의 하위 속성이 있어요.

event.scope는 다음 값 중 하나예요:

  • "repo" - 저장소의 전역 이벤트. 연관 action의 가능한 값: "create", "delete", "update", "move".
  • "repo.content" - 저장소 콘텐츠의 이벤트(새 커밋, 태그 등). 새로 생성된 reference/commit 때문에 새 풀 리퀘스트에서도 트리거돼요. 버킷의 경우 Git ref가 아닌 파일 추가·삭제에서 트리거돼요. 연관 action은 항상 "update".
  • "repo.config" - 구성 이벤트: Space 시크릿 업데이트, 설정 업데이트, DOI 업데이트, 비활성화 여부 등. 연관 action은 항상 "update".
  • "discussion" - 토론이나 풀 리퀘스트 생성, 제목·상태 업데이트, 머지. 연관 action의 가능한 값: "create", "delete", "update".
  • "discussion.comment" - 댓글 생성, 업데이트, 숨김. 연관 action의 가능한 값: "create", "update".

앞으로 더 많은 scope가 추가될 수 있어요. 알 수 없는 이벤트를 처리하려면 웹훅 핸들러가 좁은 scope의 모든 action을 더 넓은 scope의 "update" action으로 간주하면 돼요.

예를 들어, 미래에 "repo.config.dois" scope가 추가되면 그 scope의 모든 이벤트를 웹훅 핸들러가 "repo.config" scope의 "update" action으로 간주할 수 있어요.

Repo

현재 웹훅 버전에서 최상위 속성 repo는 항상 지정돼요. 이벤트가 항상 저장소와 연관될 수 있기 때문이에요. 예:

"repo": {
	"type": "model",
	"name": "some-user/some-repo",
	"id": "6366c000a2abcdf2fd69a080",
	"private": false,
	"url": {
		"web": "https://huggingface.co/some-user/some-repo",
		"api": "https://huggingface.co/api/models/some-user/some-repo"
	},
	"headSha": "c379e821c9c95d613899e8c4343e4bfee2b0c600",
	"owner": {
		"id": "61d2000c3c2083e1c08af22d"
	}
}

repo.headSha는 저장소 main 브랜치의 최신 커밋 sha예요. event.scope"repo"로 시작할 때만 전송되며, 토론·댓글 같은 커뮤니티 이벤트나 Git 이력이 없는 버킷에서는 전송되지 않아요.

코드 변경

코드 변경 시 저장소 이벤트에서 최상위 속성 updatedRefs가 지정돼요. 업데이트된 reference의 배열이에요. 예:

"updatedRefs": [
  {
    "ref": "refs/heads/main",
    "oldSha": "ce9a4674fa833a68d5a73ec355f0ea95eedd60b7",
    "newSha": "575db8b7a51b6f85eb06eee540738584589f131c"
  },
  {
    "ref": "refs/tags/test",
    "oldSha": null,
    "newSha": "575db8b7a51b6f85eb06eee540738584589f131c"
  }
]

새로 생성된 reference는 oldShanull이에요. 삭제된 reference는 newShanull이에요.

특정 풀 리퀘스트의 새 커밋, 새 태그, 새 브랜치에 반응할 수 있어요.

버킷

버킷은 Git 저장소가 아니에요. 커밋·브랜치·태그가 없어요. 수명주기 이벤트(create, delete, move, 공개 여부 같은 구성 업데이트)는 다른 저장소 유형과 같은 "repo" / "repo.config" scope를 사용해요.

파일 변경은 "repo.content"를 사용하지만, 페이로드는 updatedRefs 대신 updatedFiles 속성을 가져요. 기존 파일을 덮어쓰는 것은 "add"로 보고돼요. 파일 하나가 추가되고 다른 파일이 삭제된 후의 예:

{
  "event": {
    "action": "update",
    "scope": "repo.content"
  },
  "repo": {
    "type": "bucket",
    "name": "some-user/some-bucket",
    "id": "6366c000a2abcdf2fd69a080",
    "private": false,
    "url": {
      "web": "https://huggingface.co/buckets/some-user/some-bucket",
      "api": "https://huggingface.co/api/buckets/some-user/some-bucket"
    },
    "owner": {
      "id": "61d2000c3c2083e1c08af22d"
    }
  },
  "updatedFiles": [
    {
      "path": "data/train.txt",
      "action": "add",
      "xetHash": "55faef2f2f80cd1a087c35b729f228960739441d38073cd5aa4320751e137166",
      "size": 20
    },
    {
      "path": "data/old.txt",
      "action": "delete"
    },
    ...
  ],
  "updatedFilesTruncated": true,
  "webhook": {
    "id": "6390e855e30d9209411de93b",
    "version": 3
  }
}

10,000개를 넘으면 목록이 잘리고 updatedFilesTruncatedtrue로 설정돼요. 이 경우 버킷을 나열해 전체 그림을 확인하세요.

버킷에는 토론이나 풀 리퀘스트가 없으므로 "discussion""discussion.comment" 이벤트를 받지 않아요.

클라이언트가 여러분 서버로의 콜백 대신 버킷의 파일 변경을 직접 추적하려면 버킷도 실시간 팔로우 스트림을 제공해요.

구성 변경

event.scope"repo.config"일 때 updatedConfig 속성이 지정돼요. 업데이트된 구성을 담은 객체예요. 예:

"updatedConfig": {
  "private": false
}

업데이트된 구성 키가 웹훅에서 지원되지 않으면 객체는 비어 있어요:

"updatedConfig": {}

현재는 private만 지원해요. 더 많은 구성 키가 여기 있었으면 좋겠다면 [email protected] 알려 주세요.

토론과 풀 리퀘스트

최상위 속성 discussion은 커뮤니티 이벤트(토론과 풀 리퀘스트)에서 지정돼요. discussion.isPullRequest는 토론이 풀 리퀘스트이기도 한지(이 Hub에서 PR은 특수한 토론 유형) 나타내는 불리언이에요. 예:

"discussion": {
	"id": "639885d811ae2bad2b7ba461",
	"title": "Hello!",
	"url": {
		"web": "https://huggingface.co/some-user/some-repo/discussions/3",
		"api": "https://huggingface.co/api/models/some-user/some-repo/discussions/3"
	},
	"status": "open",
	"author": {
		"id": "61d2000c3c2083e1c08af22d"
	},
	"isPullRequest": true,
	"changes": {
		"base": "refs/heads/main"
	}
	"num": 3
}

댓글

댓글이 생성(토론 생성 포함)되거나 업데이트될 때 최상위 속성 comment가 지정돼요. 예:

"comment": {
	"id": "6398872887bfcfb93a306f18",
	"author": {
		"id": "61d2000c3c2083e1c08af22d"
	},
	"content": "This adds an env key",
	"hidden": false,
	"url": {
		"web": "https://huggingface.co/some-user/some-repo/discussions/4#6398872887bfcfb93a306f18"
	}
}

웹훅 시크릿

웹훅 시크릿을 설정하면 여러분의 웹훅 핸들러 URL로 전송되는 페이로드가 실제로 Hugging Face에서 온 것인지 확인하는 데 유용해요.

웹훅 시크릿을 설정하면 모든 요청에 X-Webhook-Secret HTTP 헤더로 함께 전송돼요. ASCII 문자만 지원돼요.

[!TIP] 핸들러 URL에 시크릿을 직접 추가할 수도 있어요. 예를 들어 쿼리 파라미터로: https://example.com/webhook?secret=XXX.

웹훅 핸들러에서 요청 HTTP 헤더에 접근하기 어려울 때 유용해요.

전송과 재시도

웹훅 페이로드는 Hub에서 이벤트가 발생한 직후 비동기적으로 전송돼요. 순서는 보장되지 않아요: 여러 이벤트가 가까이 발생하면 순서가 뒤섞여 도착할 수 있어요.

핸들러는 전송을 2xx 상태 코드로 확인(acknowledge)해야 해요. 다른 상태 코드는 연결 오류와 마찬가지로 실패한 전송으로 간주되고, 백오프 방식으로 재시도돼요. 처리 속도가 느리다면 즉시 2xx로 답하고 작업을 비동기로 수행해 전송이 실패로 간주되지 않게 하세요.

각 전송에는 고유한 Webhook-Id HTTP 헤더가 있어요. 실패한 전송의 재시도는 같은 ID를 재사용하므로, 이를 멱등성 키로 취급해 각 이벤트를 한 번만 처리할 수 있어요.

웹훅으로의 전송이 계속 실패하면 웹훅이 자동으로 일시 중단되고 소유자에게 이메일로 통지돼요. 웹훅 설정에서 문제를 해결하고 다시 활성화할 수 있어요.

속도 제한

각 웹훅은 24시간당 1,000회 트리거로 제한돼요. 웹훅 설정 페이지의 "Activity" 탭에서 사용량을 볼 수 있어요.

트리거 수를 늘려야 한다면 PRO, Team 또는 Enterprise로 업그레이드하고 [email protected] 연락해 주세요.

웹훅 개발

HTTPS 엔드포인트/URL이 없다면 웹훅 테스트용 공개 도구를 시도할 수 있어요. 이 도구들은 전송된 요청을 모두 잡아내고 200 OK 상태 코드를 반환해요. Beeceptor는 임시 HTTP 엔드포인트를 만들어 들어오는 페이로드를 검토하게 해주는 도구예요. Webhook.site도 비슷한 도구예요.

추가로, 개발 중 로컬 머신에서 실행되는 코드로 실제 웹훅 페이로드를 라우팅할 수 있어요. 더 빠른 통합을 위해 테스트·디버깅하기 좋은 방법이에요. localhost 포트를 인터넷에 노출하면 되는데, ngrok 또는 localtunnel을 사용할 수 있어요.

웹훅 디버깅

웹훅에 대해 최근 생성된 이벤트를 쉽게 찾을 수 있어요. 웹훅의 activity 탭을 열면 최근 이벤트 목록이 보여요.

여기서 생성된 이벤트의 HTTP 상태 코드와 페이로드를 검토할 수 있어요. 또한 Replay 버튼을 클릭해 이벤트를 다시 재생할 수도 있어요!

참고: 웹훅의 대상 URL이나 시크릿을 변경하면 이벤트 재생 시 업데이트된 URL로 페이로드가 전송돼요.

참고: 재생된 이벤트는 원래 전송과 같은 Webhook-Id로 전송돼요.

FAQ

조직 대 사용자 계정에 웹훅을 정의할 수 있나요?

아니요, 현재는 지원되지 않아요.

HF의 모든 이벤트(또는 모델 전체 같은 전체 저장소 유형)를 어떻게 구독하나요?

현재는 end user에게 노출되지 않지만, [email protected] 이메일을 보내면 설정해 드릴 수 있어요.

더 알아보기 (Learn more)