OpenHandsのLLMにCloudflare AI Gatewayを使おうとしてダメだった話
OpenHandsを利用したAIコーディングに挑戦中。CloudflareのAI Gatewayは使えない可能性が高い。API構造が異なるため、エラー発生。CloudflareのAPI key取得やAI Gateway作成を試みるも失敗。OpenHandsの準備も進めるも、API認証エラーで実行失敗。401 UnauthorizedエラーでCloudflare AI Gatewayのサポート必要。OSSな解決策を模索。今後同様の課題に直面する方への参考に。
目次
OpenHandsを利用したAIコーディングに現在挑戦中です。どのようなモデルやAPIが使えるかを深掘りしてみるため、GitHub Actionsから利用する方法でCloudflareのAI Gatewayが使えるかどうかを検証してみます。
結論: Cloudflare AI Gatewayは現状使えなさそう
いくつかのメソッドでパスがハードコードされています。Cloudflare AI GatewayのAPI構造が他のLLMと異なる形式であるため、現状は使うことができなさそうです。以下に発生したエラー文を記載します。
07:27:16 - openhands:ERROR: agent_controller.py:228 - [Agent Controller default] Error while running the agent (session ID: default): litellm.AuthenticationError: AnthropicException - {"result":null,"success":false,"errors":[{"code":10000,"message":"Authentication error"}],"messages":[]}. Traceback: Traceback (most recent call last):
File "/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/litellm/llms/anthropic/chat/handler.py", line 412, in completion
response = client.post(
^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/litellm/llms/custom_httpx/http_handler.py", line 557, in post
raise e
File "/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/litellm/llms/custom_httpx/http_handler.py", line 538, in post
response.raise_for_status()
File "/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/httpx/_models.py", line 829, in raise_for_status
raise HTTPStatusError(message, request=request, response=self)
httpx.HTTPStatusError: Client error '401 Unauthorized' for url '***/v1/messages'
とはいえせっかくなので、挑戦の記録をここに残しておきます。
CloudflareのAPI keyを取得する
CloudflareのAI Gatewayを使うには、ユーザーAPIトークンを取得します。これはアカウントのプロフィールページから取得しましょう。「Workers AI」のテンプレートがありますので、これを使用します。

取得したトークンはGitHub Actionsで利用しますのでコピーしておきましょう。
Cloudflare AI Gatewayを作成する
続いてAI Gatewayを作成しましょう。レート制限については少なくともオフにしておきましょう。

動作確認のため、curlで呼び出してみましょう。ここではDeepSeekを選んでみました。
curl -X POST https://gateway.ai.cloudflare.com/v1/xxxxx/for-openhands-demo/workers-ai/@hf/thebloke/deepseek-coder-6.7b-base-awq \
--header 'Authorization: Bearer CF_TOKEN' \
--header 'Content-Type: application/json' \
--data '{"prompt": "What is Cloudflare?"}'
返答が生成されている様子ならば、Cloudflare側の設定はOKです。
GitHubリポジトリのシークレットへ投入する
続いてOpenHandsの準備を始めます。ドキュメントに従って環境変数を設定しましょう。
LLM_API_KEY
: CloudflareのAPIキーLLM_BASE_URL
: Cloudflare AI GatewayのURL
LLM_MODEL
も指定できそうなのですが、OpenHandsが想定するフォーマットではないため、今回は指定せずに進めます。
実行結果: API認証エラーで失敗
ドキュメントを読む範囲では、これで動きそう・・・に見えます。ただ、実際にActionsを実行するとエラーが発生しました。

エラーログがこちらです。POSTリクエストがうまくいっていない様子が伺えます。
07:27:16 - openhands:ERROR: agent_controller.py:228 - [Agent Controller default] Error while running the agent (session ID: default): litellm.AuthenticationError: AnthropicException - {"result":null,"success":false,"errors":[{"code":10000,"message":"Authentication error"}],"messages":[]}. Traceback: Traceback (most recent call last):
File "/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/litellm/llms/anthropic/chat/handler.py", line 412, in completion
response = client.post(
^^^^^^^^^^^^
File "/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/litellm/llms/custom_httpx/http_handler.py", line 557, in post
raise e
File "/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/litellm/llms/custom_httpx/http_handler.py", line 538, in post
response.raise_for_status()
File "/opt/hostedtoolcache/Python/3.12.9/x64/lib/python3.12/site-packages/httpx/_models.py", line 829, in raise_for_status
raise HTTPStatusError(message, request=request, response=self)
httpx.HTTPStatusError: Client error '401 Unauthorized' for url '***/v1/messages'
エラー文に、 '401 Unauthorized' for url '***/v1/messages'
と記載されています。Cloudflare AI Gatewayを使っているならば、v1/messages
というパスにはならないため、この辺りはハードコードされたパスである可能性が高そうです。
実現するには?
Cloudflare AI Gatewayをサポートしてもらうしかなさそうです。Amazon BedrockのProxyらしきものがありましたので、同様のクラスを作ってマージしてもらうか、forkすることで対応できるのではないかと思います。この辺りはOSSならではの解決策ではありますね。
同様にCloudflare AI Gatewayの利用を検討される方が今後出てきた時、この記事が対応や提案の参考になれば幸いです。