カゴシマニアックスで動かしている WordPress プラグイン「PRTIMES記事ジェネレーター」に、ロリポップ!AIゲートウェイ を足しました。
この記事は、その実装手順のメモです。Claude / ChatGPT / Gemini の直APIに加えて、ゲートウェイ経由でも記事生成できるようにしたときの記録です。
プラグインが元々やっていること
PR TIMES の URL を管理画面に貼ると、次の流れで下書きができます。
- プレスリリースの HTML を取得する
- タイトル・企業名・日付・本文を抜き出す
- 生成AIに渡してニュース記事にする
- WordPress の下書きとして保存する
生成AIは、もともと次の3つを直接呼んでいました。
- Claude(Anthropic)
- ChatGPT(OpenAI)
- Gemini(Google)
ここに第4のプロバイダーとして、ロリポップ!AIゲートウェイを足しました。プラグインの版は v1.3.0 です。
なぜゲートウェイを足したか
直APIは、プロバイダーごとにキーもエンドポイントも違います。モデルを切り替えたいたびに、Anthropic / OpenAI / Google の契約と請求が分かれます。
ロリポップ!AIゲートウェイは、1つのAPIキーと1つの接続先で複数モデルを呼べます。支払いは日本円で、許可モデルや自動選択もゲートウェイ側で管理できます。カゴシマニアックスのサーバーがロリポップなので、同じ系列のサービスを足す判断にしました。
公式ドキュメントで確認したこと
実装前に見たのは、次のページです。
今回のプラグインで使ったのは、この4点だけです。
| 項目 | 値 |
|---|---|
| ベースURL | https://ai-gateway.lolipop.jp |
| 認証 | Authorization: Bearer <APIキー> |
| 生成 |
POST /v1/chat/completions(OpenAI Chat Completions 互換) |
| モデル確認 | GET /v1/models |
ゲートウェイは Responses API と Anthropic Messages API にも対応しています。既存プラグインが OpenAI 形式だったので、今回は Chat Completions だけ使いました。コードを大きく書き換えずに足せます。
モデルIDの扱い
ドキュメントの注意が重要でした。
-
modelにはGET /v1/modelsが返す公開名をそのまま指定する - アプリケーションへモデルIDを固定で埋め込まない
-
autoが使えるAPIキーなら、それを指定すると許可候補から自動選択される - Claude も
anthropic/claude-sonnet-4-6のような接頭辞ではなく、公開名(例:claude-sonnet-4-6)で返る
公開カタログの表は openai/gpt-5-6-luna のような表記ですが、APIに渡すのは一覧APIが返す文字列です。迷ったら auto にします。
max_tokens を省略すると、ゲートウェイ側が 4096 を補います。このプラグインでは他プロバイダーに合わせて 2048 を明示しています。
実装手順
既存のプロバイダー追加と同じ型に乗せました。直APIを3本持っているので、ゲートウェイも「キー・モデル・プロンプト・呼び出し関数」のセットです。
1. 設定項目を登録する
wp_options に次を足します。接頭辞は既存どおり prtag_ です。
prtag_lolipop_api_keyprtag_lolipop_modelprtag_prompt_lolipop
$fields = [
'prtag_provider',
'prtag_claude_api_key', 'prtag_claude_model',
'prtag_openai_api_key', 'prtag_openai_model',
'prtag_gemini_api_key', 'prtag_gemini_model',
'prtag_lolipop_api_key', 'prtag_lolipop_model',
'prtag_default_category',
'prtag_prompt_claude',
'prtag_prompt_openai',
'prtag_prompt_gemini',
'prtag_prompt_lolipop',
];
foreach ( $fields as $f ) {
register_setting( 'prtag_options_group', $f, [
'sanitize_callback' => 'sanitize_textarea_field',
] );
}
2. 生成時の振り分けに lolipop を足す
AJAX の provider は sanitize_key() 済みです。許可リストへ lolipop を追加し、既存の prtag_call_ai() から呼びます。
if ( ! in_array( $provider, [ 'claude', 'openai', 'gemini', 'lolipop' ], true ) ) {
$provider = 'claude';
}
switch ( $provider ) {
case 'openai': return prtag_call_openai( $system, $user_msg );
case 'gemini': return prtag_call_gemini( $system, $user_msg );
case 'lolipop': return prtag_call_lolipop( $system, $user_msg );
default: return prtag_call_claude( $system, $user_msg );
}
内部キーは lolipop、画面上のラベルは「AIゲートウェイ」にしています。ユーザーが見る名前と、option 名を分けた方が後から追いやすいです。
3. Chat Completions を叩く
ここが本体です。既存の OpenAI 呼び出しとほぼ同じで、エンドポイントとキーの option 名だけ変えています。
function prtag_call_lolipop( string $system, string $user_msg ): array|WP_Error {
$api_key = get_option( 'prtag_lolipop_api_key', '' );
if ( empty( $api_key ) ) {
return new WP_Error( 'no_key', 'ロリポップ!AIゲートウェイのAPIキーが未設定です。設定ページで入力してください。' );
}
$model = get_option( 'prtag_lolipop_model', 'auto' );
if ( $model === '' ) {
$model = 'auto';
}
$res = wp_remote_post( 'https://ai-gateway.lolipop.jp/v1/chat/completions', [
'timeout' => 90,
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => 'Bearer ' . $api_key,
],
'body' => wp_json_encode( [
'model' => $model,
'max_tokens' => 2048,
'messages' => [
[ 'role' => 'system', 'content' => $system ],
[ 'role' => 'user', 'content' => $user_msg ],
],
] ),
] );
if ( is_wp_error( $res ) ) {
return $res;
}
$code = wp_remote_retrieve_response_code( $res );
$data = json_decode( wp_remote_retrieve_body( $res ), true );
if ( $code !== 200 ) {
$msg = $data['error']['message'] ?? "AIゲートウェイ APIエラー (HTTP {$code})";
$err_code = $data['error']['code'] ?? '';
$hints = [
'model_not_found' => 'モデルIDが一覧にありません。「auto」にするか、ダッシュボードで確認した公開名を指定してください。',
'model_not_allowed' => 'このAPIキーでは指定モデルが許可されていません。ダッシュボードの許可モデルを確認してください。',
'auto_required' => 'このAPIキーは自動選択が必須です。モデルを「auto」に設定してください。',
'auto_resolution_failed' => 'auto で実行できる候補がありません。ダッシュボードの許可モデルと自動選択候補を確認してください。',
];
if ( isset( $hints[ $err_code ] ) ) {
$msg .= ' — ' . $hints[ $err_code ];
}
return new WP_Error( 'lolipop_error', $msg );
}
$text = $data['choices'][0]['message']['content'] ?? '';
if ( empty( $text ) ) {
return new WP_Error( 'empty', 'AIからの応答が空でした' );
}
return prtag_parse_generated( $text );
}
成功時の本文は choices[0].message.content です。クイックスタートのレスポンス例と同じ形です。auto を指定した場合、レスポンスの model には実際に応答した公開名が入ります。
エラーコードは公式の「使えないモデルを指定したとき」に合わせています。ゲートウェイ特有の失敗を、管理画面で日本語のまま見られるようにしました。
4. 管理画面のタブと設定ページ
生成画面のプロバイダー選択へ、キーが保存されているときだけ有効なタブを足します。
$has_lolipop = (bool) get_option( 'prtag_lolipop_api_key' );
$provider_labels = [
'claude' => [ 'label' => 'Claude', 'icon' => '🟠', 'set' => $has_claude ],
'openai' => [ 'label' => 'ChatGPT', 'icon' => '🟢', 'set' => $has_openai ],
'gemini' => [ 'label' => 'Gemini', 'icon' => '🔵', 'set' => $has_gemini ],
'lolipop' => [ 'label' => 'AIゲートウェイ', 'icon' => '🍭', 'set' => $has_lolipop ],
];
設定ページのモデル候補は、よく使う公開名と auto です。一覧に無いIDは、既存の「カスタムモデル名」入力に任せています。
$lolipop_models = [
'auto' => 'auto(推奨・ゲートウェイが自動選択)',
'claude-sonnet-4-6' => 'Claude Sonnet 4.6',
'claude-haiku-4-5' => 'Claude Haiku 4.5',
'gpt-5-6-luna' => 'GPT-5.6 Luna(低コスト)',
'gpt-5-6-terra' => 'GPT-5.6 Terra',
'gemini-2.5-flash' => 'Gemini 2.5 Flash',
'gemini-3-5-flash-lite' => 'Gemini 3.5 Flash Lite',
];
フロントの admin.js は、ローディング文言と結果バッジに lolipop を足すだけです。生成・下書き保存のAJAXは、既存の provider パラメータを流用しています。
WordPress での設定手順
コードを上げたあとの操作です。
- ロリポップ!AIゲートウェイ のダッシュボードでプロジェクトを開く
- APIキーを発行する
- WordPress 管理画面の PR記事生成 → 設定 を開く
- 「ロリポップ!AIゲートウェイ」にAPIキーを貼る
- モデルはまず
autoにする - 設定を保存する
- PR記事生成 の画面で「AIゲートウェイ」タブを選ぶ
- PR TIMES の URL を入れて生成する
キーが空だと、タブは「APIキー未設定」で選べません。既存の Claude / ChatGPT / Gemini はそのまま残っています。直APIを止める必要はありません。
特定モデルを使いたい場合は、ダッシュボードか GET /v1/models で公開名を確認してから指定します。
curl "https://ai-gateway.lolipop.jp/v1/models" \
-H "Authorization: Bearer $AI_GATEWAY_API_KEY"
本番への上げ方
カゴシマニアックスでは、ローカルのプラグインディレクトリを wp-content/plugins/prtimes-article-generator/ へ同期しました。上げる前に、稼働中の v1.2.0 をサーバー側で tar バックアップしています。
上げる対象は次だけです。
prtimes-article-generator.phpassets/admin.jsassets/admin.css
.git は本番へ出していません。PHP の構文チェックは、上げたあとサーバー上でも実行しています。
今回やらなかったこと
実装を小さく保つために、次は入れていません。
-
GET /v1/modelsを設定画面から自動取得する - Responses API / Anthropic Messages API
- ストリーミング
- BYOK(手元の Anthropic / OpenAI キーをゲートウェイへ預ける)
ドキュメントは「モデルIDをアプリへ固定で埋め込まない」と書いています。今回はプリセット+カスタム入力で逃しています。モデルが増えたら、一覧APIから select を埋める方が正しいです。
参考
既存の直APIを残したまま、OpenAI互換の1本を足すだけでゲートウェイに載せられました。同じ型の WordPress プラグインなら、呼び出し関数を1つ追加する作業です。