Skip to main content
모든 LLM 응답에는 해당 요청이 소비한 토큰을 보고하는 usage 객체가 포함됩니다. Sunra는 호출한 endpoint의 시맨틱으로 사용량을 보고합니다. Messages 응답은 Anthropic 계약을 따르고, Chat Completions 또는 Responses 응답은 OpenAI 계약을 따릅니다. 이 형태들이 서로 다른 것은 의도된 것입니다. Chat Completions 응답을 읽는 OpenAI SDK는 OpenAI 시맨틱을 가정하고, Messages 응답을 읽는 Anthropic SDK는 Anthropic 시맨틱을 가정합니다. 각 endpoint는 하나의 공통된 형태로 강제되는 대신 자신의 명세가 약속하는 바를 지킵니다.
/v1/messages/v1/responses는 모두 input_tokens라는 필드 이름을 사용하지만, 두 곳에서 의미가 같지 않습니다. Messages에서는 신규 입력만을 뜻합니다. Responses에서는 캐시된 토큰을 포함한 프롬프트 전체를 뜻합니다.

Messages

/v1/messages에서는 세 가지 입력 버킷이 상호 배타적입니다. 어떤 토큰도 두 번 계산되지 않으므로, 프롬프트 총합은 이들의 합입니다:
integer
신규 입력 토큰만 해당합니다. 두 캐시 버킷은 모두 제외합니다.
integer
이 요청이 캐시에 기록한 토큰.
integer
이 요청에 캐시에서 제공된 토큰.
integer
모델이 생성한 토큰.
integer
세 가지 입력 버킷과 output_tokens의 합. 비스트리밍 응답에 존재합니다.
존재하지 않거나 null인 버킷은 0으로 계산됩니다.

실제 예시

동일한 19,000 토큰 프리픽스를 claude-opus-4-8에 두 번 전송한 경우입니다. 첫 번째 요청은 캐시를 기록하고, 두 번째 요청은 캐시를 읽습니다.
콜드 요청(캐시 쓰기)
웜 요청(캐시 읽기)
input_tokens는 두 요청 모두에서 8로 유지되는데, 매번 실제로 새로 추가된 입력이 8이기 때문입니다. 19,349 토큰 프리픽스는 생성 버킷에서 읽기 버킷으로 이동합니다. 두 요청 모두 합계는 동일한 19,359 토큰이지만 비용은 같지 않습니다. 캐시 쓰기와 캐시 읽기는 각각 자체 토큰당 요율로 과금되며, 그래서 별도의 버킷으로 보고됩니다.

Chat Completions와 Responses

이 endpoint들은 OpenAI 시맨틱을 변경 없이 그대로 보고합니다. 프롬프트 카운트는 프롬프트 전체이며, 캐시된 토큰은 그 부분집합으로 별도로 보고됩니다.
/v1/chat/completions
prompt_tokenscached_tokens를 더하면 이중 계산이 됩니다. 이 endpoint들에서 신규 입력을 구하려면 빼야 합니다:
같은 규칙이 /v1/responsesinput_tokensinput_tokens_details.cached_tokens에도 적용됩니다.

sunra_usage_semantics 마커

Sunra가 정규화한 응답에는 usage 안에 마커가 포함됩니다:
이 마커는 개별 이벤트나 필드가 아니라 응답에 대한 약속입니다. 비스트리밍 응답에서는 루트 usage 객체에 나타나고, 스트리밍 응답에서는 입력 버킷을 전달하는 이벤트인 message_start에 나타납니다. 이 값으로 존재할 때 이 페이지의 보장이 성립합니다. 세 가지 입력 버킷은 상호 배타적이며, total_tokens는 — 존재하는 경우 — 이들의 합에 output_tokens를 더한 값입니다. 마커의 부재 또한 하나의 약속입니다. Sunra는 자신이 측정한 응답 형태만 정규화합니다. 그 외의 것은 업스트림 제공자로부터 손대지 않은 채 전달되며 마커가 붙지 않습니다. 마커가 없는 응답은 그 버킷을 Sunra가 보증하지 않는 응답입니다. 응답이 어떤 규약을 따르는지 숫자를 들여다보며 추측하지 말고, 마커를 검증하세요. 형태를 짐작하는 방식(shape sniffing)이야말로 이 필드가 대체하려는 대상입니다. “버킷의 합이 프롬프트 카운트보다 크므로 버킷은 배타적일 것이다” 같은 휴리스틱은 둘 이상의 규약에서 성립하며, 언젠가는 응답을 잘못 읽게 됩니다.
마커를 포함하는 것은 /v1/messages 응답뿐입니다. Chat Completions와 Responses는 OpenAI 명세를 따르며 마커가 붙지 않습니다. 이 값에는 버전이 있습니다. 이 필드들의 의미에 대한 파괴적 변경은 새로운 값으로 배포되므로, anthropic.exclusive.v1에 대한 동등성 검사가 조용히 다른 계약을 읽기 시작하는 일은 없습니다.

Streaming

스트리밍 Messages 요청에서는 사용량이 두 개의 이벤트에 걸쳐 도착합니다. message_start는 입력 측과 마커를 전달합니다. 마지막 message_deltaoutput_tokens만 전달합니다. 마커 검증은 message_start에서 수행하고, 그런 다음 두 이벤트를 통상적인 방식으로 병합 — 나중 이벤트의 usage를 앞선 이벤트 위에 적용 — 하면 위에 문서화된 값이 나옵니다. total_tokens는 스트리밍 응답에서 생략됩니다. 어떤 단일 이벤트도 입력 측과 출력 측을 모두 알지 못하므로, 스트림 도중에 계산한 총합은 잘못된 값이 됩니다. 스트림이 완료된 후 직접 버킷을 합산하세요.

이전 동작에서의 마이그레이션

Sunra는 이전에 /v1/messages에서 업스트림 usage 객체를 그대로 전달했습니다. 일부 제공자 앞단의 변환 계층은 캐시 생성 토큰을 input_tokens 안으로 접어 넣기 때문에, Anthropic 계약을 따라 세 버킷을 합산한 호출자는 캐시 생성 토큰을 두 번 계산했습니다.
  • 세 버킷을 합산한다면, Anthropic 계약이 설명하는 그대로이며, 이제 올바릅니다. 여러분 쪽에서 변경할 것은 없습니다.
  • 이전 동작을 보정하고 있었다면input_tokens에서 cache_creation_input_tokens를 직접 빼거나, 접어 넣는 동작을 역으로 계산하고 있었다면 — 중단하세요. 그 보정은 이제 이미 제외된 토큰을 빼는 것이 되어 입력을 과소 계상하게 됩니다.
  • total_tokens를 읽는다면, 이 값은 계속해서 전체 카운트를 보고합니다. 신규 입력, 두 캐시 버킷, 그리고 출력입니다.
배포 날짜가 아니라 마커를 기준으로 변경을 게이팅하세요. 그래야 마커가 있는 응답과 없는 응답 모두에 대해 동일한 코드 경로가 올바르게 동작합니다.