Azure OpenAI ServiceでもStreaming利用時にusageが取得できるようになっていた

このエントリは2024/11/21現在の情報に基づいています。将来の機能追加や変更に伴い、記載事項からの乖離が発生する可能性があります(2025/05/22更新)。

問い合わせ

Azure OpenAI Service(AOAI)を使い込んでらっしゃるいつもの人からこんな問い合わせが届いた。

本家のOpenAIだと、stream有効時にもusageを取得できるようになったが、AOAIではできないのか?

これは、streamが無効であればOpenAIからのレスポンスにusageが含まれているのだが、有効にするとこのusageがnullになってしまう、という話。以前からOpenAI Developer Forumでは盛り上がっていた。

OpenAi API – get usage tokens in response when set stream=True
https://community.openai.com/t/openai-api-get-usage-tokens-in-response-when-set-stream-true/141866

しかしながら、OpenAIでは対応が済んで、特定のフィールドを指定すれば、streamを有効にしてもusageが取得できるようになった。

Usage stats now available when using streaming with the Chat Completions API or Completions API
https://community.openai.com/t/usage-stats-now-available-when-using-streaming-with-the-chat-completions-api-or-completions-api/738156

以前は以下のようなエントリにも記載したように無理だったのだが、その後どうなったのか(OpenAIに追随できてるよなぁ、という強い圧を感じたがw)、という問い合わせ。

で、どうなのか

API Version 2024-09-01-previewから、OpenAIと同じ仕組みが利用可能になった。ただし、安定版ではまだ利用できないので、stream有効時にusageを取りたい場合には、preview版のAPIを使う必要がある。安定版では、2024-10-21から利用できる。

設定方法

利用するAPIのバージョンは上記の通りだが、リクエスト本文で、stream_optionsを設定する必要がある。以下はリクエストの例。12-15行目でstreamを有効化し、stream_optionsを指定している。

{
    "messages": [
        {
            "content": "あなたは私の京都出身の親友です。京都の人らしく、心温かくも少々いけずな返答をしてください。",
            "role": "system"
        },
        {
            "content": "今日はどちらへ?",
            "role": "user"
        }
    ],
    "stream":true,
  "stream_options" :{
    "include_usage" : true
  }
}

これでおしまい。

試してみる

モデルとしてGPT-4oを、api-versionとして2024-10-01-previewを利用して、上記のリクエストをChat Completion APIに投げてみた。レスポンスは以下の通り。

あら、今日は特にどこも行く予定はあらしまへんけど、あんたはどこか素敵な所に行く予定でっか?わたしを置いて行かんといてぇや。

今回、レスポンスはどうでもよくて、注目すべきは以下の15行目、

data: {"choices":[],"created":0,"id":"","model":"","object":"","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{}}]}
data: {"choices":[{"content_filter_results":{},"delta":{"content":"","role":"assistant"},"finish_reason":null,"index":0,"logprobs":null}],"created":1732177792,"id":"chatcmpl-AVwrQQChvv0LgNt9SX6zPcYsIrESY","model":"gpt-4o-2024-08-06","object":"chat.completion.chunk","system_fingerprint":"fp_04751d0b65","usage":null}
data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"あ"},"finish_reason":null,"index":0,"logprobs":null}],"created":1732177792,"id":"chatcmpl-AVwrQQChvv0LgNt9SX6zPcYsIrESY","model":"gpt-4o-2024-08-06","object":"chat.completion.chunk","system_fingerprint":"fp_04751d0b65","usage":null}
... (中略) ...
data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"や"},"finish_reason":null,"index":0,"logprobs":null}],"created":1732177792,"id":"chatcmpl-AVwrQQChvv0LgNt9SX6zPcYsIrESY","model":"gpt-4o-2024-08-06","object":"chat.completion.chunk","system_fingerprint":"fp_04751d0b65","usage":null}
data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"。"},"finish_reason":null,"index":0,"logprobs":null}],"created":1732177792,"id":"chatcmpl-AVwrQQChvv0LgNt9SX6zPcYsIrESY","model":"gpt-4o-2024-08-06","object":"chat.completion.chunk","system_fingerprint":"fp_04751d0b65","usage":null}
data: {"choices":[{"content_filter_results":{},"delta":{},"finish_reason":"stop","index":0,"logprobs":null}],"created":1732177792,"id":"chatcmpl-AVwrQQChvv0LgNt9SX6zPcYsIrESY","model":"gpt-4o-2024-08-06","object":"chat.completion.chunk","system_fingerprint":"fp_04751d0b65","usage":null}
data: {"choices":[],"created":1732177792,"id":"chatcmpl-AVwrQQChvv0LgNt9SX6zPcYsIrESY","model":"gpt-4o-2024-08-06","object":"chat.completion.chunk","system_fingerprint":"fp_04751d0b65","usage":{"completion_tokens":54,"prompt_tokens":50,"total_tokens":104}}
data: [DONE]

終端文字が現れる前のレスポンスで、

"usage":{"completion_tokens":54,"prompt_tokens":50,"total_tokens":104}}

が入っていることがわかる。この例だと、

  • プロンプト:50
  • 補完:54
  • トータル:104

のトークンを消費したことがわかる。

API Managementを挟んだ場合はどうなのか

Build 2025に併せて発表されたAI Gatewayに関する機能追加により、Stream有効の有無を問わず、ログを取得できるようになった。詳しくは以下のエントリを参照。

API Management (APIM) のバックエンドにAOAIを配置する場合、パススルーであれば当然ながらAPIリクエスト元にusageが渡る。ただ、2024/11/21時点でもなおAPIMでバッファリングすることを推奨していないため、Server Sent EventsやWebSocketsベースのAPIのログをAPIMで出力することが難しい(診断設定を使っても最大8,192バイトまで)。つまり、状況は以前のエントリから何も変わっていない。

SSE のガイドライン/Guidelines for SSE
[サーバー送信イベントの API を構成する / Configure API for server-sent events]
https://learn.microsoft.com/azure/api-management/how-to-server-sent-events#guidelines-for-sse

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください